传送门
http://www.lydsy.com/JudgeOnline/problem.php?id=2286
题目大意
给定一棵树,树上有边权,切断一条边消耗边权大小的能量
每次给定一些关键点,使这些关键点都不能与1联通,询问最小代价
题解
树形DP
dp[i]:使i不与它子树中任意一个关键点联通的最小代价
dp[i]=∑min{dp[son[i]],w[i,son[i]]}son[i]不是关键点/w[i,son[i]]son[i]是关键点
复杂度
O(NM)
超时
我们发现其实不用每次都对整棵树做一遍
DP
,每次只留下关键点和汇集关键点的点(
lca
)即可
(下面的父子关系是省略无关点后的新树)
dp[i]=∑min{dp[son[i]],w[i,son[i]]}son[i]不是关键点/w[i,son[i]]son[i]是关键点
其中
w[i,son[i]]
是原树中两点见的最小边权
可以用ST表预处理出来做到每次
O(logN)
(这一种抛去无关点建立的新树通常叫做虚树)
如何建立,看下面一段
接下来所说的”树”均指虚树,原来那棵树叫做”原树”.
构建过程如下:
按照原树的dfs序号(记为dfn)递增顺序遍历选择的节点. 每次遍历节点都把这个节点插到树上.
首先虚树一定要有一个根. 随便扯一个不会成为询问点的点作根.
维护一个栈,它表示在我们已经(用之前的那些点)构建完毕的虚树上,以最后一个插入的点为端点的DFS链.
设最后插入的点为p(就是栈顶的点),当前遍历到的点为x.我们想把x插入到我们已经构建的树上去.
求出lca(p,x),记为lca.有两种情况:
1.p和x分立在lca的两棵子树下.
2.lca是p.
(为什么lca不能是x?
因为如果lca是x,说明dfn(lca)=dfn(x)
const
maxn=250000;
var
w:array[0..3*maxn,1..3]of longint;
dp:array[0..maxn]of int64;
pos,dep,t,s,v,p:array[0..maxn]of longint;
st:array[0..maxn,0..20]of longint;
dis:array[0..maxn,0..20]of int64;
i,j,k:longint;
n,m,len,a,b,c,tt:longint;
function min(a,b:int64):int64;
begin if a>b then exit(b) else exit(a); end;
procedure init(a,b,c:longint);
begin
w[len,1]:=b; w[len,2]:=c;
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 sort(l,r:longint);
var i,j,a,b:longint;
begin
i:=l;j:=r;a:=pos[s[(l+r)div 2]];
repeat
while (pos[s[i]]<a) do inc(i);
while (pos[s[j]]>a) do dec(j);
if not(i>j) then begin b:=s[i]; s[i]:=s[j]; s[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:longint);
var tt:longint;
begin
tt:=w[a,3]; inc(len); pos[a]:=len;
while tt<>0 do
begin
if (w[tt,1]<>st[a,0])
then
begin
dep[w[tt,1]]:=dep[a]+1;
st[w[tt,1],0]:=a;
dis[w[tt,1],0]:=w[tt,2];
dfs(w[tt,1]);
end;
tt:=w[tt,3];
end;
end;
procedure prepare;
var i:longint;
begin
len:=0; dep[1]:=1; dfs(1);
for i:=1 to 20 do
for j:=1 to n do
st[j,i]:=st[st[j,i-1],i-1];
for i:=1 to 20 do
for j:=1 to n do
dis[j,i]:=min(dis[j,i-1],dis[st[j,i-1],i-1]);
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 distance(a,b:longint):int64;
var ans:int64; i:longint;
begin
if dep[a]<dep[b] then begin i:=a; a:=b; b:=i; end;
ans:=maxlongint;
for i:=20 downto 0 do
if dep[st[a,i]]>=dep[b]
then begin ans:=min(ans,dis[a,i]); a:=st[a,i]; end;
exit(ans);
end;
begin
readln(n); len:=n+1;
for i:=1 to n-1 do
begin
readln(a,b,c);
init(a,b,c); init(b,a,c);
end;
prepare;
readln(m);
for i:=1 to m do
begin
read(a);
for j:=1 to a do
begin read(s[j]); v[s[j]]:=1; end;
sort(1,a);
t[1]:=1; t[0]:=1; p[0]:=1; p[1]:=1; len:=n+1;
for j:=1 to a do
begin
tt:=lca(t[t[0]],s[j]);
if tt=t[t[0]]
then begin inc(t[0]); t[t[0]]:=s[j]; inc(p[0]); p[p[0]]:=s[j]; end
else
begin
while dep[t[t[0]-1]]>dep[tt] do
begin
if v[t[t[0]]]=1
then inc(dp[t[t[0]-1]],distance(t[t[0]],t[t[0]-1]))
else inc(dp[t[t[0]-1]],min(distance(t[t[0]],t[t[0]-1]),dp[t[t[0]]]));
dp[t[t[0]]]:=0; dec(t[0]);
end;
if v[t[t[0]]]=1
then inc(dp[tt],distance(tt,t[t[0]]))
else inc(dp[tt],min(distance(tt,t[t[0]]),dp[t[t[0]]]));
dp[t[t[0]]]:=0; dec(t[0]);
if t[t[0]]<>tt then begin inc(t[0]); t[t[0]]:=tt; inc(p[0]); p[p[0]]:=s[j]; end;
inc(t[0]); t[t[0]]:=s[j]; inc(p[0]); p[p[0]]:=s[j];
end;
end;
while t[0]>1 do
begin
if v[t[t[0]]]=1
then inc(dp[t[t[0]-1]],distance(t[t[0]],t[t[0]-1]))
else begin inc(dp[t[t[0]-1]],min(distance(t[t[0]],t[t[0]-1]),dp[t[t[0]]])); dp[t[t[0]]]:=0; end;
dec(t[0]);
end;
writeln(dp[1]);
for j:=1 to p[0] do
dp[p[j]]:=0;
for j:=1 to a do
v[s[j]]:=0;
end;
end.