[AHOI2009]中国象棋 题解

Statement

[P2051 AHOI2009]中国象棋 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Solution

显然的性质是,同一行列不能放两个以上的棋子

问题在于如何处理行列的关系,不妨把行拎出来考虑

假设我们正在填写第 \(i\) 行,发现我们只需要知道有多少个列没有填,多少个列填了一个,多少个列填了两个(满了)就可以进一步填写了(不必知道具体填写方案)。

设 \(f[i][j][k]\) 表示前 \(i\) 行,\(j\) 列填了一个棋子,\(k\) 列填了两个棋子的方案数

此时,有 \(m-j-k\) 列什么也没有填,考虑状态转移

不填 \(f[i][j][k]=f[i-1][j][k]\)

填 1 个

\[f[i][j][k]=\left\{ \begin{array}{l} f[i-1][j+1][k-1]\times (j+1), &填在放了一个的列\\ f[i-1][j-1][k] \times (m-j-k+1),&填在本来就没有的列 \end{array} \right. \]

填 2 个

\[f[i][j][k]=\left\{ \begin{array}{l} f[i-1][j-2][k]\times \tbinom{m-j-k+2}{2},&全部填在本来就没有的列\\ f[i-1][j][k-1]\times j\times (m-j-k+1),&一个填在没有,一个填在有一个\\ f[i-1][j+2][k-2]\times \tbinom{j+2}{2},& 全部填在放了一个的列\\ \end{array} \right. \]

注意到放了两个的列显然不能再放,一行最多放两个

初状态 \(f[0][0][0]=1\)

答案 \(\sum_{i=0}^m\sum_{j=0}^{i+j\leq\max(m,2n)}f[n][i][j]\)

状态数 \(O(n^3)\) ,转移 \(O(1)\) 时间复杂度 \(O(n^3)\)

只用到 \(f[i],f[i-1]\) ,空间 \(O(n^2)\)

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 105;
const int mod = 9999973;

int f[N][N][N];
int n,m,ans;

int C(int x){return (x*(x-1)/2)%mod;}

signed main(){
    scanf("%lld%lld",&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-1][j][k];
                if(k>=1)(f[i][j][k]+=f[i-1][j+1][k-1]*(j+1))%=mod;
                if(j>=1)(f[i][j][k]+=f[i-1][j-1][k]*(m-j-k+1))%=mod;
                if(j>=2)(f[i][j][k]+=f[i-1][j-2][k]*C(m-j-k+2))%=mod;
                if(k>=1)(f[i][j][k]+=f[i-1][j][k-1]*j*(m-j-k+1))%=mod;
                if(k>=2)(f[i][j][k]+=f[i-1][j+2][k-2]*C(j+2))%=mod;
            }
    int ans=0;
    for(int i=0;i<=m;++i)
        for(int j=0;j<=m;++j)
            (ans+=f[n][i][j])%=mod;
    printf("%lld\n",ans);
    return 0;
}
上一篇:第六周学习


下一篇:Codeforces Round 748