问题描述
(题面太长,直接挂链接了)
解析
首先,我们考虑转化一下问题。我们不妨将棋盘从上边界和右边界再往外扩展一圈,那么到达上边界和右边界就结束了。进一步想,到达边界之后,如果还要走的话,一定只有一个方向,并且最后会到达右上角的格子。那么,答案转化为从(1,1)到(n+1,m+1)的方案数。
第二步,考虑如何处理不能走到的点。先将所有点从左下到右上排序。记 (f_i) 为从(1,1)到第 (i) 个点且不经过其他标记点的方案数;记 (cal(x,y)) 表示从(1,1)走到(x,y)的方案数。我们可以容斥一下:
[f_i=cal(x_i,y_i)-sum_{j=1}^{i-1}f_j imes cal(x_i-x_j+1,y_i-y_j+1)
]
如果将(n+1,m+1)记为第k+1个标记点,那么答案就是 (f_{k+1})。
第三步,考虑如何计算 (cal(x,y)) 。如果没有斜着走的操作,答案就是 (C_{x+y-2,x-1})。现在将斜着走的操作加入进去。每斜着走一次,总的步数就会减一。我们依次枚举走了 (i) 次斜的,就需要在x+y-2次中选择 (i) 次变成斜的,然后剩下的按照普通走法。即:
[cal(x,y)=sum_{i=0}^{min(n,m)-1}C_{x+y-2}^{i}C_{x+y-2-2i}^{x-i-1}
]
由于n是1e7级别的,我们用卢卡斯定理计算组合数。复杂度 (O(k^2nlog_{mod} n))。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#define int long long
#define N 22
using namespace std;
const int mod=59393;
struct node{
int x,y;
}a[N];
int n,m,k,i,j,fac[mod+2],inv[mod+2],f[N];
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
int poww(int a,int b)
{
int ans=1,base=a;
while(b){
if(b&1) ans=ans*base%mod;
base=base*base%mod;
b>>=1;
}
return ans;
}
int C(int n,int m)
{
if(n<m) return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int Lucas(int n,int m)
{
if(m==0) return 1;
return C(n%mod,m%mod)*Lucas(n/mod,m/mod)%mod;
}
int cal(int n,int m)
{
int ans=0;
for(int i=0;i<=min(n,m)-1;i++) ans=(ans+Lucas(n+m-2*i-2,n-i-1)*Lucas(n+m-2-i,i)%mod)%mod;
return ans;
}
int my_comp(const node &a,const node &b)
{
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
signed main()
{
n=read()+1;m=read()+1;k=read()+1;
for(i=1;i<k;i++) a[i].x=read(),a[i].y=read();
a[k].x=n;a[k].y=m;
for(i=fac[0]=1;i<mod;i++) fac[i]=fac[i-1]*i%mod;
inv[mod-1]=poww(fac[mod-1],mod-2);
for(i=mod-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
sort(a+1,a+k+1,my_comp);
for(i=1;i<=k;i++){
f[i]=cal(a[i].x,a[i].y);
for(j=1;j<i;j++) f[i]=(f[i]-f[j]*cal(a[i].x-a[j].x+1,a[i].y-a[j].y+1)%mod+mod)%mod;
}
printf("%lld
",f[k]);
return 0;
}