https://www.luogu.com.cn/problem/AT5693
题意:
给出一个数组,有m次操作,每次选中V个数+1,然后选中最大的P个数(相同的数随意排序),问多少个位置的数可能被选中
解析:
排序后,考虑第 i i i个数是否可能被选中:
- 首先a[i]这个数每次都会被选中,所以要求操作后大于 a [ i ] + m a[i]+m a[i]+m的数少于 P P P个。
- 也就是说对于大的数,尽可能分配少一点。
- 小于等于 a [ i ] a[i] a[i]的可以每次全部选。
- 大于 a [ i ] + m a[i]+m a[i]+m的数不管分不分都大于 a [ i ] + m a[i]+m a[i]+m,所以也是每次选。
- 所以考虑的区间要求 a [ i ] < a [ j ] ≤ a [ i ] + m a[i]< a[j]\leq a[i]+m a[i]<a[j]≤a[i]+m,这个用尺取法维护
- 区间内二分得到至少多少个数加完后大于
a
[
i
]
+
m
a[i]+m
a[i]+m,这些数默认分配
m
m
m
- 区间内大于等于 a [ i ] + m a[i]+m a[i]+m的可以分配 a [ i ] + m − a [ j ] a[i]+m-a[j] a[i]+m−a[j],这个用前缀和直接得到
代码:
/*
* Author : Jk_Chen
* Date : 2020-10-07-12.07.54
*/
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define LD long double
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<"> "<<x<<" ";test(args...);}
const LL mod=1e9+7;
const int maxn=1e5+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
#define rd rd()
/*_________________________________________________________begin*/
LL n=rd,m=rd,v=rd,p=rd;
LL a[maxn];
LL sum[maxn];
inline LL segsum(int l,int r){return sum[r]-(l==1?0:sum[l-1]);}
int main(){
rep(i,1,n)a[i]=rd;
sort(a+1,a+1+n);
rep(i,1,n)sum[i]=sum[i-1]+a[i];
int mx=a[n];
int l=1,r=0;
int ans=0;
rep(i,1,n){
if(a[i]==mx){ans++;continue;}
/// a[j]>a[i]
while(a[l]==a[i])l++;
/// a[j]<=a[i]+m
while(r<n&&a[r+1]<=a[i]+m)r++;
LL res=v-(l-1);
LL g=n-r;
res-=g;
/// [l,r]
if(l<=r&&res>0){
/// 二分区间内后面_ans个数直接+m
int L=0,R=r-l+1,_ans=-1;
while(L<=R){
int mid=L+R>>1;
LL left=1ll*(r-l+1-mid)*(a[i]+m)-segsum(l,r-mid);
if((res-mid)*m<=left){
_ans=mid;
R=mid-1;
}
else{
L=mid+1;
}
}
g+=_ans;
}
if(g<p)ans++;
}
printf("%d\n",ans);
return 0;
}
/*_________________________________________________________end*/