线段树

\(NOIP\)阶段只要不涉及区间翻转、区间插入的序列问题都可以用线段树实现。

每一个节点维护一个区间的信息。

线段树有分治的感觉。

线段树可以处理很多符合结合律的操作。

时间复杂度,建树为\(O(n)\),区间查询和区间修改为\(O(log\ n)\)。

空间要开原序列长度的四倍。

查询的话就是序列分割,由于分割次数近似\(log\) \(n\),故总复杂度为\(O(n\ log\ n)\)

\(code:\)

void pushup(int cur)
{
    val[cur]=val[ls[cur]]+val[rs[cur]];
}
void pushdown(int cur,int l,int r)
{
    if(!lazy[cur]) return;
    lazy[ls[cur]]+=lazy[cur];
    lazy[rs[cur]]+=lazy[cur];
    int mid=(l+r)>>1;
    val[ls[cur]]+=lazy[cur]*(mid-l+1);
    val[rs[cur]]+=lazy[cur]*(r-mid);
    lazy[cur]=0;
}
void build(int L,int R,int &cur)
{
    cur=++tree_cnt;
    if(L==R)
    {
        val[cur]=a[L];
        return;
    }
    int mid=(L+R)>>1;
    build(L,mid,ls[cur]);
    build(mid+1,R,rs[cur]);
    pushup(cur);
}
void modify(int L,int R,int l,int r,ll add,int cur)
{
    if(L<=l&&R>=r)
    {
        val[cur]+=add*(r-l+1);
        lazy[cur]+=add;
        return;
    }
    pushdown(cur,l,r);
    int mid=(l+r)>>1;
    if(L<=mid) modify(L,R,l,mid,add,ls[cur]);
    if(R>mid) modify(L,R,mid+1,r,add,rs[cur]);
    pushup(cur);
}
ll query(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return val[cur];
    pushdown(cur,l,r);
    ll sum=0;
    int mid=(l+r)>>1;
    if(L<=mid) sum+=query(L,R,l,mid,ls[cur]);
    if(R>mid) sum+=query(L,R,mid+1,r,rs[cur]);
    return sum;
}

......

build(1,n,root);//建树
modify(x,y,1,n,z,root);//区间修改,x到y的区间加上z
query(x,y,1,n,root)//区间查询,x到y的和

动态开点

\(code:\)

void pushup(int cur)
{
    val[cur]=val[ls[cur]]+val[rs[cur]];
}
void pushdown(int cur,int l,int r)
{
    if(!lazy[cur]) return;
    int mid=(l+r)>>1;
    if(!ls[cur]) ls[cur]=++tree_cnt;
    lazy[ls[cur]]+=lazy[cur];
    val[ls[cur]]+=lazy[cur]*(mid-l+1);
    if(!rs[cur]) rs[cur]=++tree_cnt;
    lazy[rs[cur]]+=lazy[cur];
    val[rs[cur]]+=lazy[cur]*(r-mid);
    lazy[cur]=0;
}
void modify(int L,int R,int l,int r,ll add,int &cur)
{
    if(!cur) cur=++tree_cnt;
    if(L<=l&&R>=r)
    {
        val[cur]+=add*(r-l+1);
        lazy[cur]+=add;
        return;
    }
    pushdown(cur,l,r);
    int mid=(l+r)>>1;
    if(L<=mid) modify(L,R,l,mid,add,ls[cur]);
    if(R>mid) modify(L,R,mid+1,r,add,rs[cur]);
    pushup(cur);
}
ll query(int L,int R,int l,int r,int cur)
{
    if(L<=l&&R>=r) return val[cur];
    pushdown(cur,l,r);
    ll sum=0;
    int mid=(l+r)>>1;
    if(L<=mid) sum+=query(L,R,l,mid,ls[cur]);
    if(R>mid) sum+=query(L,R,mid+1,r,rs[cur]);
    return sum;
}

线段树合并

将\(x\)和\(y\)合并为\(x\)

通常用来合并权值线段树(一个节点权值为其值域区间元素个数)

更新信息时要特判叶子节点的情况,防止信息覆盖

\(code:\)

int merge(int x,int y)
{
    if(!x||!y) return x+y;
    ls[x]=merge(ls[x],ls[y]);
    rs[x]=merge(rs[x],rs[y]);
    pushup(x);
    return x;
}

线段树优化建图

\(code:\)

void build_in(int L,int R,int &cur)
{
    cur=++tree_cnt;
    if(L==R)
    {
        in_num[L]=cur;
        return;
    }
    int mid=(L+R)>>1;
    build_in(L,mid,ls[cur]);
    build_in(mid+1,R,rs[cur]);
    add(ls[cur],cur,0),add(rs[cur],cur,0);
}
void build_out(int L,int R,int &cur)
{
    cur=++tree_cnt;
    if(L==R)
    {
        out_num[L]=cur;
        return;
    }
    int mid=(L+R)>>1;
    build_out(L,mid,ls[cur]);
    build_out(mid+1,R,rs[cur]);
    add(cur,ls[cur],0),add(cur,rs[cur],0);
}
void modify_in(int L,int R,int l,int r,int pos,int val,int &cur)
{
    if(L<=l&&R>=r)
    {
        add(cur,pos,val);
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) modify_in(L,R,l,mid,pos,val,ls[cur]);
    if(R>mid) modify_in(L,R,mid+1,r,pos,val,rs[cur]);
}
void modify_out(int L,int R,int l,int r,int pos,int val,int &cur)
{
    if(L<=l&&R>=r)
    {
        add(pos,cur,val);
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) modify_out(L,R,l,mid,pos,val,ls[cur]);
    if(R>mid) modify_out(L,R,mid+1,r,pos,val,rs[cur]);
}
上一篇:线段树染色问题(例题为poj2777)


下一篇:python中函数作为反回值时