洛谷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; }