题目连接:https://www.luogu.org/problemnew/show/P4602
因为题中说是让最小值最大,所以自然想到二分答案。对于每一个二分的值,判断是否合法,若合法,在右区间二分,否则在左区间二分。
那么如何判断是否合法呢?首先,对于每一个二分值mid,我们应该在[mid, n]的区间中贪心的取价格尽量低的果汁,当该小朋友所有的钱花光后,判断取到的果汁是否到了Lj升。
至于如何取,首先因为价格是无序的,所以可以建立一个大小为价格的值域的线段树,同时维护每一个区间的价格之和以及对应多少升之和(原谅我这小学水平的语文)。又因为是在动区间查询,线段树无法胜任,所以就想到了主席树(想想板子题区间第k小)。
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 #include<cctype> 7 using namespace std; 8 #define enter printf(" ") 9 #define space printf(" ") 10 typedef long long ll; 11 const int INF = 0x3f3f3f3f; 12 const int maxn = 1e5 + 5; 13 const int max_size = 2e6 + 5; 14 const int Max = 1e5; 15 inline ll read() 16 { 17 ll ans = 0; 18 char ch = getchar(), last = ' '; 19 while(!isdigit(ch)) {last = ch; ch = getchar();} 20 while(isdigit(ch)) 21 { 22 ans = ans * 10 + ch - '0'; ch = getchar(); 23 } 24 if(last == '-') ans = -ans; 25 return ans; 26 } 27 inline void write(ll x) 28 { 29 if(x < 0) x = -x, putchar('-'); 30 if(x >= 10) write(x / 10); 31 putchar('0' + x % 10); 32 } 33 34 int n, m; 35 struct Juice 36 { 37 int d, p, l; 38 bool operator < (const Juice& other)const //按d排序 39 { 40 return d < other.d; 41 } 42 }a[maxn]; 43 44 int tot = 0, root[max_size], lson[max_size], rson[max_size]; 45 ll sum_co[max_size], sum_lit[max_size]; //sun_co[]:当前区间全买所花的钱,sum_lit[]当前区间有多少升 46 void update(int old, int& now, int L, int R, int p, int v) 47 { 48 now = ++tot; 49 lson[now] = lson[old]; rson[now] = rson[old]; 50 sum_co[now] = sum_co[old] + (ll)p * v; 51 sum_lit[now] = sum_lit[old] + v; 52 if(L == R) return; 53 int mid = (L + R) >> 1; 54 if(p <= mid) update(lson[old], lson[now], L, mid, p, v); 55 else update(rson[old], rson[now], mid + 1, R, p, v); 56 } 57 ll query(int old, int now, int L, int R, ll p) 58 { 59 if(L == R) return min(p / L, sum_lit[now] - sum_lit[old]); //判断当前剩的钱是否够买这种果汁的所有容量,否则只买一部分 60 ll sum_cost = sum_co[lson[now]] - sum_co[lson[old]]; //判断是否能买做区间的所有东西 61 int mid = (L + R) >> 1; 62 if(p <= sum_cost) return query(lson[old], lson[now], L, mid, p); 63 else return sum_lit[lson[now]] - sum_lit[lson[old]] + query(rson[old], rson[now], mid + 1, R, p - sum_cost); //说明能买这些东西了,在右子区间查找 64 } 65 66 ll GG, LL; 67 bool judge(int x) //钱数为下标,判断能买到的果汁有多少升 68 { 69 return query(root[x - 1], root[n], 1, Max, GG) >= LL; 70 } 71 int solve() 72 { 73 int L = 0, R = n; 74 if(GG < LL) return -1; 75 while(L < R) 76 { 77 int mid = (L + R + 1) >> 1; 78 if(judge(mid)) L = mid; //注意是L = mid,而不是L = mid +1,因为mid是当前合法的条件,不能舍去 79 else R = mid - 1; 80 } 81 return !L ? -1 : a[L].d; //如果一直不合法,那么就会一直往做区间找,所以无法满足就是一直找到0 82 } 83 84 int main() 85 { 86 n = read(); m = read(); 87 for(int i = 1; i <= n; ++i) {a[i].d = read(); a[i].p = read(); a[i].l = read();} 88 sort(a + 1, a + n + 1); 89 for(int i = 1; i <= n; ++i) update(root[i - 1], root[i], 1, Max, a[i].p, a[i].l); 90 for(int i = 1; i <= m; ++i) 91 { 92 GG = read(), LL = read(); 93 write(solve()); enter; 94 } 95 return 0; 96 }