区间最大——用线段树维护区间前后缀的问题
与之前的那道 你能回答这些问题吗 一样,可以作为线段树维护区间的前后缀来得到最值的问题的模板题,需要知道怎样 pushup, 来得到前后缀最值以及最值
在这道题中,前后缀的最值的连续与相邻区间是否可以连接相关,如果相邻区间的相邻端点是不同的,则遵循前后缀连接到规则,即
if (a[mid] != a[mid + 1]) {
tr[p].ans = max({tr[ls].ans, tr[rs].ans, tr[ls].R + tr[rs].L});
if (tr[ls].L == tr[ls].len) tr[p].L = tr[ls].len + tr[rs].L;
else tr[p].L = tr[ls].L;
if (tr[rs].R == tr[rs].len) tr[p].R = tr[rs].len + tr[rs].R;
else tr[p].R = tr[rs].R;
}
否则,两个区间不可连接,则
if (a[mid] == a[mid + 1]) {
tr[p].ans = max(tr[ls].ans, tr[rs].ans);
tr[p].L = tr[ls].L;
tr[p].R = tr[rs].R;
}
这也符合线段树的维护方式,每次更新儿子节点后,都重新需要计算父节点,前后缀的存在就是为了维护需要连接的属性,例如:动态的最长问题
而上方的两种可连接与不可连接的 pushup 方式,可以得知这种前后缀的线段树,答案与前后缀是如何联系在一起的
下方 AC 代码,变量名与讲解不同,因为懒得重写一遍了
#include <bits/stdc++.h>
#define ls (p << 1)
#define rs (p << 1 | 1)
using namespace std;
const int N = 2E5 + 10;
struct Node {
int l, r, len, L, R, s;
}tr[N << 2];
int a[N], n;
void pushup(int p) {
int mid = tr[p].l + tr[p].r >> 1;
tr[p].len = max(tr[ls].len, tr[rs].len);
tr[p].L = tr[ls].L, tr[p].R = tr[rs].R;
if (a[mid] != a[mid + 1]) {
tr[p].len = max(tr[p].len, tr[ls].R + tr[rs].L);
if (tr[ls].len == tr[ls].s) tr[p].L = tr[ls].s + tr[rs].L;
if (tr[rs].len == tr[rs].s) tr[p].R = tr[rs].s + tr[ls].R;
}
}
void build(int p, int l, int r) {
tr[p] = {l, r, 1, 1, 1, r - l + 1};
if (l == r) return;
int mid = l + r >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
}
void update(int p, int x) {
if (tr[p].l == x && tr[p].r == x) {
a[x] ^= 1;
return;
}
int mid = tr[p].l + tr[p].r >> 1;
if (x <= mid) update(ls, x);
else update(rs, x);
pushup(p);
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int q;
cin >> n >> q;
build(1, 1, n);
while (q --) {
int x;
cin >> x;
update(1, x);
cout << tr[1].len << endl;
}
return 0;
}