LGV引理小记
由于是看 oi-wiki 学的,内容基本是搬过来的。
小前提: LGV 引理仅适用于有向无环图。
定义
\(\omega(P)\) 表示 \(P\) 这条路径上所有边的边权之积。(路径计数时,可以将边权都设为 \(1\))(事实上,边权可以为生成函数)
\(e(u,v)\) 表示从 \(u\) 到 \(v\) 的每一条路径 \(P\) 的 \(\omega(P)\) 之和,即 \(e(u,v)=\sum\limits_{P:u\rightarrow v}\omega(P)\)。
起点集合 \(A\),是有向无环图点集的一个子集,大小为 \(n\)。
终点集合 \(B\),也是有向无环图点集的一个子集,大小也为 \(n\)。
一组 \(A\rightarrow B\) 的不相交路径 \(S\):\(S_i\) 是一条从 \(A_i\) 到 \(B_{\sigma(S)_i}\) 的路径(\(\sigma(S)\) 是一个排列),对于任何 \(i\ne j\),\(S_i\) 和 \(S_j\) 没有公共顶点。
\(t(\sigma)\) 表示排列 \(\sigma\) 的逆序对个数。
引理
其中 \(\sum\limits_{S:A\rightarrow B}\) 表示满足上文要求的 \(A\rightarrow B\) 的每一组不相交路径 \(S\)。
证明
由行列式定义可得
观察到 \(\prod\limits_{i=1}^n \sum\limits_{P:a_i\to b_{\sigma(i)}} \omega(P)\),实际上是所有从 \(A\) 到 \(B\) 排列为 \(\sigma\) 的路径组 \(P\) 的 \(\omega(P)\) 之和。
此处 \(P\) 为任意路径组。
设 \(U\) 为不相交路径组,\(V\) 为相交路径组,
设 \(P\) 中存在一个相交路径组 \(P_i:a_1 \to u \to b_1\),\(P_j:a_2 \to u \to b_2\),则必然存在和它相对的一个相交路径组 \(P_i'=a_1\to u\to b_2\),\(P_j'=a_2\to u\to b_1\),\(P'\) 的其他路径与 \(P\) 相同。可得 \(\omega(P)=\omega(P'),t(P)=t(P')\pm 1\)。
因此我们有 \(\sum\limits_{V:A\to B}(-1)^{t(\sigma)}\prod\limits_{i=1}^n \omega(V_i)=0\)。
则 \(\det(M)=\sum\limits_{U:A\to B}(-1)^{t(U)}\prod\limits_{i=1}^n \omega(U_i)=0\)。
证毕。
例题
CF348D Turtles
比较直接的 LGV 引理的应用。考虑所有合法路径,发现从 \((1,1)\) 出发一定要经过 \(A=\{(1,2), (2,1)\}\),而到达终点一定要经过 \(B=\{(n-1, m), (n, m-1)\}\),则 \(A, B\) 可立即选定。应用 LGV 引理可得答案为:
其中 \(f(a, b)\) 为图上 \(a\rightarrow b\) 的路径数,带有障碍格点的路径计数问题可以直接做一个 \(\Theta(nm)\) 的 dp,则 \(f\) 易求。最终复杂度 \(\Theta(nm)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MOD=1e9+7;
const int MAXN=3e3+5;
int n,m;
int dp[MAXN][MAXN];
char ma[MAXN][MAXN];
inline int solve(int x1,int y1,int x2,int y2)
{
memset(dp,0,sizeof(dp));
dp[x1][y1]=(ma[x1][y1]=='.');
for(int i=1;i<=x2;i++)
for(int j=1;j<=y2;j++)
{
if(ma[i][j]=='#') continue;
dp[i][j]=(dp[i][j]+dp[i-1][j])%MOD;
dp[i][j]=(dp[i][j]+dp[i][j-1])%MOD;
}
return dp[x2][y2]%MOD;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>ma[i][j];
int res1=solve(1,2,n-1,m);
int res2=solve(1,2,n,m-1);
int res3=solve(2,1,n-1,m);
int res4=solve(2,1,n,m-1);
int ans=((res1*res4)%MOD-(res2*res3)%MOD+MOD)%MOD;
printf("%lld\n",ans);
return 0;
}