LGV 引理

Conclusion

显然只需要这个

LGV 引理

只适用于有向无环图

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

起点集合 A 与终点集合 B 大小一样
一组不相交路径 SSi 表示一条从 AiBpi 的路径,满足 SiSj(i!=j) 没有公共交点
τ(p) 表示排列 p 的逆序对

考虑矩阵 M

M=[e(a1,b1)e(a1,b2)e(a1,bn)e(a2,b1)e(a2,b2)e(a2,bn)e(an,b1)e(an,b2)e(an,bn)]

有定理

det(M)=S:AB(1)τ(S)i=1nω(Si)

其中 S:AB 表示每一组不相交的路径

然后就是做题理解了

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

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 发现行列式相乘即为答案
原因:记 fi,gi 表示矩阵 i 偶排列和奇排列的答案,那么行列式相乘 (figi)(fi+1gi+1)=figi+gigi+1figi+1fi+1gi 恰好为答案

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

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());
}
}
posted @   leiyuanze  阅读(107)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示