P6012 [template] segment tree split

(Because there is no certification, so this question on the Froggy upload)
segment tree split place really is not much used, the luogu no question this channel template before, so out of the one, really can not think how the template , so this question may be able to look at the past with other algorithms water.

Front cheese

  1. Segment tree : This title is used in weight segment tree (the number of times each query appear in the sequence, the sequence of k large number of other operations).
  2. Segment tree merge: In order to increase the amount of code before it is put .

Segment tree split

Since it is a question template, this is the natural focus.
The following such a segment tree:
Here Insert Picture Description
needs to be part of the orange segments covered splinter, need to build a new tree, when the interval represented by a node with the need to split the section have to pay when the need to open a new node (the green part), the original tree will need to be split across those parts directly to the new tree below, and will be the original tree edge off (the red part), it can be found disconnect the side there will only be a maximum of log 2 N \log_2N article, the final split every time complexity is log 2 N \log_2N (proof modify the interval N log 2 N N\log_2N same).

Code

#include<bits/stdc++.h>
#define rap(i,first,last) for(int i=first;i<=last;++i)
//线段树标准define
#define Lson (tree[now].lson)
#define Rson (tree[now].rson)
#define Middle ((left+right)>>1)
#define Left Lson,left,Middle
#define Right Rson,Middle+1,right
#define Now nowleft,nowright
using namespace std;
const int maxN=6e5+7;//因为有分裂,需要大一点的空间
int N,M;
struct Tree//线段树中每一个节点
{
	int lson,rson;//动态开点,记录左右儿子
	long long sum;//区间和
}tree[maxN*4];
int cnt=0;
//一下是一个空间回收,可以将在线段树合并后没有用的点回收,更好地利用空间
int tot=0;//删掉的节点的个数
int rubbish[maxN*4];//用来放删掉的节点的编号
void Del(int &now)//删除一个节点
{
	tree[now].lson=tree[now].rson=tree[now].sum=0;//先清空
	rubbish[++tot]=now;//放入垃圾桶
	now=0;
}
int New()//得到一个新的节点
{
	if(tot)return rubbish[tot--];//垃圾桶不是空的就从垃圾桶上面拿一个
	return ++cnt;//垃圾桶是空的就拿一个全新的节点
}
//空间回收代码结束
int arr[maxN];//开始读入的数组
void PushUp(int now)//合并信息
{
	tree[now].sum=tree[Lson].sum+tree[Rson].sum;
}
void Build(int &now,int left=1,int right=N)//建树
{
	if(!now)now=New();//得到一个新节点
	if(left==right)
	{
		tree[now].sum=arr[left];//叶节点直接赋值
		return;
	}
	Build(Left);//建左子树
	Build(Right);//建右子树
	PushUp(now);//合并信息
}
void Merge(int &tree1,int &tree2,int left=1,int right=N)//线段树合并,将tree2合并到tree1上
{
	if(!tree1||!tree2)//如果当前这棵树只有其中一颗树有就可以直接赋值
	{
		tree1+=tree2;
		return;
	}
	if(left==right)//叶节点就直接合并
	{
		tree[tree1].sum+=tree[tree2].sum;
		Del(tree2);//删掉tree2
		return;
	}
	Merge(tree[tree1].lson,tree[tree2].lson,left,Middle);//继续合并
	Merge(tree[tree1].rson,tree[tree2].rson,Middle+1,right);
	Del(tree2);//删掉tree2
	PushUp(tree1);//合并信息
}
void Split(int &tree1,int &tree2,int nowleft,int nowright,int left=1,int right=N)//线段树分裂(将tree1中nowleft~nowright部分分裂到tree2中)
{
	if(right<nowleft||nowright<left)return;//没有被覆盖就直接返回
	if(!tree1)return;//如果tree1没有自然也没有用了
	if(nowleft<=left&&right<=nowright)//被完全覆盖时
	{
		tree2=tree1;
		tree1=0;//把边断开
		return;
	}
	if(!tree2)tree2=New();//得到一个新节点
	Split(tree[tree1].lson,tree[tree2].lson,Now,left,Middle);//左右子树继续分裂
	Split(tree[tree1].rson,tree[tree2].rson,Now,Middle+1,right);
	PushUp(tree1);//合并两数信息
	PushUp(tree2);
}
void UpData(int num,int add,int &now,int left=1,int right=N)//单点修改(不多讲)
{
	if(num<left||num>right)return;
	if(!now)now=New();
	if(left==right)
	{
		tree[now].sum+=add;
		return;
	}
	UpData(num,add,Left);
	UpData(num,add,Right);
	PushUp(now);
}
long long QuerySum(int nowleft,int nowright,int now,int left=1,int right=N)//查询区间和(不多讲)
{
	if(nowright<left||right<nowleft)return 0;
	if(!now)return 0;
	if(nowleft<=left&&right<=nowright)
	{
		return tree[now].sum;
	}
	return QuerySum(Now,Left)+QuerySum(Now,Right);
}
int QueryKth(long long k,int now,int left=1,int right=N)//查询区间第k大(不多讲)
{
	if(k<=0)return -1;
	if(left==right)return left;
	if(tree[Lson].sum>=k)return QueryKth(k,Left);
	return QueryKth(k-tree[Lson].sum,Right);
}
int root[maxN];//记录每一个序列的线段树的根节点
int cnttree=1;//序列的编号
int main()
{
	scanf("%d%d",&N,&M);
	rap(i,1,N)scanf("%d",&arr[i]);
	Build(root[1]);//以root[1]建树
	int check,p,t,x,y,k,q,kth;
	rap(i,1,M)
	{
		scanf("%d",&check);
		if(check==0)
		{
			scanf("%d%d%d",&p,&x,&y);
			Split(root[p],root[++cnttree]/*建一颗新树*/,x,y);//分裂
		}
		if(check==1)
		{
			scanf("%d%d",&p,&t);
			Merge(root[p],root[t]);//合并
		}
		if(check==2)
		{
			scanf("%d%d%d",&p,&x,&q);
			UpData(q,x,root[p]);//修改
		}
		if(check==3)
		{
			scanf("%d%d%d",&p,&x,&y);
			printf("%lld\n",QuerySum(x,y,root[p]));//区间和
		}
		if(check==4)
		{
			scanf("%d%d",&p,&k);
			if(QuerySum(1,N,root[p])<k)kth=-1;//区间和都没有k自然是不会有第k大了
			else
			kth=QueryKth(k,root[p]);
			printf("%d\n",kth);
		}
	}
}
Published 72 original articles · won praise 22 · views 5517

Guess you like

Origin blog.csdn.net/sxy__orz/article/details/104074022