Solution -「CTSC 2018」「洛谷 P4602」混合果汁 (mathcal{Description}) (mathcal{Solution})

Solution -「CTSC 2018」「洛谷 P4602」混合果汁
(mathcal{Description})
(mathcal{Solution})

  Link.

  (n) 种果汁,第 (i) 种美味度为 (d_i),每升价格 (p_i),一共 (l_i) 升。(m) 组询问,给定花费上限 (g) 和果汁需求量 (L),求混合多种果汁以满足要求时,所用果汁最小美味度的最大值。

  (n,m,p_ile10^5)

(mathcal{Solution})

  最小值最大,显然二分。

  需要 check:能否用美味度不小于 (mid) 的果汁混合出 (L) 升,使得价格不超过 (g)

  没有美味度的限制,贪心地用单价更低的果汁就好啦!

  回归到原问题,以按美味度降序排列后的果汁编号为版本轴建主席树,树是以单价为下标的权值线段树。外层二分出 (mid),再在以 (mid) 为根的树上走,贪心地购买果汁(先买左子树,不够再去右子树)。

  就完了 qwq。复杂度 (mathcal O(nlog^2n))

(mathcal{Code})

#include <cstdio>
#include <algorithm>

const int MAXN = 1e5;
int n, m, root[MAXN + 5];

typedef long long LL;

inline LL rint () {
	LL x = 0; char s = getchar ();
	for ( ; s < '0' || '9' < s; s = getchar () );
	for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
	return x;
}

struct Juice {
	int d, p, l;
	inline void read () { d = rint (), p = rint (), l = rint (); }
	inline bool operator < ( const Juice t ) const { return d > t.d; }
} juice[MAXN + 5];

struct PersistentSegmentTree {
	static const int MAXND = MAXN * 40;
	int cntnd, ch[MAXND + 5][2];
	LL sum[MAXND + 5], prc[MAXND + 5];

	inline void build ( int& rt, const int l, const int r ) {
		rt = ++ cntnd;
		if ( l == r ) return ;
		int mid = l + r >> 1;
		build ( ch[rt][0], l, mid ), build ( ch[rt][1], mid + 1, r );
	}

	inline void pushup ( const int rt ) {
		sum[rt] = sum[ch[rt][0]] + sum[ch[rt][1]];
		prc[rt] = prc[ch[rt][0]] + prc[ch[rt][1]];
	}

	inline void insert ( int& rt, const int l, const int r, const int p, const int v ) {
		int old = rt, mid = l + r >> 1; rt = ++ cntnd;
		ch[rt][0] = ch[old][0], ch[rt][1] = ch[old][1], sum[rt] = sum[old], prc[rt] = prc[old];
		if ( l == r ) return sum[rt] += v, prc[rt] += 1ll * v * p, void ();
		if ( p <= mid ) insert ( ch[rt][0], l, mid, p, v );
		else insert ( ch[rt][1], mid + 1, r, p, v );
		pushup ( rt );
	}

	inline LL buy ( const int rt, const int l, const int r, LL money, LL need ) {
		if ( sum[rt] < need || money < 0 ) return -1;
		if ( l == r ) return need * l <= money ? need * l : -1;
		int mid = l + r >> 1;
		if ( sum[ch[rt][0]] >= need ) return buy ( ch[rt][0], l, mid, money, need );
		else {
			LL t = buy ( ch[rt][1], mid + 1, r, money - prc[ch[rt][0]], need - sum[ch[rt][0]] );
			return ~ t ? t + prc[ch[rt][0]] : -1;
		}
	}
} pst;

int main () {
	n = rint (), m = rint ();
	int mxp = 0;
	for ( int i = 1; i <= n; ++ i ) {
		juice[i].read ();
		if ( mxp < juice[i].p ) mxp = juice[i].p;
	}
	std::sort ( juice + 1, juice + n + 1 );
	pst.build ( root[0], 1, mxp );
	for ( int i = 1; i <= n; ++ i ) {
		pst.insert ( root[i] = root[i - 1], 1, mxp, juice[i].p, juice[i].l );
	}
	for ( LL g, L; m --; ) {
		g = rint (), L = rint ();
		int l = 1, r = n;
		LL ans = -1, tmp;
		while ( l <= r ) {
			int mid = l + r >> 1;
			if ( ~ pst.buy ( root[mid], 1, mxp, g, L ) ) r = ( ans = mid ) - 1;
			else l = mid + 1;
		}
		printf ( "%d
", ~ ans ? juice[ans].d : -1 );
	}
	return 0;
}