CF999D Equalize the Remainders 题解
题意
给定一个长度为
First. 如何最小化操作次数
由于每次操作会使
对于
这里的查找可以用 s.lower_bound
。如果没有,那么取 set 里的第一个元素(因为模数
每次操作完,如果该模数已经满足,那么将其从 set 中删去。
这样,由于每次我们移动都是移至最近的一个没有满足的模数(移至满足了的模数没有意义,因为枚举到它的时候又会传给下一个数),那么操作次数也肯定是最少的。
Second. 怎么输出序列
这个很简单,你在维护集合的过程中,集合中既存数值,也存该数值的编号。移动的时候只修改值。最后再加上修改完的值与原来的值的差即可。
Third. 细节和时间复杂度分析
时间复杂度分析
对于每个数,我们只将其分组 + 移动,移动过后不会再操作(因为刚好达到条件的组不需要再次操作),而每次移动是
细节
- 题目要求最终的值不能超过
那么每次移动数要尽可能小。
Code
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N = 2e5 + 10;
int n, m;
int a[N], c[N];
set<int> s;
vector< pair<int, int> > b[N];
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
int len = n / m;
for (int i = 1; i <= n; i ++) cin >> a[i], b[a[i] % m].push_back({a[i], i});
for (int i = 0; i < m; i ++) {
sort(b[i].begin(), b[i].end(), greater<pair<int, int> > ());
if (b[i].size() < len) s.insert(i);
}
for (int i = 0; i < m; i ++) {
while (b[i].size() > len) {
auto pos = s.lower_bound(i);
pair<int, int> ft = b[i].back();
b[i].pop_back();
if (pos == s.end()) pos = s.begin();
int nxt = *pos;
b[nxt].push_back({ft.first + (nxt - i + m)% m/*操作次数*/ , ft.second});
if (b[nxt].size() == len) s.erase(pos);
}
}
for (int i = 0; i < m; i ++) {
for (auto x : b[i]) {
c[x.second] = x.first;
}
}
int ans = 0;
for (int i = 1; i <= n; i ++) {
ans += c[i] - a[i];
}
cout << ans << endl;
for (int i = 1; i <= n; i ++) cout << c[i] << ' ';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下