视频连接:C02【模板】线段树+懒标记 Luogu P3372 线段树 1_哔哩哔哩_bilibili
题目链接:P3372 【模板】线段树 1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
P3374 【模板】树状数组 1 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
算法思路
递归建树
每个节点存储一段区间的左右端点以及区间的某种属性。通过递归建树。
点修改
点修改可以算是区间修改的一个特例。
区间查询
区间查询:主要的思路是拼凑与拆分。
区间修改
在区间修改当中加入了懒标记以增加效率,它的作用是当找到一个小区间被所求区间覆盖的话,就不再往下进行递归查找,只对当前小区间进行标记。等到下次查询或更新的时候再对小区间中的节点进行更新。
所以懒标记会在对小区间的查询或修改的时候率先更新,这体现在更新区间和查询区间的函数中,在不覆盖的情况下,会率先调用push_down函数对子节点进行更新。
算法模板
#include<bits/stdc++.h>
using namespace std;
#define lc p<<1
#define rc p<<1|1
#define N 100010
typedef long long ll;
ll n,w[N];
struct node{
ll l,r,sum,add;
}tr[N*4];
void pushup(int p){
tr[p].sum = tr[lc].sum + tr[rc].sum;
}
void pushdown(int p){
if(tr[p].add){
tr[lc].sum+=tr[p].add*(tr[lc].r-tr[lc].l+1);
tr[rc].sum+=tr[p].add*(tr[rc].r-tr[rc].l+1);
tr[lc].add+=tr[p].add;
tr[rc].add+=tr[p].add;
tr[p].add = 0;
}
}
void build(int p,int l,int r){
tr[p]={l,r,w[l],0};
if(l==r) return;
int m=l+r>>1;
build(lc,l,m);
build(rc,m+1,r);
pushup(p);
}
void update(int p,int x,int y,int k){
if(x<=tr[p].l&&tr[p].r<=y){
tr[p].sum+=(tr[p].r-tr[p].l+1)*k;
tr[p].add+=k;
return;
}
int m = tr[p].l+tr[p].r>>1;
pushdown(p);
if(x<=m) update(lc,x,y,k);
if(y>m) update(rc,x,y,k);
pushup(p);
}
int query(int p,int x,int y){
if(x<=tr[p].l&&tr[p].r<=y){
return tr[p].sum;
}
int m = tr[p].l+tr[p].r>>1;
pushdown(p);
int sum = 0;
if(x<=m) sum+=query(lc,x,y);
if(y>m) sum+=query(rc,x,y);
return sum;
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>w[i];
build(1,1,n);
while(m--){
ll a,b,c,k;
cin>>a>>b>>c;
if(a==1){
cin>>k;
update(1,b,c,k);
}else{
cout<<query(1,b,c)<<endl;
}
}
return 0;
}