码迷,mamicode.com
首页 > 其他好文 > 详细

CF-1328 F. Make k Equal

时间:2020-04-06 00:10:19      阅读:98      评论:0      收藏:0      [点我收藏+]

标签:namespace   using   多少   turn   选择   log   include   cto   操作   

F. Make k Equal

题目链接

题意

长度为n的序列,每次可以选择一个最大的数字将其减一或者选择一个最小的数字将其加一,问最少操作多少次可以使得序列中至少存在 k 个一样的数字

分析

官方题解:http://codeforces.com/blog/entry/75246

可以想到最后一样的数字,一定是在原序列里面出现的,所以将原数组离散化之后,枚举最后一样的数字,并努力把它凑够 k 个。如何凑?借助左侧或者右侧的数字。只要借助了某侧的数字,那么这一侧全部的数字都要先挪动它旁边的那个位置,然后再按照需要搬到当前这个位置上来。

所以最后只会有两种方案:优先选左侧或者优先选右侧,我们都试一下就好了,结果取最小(计算细节可以参考代码以及官方题解)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
#define dbg(x...) do { cout << "\033[32;1m" << #x <<" -> "; err(x); } while (0)
void err() { cout << "\033[39;0m" << endl; }
template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
const int N = 200000 + 5;
int n, k, a[N];
vector<int> v;
ll cnt[N], sum[N];
ll pre[N];

int main(){
    scanf("%d%d", &n, &k);
    for(int i=1;i<=n;i++){
        scanf("%d", &a[i]);
        v.push_back(a[i]);
    }
    v.push_back(0);
    sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());
    for(int i=1;i<=n;i++){
        int id = lower_bound(v.begin(),v.end(),a[i]) - v.begin();
        cnt[id] ++;
    }
    n = v.size()-1;
    for(int i=1;i<=n;i++) {
        sum[i] = sum[i-1] + cnt[i];
        pre[i] = pre[i-1] + cnt[i] * v[i];
    }
    ll res = pre[n];
    for(int i=1;i<=n;i++){
        ll need = max(0ll, k - cnt[i]);
        // 先左后右
        {
            ll tmp = 0;
            int lnum = min(need, sum[i-1]);
            // 只有当lnum>0时才需要计算,下面同理
            if(lnum){
                tmp = sum[i-1] * (v[i]-1) - pre[i-1];
                tmp += lnum;
            }
            int rnum = min(need-lnum, sum[n] - sum[i]);
            if(rnum){
                tmp += (pre[n] - pre[i]) - (sum[n]-sum[i]) * (v[i]+1);
                tmp += rnum;
            }
            res = min(res, tmp);
        }
        // 先右后左
        {
            ll tmp = 0;
            int rnum = min(need, sum[n] - sum[i]);
            if(rnum){
                tmp = (pre[n] - pre[i]) - (sum[n] - sum[i]) * (v[i]+1);
                tmp += rnum;
            }
            int lnum = min(need-rnum, sum[i-1]);
            if(lnum){
                tmp += sum[i-1] * (v[i]-1) - pre[i-1];
                tmp += lnum;
            }
            res = min(res, tmp);
        }
    }
    cout << res << endl;
    return 0;
}

2400难度的题目,平常心看待就好了

CF-1328 F. Make k Equal

标签:namespace   using   多少   turn   选择   log   include   cto   操作   

原文地址:https://www.cnblogs.com/1625--H/p/12639963.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!