1. 程式人生 > >[轉]Splay算法

[轉]Splay算法

多少 def 返回 大小 while nod %s ltr 廣泛

首先聲明,本教程的對象是完全沒有接觸過splay的OIer,大牛請右上角。

先看一道題目:

skydec有n個數,每次他都會把一些數放進一些盒子裏,由於skydec太傻×,所以他不能判斷數的大小,現在他請求你幫他求盒子裏的第K小數

輸入:一個數n表示數的個數,一個數m表示操作的個數 (n<=m<=100000)

操作由2部分組成,簡稱為a和b,如果a=0,則表示將b放進盒子裏,如果a=1,則表示詢問盒子裏的第b小數

輸出:對於每次詢問輸出答案

由於是臨時造的題目,就不造樣例了,引用一句RZZ大神的話:題是死的,人是活的,出題人是懶的。

如果是普通的查詢的話,只要排序一遍直接輸出data[b]。但是這是動態的

這裏就要引進二叉搜索樹了,二叉搜索樹我就不介紹了,這都不知道的話也沒必要來讀這篇教程了= =。對於一個節點,我們可以從根開始,一直左右子樹搜尋,直到搜到一個排名為k的數,具體實現:find(node,k)表示在以node為根的樹中查找第k小的樹。

當k=size[leftson[node]]+1時,顯然根結點就是答案

然後分類討論,當k<size[leftson[node]]+1時,往左子樹搜尋,即返回find(leftson[node],k)

當k>size[leftson[node]]時,往右子樹搜尋,即返回find(rightson[node],k-1-size[leftson[node]])

至於插入過程,我就不說了,太簡單了

long find(long x,long k)
{
long leftsize=size[leftson[x]];
if(k==leftsize+1)return x;
if(k<leftsize+1) return find(leftson[x],k);
else return find(rightson[x],k-leftsize-1);
}

如果是隨機數據的話,這題隨便無壓力過,但是skydec開始喪心病狂了,他插入的數是遞減的,這樣的話這種方法就會導致時間復雜度O(n^2)
那麽現在就要引進傳說中的splay樹了!

splay樹是通過不斷的旋轉來維持logn的平均復雜度的,他不用像AVL樹嚴格平衡,也不用記錄高度等平衡信息,是十分靈活的,唯一的缺點就是常數比其他平衡樹要大一點

但是我初學時一直有一個疑問:splay是如何維持logn的復雜度的呢

由於splay是不斷翻轉的,所以就算某一時刻他成了一條鏈,也會馬上變成另外的形態,通過這樣不斷地變換可以防止長期停留在鏈的狀態

打個比方,如果你有100元,你可以在某個彩票上孤註一擲,也可以買50張彩票,由於孤註一擲可能會跪掉,所以肯定是買50張好,因為就算跪了,也不會賠太多錢

然後平均時間就是logn了

現在引入旋轉這個概念

其中,我們是以X結點為主角的

現在來解釋一下這一幅圖

解釋之前,肯定有人要問了:這旋轉有個鳥用。其實這次旋轉成功地將結點X上提了一步,其實splay的最終目標就是把某個結點提到他的某個祖先的下面。

右旋其實只需要三步:

1.將X的右子樹B(如果有的話)作為Y的左子樹,同時讓B認Y作爹

2.設Z為原本Y結點的父親,讓X認Z做爹(如果Z存在的話),將X作為Z的兒子(是左是右得由Y是Z的左兒子還是右兒子決定,要左右一致)

3.將Y作為X的右子樹,同時讓Y認X作爹

下面貼個代碼吧,希望看得懂

void right_rotate(long x)
{
long y=father[x];long z=father[y];
leftson[y]=rightson[x];
if(rightson[x]!=0)father[rightson[x]]=y;
//對圖中B子樹,即X的右子樹的處理
father[x]=z;
if(z!=0)
{
if(leftson[z]==y)leftson[z]=x;else rightson[z]=x;
}
rightson[x]=y;father[y]=x;
}

左旋同理,為了節省時間,就不多解釋了,直接貼代碼吧

void left_rotate(long x)
{
long y=father[x];long z=father[y];
rightson[y]=leftson[x];
if(leftson[x]!=0)father[leftson[x]]=y;
father[x]=z;
if(z!=0)
{
if(leftson[z]==y)leftson[z]=x;else rightson[z]=x;
}
leftson[x]=y;father[y]=x;
}

