Description

Applese有1个容量为v的背包,有n个物品,每一个物品有一个价值ai,以及一个大小bi
然后他对此提出了自己的疑问,如果我不要装的物品装的价值最大,只是一定需要装m个物品,要使得求出来的物品价值的中位数最大
Applese觉得这个题依然太菜,于是他把这个问题丢给了你
当物品数量为偶数时,中位数即中间两个物品的价值的平均值

Solution

想起了之前也有道中位数的题,中位数问题可以用堆来优化,这道题要分类讨论,奇偶的情况不太一样
对于中位数,我们可以维护左边 个最少花费和右边 个最少花费,然后遍历取最优
最少花费可以维护一个前缀和与一个后缀和
然后分奇偶

  • 奇数: 直接做
  • 偶数:在奇数的前提下,由于我们对价值排了序,满足单调性,可以二分找到第二个数字

Code

#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;

const int N = 1e5 + 5;
struct node {
    long long l, r;
    bool operator < (const node &s) {
        return l < s.l;
    }
}a[N];

long long sum1[N], sum2[N];
int main() {
    long long v; int n, m;
    cin >> v >> n >> m;
    for(int i = 1; i <= n; i++) {
        cin >> a[i].l >> a[i].r;
    }
    sort(a + 1, a + 1 + n);
    long long ans = -1;
    priority_queue<long long> q;
    if(m & 1) {    
        for(int i = 1; i <= n; i++) {
            sum1[i] = sum1[i - 1] + a[i].r; // 维护 m / 2 个小值的最少花费 
            q.push(a[i].r);
            if(q.size() > m / 2) {
                sum1[i] -= q.top(); q.pop();
            }
        }
        while(!q.empty()) q.pop();
        for(int i = n; i >= 1; i--) {
            sum2[i] = sum2[i + 1] + a[i].r;
            q.push(a[i].r);
            if(q.size() > m / 2) {
                sum2[i] -= q.top(); q.pop(); // 维护 m / 2 个大值的最少花费 
            } 
        }
        for(int i = m / 2 + 1; i <= n - m / 2; i++) {
            if(a[i].r + sum1[i - 1] + sum2[i + 1] <= v) {
                ans = max(ans, a[i].l);
            }
        } 
    } else {
        for(int i = 1; i <= n; i++) {
            sum1[i] = sum1[i - 1] + a[i].r;
            q.push(a[i].r);
            if(q.size() > m / 2 - 1) {
                sum1[i] -= q.top(); q.pop();
            }
        }
        while(!q.empty()) q.pop();
        for(int i = n; i >= 1; i--) {
            sum2[i] = sum2[i + 1] + a[i].r;
            q.push(a[i].r);
            if(q.size() > m / 2) {
                sum2[i] -= q.top(); q.pop();
            }
        }
        for(int i = m / 2; i <= n - m / 2; i++) {
            int left = i + 1;
            int right = n - (m / 2) + 1;
            while(left <= right) {
                int mid = left + right >> 1;
                if(sum2[mid] + sum1[i - 1] + a[i].r <= v) { // 注意这里是用 sum2[mid] 细节 
                    left = mid + 1;
                    ans = max(ans, (a[i].l + a[mid].l) / 2);
                } else {
                    right = mid - 1;
                }
            }
        }
    }
    cout << ans << "\n";
    return 0;
}