题意
给定一个长度为 \(n\) 的序列和一个模数 \(m\),记 \(c_i\) 表示 \(\bmod m\) 后的结果为 \(i\) 的数的个数。现在可以使每个数增加 \(1\),请问最少要操作多少次才能使所有 \(c_i = \frac{n}{m}\)。并输出最后的序列。
First. 如何最小化操作次数
由于每次操作会使 \(c_{a_i \bmod m} - 1\),\(c_{(a_i + 1) \bmod m} + 1\),那么不妨先将所有 \(a_i\) 按照 \(\bmod m\) 后的结果分组,下文称模数集合。对于每个余数考虑是否将其中的一些数给到下一个没有达到 \(\frac{n}{m}\) 的余数。
对于 \(c_i > \frac{n}{m}\) 的模数 \(i\),首先,它里面的值一定都要加至没要达到 \(\frac{n}{m}\) 的模数集合里。那么,肯定移动到的模数集合的距离越近越好。所以,可以用一个 set 来维护所有没有达到 \(\frac{n}{m}\) 的模数,查找离其最近的一个模数 \(nxt\),然后将 \(i\) 中的元素加至 \(nxt\) 中。
这里的查找可以用 s.lower_bound
。如果没有,那么取 set 里的第一个元素(因为模数 \(+ 1\) 是循环的)。
每次操作完,如果该模数已经满足,那么将其从 set 中删去。
这样,由于每次我们移动都是移至最近的一个没有满足的模数(移至满足了的模数没有意义,因为枚举到它的时候又会传给下一个数),那么操作次数也肯定是最少的。
Second. 怎么输出序列
这个很简单,你在维护集合的过程中,集合中既存数值,也存该数值的编号。移动的时候只修改值。最后再加上修改完的值与原来的值的差即可。
Third. 细节和时间复杂度分析
时间复杂度分析
对于每个数,我们只将其分组 + 移动,移动过后不会再操作(因为刚好达到条件的组不需要再次操作),而每次移动是 \(O(\log_2n)\) 的,数又有 \(n\) 个,于是整体复杂度是 \(O(n \log_2n)\) 的。
细节
- 题目要求最终的值不能超过 \(10^{18}\) 那么每次移动数要尽可能小。
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;
}
标签:CF999D,nxt,set,frac,Remainders,int,题解,pos,模数
From: https://www.cnblogs.com/Ice-lift/p/18080405