下面來一個合集
O(∩_∩)O~請允許我寫一個裝13的代碼

void rotate(long x,int kind)
//kind=0表示左旋,kind=1表示右旋 ch[X][0..1]表示X的左兒子和右兒子
{
long y=father[x];long z=father[y];
ch[y][!kind]=ch[x][kind];if(!ch[x][kind])father[ch[x][kind]]=y;
father[x]=z;if(!z)ch[z][ch[z][1]==y]=x;
father[y]=x;ch[x][kind]=y;
}
那麽現在,最基礎的左旋右旋解決了
開始splay樹最最最最最核心的操作----------splay

splay(x,Ancestry)表示將x不停向上旋轉,知道X成為結點Ancestry的子樹 (Ancestry意為祖先,裝13專用)

一般應用比較廣泛的是將X結點Splay到根節點

下面放一下偽代碼

void splay(long x,long Ancestry)
{
while(father[x]!=Ancestry)
{
各種旋轉(~ o ~)~zZ
}
}

眾人:(啪!)你特麽不是寫了跟沒寫一樣麽
好吧,下面開始補全各種旋轉

旋轉分為2種情況,其中兩種情況又總共分為6種情況

第一種情況,Ancestry是X的父親的父親(可以稱作爺爺了。。)

然後又分為兩種情況:

1.

其中有些結點我懶得話了,比如X的右兄弟

這種情況下,只需要執行一遍 right_rotate(x)即可

2.

這種情況下,只需要執行一遍left_rotate(x)

//-------------------------------------------------------------------------------------------------------------------------------//

以上兩種都是Ancestry是X的爺爺的情況

下面兩種是Ancestry還不是X的爺爺的情況

3.

當leftson[z]=y且rightson[y]=x時,只需要執行兩部:left_rotate(x), right_rotate(x)

4.

當rightson[z]=y且leftson[y]=x時,只需要:right_rotate(x),left_rotate(x)即可

五六兩種情況合起來討論:

當形成類似一條線時

左邊的情況:right_rotate(y),right_rotate(x);

右邊的情況:left_rotate(y);left_rotate(x);

至於為什麽要先旋轉Y結點,我也不知道

那麽到此為止,6種旋轉都介紹完了,下面貼一波代碼

void splay(long x,long Ancestry)
{
while(father[x]!=Ancestry)
{
long y=father[x];long z=father[y];
if(z==Ancestry)
{
if(rightson[y]==x)left_rotate(x);else right_rotate(x);
}
else
{
if(rightson[z]==y&&rightson[y]==x){left_rotate(y);left_rotate(x);}
else if(rightson[z]==y&&leftson[y]==x) {right_rotate(x);left_rotate(x);}
else if(leftson[z]==y&&leftson[y]==x) {right_rotate(y);right_rotate(x);}
else {left_rotate(x);right_rotate(x);}
}
}
if(Ancestry==0)root=x;
}

然後下面來一個裝13版
void splay(long x,long Ancestry)
{
while(father[x]!=Ancestry)
{
long y=father[x];long z=father[y];
if(z==Ancestry)rotate(x,ch[y][0]==x);
else
{
if(ch[z][0]==y)
{
if(ch[y][0]==x)rotate(y,1),rotate(x,1);
else rotate(x,0),rotate(x,1);
}
else
{
if(ch[y][1]==x)rotate(y,0),rotate(x,0);
else rotate(x,1),rotate(x,0);
}
}
}
if(Ancestry==0)root=x;
}

1588: [HNOI2002]營業額統計

Time Limit: 5 Sec Memory Limit: 162 MB
Submit: 6923 Solved: 2286
[Submit][Status]
Description

營業額統計 Tiger最近被公司升任為營業部經理,他上任後接受公司交給的第一項任務便是統計並分析公司成立以來的營業情況。 Tiger拿出了公司的賬本,賬本上記錄了公司成立以來每天的營業額。分析營業情況是一項相當復雜的工作。由於節假日,大減價或者是其他情況的時候,營業額會出現一定的波動,當然一定的波動是能夠接受的,但是在某些時候營業額突變得很高或是很低,這就證明公司此時的經營狀況出現了問題。經濟管理學上定義了一種最小波動值來衡量這種情況: 該天的最小波動值 當最小波動值越大時,就說明營業情況越不穩定。 而分析整個公司的從成立到現在營業情況是否穩定,只需要把每一天的最小波動值加起來就可以了。你的任務就是編寫一個程序幫助Tiger來計算這一個值。 第一天的最小波動值為第一天的營業額。 ? 輸入輸出要求
Input

