青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

終于摘掉了不會平衡樹的帽子

Posted on 2011-06-18 21:21 Mato_No1 閱讀(1089) 評論(0)  編輯 收藏 引用 所屬分類: 平衡樹 、NOI
今天真是有紀念意義啊……
以前試著捉了N遍的cashier今天竟然AC了,本沙茶終于掌握了平衡樹!!!

【Splay Tree及其實現】
<1>結點記錄的信息:
一般情況下Splay Tree是用線性存儲器(結構數組)來存儲的,可以避免在Linux下的指針異常問題。
這樣對于某個結點,至少要記錄以下的域:值(又叫關鍵字)、左子結點的下標、右子結點的下標、父結點下標、子樹大小(就是以這個結點為根的子樹中結點的總數)以及左右標志(為一個bool值,表示該結點是其父結點的左子結點還是右子結點),所要記錄的其它域根據題目要求而定。另外還有一個域:重復次數mul,就是整棵樹中與這個結點值相同的結點總數(關于這個域的作用將在下一篇里面總結)。
struct node {
    
int v, c[2], p, sz, mul;
    
bool d;
} T[MAXN];
以上v為值、c[0]和c[1]表示左右子結點下標、p表示父結點下標、sz表示子樹大小、mul表示重復次數、d表示左右標志。
另外,為了防止越界,將T[0]預留出來作為哨兵結點。在樹中,根結點的p值和葉結點的c值均為0。這個T[0]的sz值必須是0,其余的域無意義。
<2>旋轉操作和伸展操作:
右旋(ZIG):如果某個非根結點X是其父結點Y的左子結點,則可以通過右旋操作將X旋轉到Y的位置,即:先將Y的左子結點設為X的右子結點,再將X的右子結點設為Y;
左旋(ZAG):如果某個非根結點X是其父結點Y的右子結點,則可以通過左旋操作將X旋轉到Y的位置,即:先將Y的右子結點設為X的左子結點,再將X的左子結點設為Y;
ZIG和ZAG操作可以合并,稱為rot,代碼如下:
void rot(int x)
{
    
int y = T[x].p, d = T[x].d;
    
if (y == root) {root = x; T[root].p = 0;} else sc(T[y].p, x, T[y].d);
    sc(y, T[x].c[
!d], d); sc(x, y, !d); upd(y);
}
其中的sc是set child的縮寫,sc(_p, _c, _d)表示將T[_p]的_d子結點(0:左;1:右。下同)置為_c,代碼如下(_c也可以是0,表示刪除T[_p]對應的子結點):
void sc(int _p, int _c, bool _d)
{
    T[_p].c[_d] 
= _c; T[_c].p = _p; T[_c].d = _d;
}
其中的upd是update的縮寫,upd(x)表示當x的子結點改變時,更新x的一些可維護域(這里只有sz值,有的題目比如NOI2005 sequence里面有其它的可維護域):
void upd(int x)
{
    T[x].sz 
= T[T[x].c[0]].sz + T[T[x].c[1]].sz + T[x].mul;
}

