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大数字,可以靠f[i]数组轻易求出大于num[i]的数字个数和小于num[i]的数字个数

2. 用一个树状数组维护  cur代表离散化后一共cur个不同的数字。

3.对于任意第i大数字,若k<=cnt[i],则ans = 0,不用做任何操作,这是很显然的结论

4.对于任意第i大数字,若k>cnt[i],设 d = k - cnt[i],分为以下三种情况讨论:

(i) 比第i个数字大的数字有大于d个时,可以把大于num[i]的数字移动到num[i]+1的位置,这个过程的花费设为sum,再把d个数字移动到num[i],花费sum+d,则即可满足条件

(ii)比第i个数字小的数字有大于d个时,可以把小于num[i]的数字移动到num[i]-1的位置, 这个过程的花费设为sum,再把d个数字移动到num[i],花费sum+d,则即可满足条件

(iii)比第i个数字大的数字小于d个和比第i个数字小的数字小于d个同时发生,那么需要把大于num[i]和小于num[i]的数字全部分别移动到num[i]+1和num[i]-1,这整个过程花费记为sum3,再把d个数字移动到num[i],总花费sum3+d,满足条件。

针对每个num[i]做以上三种操作,取min即可

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn = 2e5+5;
 5 ll c[maxn],a[maxn],f[maxn];
 6 int n,k;
 7 struct node{
 8     ll cnt,num;
 9     bool operator < (const node &b){
10         return num<b.num ;
11     }
12 }p[maxn],t[maxn];
13 ll lowbit(ll x){
14     return x& -x;
15 }
16 void add(ll x,ll k){
17     while(x<=n){
18         c[x] = c[x] + k;
19         x = x + lowbit(x);
20     }
21 }
22 ll getsum(ll x){
23     ll ans = 0;
24     while(x>=1){
25         ans +=c[x];
26         x = x-lowbit(x);
27     }
28     return ans;
29 }
30 int main(){
31     scanf("%d%d",&n,&k);
32     for(int i = 1;i<=n;i++) {
33        scanf("%lld",&p[i].num );
34     }
35     sort(p + 1,p + 1 +n);//离散化
36     t[1].num = p[1].num,t[1].cnt = 1,f[1] = 1;
37     int cur = 1;
38     for(int i = 2;i<=n;i++){
39         if(p[i].num !=t[cur].num ) t[cur+1].num = p[i].num ,t[cur+1].cnt = 1,cur++,f[cur]=f[cur-1]+1;
40         else t[cur].cnt ++,f[cur]++;//处理f函数和t数组
41     }
42     for(int i = 1;i<=cur;i++){
43         add(i,t[i].cnt *t[i].num );//维护前缀和
44     }
45     ll ans = 1e15+5;
46     for(int i = 1;i<=cur;i++){
47         if(k<=t[i].cnt ) {ans = 0;break;} //存在k>=cnt[i]直接break,ans = 0
48         else{
49             ll d = k - t[i].cnt ;//剩余需要d个移动到num[i]
50             if(f[cur]-f[i]>=d){
51                 ll sum = getsum(cur) - getsum(i) - (f[cur]-f[i]) *(t[i].num+1);
52                 ans = min(ans,sum+d);
53
54             }
55             if(f[i-1]>=d){
56                 ll sum = f[i-1]*(t[i].num -1)- getsum(i-1);
57                 ans = min(ans,sum+d);
58             }
59             if(f[cur]-f[i]<d && f[i-1]<d){
60                 ll sum = getsum(cur) - getsum(i) - (f[cur]-f[i]) *(t[i].num+1);
61                 sum = sum + f[i-1]*(t[i].num-1) - getsum(i-1);
62                 ans = min(ans,sum+d);
63             }
64         }
65     }
66     cout<<ans;
67     return 0;
68 }

原文地址:https://www.cnblogs.com/AaronChang/p/12590292.html

时间: 2024-12-18 02:39:31

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

Codeforces Round #225 (Div. 1) C. Propagating tree dfs序+树状数组

C. Propagating tree Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/383/problem/C Description Iahub likes trees very much. Recently he discovered an interesting tree named propagating tree. The tree consists of n nodes numb

Codeforces Round #609 (Div. 2)E--K Integers(贪心+二分+树状数组+逆序对)

K Integers 参考博客:https://blog.csdn.net/Q755100802/article/details/103664555 [题意] 给定一个1到n的排列,可以交换相邻的两个元素. 现在定义一个函数f(x),表示在原排列中,通过交换操作,形成一个1,2,3....x的排列的子串,需要的最小操作步骤. 子串意味着这个排列必须是相邻的.现在你需要求出f(1),f(2),f(3)......f(n). [分析] 在1~x这几个元素相邻的情况下,因为最后排列不存在逆序对,根据贪

Codeforces Round #227 (Div. 2)---E. George and Cards(贪心, 树状数组+set维护, 好题!)

George is a cat, so he loves playing very much. Vitaly put n cards in a row in front of George. Each card has one integer written on it. All cards had distinct numbers written on them. Let's number the cards from the left to the right with integers f

Codeforces Round #365 (Div. 2) D. Mishka and Interesting sum 树状数组

D. Mishka and Interesting sum 链接: http://codeforces.com/problemset/problem/703/D 题意: 给一个序列 每次询问一个区间 求区间中出现次数为偶数次的数的异或和 代码: 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<map> 5 using namespace std; 6 7 str

Codeforces Round #365 (Div. 2) D.Mishka and Interesting sum 树状数组+离线

D. Mishka and Interesting sum time limit per test 3.5 seconds memory limit per test 256 megabytes input standard input output standard output Little Mishka enjoys programming. Since her birthday has just passed, her friends decided to present her wit

Codeforces Round #486 (Div. 3) F. Rain and Umbrellas

Codeforces Round #486 (Div. 3) F. Rain and Umbrellas 题目连接: http://codeforces.com/group/T0ITBvoeEx/contest/988/problem/E Description Polycarp lives on a coordinate line at the point x=0. He goes to his friend that lives at the point x=a. Polycarp can

Codeforces Round #501 (Div. 3) F. Bracket Substring

题目链接 Codeforces Round #501 (Div. 3) F. Bracket Substring 题解 官方题解 http://codeforces.com/blog/entry/60949 ....看不懂 设dp[i][j][l]表示前i位,左括号-右括号=j,匹配到l了 状态转移,枚举下一个要填的括号,用next数组求状态的l,分别转移 代码 #include<bits/stdc++.h> using namespace std; const int maxn = 207;

Playrix Codescapes Cup (Codeforces Round #413, rated, Div. 1 + Div. 2) C. Fountains 【树状数组维护区间最大值】

题目传送门:http://codeforces.com/contest/799/problem/C C. Fountains time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Arkady plays Gardenscapes a lot. Arkady wants to build two new fountains. The

CodeForces 61E Enemy is weak 求i&lt;j&lt;k &amp;&amp; a[i]&gt;a[j]&gt;a[k] 的对数 树状数组

题目链接:点击打开链接 题意是求 i<j<k && a[i]>a[j]>a[k] 的对数 如果只有2元组那就是求逆序数的做法 三元组的话就用一个树状数组x表示 数字i前面有多少个比自己大的个数 然后每次给这个y数组求和,再把x中>a[i]的个数存入y中即可 #include <algorithm> #include <cctype> #include <cassert> #include <cstdio> #in