1. 程式人生 > >【樹套樹】【BZOJ3196】二逼平衡樹

【樹套樹】【BZOJ3196】二逼平衡樹

【題目描述】

您需要寫一種資料結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:
1.查詢 x在區間內的排名;
2.查詢區間內排名為 k 的值;
3.修改某一位置上的數值;
4.查詢 x 在區間內的前趨(前趨定義為小於 x,且最大的數);
5.查詢 x 在區間內的後繼(後繼定義為大於 x,且最小的數)。

【輸入格式】

第一行兩個數 n,m,表示長度為 n 的有序序列和 m 個操作。
第二行有 n個數,表示有序序列。
下面有 m 行,每行第一個數表示操作型別:
1.之後有三個數 l,r,x表示查詢 x在區間 [l,r] 的排名;
2.之後有三個數 l,r,k表示查詢區間 [l,r]內排名為 k的數;
3.之後有兩個數 pos,x表示將 pos位置的數修改為 x;
4.之後有三個數 l,r,x表示查詢區間 [l,r]內 x 的前趨;
5.之後有三個數 l,r,x表示查詢區間 [l,r]內 x 的後繼。

【輸出格式】

對於操作 1,2,4,5各輸出一行,表示查詢結果。


非常經典的樹套樹題目,這裡採用的是線段樹套平衡樹的做法

對於操作一,我們考慮查詢每一分割槽間小於查詢值的數的數量累加起來+1即可,查詢過程只需要將其嚴格次小字首轉到根即可

對於操作二,我們考慮二分一個數,然後參照操作一的做法,查詢所有小於等於二分值的數的數量,可以明白,這是單調遞增的,故二分可行,注意與操作一不一樣的是這裡我們需要將非嚴格次小字首轉到根

對於操作三,直接對每一包含該位置的區間一次刪除,一次插入即可

對於操作四,查詢每一分割槽間的嚴格次小字首,取較大值

