行列式与矩阵树定理
行列式取模板子:对于模数非质数采用辗转相除
消元操作是$ Θ(n^3)$的,辗转相除法是 Θ ( l o g p ) Θ(logp) Θ ( l o g p ) 的,因为辗转相除和消元每次必然使得数变小,势能只会减少,所以这个是均摊到$ Θ(n^2)$ 的,最终有复杂度 Θ ( n 2 l o g n + n 3 ) Θ(n^2logn+n^3) Θ ( n 2 l o g n + n 3 ) 。
P7112 【模板】行列式求值 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ll MOD; int cal (vector<vector<int >>& a, int n) { ll flag = 1 ; for (int i = 1 ; i <= n; ++i) { for (int k = i + 1 ; k <= n; ++k) { while (a[i][i]) { ll tim = a[k][i] / a[i][i]; for (int j = i; j <= n; ++j) a[k][j] = (a[k][j] - tim * a[i][j] % MOD + MOD) % MOD; swap (a[k], a[i]); flag = -flag; } swap (a[k], a[i]); flag = -flag; } } ll res = 1 ; for (int i = 1 ; i <= n; ++i) res = res * a[i][i] % MOD; res *= flag; return (res + MOD) % MOD; } void solve () { int n; cin >> n >> MOD; vector b (n + 1 , vector<int >(n + 1 )) ; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= n; j++) cin >> b[i][j]; ll ans = cal (b, n); cout << ans << endl; }
Matrix-Tree 矩阵树定理 - OI Wiki (oi-wiki.org)
本篇中的图,无论无向还是有向,都允许重边,但是默认没有自环。自环并不影响生成树的个数,也不影响下文中 Laplace 矩阵的计算,故而矩阵树定理对有自环的情形依然成立。
无向图的 Laplace 矩阵所有n-1阶主子式都相等,且都等于图的生成树的个数。
$ Laplace 矩阵 =度数矩阵D-邻接矩阵A$
度数矩阵:G的度数矩阵 D 是一个 n×n 的矩阵,并且满足:当 $$ i\neq j $$时, d i j = 0 d_{ij}=0 d i j = 0 ;当 i=j时,$dij $等于 i 的度数。
邻接矩阵:如果i和j之间有边相连,则A i j = 1 A_{ij}=1 A i j = 1 ,否则为0.
有向图分为根向树形图和叶向树形图。
定义出度矩阵:𝐷 i j o u t ( 𝐺 ) = d e g i o u t , 𝐷 i j o u t = 0 , 𝑖 ≠ 𝑗 𝐷_{ij}^{out} (𝐺) = deg_{i}^{out}, 𝐷_{ij}^{out} = 0, 𝑖 ≠ 𝑗 D i j o u t ( G ) = d e g i o u t , D i j o u t = 0 , i = j
定义入度矩阵:𝐷 i j i n ( 𝐺 ) = d e g i i n , 𝐷 i j i n = 0 , 𝑖 ≠ 𝑗 𝐷_{ij}^{in} (𝐺) = deg_{i}^{in}, 𝐷_{ij}^{in} = 0, 𝑖 ≠ 𝑗 D i j i n ( G ) = d e g i i n , D i j i n = 0 , i = j
设𝑒(𝑖, 𝑗) 为点𝑖 指向点𝑗 的有向边数,并定义邻接矩阵𝐴 为:
𝐴 𝑖 𝑗 ( 𝐺 ) = 𝑒 ( 𝑖 , 𝑗 ) , 𝑖 ≠ 𝑗 𝐴_{𝑖𝑗}(𝐺) = 𝑒(𝑖, 𝑗), 𝑖 ≠ 𝑗 A i j ( G ) = e ( i , j ) , i = j
定义出度 L a p l a c e 矩阵 𝐿 𝑜 𝑢 𝑡 为: 𝐿 𝑜 𝑢 𝑡 ( 𝐺 ) = 𝐷 𝑜 𝑢 𝑡 ( 𝐺 ) − 𝐴 ( 𝐺 ) 定义出度Laplace 矩阵𝐿_{𝑜𝑢𝑡} 为:𝐿^{𝑜𝑢𝑡}(𝐺) = 𝐷^{𝑜𝑢𝑡}(𝐺) − 𝐴(𝐺) 定 义 出 度 L a p l a c e 矩 阵 L o u t 为 : L o u t ( G ) = D o u t ( G ) − A ( G )
定义入度 L a p l a c e 矩阵 𝐿 i n 为: 𝐿 i n ( 𝐺 ) = 𝐷 i n ( 𝐺 ) − 𝐴 ( 𝐺 ) 定义入度Laplace 矩阵𝐿_{in} 为:𝐿^{in}(𝐺) = 𝐷^{in}(𝐺) − 𝐴(𝐺) 定 义 入 度 L a p l a c e 矩 阵 L i n 为 : L i n ( G ) = D i n ( G ) − A ( G )
记图𝐺 的以𝑟 为根的所有根向树形图个数为𝑡 r 𝑜 𝑜 𝑡 ( 𝐺 , 𝑟 ) 𝑡^{r𝑜𝑜𝑡}(𝐺, 𝑟) t r o o t ( G , r ) 。所谓根向树形图,是说这张图的基图是一棵树,所有的边全部指向父亲。
记图𝐺 的以𝑟 为根的所有叶向树形图个数为𝑡 l e a f ( 𝐺 , 𝑟 ) 𝑡^{leaf}(𝐺, 𝑟) t l e a f ( G , r ) 。所谓叶向树形图,是说这张图的基图是一棵树,所有的边全部指向儿子。
那有向图的生成树如何求呢?
定理3(矩阵树定理,有向图根向形式)对于任意的𝑘,都有有向图的出度Laplace 矩阵删去第 k行第 k列得到的主子式等于以 k为根的叶向树形图的个数。
定理4(矩阵树定理,有向图叶向形式)对于任意的𝑘,都有有向图的入度Laplace 矩阵删去第 k行第 k列得到的主子式等于以 k为根的根向树形图的个数。
SPOJ.com - Problem HIGH n ≤ 12 n \le 12 n ≤ 1 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 int cal (vector<vector<int >>& a, int n) { ll flag = 1 ; for (int i = 1 ; i <= n; ++i) { for (int k = i + 1 ; k <= n; ++k) { while (a[i][i]) { ll tim = a[k][i] / a[i][i]; for (int j = i; j <= n; ++j) a[k][j] = (a[k][j] - tim * a[i][j]); swap (a[k], a[i]); flag = -flag; } swap (a[k], a[i]); flag = -flag; } } ll res = 1 ; for (int i = 1 ; i <= n; ++i) res = res * a[i][i]; res *= flag; return res; } void solve () { int n, m; cin >> n >> m; vector<vector<int >> b (n + 1 , vector <int >(n + 1 )); for (int i = 1 ; i <= m; i++) { int u, v; cin >> u >> v; b[u][u]++; b[v][v]++; b[u][v]--; b[v][u]--; } int ans = cal (b, n - 1 ); cout << ans << endl; }