[BZOJ2588] Spoj 10628. Count on a tree

传送门

http://www.lydsy.com/JudgeOnline/problem.php?id=2588

题目大意

给定一棵树,询问两点间点权第k小的值,强制在线

题解

clj的论文上貌似有这个例题
主席树数组开小了RE了好几次QAQAQ
空间复杂度应该是 O(NlogN)

{$m 10000000}
const
    maxn=101000; //0.1
var
    y,z,dep,fa,root:array[0..maxn]of longint;
    st:array[0..maxn,0..20]of longint;
    x:array[0..maxn,1..2]of longint;
    t:array[0..3*maxn,1..2]of longint;
    w:array[0..20*maxn,1..3]of longint;
    i,j,k:longint;
    n,m,len,a,b,c,lastans:longint;
procedure sort(l,r:longint);
var i,j,a,b:longint;
begin
    i:=l; j:=r; a:=x[(l+r) div 2,1];
    repeat
        while x[i,1]<a do inc(i);
        while a<x[j,1] do dec(j);
        if not(i>j) then
        begin
            b:=x[i,1]; x[i,1]:=x[j,1]; x[j,1]:=b;
            b:=x[i,2]; x[i,2]:=x[j,2]; x[j,2]:=b;
            inc(i); dec(j);
        end;
    until i>j;
    if l<j then sort(l,j);
    if i<r then sort(i,r);
end;

procedure init(a,b:longint);
begin
    t[len,1]:=b;
    if t[a,2]=0
    then t[a,2]:=len else t[t[a,1],2]:=len;
    t[a,1]:=len; inc(len);
end;

function lca(a,b:longint):longint;
var i,j:longint;
begin
    if dep[a]>dep[b] then begin i:=a; a:=b; b:=i; end;
    for i:=19 downto 0 do
        if dep[st[b,i]]>=dep[a]
        then b:=st[b,i];
    if a=b then exit(a);
    for i:=19 downto 0 do
        if st[a,i]<>st[b,i]
        then begin a:=st[a,i]; b:=st[b,i]; end;
    exit(fa[a]);
end;

procedure build(pre,now,val,l,r:longint);
var mid:longint;
begin
    mid:=(l+r)>>1; w[now,1]:=w[pre,1]; w[now,2]:=w[pre,2]; w[now,3]:=w[pre,3]+1; inc(len);
    if l=r then exit;
    if val<=mid
    then begin w[now,1]:=len; build(w[pre,1],w[now,1],val,l,mid); end
    else begin w[now,2]:=len; build(w[pre,2],w[now,2],val,mid+1,r); end;
end;

function query(a,b,c,d,k,l,r:longint):longint;
var mid,tt:longint;
begin
    if l=r then exit(l);
    mid:=(l+r)>>1;
    tt:=w[w[a,1],3]+w[w[b,1],3]-w[w[c,1],3]-w[w[d,1],3];
    if tt>=k
    then exit(query(w[a,1],w[b,1],w[c,1],w[d,1],k,l,mid))
    else exit(query(w[a,2],w[b,2],w[c,2],w[d,2],k-tt,mid+1,r));
end;

procedure dfs(a:longint);
var tt:longint;
begin
    tt:=t[a,2]; inc(len); root[a]:=len;
    build(root[fa[a]],root[a],y[a],1,n);
    while tt<>0 do
        begin
            if t[tt,1]<>fa[a]
            then
                begin
                    fa[t[tt,1]]:=a;
                    dep[t[tt,1]]:=dep[a]+1;
                    st[t[tt,1],0]:=a;
                    dfs(t[tt,1]);
                end;
            tt:=t[tt,2];
        end;
end;

begin
    readln(n,m);
    for i:=1 to n do
        begin
            read(x[i,1]);
            x[i,2]:=i;
        end;
    sort(1,n); {x[i,1]}
    for i:=1 to n do
        begin
            y[x[i,2]]:=i; //离散化后的值
            z[i]:=x[i,1]; //原始值
        end;
    len:=n+1;
    for i:=1 to n-1 do
        begin
            readln(a,b);
            init(a,b); init(b,a);
        end;
    fa[1]:=0; dep[1]:=1; len:=0;
    dfs(1);
    for j:=1 to 19 do
        for i:=1 to n do
            st[i,j]:=st[st[i,j-1],j-1];
    lastans:=0; fa[0]:=0;
    for i:=1 to m do
        begin
            readln(a,b,k);
            a:=a xor lastans;
            c:=lca(a,b);
            lastans:=query(root[a],root[b],root[c],root[fa[c]],k,1,n);
            lastans:=z[lastans];
            if i=m
            then write(lastans)
            else writeln(lastans);
        end;
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值