【BZOJ5343】混合果汁(CTSC2018)-二分答案+主席树

测试地址:混合果汁
做法:本题需要用到二分答案+主席树。
注意到,如果一个最小美味度为d的混合果汁可以配出,那么最小美味度<d的所有可能的混合果汁也可以配出,这个性质具有单调性,所以我们可以二分最小美味度d,问题转化成判定性问题。
判定的话也很简单,容易想到贪心选取最便宜的L升果汁,然后看它们的总价值,如果不超过G,就代表可以配出,否则就不能配出。那么我们可以按果汁的价值建线段树,每个区间维护区间内所有果汁的体积和与它们的价值总和,那么在线段树上二分就可解决上面问题。
另外,注意到这棵线段树是可以按美味度可持久化的,于是我们就用O(nlog2n)解决了这个问题。
以下是本人代码:

#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;
}
posted @ 2018-05-21 10:10  Maxwei_wzj  阅读(93)  评论(0编辑  收藏  举报