CF-1328 F. Make k Equal

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难度的题目,平常心看待就好了

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

时间: 2024-11-09 10:32:07

CF-1328 F. Make k Equal的相关文章

Codeforces Round #629 (Div. 3) F - Make k Equal (离散化 树状数组维护前缀和)

https://codeforces.com/contest/1328/problem/F 首先把a数组处理成pair对(num,cnt),表示数字num有cnt个,然后按num升序排序离散化一下. 对于一个数x,若想使得小于x的数字都变成x,必须先把所有小于x的数变成x-1,然后再+1变成x. 同理,要使得大于x的数变成x,必须把所有大于x的数字变成x+1,然后再-1变成x. 以上是题意所要求的必须操作. 思路: 1. 用f[i]数组记录离散化后前i大的数字的总数,那么对于任意第i大数字,可以

hdu 1588 求f(b) +f(k+b) +f(2k+b) +f((n-1)k +b) 之和 (矩阵快速幂)

g(i)=k*i+b; 0<=i<nf(0)=0f(1)=1f(n)=f(n-1)+f(n-2) (n>=2)求f(b) +f(k+b) +f(2*k+b) +f((n-1)*k +b) 之和 Sample Input2 1 4 100 // k b n MOD2 0 4 100 Sample Output2112 矩阵A      相当于 1 1          f(2)  f(1) 1 0          f(1)  f(0) | 1       1| ^b          |

约瑟夫环 数学解法 f(n,k)=(f(n-1,k)+k)%n 公式讲解

问题:有n个人站成环 从1开始报数,报k的人去死,之后下一个人报1,问当你是第几个的时候可以活下来? 这篇文章主要是讲解  f(n,k)=(f(n-1,k)+k)%n 这个公式是什么意思为什么是对的 虽然公式是使用数学解法 但开始时我会手动的模拟过程 其是有意义的 十分有助于理解 首先我们看样一个问题 n=2, k=3 a b 我们首先使用人力来数 a b a 很好 a死 接下来在试一遍 n=2 k=4 a b 人力:a b a b 很好b死 n=2 k=5 人力 a b a b a 很好a死

CF 494 F. Abbreviation(动态规划)

题目链接:[http://codeforces.com/contest/1003/problem/F] 题意:给出一个n字符串,这些字符串按顺序组成一个文本,字符串之间用空格隔开,文本的大小是字母+空格的个数.在这个文本中找k(k>=2)个区间,使得这k个区间完全相同,字符串不能分开,然后把每段的字符串变成单个的字符,并去掉中间的空格.可能有多种方案,求文本的最小长度.[表达能力有限,望理解,具体可以看题目] You are given a text consisting of nn space

Make k Equal from Codeforces Round #629 (Div. 3)

description you are given an array and you are asked to make \(k\) elements of it equal after some operations. you can make one of the following operations: take one of the minimum and increase its value by \(1\) take one of the maximum and subtract

698. Partition to K Equal Sum Subsets

问题描述: Given an array of integers nums and a positive integer k, find whether it's possible to divide this array into k non-empty subsets whose sums are all equal. Example 1: Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4 Output: True Explanation: It's po

CF - 1117 F Crisp String

题目传送门 题解: 枚举非法对. 如果 ‘a'  和 ’b' 不能相邻的话,那么删除 'a' 'b'之间的字符就是非法操作了. 假设题目给定的字符串为 "acdbe",所以删除cd是非法操作, 因为cd是非法了,所以cde也是非法操作, 也就是说找到所有的非法操作之后往外推,比他多删的状态就一样是非法的了,当然对于上述的“acdbe"来说,不能确定 ”acd"是非法操作,因为在枚举非法对的时候,该非法对的字符并不能被删除. 然后把所有非法对的非法状态都存下来.然后从

[Swift]LeetCode698. 划分为k个相等的子集 | Partition to K Equal Sum Subsets

Given an array of integers nums and a positive integer k, find whether it's possible to divide this array into k non-empty subsets whose sums are all equal. Example 1: Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4 Output: True Explanation: It's possible

Partition to K Equal Sum Subsets

Given an array of integers nums and a positive integer k, find whether it's possible to divide this array into k non-empty subsets whose sums are all equal. Example 1: Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4 Output: True Explanation: It's possible