• AGC018C Coins (set)


    题目大意:

    给出n个人,每个人手里都有xi个金牌,yi个银牌,ci个铜牌。

    你需要选出X个人,拿走他们手里的金牌,选出Y个人,拿走他们手里的银牌,选出Z个人,拿走他们手里的铜牌

    X+Y+Z = n。并且选的这X,Y,Z个人里不能有重复的。

    题解:

    不妨先考虑只有金牌和银牌怎么做。

    如果只有两种,就变成很简单的贪心了,依据每个人手中的金牌数x减去银牌数y然后排序,按差值选即可

    现在又多了一个铜牌,我们还是按照这个思路

    依据每个人手中的金牌数x减去银牌数y然后排序。

    然后我们考虑如何分配我们的X和Y。

    注意到,排完序以后,这里有一个奇妙的性质出现了

    这个性质简单来说就是,银牌从排序靠前的元素选,金牌从排序靠后的元素选,这样做会更优

    或者这样说,银牌和金牌的选择是互不交叉的。(考虑如果有交叉,那么相互交换一下会更优)

    那么我们就可以这样求出答案

    枚举一个k,从前k个元素中选出Y个银牌,从后n-k个元素中选出X个金牌,其他都是选铜牌

    这样我们只需要把k从Y枚举到n-X就可以了

    那铜牌的影响怎么处理呢?

    就让金牌数和银牌数直接减去铜牌数,然后最后的答案加上铜牌数,这样所有的铜牌数就变成了0

    就可以不考虑它的影响了。

    对每一个k,答案就是前k个元素中最大的Y个银牌数和,加上后n-k个元素中最大的X个银牌数和

    这个显然可以用set来维护。

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <set>
    #include <map>
    #define fi first
    #define se second
    using namespace std;
    const int maxn = 1e5 + 100;
    typedef pair<int, int> PII;
    bool cmp(const PII &A, const PII &B){
        return A.fi - A.se < B.fi - B.se;
    }
    PII a[maxn];
    set<int> S[3];
    map<int, int> H[3];
    int ssz[3], sz[3];
    long long ans = 0, temp, ans1[maxn], ans2[maxn];
    void Erase(int x, int ty){
        if(H[ty][x] == 1) S[ty].erase(x);
        H[ty][x]--;
        ans -= x;
        ssz[ty]--;
    }
    void Insert(int x, int ty){
        if(H[ty][x] == 0) S[ty].insert(x);
        H[ty][x]++;
        ans += x;
        ssz[ty]++;
    }
    
    void S_Insert(int x, int ty){
        if(ssz[ty] == sz[ty]){
            int y = *S[ty].begin();
            if(y > x) return;
            Erase(y, ty);
        }
        Insert(x, ty);
    }
    
    int main()
    {
        int X, Y, Z, x, y, z;
        cin>>X>>Y>>Z;
        int n = X+Y+Z;
        for(int i = 1; i <= n; i++){
            scanf("%d %d %d", &x, &y, &z);
            x -= z;
            y -= z;
            ans += z;
            a[i].fi = x;
            a[i].se = y;
        }
        temp = ans;
        ans = 0;
        sort(a+1, a+1+n, cmp);
        sz[1] = Y; sz[0] = X;
        for(int i = 1; i <= Y; i++) S_Insert(a[i].se, 1);
        ans1[Y] = ans;
        for(int i = Y+1; i <= n-X; i++){
            S_Insert(a[i].se, 1);
            ans1[i] = ans;
        }
        ans = 0;
        for(int i = n; i >= n-X+1; i--) S_Insert(a[i].fi, 0);
        ans2[n-X+1] = ans;
        for(int i = n-X; i >= Y+1; i--){
            S_Insert(a[i].fi, 0);
            ans2[i] = ans;
        }
        ans = -1e18;
        for(int i = Y; i <= n-X; i++) ans = max(ans, ans1[i]+ans2[i+1]);
        cout<<ans+temp<<endl;
        return 0;
    }
  • 相关阅读:
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    block的是发送信号的线程,又不是处理槽函数的线程
  • 原文地址:https://www.cnblogs.com/Saurus/p/7239252.html
Copyright © 2020-2023  润新知