矩阵树定理学习笔记

这也是一个黑科技

设一个无向图的邻接矩阵为$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 }

posted @ 2019-03-03 12:16  mizu164  阅读(297)  评论(0)    收藏  举报