CF999D Equalize the Remainders 题解

题意

给定一个长度为 \(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)\) 的。

细节

  1. 题目要求最终的值不能超过 \(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;
}
posted @ 2024-03-18 14:51  固态H2O  阅读(6)  评论(0编辑  收藏  举报