Codeforces1328F. Make k Equal(思维/前缀后缀)

Description

You are given the array a consisting of n elements and the integer k≤n.

You want to obtain at least k equal elements in the array a. In one move, you can make one of the following two operations:

Take one of the minimum elements of the array and increase its value by one (more formally, if the minimum value of a is mn then you choose such index i that ai=mn and set ai:=ai+1);
take one of the maximum elements of the array and decrease its value by one (more formally, if the maximum value of a is mx then you choose such index i that ai=mx and set ai:=ai−1).
Your task is to calculate the minimum number of moves required to obtain at least k equal elements in the array.

Input

The first line of the input contains two integers n and k (1≤k≤n≤2⋅105) — the number of elements in a and the required number of equal elements.

The second line of the input contains n integers a1,a2,…,an (1≤ai≤109), where ai is the i-th element of a.

Output

Print one integer — the minimum number of moves required to obtain at least k equal elements in the array.

题目大意

有n个数字,可以进行两种操作,将最大的减1,将最小的加1.需要获得k个相等的数字,输出最少需要的操作数

思路

先排序,维护前缀pre和后缀suf
枚举每一个可能的数字x,有四种可能的情况
1.原本的数量就大于等于k(ans=0)
2.将左边小的加成x
3.将右边大的减成x
4.左边小的加成x,右边大的减成x(这种情况只存在于单操作左或者右都不够k的时候(不然只操作一边更优))

具体的:
变量数组,得到某一数字x的最前和最后下标 l,r 区间[l,r]都是数字x
对于将左边全部加成x的操作数为a[l]×l-pre[l]
当然可能前面的数字挺多的,大于k了那就反悔,将多出来的退一步(减一,不能减更多了)。
右边同理:suf[r]-(n-r+1)×a[r];
两边:左边+右边…

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5;
const ll inf=0x3f3f3f3f3f3f3f;
ll a[maxn],pre[maxn],suf[maxn];
int main()
{
    int n,k;
    scanf("%d %d",&n,&k);
    for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;++i)pre[i]=pre[i-1]+a[i];
    for(int i=n;i>=1;--i)suf[i]=suf[i+1]+a[i];
    ll ans=inf,num;
    for(int i=1;i<=n;++i){
        int l=i,r=i;
        while(r<n&&a[r]==a[r+1])++r;
        i=r;
        if(r-l+1>=k){
            ans=0;
            break;
        }
        if(r>=k){//左边够
            num=l*a[l]-pre[l];//l左边移动到l
            num=num-(r-k);//多余的移动反悔
            ans=min(ans,num);
        }
        if(n-l+1>=k){//右边够
            num=suf[r]-(n-r+1)*a[r];
            num=num-((n-l+1)-k);//多余的移动反悔
            ans=min(ans,num);
        }
        if(r<k&&n-l+1<k){//需要两边
            ll num1=l*a[l]-pre[l];//l左边移动到l
            ll num2=suf[r]-(n-r+1)*a[r];
            num=num1+num2-(n-k);
            ans=min(ans,num);
        }
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43984169/article/details/105707055