CodeForces 999D 贪心+set+二分

54 篇文章 1 订阅

http://codeforces.com/problemset/problem/999/D

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

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

Let's calculate crcr (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=n/m .

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

Input

The first line of input contains two integers nn and mm (1≤n≤2⋅10^5,1≤m≤n). It is guaranteed that m is a divisor of n .

The second line of input contains nn integers a1,a2,…,an(0≤ai≤10^9), 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 n/m .

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 10^18 .

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个数字,(输出保证m整除n),第i组的所有数%m的结果为i-1。第i组的数不一定要连续放在一起,你可以对原序列任意一个数进行无限制的加1操作,问你至少需要多少次操作才能得到这样的一个序列。

思路:用set<int> s来存储未分配好的余数,初始化把0到m-1全部插入到s中,下面每读取一个数字x,先令t=x%m,然后在s中二分搜索第一个大于等于t的数。(贪心的体现,这样的操作数肯定最少)需要注意的是t可能大于集合s中的最大值,因此需要一个特判:t>*s.rbegin()。(rbegin()是反向迭代器,记录了容器的最后一个元素,那么rend()记录的自然是容器最前面的元素的前一个位置。)

补充:这道题刚开始也是不会做,上网看了题解才明白,发现一个博主的代码写的特别精妙,很短而且易读性很好,借鉴了一下,下面的链接是那个博主的题解。

https://blog.csdn.net/ZscDst/article/details/80792706

#include<iostream>
#include<set>
#include<cstdio>
typedef long long ll;
using namespace std;

set<int> s;	//余数为i的组且该组元素个数小于n/m
ll a[200005],b[200005],n,m,cnt;

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",&b[i]);
		int c=b[i]%m;
		int it;
		if(c>*s.rbegin())//大于当前最大的余数  rbegin()指向容器的最后一个元素
			it=*s.begin();
		else
			it=*s.lower_bound(c);
		if(++a[it]==n/m)
			s.erase(it);
		b[i]+=(it-c+m)%m;
		cnt+=(it-c+m)%m;
	}
	printf("%lld\n",cnt);
	for(int i=0;i<n;++i)
		printf("%lld ",b[i]);
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值