Description
国家有一个大工程,要给一个非常大的交通网络里建一些新的通道。
我们这个国家位置非常特殊,可以看成是一个单位边权的树,城市位于顶点上。
在 2 个国家 a,b 之间建一条新通道需要的代价为树上 a,b 的最短路径。
现在国家有很多个计划,每个计划都是这样,我们选中了 k 个点,然后在它们两两之间 新建 C(k,2)条 新通道。
现在对于每个计划,我们想知道:
1.这些新通道的代价和
2.这些新通道中代价最小的是多少
3.这些新通道中代价最大的是多少
n<=1000000
q<=50000并且保证所有k之和<=2*n
Solution
考虑树形dp,f[x]表示x子树内关键点到x的距离和,g[x]表示x子树内关键点数量,转移比较显然
然后二三问就是mx[x]和mn[x]表示x与最近/最远关键点的距离
我们发现Σk不大于是上虚树就over了
最开始有一个nlog^2n的做法,第一个答案就暴力插入关键点然后树链剖分求,第二、三个答案就是枚举lca然后子树内求minmax。不过仔细撕烤一下就可以发现这个和上面的dp其实是等价的。。
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
typedef long long LL;
const LL INF=1e15;
const int N=2000005;
struct Graph {
struct edge {int y,w,next;} e[N*2];
int ls[N],fa[N],edCnt;
edge operator [](int x) {
return e[x];
}
void add_edge(int x,int y,int w) {
e[++edCnt]=(edge) {y,w,ls[x]}; ls[x]=edCnt;
}
} G,T;
int bl[N],dep[N],pos[N],size[N];
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void dfs1(int now) {
size[now]=1;
for (int i=G.ls[now];i;i=G[i].next) {
if (G[i].y==G.fa[now]) continue;
G.fa[G[i].y]=now; dep[G[i].y]=dep[now]+1;
dfs1(G[i].y); size[now]+=size[G[i].y];
}
}
void dfs2(int now,int up) {
bl[now]=up; pos[now]=++pos[0];
int mx=0;
for (int i=G.ls[now];i;i=G[i].next) {
if (G[i].y!=G.fa[now]&&size[G[i].y]>size[mx]) mx=G[i].y;
}
if (!mx) return ;
dfs2(mx,up);
for (int i=G.ls[now];i;i=G[i].next) {
if (G[i].y!=G.fa[now]&&G[i].y!=mx) dfs2(G[i].y,G[i].y);
}
}
int get_lca(int x,int y) {
for (;bl[x]!=bl[y];) {
if (dep[bl[x]]<dep[bl[y]]) std:: swap(x,y);
x=G.fa[bl[x]];
}
return dep[x]<dep[y]?x:y;
}
int get_dis(int x,int y) {
int lca=get_lca(x,y);
return dep[x]+dep[y]-dep[lca]*2;
}
bool cmp(int x,int y) {
return pos[x]<pos[y];
}
void solve() {
static int p[N],h[N],stack[N];
static LL mx[N],mn[N],f[N],g[N];
int k=read(),top=0,m=k;
rep(i,1,k) h[i]=p[i]=read();
std:: sort(p+1,p+k+1,cmp);
rep(i,1,k) {
if (!top) {
stack[++top]=p[i];
continue;
}
int lca=get_lca(p[i],stack[top]);
for (;dep[lca]<dep[stack[top]];top--) {
if (top&&dep[stack[top-1]]<=dep[lca]) {
T.fa[stack[top]]=lca;
}
}
if (lca!=stack[top]) {
T.fa[lca]=stack[top];
stack[++top]=lca;
h[++m]=lca;
f[lca]=g[lca]=0;
mn[lca]=INF; mx[lca]=0;
}
T.fa[p[i]]=stack[top];
stack[++top]=p[i];
}
rep(i,1,k) {
f[p[i]]=0,g[p[i]]=1;
mn[p[i]]=mx[p[i]]=0;
}
LL ans1=0,ans2=INF,ans3=0;
std:: sort(h+1,h+m+1,cmp);
rep(i,1,m) T.add_edge(T.fa[h[i]],h[i],get_dis(T.fa[h[i]],h[i]));
drp(ti,m,1) {
int now=h[ti];
for (int i=T.ls[now];i;i=T[i].next) {
ans1+=(f[T[i].y]+g[T[i].y]*T[i].w)*g[now]+f[now]*g[T[i].y];
ans2=std:: min(ans2,mn[now]+mn[T[i].y]+T[i].w);
ans3=std:: max(ans3,mx[now]+mx[T[i].y]+T[i].w);
g[now]+=g[T[i].y];
f[now]+=f[T[i].y]+g[T[i].y]*T[i].w;
mx[now]=std:: max(mx[now],mx[T[i].y]+T[i].w);
mn[now]=std:: min(mn[now],mn[T[i].y]+T[i].w);
}
}
printf("%lld %lld %lld\n", ans1,ans2,ans3);
T.edCnt=0;
rep(i,1,m) T.ls[h[i]]=T.fa[h[i]]=0;
}
int main(void) {
freopen("data.in","r",stdin);
freopen("myp.out","w",stdout);
int n=read();
rep(i,2,n) {
int x=read(),y=read();
G.add_edge(x,y,1);
G.add_edge(y,x,1);
}
dfs1(dep[1]=1); dfs2(1,1);
for (int Q=read();Q--;) solve();
return 0;
}