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