貌似跟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;
}