假设要求的数字是t
发现可以维护最少走了多少人 就是sigma max(0,a[i] - t)
然后二分答案找t就行了
当维护的这个值很大的时候 (大于k非法)就是说你给的t太小了 把二分的左边左移
反之右移
最后返回的r就是要求的满足条件的最小值
发现用两个后缀和 维护大于等于t的班级个数 已经 大于等于t的人数就行了
一定要弄清楚你二分数值的含义,我一开始没弄明白t到底是剩余的人数还是什么玩意的上界?
导致自己蠢了很长时间,还有就是当如下写二分模板的时候一定要把左右范围调整的比答案要大
不然会到不了
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N = 2e6+10;
int a[N],sum1[N],sum2[N];
int n,m,k;
bool check(int x,int id){
return sum2[x] - (a[id]>=x)*a[id] - x*(sum1[x]-(a[id]>=x))>k;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
int x;cin>>x;
a[x]++;//统计每个班的数量 a【i】 为i号班的数量
}
for(int i=1;i<=m;i++)
sum1[a[i]]++,
sum2[a[i]]+=a[i];
for(int i=n;i>=0;i--)
sum1[i]+=sum1[i+1],
sum2[i]+=sum2[i+1];
//最大上界的最小值r
for(int i=1;i<=m;i++){
int x = a[i];
int tem = n-k-x;
if(tem<0)cout<<-1<<" ";
else{
int l = -1,r = n+1;
while(l+1!=r){
int mid = l+r>>1;
if(check(mid,i))l = mid;
else r = mid;
}
cout<<r<<" ";
}
}
return 0;
}