【树套树】【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);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/Dy_Dream/article/details/84990289
今日推荐