题目链接:https://ac.nowcoder.com/discuss/437998
题目大意:
思路:我们按价值排序,枚举中位数,那么中位数左右选择大小最小的m/2(m/2-1)个数。如果和<=s那么可以满足条件。
现在就是预处理s1[i]:1-i选择m个物品,价值最小的和。s[i]:n-i选择m个物品,价值最小的和。
我们用优先队列来维护m个最小的和。预处理后就枚举。
m是奇数:那么枚举的数就是中位数。
m是偶数:我们希望右边的最小值越小越好。并且s2[i]:从1-n一定是单调递增。那么我们二分下。右边选取的位置就可以了。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
struct node{
LL a, b;
}a[100005];
LL s1[100005], s2[100005];
priority_queue<LL> q;
LL slove(LL l, LL r, LL x){
LL ans=-1;
while(l<=r){
LL mid=l+r>>1;
if(s2[mid]<=x){
l=mid+1;
ans=a[mid].a;
}
else{
r=mid-1;
}
}
return ans;
}
int main(){
LL v, n, m; scanf("%lld%lld%lld", &v, &n, &m);
LL pos=!(m%2); m/=2;
for(int i=1; i<=n; i++){
scanf("%lld%lld", &a[i].a, &a[i].b);
}
sort(a+1, a+1+n, [](node &a, node &b){return a.a<b.a;});
for(int i=1; i<=n; i++){//预处理左半部分
q.push(a[i].b), s1[i]+=s1[i-1]+a[i].b;
if(q.size()>m-pos){
s1[i]-=q.top(); q.pop();
}
}
while(!q.empty()) q.pop();
for(int i=n; i>=1; i--){//预处理右半部分
q.push(a[i].b), s2[i]+=s2[i+1]+a[i].b;
if(q.size()>m){
s2[i]-=q.top(); q.pop();
}
}
LL ans=*0;
if(!pos){
for(int i=m+1; i<=n-m; i++){
if(s1[i-1]+s2[i+1]+a[i].b<=v){
ans=a[i].a;
}
}
printf("%lld\n", ans);
}
else{
for(int i=m; i<=n-m; i++){
LL s=slove(i+1, n-m+1, v-s1[i-1]-a[i].b);
if(ans!=-1){
ans=max(ans, a[i].a+s);
}
}
printf("%lld\n", ans/2);
}
return 0;
}

京公网安备 11010502036488号