挂上点分治的标签是因为如果数据范围小一点我就真的去写点分了。。。
先把虚树造出来。统计所有路径的最值和权值和。。。点分治!
成功被数据范围拦了下来。。。(刚学点分治和虚树的我表示两样能一块练多好)
考虑$DP$,最大值类似于树的直径,只不过两端点必须为关键点。
设$ma(i)$为以点$i$到其子树中的关键点的最长路径长度。若$i$为关键点,$ma(i)$初值为$0$,否则为$-inf$,然后和树的直径一样$DP$即可。最小值类似。
对于权值和,记$siz(i)$为点$i$的子树中关键点的个数(包括$i$),$len(i)$为点$i$到其子树中所有关键点的路径权值之和。
显然,$siz(i)=\sum\limits_{edge(i,j)}siz(j)$,$len(i)=\sum\limits_{edge(i,j)}len(j)+siz(j)*length(edge(i,j))$。
对答案的贡献即$\sum\limits_{edge(i,j)}siz(i)*[len(j)+siz(j)*length(edge(i,j))]+siz(j)*len(i)$,注意此处的$siz(i)$和$len(i)$是统计到$j$时的值。
其实还有更简单的方法,直接统计每条边被经过的次数乘上边长,即$length(edge(i,j))*siz(j)*(k-siz(j))$
复杂度$O(\sum k\log n)$,$\log n$是小常数树剖$lca$和排序。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#define maxn 1000005
#define inf 0x3f3f3f3f
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
struct edge{
int pre,to,l;
};
namespace origin{
int h[maxn],seg[maxn],top[maxn],son[maxn],fa[maxn],deep[maxn],siz[maxn],num,all;
edge e[maxn<<1];
inline void add(int from,int to){
e[++num].pre=h[from],h[from]=num,e[num].to=to;
}
void dfs1(int node=1){
siz[node]=1;
int x;
for(register int i=h[node];i;i=e[i].pre){
x=e[i].to;
if(!siz[x]){
fa[x]=node,deep[x]=deep[node]+1;
dfs1(x),siz[node]+=siz[x];
if(siz[x]>siz[son[node]])son[node]=x;
}
}
}
void dfs2(int node=1){
seg[node]=++all;
if(son[node]){
top[son[node]]=top[node],dfs2(son[node]);
int x;
for(register int i=h[node];i;i=e[i].pre){
x=e[i].to;
if(!seg[x])top[x]=x,dfs2(x);
}
}
}
int lca(int x,int y){
while(top[x]!=top[y])deep[top[x]]<deep[top[y]]?y=fa[top[y]]:x=fa[top[x]];
return deep[x]<deep[y]?x:y;
}
}
namespace virt{
int ma[maxn],mi[maxn],h[maxn],siz[maxn],num,ans2,ans3;
long long len[maxn],ans1;
bool vis[maxn];
vector<int>p;
edge e[maxn<<1];
inline void add(int from,int to){
int l=abs(origin::deep[from]-origin::deep[to]);
e[++num].pre=h[from],h[from]=num,e[num].to=to,e[num].l=l;
e[++num].pre=h[to],h[to]=num,e[num].to=from,e[num].l=l;
}
struct Monostack{
int sta[maxn],top;
void clear(){
for(register int i=2;i<=top;++i)add(sta[i],sta[i-1]);
top=0;
}
void push(int x){
sta[++top]=x;
}
void check(int x){
if(x==1)return;
int l=origin::lca(x,sta[top]);
h[x]=0;
if(l!=sta[top]){
while(origin::seg[l]<origin::seg[sta[top-1]])add(sta[top],sta[top-1]),--top;
--top;
if(l!=sta[top])h[l]=0,add(sta[top+1],l),push(l);
else add(sta[top+1],l);
}
push(x);
}
}s;
inline bool cmp(int x,int y){
return origin::seg[x]<origin::seg[y];
}
void build(){
h[1]=0,s.push(1);
for(vector<int>::iterator iter=p.begin();iter!=p.end();++iter)s.check(*iter);
s.clear();
}
void dfs(int node=1,int f=0){
siz[node]=vis[node],len[node]=0,mi[node]=vis[node]?0:inf,ma[node]=vis[node]?0:-inf;
int x;
long long l;
for(register int i=h[node];i;i=e[i].pre){
x=e[i].to;
if(x==f)continue;
dfs(x,node);
l=len[x]+1ll*e[i].l*siz[x];
ans1+=siz[node]*l+siz[x]*len[node];
siz[node]+=siz[x],len[node]+=l;
ans2=min(ans2,mi[node]+mi[x]+e[i].l);
ans3=max(ans3,ma[node]+ma[x]+e[i].l);
mi[node]=min(mi[node],mi[x]+e[i].l);
ma[node]=max(ma[node],ma[x]+e[i].l);
}
}
void solve(){
p.clear(),num=ans1=ans3=0,ans2=inf;
int n=read(),x;
for(register int i=1;i<=n;++i)p.push_back(x=read()),vis[x]=1;
sort(p.begin(),p.end(),cmp);
build(),dfs();
printf("%lld %d %d\n",ans1,ans2,ans3);
for(vector<int>::iterator iter=p.begin();iter!=p.end();++iter)vis[*iter]=0;
}
}
int main(){
int n=read(),x,y;
for(register int i=1;i<n;++i)x=read(),y=read(),origin::add(x,y),origin::add(y,x);
origin::dfs1(),origin::dfs2();
n=read();
while(n--)virt::solve();
}