线段树

nexthexonextbutterflyvolantisyearnyiliashokaindigoapollolandscapecactusmateryicarusfluidmaterial
转移自老blog

线段树

线段树

        线段树是树状数组的强化版,它每次对区间进行二分,每一个深度都维护了整个区间,在同一深度里面,每个节点维护的区间长度大致相同,而每深入一层又大致比上一层多一倍的节点,故空间复杂度为Onlgn

节点信息

        每个非叶子节点维护一个区间[l,r],令mid=(l+r)>>1,则该节点的左儿子维护[l,mid],右儿子维护[mid+1,r];
        每个叶子节点维护一个点;

线段树的平衡性

        线段树是一颗几乎完全平衡的树。

线段树的区间操作

        因此当我们对于一个长度为n区间[l,r]进行操作的时候,我们就要操作所有与这些区间相关节点,这些节点数目接近2*N。要操作2*N个节点,,,,复杂度过高,,,问题无法解决。
        我们的操作相当于对树进行函数递归。

对节点分类

        这里我们把这个问题简化一下,对于那些节点对应区间属于[l,r]的我们分为一类,对于那些节点对应区间与[l,r]有交集,但不是[l,r]的子区间的节点我们分为第二类,第二类节点一定是某个第一类节点的父亲节点,借此我们可以在函数回溯时候利用区间加法处理,容易解决。

继续分析复杂度

        我们容易发现第一类节点数目接近ON而第二类节点数目接近OlgN,第二类节点一个一个处理可以接受,第一类节点必须安排一下。可以证明第一类节点恰好可以用OlgN颗子树的所有节点唯一表达。
        在这里我们简化问题为对于OlgN个节点以及OlgN颗子树的操作,

子树的操作

        现在我们只需要考虑对某颗子树进行操作

懒惰标记(懒修改)

        当我们进行修改的时候,我们试着只对该子树的根进行修改,并为其加入一个懒惰标记,表示他的所有子孙都有一个与懒惰标记有关的操作还未进行,(例如整段区间加上d,我们就设懒惰标记为d) ,如此区间修改就不用跑到叶子节点,就成了OlgN
        当我们进行查询时,每当我们查询一个节点,我们要查询的恰好为该节点所对应的区间,直接就处理了;如果不是该区间,而是该区间的某个子区间,那么我们考虑把该区间的懒惰标记下放到左右儿子,然后递归左右儿子,回溯答案。over
        为什么线段树快,因为它把区间分成OlgN个处理,并且每次在树上只跑了OlgN个节点,依据区间加法处理询问,依据区间加法回溯处理修改,依据懒惰标记不实时更新叶子节点。所以线段树快。

线段树和树状数组

        为什么我在开头说线段树是树状数组的强化版,因为树状数组虽然能依据区间加法处理询问,也可以依靠区间加法处理更新,但是它的非叶子节点过少,导致复杂的数据维护时程序较为复杂,不太好处理。
#define ls (u<<1)
#define rs (u<<1|1)
#define ml ((l+r)>>1)
#define mr (ml+1)
struct segment_tree{
    static const ll maxn=2.1e5;
    ll n;
    ll a[maxn],tree[maxn<<2],lazy[maxn<<2];
    //////////////
    //树链剖分接口
    ll rnk[maxn];
    //////////////
    void pushdown(ll u,ll l,ll r){
        //lazy[ls]+=lazy[u];
        //lazy[rs]+=lazy[u];
        //tree[ls]+=lazy[u]*(ml-l+1);
        //tree[rs]+=lazy[u]*(r-mr+1);
        lazy[u]=0;
    }
    void pushup(ll u){
        //tree[u]=tree[ls]+tree[rs];
    }
    void build(ll nn){
        n=nn;
        build(1,n,1);
    }


    void build(ll l,ll r,ll u){
        lazy[u]=0;
        if(l==r){
            //tree[u]=a[rnk[l]];
            return ;
        }

        build(l,ml,ls);
        build(mr,r,rs);
        pushup(u);
    }
    void update(ll ql,ll qr,ll d){
        update(ql,qr,d,1,1,n);
    }
    void update(ll ql,ll qr,ll d,ll u,ll l,ll r){
        if(ql<=l&&r<=qr){
            //tree[u]+=(r-l+1)*d;
            //lazy[u]+=d;
            return ;
        }
        if(lazy[u])pushdown(u,l,r);
        if(ql<=ml)update(ql,qr,d,ls,l,ml);
        if(qr>=mr)update(ql,qr,d,rs,mr,r);
        pushup(u);
    }
    ll query(ll ql,ll qr){
        return query(ql,qr,1,1,n);
    }
    ll query(ll ql,ll qr,ll u,ll l,ll r){
        if(ql<=l&&r<=qr)return tree[u];
        ll ret=0;
        if(lazy[u])pushdown(u,l,r);
        if(ql<=ml)ret+=query(ql,qr,ls,l,ml);
        if(qr>=mr)ret+=query(ql,qr,rs,mr,r);
        return ret;
    }
};
#define ml ((l+r)>>1)
#define mr (ml+1)
const int maxn=5e4+5;
int rev[maxn*2*35],cov[maxn*2*35],sum[maxn*2*35],ls[maxn*2*35],rs[maxn*2*35],a[maxn],tot;

void push_son(int&son,int l,int r,int covrt,int revrt){// 这个函数要注意重写
    if(son==0) {
        son=++tot;
        cov[son]=-1;
        rev[son]=0;
        sum[son]=0;
        ls[son]=0;
        rs[son]=0;
    }
    if(covrt!=-1){
        sum[son]=(r-l+1)*covrt;
        cov[son]=covrt;
        rev[son]=0;
    }
    if(revrt!=0){
        sum[son]=(r-l+1)-sum[son];
        rev[son]^=1;
    }
}

void push_down(int rt,int l,int r){
    push_son(ls[rt],l,ml,cov[rt],rev[rt]);// 这行要注意重写
    push_son(rs[rt],mr,r,cov[rt],rev[rt]);// 这行要注意重写
    cov[rt]=-1; rev[rt]=0;// 这行要注意重写
}

void push_up(int rt,int l,int r){
    sum[rt]=sum[ls[rt]]+sum[rs[rt]];// 这行要注意重写
}

void build(int&rt,int l,int r){
    rt=tot=0;
    push_son(rt,l,r,-1,0);// 这行要注意重写
}

void update(int rt,int l,int r,int ql,int qr,int d,int type){//
    if(ql<=l&&r<=qr){// 这行要注意重写
        if(type==1) push_son(rt,l,r,-1,1);//rev
        if(type==2) push_son(rt,l,r, d,0);//cover
        return;
    }
    push_down(rt,l,r);
    if(ml>=ql) update(ls[rt],l,ml,ql,qr,d,type);
    if(mr<=qr) update(rs[rt],mr,r,ql,qr,d,type);
    push_up(rt,l,r);
}

int query(int rt,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr) return sum[rt];// 这行要注意重写
    push_down(rt,l,r);
    int ret=0;// 这行要注意重写
    if(ml>=ql) ret+=query(ls[rt],l,ml,ql,qr);// 这行要注意重写
    if(mr<=qr) ret+=query(rs[rt],mr,r,ql,qr);// 这行要注意重写
    return ret;
}

感谢您的阅读。 🙏 关于转载请看这里