• [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 }
  • 相关阅读:
    【区间DP&&记忆化搜索】乘法游戏
    洛谷P1608路径统计
    2021省选游记
    涂色计划P4170
    01迷宫及路径记录(DFS&&BFS)
    [YBTOJ递推算法强化训练4]序列个数
    C++关于string 的优先队列以及重载运算符
    浅谈C++STL容器
    集合的划分
    图的表示
  • 原文地址:https://www.cnblogs.com/mrclr/p/9208270.html
Copyright © 2020-2023  润新知