CodeForces ~ 999D ~ Equalize the Remainders (思维 + set二分)

题意

n个数字,对m取余有m种情况,使得每种情况的个数都为n/m个(保证n%m=0),最少需要操作多少次?
操作:把某个数字+1。输出最少操作次数,和操作后的序列(可以输出任意一种)。


思路

对于余数x来说,如果余数为x的数没凑够n/m个,那么就让他不变,如果已经凑够了,我们就让他变为距离他最近的没凑够的余数(且通过+1得到的)。
所以,C[i]数组维护余数为 i i 的数的个数,定义set<int>s,维护还没凑够的余数。所以对于每一个数字求出他的余数d,然后找到s中第一个大于等于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
*/
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值