線段樹維護單調棧
阿新 • • 發佈:2020-10-12
例題
淘淘摘蘋果
講解
- 由於單調棧在維護時,左兒子會對右兒子有影響,所以有一個 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; }