[BZOJ3991] [SDOI2015]寻宝游戏

传送门

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

题目大意

给定一棵树,询问从某一关键点出发再回到该点遍历所有关键点的最短距离
支持加入1个和删除1个关键点

题解

首先最短路径一定是按照DFS序走
所以就动态维护关键点的DFS序就好了
(c++能用set!!我只好写了个SplayQAQAQAQ)

{$M 100000000,0,100000000}
const
    maxn=100050;
    inf=100000000000000;
var
    w:array[0..3*maxn,1..3]of longint;
    dd,pos,vis:array[-1..maxn]of longint;
    dep:array[-1..maxn]of int64;
    son:array[-1..maxn,1..2]of longint;
    fa,val:array[-1..maxn]of longint;
    st:array[0..maxn,0..20]of longint;
    i,j,k:longint;
    n,m,len,a,b,c,root:longint;
    ans:int64;
procedure init(a,b,c:longint);
begin
    w[len,1]:=b; w[len,2]:=c; w[len,3]:=0;
    if (w[a,3]=0) then w[a,3]:=len else w[w[a,1],3]:=len;
    w[a,1]:=len; inc(len);
end;

procedure dfs(a,fa:longint);
var tt:longint;
begin
    tt:=w[a,3]; inc(len); pos[a]:=len;
    while (tt<>0) do
    begin
        if (w[tt,1]<>fa) then
        begin
            dep[w[tt,1]]:=dep[a]+int64(w[tt,2]);
                    dd[w[tt,1]]:=dd[a]+1;
            st[w[tt,1],0]:=a;
            dfs(w[tt,1],a);
        end;
        tt:=w[tt,3];
    end;
end;

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

function dis(a,b:longint):int64;
begin
    exit(dep[a]+dep[b]-2*dep[lca(a,b)]);
end;

procedure rotate(a,kind:longint);
var b,c,unkind:longint;
begin
    b:=fa[a]; c:=fa[b]; unkind:=kind xor 3;
    fa[son[a,unkind]]:=b; fa[b]:=a;
        son[b,kind]:=son[a,unkind]; son[a,unkind]:=b;
    fa[a]:=c;
    if (son[c,1]=b)
        then son[c,1]:=a
        else son[c,2]:=a;
end;

procedure splay(a,goal:longint);
var kind,unkind,b:longint;
begin
    while (fa[a]<>goal) do
    begin
        b:=fa[a]; if (son[b,1]=a) then kind:=1 else kind:=2; unkind:=kind xor 3;
        if (fa[b]=goal) then rotate(a,kind)
        else
            if (son[fa[b],kind]=b)
            then begin rotate(b,kind); rotate(a,kind); end
            else begin rotate(a,kind); rotate(a,unkind); end;
    end;
    if (goal=-1) then root:=a;
end;

procedure insert(a:longint);
var tt,fath,kind:longint;
begin
    tt:=root;
    while (tt<>-1) do
        if (pos[a]<pos[val[tt]])
        then begin fath:=tt; kind:=1; tt:=son[tt,1]; end
        else begin fath:=tt; kind:=2; tt:=son[tt,2]; end;
    inc(len); son[len,1]:=-1; son[len,2]:=-1; fa[len]:=fath; val[len]:=a; son[fath,kind]:=len;
    splay(len,-1);
end;

procedure delete(a:longint);
var tt:longint;
begin
    tt:=root;
    while (val[tt]<>a) do
        if (pos[a]<pos[val[tt]])
        then tt:=son[tt,1]
        else tt:=son[tt,2];
    splay(tt,-1);
    tt:=son[root,1];
    while (son[tt,2]<>-1) do
        tt:=son[tt,2];
    splay(tt,root);
    son[tt,2]:=son[root,2]; root:=tt; fa[son[root,2]]:=tt; fa[root]:=-1;
end;

function getpre(a:longint):longint;
var tt:longint;
begin
    tt:=root;
    while (val[tt]<>a) do
        if (pos[a]<pos[val[tt]])
        then tt:=son[tt,1]
        else tt:=son[tt,2];
    splay(tt,-1);
        splay(1,root);
        if (son[1,2]=-1) then begin splay(2,-1); splay(1,root); end;
        tt:=son[son[root,1],2];
        while (son[tt,2]<>-1) do
                tt:=son[tt,2];
        exit(val[tt]);
end;

function getsuf(a:longint):longint;
var tt:longint;
begin
    tt:=root;
    while (val[tt]<>a) do
        if (pos[a]<pos[val[tt]])
        then tt:=son[tt,1]
        else tt:=son[tt,2];
    splay(tt,-1);
        splay(2,root);
        if (son[2,1]=-1) then begin splay(1,-1); splay(2,root); end;
        tt:=son[son[root,2],1];
        while (son[tt,1]<>-1) do
                tt:=son[tt,1];
        exit(val[tt]);
end;

begin
    readln(n,m); len:=n+1;
    for i:=1 to n-1 do
    begin readln(a,b,c); init(a,b,c); init(b,a,c); end;
    dep[1]:=1; dd[1]:=1; st[1,0]:=0; len:=0; dfs(1,0);
    for j:=1 to 20 do
        for i:=1 to n do
            st[i,j]:=st[st[i,j-1],j-1];
    fillchar(vis,sizeof(vis),0);
    ans:=0; pos[-1]:=-maxlongint; pos[maxn]:=maxlongint;
    len:=2; root:=1;
    son[1,1]:=-1; son[1,2]:=2; fa[1]:=-1; val[1]:=-1;
    son[2,1]:=-1; son[2,2]:=-1; fa[2]:=1; val[2]:=maxn;
    for i:=1 to m do
    begin
        readln(a);
        if (vis[a]=0) then
        begin
            vis[a]:=1; insert(a);
            b:=getpre(a); c:=getsuf(a);
            ans:=ans-dis(b,c)+dis(a,b)+dis(a,c);
        end
        else
        begin
            vis[a]:=0;
            b:=getpre(a); c:=getsuf(a);
            ans:=ans-dis(a,b)-dis(a,c)+dis(b,c);
            delete(a);
        end;
        writeln(ans);
    end;
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值