題目
分析
首先不難想到直接用 個點上下左右連邊建圖,既然要走出去,就把“外面”建成一個點 ,所以問題變爲求以 爲根的內向樹個數。但這樣時間複雜度是 。觀察到 ,因此考慮只保留 .
,把 .
向它上下左右能走到的另一個 .
連邊即可。記憶化搜索實現,注意每個點都要搜一遍找環。
如果是求有向圖以 爲根的個數,我們只需要算 就是答案。
證明:在 矩陣樹定理 - 連通性引理 3 的證明中提到,高斯消元的最後一個元素 ,此時他的入度爲 0,這就意味着它是根,因此把 的 行 列刪掉就可以看出這個圖是不是以 爲根的樹。若是,則 ,否則 。
代碼
#include <bits/stdc++.h>
typedef std::pair<int, int> PII;
const int MAXN = 200;
const int MAXK = 300;
const int MOD = 1000000007;
int N, M;
char Map[MAXN + 5][MAXN + 5];
inline int Add(int x, const int &y) {
x += y; if (x >= MOD) x -= MOD; return x;
}
inline int Sub(int x, const int &y) {
x -= y; if (x < 0) x += MOD; return x;
}
inline int Mul(const int &x, const int &y) {
return (long long)x * y % MOD;
}
int Pow(int x, int y) {
int ret = 1;
while (y) {
if (y & 1)
ret = Mul(ret, x);
x = Mul(x, x);
y >>= 1;
}
return ret;
}
int Mat[MAXK + 5][MAXK + 5];
int Det(int n) {
n--;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
Mat[i][j] = Sub(Mat[i][j], 0);
int ret = 1;
for (int i = 1; i <= n; i++) {
int p = i;
for (int j = i + 1; j <= n; j++)
if (Mat[p][i] < Mat[j][i])
p = j;
if (p != i) {
std::swap(Mat[p], Mat[i]);
ret = Sub(0, ret);
}
if (!Mat[i][i])
return 0;
ret = Mul(ret, Mat[i][i]);
int inv = Pow(Mat[i][i], MOD - 2);
for (int j = i + 1; j <= n; j++) {
int tmp = Mul(Mat[j][i], inv);
for (int k = i; k <= n; k++)
Mat[j][k] = Sub(Mat[j][k], Mul(Mat[i][k], tmp));
}
}
return ret;
}
int DirID[30];
const int Dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int ID[MAXN + 5][MAXN + 5];
int To[MAXN + 5][MAXN + 5], Vis[MAXN + 5][MAXN + 5];
int Go(int x, int y, int c) {
if (To[x][y])
return To[x][y];
if (Vis[x][y] == c)
return To[x][y] = -1;
Vis[x][y] = c;
int id = DirID[Map[x][y] - 'a'];
return To[x][y] = Go(x + Dir[id][0], y + Dir[id][1], c);
}
void AddEdge(int u, int v) {
Mat[v][v]++, Mat[u][v]--;
}
int main() {
DirID['R' - 'a'] = 0, DirID['L' - 'a'] = 1;
DirID['D' - 'a'] = 2, DirID['U' - 'a'] = 3;
int T; scanf("%d", &T);
while (T--) {
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; i++)
scanf("%s", Map[i] + 1);
int cnt = 0;
for (int i = 1; i <= N; i++)
for (int j = 1; j <= M; j++) {
if (Map[i][j] == '.')
To[i][j] = ID[i][j] = ++cnt;
Vis[i][j] = 0;
}
for (int i = 0; i <= N + 1; i++)
for (int j = 0; j <= M + 1; j++) {
if (i == 0 || i == N + 1 || j == 0 || j == M + 1)
To[i][j] = cnt + 1;
else if (Map[i][j] != '.')
To[i][j] = 0;
}
for (int i = 1; i <= cnt + 1; i++)
for (int j = 1; j <= cnt + 1; j++)
Mat[i][j] = 0;
int col = 0;
bool C = false;
for (int i = 1; i <= N && !C; i++)
for (int j = 1; j <= M && !C; j++) {
if (Map[i][j] == '.') {
for (int k = 0; k < 4; k++) {
int x = i + Dir[k][0], y = j + Dir[k][1];
int u = Go(x, y, ++col);
if (u == -1)
C = true;
else AddEdge(u, ID[i][j]);
}
}
else if (Go(i, j, ++col) == -1)
C = true;
}
if (C) {
puts("0");
continue;
}
printf("%d\n", Det(cnt + 1));
}
return 0;
}