第一行為正整數 ,表示該公司從成立一直到現在的天數,接下來的n行每行有一個正整數 ,表示第i天公司的營業額。
Output

輸出文件僅有一個正整數,即Sigma(每天最小的波動值) 。結果小於2^31 。
Sample Input

6
5
1
2
5
4
6
Sample Output

12
HINT

結果說明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12

Source

這題除了數據坑爹以外,其他還可以 {數據有殘缺,殘缺的地方用0補上就行了}
請讀者思考5分鐘後再往下看

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

請思考~

好了五分鐘到了!
下面分析一下題目

題目大概意思:每次插入一個數,在數列中找到一個數,使得該數與插入的數的差值最小

具體做法:我們可以按照大小建立一顆伸展樹,然後每插入一個數,尋找他的前驅的後繼

前驅尋找方法:尋找該結點左子樹中最大的數,一顆樹中最大的結點就是這顆樹最右邊的結點

long findpre(long x)
{
long left=leftson[x];
while(rightson[left]!=0)left=rightson[left];
return left;
}

後繼也同理,尋找該結點右子樹中最小的數
long findsuc(long x)
{
long right=rightson[x];
while(leftson[right]!=0)right=leftson[right];
return right;
}

最後比較一下前驅和後繼即可
下面貼個完整代碼

#include<stdio.h>
using namespace std;
struct node{long father,left,right,data;}tree[100008];
long time=0,n,root,sum=0;bool flag;
long MINS(long a,long b)
{
if(a<b)return a;else return b;
}
long abs(long a)
{
if(a<0)return -a;else return a;
}
void rightrotate(long x)
{
long y=tree[x].father;long z=tree[y].father;
tree[y].left=tree[x].right;
if(tree[x].right!=-1){tree[tree[x].right].father=y;}
tree[x].father=z;
if(z!=-1){if(tree[z].left==y)tree[z].left=x;else tree[z].right=x;}
tree[x].right=y;tree[y].father=x;
}
void leftrotate(long x)
{
long y=tree[x].father;long z=tree[y].father;
tree[y].right=tree[x].left;
if(tree[x].left!=-1){tree[tree[x].left].father=y;}
tree[x].father=z;
if(z!=-1){if(tree[z].left==y)tree[z].left=x;else tree[z].right=x;}
tree[x].left=y;tree[y].father=x;
}
void splay(long x)
{
while(tree[x].father!=-1)
{
long y=tree[x].father;long z=tree[y].father;
if(z==-1){
if(tree[y].left==x)rightrotate(x);else leftrotate(x);
}
else
{
if(tree[z].left==y&&tree[y].left==x){rightrotate(y);rightrotate(x);}
else if(tree[z].left==y&&tree[y].right==x){leftrotate(x);rightrotate(x);}
else if(tree[z].right==y&&tree[y].right==x){leftrotate(y);leftrotate(x);}
else {rightrotate(x);leftrotate(x);}
}}
root=x;
}
long qq(long x)
{
long y=tree[x].left;if(y==-1)return y;
while(tree[y].right!=-1)y=tree[y].right;
return y;
}
long long hj(long x)
{
long y=tree[x].right;if(y==-1)return y;
while(tree[y].left!=-1)y=tree[y].left;
return y;
}
int BST_insert(long dat,long x)
{
if(dat==tree[x].data){flag=false;splay(x);return 0;}
if(dat<tree[x].data)
{
if(tree[x].left==-1){tree[x].left=time;tree[time].father=x;tree[time].left=tree[time].right=-1;tree[time].data=dat;}
else BST_insert(dat,tree[x].left);
}
else
{
if(tree[x].right==-1){tree[x].right=time;tree[time].father=x;tree[time].left=tree[time].right=-1;tree[time].data=dat;}
else BST_insert(dat,tree[x].right);
}
}
int insert(long dat)
{
flag=true;time++;
BST_insert(dat,root);
if(flag==false) return 0;
splay(time);
long q=qq(time);long h=hj(time);
long min=2000000000;
if(q!=-1)min=MINS(min,abs(tree[q].data-dat));
if(h!=-1)min=MINS(min,abs(tree[h].data-dat));
sum+=min;
}

