【洛谷P3380】【模板】二逼平衡树(树套树)

题目

题目链接:https://www.luogu.com.cn/problem/P3380
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:

  1. 查询 \(k\) 在区间内的排名。
  2. 查询区间内排名为 \(k\) 的值。
  3. 修改某一位值上的数值。
  4. 查询 \(k\) 在区间内的前驱(前驱定义为严格小于 \(x\),且最大的数,若不存在输出 \(-2147483647\))。
  5. 查询 \(k\) 在区间内的后继(后继定义为严格大于 \(x\),且最小的数,若不存在输出 \(2147483647\))。

思路

终于有一次写平衡树调试时间不超过 1h 了 /kk。
一般的二逼平衡树复杂度是 \(O(m\log^3 n)\) 的,瓶颈在于操作 2 需要二分。
但是我们知道有一些二分是可以直接放在线段树上的,这样可以去掉一个 \(\log n\)。但是由于操作二并不是一个前缀的询问,所以没有办法放到线段树上。
但是我们发现,排名为 \(k\) 是一个前缀且支持二分,所以我们考虑换一种思路,我们用线段树维护 val,平衡树维护 key。
也就是线段树上区间 \([l,r]\) 的平衡树表示的是数值在 \([l,r]\) 的位置的下标有哪些。显然一个下标只会被 \(O(\log n)\) 个区间覆盖。所以空间复杂度是 \(O(n\log n)\)
接下来对于每一个操作:

  1. 直接查询数值在 \([1,k]\) 中有多少个下标在 \([l,r]\)
  2. 在线段树上二分数值,每次查询左子树有多少个下标在 \([l,r]\) 内,选择往左还是往右。
  3. 将所有包含这个下标的数值区间所对应平衡树内删掉这个下标,然后插入新的下标。为了省空间,我写了一个垃圾回收。
  4. 先查询 \(k-1\) 在区间内的排名 \(rk\),然后查询排名为 \(rk\) 的数值。
  5. 先查询 \(k\) 在区间内的排名 \(rk\),然后查询排名为 \(rk+1\) 的数值。

注意询问之前要把所有包含数值的操作离散化。
时空复杂度均为 \(O(m\log^2 n)\)

代码

4.5Kb。比我想象的长一些。

// I LOVE DATA STRUCTURE!!!
#include <bits/stdc++.h>
using namespace std;

const int N=100010,LG=18,Inf=2147483647;
int n,m,cnt,orz,a[N],b[N];

struct Query
{
	int l,r,opt,k;
}ask[N];

struct Treap
{
	int lc[N*LG],rc[N*LG],val[N*LG],size[N*LG],dat[N*LG];
	queue<int> q;
	
	Treap()
	{
		while (q.size()) q.pop();
		for (int i=1;i<N*LG;i++) q.push(i);
	}
	
	int New(int v)
	{
		int x=q.front(); q.pop();
		lc[x]=rc[x]=0; size[x]=1;
		val[x]=v; dat[x]=rand();
		return x;
	}
	
	void pushup(int x)
	{
		size[x]=size[lc[x]]+size[rc[x]]+1;
	}
	
	void zig(int &x)
	{
		int y=lc[x],z=rc[y];
		rc[y]=x; lc[x]=z; x=y;
		pushup(rc[x]); pushup(x);
	}
	
	void zag(int &x)
	{
		int y=rc[x],z=lc[y];
		lc[y]=x; rc[x]=z; x=y;
		pushup(lc[x]); pushup(x);
	}
	
	int build()
	{
		int x=New(Inf),y=New(-Inf);
		lc[x]=y; size[x]=2;
		return x;
	}
	
	int ins(int x,int v)
	{
		if (!x) x=New(v);
		else if (val[x]>v)
		{
			lc[x]=ins(lc[x],v);
			if (dat[lc[x]]>dat[x]) zig(x);
		}
		else
		{
			rc[x]=ins(rc[x],v);
			if (dat[rc[x]]>dat[x]) zag(x);
		}
		pushup(x);
		return x;
	}
	
	int del(int x,int v)
	{
		if (val[x]==v)
		{
			if (!lc[x] && !rc[x]) { q.push(x); return 0; }
			if (!rc[x] || (lc[x] && dat[rc[x]]<dat[lc[x]]))
				zig(x),rc[x]=del(rc[x],v);
			else
				zag(x),lc[x]=del(lc[x],v);
		}
		else if (val[x]>v)
		{
			lc[x]=del(lc[x],v);
			if (dat[lc[x]]>dat[x]) zig(x);
		}
		else
		{
			rc[x]=del(rc[x],v);
			if (dat[rc[x]]>dat[x]) zag(x);
		}
		pushup(x);
		return x;
	}
	
