[CodeForces 999D] Equalize the Remainders

You are given an array consisting of n n n integers a a a1, a a a2,…, a a an, and a positive integer m m m. It is guaranteed that m m m is a divisor of n n n.In a single move, you can choose any position i i i between 11 and n n n and increase a a ai by 1 1 1.Let’s calculate C C Cr ( 0 ≤ r ≤ m − 1 ) (0≤r≤m−1) (0rm1) — the number of elements having remainder r r r when divided by m m m. In other words, for each remainder, let’s find the number of corresponding elements in a a a with that remainder.Your task is to change the array in such a way that C C C0= C C C1=⋯= C C Cm-1= n / m n/m n/m.Find the minimum number of moves to satisfy the above requirement.
Input
The first line of input contains two integers n n n and m m m
( 1 ≤ n ≤ 2 ⋅ 105 , 1 ≤ m ≤ n 1 ≤ n ≤ 2 ⋅ 105 , 1 ≤ m ≤ n ) (1≤n≤2⋅105,1≤m≤n1≤n≤2⋅105,1≤m≤n) (1n2105,1mn1n2105,1mn). It is guaranteed that m m m is a divisor of n n n.The second line of input contains n n n integers a a a1, a a a2,…, a a an (0≤ a a ai≤109), the elements of the array.OutputIn the first line, print a single integer — the minimum number of moves required to satisfy the following condition: for each remainder from 0 0 0 to m − 1 m−1 m1, the number of elements of the array having this remainder equals n / m n/m 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 1018.
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 0 0 to m − 1 m−1 m1, the number of elements of the array having this remainder equals n / m n/m 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 1018.

题意
给一个n及一个m,保证m为n的因子,接下来给n个数 C C C1 ~ C C Cn。在有限步操作后使这n个数中模 m m m 0 0 0~ m − 1 m-1 m1的数个数均为 n / m n/m n/m个。每步操作可以任选一个数+1。问最少多少步操作可实现这个目标。输出这个最少步数及修改后的数列。
sample input

6 3 
3 2 0 6 10 12 

sample output

3
3 2 0 7 10 14 

sample input

4 2
0 1 2 3

sample output

0
0 1 2 3 

这题思路就是简单贪心,对于每个数 a a ai,判断在这之前是否已经有 n / m n/m n/m个数模 m m m的余数为 a a ai% m m m的数,如果有就把 a a ai+1直到之前没有 n / m n/m n/m个数模 m m m的余数为 a a ai% m m m为止,同时把操作步数计入 a n s ans ans,最后输出 a n s ans ans即可。
最开始用数组模拟指针的形式写了个程序, b [ i ] b[i] b[i]记录多余多少个余数为 i i i 的数, p [ i ] [ 0 ] p[i][0] p[i][0]指向 i i i之前最近的一个 p [ ] p[] p[]不为0的值, p [ i ] [ 1 ] p[i][1] p[i][1]指向 i i i之后最近的一个 p [ ] p[] p[]不为0的值,然而TLE了。。。

#include<stdio.h>
long long a[200010],b[200010];
long long n,m;
long long target,temp,s,z;
long long ans;
long long p[200010][2];//¨ºy¡Á¨¦?¡ê?a???? 
long long abs(long long a)
{
 if (a<0) return -a;else return a;
}
void change(long long i)
{
 p[p[i][0]][1]=p[i][1];
 p[p[i][1]][0]=p[i][0];
 return;
}
int main()
{
 while(scanf("%lld%lld",&n,&m)!=EOF)
 {
  ans=0;
  target=n/m;
  for(long long i=0;i<n;i++)
  {
   scanf("%lld",&a[i]);
   b[a[i]%m]++;
  }
  for(long long i=0;i<m;i++)p[i][1]=(i+1)%m,p[i][0]=(i+m-1)%m;
  for(long long i=0;i<m;i++) {b[i]-=target;if(!b[i])change(i);}
  for(long long i=0;i<n;i++)
  {
   temp=a[i]%m;
   if(b[temp]>0)
   {
    s=p[temp][1];
    while(b[s]>=0)s=p[s][1];  
    ans+=(s+m-temp)%m;
    a[i]+=(s+m-temp)%m;
    b[temp]--;
    b[s]++;
    if(!b[temp]) change(temp);
    if(!b[s]) change(s);
   }
  }
  printf("%lld\n",ans);
  for(long long i=0;i<n;i++) printf("%lld ",a[i]);
  printf("\n");
 }
 return 0;
}

然后用STL map重写了一个,思路没有变

#include<stdio.h>
#include<map>
using namespace std;
long long a[200010],b[200010];
long long n,m;
long long target,temp,s;
long long ans;
map<long long,int>z;
int main()
{
 scanf("%lld%lld",&n,&m);
 ans=0;
 target=n/m;
 for(long long i=0;i<n;i++)
 {
  scanf("%lld",&a[i]);
 }
 for(long long i=0;i<m;i++)z[i]=target;
 for(long long i=0;i<n;i++)
 {
  long long c;
  temp=a[i]%m;
  if(temp>(*z.rbegin()).first) {c=(*z.begin()).first;(*z.begin()).second--;if(!(*z.begin()).second) z.erase((*z.begin()).first);}
  else {c=(*z.lower_bound(temp)).first;(*z.lower_bound(temp)).second--;if(!(*z.lower_bound(temp)).second) z.erase((*z.lower_bound(temp)).first);}
  ans=ans+(c+m-temp)%m;
  a[i]=a[i]+(c+m-temp)%m;
 }
 printf("%lld\n",ans);
 for(long long i=0;i<n;i++) printf("%lld ",a[i]);
 printf("\n");
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值