【贪心】【set】 CodeForces - 999D Equalize the Remainders 【n个数对m取余,使0 - m-1每种情况的个数都为n/m个,求最少需操作次数】

【贪心】【set】 CodeForces - 999D Equalize the Remainders 【n个数对m取余,使0 - m-1每种情况的个数都为n/m个,求最少需操作次数】

You are given an array consisting of n integers a1,a2,…,an, and a positive integer m. It is guaranteed that m is a divisor of n.

In a single move, you can choose any position i between 1 and n and increase ai by 1.

Let’s calculate cr (0≤r≤m−1) — the number of elements having remainder r when divided by m. In other words, for each remainder, let’s find the number of corresponding elements in a with that remainder.

Your task is to change the array in such a way that c0=c1=⋯=cm−1=nm.

Find the minimum number of moves to satisfy the above requirement.

Input
The first line of input contains two integers n and m (1≤n≤2⋅105,1≤m≤n). It is guaranteed that m is a divisor of n.

The second line of input contains n integers a1,a2,…,an (0≤ai≤109), the elements of the array.

Output
In the first line, print a single integer — the minimum number of moves required to satisfy the following condition: for each remainder from 0 to m−1, the number of elements of the array having this remainder equals nm.

In the second line, print any array satisfying the condition and can be obtained from the given array with the minimum number of moves. The values of the elements of the resulting array must not exceed 1018.

Examples
Input
6 3
3 2 0 6 10 12
Output
3
3 2 0 7 10 14
Input
4 2
0 1 2 3
Output
0
0 1 2 3

题意:

给n个数,对m取余数,(保证n % m = 0),有0 ~ m-1这m种情况,要使得每种情况的个数均为n / m,每次调整可以给某个数+1,问要达到题目描述的状态至少操作几次。

思路:

将一个余数为cur的数,变成余数为goal的数,需要的操作次数为(m - cur + goal)% m,这里加m的原因是,如果余数cur比goal大,比如cur = m - 2, goal = 1,那么首先要把cur变为0,需要m - cur,然后再加上goal,才可以。如果余数cur比goal小,那么直接用goal - cur即可。
要使得操作次数最小,那么当数量不足的时候,应当将它变成比它大的离它最近的一个数量不满的数,【如果最近的一个不满数都比它小,那么应当从前面找最小的数】。

这里我们可以一边输入一边处理,而不需要把所有数输入、统计完再计算。因为我们的每一次操作都是只+1,如果我一边输入一边处理时,把这个数a加成了另外一个数b,而在后面的输入中b其实是可以满足n/m个数量的,这不要紧,我们可以再对b中多出来的这个数(其实这个b就是a通过+1得到的),再执行+1操作变成c,这样其实和【把a直接+2变成c】没区别,只是将+1的两步分开操作了而已。

AC代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <vector>
#include <set>
#include <queue>
#define INF 0x3f3f3f3f

using namespace std;

const int maxn = 200005;
long long a[maxn];
int n, m, cnt[maxn];//记录每一种余数的个数
set<int> s;//用于存储数量还不满n/m的余数是哪些

int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        memset(cnt, 0, sizeof(cnt));
        s.clear();
        for(int i = 0; i < m; i++)
            s.insert(i);
        long long ans = 0;
        int need = n / m;
        for(int i = 0; i < n; i++)
        {
            scanf("%lld", &a[i]);
            int yu = a[i] % m;
            if(cnt[yu] < need)
            {
                if(++cnt[yu] == need)
                    s.erase(yu);
            }
            else//余数数量超过了,需要把它改变成别的数
            {
                int goal;
                if(yu > *s.rbegin())//这个余数比所有不满数量的余数都还大,那么应该从头开始找最小的数
                    goal = *s.begin();
                else
                    goal = *s.lower_bound(yu);//找到离这个余数最近的不满数量的余数
                int cost = (m + goal - yu) % m;
                ans += cost;
                a[i] += cost;
                if(++cnt[goal] == need)//因为我们将yu变成了goal,所以大小为goal的余数数量要+1,而yu不需要+1
                    s.erase(goal);
            }
        }
        printf("%lld\n", ans);
        for(int i = 0; i < n; i++)
            printf("%lld ", a[i]);
        printf("\n");
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/Floraqiu/article/details/81515468
今日推荐