这一题是线段树懒标记的入门,懒标记的思想就是,当查询或者更改时,需要用到某段区间,那么我们就去将需要用到的区间传递下去,否者我们不动他,像不像你不用的知识不复习,用的时候再复习呢?
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int w[N];
struct Node{
ll l, r, sum, lazy; //懒标记
}tr[N << 2];
inline ll ls(ll u) {return u << 1; }
inline ll rs(ll u) {return u << 1 | 1; }
void pushup(ll u, ll l, ll r){ //更新父节点
tr[u] = {l, r, tr[ls(u)].sum + tr[rs(u)].sum, 0};
}
void pushdown(ll u) //传递懒标记
{
if(tr[u].lazy) //如果懒标记不是0
{
ll mid = tr[u].l + tr[u].r >> 1;
tr[ls(u)].lazy += tr[u].lazy; //给左子树打上懒标记
tr[ls(u)].sum += tr[u].lazy * (mid - tr[u].l + 1); //更新左子树的和
tr[rs(u)].lazy += tr[u].lazy; //给右子树打上懒标记
tr[rs(u)].sum += tr[u].lazy * (tr[u].r - mid); //更新左子树的和
tr[u].lazy = 0;
}
}
void build(ll u, ll l, ll r)
{
if(l == r){
tr[u] = {l, r, w[l], 0};
return;
}
ll mid = l + r >> 1;
build(ls(u), l, mid);
build(rs(u), mid + 1, r);
pushup(u, l, r);
}
void modify(ll u, ll l, ll r, ll lazy) //修改
{
if(l <= tr[u].l && tr[u].r <= r)
{
tr[u].sum += (tr[u].r - tr[u].l + 1) * lazy;
tr[u].lazy += lazy;
return;
}
pushdown(u); //懒标记下放
ll mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(ls(u), l, r, lazy);
if(r > mid) modify(rs(u), l, r, lazy);
tr[u].sum = tr[ls(u)].sum + tr[rs(u)].sum; //更新区间和
}
ll query(ll u, ll l, ll r){
if(l <= tr[u].l && tr[u].r <= r){
return tr[u].sum;
}
pushdown(u); //向下传递懒标记
ll mid = tr[u].l + tr[u].r >> 1, sum = 0;
if(l <= mid) sum += query(ls(u), l, r);
if(r > mid) sum += query(rs(u), l, r);
return sum;
}
int main()
{
// freopen("in.txt", "r", stdin);
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);
build(1, 1, n);
while(m--)
{
int op;
scanf("%d", &op);
if(op == 1)
{
int x, y, k;
scanf("%d%d%d", &x, &y, &k);
modify(1, x, y, k);
}
else
{
int x, y;
scanf("%d%d", &x, &y);
printf("%lld\n", query(1, x, y));
}
}
return 0;
}