int main()
{
//freopen("test.in","r",stdin);freopen("testWA.out","w",stdout);
long n,a1;
while(scanf("%d",&n)!=-1){
sum=0;time=0;
scanf("%ld",&a1);
sum=a1;time++;tree[time].father=-1;
tree[time].left=tree[time].right=-1;
tree[time].data=a1;root=time;
for(long i=1;i<=n-1;i++)
{
long dats=0;scanf("%ld",&dats);insert(dats);
}
printf("%ld\n",sum);}
return 0;
}

剛開始被數據坑了好多次,輸入前變量置為0即可,這樣即使讀入失敗還是0,不然跳出一堆2816947219840912
1208: [HNOI2004]寵物收養所

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 3501 Solved: 1327
[Submit][Status]
Description

最近,阿Q開了一間寵物收養所。收養所提供兩種服務:收養被主人遺棄的寵物和讓新的主人領養這些寵物。每個領養者都希望領養到自己滿意的寵物,阿Q根據領養者的要求通過他自己發明的一個特殊的公式,得出該領養者希望領養的寵物的特點值a(a是一個正整數,a<2^31),而他也給每個處在收養所的寵物一個特點值。這樣他就能夠很方便的處理整個領養寵物的過程了,寵物收養所總是會有兩種情況發生:被遺棄的寵物過多或者是想要收養寵物的人太多,而寵物太少。 1. 被遺棄的寵物過多時,假若到來一個領養者,這個領養者希望領養的寵物的特點值為a,那麽它將會領養一只目前未被領養的寵物中特點值最接近a的一只寵物。(任何兩只寵物的特點值都不可能是相同的,任何兩個領養者的希望領養寵物的特點值也不可能是一樣的)如果有兩只滿足要求的寵物,即存在兩只寵物他們的特點值分別為a-b和a+b,那麽領養者將會領養特點值為a-b的那只寵物。 2. 收養寵物的人過多,假若到來一只被收養的寵物,那麽哪個領養者能夠領養它呢?能夠領養它的領養者,是那個希望被領養寵物的特點值最接近該寵物特點值的領養者,如果該寵物的特點值為a,存在兩個領養者他們希望領養寵物的特點值分別為a-b和a+b,那麽特點值為a-b的那個領養者將成功領養該寵物。 一個領養者領養了一個特點值為a的寵物,而它本身希望領養的寵物的特點值為b,那麽這個領養者的不滿意程度為abs(a-b)。【任務描述】你得到了一年當中,領養者和被收養寵物到來收養所的情況,希望你計算所有收養了寵物的領養者的不滿意程度的總和。這一年初始時,收養所裏面既沒有寵物,也沒有領養者。

Input

第一行為一個正整數n,n<=80000,表示一年當中來到收養所的寵物和領養者的總數。接下來的n行,按到來時間的先後順序描述了一年當中來到收養所的寵物和領養者的情況。每行有兩個正整數a, b,其中a=0表示寵物,a=1表示領養者,b表示寵物的特點值或是領養者希望領養寵物的特點值。(同一時間呆在收養所中的,要麽全是寵物,要麽全是領養者,這些寵物和領養者的個數不會超過10000個)

Output

僅有一個正整數,表示一年當中所有收養了寵物的領養者的不滿意程度的總和mod 1000000以後的結果。

Sample Input

5
0 2
0 4
1 3
1 2
1 5
Sample Output

3
(abs(3-2) + abs(2-4)=3,最後一個領養者沒有寵物可以領養)
HINT

Source

Splay

這題。。跟上一題基本沒區別,也是求前驅後繼。
對於一顆樹,他多加了一個屬性:是寵物樹還是顧客樹

如果讀入一個顧客,而當前是寵物樹,設顧客為X,插入X,尋找X的前驅和後繼,選一個與X差值最小的,將他與X一起刪除,累加答案

如果當前是顧客樹,直接將該顧客插入

如果是空樹,屬性改為顧客樹,插入顧客

寵物也同理

但是這題多了一個刪除操作!

現在來講一下刪除這個操作

首先,為了方便,我們將待刪除點X提到根結點上來

然後尋找X的左子樹中的最大值,將他提到X的左兒子(即圖中的紅點)

