【问题描述】
世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。世界树的形态可以用一个数学模型来描述: 世界树中有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年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。
现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。
【输入格式】
输入文件名为worldtree.in。
第一行为一个正整数n,表示世界树中种族的个数。
接下来n-1行,每行两个正整数x,y,表示x聚居地与y聚居地之间有一条长度为1的双
向道路。
接下来一行为一个正整数q,表示国王询问的年数。
接下来q块,每块两行:
第i块的第一行为1个正整数m[i],表示第i年授权的临时议事处的个数。
第i 块的第二行为m[i]个正整数h[1]、h[2]、…、h[m[i]],表示被授权为临时议事处
的聚居地编号(保证互不相同)。
【输出格式】
输出文件名为worldtree.out。
输出包含q行,第i 行为m[i]个整数,该行的第j(j=1,2,…,m[i])个数表示第i年被授权的聚居地h[j]的临时议事处管理的种族个数。
世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。世界树的形态可以用一个数学模型来描述: 世界树中有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年的时间里,每一年完成授权后,当年每个临时议事处将会管理多少个种族(议事处所在的聚居地也将接受该议事处管理)。
现在这个任务交给了以智慧著称的灵长类的你:程序猿。请帮国王完成这个任务吧。
【输入格式】
输入文件名为worldtree.in。
第一行为一个正整数n,表示世界树中种族的个数。
接下来n-1行,每行两个正整数x,y,表示x聚居地与y聚居地之间有一条长度为1的双
向道路。
接下来一行为一个正整数q,表示国王询问的年数。
接下来q块,每块两行:
第i块的第一行为1个正整数m[i],表示第i年授权的临时议事处的个数。
第i 块的第二行为m[i]个正整数h[1]、h[2]、…、h[m[i]],表示被授权为临时议事处
的聚居地编号(保证互不相同)。
【输出格式】
输出文件名为worldtree.out。
输出包含q行,第i 行为m[i]个整数,该行的第j(j=1,2,…,m[i])个数表示第i年被授权的聚居地h[j]的临时议事处管理的种族个数。
这题考场上想到了树链剖分,但是不知道应该维护什么信息。回来看了Linux机房的某个大神的代码才明白怎么写。主要思想就是把议事处按照深度和编号大小排序,然后把树上的点按规则分配给第1~i个议事点,然后再去更新第i+1个议事处应该管辖哪些点(更新答案时,一个点u被点v管辖代表着点u及其子树都被点v管辖)。用树链剖分来维护每条重链上最深的被管辖的点属于哪个议事处。做到第i个议事处x时,找到所在的重链上最深的被管辖的点所属的议事处y(如果所在的重链上没有已经分配的点,就一直往root跳)。两个点之间的距离dis=dep[x]+dep[y]-2*dep[lca(x,y)]。因为x的深度必然大于等于y的深度,那么x到y这条路径上下半部分的点都属于x管辖,上半部分属于y管辖,对于到x和y距离相等的点,属于编号较小的y管辖,用线段树来维护即可。
CODE
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
class DREAD{
friend int fread();
static const int buff_max=20000000+10;
char buf[buff_max],*pt;
public:
void clear(){ for(; isspace(*pt); ++pt); }
void Init(){ buf[fread(buf,1,buff_max,stdin)]=EOF;pt=buf; }
bool eoln(){ return *pt=='\n'; }
inline bool eof(){ return *pt==EOF; }
int Int(){
bool pos=1;int res=0;
for (; !isdigit(*pt) && *pt!='-' && !eof(); ++pt);if (*pt=='-') pos=0,++pt;
for (; isdigit(*pt); ++pt) res=res*10+(*pt-'0');
return pos?res:-res;
}
}READ;
const int N_MAX=300000+10,M_MAX=600000+10;
int pos[N_MAX],pre[M_MAX],son[M_MAX],tot=0;
void add(int a,int b){
pre[++tot]=pos[a]; pos[a]=tot; son[tot]=b;
pre[++tot]=pos[b]; pos[b]=tot; son[tot]=a;
}
int N,Q;
namespace Nwork{
int qs[N_MAX],ans[N_MAX],in[N_MAX];
const int root=1;
int heavy[N_MAX],fa[N_MAX],size[N_MAX],top[N_MAX],newid[N_MAX],dep[N_MAX],usedid=0;
bool cmp(int x,int y){
return dep[x]==dep[y]?x<y:dep[x]<dep[y];
}
/*深度做第一关键字*/
int st[20][N_MAX];
void dfs1(int x=root,int f=0){
int maxsize=-1;
size[x]=1; fa[x]=f;
for (int i=1; i<=19; ++i)
st[i][x]=st[i-1][st[i-1][x]];
for (int p=pos[x]; p; p=pre[p])
if (son[p]!=f){
st[0][son[p]]=x; dep[son[p]]=dep[x]+1;
dfs1(son[p],x);
size[x]+=size[son[p]];
if (size[son[p]]>maxsize) heavy[x]=son[p],maxsize=size[son[p]];
}
}
void dfs2(int x=root,int tp=root){
newid[x]=++usedid; top[x]=tp;
if (heavy[x]) dfs2(heavy[x],tp);
for (int p=pos[x]; p; p=pre[p])
if (son[p]!=fa[x] && son[p]!=heavy[x])
dfs2(son[p],son[p]);
}
/*树链剖分*/
struct Tseg{
static const int SEG_MAX=300000*8+10;
struct Tnode{
int val,tag;
Tnode():val(0),tag(-1){}
}t[SEG_MAX];
#define ls(_) ((_)<<1)
#define rs(_) (ls(_)|1)
void change(int p,int x){
t[p].val=t[p].tag=x;
}
void update(int p){
t[p].val=t[rs(p)].val?t[rs(p)].val:t[ls(p)].val;
}
/*因为按dep排过序,所以只要找重链上最靠下的被管辖的点,靠上的点不可能被当前点管辖,即为右儿子的值*/
void pushdown(int p){
if (t[p].tag==-1) return;
change(ls(p),t[p].tag);
change(rs(p),t[p].tag);
t[p].tag=-1;
}
void Modify(int fir,int lst,int val,int p=1,int L=1,int R=N){
if (fir==L && lst==R) return change(p,val);
pushdown(p);
int mid=(L+R)>>1;
if (lst<=mid) Modify(fir,lst,val,ls(p),L,mid);
else if (fir>mid) Modify(fir,lst,val,rs(p),mid+1,R);
else Modify(fir,mid,val,ls(p),L,mid),Modify(mid+1,lst,val,rs(p),mid+1,R);
update(p);
}
int Query(int fir,int lst,int p=1,int L=1,int R=N){
if (fir==L && lst==R) return t[p].val;
pushdown(p);
int mid=(L+R)>>1;
if (lst<=mid) return Query(fir,lst,ls(p),L,mid);
else if (fir>mid) return Query(fir,lst,rs(p),mid+1,R);
else{
int Lv=Query(fir,mid,ls(p),L,mid);
int Rv=Query(mid+1,lst,rs(p),mid+1,R);
return Rv?Rv:Lv;
}
}
}SEG;
int LCA(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
for (int i; dep[x]>dep[y]; x=st[i][x])
for (i=0; dep[st[i+1][x]]>dep[y]; ++i);
for (int i; x!=y; x=st[i][x],y=st[i][y])
for (i=0; dep[st[i+1][x]]!=dep[st[i+1][y]]; ++i);
return x;
}
void Dye(int l,int r,int x){
for (; top[l]!=top[r]; r=fa[top[r]])
SEG.Modify(newid[top[r]],newid[r],x);
SEG.Modify(newid[l],newid[r],x);
}
/*把一条重链上L到R的点及其子树全部改为由x管辖*/
int dis(int x,int y){
int lca=LCA(x,y);
return dep[x]+dep[y]-2*dep[lca];
}
void main(){
dep[1]=1;
dfs1(); dfs2();
Q=READ.Int();
for (int k=1; k<=Q; ++k){
int q=READ.Int();
for (int i=1; i<=q; ++i)
in[i]=qs[i]=READ.Int(),ans[qs[i]]=0;
sort(qs+1,qs+q+1,cmp);
ans[qs[1]]=N; Dye(1,qs[1],qs[1]);
for (int i=2; i<=q; ++i){
int x=qs[i],y;
for (int tmp=x; !(y=SEG.Query(newid[top[tmp]],newid[tmp])); tmp=fa[top[tmp]]);
/*找到被管辖的点*/
int d=(dis(x,y)-(y<x))/2,tmp=x;
/*dis-(y<x)是为了解决到x和y距离相等的问题*/
for (int j; d; d-=1<<j,tmp=st[j][tmp])
for (j=0; (1<<(j+1))<=d; ++j);
Dye(tmp,x,x); ans[qs[i]]=size[tmp]; ans[y]-=ans[qs[i]];
}
for (int i=1; i<=q; ++i) printf("%d ", ans[in[i]]);
cout<<endl;
SEG.Modify(1,N,0);
}
}
}
namespace Ninit{
void init(){
N=READ.Int();
for (int i=1; i<N; ++i){
int a=READ.Int(),b=READ.Int();
add(a,b);
}
}
}
int main(){
freopen("worldtree.in","r",stdin);
freopen("worldtree.out","w",stdout);
READ.Init();
Ninit::init();
Nwork::main();
return 0;
}