Forever Young

51Nod 1327 棋盘游戏

思路

打死都不可能想到状态设计DP系列= =

其实我不会这道题/kk

以行为状态转移无法记录各列的状态,所以以列为状态转移

\(f[i][j][k]\)为处理到第\(i\)列,前面有\(j\)列没有填棋子,有\(k\)行已经填到了右区间的方案数

\(l[i],r[i],mid[i]\)分别为左区间右端点为\(i\)的行数、右区间左端点为\(i\)的行数、第\(i\)列没有被左右区间覆盖的行数

每次到达左区间右端点的限制\(l_{i}\)时,再考虑如何满足这些左区间,则有如下三种转移:

  • 放在左区间内,就要满足将\(l_{i+1}\)行安排到前面没放棋子的\(j\)列中,因为顺序没有要求,所以直接乘上排列数,转移为

    \[f[i+1][j+1-l_{i+1}][k+r_{i+1}]+=f[i][j][k]\times A_{j+1}^{l_{i+1}} \]

  • 放在右区间内,要乘上左右两侧的方案数

    \[f[i+1][j-l_{i+1}][k+r_{i+1}-1]+=f[i][j][k]\times A_{j}^{l_{i+1}}\times (k+r_{i+1}) \]

  • 放在未被覆盖的中间位置,要乘上左侧和中间的方案数

    \[f[i+1][j-l_{i+1}][k+r_{i+1}]+=f[i][j][k]\times mid_{i+1}\times A_{j}^{l_{i+1}} \]

初始状态为\(f[0][0][0]=1\),最后的答案为\(\sum_{i=1}^{m}f[m][i][0]\)

代码

/*
Author:loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;

const int A = 211;
const int B = 61;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
    char c = getchar();
    int x = 0, f = 1;
    for (; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for (; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

int n, m, ans, l[A], r[A], mid[A], c[A][A], fac[A], f[A][A][B];

signed main() {
    n = read(), m = read();
    for (int i = 1; i <= n; i++) {
        int L = read(), R = read();
        l[L]++, r[m - R + 1]++, mid[L + 1]++, mid[m - R + 1]--;
    }
    for (int i = 1; i <= m; i++) mid[i] += mid[i - 1];
    c[0][0] = 1, fac[0] = 1, f[0][0][0] = 1;
    for (int i = 1; i <= m; i++) {
        c[i][0] = c[i][i] = 1, fac[i] = fac[i - 1] * i % mod;
        for (int j = 1; j < i; j++) c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
    }
    for (int i = 0; i < m; i++)
        for (int j = 0; j <= i; j++)
            for (int k = 0; k <= n; k++)
                if (f[i][j][k]) {
                    if (j + 1 >= l[i + 1])  //放在左区间
                        (f[i + 1][j + 1 - l[i + 1]][k + r[i + 1]] += f[i][j][k] * (c[j + 1][l[i + 1]] * fac[l[i + 1]] % mod) % mod) %= mod;
                    if (j >= l[i + 1])  //放在中间
                        (f[i + 1][j - l[i + 1]][k + r[i + 1]] += f[i][j][k] * (c[j][l[i + 1]] * fac[l[i + 1]] % mod) % mod * mid[i + 1] % mod) %= mod;
                    if (j >= l[i + 1] && k + r[i + 1])  //放在右区间
                        (f[i + 1][j - l[i + 1]][k + r[i + 1] - 1] += f[i][j][k] * (c[j][l[i + 1]] * fac[l[i + 1]] % mod) % mod * (k + r[i + 1]) % mod) %= mod;
                }
    for (int i = 0; i <= m; i++) ans = (ans + f[m][i][0]) % mod;
    cout << ans << '\n';
    return 0;
}
posted @ 2020-07-05 11:45  Loceaner  阅读(168)  评论(0编辑  收藏  举报