zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",ans);
    122     }
    123 }
    124 
    125 int main()
    126 {
    127     work();
    128     return 0;
    129 }
  • 相关阅读:
    mybatis(2)操作数据库
    《构建之法》阅读笔记02
    mybatis(4)使用注解方式配置sql语句
    mybatis(5)参数传递
    Android Studio安装
    《构建之法》阅读笔记01
    反射
    mybatis(3)利用Mapper接口实现数据的增删改查
    activity组件
    mybatis(1)
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6227734.html
Copyright © 2011-2022 走看看