这也是一个黑科技
设一个无向图的邻接矩阵为$A$,度数矩阵为$D$,则基尔霍夫矩阵$K=D-A$的行列式的值就是生成树的个数。
注意这里的$K$是要把最后一行和最后一列去掉的。
(证明?不存在的)
它还有一个扩展,叫做变元矩阵树定理
若将邻接矩阵的$A[i][j]$设为边权,度数矩阵的$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]黑暗前的幻想乡
这道题就是要套一个容斥原理。
都要选=可以都选-一个不选+二个不选-三个不选...
然后枚举子集,重构一下矩阵就可以过了。
时间复杂度$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]最小生成树计数
【TODO】