📜  | |问题 9(1)

📅  最后修改于: 2023-12-03 15:35:50.728000             🧑  作者: Mango

'| |问题 9'介绍

问题描述

题目名称:'| |问题 9'

题目链接:https://www.luogu.com.cn/problem/P4230

题目描述:给定一个长度为 $n$ 的序列 $A$,和一个长度为 $m$ 的操作序列 $B$,操作序列的每个元素为 $1$ 或 $2$,当操作序列中的元素为 $1$ 时,表示将序列 $A$ 中第 $i$ 到第 $j$ 的数全部加 $w$。当操作序列中的元素为 $2$ 时,表示询问序列 $A$ 中第 $i$ 到第 $j$ 的数中有多少个数在区间 $[l,r]$ 中。

解题思路

本题可以通过线段树来实现。

首先,需要将每次加 $w$ 操作拆分成两个单独的操作,即将序列 $A$ 中第 $i$ 到第 $j$ 个数分别加 $w$。

然后,对于询问操作,可以使用线段树维护区间和。对于每个节点,记录该节点表示的区间的区间和。当查询区间 $[l,r]$ 时,查询区间与当前节点代表区间的交集 $[max(l,l_i),min(r,r_i)]$,其中 $l_i$ 和 $r_i$ 分别是当前节点代表的区间的左右端点。如果当前节点表示的区间与查询区间没有交集,则返回 $0$;否则,如果当前节点表示的区间完全包含查询区间,则返回该节点的区间和;否则,将查询区间拆分成两部分分别递归查询。

代码实现
class SegmentTree {
public:
    struct Node {
        int l, r;
        long long sum, delta; //记录当前节点代表区间的区间和以及懒惰标记
    } tree[N << 2];

    void pushup(int x) {
        tree[x].sum = tree[x << 1].sum + tree[x << 1 | 1].sum;
    }

    void pushdown(int x) {
        if (tree[x].delta) {
            tree[x << 1].delta += tree[x].delta;
            tree[x << 1 | 1].delta += tree[x].delta;
            tree[x << 1].sum += (long long)(tree[x << 1].r - tree[x << 1].l + 1) * tree[x].delta;
            tree[x << 1 | 1].sum += (long long)(tree[x << 1 | 1].r - tree[x << 1 | 1].l + 1) * tree[x].delta;
            tree[x].delta = 0;
        }
    }

    void build(int x, int l, int r) {
        tree[x].l = l, tree[x].r = r, tree[x].delta = 0;
        if (l == r) {
            cin >> tree[x].sum;
        }
        else {
            int mid = (l + r) >> 1;
            build(x << 1, l, mid);
            build(x << 1 | 1, mid + 1, r);
            pushup(x);
        }
    }

    void update(int x, int l, int r, int val) {
        if (tree[x].l >= l && tree[x].r <= r) {
            tree[x].delta += val;
            tree[x].sum += (long long)(tree[x].r - tree[x].l + 1) * val;
        }
        else {
            pushdown(x);
            int mid = (tree[x].l + tree[x].r) >> 1;
            if (l <= mid) {
                update(x << 1, l, r, val);
            }
            if (r > mid) {
                update(x << 1 | 1, l, r, val);
            }
            pushup(x);
        }
    }

    long long query(int x, int l, int r) {
        if (tree[x].l >= l && tree[x].r <= r) {
            return tree[x].sum;
        }
        else {
            pushdown(x);
            int mid = (tree[x].l + tree[x].r) >> 1;
            long long res = 0;
            if (l <= mid) {
                res += query(x << 1, l, r);
            }
            if (r > mid) {
                res += query(x << 1 | 1, l, r);
            }
            return res;
        }
    }
};

int main() {
    SegmentTree st;
    int n, m;
    cin >> n >> m;
    st.build(1, 1, n);
    for (int i = 1; i <= m; i++) {
        int op;
        cin >> op;
        if (op == 1) {
            int l, r, w;
            cin >> l >> r >> w;
            st.update(1, l, r, w);
        }
        else {
            int l, r, L, R;
            cin >> l >> r >> L >> R;
            cout << st.query(1, l, r, L, R) << endl;
        }
    }
    return 0;
}
总结

本题是线段树模板题,使用线段树维护区间和即可。

需要注意的是每次加 $w$ 操作需要拆分成两个单独的操作,同时每个节点还需要维护懒惰标记。

同时,递归查询时需要将查询区间拆分成两部分,分别递归查询。

总的来说,本题难度不算太高,但需要认真分析并实现。