\(\text{Conclusion}\)

显然只需要这个

\(\text{LGV}\) 引理

只适用于有向无环图

定义 \(\omega(P)\) 表示 \(P\) 这条路径上所有边权的乘积
\(e(u,v)\) 表示 \(u\)\(v\) 每一条路径 \(P\)\(\omega(P)\) 之和

起点集合 \(A\) 与终点集合 \(B\) 大小一样
一组不相交路径 \(S\)\(S_i\) 表示一条从 \(A_i\)\(B_{p_i}\) 的路径,满足 \(S_i\)\(S_j(i!=j)\) 没有公共交点
\(\tau(p)\) 表示排列 \(p\) 的逆序对

考虑矩阵 \(M\)

\[M=\begin{bmatrix}e(a_1, b_1) & e(a_1, b_2) & \cdots & e(a_1, b_n) \\ e(a_2, b_1) & e(a_2, b_2) & \cdots & e(a_2, b_n) \\ \vdots & \vdots & \ddots & \vdots \\ e(a_n, b_1) & e(a_n, b_2) & \cdots & e(a_n, b_n)\\\end{bmatrix}
\]

有定理

\[\det(M)=\sum_{S:A\to B}{(-1)^{\tau(S)}\prod_{i=1}^{n}\omega(S_i)}
\]

其中 \(\sum_{S:A\to B}\) 表示每一组不相交的路径

然后就是做题理解了

\(P6657\) 【模板】LGV 引理
注意到网格图中 \(a_i,b_i\) 递增,那么满足不相交的路径组的只能是 \(a_i\rightarrow b_i\)
排列 \(S\) 只能是 \((1,2,...,n)\),所以直接行列式就是答案

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

typedef long long LL;
const int P = 998244353, N = 2e6 + 5;
int a[105], b[105], c[105][105], fac[N], ifac[N], m, n;

IN int C(int n, int m){return (LL)fac[n] * ifac[n - m] % P * ifac[m] % 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 ct = 0, res = 1;
	for(int i = 1; i <= m; i++) {
		int z = 0;
		for(int j = i; j <= m; j++) if (c[j][i]) z = j;
		if (!z) return 0;
		if (z ^ i) {
			for(int j = 1; j <= m; j++) swap(c[i][j], c[z][j]);
			ct ^= 1;
		}
		int inv = fpow(c[i][i], P - 2);
		for(int j = i + 1; j <= m; j++) {
			int d = (LL)c[j][i] * inv % P;
			for(int k = i; k <= m; k++) c[j][k] = (c[j][k] - (LL)c[i][k] * d % P) % P;
		}
		res = (LL)res * c[i][i] % P;
	}
	return (ct ? (P - res) % P : (res + P) % P);
}

int main() {
	fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
	for(int i = 2; i <= N - 5; i++) fac[i] = (LL)fac[i - 1] * i % P, ifac[i] = (LL)ifac[P % i] * (P - P / i) % P;
	for(int i = 2; i <= N - 5; i++) ifac[i] = (LL)ifac[i] * ifac[i - 1] % P;
	int t; scanf("%d", &t);
	for(; t; --t) {
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= m; i++) scanf("%d%d", &a[i], &b[i]);
		for(int i = 1; i <= m; i++)
			for(int j = 1; j <= m; j++)
				c[i][j] = (a[i] > b[j] ? 0 : C(b[j] - a[i] + n - 1, n - 1));
		printf("%d\n", Det());
	}
}

\(P7736\) [NOI2021] 路径交点
考虑 \(K=2\),行列式即答案
\(K>2\) 发现行列式相乘即为答案
原因:记 \(f_i,g_i\) 表示矩阵 \(i\) 偶排列和奇排列的答案,那么行列式相乘 \((f_i-g_i)(f_{i+1}-g_{i+1})=f_ig_i+g_ig_{i+1}-f_ig_{i+1}-f_{i+1}g_i\) 恰好为答案

于是就有 \(75\) 分了
行列式有结论 \(|A||B|=|AB|\)
即两矩阵行列式的积为两矩阵积的行列式
这样就没了
另一种考虑是把邻接矩阵乘起来,直接应用 \(LGV\) 引理便是对的了

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

typedef long long LL;
const int N = 205, P = 998244353;
int ctn[N], ctm[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;}

struct Matrix {
    int n, m, a[N][N];
    IN Matrix(int _n = 0, int _m = 0) {
        n = _n, m = _m;
        for(int i = 0; i <= n; i++)
            for(int j = 0; j <= m; j++) a[i][j] = 0;
    }
    IN Matrix operator * (const Matrix &b) {
        Matrix c(n, b.m);
        for(int i = 1; i <= n; i++)
            for(int k = 1; k <= m; k++)
                for(int j = 1; j <= b.m; j++)
                    Add(c.a[i][j], a[i][k] * b.a[k][j] % P);
        return c;
    }
    IN int Det() {
        int fl = 0, 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)inv * a[j][i] % 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);
    }
}ret, A;

int main() {
    int t; scanf("%d", &t);
    for(; t; --t) {
        int K; scanf("%d", &K);
        for(int i = 1; i <= K; i++) scanf("%d", &ctn[i]);
        for(int i = 1; i < K; i++) scanf("%d", &ctm[i]);
        for(int i = 1; i < K; i++) {
            A = Matrix(ctn[i], ctn[i + 1]);
            for(int j = 1, u, v; j <= ctm[i]; j++) scanf("%d%d", &u, &v), ++A.a[u][v];
            if (i == 1) ret = A; else ret = ret * A;
        }
        printf("%d\n", ret.Det());
    }
}