大致讲解
如果采用单点修改的方式,对需要修改区间的每一个叶子节点都进行修改,并且pushup()
,时间复杂度会达到
O
(
n
)
O(n)
O(n)
sum
:如果考虑当前节点,及其子节点上的所有标记,当前的区间和是多少,并且没有考虑所有祖先节点上的标记
add
:给当前节点的所有子区间加上add
- 在这种求区间和问题中,凡是涉及到
modify
和build
的操作,都需要在最后pushup
一遍,因为只要子节点发生了变化,其祖先就会受其影响;在一开始做的时候,我没理解为什么在进行build
操作时,也需要pushup
,其实想一想,我们build
操作是一个递归的过程,原始数据数组w[i]
的值只赋值给了叶子节点,除叶子节点外的值都是都pushup
操作完成的,当我们对一个叶子节点赋值完时,它的祖先节点的值大多都还是0,所以需要pushup
操作
所以只需要对某个区间添加一个add
成员(也就是懒标记),代表此节点所有的子节点都会进行修改*(此节点可修改,也可不修改,只要前后对应即可)*,具体操作为:
// 对于一个节点root,和它的左孩子left、右孩子right
left.add += root.add;
left.sum += left.sum + (left.r - left.l + 1) * root.add;
right.add += root.add;
right.sum += right.sum + (right.r - right.l + 1) * root.add;
root.add=0; //根的懒标记清空
代码
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 5;
int n, q, w[N];
struct Node {
int l, r;
ll sum, add;
} tr[N * 4];
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u) {
Node &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];//方便书写,涉及修改,使用引用
if (root.add) {
left.add += root.add;//懒标记下方左子树
left.sum += (ll)(left.r - left.l + 1) * root.add;
right.add += root.add;// 懒标记下放右子树
right.sum += (ll)(right.r - right.l + 1) * root.add;
root.add = 0;//清空懒标记
}
}
void build(int u, int l, int r) {
if (l == r)
tr[u] = { l, r, w[r], 0 };
else {
tr[u] = { l, r };
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);// 赋值只对叶子节点赋值,其他的节点仍然为0,所以要进行pushup
}
}
ll query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r)
return tr[u].sum;// 当前树的区间被查询区间完全包含,就直接返回树区间的值
ll sum = 0;
pushdown(u);
// 不满足tr[u].l >= l && tr[u].r <= r,说明查询区间范围是大于当前树区间的
// 那么我们需要把祖先节点的add标记往下传
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid)
sum += query(u << 1, l, r);
if (r > mid)
sum += query(u << 1 | 1, l, r);
return sum;
}
void modify(int u, int l, int r, int d) {
if (tr[u].l >= l && tr[u].r <= r) {
tr[u].sum += (ll)(tr[u].r - tr[u].l + 1) * d;
tr[u].add += d;
} else {
// nmd脑子越想越乱,明天再来写 -- 2021.9.12 22:13 CST
// 修改区间大于当前树区间,就需要下传懒标记
pushdown(u);
int mid = tr[u].r + tr[u].l >> 1;
if (l <= mid)
modify(u << 1, l, r, d);
if (mid < r)
modify(u << 1 | 1, l, r, d);
pushup(u);
}
}
int main(void) {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++)
scanf("%d", &w[i]);
build(1, 1, n);
int op, l, r, x;
while (q--) {
scanf("%d%d%d", &op, &l, &r);
if (op == 1) {
scanf("%d", &x);
modify(1, l, r, x);
} else
printf("%lld\n", query(1, l, r));
}
return 0;
}