2016 多校 Multi-University Training Contest 6 A Simple Chess

hdu 5794 A Simple Chess

题意很简单,给你一个棋盘,和只能像国际象棋马的走法的棋子,棋子只能从11开始走 只能向斜下方走,棋盘上有写位置不能走,问你最后走到终点有几种走法。

凡事都从观察开始,棋盘很大,long long才能存,但是不能走的位置很少只有100,再结合题意,很明显的容斥,之后我们画图,找一些简单的情况分析,发现每个能去的位置都在一些平行的直线上,然后我们在把前几排的可行位置的方案数出来,发现刚好是一个斜过来的杨辉三角,这就意味着,这玩意的走法刚好是一个组合数,和普通的棋子走法类似,那么我们其实可以把他转换为普通的横着走和竖着走的棋子,将整个棋盘的坐标都进行转化,其实就是使坐标可以直接拿来算组合数。

到了这个时候,基本做法就已经出来了,容斥,组合数,取模,很明显,就是容斥+lucas。统计时,不重不漏的常用方法,就是有序化思想,这里也是一样,我们先定义一个dp

dp[i]表示走过第I个障碍,且不经过之前的障碍物的走法,这样子我们只需要对障碍物进行双关键字排序,按照顺序计算即可,具体转移方程见代码。

ps:这题坑了我很久,因为输入时就可以确定该棋盘是否无解,我在确认无解时直接跳出了,这样子不行,因为后面会有障碍物坐标的输入,而程序把他当做下一组数据的输入了,导致各种错.....


#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

typedef long long LL;
bool flag;
int sums;
int cnt;
LL n, m;
LL h, w;
LL a, b;
LL fac[1000000];
const LL MOD = 110119;

struct node
{
	LL x, y;
	LL sum;
	bool operator < (const node& n) const
	{
		return x < n.x || (x == n.x && y < n.y);
	}
}pos[105];

void init()
{
	fac[0] = 1;
	for (LL i = 1; i <= MOD; i++)
		fac[i] = (fac[i - 1] * i) % MOD;
}

LL PowerMod(LL a, LL b, LL c)
{
	LL ans = 1;
	a %= c;
	while (b)
	{
		if (b & 1)
			ans = (ans*a) % c;
		b >>= 1;
		a = (a*a) % c;
	}
	return ans;
}

LL comb(LL a, LL b, LL MOD)
{
	if (b > a) return 0;
	return fac[a] * PowerMod(fac[b] * fac[a - b], MOD- 2, MOD) % MOD;
}

LL lucas(LL n, LL m, LL MOD)
{
	LL ans = 1;
	while (n && m)
	{
		ans = (ans * comb(n%MOD, m%MOD, MOD)) % MOD;
		n /= MOD;
		m /= MOD;
	}
	return ans;
}

int main(void)
{
	int cas = 1;
	init();
	while (~scanf("%lld%lld%d", &n, &m, &sums))
	{
		LL step;
		cnt = 0; 
		flag = true;
		memset(pos, 0, sizeof pos);
		if ((n + m - 2) % 3 != 0) flag = false;
		if (flag)
		{
			step = (n + m - 2) / 3 + 2;
			h = n - step + 2;
			w = step - h;
			if (h <= 0 || h >= step) flag = false;
		}
		LL tmp, ta, tb;
		for (int i = 0; i < sums; i++)
		{
			scanf("%lld%lld", &a, &b);
			if (a == n && b == m)
			{
				flag = false;
				continue;
			}
			if (!flag) continue;
			if ((a + b - 2) % 3 != 0) continue;
			tmp = (a + b - 2) / 3 + 2;
			ta = a - tmp + 2;
			tb = tmp - ta;
			if (ta <= 0 || ta >= tmp) continue;
			//if (ta > h || tb > w) continue;
			pos[cnt].x = ta;
			pos[cnt].y = tb;
			if (ta == 1 || tb == 1) pos[cnt++].sum = 1;
			else pos[cnt++].sum = lucas(ta + tb - 2, ta - 1, MOD) % MOD;
		}
		if (!flag)
		{
			printf("Case #%d: 0\n", cas++);
			continue;
		}
		sort(pos, pos + cnt);
		pos[cnt].x = h;
		pos[cnt].y = w;
		pos[cnt].sum = lucas(h + w - 2, h - 1, MOD) % MOD;
		for (int i = 0; i <= cnt; i++)
		{
			for (int j = 0; j < i; j++)
			{
				if (pos[j].y > pos[i].y) continue;
				LL dx = pos[i].x - pos[j].x;
				LL dy = pos[i].y - pos[j].y;
				pos[i].sum = (pos[i].sum -  (pos[j].sum * lucas(dx + dy, dy, MOD) % MOD) + MOD) % MOD;
			}
		}
		printf("Case #%d: %lld\n", cas++, pos[cnt].sum);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值