洛谷P4602/LOJ2555/UOJ402/BZOJ5343[CTSC2018]混合果汁(二分+线段树+可持久化)

首先二分$d$,那么要取最小价值,就要在所有满足$d_i\ge d$的$i$中选$p_i$最小的,一路取到$L$。这个东西和名次树很类似,可以在权值线段树上二分。

而快速找到$d_i\ge d$的数据构成的线段树明显是排序+可持久化了。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100050;
char rB[1<<21],*rS,*rT;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;}
inline ll rd(){
    char c=gc();
    while(c<48||c>57)c=gc();
    ll x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
int lc[N<<5],rc[N<<5],sz=0,rt[N];
ll cnt[N<<5],sum[N<<5],sr[N];
struct node{
    int d,p,l;
}a[N];
inline bool cmp(node a,node b){return a.d<b.d;}
int ins(int pre,int L,int R,int x,int k){
    int o=++sz;
    cnt[o]=cnt[pre]+k;sum[o]=sum[pre]+(ll)x*k;
    if(L<R){
        int M=L+R>>1;
        if(x<=M){lc[o]=ins(lc[pre],L,M,x,k);rc[o]=rc[pre];}
        else{rc[o]=ins(rc[pre],M+1,R,x,k);lc[o]=lc[pre];}
    }
    return o;
}
ll query(int o,int L,int R,ll lim){
    if(L==R)return lim*L;
    int M=L+R>>1;
    if(cnt[lc[o]]>=lim)return query(lc[o],L,M,lim);
    return sum[lc[o]]+query(rc[o],M+1,R,lim-cnt[lc[o]]);
}
int main(){
    int n=rd(),q=rd(),i,l,r,mid;
    ll g,lim;
    for(i=1;i<=n;++i){a[i].d=rd();a[i].p=rd();a[i].l=rd();}
    sort(a+1,a+n+1,cmp);
    for(i=n;i;--i){
        sr[i]=sr[i+1]+a[i].l;
        rt[i]=ins(rt[i+1],1,100000,a[i].p,a[i].l);
    }
    while(q--){
        g=rd();lim=rd();
        l=0;r=n;
        while(l<r){
            mid=l+r+1>>1;
            if(sr[mid]>=lim&&query(rt[mid],1,100000,lim)<=g)l=mid;
            else r=mid-1;
        }
        if(!l)puts("-1");
        else printf("%d\n",a[l].d);
    }
    return 0;
}
View Code

 

posted @ 2019-08-19 16:10  wangyuchen  阅读(126)  评论(0编辑  收藏  举报