「NOI2019」序列

有趣的一道题

首先考虑费用流建图,大概长这样:

(其余边流量均为1,源点至多流出K单位流量)

然后我们有一个显然的发现:一个点与源点或汇点的连边一旦被选了,就不会退掉

于是我们得出一个结论:一个数只要被选就不可能将它删除

我们抛开费用流,考虑这样一个算法:

我们每次上下各选一个点,在零散点对数量不超过\(K-L\)的前提下权值和最大,这本质也是费用流过程,但具体的流量什么的可以忽略

于是我们贪心地考虑几种情况即可,用堆维护

#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, K, L, t, i, j, a[N], b[N], f[N], tot;
long long ans = 0;
struct str {
    int a, x;
};
bool operator<(str a, str b) {
    if (a.a == b.a)
        return a.x < b.x;
    return a.a < b.a;
}
struct PR {
    priority_queue<str> q, del;
    void Del() {
        while (!del.empty() && q.top().a == del.top().a && q.top().x == del.top().x) del.pop(), q.pop();
    }
    void Push(str x) {
        q.push(x);
        Del();
    }
    void Erase(str x) {
        del.push(x);
        Del();
    }
    str Top() {
        Del();
        return q.top();
    }
    bool Empty() {
        Del();
        return q.empty();
    }
    void Pop() {
        Del();
        q.pop();
    }
    void Clear() {
        while (!q.empty()) q.pop();
        while (!del.empty()) del.pop();
    }
} q1, q2, q3, q4, q5;
void cha(str x) {
    if (f[x.x] == 2)
        q4.Erase((str){ a[x.x], x.x });
    else {
        q1.Erase((str){ a[x.x] + b[x.x], x.x });
        q5.Push((str){ b[x.x], x.x });
    }
    q2.Erase(x);
}
void chb(str x) {
    if (f[x.x] == 1)
        q5.Erase((str){ b[x.x], x.x });
    else {
        q1.Erase((str){ a[x.x] + b[x.x], x.x });
        q4.Push((str){ a[x.x], x.x });
    }
    q3.Erase(x);
}
int main() {
    freopen("sequence.in", "r", stdin);
    freopen("sequence.out", "w", stdout);
    scanf("%d", &t);
    while (t--) {
        scanf("%d %d %d", &n, &K, &L);
        for (i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            f[i] = 0;
        }
        q1.Clear(), q2.Clear(), q3.Clear(), q4.Clear(), q5.Clear();
        for (i = 1; i <= n; ++i) scanf("%d", &b[i]);
        for (i = 1; i <= n; ++i) {
            q1.Push((str){ a[i] + b[i], i });
            q2.Push((str){ a[i], i });
            q3.Push((str){ b[i], i });
        }
        tot = K - L;
        ans = 0;
        while (K--) {
            if (tot) {
                --tot;
                str x = q2.Top();
                str y = q3.Top();
                ans += x.a + y.a;
                if (f[x.x] == 2)
                    ++tot;
                if (f[y.x] == 1)
                    ++tot;
                if (x.x == y.x)
                    ++tot;
                cha(x);
                f[x.x] |= 1;
                chb(y);
                f[y.x] |= 2;
            } else {
                str x = (str){ -1000000000, 0 };
                if (!q4.Empty())
                    x = q4.Top();
                str y = (str){ -1000000000, 0 };
                if (!q5.Empty())
                    y = q5.Top();
                str A = q2.Top();
                str B = q3.Top();
                str c = q1.Top();
                if (c.a > A.a + y.a && c.a > B.a + x.a) {
                    ans += c.a;
                    q1.Pop();
                    q2.Erase((str){ a[c.x], c.x });
                    q3.Erase((str){ b[c.x], c.x });
                    f[c.x] = 3;
                } else if (A.a + y.a > B.a + x.a) {
                    ans += A.a + y.a;
                    if (f[A.x] == 2)
                        ++tot;
                    cha(A);
                    chb(y);
                    f[A.x] |= 1;
                    f[y.x] |= 2;
                } else {
                    ans += B.a + x.a;
                    if (f[B.x] == 1)
                        ++tot;
                    cha(x);
                    chb(B);
                    f[B.x] |= 2;
                    f[x.x] |= 1;
                }
            }
        }
        printf("%lld\n", ans);
    }
}
posted @ 2020-07-03 20:14  夜螢光  阅读(208)  评论(0编辑  收藏  举报