一、线段树简介
线段树是一种适用于维护区间信息的数据结构,可以在 O( l o g N logN logN) 的时间复杂度内实现单点修改、区间修改、区间查询等操作。(利用二叉堆思想)
二、代码实现(以实现区间和为例)
1、建树
递归分割,末端赋值,回溯合并
数组空间
2
⌈
l
o
g
2
n
⌉
+
1
+
1
{2}^{\lceil{log_2^n}\rceil+1}+1
2⌈log2n⌉+1+1
一般来说数组开4N
// s,t指当前区间 p指当前序号
void build(int s,int t,int p){
if(s==t){
d[p]=a[s];
return;
}
else{
int m=s+((t-s)>>1);
//与 m=(t+s)>>1 几乎等价,但上式可以防止计算过程中产生溢出
build(s,m,p*2);
build(m+1,t,p*2+1);
d[p]=d[p*2]+d[p*2+1]; //更新
}
}
2、LAZY TAG 懒标记
Tip: 极大区间实质上是被包含于查询区间 [L,R] 的最简区间 [S,T],此 [S,T] 区间之上的区间就超出了区间 [L,R] 的范围,其下的小区间又显然没有上面的大区间优。
在更新区间(如区间整体加减)时,我们会找到几个极大区间(区间数在 O(
l
o
g
N
logN
logN) 以内),因为不想将极大区间麾下的所有小区间全部更新(影响时间复杂度),所以用一种懒标记,这种标记存放于已经修改过值的节点,当有需求向下分割时再进行懒标记的下放(叶节点无需再次下放)
3、区间修改
// l,r指修改区间
void update(int l,int r,int c,int s,int t,int p){
if(l<=s&&t<=r){
//极大区间
d[p]+=(t-s+1)*c;
b[p]+=c;
return;
}
else{
int m=s+((t-s)>>1);
if(b[p]&&s!=t){
//非叶节点下放懒标记
d[p*2]+=b[p]*(m-s+1);
d[p*2+1]+=b[p]*(t-m);
b[p*2]+=b[p];
b[p*2+1]+=b[p];
b[p]=0;
}
if(l<=m) update(l,r,c,s,m,p*2);
if(m+1<=r) update(l,r,c,m+1,t,p*2+1);
d[p]=d[p*2]+d[p*2+1]; //更新
}
}
4、区间查询
typedef long long ll;
ll getsum(int l,int r,int s,int t,int p){
if(l<=s&&t<=r) return d[p];
int m=s+((t-s)>>1);
//有需求向下分割,下放懒标记
if(b[p]){
d[p*2]+=b[p]*(m-s+1);
d[p*2+1]+=b[p]*(t-m);
b[p*2]+=b[p];
b[p*2+1]+=b[p];
b[p]=0;
}
ll sum=0;
if(l<=m) sum+=getsum(l,r,s,m,p*2);
if(m+1<=r) sum+=getsum(l,r,m+1,t,p*2+1);
return sum;
}
5、一棵刚出炉的线段树
代码相关题目:P3372 【模板】线段树 1
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
int n,q;
ll a[N],d[4*N],b[4*N];
void build(int s,int t,int p){
if(s==t){
d[p]=a[s];
return;
}
else{
int m=s+((t-s)>>1);
build(s,m,p*2);
build(m+1,t,p*2+1);
d[p]=d[p*2]+d[p*2+1];
}
}
void update(int l,int r,int c,int s,int t,int p){
if(l<=s&&t<=r){
d[p]+=(t-s+1)*c;
b[p]+=c;
return;
}
else{
int m=s+((t-s)>>1);
if(b[p]&&s!=t){
d[p*2]+=b[p]*(m-s+1);
d[p*2+1]+=b[p]*(t-m);
b[p*2]+=b[p];
b[p*2+1]+=b[p];
b[p]=0;
}
if(l<=m) update(l,r,c,s,m,p*2);
if(m+1<=r) update(l,r,c,m+1,t,p*2+1);
d[p]=d[p*2]+d[p*2+1];
}
}
ll getsum(int l,int r,int s,int t,int p){
if(l<=s&&t<=r){
return d[p];
}
int m=s+((t-s)>>1);
if(b[p]){
d[p*2]+=b[p]*(m-s+1);
d[p*2+1]+=b[p]*(t-m);
b[p*2]+=b[p];
b[p*2+1]+=b[p];
b[p]=0;
}
ll sum=0;
if(l<=m) sum+=getsum(l,r,s,m,p*2);
if(m+1<=r) sum+=getsum(l,r,m+1,t,p*2+1);
return sum;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,n,1);
for(int i=1;i<=q;i++){
int type;
scanf("%d",&type);
if(type==1){
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
update(x,y,k,1,n,1);
}
else{
int x,y;
scanf("%d%d",&x,&y);
cout<<getsum(x,y,1,n,1)<<endl;
}
}
return 0;
}