【BZOJ5343】混合果汁(CTSC2018)-二分答案+主席树
测试地址:混合果汁
做法:本题需要用到二分答案+主席树。
注意到,如果一个最小美味度为的混合果汁可以配出,那么最小美味度的所有可能的混合果汁也可以配出,这个性质具有单调性,所以我们可以二分最小美味度,问题转化成判定性问题。
判定的话也很简单,容易想到贪心选取最便宜的升果汁,然后看它们的总价值,如果不超过,就代表可以配出,否则就不能配出。那么我们可以按果汁的价值建线段树,每个区间维护区间内所有果汁的体积和与它们的价值总和,那么在线段树上二分就可解决上面问题。
另外,注意到这棵线段树是可以按美味度可持久化的,于是我们就用解决了这个问题。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,maxp,tot=0,rt[100010],ch[2000010][2];
ll seg[2000010],sum[2000010];
struct juice
{
int d;
ll p,l;
}q[100010];
bool cmp(juice a,juice b)
{
if (a.d!=b.d) return a.d<b.d;
else return a.p<b.p;
}
void buildtree(int &no,int l,int r)
{
no=++tot;
seg[no]=sum[no]=0;
if (l==r) return;
int mid=(l+r)>>1;
buildtree(ch[no][0],l,mid);
buildtree(ch[no][1],mid+1,r);
}
void insert(int &no,int last,int l,int r,ll x,ll L)
{
no=++tot;
seg[no]=seg[last];
sum[no]=sum[last];
ch[no][0]=ch[last][0],ch[no][1]=ch[last][1];
if (l==r)
{
seg[no]+=L;
sum[no]+=x*L;
return;
}
int mid=(l+r)>>1;
if (x<=mid) insert(ch[no][0],ch[last][0],l,mid,x,L);
else insert(ch[no][1],ch[last][1],mid+1,r,x,L);
seg[no]=seg[ch[no][0]]+seg[ch[no][1]];
sum[no]=sum[ch[no][0]]+sum[ch[no][1]];
}
ll query(int last,int no,int l,int r,ll L)
{
ll totsum,s;
int mid=(l+r)>>1;
if (l==r) return (ll)l*L;
if (seg[ch[no][0]]-seg[ch[last][0]]<L)
{
totsum=sum[ch[no][0]]-sum[ch[last][0]];
s=seg[ch[no][0]]-seg[ch[last][0]];
return totsum+query(ch[last][1],ch[no][1],mid+1,r,L-s);
}
else return query(ch[last][0],ch[no][0],l,mid,L);
}
int main()
{
scanf("%d%d",&n,&m);
maxp=0;
for(int i=1;i<=n;i++)
{
scanf("%d%lld%lld",&q[i].d,&q[i].p,&q[i].l);
maxp=max(maxp,(int)q[i].p);
}
sort(q+1,q+n+1,cmp);
buildtree(rt[0],1,maxp);
for(int i=1;i<=n;i++)
insert(rt[i],rt[i-1],1,maxp,q[i].p,q[i].l);
for(int i=1;i<=m;i++)
{
ll G,L;
scanf("%lld%lld",&G,&L);
int l=0,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if (seg[rt[n]]-seg[rt[mid]]<L) {r=mid;continue;}
if (query(rt[mid],rt[n],1,maxp,L)<=G) l=mid+1;
else r=mid;
}
if (l==0) printf("-1\n");
else printf("%d\n",q[l].d);
}
return 0;
}