hdu 5794 A Simple Chess(Lucas 定理)

A Simple Chess

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 87    Accepted Submission(s): 15


Problem Description
There is a n×m board, a chess want to go to the position
(n,m) from the position (1,1) .
The chess is able to go to position (x2,y2) from the position (x1,y1) , only and if only x1,y1,x2,y2 is satisfied that (x2x1)2+(y2y1)2=5, x2>x1, y2>y1 .
Unfortunately, there are some obstacles on the board. And the chess never can stay on the grid where has a obstacle.
I want you to tell me, There are how may ways the chess can achieve its goal.
 

Input
The input consists of multiple test cases.
For each test case:
The first line is three integers, n,m,r,(1n,m1018,0r100) , denoting the height of the board, the weight of the board, and the number of the obstacles on the board.
Then follow r lines, each lines have two integers, x,y(1xn,1ym) , denoting the position of the obstacles. please note there aren't never a obstacles at position (1,1) .
 

Output
For each test case,output a single line "Case #x: y", where x is the case number, starting from 1. And y is the answer after module 110119 .
 

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
 

题意: 题目意思抽象出来就是一个n*m的矩阵,你从左上走到右下有多少种方式,只能走日字型,走的格子必须是右下方,而且规定一些方格是不能走的。


思路:如果不考虑不能走的方格,稍微自己化个图就能很清楚的发现规律;

定义最左上点为(0,0);

可知,只有格点横纵坐标和为3的倍数的某些点才能够到达。

相同横纵坐标和的格点的方案数是按照排列数的值分布的。



这个规律找出来之后就相对好做了。

有些点是不能走的。

那就计算这些点对到达末点的贡献度,减去就好了。


代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=110119;
struct node
{
    ll x,y,w;
    bool operator<(const node&p)const
    {
        if(x!=p.x)
            return x<p.x;
        return y<p.y;
    }
}A[111];

ll PowMod(ll a,ll b,ll MOD){
    ll ret=1;
    while(b){
        if(b&1) ret=(ret*a)%MOD;
        a=(a*a)%MOD;
        b>>=1;
    }
    return ret;
}
ll fac[110219];
ll Get_Fact(ll p)
{
    fac[0]=1;
    for(int i=1;i<=p;i++)
        fac[i]=(fac[i-1]*i)%p;
}
ll Lucas(ll n,ll m,ll p){
    ll ret=1;
    while(n&&m){
        ll a=n%p,b=m%p;
        if(a<b) return 0;
        ret=(ret*fac[a]*PowMod(fac[b]*fac[a-b]%p,p-2,p))%p;
        n/=p;
        m/=p;
    }
    return ret;
}
int main()
{
    ll n,m;
    int r,t=0;
    Get_Fact(mod);
    while(scanf("%I64d%I64d%d",&n,&m,&r)!=EOF)
    {
        n--,m--;
        int len=0;
        for(int i=0;i<r;i++)
        {
            ll a,b;
            scanf("%I64d%I64d",&a,&b);
            a--;
            b--;
            if((a+b)%3)
                continue;
            ll cc=a+b;
            cc/=3;

            if(b<cc||b>cc*2+1)
                continue;
            A[len].x=a;A[len].y=b;A[len].w=0;
            len++;
        }
        sort(A,A+len);
        ll cc=n+m;

        if(cc%3||m<cc/3||m>cc/3*2+1)
        {
            printf("Case #%d: 0\n",++t);
            continue;
        }
        cc/=3;
        ll ans=Lucas(cc,m-cc,mod);
        for(int i=0;i<len;i++)
        {
            ll x=A[i].x,y=A[i].y;
             cc=x+y;
            if(cc%3||y<cc/3||y>cc/3*2+1)
                continue;
            cc/=3;
            ll res=Lucas(cc,y-cc,mod);
            A[i].w=res;
        }
        for(int i=0;i<len;i++)
        {
            for(int j=i+1;j<len;j++)
            {
                ll x=A[j].x-A[i].x,y=A[j].y-A[i].y;
                cc=x+y;
                if(cc%3||y<cc/3||y>cc/3*2+1)
                    continue;
                cc/=3;
                ll res=Lucas(cc,y-cc,mod);
                A[j].w-=A[i].w*res%mod;
                A[j].w=(A[j].w%mod+mod)%mod;
            }
            ll x=n-A[i].x,y=m-A[i].y;
            cc=x+y;
            if(cc%3||y<cc/3||y>cc/3*2+1)
                continue;
            cc/=3;
            ll res=Lucas(cc,y-cc,mod);
            ans-=res*A[i].w%mod;
            ans=(ans%mod+mod)%mod;
        }
        printf("Case #%d: %I64d\n",++t,ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值