「JOI Open 2019」三级跳(找性质缩状态+线段树)
题解:
若有\(a[i]、a[j](i<j)\),若存在\(mid\)满足\(i<mid<j,a[mid]>=a[i],a[j]\),则\(a[i]、a[mid]\)作为前两个一定更优。
那么,所以有用的\(a[i]、a[j]\)便缩减到了\(O(n)\)个,具体为每个数和它左边第一个比他的大的数形成的对+每个数和它右边第一个大于等于它的数形成的对。
可以用栈找出这\(O(n)\)对有用的。
接着,对于询问,按l从大到小排序,在求解的同时加入有用的对。
现在问题变成了,有\(n\)个标记\(c[1..n]\),一开始全0,每次使\(c[x..n]\)对一个数取max,或者查询一个区间的\(c[i]+a[i]\)的最大值。
由于查询也是求区间max,所以不用吉利线段树(或者什么单调栈),普通lazytag线段树就可以做了。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
const int N = 5e5 + 5;
int n, a[N], Q;
struct ask {
int l, r;
} b[N];
vector<int> q[N];
#define pb push_back
void Init() {
scanf("%d", &n);
fo(i, 1, n) scanf("%d", &a[i]);
scanf("%d", &Q);
fo(i, 1, Q) scanf("%d %d", &b[i].l, &b[i].r), q[b[i].l].pb(i);
}
#define i0 i + i
#define i1 i + i + 1
int t[N * 4], mx[N * 4], lz[N * 4];
int pl, pr, px;
void bt(int i, int x, int y) {
if(x == y) { mx[i] = t[i] = a[x]; return;}
int m = x + y >> 1;
bt(i0, x, m); bt(i1, m + 1, y);
mx[i] = t[i] = max(t[i0], t[i1]);
}
void qmax(int i, int v) {
lz[i] = max(lz[i], v);
t[i] = max(t[i], v + mx[i]);
}
void down(int i) {
if(lz[i]) {
qmax(i0, lz[i]);
qmax(i1, lz[i]);
lz[i] = 0;
}
}
void add(int i, int x, int y) {
if(y < pl || x > pr) return;
if(x >= pl && y <= pr) {
qmax(i, px); return;
}
int m = x + y >> 1; down(i);
add(i0, x, m); add(i1, m + 1, y);
t[i] = max(t[i0], t[i1]);
}
void ft(int i, int x, int y) {
if(y < pl || x > pr) return;
if(x >= pl && y <= pr) {
px = max(px, t[i]); return;
}
int m = x + y >> 1; down(i);
ft(i0, x, m); ft(i1, m + 1, y);
}
int z[N], z0;
int ans[N];
vector<int> p[N];
void build() {
bt(1, 1, n);
fo(i, 1, n) {
while(z0 > 0 && a[i] > a[z[z0]]) {
p[z[z0]].pb(i);
z0 --;
}
z[++ z0] = i;
}
z0 = 0;
fd(i, n, 1) {
while(z0 > 0 && a[i] >= a[z[z0]]) {
int j = z[z0];
pl = 2 * j - i, pr = n; px = a[i] + a[j];
add(1, 1, n);
z0 --;
}
z[++ z0] = i;
ff(k, 0, p[i].size()) {
int j = p[i][k];
pl = 2 * j - i, pr = n; px = a[i] + a[j];
add(1, 1, n);
}
ff(j, 0, q[i].size()) {
int x = q[i][j];
pl = i; pr = b[x].r; px = 0;
ft(1, 1, n);
ans[x] = px;
}
}
fo(i, 1, Q) pp("%d\n", ans[i]);
}
int main() {
Init();
build();
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址