「JOI Open 2019」三级跳(找性质缩状态+线段树)

https://loj.ac/problem/3153

题解:

若有\(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();
}

posted @ 2020-02-29 22:30  Cold_Chair  阅读(430)  评论(0编辑  收藏  举报