CF559C Gerald and Giant Chess(计数DP)

CF559C Gerald and Giant Chess
因为h,w范围很大,考虑求不合法方案数,最后用总方案数减去不合法的。
(f(i)) 为从 ((1, 1)) 到第 i 个黑方格且不经过其他黑方格的方案数。
可以得到状态转移方程:(第 i 个黑方格坐标为((i.x, i.y))。)

[egin{aligned} f(i) = C_{i.x + i.y - 2}^{i.x - 1} - sum_{j.x leq i.x, j.y leq i.y} C_{i.x + i.y - j.x - j.y}^{i.x - j.x} cdot f(j) end{aligned} ]

最终答案为:

[egin{aligned} ans = C_{h + w - 2}^{h - 1} - sum_{j = 1}^{n} C_{h + w - j.x - j.y}^{h - j.x} cdot f(j) end{aligned} ]

将输入的黑方格按x坐标为第一关键字,y坐标为第二关键字由小到大排序,组合数用乘法逆元直接算。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long lld;
const int N = 2005;
const int M = 1e5 + 5;
const lld p = 1e9 + 7;
lld fac[M << 1], inv[M << 1], f[N];
int n, h, w;
struct node {
	int x, y;
} a[N];
bool cmp(node a, node b) {
	if(a.x == b.x) return a.y < b.y;
	return a.x < b.x;
}
lld powe(lld a, lld b) {
	lld base = 1;
	while(b) {
		if(b & 1) base = (base * a) % p;
		a = (a * a) % p; b >>= 1;
	}
	return base;
}
lld comb(lld x, lld y) {
	if(x <= 0) return 0;
	lld tmp = (fac[x] * inv[y]) % p;
	tmp = (tmp * inv[x - y]) % p;
	return tmp;
}
int main() {
//	freopen("data.in", "r", stdin);
	scanf("%d%d%d", &h, &w, &n);
	for(int i = 1, x, y; i <= n; i++) {
		scanf("%d%d", &a[i].x, &a[i].y);
	}
	fac[0] = inv[0] = 1;
	for(int i = 1; i <= h + w; i++) {
		fac[i] = (fac[i - 1] * lld(i)) % p;
		inv[i] = powe(fac[i], p - 2);
	}
	sort(a + 1, a + 1 + n, cmp);
	for(int i = 1; i <= n; i++) {
		f[i] = comb(a[i].x + a[i].y - 2, a[i].x - 1);
		for(int j = 1; j < i; j++) {
			if(a[j].x <= a[i].x && a[j].y <= a[i].y) {
				f[i] = f[i] - (comb(a[i].x + a[i].y - a[j].x - a[j].y, a[i].x - a[j].x) * f[j]) % p;
				f[i] = (f[i] + p) % p;
			}
		}
	}
	lld ans = comb(h + w - 2, h - 1);
	for(int i = 1; i <= n; i++) {
		ans = ans - (comb(h + w - a[i].x - a[i].y, h - a[i].x) * f[i]) % p;
		ans = (ans + p) % p;
	}
	printf("%lld", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/mcggvc/p/13871766.html