G
N
I
D
A
O
L

【noi复习】LGV引理

LGV 引理 只适用于 有向无环图

记起点集合为 \(A=\{a_1,a_2,\cdots a_n\}\),终点集合为 \(B=\{b_1,b_2\cdots b_n\}\),两个集合大小均为 \(n\)

对于一条路径 \(P=\displaystyle\{u_1,w_{u_1u_2},u_2,\cdots u_t \}\),用 \(\omega(P)\) 表示 \(P\) 上所有边权之

\(e(u,v)\) 表示从 \(u\)\(v\) 所有路径的 \(\omega\)

定义两条路径“不相交”为不存在公共顶点

定义一组“从起点集合 \(A\) 出发,结束于终点集合 \(B\) 的路径”为选定一个排列 \(\sigma_1,\cdots\sigma_n\) ,让 \(a_i\) 走向 \(\displaystyle b_{\sigma_i}\),这 \(n\) 条路径构成的集合,记该集合为 \(S\)\(a_i\) 走向 \(b_{\sigma_i}\) 的路径为 \(S_i\)

定义一组 “\(A\rarr B\) 的不相交路径”为如上的 \(S\) ,且 \(S_i\)\(S_j\) 两两不相交。

\(t(\sigma)\) 代表排列 \(\sigma\) 的逆序对个数。

LGV 引理如下:

\[设\ 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\limits_{S:A\rightarrow B}(-1)^{t(\sigma(S))}\prod\limits_{i=1}^n \omega(S_i) \]

其中 \(\sum\limits_{S:A\rightarrow B}\) 表示满足上文要求的 \(A\rightarrow B\) 的每一组不相交路径 \(S\)

例题

P6657 【模板】LGV 引理

题意

有一个 \(n\times n\) 的棋盘,左下角为 \((1,1)\),右上角为 \((n,n)\),若一个棋子在点 \((x,y)\),那么走一步只能走到 \((x+1,y)\)\((x,y+1)\)
现在有 \(m\) 个棋子,第 \(i\) 个棋子一开始放在 \((a_i,1)\),最终要走到 \((b_i,n)\)。问有多少种方案,使得每个棋子都能从起点走到终点,且对于所有棋子,走过路径上的点互不相交。输出方案数 \(\bmod\ 998244353\) 的值。
两种方案不同当且仅当存在至少一个棋子所经过的点不同。\(\{a_n\}\)\(\{b_n\}\) 均从小到大排序。

题解

