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

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

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 3529  Solved: 1404
[Submit][Status][Discuss]

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,000。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过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

HINT

 

Source

题解:同一个图的最小生成树,满足:

1)同一种权值的边的个数相等

2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性相等

这样,先做一次Kruscal求出每种权值的边的条数,再按照权值从小到大,对每种边进行 DFS, 求出这种权值的边有几种选法。

最后根据乘法原理将各种边的选法数乘起来就可以了。

特别注意:在DFS中为了在向下DFS之后消除决策影响,恢复f[]数组之前的状态,在DFS中调用的Find()函数不能路径压缩。 

代码们:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<queue>
 6 #include<cstring>
 7 #define PAU putchar(' ')
 8 #define ENT putchar('\n')
 9 using namespace std;
10 const int maxn=100+10,maxm=1000+10,inf=-1u>>1,mod=31011;
11 struct edge{int x,y,w;}e[maxm];struct data{int L,R,num;}a[maxm];
12 int n,m,cnt,tot,ans=1,sum,fa[maxn];
13 bool cmp(const edge&a,const edge&b){return a.w<b.w;}
14 int find(int x){return x==fa[x]?x:find(fa[x]);}
15 int findset(int x){return x==fa[x]?x:fa[x]=findset(fa[x]);}
16 void dfs(int x,int pos,int k){
17     if(pos>a[x].R){if(k==a[x].num)sum++;return;}
18     int f1=find(e[pos].x),f2=find(e[pos].y);
19     if(f1!=f2)fa[f1]=f2,dfs(x,-~pos,-~k),fa[f1]=f1;
20     dfs(x,-~pos,k);return;
21 }
22 inline int read(){
23     int x=0,sig=1;char ch=getchar();
24     while(!isdigit(ch)){if(ch=='-')sig=-1;ch=getchar();}
25     while(isdigit(ch))x=10*x+ch-'0',ch=getchar();
26     return x*=sig;
27 }
28 inline void write(int x){
29     if(x==0){putchar('0');return;}if(x<0)putchar('-'),x=-x;
30     int len=0,buf[15];while(x)buf[len++]=x%10,x/=10;
31     for(int i=len-1;i>=0;i--)putchar(buf[i]+'0');return;
32 }
33 void init(){
34     n=read();m=read();
35     for(int i=1;i<=n;i++) fa[i]=i;
36     for(int i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].w=read();
37     sort(e+1,e+m+1,cmp);
38     for(int i=1;i<=m;i++){
39         if(e[i].w!=e[i-1].w){a[++cnt].L=i;a[cnt-1].R=i-1;}
40         int f1=findset(e[i].x),f2=findset(e[i].y);
41         if(f1!=f2){fa[f1]=f2;a[cnt].num++;tot++;}
42     }
43     return;
44 }
45 void work(){
46     a[cnt].R=m;
47     if(tot<n-1){write(0);return;}
48     for(int i=1;i<=n;i++) fa[i]=i;
49     for(int i=1;i<=cnt;i++){
50         sum=0;dfs(i,a[i].L,0);
51         ans=(ans*sum)%mod;
52         for(int j=a[i].L;j<=a[i].R;j++){
53             int f1=find(e[j].x),f2=find(e[j].y);
54             if(f1!=f2)fa[f1]=f2;
55         }
56     }write(ans);
57     return;
58 }
59 void print(){
60     return;
61 }
62 int main(){init();work();print();return 0;}

 

posted @ 2015-07-15 17:21  AI_Believer  阅读(346)  评论(2编辑  收藏  举报