链接: http://pan.baidu.com/s/1i3waHBR 密码: cfy5
个人感觉讲的比较清楚的(百度云里都包括,贴一下百度文库方便查看)
The Magical Splay
BST 拓展与伸展树 (Splay) 一日通
杨思雨 2004国家集训队论文 《伸展树的基本操作与应用》
Splay分为左旋(zag),右旋(zig),通过组合我们又可以得到zig-zig,zag-zag,zig-zag,其中可以证明zig-zag=zig+zag,所以我们只需要zig,zag,zig-zig,zag-zag,考虑对称性,就分为单旋和双旋了
procedure rotate(a,kind:longint); //kind=1右旋;kind=2左旋
var b,unkind;
begin
b:=w[a,3]; unkind:=kind xor 3;
w[a,4]:=w[b,4]; dec(w[b,4],w[a,5]+w[w[a,kind],4]);
w[w[a,unkind],3]:=b; w[b,kind]:=w[a,unkind];
w[a,unkind]:=b; w[a,3]:=w[b,3]; w[b,3]:=a;
if w[a,3]<>-1
then
if w[w[a,3],1]=b
then w[w[a,3],1]:=a
else w[w[a,3],2]:=a;
end;
Splay的均摊复杂度为每次O(logN)就是靠着每次将要操作的节点提为根节点来维持的
以下我们以a为操作节点,b=fa[a]来说
我们定义a为它父节点的左儿子(son[b,1]=a)那么kind=1反之kind=2
它父节点的另一个儿子为unkind(unkind=kind xor 3)
a的父节点为目标节点时执行此操作
a,a的父节点,a的父节点的父节点为同一侧的子节点
a,a的父节点,a的父节点的父节点为不同侧的子节点
procedure splay(a,goal:longint);
var b,kind,unkind:longint;
begin
while w[a,3]<>goal do
begin
b:=w[a,3]; if w[b,1]=a then kind:=1 else kind:=2; unkind:=kind xor 3;
if w[b,3]=goal then rotate(a,kind)
else
if w[w[b,3],kind]=b
then begin rotate(b,kind); rotate(a,kind); end
else begin rotate(a,kind); rotate(a,unkind); end;
end;
if goal=-1 then root:=a;
end;
root表示splay的根,sum表示最后一个节点在数组的位置
注意有重复的问题
procedure init(a:longint);
var tt,fa,kind:longint;
begin
tt:=root;
while tt<>-1 do
begin
//标记下传 if w[tt,7]<>0 then pushdown(tt);
inc(w[tt,4]); fa:=tt;
if w[tt,6]=a then break;
if a<w[tt,6]
then begin tt:=w[tt,1]; kind:=1; end
else begin tt:=w[tt,2]; kind:=2; end;
end;
if tt<>-1
then begin inc(w[tt,5]); splay(tt,-1); end
else begin inc(sum); w[sum,1]:=-1; w[sum,2]:=-1; w[sum,3]:=fa; w[sum,4]:=1; w[sum,5]:=1; w[sum,6]:=a; w[fa,kind]:=sum; splay(sum,-1); end;
end;
还是注意重复问题
function getmax(a:longint):longint; //在a的子树中找到最大的节点
var tt:longint;
begin
tt:=a;
while w[tt,2]<>-1 do
tt:=w[tt,2];
exit(tt);
end;
procedure del(a:longint);
var tt:longint;
begin
tt:=root;
while w[tt,6]<>a do
if a<w[tt,6]
then tt:=w[tt,1]
else tt:=w[tt,2];
splay(tt,-1);
if w[tt,5]>1
then begin dec(w[tt,4]); dec(w[tt,5]); end
else begin splay(getmax(w[root,1]),root); w[w[root,1],2]:=w[root,2]; w[w[root,1],4]:=w[root,4]-1; root:=w[root,1]; w[w[root,2],3]:=root; w[root,3]:=-1; end;
end;
我们可以很容易地将任何一条线段用Splay夹出来,由于Splay也是一棵二叉树与线段树相似,所以就产生了Splay的特殊操作:翻转操作(就是打标记下传,没什么区别)
时间复杂度为每次操作均摊 O(logN) ,但常数问题…,势能分析得到的常数大概是...
const
maxn=100005;
var
w:array[-1..maxn,1..6]of longint;
i,j,k:longint;
n,sum,root,a,b:longint;
procedure print(a:longint);
var i:longint;
begin
if w[a,1]<>-1 then print(w[a,1]);
for i:=1 to w[a,5] do write(w[a,6],' ');
if w[a,2]<>-1 then print(w[a,2]);
if a=root then writeln;
end;
procedure rotate(a,kind:longint);
var b,unkind:longint;
begin
b:=w[a,3]; unkind:=kind xor 3;
w[a,4]:=w[b,4]; dec(w[b,4],w[a,5]+w[w[a,kind],4]);
w[b,kind]:=w[a,unkind];
w[w[a,unkind],3]:=b;
w[a,unkind]:=b;
w[a,3]:=w[b,3];
w[b,3]:=a;
if w[a,3]<>-1
then
if w[w[a,3],1]=b
then w[w[a,3],1]:=a
else w[w[a,3],2]:=a;
end;
procedure splay(a,goal:longint);
var b,kind,unkind:longint;
begin
while w[a,3]<>goal do
begin
b:=w[a,3];
if w[b,1]=a then kind:=1 else kind:=2; unkind:=kind xor 3;
if w[b,3]=goal then rotate(a,kind)
else
if w[w[b,3],kind]=b
then begin rotate(b,kind); rotate(a,kind); end
else begin rotate(a,kind); rotate(a,unkind); end;
end;
if goal=-1 then root:=a;
end;
procedure init(a:longint);
var tt,fa,kind:longint;
begin
tt:=root;
while tt<>-1 do
begin
inc(w[tt,4]);
if w[tt,6]=a then break;
fa:=tt;
if a<w[tt,6]
then begin kind:=1; tt:=w[tt,1]; end
else begin kind:=2; tt:=w[tt,2]; end;
end;
if w[tt,6]=a
then inc(w[tt,5])
else begin inc(sum); w[sum,1]:=-1; w[sum,2]:=-1; w[sum,3]:=fa; w[sum,4]:=1; w[sum,5]:=1; w[sum,6]:=a; w[fa,kind]:=sum; tt:=sum; end;
splay(tt,-1);
end;
function getmax(a:longint):longint;
var tt:longint;
begin
tt:=a;
while w[tt,2]<>-1 do
tt:=w[tt,2];
exit(tt);
end;
function getmin(a:longint):longint;
var tt:longint;
begin
tt:=a;
while w[tt,1]<>-1 do
tt:=w[tt,1];
exit(tt);
end;
procedure del(a:longint);
var tt:longint;
begin
tt:=root;
while w[tt,6]<>a do
if a<w[tt,6]
then tt:=w[tt,1]
else tt:=w[tt,2];
splay(tt,-1);
if w[root,5]=1
then
begin
splay(getmax(w[root,1]),root);
w[w[root,1],2]:=w[root,2]; w[w[root,1],4]:=w[root,4]-1; root:=w[root,1]; w[w[root,2],3]:=root; w[root,3]:=-1;
end
else
begin dec(w[root,5]); dec(w[root,4]); end;
end;
function getrank(a:longint):longint;
var tt:longint;
begin
tt:=root;
while w[tt,6]<>a do
if a<w[tt,6]
then tt:=w[tt,1]
else tt:=w[tt,2];
splay(tt,-1);
exit(w[w[tt,1],4]);
end;
function getkth(a:longint):longint;
var tt:longint;
begin
tt:=root;
while (a<=w[w[tt,1],4])or(a>w[w[tt,1],4]+w[tt,5]) do
if a<=w[w[tt,1],4]
then tt:=w[tt,1]
else begin dec(a,w[w[tt,1],4]+w[tt,5]); tt:=w[tt,2]; end;
exit(w[tt,6]);
end;
begin
readln(n); sum:=2; root:=2;
w[1,1]:=-1; w[1,2]:=-1; w[1,3]:=2; w[1,4]:=1; w[1,5]:=1; w[1,6]:=-1000000005;
w[2,1]:=1; w[2,2]:=-1; w[2,3]:=-1; w[2,4]:=2; w[2,5]:=1; w[2,6]:=1000000005;
for i:=1 to n do
begin
readln(a,b);
case a of
1:begin init(b); end;
2:begin del(b); end;
3:begin writeln(getrank(b)); end;
4:begin writeln(getkth(b+1)); end;
5:begin init(b); writeln(w[getmax(w[root,1]),6]); del(b); end;
6:begin init(b); writeln(w[getmin(w[root,2]),6]); del(b); end;
end;
end;
end.
[BZOJ3223] Tyvj 1729 文艺平衡树 关于翻转标记
[BZOJ3224] Tyvj 1728 普通平衡树平衡树基本操作
[BZOJ1503] [NOI2004]郁闷的出纳员带+-标记的平衡树
[BZOJ1208] [HNOI2004]宠物收养所
[BZOJ1251] 序列终结者注意标记下放的过程