题目大意:有一个$n imes m$的房间,一些位置是房间,另一些位置是柱子,相邻两个房间之间有墙,问有多少种方案可以打通一些墙把所有房间连成一棵树,柱子不可以打通
题解:矩阵树定理,把房间当点,墙当边,一张图的生成树个数为每个点的度数矩阵减去邻接矩阵的任意一个代数余子式的值。
模数是$10^9$,不可以直接高斯消元,可以用辗转相除法来消元
卡点:无
C++ Code:
#include <algorithm> #include <cstdio> #include <cstring> #define maxn 10 const int mod = 1e9; int n, m; int s[maxn][maxn], idx; char buf[maxn]; inline void up(int &a, int b) {a += b - mod, a += a >> 31 & mod;} inline void down(int &a, int b) {a -= b, a += a >> 31 & mod;} struct Deter { #define maxm 85 int s[maxm][maxm]; inline void addedge(int x, int y) { s[x][x]++, s[y][y]++; s[x][y] = s[y][x] = mod - 1; } int gauss() { int f = 0, ans = 1; for (int i = 1; i < idx; i++) { for (int j = i + 1; j < idx; j++) { while (s[j][i]) { int tmp = s[i][i] / s[j][i]; for (int k = i; k < idx; k++) { down(s[i][k], static_cast<long long> (s[j][k]) * tmp % mod); std::swap(s[i][k], s[j][k]); } f ^= 1; } } ans = static_cast<long long> (ans) * s[i][i] % mod; if (!ans) return 0; } return f ? mod - ans : ans; } #undef maxm } D; int main() { memset(s, -1, sizeof s); scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) { scanf("%s", buf); for (int j = 0; j < m; j++) if (buf[j] == '.') { s[i][j] = idx++; if (i && ~s[i - 1][j]) D.addedge(s[i - 1][j], s[i][j]); if (j && ~s[i][j - 1]) D.addedge(s[i][j - 1], s[i][j]); } } printf("%d ", D.gauss()); return 0; }