T2 礼物(gift)

礼物(gift)

题面

\(n\) 个物品,每个物品有价值 \(v_i\) 和代价 \(w_i\)。现在有 \(T\) 个询问, 每个询问对应一个奇数 \(q_i\),问选 \(q_i\) 个物品使得代价和不超过 \(m\) 的情况下价值的中位数最大是多少?

\(1\le n,T\le 10^6,1\le v_i,w_i,m\le 10^6, 1\le q_i \le n\)

思路

赛时玩了会儿游戏,然后去了个灵感爆发之地——厕所,就想到了关键思路:离线,提前处理每一个询问的。以代价为第一关键字排序后,选定一个中位数,其代价和就等于它的代价加上左右两边的 k 小值和,但后面我们要反复用所以还要可持久化。

从大往小枚举每一个数,可以用二分左右两边同时最大的 k,于是一但必它后面的数有多出来的最大值,就设置其答案为当前的价值即可。

代码

#include<bits/stdc++.h>
using namespace std;
// typedef long long ll;
#define int long long
typedef long double ld;
#define freop(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define endl '\n'
#define lb(x) ((x)&-(x))
#define inf 0x7f7f7f7f7f7f7f7f
#define pii pair<int,int>
#define fir first
#define sec second
#define mkp(a,b) make_pair(a,b)
const int N=5e5+10,M=1e6;
int n,m,T,q,ans[N];pii a[N];

class Segment_Tree{
    int tot=N,dx,ans;
    struct node{
        int ls,rs,w,v;
    }tr[N<<4];
    void change(int u,int v,int l=1,int r=M){
        tr[u]=tr[v],++tr[u].v,tr[u].w+=dx;
//        cerr << u << ' ' << tr[u].w << ' ' << tr[u].v << endl;
        if(l==r) return ;
        int mid=l+r>>1;
        if(dx<=mid) change(tr[u].ls=++tot,tr[v].ls,l,mid);
        else change(tr[u].rs=++tot,tr[v].rs,mid+1,r);
    }
    int query(int u,int nw,int l=1,int r=M){
        if(!nw||!u) return nw;
//         cerr << u << ':' << tr[u].w << ' ' << tr[u].v << endl;
        if(tr[u].v<=nw){ans+=tr[u].w;return nw-tr[u].v;}
        if(l==r){ans+=nw*l;return 0;}
        int mid=l+r>>1,t;
        t=query(tr[u].ls,nw,l,mid);
        query(tr[u].rs,t,mid+1,r);

        return 0;
    }
    public:
    inline void add(int u,int v,int x){dx=x,change(u,v);}
    inline int ask(int u,int x){ans=0,query(u,x);return ans;}
}pre,bac;

signed main(){
    cin.tie(0)->ios::sync_with_stdio(0);
    cout.tie(0);
    freop("gift");
//↑以上为初始化↑
//------------------------------
    cin >> n >> m >> T;
    for(int i = 1;i<=n;++i){
        cin >> a[i].fir >> a[i].sec;
        if(a[i].sec>m) --n,--i;
    }
    sort(a+1,a+n+1);
    // for(int i = 1;i<=n;++i) cerr << a[i].fir << ' ' << a[i].sec << endl;
    for(int i = 1;i<=n;++i)
        pre.add(i,i-1,a[i].sec);
    for(int i = n;i;--i)
        bac.add(n-i+1,n-i,a[i].sec);
    for(int i = n,lst=0;i;--i){
        int l=0,r=min(i-1,n-i),mid,tm=m-a[i].sec;
        while(l<r){
            mid=l+r+1>>1;
//            cerr << mid << ':' << pre.ask(i-1,mid) << ' ' << bac.ask(n-i,mid) << endl;
            if(pre.ask(i-1,mid)+bac.ask(n-i,mid)<=tm) l=mid;
            else r=mid-1;
        }
//        cerr << i << ' ' << l << endl;
        for(;lst<=l;++lst) 
			ans[lst]=a[i].fir;
    }
    while(T--) cin >> q,cout << (ans[q/2]?ans[q/2]:-1) << endl;
    return 0;
}
posted @ 2026-10-21 20:59  elon_giao  阅读(3)  评论(0)    收藏  举报