HDU-5794 A Simple Chess
题目描述
棋子在\(n*m\)棋盘上可以走马步(横走\(1\)竖走\(2\)或横走\(2\)竖走\(1\))
只能往格子坐标增大的方向跳,且棋盘中会有一些障碍物
问棋子从\((1,1)\)走到\((n,m)\)有几种方案mod(\(110119\))
Input
输入包含多组数据
第一行三个整数\(n,m,r,(1<=n,m<=1e18,0<=r<=100),\)
接下来\(r\)行,每行两个整数\(x,y,(1<=x<=n,1<=y<=m),\)
表示障碍物位置\((1,1)\)上不会有障碍物
Output
对于每组测试数据,输出一行"Case #x: y",x是组数,y是答案
Sample Input
1 1 0
3 3 0
4 4 1
2 1
4 4 1
3 2
7 10 2
1 2
7 1
Sample Output
Case #1: 1
Case #2: 0
Case #3: 2
Case #4: 1
Case #5: 5
容斥\(dp\)+\(lucas\)定理。
题目要我们求出不碰到任意障碍物的方案数,直接求这个方案数是十分麻烦的。
我们可以转换成从起点出发到第\(i\)个障碍物不碰到任意障碍物的方案数。
我们先把在\((n,m)\)处放置一个障碍物,再把所有的障碍物按照\(x\)排序。
我们定义\(dp[i]\)表示从起点出发到第\(i\)个障碍物不碰到任意障碍物的方案数。
\(calc[x1,y1,x2,y2]\)表示从\(x1,y1\)到第\(x2,y2\)个障碍物的方案数。
排序好之后,我们对于第\(i\)个障碍物:
\(dp[i]=calc[1,1,x_i,y_i]-\sum calc(x_j,y_j,x_i,y_i)*dp[j],(j=起点到第i的路径上的障碍)\)。
现在我们就是要求出\(calc(x1,y1,x2,y2)\)。
首先设\(X=x2-x1,Y=y2-y1\),横走\(1\)竖走\(2\)走了\(a\)步,或横走\(2\)竖走\(1\)走了\(b\)步。
所以我们有\(a+2*b=X\)和\(b+2*a=Y\)。
我们可以解出\(a,b\),若\(a,b\)无整数解则无法从\((x1,y1)\)到\((x2,y2)\)。
解出了\(a,b\)后答案就是\(C(a+b,a)\)了。
只是\(a,b\)比较大,而我们发现模数\(110119\)是一个质数。
所以我们可以利用\(lucas\)定理。
\(lucas\)定理:当\(p\)为质数时,\(C(a,b)\%p=C(a/p,b/p)*C(a\%p,b\%p)\%p\)。
代码如下
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
#define int long long
#define reg register
#define Raed Read
#define clr(a,b) memset(a,b,sizeof a)
#define Mod(x) (x>=mod)&&(x-=mod)
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)>(b)?(b):(a))
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])
#pragma GCC target("avx,avx2,sse4.2")
#pragma GCC optimize(3)
inline int Read(void) {
int res=0,f=1;
char c;
while(c=getchar(),c<48||c>57)if(c=='-')f=0;
do res=(res<<3)+(res<<1)+(c^48);
while(c=getchar(),c>=48&&c<=57);
return f?res:-res;
}
template<class T>inline bool Min(T &a, T const&b) {
return a>b?a=b,1:0;
}
template<class T>inline bool Max(T &a, T const&b) {
return a<b?a=b,1:0;
}
const int N=105,M=1e5+5,mod=110119;
bool MOP1;
struct node {
int x,y;
bool operator<(node _)const {
if(x==_.x)return y<_.y;
return x<_.x;
}
} A[N];
int dp[N];
bool MOP2;
int Fac[mod+5],Inv[mod+5],V[mod+5];
int C(int a,int b) {
if(a<b||b<0)return 0;
if(a<mod&&b<mod)return 1ll*Fac[a]*((1ll*Inv[a-b]*Inv[b])%mod)%mod;
return (1ll*C(a%mod,b%mod)*C(a/mod,b/mod)%mod)%mod;
}
inline int calc(int x,int y) {
int tot=x+y;
if(tot%3)return 0;
int a=x-tot/3,b=y-tot/3;
return C(a+b,a);
}
inline void _main(void) {
int Case=0,n,m,k;
Fac[0]=Inv[0]=Fac[1]=V[1]=Inv[1]=1ll;
ret(i,2,mod) {
Fac[i]=(1ll*Fac[i-1]*i)%mod;
V[i]=1ll*(mod-mod/i)*V[mod%i]%mod;
Inv[i]=(1ll*Inv[i-1]*V[i])%mod;
}
while(~scanf("%lld %lld %lld",&n,&m,&k)) {
int f=0;
rep(i,1,k) {
A[i]=(node)<%Read(),Read()%>;
if(A[i].x==n&&A[i].y==m)f=1;
}
A[++k]=(node)<%n,m%>,sort(A+1,A+k+1);
clr(dp,0);
rep(i,1,k) {
dp[i]=calc(A[i].x-1,A[i].y-1);
ret(j,1,i)if(A[j].y<=A[i].y) {
int res=(dp[j]*calc(A[i].x-A[j].x,A[i].y-A[j].y))%mod;
dp[i]=((dp[i]-res)%mod+mod)%mod;
}
}
printf("Case #%lld: %lld\n",++Case,dp[k]);
}
}
signed main() {
_main();
return 0;
}