zoukankan      html  css  js  c++  java
  • cf 799E

    $des$
    有 $n$ 个物品,第 $i$ 个物品的价格是 $v_i$ ,有两个人,每个人都喜欢 $n$ 个物品中的一些物品。
    要求选出正好 $m$ 个物品,满足选出的物品中至少有 $k$ 个物品被第一个人喜欢,$k$ 个物品被第二个人喜欢。并求出最小的价格和。

    $sol$
    将所有物品分成 $4$ 类
    1. $a cap b$
    2. $a - a cap b$
    3. $b - a cap b$
    4. $全集 - a cup b$

    枚举 $1$ 选了多少个
    此时 $2, 3$ 选多少个是固定的了
    这样的话只需维护剩余元素的前 $x$ 小值之和

    对顶堆维护
    大根堆维护前 $x$ 小
    查询时就是大根堆的权值之和
    删除元素标记

    维护剩余元素的前 $x$ 小值之和也可以用权值线段树

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 2e5 + 10;
    
    #define gc getchar()
    inline int read() {
        int 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 ++)
    #define LL long long
    #define Fin(a) freopen(a, "r", stdin)
    #define Fout(a) freopen(a, "w", stdout)
    #define E return
    #define End cout << "
    "
    
    map <int, int> Map;
    int W[N];
    int n, m, k, a, b;
    bool usea[N], useb[N];
    
    priority_queue <int, vector <int>, less <int> > Big;
    priority_queue <int, vector <int>, greater <int> > Small;
    
    int szab[N], jsab;
    int sza[N], jsa;
    int szb[N], jsb;
    int sznot[N], jsnot;
    LL sum1[N], sum2[N], sum3[N];
    
    int Size;
    LL Sum;
    
    void Update() {
        while(Big.size() && Map[Big.top()]) Map[Big.top()] --, Big.pop();
        while(Small.size() && Map[Small.top()]) Map[Small.top()] --, Small.pop();
    }
    
    int Now_size;
    int ato, bto, abto;
    
    void Add(int x) {
        Update();
        while(Size < Now_size && Small.size()) {
            int topsmall = Small.top(); Small.pop();
            Size ++, Sum += topsmall, Big.push(topsmall);
        }
        if(Size == Now_size) {
            if(Big.size() == 0) Small.push(x);
            else if(x < Big.top()) {
                int topbig = Big.top();
                Sum += (x - topbig);
                Big.pop(); Big.push(x);
                Small.push(topbig);
            }
            return ;
        }
        if(Size < Now_size) {
            Big.push(x);
            Size ++; Sum += x;
            return ;
        }
        Update();
        int topbig = Big.top();
        if(x < topbig) {
            Sum += (x - topbig);
            Big.pop(); Big.push(x);
            Small.push(topbig); 
        } else {
            Small.push(x);
        }
    }
    
    bool Judge() {
        if(m < k) return 1;
        if(jsab + jsa < k || jsab + jsb < k) return 1;
        if(k * 2 - jsab > m) return 1;
        return 0;
    }
    
    void RE() {
        cout << "error" << "
    ";
    }
    
    void Bef_work() {
        Rep(i, 1, n) {
            if(usea[i] && useb[i]) szab[++ jsab] = W[i];
            else if(usea[i]) sza[++ jsa] = W[i];
            else if(useb[i]) szb[++ jsb] = W[i];
            else sznot[++ jsnot] = W[i];
        }
        
        sort(szab + 1, szab + jsab + 1);
        sort(sza + 1, sza + jsa + 1);
        sort(szb + 1, szb + jsb + 1);
        sort(sznot + 1, sznot + jsnot + 1);
        
        Rep(i, 1, jsab) sum1[i] = sum1[i - 1] + szab[i];
        Rep(i, 1, jsa) sum2[i] = sum2[i - 1] + sza[i];
        Rep(i, 1, jsb) sum3[i] = sum3[i - 1] + szb[i];
        
        if(Judge()) {puts("-1"); exit(0) ;}
        
        Rep(i, 1, jsab) Add(szab[i]);
        Rep(i, k + 1, jsa) Add(sza[i]);
        Rep(i, k + 1, jsb) Add(szb[i]);
        Rep(i, 1, jsnot) Add(sznot[i]);    
        
        ato = min(k, jsa);
        bto = min(k, jsb);
        abto = 0;
        
    }
    
    bool Judge2(int x) {
        if(k - x > jsa || k - x > jsb || k * 2 - x > m) return 0;
        return 1;
    }
    
    void Del(int x) {
        Update();
        Map[x] ++;
        int topbig = Big.top();
        if(x <= topbig) {
            Size --, Sum -= x;
        }
        Update();
    }
    
    LL Ask(int x) {
        Update();
        while(Size > x && Big.size()) {
            int topbig = Big.top();
            Size --, Sum -= topbig;
            Big.pop();
            Small.push(topbig);
            Update();
        }
        while(Size < x && Small.size()) {
            int topsmall = Small.top();
            Size ++, Sum += topsmall;
            Small.pop();
            Big.push(topsmall);
            Update();
        }
        return Sum;
    }
    
    LL Calc(int x) {
        LL ret = 0;
        while(abto <= x && abto) {
            Del(szab[abto]);
            abto ++;
        }
        ret += sum1[abto - 1];
        while(ato > k - x && ato) {
            Add(sza[ato]);
            ato --;
        }
        ret += sum2[ato];
        while(bto > k - x && bto) {
            Add(szb[bto]);
            bto --;
        }
        ret += sum3[bto];
        ret += Ask(m - k * 2 + x);
        return ret;
    }
    
    int main() {
        n = read(), m = read(), k = read();
        Rep(i, 1, n) W[i] = read();
        a = read(); Rep(i, 1, a) W[0] = read(), usea[W[0]] = 1;
        b = read(); Rep(i, 1, b) W[0] = read(), useb[W[0]] = 1;
        
        Now_size = m;
        Bef_work();
        
        abto = 1;
        
        LL Answer = 1e18;
        Rep(i, 0, jsab) {
            if(!Judge2(i)) continue;
            Now_size = m - k * 2 + i;
            Answer = min(Answer, Calc(i));
        }
        cout << (Answer == 1e18 ? -1 : Answer);
        return 0;
    }
    对顶堆

    orz zbq

    /*
    枚举两个人都喜欢的物品选择了几个, 那么我们就知道了只有一个人喜欢的物品选择了几个
    这两种显然贪心要最小的
    然后剩下的都可以随便选 所以扔到数据结构里找前k小的和
    枚举移动一位时, 线段树里的数据改动量是O1的即可 
     
    
    
    
    */
    
    
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #include<iostream>
    #define ll long long
    #define M 300010
    #include<map>
    #define tmp tot
    using namespace std;
    int read() {
        int nm = 0, f = 1;
        char c = getchar();
        for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
        for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0';
        return nm * f;
    }
    
    ll n, m, k;
    int ver[M];
    bool vis1[M], vis2[M];
    ll st1[M], tp1, st2[M], tp2,st3[M], tp3,st4[M], tp4;
    ll ans = 1000000000000000010ll;
    ll allans = 0;
    ll note[M], biao[M];
    map<ll, ll> mp;
    ll notev[M * 20];
    
    ll t[M * 10], sz[M * 10];
    #define lson l, mid, now << 1
    #define rson mid + 1, r, now << 1 | 1
    
    void insert(int l, int r, int now, int x) {
        if(l > x || r < x) return;
        if(l == r) {
            t[now] += biao[x];
            sz[now]++;
            notev[now] = biao[x];
            return;
        }
        int mid = (l + r) >> 1;
        insert(lson, x), insert(rson, x);
        t[now] = t[now << 1] + t[now << 1 | 1];
        sz[now] = sz[now << 1] + sz[now << 1 | 1];
    }
    
    void del(int l, int r, int now, int x) {
        if(l > x || r < x) return;
        if(l == r) {
            t[now] -= biao[x];
            sz[now]--;
            return;
        }
        int mid = (l + r) >> 1;
        del(lson, x), del(rson, x);
        t[now] = t[now << 1] + t[now << 1 | 1];
        sz[now] = sz[now << 1] + sz[now << 1 | 1];
    }
    
    
    ll query(int k) {
    
        int now = 1;
        ll tmp = 0;
        while(k) {
            if(sz[now] == 0) break;
            if(sz[now] && !sz[now << 1] && !sz[now << 1 | 1])tmp += notev[now] * k, k = 0 /*-= sz[now]*/, now = now << 1 | 1;
            else if(sz[now << 1] <= k) tmp += t[now << 1], k-= sz[now << 1], now = now << 1 | 1;
            else now = now << 1;
        }
        return tmp;
    }
    
    int main() {
        n = read(), m = read(), k = read();
        for(int i = 1; i <= n; i++) {
            note[i] = ver[i] = read();
        }
        int q = read();
        for(int i = 1; i <= q; i++) {
            int op = read();
            vis1[op] = true;
        }
        q = read();
        for(int i = 1; i <= q; i++) {
            int op = read();
            vis2[op] = true;
        }
        for(int i = 1; i <= n; i++) {
            if(vis1[i] && vis2[i]) st1[++tp1] = ver[i];
            else if(vis1[i]) st2[++tp2] = ver[i];
            else if(vis2[i]) st3[++tp3] = ver[i];
            else st4[++tp4] = ver[i];
        }
        sort(st1 + 1, st1 + tp1 + 1);
        sort(st2 + 1, st2 + tp2 + 1);
        sort(st3 + 1, st3 + tp3 + 1);
        sort(st4 + 1, st4 + tp4 + 1);
        sort(note + 1, note + n + 1);
        note[0] = -1;
        int tot = 0;
        for(int i = 1; i <= n; i++) {
            if(note[i] == note[i - 1]) continue;
            tot++;
            biao[tot] = note[i];
            mp[note[i]] = tot;
        }
        /* 前面是数据预处理, 离散化部分 
        */
        int rx = min(k, tp1);
        int lx = max(0ll, max(k - tp2, k - tp3));
        /*
            确定m上下界, 不能弄出不够的情况 
        */
        if(min(tp2, tp3) + tp1 < k || rx < lx) {
            printf("-1");
            return 0;
        }
        
        for(int i = 1; i <= tp4; i++) insert(1, tmp, 1, mp[st4[i]]);
        for(int i = lx + 1; i <= tp1; i++) insert(1, tmp, 1, mp[st1[i]]);
        for(int i = k - lx + 1; i <= tp2; i++) insert(1, tmp, 1, mp[st2[i]]);
        for(int i = k - lx + 1; i <= tp3; i++) insert(1, tmp, 1, mp[st3[i]]);
        /*
            将所有随便选的扔到线段树里 
        */
        for(int i = 1; i <= lx; i++) allans += st1[i];
        for(int i = 1; i <= k - lx; i++) allans += st2[i] + st3[i];
        /*
            统计当前答案 
        */
        int tppp = k - lx; // 这个变量是一个人喜欢的选了几个 
        int zz = m - 2 * (k - lx) - lx;
        if(zz >= 0) ans = min(ans, allans + query(zz));//当前可行就统计答案 
        for(int i = lx + 1; i <= rx; i++) {
            // i是两个人喜欢的 
            del(1, tmp, 1, mp[st1[i]]); ;// 强制要选 所以从线段树中删除 
            allans += st1[i];
            if(tppp == 0) break;//不合法 
            insert(1, tmp, 1, mp[st2[tppp]]), allans -= st2[tppp];
            insert(1, tmp, 1, mp[st3[tppp]]), allans -= st3[tppp];
            /* 将已经可以随便选的 一个人喜欢的加入线段树 
            */
            tppp--;
            int op = m - 2 * tppp - i; //从线段树中要拿多少 
            if(op < 0) continue;
            if(op > sz[1]) continue;
            ans = min(ans, allans + query(op));
        }
        if(ans == 1000000000000000010ll) ans = -1;
        cout << ans << "
    ";
        return 0;
    }
    权值线段树
  • 相关阅读:
    史上最全的maven pom.xml文件教程详解
    Java中的断言Assert
    Log4j详细配置
    (实战篇)SSM三大框架整合详细教程(Spring+SpringMVC+MyBatis)
    Java的反射机制
    mysql数据库常用SQL语句
    响应式布局——@media详解
    CXF使用教程(三)——基于Spring的webService开发
    CXF使用教程(二)——webService自动生成客户端代码
    Spring事务管理
  • 原文地址:https://www.cnblogs.com/shandongs1/p/9765508.html
Copyright © 2011-2022 走看看