带简单懒标记的线段树
学习过线段树后,才可以理解带懒标记的线段树,下面这题比较基础,可以直接阅读代码的来进行理解,注释写的比较完善
题目:P3372 【模板】线段树 1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node//懒标记结构体
{
ll number;//记录线段树数组中的元素所管理的区间的区间和
ll lazy;//懒标记,记录其所管理的区间的每一个值要加上多少
node() : number(0), lazy(0) {}
} tree[6000000];
ll a[1000000] = {0};//原数组
void build(ll x, ll l, ll r)//建树操作和没有加懒标记的一样。参数意义是:在线段树下标为x的元素,其管理的范围是[l,r]
{
if (l == r)
{
tree[x].number = a[l];
return;
}
ll m = (l + r) / 2;
build(x * 2, l, m);
build(x * 2 + 1, m + 1, r);
tree[x].number = tree[x * 2].number + tree[x * 2 + 1].number;
}
void spread(ll x, ll l, ll r)//这个函数是用来处理懒标记下放的,也就是当这个需要下放的这个节点的懒标记不为空,那么他的每一个子树所管理的范围[l,r](即原数组下标从 l 到 r 的元素)里面的值都要加上懒标记的值lazy
{
if (tree[x].lazy != 0)
{
//修改维护额度左右儿子的值
ll m = (l + r) / 2;
tree[x * 2].number += (tree[x].lazy * (m - l + 1));//对于左子树所管理的区间和更新,因为左子树管理的范围的每一个点都会加上lazy,因此其number值的更新应该是lazy * 范围
tree[x * 2].lazy += tree[x].lazy;//并更新左子树的lazy标记
tree[x * 2 + 1].number += (tree[x].lazy * (r - (m + 1) + 1));//更新右子树
tree[x * 2 + 1].lazy += tree[x].lazy;
//注意下传后这个节点的懒标记应该清零
tree[x].lazy = 0;
}
}
void upd(ll p1, ll p2, ll v, ll x, ll l, ll r)//更新原数组下标为[p1,p2]里的每个元素都加上v
{
if (l >= p1 && r <= p2) //当管理的区间全部都要修改的时候才加上懒标记并更新节点的值
{
tree[x].number += (v * (r - l + 1));
tree[x].lazy += v;
return;
}
spread(x, l, r);//当这个节点所管理的区间不是全部都要修改的时候,就不能在这个节点的懒标记加上新的值了,因为这样的话原数组那些不需要被修改的元素也会被误加。因此要下放懒标记,直到某个节点的所管理的区间全部需要修改为止
//若下标为x的线段树节点管理的范围里并不是每一个都需要修改,那么这个时候就需要下放懒标记
ll m = (l + r) / 2;
if (p1 <= m)
upd(p1, p2, v, x * 2, l, m);
if (p2 > m)//注意这里不是else if,因为如果p1、p2横跨l、r,那么右子树也是需要改懒标记的
upd(p1, p2, v, x * 2 + 1, m + 1, r);
tree[x].number = tree[x * 2].number + tree[x * 2 + 1].number;
}
ll ask(ll x, ll l, ll r, ll p1, ll p2)
{
if (l >= p1 && r <= p2)
return tree[x].number;//懒标记只是要加在子树上的值,而子树总体会加上多少已经算在number里了
spread(x, l, r);
ll m = (l + r) / 2;
if (p2 <= m)
return ask(x * 2, l, m, p1, p2);
else if (p1 > m)
return ask(x * 2 + 1, m + 1, r, p1, p2);
return ask(x * 2, l, m, p1, m) + ask(x * 2 + 1, m + 1, r, m + 1, p2);
}
int main()
{
ll n, m;
cin >> n >> m;
for (ll i = 1; i <= n; i++)
cin >> a[i];
build(1, 1, n);
for (ll i = 0; i < m; i++)
{
int choice;
cin >> choice;
if (choice == 1)
{
ll x, y, k;
cin >> x >> y >> k;
upd(x, y, k, 1, 1, n);
}
else
{
ll x, y;
cin >> x >> y;
cout << ask(1, 1, n, x, y) << endl;
}
}
return 0;
}