BZOJ5343[Ctsc2018]混合果汁——主席树+二分答案

题目链接:

CTSC2018混合果汁

 

显然如果美味度高的合法那么美味度低的一定合法,因为美味度低的可选方案包含美味度高的可选方案。

那么我们二分一个美味度作为答案然后考虑如何验证?

选择时显然要贪心的先选单价低的果汁。

那么我们按美味度从大到小将每种果汁排序,然后对于每种果汁建立一个版本的主席树,主席树维护的权值是果汁单价。

每次验证时在对应版本主席树中查找,如果左子树中总体积大于L则递归左子树,否则将答案加上左子树所有果汁的总价然后递归右子树。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
struct miku
{
    int d;
    ll g,l;
}s[100010];
int cnt;
int n,m,q;
ll G,L;
int ls[2000010];
int rs[2000010];
ll sum[2000010];
ll num[2000010];
int root[100010];
ll h[100010];
bool cmp(miku a,miku b)
{
    if(a.d!=b.d)
    {
        return a.d>b.d;
    }
    return a.g<b.g;
}
void updata(int &rt,int pre,int l,int r,int x,ll val,ll lim)
{
    rt=++cnt;
    sum[rt]=sum[pre]+val;
    num[rt]=num[pre]+lim;
    ls[rt]=ls[pre];
    rs[rt]=rs[pre];
    if(l==r)
    {
        return ;
    }
    int mid=(l+r)>>1;
    if(x<=mid)
    {
        updata(ls[rt],ls[pre],l,mid,x,val,lim);
    }
    else
    {
        updata(rs[rt],rs[pre],mid+1,r,x,val,lim);
    }
}
ll query(int rt,int l,int r,ll k)
{
    if(l==r)
    {
        return k*h[l];
    }
    int mid=(l+r)>>1;
    if(num[ls[rt]]>=k)
    {
        return query(ls[rt],l,mid,k);
    }
    else
    {
        return sum[ls[rt]]+query(rs[rt],mid+1,r,k-num[ls[rt]]);
    }
}
bool check(int x)
{
    if(num[root[x]]<L)
    {
        return false;
    }
    if(query(root[x],1,m,L)<=G)
    {
        return true;
    }
    return false;
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%lld%lld",&s[i].d,&s[i].g,&s[i].l);
        h[i]=s[i].g;
    }
    sort(s+1,s+1+n,cmp);
    sort(h+1,h+1+n);
    m=unique(h+1,h+1+n)-h-1;
    for(int i=1;i<=n;i++)
    {
        updata(root[i],root[i-1],1,m,lower_bound(h+1,h+1+m,s[i].g)-h,s[i].g*s[i].l,s[i].l);
    }
    while(q--)
    {
        scanf("%lld%lld",&G,&L);
        int l=1;
        int r=n;
        int ans=-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid)==true)
            {
                ans=mid;
                r=mid-1;
            }
            else
            {
                l=mid+1;
            }
        }
        if(ans==-1)
        {
            printf("-1\n");
            continue;
        }
        printf("%d\n",s[ans].d);
    }
}
posted @ 2018-11-27 20:54  The_Virtuoso  阅读(169)  评论(0编辑  收藏  举报