III.[HEOI2014]大工程
仍然建出虚树。
我们考虑设\(sz_x\)表示\(x\)子树中实点(即原本点集中的点)的数量,再设\(f_x\)表示\(x\)到\(x\)子树中某个实点的最长路径,\(g_x\)则表示最短路径。
我们先考虑求\(\min\)和\(\max\)的部分。
对于一个实点,它初始值\(f_x=g_x=0\)(有一条到自己的路径);对于一条虚点,它初始\(f_x=-\infty,g_x=\infty\)。
我们枚举它的儿子\(y\)。设\(z\)表示这条边的边权(实际上就是\(y\)的深度减去\(x\)的深度),则我们可以用\(f_x+z+f_y\)拼成一条最长路径,\(g_x+z+g_y\)拼成一条最短路径。然后更新\(f\)与\(g\)即可,就跟正常树中求直径的做法没啥区别。
我们再考虑求和的地方。
显然,对于一条边\((x,y)\)(这里令\(x\)为\(y\)在虚树上的父亲),它总共会被经过\(sz_y\times(q-sz_y)\)次,其中\(q\)是点集大小。这也很好理解——对于\(y\)侧树中每个实点,它都会与\(x\)侧树中每个实点有一条边。则只需要用次数乘上边权,再求和,便是答案。
这题我担心\(n\log n\)空间的倍增或者ST表可能会MLE,就使用了树剖。但是总复杂度仍然是\(O(n\log n)\)的。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,fa[1001000],dep[1001000],dfn[1001000],top[1001000];
namespace real{
int sz[1001000],son[1001000],tot;
vector<int>v[1001000];
void dfs1(int x,int Fa){
fa[x]=Fa,dep[x]=dep[Fa]+1,dfn[x]=++tot,sz[x]=1;
for(auto y:v[x]){
if(y==fa[x])continue;
dfs1(y,x),sz[x]+=sz[y];
if(sz[son[x]]<sz[y])son[x]=y;
}
}
void dfs2(int x){
if(son[x])top[son[x]]=top[x],dfs2(son[x]);
for(auto y:v[x])if(y!=fa[x]&&y!=son[x])top[y]=y,dfs2(y);
}
}
int LCA(int x,int y){
while(top[x]!=top[y])dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
return dep[x]<dep[y]?x:y;
}
namespace imag{
int q,stk[1001000],tp,a[1001000],sz[1001000],mn,mx,f[1001000],g[1001000];
ll res;
vector<int>v[1001000];
bool cmp(int x,int y){
return dfn[x]<dfn[y];
}
void ins(int x){
sz[x]=1;
if(!tp){stk[++tp]=x;return;}
int lca=LCA(x,stk[tp]);
while(tp>=2&&dep[lca]<dep[stk[tp-1]])v[stk[tp-1]].push_back(stk[tp]),tp--;
if(tp&&dep[lca]<dep[stk[tp]])v[lca].push_back(stk[tp--]);
if(!tp||stk[tp]!=lca)stk[++tp]=lca;
stk[++tp]=x;
}
void fin(){
while(tp>=2)v[stk[tp-1]].push_back(stk[tp]),tp--;
tp--;
}
void dfs0(int x){
if(sz[x])f[x]=g[x]=0;
else f[x]=-0x3f3f3f3f,g[x]=0x3f3f3f3f;
for(auto y:v[x]){
dfs0(y);
mx=max(mx,f[x]+(dep[y]-dep[x])+f[y]),f[x]=max(f[x],(dep[y]-dep[x])+f[y]);
mn=min(mn,g[x]+(dep[y]-dep[x])+g[y]),g[x]=min(g[x],(dep[y]-dep[x])+g[y]);
}
}
void dfs1(int x){
for(auto y:v[x])dfs1(y),res+=1ll*(dep[y]-dep[x])*sz[y]*(q-sz[y]),sz[x]+=sz[y];
}
void dfs2(int x){
sz[x]=0;
for(auto y:v[x])dfs2(y);
v[x].clear();
}
void work(){
res=0,mn=0x3f3f3f3f,mx=-0x3f3f3f3f,scanf("%d",&q);
for(int i=1;i<=q;i++)scanf("%d",&a[i]);
sort(a+1,a+q+1,cmp);
if(a[1]!=1)stk[++tp]=1;
for(int i=1;i<=q;i++)ins(a[i]);
fin();
dfs0(1);
dfs1(1);
printf("%lld %lld %lld\n",res,mn,mx);
dfs2(1);
}
}
int main(){
scanf("%d",&n);
for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),real::v[x].push_back(y),real::v[y].push_back(x);
real::dfs1(1,0),top[1]=1,real::dfs2(1);
scanf("%d",&m);
while(m--)imag::work();
return 0;
}