HNOI2014D1T2 worldtree 题解

来源:互联网 发布:热血江湖 2.0发招优化 编辑:程序博客网 时间:2024/06/07 01:47
【问题描述】
世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界。在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息、持续运转的根本基石。世界树的形态可以用一个数学模型来描述:  世界树中有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;}


0 0
原创粉丝点击