然后就是Splay Tree的核心操作——伸展操作(Splay):
Splay(x, r)表示將x伸展到r的子結點處,若r=0,則表示伸展到根(因為根的父結點為T[0])。過程如下:
(1)設x的父結點為p。若p的父結點即是r,則rot(x);
(2)若p的父結點不是r且T[x].d=T[p].d,則先rot(p)再rot(x);
(3)若p的父結點不是r且T[x].d!=T[p].d,則兩次rot(x);
(4)重復以上過程直到x的父結點為r;
void splay(int x, int r)
{
    
int p; while ((p = T[x].p) != r) if (T[p].p == r) rot(x); else if (T[x].d == T[p].d) {rot(p); rot(x);} else {rot(x); rot(x);} upd(x);
}
這里有一個問題:為什么在旋轉操作中只更新y不更新x,而在伸展操作的最后則要更新x?這個在JZP神犇的論文中有解釋:因為在旋轉過程中,x的子結點一直在改變,故過早地跟新x沒有意義。
<3>查找操作:
下面開始進入正式操作了。
首先是查找。find(x)表示在樹中找值為x的結點,若找到返回其下標,若找不到返回0。這個應該是很容易實現的。
int find(int x)
{
    
int i = x, v0; while (i) {v0 = T[i].v; if (v0 == x) breakelse i = T[i].c[v0 > x];} return i;
}
<4>插入操作:
ins(_v)表示在樹中插入一個值為_v的結點。由于樹是否為空的問題以及mul的引入,插入操作有三種可能結果:
(1)樹為空(根結點為0):此時將插入一個新的結點,值為_v,初始sz、mul值均為1,并將其作為根結點;
(2)樹非空且值為_v的結點在樹中不存在:此時將插入一個新的結點,值為_v,初始sz、mul值均為1;
(3)樹非空且值為_v的結點在樹中已存在:此時會將樹中這個值為_v的結點的mul值加1;
void ins(int _v)
{
    
if (!root) {T[++n].v = _v; T[n].c[0= T[n].c[1= T[n].p = 0; T[n].sz = T[n].mul = 1; root = n; return;}
    
int i = root, j;
    
while (1) {
        T[i].sz
++;
        
if (T[i].v == _v) {T[i].mul++; splay(i, 0); return;}
        j 
= T[i].c[_v > T[i].v];
        
if (!j) breakelse i = j;
    }
    T[
++n].v = _v; T[n].c[0= T[n].c[1= 0; T[n].sz = T[n].mul = 1; sc(i, n, _v > T[i].v); splay(n, 0);
}
<5>刪除操作:
del(x)表示將下標為x的結點刪除。
除了一般的二叉查找樹的刪除方法外,Splay Tree還有一種刪除方式:先找到x的前趨P和x的后繼S(具體操作見<6>),并將P伸展到根,S伸展到P的右子結點處,這樣S的左子樹中只有一個結點,就是x,然后再將S的左子結點置為0即可。需要注意的是幾種特殊情況:
(1)x無前趨或無后繼:此時將x伸展到根后,x只有一棵子樹,直接將根結點設為x的那個子結點即可;
(2)x既無前趨也無后繼:此時x就是樹中的唯一一個結點,將根結點設為0即可;
(3)T[x].mul>1,直接將T[x].mul值減1(與插入類似);
void del(int x)
{
    
if (T[x].mul > 1) T[x].mul--else {
        splay(x, 
0);
        
int y = succ(), y2 = pred();
        
if (!y) {root = T[x].c[0]; T[root].p = 0;} else if (!y2) {root = T[x].c[1]; T[root].p = 0;} else {
            splay(y2, 
0); splay(y, root);
            T[x].p 
= 0; T[y].c[T[x].d] = 0; upd(y); upd(root);
        }
    }
}
<6>找前趨和后繼:
pred(x)和succ(x):分別求出x的前趨和后繼(x的前趨表示樹中值小于x的最大的結點;x的后繼表示樹中值大于x的最小的結點),并返回它們的下標,若不存在返回0。
先將x伸展到根,然后x的左子結點的右鏈上的最后一個結點就是x的前趨,x的右子結點的左鏈上的最后一個結點就是x的后繼。
int pred()
{
    
int i = T[root].c[0], j;
    
if (!i) return 0;
    
while (j = T[i].c[1]) i = j;
    
return i;
}
int succ()
{
    
int i = T[root].c[1], j;
    
if (!i) return 0;
    
while (j = T[i].c[0]) i = j;
    
return i;
}
注意,這里的pred和succ是求根結點的前趨和后繼,因此在調用pred或succ前必須保證結點x已經被伸展到了根的位置。
前趨和后繼還有另一種定義方式:x的前趨表示樹中值
不大于x的最大的結點,x的后繼表示樹中值不小于x的最小的結點,此時只需在pred和succ函數的開頭分別加入if (T[root].mul > 1) return root;即可。
<7>找第K小以及找指定結點是第幾?。?br />找第K小以及找指定結點是第幾小的操作是平衡樹的特有操作。Splay Tree找第K小的操作與其它平衡樹相同。
int Find_Kth(int K)
{
    
int i = root, s0, m0;
    
while (1) {
        s0 
= T[T[i].c[0]].sz; m0 = T[i].mul;
        
if (K <= s0) i = T[i].c[0]; else if (K <= s0 + m0) return T[i].v; else {K -= s0 + m0; i = T[i].c[1];}
    }
}
而Splay Tree找指定結點是第幾小的操作則是特有的:將這個結點伸展到根,則其(左子樹大小+1)即為結果。
int rank(int x)
{
    splay(x, 
0); return T[T[x].c[0]].sz + 1;
}

【例題】NOI2004 cashier
本題中涉及到一個進階操作:刪除值在某一區間內的所有結點。這一操作會在下一篇里總結。
#include <iostream>
#include 
<stdio.h>
using namespace std;
#define re(i, n) for (int i=0; i<n; i++)
const int MAXN = 100001, INF = ~0U >> 2;
struct node {
    
int v, c[2], p, sz, mul;
    
bool d;
} T[MAXN];
int n = 0, root, res, tot = 0;
void upd(int x)
{
    T[x].sz 
= T[T[x].c[0]].sz + T[T[x].c[1]].sz + T[x].mul;
}
void sc(int _p, int _c, bool _d)
{
    T[_p].c[_d] 
= _c; T[_c].p = _p; T[_c].d = _d;
}
void rot(int x)
{
    
int y = T[x].p, d = T[x].d;
    
if (y == root) {root = x; T[root].p = 0;} else sc(T[y].p, x, T[y].d);
    sc(y, T[x].c[
!d], d); sc(x, y, !d); upd(y); upd(x);
}
void splay(int x, int r)
{
    
int i = x, p, p0;
    
while ((p0 = T[i].p) != r) {
        p 
= T[p0].p;
        
if (p == r) rot(i); else if (T[i].d == T[p0].d) {rot(p0); rot(i);} else {rot(i); rot(i);}
    }
}
void ins(int _v)
{
    
if (!root) {T[++n].v = _v; T[n].c[0= T[n].c[1= T[n].p = 0; T[n].sz = T[n].mul = 1; root = n; return;}
    
int i = root, j;
    
while (1) {
        T[i].sz
++;
        
if (T[i].v == _v) {T[i].mul++; splay(i, 0); return;}
        j 
= T[i].c[_v > T[i].v];
        
if (!j) breakelse i = j;
    }
    T[
++n].v = _v; T[n].c[0= T[n].c[1= 0; T[n].sz = T[n].mul = 1; sc(i, n, _v > T[i].v); splay(n, 0);
}
void del(int lmt)
{
    
if (!root) return;
    
int i = root,  _min = INF, b = 0, v0;
    
while (i) {
        v0 
= T[i].v;
        
if (v0 == lmt) {b = i; break;}
        
if (v0 < lmt) i = T[i].c[1]; else {if (v0 < _min) {_min = v0; b = i;} i = T[i].c[0];}
    }
    
if (!b) {tot += T[root].sz; root = 0;} else {splay(b, 0); tot += T[T[root].c[0]].sz; T[T[root].c[0]].p = 0; T[root].c[0= 0; upd(root);}
}
int Find_Kth(int K)
{
    
int i = root, s0, m0;
    
while (1) {
        s0 
= T[T[i].c[0]].sz; m0 = T[i].mul;
        
if (K <= s0) i = T[i].c[0]; else if (K <= s0 + m0) return T[i].v; else {K -= s0 + m0; i = T[i].c[1];}
    }
}
int main()
{
    freopen(
"cashier.in""r", stdin);
    freopen(
"cashier.out""w", stdout);
    
int m, minv, delt = 0, x;
    
char ch;
    scanf(
"%d%d%*c"&m, &minv);
    re(i, m) {
        scanf(
"%c%d%*c"&ch, &x);
        
switch(ch) {
            
case 'I': {if (x >= minv) ins(x - delt); break;}
            
case 'A': {delt += x; break;}
            
case 'S': {delt -= x; del(minv - delt); break;}
            
case 'F'if (T[root].sz - x + 1 <= 0) printf("%d\n"-1); else printf("%d\n", Find_Kth(T[root].sz - x + 1+ delt);
        }
    }
    printf(
"%d\n", tot);
    fclose(stdin); fclose(stdout);
    
return 0;
}

【相關論文】
(1)The Magical Splay
(2)運用伸展樹解決數列維護問題
【感謝】
CLJ 神犇
GYZ 神犇
Jollwish 神犇
以及網上提供cashier標程的
etc.
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一区二区三区波多野结衣在线观看| 欧美性一区二区| 午夜视频一区| 亚洲人成人一区二区三区| 国产美女精品视频| 亚洲在线电影| 午夜免费电影一区在线观看 | 激情视频一区二区| 欧美体内she精视频在线观看| 亚洲国产精品久久久久久女王| 欧美区一区二| 欧美日韩在线免费| 欧美日韩激情小视频| 欧美视频你懂的| 国产精品国产三级国产普通话蜜臀 | 亚洲美女精品一区| 亚洲黄一区二区| a4yy欧美一区二区三区| 日韩视频中文| 久久精品国产v日韩v亚洲 | 国产精品日韩在线播放| 亚洲视频精品| 91久久黄色| 99亚洲一区二区| 欧美一区二区三区精品| 久久免费视频网站| 欧美电影免费观看大全| 国产精品视频你懂的| 一区久久精品| 亚洲免费在线视频| 裸体女人亚洲精品一区| 一区二区三区www| 久久美女性网| 国产精品美女一区二区| 日韩视频在线免费观看| 欧美视频中文字幕在线| 激情五月婷婷综合| 亚洲男人的天堂在线| 亚洲成色999久久网站| 欧美一级片在线播放| 欧美精品一区在线观看| 1000部国产精品成人观看| 欧美一区二区三区在线| 日韩视频精品| 欧美视频二区36p| 日韩午夜在线电影| 99国产精品国产精品久久| 亚洲男人第一网站| 亚洲人成人99网站| 欧美电影电视剧在线观看| 亚洲高清网站| 欧美激情小视频| 欧美激情中文不卡| 亚洲一区日韩在线| 一区二区三区高清| 国产免费观看久久| 嫩模写真一区二区三区三州| 久久综合色综合88| 一区二区三区欧美激情| 亚洲素人一区二区| 一区二区自拍| 一本色道久久综合亚洲二区三区| 国产精品美女在线观看| 久久亚洲综合| 欧美午夜视频一区二区| 久久九九国产| 欧美色偷偷大香| 蜜臀av在线播放一区二区三区| 欧美成人精品激情在线观看| 午夜精品999| 欧美成人免费观看| 欧美肥婆在线| 亚洲福利视频专区| 亚洲国产精品视频一区| 国产精品国产三级国产专播精品人| 欧美一区二区在线播放| 欧美丰满少妇xxxbbb| 久久久久一区二区三区| 欧美三级精品| 一区二区三区视频在线播放| 亚洲欧洲日本国产| 久久中文字幕导航| 久久久久在线| 又紧又大又爽精品一区二区| 久久成人久久爱| 久久在线观看视频| 国产一区二区三区在线观看免费| 中文在线资源观看视频网站免费不卡| 亚洲欧洲综合另类| 欧美日韩mv| 亚洲午夜av电影| 久久久www成人免费无遮挡大片| 国产精品成人一区| 亚洲欧美一区二区视频| 久久婷婷蜜乳一本欲蜜臀| 怡红院精品视频| 欧美激情亚洲另类| 99精品99| 蜜臀91精品一区二区三区| 国内精品久久久久影院 日本资源 国内精品久久久久伊人av | 伊人久久婷婷| 欧美精品手机在线| 亚洲网在线观看| 久热精品在线| 牛夜精品久久久久久久99黑人| 欧美 日韩 国产一区二区在线视频| 亚洲高清免费| 国产精品久久久久9999| 噜噜噜噜噜久久久久久91| 91久久在线播放| 久久福利影视| 亚洲一区二区精品| 精品1区2区| 国产精品老女人精品视频| 欧美激情在线狂野欧美精品| 欧美有码视频| 亚洲婷婷综合久久一本伊一区| 美日韩精品视频| 久久国产欧美日韩精品| 亚洲欧美日韩在线一区| 国产精品一区二区在线观看网站| 久久久久久夜精品精品免费| 午夜国产精品影院在线观看| 夜夜嗨av一区二区三区免费区| 欧美午夜精品久久久久久久| 亚洲第一成人在线| 欧美va亚洲va国产综合| 久久久久国产精品www| 久久av红桃一区二区小说| 一区二区三区欧美成人| 亚洲午夜成aⅴ人片| 91久久精品一区二区别| 亚洲国产裸拍裸体视频在线观看乱了中文 | 亚洲区中文字幕| 久久视频一区二区| 狼人社综合社区| 欧美精品国产精品| 欧美视频一区在线| 国产欧美一区二区精品秋霞影院| 国产精品日韩一区二区三区| 黑人一区二区三区四区五区| 伊人久久亚洲美女图片| 亚洲精品久久久久中文字幕欢迎你| 亚洲三级免费电影| 欧美中文字幕在线视频| 久久一区视频| 久久九九电影| 日韩亚洲在线| 亚洲影院色在线观看免费| 亚洲欧美视频在线观看| 午夜精品久久久久久久| 欧美高清在线观看| 久久爱www.| 久久久欧美一区二区| 欧美日韩综合另类| 在线视频国内自拍亚洲视频| 亚洲欧美国产77777| 亚洲国产另类久久精品| 久久久久久9999| 国产欧美丝祙| 欧美一级专区| 亚洲视频播放| 国产精品一区二区久久| 亚洲小视频在线观看| 亚洲国产成人在线| 久久亚洲精品伦理| 日韩亚洲在线观看| 国产精品揄拍一区二区| 久久精品国产亚洲一区二区| 午夜亚洲精品| 黑人巨大精品欧美一区二区| 噜噜噜久久亚洲精品国产品小说| 欧美一区免费视频| 亚洲大片免费看| 亚洲高清久久| 欧美日韩精品免费观看视一区二区| 亚洲精选在线| 小嫩嫩精品导航| 在线精品国精品国产尤物884a| 欧美激情一区二区三区全黄| 久久精品综合一区| 亚洲私拍自拍| 欧美一区二区在线播放| 亚洲高清不卡在线| 亚洲一区二区三区四区中文| 在线观看一区视频| 一区二区三区久久| 在线观看亚洲精品视频| 一区二区日韩免费看| 卡一卡二国产精品| 久久riav二区三区| 欧美日韩人人澡狠狠躁视频| 久久精品久久99精品久久| 欧美日韩综合视频| 亚洲国产精品www| 精品99一区二区| 欧美亚洲视频一区二区| 午夜精品久久久| 国产精品欧美日韩| 亚洲伊人伊色伊影伊综合网|