LOJ 3153 「JOI Open 2019」三级跳
数据范围看起来不太能用偏暴力的做法。
于是就需要从 \(a, b, c\) 来入手去解决问题。
对于这种区间内部选取问题,通常可以考虑支配对。
也就是找出部分变量 \(x\le y\),若 \(x\le x'\le y'\le y\),且选取 \((x', y')\) 比选取 \((x, y)\) 优秀,那么这组 \((x, y)\) 相当于是无用的(限制更紧且更劣)。
而通过分析,可以使最终有用的 \((x, y)\) 的数量级较为理想就是需要达成的条件,因为这样就只需要考虑去维护有用的 \((x, y)\) 了。
考虑到题目中 \(b - a\le c - b\) 的限制,那么就可以从两个方向思考:
- 使 \(b - a\) 更小且 \(b\) 不会向右移动。
- 使 \(c - b\) 更大且 \(b\) 不会向右移动。
第一个方向是相对来说比第二个方向好考虑的。
这是因为”使 \(c - b\) 更大且 \(b\) 不会向右移动“,那么 \(b\) 就是向左,且如果是让 \(c\) 向右就会去冲开范围的限制(\(c\) 是最大的,不能让右边界往外扩展),所以实际上只能 \(b, c\) 都是向左移动,那么不好控制 \(c - b\) 的值。
然而如果是“使 \(b - a\) 更小且 \(b\) 不会向右移动”,那么 \(b\) 是向左,同理 \(a\) 向右,这样 \(b - a\) 的值是很好控制的,因为只要 \(b\) 往左移或者 \(a\) 往右移 \(b - a\) 就变小了。
于是考虑分析什么样的 \((a, b)\) 是无用的。
那么根据之前的分析,就是要找到一个 \(a < d < b\),然后用 \(d\) 去替代 \(a\) 或 \(b\)。
那么可以知道,如果 \(A_d\ge A_a\),那么 \((d, b)\) 比 \((a, b)\) 优;如果 \(A_d\ge A_b\),那么 \((a, d)\) 比 \((a, b)\) 优。
于是一个 \((a, b)\) 合法的条件是不存在 \(a < d < b\),使得 \(A_d\ge \min\{A_a, A_b\}\)。
此时 \((a, b)\) 的数量就已经是 \(\mathcal{O}(n)\) 的了。
对此可以考虑如何求出 \((a, b)\),直接令 \(A_a\le A_b\) 然后对于 \(a\) 去求解。
如果有 \(a < x < y\) 满足 \(A_x\ge A_a, A_y\ge A_a\),那么 \((a, y)\) 一定是不合法的。
于是 \(b\) 一定是满足 \(b > a, A_b\ge A_a\) 中的最小的 \(b\)。
同时再钦定一次 \(A_b\le A_a\),就可以得到所有支配对了。
即使会有部分重复,但也可以通过分析知道至多也只会有 \(2n\) 个支配对。
接下来考虑知道了支配对又如何处理询问。
首先可以离线下来扫描线,扫描 \(c\) 时对于每个 \(a\) 维护 \(a < b < c'\le c\) 的合法的 \(A_a + A_b + A_{c'}\) 的最大值。
对于支配对 \((a, b)\),其贡献就是 \(A_a + A_b\),对应合法的 \(c'\) 需要满足 \(c'\ge 2b - a\)。
于是可以考虑在扫到 \(c = 2b - a\) 时在 \(a\) 加入这个支配对,对应维护一个 \(\operatorname{mx}_a\leftarrow \max\{\operatorname{mx}_a, A_a + A_b\}\) 的操作,相当于是提前放好 \(A_a + A_b\) 的贡献。
然后在加入完后,此时已经加入的支配对肯定对于当前的 \(c\) 都合法,于是维护一个 \(\forall i\in [1, c], \operatorname{mx'}_i = \max\{\operatorname{mx'}_i, \operatorname{mx}_i + A_c\}\) 的操作,相当于是在此处再算上 \(A_c\) 的贡献。
那么能够发现 \(\operatorname{mx'}_a\) 就是前面提到的 \(a < b < c'\le c\) 的合法的 \(A_a + A_b + A_{c'}\) 的最大值,那么对于询问直接查询 \(\max\limits_{i = l}^r \operatorname{mx'}_i\) 即可。
这些都可以直接用线段树维护。
时间复杂度 \(\mathcal{O}((n + q)\log n)\)。
#include<bits/stdc++.h>
constexpr int inf = 0x3f3f3f3f;
constexpr int maxn = 5e5 + 10, maxm = 1e6 + 10;
int n, m, q;
int a[maxn];
int stk[maxn], top;
int pl[maxm], pr[maxm];
std::vector<int> up[maxn], uq[maxn];
int ql[maxn], qr[maxn], qans[maxn];
int mxv[maxm * 4], tag[maxm * 4], mx[maxm * 4];
inline void pushtag(int k, int v) {
mx[k] = std::max(mx[k], mxv[k] + v), tag[k] = std::max(tag[k], v);
}
inline void pushdown(int k) {
if (tag[k]) {
pushtag(k << 1, tag[k]), pushtag(k << 1 | 1, tag[k]);
tag[k] = 0;
}
}
inline void updv(int x, int y, int k = 1, int l = 1, int r = n) {
mxv[k] = std::max(mxv[k], y);
if (l == r) return ;
pushdown(k);
int mid = l + r >> 1;
x <= mid ? updv(x, y, k << 1, l, mid) : updv(x, y, k << 1 | 1, mid + 1, r);
mx[k] = std::max(mx[k << 1], mx[k << 1 | 1]);
}
inline int query(int x, int y, int k = 1, int l = 1, int r = n) {
if (x <= l && r <= y) return mx[k];
pushdown(k);
int mid = l + r >> 1;
if (y <= mid) return query(x, y, k << 1, l, mid);
if (x > mid) return query(x, y, k << 1 | 1, mid + 1, r);
return std::max(query(x, y, k << 1, l, mid), query(x, y, k << 1 | 1, mid + 1, r));
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; i++) {
while (top && a[i] > a[stk[top]]) top--;
if (top && i * 2 - stk[top] <= n) {
m++, pl[m] = stk[top], pr[m] = i;
}
stk[++top] = i;
}
top = 0;
for (int i = n; i; i--) {
while (top && a[i] > a[stk[top]]) top--;
if (top && stk[top] * 2 - i <= n) {
m++, pl[m] = i, pr[m] = stk[top];
}
stk[++top] = i;
}
for (int i = 1; i <= m; i++) up[pr[i] * 2 - pl[i]].push_back(i);
scanf("%d", &q);
for (int i = 1; i <= q; i++) {
scanf("%d%d", &ql[i], &qr[i]);
uq[qr[i]].push_back(i);
}
memset(mxv, -0x3f, sizeof(mxv));
memset(mx, -0x3f, sizeof(mx));
for (int i = 1; i <= n; i++) {
for (int j : up[i]) updv(pl[j], a[pl[j]] + a[pr[j]]);
pushtag(1, a[i]);
for (int j : uq[i]) qans[j] = query(ql[j], qr[j]);
}
for (int i = 1; i <= q; i++) {
printf("%d\n", qans[i]);
}
return 0;
}
浙公网安备 33010602011771号