[学习笔记] 主席树

早上考了一场叫做“信心赛”的变态比赛,心态爆炸ing。。。

容我先bb两句:

学习主席树这个东西的话,你要点的前置技能是动态开点线段树和权值线段树,不会的话还请点击浏览器右上角神秘按钮(据说点了之后你就能懂得一个人生哲理)。

主席树这个东西,据说是一个叫做黄嘉泰的dalao在考模拟赛的时候因为不会写划分树而想出来的,然后因为它名字的首拼音是hjt和某位叫胡什么锦什么涛什么的人重合了,所以就取了主席树这个名字。

主席树的用途:

既然要讲主席树,那么我们就先来说说主席树是用来干哈子的吧。

就讲一道例题吧,洛谷上的P3834。(这里只谈主席树做法,离线排序什么的不进行讨论)

注意到:如果用权值线段树做的话,那么我们就要开m棵权值线段树,那空间肯定吃不消,怎么办呢?

这个时候主席树就前来救场啦!

主席树的核心思路:

既然存m棵权值线段树会炸,那么我们应该如何来解决这个问题呢?

我们的小天才黄嘉泰同学(dalao我错了)就非常机智的发现了:我们修改一个节点,会对权值线段树产生的影响只是从它到根这一条链上的值,那么我们就没有必要每次存一颗树,只要存一条链就可以了,只需要新开log n个节点。那么对于没有修改的节点我们只需要连到原节点就行了。

查询操作的话就要用到差分或者说是前缀和思想,对于这一道题目的话,那么我们就存每个节点左右子树的规模,然后对于他给我们的区间只要差分一下就可以知道左子树中属于这个区间的数的个数,然后再比较它和需要我们求的k的大小,如果它小于k那么就说明第k小数在左子树,否则就是在右子树中。

代码实现:

var
    ls,rs,siz:array[0..5000000]of longint;
    a,b,c,rt,id:array[0..1000000]of longint;
    n,q,i,j,x,y,k,cnt,tot:longint;
procedure sort(l,r:longint);
var
    i,j,x,y:longint;
begin
    i:=l; j:=r;
    x:=a[(l+r) div 2];
    repeat
        while a[i]<x do inc(i);
        while x<a[j] do dec(j);
        if not(i>j) then
        begin
            y:=a[i]; a[i]:=a[j]; a[j]:=y;
            y:=id[i]; id[i]:=id[j]; id[j]:=y;
            inc(i); j:=j-1;
        end;
    until i>j;
    if l<j then sort(l,j);
    if i<r then sort(i,r);
end;
function build(l,r:longint):longint;
var
    mid,root:longint;
begin
    inc(cnt); root:=cnt;
    if l=r then exit(root);
    mid:=(l+r)>>1;
    ls[root]:=build(l,mid);
    rs[root]:=build(mid+1,r);
    exit(root);
end;
function update(k,l,r,root:longint):longint;
var
    dir,mid:longint;
begin
    inc(cnt); dir:=cnt;
    ls[dir]:=ls[root]; rs[dir]:=rs[root];
    siz[dir]:=siz[root]+1;
    if l=r then exit(dir);
    mid:=(l+r)>>1;
    if k<=mid then ls[dir]:=update(k,l,mid,ls[dir])
    else rs[dir]:=update(k,mid+1,r,rs[dir]);
    exit(dir);
end;
function query(x,y,l,r,k:longint):longint;
var
    mid,derta:longint;
begin
    derta:=siz[ls[y]]-siz[ls[x]];
    if l=r then exit(l);
    mid:=(l+r)>>1;
    if derta>=k then exit(query(ls[x],ls[y],l,mid,k))
    else exit(query(rs[x],rs[y],mid+1,r,k-derta));
end;
begin
    read(n,q);
    for i:=1 to n do
    begin
        read(a[i]);
        id[i]:=i;
    end;
    sort(1,n);
    for i:=1 to n do
    begin
        if a[i]<>b[tot] then
        begin
            inc(tot);
            b[tot]:=a[i];
        end;
        c[id[i]]:=tot;
    end;
    rt[0]:=build(0,tot);
    for i:=1 to n do
        rt[i]:=update(c[i],0,tot,rt[i-1]);
    while q>0 do
    begin
        read(x,y,k);
        writeln(b[query(rt[x-1],rt[y],0,tot,k)]);
        dec(q);
    end;
end.                                          //机智的我把Pascal的tab size改成了4
原文地址:https://www.cnblogs.com/WR-Eternity/p/9908645.html