线段树学习笔记
线段树注意事项
- 线段树是一种常数非常大的数据结构,谨慎使用。
- 线段树在开 空间 时 不能吝啬 。
基础线段树(一维)
递归思想:模版
struct Segment_Tree{
#define ls (p<<1)
#define rs (p<<1|1)
#define mid (tr[p].l+tr[p].r>>1)
int n;
struct node{
int l,r,len,c,mark;
}tr[N<<2];
/*初始化*/
void init(int _n){
return (void)(n=_n,build(1,n,1));
}
/*建树*/
void build(int l,int r,int p){
tr[p]={l,r,r-l+1,0,0};
if(l==r)return (void)(tr[p].c=Local[l]);
build(l,mid,ls);
build(mid+1,r,rs);
push_up(p);
}
/*传递*/
void push_up(int p){
tr[p].c=tr[ls].c+tr[rs].c;
}
void push_down(int p){
if(tr[p].len<=1)return;
tr[ls].mark+=tr[p].mark;
tr[rs].mark+=tr[p].mark;
tr[ls].c+=tr[p].mark*tr[ls].len;
tr[rs].c+=tr[p].mark*tr[rs].len;
tr[p].mark=0;
}
/*修改*/
void update(int p,int l,int r,int d){
if(l<=tr[p].l&&tr[p].r<=r)return tr[p].c+=tr[p].len*d,tr[p].mark+=d,void();
push_down(p);
if(mid>=l)update(ls,l,r,d);
if(mid<r)update(rs,l,r,d);
push_up(p);
}
/*查询*/
int query(int p,int l,int r){
if(l<=tr[p].l&&tr[p].r<=r)return tr[p].c;
push_down(p);
int ans=0;
if(mid>=l)ans+=query(ls,l,r);
if(mid<r)ans+=query(rs,l,r);
return ans;
}
#undef ls
#undef rs
#undef mid
}seg;
递推思想:模版
struct Recursion_Segment_Tree{
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
int n,m,c[N<<2],mark[N<<2];//精髓:延迟标记
void init(int _n){
return (void)(n=_n,build(n));
}//初始化
void build(int n){
m=1;while(m<n+2)m<<=1;
FOR(i,1,n)c[m+i]=a[i];
DOR(i,m-1,1)c[i]=c[ls(i)]+c[rs(i)],mark[i]=0;
}//建树
void update(int l,int r,int d){
int s,t,lc=0,rc=0,x=1;
for(int s=m=l-1,t=m+r+1;s^t^1;s>>=1,t>>=1,x<<=1){
c[s]+=d*lc;
c[t]+=d*rc;
if(~s&1)c[s^1]+=d*x,lc+=x,mark[s^1]+=d;
if(t&1)c[t^1]+=d*x,rc+=x,mark[t^1]+=d;
}
for(;s;s>>=1,t>>=1){
c[s]+=d*lc;
c[t]+=d*rc;
}return;
}//区间修改
int query(int l,int r){
int lc=0,rc=0,x=1;
int ans=0;
for(int s=m=l-1,t=m+r+1;s^t^1;s>>=1,t>>=1,x<<=1){
if(mark[s])ans+=mark[s]*lc;
if(mark[t])ans+=mark[t]*rc;
if(~s&1)ans+=c[s^1],lc+=x;
if(t&1)ans+=c[t^1],rc+=x;
}
for(;s;s>>=1,t>>=1){
ans+=mark[s]*lc;
ans+=mark[t]*rc;
}return ans;
}//区间查询
};
标记永久化:扔在主席树部分了。
相关资料
- 线段树详解 (原理,实现与应用) - AC_King - 博客园 (cnblogs.com).
- 线段树详解 (原理,实现与应用)-CSDN博客.
- 线段树(segment tree)、区间树(interval tree) - 知乎 (zhihu.com).
- 线段树 - OI Wiki (oi-wiki.org).
- 线段树 从入门到进阶(超清晰,简单易懂)_线段树怎么写-CSDN博客.
求区间第 k 大值的若干方法 - 洛谷专栏 (luogu.com)
树状数组+主席树 ,解决动态第k大问题 - 知乎 (zhihu.com).
线段树优化建图
权值线段树
就是把值域当线段…(真正理解线段树的朋友应该可以懂)
扫描线
主席树(可持久化线段树)
势能线段树
线段树解题思路
在考试的时候,如果感到紧张,可以学着像我一样用下面的方法:
-
确定自己的线段树是用来干嘛的,把它的用途以及各种标记确定下来;
-
开始建树,如果你感到很烦躁,紧张,或是无处下手,可以另开一个文件,专心沉浸在建树之中。建树时,也分步进行,可以沿用我下面的模版(有些没必要就省了吧):
struct Segment_Tree{
/*定义宏变量*/
/*定义变量*/
/*初始化*/
/*建树*/
/*传递*/
/*修改*/
/*查询*/
}seg;
- 回到主程序,完成题目的要求,然后你会发现简单了许多。