CF1328F-Make k equal (补)(二分)
题目链接:
https://codeforces.com/contest/1328/problem/F
思路:
题目大意是你可以进行两种操作,一种是将最大数减1,另一种是将最小数加1,问你最少经过多少次操作,能使序列中存在k个相同的数
我们先将数组进行排序,然后用前缀和对数组进行预处理。
假设我们以a[i]这个数为标准,通过二分找到第一个大于a[i]的位置index,对于大于a[i]的数来说,我们首先通过操作将
a[index]-a[n]中的所有数变成a[i]+1,那么这一过程中消耗的操作数为(sum[n]-sum[index-1])-(n-index+1)*(x+1),
若原数组中有num个a[i]这样的数,那么我们接下来要消耗的操作数为min(k-num,n-index+1),如果此时a[i]的个数仍然
小于K,我们再考虑对小于a[i]的数进行操作,这里不再赘述,由于先找小的可能比先找大的更优,所以我又分情况讨论了,
代码很复杂,但看别人的代码好像很简单
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int MAXN=2e5+5; 5 const ll INF=3e18; 6 ll a[MAXN],n,k,sum[MAXN]; 7 ll ans=INF; 8 map<int,int>m; 9 ll check1(int x) 10 { 11 ll res=0;ll index;ll num=m[x]; 12 index=upper_bound(a+1,a+1+n,x)-a; 13 res+=(sum[n]-sum[index-1])-(n-index+1)*(x+1); 14 res+=min(k-num,n-index+1),num+=min(k-num,n-index+1); 15 if(num<k) 16 { 17 index=lower_bound(a+1,a+1+n,x)-a-1;//cout<<index<<" "<<num<<" "<<endl; 18 res+=index*(x-1)-sum[index]; 19 res+=k-num; 20 } 21 return res; 22 } 23 ll check2(int x) 24 { 25 ll res=0;ll index;ll num=m[x]; 26 index=lower_bound(a+1,a+1+n,x)-a-1; 27 res+=index*(x-1)-sum[index]; 28 res+=min(k-num,index),num+=min(k-num,index); 29 if(num<k) 30 { 31 index=upper_bound(a+1,a+1+n,x)-a;//cout<<index<<" "<<num<<" "<<endl; 32 res+=(sum[n]-sum[index-1])-(n-index+1)*(x+1); 33 res+=k-num; 34 } 35 return res; 36 } 37 int main() 38 { 39 ios::sync_with_stdio(false); 40 cin.tie(0); 41 cin>>n>>k; 42 for(int i=1;i<=n;i++) 43 { 44 cin>>a[i];m[a[i]]++; 45 if(m[a[i]]>=k) 46 { 47 cout<<"0"<<endl; 48 return 0; 49 } 50 } 51 sort(a+1,a+1+n); 52 for(int i=1;i<=n;i++) 53 sum[i]=sum[i-1]+a[i]; 54 for(int i=1;i<=n;i++) 55 ans=min(ans,min(check1(a[i]),check2(a[i]))); 56 cout<<ans<<endl; 57 return 0; 58 }