hdu5794A Simple Chess(组合数学)

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5794

题目大意:

给一个n*m的棋盘,要求从(1,1)走到(n,m),只能以日字的形式走过去,然后不能经过某些坏点,问有几种走法。

范围:

n,m<=10^18,mod=110119。

思路:

和上一题的模型很相似。具体见http://blog.csdn.net/aaaaacmer/article/details/52132704

但是要进行转化。转换方式比较神奇,就是先将x,y坐标都减1,然后将X=-1/3x+2/3y,Y=2/3x-1/3y。转化以后可以知道,如果新的坐标不为非负整数,就是不可到达的点,然后利用模型直接计算。因为n,m比较大,而mod比较小,所以可以用卢卡斯定理解决。

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<math.h>
#define eps 0.001
#define mod 110119
#define ll __int64
using namespace std;
ll f[200];
struct node{
	ll x,y;
}p[200];

bool cmp(node a,node b){
	if(a.x!=b.x)return a.x<b.x;
	return a.y<b.y;
}
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[110121];    
ll Get_Fact(ll p){    
    fac[0]=1;    
    for(ll 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 i,j,k,r,tt;
	int icase=0;
	Get_Fact(mod);
	while(~scanf("%I64d%I64d%d",&n,&m,&r)){
		icase++;
		tt=0;
		ll xx,yy;
		for(i=1;i<=r;i++){
			scanf("%I64d%I64d",&xx,&yy);
			xx--;
			yy--;
			ll x,y;
			x=-xx+2*yy;
			y=2*xx-1*yy;
			if(x%3==0&&y%3==0&&x>=0&&y>=0){
				p[++tt].x=x/3;
				p[tt].y=y/3;
			}
		}
		printf("Case #%d: ",icase);
		ll edx,edy;
		edx=-(n-1)+2*(m-1);
		edy=2*(n-1)-1*(m-1);
		if(!(edx%3==0&&edy%3==0&&edx>=0&&edy>=0)){
			printf("0\n");
			continue;
		}
		sort(p+1,p+1+tt,cmp);
		memset(f,0,sizeof(f));
		p[tt+1].x=edx/3;
		p[tt+1].y=edy/3;
		for(i=1;i<=tt+1;i++){
			ll dx,dy;
			dx=p[i].x;
			dy=p[i].y;
			ll tmp=Lucas(dx+dy,dx,mod);
			for(j=1;j<i;j++){
				if(p[i].x>=p[j].x&&p[i].y>=p[j].y){
					ll dxx,dyy;
					dxx=p[i].x-p[j].x;
					dyy=p[i].y-p[j].y;
					tmp-=f[j]*Lucas(dxx+dyy,dxx,mod)%mod;
					tmp=(tmp+mod)%mod;		
				}
			
			}	
			f[i]=(tmp+mod)%mod;
		}
		printf("%I64d\n",(f[tt+1]+mod)%mod);
	}	
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值