LGV 引理
显然只需要这个
引理
只适用于有向无环图
定义 表示 这条路径上所有边权的乘积
表示 到 每一条路径 的 之和
起点集合 与终点集合 大小一样
一组不相交路径 : 表示一条从 到 的路径,满足 和 没有公共交点
表示排列 的逆序对
考虑矩阵
有定理
其中 表示每一组不相交的路径
然后就是做题理解了
【模板】LGV 引理
注意到网格图中 递增,那么满足不相交的路径组的只能是
排列 只能是 ,所以直接行列式就是答案
#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()); } }
[NOI2021] 路径交点
考虑 ,行列式即答案
发现行列式相乘即为答案
原因:记 表示矩阵 偶排列和奇排列的答案,那么行列式相乘 恰好为答案
于是就有 分了
行列式有结论
即两矩阵行列式的积为两矩阵积的行列式
这样就没了
另一种考虑是把邻接矩阵乘起来,直接应用 引理便是对的了
#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()); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现