[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;
}