洛谷·[CTSC2018]混合果汁

初见安~这里是传送门:洛谷P4602 [CTSC2018]混合果汁

题解

这个题藏得太深了……真的是个好题。

因为一瓶饮料的美味度取决于最小的那个美味度,换言之所有美味度比它大的饮料都可以用。想到啥了?对,二分答案。先按美味度对运料排序,而后二分答案,我们就解锁了一部分可以用的饮料。接下来我们要看能不能在规定钱以内达到那么多的体积。有一个显然的贪心思路是在现在的范围内买性价比最高的饮料,并且美味度尽量高。对于当前有区间划分限制以及求最优单点的情况,我们其实已经可以想到用主席树维护了。

按美味度划分时间点,按价格建权值线段树,每个点维护下方子节点的饮料买最多的情况下可以达到的体积以及对应的开销,然后把要达到的体积放进去二分下找。这里因为是按从大到小的美味度建树,所以如果左子树的最大体积大于等于所求体积,那么就往左去找饮料(美味度也尽量大),反之去右子树,并且让左子树分担尽量多的体积(右子树尽量少,可使用到的美味度尽量靠左)。找到叶子节点后返回需要的体积*该点代表的价格。

上代码——

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 100005
using namespace std;
typedef long long ll;
const int INF = 1e5;
ll read() {
	ll x = 0, f = 1, ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x * f;
}

int root[maxn], tot = 0;
struct TREE {int l, r; ll v, w;} t[maxn << 5];
int insert(int rt, int l, int r, int x, ll v, ll w) {
	register int p = ++tot, mid = l + r >> 1; t[p] = t[rt];
	t[p].v += v, t[p].w += w;
	if(l == r) return p;
	if(x <= mid) t[p].l = insert(t[rt].l, l, mid, x, v, w);
	else t[p].r = insert(t[rt].r, mid + 1, r, x, v, w);
	return p;
}

ll ask(int p, int l, int r, ll L) {
	if(l == r) return L * l; 
	register int mid = l + r >> 1;
	
	if(L <= t[t[p].l].v) return ask(t[p].l, l, mid, L); 
	else return t[t[p].l].w + ask(t[p].r, mid + 1, r, L - t[t[p].l].v);//分一部分给左边去
}

int n, m;
struct node {
	ll d, p, l;
	bool operator < (const node &x) const{return d > x.d;}
}a[maxn];

ll query(ll L, ll lim) {
	if(L > lim) return -1;
	register int l = 1, r = n + 1, mid, ans = n + 1;
	while(l <= r) {//二分答案
		mid = l + r >> 1;
		ll tmp = ask(root[mid], 1, INF, L);
		if(L <= t[root[mid]].v && tmp <= lim) ans = mid, r = mid - 1;//二分下标,往前分表示美味度变大
		else l = mid + 1;
	}
	return 1ll * a[ans].d;
}

signed main() {
	n = read(), m = read(); a[n + 1].d = -1;
	for(int i = 1; i <= n; i++) a[i].d = read(), a[i].p = read(), a[i].l = read();
	sort(a + 1, a + 1 + n);//美味度从大到小排序
	for(int i = 1; i <= n; i++) root[i] = insert(root[i - 1], 1, INF, a[i].p, a[i].l, a[i].l * a[i].p);

	ll G, L;
	for(int i = 1; i <= m; i++) {
		G = read(), L = read();
		printf("%lld\n", query(L, G));
	}
	return 0;
}

迎评:)
——End——

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值