1. 程式人生 > 實用技巧 >【洛谷P3380】【模板】二逼平衡樹(樹套樹)

【洛谷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;
}