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;
}

 

posted @ 2018-10-15 19:52  xayata  阅读(324)  评论(2编辑  收藏  举报