线段树学习笔记

线段树学习笔记


线段树注意事项

  1. 线段树是一种常数非常大的数据结构,谨慎使用
  2. 线段树在开 空间不能吝啬

基础线段树(一维)

递归思想:模版

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;
    }//区间查询 
};

标记永久化:扔在主席树部分了。

相关资料

  1. 线段树详解 (原理,实现与应用) - AC_King - 博客园 (cnblogs.com).
  2. 线段树详解 (原理,实现与应用)-CSDN博客.
  3. 线段树(segment tree)、区间树(interval tree) - 知乎 (zhihu.com).
  4. 线段树 - OI Wiki (oi-wiki.org).
  5. 线段树 从入门到进阶(超清晰,简单易懂)_线段树怎么写-CSDN博客.

求区间第 k 大值的若干方法 - 洛谷专栏 (luogu.com)

树状数组+主席树 ,解决动态第k大问题 - 知乎 (zhihu.com).


线段树优化建图

线段树优化建图学习笔记-CSDN博客


权值线段树

就是把值域当线段…(真正理解线段树的朋友应该可以懂)


扫描线

线段树+扫描线算法学习笔记-CSDN博客


主席树(可持久化线段树)

主席树(可持久化线段树)学习笔记-CSDN博客


势能线段树

势能线段树学习笔记-CSDN博客


线段树解题思路

在考试的时候,如果感到紧张,可以学着像我一样用下面的方法:

  • 确定自己的线段树是用来干嘛的,把它的用途以及各种标记确定下来;

  • 开始建树,如果你感到很烦躁,紧张,或是无处下手,可以另开一个文件,专心沉浸在建树之中。建树时,也分步进行,可以沿用我下面的模版(有些没必要就省了吧):

struct Segment_Tree{
    /*定义宏变量*/
    /*定义变量*/
    /*初始化*/
    /*建树*/
    /*传递*/
    /*修改*/
    /*查询*/
}seg;
  • 回到主程序,完成题目的要求,然后你会发现简单了许多。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值