1 条题解
-
0
P6790 题解
题目大意
给一张 个点 条边的图,保证存在某种保留 条边的方案使得新图是边仙人掌,求图的生成树个数。
数据范围:。
思路分析
注意到原图环很少,因此考虑证明原图是广义串并联图,任取四个点,容易发现无法选出六条边不交的路径将他们两两连接,因此原图是广义串并联图。
考虑用广义串并联图方法逐步简化这张图(删一度点、缩二度点、叠合重边)。
观察广义串并联图缩边后的一条边 ,显然对应原图的一个子图,且子图里的点除了 以外只和 连接,因此这个子图最后的状态只有两种: 连通或 不连通。
不妨设 连通方案数为 ,不连通方案数为 。
- 删一度点:答案乘上 。
- 缩二度点:合并 得到 $f(e^*)=f(e_1)\times f(e_2),g(e^*)=f(e_1)\times g(e_2)+f(e_2)\times g(e_1)$。
- 叠合重边:合并 得到 $f(e^*)=f(e_1)\times g(e_2)+f(e_2)\times g(e_1),g(e^*)=g(e_1)\times g(e_2)$。
用队列维护度数 的点,逐步 BFS 即可。
时间复杂度 。
代码呈现
#include<bits/stdc++.h> #define ll long long #define fi first #define se second using namespace std; const int MAXN=5e5+5,MOD=998244353; int n,m; map <int,array<ll,2>> g[MAXN]; queue <int> Q; ll ans=1; inline void link(int u,int v,array<ll,2> x) { if(g[u].count(v)) { array<ll,2> y=g[u][v]; g[u][v]=g[v][u]={x[0]*y[0]%MOD,(x[0]*y[1]+x[1]*y[0])%MOD}; } else g[u][v]=g[v][u]=x; } inline void upd(int x) { if(g[x].size()<=2) Q.push(x); } signed main() { scanf("%d%d",&n,&m); for(int i=1,u,v;i<=m;++i) scanf("%d%d",&u,&v),link(u,v,{1,1}); for(int i=1;i<=n;++i) upd(i); while(Q.size()) { int u=Q.front(); Q.pop(); if(g[u].size()==1) ans=ans*(g[u].begin()->se)[1]%MOD; if(g[u].size()==2) { int x=g[u].begin()->fi,y=g[u].rbegin()->fi; auto c=g[u].begin()->se,d=g[u].rbegin()->se; link(x,y,{(c[0]*d[1]+c[1]*d[0])%MOD,c[1]*d[1]%MOD}); } for(auto e:g[u]) g[e.fi].erase(u),upd(e.fi); g[u].clear(); } printf("%lld\n",ans); return 0; }
- 1
信息
- ID
- 2433
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者