权值线段树的一个实现

在阅读这篇文章之前,请先了解权值线段树
首先我们要求动态开点,则需要记录一个可用的点的队列,不过一般不会去回收内存,所以在使用时用一个尾指针记录一下即可.维护的信息可以用结构体或者直接用数组存下来。

struct node{ ... } Line[ mxSize ];
int cnt;

<argType1> <argv1>[ mxSize ]
<argType2> <argv2>[ mxSize ]
...
int cnt

考虑到内存的限制,我们选择尽量少的维护信息,所以就不再维护线段的左右端点,但必须维护节点的左右儿子。在树上递归时若不需要区间的左右端点信息时,以该节点是否不存在判断是否继续递归。否则则递归时需要传递左右端点的信息.

考虑建树

#define LR l,mid
#define RR mid + 1,r
#define mid ( (l + r) >> 1 )
int ls[ mxSize ]; //左儿子编号
int rs[ mxSize ]; //右儿子编号
int cnt;

// (build) As int(int,int,int,[argv])
// 返回值 表示当前正在递归的节点的编号
// l,r 当前递归时规定的左右端点
// p 表示建树的目标位置,由于不会变化,可以提到函数外。
// [argv] 其他建树时需要的信息
int build(int l,int r,int p,...){
    int now = ++ cnt; // 从队列中获取一个新的编号作为当前编号
    if( l == r )return now;
    /* ... */ //各种建树前的操作
    if( p <= mid )ls[now] = build( LR,p,... );
    if( p >  mid )rs[now] = build( RR,p,... );
    /* ... */ //各种建树后的操作
    return now;
}

合并两棵权值线段树

#define LR l,mid
#define RR mid + 1,r
#define mid ( (l + r) >> 1 )
int ls[ mxSize ];
int rs[ mxSize ];
int cnt;

// (merge) As int(int,int,[argv])
// u,v 指当前正在合并的两个节点
int merge(int u,int v,... ){
    //如果某个某一方节点不存在,则直接把存在的一方接到合成后的树上
    //一般地,我们规定新树覆蓋原树u,若不覆蓋,则需要更多的空间
    if( !u or !v )return u + v;
    /* ... */ // 合并前的操作
    ls[u] = merge( ls[u],ls[v],... );
    rs[u] = merge( rs[u],rs[v],... );
    /* ... */ // 合并后的操作
    return u;
}
//由于v已经失去了作用则可以把它丢回队列,回收空间

以上是基础操作。有了这两个基础可以做大部分的事情了。

考虑插入,分为小常数和大常数版:

#define LR l,mid
#define RR mid + 1,r
#define mid ( (l + r) >> 1 )
int ls[ mxSize ];
int rs[ mxSize ];
int cnt;

int build(int l,int r,int p);
int merge(int u,int v);

// insert As int(int,int,int,int)
// now 表示插入到当前树下
// 大常数
int insert(int now,int l,int r,int p){
    return merge( now, build( l, r, p ) );
}
// 小常数
int insert(int now,int l,int r,int p){
    if( !now )now = ++ cnt;
    if( l == r )return now;
    if( p <= mid )ls[now] = insert( ls[now],LR,p );
    if( p >  mid )rs[now] = insert( rs[now],RP,p );
    return now;
}

考虑删除

#define LR l,mid
#define RR mid + 1,r
#define mid ( (l + r) >> 1 )
int ls[ mxSize ];
int rs[ mxSize ];
int cnt;

// erase As int(int,int,int,int)
// 返回值 : 删除后该树的编号,若不存在,返回0
int erase(int now,int l,int r,int p){
    if( !now or l == r )return 0;
    if( p <= mid )ls[now] = earse( ls[now],LR,p );
    if( p >  mid )rs[now] = erase( rs[now],RR,p );
    return ls[now] | rs[now] ? now : 0;
}

考虑内存回收,定义一个队列,记录回收的内存

//用 apply() 替换原来的所有 ++cnt
queue<int>bin;int cnt;
int apply(){
    if( bin.empty() )return ++ cnt;
    int res = bin.front();bin.pop;
    return res;
}
void recycle(int p){
    bin.push(p);
}

以上为基本操作,若需要更多操作,如查询某些信息,则需要维护更多信息

完整版:

namespace WST{
    #define LR l,mid
    #define RR mid + 1,r
    #define mid ( (l + r) >> 1 )
    const int mxSize = 1e7;

    int ls[ mxSize ];
    int rs[ mxSize ];
    queue<int>bin;int cnt;

    int apply(){
        if( bin.empty() )return ++ cnt;
        int res = bin.front();bin.pop;
        return res;
    }

    void recycle(int p){
        bin.push(p);
    }

    int build(int l,int r,int p){
        int now = apply(); 
        if( l == r )return now;
        if( p <= mid )ls[now] = build( LR,p );
        if( p >  mid )rs[now] = build( RR,p );
        return now;
    }

    int merge(int u,int v ){
        if( !u or !v )return u + v;
        ls[u] = merge( ls[u],ls[v] );
        rs[u] = merge( rs[u],rs[v] );
        return u;
    }

    int insert(int now,int l,int r,int p){
        if( !now )now = ++ cnt;
        if( l == r )return now;
        if( p <= mid )ls[now] = insert( ls[now],LR,p );
        if( p >  mid )rs[now] = insert( rs[now],RP,p );
        return now;
    }

    int erase(int now,int l,int r,int p){
        if( !now or l == r )return 0;
        if( p <= mid )ls[now] = earse( ls[now],LR,p );
        if( p >  mid )rs[now] = erase( rs[now],RR,p );
        return ls[now] | rs[now] ? now : 0;
    }
}
点赞