BZOJ1016 最小生成树计数
Posted ljh2000
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ1016 最小生成树计数相关的知识,希望对你有一定的参考价值。
Description
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的
最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生
成树可能很多,所以你只需要输出方案数对31011的模就可以了。
Input
第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整
数编号。接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,0
00。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。
Output
输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。
Sample Input
4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
8
正解:克鲁斯卡尔最小生成树+搜索
解题报告:
今天考场上考了一道这个题目,我居然没看出来就是一道求最小生成树个数的裸题。。。
今天考场上考了一道这个题目,我居然没看出来就是一道求最小生成树个数的裸题。。。
说起来还是挺水的,就是有许多性质还掌握的不够熟练。不同的最小生成树方案,每种权值的边的数量是确定的,每种权值的边的作用是确定的。所以每种权值看作一个集合,这个集合若想对最终结果产生贡献,即是最小生成树,需要选取的边数是固定的。
排序以后先做一遍最小生成树,得出每种权值的边使用的数量x
然后对于每一种权值的边搜索,得出每一种权值的边选择方案。就是枚举某条边选不选,暴力搜索是否可行。(因为题目说同种权值不超过10,所以指数级大暴力不虚)
最后乘法原理,每一步的方案数相乘。
1 //It is made by jump~ 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 #include <ctime> 9 #include <vector> 10 #include <queue> 11 #include <map> 12 #include <set> 13 #ifdef WIN32 14 #define OT "%I64d" 15 #else 16 #define OT "%lld" 17 #endif 18 using namespace std; 19 typedef long long LL; 20 const int MAXN = 211; 21 const int MAXM = 10011; 22 const int MOD = 31011; 23 int n,m; 24 int first[MAXN],next[MAXM],to[MAXM]; 25 int cnt; 26 int father[MAXN]; 27 int ans,lin; 28 29 struct build{ 30 int l,r,cnt; 31 }a[MAXM]; 32 33 struct edge{ 34 int x,y,z; 35 }e[MAXM]; 36 37 inline int getint() 38 { 39 int w=0,q=0; 40 char c=getchar(); 41 while((c<‘0‘ || c>‘9‘) && c!=‘-‘) c=getchar(); 42 if (c==‘-‘) q=1, c=getchar(); 43 while (c>=‘0‘ && c<=‘9‘) w=w*10+c-‘0‘, c=getchar(); 44 return q ? -w : w; 45 } 46 47 inline bool cmp(edge q,edge qq){ return q.z<qq.z; } 48 49 inline int find(int x){ 50 //不能路径压缩!!! 51 if(father[x]!=x) return find(father[x]); //father[x]=find(father[x]); 52 return father[x]; 53 } 54 55 inline void dfs(int x,int now,int gong){ 56 if(now==a[x].r+1) { 57 if(gong==a[x].cnt) lin++;//必须选指定的条数 58 return ; 59 } 60 int r1=find(e[now].x),r2=find(e[now].y); 61 if(r1!=r2) {//枚举选还是不选 62 father[r1]=r2; 63 dfs(x,now+1,gong+1); 64 father[r1]=r1; father[r2]=r2; 65 } 66 dfs(x,now+1,gong);//不选 67 } 68 69 inline void solve(){ 70 n=getint(); m=getint(); 71 int x,y,z; 72 for(int i=1;i<=m;i++) { 73 x=getint(); y=getint(); z=getint(); 74 e[i].x=x; e[i].y=y; e[i].z=z; 75 } 76 77 sort(e+1,e+m+1,cmp); 78 79 for(int i=1;i<=n;i++) father[i]=i; 80 81 int gg=0; 82 for(int i=1;i<=m;i++) { 83 if(e[i].z!=e[i-1].z) { cnt++; a[cnt].l=i; a[cnt-1].r=i-1; } 84 int r1=find(e[i].x),r2=find(e[i].y); 85 if(r1!=r2) { 86 father[r2]=r1; 87 a[cnt].cnt++; 88 gg++; 89 //if(gg==n-1) break; 90 } 91 } 92 a[cnt].r=m; 93 94 if(gg!=n-1) { printf("0"); return ; } 95 for(int i=1;i<=n;i++) father[i]=i; 96 97 ans=1; 98 for(int i=1;i<=cnt;i++) { 99 lin=0; 100 dfs(i,a[i].l,0); 101 ans*=lin; if(ans>=MOD) ans%=MOD; 102 103 for(int j=a[i].l;j<=a[i].r;j++) { 104 int r1=find(e[j].x),r2=find(e[j].y); 105 if(r1!=r2) father[r2]=r1; 106 } 107 } 108 109 printf("%d",ans); 110 } 111 112 int main() 113 { 114 solve(); 115 return 0; 116 }
以上是关于BZOJ1016 最小生成树计数的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ1016: [JSOI2008]最小生成树计数 深搜+并查集