BZOJ1016:[JSOI2008]最小生成树计数(最小生成树,DFS)

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

Solution 

有两条定理:

1.不同的最小生成树中,每种权值的边出现的个数是确定的。

2.不同的生成树中,某一种权值的边连接完成后,形成的联通块状态是一样的 。

也就是说可以对于权值相同的那些边分别处理,爆搜出所有可能的连边情况,然后乘法原理计数即可。

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #define N (1009)
 5 #define MOD (31011)
 6 using namespace std;
 7 
 8 struct Edge
 9 {
10     int x,y,v;
11     bool operator < (const Edge &a) const{return v<a.v;}
12 }E[N];
13 struct Node{int l,r;}a[N];
14 int n,m,k,fa[N],size[N],cnt,ans=1,sum;
15 
16 int Find(int x){return x==fa[x]?x:Find(fa[x]);}
17 
18 void Dfs(int l,int r,int d,int v)
19 {
20     if (l>r)
21     {
22         if (d==size[v]) sum=(sum+1)%MOD;
23         return;
24     }
25     if (r-l+1+d<size[v]) return;
26     int fx=Find(E[l].x), fy=Find(E[l].y);
27     if (fx!=fy && d<size[v])
28     {
29         fa[fx]=fy;
30         Dfs(l+1,r,d+1,v);
31         fa[fx]=fx;
32     }
33     Dfs(l+1,r,d,v);
34 }
35 
36 int main()
37 {
38     scanf("%d%d",&n,&m);
39     for (int i=1; i<=m; ++i)
40         scanf("%d%d%d",&E[i].x,&E[i].y,&E[i].v);
41     sort(E+1,E+m+1);
42     for (int i=1; i<=n; ++i) fa[i]=i;
43     for (int i=1; i<=m; ++i)
44     {
45         if (E[i].v!=E[i-1].v) a[++k].l=i, a[k-1].r=i-1;
46         int fx=Find(E[i].x), fy=Find(E[i].y);
47         if (fx!=fy) fa[fx]=fy,cnt++,size[k]++;
48     }
49     a[k].r=m;
50     if (cnt!=n-1){puts("0"); return 0;}
51 
52     for (int i=1; i<=n; ++i) fa[i]=i;
53     for (int i=1; i<=k; ++i)
54     {
55         if (!size[i]) continue;
56         sum=0;
57         Dfs(a[i].l,a[i].r,0,i);
58         ans=sum*ans%MOD;
59         for(int j=a[i].l;j<=a[i].r;j++)
60         {
61             int fx=Find(E[j].x), fy=Find(E[j].y);
62             if(fx!=fy) fa[fx]=fy;
63         }
64     }
65     printf("%d\n",ans);
66 }
posted @ 2018-09-26 10:20  Refun  阅读(253)  评论(0编辑  收藏  举报