BZOJ 1016: [JSOI2008]最小生成树计数
Posted 谦谦君子,陌上其华
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 1016: [JSOI2008]最小生成树计数相关的知识,希望对你有一定的参考价值。
http://www.lydsy.com/JudgeOnline/problem.php?id=1016
题意:
思路:
一个无向图所有的最小生成树中某种权值的边的数目均相同。
引用一篇大牛的证明:
我们证明以下定理:一个无向图所有的最小生成树中某种权值的边的数目均相同。
开始时,每个点单独构成一个集合。
首先只考虑权值最小的边,将它们全部添加进图中,并去掉环,由于是全部尝试添加,那么只要是用这种权值的边能够连通的点,最终就一定能在一个集合中。
那么不管添加的是哪些边,最终形成的集合数都是一定的,且集合的划分情况一定相同。那么真正添加的边数也是相同的。因为每添加一条边集合的数目便减少1.
那么权值第二小的边呢?我们将之间得到的集合每个集合都缩为一个点,那么权值第二小的边就变成了当前权值最小的边,也有上述的结论。
因此每个阶段,添加的边数都是相同的。我们以权值划分阶段,那么也就意味着某种权值的边的数目是完全相同的。
所以先用克鲁斯卡尔算法计算一遍最小生成树,统计每一权值的边组需要加几条边,最后暴搜,乘法原理相乘即可。注意,要判断无法连通的情况。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<sstream> 6 #include<vector> 7 #include<stack> 8 #include<queue> 9 #include<cmath> 10 #include<map> 11 #include<set> 12 using namespace std; 13 typedef long long ll; 14 typedef pair<int,ll> pll; 15 const int INF = 0x3f3f3f3f; 16 const int maxn=10000+5; 17 const int mod=31011; 18 19 struct node 20 { 21 int u,v,w; 22 }e[10*maxn]; 23 24 struct node2 25 { 26 int l,r; 27 int c; 28 }a[maxn]; 29 30 int n, m; 31 int sum; 32 int cnt,tot; 33 int p[maxn]; 34 35 bool cmp(node a, node b) 36 { 37 return a.w<b.w; 38 } 39 40 int find(int x) 41 { 42 return x==p[x]?x:find(p[x]); 43 } 44 45 void dfs(int cur, int num ,int pos) 46 { 47 if(pos>a[cur].r) 48 { 49 if(num==a[cur].c) sum++; 50 return; 51 } 52 int x=find(e[pos].u); 53 int y=find(e[pos].v); 54 if(x!=y) 55 { 56 p[x]=y; 57 dfs(cur,num+1,pos+1); 58 p[x]=x; 59 } 60 dfs(cur,num,pos+1); 61 } 62 63 int main() 64 { 65 //freopen("in.txt","r",stdin); 66 while(~scanf("%d%d",&n,&m)) 67 { 68 for(int i=1;i<=n;i++) p[i]=i; 69 for(int i=1;i<=m;i++) 70 scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 71 72 sort(e+1,e+m+1,cmp); 73 cnt=0; tot=0; 74 for(int i=1;i<=m;i++) 75 { 76 if(e[i].w!=e[i-1].w) 77 { 78 a[++cnt].l=i; 79 a[cnt-1].r=i-1; 80 a[cnt].c=0; 81 } 82 int x=find(e[i].u); 83 int y=find(e[i].v); 84 if(x!=y) 85 { 86 p[x]=y; 87 a[cnt].c++; 88 tot++; 89 } 90 } 91 a[cnt].r=m; 92 93 if(tot!=n-1) //连不起来的情况 94 { 95 printf("0\\n"); 96 continue; 97 } 98 99 100 for(int i=1;i<=n;i++) p[i]=i; 101 int ans=1; 102 for(int i=1;i<=cnt;i++) 103 { 104 sum=0; 105 dfs(i,0,a[i].l); 106 ans=(ans*sum)%mod; 107 for(int j=a[i].l;j<=a[i].r;j++) 108 { 109 int x=find(e[j].u); 110 int y=find(e[j].v); 111 if(x!=y) p[x]=y; 112 } 113 } 114 printf("%d\\n",ans); 115 } 116 return 0; 117 }
以上是关于BZOJ 1016: [JSOI2008]最小生成树计数的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ-1016: [JSOI2008]最小生成树计数 (kruscal+搜索)
BZOJ1016:[JSOI2008]最小生成树计数——题解