线段树
P3372 【模板】线段树 1https://www.luogu.com.cn/problem/P3372
关于lz标记和push_down
线段树进行区间修改后会出现未被修改的叶子节点,再次进行查询或修改操作可能会出错,但如果每次修改都修改到叶子节点的话线段树会退化到O(n),所以我们对根节点加上lz标志,来记录左右儿子应该进行的修改操作。当再次进行修改或查询时,进行push_down,将左右儿子节点的数值更新,就可以避免出错,且复杂度仍为long(n)
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
#define TLE std::ios::sync_with_stdio(false)
#define ll long long
int n, m;
int a[N];
struct node
{
int l, r;
int lz;//lazy标志,所加数的大小被存入lz中;
ll sum;
}tr[4*N];//线段树一般开4倍的空间
void build(int i, int l, int r)
{
tr[i].l = l; tr[i].r = r; tr[i].lz = 0;
if (l == r)
{
tr[i].sum = a[l];
return;
}
int mid = (r + l) / 2;
build(i * 2, l, mid);
build(i * 2 + 1, mid + 1, r);
tr[i].sum = tr[i * 2].sum + tr[i * 2 + 1].sum;
}
void push_down(int i)
{
if (tr[i].lz!=0)//此处不能写if(!tr[i].lz)修改值可能为负值
{
tr[i * 2].lz += tr[i].lz;
tr[i * 2 + 1]. lz += tr[i].lz;
int mid = (tr[i].l + tr[i].r) / 2;
tr[i * 2].sum += tr[i].lz * (mid - tr[i*2].l+1);
tr[i * 2 + 1].sum += tr[i].lz * (tr[i*2+1].r - mid);
tr[i].lz = 0;
}
return;
}
void add(int i, int l, int r, int k)
{
if (tr[i].l >= l && tr[i].r <= r)
{
tr[i].sum += k*(tr[i].r-tr[i].l+1);
tr[i].lz += k;
return;
}
push_down(i);
if (tr[i * 2].r >= l)
add(i * 2, l, r, k);
if (tr[i * 2 + 1].l <= r)
add(i * 2 + 1, l, r, k);
tr[i].sum = tr[i * 2].sum + tr[i * 2 + 1].sum;
return;
}
long long search(int i, int l, int r)
{
if (tr[i].l >= l && tr[i].r <= r)
return tr[i].sum;
push_down(i);
long long s = 0;
if (tr[i * 2].r >= l)
s += search(i * 2, l, r);
if (tr[i * 2 + 1].l <= r)
s += search(i * 2 + 1, l, r);
return s;
}
int main()
{
TLE;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
build(1,1,n);
int t;
while(m--)
{
cin >>t ;
if (t == 1)
{
int x, y, k;
cin >> x >> y >> k;
add(1, x, y, k);
}
else if (t == 2)
{
int x, y;
cin >> x>>y;
cout << search(1, x, y) <<endl;
}
}
return 0;
}