【学习笔记】LGV引理

发布时间 2023-04-15 09:19:26作者: T-water

令$ w(P) $表示路径 $ P$ 的所有边权之积,\(e(u,v)\) 表示所有 \(u\)\(v\) 的路径 \(w(P)\) 之和,令:

\[M= \begin{bmatrix} e(A_1,B_1) \quad e(A_1,B_2) \quad ... \quad e(A_1,B_n) \\ e(A_2,B_1) \quad e(A_2,B_2) \quad ... \quad e(A_2,B_n) \\ ... \\ e(A_n,B_1) \quad e(A_n,B_2) \quad ... \quad e(A_n,B_n) \end{bmatrix} \]

\(M\) 的行列式即为所有从 \(A_i\)\(B_i\) 的路径不交的所有方案个数。
感性理解:如果两条路径相交,那么将相交点后的路径交换两条路径仍然相交,但在行列式中逆序对中的奇偶性变化,两者路径相加值为0 。于是之剩下不交的路径。
详细证明见 oiwiki。
关于 $ w(P) $ 的理解:

  • 路径计数时此边能/否走即赋值为1/0,即 \(e(u,v)\) 表示所有 \(u\)\(v\) 的路径条数。
  • 生成函数?长大后再学习吧!

luogu模板题

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=1000010,M=2000000,K=110;
const int mod=998244353;
int fps[M+10],inv[M+10];
inline int C(int x,int y){return (x<0||y<0||x<y)?0:fps[x]*inv[y]%mod*inv[x-y]%mod;}
inline int pw(int x,int y){
	int ansn=1;
	while(y){
		if(y&1)ansn=ansn*x%mod;
		x=x*x%mod,y/=2;
	}
	return ansn;
}
int n,m,a[K],b[K];
int f[K][K];
void pri(){
	for(int i=1;i<=m;i++){
		for(int j=1;j<=m;j++)cout<<f[i][j]<<" ";
		cout<<"\n";
	}
	cout<<"\n";
	return ;
}
void work(){
	n=rd(),m=rd();
	for(int i=1;i<=m;i++)a[i]=rd(),b[i]=rd();
	for(int i=1;i<=m;i++){
		for(int j=1;j<=m;j++){
			f[i][j]=(a[i]>b[j])?0:C(n-1+b[j]-a[i],b[j]-a[i]);
//			cout<<i<<" "<<j<<":"<<n-1+b[j]-a[i]<<" "<<b[j]-a[i]<<"-"<<C(n-1+b[j]-a[i],b[j]-a[i])<<":"<<f[i][j]<<"\n";
//			cout<<f[i][j]<<" ";
		}
//		cout<<"\n";
	}
	int k=1;
	for(int i=1;i<m;i++){
//		cout<<"wk:"<<i<<"\n";
//		pri();
		int p;
		for(p=i;p<=m;p++)if(f[p][i])break;
		if(p==m+1){
			printf("0\n");
			return ;
		}
		swap(f[p],f[i]),k*=-1;
		for(int j=i+1;j<=m;j++){
			if(!f[j][i])continue;
			
			p=f[j][i]*pw(f[i][i],mod-2)%mod;
			for(int o=i;o<=m;o++)f[j][o]-=f[i][o]*p%mod,f[j][o]=(f[j][o]+mod)%mod;
			
		}
	}
	for(int i=1;i<=m;i++)k=k*f[i][i]%mod;
	printf("%lld\n",abs(k));
	return ;
}
signed main(){
	fps[0]=inv[0]=1;
	for(int i=1;i<=M;i++)fps[i]=fps[i-1]*i%mod;
	inv[M]=pw(fps[M],mod-2);
	for(int i=M-1;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;
	int T=rd();
	while(T--)work();
	return 0;
}