[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;
}