然後X的右子樹=X的左兒子的右兒子

下面畫個圖來幫助理解

實現代碼:

void del(long x)
{
//整顆樹中就X一個節點了,變成了空樹
splay(x);if(ch[x][0]==-1&&ch[x][1]==-1){root=-1;return;}
//沒有左子樹
if(ch[x][0]==-1){root=ch[x][1];fa[ch[x][1]]=-1;return;}
//沒有右子樹
if(ch[x][1]==-1){root=ch[x][0];fa[ch[x][0]]=-1;return;}
//左右子樹俱全
long j=ch[x][0];while(ch[j][1]!=-1)j=ch[j][1];fa[ch[x][0]]=-1;splay(j);ch[j][1]=ch[x][1];fa[ch[x][1]]=j;root=j;
}
下面貼一下代碼~~~
#include<stdio.h>
#define MAXN 200000
using namespace std;
long ch[MAXN][2],fa[MAXN],data[MAXN];
long root,tot;long long sum=0;long n;
void rotate(long x,int kind)//kind=0 is leftrotate.kind=1 is rightrotate
{
long y=fa[x];long z=fa[y];
ch[y][1-kind]=ch[x][kind];if(ch[x][kind]!=-1)fa[ch[x][kind]]=y;
fa[x]=z;if(z!=-1)ch[z][ch[z][1]==y]=x;fa[y]=x;ch[x][kind]=y;
}
void splay(long x)
{
while(fa[x]!=-1)
{
long y=fa[x];long z=fa[y];
if(z==-1)rotate(x,ch[y][1]!=x);
else
{
if(ch[z][0]==y)
{
if(ch[y][0]==x){rotate(y,1);rotate(x,1);}else{rotate(x,0);rotate(x,1);}
}
else
{
if(ch[y][0]==x){rotate(x,1);rotate(x,0);}else{rotate(y,0);rotate(x,0);}
}
}}
root=x;
}
void newnode(long da,long fat)
{
tot++;data[tot]=da;fa[tot]=fat;ch[tot][0]=ch[tot][1]=-1;
}
void BST_insert(long dat)
{
if(root==-1){newnode(dat,-1);root=tot;return;}long now=root;
while(true)
{
if(dat>data[now])
{
if(ch[now][1]==-1){newnode(dat,now);ch[now][1]=tot;return;}
else now=ch[now][1];
}
else
{
if(ch[now][0]==-1){newnode(dat,now);ch[now][0]=tot;return;}
else now=ch[now][0];
}
}
}
void del(long x)
{
//整顆樹中就X一個節點了,變成了空樹
splay(x);if(ch[x][0]==-1&&ch[x][1]==-1){root=-1;return;}
//沒有左子樹
if(ch[x][0]==-1){root=ch[x][1];fa[ch[x][1]]=-1;return;}
//沒有右子樹
if(ch[x][1]==-1){root=ch[x][0];fa[ch[x][0]]=-1;return;}
//左右子樹俱全
long j=ch[x][0];while(ch[j][1]!=-1)j=ch[j][1];fa[ch[x][0]]=-1;splay(j);ch[j][1]=ch[x][1];fa[ch[x][1]]=j;root=j;
}
long prenum,pre,suc,sucnum;
void find_pre(long dat)
{
long now=root;pre=-1;prenum=2100000000;
while(true)
{
if(now==-1)return;
if(data[now]<dat&&(dat-data[now])<prenum){pre=now;prenum=dat-data[now];}
if(data[now]<dat)now=ch[now][1];else now=ch[now][0];
}
}
void find_suc(long dat)
{
long now=root;suc=-1;sucnum=2100000000;
while(true)
{
if(now==-1)return;
if(data[now]>dat&&(data[now]-dat)<sucnum){suc=now;sucnum=data[now]-dat;}
if(data[now]<dat)now=ch[now][1];else now=ch[now][0];
}
}
int main()
{
long first_data,kind;scanf("%ld",&n);scanf("%ld%ld",&kind,&first_data);tot=0;newnode(first_data,-1);root=tot;sum=0;
for(long i=2;i<=n;i++)
{
long k,dat;scanf("%ld%ld",&k,&dat);
if(root==-1)
{
kind=k;newnode(dat,-1);root=tot;}
else
{
if(k==kind){BST_insert(dat);splay(tot);}
else
{
find_pre(dat);find_suc(dat);
if(pre!=-1||suc!=-1)
{
long mins=2100000001;long num=0;
if(pre!=-1&&prenum<mins)mins=prenum,num=pre;
if(suc!=-1&&sucnum<mins)mins=sucnum,num=suc;
sum=(sum+mins)%1000000;del(num);//printf("%ld\n",mins);
}
}
}
}
printf("%lld",sum);
//for(;;);
return 0;
}

