「主席树」[Ctsc2018]混合果汁

[Ctsc2018]混合果汁

题目链接:混合果汁

这道题增强了我对二分和主席树的认识,很好的一道题

终于开始认真学OI了,还是比较上瘾 开始刷bzoj 刷刷刷

题目大意

每次给你三个信息,表示一瓶果汁的价值量(d)、每升价格(p)、一瓶饮料中最多添加多少升(l)

然后需要一瓶混合果汁,每次给你这瓶混合果汁的限制条件(G)(L),分别代表最多能支付的钱和至少多少升饮料,然后这瓶果汁的价值要尽可能的大,每瓶混合果汁的价值为所有加进去的果汁的价值量的最小值。

题目题解

我觉得比较水的一道题,但是不看下题解还真的写不出

正好学了主席树,听zbww介绍写的一道题,看了下题没看懂,再看了一遍,一瓶混合果汁的最小值是由某一瓶果汁的价值量来定的,那么我们可以根据价值量进行从小到大的排序,然后再发现,每次会有两个限制条件(G)(L),看了题解发现可以用二分判定来搞,那怎么很快的维护判断(G)(L)?很显然对于单独数据可以在权值线段树上来进行,但是这样就会导致一个问题,我们之前二分的(d)就不能加入判断当中了,因为二分的(d)一直在变,而我们的权值线段树却不能一直变,而且对于每组数据(d)可能重复,哎?重复,这不就跟历史版本一样吗,很明显我们就得到了主席树(可持久化线段树)这样一个算法来维护判断(G, L),和求第(k)大一样即可

那这样我们就可以得到代码

/**************************************************************
    Problem: 5343
    User: Nicoppa
    Language: C++
    Result: Accepted
    Time:15712 ms
    Memory:104428 kb
****************************************************************/
 
//#define fre yes
 
#include <cstdio>
#include <iostream>
#include <algorithm>
#define int long long
//草 要开longlong 不开只有30
const int N = 100005;
struct Other {
    int d, p, l;
} juice[N];
struct Node {
    int l, r, w, lis;
} tree[N << 5];
int T[N];
 
bool cmp(Other x, Other y) {
    return x.d < y.d;
} //对d排序
 
int cnt;
void build(int l, int r) {
    int rt = ++cnt;
    tree[rt].l = l;
    tree[rt].r = r;
    tree[rt].lis = tree[rt].w = 0;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(l, mid);
    build(mid + 1, r);
} //建个空树
 
int update(int pre, int l, int r, int p, int lis) {
    int rt = ++cnt;
    tree[rt].l = tree[pre].l;
    tree[rt].r = tree[pre].r;
    tree[rt].w = tree[pre].w + p * lis;
    tree[rt].lis = tree[pre].lis + lis;
    if(l == r) return rt;
    int mid = (l + r) >> 1;
    if(mid >= p) tree[rt].l = update(tree[pre].l, l, mid, p, lis);
    else tree[rt].r = update(tree[pre].r, mid + 1, r, p, lis);
    return rt;
} //更新每次版本
 
int query(int u, int v, int l, int r, int k) {
    if(l == r) return l * k;
    int t = tree[tree[v].l].lis - tree[tree[u].l].lis;
    int mid = (l + r) >> 1;
    if(t >= k) return query(tree[u].l, tree[v].l, l, mid, k);
    else return tree[tree[v].l].w - tree[tree[u].l].w + query(tree[u].r, tree[v].r, mid + 1, r, k - t);
} //查询
 
signed main() {
    static int n, m, pMax;
    scanf("%lld %lld", &n, &m);
    for (int i = 1; i <= n; i++) {
        int d, p, l;
        scanf("%lld %lld %lld", &d, &p, &l);
        juice[i].d = d;
        juice[i].p = p;
        juice[i].l = l;
        pMax = std::max(pMax, p);
    } std::sort(juice + 1, juice + 1 + n, cmp);
 
    T[0] = 0;
    build(1, pMax);
    for (int i = 1; i <= n; i++) {
        T[i] = update(T[i - 1], 1, pMax, juice[i].p, juice[i].l);
    }
 
    for (int i = 1; i <= m; i++) {
        int g, lis;
        scanf("%lld %lld", &g, &lis);
        int l = 1, r = n, ans = -1;
        while(l <= r) {
            int mid = (l + r) >> 1;
            if(query(T[mid - 1], T[n], 1, pMax, lis) <= g && tree[T[n]].lis - tree[T[mid - 1]].lis >= lis) {
                l = mid + 1; ans = mid;
            } else r = mid - 1;
        }
 
        if(ans == -1) puts("-1");
        else printf("%lld
", juice[ans].d);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Nicoppa/p/11495431.html