关于普通平衡树的均摊复杂度的优化

如何优化 \(\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}\) :

1.PNG

此图为随机数总值为 \(\texttt{某民国大佬生日}\) 的最优的普通 \(\texttt{FHQTreap}\) :

3.PNG

此图为随机数总值为 \(100\) 的普通 \(\texttt{AYH FHQTreap}\) :

2.PNG

代码只有 \(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.
posted @ 2019-03-07 19:13  _ARFA  阅读(221)  评论(0编辑  收藏  举报