BZOJ1016 [JSOI2008]最小生成树计数
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
HINT
Source
分析:
首先需要一个结论,对于一个图的不同最小生成树,每种方案所包含的每种权值的边的数量一定一致。
换句话说,把每种方案包含的所有边的边权都写下来,写出来的序列一定都一样。
证明:
考虑kruskal的过程,开始时,每个点单独构成一个集合。
首先只考虑权值最小的边,将它们全部添加进图中(记作步骤1)。
那么现在的图有若干部分是联通的了。这就是添加完最短边后,能够形成的最多的联通分量的数目,注意,根据Kruskal,也是这种权值的边添加完成之后的联通分量数目(贪心原则)
现在我们得出一个结论,添加完最小权值的边后,最终形成的联通分量是一定的,且集合的划分情况一定相同。
那么真正添加的边数也是相同的。因为每添加一条边集合的数目便减少1,不可能出现添加一条边集合数目不变或减少2的情况。
那么权值第二小的边呢?我们将之间得到的集合每个集合都缩为一个点,那么权值第二小的边就变成了当前权值最小的边,也有上述的结论。
因此每个阶段,添加的边数都是相同的。我们以权值划分阶段,那么也就意味着某种权值的边的数目是完全相同的。
根据上面这个结论,我们只需要用Kruskal求一遍最小生成树,统计每种权值的边出现的次数。
然后dfs对每一种权值的边有多少种选边方法即可
另外,可以看一下周冬的OI集训队论文《生成树的计数及其应用》,里面有更厉害的解法
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<algorithm> 5 using namespace std; 6 const int mod=31011; 7 struct edge 8 { 9 int x,y,l; 10 bool operator < (const edge &e) const 11 { 12 return l<e.l; 13 } 14 }g[1010]; 15 vector<edge> v[1010]; 16 int cnt[1010],f[110],tot; 17 int find(int x) 18 { 19 if (x==f[x]) return x; 20 f[x]=find(f[x]); 21 return f[x]; 22 } 23 int find2(int x) 24 { 25 if (x==f[x]) return x; 26 return find2(f[x]); 27 } 28 int dfs(int k,int p,int now) 29 { 30 if (p==v[k].size()) 31 return now==cnt[k]; 32 int ret=0,x,y,z; 33 if (now<cnt[k]) 34 { 35 x=find2(v[k][p].x); 36 y=find2(v[k][p].y); 37 if (x!=y) 38 { 39 f[x]=y; 40 ret+=dfs(k,p+1,now+1); 41 f[x]=x; 42 } 43 } 44 if (now+v[k].size()-p-1>=cnt[k]) ret+=dfs(k,p+1,now); 45 return ret; 46 } 47 int main() 48 { 49 int i,j,k,m,n,p,q,x,y,z,now=0,ans; 50 scanf("%d%d",&n,&m); 51 for (i=1;i<=m;i++) 52 scanf("%d%d%d",&g[i].x,&g[i].y,&g[i].l); 53 sort(g+1,g+m+1); 54 for (i=1;i<=n;i++) 55 f[i]=i; 56 for (i=1;i<=m;i++) 57 { 58 if (g[i].l>g[i-1].l) tot++; 59 v[tot].push_back(g[i]); 60 x=find(g[i].x); 61 y=find(g[i].y); 62 if (x!=y) 63 { 64 cnt[tot]++; 65 f[x]=y; 66 now++; 67 } 68 } 69 if (now<n-1) 70 { 71 printf("0\n"); 72 return 0; 73 } 74 ans=1; 75 for (i=1;i<=n;i++) 76 f[i]=i; 77 for (i=1;i<=tot;i++) 78 { 79 ans=(ans*dfs(i,0,0))%mod; 80 for (j=0;j<v[i].size();j++) 81 { 82 x=find(v[i][j].x); 83 y=find(v[i][j].y); 84 f[x]=y; 85 } 86 } 87 printf("%d\n",ans); 88 }