1 条题解

  • 0
    @ 2026-4-29 15:18:31

    一、题意

    给定长度为 nn 的数组 aa、操作次数 kk。 共执行 kk 轮操作: 每一轮对每个位置 ii: 把 aia_i 替换为删掉 aia_i 后剩下所有元素的 mex\text{mex},所有元素同时更新

    mex\text{mex} 定义:集合中最小的非负未出现整数

    数据范围: $1\le t\le 10^4,\ 2\le n\le 2\cdot 10^5,\ 1\le k\le 10^9$ n2105\sum n\le 2\cdot 10^5

    kk 次操作后数组所有元素的和。

    二、核心观察

    1. kk 最大到 10910^9不能暴力模拟 kk
    2. 数组变换只会出现两种情况:
      • 很快进入稳定态:数组不再变化;
      • 进入二元周期:在两个状态来回切换;
    3. 一旦出现周期,剩余操作次数只看奇偶性k&=1k \mathrel{\&}=1 即可。

    三、每轮变换原理

    设当前数组为 aa

    1. 统计每个数出现频率 F[x]F[x]
    2. 求全局 mex\text{mex}
    mex=min{x0F[x]=0}\text{mex} = \min\{x\ge 0\mid F[x]=0\}
    1. 构造新数组 BB
      • 若当前数值 xx 出现次数 F[x]>1F[x]>1: 删掉一个不影响整体集合,去掉自身后的 mex\text{mex} 仍等于全局 mex\text{mex}
      • F[x]=1F[x]=1: 删掉 xx 后最小值会上移,用 mmmm 从小到大依次赋值;
    2. 新数组排序,作为下一轮初始数组。

    四、带注释 C++ 完整标程

    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        
        int t;
        cin >> t;
        while (t--)
        {
            int n, k;
            cin >> n >> k;
            vector<int> a(n);
            for (int i = 0; i < n; ++i)
                cin >> a[i];
            
            vector<int> l, sl;   // l:上一轮数组  sl:上上轮数组
            bool first = true;
    
            // 还有操作次数 且 数组未稳定
            while (k > 0 && (first || l != a))
            {
                first = false;
                k--;
                sl = l;     // 保存上上轮
                l = a;      // 保存上一轮
    
                // 1. 统计频率
                vector<int> F(n + 2, 0);
                for (int x : a) F[x]++;
    
                // 2. 求全局 mex
                int mex = 0;
                while (F[mex]) mex++;
    
                // 3. 构造新数组 B
                vector<int> B;
                int mm = 0;
                for (int x : a)
                {
                    if (F[x] > 1)
                        B.push_back(mex);
                    else
                    {
                        B.push_back(mm);
                        if (x == mm) mm++;
                    }
                }
    
                // 4. 排序进入下一轮
                sort(B.begin(), B.end());
                a.swap(B);
    
                // 发现二元周期,剩余次数只看奇偶
                if (a == sl)
                    k &= 1;
            }
    
            // 计算数组和
            ll sum = 0;
            for (int x : a) sum += x;
            cout << sum << '\n';
        }
        return 0;
    }
    

    五、算法复杂度

    每轮:频率统计 O(n)O(n) + 求 mex\text{mex} O(n)O(n) + 构造新数组 O(n)O(n) + 排序 O(nlogn)O(n\log n)。 数组最多变换常数轮就进入稳态/周期, 总复杂度:O(nlogn)O(\sum n\log n),完全满足题目数据限制。

    六、关键代码关键点

    1. ios::sync_with_stdio(false); cin.tie(nullptr); 快读,适配大数据;
    2. l, sl 保存前两轮状态,判周期;
    3. 遇到周期直接 k &= 1,跳过巨量无效模拟;
    4. 严格复刻原题构造新数组的规则,保证和样例输出一致。

    直接复制即可提交通过。

    • 1

    信息

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