[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
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
8
**定理一:**如果 $A, B$ 同为 $G$ 的最小生成树,且 $A$ 的边权从小到大为 $w(a_1), w(a_2), w(a_3), \cdots w(a_n)$,$B$ 的边权从小到大为 $w(b_1), w(b_2), w(b_3), \cdots w(b_n)$,则有 $w(a_i) = w(b_i)$。
**定理二:**如果 $A, B$ 同为 $G$ 的最小生成树,如果 $A, B$ 都从零开始从小到大加边($A$ 加 $A$ 的边,$B$ 加 $B$ 的边)的话,每种权值加完后图的联通性相同。
证明(把证明放到markdown上看)根据定理1
假设边权为w的边有很多
那么每种生成树的w边数是一样的
根据定理2
只要算出每种边权下的方案相乘就行了
算完后给树加边,我们无论怎麽分配w的边,只要尽可能放完就行了
不会因为分配不同就影响到后面大于w的边的分配
假设s1,s2,s3都是之前加的小边构成的树,他们构成一个森林
虚线是边权为w,当前要分配的边
把s1,s2,s3缩成一个点
当前的分配方案就是这个无向图的生成树个数
可以dfs,也可以矩阵树(dfs做法见此)
求完后直接往森林中随便加3条边
但是要考虑这个新图无法建成生成树
如果是这样的情况,虽然没有把森林联通,但显然也要算方案,但是矩阵树算法会算出0
所以还要把这个新图加桥,使得正好联通,不会改变矩阵树算出来的方案
要判断无解的情况
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 struct Node 8 { 9 int u,v,d; 10 }edge[2001],edg[2001]; 11 int Mod=31011; 12 int a[101][101],set[101],tmp[101],set2[101],ans=1,n,m,sum; 13 bool cmp(Node a,Node b) 14 { 15 return a.d<b.d; 16 } 17 int gi() 18 { 19 char ch=getchar(); 20 int x=0; 21 while (ch<'0'||ch>'9') ch=getchar(); 22 while (ch>='0'&&ch<='9') 23 { 24 x=x*10+ch-'0'; 25 ch=getchar(); 26 } 27 return x; 28 } 29 int find(int x) 30 { 31 if (set[x]!=x) set[x]=find(set[x]); 32 return set[x]; 33 } 34 int find2(int x) 35 { 36 if (set2[x]!=x) set2[x]=find2(set2[x]); 37 return set2[x]; 38 } 39 int guass(int S) 40 {int i,j,k,as=0; 41 S--; 42 for (i=1;i<=S;i++) 43 { 44 for (j=1;j<=S;j++) 45 { 46 a[i][j]=(a[i][j]+Mod)%Mod; 47 } 48 } 49 as=1; 50 for (i=1;i<=S;i++) 51 { 52 for (j=i+1;j<=S;j++) 53 while (a[j][i]) 54 { 55 int t=a[i][i]/a[j][i]; 56 for (k=i;k<=S;k++) 57 { 58 a[i][k]=(a[i][k]-1ll*t*a[j][k]%Mod+Mod)%Mod; 59 swap(a[i][k],a[j][k]); 60 } 61 as*=-1; 62 } 63 as=1ll*as*a[i][i]%Mod;; 64 } 65 return (as+Mod)%Mod; 66 } 67 void cal(int st,int ed) 68 {int cnt,i; 69 cnt=0; 70 for (i=st;i<=ed;i++) 71 { 72 edg[i]=edge[i]; 73 int p=find(edg[i].u),q=find(edg[i].v); 74 edg[i].u=p;edg[i].v=q; 75 if (p==q) continue; 76 tmp[++cnt]=edg[i].u; 77 tmp[++cnt]=edg[i].v; 78 } 79 sort(tmp+1,tmp+cnt+1); 80 cnt=unique(tmp+1,tmp+cnt+1)-tmp-1; 81 memset(a,0,sizeof(a)); 82 for (i=1;i<=cnt;i++) 83 set2[i]=i; 84 for (i=st;i<=ed;i++) 85 { 86 if (edg[i].u==edg[i].v) continue; 87 int p=find(edg[i].u),q=find(edg[i].v); 88 if (p!=q) 89 { 90 --sum; 91 set[p]=q; 92 } 93 int u=lower_bound(tmp+1,tmp+cnt+1,edg[i].u)-tmp; 94 int v=lower_bound(tmp+1,tmp+cnt+1,edg[i].v)-tmp; 95 a[u][u]++;a[v][v]++; 96 a[u][v]--;a[v][u]--; 97 p=find2(u),q=find2(v); 98 if (p!=q) set2[p]=q; 99 } 100 for (i=2;i<=cnt;i++) 101 if (find2(i)!=find2(i-1)) 102 { 103 int p=find2(i),q=find2(i-1); 104 a[p][p]++;a[q][q]++; 105 a[p][q]--;a[q][p]--; 106 if (p!=q) set2[p]=q; 107 } 108 ans=1ll*ans*guass(cnt)%Mod; 109 } 110 int main() 111 {int i; 112 cin>>n>>m; 113 for (i=1;i<=m;i++) 114 { 115 edge[i].u=gi();edge[i].v=gi();edge[i].d=gi(); 116 } 117 sort(edge+1,edge+m+1,cmp); 118 for (i=1;i<=n;i++) 119 set[i]=i; 120 sum=n; 121 int j; 122 for (i=1;i<=m;i=j) 123 { 124 for (j=i;j<=m;j++) 125 if (edge[j].d!=edge[i].d) break; 126 if (j-i>1) cal(i,j-1); 127 else 128 { 129 int p=find(edge[i].u),q=find(edge[i].v); 130 if (p!=q) 131 { 132 set[p]=q; 133 --sum; 134 } 135 } 136 } 137 if (sum>1) printf("0"); 138 else printf("%d",ans); 139 }