洛谷题单指南-线段树的进阶用法-P4602 [CTSC2018] 混合果汁

原题链接:https://www.luogu.com.cn/problem/P4602

题意解读:在一堆果汁中选出若干果汁,使得最小的美味度最大,且总体价格小于等于g,总体体积大于L,求这个最大的美味度。

解题思路:

显然,应该对答案进行二分,二分到一个美味度x,那么接下来check()函数要做的事,就是在所有美味度>=x的果汁中,

查询出取到体积L时所需要付出的总价格sum,只要sum<=g并且所有美味度>=x的果汁的总可用体积<=L,则check结果为true,继续找更大的美味度。

查询的时候,可以使用贪心策略,为了不超总价,显然价格越低的越应该优先选择。

如何在美味度>=x的果汁查询正好取到体积L的果汁总价格,并且还优先差价格小的呢?可以借助权值线段树!

用果汁价格作为权值,果汁能取的体积作为权值的数量cnt,然后维护区间所有果汁的总价格sum,就可以计算权值数量前L小的果汁总价格,

这里可以参考:https://www.cnblogs.com/jcwy/p/18674205

由于要实现在所有比美味度x大的果汁中查询,可以用可持久化线段树,将果汁按美味度从大到小排序,root[i]线段树就是维护所有美味度大于i位置美味度的果汁情况,对位置进行二分即可。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 100005;
typedef long long LL;

struct Juice
{
    int d, p, l;
    bool operator < (const Juice &j)  const &
    {
        return d > j.d;
    }
} a[N];

struct Node
{
    int L, R;
    LL cnt; //区间所有可用体积之和
    LL sum; //区间所有可用果汁价格之和
} tr[N * 80];
int root[N], idx;
LL g, L;
int n, m;

//基于根为pre的线段树复制,并将值为v的cnt加上num,并更新相应的sum
int update(int pre, int l, int r, int v, int num)
{
    int u = ++idx;
    tr[u] = tr[pre];
    tr[u].cnt += num;
    tr[u].sum += 1ll * v * num;
    if(l == r) return u;
    int mid = l + r >> 1;
    if(v <= mid) tr[u].L = update(tr[pre].L, l, mid, v, num);
    else tr[u].R = update(tr[pre].R, mid + 1, r, v, num);
    return u;
}

//在根为u的线段树中查询权值前k小的所有价格之和
LL query(int u, int l, int r, int k)
{
    if(l == r) return 1ll * l * k;
    int mid = l + r >> 1;
    LL leftcnt = tr[tr[u].L].cnt;
    if(leftcnt >= k) return query(tr[u].L, l, mid, k);
    else return tr[tr[u].L].sum + query(tr[u].R, mid + 1, r, k - leftcnt);
}

bool check(int x)
{
    LL res = query(root[x], 1, N, L);
    if(res <= g && tr[root[x]].cnt >= L) return true; //总价格<=g且可用的总体积>=L
    return false;
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i].d >> a[i].p >> a[i].l;
    sort(a + 1, a + n + 1);

    for(int i = 1; i <= n; i++) root[i] = update(root[i - 1], 1, N, a[i].p, a[i].l);

    while(m--)
    {
        cin >> g >> L;
        int l = 1, r = n, ans = -1;
        while(l <= r)
        {
            int mid = l + r >> 1;
            if(check(mid)) ans = a[mid].d, r = mid - 1; //mid更小也就是美味度更大
            else l = mid + 1;
        }
        cout << ans << endl;
    }
    
    return 0;
}

 

posted @   五月江城  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示