线段树分裂与合并

对于动态开点的线段树来说,当有很多棵线段树时,我们可以进行线段树合并,线段树合并经常用于多个点的贡献,合并两棵线段树的复杂度为两棵线段树相同节点的数m*logm。
将y这棵线段树合并到x这棵线段树,对于x没有y有的节点,直接复制y的就行,x有y没有的不用管,都有的加起来即可,这样复杂度就是x与y相同都有的点数。
线段树分裂一般用于一些区间操作,把一个区间单独拉出来成树,将这棵线段树分裂成一些线段树,找要用的那棵。分裂的复杂度为严格的log。

struct node{   //权值线段树 
	int l,r;
	ll num;
}a[maxn*20];
int root[maxn],cnt,top = 2e5;
vector<int> dump;

int newnode()    //新建一个节点 
{
	if( dump.empty() ) return ++cnt;
	else
	{
		int res = dump.back();
		dump.pop_back();
		return res;
	}
}
void del(int p)  //这个点没用了可以删除 
{
	a[p].l = a[p].r = a[p].num = 0;
	dump.push_back(p);
}
void update(int x)
{
	a[x].num = a[a[x].l].num+a[a[x].r].num;
}
void modify(int &now,int l,int r,int p,ll v)   //动态开点 
{
	if( now == 0 ) now = newnode();
	if( l == r )
	{
		a[now].num += v;
		return;
	}
	int m = (l+r)>>1;
	if( p <= m ) modify(a[now].l,l,m,p,v);
	else modify(a[now].r,m+1,r,p,v);
	update(now);
}
void merge(int &x,int y,int l,int r)   //线段树合并 
{
	if( y == 0 ) return;
	if( x == 0 )
	{
		x = y;
		return;
	}
	if( l == r )
	{
		a[x].num += a[y].num;
		return;
	}
	int m = (l+r)>>1;
	merge(a[x].l,a[y].l,l,m);merge(a[x].r,a[y].r,m+1,r);//合并左右子树,传的是引用所以儿子关系都弄好了 
	del(y);
	update(x);
}
void split(int &x,int &y,ll k)   //将x这棵线段树分为两棵,前k个留在x树,剩下的给y树 
{
	if( x == 0 ) return;
	y = newnode();                  //y需要新建节点 
	ll v = a[a[x].l].num;      //x左子树的数量 
	if( k > v ) split(a[x].r,a[y].r,k-v);   //k大于它,那么左子树留在x上,分裂右子树 
	else a[y].r = a[x].r,a[x].r = 0;        //否则右子树给y,x的右子树消失 
	if( k < v ) split(a[x].l,a[y].l,k);     //k小于v时才需要继续分裂左子树,等于的时候就不用了 
	a[y].num = a[x].num - k;    //y的数量有x的数量减k 
	a[x].num = k;               //分裂后x就只剩下k个了 
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值