矩阵树
这题不错,又学到了一个知识点。欸嘿嘿。
题意:
分析:
我们来看这道题:
首先,我们不妨把外面虚拟成一个点。那么问的就是,矩形中的所有点能否到达外面的点。
这时,我们想想。如果能都联通的话应该会是什么情况?
首先肯定有一些点本来就联通。我们以虚构的最外面的点出发,进行深度优先搜索。肯定能将本来就来联通的点标记出来。成为一个连通块1
然后剩下没有搜索到的点有两种情况:
1.他们与'.'联通,构成一个连通块,然后可能可以通过填充'.'来与连通块1联通。
2.他们没有与'.'联通,也就是说这些点无法通过变数'.'来与连通块1联通。over了。
就这两种吧!
那我们不妨把每个连通块都遍历出来好了。
这里再给出一个结论:
每个连通块都至多有一个'.'!这时必然的!
因为对于每一个点入度为1到4但是出度只能为1而'.'的出度为0.也就说明'.'是连通块遍历的终点!!
有点难以理解,不妨画画简单的图,更容易明白。
那么我们,就以最外面的节点和各个'.'为起始节点遍历连通块。
最终遍历出k个连通块。'.'的个数+1
然后看看还有没有没有被遍历到的点。这就是上面的第二种情况。直接over。return 0
都被遍历了后。
我们就要考虑:如何把这些联通块连在一起呢?
依靠填补'.'我们可以和别的连通块连接。那么,一共我们有几种连接方法能使所有的连通块联结在一起呢?
这里就要使用矩阵树定理了!!!!
我们不妨把每一个连通块看组成一个节点。节点i通过'.'能和其他节点建立一些有向边(可能不止一条)。
这样就形成了一张有向图。
我们计算这张图能以1号节点(连通块1)为根能形成多少外向树或者内向树(看你怎么连边了,我是外向树).
这里给一个关于矩阵树的博客,希望对你有所帮助:
https://www.cnblogs.com/ezoiLZH/p/9388855.html
这里再给一个计算行列式的模板:
ll f[max_n][max_n]; ll mod = 1e9 + 7; ll det(ll n) { ll res = 1; for (int i = 1;i <= n;++i) { if (!f[i][i]) { bool flag = false; for (int j = i + 1;j <= n;++j) { if (f[j][i]) { flag = true; for (int k = i;k <= n;++k) swap(f[i][k], f[j][k]); res = -res; break; } } if (!flag) return 0; } for (int j = i + 1;j <= n;++j) { while (f[j][i]) { ll t = f[i][i] / f[j][i]; for (int k = i;k <= n;++k) { f[i][k] = (f[i][k] - t * f[j][k]) % mod; swap(f[i][k], f[j][k]); } res = -res; } } res *= f[i][i]; res %= mod; } if (res < 0) res += mod; return res; }
如此,答案就出来了。
代码如下:
#include<iostream> #include<algorithm> #include<vector> using namespace std; const int max_n = 210; typedef long long ll; typedef pair<int, int> pii; int n, m; char G[max_n][max_n]; int col[max_n][max_n]; int cnt = 1; int tot = 0; vector<pii> point; const ll mod = 1e9 + 7; ll f[410][410]; ll det(ll n) { ll res = 1; for (int i = 2;i <= n;++i) { if (!f[i][i]) { bool flag = false; for (int j = i + 1;j <= n;++j) { if (f[j][i]) { flag = true; for (int k = i;k <= n;++k) swap(f[i][k], f[j][k]); res = -res; break; } } if (!flag) return 0; } for (int j = i + 1;j <= n;++j) { while (f[j][i]) { ll t = f[i][i] / f[j][i]; for (int k = i;k <= n;++k) { f[i][k] = (f[i][k] - t * f[j][k]) % mod; swap(f[i][k], f[j][k]); } res = -res; } } res *= f[i][i]; res %= mod; } if (res < 0) res += mod; return res; } void dfs(int sx,int sy,int color) { tot++; col[sx][sy] = color; if (sx - 1 > 0 && G[sx - 1][sy] == 'D')dfs(sx - 1, sy, color); if (sx + 1 <= n && G[sx + 1][sy] == 'U')dfs(sx + 1, sy, color); if (sy - 1 > 0 && G[sx][sy - 1] == 'R')dfs(sx, sy - 1, color); if (sy + 1 <= m && G[sx][sy + 1] == 'L')dfs(sx, sy + 1, color); } void init() { point.clear(); for (int i = 0;i <= 310;i++) for (int j = 0;j <= 310;j++) f[i][j] = 0; for (int i = 0;i <= n;i++) for (int j = 0;j <= m;j++) col[i][j] = 0; cnt = 1;tot = 0; } int main() { ios::sync_with_stdio(0); int t;cin >> t; while (t--) { cin >> n >> m;init(); for (int i = 1;i <= n;i++) for (int j = 1;j <= m;j++) { cin >> G[i][j]; if (G[i][j] == '.') point.push_back({ i,j }); } for (int i = 1;i <= n;i++) { for (int j = 1;j <= m;j++) { if (G[i][j] == '.') dfs(i, j, ++cnt); else if (i == 1 && G[i][j] == 'U' || j == 1 && G[i][j] == 'L' || i == n && G[i][j] == 'D' || j == m && G[i][j] == 'R') dfs(i, j, 1); } } if (tot != n * m) { cout << 0 << endl; continue; } for (int i = 0;i < point.size();i++) { pii p = point[i]; int x = p.first;int y = p.second; int val = col[x][y]; if (x == 1) { f[1][val]--; f[val][val]++; } if (x == n) { f[1][val]--; f[val][val]++; } if (y == 1) { f[val][val]++; f[1][val]--; } if (y == m) { f[val][val]++; f[1][val]--; } if (x != 1) { int to = col[x - 1][y]; if (to != val) { f[to][val]--; f[val][val]++; } } if (x != n) { int to = col[x + 1][y]; if (to != val) { f[to][val]--; f[val][val]++; } } if (y != m) { int to = col[x][y + 1]; if (to != val) { f[to][val]--; f[val][val]++; } } if (y != 1) { int to = col[x][y - 1]; if (to != val) { f[to][val]--; f[val][val]++; } } } cout << det(cnt) << endl; } }
//话说为什么会有最小生成树的标签啊?!