\(\text{Conclusion}\)

显然只需要这个

矩阵行列式

定义矩阵的行列式 \(\det(A)=\sum_p \mathbb{sgn} \prod a_{i,p_i}\)\(p\) 为一个排列
交换矩阵两行行列式变为相反数,一行加减另一行若干倍行列式不变
求行列式的方法:高斯消元消成上三角然后对角线乘积即为行列式
若模数不为质数,则辗转相除消元

矩阵树定理

定义:\(A\) 为图的邻接矩阵
无向图:\(D\) 为图的度数矩阵
有向图:\(D^{in}\) 为图的入度矩阵,\(D^{out}\) 为图的出度矩阵
注:若边带权,把它当做权值条边即可

生成树权值和计数(权值为树边乘积):
\(i\) 为根,去掉基尔霍夫矩阵第 \(i\) 行和第 \(i\) 列后的行列式即为答案
无向图删谁都可以,答案是一样的

基尔霍夫矩阵 \(K\)
无向图:\(K=D-A\)
有向图外向树:\(K=D^{in}-A\)
有向图内向树:\(K=D^{out}-A\)

\(P6178\) 【模板】\(Matrix-Tree\) 定理

$\text{Code}$
#include <bits/stdc++.h>
#define IN inline
using namespace std;

typedef long long LL;
const int N = 305, P = 1e9 + 7;
int n, m, t, deg[N], adj[N][N], a[N][N];

IN void Add(int &x, int y){if ((x += y) >= P) x -= P;}
IN int fpow(int x, int y){int s = 1; for(; y; y >>= 1, x = (LL)x * x % P) if (y & 1) s = (LL)s * x % P; return s;}

IN int Det(int n) {
    int fl = 1, res = 1;
    for(int i = 1; i <= n; i++) {
        int t = 0;
        for(int j = i; j <= n; j++) if (a[j][i]) {t = j; break;}
        if (!t) return 0;
        if (t ^ i) {
            for(int j = 1; j <= n; j++) swap(a[i][j], a[t][j]);
            fl ^= 1;
        }
        int inv = fpow(a[i][i], P - 2);
        for(int j = i + 1; j <= n; j++) {
            t = (LL)a[j][i] * inv % P;
            for(int k = i; k <= n; k++) a[j][k] = ((LL)a[j][k] - (LL)a[i][k] * t % P + P) % P;
        }
        res = (LL)res * a[i][i] % P;
    }
    return (fl ? P - res : res);
}

int main() {
    scanf("%d%d%d", &n, &m, &t);
    for(int i = 1, u, v, w; i <= m; i++) {
        scanf("%d%d%d", &u, &v, &w);
        Add(adj[v][u], w), Add(deg[v], w);
        if (!t) Add(deg[u], w), Add(adj[u][v], w);
    }
    for(int i = 1; i <= n; i++) {
        a[i][i] = deg[i];
        for(int j = 1; j <= n; j++) a[i][j] = (a[i][j] - adj[i][j] + P) % P;
    }
    for(int i = 1; i <= n; i++) swap(a[1][i], a[n][i]);
    printf("%d\n", Det(n - 1));
}

\(P4111\) [HEOI2015] 小 Z 的房间

$\text{Code}$
#include <cstdio>
#include <iostream>
#define IN inline
using namespace std;

template <typename Tp>
IN void read(Tp &x) {
	x = 0; char ch = getchar(); int f = 0;
	for(; !isdigit(ch); f = (ch == '-' ? 1 : f), ch = getchar());
	for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar());
	if (f) x = ~x + 1;
}

typedef long long LL;
const int N = 105, P = 1e9;
int n, m, a[N][N], id[N][N];
char s[N][N];

IN void add(int x, int y){++a[x][x], ++a[y][y], --a[x][y], --a[y][x];}

int main() {
	read(n), read(m);
	int cnt = 0;
	for(int i = 1; i <= n; i++) {
		scanf("%s", s[i] + 1);
		for(int j = 1; j <= m; j++) if (s[i][j] == '.') id[i][j] = ++cnt;
	}
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++) if (s[i][j] == '.') {
			if (j < m && s[i][j + 1] == '.') add(id[i][j], id[i][j + 1]);
			if (i < n && s[i + 1][j] == '.') add(id[i][j], id[i + 1][j]);
		}
	--cnt;
	int ans = 1;
	for(int i = 1; i <= cnt; i++)
		for(int j = i + 1; j <= cnt; j++)
			while (a[j][i]) {
				int p = a[i][i] / a[j][i];
				for(int k = i; k <= cnt; k++) a[i][k] = (a[i][k] - (LL)a[j][k] * p % P + P) % P;
				for(int k = i; k <= cnt; k++) swap(a[i][k], a[j][k]);
				ans *= -1;
			}
	for(int i = 1; i <= cnt; i++) ans = ((LL)ans * a[i][i] % P + P) % P;
	printf("%d\n", ans);
}

BEST 定理

有向欧拉图本质不同的欧拉回路数量为:\(T\prod (out_i-1)!\)
\(T\) 为内向生成树数量

【模板】BEST 定理
这题答案为未去掉循环同构的欧拉回路,所以最后要乘上 \(out_1\)

$\text{Code}$
#include <cstdio>
#include <iostream>
#include <cstring>
#define IN inline
using namespace std;

template <typename Tp>
IN void read(Tp &x) {
	x = 0; char ch = getchar(); int f = 0;
	for(; !isdigit(ch); f = (ch == '-' ? 1 : f), ch = getchar());
	for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar());
	if (f) x = ~x + 1;
}

typedef long long LL;
const int N = 105, P = 1e6 + 3;
int n, a[N][N], fa[N], out[N], in[N], fac[200005];
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}

IN int Gauss() {
	int m = n - 1, res = 1;
	for(int i = 1; i <= m; i++)
		for(int j = i + 1; j <= m; j++)
			while (a[j][i]) {
				int p = a[i][i] / a[j][i];
				for(int k = i; k <= m; k++) a[i][k] = (a[i][k] - (LL)a[j][k] * p % P + P) % P;
				for(int k = i; k <= m; k++) swap(a[i][k], a[j][k]);
				res *= -1;
			}
	for(int i = 1; i <= m; i++) if (a[i][i]) res = ((LL)res * a[i][i] % P + P) % P;
	return res;
}

int main() {
	fac[0] = 1; for(int i = 1; i <= 200000; i++) fac[i] = (LL)fac[i - 1] * i % P;
	int t; read(t);
	for(; t; --t ) {
		read(n), memset(a, 0, sizeof a), memset(in, 0, sizeof in);
		for(int i = 1; i <= n; i++) fa[i] = i;
		for(int i = 1, s; i <= n; i++) {
			read(s), out[i] = a[i][i] = s;
			for(int j = 1, x; j <= s; j++) read(x), --a[i][x], ++in[x], fa[find(i)] = find(x);
		}
		int flag = 0;
		for(int i = 1; i <= n; i++) if (out[i] != in[i]) {flag = 1; break;}
		for(int i = 1; i <= n; i++) if (find(i) != find(1) && out[i]) {flag = 1; break;}
		if (flag) {puts("0"); continue;}
		if (!out[1]) {puts("1"); continue;}
		int ans = (LL)out[1] * Gauss() % P;
		for(int i = 1; i <= n; i++) if (out[i]) ans = (LL)ans * (fac[out[i] - 1] + P) % P;
		printf("%d\n", ans);
	}
}