P2051 [AHOI2009]中国象棋
大致题意
给一个\(n×m\)的棋盘,在上面放若干个炮,求有多少种放置方法可以使没有一个炮可以攻击到另一个炮
\(1≤n,m≤100\)
分析
观察可以发现,每行每列最多只能放两个炮,一列能放炮的位置和之前该列放过的炮的个数有关,且两列棋子数相等的列是等价的,考虑设\(f(i,j,k)\)表示前\(i\)行中,有\(j\)列放了1个棋子,\(k\)列放了两个棋子
分类讨论每种情况,有转移:
\(\begin{cases}f(i+1,j,k) += f(i,j,k)&(一个都不放)\\f(i+1,j-1,k+1) +=f(i,j,k)×j&(放一个,放在之前已经放了一个的列上)\\f(i+1,j+1,k) +=f(i,j,k)×(m-j-k)&(放一个,放在之前一个都没有的列上)\\f(i+1,j+1,k) +=f(i,j,k)×C_{m-j-k}^2&(放两个,都放在之前一个都没有的列上)\\f(i+1,j-2,k+2) +=f(i,j,k)×C_j^2&(放两个,都放在之前已经放了一个的列上)\\f(i+1,j,k+1) +=f(i,j,k)×(m-j-k)×j&(放两个,一个放在之前已经放了一个的列上,一个放在之前一个都没有的列上)\end{cases}\)
\(code:\)
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 110;
#define int long long
#define mo 9999973
#define C(x) (((x)*(x-1)/2)%mo)
int f[MAXN][MAXN][MAXN];
int n,m;
signed main(){
cin>>n>>m;
f[0][0][0] = 1;
for(int i=0;i<=n;i++){
for(int j=0;j<=m;j++){
for(int k=0;k+j<=m;k++){
if(f[i][j][k]){
f[i+1][j][k] = (f[i][j][k]+f[i+1][j][k])%mo;
if(j>=1) f[i+1][j-1][k+1] = (f[i+1][j-1][k+1]+f[i][j][k]*j)%mo;
if(j>=1&&(m-j-k)>0) f[i+1][j][k+1] =(f[i+1][j][k+1]+f[i][j][k]*(m-j-k)*j)%mo;
if((m-j-k)>0) f[i+1][j+1][k] = (f[i+1][j+1][k]+f[i][j][k]*(m-j-k))%mo;
if(j>=2) f[i+1][j-2][k+2] = (f[i+1][j-2][k+2]+f[i][j][k]*C(j))%mo;
if((m-j-k)>1) f[i+1][j+2][k] = (f[i+1][j+2][k]+f[i][j][k]*C(m-j-k))%mo;
}
}
}
}
int ans = 0;
for(int i=0;i<=m;i++){
for(int j=0;j+i<=m;j++){
ans = (ans+f[n][i][j])%mo;
}
}
cout<<ans%mo;
}
重构于2021/6/29

浙公网安备 33010602011771号