「 题解 」HDU5794 A Simple Chess


小兔的话

欢迎大家在评论区留言哦~


简单题意

有一个 \(n \times m\) 的棋盘,规定左上角的方格的坐标为 \((1, 1)\),右下角的方格的坐标为 \((n, m)\),其中有 \(r\) 个障碍物,每个障碍物的坐标是 \((x_i, y_i)\)
当一颗棋子的坐标为 \((x, y)\) 时,下一步可以走到 \((x + 1, y + 2)\) 或者 \((x + 2, y + 1)\)
现在 \((1, 1)\) 上有一颗棋子,请求出它走到 \((n, m)\) 不经过障碍物的方案数 \((\)\(110119\) 取模 \()\),特别地,如果方格 \((1, 1)\) 上有障碍物,方案数应该为 \(0\)

数据范围

  • \(1 \leq n, m \leq 10^{18}\)\(1 \leq r \leq 100\)
  • \(\forall \ 1 \leq i \leq r\)\(1 \leq x_i \leq n, 1 \leq y_i \leq m\)

题目解析

  • \((x, y)\) 走到 \((x + 1, y + 2)\) 记为类型 \(1\)
  • \((x, y)\) 走到 \((x + 2, y + 1)\) 记为类型 \(2\)

考虑一个格子向右走了 \(N\) 个单位长度,向下走了 \(M\) 个单位长度的方案数,记为 \(f(N, M)\)

设类型 \(1\) 的步骤有 \(x\) 个,类型 \(2\) 的步骤有 \(y\) 步,那么方案数就是 \(\dbinom {x + y} {x}\)(由于 \(n, m\) 可能很大,所以 \(x, y\) 也可能很大,因此组合数对 \(110119\) 取模的值不能使用常规做法求解,可以使用 \(\texttt {Lucas}\) 定理)。
由于棋盘的限制,\(x, y\) 需要满足一定的条件:

  • \(x, y\) 都是 非负整数
  • \(x + 2y = N\)
  • \(2x + y = M\)

\(\therefore x = \dfrac {2M - N} {3}\)\(y = \dfrac {2N - M} {3}\)

只需要判断 \(x, y\) 是否都是非负整数即可。

对于 \((1, 1)\) 走到 \((n, m)\) 不经过障碍物的方案数,可以用总方案减去经过障碍物的方案数。
总方案为 \(f(n - 1, m - 1)\),只需要考虑经过障碍物的方案数即可。
考虑到达每个障碍物之前没有经过其它的障碍物的方案数 \(dp_i\)\(dp\) 的和就是经过障碍物的方案数了,其对答案的贡献就是 \(dp_i \times f(n - x_i, m - y_i)\)

对于第 \(i\) 障碍物,若到达它之前还经过了第 \(j\) 个障碍物,那么一定满足 \(x_j < x_i\) 并且 \(y_j < y_i\)
所以 \(dp_i = f(x_i - 1, y_i - 1) \times \sum \limits _ {x_j < x_i \land y_j < y_i} dp_j\)
求解过程中可以先将障碍物按照 \((x, y)\) 排序,这样就只用算 \(1 \leq j \leq i - 1\)\(i\) 的影响了。


代码

#include 
#include 
using namespace std;

#define int long long
#define uint unsigned int

int rint()
{
	int x = 0, fx = 1; char c = getchar();
	while (c < '0' || c > '9') { fx ^= (c == '-'); c = getchar(); }
	while ('0' <= c && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); }
	if (!fx) return -x;
	return x;
}

int qpow(int a, int b, int mod)
{
	int res = 1 % mod; a %= mod;
	while (b > 0)
	{
		if (b & 1) res = res * a % mod;
		a = a * a % mod; b >>= 1;
	}
	return res;
}

const int mod = 110119;
const int mx = mod - 1;
const int MAX_r = 100;

int dp[MAX_r + 5];
int f[mx + 5], inv[mx + 5];

struct node
{
	int x, y;
	node () {}
	bool operator < (const node &rhs) const
	{
		if (x != rhs.x) return x < rhs.x;
		return y < rhs.y;
	}
} p[MAX_r + 5];

void init()
{
	f[0] = 1; for (int i = 1; i <= mx; i++) f[i] = f[i - 1] * i % mod;
	inv[mx] = qpow(f[mx], mod - 2, mod); for (int i = mx; i >= 1; i--) inv[i - 1] = inv[i] * i % mod;
}

int A(int N, int M) { return (N < M || M < 0) ? 0 : (f[N] * inv[N - M] % mod); }
int C(int N, int M) { return A(N, M) * inv[M] % mod; }

int Lucas(int N, int M)
{
	int res = 1;
	while (N || M)
	{
		res = res * C(N % mod, M % mod) % mod;
		if (!res) return 0;
		N /= mod; M /= mod;
	}
	return res;
}

int calc(int N, int M) // 计算 f(N, M) 
{
	int x = 2 * N - M, y = 2 * M - N;
	if (x % 3 || y % 3 || x < 0 || y < 0) return 0;
	return Lucas(x / 3 + y / 3, x / 3);
}

signed main()
{
	int Case = 0, n, m, r; init();
	while (scanf("%lld %lld %lld", &n, &m, &r) != EOF)
	{
		for (int i = 1; i <= r; i++)
			p[i].x = rint(), p[i].y = rint();
		sort(p + 1, p + r + 1);
		int res = calc(n - 1, m - 1);
		for (int i = 1; i <= r; i++)
		{
			dp[i] = calc(p[i].x - 1, p[i].y - 1);
			for (int j = 0; j < i; j++)
				if (p[j].x < p[i].x && p[j].y < p[i].y) dp[i] = (dp[i] - dp[j] * calc(p[i].x - p[j].x, p[i].y - p[j].y) % mod + mod) % mod;
			res = (res - dp[i] * calc(n - p[i].x, m - p[i].y) % mod + mod) % mod;
		}
		printf("Case #%lld: %lld\n", ++Case, res);
	}
	return 0;
}