對於操作五,查詢每一分割槽間的嚴格次大字尾,取較小值

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
int n,m,a[50005],root[200005],sign,ans,op,sum;
struct Tree
{
	int ch[2],fa;
	int recy,sum;
	int v;
}tree[1650005];
int crepoint(int v,int fa)
{
	sign++;
	tree[sign].v=v;
	tree[sign].fa=fa;
	tree[sign].recy=tree[sign].sum=1;
	return sign;
}
int whoson(int x)
{
	return (tree[tree[x].fa].ch[0]==x)?0:1;
}
void connect(int x,int fa,int son)
{
	tree[x].fa=fa;
	tree[fa].ch[son]=x;
}
void push_up(int k)
{
	tree[k].sum=tree[tree[k].ch[0]].sum+tree[tree[k].ch[1]].sum+tree[k].recy;
}
void rotate(int x)
{
	int y=tree[x].fa;
	int mroot=tree[y].fa;
	int yson=whoson(x);
	int mrootson=whoson(y);
	int B=tree[x].ch[yson^1];
	connect(B,y,yson);
	connect(y,x,yson^1);
	connect(x,mroot,mrootson);
	push_up(y);
	push_up(x);
}
void Splay(int x,int to,int k)
{
	while(tree[x].fa!=to)
	{
		int y=tree[x].fa,z=tree[y].fa;
		if(z!=to)
		{
			if(whoson(x)^whoson(y)) rotate(x);
			else rotate(y);
		}
		rotate(x);
	}
	if(!to) root[k]=x;
}
void find(int nowroot,int v,int k)
{
	int now=nowroot;
	while(now)
	{
		if(v==tree[now].v)
		{
			Splay(now,0,k);
			return ;
		}
		int nex=(v<tree[now].v)?0:1;
		now=tree[now].ch[nex];
	}
}
void push(int &nowroot,int v,int k)
{
	if(!nowroot)
	{
		nowroot=crepoint(v,0);
		return ;
	}
	int now=nowroot;
	while(now)
	{
		tree[now].sum++;
		if(v==tree[now].v)
		{
			tree[now].recy++;
			Splay(now,0,k);
			return ;
		}
		int nex=(v<tree[now].v)?0:1;
		if(!tree[now].ch[nex])
		{
			tree[now].ch[nex]=crepoint(v,now);
			Splay(tree[now].ch[nex],0,k);
			return ;
		}
		now=tree[now].ch[nex];
	}
}
void pop(int &nowroot,int v,int k)
{
	find(nowroot,v,k);
	if(tree[nowroot].recy>1)
	{
		tree[nowroot].recy--;
		tree[nowroot].sum--;
		return ;
	}
	int ls=tree[nowroot].ch[0],rs=tree[nowroot].ch[1];
	if(!ls&&!rs)
	{
		nowroot=0;
		return ;
	}
	if(!ls)
	{
		nowroot=rs;
		tree[rs].fa=0;
		return ;
	}
	if(!rs)
	{
		nowroot=ls;
		tree[ls].fa=0;
		return ;
	}
	while(tree[ls].ch[1]) ls=tree[ls].ch[1];
	Splay(ls,0,k);
	tree[nowroot].ch[1]=rs;
	if(rs) tree[rs].fa=nowroot;
}
void build(int k,int l,int r)
{
	for(int i=l;i<=r;i++) push(root[k],a[i],k);
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}
int super(int nowroot,int v)
{
	int maxx=-0x7fffffff,nows=0;
	int now=nowroot;
	while(now)
	{
		if(tree[now].v<v) if(maxx<tree[now].v) maxx=tree[now].v,nows=now;
		int nex=(v<=tree[now].v)?0:1;
		now=tree[now].ch[nex];
	}
	if(op==4) ans=max(ans,maxx);
	return nows;
}
int super2(int nowroot,int v)
{
	int maxx=-0x7fffffff,nows=0;
	int now=nowroot;
	while(now)
	{
		if(tree[now].v<=v) if(maxx<tree[now].v) maxx=tree[now].v,nows=now;
		int nex=(v<tree[now].v)?0:1;
		now=tree[now].ch[nex];
	}
	return nows;
}
int upper(int nowroot,int v)
{
	int minn=0x7fffffff,nows=0;
	int now=nowroot;
	while(now)
	{
		if(tree[now].v>v) if(minn>tree[now].v) minn=tree[now].v,nows=now;
		int nex=(v<tree[now].v)?0:1;
		now=tree[now].ch[nex];
	}
	ans=min(ans,minn);
	return nows;
}
void ask(int k,int l,int r,int ql,int qr,int v)
{
	if(ql<=l&&r<=qr)
	{
		int now=super(root[k],v);
		if(now) Splay(now,0,k);
		if(now) ans+=tree[now].sum-tree[tree[now].ch[1]].sum;
		return ;
	}
	int mid=(l+r)>>1;
	if(ql<=mid) ask(k<<1,l,mid,ql,qr,v);
	if(mid<qr) ask(k<<1|1,mid+1,r,ql,qr,v);
}
void ask2(int k,int l,int r,int ql,int qr,int v)
{
	if(ql<=l&&r<=qr)
	{
		int now=super2(root[k],v);
		if(now) Splay(now,0,k);
		if(now) sum+=tree[now].sum-tree[tree[now].ch[1]].sum;
		return ;
	}
	int mid=(l+r)>>1;
	if(ql<=mid) ask2(k<<1,l,mid,ql,qr,v);
	if(mid<qr) ask2(k<<1|1,mid+1,r,ql,qr,v);
}
void change(int k,int l,int r,int x,int v1,int v2)
{
	pop(root[k],v1,k);
	push(root[k],v2,k);
	if(l==r) return ;
	int mid=(l+r)>>1;
	if(x<=mid) change(k<<1,l,mid,x,v1,v2);
	else change(k<<1|1,mid+1,r,x,v1,v2);
}
void ask3(int k,int l,int r,int ql,int qr,int v)
{
	if(ql<=l&&r<=qr)
	{
		int now=super(root[k],v);
		if(now) Splay(now,0,k);
		return ;
	}
	int mid=(l+r)>>1;
	if(ql<=mid) ask3(k<<1,l,mid,ql,qr,v);
	if(mid<qr) ask3(k<<1|1,mid+1,r,ql,qr,v);
}
void ask4(int k,int l,int r,int ql,int qr,int v)
{
	if(ql<=l&&r<=qr)
	{
		int now=upper(root[k],v);
		if(now) Splay(now,0,k);
		return ;
	}
	int mid=(l+r)>>1;
	if(ql<=mid) ask4(k<<1,l,mid,ql,qr,v);
	if(mid<qr) ask4(k<<1|1,mid+1,r,ql,qr,v);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d%d%d",&x,&y,&z);
			ans=0;
			ask(1,1,n,x,y,z);
			printf("%d\n",ans+1);
		}
		else if(op==2)
		{
			scanf("%d%d%d",&x,&y,&z);
			ans=-1e8;
			int l=-1e8,r=1e8;
			while(l<=r)
			{
				int mid=(l+r)>>1;
				sum=0;
				ask2(1,1,n,x,y,mid);
				if(sum>=z) ans=mid,r=mid-1;
				else l=mid+1;
			}
			printf("%d\n",ans);
		}
		else if(op==3)
		{
			scanf("%d%d",&x,&y);
			change(1,1,n,x,a[x],y);
			a[x]=y;
		}
		else if(op==4)
		{
			scanf("%d%d%d",&x,&y,&z);
			ans=-0x7fffffff;
			ask3(1,1,n,x,y,z);
			printf("%d\n",ans);
		}
		else
		{
			scanf("%d%d%d",&x,&y,&z);
			ans=0x7fffffff;
			ask4(1,1,n,x,y,z);
			printf("%d\n",ans);
		}
	}
}