題目鏈接:https://atcoder.jp/contests/agc041/tasks/agc041_b
題目大意:
有n個問題,m個裁判,每個裁判會給v個不同的問題投票,投一票加一分;同時每個問題還有一個ai作爲初始分數,現在在所有裁判投票完成後,選取前p大的問題,如果a[p]==a[p+1]==a[p+2]這樣的話,相同的分數都會選擇進來,問你有多少個問題可能被選擇?
思路:
比賽時二分寫醜了,改了一下就過了。。。
首先肯定按照初始分數排序,這裏按照從大到小排,每個問題最多被加m分(m個裁判都選擇了這個問題),且最多會有v個問題被加m分。
如果a[i]這個問題可以,那麼分數>a[i]的肯定也可以,所以我們考慮二分答案mid;
如何判斷mid這個位置可不可以被選呢?
- 如果mid<=p,那麼一定是可以的。
- 如果a[mid]+m<a[p],不管怎麼加肯定不行,一定是不可以的。
- 剩下的情況,如果可以被選,那麼我最優的選擇方法肯定是a[1],a[2],a[3]........a[p-1],a[mid]。換句話說,我只需要保證a[mid]在所有加分完成之後比a[p]大就可以了。
現在考慮如何加分是最優的,我們一共有m*v分,首先將m分加給a[mid],然後有如下策略:
- 如果我將m分加給a[1],a[2]....a[p-1],並不會影響我與a[p]比較,這樣做可以捨去(p-1)*v分。
- 如果我將m分加給a[mid+1],a[mid+2].....a[n],也不會有影響,因爲不管怎麼加,a[mid]+m>=a[mid+1]+m;這樣做可以捨去(n-mid)*v分
- 剩下的分,我們平攤給p<=i<mid這些元素,保證每個元素加的分最多爲min(m,a[mid]+m-a[i]),也就是每個元素加分後不能大於a[mid]+m;
這樣加完分之後,如果分數還有剩餘,那說明一定有一個在p之後的元素會加分加到大於a[mid]+m,這種情景不行;否則,mid就是合法的。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e5+10;
int a[maxn];
int cmp(const int a,const int b){
return a>b;
}
int n,m,v,p;
int b[maxn];
bool judge(int mid){
//printf("hello %lld\n",mid);
if(mid<=p)return true;
if(a[mid]+m<a[p])return false;
if(v<=p-1&&a[mid]+m>=a[p])return true;
for(int i=1;i<=n;i++)b[i]=a[i];
int temp=0;
for(int i=1;i<=p-1;i++){
b[i]+=m;
temp+=m;
}
//給開頭的p-1個元素加值
int inx=n,cnt=v-p+1;
//給後面的元素加值
while(inx>mid&&cnt>1){
temp+=m;
inx--;
cnt--;
b[inx]+=m;
}
temp+=m;
int now=a[mid]+m;
int res=m*v-temp;
//中間還能被填多少
for(int i=p;i<mid;i++){
int add=min(m,max(now-a[i],0ll));
if(add>res)add=res;
res-=add;
b[i]+=add;
}
return now>=b[p]&&res<=0;
}
signed main(){
cin>>n>>m>>v>>p;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1,cmp);
int low=1,high=n;
int ans=0;
while(low<=high){
int mid=(low+high)>>1;
if(judge(mid)){
low=mid+1;
ans=mid;
}
else{
high=mid-1;
}
}
cout<<ans<<endl;
return 0;
}