1. 程式人生 > 實用技巧 >線段樹維護單調棧

線段樹維護單調棧

例題

淘淘摘蘋果

講解

  • 由於單調棧在維護時,左兒子會對右兒子有影響,所以有一個 calc 函式,我的 calc 函式傳入兩個引數,rt 和 val,有一下三種情況
    • 如果左兒子的最大值大於 val,那麼直接遞迴左兒子,然後加上右兒子的貢獻,因為右兒子的最小值一定大於左兒子的最大值,這是在建樹 pushup 時保證的。
    • 否則直接遞迴右兒子,因為左兒子在這時候一定是不滿足的,就直接遞迴右兒子就行。(因為要找的是以val開始的上升序列長度)
    • 到葉子節點判斷一下和 val 的大小就行。
  • pushup 的時候用左兒子和諧一下右兒子,保證單調性。

code

#include <bits/stdc++.h>
using namespace std;
inline int read() {
    int k = 0, f = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    for (; isdigit(ch); ch = getchar()) k = k * 10 + ch - '0';
    return k * f;
}
const int maxn = 4e5 + 100; 
int h[maxn];
int n, m;
struct node { int l, r, maxx, len; } t[maxn];
#define tl t[rt].l
#define tr t[rt].r
#define ls (rt << 1)
#define rs (rt << 1 | 1)
int calc(int rt, int val) {
    if (tl == tr) return t[rt].maxx > val;
    if (t[ls].maxx > val) return calc(ls, val) + t[rs].len;
    else return calc(rs, val);
}
void pushup(int rt) {
    t[rt].maxx = max(t[ls].maxx, t[rs].maxx);
    t[rs].len = calc(rs, t[ls].maxx);
}
void build(int rt, int l, int r) {
    tl = l, tr = r;
    if (l == r) return t[rt].maxx = h[l], void();
    int mid = (l + r) >> 1;
    build(ls, l, mid), build(rs, mid + 1, r);
    pushup(rt);
}
void modify(int rt, int pos) {
    if (tl == tr) return t[rt].maxx = h[pos], void();
    int mid = (tl + tr) >> 1;
    if (pos <= mid) modify(ls, pos);
    else modify(rs, pos);
    pushup(rt);
}
int main() {
#ifdef debug
#else
    freopen("taopapp.in", "r", stdin);
    freopen("taopapp.out", "w", stdout);
#endif
    n = read(), m = read();
    for (int i = 1; i <= n; i++) h[i] = read();
    build(1, 1, n);
    while (m--) {
        int pos = read(), val = read(), tmp = h[pos];
        h[pos] = val;
        modify(1, pos);
        printf("%d\n", calc(1, -1));
        h[pos] = tmp;
        modify(1, pos);
    }
    return 0;
}