[BZOJ3611] [Heoi2014]大工程

本文探讨了在树形结构中进行路径查询的高效算法,包括路径和、最长路径、最短路径的计算。通过虚树与树形DP技术,实现复杂查询的快速解决。特别关注关键点对路径的影响,以及如何利用动态规划优化算法性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

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

题目大意

给定一些树上的关键点询问
1.两两之间的路径和
2.两两之间的最长路径
3.两两之间的最短路径

题解

虚树+树形DP
第一个问题
f[i]+=f[son[i]]+size[son[i]](cntsize[son[i]])dis(i,son[i])
cnt 为关键点总数, size[i] 为以 i 为根的子树内关键点个数
第二,三个问题
minans=min(minans,g[i]+g[son[i]]+dis(i,son[i]))
g[i] 为以 i 为根的子树中到i的最小长度
max 同理
注意初值

const
    maxn=1000050;
    maxint=1000000000;
var
    w:array[0..3*maxn,1..2]of longint;
    t,x,dep,pos,g,h,cle:array[0..maxn]of longint; {g-->min;h-->max}
    size,f:array[0..maxn]of int64;
    st:array[0..maxn,0..20]of longint;
    i,j,k:longint;
    n,m,len,a,b,top,cnt,ans1,ans2,tt:Longint; {ans1-->min;ans2-->max}
function min(a,b:longint):longint;
begin if a>b then exit(b) else exit(a); end;

function max(a,b:longint):longint;
begin if a<b then exit(b) else exit(a); end;

procedure init(a,b:longint);
begin
    w[len,1]:=b;
    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; st[a,0]:=fa;
    while tt<>0 do
    begin
        if (w[tt,1]<>fa) then
        begin
            dep[w[tt,1]]:=dep[a]+1;
            dfs(w[tt,1],a); 
        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;

procedure dp(a,fa:longint); {g-->min;h-->max} {ans1-->min;ans2-->max}
begin
    f[fa]:=f[fa]+f[a]+size[a]*(cnt-size[a])*(dep[a]-dep[fa]);
    size[fa]:=size[fa]+size[a];
    ans1:=min(ans1,g[fa]+g[a]+dep[a]-dep[fa]);
    ans2:=max(ans2,h[fa]+h[a]+dep[a]-dep[fa]);
    g[fa]:=min(g[fa],g[a]+dep[a]-dep[fa]);
    h[fa]:=max(h[fa],h[a]+dep[a]-dep[fa]);
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);
    len:=0; dep[0]:=1; st[0,0]:=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];
    for i:=0 to n do begin g[i]:=maxint; h[i]:=-maxint; end;
    readln(m);
    for i:=1 to m do
    begin
        readln(cnt);
        for j:=1 to cnt do begin read(x[j]); size[x[j]]:=1; cle[j]:=x[j]; g[x[j]]:=0; h[x[j]]:=0; end;
        sort(1,cnt);
        top:=1; t[1]:=0; cle[0]:=cnt+1; cle[cnt+1]:=0; ans1:=maxint; ans2:=-maxint;
        for j:=1 to cnt do
        begin
            tt:=lca(t[top],x[j]); 
            while (dep[tt]<dep[t[top]]) do
            begin
                if (dep[t[top-1]]<=dep[tt]) then
                begin
                    dp(t[top],tt); {dp(t[top]-->tt)} dec(top);
                    if (t[top]<>tt) then begin inc(top); t[top]:=tt; inc(cle[0]); cle[cle[0]]:=tt; end;
                    break;
                end;
                dp(t[top],t[top-1]); {dp(t[top]-->t[top-1])} dec(top);
            end;
            inc(top); t[top]:=x[j];
        end;
        while (top>1) do
        begin
            dp(t[top],t[top-1]); {dp(t[top]-->t[top-1])} dec(top);
        end;
        writeln(f[0],' ',ans1,' ',ans2);
        for j:=1 to cle[0] do
        begin
            size[cle[j]]:=0;
            f[cle[j]]:=0;
            g[cle[j]]:=maxint;
            h[cle[j]]:=-maxint;
        end;
    end;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值