关于普通平衡树的均摊复杂度的优化
如何优化 \(\texttt{FHQ Treap}\) 的常数
前几个星期学了 \(\texttt{FHQ Treap}\),似乎跑得很慢。然后想出一个可以优化平衡树的均摊时间复杂度的方法。
首先我们按照权值分块 (\(node\_num\) 每一个块的权值个数,\(block\_num\) 为块的数量),每一个值 \(val\) 插入的时候,就插入到第 \(\frac{val}{node\_num}\) 珂平衡树里。删除同理。如果所有的值都在同一个块里 (一般不会这样子),那么时间复杂度为 \(O(\log N)\),均摊为 \(O(\log (\sqrt{\max val}))\)。空间的话可以简单的写为 \(O(N \sqrt{\max val})\),如果你愿意可以动态内存,为 \(O(N)\)。
那么我们如何查询第 \(K\) 小呢? 不可能直接在分块上面找吧? 那么我们在分块上面架一个线段树,维护的是每一个分块的平衡树的 \(size\) 值。因为是分块,所以这个线段树的长度是 \(\sqrt{N}\),它查询一次是 \(O(\log (\sqrt{\max val}))\) 的。我们查询第 \(K\) 小只需要知道 \(K\) 所在的块 (假如是 \(id\)),我们查询 \(1\)~\(id-1\) 线段树上的 \(size\) 值,然后查找 \(id\) 这颗线段树上的 \(k-size_{1..id-1}\) 的值就好了。后面那东西均摊复杂度也是 \(O(\log (\sqrt{\max val}))\),最差是 \(O(\log N)\)。
其次是一个数字的排名。我们只需要输出这个数字在它所在的块的平衡树的排名+\(size_{1,id-1}\)就可以了。
然后是前驱。这个东西就是先用线段树找出它的前驱在哪里 (是在哪个块)。如果它的前驱不在它自己所在的块的平衡树,那么就用线段树往其他地方找 (大概打过主席树的大佬都会,写一个维护最小值的线段树就好了)。后继同理。最后像 \(\texttt{FHQ Treap}\) 那样找就好了。下面是 \(YH\) (优化?) 后的图示。
上述的方法会让你的代码复杂度大大增加,并不是十分实用。总结起来,它可以使理论复杂度降低比较多。如果有发现错误或者更优的方法,欢迎指导。
此图为随机数总值为 \(100\) 的普通 \(\texttt{FHQTreap}\) :
此图为随机数总值为 \(\texttt{某民国大佬生日}\) 的最优的普通 \(\texttt{FHQTreap}\) :
此图为随机数总值为 \(100\) 的普通 \(\texttt{AYH FHQTreap}\) :
代码只有 \(92\) (因为时间紧张的我根本没有考虑负数的情况,我说的是线段数那一个部分),其现测最快速度通过本题为 \(372ms\) (那是 \(\texttt{Pascal}\) 喂)。
所以并没有什么卵用。
// AYH FHQTreap
Uses math;
Const
memony=10000000;
block_memony=trunc(sqrt(memony*1.35));
total=trunc(block_memony*1.5);
RP=100;
var
size,tree,heap:array[-block_memony..block_memony,-1..total] of longint;
son:array[-block_memony..block_memony,-1..total,1..2] of longint;
tail,root,section:array[-block_memony..block_memony*3] of longint;
site:array[-block_memony..block_memony*3,1..2] of longint;
i,m,n,k,key,order,node_num,block_num:longint;
// <-- 分块操作 -->
function Locate(node:longint):longint;
begin
if node mod node_num=0 then exit(node div node_num);
exit(node div node_num+1);
end;
// <-- 线段树操作 -->
procedure Modify(l,r,k,key,num:longint); // 修改某一个平衡树的 size
var mid:longint;
begin
if l=r then
begin
section[k]:=num;
if section[k]<>0 then site[k,1]:=l else site[k,1]:=maxlongint;
if section[k]<>0 then site[k,2]:=l else site[k,2]:=-maxlongint;
exit;
end;
mid:=(l+r) >> 1;
if key<=mid then Modify(l,mid,k << 1,key,num) else Modify(mid+1,r,k << 1+1,key,num);
section[k]:=section[k << 1]+section[k << 1+1];
site[k,1]:=min(site[k << 1,1],site[k << 1+1,1]);
site[k,2]:=max(site[k << 1,2],site[k << 1+1,2]);
end;
function Position(l,r,k:longint):longint; // 找第 k 小的位置
var mid:longint;
begin
Position:=0; if l=r then exit(l);
mid:=(l+r) >> 1;
if section[k << 1]>=key then Position:=Position(l,mid,k << 1) else
begin dec(key,section[k << 1]); Position:=Position(mid+1,r,k << 1+1); end;
end;
function Inquiry(l,r,k,x,y:longint):longint; // 查询一段的 size 值
var mid:longint;
begin
Inquiry:=0; if x>y then exit;
if (x<=l)and(r<=y) then exit(section[k]);
mid:=(l+r) >> 1;
if x<=mid then inc(Inquiry,Inquiry(l,mid,k << 1,x,y));
if y>mid then inc(Inquiry,Inquiry(mid+1,r,k << 1+1,x,y));
end;
function Find(l,r,k,x,y,mode:longint):longint; // 查找最靠近存在的位置
var mid:longint;
begin
if mode=1 then Find:=maxlongint else Find:=-maxlongint;
if (x<=l)and(r<=y) then exit(site[k,mode]);
mid:=(l+r) >> 1;
if mode=1 then
begin
if x<=mid then Find:=min(Find,Find(l,mid,k << 1,x,y,mode));
if y>mid then Find:=min(Find,Find(mid+1,r,k << 1+1,x,y,mode));
end;
if mode=2 then
begin
if x<=mid then Find:=max(Find,Find(l,mid,k << 1,x,y,mode));
if y>mid then Find:=max(Find,Find(mid+1,r,k << 1+1,x,y,mode));
end;
end;
// <-- FHQ Treap 操作 -->
procedure Add(val:longint);
var real:longint;
begin
real:=Locate(val); inc(tail[real]);
size[real,tail[real]]:=1;
tree[real,tail[real]]:=val;
heap[real,tail[real]]:=random(RP);
end;
procedure Split(now:longint;var a,b:longint;val,real:longint);
begin
if now=0 then begin a:=0; b:=0; exit; end;
if tree[real,now]<=val then
begin a:=now; Split(son[real,now,1],son[real,a,1],b,val,real); end
else begin b:=now; Split(son[real,now,0],a,son[real,b,0],val,real); end;
size[real,now]:=size[real,son[real,now,0]]+size[real,son[real,now,1]]+1;
end;
procedure Merge(var now:longint;a,b,real:longint);
begin
if (a=0)or(b=0) then begin now:=a+b; exit; end;
if (heap[real,a]<heap[real,b]) then
begin now:=a; Merge(son[real,now,1],son[real,a,1],b,real); end
else begin now:=b; Merge(son[real,now,0],a,son[real,b,0],real); end;
size[real,now]:=size[real,son[real,now,0]]+size[real,son[real,now,1]]+1;
end;
procedure Insert;
var x,y,o,real:longint;
begin
real:=Locate(k); x:=0; y:=0;
Add(k); o:=tail[real];
if o<>root[real] then
begin
Split(root[real],x,y,k,real);
Merge(x,x,o,real); Merge(root[real],x,y,real);
end;
Modify(1,block_num,1,real,size[real,root[real]]);
end;
procedure Delete;
var x,y,o,real:longint;
begin
real:=Locate(k); x:=0; y:=0; o:=0;
Split(root[real],x,y,k,real); Split(x,x,o,k-1,real);
Merge(o,son[real,o,0],son[real,o,1],real);
Merge(x,x,o,real); Merge(root[real],x,y,real);
Modify(1,block_num,1,real,size[real,root[real]]);
end;
function Query(now,k,real:longint):longint;
begin
Query:=0;
if size[real,son[real,now,0]]+1=k then exit(tree[real,now]);
if size[real,son[real,now,0]]>=k then Query:=Query(son[real,now,0],k,real) else
Query:=Query(son[real,now,1],k-size[real,son[real,now,0]]-1,real);
end;
function Rank:longint;
var x,y,real:longint;
begin
real:=Locate(k); x:=0; y:=0;
Split(root[real],x,y,k-1,real);
Rank:=size[real,x]+1;
Merge(root[real],x,y,real);
exit(Rank+Inquiry(1,block_num,1,1,real-1));
end;
function Kanr:longint;
var x,y,real:longint;
begin
key:=k; real:=Position(1,block_num,1);
exit(Query(root[real],key,real));
end;
function Precursor:longint;
var x,y,real,minn:longint;
begin
real:=Locate(k); x:=0; y:=0;
if size[real,root[real]]>0 then minn:=Query(root[real],1,real) else minn:=maxlongint;
if minn>=k then real:=Find(1,block_num,1,1,Locate(k)-1,2);
Split(root[real],x,y,k-1,real);
Precursor:=Query(x,size[real,x],real);
Merge(root[real],x,y,real);
end;
function Next:longint;
var x,y,real,maxn:longint;
begin
real:=Locate(k); x:=0; y:=0;
if size[real,root[real]]>0 then maxn:=Query(root[real],size[real,root[real]],real) else maxn:=-maxlongint;
if maxn<=k then real:=Find(1,block_num,1,Locate(k)+1,block_num,1);
Split(root[real],x,y,k,real);
Next:=Query(y,1,real);
Merge(root[real],x,y,real);
end;
begin
randomize; read(m);
node_num:=trunc(sqrt(memony)); block_num:=Locate(memony);
for i:=1 to block_num+1 do root[i]:=1;
for i:=1 to trunc(sqrt(memony))*3 do begin site[i,1]:=maxlongint; site[i,2]:=-maxlongint; end;
for i:=1 to m do
begin
read(order,k);
Case order of
1 : Insert;
2 : Delete;
3 : writeln(Rank); // Get num's rank
4 : writeln(Kanr); // Get rank's num
5 : writeln(Precursor);
6 : writeln(Next);
end;
end;
end.