	int getrk(int x,int v)
	{
		if (!x) return 0;
		if (val[x]==v) return size[lc[x]];
		if (val[x]>v) return getrk(lc[x],v);
		if (val[x]<v) return size[lc[x]]+1+getrk(rc[x],v);
		return 23333;
	}
	
	void debug(int x)
	{
		if (lc[x]) printf("%d %d\n",val[x],val[lc[x]]);
		if (rc[x]) printf("%d %d\n",val[x],val[rc[x]]);
		if (lc[x]) debug(lc[x]);
		if (rc[x]) debug(rc[x]);
	}
}treap;

struct SegTree
{
	int rt[N*4];
	
	void build(int x,int l,int r)
	{
		rt[x]=treap.build();
		if (l==r) return;
		int mid=(l+r)>>1;
		build(x*2,l,mid); build(x*2+1,mid+1,r);
	}
	
	void update(int x,int l,int r,int k,int v,bool tag)
	{
		if (tag) rt[x]=treap.ins(rt[x],v);
			else rt[x]=treap.del(rt[x],v);
		if (l==r) return;
		int mid=(l+r)>>1;
		if (k<=mid) update(x*2,l,mid,k,v,tag);
			else update(x*2+1,mid+1,r,k,v,tag);
	}
	
	int query1(int x,int l,int r,int ql,int qr,int pl,int pr)
	{
		if (ql<=l && r<=qr)
			return treap.getrk(rt[x],pr+1)-treap.getrk(rt[x],pl);
		int mid=(l+r)>>1;
		int sum=0;
		if (ql<=mid) sum+=query1(x*2,l,mid,ql,qr,pl,pr);
		if (qr>mid) sum+=query1(x*2+1,mid+1,r,ql,qr,pl,pr);
		return sum;
	}
	
	int query2(int x,int l,int r,int ql,int qr,int k)
	{
		if (l==r) return l;
		int mid=(l+r)>>1;
		int cnt=treap.getrk(rt[x*2],qr+1)-treap.getrk(rt[x*2],ql);
		if (k<=cnt) return query2(x*2,l,mid,ql,qr,k);
			else return query2(x*2+1,mid+1,r,ql,qr,k-cnt);
	}
}seg;

int main()
{
	srand(53962);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[++cnt]=a[i];
	}
	for (int i=1;i<=m;i++)
	{
		scanf("%d",&ask[i].opt);
		if (ask[i].opt==3)
		{
			scanf("%d%d",&ask[i].l,&ask[i].k);
			b[++cnt]=ask[i].k;
		}
		else
			scanf("%d%d%d",&ask[i].l,&ask[i].r,&ask[i].k);
	}
	sort(b+1,b+1+cnt);
	cnt=unique(b+1,b+1+cnt)-b-1;
	seg.build(1,1,cnt);
	for (int i=1;i<=n;i++)
	{
		a[i]=lower_bound(b+1,b+1+cnt,a[i])-b;
		seg.update(1,1,cnt,a[i],i,1);
	}
	for (int i=1;i<=m;i++)
	{
		if (ask[i].opt==1)
		{
			int k=upper_bound(b+1,b+1+cnt,ask[i].k-1)-b-1;
			printf("%d\n",seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r)+1);
		}
		if (ask[i].opt==2)
			printf("%d\n",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,ask[i].k)]);
		if (ask[i].opt==3)
		{
			int k=lower_bound(b+1,b+1+cnt,ask[i].k)-b;
			seg.update(1,1,cnt,a[ask[i].l],ask[i].l,0);
			seg.update(1,1,cnt,k,ask[i].l,1);
			a[ask[i].l]=k;
		}
		if (ask[i].opt==4)
		{
			int k=upper_bound(b+1,b+1+cnt,ask[i].k-1)-b-1;
			if (k==0) { printf("%d\n",-Inf); continue; }
			int rk=seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r);
			if (rk==0) { printf("%d\n",-Inf); continue; }
			printf("%d\n",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,rk)]);
		}
		if (ask[i].opt==5)
		{
			int k=upper_bound(b+1,b+1+cnt,ask[i].k)-b-1;
			int rk=seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r)+1;
			if (rk>ask[i].r-ask[i].l+1) { printf("%d\n",Inf); continue; }
			printf("%d\n",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,rk)]);
		}
	}
	return 0;
}
posted @ 2021-01-03 20:46  stoorz  阅读(73)  评论(0编辑  收藏  举报