P7557 [USACO21OPEN] Acowdemia S 题解
huangboming · · 题解
前言
我有一个不用二分,极其简单的方法。
题意
给一个序列 1 1 2 2 3 这里最大的 2 2 3,而且 3,而且
分析
首先,我们观察题意。
我们不难发现,题目想我们这么做:
- 当
\ge h 的数的个数不\ge h 个时。 - 先算出在序列中小于
h 的同时最大的h-o 个数(o 为大于h 的数的个数)。 - 将这
h-o 个数的数字大小加起来,叫做sum 。 - 算出
h \times (h-o) 即所需大小的总和减sum 的值,叫做sumi 。 - 最后比较
sumi 与k ,l 大小关系,看一下操作数量够不够。
那么如何用最短的时间求最大的
- 我们可以先
sort排序数列保证有单调性。(从小到大) - 后算出前缀和。
- 从小到大枚举
h 指数,当a_i\ge i 时代表这个大小的h 合法,相反则代表需要进行操作使这h-o 个数加上sumi 。o 在第一次a_i<i 时使其等于i 。而后每次向前遍历o 直到a_o>i 。注意这里的o 无需更改,因为sort后的数列有单调性。 - 最后判断,当
k 要大于等于h-o 中的最小值,且\min(l,n)\times k\ge h\times(h-o) 。如果成立更新ans=i 即h 指数。最最后输出ans 即可。
于是,我们不难调出如下代码。但是,需要注意的是!要特判
时间复杂度及其证明
时间复杂度:
证明:用到了 sort 所以极限情况是
本人代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,a[2000005],x,ans,k,l,qzh[200005],m,o;
bool cmp(ll ai,ll bi){
return ai>bi;
}
int main(){
cin>>n>>k>>l;
if(l>n)l=n;//特判l大于n时则多余的为无意义要舍去
m=k*l;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
sort(a+1,a+1+n,cmp);//排序数列保证其有单调性(从小到大)
for(int i=1;i<=n;i++){
qzh[i]=qzh[i-1]+a[i];
}//求前缀和
for(int i=1;i<=n;i++){//从小到大枚举h
if(a[i]<i){
for(;a[o]<i&&o;o--){} //求大于h的数有哪些
if(i*(i-o)-qzh[i]+qzh[o]<=m&&i-a[i]<=k){//判断操作是否够
ans=i;//更新ans即最大的h指数。
}
}
else {
ans=i;//更新ans在无需操作时最大的h指数。
o=i;//记录>h的个数
}
}
cout<<ans;
return 0;
}