题目
题目描述
世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。
世界树的形态可以用一个数学模型来描述:世界树中有 nn 个种族,种族的编号分别从 11 到 nn,分别生活在编号为 11 到 nn 的聚居地上,种族的编号与其聚居地的编号相同。有的聚居地之间有双向的道路相连,道路的长度为 11。保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环。定义两个聚居地之间的距离为连接他们的道路的长度;例如,若聚居地 aa 和 bb 之间有道路,bb 和 cc 之间有道路,因为每条道路长度为 11 而且又不可能出现环,所以 aa 与 cc 之间的距离为 22。
出于对公平的考虑,第 ii 年,世界树的国王需要授权 m_im
i
个种族的聚居地为临时议事处。对于某个种族 xx(xx 为种族的编号),如果距离该种族最近的临时议事处为 yy(yy 为议事处所在聚居地的编号),则种族 xx 将接受 yy 议事处的管辖(如果有多个临时议事处到该聚居地的距离一样,则 yy 为其中编号最小的临时议事处)。
现在国王想知道,在 qq 年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。
输入格式
第一行为一个正整数 nn,表示世界树中种族的个数。接下来 n-1n−1 行,每行两个正整数 x,yx,y,表示 xx 聚居地与 yy 聚居地之间有一条长度为 11 的双向道路。接下来一行为一个正整数 qq,表示国王询问的年数。接下来 qq 块,每块两行:第 ii 块的第一行为 11 个正整数 m_im
i
,表示第 ii 年授权的临时议事处的个数。第 ii 块的第二行为 m_im
i
个正整数 h_1, h_2,\ldots,h_{m_i}h
1
,h
2
,…,h
m
i
,表示被授权为临时议事处的聚居地编号(保证互不相同)。
输出格式
输出包含 qq 行,第 ii 行为 m_im
i
个整数,该行的第 jj (j=1, 2,\ldots, m_ij=1,2,…,m
i
) 个数表示第 ii 年被授权的聚居地 h_jh
j
的临时议事处管理的种族个数。
输入输出样例
输入 #1复制
10
2 1
3 2
4 3
5 4
6 1
7 3
8 3
9 4
10 1
5
2
6 1
5
2 7 3 6 9
1
8
4
8 7 10 3
5
2 9 3 5 8
输出 #1复制
1 9
3 1 4 1 1
10
1 1 3 5
4 1 3 1 1
说明/提示
对于 100%100% 的数据,N\leq 300000N≤300000, q\leq 300000q≤300000, \sum^q_{i=1}m_i \leq 300000∑
i=1
q
m
i
≤300000。
思路
虚树+超麻烦的dp
一个点x拥有的点:sz[x]-sz[儿子]
x,f[x] 分界点p(p上面的属于f[x])
则f[x]拥有的点:sz[p方向上的儿子]-sz[p]
x拥有的点:sz[p]-sz[x]
p与x的dis
dis+dis[x]=(dis(f[x],x)-dis)+dis[y]
dis=(dis(f[x],x)-dis[x]+dis[y])/2
dp时,每个点记录near[i] 最近点,dis[i] 与其距离
代码
#include<bits/stdc++.h>
using namespace std;
#define N 300077
int t[N];
struct E
{
int to,next;
}l[2000000];
int e;
void add_e(int x,int y)
{
l[++e]=(E){y,t[x]};t[x]=e;
}
struct tree
{
int f[20],dep,sz,dfn;
}T[N];int tot;
void dfs(int x,int f,int dep)
{
T[x].sz=1;T[x].dep=dep;
T[x].f[0]=f;
T[x].dfn=++tot;
int i,y;
for (i=0,y=f;y=T[y].f[i];T[x].f[++i]=y);
++dep;
for (i=t[x];i;i=l[i].next)
if ((y=l[i].to)!=f)
{
dfs(y,x,dep);
T[x].sz+=T[y].sz;
}
}
int jump(int x,int l)
{
for (int i=0;l;++i,l>>=1)
if (l&1) x=T[x].f[i];
return x;
}
int get_lca(int x,int y)
{
if (T[x].dep<T[y].dep) swap(x,y);
x=jump(x,T[x].dep-T[y].dep);
if (x==y) return x;
for (int i=19;i>=0;--i)
if (T[x].f[i]!=T[y].f[i])
{
x=T[x].f[i];y=T[y].f[i];
}
return T[x].f[0];
}
bool dfn_xiao(int x,int y)
{
return T[x].dfn<T[y].dfn;
}
int i,x,y;
int q0[N],q[N],num0,num;
int st[N],top;
struct xutree
{
int f;
bool d;
}_T[N];
int near[N],dis[N],sz[N];
void build()
{
sort(q+1,q+num+1,dfn_xiao);
st[top=1]=q[1];
_T[q[1]]=(xutree){0,1};
for (i=2;i<=num0;++i)
{
x=q[i];
int lca=get_lca(y=st[top],x);
if (lca!=y)
{
while (T[lca].dep<T[st[top]].dep) y=st[top--];
if (st[top]!=lca)
{
_T[y].f=lca;
_T[lca]=(xutree){st[top],0};
st[++top]=lca;q[++num]=lca;
}
}
_T[x]=(xutree){st[top],1};st[++top]=x;
}
sort(q+1,q+num+1,dfn_xiao);
}
void upd(int _dis,int _near,int y)
{
if (_dis<dis[y]||_dis==dis[y]&&_near<near[y])
{
dis[y]=_dis;near[y]=_near;
}
}
int n;
int ans[N];
void dp()
{
int _dis;
for (i=1;i<=num;++i)
{
x=q[i];
sz[x]=T[x].sz;
if (_T[x].d) {near[x]=x;dis[x]=0;}
else dis[x]=1<<30;
}
for (i=num;i>1;--i)
{
x=q[i];y=_T[x].f;
_dis=dis[x]+T[x].dep-T[y].dep;
upd(_dis,near[x],y);
}
for (i=2;i<=num;++i)
{
x=q[i];y=_T[x].f;
_dis=dis[y]+T[x].dep-T[y].dep;
upd(_dis,near[y],x);
}
int k;
for (i=num;i>1;--i)
{
x=q[i];y=_T[x].f;
k=jump(x,T[x].dep-T[y].dep-1);
sz[y]-=T[k].sz;
}
x=q[1];
ans[near[x]]=sz[x]+n-T[x].sz;
for (i=2;i<=num;++i)
{
x=q[i];
ans[near[x]]+=sz[x];
y=_T[x].f;k=jump(x,T[x].dep-T[y].dep-1);
if (near[y]==near[x]) ans[near[x]]+=T[k].sz-T[x].sz;
else
{
_dis=T[x].dep-T[y].dep-dis[x]+dis[y];
if (_dis&1) _dis>>=1; else
if (near[x]<near[y]) _dis>>=1;
else (_dis>>=1)-=1;
int p=jump(x,_dis);
ans[near[x]]+=T[p].sz-T[x].sz;
ans[near[y]]+=T[k].sz-T[p].sz;
}
}
}
int main()
{
freopen("1.in","r",stdin);
scanf("%d",&n);
for (i=1;i<n;++i)
{scanf("%d%d",&x,&y);
add_e(x,y);add_e(y,x);
}
dfs(1,0,1);
int m;
scanf("%d",&m);
while (m--)
{
scanf("%d",&num);num0=num;
for (i=1;i<=num;++i) {scanf("%d",q+i);q0[i]=q[i];}
build();
dp();
for (i=1;i<=num0;++i) {printf("%d ",ans[q0[i]]);ans[q0[i]]=0;}
printf("\n");
}
}