BZOJ4539 [Hnoi2016]树
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
Description
小A想做一棵很大的树,但是他手上的材料有限,只好用点小技巧了。开始,小A只有一棵结点数为N的树,结
点的编号为1,2,…,N,其中结点1为根;我们称这颗树为模板树。小A决定通过这棵模板树来构建一颗大树。构建过
程如下:(1)将模板树复制为初始的大树。(2)以下(2.1)(2.2)(2.3)步循环执行M次(2.1)选择两个数字a,b,
其中1<=a<=N,1<=b<=当前大树的结点数。(2.2)将模板树中以结点a为根的子树复制一遍,挂到大树中结点b的下
方(也就是说,模板树中的结点a为根的子树复制到大树中后,将成为大树中结点b的子树)。(2.3)将新加入大树
的结点按照在模板树中编号的顺序重新编号。例如,假设在进行2.2步之前大树有L个结点,模板树中以a为根的子
树共有C个结点,那么新加入模板树的C个结点在大树中的编号将是L+1,L+2,…,L+C;大树中这C个结点编号的大小
顺序和模板树中对应的C个结点的大小顺序是一致的。下面给出一个实例。假设模板树如下图:
根据第(1)步,初始的大树与模板树是相同的。在(2.1)步,假设选择了a=4,b=3。运行(2.2)和(2.3)后,得到新的
大树如下图所示
现在他想问你,树中一些结点对的距离是多少。
Input
第一行三个整数:N,M,Q,以空格隔开,N表示模板树结点数,M表示第(2)中的循环操作的次数,Q 表示询问数
量。接下来N-1行,每行两个整数 fr,to,表示模板树中的一条树边。再接下来M行,每行两个整数x,to,表示将模
板树中 x 为根的子树复制到大树中成为结点to的子树的一次操作。再接下来Q行,每行两个整数fr,to,表示询问
大树中结点 fr和 to之间的距离是多少。N,M,Q<=100000
Output
输出Q行,每行一个整数,第 i行是第 i个询问的答案。
Sample Input
1 4
1 3
4 2
4 5
4 3
3 2
6 9
1 8
5 3
Sample Output
3
3
HINT
经过两次操作后,大树变成了下图所示的形状:
结点6到9之间经过了6条边,所以距离为6;类似地,结点1到8之间经过了3条边;结点5到3之间也经过了3条边。
正解:主席树+倍增lca
解题报告:
码农题+细节题。
这道题的想法很直接,就是把一棵子树复制过去之后不必复制整棵树,而是把这棵树作为一个结点,而新的树没必要构出来,我们只需要知道每个点代表的是哪棵子树即可。
具体做法:
首先对于模板树进行预处理,dfs一遍得到dfs序,为了维护子树第k小编号的查询操作,构主席树。
(ps:在主席树上查找子树第$k$小编号是一个经典问题,按照$dfs$序依次把每个编号相应的位置$+1$,然后就是常规的区间第k小问题)
对于复制操作,复制操作的话对于每次复制我只需要记录这次复制的这棵子树的根,根接到了哪个点的下方,当然上述记录的都是在原树中的相应编号。同时为了方便之后计算这棵子树内到根的距离,不妨记录一下根在原树到$1$的距离,这样以来当我想查询这棵子树内的某个点到根的距离时,直接用在原树中与1的距离之差即可。
对于最后的查询操作,需要仔细考虑了,有很多细节。
当$x$和$y$处在同一块中(也就是新树的同一结点上时),直接查询。否则,先求出在$x、y$各自所处的块(也就是新树上的两个结点,不妨设为$Rx、Ry$,且$deep[Ry]<deep[Rx]$)在新树上的$lca$,如果$Ry=lca$,则不用处理;否则就把$x、y$先跳到所在的块的顶端,记录贡献,再一直跳到$lca$的儿子结点(实际上就是儿子结点所表示的块中的根结点),接着往上走一步即可进入$lca$的块中。对于同一块中的直接查询即可。细节的话太多不赘述了,最重要的一点就是因为最大情况下,点数肯定是超$int$的,所以最好是都开$long long$。
1 //It is made by ljh2000 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <algorithm> 8 using namespace std; 9 typedef long long LL; 10 const int MAXN = 200011; 11 int n,m,q,up[MAXN]/*每个块的根相连的点,对应的是在原树中的编号*/; 12 int root[MAXN];//每个块的根,对应的是在原树中的编号 13 int kcnt;//块的个数 14 int rt[MAXN];//主席树上的每个点的线段树结点编号的开始位置; 15 int D[MAXN];//每个块的根结点在原树中的dis 16 LL tot,ans,id[MAXN]; 17 struct node{ int ls,rs,nn; }t[MAXN*20]; 18 struct Tree{ 19 int ecnt,first[MAXN],next[MAXN*2],to[MAXN*2],size[MAXN],f[MAXN][18]; 20 int dfn[MAXN],pos[MAXN]; 21 LL deep[MAXN],dis[MAXN],w[MAXN*2]; 22 inline int jump(int x,int y){ for(int i=17;i>=0;i--) if(deep[f[x][i]]>deep[y]) x=f[x][i]; return x; }//调到lca的儿子结点 23 inline void link(int x,int y,LL z){ 24 next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z; 25 next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=z; 26 } 27 28 inline void dfs(int x,int fa){ 29 size[x]=1; dfn[x]=++ecnt; pos[ecnt]=x; 30 for(int j=1;j<=17;j++) if(f[x][j-1]!=0) f[x][j]=f[f[x][j-1]][j-1]; else break; 31 for(int i=first[x];i;i=next[i]) { 32 int v=to[i]; if(v==fa) continue; f[v][0]=x; 33 deep[v]=deep[x]+1; dis[v]=dis[x]+w[i]; 34 dfs(v,x); size[x]+=size[v]; 35 } 36 } 37 38 inline int lca(int x,int y){ 39 if(deep[x]<deep[y]) swap(x,y); int t=0; while((1<<t)<=deep[x]) t++; t--; 40 for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=f[x][i]; if(x==y) return x; 41 for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; 42 return f[x][0]; 43 } 44 45 }t1,t2; 46 47 inline int getint(){ 48 int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar(); 49 if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; 50 } 51 52 inline LL getlong(){ 53 LL w=0;int q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar(); 54 if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; 55 } 56 57 inline int build(int k,int l,int r,int val){ 58 tot++; t[tot]=t[k]; t[tot].nn++; 59 if(l==r) return tot; int cc=tot,mid=(l+r)>>1; 60 if(val<=mid) t[cc].ls=build(t[k].ls,l,mid,val); 61 else t[cc].rs=build(t[k].rs,mid+1,r,val); 62 return cc; 63 } 64 65 inline int query(int now,int from,int l,int r,int val){ 66 if(l==r) return l; int mid=(l+r)>>1; 67 int cp=t[t[now].ls].nn-t[t[from].ls].nn; 68 if(val<=cp) return query(t[now].ls,t[from].ls,l,mid,val); 69 else return query(t[now].rs,t[from].rs,mid+1,r,val-cp); 70 } 71 72 inline int belong_block(LL x){ //查询所在的块的编号 73 if(x<=n) return 1; 74 int l=1,r=kcnt,mid; int cc=1; 75 while(l<=r) { 76 mid=(l+r)>>1; 77 if(x>=id[mid]) cc=mid,l=mid+1; 78 else r=mid-1; 79 } 80 return cc; 81 } 82 83 inline int kth_query(LL x,int kuai){ //查询在编号为kuai的块中编号为x的结点在原树中的实际编号 84 if(x<=n) return x; 85 x=x-id[kuai]+1; int R=root[kuai]; 86 int l=t1.dfn[R];int r=l+t1.size[R]-1;//!!! 87 return query(rt[r],rt[l-1],1,n,x); 88 } 89 90 inline void work(){ 91 n=getint(); m=getint(); q=getint(); int x_belong,y_belong; LL x,y; 92 for(int i=1;i<n;i++) { x=getint(); y=getint(); t1.link(x,y,1); } 93 t1.ecnt=0; t1.deep[1]=0; t1.dfs(1,0); tot=0; 94 for(int i=1;i<=n;i++) { rt[i]=build(rt[i-1],1,n,t1.pos[i]); } 95 kcnt=1; root[1]=1; id[1]=1; up[1]=0; D[0]=0; tot=n; 96 t2.ecnt=0; 97 for(int i=1;i<=m;i++) { 98 x=getint(); y=getlong(); y_belong=belong_block(y);//y所在的块的编号 99 kcnt++; D[kcnt]=t1.dis[x]; root[kcnt]=x; id[kcnt]=tot+1; 100 tot+=t1.size[x]; up[kcnt]=kth_query(y,y_belong); 101 t2.link(y_belong,kcnt,t1.dis[up[kcnt]]-D[y_belong]+1);//两个块相连的地方还有1的距离 102 } 103 t2.deep[1]=0; t2.ecnt=0; t2.dfs(1,0); 104 int Yx,Yy,Ylca;//在原树中的对应编号 105 int Xlca;//两个块在新树上的lca 106 for(int i=1;i<=q;i++) { 107 x=getlong(); y=getlong(); ans=0; x_belong=belong_block(x); y_belong=belong_block(y); 108 Yx=kth_query(x,x_belong); Yy=kth_query(y,y_belong); Ylca=t1.lca(Yx,Yy); 109 if(x_belong==y_belong) { ans=t1.dis[Yx]+t1.dis[Yy]-2*t1.dis[Ylca]; printf("%lld\n",ans); continue; }//已经处于同一块中 110 if(t2.deep[x_belong]<t2.deep[y_belong]) swap(x,y),swap(x_belong,y_belong),swap(Yx,Yy); 111 Xlca=t2.lca(x_belong,y_belong); 112 if(Xlca!=y_belong) { 113 ans=t1.dis[Yy]-D[y_belong]; ans++; y=t2.jump(y_belong,Xlca); 114 ans+=t2.dis[y_belong]-t2.dis[y];//块与块之间的距离直接累加,可以跨越 115 y=up[y];//往上走一步,走到lca代表的块中 116 } 117 else y=kth_query(y,y_belong); 118 ans+=t1.dis[Yx]-D[x_belong]; ans++; x=t2.jump(x_belong,Xlca); 119 ans+=t2.dis[x_belong]-t2.dis[x]; x=up[x]; Ylca=t1.lca(x,y); 120 ans+=t1.dis[x]+t1.dis[y]-2*t1.dis[Ylca]; 121 printf("%lld\n",ans); 122 } 123 } 124 125 int main() 126 { 127 work(); 128 return 0; 129 }