P4602 [CTSC2018]混合果汁(整体二分+主席树)

传送门
多个询问且答案显然是具有单调性的,很自然的能想到整体二分.这题的难点可能是如何在二分的过程中快速处理数据.
二分这个maxd的时候,需要对美味度在1~mid的果汁按价格排序,如果每段都排序的话复杂度会退化,这题不和其他整体二分的题目一样,不能直接把询问放到右区间的同时减掉cost.因为maxd增大了,可能会有更便宜的果汁供我们选择.
观察到只需要知道1~前缀的排序序列,这个显然可以用主席树(或者说可持久化的权值线段树)来维护.具体来说就是把每个果汁的权值当作节点,维护num和sum两个变量,num表示这个区间有多少L的果汁,sum表示买这numL的果汁要多少钱,直接在权值线段树上面二分判断就好了.
小优化:可以先离散化一下权值,加快权值线段树的二分过程.
还需要注意的是在二分maxd的时候,我们发现d也是会有重复的,所以可以把d也给离散化了.
具体看代码吧(突然脑抽了要用namespace来写这个主席树).

#define LL long long
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<LL,LL>
#define fir(i,a,b) for(int i=a;i<=(int)b;++i)
#define afir(i,a,b) for(int i=(int)a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define ALL(a) a.begin(),a.end()
#define bug puts("-------")
#define mpr(a,b) make_pair(a,b)
#include <bits/stdc++.h>

using namespace std;
const int N = 2e5+10;

inline void read(LL &a){
    LL x = 0,f=1;char ch = getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    a = x*f;
}

struct C{
    int d,p,l;
    bool operator<(const C& w){
        return d < w.d;
    }
}c[N*4];
struct Q{
    LL g,l;
    int id;
}q[N],q1[N],q2[N];
int tn,n,m,ans[N];
vi all;
int getpos(int x){
    return lower_bound(ALL(all),x) - all.begin();
}
namespace seg{
    LL sum[N*20],num[N*20];
    int ls[N*20],rs[N*20],tot = 0,root[N];
    #define L(i) ls[i]
    #define R(i) rs[i]
    void add(int &now,int pre,int l,int r,int p,int Num){
        sum[now = ++ tot] = sum[pre] + 1LL*all[p]*Num;
        num[now] = 1LL*Num + num[pre];
        ls[now] = ls[pre],rs[now] = rs[pre];
        if(l == r) return;
        int mid = l + r >> 1;
        if(p <= mid) add(L(now),L(pre),l,mid,p,Num);
        else add(R(now),R(pre),mid+1,r,p,Num);
    }
    LL query(int i,int l,int r,LL v){
        if(v >= sum[i]) return num[i];
        if(l == r){
            return min(v/(LL)all[l],(LL)num[i]);
        }
        int mid = l + r >> 1;
        if(v <= sum[L(i)]) return query(L(i),l,mid,v);
        else return num[L(i)] + query(R(i),mid+1,r,v-sum[L(i)]);
    }
};
int ro[N],ff[N];
void solve(int l,int r,int L,int R){
    if(l > r || L > R) return;
    if(l == r){
        fir(i,L,R) ans[q[i].id] = ff[l];
        return;
    }
    int mid = (l + r) >> 1,p1=0,p2=0;
    fir(i,L,R){
        LL num = seg::query(ro[mid],1,tn,q[i].g);
        if(num >= q[i].l) q1[++p1] = q[i];
        else q2[++p2] = q[i]; 
    }
    fir(i,1,p1) q[L+i-1] = q1[i];
    fir(i,1,p2) q[L+p1+i-1] = q2[i];
    solve(l,mid,L,L+p1-1);solve(mid+1,r,L+p1,R);
}
int main(){
    LL t1,t2;
    read(t1);read(t2);
    n = t1,m = t2;
    all.pb(0);
    fir(i,1,n){
        read(t1),c[i].d = t1;read(t1);read(t2);c[i].p = t1,c[i].l = t2;all.pb(c[i].p);
    }
    fir(i,1,m){
        read(q[i].g);read(q[i].l);q[i].id = i;
    }
    sort(c+1,c+1+n);
    sort(ALL(all));
    all.erase(unique(ALL(all)),all.end());
    tn = all.size()-1;
    fir(i,1,n) c[i].p = getpos(c[i].p);
    int cnt = 0;
    afir(i,n,1){
        seg::add(seg::root[i],seg::root[i+1],1,tn,c[i].p,c[i].l);
        if(c[i].d != c[i+1].d) ff[++cnt] = c[i].d;
        ro[cnt] = seg::root[i];
    }
    solve(1,cnt+1,1,m);
    fir(i,1,m) printf("%d\n",(ans[i]?ans[i]:-1));

    return 0;
}    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值