给定连通图,需要选定至少一个关键点,然后保护至少一条边,被保护的边不会被破坏,需要保证无论破坏哪条边都不会影响关键点之间的连通性。求方案数。
这题我感觉还是有点难度的,主要是细节很多,如果不是我写的太麻烦了的话,感觉评个紫应该比较合理(不过似乎洛谷上西西弗的题难度评级都偏低一些
首先这个题意读完应该就能想到 Tarjan 边双缩点吧,这个还是很显然的。如果在同一个边双里,那么无论删去这里面的任意哪条边都不会使方案不合法,所以可以直接进行 e-BCC 缩点,这样对于每一个 e-BCC 里,如果设其中的边数为
然后同时不难发现因为图本身是连通的,所以进行 e-BCC 缩点之后原图就会变成一棵树,所以此时这个东西就很显然变成了一个 树形DP。或者说就是变成了
考虑 DP,不难想到一个很显然的状态,设
考虑转移,对于任意一个
然后考虑
同时注意对于这里的转移
于是我们就发现,这东西假了。。
因为我们漏下了一些情况,再回头考虑我们设置的时候为必须满足
这个东西我们可以考虑将状态改为
我们实际上可以在 e-BCC 缩点之后再跑一遍 dfs,维护一下对应子树中的包括被缩掉的所有边的数量,设其为
考虑发现如果
然后注意对于根节点时,其不存在我们刚才说的漏下的那些方案,但需要加上一般的答案,也就是
至此,这道细节巨多的题就做完了。
xxxxxxxxxx138123
45678910
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
2223
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 初稿