某个奇怪的引理 学习总结

貌似跟OI的关系不是很密切?

但是既然考到了就来学习一发

吐槽一下今天的题目:

第二题要用这个奇怪的引理(其实貌似用容斥原理可以直接推?

第三题要用格林公式(根本不会

然后就考得非常的惨烈

 

这个引理的名字叫Lindström–Gessel–Viennot

具体可以去wiki一下QAQ

貌似是用来解决网格图不相交路径计数的

题目类型貌似很单一,通常都是上面有点集A,下面有点集B,然后求每个A到对应B的不相交路径总数

可能还会有一些限制QAQ

然后我们考虑我们求出f(i,j)表示A的第i个点到B的第j个点的路径方案

考虑如果设排列P,我们考虑i->P(i)

则这个排列有多少个逆序对就至少有多少个交点

这里出现了至少,我们可以考虑容斥原理

写出容斥原理的式子之后我们会得到一个O(n!)的算法

之后仔细(不用)观察会发现容斥原理的式子实际上就是求行列式

然后O(n^3)求行列式即可

以上是本蒟蒻的理解,具体证明还是看wiki吧

至于f(i,j)的求解,依据题目选择合适的DP

 

codeforces #202 div1  D

直接O(nm)DP可以求出f(i,j),之后直接上引理就可以了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;
const int mod=1000000007;
const int maxn=3010;
int n,m;
char c[maxn][maxn];
int dp[maxn][maxn];
int f[3][3];

int Get_DP(int qx,int qy,int zx,int zy){
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			if(c[i][j]=='#')continue;
			if(i==qx&&j==qy){dp[i][j]=1;continue;}
			dp[i][j]=(dp[i-1][j]+dp[i][j-1])%mod;
		}
	}return dp[zx][zy];
}
int det(int n){
	int ret=1;
	for(int i=1;i<=n;++i){
		for(int j=i+1;j<=n;++j){
			while(f[j][i]){
				LL tmp=f[i][i]/f[j][i];
				for(int k=i;k<=n;++k)f[i][k]=(f[i][k]-tmp*f[j][k]%mod+mod)%mod;
				for(int k=i;k<=n;++k)swap(f[i][k],f[j][k]);
				ret=-ret;
			}
		}
		if(f[i][i]==0)return 0;
		ret=1LL*ret*f[i][i]%mod;
	}return (ret%mod+mod)%mod;
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			c[i][j]=getchar();
			while(c[i][j]<'!')c[i][j]=getchar();
		}
	}
	f[1][1]=Get_DP(2,1,n,m-1);
	f[1][2]=Get_DP(2,1,n-1,m);
	f[2][1]=Get_DP(1,2,n,m-1);
	f[2][2]=Get_DP(1,2,n-1,m);
	printf("%d\n",det(2));
	return 0;
}

然后就是今天的题目QAQ

貌似不能多说些什么

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;
const int mod=998244353;
const int maxn=420;
int n,m,p,q,cnt;
int jc[300010],inv[300010];
int a[maxn],b[maxn];
int f[maxn][maxn];
int dp[maxn<<1];
struct Point{
	int x,y;
}c[maxn<<1];

bool cmp(const Point &A,const Point &B){
	if(A.x==B.x)return A.y<B.y;
	return A.x<B.x;
}
int pow_mod(int v,int p){
	int tmp=1;
	while(p){
		if(p&1)tmp=1LL*tmp*v%mod;
		v=1LL*v*v%mod;p>>=1;
	}return tmp;
}
bool pd(int x,int y,int i){return x<=c[i].x&&y<=c[i].y;}
int C(int n,int m){return 1LL*jc[n]*inv[m]%mod*inv[n-m]%mod;}
void Get_f(int now,int qx,int qy){
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=cnt;++i){
		if(pd(qx,qy,i)){
			dp[i]=C(c[i].x+c[i].y-qx-qy,c[i].x-qx);
			for(int j=1;j<i;++j){
				if(pd(c[j].x,c[j].y,i)&&dp[j]){
					dp[i]=dp[i]-1LL*C(c[i].x+c[i].y-c[j].x-c[j].y,c[i].x-c[j].x)*dp[j]%mod;
					if(dp[i]<0)dp[i]+=mod;
				}
			}
		}
	}
	for(int i=q+1;i<=cnt;++i)f[now][i-q]=dp[i];
}
int det(int n){
	int ret=1;
	for(int i=1;i<=n;++i){
		for(int j=i+1;j<=n;++j){
			while(f[j][i]){
				LL tmp=f[i][i]/f[j][i];
				for(int k=i;k<=n;++k)f[i][k]=(f[i][k]-tmp*f[j][k]%mod+mod)%mod;
				for(int k=i;k<=n;++k)swap(f[i][k],f[j][k]);
				ret=-ret;
			}
		}
		if(f[i][i]==0)return 0;
		ret=1LL*ret*f[i][i]%mod;
	}return (ret%mod+mod)%mod;
}

int main(){
	scanf("%d%d%d%d",&n,&m,&p,&q);
	jc[0]=1;
	for(int i=1;i<=n+m;++i)jc[i]=1LL*jc[i-1]*i%mod;
	inv[n+m]=pow_mod(jc[n+m],mod-2);
	for(int i=n+m-1;i>=0;--i)inv[i]=1LL*inv[i+1]*(i+1)%mod;
	for(int i=1;i<=p;++i)scanf("%d",&a[i]);
	for(int i=1;i<=p;++i)scanf("%d",&b[i]);
	sort(a+1,a+p+1);sort(b+1,b+p+1);
	for(int i=1;i<=q;++i)scanf("%d%d",&c[i].x,&c[i].y);
	sort(c+1,c+q+1,cmp);cnt=q;
	for(int i=1;i<=p;++i)cnt++,c[cnt].x=n,c[cnt].y=b[i];
	for(int i=1;i<=p;++i)Get_f(i,0,a[i]);
	printf("%d\n",det(p));
	return 0;
}

  

posted @ 2016-06-22 16:57  _Vertical  阅读(916)  评论(0编辑  收藏  举报