容易发现的一点是,\(a_i\) 必定和 \(b_i\) 对应,否则一定会产生相交路径,因此 \(\sigma\) 唯一确定。路径计数问题一般考虑令边权均为 \(1\) ,且 \(\sigma\) 中逆序对数量为 \(0\) ,所以 \(\mathrm {RHS}=\sum_{S:A\rightarrow B} 1=\#S:A\rightarrow B\),即为所要求的答案。因此直接计算行列式的值即可,其中 \(e(a_i,b_j)\) 的值为从 \((a_i,1)\) 走到 \((b_i,n)\) 的路径条数,简单推导知其值为 \(\binom{b_i-a_i+n-1}{n-1}\) ,特别地,如果 \(b_i<a_i\) ,那么答案为 \(0\)

CF348D Turtles

题意

有一个 \(n\times m\) 的格点棋盘,其中某些格子可走,某些格子不可走。从 \((x, y)\) 只能走到 \((x+1, y)\)\((x, y+1)\) 的位置,求两只海龟从 \((1, 1)\)\((n, m)\) 的不相交路径数对 \(10^9+7\) 取模之后的结果。\(2\le n,m\le3000\)

题解

两只海龟分别会经过 \((1,2)\)\((2,1)\) ,以及 \((n-1,m)\)\((n,m-1)\) 。注意到 \((1,2)\)\((n-1,m)\) 是一组, \((2,1)\)\((n,m-1)\) 是另一组,否则会有相交路径。因此不相交路径数即为:

\[\begin{vmatrix} e(a_1, b_1) & e(a_1, b_2) \\ e(a_2, b_1) & e(a_2, b_2) \end{vmatrix} = e(a_1, b_1)\times e(a_2, b_2) - e(a_1, b_2)\times e(a_2, b_1) \]

其中 \(a_1=(1,2),b_1=(n-1,m),a_2=(2,1),b_2=(n,m-1)\)。由于某些格子不能走,使用 dp 求出方案数即可。

P7736 [NOI2021] 路径交点

题意

给一个分层 DAG,共 \(k\) 层,起点和终点(第一层和最后一层)点的数量相同,均为 \(n_1\) ,要求路径没有公共点。问所有路径方案中,路径有偶数个交点的方案数 减去 有奇数个交点的方案数 是多少。

\(2\leq k,n_1\leq 100\)

题解

起点终点数量相同的提示性很强,这是 LGV 引理比较特别的。

考虑如何刻画交点个数,对于两条路径 \(a_1\rarr b_1\)\(a_2\rarr b_2\) 而言,不妨 \(a_1<a_2\),如果 \(b_1<b_2\) 那么它们会产生偶数个交点,如果 \(b_1>b_2\) 那么它们会产生奇数个交点。那么所有路径交点个数为奇数,也就即为,设 \(a_i \rarr b_{\sigma_i}\),那么 \(t(\sigma)\) 为奇数。到这里我们突然发现,题目中奇数个交点方案数的容斥系数为 \(-1\) ,也就是 \((-1)^{t(\sigma)}\) !相对应的,偶数个交点也是如此。

由 LGV 引理,还是设边权均为 \(1\),则 \(\mathrm{RHS}\) 就是题目要我们求的东西,因此只需要求出左边行列式的值。

\[\begin{vmatrix}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{vmatrix} \]

只需要 dp 算出起点到终点任意两点间的路径数量即可。时间复杂度 \(O(n^3)\) 。完全不需要脑子就能做。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read(){
	ll x=0; bool f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=0; ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48); ch=getchar();
	}
	return f?x:-x;
}
inline void write(ll x){
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10);
	putchar('0'+x%10);
}
const ll MOD=998244353;
const ll N=209;
ll n[N],a[N][N];
ll Gauss(){
	ll res=1;
	for(ll i=1;i<=n[1];i++){
		ll r=i;
		for(ll j=i+1;j<=n[1];j++){
			if(a[j][i]){
				r=j;
				break;
			}
		}
		if(r!=i) (res*=MOD-1)%=MOD,swap(a[r],a[i]);
		if(a[i][i]==0){
			return 0;
		}
		for(ll j=i+1;j<=n[1];j++){
			while(a[j][i]){
				ll factor=a[i][i]/a[j][i];
				for(ll k=i;k<=n[1];k++){
					a[i][k]-=a[j][k]*factor%MOD;
					a[i][k]%=MOD,a[i][k]+=MOD,a[i][k]%=MOD;
				}
				swap(a[j],a[i]);
				(res*=MOD-1)%=MOD;
			}
		}
	}
	for(ll i=1;i<=n[1];i++){
		res=(res*a[i][i])%MOD;
	}
	return res;
}
ll g[N][N],m[N];
void solve(){
	ll k=read();
	for(ll i=1;i<=k;i++){
		n[i]=read();
	}
	for(ll i=1;i<=k-1;i++){
		m[i]=read();
	}
	for(ll i=1;i<=n[1];i++){
		a[i][i]=1;
	}
	for(ll i=1;i<=k-1;i++){
		memset(g,0,sizeof(g));
		for(ll j=1;j<=m[i];j++){
			ll u=read(),v=read();
			for(ll t=1;t<=n[1];t++){
				g[t][v]+=a[t][u];
				if(g[t][v]>=MOD) g[t][v]-=MOD;
			}
		}
		memcpy(a,g,sizeof(g));
	}
	printf("%lld\n",Gauss());
}
int main(){
	ll T=read();
	while(T--){
		solve();
	}
	return 0;
}

posted @ 2025-06-28 23:26  QWQcoding  阅读(29)  评论(0)    收藏  举报