给你一个n×m的棋盘,给出r个点,表示棋盘中有r个坏点不能走,问马从(1,1)走到(n,m)有多少中走法,每一步必须是往横纵坐标不减小的方向走。
如果棋盘上没有坏点,那从(1,1)走到(n,m)满足方程1+2x+y=n && 1+2y+x=m ,如果方程的两个解大于等于0,那就可达,否则不可达。如果可达,方案数就是组合数C(x+y,x)。
如果棋盘上有一个坏点,方案数就是(没有坏点的方案数)-((1,1)到坏点的方案数×坏点到(n,m)的方案数);
如果有 多个坏点,设置一个数组dp,dp【i】最终结果表示不经过任何坏点 而到达坏点i的方案数,把终点也算到坏点里,dp【r】就是结果;
dp【i】的初始值为起点到该点的总方案数(总方案数指 假设棋盘上没有坏点的方案数),假设坏点i的坐标为xi,yi,如果区域x<=xi && y<=yi内没有坏点,那么dp【i】=起点到坏点i的总方案数(就是初值);否则对于区域x<=xi && y<=yi内的所有坏点j,dp【i】=dp【i】-∑(dp【j】×坏点j到坏点i的总方案数),(中间有点容斥的意思,减去的那些路径肯定没有完全重合的,这一点是比较难理清的,因为路径中第一个遇见的坏点不同,所以坑定不会重复,不会多减);O(r^2)的dp,r最大100,咩问题。
Lucas效率不够高的话会TLE;
用了Lucas,结果是取过模的,所以减的时候要先加MOD再减,再取余就行了;
这是我在杭电做过的第一道用%lld WA,%I64d AC的题,以前即使提示用%I64d也没用过,用%lld都能过,这次信了。
需要注意的几组数据:
1 1 0
结果是1
2 3 1
2 3
结果是0
#include <algorithm>
#include <iostream>
#include <numeric>
#include <cstring>
#include <iomanip>
#include <string>
#include <vector>
#include <cstdio>
#include <queue>
#include <stack>
#include <cmath>
#include <map>
#include <set>
#define DEBUG
#define LL long long
#define maxr 1100
#define maxn 210000
#define MOD 110119
using namespace std;
struct node
{
LL x,y;
friend bool operator<(node a,node b)
{
if(a.x!=b.x)
return a.x<b.x;
else
return a.y<b.y;
}
}dot[maxr];
LL dp[maxr];
LL fac[maxn];
void LucasIni()
{
fac[0]=1LL;
for(int i=1;i<=MOD+10;++i)
fac[i]=fac[i-1]*i % MOD;
}
LL qpow(LL a,LL b)
{
LL temp=a%MOD;
LL ans=1;
while(b)
{
if(b&1) ans=ans*temp % MOD;
temp=temp * temp %MOD;
b>>=1;
}
return ans;
}
LL _C(LL n, LL m)
{
if(m>n )
return 0;
return fac[n]*qpow(fac[m]*fac[n-m],MOD-2)%MOD;
}
LL Lucas(LL n,LL m)
{
if(m==0)
return 1;
else
return (_C(n%MOD,m%MOD) * Lucas(n/MOD,m/MOD)) % MOD;
}
LL get(LL n,LL m)
{
if(n==1 && m==1)
return 1;
LL x=(n-m+n-1)/3;
LL y=(m-n+m-1)/3;
if((n+m-2)%3!=0 || n==1 || m==1 || x<0 || y<0 )
return 0;
return Lucas(x+y,y);
}
int main()
{
LL n,m;
int r,I=0;
LucasIni();
// cout<<Lucas(0,0);
while(scanf("%I64d%I64d%d",&n,&m,&r)!=EOF)
{
for(int i=0;i<r;++i)
{
scanf("%I64d%I64d",&dot[i].x,&dot[i].y);
}
dot[r].x=n,dot[r].y=m;
sort(dot,dot+r+1);
memset(dp,0,sizeof(dp));
for(int i=0;i<=r;++i)
{
dp[i]=get(dot[i].x,dot[i].y);
}
for(int i=0;i<=r;++i)
{
for(int j=i-1;j>=0;--j)
{
if(dot[i].x>=dot[j].x && dot[i].y>=dot[j].y)
dp[i]=(dp[i]+MOD-dp[j]*get(dot[i].x-dot[j].x+1,dot[i].y-dot[j].y+1)%MOD)%MOD;
}
}
printf("Case #%d: %I64d\n",++I,dp[r]);
}
}