1 条题解
-
0
一、题意
给定长度为 的数组 、操作次数 。 共执行 轮操作: 每一轮对每个位置 : 把 替换为删掉 后剩下所有元素的 ,所有元素同时更新。
定义:集合中最小的非负未出现整数。
数据范围: $1\le t\le 10^4,\ 2\le n\le 2\cdot 10^5,\ 1\le k\le 10^9$
求 次操作后数组所有元素的和。
二、核心观察
- 最大到 ,不能暴力模拟 轮;
- 数组变换只会出现两种情况:
- 很快进入稳定态:数组不再变化;
- 进入二元周期:在两个状态来回切换;
- 一旦出现周期,剩余操作次数只看奇偶性: 即可。
三、每轮变换原理
设当前数组为 :
- 统计每个数出现频率 ;
- 求全局 :
- 构造新数组 :
- 若当前数值 出现次数 : 删掉一个不影响整体集合,去掉自身后的 仍等于全局 ;
- 若 : 删掉 后最小值会上移,用 从小到大依次赋值;
- 新数组排序,作为下一轮初始数组。
四、带注释 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; }五、算法复杂度
每轮:频率统计 + 求 + 构造新数组 + 排序 。 数组最多变换常数轮就进入稳态/周期, 总复杂度:,完全满足题目数据限制。
六、关键代码关键点
ios::sync_with_stdio(false); cin.tie(nullptr);快读,适配大数据;- 用
l, sl保存前两轮状态,判周期; - 遇到周期直接
k &= 1,跳过巨量无效模拟; - 严格复刻原题构造新数组的规则,保证和样例输出一致。
直接复制即可提交通过。
- 1
信息
- ID
- 6700
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者