[AHOI2009]中国象棋

[AHOI2009]中国象棋

题意:

在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。

这题很久以前就见过了, 但当时dp太菜, 一看计数就不敢做了. 现在一看, 不是水题吗

显然的思路应该是记录一下每行放了几个炮, 但行太多肯定记录不下, 由炮的性质可知一行最多2个炮, 于是我们有了正确的解法

\(f[i][j][k]\)表示前\(i\)列有\(j\)行没放炮, \(k\)列放了1个炮的方案数, 然后枚举当前列放几个炮, 注意放两个的时候要用组合数转移

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int P = 9999973;

template <typename T>
void read(T &x) {
    x = 0; bool f = 0;
    char c = getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
    for (;isdigit(c);c=getchar()) x=x*10+(c^48);
    if (f) x=-x;
}

template <typename T>
void write(T x) {
    if (x < 0) putchar('-'), x = -x;
    if (x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

int n, m;
const int N = 200;
ll f[N][N][N]; // 0 1  

inline void add(ll &x, ll y) {
	(x += y) %= P;
}

ll ans = 0;
int main() {
	read(n), read(m);
	f[0][n][0] = 1;
	for (int i = 1;i <= m; i++) {
		for (int j = 0;j <= n; j++) {
			for (int k = 0;k + j <= n; k++) {
				ll x = 0;
				add(x, f[i-1][j][k]);
				if (k) add(x, (j + 1) * f[i-1][j+1][k-1]);
				add(x, (k + 1) * f[i-1][j][k+1]);
				add(x, (ll)(j + 1) * k % P * f[i-1][j+1][k]);
				if (k >= 2) add(x, (ll)(j + 2) * (j + 1) / 2 % P * f[i-1][j+2][k-2]);
				add(x, (ll)(k + 2) * (k + 1) / 2 % P * f[i-1][j][k+2]);
				f[i][j][k] = x;
			}
		}
	}
	for (int i = 0;i <= n; i++) 
		for (int j = 0;j + i <= n; j++) 
			(ans += f[m][i][j]) %= P;
	cout << ans << endl;
	return 0;
}
posted @ 2020-01-27 12:26  Hs-black  阅读(130)  评论(0编辑  收藏  举报