AHOI2009中国象棋

题目来源:https://www.luogu.org/problem/show?pid=2051#sub

状态很难想。本题难就难在如何定状态。

再看题解之前,我一点思路也没有。看到题解的状态表示后,我立刻知道怎么做了。

f[i][j][k]表示至第i行,有j列放1个,有k列放2个。

这样f[i][j][k]即为第i行不放、放1个、放2个的数量总和。

状态转移方程很长,用到组合的相关知识。

i=1时需特殊处理。

详见代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#include <queue>
#include <string>
using namespace std;
long long f[101][101][101]={0};
const long long mod=9999973;
long long c(long long n,long long m) {
    if (m == 0)return 1;
    long long l = n - m + 1;
    long long ans = 1;
    long long a[100] = {0};
    int cnt = 0;
    for (long long i = n; i >= n - m + 1; i--)a[++cnt] = i;
    while (m > 1) {
        for (int i = 1; i <= cnt; i++)
            if (a[i] % m == 0) {
                a[i] /= m;
                break;
            }
        m--;
    }
    for (int i = 1; i <= cnt; i++)ans = (ans * a[i]) % mod;
    return ans;
}
int main() {
    ios::sync_with_stdio(false);
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++) {
            for (int k = 0; k <= m; k++) {
                if (j + k <= m) {
                    if (i == 1) {
                        if (k == 0) {
                            if (j == 1)f[i][j][k] = m;
                            if (j == 2)f[i][j][k] = c(m, 2);
                            if (j == 0)f[i][j][k] = 1;
                        }
                    } else {
                        if (j + 2 <= m && k - 2 >= 0)
                            f[i][j][k] =
                                (f[i][j][k] + f[i - 1][j + 2][k - 2] * (c(j + 2, 2))) % mod;
                        if (j - 2 >= 0 && m - k - j >= 0)
                            f[i][j][k] =
                                (f[i][j][k] + f[i - 1][j - 2][k] * (c((m - k - j + 2), 2))) % mod;
                        if (k - 1 >= 0 && m - k - j + 1 > 0)
                            f[i][j][k] =
                                (f[i][j][k] + f[i - 1][j][k - 1] * (j * (m - k - j + 1))) % mod;
                        if (j - 1 >= 0 && m - j - k + 1 > 0)
                            f[i][j][k] =
                                (f[i][j][k] + f[i - 1][j - 1][k] * (m - j - k + 1)) % mod;
                        if (j + 1 <= m && k - 1 >= 0)
                            f[i][j][k] =
                                (f[i][j][k] + f[i - 1][j + 1][k - 1] * (j + 1)) % mod;
                        f[i][j][k] = (f[i][j][k] + f[i - 1][j][k]) % mod;
                    }
                }
            }
        }
    }
    long long tot = 0;
    for (int i = 0; i <= m; i++) {
        for (int j = 0; j <= m; j++) {
            tot += f[n][i][j];
        }
    }
    cout << tot % mod;
    return 0;
}

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值