1861: [Zjoi2006]Book 書架

Time Limit: 4 Sec Memory Limit: 64 MB
Submit: 414 Solved: 251
[Submit][Status]
Description

小T有一個很大的書櫃。這個書櫃的構造有些獨特,即書櫃裏的書是從上至下堆放成一列。她用1到n的正整數給每本書都編了號。 小T在看書的時候,每次取出一本書,看完後放回書櫃然後再拿下一本。由於這些書太有吸引力了,所以她看完後常常會忘記原來是放在書櫃的什麽位置。不過小T的記憶力是非常好的,所以每次放書的時候至少能夠將那本書放在拿出來時的位置附近,比如說她拿的時候這本書上面有X本書,那麽放回去時這本書上面就只可能有X-1、X或X+1本書。 當然也有特殊情況,比如在看書的時候突然電話響了或者有朋友來訪。這時候粗心的小T會隨手把書放在書櫃裏所有書的最上面或者最下面,然後轉身離開。 久而久之,小T的書櫃裏的書的順序就會越來越亂,找到特定的編號的書就變得越來越困難。於是她想請你幫她編寫一個圖書管理程序,處理她看書時的一些操作,以及回答她的兩個提問:(1)編號為X的書在書櫃的什麽位置;(2)從上到下第i本書的編號是多少。
Input

第一行有兩個數n,m,分別表示書的個數以及命令的條數;第二行為n個正整數:第i個數表示初始時從上至下第i個位置放置的書的編號;第三行到m+2行,每行一條命令。命令有5種形式: 1. Top S——表示把編號為S的書房在最上面。 2. Bottom S——表示把編號為S的書房在最下面。 3. Insert S T——T∈{-1,0,1},若編號為S的書上面有X本書,則這條命令表示把這本書放回去後它的上面有X+T本書; 4. Ask S——詢問編號為S的書的上面目前有多少本書。 5. Query S——詢問從上面數起的第S本書的編號。
Output

對於每一條Ask或Query語句你應該輸出一行,一個數,代表詢問的答案。
Sample Input

10 10
1 3 2 7 5 8 10 4 9 6
Query 3
Top 5
Ask 6
Bottom 3
Ask 3
Top 6
Insert 4 –1
Query 5
Query 2
Ask 2

Sample Output

2
9
9
7
5
3

數據範圍
30%的數據,n,m < = 10000
100%的數據,n,m < = 80000

HINT

Source

Day2

對於這題,建立一個以順序為關鍵字的伸展樹
用一個pos[x]數組表示代表編號為X的書的結點的編號

Top S:刪除結點pos[S],將S插入到位置為1的地方

Botton S:刪除結點pos[S],將S插入到位置為N的地方

Insert S T:刪除位置為第S+1的書,然後將該書插入到S+1+T的位置

Ask S:將pos[S]splay到根節點,統計左子樹的結點數

Query S:輸出排名為S的書。

其中最後一個操作得好好講講,輸出排名為S的書,我們可以用類似於插入的方法來寫

這題由於是按順序來排的,所以還要記錄子樹的大小

關於按順序排序的求排名為K的編號的的方法,本文開頭的skydec的傻×題裏已經有類似的講解了

下面直接貼代碼吧:

/**************************************************************
Problem: 1861
User: SKYDEC
Language: C++
Result: Accepted
Time:1604 ms
Memory:8608 kb
****************************************************************/

