[BZOJ1016][JSOI2008]最小生成树计数
https://blog.sengxian.com/solutions/bzoj-1016
先总结一下MST的性质吧:
1.(圈性质)考虑一条非树边和一些树边构成的环,环上所有树边的权值一定不大于这条非树边。
2.(割性质)考虑图的一个割,这个割中的最小边一定被选入了MST。
3.考虑同一张图的两个不同MST A和B,将两个MST的边分别从小到大排列,则每对同一位置的边权都是相等的。
4.从小到大将MST加入到图中,在加完任一权值的所有边后,图的连通性是固定的。
考虑利用这些定理,根据(3)和(4),我们只要先求出一个MST,就能知道每种权值的边在MST中要出现多少次,以及每种边权的边全部加完后图的连通块个数。有了这些信息,我们只需要对于每种权值搜索选取哪些边,然后用乘法原理即可得到答案。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 using namespace std; 5 6 const int N=1010,mod=31011; 7 int n,m,ans=1,sm,cnt,tot,fa[N],sz[N]; 8 struct E{ int u,v,w; }e[N]; 9 struct P{ int l,r,v; }a[N]; 10 11 inline int rd(){ 12 int x=0; char ch=getchar(); 13 while (ch<'0' || ch>'9') ch=getchar(); 14 while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar(); 15 return x; 16 } 17 18 bool cmp(E a,E b){ return a.w<b.w; } 19 int find(int x){ return (x==fa[x]) ? x : find(fa[x]); } 20 21 void dfs(int x,int now,int k){ 22 if (now==a[x].r+1){ 23 if (k==a[x].v) sm++; 24 return; 25 } 26 int p=find(e[now].u),q=find(e[now].v); 27 if (p!=q){ 28 if (sz[p]<sz[q]) swap(p,q); 29 fa[p]=q; sz[q]+=sz[p]; 30 dfs(x,now+1,k+1); 31 fa[p]=p; sz[q]-=sz[p]; 32 } 33 dfs(x,now+1,k); 34 } 35 36 int main(){ 37 freopen("bzoj1016.in","r",stdin); 38 freopen("bzoj1016.out","w",stdout); 39 n=rd(); m=rd(); 40 rep(i,1,n) fa[i]=i,sz[i]=1; 41 rep(i,1,m) e[i].u=rd(),e[i].v=rd(),e[i].w=rd(); 42 sort(e+1,e+m+1,cmp); 43 rep(i,1,m){ 44 if (e[i].w!=e[i-1].w) a[cnt].r=i-1,a[++cnt].l=i; 45 int p=find(e[i].u),q=find(e[i].v); 46 if (p==q) continue; 47 a[cnt].v++; tot++; 48 if (sz[p]>sz[q]) swap(p,q); 49 fa[p]=q; sz[q]+=sz[p]; 50 } 51 a[cnt].r=m; 52 if (tot!=n-1){ puts("0"); return 0; } 53 rep(i,1,n) fa[i]=i,sz[i]=1; 54 rep(i,1,cnt){ 55 sm=0; dfs(i,a[i].l,0); ans=ans*sm%mod; 56 rep(j,a[i].l,a[i].r){ 57 int p=find(e[j].u),q=find(e[j].v); 58 if (p==q) continue; 59 if (sz[p]>sz[q]) swap(p,q); 60 fa[p]=q; 61 } 62 } 63 printf("%d\n",ans); 64 return 0; 65 }