zoukankan      html  css  js  c++  java
  • 99: AGC-018C 堆+思维

    $des$
    有 $X + Y + Z$ 个人,第 $i$ 个人有 $A_i$ 个金币,$B_i$ 个银币,$C_i$ 个铜币。
    选出 $X$ 个人获得其金币,选出 $Y$ 个人获得其银币,选出 $Z$ 个人获得
    其铜币,在不重复选某个人的前提下,最大化获得的币的总数。
    $sol$
    令 $A_i = A_i - C_i, B_i = B_i - C_i$ ,问题变为选出 $X$ 个人获得
    其金币,选出 $Y$ 个人获得其银币,再将答案加上 $sum C_i$.
    按 $A_i$ 从大到小排序,枚举选出的 $X$ 个人中 $A_i$ 最小的人,显然这个
    人之前的人要么在选出的 $X$ 个人中,要么在选出的 $Y$ 个人中。
    因为假设存在 $j in [1, i]$ 并没有使用 $j$, 那么把 $A_i$ 换成 $A_j$ 答案一定更优
    那么只要对每个位置 $i, i in [X, X + y]$ 计算两个信息:
    $i$ 之前 $A_i - B_i$ 最大的 $X$ 个人的 $A_i - B_i$ 的和,这里相当于令所有的 $A_i = A_i - B_i$
    最后再加上 $sum B_i$.
    $i$ 之后 $B_i$ 最小的 $Z$ 个人的 $B_i$ 之和, 这 $Z$ 个人是选 $C$ 的。
    于是我需要从前往后扫一遍,用小根堆维护当前 $A_i - B_i$ 最大的 $X$ 个人,每加入一个人与堆顶比较;
    再从后往前用大根堆维护第二个信息即可。
    时间复杂度 $O(nlogn)$

    $code$

    #include <bits/stdc++.h>
    
    #define LL long long
    
    using namespace std;
    
    #define gc getchar()
    inline LL read() {
        LL x = 0; char c = gc;
        while(c < '0' || c > '9') c = gc;
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;
        return x;
    }
    
    #define Rep(i, a, b) for(int i = a; i <= b; i ++) 
    
    const int N = 1e5 + 10;
    
    struct Node {
        LL A, B, C, A_B;
    } Num[N];
    LL L[N], R[N];
    
    priority_queue <LL, vector<LL>, greater<LL> > Q;
    priority_queue <LL, vector<LL>, less<LL> > Qu;
    
    bool Cmp(Node a, Node b) {
        return a.A > b.A;
    }
    
    int main() {
        LL X, Y, Z, n;
        LL Sum_C = 0, Sum_B = 0, Sum = 0;
        
        X = read(), Y = read(), Z = read();
        n = X + Y + Z;
        Rep(i, 1, n) {
            Num[i] = (Node) {read(), read(), read(), 0};
            Sum_C += Num[i].C; 
            Num[i].A -= Num[i].C, Num[i].B -= Num[i].C;
            Num[i].A_B = Num[i].A - Num[i].B;
            Sum_B += Num[i].B;
        }
        
        sort(Num + 1, Num + n + 1, Cmp);
        
        Rep(i, 1, X) Q.push(Num[i].A_B), Sum += Num[i].A_B;
        L[X] = Sum;
        Rep(i, X + 1, X + Y) {
            int a_b = Num[i].A_B, tp = Q.top();
            if(a_b > tp) {
                Q.pop(); Q.push(a_b); Sum += a_b - tp;
            }
            L[i] = Sum;
        }
        
        Sum = 0;
        for(int i = n; i > X + Y; i --) Qu.push(Num[i].B), Sum += Num[i].B;
        R[X + Y + 1] = Sum;
        for(int i = X + Y; i >= X; i --) {
            int b = Num[i].B, tp = Qu.top();
            if(b < tp) {
                Qu.pop(); Qu.push(b); Sum += b - tp;
            }
            R[i] = Sum;
        }
        
        LL Answer = - 1e18;
        Rep(i, X, X + Y) {
            Answer = max(Answer, L[i] - R[i + 1]);
        }
        
        cout << Answer + Sum_C + Sum_B;
        return 0;
    }
  • 相关阅读:
    芯片难题
    permutation
    小凸玩矩阵
    gender
    NOI2019序列非启发式做法
    莫比乌斯函数&莫比乌斯反演
    「雅礼day2」最大公约数gcd
    容斥原理&反演
    树上路径的交和并
    CF906D Power Tower
  • 原文地址:https://www.cnblogs.com/shandongs1/p/9794225.html
Copyright © 2011-2022 走看看