P9891 [ICPC2018 Qingdao R] Repair the Artwork 题解

所求即为选择的区间恰好包含所有 \(a_i = 2\) 的位置的方案数。

设所有 \(a_i = 2\) 的位置 \(i\) 组成集合 \(S\),考虑容斥被选中的位置是 \(S\) 的子集的方案数 \(g(S)\)

\(T\)\(S\) 的子集,\(T\) 的贡献 \(f(T)\) 为:选中的位置都在 \(T\) 的子集中的方案数乘容斥系数 \((-1)^{|S| - |T|}\)

将所有 \(i \in T\) 视为 \(a_i = 0\)(可选可不选);将所有 \(i \in U - T\) 视为 \(a_i = 1\)(必须不选)。\(f(T)\) 可重新表述为不包含 \(1\) 的区间的数量的 \(m\) 次方(一个区间允许多次选,方案有序)。

只需求满足条件的区间数量,设所有被视为 \(a_i = 1\) 的位置 \(i\) 依次组成长为 \(m\) 的数列 \(b_1, b_2, \cdots b_m\),则满足条件的区间数量为

\[\sum\limits_{i = 2}^m \frac 1 2 (b_i - b_{i - 1}) (b_i - b_{i - 1} - 1) \]

考虑 DP,发现答案只与 \(b\) 的上一项有关,设 \(dp_{i, j}\) 表示 \(i\) 被视为 \(a_i = 1\),区间数量为 \(j\) 的方案数。(为了方便初始化和统计答案,规定 \(a_0 = a_{n + 1} = 1\)

转移

\[dp_{k, j + w} = \sum\limits_{i = 0}^{k - 1} c_i \cdot dp_{i, j} \]

其中 \(c_i\) 是容斥系数,\(w\) 是权值。

发现容斥系数的指数实际上是有多少个 \(a_i = 2\) 被视为 \(a_i = 1\),所以

\[c_i = \begin{cases} 1, &a_i = 1, \\ -1, &a_i = 2, \\ 0, &\text{otherwise}. \end{cases} \]

该式中 \(a_i\) 是原数列中的 \(a_i\)

\[w = \frac 1 2 (k - i) (k - i - 1) \]

边界

\[dp_{0, 0} = 1 \]

答案

\[\sum\limits_{x = 1}^{\frac 1 2 n (n + 1)} dp_{n + 1, x} \cdot x^m \]

#include <iostream>

#define int long long

using namespace std;

const int mod = 1e9 + 7;

static inline int qpow(int a, int b) {
    int ret = 1;
    while (b) {
        if (b & 1)
            ret = ret * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ret;
}

int n, m;
int a[105];
int dp[105][10005];

static inline void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    for (int i = 0; i <= n + 1; ++i) {
        int mx = n * (n + 1) / 2;
        for (int j = 0; j <= mx; ++j)
            dp[i][j] = 0;
    }
    a[n + 1] = 1;
    dp[0][0] = 1;
    for (int i = 0; i <= n; ++i) {
        int mx = i * (i + 1) / 2;
        for (int j = 0; j <= mx; ++j) {
            for (int k = i + 1; k <= n + 1; ++k) {
                int w = (k - i) * (k - i - 1) / 2;
                if (a[k] == 1) {
                    dp[k][j + w] = (dp[k][j + w] + dp[i][j]) % mod;
                    break;
                } else if (a[k] == 2) {
                    dp[k][j + w] = (dp[k][j + w] - dp[i][j] + mod) % mod;
                }
            }
        }
    }
    int mx = n * (n + 1) / 2;
    int ans = 0;
    for (int i = 0; i <= mx; ++i)
        ans = (ans + dp[n + 1][i] * qpow(i, m) % mod) % mod;
    cout << ans << endl;
}

signed main() {
#ifndef ONLINE_JUDGE
    freopen("P9891.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T;
    cin >> T;
    while (T--)
        solve();
    return 0;
}
posted @ 2024-09-14 15:57  bluewindde  阅读(6)  评论(0编辑  收藏  举报