基尔霍夫矩阵题目泛做(AD第二轮)
题目1: SPOJ 2832
题目大意:
求一个矩阵行列式模一个数P后的值。p不一定是质数。
算法讨论:
因为有除法而且p不一定是质数,不一定有逆元,所以我们用辗转相除法。
1 #include <cstdio> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <algorithm> 6 7 using namespace std; 8 9 const int N = 205; 10 typedef long long ll; 11 12 int n; 13 ll p, mat[N][N]; 14 15 ll det(ll a[N][N]) { 16 ll res = 1; 17 18 for(int i = 1; i <= n; ++ i) { 19 for(int j = i + 1; j <= n; ++ j) { 20 while(a[j][i]) { 21 ll t = a[i][i] / a[j][i]; 22 23 for(int k = i; k <= n; ++ k) 24 a[i][k] = (a[i][k] - a[j][k] * t) % p; 25 for(int k = i; k <= n; ++ k) 26 swap(a[i][k], a[j][k]); 27 res = -res; 28 } 29 } 30 if(a[i][i] == 0) return 0; 31 res = 1LL * res * a[i][i] % p; 32 } 33 return (res + p) % p; 34 } 35 36 int main() { 37 while(~scanf("%d%lld", &n, &p)) { 38 for(int i = 1; i <= n; ++ i) { 39 for(int j = 1; j <= n; ++ j) { 40 scanf("%lld", &mat[i][j]); 41 mat[i][j] %= p; 42 } 43 } 44 printf("%lld\n", det(mat)); 45 } 46 return 0; 47 }
题目2: BZOJ 1002 轮状病毒
题目大意:
一棵有规律的树,求其生成树的数量。基尔霍夫矩裸上。
关于矩阵树定理,有一个递推式: f[n] = 3 * f[n - 1] - f[n - 2] + 2;
高精度一下即可。
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <algorithm> 5 #include <iostream> 6 7 using namespace std; 8 9 const int rad = 10; 10 11 int n; 12 13 struct BigInt { 14 int v[1005]; 15 int len; 16 17 BigInt() { 18 memset(v, 0, sizeof v); 19 len = 0; 20 } 21 22 friend BigInt operator + (BigInt a, BigInt b) { 23 int tmp = max(a.len, b.len); 24 25 for(int i = 1; i <= tmp; ++ i) { 26 a.v[i] += b.v[i]; 27 if(a.v[i] >= rad) { 28 a.v[i + 1] += a.v[i] / rad; 29 a.v[i] %= rad; 30 } 31 } 32 while(a.v[tmp + 1]) tmp ++; 33 a.len = tmp; 34 35 return a; 36 } 37 38 friend BigInt operator * (BigInt a, BigInt b) { 39 BigInt c; 40 int tmp = a.len + b.len; 41 42 for(int i = 1; i <= a.len; ++ i) { 43 for(int j = 1; j <= b.len; ++ j) { 44 c.v[i + j - 1] += a.v[i] * b.v[j]; 45 } 46 } 47 for(int i = 1; i <= tmp; ++ i) { 48 if(c.v[i] >= rad) { 49 c.v[i + 1] += c.v[i] / rad; 50 c.v[i] %= rad; 51 } 52 if(c.v[i]) c.len = i; 53 } 54 55 return c; 56 } 57 58 friend BigInt operator * (BigInt a, int k) { 59 for(int i = 1; i <= a.len; ++ i) { 60 a.v[i] *= k; 61 } 62 for(int i = 1; i <= a.len; ++ i) { 63 a.v[i + 1] += a.v[i] / rad; 64 a.v[i] %= rad; 65 } 66 if(a.v[a.len + 1]) a.len ++; 67 return a; 68 } 69 70 friend BigInt operator - (BigInt a, BigInt b) { 71 for(int i = 1; i <= a.len; ++ i) { 72 a.v[i] -= b.v[i]; 73 if(a.v[i] < 0) { 74 a.v[i + 1] -= 1; 75 a.v[i] += rad; 76 } 77 } 78 while(a.v[a.len] == 0)a.len --; 79 return a; 80 } 81 void getint(int k) { 82 while(k) { 83 v[++ len] = k % 10; 84 k /= 10; 85 } 86 } 87 88 void print() { 89 for(int i = len; i >= 1; -- i) { 90 printf("%d", v[i]); 91 } 92 } 93 }f[105], cst; 94 95 void Input() { 96 scanf("%d", &n); 97 } 98 99 void Solve() { 100 f[1].getint(1); 101 f[2].getint(5); 102 cst.getint(2); 103 for(int i = 3; i <= n; ++ i) { 104 f[i] = f[i - 1] * 3 - f[i - 2] + cst; 105 } 106 } 107 108 void Output() { 109 f[n].print(); 110 } 111 112 int main() { 113 Input(); 114 Solve(); 115 Output(); 116 117 return 0; 118 }
题目3: SPOJ 104 HighWays
题目大意:
给一张图,求其生成树的个数。
算法讨论:裸上基尔霍夫。
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdlib> 5 #include <algorithm> 6 7 using namespace std; 8 9 typedef long long ll; 10 const int N = 15; 11 12 ll ans; 13 int n, m; 14 ll degree[N][N], G[N][N], self[N][N]; 15 16 ll det(ll a[N][N]) { 17 ll res = 1; 18 19 for(int i = 1; i < n; ++ i) { 20 for(int j = i + 1; j < n; ++ j) { 21 while(a[j][i]) { 22 ll t = a[i][i] / a[j][i]; 23 24 for(int k = i; k < n; ++ k) 25 a[i][k] = (a[i][k] - a[j][k] * t); 26 for(int k = i; k < n; ++ k) 27 swap(a[i][k], a[j][k]); 28 res = -res; 29 } 30 } 31 if(a[i][i] == 0) return 0; 32 res = 1LL * res * a[i][i]; 33 } 34 return res; 35 } 36 37 void Input() { 38 int x, y; 39 40 memset(self, 0, sizeof self); 41 memset(degree, 0, sizeof degree); 42 43 scanf("%d%d", &n, &m); 44 for(int i = 1; i <= m; ++ i) { 45 scanf("%d%d", &x, &y); 46 degree[x][y] ++; 47 degree[y][x] ++; 48 self[x][x] ++; self[y][y] ++; 49 } 50 for(int i = 1; i <= n; ++ i) { 51 for(int j = 1; j <= n; ++ j) { 52 G[i][j] = self[i][j] - degree[i][j]; 53 } 54 } 55 } 56 57 void Solve() { 58 ans = det(G); 59 } 60 61 void Output() { 62 printf("%lld\n", ans); 63 } 64 65 int main() { 66 int t; 67 68 scanf("%d", &t); 69 70 while(t --) { 71 Input(); 72 Solve(); 73 Output(); 74 } 75 76 return 0; 77 }
题目4: BZOJ 4031 [HEOI 2015] 小Z的房间
题目大意:并没有搞懂。只是抄的。河北的题还真是够呛。(PS:我是河北的)
算法讨论:裸上Matrix-Tree定理吧。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 const int N = 15; 6 const int mod = 1e9; 7 typedef long long ll; 8 9 int n, m, cnt; 10 int p[N][N]; ll a[N * N][N * N]; 11 int dx[]={0, 0, 1, -1}; 12 int dy[]={1, -1, 0, 0}; 13 char maze[N][N]; 14 15 ll det() { 16 ll res = 1; 17 18 for(int i = 1; i < cnt; ++ i) 19 for(int j = 1; j < cnt; ++ j) 20 a[i][j] = (a[i][j] + mod) % mod; 21 for(int i = 1; i < cnt; ++ i) { 22 for(int j = i + 1; j < cnt; ++ j) { 23 while(a[j][i]) { 24 ll t = a[i][i] / a[j][i]; 25 26 for(int k = i; k < cnt; ++ k) 27 a[i][k] = (a[i][k] - a[j][k] * t % mod + mod) % mod; 28 for(int k = i; k < cnt; ++ k) 29 swap(a[i][k], a[j][k]); 30 res = -res; 31 } 32 } 33 if(a[i][i] == 0) return 0; 34 res = 1LL * res * a[i][i] % mod; 35 } 36 return (res + mod) % mod; 37 } 38 39 int main() { 40 scanf("%d%d", &n, &m); 41 for(int i = 1; i <= n; ++ i) { 42 scanf("%s", maze[i] + 1); 43 } 44 for(int i = 1; i <= n; ++ i) 45 for(int j = 1; j <= m; ++ j) 46 if(maze[i][j] == '.') 47 p[i][j] = ++ cnt; 48 for(int i = 1; i <= n; ++ i) { 49 for(int j = 1; j <= m; ++ j) { 50 if(maze[i][j] != '.') continue; 51 for(int k = 0; k < 4; ++ k) { 52 int nx = dx[k] + i, ny = dy[k] + j; 53 54 if(nx < 1 || ny < 1 || nx > n || ny > m || maze[nx][ny] != '.') continue; 55 int u = p[i][j], v = p[nx][ny]; 56 57 a[u][u] ++; a[u][v] --; 58 } 59 } 60 } 61 printf("%lld\n", det()); 62 return 0; 63 }
题目5: Uva 10766
题目大意:
求一个有根树的生成树的数量。
算法讨论:
其实这个有根和无根是一样的。直接做就行。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 const int N = 55; 7 8 int n, m, kk; 9 bool lk[N][N]; 10 ll a[N][N]; 11 12 ll det() { 13 ll res = 1; 14 15 for(int i = 1; i < n; ++ i) { 16 for(int j = i + 1; j < n; ++ j) { 17 while(a[j][i]) { 18 ll t = a[i][i] / a[j][i]; 19 20 for(int k = i; k < n; ++ k) 21 a[i][k] = a[i][k] - a[j][k] * t; 22 for(int k = i; k < n; ++ k) 23 swap(a[i][k], a[j][k]); 24 res = -res; 25 } 26 } 27 if(a[i][i] == 0) return 0; 28 res = res * a[i][i]; 29 } 30 31 return abs(res); 32 } 33 34 int main() { 35 int u, v; 36 37 while(~scanf("%d%d%d", &n, &m, &kk)) { 38 memset(lk, false, sizeof lk); 39 memset(a, 0, sizeof a); 40 for(int i = 1; i <= m; ++ i) { 41 scanf("%d%d", &u, &v); 42 lk[u][v] = lk[v][u] = true; 43 } 44 for(int i = 1; i <= n; ++ i) { 45 int cnt = 0; 46 47 for(int j = 1; j <= n; ++ j) { 48 if(i != j && !lk[i][j]) { 49 a[i][j] = -1; 50 ++ cnt; 51 } 52 } 53 a[i][i] = cnt; 54 } 55 printf("%lld\n", det()); 56 } 57 58 return 0; 59 }
题目6: BZOJ1016 && JSOI2008最小生成树计数
题目大意:
求一个图的最小生成树的个数。
算法讨论:
将边权从小到大SORT。然后对于相同边权的做一次处理进行缩点。如果缩点后出现多个联通块,有两种方法:对每个联通块做Matrix-Tree,然后相乘,或者在两个联通块加一个桥,这样方案数不会受影响。
坑点就是最后如果有形不成树的情况。
代码:
1 #include <cstdlib> 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #include <vector> 7 8 using namespace std; 9 const int N = 100 + 5; 10 const int M = 1000 + 5; 11 const int mod = 31011; 12 typedef long long ll; 13 14 int n, m, tim, cnt; 15 ll A[N][N], ans = 1; 16 int fa[N], lab[N], repos[N], f2[N]; 17 18 struct Edge { 19 int u, v, d; 20 bool operator < (const Edge &k) const { 21 return d < k.d; 22 } 23 }e[M]; 24 vector <Edge> p[N]; 25 26 void Init() { 27 scanf("%d%d", &n, &m); 28 for(int i = 1; i <= m; ++ i) { 29 scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].d); 30 } 31 } 32 33 ll Det(ll a[N][N], int ns) { 34 ll res = 1; 35 for(int i = 1; i < ns; ++ i) { 36 for(int j = i + 1; j < ns; ++ j) { 37 while(a[j][i]) { 38 ll t = a[i][i] / a[j][i]; 39 for(int k = i; k < ns; ++ k) 40 a[i][k] = (a[i][k] - a[j][k] * t); 41 for(int k = i; k < ns; ++ k) 42 swap(a[i][k], a[j][k]); 43 res = -res; 44 } 45 } 46 if(a[i][i] == 0) return 0; 47 res = res * a[i][i]; 48 } 49 return res; 50 } 51 52 int find(int *f, int x) { 53 return f[x] == x ? x : (f[x] = find(f, f[x])); 54 } 55 56 void Work(vector <Edge> &v) { 57 vector <Edge> :: iterator it; 58 int poi = 0; 59 ++ tim; 60 for(it = v.begin(); it != v.end(); ++ it) { 61 if(lab[fa[(*it).u]] != tim) { 62 lab[fa[(*it).u]] = tim; 63 repos[fa[(*it).u]] = ++ poi; 64 } 65 if(lab[fa[(*it).v]] != tim) { 66 lab[fa[(*it).v]] = tim; 67 repos[fa[(*it).v]] = ++ poi; 68 } 69 } 70 for(int i = 1; i <= poi; ++ i) 71 for(int j = 1; j <= poi; ++ j) 72 A[i][j] = 0; 73 for(it = v.begin(); it != v.end(); ++ it) { 74 -- A[repos[fa[(*it).u]]][repos[fa[(*it).v]]]; 75 -- A[repos[fa[(*it).v]]][repos[fa[(*it).u]]]; 76 ++ A[repos[fa[(*it).u]]][repos[fa[(*it).u]]]; 77 ++ A[repos[fa[(*it).v]]][repos[fa[(*it).v]]]; 78 } 79 ans = ans * Det(A, poi) % mod; 80 } 81 82 void Calc(int l, int r) { 83 for(int i = l; i <= r; ++ i) { 84 f2[fa[e[i].u]] = fa[e[i].u]; 85 f2[fa[e[i].v]] = fa[e[i].v]; 86 p[fa[e[i].u]].clear(); 87 p[fa[e[i].v]].clear(); 88 } 89 for(int i = l; i <= r; ++ i) { 90 f2[find(f2, fa[e[i].u])] = find(f2, fa[e[i].v]); 91 } 92 for(int i = l; i <= r; ++ i) { 93 if(fa[e[i].u] != fa[e[i].v]) { 94 p[find(f2, fa[e[i].u])].push_back(e[i]); 95 } 96 } 97 for(int i = l; i <= r; ++ i) { 98 if(!p[fa[e[i].u]].empty()) { 99 Work(p[fa[e[i].u]]), p[fa[e[i].u]].clear(); 100 } 101 if(!p[fa[e[i].v]].empty()) { 102 Work(p[fa[e[i].v]]), p[fa[e[i].v]].clear(); 103 } 104 } 105 } 106 107 void Solve() { 108 sort(e + 1, e + m + 1); 109 cnt = n; 110 for(int i = 1; i <= n; ++ i) fa[i] = i; 111 for(int i = 1; i <= m && cnt > 1;) { 112 int j = i; 113 for(; e[j].d == e[i].d && j <= m; ++ j) { 114 find(fa, e[j].u); find(fa, e[j].v); 115 } 116 Calc(i, j - 1); 117 for(int k = i; k < j; ++ k) { 118 int fx = find(fa, e[k].u), fy = find(fa, e[k].v); 119 if(fx != fy) { 120 fa[fx] = fy; 121 cnt --; 122 } 123 } 124 i = j; 125 } 126 if(cnt != 1) ans = 0; 127 printf("%lld\n", ans % mod); 128 } 129 130 #define stone_eee 131 int main() { 132 #ifndef stone_ 133 freopen("mst.in", "r", stdin); 134 freopen("mst.out", "w", stdout); 135 #endif 136 137 Init(); 138 Solve(); 139 140 #ifndef stone_ 141 fclose(stdin); fclose(stdout); 142 #endif 143 return 0; 144 }