[CTSC2018]混合果汁

题目

展开
题目描述
小 R 热衷于做黑暗料理,尤其是混合果汁。

商店里有 nn 种果汁,编号为 0,1,\cdots,n-10,1,⋯,n−1 。ii 号果汁的美味度是 d_id
i

,每升价格为 p_ip
i

。小 R 在制作混合果汁时,还有一些特殊的规定,即在一瓶混合果汁中,ii 号果汁最多只能添加 l_il
i

升。

现在有 mm 个小朋友过来找小 R 要混合果汁喝,他们都希望小 R 用商店里的果汁制作成一瓶混合果汁。其中,第 jj 个小朋友希望他得到的混合果汁总价格不大于 g_jg
j

,体积不小于 L_jL
j

。在上述这些限制条件下,小朋友们还希望混合果汁的美味度尽可能地高,一瓶混合果汁的美味度等于所有参与混合的果汁的美味度的最小值。请你计算每个小朋友能喝到的最美味的混合果汁的美味度。

输入格式
输入第一行包含两个正整数 n, mn,m,表示果汁的种数和小朋友的数量。接下来 nn 行,每行三个正整数 d_i, p_i, l_id
i

,p
i

,l
i

,表示 ii 号果汁的美味度为 d_id
i

,每升价格为p_ip
i

,在一瓶果汁中的添加上限为 l_il
i

接下来 mm 行依次描述所有小朋友:每行两个数正整数 g_j, L_jg
j

,L
j

描述一个小朋友,表示他最多能支付 g_jg
j

元钱,他想要至少 L_jL
j

升果汁。

输出格式
对于所有小朋友依次输出:对于每个小朋友,输出一行,包含一个整数,表示他能喝到的最美味的混合果汁的美味度。如果无法满足他的需求,则输出 -1−1。

输入输出样例
输入 #1复制
3 4
1 3 5
2 1 3
3 2 5
6 3
5 3
10 10
20 10
输出 #1复制
3
2
-1
1
说明/提示
对于所有的测试数据,保证 n, m \le 100000n,m≤100000,1 \le d_i,p_i,l_i \le 10^5, 1 \le g_j, L_j \le 10^{18}1≤d
i

,p
i

,l
i

≤10
5
,1≤g
j

,L
j

≤10
18

测试点编号 n=n= m=m= 其他限制
1,2,3 1010 1010 无
4,5,6 500500 500500 无
7,8,9 50005000 50005000 无
10,11,12 100000100000 100000100000 p_i=1p
i

=1
13,14,15 100000100000 100000100000 l_i=1l
i

=1
16,17,18,19,20 100000100000 100000100000 无

思路

首先把dd排序,枚举一个答案d,d,那么我们肯定是贪心地选择美味程度不小于dd的并且最便宜的果汁

可以发现其具有单调性,考虑二分一个d,d,给美味程度不小于dd的果汁建立一颗以价格为下标的线段树

每个节点记录一下果汁总量和价格和,这样就可以在线段树上二分得到LimLim对应的价格了

然后这个线段树是可以对dd进行可持久化的

代码

#include<bits/stdc++.h>
#define N 100005
#define ll long long
using namespace std;
struct juice{
    ll d,p,l;
}x[N];
bool cmp(juice a,juice b){
    return a.d<b.d;
}
int n,m,root[N],cnt;
struct node{
    int l,r;
    ll lit,W;
}g[N*21];
void update(int &rt,int lb,int rb,ll P,ll LIT){
    g[++cnt]=g[rt];rt=cnt;g[rt].lit+=LIT;g[rt].W+=P*LIT;
    if (lb==rb) return;
    int mid=lb+rb>>1;
    if (mid>=P) update(g[rt].l,lb,mid,P,LIT);
    else update(g[rt].r,mid+1,rb,P,LIT);
}
ll query(int i,int j,int lb,int rb,ll LIT){
    if (lb==rb) return 1LL*LIT*lb;
    int mid=lb+rb>>1;ll TOT=g[g[j].l].lit-g[g[i].l].lit;
    if (TOT>=LIT) return query(g[i].l,g[j].l,lb,mid,LIT);
    return g[g[j].l].W-g[g[i].l].W+query(g[i].r,g[j].r,mid+1,rb,LIT-TOT);
}
signed main(){
    n=read(),m=read();
    ll MAXP=0;
    for (int i=1;i<=n;i++){
        x[i].d=read(),x[i].p=read(),x[i].l=read();
        MAXP=max(MAXP,x[i].p);
    }
    sort(x+1,x+1+n,cmp);
    for (int i=1;i<=n;i++){
        root[i]=root[i-1];
        update(root[i],1,MAXP,x[i].p,x[i].l);
    }
    while (m--){
        ll G=read(),L=read();
        ll lb=1,rb=n,ans=-1;
        while (lb<=rb){
            ll mid=lb+rb>>1;
            if (query(root[mid-1],root[n],1,MAXP,L)<=G&&g[root[n]].lit-g[root[mid-1]].lit>=L){
                lb=mid+1;ans=mid;
            }else{
                rb=mid-1;
            }
        }
        if (ans==-1) printf("-1\n");
        else printf("%lld\n",x[ans].d);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值