[AHOI2009]中国象棋

题目描述

这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。大家肯定很清楚,在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好 有一个棋子。你也来和小可可一起锻炼一下思维吧!

输入输出格式

输入格式:

一行包含两个整数N,M,之间由一个空格隔开。

输出格式:

总共的方案数,由于该值可能很大,只需给出方案数模9999973的结果。

输入输出样例

输入样例#1:

1 3

输出样例#1:

7

说明

样例说明

除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有222-1=7种方案。

数据范围

100%的数据中N和M均不超过100

50%的数据中N和M至少有一个数不超过8

30%的数据中N和M均不超过6

一开始看以为是状压

但数据范围显然不支持状压==

膜题解后发现棋子的放置顺序对答案煤油影响

所以只需要考虑放0,1,2枚棋子的有几列而不需要考虑其具体位置

f[i][j][k] 表示前i行有j列有1个棋子,k列有2个棋子

所以有m - j - k列放了0个棋子

假设这一行煤油放棋子

暴力转移

假设这一行放1枚棋子

可以使有0个棋子的列变为1个

可以使有1个棋子的列变为2个

如果这一行放2枚棋子

可以使两个有1个棋子的列变成2个.

也可以使两个有0个棋子的列变成1个 .

也可以使一个有0个棋子的列和一个有1个棋子的列变成一个有1个棋子的列和一个有2个棋子的列

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
# define LL long long
const int M = 105 ;
const int mod = 9999973 ;
using namespace std ;
int n , m ;
LL f[M][M][M] , Ans ; 
inline int C(int n , int m) {
	return n * (n - 1) / 2 ;
} 
int main() {
	cin >> n >> m ;
	f[0][0][0] = 1 ;
	for(int i = 1 ; i <= n ; i ++) {
		for(int j = 0 ; j <= m ; j ++)
		    for(int k = 0 ; k <= m - j ; k ++) {
		        f[i][j][k] = (f[i][j][k] + f[i - 1][j][k])%mod ;
		        if(j > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 1][k] * (m - j - k + 1))%mod ; 
			    if(k > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 1][k - 1] * (j + 1))%mod ;    
				if(j > 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j - 2][k] * C(m - j - k + 2 , 2))%mod ;
				if(k > 1) f[i][j][k] = (f[i][j][k] + f[i - 1][j + 2][k - 2] * C(j + 2 , 2))%mod ;
				if(k > 0) f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1] * j * (m - j - k + 1))%mod ;
		    }
	}
	for(int i = 0 ; i <= m ; i ++)
	    for(int j = 0 ; j <= m ; j ++)
	        Ans = (Ans + f[n][i][j])%mod ;
	cout << Ans << endl ;
	return 0 ;
}
原文地址:https://www.cnblogs.com/beretty/p/9481184.html