#include <stdio.h>
int main()
{
puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢");
puts("网址:blog.csdn.net/vmurder/article/details/46506883");
}
首先构建虚树,然后在虚树上DP。
过程很简单。
先找出每个虚树节点 i 旁边最近的询问节点 neari (因为有一些lca也被加入了虚树所以虚树节点不全是询问节点,呃怕你们不懂,但其实这是废话Qwq。)
然后对于每条链 [l,r] ,找出 [nearl,nearr] 中点,然后两边随便给一给就好了。。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LOGN 20
#define N 301000
#define ti tree[i]
#define pi point[i]
#define inf 0x3f3f3f3f
using namespace std;
struct Eli
{
int v,n;
}e[N<<1];
int head[N],cnt;
inline void add(int u,int v)
{
e[++cnt].v=v;
e[cnt].n=head[u];
head[u]=cnt;
}
int f[N][LOGN],dep[N];
int size[N],dfn[N];
int init(int x,int p) // 处理出以上四个数组
{
int i,u,v;
dfn[x]=++cnt,size[x]=1;
f[x][0]=p,dep[x]=dep[p]+1;
for(i=1;f[x][i-1];i++)
f[x][i]=f[f[x][i-1]][i-1];
for(i=head[x];i;i=e[i].n)
{
v=e[i].v;
if(v==p)continue;
size[x]+=init(v,x);
}
return size[x];
} // 会不会爆栈我并不想考虑。并不想。因为很不想所以说两遍。
int getlca(int x,int y) // 得到x、y的lca
{
if(dep[x]<dep[y])swap(x,y);
for(int i=LOGN-1;~i;i--)
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y)return x;
for(int i=LOGN-1;~i;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int find(int x,int d) // 找到x的深度为d的祖先
{
for(int i=LOGN-1;~i;i--)
if(dep[f[x][i]]>=d)
x=f[x][i];
return x;
}
int n,m,p,rec[N];
bool cmp_dfn(int a,int b){return dfn[a]<dfn[b];}
int stk[N],top,val[N],ans[N];
int point[N],tree[N],fa[N];
int near[N],dis[N],len[N];
void solve()
{
int i,x,s;
cnt=top=0;
scanf("%d",&p);
for(i=1;i<=p;i++)
{
scanf("%d",&pi);
tree[++cnt]=pi;
// rec : point[]被排序了,得存一个下来
rec[i]=near[pi]=pi;
dis[pi]=ans[pi]=0;
}
sort(point+1,point+p+1,cmp_dfn);
for(i=1;i<=p;i++) // 构建虚树
{
if(!top)fa[stk[++top]=pi]=0;
else {
int lca=getlca(pi,stk[top]);
for(;dep[stk[top]]>dep[lca];top--)
if(dep[stk[top-1]]<=dep[lca])
fa[stk[top]]=lca;
if(stk[top]!=lca)
{
fa[lca]=stk[top];
stk[++top]=tree[++cnt]=lca;
near[lca]=0,dis[lca]=inf;
}
fa[stk[++top]=pi]=lca;
}
}
sort(tree+1,tree+cnt+1,cmp_dfn);
for(i=1;i<=cnt;i++)
{
val[ti]=size[ti];
len[ti]=dep[ti]-dep[fa[ti]];
}
for(i=cnt;i>=2;i--) // 给每个虚树节点找最近要求点
{
if(dis[fa[ti]]>dis[ti]+len[ti])
{
dis[fa[ti]]=dis[ti]+len[ti];
near[fa[ti]]=near[ti];
}
else if(dis[fa[ti]]==dis[ti]+len[ti])
near[fa[ti]]=min(near[fa[ti]],near[ti]);
}
for(i=2;i<=cnt;i++) // 给每个虚树节点找最近要求点
{
if(dis[ti]>dis[fa[ti]]+len[ti])
{
dis[ti]=dis[fa[ti]]+len[ti];
near[ti]=near[fa[ti]];
}
else if(dis[ti]==dis[fa[ti]]+len[ti])
near[ti]=min(near[ti],near[fa[ti]]);
}
for(i=1;i<=cnt;i++) // 每条链找中点进行处理
{
if(i==1)ans[near[ti]]+=n-size[ti];
else {
x=find(ti,dep[fa[ti]]+1);
s=size[x]-size[ti];
val[fa[ti]]-=size[x];
if(near[fa[ti]]==near[ti])
ans[near[ti]]+=s;
else {
int num=dis[fa[ti]]+dis[ti]+len[ti]+1;
int mid=dep[ti]-((num+1)/2-dis[ti])+1;
if((num&1)&&near[ti]>near[fa[ti]])mid++;
x=size[x]-size[find(ti,mid)];
ans[near[fa[ti]]]+=x;
ans[near[ti]]+=s-x;
}
}
}
for(i=1;i<=cnt;i++)ans[near[ti]]+=val[ti];
for(i=1;i<=p;i++)printf("%d ",ans[rec[i]]);
puts("");
}
int main()
{
int i,j,k;
int a,b,c;
scanf("%d",&n);
for(i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}cnt=0;
init(1,0);
for(scanf("%d",&m);m--;)solve();
return 0;
}