入门线段树学习笔记
最基础的线段树,理论挺简单的,这篇倒也不算学习笔记。。。
算个基础的模板。
建树:
void build(int p,int l,int r){
if(l==r){
/*something*/
return;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
updata(p);
}
updata(p)中是要维护的数据
如区间和&最值:
void updata(int p){
sum[p]=sum[p<<1]+sum[p<<1|1];
mx[p]=max(mx[p<<1],mx[p<<1|1]);
mn[p]=min(mn[p<<1],mn[p<<1|1]);
}
将第x个数改成v,也就是单点修改
单点修改:
void change(int p,int l,int r,int x,int val){
if(l==r){
/*something*/
return;
}
int mid=(l+r)>>1;
if(x<=mid)change(p<<1,l,mid,x,val);
else change(p<<1|1,mid+1,r,x,val);
updata(p);
}
查询 $l~r$ 的信息,如区间和
区间查询:
int ask(int p,int l,int r,int x,int y){
if(l>=x&&r<=y)return sum[p];
int mid=(l+r)>>1,val=0;
if(x<=mid)val+=ask(p<<1,l,mid,x,y);
if(y>mid)val+=ask(p<<1|1,mid+1,r,x,y);
return val;
}
修改 l~r的信息,如区间和
如果递归逐一更新修改,时间复杂度为 $O(n)$。
要用一个叫延迟标记的东西
延迟标记:
void pushdown(int p,int l,int r){
if(!tag[p])return;
int mid=(l+r)>>1;
tag[p<<1]+=tag[p];
tag[p<<1|1]+=tag[p];
sum[p<<1]+=tag[p]*(mid-l+1);
sum[p<<1|1]+=tag[p]*(r-mid);
tag[p]=0;
}
区间修改:
void change(int p,int l,int r,int x,int y,int k){
if(l>=x&&r<=y){
tag[p]+=k;
sum[p]+=k*(r-l+1);
return;
}
pushdown(p,l,r);
int mid=(l+r)>>1;
if(x<=mid)change(p<<1|1,l,mid,x,y,k);
if(y>mid)change(p<<1|1,mid+1,r,x,y,k);
updata(p);
}
在查询的时候,向下递归时就下传延迟标记
区间查询:
int ask(int p,int l,int r,int x,int y){
if(l>=x&&r<=y)return sum[p];
pushdown(p,l,r);
int mid=(l+r)>>1,val=0;
if(x<=mid)val+=ask(p<<1,l,mid,x,y);
if(y>mid)val+=ask(p<<1|1,mid+1,r,x,y);
return val;
}
模板题:
https://www.luogu.com.cn/problem/P3372

浙公网安备 33010602011771号