HDU3436[离散化splay]

8 篇文章 0 订阅
1 篇文章 0 订阅

一开始拿到题,恩,还行吧,就是splay模拟一下。

看了一下数据,卧槽,n这么大,不过好在q比较小,所以需要离散化。

线段树的离散化是把数字排序去重,每个数字对应在数组中的位置。

splay的离散化要是这么写估计得挂。           



splay的离散化:将要操作的点加到数组中排序、去重,按顺序,每个点加入,并且相连两个点之间的区间算作一个点加入。

也就是,splay的每个点实际上维护了一段区间。   到时候二分一下,就可以找到每个数字对应在哪个点的区间内。



题意:n个点,q次操作。

Top x,把x这个数取出来放到最前面。           存一下每个splay点的地址,直接找一下,把这个点(离散这个点保证区间长度为1)旋到根,删掉,再在最左边加入。

Query x,询问x是排名第几的数              把x所在的区间对应的点放到根(离散这个点保证区间长度为1),root左区间长+1就是答案。

Rank x,询问排名第k的是哪一个数        按照找排名第k那样做,把排名为第k的区间找到,旋到根,k-左子树-1就是它在root的区间内的排名。


首先,照样来上splay的版(rotate,splay       还有一个insert,我比较懒,不想用build)

void rotate(node *u)
{
	node *f=u->f;
	if(f==null)return ;
	node *ff=f->f;
	int d=u==f->ch[1];
	int dd=0;
	if(ff!=null)dd=f==ff->ch[1];
	
	if(u->ch[d^1]!=null)u->ch[d^1]->f=f;
	f->ch[d]=u->ch[d^1];
	
	u->ch[d^1]=f;
	f->f=u;
	
	if(ff!=null)ff->ch[dd]=u;
	u->f=ff;
	f->rs();
	u->rs();
}
void splay(node *u,node *p)
{
	while(u->f!=p)
	{
		node *f=u->f;
		node *ff=f->f;
		if(ff==p)
		{
			rotate(u);
			break;
		}
		int d=u==f->ch[1];
		int dd=f==ff->ch[1];
		if(d==dd)rotate(f);
		else rotate(u);
		rotate(u);
	}
	if(p==null)root=u;
	u->rs();
}
void insert(node *&u,node *&fa,int key)
{
        if(u==null)
       {
	u=newnode(key);
	u->f=fa;
	splay(u,null);
	return ;
	}
	insert(u->ch[1],u,key);
        u->rs();
}



然后对于Top:

我们先找到x对应区间标号,二分:

int bin(int x)
{
	int l=1,r=tot;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(s[mid]>x)r=mid-1;
		else if(e[mid]<x)l=mid+1;
		else return mid;
	}
	return l;
}

然后,把标号为bin(x)旋到根(存过地址)

然后,DEL ROOT()

void Del()
{
	node *u=root;
	if(root->ch[1]!=null)
	{
		root=root->ch[1];
		select(1);//(select是从root找排名第k的点并旋到根)
		if(u->ch[0]!=null)u->ch[0]->f=root;
		root->ch[0]=u->ch[0];
	}
	else 
	{
		root=root->ch[0];
	}
	root->f=null;
	if(root==null)return ;
	root->rs();
}

然后在最左边重新插入:

void rin(node *&u,node *&fa,int key)
{
	if(u==null)
	{
		u=newnode(key);
		u->f=fa;
		return ;
	}
	rin(u->ch[0],u,key);
	u->rs();
}




QUERY x://询问x的排名

把x对应的区间的点找到(二分+地址),旋到根,直接左子树+1


RANK x://询问排第x的是谁

利用select函数,查找排名为k的对应区间,旋到根。

x-root->ch[0]-sz-1是在根的区间中对应的位置。


void select(int k)//查询第k个的位置并将其移到根 
{
	node *u=root;
	while(1)
	{
		if(k<=u->ch[0]->sz+u->num&&k>u->ch[0]->sz)break;
		if(k<=u->ch[0]->sz)u=u->ch[0];
		else
		{
			k-=u->ch[0]->sz+u->num;
			u=u->ch[1];
		}
	}
	splay(u,null);
}