#include<stdio.h>
#define MAXN 300000
using namespace std;
long s[MAXN],f[MAXN],ch[MAXN][2],data[MAXN],root,tot,n,m;
void updata(long x)
{
s[x]=1;if(ch[x][0]!=-1)s[x]+=s[ch[x][0]];if(ch[x][1]!=-1)s[x]+=s[ch[x][1]];
}
void rot(long x,int kind)
{
long y=f[x];long z=f[y];
ch[y][!kind]=ch[x][kind];if(ch[x][kind]!=-1)f[ch[x][kind]]=y;
f[x]=z;if(z!=-1)ch[z][ch[z][1]==y]=x;
f[y]=x;ch[x][kind]=y;updata(y);updata(x);
}
void splay(long x,long place)
{
while(f[x]!=place)
{
long y=f[x];long z=f[y];
if(z==-1||z==place)rot(x,ch[y][1]!=x);
else
{
if(ch[y][1]==x&&ch[z][1]==y)rot(y,0),rot(x,0);
else if(ch[y][1]==x&&ch[z][0]==y)rot(x,0),rot(x,1);
else if(ch[y][0]==x&&ch[z][0]==y)rot(y,1),rot(x,1);
else rot(x,1),rot(x,0);
}
}
if(place==-1)root=x;
}
void newnode(long fa,long dat){tot++;f[tot]=fa;s[tot]=1;ch[tot][0]=ch[tot][1]=-1;data[tot]=dat;}
long getsize(long x){if(x==-1)return 0;else return s[x];}
void insert(long x,long dat,long k)
{
if(k==1&&ch[x][0]==-1){newnode(x,dat);ch[x][0]=tot;return;}
if(k==2+getsize(ch[x][0])&&ch[x][1]==-1){newnode(x,dat);ch[x][1]=tot;return;}
if(k<=1+getsize(ch[x][0]))insert(ch[x][0],dat,k);
else insert(ch[x][1],dat,k-1-getsize(ch[x][0]));
updata(x);
}
void del(long x)
{
splay(x,-1);
if(ch[x][0]==-1&&ch[x][1]==-1){root=-1;return;}
if(ch[x][0]==-1){root=ch[x][1];f[ch[x][1]]=-1;return;}
if(ch[x][1]==-1){root=ch[x][0];f[ch[x][0]]=-1;return;}
long j=ch[x][0];while(ch[j][1]!=-1)j=ch[j][1];
splay(j,x);
ch[j][1]=ch[x][1];
f[ch[x][1]]=j;f[j]=-1;root=j;
updata(j);
}
long find(long k)
{
long p=k;
long a=root;
while(true)
{
//if(p==5)printf("finding:%ld %ld %ld\n",a,data[a],k);
if(k==1+getsize(ch[a][0]))return a;
if(k>1+getsize(ch[a][0]))k-=1+getsize(ch[a][0]),a=ch[a][1];
else a=ch[a][0];
}
}
long get(long x)
{
splay(x,-1);
return getsize(ch[x][0])+1;
}
void bltree(long x)
{
if(x==-1)return;
printf("bl:%ld data:%ld l:%ld r:%ld s:%ld\n",x,data[x],ch[x][0],ch[x][1],s[x]);
bltree(ch[x][0]);bltree(ch[x][1]);
}
long pos[100000];long a[100000];
int main()
{
scanf("%ld%ld",&n,&m);for(long i=1;i<=n;i++)scanf("%ld",&a[i]),pos[a[i]]=i;tot=0;root=-1;
for(long i=1;i<=n;i++)
{
if(i==1)newnode(-1,a[i]),root=tot;
else{insert(root,a[i],i);if(i%2==0)splay(tot,-1);}
}
for(long ip=1;ip<=m;ip++)
{
char p[100];long j,k,i;
scanf("%s%ld",&p,&i);
if(p[0]==‘I‘)scanf("%ld",&j);
if(p[0]==‘T‘){del(pos[i]);insert(root,i,1);pos[i]=tot;splay(tot,-1);}
if(p[0]==‘B‘){del(pos[i]);insert(root,i,n);pos[i]=tot;splay(tot,-1);}
if(p[0]==‘I‘){long u=get(pos[i]);u=u-1+j;del(pos[i]);insert(root,i,u+1);splay(tot,-1);pos[i]=tot;}
if(p[0]==‘A‘){printf("%ld\n",get(pos[i])-1);}
if(p[0]==‘Q‘){printf("%ld\n",data[find(i)]);}
//if(p[0]==‘S‘){
// for(long i=1;i<=n;i++)printf("%ld ",pos[i]);
// bltree(root);}
}
//for(;;);
return 0;
}
---------------------
作者:SKY的C
來源:CSDN
原文:https://blog.csdn.net/skydec/article/details/20151805
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

[轉]Splay算法