权值线段树的一个实现

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

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;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值