(m<n<=1e5,有重边)
题目表述有问题.....
给定一张图(不一定联通),每条边可以选择连接的两个点之一,剩余的点可以自己成对,问方案数。
一开始是真的被吓到了....觉得可写性极低的一题.....
但是两个结论如果推出来的话就蛮好的了
solution:
一开始想:对于每个块进行大小统计,然后组合数乘在一起。但是,有点麻烦:
有环的情况:对于一个联通块有环,那么就会有n个点,n条边,那就意味着会有一个联通块只有一个单独的点。单独考虑环块(下统称环块)
看看这个三元环(误),先确定第一条边选左或右两个点,如果第一个边确定了自己的选择,那么它就会占用下一条边的一个选择的权利,也就是:
如果确定了一条边,就可以确定整个环上的方案数:2
如果不是裸环的环块呢?
看这个:
同上,确定了环上的两个,链的一端就会被占用,也就是说:
只要是环块,对答案的贡献就是2!
(伟大的发现)
考虑无环的情况:
一共n个点,n-1条边(无向图,不能有环就是树),一共有n种方案。感性证明一下(不知道该怎么理性):
最后一个点不被选,而这个不被选的点一共有n种情况,这就是n种方案。
最后根据乘法原理,把所有的方案数乘起来就是ans。
综上结论,题目就变成了:给定一张图,判断联通块大小,找联通块里的环,统计答案
联通块?我有并查集!
找环?我有tarjan!
(旁边两位z姓大佬给我闷头两巴掌,这题还tarjan?你想变成zwjdd长度???)
这里介绍一种无向图判环方法(注意,不是找环,只能判断存在)
还是使用并查集,原理就是在一个联通块内,如果遍历边的次数=点的个数,那么这里就存在环。
貌似是这样哈....我太弱了
代码(注释版):
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+5; const int mod=1e9+7; long long f[maxn],num[maxn],n,m,ans=1,c[maxn]; long long find(long long x){return f[x]==x?x:f[x]=find(f[x]);}//被大佬逼的一行冰茶姬//variable declare:num[]:联通块大小,f[]不解释,c[]联通块内边的数量 int main() { scanf("%lld%lld",&n,&m); for(int i=1;i<=n;i++) f[i]=i,num[i]=1;//初始化 for(int i=1;i<=m;i++) { long long x,y; scanf("%lld%lld",&x,&y); long long fa=find(x); long long fb=find(y); if(fa!=fb)//合并 { f[fa]=fb; num[fb]+=num[fa];//把联通块大小合并 num[fa]=0;//清零联通块大小 c[fb]+=c[fa]+1;//联通块内多了一条边 } else { c[fb]++;//否则多了一条非树边 } } for(int i=1;i<=n;i++) { if(f[i]==i&&c[i]<num[i])//如果边数没有点数多 ans=ans*num[i]%mod;//统计答案 else if(f[i]==i&&c[i]>=num[i])//否则就是有环,直接*2 ans=(ans<<1)%mod;//记得续命 } printf("%lld",ans);//longlong又出锅了 return 0; }
(完)