全代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int maxn=100000+15;
struct node
{
	int key,sz,num;//key离散后的点,sz大小,num维护的单点区间长
	node *f;
	node *ch[2]; 
	node(){}
	node (int a,int b)
	{
		key=a;
		sz=num=b;
	}
	void rs()
	{
		sz=ch[0]->sz+ch[1]->sz+num;
	}
}S[maxn<<2],*pos[maxn],*root,*null;
int cnt,ncnt,tot;
int det[maxn];
int s[maxn],e[maxn];
struct Ans
{
	int k,x;
}ans[maxn];
int n,q;
void clear()
{
	null=new node(-1,0);
	root=null;
	ncnt=0;
}
node *newnode(int x)
{
	node *u=&S[++ncnt];
	u->key=x;
	pos[x]=u;
	u->sz=u->num=e[x]-s[x]+1;
	u->f=u->ch[0]=u->ch[1]=null;
	return u;
}
void rotate(node *u)
{
	node *f=u->f;
	if(f==null)return ;
	node *ff=f->f;
	int d=u==f->ch[1];
	int dd=0;
	if(ff!=null)dd=f==ff->ch[1];
	
	if(u->ch[d^1]!=null)u->ch[d^1]->f=f;
	f->ch[d]=u->ch[d^1];
	
	u->ch[d^1]=f;
	f->f=u;
	
	if(ff!=null)ff->ch[dd]=u;
	u->f=ff;
	f->rs();
	u->rs();
}
void splay(node *u,node *p)
{
	while(u->f!=p)
	{
		node *f=u->f;
		node *ff=f->f;
		if(ff==p)
		{
			rotate(u);
			break;
		}
		int d=u==f->ch[1];
		int dd=f==ff->ch[1];
		if(d==dd)rotate(f);
		else rotate(u);
		rotate(u);
	}
	if(p==null)root=u;
	u->rs();
}

int bin(int x)
{
	int l=1,r=tot;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(s[mid]>x)r=mid-1;
		else if(e[mid]<x)l=mid+1;
		else return mid;
	}
	return l;
}
void select(int k)//查询第k个的位置并将其移到根 
{
	node *u=root;
	while(1)
	{
		if(k<=u->ch[0]->sz+u->num&&k>u->ch[0]->sz)break;
		if(k<=u->ch[0]->sz)u=u->ch[0];
		else
		{
			k-=u->ch[0]->sz+u->num;
			u=u->ch[1];
		}
	}
	splay(u,null);
}
void Del()
{
	node *u=root;
	if(root->ch[1]!=null)
	{
		root=root->ch[1];
		select(1);
		if(u->ch[0]!=null)u->ch[0]->f=root;
		root->ch[0]=u->ch[0];
	}
	else 
	{
		root=root->ch[0];
	}
	root->f=null;
	if(root==null)return ;
	root->rs();
}
void find(int x)
{
	node *u=pos[x];
	splay(u,null);
}
void rin(node *&u,node *&fa,int key)
{
	if(u==null)
	{
		u=newnode(key);
		u->f=fa;
		return ;
	}
	rin(u->ch[0],u,key);
	u->rs();
}
void insert(node *&u,node *&fa,int key)
{
	if(u==null)
	{
		u=newnode(key);
		u->f=fa;
		splay(u,null);
		return ;
	}
	insert(u->ch[1],u,key);
	u->rs();
}void dfs(node *u)
{
	if(u->ch[0]!=null)dfs(u->ch[0]);
	cout<<u->key<<"  "<<u->sz<<"  "<<u->num<<endl;
	if(u->ch[1]!=null)dfs(u->ch[1]);
}
void Top(int x)
{
	int k=bin(x);//k是第几个点 
	find(k);
	Del();
	rin(root,null,k);
	find(k);
}
int getrank(int x)//第几个的排名 
{
	find(bin(x));
	return root->ch[0]->sz+1;
}
int getKth(int x)//排名第x是谁 
{
	select(x);//查询排名第几所在的点(区间) 
	return s[root->key]+(x-root->ch[0]->sz)-1;
}
char ss[20];

int main()
{
	int T;
	scanf("%d",&T);
	int cas=0;
	while(T--)
	{
		printf("Case %d:\n",++cas);
		scanf("%d%d",&n,&q);
		cnt=0;
		clear();
		det[++cnt]=0;
		for(int i=1;i<=q;i++)
		{
			scanf("%s%d",ss,&ans[i].x);
			if(ss[0]=='T')ans[i].k=1;
			if(ss[0]=='Q')ans[i].k=2;
			if(ss[0]=='R')ans[i].k=3;
			if(ans[i].k!=3)det[++cnt]=ans[i].x;
		}
		det[++cnt]=n;
		sort(det+1,det+cnt+1);
		int m=unique(det+1,det+cnt+1)-det-1;
		tot=0;
		for(int i=2;i<=m;i++)
		{
			if(det[i]-det[i-1]>1)
			{
				tot++;
				s[tot]=det[i-1]+1;
				e[tot]=det[i]-1;
			}
			tot++;
			s[tot]=e[tot]=det[i];
		}
		for(int i=1;i<=tot;i++)insert(root,null,i);
		for(int i=1;i<=q;i++)
		{
			if(ans[i].k==1)
			{
				Top(ans[i].x);
			}
			else if(ans[i].k==2)
			{
				printf("%d\n",getrank(ans[i].x));
			}
			else 
			{
				printf("%d\n",getKth(ans[i].x));
			}
		}
	}
	return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值