2022杭电多校10 J Tree

题目描述

简要题意:给定一个 $n$ 个点 $m$ 条边的有向图,求以每个点为根的外向树的个数

$n\le 500$

Solution

我们令 $A$ 表示该图的基尔霍夫矩阵,不妨设 $r(A)=n-1$,如果 $r(A)\neq n-1$,那么答案肯定都是 $0$

为 $n-1$ 的 $n$ 阶方阵 $A$ 的伴随矩阵 $A^$ 的秩为 $1$,因为 $A$ 的秩为 $n-1$,所以一定存在不全为 $0$ 的实数 $x_1,\cdots,x_n$ 使得 $\sum_{i=1}^nx_ia_i=0$,其中 $a_i$ 表示 $A$ 的第 $i$ 行的行向量,同理可以得出关于列向量的 $y_i$,可以证明 $A^$ 的第 $i$ 行和第 $j$ 行的比例为 $\frac{x_i}{x_j}$,列同理,那么有 $A_{a,b}=A_{i,j}\frac{x_ay_b}{x_iy_j}$

对于一组 $x,y$,我们只需要高消一下即可,然后找一个不为 $0$ 的 $x_i$ 和 $y_j$ 然后求出 $A_{i,j}$ 即可求出所有的 $A_{k,k}$

时间复杂度 $O(n^3)$

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <iostream>
#define maxn 510
#define ll long long
using namespace std;

const int p = 1000000007;
inline int add(int x, int y) { return (x += y) >= p ? x - p : x; }
inline int mul(int x, int y) { return 1ll * x * y % p; }
inline int add(initializer_list<int> lst) { int s = 0; for (auto t : lst) s = add(s, t); return s; }
inline int mul(initializer_list<int> lst) { int s = 1; for (auto t : lst) s = mul(s, t); return s; }
int pow_mod(int x, int n) {
int s = 1;
for (; n; n >>= 1, x = mul(x, x))
if (n & 1) s = mul(s, x);
return s;
}

int g[maxn][maxn];
void gauss(int n) {
for (int i = 1; i <= n; ++i) {
int l = i;
for (int j = i + 1; j <= n; ++j) if (g[j][i]) l = j;
swap(g[l], g[i]); if (!g[i][i]) g[i][i] = 1, g[i][n + 1] = add(g[i][n + 1], 1);
int inv = pow_mod(g[i][i], p - 2);
for (int j = i; j <= n + 1; ++j) g[i][j] = mul(g[i][j], inv);
for (int j = 1; j <= n; ++j) {
if (i == j) continue;
for (int k = i + 1; k <= n + 1; ++k) g[j][k] = add(g[j][k], p - mul(g[j][i], g[i][k]));
g[j][i] = 0;
}
}
}

namespace Gauss {
int g[maxn][maxn];
int gauss(int n) {
int res = 1;
for (int i = 1; i <= n; ++i) {
int pos = -1;
for (int j = i; j <= n; ++j) if (g[j][i]) pos = j;
if (pos == -1) return 0; swap(g[i], g[pos]); res = pos == i ? res : p - res;
res = mul(res, g[i][i]); int inv = pow_mod(g[i][i], p - 2);
for (int j = i + 1; j <= n; ++j)
for (int k = n; k >= i; --k)
g[j][k] = add(g[j][k], p - mul({ g[j][i], inv, g[i][k] }));
} return res;
}
}

int n, m;
int w[maxn][maxn], x[maxn], y[maxn];

void work() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) w[i][j] = 0;
for (int i = 1; i <= m; ++i) {
int x, y; cin >> x >> y;
w[x][y] = add(w[x][y], p - 1);
++w[y][y];
}

for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) g[i][j] = w[j][i];
g[i][n + 1] = 0;
} gauss(n);
for (int i = 1; i <= n; ++i) x[i] = g[i][n + 1];

for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) g[i][j] = w[i][j];
g[i][n + 1] = 0;
} gauss(n);
for (int i = 1; i <= n; ++i) y[i] = g[i][n + 1];

int px = 0, py = 0;
for (int i = 1; i <= n; ++i) if (x[i]) px = i;
for (int i = 1; i <= n; ++i) if (y[i]) py = i;
if (!px || !py) {
for (int i = 1; i <= n; ++i) cout << (n == 1) << " \n"[i == n];
return ;
}

for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) Gauss::g[i][j] = w[i][j];
for (int i = px; i < n; ++i)
for (int j = 1; j <= n; ++j) Gauss::g[i][j] = Gauss::g[i + 1][j];
for (int j = py; j < n; ++j)
for (int i = 1; i < n; ++i) Gauss::g[i][j] = Gauss::g[i][j + 1];
int res = mul({ Gauss::gauss(n - 1), pow_mod(x[px], p - 2), pow_mod(y[py], p - 2) });
if (px + py & 1) res = p - res;
for (int i = 1; i <= n; ++i) cout << mul({ res, x[i], y[i] }) << " \n"[i == n];
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

int T; cin >> T;
while (T--) work();
return 0;
}