ABC 450G - Random Subtraction 题解

Link

首先最后的 \(x\) 一定是 \(\sum\limits_{i=1}^{n} op_iA_i\) 的形式,其中 \(op_i \in \{-1,1\}\)

那么 \(E(x^2)=E(\sum\limits_{i=1}^{n} op_iA_i)=E(\sum\limits_{i=1}^{n}A_i^2)+2\sum\limits_{i=1}^{n}\sum\limits_{j=1,j\ne i}^{n}E(op_iop_jA_iA_j)\)

以下将 \(E(op_iop_jA_iA_j)\) 简称为 \(E(i,j)\)

发现两堆合并的本质是将两两之间的内部元素之间构成的乘积形式进行翻转(\(-1\) 变成 \(1\)\(1\) 变成 \(-1\)),那么考虑在第 \(i\) 次操作中,我们钦定的 \(u\)\(v\) 两个如何合并。

\(dp_{i,0/1}\) 表示在第 \(i\) 次操作中,我们目前对已经钦定的两个数已经做了奇数次或者偶数次操作的概率是多少。

考虑转移。

Operation 1 不做任何操作

\[dp_{i,j}=dp_{i-1,j}\times \frac{n-i-1}{n-i+1} \]

Operation 2 将其中一个合并在另外一个堆中

\[dp_{i,j}=dp_{i-1,1-j}\times \frac{2(n-i-1)}{(n-i+1)(n-i)} \]

Operation 3 将钦定的两个合并

那么之后两者不会有任何贡献变化。

\[ans_{1-j}=dp_{i-1,j}\times \frac{1}{(n-i-1)(n-i)} \]

然后最后统计答案就是 \(\sum\limits_{i=1}^{n} a[i](sum-a[i])(ans_0-ans_1)\)

Code

#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int Md = 998244353;
const int N = 2e5 + 5;
int a[N], n;
ll dp[N][2], inv[N], cnt[2];
void init(void) {
    inv[0] = inv[1] = 1;
    for (int i = 2; i < N; ++i)
        inv[i] = (Md - (Md / i) * inv[Md % i] % Md) % Md;
}
void trans(ll& x, ll y) {
    x = (x + y) % Md;
}
int main() {
    FASTIO;
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    ll ret = 0, sum = 0;
    for (int i = 1; i <= n; ++i) trans(sum, a[i]);
    init();
    for (int i = 1; i <= n; ++i)
        ret = (ret + (1ll * a[i]) * a[i] % Md) % Md;
    dp[0][0] = 1;
    for (int i = 1; i < n - 1; ++i) {
        for (int j = 0; j < 2; ++j) {
            trans(dp[i][j], dp[i - 1][j] * inv[n - i + 1] % Md * (n - i - 1) % Md);
            trans(dp[i][j], 2ll * dp[i - 1][j ^ 1] % Md * inv[n - i + 1] % Md * (n - i - 1) % Md * inv[n - i] % Md);
        }
    }
    for (int i = 0; i < n - 1; ++i) {
        trans(cnt[0], 2ll * dp[i][1] % Md * inv[n - i] % Md * inv[n - i - 1] % Md);
        trans(cnt[1], 2ll * dp[i][0] % Md * inv[n - i] % Md * inv[n - i - 1] % Md);
    }
    for (int i = 1; i <= n; ++i) {
        ll other = (sum - a[i] + Md) % Md;
        trans(ret, a[i] * other % Md * ((cnt[0] - cnt[1]) % Md + Md) % Md);
    }
    cout << ret << '\n';
    return 0;
}
posted @ 2026-03-22 20:52  To_string  阅读(9)  评论(0)    收藏  举报