Solution -「CTSC 2018」「洛谷 P4602」混合果汁
\(\mathcal{Description}\)
Link.
\(n\) 种果汁,第 \(i\) 种美味度为 \(d_i\),每升价格 \(p_i\),一共 \(l_i\) 升。\(m\) 组询问,给定花费上限 \(g\) 和果汁需求量 \(L\),求混合多种果汁以满足要求时,所用果汁最小美味度的最大值。
\(n,m,p_i\le10^5\)。
\(\mathcal{Solution}\)
最小值最大,显然二分。
需要 check:能否用美味度不小于 \(mid\) 的果汁混合出 \(L\) 升,使得价格不超过 \(g\)。
没有美味度的限制,贪心地用单价更低的果汁就好啦!
回归到原问题,以按美味度降序排列后的果汁编号为版本轴建主席树,树是以单价为下标的权值线段树。外层二分出 \(mid\),再在以 \(mid\) 为根的树上走,贪心地购买果汁(先买左子树,不够再去右子树)。
就完了 qwq。复杂度 \(\mathcal O(n\log^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\n", ~ ans ? juice[ans].d : -1 );
}
return 0;
}