[BZOJ3572] [Hnoi2014]世界树

传送门

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

题目大意

给定一棵树,每次给一些关键点,树上每个点都被离他最近的关键点支配,距离相同取编号小的关键点,每次询问每个关键点支配的点个数

题解

显然虚树(数据范围),主要在统计上
建出虚树后,我们先求出来虚树上的每个点被哪个关键点支配
这个两次DFS统计即可(一次用 i 子树中的节点更新i一次用 i 的父节点更新i)
然后我们统计每个关键点支配个数
虚树上的边 (u,v) 分为
bel[u]=bel[v] 边上的点全部计入 ans[bel[u]]
bel[u]!=bel[v] 倍增找到分界点加入两边
如果原树上某条路径的两端只有一个在虚树上,我们这么统计
很明显这些点一定是与在虚树上的那一端属于同一个点支配的
定义点 u 他的虚树上的儿子son[u]以及 uson[u]uv
那么没被统计的部分就是 size[u]size[v]
以上就都统计到了

const
    maxn=300050;
    inf=1000000000;
var
    w:array[0..3*maxn,1..2]of longint;
    bel,pos,dep,x,y,ans,t,size,key,cle:array[0..maxn]of longint;
    st:array[0..maxn,0..20]of longint;
    i,j,k:longint;
    n,m,len,a,b,top,cnt,tt:longint;
procedure init(a,b:longint);
begin
    w[len,1]:=b; w[len,2]:=0;
    if (w[a,2]=0) then w[a,2]:=len else w[w[a,1],2]:=len;
    w[a,1]:=len; inc(len);
end;

procedure sort(l,r:longint);
var i,j,a,b:longint;
begin
    i:=l; j:=r; a:=pos[x[(l+r)div 2]];
    repeat
        while (pos[x[i]]<a) do inc(i);
        while (a<pos[x[j]]) do dec(j);
        if not(i>j) then
        begin  
            b:=x[i]; x[i]:=x[j]; x[j]:=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 dfs(a,fa:longint);
var tt:longint;
begin
    tt:=w[a,2]; inc(len); pos[a]:=len; size[a]:=1;
    while tt<>0 do
    begin  
        if (w[tt,1]<>fa) then
        begin
            dep[w[tt,1]]:=dep[a]+1;
            st[w[tt,1],0]:=a;
            dfs(w[tt,1],a);
            inc(size[a],size[w[tt,1]]);
        end;
        tt:=w[tt,2];
    end;
end;

function lca(a,b:longint):longint;
var i:longint;
begin
    if (dep[a]<dep[b]) then begin i:=a; a:=b; b:=i; end;
    for i:=20 downto 0 do
        if (dep[st[a,i]]>=dep[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 get(fa,a:longint):Longint;
var i:longint;
begin
    for i:=20 downto 0 do
        if (dep[st[a,i]]>dep[fa]) then a:=st[a,i];
    exit(a);
end;

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

procedure dfs1(a:longint); //用子节点更新父节点的bel值
var tt:longint;
begin
    tt:=w[a,2]; 
    if (key[a]=1) then bel[a]:=a else bel[a]:=inf;
    while (tt<>0) do
    begin
        dfs1(w[tt,1]);
        if (bel[w[tt,1]]<>inf)and((bel[a]=inf)or(dis(bel[w[tt,1]],a)<dis(bel[a],a))or((dis(bel[w[tt,1]],a)=dis(bel[a],a))and(bel[w[tt,1]]<bel[a])))
        then bel[a]:=bel[w[tt,1]]; 
        tt:=w[tt,2];
    end;
end;

procedure dfs2(a,fa:longint); //用父节点更新子节点的bel值
var tt:longint;
begin
    if (a<>0)and((dis(bel[fa],a)<dis(a,bel[a]))or((dis(bel[fa],a)=dis(a,bel[a]))and(bel[fa]<bel[a]))) 
    then bel[a]:=bel[fa];
    tt:=w[a,2];
    while (tt<>0) do
    begin dfs2(w[tt,1],a); tt:=w[tt,2]; end;
end;

procedure solve(a,fa:longint); //统计贡献
var tt,sum,aa,i:longint;
begin
    if (fa=0) then inc(ans[bel[a]],size[1]-size[a])
    else
    begin
        tt:=get(fa,a); aa:=a;
        if (bel[a]=bel[fa]) then inc(ans[bel[a]],size[tt]-size[a])
        else
        begin          
            for i:=20 downto 0 do
            begin
                if (dep[st[aa,i]]<dep[tt]) then continue;
                if (dis(st[aa,i],bel[a])<dis(st[aa,i],bel[fa]))or((dis(st[aa,i],bel[a])=dis(st[aa,i],bel[fa]))and(bel[a]<bel[fa]))
                then aa:=st[aa,i];
            end;
            inc(ans[bel[a]],size[aa]-size[a]);
            inc(ans[bel[fa]],size[tt]-size[aa]);
        end;                
    end;
    tt:=w[a,2]; sum:=size[a]; 
    while (tt<>0) do
    begin
        dec(sum,size[get(a,w[tt,1])]); 
        solve(w[tt,1],a);
        tt:=w[tt,2];
    end;
    inc(ans[bel[a]],sum);
end;

begin
    readln(n); len:=n+1;
    for i:=1 to n-1 do
    begin readln(a,b); init(a,b); init(b,a); end;
    init(0,1); init(1,0);
    dep[0]:=1; len:=0; dfs(0,0);
    for j:=1 to 20 do
        for i:=1 to n do
            st[i,j]:=st[st[i,j-1],j-1];
    fillchar(key,sizeof(key),0);
    fillchar(bel,sizeof(bel),0);
    fillchar(ans,sizeof(ans),0);
    fillchar(w,sizeof(w),0);
    readln(m);
    for i:=1 to m do
    begin
        readln(cnt);
        for j:=1 to cnt do
        begin read(x[j]); y[j]:=x[j]; cle[j]:=x[j]; key[x[j]]:=1; end;
        sort(1,cnt);
        top:=1; t[top]:=0; len:=n+1; cle[0]:=cnt+1; cle[cnt+1]:=0;
        for j:=1 to cnt do
        begin
            tt:=lca(x[j],t[top]);
            while (dep[tt]<dep[t[top]]) do
            begin
                if (dep[t[top-1]]<=dep[tt]) then
                begin
                    init(tt,t[top]);
                    dec(top);
                    if (t[top]<>tt) then begin inc(top); t[top]:=tt; inc(cle[0]); cle[cle[0]]:=tt; end;
                    break;
                end;
                init(t[top-1],t[top]);
                dec(top); 
            end;
            inc(top); t[top]:=x[j];
        end;
        while (top>1) do
        begin init(t[top-1],t[top]);
        dec(top); end;
        dfs1(0); 
        dfs2(0,0);
        solve(w[w[0,2],1],0);
        for j:=1 to cnt do write(ans[y[j]],' '); writeln;
        for j:=1 to cle[0] do
        begin
            w[cle[j],2]:=0;
            key[cle[j]]:=0;
            bel[cle[j]]:=inf;
            ans[cle[j]]:=0;
        end;
    end;
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值