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

解题思路:

  • 中位数取决于中间两个数的大小
  • 先对物品根据价值进行排序
  • m为奇数时直接枚举中间的那个数,左边选m/2-1个体积最小的,右边选m/2-1个体积最小的
  • m为偶数时,考虑枚举右半部分的最小价值,这个最小价值对应了一个最小体积,然后多出来了部分体积,在这部分体积里选择价值最大的,需要用数据结构来维护。
  • 对于偶数,维护两个数组L[],R[]。R数组表示从i到n选Rsz个体积最小的物品。L数组表示从0到i选Lsz个物品,且价值最大的是a[i],对应的最小体积
#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define me0(a) memset(a,0,sizeof(a))
#define me1(a) memset(a,-1,sizeof(a))
#define op freopen("in.txt", "r", stdin)
#define pii pair<int,int>
#define mp(x,y) make_pair(x,y)
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
void read(int& val) { int x = 0; int bz = 1; char c; for (c = getchar(); (c<'0' || c>'9') && c != '-'; c = getchar()); if (c == '-') { bz = -1; c = getchar(); }for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - 48; val = x * bz; }
const int mod=1e9+7;
const int maxn = 1e6+10;

int n,m,v;
struct node{
    int a,b;//价值,体积
    bool operator<(const node t)const {
        return a<t.a;
    }
}p[maxn];
LL R[maxn], L[maxn];
void init() {
    int Rsz = (m + 1) >> 1, Lsz = m - Rsz;
    priority_queue<int> q;
    q.push(1e9 + 1);
    LL nwsum = 1e9 + 1;
    //R数组表示从i到n选Rsz个体积最小的物品,L数组表示从0到i选Lsz个物品,且体积最大的是i,的最小体积
    fod(i, n, 1) {
        q.push(p[i].b);
        nwsum += p[i].b;
        if (q.size() > Rsz) {
            nwsum -= q.top();q.pop();
            R[i] = nwsum;
        }
        else {
            R[i] = v + 1;
        }
    }

    while (!q.empty()) q.pop();
    q.push(1e9 + 1);
    nwsum = 1e9 + 1;
    fo(i, 1, n) {
        if (q.size() == Lsz) {
            L[i] = nwsum - q.top() + p[i].b;
            q.push(p[i].b);
            nwsum += p[i].b-q.top();
            q.pop();
        } else {
            q.push(p[i].b);
            nwsum += p[i].b;
            L[i] = v + 1;
        }
    }
}


//将体积离散化
LL vb[maxn];
int tot=0;

int mx[maxn];
void up(int root){
    mx[root] = max(mx[root<<1],mx[root<<1|1]);
}
int qu(int root,int l,int r,int R){
    if(r<=R) return mx[root];
    if(l>R) return 0;
    int mid=l+r>>1;
    return max(qu(root<<1,l,mid,R),qu(root<<1|1,mid+1,r,R));
}
void upd(int root,int l,int r,int pos,int v){
    if(l==r) {
        mx[root]=max(mx[root],v);return;
    }
    int mid=l+r>>1;
    if(pos<=mid) upd(root<<1,l,mid,pos,v);
    else upd(root<<1|1,mid+1,r,pos,v);
    up(root);
}

void solve2() {
    //选偶数个的策略是,枚举右边界,在剩余的体积内选择价值最大的
    fo(i, 1, n) {
        if (L[i] <= v) vb[++tot] = L[i];
        if (R[i] <= v) vb[++tot] = R[i];
    }
    sort(vb + 1, vb + 1 + tot);
    tot = unique(vb + 1, vb + 1 + tot) - vb - 1;

    int ans = 0;
    fo(i, 1, n) {
        if (R[i] <= v) {
            int mxv = lower_bound(vb + 1, vb + 1 + tot, v - R[i] + 1) - vb - 1;
            if (mxv >= 1) {
                int lans = qu(1, 1, tot, mxv);//在剩余体积里可以选的最大价值
                ans =max(ans, lans + p[i].a);
            }
        }
        if (L[i] <= v) {
            upd(1, 1, tot, lower_bound(vb + 1, vb + 1 + tot, L[i]) - vb, p[i].a);
        }
    }
    printf("%d\n", ans / 2);
}

void solve1(){
    //选奇数个的策略是,枚举中间点,左边选体积最小的m/2-1个即可
    priority_queue<int>q;
    LL nw= 0;
    int ans=0;
    fo(i,1,n){
        if(nw+R[i]<=v) ans=max(ans,p[i].a);
        q.push(p[i].b);nw+=p[i].b;
        if(q.size()>m/2) nw-=q.top(),q.pop();
    }
    printf("%d\n",ans);
}

int main(){
    read(v);read(n);read(m);
    fo(i,1,n)  read(p[i].a),read(p[i].b);
    sort(p+1,p+1+n);//对价值进行排序
    init();

    if(m==1){
        int  ans = 0;
        fo(i,1,n){
            if(p[i].b<=v) ans=max(ans,p[i].a);
        }
        printf("%d\n",ans);
    }
    else if(m&1) solve1();
    else solve2();
    return 0;
}