CF2123E MEX Count 题解

题目简述

给定一个非负整数数组 a,我们定义数组的 MEX(最小排除值)为数组中未出现的最小非负整数。

例如:

  • MEX([2,2,1]) = 0
  • MEX([3,1,0,1]) = 2
  • MEX([0,3,1,2]) = 4

题目要求:对于每个 k = 0..n,统计删除恰好 k 个元素后,数组可能的不同 MEX 数量。


核心思路

直接枚举删除元素组合来求 MEX 是非常复杂的,我们换个角度:

  1. 统计每个数字出现次数

    • 维护一个频率数组 cnt[i],记录每个数字的出现次数
  2. 找到原数组 MEX

    • 遍历 0..n 找第一个缺失的整数,记为 MEX_orig
  3. 枚举可能的 MEX

    • 从原 MEX 向下枚举每个可能的 MEX = i

    • 对每个 MEX=i,把数组元素分为三类:

      1. 必删元素:值等于 i,必须删除,数量 = cnt[i]
      2. 不可删元素:值 < i,每个至少保留一个,否则 MEX 会变小
      3. 可删可不删元素:值 < i 的重复 + 大于 i 的元素,数量固定,可以任意选择删除
  4. 确定删除元素个数区间

    • 删除元素总数 k = 必删数量 + 可删可不删部分

    • 因此对每个 MEX=i,合法删除数量 k 构成 连续区间 [L,R]

      • L = cnt[i](必须删除数量)
      • R = n - i(必删 + 可删可不删数量)
  5. 用差分数组统计区间贡献

    • 对每个 MEX=i 的区间 [L,R]

      diff[L] += 1;
      diff[R+1] -= 1;
      
    • 前缀和展开后,得到每个 k 的可能 MEX 数量


代码实现(C++)

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e5 + 55;
int n, a[N], b[N], c[N];

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    
    int T;
    cin >> T;
    while(T--) {
        cin >> n;
        // 读入数组并统计频率
        for(int i = 1; i <= n; i++) {
            cin >> a[i];
            b[a[i]]++;
        }
        
        // 找原数组 MEX
        int MEX = 0;
        for(int i = 0; i <= n; i++) {
            if(b[i] == 0) {
                MEX = i;
                break;
            }
        }
        
        // 枚举可能的 MEX,从原 MEX 向下到 0
        for(int i = MEX; i >= 0; i--) {
            int L = b[i];    // 必须删除数量
            int R = n - i;   // 必删 + 可删可不删数量
            c[L]++;          // 区间开始,贡献 +1
            c[R+1]--;        // 区间结束,撤销贡献
        }
        
        // 前缀和展开,得到每个 k 的答案
        for(int i = 0; i <= n; i++) {
            if(i >= 1) c[i] += c[i-1];
            cout << c[i] << " ";
        }
        cout << "\n";
        
        // 清空数组,为下一组测试用例做准备
        for(int i = 0; i <= n+1; i++)
            a[i] = b[i] = c[i] = 0;
    }
    return 0;
}

总结

  • 核心技巧:将每个 MEX 对应的删除数量转化为连续区间,利用差分数组 + 前缀和统计每个 k 的贡献

  • 复杂度:O(n) per 测试用例

  • 差分数组的作用是:

    • 不需要枚举 [L,R] 内每个 k
    • 只记录区间开始/结束,前缀和展开自动得到每个 k 的答案
  • 这道题比直接枚举组合简单得多,关键在于理解:

    1. MEX 的连续性
    2. 差分数组的区间加法


posted on 2025-08-17 21:35  小高QAQ  阅读(6)  评论(0)    收藏  举报