题意
传送门 AcWing 163 生日礼物
题解
预处理出连续的非负与负数部分,得到非负数与负数连续、交替的数列 a a a。当没有 M M M 的限制时,答案为所有连续的非负部分(设其数量为 c n t cnt cnt)的和 s u m sum sum。考虑 M M M 的限制,若 c n t ≤ M cnt\leq M cnt≤M,则答案为 s u m sum sum;反之,需要考虑将选择的连续部分减少 K , K = c n t − M K,K=cnt-M K,K=cnt−M,并处理 s u m sum sum 中对应的贡献。
若 K = 1 K=1 K=1,则减少连续部分为存在两种情况:合并相邻的 2 2 2 个非负数 a i , a i + 2 a_i,a_{i+2} ai,ai+2;删除已选择的一个非负数 a j a_j aj。对于前者,贡献为非负数间的负数 a i + 1 a_{i+1} ai+1;对于后者,贡献为其负数 − a j -a_j −aj。可以观察到上述两种情况不可能出现在连续的位置。那么答案为 s u m sum sum 加上所有负数以及取负后的非负数 a ′ a' a′ 中的最大值。
若 K = 2 K=2 K=2,根据贪心策略,答案一定为以下两种情况之一:选择最大值 a i ′ a'_i ai′,以及除了 a i − 1 ′ , a i ′ , a i + 1 ′ a'_{i-1},a'_i,a'_{i+1} ai−1′,ai′,ai+1′ 之外的最小值;选择 a i − 1 ′ , a i + 1 ′ a'_{i-1},a'_{i+1} ai−1′,ai+1′。按照这个思路,可以推广到 K > 2 K>2 K>2 的情况。
使用一个链表以及一个大根堆维护 a ′ a' a′,当 c n t > M cnt>M cnt>M 时,不断取出堆顶,设其为 a i ′ a'_i ai′,将其贡献加入答案,删除 a i − 1 ′ , a i ′ , a i + 1 ′ a'_{i-1},a'_i,a'_{i+1} ai−1′,ai′,ai+1′,插入 a i − 1 ′ + a i + 1 ′ − a i ′ a'_{i-1}+a'_{i+1}-a'_i ai−1′+ai+1′−ai′(相当于取消 a i ′ a'_i ai′ 的操作,而进行 a i − 1 ′ , a i + 1 ′ a'_{i-1},a'_{i+1} ai−1′,ai+1′ 的操作),此时涵盖了上述推论的两种情况,且保证序列保持非负数与负数交替、连续。
考虑边界情况,位于左右边界的负数部分显然不可能取到,且会影响合并连续部分时的逻辑,需要在更新链表的同时进行删除。
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
typedef pair<int, int> P;
const int maxn = 100005;
struct node
{
#define a(x) list[x].a
#define pre(x) list[x].pre
#define nxt(x) list[x].nxt
int a, pre, nxt;
} list[maxn];
int N, M, A[maxn], tot, head, tail;
bool del[maxn];
priority_queue<P> Q;
void init()
{
tot = 2, head = 1, tail = 2;
a(head) = a(tail) = 0;
nxt(head) = tail, pre(tail) = head;
}
int insert(int p, int a)
{
int q = ++tot;
a(q) = a;
pre(nxt(p)) = q, nxt(q) = nxt(p);
nxt(p) = q, pre(q) = p;
return q;
}
void remove(int p)
{
pre(nxt(p)) = pre(p), nxt(pre(p)) = nxt(p);
}
int main()
{
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; ++i)
scanf("%d", A + i);
int res = 0, cnt = 0, l = 1, r = N;
while (l <= r && A[l] < 0)
++l;
while (l <= r && A[r] < 0)
--r;
init();
for (int i = l, x = head; i <= r; ++i)
{
bool f = A[i] >= 0;
int s = A[i];
while (i + 1 <= r && f == (A[i + 1] >= 0))
s += A[++i];
if (s >= 0)
res += s, ++cnt;
x = insert(x, f ? -s : s);
Q.push(P(a(x), x));
}
while (cnt > M)
{
--cnt;
while (del[Q.top().second])
Q.pop();
res += Q.top().first;
int x = Q.top().second;
Q.pop();
if (x == nxt(head))
{
del[nxt(x)] = del[x] = 1;
remove(nxt(x)), remove(x);
}
else if (x == pre(tail))
{
del[pre(x)] = del[x] = 1;
remove(pre(x)), remove(x);
}
else
{
a(x) = a(pre(x)) + a(nxt(x)) - a(x);
Q.push(P(a(x), x));
del[pre(x)] = del[nxt(x)] = 1;
remove(pre(x)), remove(nxt(x));
}
}
printf("%d\n", res);
return 0;
}