题解:P10093 [ROIR 2022] 礼物 (Day 2)
题意简述
给定 \(k\) 和一个长度为 \(n\) 的序列 \(a\),定义一个区间 \([l,r](l\leq r\land r-l+1\geq k)\) 的权值为 \(a[l,r]\) 中除去前 \(k\) 大的元素和。求出最大权值。
对于所有数据,\(1\leq n\leq 2\times 10^5\),\(0\leq k\leq \min(100,n)\),\(-10^9\leq a_i\leq 10^9\)。
题解
有点乱炖的意思了。
对这种权值的一类刻画方式就是枚举某个特定的值。在本题中,容易想到枚举第 \(k\) 大 \(a_p\)。
思考怎样的区间 \(l\leq p\leq r\) 会使得 \(a_p\) 为该区间的第 \(k\) 大。对于排序网络相关的问题,考虑构造 \(0/1\) 序列。具体来说,构造 \(0/1\) 序列 \(b_i=[a_i\geq a_p]\),那么区间 \([l,r]\) 合法的充要条件就是 \(\sum_{i=l}^r b_i=k\)。并且容易注意到,一个区间的权值,转化为了所有为 \(0\) 的位置对应的元素和。
注意到 \(k\) 很小,考虑从这里入手去做一些偏暴力的东西。一个自然的想法就是分别找到 \(a_p\) 左右侧的 \(k-1\) 个 \(1\),得到最多 \(2k-1\) 个 \(1\),那么我们就可以 \(O(k)\) 地枚举出区间和为 \(k\) 的所有合法区间。
假装我们已经可以找到这些 \(1\),然后正在枚举某个区间和为 \(k\) 且两端都是 \(1\) 的区间 \([l,r]\),这时候我们肯定是保证区间和不变的基础上,尝试拓展区间的两端到 \([l',r']\)。 显然可以贪心,以左端点为例,设 \(l\) 前面最近的 \(1\) 的位置为 \(l_1\),那么我们就要求出
其中 \(s\) 为 \(a\) 的前缀和序列。对 \(s\) 建立 ST 表即可 \(O(1)\) 求出上面的东西,右端点同理。还有一部分是 \([l,r]\) 中 \(0\) 的位置对应的元素和,这个直接枚举区间的过程中存一下就行了。把这些值全部加起来就是这个区间能得到的最大权值,和答案取 \(\max\) 即可。
最后就是怎么找到 \(a_p\) 左右侧的 \(k-1\) 个 \(1\)。这个也很典。对 \(a\) 建立一个链表,然后从小到大扫每个元素,直接在链表上分别跳 \(k-1\) 次前驱和后继就是我们要找的 \(1\),最后在链表中把当前元素删掉即可。
时间复杂度为 \(O(n\log{n}+nk)\)。
实现 & 代码
细节还是有点多的。
首先把 \(k=0\) 特判掉,枚举端点,用建出来的 ST 表就可以做,注意这种 case 答案不能和 \(0\) 取 \(\max\),因为 \(l\leq r\),我们不能不选。
\(k>0\) 的情况在枚举区间时可能会有一些边界问题,改成左右各找 \(k\) 个 \(1\) 可以实现得更优雅一些。
应该没啥了,贴个代码。
#include <iostream>
#include <algorithm>
using namespace std;
#define lowbit(x) ((x) & -(x))
#define chk_min(x, v) (x) = min((x), (v))
#define chk_max(x, v) (x) = max((x), (v))
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5, LOGN = 18 + 5, K = 205;
const ll INF = 1e16;
int n, k, a[N], lg2[N];
int szc, c[K];
int top, stk[K];
pii b[N];
ll ans = -INF, s[N];
struct ST {
ll mx[LOGN][N], mn[LOGN][N];
void init() {
for (int i = 0; i <= n; ++i) mx[0][i] = mn[0][i] = s[i];
for (int i = 1; i <= lg2[n + 1]; ++i)
for (int j = 0; j <= n - (1 << i) + 1; ++j)
mx[i][j] = max(mx[i - 1][j], mx[i - 1][j + (1 << (i - 1))]),
mn[i][j] = min(mn[i - 1][j], mn[i - 1][j + (1 << (i - 1))]);
}
ll query_mx(int l, int r) {
int k = lg2[r - l + 1];
return max(mx[k][l], mx[k][r - (1 << k) + 1]);
}
ll query_mn(int l, int r) {
int k = lg2[r - l + 1];
return min(mn[k][l], mn[k][r - (1 << k) + 1]);
}
} st;
int pos[N], id[N];
struct List {
int tot, head, tail, prv[N], nxt[N], val[N];
void init() {
head = 1, tail = 2; tot = 2;
nxt[head] = tail, prv[tail] = head;
id[head] = 0, id[tail] = n + 1;
}
void insert(int p, int v) {
int q = ++tot; val[q] = v;
nxt[q] = nxt[p], prv[nxt[p]] = q;
prv[q] = p, nxt[p] = q;
}
void del(int p) { prv[nxt[p]] = prv[p], nxt[prv[p]] = nxt[p]; }
} lt;
void preproc() {
lg2[1] = 0;
for (int i = 2; i <= n + 1; ++i) lg2[i] = lg2[i >> 1] + 1;
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
cin >> n >> k; lt.init();
int last = lt.head;
for (int i = 1; i <= n; ++i) {
cin >> a[i]; b[i] = { a[i], i };
s[i] = s[i - 1] + a[i];
lt.insert(last, a[i]), last = pos[i] = lt.tot, id[lt.tot] = i;
}
preproc(), st.init();
sort(b + 1, b + n + 1);
for (int i = 1; i <= n; ++i) {
if (!k) { chk_max(ans, s[i] - st.query_mn(0, i - 1)); continue; }
int p = pos[b[i].second];
stk[++top] = p, p = lt.prv[p];
for (int cnt = 1; cnt <= k && p; ++cnt, p = lt.prv[p]) stk[++top] = p;
szc = -1;
while (top) c[++szc] = stk[top--];
p = lt.nxt[pos[b[i].second]];
for (int cnt = 1; cnt <= k && p; ++cnt, p = lt.nxt[p]) c[++szc] = p;
--szc;
ll sum = 0;
for (int j = 1; j <= k; ++j) sum += a[id[c[j]]];
for (int j = 1; j <= szc - k + 1; ++j) {
int l = id[c[j - 1]], r = id[c[j]] - 1;
ll lmx = s[r] - st.query_mn(l, r);
l = id[c[j + k - 1]], r = id[c[j + k]] - 1;
ll rmx = st.query_mx(l, r) - s[l];
ll md = s[id[c[j + k - 1]]] - s[id[c[j]] - 1] - sum;
chk_max(ans, lmx + rmx + md);
sum += a[id[c[j + k]]] - a[id[c[j]]];
}
lt.del(pos[b[i].second]);
}
cout << ans;
return 0;
}

浙公网安备 33010602011771号