[CTSC2018]混合果汁

题目连接: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 }
原文地址:https://www.cnblogs.com/mrclr/p/9208270.html