best 定理居然还有运用范围。
思路
考虑如何来判断是否有解。
由于每一条边都需要用到。
但是它是使用很多条路径进行覆盖。
我们考虑一个很巧妙的转化。
建立一个超级源点,源点向每一条路径的开头连一条边。每一条路径的结尾向源点连一条边,这样一条路径就变成了一个回路。
把所有回路连起来,就是一条欧拉回路。
问题转化成了欧拉回路计数。
对于欧拉回路计数,我们有经典的 best 定理。
它描述的是这样一个东西。
\[ans=内向树个数\times \prod (deg_i-1)!
\]
内向树个数容易使用矩阵树定理求出。
考虑这个题实际求的是匹配路径方案数。
所以要把源点的 \((deg_i-1)!\) 除掉。
时间复杂度:\(O(Tn^3)\)。
Code
#include <bits/stdc++.h>
using namespace std;const int mod = 1e9 + 7;int n, k, ru[110], cu[110];
int f[110];
int a[110][110];inline int power(int x, int y) {int res = 1;while (y) {if (y & 1) res = 1ll * res * x % mod;x = 1ll * x * x % mod, y >>= 1;}return res;
}
inline int gauss() {int res = 1;for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)if (a[i][j] < 0) a[i][j] += mod;for (int i = 1; i <= n; i++) {if (ru[i] == 0 && cu[i] == 0) continue;int id = i;for (int j = i; j <= n; j++) if (a[j][i]) id = j;if (id != i) {swap(a[i], a[id]), res = mod - res;}int p = power(a[i][i], mod - 2);for (int j = i + 1; j <= n; j++) {int c = 1ll * a[j][i] * p % mod;for (int k = i; k <= n; k++) {a[j][k] = (a[j][k] - 1ll * a[i][k] * c) % mod;if (a[j][k] < 0) a[j][k] += mod;}}}for (int i = 1; i <= n; i++)if (ru[i] || cu[i]) res = 1ll * res * a[i][i] % mod;return res;
}
inline void solve() {cin >> n >> k;for (int i = 1; i <= n; i++) {char c;cin >> c;if (c == 'S') a[0][i]--, a[0][0]++, cu[0]++, ru[i]++;if (c == 'R') a[i][0]--, a[i][i]++, cu[i]++, ru[0]++;}for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {char c;cin >> c;if (c == '1') {a[i][j]--;a[i][i]++;cu[i]++, ru[j]++;}}}bool flag = 0;for (int i = 0; i <= n; i++)if (ru[i] != cu[i]) flag = 1;if (flag == 0) {int ns = gauss();for (int i = 1; i <= n; i++)if (cu[i]) ns = 1ll * ns * f[cu[i] - 1] % mod;cout << ns << "\n";} else {cout << 0 << "\n";}memset(a, 0, sizeof a);memset(ru, 0, sizeof ru);memset(cu, 0, sizeof cu);
}int main() {int t;cin >> t;f[0] = 1;for (int i = 1; i <= 100; i++) f[i] = 1ll * f[i - 1] * i % mod;while (t--) solve();
}