「TC SRM625 D1L3」Seatfriends

思路

首先,对于计数题,不是 \(\text{dp}\) 就是排列组合,这题多思考一会儿就发现单纯 \(\text{dp}\) 和排列组合是做不出来的。然后激动人心地发现,这题是 \(\text{dp} \ +\) 排列组合。
现在来思考怎么做,我们可以发现如果不考虑区间两两之间的空座位,当成选为一个个集合的话是非常好 \(\text{dp}\) 的,但对于空格的处理无法使用 \(\text{dp}\) 处理的,同时对于两两不相邻的区间,必须插入至少 \(1\) 个空格以消除 \(\text{dp}\) 没考虑空格的影响。
现在问题就转换为有 \(i\)不同物品,共 \(n - k\) 块隔板,要在每两个物品间插入至少一个相同隔板,问有多少种方案数,即盒子与球,计数为 \(C_{n - k - 1}^{i - 1}\) ,再乘上 \(dp_{k, i}\) 并将所有 \(i\) 加起来。

细节

对于 \(\text{dp}\) ,是一个插入 \(\text{dp}\) ,使用刷表更容易,定义 \(dp_{i, j}\) 表示选了前 \(i\) 个人,共 \(j\) 个区间的方案数,然后推转移,若刷表即如下:

\[dp_{i, j} \times j \to dp_{i + 1, j + 1} , dp_{i + 1, j - 1} \]

\[dp_{i, j} \times 2j \to dp_{i, j} \]

对于 \(dp_{i, j} \times j\) 即将当前数插入到/合并已有的 \(j\) 个区间。对于 \(dp_{i, j} \times 2j\) 即加入到已有的 \(j\) 个区间的两侧。


对于计数,盒子与球的公式已给,现在就要将所有 \(dp_{m, i}\) 加起来,答案就是

\[\sum_{i = 1}^{k}{dp_{m, i} \times C_{n - m - 1}^{i - 1}} \]

此外,对于 \(n = m\) 的情况,上述式子就失效了,因为 \(n - m - 1 < 0\) ,但因为最后只剩一个区间,所以可以取 \(dp_{m - 1, 1}\) 为答案,\(m - 1\) 的原因即在最后连成环之前必须是只剩一个区间而最后 \(dp_{m, 1}\) 会从不合法的 \(dp_{m - 1, 2}\) 转移。

Code

/*
address:https://vjudge.net/problem/TopCoder-12909
AC 2024/12/27 21:30
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2005;
const int mod = 1e9 + 7;
int n, m, t;
LL dp[N][N];
LL C[N][N];
inline void trans(LL& x, LL y) { x = (x + y) % mod; }
inline LL power(LL a, LL k) {
    LL ret = 1;
    while (k > 0) {
        if (k & 1) ret = ret * a % mod;
        a = a * a % mod;
        k >>= 1;
    }
    return ret;
}
inline LL solve() {
    dp[1][1] = n;
    for (int i = 1;i < m;i++)
        for (int j = 1;j <= i && j <= t;j++) {
            if (j < t) trans(dp[i + 1][j + 1], dp[i][j] * j % mod);
            if (j > 1) trans(dp[i + 1][j - 1], dp[i][j] * j % mod);
            trans(dp[i + 1][j], dp[i][j] * j % mod * 2 % mod);
        }
    if (n == m) return dp[m - 1][1];
    LL ret = 0;
    for (int i = 1;i <= t;i++)
        trans(ret, dp[m][i] * C[n - m - 1][i - 1] % mod);
    return ret;
}
int main() {
    scanf("%d%d%d", &n, &m, &t);
    C[0][0] = 1;
    for (int i = 1;i <= n;i++) {
        C[i][0] = 1;
        for (int j = 1;j <= i;j++) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
    }
    printf("%lld", solve());
    return 0;
}
posted @ 2025-01-17 21:18  keysky  阅读(43)  评论(1)    收藏  举报