(多校第六场1002)HDU5794 A Simple Chess(Lucas+dp)

给你一个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]);
	}
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值