原题网址:http://codeforces.com/contest/700/problem/B
这道题有一个补集思想,对于一棵有根树,两点x,y之间的路径长可以看作两点之间的到根距离减去lca(x,y)到根的距离。假设根已经确定,要使两两的lca到根路径和最小,最小的情况是lca都是根节点,假如根节点的含特殊点最多的一棵子树所含特殊点<=k,那么肯定存在一种配对方案,使得每组点对之间的lca都是根节点,否则肯定存在若干对点对是同属于这棵子树的,lca便不再是根节点,lca到根节点的距离需要从答案中减去。再递归做这棵子树。
const
mxn=200000;
type
point=record
y,next:longint;
end;
var
map:array[0..mxn*2+50] of point;
first,size:array[0..mxn+50] of longint;
n,k,i,x,y,s:longint;
ans:int64;
procedure ins(x,y:longint);
begin
inc(s);map[s].y:=y;
map[s].next:=first[x];first[x]:=s;
end;
procedure dfs1(x,fa,dep:longint);
var
t:longint;
begin
if size[x]=1 then inc(ans,dep);//将所有特殊点的深度加起来
t:=first[x];
while t>0 do
begin
if map[t].y<>fa then
begin
dfs1(map[t].y,x,dep+1);
inc(size[x],size[map[t].y]);
end;
t:=map[t].next;
end;
end;
procedure dfs2(x,fa:longint);
var
t,mxsz,mxi:longint;
begin
t:=first[x];mxsz:=0;
while t>0 do//找到最大的子树
begin
if (map[t].y<>fa)and(size[map[t].y]>mxsz) then
begin
mxsz:=size[map[t].y];
mxi:=map[t].y;
end;
t:=map[t].next;
end;
if mxsz*2>size[1] then
begin
dec(ans,size[mxi]*2-size[1]);//从答案中减去lca到根距离
dfs2(mxi,x);
end;
end;
begin
readln(n,k);
ans:=0;
for i:=1 to k<<1 do
begin
read(x);
size[x]:=1;
end;
fillchar(first,sizeof(first),0);s:=0;
for i:=1 to n-1 do
begin
read(x,y);
ins(x,y);
ins(y,x);
end;
ans:=0;
dfs1(1,0,0);
dfs2(1,0);
writeln(ans);
end.
刚才考虑了如果存在一棵子树特殊点数量超过k的情况,如果将树的重心(此处的“重心”定义为特殊点最多的子树特殊点数量不超过k的点)设为根就可以避免这种情况。如果根为重心,必然存在一种配对方案,使得任一点对lca都为根节点,只要将特殊点的深度相加即可。
type
point=record
y,next:longint;
end;
var
map:array[0..500000] of point;
cnt,size,first:array[0..200050] of longint;
n,k,i,x,y,root,t,mxsz,mxi,s:longint;
ans:int64;
procedure ins(x,y:longint);
begin
inc(s);map[s].y:=y;
map[s].next:=first[x];first[x]:=s;
end;
procedure dfs(x,fa:longint);
var
t:longint;
begin
t:=first[x];
while t>0 do
begin
if map[t].y<>fa then
begin
dfs(map[t].y,x);
inc(size[x],size[map[t].y]);
end;
t:=map[t].next;
end;
end;
procedure dfs2(x,dep,fa:longint);
var
t:longint;
begin
if cnt[x]=1 then inc(ans,dep);
t:=first[x];
while t>0 do
begin
if map[t].y<>fa
then dfs2(map[t].y,dep+1,x);
t:=map[t].next;
end;
end;
begin
readln(n,k);
for i:=1 to k<<1 do
begin
read(x);
cnt[x]:=1;
size[x]:=1;
end;
for i:=1 to n-1 do
begin
read(x,y);
ins(x,y);
ins(y,x);
end;
root:=1;
dfs(root,0);
repeat//找重心
t:=first[root];
mxsz:=0;
while t>0 do
begin
if size[map[t].y]>mxsz then
begin
mxsz:=size[map[t].y];
mxi:=map[t].y;
end;
t:=map[t].next;
end;
if mxsz*2>size[root]
then
begin
size[root]:=size[root]-size[mxi];
size[mxi]:=size[mxi]+size[root];
root:=mxi;
end
else break;
until false;
dfs2(root,0,0);
writeln(ans);
end.