区间最大——用线段树维护区间前后缀的问题

与之前的那道 你能回答这些问题吗 一样,可以作为线段树维护区间的前后缀来得到最值的问题的模板题,需要知道怎样 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;
}
posted @ 2025-03-05 13:13  he_jie  阅读(16)  评论(0)    收藏  举报