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

posted @ 2020-08-30 19:27  xcxc82  阅读(83)  评论(0)    收藏  举报