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