您的位置:首页 > 其它

HDU 5794 A Simple Chess (容斥原理+Lucas定理+dp)

2018-03-08 19:11 543 查看

A Simple Chess

问题描述

有一个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行,每行两个整数,表示一个有障碍物的格子的坐标。

<
4000
strong>输出格式[/b]

对于每组数据,输出一行,一个整数,表示所求方案数。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,yx,y坐标都减一,变成从(0,0)(0,0)到(n−1,m−1)(n−1,m−1)

如果不考虑障碍,那么从(0,0)(0,0)到(x,y)(x,y)的方案数是一个组合数,这个比较好推,就是C2x−y3x+y3Cx+y32x−y3,注意到(x,y)(x,y)可以到达当且仅当x+y≡0 mod 3x+y≡0 mod 3

障碍数比较小,考虑容斥,令F[i]F[i]表示从(1,1)(1,1)出发,经过的第一个障碍是第ii个障碍,那么对于每一个在ii左下角的障碍jj,F[i]=C2xi−yi3xi+yi3−∑jF[j]×C2tx−ty3tx+ty3,tx=xi−xj,ty=yi−yjF[i]=Cxi+yi32xi−yi3−∑jF[j]×Ctx+ty32tx−ty3,tx=xi−xj,ty=yi−yj

直接写个记忆化搜索,暴力枚举那些点在左下方就行了。

组合数用LucasLucas处理,预处理阶乘和逆元,注意到2x−y2x−y可能小于00,此时也意味着走不到,因此要返回00

另外还需要注意如果终点是障碍,那么答案也是00

总时间复杂度O(n2×???)O(n2×???),还是很快。

代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 105
#define ll long long
using namespace std;
const ll mod=110119;
ll n,m,r,F
,X
,Y
,fac[110220],inv[110220];
bool mark
;
ll C(ll a,ll b)
{
if(a<b)return 0;
return fac[a]*inv[b]%mod*inv[a-b]%mod;
}
ll Lucas(ll a,ll b)
{
if(a<b)return 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]<X[p]&&Y[i]<Y[p])
{
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));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: