【洛谷P5494】【模板】线段树*

题目

题目链接:https://www.luogu.com.cn/problem/P5494
给出一个可重集 (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

(n,mleq 2 imes 10^5)

思路

操作 (1,2,3,4) 可以用权值线段树和线段树合并解决。至于操作 (1) 线段树合并的复杂度,每次可以看作是 (O( ext{节点数较小值})) 的,所以一般的线段树合并是 (O(nlog n))。在本题中,操作 (0,2) 单次加入的节点数均为 (O(log n)),所以复杂度没有问题。
考虑操作 (0),也就是把权值线段树的一个区间 ([l,r]) 分为 ([l,k])([k+1,r]) 扔到两棵权值线段树上。类似 FHQ Treap 的 split,对于当前区间 ([l,r]),如果 (kleq mid),则左边的权值线段树的这一层节点为空,右边的直接设为 (x);反之同理。这样就保证了每次只会找 (O(log n)) 个节点。
而合并 ([0,x),(y,n]) 区间的时候,由于这两棵线段树没有交,单次复杂度也是 (O(log n)) 的。
时间复杂度 (O(mlog n))

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=200010,LG=18;
int n,m,Q,rt[N];

struct SegTree
{
	int tot,lc[N*LG*4],rc[N*LG*4];
	ll cnt[N*LG*4];
	
	void pushup(int x)
	{
		cnt[x]=cnt[lc[x]]+cnt[rc[x]];
	}
	
	int update(int x,int l,int r,int k,ll v)
	{
		if (!x) x=++tot;
		if (l==r) { cnt[x]+=v; return x; }
		int mid=(l+r)>>1;
		if (k<=mid) lc[x]=update(lc[x],l,mid,k,v);
			else rc[x]=update(rc[x],mid+1,r,k,v);
		pushup(x);
		return x;
	}
	
	void split(int x,int l,int r,int v,int &p,int &q)
	{
		if (!x) return;
		if (l==r) { p=x; q=0; return; }
		int mid=(l+r)>>1;
		if (v<=mid)
			q=x,p=++tot,split(lc[x],l,mid,v,lc[p],lc[q]);
		else
			p=x,q=++tot,split(rc[x],mid+1,r,v,rc[p],rc[q]);
		pushup(p); pushup(q);
	}
	
	int merge(int x,int y,int l,int r)
	{
		if (!x || !y) return x|y;
		if (l==r) { cnt[x]+=cnt[y]; return x; }
		int mid=(l+r)>>1;
		lc[x]=merge(lc[x],lc[y],l,mid);
		rc[x]=merge(rc[x],rc[y],mid+1,r);
		pushup(x);
		return x;
	}
	
	ll query1(int x,int l,int r,int ql,int qr)
	{
		if (!x) return 0;
		if (ql<=l && qr>=r) return cnt[x];
		int mid=(l+r)>>1; ll res=0;
		if (ql<=mid) res+=query1(lc[x],l,mid,ql,qr);
		if (qr>mid) res+=query1(rc[x],mid+1,r,ql,qr);
		return res;
	}
	
	int query2(int x,int l,int r,ll k)
	{
		if (l==r) return l;
		int mid=(l+r)>>1;
		if (cnt[lc[x]]>=k) return query2(lc[x],l,mid,k);
			else return query2(rc[x],mid+1,r,k-cnt[lc[x]]);
	}
}seg;

int main()
{
//	freopen("P5494_1.in","r",stdin);
//	freopen("ans.out","w",stdout);
	scanf("%d%d",&n,&Q);
	for (int i=1,x;i<=n;i++)
	{
		scanf("%d",&x);
		rt[1]=seg.update(rt[1],1,n,i,x);
	}
	m=1;
	while (Q--)
	{
		int opt,x,y,z;
		scanf("%d",&opt);
		if (opt==0)
		{
			int p=0,q=0;
			scanf("%d%d%d",&x,&y,&z);
			if (y>1) seg.split(rt[x],1,n,y-1,p,rt[x]);
			seg.split(rt[x],1,n,z,rt[++m],q);
			rt[x]=seg.merge(p,q,1,n);
		}
		if (opt==1)
		{
			scanf("%d%d",&x,&y);
			rt[x]=seg.merge(rt[x],rt[y],1,n);
		}
		if (opt==2)
		{
			scanf("%d%d%d",&x,&y,&z);
			rt[x]=seg.update(rt[x],1,n,z,y);
		}
		if (opt==3)
		{
			scanf("%d%d%d",&x,&y,&z);
			cout<<seg.query1(rt[x],1,n,y,z)<<"
";
		}
		if (opt==4)
		{
			ll k;
			scanf("%d",&x); scanf("%lld",&k);
			if (seg.cnt[rt[x]]<k) cout<<"-1
";
				else cout<<seg.query2(rt[x],1,n,k)<<"
";
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/15217808.html