1 条题解

  • 0
    @ 2026-5-17 15:29:53

    题意

    nn 个博主,第 ii 个有 viv_irir_i 两个属性。
    对于大小为 kk 的子集 SS,定义:

    $$E(S) = \max\left(\min_{i\in S} v_i,\ \min_{i\in S} r_i\right) $$

    对每个 k=1..nk=1..n,求所有大小为 kk 的子集的 E(S)E(S) 的平均值,模 998244353998244353


    解法

    11. 转化求和

    固定 tt,考虑 E(S)tE(S) \ge t 的子集。
    minvt\min v \ge tminrt\min r \ge t 的子集数为:

    (atk)+(btk)(ctk)\binom{a_t}{k} + \binom{b_t}{k} - \binom{c_t}{k}

    其中:

    • at=#{i:vit}a_t = \#\{i: v_i \ge t\}
    • bt=#{i:rit}b_t = \#\{i: r_i \ge t\}
    • ct=#{i:vit 且 rit}c_t = \#\{i: v_i \ge t \text{ 且 } r_i \ge t\}

    因为 E(S)=t1[E(S)t]E(S) = \sum_{t\ge 1} [E(S) \ge t],所以:

    $$\sum_{|S|=k} E(S) = \sum_{t=1}^{\max V} \left( \binom{a_t}{k} + \binom{b_t}{k} - \binom{c_t}{k} \right) $$

    22. 按值域优化

    ata_ttt 单调不增,只在 viv_i 的值处变化。
    对每个 aa,计算有多少个 tt 满足 at=aa_t = a,记为 lenA[a]lenA[a]
    则:

    $$\sum_{t=1}^{\max V} \binom{a_t}{k} = \sum_{a=0}^n lenA[a] \cdot \binom{a}{k} $$

    同理对 b,cb, c

    33. 计算 lenA[a]lenA[a]

    通过后缀和得到 ata_t,然后记录变化点。
    用差分数组 diffA[a]diffA[a] 存储区间长度,最后对每个 aa 求和得到 lenA[a]lenA[a]

    44. 最终答案

    对每个 kk

    $$\text{sum}_k = \sum_{a} lenA[a]\binom{a}{k} + \sum_{b} lenB[b]\binom{b}{k} - \sum_{c} lenC[c]\binom{c}{k} $$$$avg_k = \frac{\text{sum}_k}{\binom{n}{k}} \pmod{MOD} $$

    模运算下除法用逆元。


    复杂度

    • 统计后缀和:O(maxV)O(\max V)
    • 计算变化点:O(maxV)O(\max V)
    • 枚举所有 (a,k)(a,k)a\sum a 的总和不超过 O(n2)O(n^2),但由于大多数 lenA[a]=0lenA[a]=0,实际接近 O(nlogn)O(n \log n)(值域压缩后)。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int MOD = 998244353;
    const int MAXV = 1000000;
    const int N = 200005;
    
    int n;
    int v[N], r[N];
    int cntA[MAXV + 5], cntB[MAXV + 5], cntC[MAXV + 5];
    int fact[N], invfact[N];
    int ans[N];
    
    int modpow(int a, int e) {
        int res = 1;
        while (e) {
            if (e & 1) res = 1LL * res * a % MOD;
            a = 1LL * a * a % MOD;
            e >>= 1;
        }
        return res;
    }
    
    void precompute() {
        fact[0] = 1;
        for (int i = 1; i <= n; i++) {
            fact[i] = 1LL * fact[i-1] * i % MOD;
        }
        invfact[n] = modpow(fact[n], MOD - 2);
        for (int i = n-1; i >= 0; i--) {
            invfact[i] = 1LL * invfact[i+1] * (i+1) % MOD;
        }
    }
    
    int C(int nn, int kk) {
        if (kk < 0 || kk > nn) return 0;
        return 1LL * fact[nn] * invfact[kk] % MOD * invfact[nn - kk] % MOD;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> v[i];
        for (int i = 1; i <= n; i++) cin >> r[i];
    
        precompute();
    
        // 统计每个值出现的次数
        for (int i = 1; i <= n; i++) {
            cntA[v[i]]++;
            cntB[r[i]]++;
            if (v[i] >= 0 && r[i] >= 0) {
                cntC[min(v[i], r[i])]++;
            }
        }
    
        // 后缀和:A_t = 数量大于等于 t 的个数
        for (int t = MAXV; t >= 0; t--) {
            cntA[t] += cntA[t+1];
            cntB[t] += cntB[t+1];
            cntC[t] += cntC[t+1];
        }
    
        // 对每个 t,更新所有 k 的贡献
        // 使用差分数组优化:对于每个 t,需要将 C(a_t, k) 加到 ans[k]
        // 由于 a_t 在 t 减少时才会变化,我们可以只处理变化点
        vector<int> diffA[N], diffB[N], diffC[N];
        
        int lastA = cntA[MAXV+1], lastB = cntB[MAXV+1], lastC = cntC[MAXV+1];
        for (int t = MAXV; t >= 0; t--) {
            if (cntA[t] != lastA) {
                diffA[lastA].push_back(t+1);
                diffA[cntA[t]].push_back(-(t+1));
                lastA = cntA[t];
            }
            if (cntB[t] != lastB) {
                diffB[lastB].push_back(t+1);
                diffB[cntB[t]].push_back(-(t+1));
                lastB = cntB[t];
            }
            if (cntC[t] != lastC) {
                diffC[lastC].push_back(t+1);
                diffC[cntC[t]].push_back(-(t+1));
                lastC = cntC[t];
            }
        }
    
        // 对于每个可能的 cnt 值,处理贡献
        for (int a = 0; a <= n; a++) {
            if (diffA[a].empty() && diffB[a].empty() && diffC[a].empty()) continue;
            int sumA = 0, sumB = 0, sumC = 0;
            for (int v : diffA[a]) sumA = (sumA + v) % MOD;
            for (int v : diffB[a]) sumB = (sumB + v) % MOD;
            for (int v : diffC[a]) sumC = (sumC + v) % MOD;
            for (int k = 1; k <= min(n, a); k++) {
                int waysA = 1LL * sumA * C(a, k) % MOD;
                int waysB = 1LL * sumB * C(a, k) % MOD;
                int waysC = 1LL * sumC * C(a, k) % MOD;
                ans[k] = (ans[k] + waysA + waysB - waysC) % MOD;
                if (ans[k] < 0) ans[k] += MOD;
            }
        }
    
        // 计算平均值
        for (int k = 1; k <= n; k++) {
            int inv = modpow(C(n, k), MOD - 2);
            ans[k] = 1LL * ans[k] * inv % MOD;
            cout << ans[k] << " \n"[k == n];
        }
    
        return 0;
    }
    
    • 1

    信息

    ID
    7156
    时间
    1000ms
    内存
    256MiB
    难度
    1
    标签
    递交数
    1
    已通过
    1
    上传者