HDU5794 A Simple Chess

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5794


【题意】给定一个n*m的棋盘,一个棋子要从(1,1)移动到(n,m),棋子只能向右下如同象棋中马一样按照日字移动,在棋盘上存在r个障碍物,棋子不能落在障碍物上,问有几种走法。


【分析】简单打表可以看出不存在障碍物时棋子走法是一个斜着的杨辉三角。由于每次棋子移动的横纵坐标差值和一定是3,所以棋子所在的层数n等于(x+y)/3。棋子从(1,1)点开始移动,只有满足(x+y)%3==2 && x>=n+1 && y>=n+1的点棋子才能到达。以一条边界线上的点看,所有的x=n+1,那么同层棋子每向里移动一格,x+1,y-1.于是可以计算得到棋子在n层的位置m=x-n-1。根据点所在的层数n和位置m,可以利用组合数快速计算出到达该点的走法为C(n,m)种。本题中由于棋盘界限很大,但取模数为110119,刚好满足鲁卡斯定理的要求,可以利用鲁卡斯定理快速求大组合数取模的结果。接着分析障碍物造成的影响。首先排除不影响走法的障碍物。以s_obs[i]表示起点到障碍物i所在点的走法,obs_e[i]表示障碍物i所在点到(n,m)的走法。那么减少的走法就是s_obs[i]*obs_e[i]。由于多个障碍物会相互影响,直接减会多减了,需要容斥处理相互影响的障碍物(PS:比赛时怎么算都觉得容斥会超时ORZ。。。虽然最后没办法硬着头皮上了。。竟然过了)


【代码】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define LL long long
const int mod=110119;
struct Node{
    LL x,y;
}obs[110];
bool cmp(Node n1,Node n2){
    return (n1.x+n1.y)/3<(n2.x+n2.y)/3;
}
LL waynum[110][110];
LL fac[120010];
LL s_obs[110];
LL obs_e[110];
LL ans;
int cnt=0;
int numtag[110];
void Extgcd(LL a,LL b,LL &x,LL &y){
    if(b==0){
        x=1;
        y=0;
        return;
    }
    Extgcd(b,a%b,x,y);
    LL tmp=x;
    x=y;
    y=tmp-(a/b)*y;
}
LL C(LL n,LL m){
    if(m>n)    return 0;
    if(n==m)    return 1;
    LL tt,x,y;
    tt=m;
    m=fac[n];
    n=(fac[tt]*fac[n-tt]%mod);
    Extgcd(n,mod,x,y);
    x*=m;
    x%=mod;
    if(x<0)    x+=mod;
    return x;
}
LL lucas(LL n,LL m){
    if(m > n / 2)    m = n - m;
    if(m==0)    return 1;
    return C(n%mod,m%mod)*lucas(n/mod,m/mod)%mod;
}
LL cal(LL x,LL y){
    LL n=(x+y)/3;
    LL m=y-n-1;
    return lucas(n,m);
}
bool canbe(LL x,LL y){
    if(x<0 || y<0 || (x+y)%3!=2)
        return false;
    LL n=(x+y)/3;
    if(x<n+1 || y<n+1)
        return false;
    return true;
}
void dfs(int pre,int pos,int d,LL tmp){
    if(tmp == 0)    return;
    if(d&1)
        ans=(ans-tmp*obs_e[pos] % mod)%mod;
    else
        ans=(ans+ tmp * obs_e[pos] % mod)%mod;
    for(int i=pos+1;i<cnt;i++)
        dfs(pos,i,d+1,tmp*waynum[pos][i] % mod);
}
void init(){
    fac[0]=fac[1]=1;
    for(int i=2;i<120000;++i)
        fac[i]=(fac[i-1]*i)%mod;
}
int main(){
    init();
    int r,cas=1;
    LL n,m,x,y;
    while(scanf("%I64d %I64d %d",&n,&m,&r)!=EOF)
    {
        memset(s_obs,0,sizeof(s_obs));
        memset(obs_e,0,sizeof(obs_e));
        cnt = 0;
        for(int i=0;i<r;++i)
        {
            scanf("%I64d %I64d",&x,&y);
            if(canbe(x,y))
            {
                obs[cnt].x=x;
                obs[cnt++].y=y;
            }
        }
        LL tmpx,tmpy;
        LL cn=(n+m)/3,cm;
        if(!canbe(n,m))
        {
            printf("Case #%d: 0\n",cas++);
            continue;
        }
        sort(obs,obs+cnt,cmp);
        cm=n-cn-1;
        ans=lucas(cn,cm);
        for(int i=0;i<cnt;++i)
        {
            s_obs[i]=cal(obs[i].x,obs[i].y);
            tmpx=n-obs[i].x+1;
            tmpy=m-obs[i].y+1;
            if(canbe(tmpx,tmpy))
                obs_e[i]=cal(tmpx,tmpy);
               else
                   obs_e[i] = 0;
            for(int j=i+1;j<cnt;++j){
                tmpx=obs[j].x-obs[i].x+1;
                tmpy=obs[j].y-obs[i].y+1;
                if(canbe(tmpx,tmpy)){
                    waynum[i][j]=cal(tmpx,tmpy);
                }
                else
                    waynum[i][j]=0;
            }
        }
        for(int i=0;i<cnt;++i)
            dfs(-1,i,1,s_obs[i]);
        ans=(ans+mod)%mod;
        printf("Case #%d: %I64d\n",cas++,ans);
    }
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值