题意
输入两个数m和n,数组a里有m个数,并且m/n=0
数组a0~am-1分别求余n,对应着m个余数,大小为从0到n-1
然后让每个余数的数量相等,且都等与m/n
ai中每个数字都只能增加1
问为了增加的最少次数是多少并且输出增加后的数组
n and m (1≤n≤2*10^5,1≤m≤n).
(0≤ai≤10^9)
研究题意就研究了好久。。。。
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小,就放入set中。
然后二分查找出大于该数的且余数个数小于n/m的数,把数组变化,同时更新对应值。
注意如果找不到大于该数的且余数个数小于n/m的数,就要set里的第一个值。(此时要增加的数最少,其他的增加的都比他多)
Code:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
#define ll long long
#define oo 0x3f3f3f3f
#define exp 1e-6
ll a[200000+5]; //原数组和更改以后的数组都是它
ll cnt[200000+5]; //这个数组用来记录每个余数的数量
int main()
{
set<ll>s;
ll n,m;
cin>>n>>m;
ll num=n/m; //每个余数的数量
memset(cnt,0,sizeof(cnt));
for(ll i=0;i<n;i++)
{
scanf("%lld",&a[i]);
cnt[a[i]%m]++;
}
for(ll i=0;i<m;i++)
{
if(cnt[i]<num)
s.insert(i); //数量不够的就插入set
}
ll ans=0; //记录最小变换次数
set<ll >::iterator it;
for(ll i=0;i<n;i++)
{
if(s.size()==0)
break;
ll x=a[i]%m;
if(cnt[x]<=num)
continue;
cnt[x]--; //原来的那个余数没有了,要变成新的了,所以一定要减!
it=s.lower_bound(x); //返回的为大于x的那个迭代器
if(it!=s.end()) //能找到大于x的
{
a[i]=a[i]+*it-x;
ans+=*it-x;
cnt[*it]++;
if(cnt[*it]==num)
s.erase(*it);
}
else //找不到大于x的,就找set里最小的那个
{
a[i]=a[i]+m-x+*s.begin();
ans=ans+m-x+*s.begin();
cnt[*s.begin()]++; //新的这个余数数量务必加1
if(cnt[*s.begin()]==num)
s.erase(*s.begin());
}
}
cout<<ans<<endl;
for(ll i=0;i<n;i++)
{
if(i!=n-1)
printf("%lld ",a[i]);
else
printf("%lld",a[i]);
}
return 0;
}