仍然建出虚树。
我们考虑设\(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; }