矩阵树定理学习笔记
這也是一個黑科技
設一個無向圖的鄰接矩陣為$A$,度數(shù)矩陣為$D$,則基爾霍夫矩陣$K=D-A$的行列式的值就是生成樹的個數(shù)。
注意這里的$K$是要把最后一行和最后一列去掉的。
(證明?不存在的)
它還有一個擴展,叫做變元矩陣樹定理
若將鄰接矩陣的$A[i][j]$設為邊權,度數(shù)矩陣的$D[i][i]$設為與$i$相連的邊權的和,則這個值就是所有生成樹邊權之積的和。
P4111 [HEOI2015]小Z的房間
這道題就是模板題,直接套定理就行。
1 #include<cstdio> 2 #include<algorithm> 3 #define Rint register int 4 using namespace std; 5 typedef long long LL; 6 const int N = 11, mod = 1e9; 7 int n, m, id[N][N], tot, f[N * N][N * N]; 8 char str[N][N]; 9 inline void add(int a, int b){ 10 ++ f[a][a]; ++ f[b][b]; 11 -- f[a][b]; -- f[b][a]; 12 } 13 inline int Gauss(){ 14 int res = 1; 15 for(Rint i = 1;i < tot;i ++){ 16 for(Rint j = i + 1;j < tot;j ++) 17 while(f[j][i]){ 18 int t = f[i][i] / f[j][i]; 19 for(Rint k = i;k < tot;k ++) 20 f[i][k] = (f[i][k] - (LL) t * f[j][k] + mod) % mod; 21 swap(f[i], f[j]); 22 res = -res; 23 } 24 res = (LL) res * f[i][i] % mod; 25 res = (res + mod) % mod; 26 } 27 return (res + mod) % mod; 28 } 29 int main(){ 30 scanf("%d%d", &n, &m); 31 for(Rint i = 1;i <= n;i ++){ 32 scanf("%s", str[i] + 1); 33 for(Rint j = 1;j <= m;j ++) if(str[i][j] == '.') id[i][j] = ++ tot; 34 } 35 for(Rint i = 1;i <= tot;i ++) 36 for(Rint j = 1;j <= tot;j ++) f[i][j] = (f[i][j] + mod) % mod; 37 for(Rint i = 1;i <= n;i ++) 38 for(Rint j = 1;j <= m;j ++) 39 if(str[i][j] == '.'){ 40 if(str[i][j - 1] == '.') add(id[i][j], id[i][j - 1]); 41 if(str[i - 1][j] == '.') add(id[i][j], id[i - 1][j]); 42 } 43 printf("%d", Gauss()); 44 }P4336 [SHOI2016]黑暗前的幻想鄉(xiāng)
這道題就是要套一個容斥原理。
都要選=可以都選-一個不選+二個不選-三個不選...
然后枚舉子集,重構(gòu)一下矩陣就可以過了。
時間復雜度$O(2^nn^3)$
1 #include<cstdio> 2 #include<vector> 3 #include<cstring> 4 #define Rint register int 5 using namespace std; 6 typedef long long LL; 7 typedef pair<int, int> pii; 8 const int mod = 1e9 + 7; 9 int n, f[18][18], ans, pre[1 << 16]; 10 vector<pii> g[18]; 11 inline void add(int a, int b){ 12 ++ f[a][a]; ++ f[b][b]; 13 -- f[a][b]; -- f[b][a]; 14 } 15 inline int Gauss(){ 16 int res = 1; 17 for(Rint i = 1;i < n;i ++){ 18 for(Rint j = i + 1;j < n;j ++) 19 while(f[j][i]){ 20 int d = f[i][i] / f[j][i]; 21 for(Rint k = i;k < n;k ++) f[i][k] = (f[i][k] - (LL) d * f[j][k] + mod) % mod; 22 swap(f[i], f[j]); 23 res = -res; 24 } 25 res = (LL) res * f[i][i] % mod; 26 } 27 return (res + mod) % mod; 28 } 29 int main(){ 30 scanf("%d", &n); 31 for(Rint i = 1;i < n;i ++){ 32 int m; 33 scanf("%d", &m); 34 while(m --){ 35 int x, y; 36 scanf("%d%d", &x, &y); 37 g[i].push_back(make_pair(x, y)); 38 } 39 } 40 for(Rint i = 1;i < (1 << n - 1);i ++) pre[i] = pre[i >> 1] + (i & 1); 41 for(Rint i = 1;i < (1 << n - 1);i ++){ 42 memset(f, 0, sizeof f); 43 for(Rint j = 1;j < n;j ++) 44 if(i & (1 << j - 1)) 45 for(Rint k = 0;k < g[j].size();k ++) 46 add(g[j][k].first, g[j][k].second); 47 for(Rint j = 1;j < n;j ++) 48 for(Rint k = 1;k < n;k ++) 49 f[j][k] = (f[j][k] + mod) % mod; 50 if((n - pre[i]) & 1) ans = (ans + Gauss()) % mod; 51 else ans = (ans + mod - Gauss()) % mod; 52 } 53 printf("%d\n", ans); 54 }P3317 [SDOI2014]重建
這道題就要稍微推一下柿子了
$$\sum_{Tree}\prod_{(u,v)\in Tree}P_{u,v}*\prod_{(u,v)\in Tree}(1-P_{u,v})$$
$$=\prod_{(u,v)}(1-P_{u,v})*\sum_{Tree}\prod_{(u,v)\in Tree}\frac{P_{u,v}}{1-P_{u,v}}$$
前一部分預處理,后一部分直接算。
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 #define Rint register int 5 using namespace std; 6 const int N = 51; 7 const double eps = 1e-8; 8 int n; 9 double p[N][N], f[N][N], ans = 1; 10 inline void add(int a, int b, double c){ 11 f[a][a] += c; f[b][b] += c; 12 f[a][b] -= c; f[b][a] -= c; 13 } 14 inline double Gauss(){ 15 double res = 1; 16 for(Rint i = 1;i < n;i ++){ 17 for(Rint j = i + 1;j < n;j ++){ 18 int t = i; 19 if(fabs(f[j][i]) > fabs(f[t][i])) t = j; 20 if(t != i) swap(f[i], f[t]), res = -res; 21 } 22 for(Rint j = i + 1;j < n;j ++){ 23 double t = f[j][i] / f[i][i]; 24 for(Rint k = i;k < n;k ++) f[j][k] -= f[i][k] * t; 25 } 26 res *= f[i][i]; 27 } 28 return res; 29 } 30 int main(){ 31 scanf("%d", &n); 32 for(Rint i = 1;i <= n;i ++) 33 for(Rint j = 1;j <= n;j ++){ 34 scanf("%lf", p[i] + j); 35 if(i < j){ 36 if(fabs(p[i][j]) < eps) p[i][j] = eps; 37 else if(fabs(1 - p[i][j]) < eps) p[i][j] = 1 - eps; 38 ans *= 1 - p[i][j]; 39 add(i, j, p[i][j] / (1 - p[i][j])); 40 } 41 } 42 printf("%.6lf", ans * Gauss()); 43 }P4208 [JSOI2008]最小生成樹計數(shù)
【TODO】
轉(zhuǎn)載于:https://www.cnblogs.com/AThousandMoons/p/10464629.html
總結(jié)
- 上一篇: bzoj 2870 最长道路tree——
- 下一篇: 开源自己写的Library到github