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;
}
本文详细解析了HDU5794 ASimpleChess问题的求解过程,通过观察棋盘特点引入容斥原理,并利用组合数学方法解决了棋子的路径计数问题。
466

被折叠的 条评论
为什么被折叠?



