给定连通图,需要选定至少一个关键点,然后保护至少一条边,被保护的边不会被破坏,需要保证无论破坏哪条边都不会影响关键点之间的连通性。求方案数。
这题我感觉还是有点难度的,主要是细节很多,如果不是我写的太麻烦了的话,感觉评个紫应该比较合理(不过似乎洛谷上西西弗的题难度评级都偏低一些
首先这个题意读完应该就能想到 Tarjan 边双缩点吧,这个还是很显然的。如果在同一个边双里,那么无论删去这里面的任意哪条边都不会使方案不合法,所以可以直接进行 e-BCC 缩点,这样对于每一个 e-BCC 里,如果设其中的边数为
然后同时不难发现因为图本身是连通的,所以进行 e-BCC 缩点之后原图就会变成一棵树,所以此时这个东西就很显然变成了一个 树形DP。或者说就是变成了
考虑 DP,不难想到一个很显然的状态,设
考虑转移,对于任意一个
然后考虑
同时注意对于这里的转移
于是我们就发现,这东西假了。。
因为我们漏下了一些情况,再回头考虑我们设置的时候为必须满足
这个东西我们可以考虑将状态改为
我们实际上可以在 e-BCC 缩点之后再跑一遍 dfs,维护一下对应子树中的包括被缩掉的所有边的数量,设其为
考虑发现如果
然后注意对于根节点时,其不存在我们刚才说的漏下的那些方案,但需要加上一般的答案,也就是
至此,这道细节巨多的题就做完了。
xxxxxxxxxx
1381
2
3
4
5
6
7
8
9
10
11using namespace std;
12
13mt19937 rnd(random_device{}());
14int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
15bool rnddd(int x){return rndd(1, 100) <= x;}
16
17typedef unsigned int uint;
18typedef unsigned long long unll;
19typedef long long ll;
20typedef long double ld;
21
22
23
24template < typename T = int >
25inline T read(void);
26
27struct Edge{
28 Edge* nxt;
29 Edge* rev;
30 int to;
31 bool vis;
32 OPNEW;
33}ed[4100000];
34ROPNEW(ed);
35Edge* head[1100000];
36Edge* ehead[1100000];
37
38int N, M;
39int dfn[1100000], low[1100000], eBCC(0), belong[1100000];
40int cnte[1100000], cntv[1100000];
41int sume[1100000];
42bool inS[1100000];
43ll dp[1100000][2];
44ll pow2[2100000];
45ll ans(0);
46
47void Tarjan(int p = 1){
48 static stack < int > cur;
49 static int cdfn(0);
50 cur.push(p); inS[p] = true;
51 low[p] = dfn[p] = ++cdfn;
52 for(auto i = head[p]; i; i = i->nxt){
53 if(i->vis)continue;
54 i->vis = i->rev->vis = true;
55 if(!dfn[SON]){
56 Tarjan(SON);
57 low[p] = min(low[p], low[SON]);
58 if(low[SON] > dfn[p]){
59 ++eBCC;
60 while(true){
61 int tp = cur.top(); cur.pop();
62 belong[tp] = eBCC;
63 inS[tp] = false;
64 ++cntv[eBCC];
65 if(tp == SON)break;
66 }
67 }
68 }else if(inS[SON])
69 low[p] = min(low[p], dfn[SON]);
70 }
71 if(p == 1 && !cur.empty()){
72 ++eBCC;
73 while(true){
74 int tp = cur.top(); cur.pop();
75 belong[tp] = eBCC;
76 inS[tp] = false;
77 ++cntv[eBCC];
78 if(tp == p)break;
79 }
80 }
81}
82void dfs_edge(int p = 1, int fa = 0){
83 sume[p] = cnte[p];
84 for(auto i = ehead[p]; i; i = i->nxt){
85 if(SON == fa)continue;
86 dfs_edge(SON, p);
87 sume[p] += sume[SON] + 1;
88 }
89}
90void MakeDP(int p = 1, int fa = 0){
91 dp[p][0] = pow2[cnte[p]];
92 dp[p][1] = (pow2[cntv[p]] - 1) * pow2[cnte[p]] % MOD;
93 for(auto i = ehead[p]; i; i = i->nxt){
94 if(SON == fa)continue;
95 MakeDP(SON, p);
96 dp[p][1] = (dp[p][1] * dp[SON][0] * 2 % MOD + dp[p][1] * dp[SON][1] % MOD + dp[p][0] * dp[SON][1] % MOD) % MOD;
97 dp[p][0] = dp[p][0] * dp[SON][0] * 2 % MOD;
98 }(ans += (p == 1 ? dp[p][1] : dp[p][1] * pow2[sume[1] - sume[p] - 1] % MOD)) %= MOD;
99}
100
101int main(){
102 N = read(), M = read();
103 pow2[0] = 1;
104 for(int i = 1; i <= 2010000; ++i)pow2[i] = pow2[i - 1] * 2 % MOD;
105 for(int i = 1; i <= M; ++i){
106 int s = read(), t = read();
107 head[s] = new Edge{head[s], npt, t};
108 head[t] = new Edge{head[t], npt, s};
109 head[s]->rev = head[t], head[t]->rev = head[s];
110 }Tarjan();
111 for(int p = 1; p <= N; ++p)
112 for(auto i = head[p]; i; i = i->nxt)
113 if(belong[p] != belong[SON])
114 ehead[belong[p]] = new Edge{ehead[belong[p]], npt, belong[SON]};
115 else ++cnte[belong[p]];
116 for(int i = 1; i <= eBCC; ++i)cnte[i] >>= 1;
117 dfs_edge();
118 MakeDP();
119 printf("%lld\n", ans);
120 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
121 return 0;
122}
123
124template < typename T >
125inline T read(void){
126 T ret(0);
127 int flag(1);
128 char c = getchar();
129 while(c != '-' && !isdigit(c))c = getchar();
130 if(c == '-')flag = -1, c = getchar();
131 while(isdigit(c)){
132 ret *= 10;
133 ret += int(c - '0');
134 c = getchar();
135 }
136 ret *= flag;
137 return ret;
138}
update-2022_11_29 初稿