1. 程式人生 > >BZOJ1801 Ahoi2009 chess 中國象棋 【DP+組合計數】*

BZOJ1801 Ahoi2009 chess 中國象棋 【DP+組合計數】*

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,d

pi,j,kdp_{i,j,k}表示i行中一共有j列有一個,k列有兩個
然後我們考慮這一行選多少

  • 當前行不選
    dpi,j,k=dpi1,j,kdp_{i,j,k}=dp_{i-1,j,k}
  • 當前行選一個
    • 選原來是0個棋子dpi,j,k+=dpi1,j1,kcnkj+11(1j)dp_{i,j,k}+=dp_{i-1,j-1,k}*c_{n-k-j+1}^1(1\le j)
    • 選原來是1個棋子dpi,j,k+=dpi1,j+1,k1cj+11(1
      k,jm1)dp_{i,j,k}+=dp_{i-1,j+1,k-1}*c_{j+1}^1(1\le k,j\le m-1)
  • 當前行選兩個
    • 選兩個原來是0的dpi,j,k+=dpi1,j2,k(2j)dp_{i,j,k}+=dp_{i-1,j-2,k}(2\le j)
    • 選兩個原來是1的dpi,j,k+=dpi1,j+2,k2(2k,jm2)dp_{i,j,k}+=dp_{i-1,j+2,k-2}(2\le k,j\le m-2)
    • 選一個是1一個是0dpi,j,k+=dpi1,j,k1(1j,1k(1))dp_{i,j,k}+=dp_{i-1,j,k-1}(1\le j,1\le 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]={0};
int n,m;
void getc(){
  fu(i,0,N-1)c[i][0]=1;
  fu(i,1,N-1)
    fu(j,1,i)c[i][j]=(c[i-1][j]+c[i-1][j-1])%Mod;
}
LL mul(LL a,LL b){return a*b%Mod;}
int main(){
  getc();
  dp[0][0][0]=1;
  scanf("%d%d",&n,&m);
  if(n<m)swap(n,m);
  fu(i,1,n)
    fu(j,0,m)
      fu(k,0,m-j){
        dp[i][j][k]=dp[i-1][j][k];
        if(j)dp[i][j][k]+=mul(dp[i-1][j-1][k],c[m-j-k+1][1]);
        if(j&&k)dp[i][j][k]+=mul(dp[i-1][j][k-1],mul(j,m-j-k+1));
        if(j>=2)dp[i][j][k]+=mul(dp[i-1][j-2][k],c[m-j-k+2][2]);
        if(k>=1&&j<=m-1)dp[i][j][k]+=mul(dp[i-1][j+1][k-1],c[j+1][1]);
        if(k>=2&&j<=m-2)dp[i][j][k]+=mul(dp[i-1][j+2][k-2],c[j+2][2]);
        dp[i][j][k]%=Mod;
      }
  int ans=0;
  fu(i,0,m)
    fu(j,0,m-i)
      ans=(ans+dp[n][i][j])%Mod;
  printf("%d",ans);
  return 0;
}