跳转至

洛谷-P3372 【模板】线段树1(区间修改+区间查询)

题目描述

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

输入格式

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

输出格式

输出包含若干行整数,即为所有操作2的结果。

输入输出样例

输入

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

输出

11
8
20

说明/提示

时空限制:1000ms,128M

数据规模:

对于30%的数据:N\leq 8, M \leq 10

对于70%的数据:N\leq 1000, M\leq 10000

对于100%的数据:N \leq 100000, M \leq 100000


#include <bits/stdc++.h>

using namespace std;


struct Node {
    int left, right;
    long long  sum;
    long long lazy; //lazy: 延迟标记

    //Node():left(0), right(0), sum(0), lazy(0) {}
};

int n = 100005; //叶节点的个数
vector<Node> tree(n * 4);
vector<int> seq(n);

#define left(x) tree[x].left
#define right(x) tree[x].right
#define sum(x) tree[x].sum
#define lazy(x) tree[x].lazy 

inline int leftChild(int x) { return x << 1; } //根节点的左子节点的下标
inline int rightChild(int x) { return x << 1 | 1; } //根节点的右子节点的下标
inline int length(int x) { return right(x) - left(x) + 1; } //根节点覆盖的区间长度

inline void update(int root)
{
    sum(root) = sum(leftChild(root)) + sum(rightChild(root));
}

void build(int root, int l, int r)
{
    left(root) = l;
    right(root) = r;

    //递归到了叶节点可以停止了
    if (l == r) { sum(root) = seq[l]; return; }

    //递归构建左右子树
    int mid = l + ((r - l) >> 1);
    build(leftChild(root), l, mid);
    build(rightChild(root), mid + 1, r);

    update(root); //构建完成要更新当前节点的sum值
}

void pushDown(int root) 
{
    if (lazy(root)) {
        sum(leftChild(root)) += lazy(root) * length(leftChild(root));
        sum(rightChild(root)) += lazy(root) * length(rightChild(root));

        lazy(leftChild(root)) += lazy(root); //左子节点延迟标记
        lazy(rightChild(root)) += lazy(root); //右子节点延迟标记
        lazy(root) = 0; //清除根节点标记
    }
}

//单点修改
void change(int root, int pos, int value)
{
    //如果当前节点就是叶节点,直接修改,停止下滤
    if (left(root) == right(root)) { sum(root) = value; return; }

    int mid = left(root) + ((right(root) - left(root)) >> 1);

    if (pos <= mid) change(leftChild(root), pos, value);
    else change(rightChild(root), pos, value);

    update(root);
}

//区间修改
void intervalChange(int root, int l, int r, long long value)
{
    //恰好找到了一个完整的区间
    if (left(root) == l && right(root) == r) {
        sum(root) += length(root) * value;
        lazy(root) += value;
        return;
    }

    pushDown(root); //延迟标记下传

    int mid = left(root) + ((right(root) - left(root)) >> 1);

    if (r <= mid) intervalChange(leftChild(root), l, r, value);
    else if (l > mid) intervalChange(rightChild(root), l, r, value);
    else {
        intervalChange(leftChild(root), l, mid, value);
        intervalChange(rightChild(root), mid + 1, r, value);
    }

    update(root);
}

long long query(int root, int l, int r)
{
    if (left(root) == l && right(root) == r) return sum(root);

    pushDown(root); //延迟标记下传

    int mid = left(root) + ((right(root) - left(root)) >> 1);
    if (r <= mid) return query(leftChild(root), l, r);
    else if (l > mid) return query(rightChild(root), l, r);

    return query(leftChild(root), l, mid) + query(rightChild(root), mid + 1, r);
}


int main()
{
    std::ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int q;
    cin >> n >> q;

    for (int i = 1; i <= n; ++i) cin >> seq[i];

    build(1, 1, n);

    while (q--) {
        int ops; cin >> ops;
        if (ops & 1) { //ops == 1
            int l, r;
            long long value;
            cin >> l >> r >> value;
            intervalChange(1, l, r, value);
        }
        else { //ops == 2
            int l, r;
            cin >> l >> r;
            cout << query(1, l, r) << endl;
        }
    }

    return 0;
}