BZOJ1801 Ahoi2009 chess 中国象棋 【DP+组合计数】*

时间:2022-03-29 08:26:01

BZOJ1801 Ahoi2009 chess 中国象棋


Description

在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。 请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧.

Input

一行包含两个整数N,M,中间用空格分开.

Output

输出所有的方案数,由于值比较大,输出其mod 9999973

Sample Input

1 3

Sample Output

7

HINT

除了在3个格子中都放满炮的的情况外,其它的都可以.
100%的数据中N,M不超过100
50%的数据中,N,M至少有一个数不超过8
30%的数据中,N,M均不超过6


不难发现每行每列最多只有2个棋子
考虑DP,dpi,j,kdp_{i,j,k}dpi,j,k​表示i行中一共有j列有一个,k列有两个
然后我们考虑这一行选多少

  • 当前行不选
    dpi,j,k=dpi−1,j,k​
  • 当前行选一个
    • 选原来是0个棋子dp(i,j,k)+=dp(i−1,j−1,k)∗c(n−k−j+1,1)(1≤j)
    • 选原来是1个棋子dp(i,j,k)+=dp(i−1,j+1,k−1)∗c(j+1,1)(1≤k,j≤m−1)
  • 当前行选两个
    • 选两个原来是0的dp(i,j,k)+=dp(i−1,j−2,k)*c(m-j-k+1,2)(2≤j)
    • 选两个原来是1的dp(i,j,k)+=dp(i−1,j+2,k−2)*c(j+2,2)(2≤k,j≤m−2)
    • 选一个是1一个是0 dp(i,j,k)+=dp(i−1,j,k−1)*j*(m-j-k+1)(1≤j,1≤k(要保证原来有1))

然后就可以进行转移了


 #include<bits/stdc++.h>
using namespace std;
#define fu(a,b,c) for(int a=b;a<=c;++a)
#define fd(a,b,c) for(int a=b;a>=c;--a)
#define N 110
#define LL long long
#define Mod 9999973
LL c[N][N];
LL dp[N][N][N]={};
int n,m;
void getc(){
fu(i,,N-)c[i][]=;
fu(i,,N-)
fu(j,,i)c[i][j]=(c[i-][j]+c[i-][j-])%Mod;
}
LL mul(LL a,LL b){return a*b%Mod;}
int main(){
getc();
dp[][][]=;
scanf("%d%d",&n,&m);
if(n<m)swap(n,m);
fu(i,,n)
fu(j,,m)
fu(k,,m-j){
dp[i][j][k]=dp[i-][j][k];
if(j)dp[i][j][k]+=mul(dp[i-][j-][k],c[m-j-k+][]);
if(j&&k)dp[i][j][k]+=mul(dp[i-][j][k-],mul(j,m-j-k+));
if(j>=)dp[i][j][k]+=mul(dp[i-][j-][k],c[m-j-k+][]);
if(k>=&&j<=m-)dp[i][j][k]+=mul(dp[i-][j+][k-],c[j+][]);
if(k>=&&j<=m-)dp[i][j][k]+=mul(dp[i-][j+][k-],c[j+][]);
dp[i][j][k]%=Mod;
}
int ans=;
fu(i,,m)
fu(j,,m-i)
ans=(ans+dp[n][i][j])%Mod;
printf("%d",ans);
return ;
}