Description
世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。
世界树的形态可以用一个数学模型来描述:世界树中有n个种族,种族的编号分别从1到n,分别生活在编号为1到n的聚居地上,种族的编号与其聚居地的编号相同。有的聚居地之间有双向的道路相连,道路的长度为1。保证连接的方式会形成一棵树结构,即所有的聚居地之间可以互相到达,并且不会出现环。定义两个聚居地之间的距离为连接他们的道路的长度;例如,若聚居地a和b之间有道路,b和c之间有道路,因为每条道路长度为1而且又不可能出现环,所卧a与c之间的距离为2。
出于对公平的考虑,第i年,世界树的国王需要授权m[i]个种族的聚居地为临时议事处。对于某个种族x(x为种族的编号),如果距离该种族最近的临时议事处为y(y为议事处所在聚居地的编号),则种族x将接受y议事处的管辖(如果有多个临时议事处到该聚居地的距离一样,则y为其中编号最小的临时议事处)。
现在国王想知道,在q年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。 现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。
虚树这个算法很妙啊。
大概讲一下虚树这个算法吧。
虚树这个东西是用来处理 多次询问一棵树的某一个点集的某先情况的做法。 简单来说就是把这些询问的点和他们两两的lca求出来,建一棵树来跑dp或者其他算法。而这个两两的lca不会超过询问的点的个数个,这个在算法的实现中会发现的,大概实现如下。
首先把所有询问的点按照在原树的dfs序排序,我们还需要一个栈来维护从根到当前节点的链。
每新枚举到一个点,如果栈为空,直接入栈,否则求它和栈顶的lca。 对于这个lca和 栈顶元素的dfs序有下面三种关系:
1. 两者相等,说明lca就是栈顶,直接把当前元素入栈。
2. lca的dfs序是小于栈顶的dfs序, 但是又大于栈顶下面元素的dfs序,这个时候就让栈顶出栈,让lca入栈。因为我们按照dfs排序了,所以此时由于lca不是栈顶,所以栈顶的子树不会再考虑了,所以可以毫不犹豫的让栈顶出栈。
3.lca的dfs序小于栈顶,也小于栈顶下面那个元素,那就一直pop到出现第一或者第二种两种情况。
由于存在第二种情况,类似于给栈中间插入了一个点,所以不能在入栈的时候建边,而是应该在一个元素出栈的时候和应该为他父亲的点连边,对于第二种情况就是lca连一条到被弹出栈顶元素的边,对于第三种情况就是栈顶下面的元素连弹出的元素。
这个就是虚树了。
对于这个题呢,我们对询问建一棵虚树,对于每个关键点肯定都是被自己管辖,但是由于我们加入到虚树的lca我们是不确定的,所以我们通过dfs1来判断对于每个lca它可以归哪个孩子管辖,再通过dfs2判断对于每个lca它是归儿子管辖近还是归父亲管辖好。
统计答案的时候,对于每条边u->v,令son为u->v上u的直系儿子,sz[i]为i这个点的原树中的大小,我们二分出一个中点mid,那么显然sz[son]-sz[mid]这一部分是归bel[u],sz[mid]-sz[v]这一部分归bel[v]。 但是这样呢对于每棵树,我们只考虑了它询问部分的子树,所以对于每个点i,我们令g[i]表示它没有考虑的节点个数,那么枚举每个虚树上的边u->v的时候,将g[u]减去sz[son],意为把这部分的子树处理了。 最后统计答案的时候再给ans[bel[i]加上 g[i]即可。
下附AC代码。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 300005
using namespace std;
int n,m,tot,top,cnt;
int st[maxn];
int a[maxn],b[maxn],c[maxn],ans[maxn],bel[maxn];
int head[maxn],nex[maxn<<1],to[maxn<<1],dfn[maxn],sz[maxn];
int dep[maxn],anc[maxn][19],dp[maxn];
void add(int x,int y)
{
if(x==0 || y==0 || x==y) return;
to[++tot]=y;nex[tot]=head[x];head[x]=tot;
}
int cmp(int i,int j)
{
return dfn[i]<dfn[j];
}
void dfs(int now,int fa)
{
for(int i=1;(1<<i)<=dep[now];i++)
anc[now][i]=(anc[anc[now][i-1]][i-1]);
sz[now]=1;
dfn[now]=++tot;
for(int i=head[now];i;i=nex[i])
if(to[i]!=fa)
{
dep[to[i]]=dep[now]+1;
anc[to[i]][0]=now;
dfs(to[i],now);
sz[now]+=sz[to[i]];
}
}
int lca(int p,int q)
{
if(dep[p]<dep[q]) swap(p,q);
int d=dep[p]-dep[q];
for(int i=0;i<=18;i++)
if((1<<i)&d)
p=anc[p][i];
if(p==q) return p;
for(int i=18;i>=0;i--)
if(anc[p][i]!=anc[q][i])
p=anc[p][i],q=anc[q][i];
return anc[p][0];
}
int dis(int p,int q)
{
return dep[p]+dep[q]-2*dep[lca(p,q)];
}
void dfs1(int now,int fa)
{
dp[now]=sz[now]; c[++cnt]=now;
// cerr<<"its "<<now<<endl;
for(int i=head[now];i;i=nex[i])
if(to[i]!=fa)
{
dfs1(to[i],now);
if(!bel[to[i]]) continue;
if(!bel[now])
{
// cerr<<"+1"<<endl;
bel[now]=bel[to[i]];
continue;
}
int dis1=dis(bel[now],now),dis2=dis(bel[to[i]],now);
if(dis2<dis1 || (dis2==dis1 && bel[to[i]]<bel[now])) bel[now]=bel[to[i]];
}
}
void dfs2(int now,int fa)
{
for(int i=head[now];i;i=nex[i])
if(to[i]!=fa)
{
if(!bel[to[i]])
{
// cerr<<"+1"<<endl;
bel[to[i]]=bel[now];
continue;
}
int dis1=dis(bel[to[i]],to[i]),dis2=dis(bel[now],to[i]);
if(dis1>dis2 || (dis2==dis1 && bel[now]<bel[to[i]])) bel[to[i]]=bel[now];
dfs2(to[i],now);
}
}
void solve(int p,int q)
{
int son=q,mid=q;
for(int i=18;i>=0;i--)
if((1<<i)+dep[p]<dep[son])
son=anc[son][i];
dp[p]-=sz[son];
// cerr<<son<<" "<<mid<<endl;
if(bel[p]==bel[q])
{
ans[bel[p]]+=(sz[son]-sz[q]);
return;
}
for(int i=18;i>=0;i--)
{
if(dep[p]<dep[anc[mid][i]])
{
int nex=anc[mid][i];
int dis1=dis(nex,bel[p]),dis2=dis(nex,bel[q]);
if(dis1>dis2 || (dis2==dis1 && bel[p]>bel[q])) mid=nex;
}
}
ans[bel[p]]+=(sz[son]-sz[mid]);
ans[bel[q]]+=(sz[mid]-sz[q]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
tot=0;
dfs(1,0);
for(int i=1;i<=n;i++) head[i]=0;
int _;
scanf("%d",&_);
// for(int i=1;i<=n;i++)
// {
// cout<<"Its "<<dep[i]<<" "<<dfn[i]<<" "<<sz[i]<<" ";
// for(int j=0;j<=3;j++)
// cout<<anc[i][j]<<" ";
// cout<<endl;
// }
while(_--)
{
tot=0;
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%d",&a[i]),b[i]=a[i],bel[a[i]]=a[i];
sort(a+1,a+1+m,cmp);
if(!bel[1]) st[++top]=1;
int temp;
// cout<<st[top]<<endl;
for(int i=1;i<=m;i++)
{
if(!top)
{
st[++top]=a[i];
continue;
}
temp=lca(a[i],st[top]);
// cerr<<st[top]<<" "<<a[i]<<" "<<temp<<endl;
while(1)
{
if(dep[st[top-1]]<=dep[temp])
{
add(temp,st[top]);
//cerr<<temp<<" "<<st[top]<<endl;
top--;
if(st[top]!=temp) st[++top]=temp;
break;
}
add(st[top-1],st[top]);
top--;
}
if(st[top]!=a[i]) st[++top]=a[i];
}
while(top>1) add(st[top-1],st[top]),top--;
// cerr<<tot<<endl;
// for(int i=1;i<=m;i++)
// cerr<<bel[a[i]]<<endl;
tot=0;
dfs1(1,0);
// for(int i=1;i<=m;i++)
// cout<<bel[a[i]]<<endl;
dfs2(1,0);
// cerr<<cnt<<endl;
for(int i=1;i<=cnt;i++)
{
// cerr<<bel[c[i]]<<endl;
for(int j=head[c[i]];j;j=nex[j])
{
// cerr<<"+1"<<endl;
solve(c[i],to[j]);
}
}
for(int i=1;i<=cnt;i++)
ans[bel[c[i]]]+=dp[c[i]];
for(int i=1;i<=m;i++)
printf("%d ",ans[b[i]]);
printf("\n");
for(int i=1;i<=cnt;i++)
ans[c[i]]=dp[c[i]]=head[c[i]]=bel[c[i]]=0;
top=cnt=tot=0;
}
}