bzoj1801[Ahoi2009]chess 中国象棋 dp

题目描述

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

输入格式

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

输出格式

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

题解

据说三进制只有50分?(其实是我不会三进制)感觉这题就跟状压没关系了,直接dp就可以了。
\(f[i][j][k]\)表示第i行有j列有1个炮,有k列有2个炮。然后转移即可。情况蛮多的,总是写不全qwq。

  • 在没有炮的一列放一个
  • 在没有炮的两列各放一个
  • 在有一个炮的一列再放一个
  • 在有一个炮的两列各再放一个
  • 在有一个炮的一列再放一个,在没有炮的一列放一个

利用组合数求解。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=9999973;
const int maxn=(1<<9)+10;
typedef long long ll;
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
ll inv[110],fac[110];
ll f[110][110][110];
ll pow(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b/=2;
    }
    return res;
}
ll c(ll n,ll m){
	if(n>m)return 0; 
    return fac[m]*inv[n]%mod*inv[m-n]%mod;
}
void init(){
    fac[0]=1;
    for(int i=1;i<=101;i++)fac[i]=fac[i-1]*i%mod;
    inv[101]=pow(fac[101],mod-2);
    for(int i=100;i>=0;i--)inv[i]=(inv[i+1]*(i+1))%mod;
}
int main(){
	init();
    n=read();m=read();
	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;k++){
        if(f[i-1][j][k]){
            f[i][j][k]=(f[i][j][k]+f[i-1][j][k])%mod;
            if(j+k<m)f[i][j+1][k]=(f[i][j+1][k]+f[i-1][j][k]*(m-j-k)%mod)%mod;
            if(j+k<m-1)f[i][j+2][k]=(f[i][j+2][k]+f[i-1][j][k]*c(2,m-j-k)%mod)%mod;
			if(j>0&&(k+1)<=m)f[i][j-1][k+1]=(f[i][j-1][k+1]+f[i-1][j][k]*j%mod)%mod;
            if(j>1&&(k+2)<=m)f[i][j-2][k+2]=(f[i][j-2][k+2]+f[i-1][j][k]*c(2,j)%mod)%mod;
            if(j>0&&(j+k)<m)f[i][j][k+1]=(f[i][j][k+1]+f[i-1][j][k]*(m-j-k)%mod*j%mod)%mod;
        }
    }
    int ans=0;
    for(int i=0;i<=m;i++) for(int j=0;j<=m;j++) 
		ans=(ans+f[n][i][j])%mod;
    printf("%d\n",ans);
    return 0;
}
posted @ 2018-10-04 11:02  南城ㄱ  阅读(98)  评论(0编辑  收藏  举报