问题描述
有一个n*m棋盘,一枚棋子要从(1,1)格子移动到(n,m)格子。
该棋子能从坐标为(x1,y1)的格子跳到格子(x2,y2),当且仅当:(x2-x1)^2+(y2-y1)^2=5 x2>x1,y2>y1
棋盘上有r个格子有障碍物,棋子不能落到有障碍物的格子上。
请你计算,该棋子从起点到达终点总共有多少种方案。
输入格式
有若干组数据(<=25组),对于每组数据,格式如下:
一行,三个整数n,m,r
接下来r行,每行两个整数,表示一个有障碍物的格子的坐标。
输出格式
对于每组数据,输出一行,一个整数,表示所求方案数。mod 110119
样例输入 1
1 1 0
3 3 0
4 4 1
2 1
4 4 1
3 2
7 10 2
1 2
7 1
样例输出 1
1
0
2
1
5
样例输入 2
58939239 79926962 4
28255565 36535194
51497377 67322260
48931736 62290476
23383627 30097072
样例输出 2
9061
提示
对于100%的数据:1≤n,m≤10^18 , 0≤r≤100
(1,1)格子不会有障碍。
为了方便,将题目中的 x,y x , y 坐标都减一,变成从 (0,0) ( 0 , 0 ) 到 (n−1,m−1) ( n − 1 , m − 1 )
如果不考虑障碍,那么从 (0,0) ( 0 , 0 ) 到 (x,y) ( x , y ) 的方案数是一个组合数,这个比较好推,就是 C2x−y3x+y3 C x + y 3 2 x − y 3 ,注意到 (x,y) ( x , y ) 可以到达当且仅当 x+y≡0 mod 3 x + y ≡ 0 m o d 3
障碍数比较小,考虑容斥,令 F[i] F [ i ] 表示从 (1,1) ( 1 , 1 ) 出发,经过的第一个障碍是第 i i 个障碍,那么对于每一个在 i i 左下角的障碍 j j ,
总时间复杂度 O(n2×???) O ( n 2 × ? ? ? ) ,还是很快。
代码:
#include
#include
#include
#include
#define N 105
#define ll long long
using namespace std;
const ll mod=110119;
ll n,m,r,F[N],X[N],Y[N],fac[110220],inv[110220];
bool mark[N];
ll C(ll a,ll b)
{
if(areturn 0;
return fac[a]*inv[b]%mod*inv[a-b]%mod;
}
ll Lucas(ll a,ll b)
{
if(areturn 0;
if(a<0||b<0)return 0;
if(b==0||a==0)return 1;
return Lucas(a/mod,b/mod)*C(a%mod,b%mod)%mod;
}
ll DP(ll p)
{
ll i,j;
if(F[p]!=-1)return F[p];
if(mark[p])return F[p]=0;
F[p]=Lucas((X[p]+Y[p])/3,(2*Y[p]-X[p])/3);
for(i=1;i<=r;i++)
if(X[i]if(mark[i])continue;
F[p]-=DP(i)*Lucas((X[p]+Y[p]-X[i]-Y[i])/3,(2*(Y[p]-Y[i])-X[p]+X[i])/3)%mod;
F[p]%=mod;
}
F[p]+=mod;F[p]%=mod;
return F[p];
}
int main()
{
ll i,j,k,x,y;bool f;
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(i=2;i<=mod;i++)
{
fac[i]=fac[i-1]*i%mod;
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
for(i=2;i<=mod;i++)inv[i]=inv[i]*inv[i-1]%mod;
while(scanf("%lld%lld%lld",&n,&m,&r)!=EOF)
{
f=0;
for(i=1;i<=r;i++)
{
scanf("%lld%lld",&X[i],&Y[i]);
if(X[i]==n&&Y[i]==m)f=1;
}
if(f){puts("0");continue;}
for(i=1;i<=r;i++)X[i]--,Y[i]--;
X[++r]=n-1;Y[r]=m-1;
for(i=1;i<=r;i++)mark[i]=(X[i]+Y[i])%3!=0;
memset(F,-1,sizeof(F));
printf("%lld\n",DP(r));
}
}