CF1567E Non-Decreasing Dilemma

CF1567E Non-Decreasing Dilemma

一道不难的数据结构题, 无论是思维难度还是代码难度都不大, 但是我写了半天...

原因很简单, 我改线段树写法了, 从来没这么写过, 然后就 \(gg\) 了, 没想到啊~

好了, 言归正传.

我们需要求出区间内的不下降子序列数量, 显然的是, 一个长为 \(n\) 的序列的贡献是 \(n * (n + 1) / 2\) . 因为对于一个符合要求的点对 \((p, q)\) , \(p\)\(q\) 分别有 \(n\) 种选择, 但是 \((p, q)\) 合法, 当且仅当 \(p == q\) 时, \((q, p)\) 才会合法, 所以方案数为 \(n * (n + 1) / 2\) .

这样我们就用线段树来维护这么几个值:

  1. 最长不下降前缀
  2. 最长不下降后缀
  3. 除了最长不下降前/后缀之外的贡献
  4. 该段是否单调不降

然后就很简单了.

单点修改很简单, 查询的时候只需要维护当前的单调不降的序列长度以及末尾元素, 每当末尾元素大于当前段的开头元素时, 我们就将当前储存的序列长度的贡献加入答案, 然后更新当前的长度以及末尾值.

\(code:\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
const int N = 2e5 + 5;
int n, a[N];
struct Node {
    Node *ls, *rs;
    ll llen, rlen, val;
    int l, r, lv, rv;
    bool flag;
    int Mid() {
        return (l + r) >> 1;
    }
    bool In(int L, int R) {
        if (l >= L && r <= R) return 1;
        return 0;
    }
    void Maintain() {
        lv = ls->lv, rv = rs->rv;
        flag = 0; val = ls->val + rs->val;
        if (ls->rv <= rs->lv) {
            if (ls->flag && rs->flag) llen = rlen = ls->rlen + rs->llen, flag = 1;
            else if (ls->flag) llen = ls->rlen + rs->llen, rlen = rs->rlen;
            else if (rs->flag) rlen = ls->rlen + rs->llen, llen = ls->llen;
            else llen = ls->llen, rlen = rs->rlen, val += ((ls->rlen + rs->llen) * (ls->rlen + rs->llen + 1) >> 1);
        }
        else {
            llen = ls->llen, rlen = rs->rlen;
            if (ls->flag && !rs->flag) val += rs->llen * (rs->llen + 1) >> 1;
            else if (rs->flag && !ls->flag) val += ls->rlen * (ls->rlen + 1) >> 1;
            else if (!ls->flag && !rs->flag) val += (ls->rlen * (ls->rlen + 1) >> 1) + (rs->llen * (rs->llen + 1) >> 1);
        }
    }
} *Root = new Node;
void Build(Node* &o, int l, int r) {
    o->l = l, o->r = r;
    if (l == r) {
        o->lv = o->rv = a[l];
        o->llen = o->rlen = 1;
        o->val = 0, o->flag = 1;
        return;
    }
    int mid = o->Mid();
    o->ls = new Node, o->rs = new Node;
    Build(o->ls, l, mid);
    Build(o->rs, mid + 1, r);
    o->Maintain();
}
void Change(Node* &o, int x, int k) {
    if (o->l == o->r) {
        o->lv = o->rv = k;
        return;
    }
    int mid = o->Mid();
    if (x <= mid) Change(o->ls, x, k);
    else if (x > mid) Change(o->rs, x, k);
    o->Maintain();
}
ll ans, len;
int lst;
void Query(Node* o, int l, int r) {
    if (o->In(l, r)) {
        ans += o->val;
        if (o->lv >= lst) {
            if (o->flag) len += o->llen;
            else {
                len += o->llen;
                ans += len * (len + 1) >> 1;
                len = o->rlen;
            }
        }
        else {
            ans += len * (len + 1) >> 1;
            if (o->flag) len = o->llen;
            else {
                len = o->llen;
                ans += len * (len + 1) >> 1;
                len = o->rlen;
            }
        }
        lst = o->rv;
        return;
    }
    int mid = o->Mid();
    if (l <= mid) Query(o->ls, l, r);
    if (r > mid) Query(o->rs, l, r);
}
int main() {
    n = read();
    int q = read();
    for (int i = 1; i <= n; i++) a[i] = read();
    Build(Root, 1, n);
    while (q--) {
        int opt = read(), x = read(), y = read();
        switch (opt) {
            case 1:
                Change(Root, x, y);
                break;
            case 2:
                ans = len = lst = 0;
                Query(Root, x, y);
                ans += len * (len + 1) >> 1;
                printf("%lld\n", ans);
                break;
            default:
                break;
        }
    }
    return 0;
}
posted @ 2021-09-08 19:36  sshadows  阅读(73)  评论(0编辑  收藏  举报