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

5 2 3
1 4
1 3
4 2
4 5
4 3
3 2
6 9
1 8
5 3

Sample Output

6
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 }

 

posted @ 2016-12-27 22:20  ljh_2000  阅读(310)  评论(0编辑  收藏  举报