您的位置:首页 > 其它

HDU 3436:splay tree

2016-01-15 22:24 399 查看
给一个1~n的序列,有三种操作:

1.TOP x :把x移到序列首部

2.Query x:询问x的位置

3.Rank x:询问第k个位置的元素

总而言之就是一个序列维护的问题,用splay tree可以解决。但是首先要解决的问题就是把题目中的操作转化为splay tree的基本操作

1. Top x:可以理解为把序列中的x找到并删除,然后再插入到序列首部

2.Query x:找到x在树中的位置并输出其在序列中的位置

3.Rank x:在树中找到维护的第x个数并输出

常规来说,我们是在splay tree中一个结点维护原序列中的一个数,但是这里n有10^8,数组是开不下的,但是q只有10^5,我们可以想想如何离散化

这里是把query 和top对应的x单独维护,其余部分作为一个区间放置进一个结点,这样可以保证结点数在10^5内

为什么要这样做呢?首先对于top来说,我们是要做的是删除和插入操作,这里的操作对象只有x一个值,所以单独列出以免影响序列其他部分

再一个就是query操作,我们查询位置的话在splay tree中的操作往往就是把x旋转到树根,那左子树的大小也就对应了x的在原序列的下标。如果x不是单独列出,那这一条定律显然是不成立的

至于Rank,就相当于是在从左往右数数咯,可以一段一段数~~

#include"cstdio"
#include"queue"
#include"cmath"
#include"stack"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"queue"
#include"map"
#include"vector"
#define ll long long
#define mems(a,b) memset(a,b,sizeof(a))

using namespace std;
const int MAXN = 200500;
const int MAXE = 200500;
const int INF = 0x3f3f3f;

int pre[MAXN],ch[MAXN][2],siz[MAXN],num[MAXN],key[MAXN],ask[MAXN],p[MAXN],e[MAXN],s[MAXN];
int root,tot,tail,n,P=1,cnt,T,q;

struct Node{
char op;
int x;
}node[MAXN];

void pushup(int x){
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+num[x];
}

void init(){
tot=0;
root=0;
cnt=0;
tail=0;
siz[0]=pre[0]=ch[0][0]=ch[0][1]=0;
}

void newnode(int &pos,int fa,int k){///k是在原数列(离散化后)中的位置,pos为在splay tree中的结点编号
pos=++tot;
pre[pos]=fa;
p[k]=pos;           ///p[原序列位置]=树中位置
key[pos]=k;         ///key[树中位置]=原序列中位置
ch[pos][0]=ch[pos][1]=0;
siz[pos]=num[pos]=e[k]-s[k]+1;
}

void build(int &x,int l,int r,int fa){
if(l>r) return;
int mid=(l+r)>>1;
newnode(x,fa,mid);
build(ch[x][0],l,mid-1,x);
build(ch[x][1],mid+1,r,x);
pushup(x);
}

void Rotate(int x,int kind){
int fa=pre[x];
ch[fa][!kind]=ch[x][kind];
pre[ch[x][kind]]=fa;
if(pre[fa]) ch[pre[fa]][ch[pre[fa]][1]==fa]=x;
pre[x]=pre[fa];
ch[x][kind]=fa;
pre[fa]=x;
pushup(fa);
}

int get_pos(int w){         ///找到w所在结点在原序列中的位置
int low=0,high=cnt-1,mid;
while(low<=high){
mid=(low+high)>>1;
if(s[mid]<=w&&w<=e[mid]) return mid;
else if(w<s[mid]) high=mid-1;
else low=mid+1;
}
}

void Splay(int r,int goal){
while(pre[r]!=goal){
if(pre[pre[r]]==goal) Rotate(r,ch[pre[r]][0]==r);
else{
int fa=pre[r];
int kind=(ch[pre[fa]][0]==fa);
if(ch[fa][kind]==r){
Rotate(r,!kind);
Rotate(r,kind);
}
else{
Rotate(fa,kind);
Rotate(r,kind);
}
}
}
pushup(r);
if(!goal) root=r;
}

void Insert(int &r,int fa,int k){///写成递归式为了pushup
if(!r){
newnode(r,fa,k);
return;
}
else{
Insert(ch[r][0],r,k);
pushup(r);
}
}

int getmax(int x){
while(ch[x][1]) x=ch[x][1];
return x;
}

void del(int x){
Splay(x,0);
if(!ch[root][0]){
root=ch[root][1];
pre[root]=0;
}
else{
int t=getmax(ch[root][0]);
Splay(t,root);
ch[t][1]=ch[root][1];
pre[ch[root][1]]=t;
root=t;
pre[root]=0;
pushup(root);
}
}

int get_kth(int r,int k){
int lst=siz[ch[r][0]];
if(k<=lst) return get_kth(ch[r][0],k);
else if(k<=lst+num[r]) return s[key[r]]+(k-lst)-1;
else return get_kth(ch[r][1],k-lst-num[r]);
}

void Top(int x){
int k=get_pos(x);
del(p[k]);
Insert(root,0,k);
Splay(tot,0);       ///这是以防该节点会在后面又被用到,这样可以保证各项操作均摊复杂度在log(n)~ORZZZZZZZZ
}

void Query(int x){
int k=get_pos(x);
Splay(p[k],0);
printf("%d\n",1+siz[ch[p[k]][0]]);
}

void Rank(int x){
printf("%d\n",get_kth(root,x));
}

int main(){
//freopen("in.txt","r",stdin);
scanf("%d",&T);
while(T--){
printf("Case %d:\n",P++);
scanf("%d%d",&n,&q);
init();

ask[tail++]=0;
for(int i=0;i<q;i++){
char arr[10];
scanf("%s %d",arr,&node[i].x);
node[i].op=arr[0];
if(arr[0]=='T'||arr[0]=='Q') ask[tail++]=node[i].x;
}
ask[tail++]=n;
sort(ask,ask+tail);
for(int i=1;i<tail;i++){
if(ask[i]!=ask[i-1]){
if(ask[i]-ask[i-1]>1){
s[cnt]=ask[i-1]+1;
e[cnt++]=ask[i]-1;
}
s[cnt]=e[cnt]=ask[i];
cnt++;
}
}
build(root,0,cnt-1,0);
for(int i=0;i<q;i++){
if(node[i].op=='T') Top(node[i].x);
else if(node[i].op=='Q') Query(node[i].x);
else Rank(node[i].x);
}
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: