[POI2014]KUR-Couriers

静态主席树

题面大意就是给定一个区间\(l,r\),让你求出一个数字\(num\)的出现次数大于\(\frac{r-l+1}{2}\)。虽然是一个很显然的主席树裸题,不过怎么能卡我空间呢?

思路就是,出现次数大于一半肯定是绝对的。如果左儿子的权值已经大于\(\frac{r-l+1}{2}\),那么右儿子肯定小于\(\frac{r-l+1}{2}\)。也可能两边都等于\(\frac{r-l+1}{2}\),不过这样子就说明没有答案。

如果左儿子(或者右儿子)的权值大于\(\frac{r-l+1}{2}\),那么答案肯定在左子树中,这样就可以\(log\ n\)的时间求出答案。

\(Code\)

// luogu-judger-enable-o2 //没有说pascal不能开
// 注意空间
var
    left,right,tree:array[-1..10010007] of longint; //分别为左儿子的位置,右儿子的位置,本节点的权值
    root:array[-1..10010007] of longint; //每一个根的编号
    recf:array[-1..1500007] of longint; //每一个数的编号,直接用桶来代替离散化
    bucket:array[-1..5001007] of longint; //每一个编号所代表的数
    i,j,n,m,cnt,now,fa,tail,num,l,r,find:longint;
    condition:real; //满足要求,即为(r-l+1)/2,其实可以是整数

procedure Build(l,r,key:longint); 
var
    mid:longint;
begin
    inc(cnt); //动态开点
    tree[cnt]:=tree[fa]+1; 
    if l=r then 
        exit;

    mid:=(l+r) div 2;
    if key<=mid then //往左走
    begin //记得全部都要往左
        left[cnt]:=cnt+1; //左儿子一定是cnt+1(除了叶子结点)
        right[cnt]:=right[fa];
        fa:=left[fa];
        Build(l,mid,key);
    end;
    if key>mid then //如上
    begin
        right[cnt]:=cnt+1;
        left[cnt]:=left[fa];
        fa:=right[fa];
        Build(mid+1,r,key);
    end;
end;

procedure Query(l,r:longint);
var
    mid,value:longint;
begin
    if l=r then
    begin
        find:=bucket[l];
        exit;
    end;

    value:=tree[left[fa]]-tree[left[now]]; //左边的权值
    mid:=(l+r) div 2;
    if value>condition then //满足
    begin
        fa:=left[fa];
        now:=left[now];
        Query(l,mid);
    end;
    value:=tree[right[fa]]-tree[right[now]]; //如上
    if value>condition then
    begin
        fa:=right[fa];
        now:=right[now];
        Query(mid+1,r);
    end;
end;

begin
    read(n,m);
    tail:=0;
    for i:=1 to n do
    begin
        read(num);
        if recf[num]=0 then //给予每一个数字编号
        begin
            inc(tail); //给予编号
            recf[num]:=tail; 
            bucket[tail]:=num;
        end;
        root[i]:=cnt+1;
        fa:=root[i-1];
        Build(1,n,recf[num]); //往编号的位置插入,代替离散化。不过你是求区间第K大的话这样子是不行的
    end;

    for i:=1 to m do
    begin
        read(l,r); 
        condition:=(r-l+1)/2; //条件,小数类型
        now:=root[l-1]; 
        fa:=root[r];
        find:=0;
        Query(1,n);
        writeln(find);
    end;
end.
posted @ 2018-09-23 08:21  _ARFA  阅读(168)  评论(0编辑  收藏  举报