题意
n个数字,对m取余有m种情况,使得每种情况的个数都为n/m个(保证n%m=0),最少需要操作多少次?
操作:把某个数字+1。输出最少操作次数,和操作后的序列(可以输出任意一种)。
思路
对于余数x来说,如果余数为x的数没凑够n/m个,那么就让他不变,如果已经凑够了,我们就让他变为距离他最近的没凑够的余数(且通过+1得到的)。
所以,C[i]数组维护余数为
i
i
的数的个数,定义set<int>s,维护还没凑够的余数。所以对于每一个数字求出他的余数d,然后找到中第一个大于等于d的数字x,++C[x],如果C[x]==n/m就把x从s中删除。但是注意一种情况,x比s中最大的数还大,那么我们就把x变为s中最小的数,举个例子m=5,余数为4和为0的数字凑够了,此时又来一个余数为4的数,该数应该变为余数为1。维护答案和序列,ans += (x-d+m)%m,a[i] += (x-d+m)%m。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5+5;
typedef long long ll;
ll n, m, a[MAXN], c[MAXN], ans;
set<int> s;
int main()
{
scanf("%lld%lld", &n, &m);
for (int i = 0; i < m; i++) s.insert(i);
for (int i = 0; i < n; i++)
{
scanf("%lld", &a[i]);
int d = a[i]%m, x;
//找到d能加到的最近的余数x
if (d > *s.rbegin()) x = *s.begin();//d比最大的还大
else x = *s.lower_bound(d);
if (++c[x] == n/m) s.erase(x);//余数为x的数凑够了
ans += (x-d+m)%m;
a[i] += (x-d+m)%m;
}
printf("%lld\n", ans);
for (int i = 0; i < n; i++) printf("%lld ", a[i]);
}
/*
6 3
3 2 0 6 10 12
*/