BZOJ1016 最小生成树计数
题面描述
给定一个n个点、m条边的无向图,求其最小生成树的个数。相同边权的边不会超过10条。
思维难度:提高+;代码难度:提高+;
题解:
先给出两个引理:
1.克鲁斯卡尔求最小生成数实际上是分成很多个阶段的,你可以感受到:很多边权相同的边因为排序顺序不同,导致它们被访问的顺序不同。但每处理完一个边权相同的边集,当前图的一些性质(连通块数、边数、代价数)是一样的,并不会因为同边权集中的边的排序顺序改变而改变。换句话说,图的本质可能会变,但它的“表现”是不变的。
2.不同的阶段产生的边,对于每棵最小生成树,是恒定的,且每个阶段在保证正确的如何选择,对后面的选择没有影响。统计答案时,应该使用乘法原理。
那么我们接下来有两种方法:Matrix Tree 或者 爆搜2333
注意到题面中写的“相同边权的边不超过10条”,直接指数级爆搜选不选就可以了。并查集时不能用路径压缩,因为你要有撤销功能。
提交次数:2次 第一次是因为没删调试代码。B站不显示错误输出真是好坑啊。
这题启示我们:省选题也有指数级爆搜的舞台(ohhh)。
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <vector> #include <cstring> #include <queue> #define LL long long int #define ls (x << 1) #define rs (x << 1 | 1) using namespace std; const int Mod = 31011; struct Node{int u,v,w;}E[Mod]; int n,m,Ans,flag,fa[Mod],vis[Mod],ans; int gi() { int x=0,res=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*res; } LL gl() { LL x=0,res=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return x*res; } bool cmp(const Node &a,const Node &b){return a.w<b.w;} int find(int x){return fa[x]==x?x:find(fa[x]);} void dfs(int dep,int ch,int nu,int ed,int num) { if(nu==num){ans++;return;} if(dep==ed)return; dfs(dep+1,0,nu,ed,num); if(find(E[dep+1].u)!=find(E[dep+1].v)) { int x=find(E[dep+1].v); fa[x]=find(E[dep+1].u); dfs(dep+1,1,nu+1,ed,num); fa[x]=x; } } int solve(int l,int r) { int num=0,fat[110];ans=0; for(int i=1;i<=n;++i)fat[i]=fa[i]; for(int i=l;i<=r;++i) if(find(E[i].u)!=find(E[i].v)) num++,fa[find(E[i].v)]=find(E[i].u); for(int i=1;i<=n;++i)swap(fa[i],fat[i]); dfs(l-1,0,0,r,num); for(int i=1;i<=n;++i)fa[i]=fat[i]; return ans; } void work() { sort(E+1,E+m+1,cmp); for(int i=1;i<=n;++i)fa[i]=i; for(int i=1,num=0;i<=m;++i) { int x=find(E[i].u),y=find(E[i].v); if(x!=y)fa[y]=x,num++; if(num==n-1)break; if(i==m)Ans=0; } for(int i=1;i<=n;++i)fa[i]=i; for(int i=1,last=1;i<=m;++i) { if(E[i].w!=E[last].w) { Ans*=solve(last,i-1); Ans%=Mod;last=i; } if(i==m){Ans*=solve(last,m);} } } int main() { n=gi();m=gi();Ans=1; for(int i=1;i<=m;++i) E[i].u=gi(),E[i].v=gi(),E[i].w=gi(); work();printf("%d",Ans%Mod); return 0; }