• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

P5494.【模板】线段树分裂

开发技术 开发技术 2周前 (04-08) 7次浏览

【模板】线段树分裂

给出一个可重集a(编号为1),它支持以下操作:

0 p x y:将可重集p中大于等于x且小于等于y的值放入一个新的可重集中(新可重集编号为从2开始的正整数,是上一次产生的新可重集的编号+1)

1 p t:将可重集t中的数放入可重集p中,且清空可重集t(数据保证在此后的操作中不会出现可重集t)

2 p x q:在p这个可重集中加入x个数字q。

3 p x y:查询可重集p中大于等于x且小于等于y的值的个数。

4 p k:查询在p这个可重集中第k小的数,不存在时输出-1。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
const int M=maxn*40;
int n,m;
int lson[M];
int rson[M];
long long sum[M];
int T[maxn];
int cnt=0;
int tot;
//删掉的节点个数 
//空间回收,可以回收线段树合并后没有用的节点
int rb[maxn];//垃圾桶
void del (int &u) {
	lson[u]=rson[u]=sum[u]=0;
	rb[++tot]=u;
	u=0;
} 
int New() {
	//申请空间
	if (tot) return rb[tot--];
	return ++cnt; 
}

int a[maxn];//原始数组
void pushup (int u) {
	sum[u]=sum[lson[u]]+sum[rson[u]];
} 
void build (int &u,int l,int r) {
	if (!u) u=New();
	if (l==r) {
		sum[u]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(lson[u],l,mid);
	build(rson[u],mid+1,r);
	pushup(u);
}
void merge (int &rt1,int &rt2,int l,int r) {
	if (!rt1||!rt2) {
		rt1+=rt2;
		return;
	}
	if (l==r) {
		sum[rt1]+=sum[rt2];
		del(rt2);
		return;
	}
	int mid=(l+r)>>1;
	merge(lson[rt1],lson[rt2],l,mid);
	merge(rson[rt1],rson[rt2],mid+1,r);
	del(rt2);
	pushup(rt1);
} 
void split (int &rt1,int &rt2,int l,int r,int L,int R) {
	if (R<l||r<L) return;
	if (!rt1) return;
	if (L<=l&&r<=R) {
		rt2=rt1;
		rt1=0;
		return;
	}
	if (!rt2) rt2=New();
	int mid=(l+r)>>1;
	split(lson[rt1],lson[rt2],l,mid,L,R);
	split(rson[rt1],rson[rt2],mid+1,r,L,R);
	pushup(rt1);
	pushup(rt2);
}
void up (int &rt,int l,int r,int p,int v) {
	if (p<l||p>r) return;
	if (!rt) rt=New();
	if (l==r) {
		sum[rt]+=v;
		return; 
	}
	int mid=(l+r)>>1;
	up(lson[rt],l,mid,p,v);
	up(rson[rt],mid+1,r,p,v);
	pushup(rt);
}
long long query_sum (int rt,int l,int r,int L,int R) {
	if (R<l||r<L) return 0;
	if (!rt) return 0;
	if (L<=l&&r<=R) return sum[rt];
	int mid=(l+r)>>1;
	return query_sum(lson[rt],l,mid,L,R)+query_sum(rson[rt],mid+1,r,L,R);
}
int query_kth (int rt,int l,int r,long long k) {
	if (k<=0) return -1;
	if (l==r) return l;
	int mid=(l+r)>>1;
	if (sum[lson[rt]]>=k) return query_kth(lson[rt],l,mid,k);
	return query_kth(rson[rt],mid+1,r,k-sum[lson[rt]]);
}
int ttt=1;
int main () {
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%d",a+i);
	build(T[1],1,n);
	while (m--) {
		int op;
		scanf("%d",&op);
		if (op==0) {
			int p,x,y;
			scanf("%d%d%d",&p,&x,&y);
			split(T[p],T[++ttt],1,n,x,y);
			//分裂出一颗新树 
		}
		else if (op==1) {
			int p,t;
			scanf("%d%d",&p,&t);
			merge(T[p],T[t],1,n);
		}
		else if (op==2) {
			int p,x,y;
			scanf("%d%d%d",&p,&x,&y);
			up(T[p],1,n,y,x);
		}
		else if (op==3) {
			int p,x,y;
			scanf("%d%d%d",&p,&x,&y);
			printf("%lldn",query_sum(T[p],1,n,x,y));
		}
		else if (op==4) {
			int x,y;
			int ans=0;
			scanf("%d%d",&x,&y);
			if (query_sum(T[x],1,n,1,n)<y) ans=-1;
			else ans=query_kth(T[x],1,n,y);
			printf("%dn",ans);
		}
	}
}

程序员灯塔
转载请注明原文链接:P5494.【模板】线段树分裂
喜欢 (0)