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

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 最小生成树计数的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1016 JSOI2008 最小生成树计数

BZOJ1016 最小生成树计数

bzoj 1016 [JSOI2008]最小生成树计数

BZOJ1016: [JSOI2008]最小生成树计数 深搜+并查集

BZOJ1016:[JSOI2008]最小生成树计数——题解

bzoj1016: [JSOI2008]最小生成树计数