线段树维护单调栈

例题

淘淘摘苹果

讲解

  • 由于单调栈在维护时,左儿子会对右儿子有影响,所以有一个 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
", calc(1, -1));
        h[pos] = tmp;
        modify(1, pos);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/hellohhy/p/13802812.html