bzoj1016 [JSOI2008]最小生成树计数
1016: [JSOI2008]最小生成树计数
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 3517 Solved: 1396
[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
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
HINT
Source
题意:问一个图的最小生成树有多少种
分析:
下面提供两种做法:
1、最小生成树+暴力枚举边
现象普通最小生成树一样,将边按权值排序,先做一遍最小生成树(Krus什么的),统计每种权值取得边数
显然,每种权值的边取的数量是一定的(根据最小生成树的性质,先假设前面权值的边已经取好,那现在这个权值的边每取一条,都会减少一个联通块,这样想,显然数量一定)
这样,对于每种权值的边暴力枚举每条边取不取,在尝试加入,若能加入,则这种权值的边的取法+1,最后把每种权值的边的取法乘起来
2、利用每种权值的边取的数量是一定的性质,很容易推出无论怎么加,加完某种权值的边后,图的连通性是一样的,那么,对于每种那些加完边后变成一个联通块的若干联通块,就相当于一个生成树,那么很显然可以用生成树计数
何为生成树计数(这么多天不添题就是搞这个去了,终于搞懂了)
虽然我搞懂了,但我也是看论文看懂的,有想学的自己看论文去吧
大致意思如下:
定义度数矩阵D:D[i][i]为第 i 个点的度数
定义边矩阵E:E[i][j] 为第 i 个点到第 j 个点的边的数量(边的数量也是成立的,当初我就是纠结这个,要完全搞懂生成树计数的原理才能懂哦)
定义矩阵C:C[i][j] = D[i][j] - E[i][j]
那么,生成树的数量就是矩阵C的任意一个n-1阶的主子树的行列式
行列式有一下性质:
1、将一行数每一个数乘以某一个数,加到令一行数上,行列式的结果不变
2、将任意两行调转,结果相反(符号取反)
3、上三角或下三角的行列式值为对角线的乘积
何为上三角、下三角
a1 a2 .... an
0 b2......bn
0 0 c3....cn
0 0 0 0........pn
即对角线的一边有数,另一边没数
PS:对于第二种做法,一定要判断能不能得到一棵树,不能的话,就输出 0 (卡了我好久T_T)
综上所述,本题得解
第一种
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <deque> 6 #include <vector> 7 #include <queue> 8 #include <iostream> 9 #include <algorithm> 10 #include <map> 11 #include <set> 12 #include <ctime> 13 using namespace std; 14 typedef long long LL; 15 typedef double DB; 16 #define For(i, s, t) for(int i = (s); i <= (t); i++) 17 #define Ford(i, s, t) for(int i = (s); i >= (t); i--) 18 #define MIT (2147483647) 19 #define INF (1000000001) 20 #define MLL (1000000000000000001LL) 21 #define sz(x) ((int) (x).size()) 22 #define clr(x, y) memset(x, y, sizeof(x)) 23 #define puf push_front 24 #define pub push_back 25 #define pof pop_front 26 #define pob pop_back 27 #define ft first 28 #define sd second 29 #define mk make_pair 30 inline void SetIO(string Name) { 31 string Input = Name+".in", 32 Output = Name+".out"; 33 freopen(Input.c_str(), "r", stdin), 34 freopen(Output.c_str(), "w", stdout); 35 } 36 37 const int N = 110, M = 1010, Mod = 31011; 38 struct Edges { 39 int u, v, c; 40 41 inline bool operator <(const Edges &A) const { 42 return c < A.c; 43 } 44 } E[M]; 45 int n, m; 46 int Fa[N], U[N], Stack[N]; 47 int Step[M], S, St[M], Ed[M]; 48 int Ans; 49 50 inline void Input() { 51 scanf("%d%d", &n, &m); 52 For(i, 1, m) 53 scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].c); 54 } 55 56 inline int Find(int x, int *P) { 57 int Len = 0; 58 while(x != P[x]) { 59 Stack[++Len] = x; 60 x = P[x]; 61 } 62 For(i, 1, Len) P[Stack[i]] = x; 63 return x; 64 } 65 66 inline int Count(int State) { 67 int Ret = 0; 68 while(State) { 69 if(State&1) Ret++; 70 State >>= 1; 71 } 72 return Ret; 73 } 74 75 inline void Solve() { 76 sort(E+1, E+1+m); 77 For(i, 1, n) Fa[i] = i; 78 79 S = 0; 80 int Last = -1; 81 For(i, 1, m) { 82 if(Last != E[i].c) { 83 Ed[S] = i-1; 84 S++, Last = E[i].c; 85 St[S] = i; 86 } 87 int u = Find(E[i].u, Fa), v = Find(E[i].v, Fa); 88 if(u == v) continue; 89 Fa[u] = v, Step[S]++; 90 } 91 Ed[S] = m; 92 93 Ans = 1; 94 For(i, 1, n) Fa[i] = i; 95 int Cnt, Len, Max; 96 bool Flag; 97 For(k, 1, S) { 98 if(!Step[k]) { 99 //printf("0\n"); 100 continue; 101 } 102 Cnt = 0, Len = Ed[k]-St[k]+1; 103 Max = 1<<Len; 104 For(State, 0, Max-1) { 105 if(Count(State) != Step[k]) continue; 106 For(i, 1, n) U[i] = Fa[i]; 107 Flag = 1; 108 For(i, 0, Len-1) 109 if((1<<i)&State) { 110 int u = Find(E[St[k]+i].u, U), 111 v = Find(E[St[k]+i].v, U); 112 if(u == v) { 113 Flag = 0; 114 break; 115 } 116 U[u] = v; 117 } 118 if(Flag) Cnt++; 119 } 120 121 For(i, St[k], Ed[k]) { 122 int u = Find(E[i].u, Fa), 123 v = Find(E[i].v, Fa); 124 if(u == v) continue; 125 Fa[u] = v; 126 } 127 128 //printf("%d\n", Cnt); 129 Ans = (Ans*Cnt)%Mod; 130 } 131 132 For(i, 1, n) Fa[i] = Find(i, Fa); 133 For(i, 2, n) 134 if(Find(i, Fa) != Find(i-1, Fa)) { 135 Ans = 0; 136 break; 137 } 138 printf("%d\n", Ans); 139 } 140 141 int main() { 142 SetIO("1016"); 143 Input(); 144 Solve(); 145 return 0; 146 }
第二种
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <deque> 6 #include <vector> 7 #include <queue> 8 #include <iostream> 9 #include <algorithm> 10 #include <map> 11 #include <set> 12 #include <ctime> 13 using namespace std; 14 typedef long long LL; 15 typedef double DB; 16 #define For(i, s, t) for(int i = (s); i <= (t); i++) 17 #define Ford(i, s, t) for(int i = (s); i >= (t); i--) 18 #define MIT (2147483647) 19 #define INF (1000000001) 20 #define MLL (1000000000000000001LL) 21 #define sz(x) ((int) (x).size()) 22 #define clr(x, y) memset(x, y, sizeof(x)) 23 #define puf push_front 24 #define pub push_back 25 #define pof pop_front 26 #define pob pop_back 27 #define ft first 28 #define sd second 29 #define mk make_pair 30 inline void SetIO(string Name) { 31 string Input = Name+".in", 32 Output = Name+".out"; 33 freopen(Input.c_str(), "r", stdin), 34 freopen(Output.c_str(), "w", stdout); 35 } 36 37 const int N = 110, M = 1010, Mod = 31011; 38 struct Edges { 39 int u, v, c; 40 41 inline bool operator <(const Edges &A) const { 42 return c < A.c; 43 } 44 } E[M]; 45 int n, m; 46 int Fa[N], Stack[N]; 47 int Edge[N][N], Con[N][N], Len[N]; 48 int C[N][N]; 49 int S, St[M], Ed[M]; 50 bool Visit[N]; 51 int Ans; 52 53 inline void Input() { 54 scanf("%d%d", &n, &m); 55 For(i, 1, m) 56 scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].c); 57 } 58 59 inline int Find(int x) { 60 int Len = 0; 61 while(x != Fa[x]) { 62 Stack[++Len] = x; 63 x = Fa[x]; 64 } 65 For(i, 1, Len) Fa[Stack[i]] = x; 66 return x; 67 } 68 69 inline int Work(int n) { 70 int Ret = 1, T; 71 For(i, 1, n) { 72 For(j, i+1, n) 73 while(C[j][i]) { 74 T = C[i][i]/C[j][i]; 75 For(k, i, n) { 76 C[i][k] -= C[j][k]*T; 77 swap(C[i][k], C[j][k]); 78 } 79 Ret = -Ret; 80 } 81 if(!C[i][i]) return 0; 82 Ret = Ret*C[i][i]; 83 } 84 return abs(Ret); 85 } 86 87 inline void Solve() { 88 sort(E+1, E+1+m); 89 For(i, 1, n) Fa[i] = i; 90 Ans = 1, S = 0; 91 92 int Last = -1; 93 For(i, 1, m) 94 if(Last != E[i].c) { 95 Ed[S] = i-1; 96 S++, Last = E[i].c; 97 St[S] = i; 98 } 99 Ed[S] = m; 100 101 int u, v, Cnt, Now; 102 For(k, 1, S) { 103 For(i, 1, n) 104 For(j, 1, n) Edge[i][j] = 0; 105 For(i, St[k], Ed[k]) { 106 u = Find(E[i].u), v = Find(E[i].v); 107 if(u == v) continue; 108 Edge[u][v]++, Edge[v][u]++; 109 } 110 111 For(i, 1, n) Visit[i] = 0; 112 For(i, St[k], Ed[k]) { 113 u = Find(E[i].u), v = Find(E[i].v); 114 if(u == v) continue; 115 Fa[u] = v, Visit[u] = Visit[v] = 1; 116 } 117 118 For(i, 1, n) Len[i] = 0; 119 For(i, 1, n) 120 if(Visit[i]) { 121 u = Find(i); 122 Con[u][++Len[u]] = i; 123 } 124 125 Now = 1; 126 For(i, 1, n) 127 if(Len[i]) { 128 Cnt = Len[i]; 129 130 For(x, 1, Cnt) 131 For(y, 1, Cnt) C[x][y] = 0; 132 For(x, 1, Cnt) 133 For(y, x+1, Cnt) 134 C[x][y] = C[y][x] = -Edge[Con[i][x]][Con[i][y]]; 135 For(x, 1, Cnt) 136 For(j, 1, Cnt) C[x][x] += Edge[Con[i][x]][Con[i][j]]; 137 138 Now = Now*Work(Cnt-1); 139 } 140 141 Ans = (Ans*Now)%Mod; 142 } 143 144 For(i, 1, n-1) 145 if(Find(i) != Find(i+1)) { 146 printf("0\n"); 147 return; 148 } 149 printf("%d\n", Ans); 150 } 151 152 int main() { 153 SetIO("1016"); 154 Input(); 155 Solve(); 156 return 0; 157 }