某个奇怪的引理 学习总结
貌似跟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; }