正题
题目大意
给出\(n\)个点\(n+k-1\)条边的一张图,求有多少种删除若干条边的方案使得图依旧联通。
\(1\leq n\leq 10^5,1\leq k\leq 10\)
解题思路
注意到\(k\)很小,我们考虑先搞出一棵\(dfs\)树然后剩下的做非树边。
这里有个结论是,我们将第\(i\)条非树边权值定为\(2^i\),树边权值定义为覆盖了它的非树边的权值的异或和,那么删除边集\(S\)后图不连通的充要条件就是存在一个子集异或和为\(0\)。
感性理解一下,如果一个边集异或和为\(0\),显然边集内肯定有树边,考虑一条非树边(如果没有显然是会被分割的),它如果跨过偶数条被删的边那么中间如果有被删除的树边就会被分割出中间的连通块,如果它跨过奇数条被删除的树边那么显然它也要被删除,无法连接两个连通块。
然后考虑一张被删除后不连通的图,我们将边集中连接原本两个连通块的边拿出来,如果只有树边显然这个树边的权值为\(0\),如果有非树边,那么中间被删除的树边也会异或上这个权值,所以异或和还是为\(0\)。
这样充分性和必要性就证明完了。
至于做法我们得到所有边的权值,那么显然权值种类不会超过\(3k\)种,我们直接爆搜每种权值选不选用线性基求解就好了。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define mp(x,y) make_pair(x,y)
using namespace std;
const int N=1e5+10;
const int P=998244353;
struct node{
int to,next,w ;
}a[N<<1];
int n,k,tot,m,ls[N],dep[N],c[N],w[N],ans,d[90];
bool v[N];
vector<pair<int,int> >f;
void addl(int x,int y){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;return;
}
void dfs(int x,int fa){
v[x]=1;
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(i==(fa^1)||a[i].w)continue;
if(v[y]){
a[i].w=a[i^1].w=1;
w[x]^=(1<<m);
w[y]^=(1<<m);
c[1<<m]++;m++;
}
else dfs(y,i);
}
return;
}
void build(int x,int fa){
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(y==fa||a[i].w)continue;
build(y,x);w[x]^=w[y];
}
if(fa)c[w[x]]++;
return;
}
void calc(int dep,int r){
if(dep==f.size())
{(ans+=r)%=P;return;}
int x=f[dep].first,p=-1;
calc(dep+1,r);
for(int i=m-1;i>=0;i--)
if((x>>i)&1){
if(d[i])x^=d[i];
else {p=i;break;}
}
if(x){
d[p]=x;
calc(dep+1,1ll*r*f[dep].second%P);
d[p]=0;
}
}
int main()
{
freopen("connected.in","r",stdin);
freopen("connected.out","w",stdout);
scanf("%d%d",&n,&k);tot=1;
for(int i=1;i<n+k;i++){
int x,y;
scanf("%d%d",&x,&y);
addl(x,y);addl(y,x);
}
dfs(1,0);
build(1,0);
for(int i=1;i<(1<<m);i++)
if(c[i])f.push_back(mp(i,c[i]));
calc(0,1);
printf("%d\n",ans);
return 0;
}