题意:给定一棵n个节点的树,q组询问,每次询问找m个关键点,求m个关键点两两之间距离的和、距离的最大值和最小值
n个点的树,对k个关键进行操作(询问)的一眼过去一般都是虚树题
因为原树边权是1,所以虚树上两点之间的边权为abs(d[x]-d[y])(d[i]表示节点i在原树的深度)
(具体虚树怎么搞请自行百度...)
以下的“树”皆表示虚树,树P还真是有点恶心...
我们维护四个数组,分别是 :
size[x] 表示以x为根的子树中关键点的个数(含x本身)
sum[x] 表示以x为根的子树中所有关键点到x的距离和
maxn[x]、minx[x] 表示以x为根的子树中,到x的距离最大(小)值
同时用三个全局变量 ans、ans_min、ans_max 表示该询问的最终答案(分别表示距离和、最小值和最大值)
当我们对于节点x,每新遍历完它的一个子树p,设连接x与p的边权为len,
考虑对最终答案的更新:
ans += sum[x]*size[p] + len*size[x]*size[p] + sum[p]*size[x]
ans_min = min{ans_min,minn[p]+len+minn[x]};
ans_max = max{ans_max,maxn[p]+len+maxn[x]}
解释:对于对最值的更新还是很好理解的,即p子树内的关键点到x的最值+之前已经更新过的x的其他子树内的关键点到x的最值(因为实际上是关键点走到关键点),由于已经更新过的子树的最值已经转移给了x(具体转移放到下面讲),即minn[x](maxn[x]),便有了上面的更新式子;
我们将它看做三部分:
绿色框内的部分表示我们已经更新过的部分(当然可能已经更新过的部分为空),这部分子树的各个值已经转移给了x(具体转移下面讲),即我们只考虑sum[x]、minn[x]和maxn[x];
蓝色的边表示连接x和p的那条边,边权为len;
第三部分就是我们刚刚更新完的子树p
真正对ans有新的贡献的就是绿色部分内的关键点通过根x和蓝色的边走到子树p内的关键点所经过的路径和。这条路线我们把它拆成三部分,即绿色部分内的关键点走到x、从x走到p、从p走到以p为根的子树内的所有关键点。
考虑各个部分对ans的贡献:
绿色部分:绿色部分内的每个关键点都会先从自己走到根x,而每个点都会走到以p为根的子树内的所有关键点,所以绿色部分内的每个关键点会走size[p]次。所以对ans贡献 sum[x]*size[p]
蓝边:蓝边x端有size[x]个关键点要走到p端size[p]个关键点,那么根据乘法原理,易知蓝边的贡献为len*size[x]*size[p]
以p为根的子树内:我们会有size[x]个关键点走到p,并进入以p为根的子树内走到每个关键点,即以p为根的子树对答案的贡献为 size[x]*sum[p]
三部分累加即对ans的贡献和,ans+=三部分贡献和
然后我们考虑子树对x的转移:
sum[x] = sum[x]+sum[p]+size[p]*len (p子树内的关键点到p的距离和+p子树内的关键点通过蓝边走到x的距离和)
maxn[x] = max{maxn[x],maxn[p]+len} (p子树内关键点到p的最值+len)
minn[x] = minn{minn[x],minn[p]+len}
结合上面的图和我们对各个数组的定义,其实还是很好理解的
注意初始化的时候,由于虚树中我们并不能保证每个点x都是关键点,所以初始化是不同的
对于关键点:初始化 size[x]=1;maxn[x]=0 ;minn[x]=0 (因为它自己本身就是关键点,初始最值当然都是0)
对于非关键点: 初始化 size[x]=0 ;maxn[x]=-inf ;minn[x]=inf (因为它自己不是关键点,所以它子树内关键点到它的最值都是未知的,初始当然是最大值为负无穷、最小值为正无穷)
然后就没有然后了...
{$M 100000000}
{$Q-}
uses math;
var
n,m,l,q,ll,tl :longint;
tot,x,y :longint;
ans :int64;
ans_min,ans_max :longint;
i,j :longint;
size,maxn,minn :array[0..1000010] of longint;
sum :array[0..1000010] of int64;
vis,flag :array[0..1000010] of boolean;
last,la :array[0..1000010] of longint;
pre,other :array[0..2000010] of longint;
pr,ot,le :array[0..2000010] of longint;
z,d,dfn,que,a :array[0..1000010] of longint;
jump :array[0..1000010,0..20] of longint;
procedure swap(var a,b:longint);
var
c:longint;
begin
c:=a; a:=b; b:=c;
end;
procedure connect(x,y:longint);
begin
inc(l);
pre[l]:=last[x];
last[x]:=l;
other[l]:=y;
end;
procedure connect2(x,y:longint);
begin
if x=y then exit;
inc(ll);
pr[ll]:=la[x];
la[x]:=ll;
ot[ll]:=y;
le[ll]:=d[y]-d[x];
end;
procedure sort(l,r:longint);
var
i,j,x:longint;
begin
i:=l; j:=r; x:=dfn[a[(l+r)>>1]];
while i<=j do
begin
while dfn[a[i]]x do dec(j);
if i<=j then
begin
swap(a[i],a[j]);
inc(i); dec(j);
end;
end;
if il then sort(l,j);
end;
procedure dfs(x:longint);
var
p,q:longint;
begin
inc(tot);
dfn[x]:=tot;
q:=last[x];
while q<>0 do
begin
p:=other[q];
if not vis[p] then
begin
vis[p]:=true;
jump[p,0]:=x;
d[p]:=d[x]+1;
dfs(p);
end;
q:=pre[q];
end;
end;
function lca(x,y:longint):longint;
var
i:longint;
begin
if d[x]>d[y] then swap(x,y);
for i:=0 to 20 do
if ((d[y]-d[x]) and (1<0) then y:=jump[y,i];
if x=y then exit(x);
for i:=20 downto 0 do
if jump[x,i]<>jump[y,i] then
begin
x:=jump[x,i];
y:=jump[y,i];
end;
exit(jump[x,0]);
end;
procedure virtree;
var
x,anc,top,p,i:longint;
begin
top:=1; z[top]:=1;
for i:=1 to m do
begin
x:=a[i]; anc:=lca(z[top],x);
while (top>0) and (d[z[top]]>d[anc]) do
begin
if (d[z[top-1]]<=d[anc]) then
begin
p:=z[top];
dec(top);
if d[z[top]]<>d[anc] then
begin
inc(top);
z[top]:=anc;
end;
connect2(anc,p);
break;
end;
connect2(z[top-1],z[top]);
dec(top);
end;
//
if z[top]<>x then
begin
inc(top);
z[top]:=x;
end;
end;
for i:=top-1 downto 1 do connect2(z[i],z[i+1]);
end;
procedure dp(x:longint);
var
p,q:longint;
begin
inc(tl); que[tl]:=x; sum[x]:=0;
if flag[x] then
begin
size[x]:=1;
maxn[x]:=0;
minn[x]:=0;
end else
begin
size[x]:=0;
maxn[x]:=-maxlongint div 10;
minn[x]:=maxlongint div 10;
end;
q:=la[x];
while q<>0 do
begin
p:=ot[q];
dp(p);
inc(ans,sum[x]*int64(size[p]) + int64(le[q])*int64(size[x])*int64(size[p]) + sum[p]*int64(size[x]));
inc(size[x],size[p]);
inc(sum[x],int64(le[q])*int64(size[p]) + sum[p]);
ans_max:=max(ans_max,maxn[p]+le[q]+maxn[x]);
ans_min:=min(ans_min,minn[p]+le[q]+minn[x]);
minn[x]:=min(minn[x],minn[p]+le[q]);
maxn[x]:=max(maxn[x],maxn[p]+le[q]);
q:=pr[q];
end;
end;
begin
read(n);
for i:=1 to n-1 do
begin
read(x,y);
connect(x,y);
connect(y,x);
end;
vis[1]:=true; d[1]:=1; dfs(1);
for j:=1 to 20 do
for i:=1 to n do jump[i,j]:=jump[jump[i,j-1],j-1];
//
read(q);
for j:=1 to q do
begin
read(m);
for i:=1 to m do read(a[i]);
for i:=1 to m do flag[a[i]]:=true;
sort(1,m);
virtree;
tl:=0; ans:=0; ans_min:=maxlongint; ans_max:=-ans_min;
dp(1);
writeln(ans,' ',ans_min,' ',ans_max);
ll:=0;
for i:=1 to m do flag[a[i]]:=false;
for i:=1 to tl do la[que[i]]:=0;
end;
end.
——by Eirlys