树链剖分求LCA

树链剖分中各种数组的作用:

siz[]数组,用来保存以x为根的子树节点个数

top[]数组,用来保存当前节点的所在链的顶端节点

son[]数组,用来保存重儿子

dep[]数组,用来保存当前节点的深度

fa[]数组,用来保存当前节点的父亲

tid[]数组,用来保存树中每个节点剖分后的新编号

rank[]数组,用来保存当前节点在线段树中的位置

树链剖分求LCA据说很快QWQ,反正我在洛谷上评测的时候比倍增整整快了3分之1.

蓝后我们来说怎么用树链剖分求:

1,第一种情况我们要比较的数在一条链上,比如比较3和12,直接返回深度比较小的数就好了

2,第二种情况我们要比较不在一条链上的两个数,比如我们想求一下10和8的LCA,我们只需要比较一下10和8的重链链顶的深度,我们发现竟然一样深,那我们就用比较早发现的10的链顶就好了,我们吧我们要比较的元素变为自己链顶的父亲(这样就可以实现垮链了),所以近下来我们只需要比较3和8就好,我们发现3和8不在一条链上,所以我们比较3和8链顶的深度,发现8的比较深,所以我们把我们要比较的数变成8的链顶的父亲,也就是3,所以这时候我们要比较的数就都在一条链上了,返回深度比较浅的数就好。

今下来是代码环节,我写了洛谷的3379模板题https://www.luogu.org/problem/show?pid=3379#sub

然后我把我ac的代码粘到这里QWQ

  1 #include<cstring>
  2 #include<iostream>
  3 #include<cstdio>
  4 using namespace std;
  5 #define MAX 1000
  6 
  7 struct NODE{
  8    int point,next;
  9    NODE(){next=0;};
 10 }edge[MAX<<1];
 11 
 12 int N,M,S;
 13 int siz[MAX],dep[MAX],son[MAX],fa[MAX];
 14 int top[MAX],tip[MAX],rank[MAX],tim=0;
 15 int edgenum=0,link[MAX];
 16 
 17 void ADD(int a,int b)
 18 {
 19    edgenum++;
 20    edge[edgenum].point=b;
 21    edge[edgenum].next=link[a];
 22    link[a]=edgenum;
 23 }
 24 
 25 void pt()
 26 {
 27    for(int i=1;i<=N;i++){
 28       printf("%3d",i);
 29    }
 30    cout<<endl;
 31    for(int i=1;i<=N;i++){
 32       printf("%3d",top[i]);
 33    }
 34 }
 35 
 36 void DFS(int x,int deep)
 37 {
 38    siz[x]=1;
 39    dep[x]=deep;
 40    for(int i=link[x];i;i=edge[i].next){
 41       int v=edge[i].point;
 42       if(v!=fa[x]){
 43          fa[v]=x;
 44          DFS(v,deep+1);
 45          siz[x]+=siz[v];
 46          if(son[x]==-1||siz[v]>siz[son[x]]){ 
 47             son[x]=v;
 48          }
 49       }
 50    }
 51 }
 52 
 53 void DFS2(int x,int tp)
 54 {
 55    //cout<<"!";
 56    top[x]=tp;
 57    tip[x]=++tim;
 58    rank[tip[x]]=x;
 59    if(son[x]==-1)return ;
 60    DFS2(son[x],tp);
 61    for(int i=link[x];i;i=edge[i].next){
 62       int v=edge[i].point;
 63       if(v!=son[x]&&v!=fa[x]){
 64          DFS2(v,v);
 65       }
 66    }
 67 }
 68 
 69 int LCA(int a,int b)  //重点的注释都在这里 
 70 {
 71    while(top[a]!=top[b]){   //如果两个的链顶不相等,我们就把他们往一起靠 
 72       if(dep[top[a]]>dep[top[b]])  //看那个点的链顶深度大改变那个点 
 73          a=fa[top[a]];
 74       else
 75          b=fa[top[b]];
 76    }
 77    return dep[a]>dep[b]?b:a;  //返回时要返回深度比较小的数 
 78 }
 79 
 80 void init()
 81 {
 82    int a,b;
 83    cin>>N>>M>>S;
 84    for(int i=1;i<N;i++){
 85       scanf("%d%d%*c",&a,&b);
 86       ADD(a,b);
 87       ADD(b,a);
 88    }
 89    memset(son,-1,sizeof(siz));
 90 }
 91 
 92 void work()
 93 {
 94    int a,b;
 95    for(int i=1;i<=M;i++){
 96       scanf("%d%d%*c",&a,&b);
 97       printf("%d\n",LCA(a,b));
 98    }
 99 }
100 
101 int main()
102 {
103    init();//初始化 
104    DFS(S,1);//树链剖分。两个dfs就行 
105    DFS2(S,S);
106    work(); //输入询问 
107    //pt(); 调试函数,复制下来你们也可以用 
108    //system("pause");
109    return 0;
110 }

 

posted on 2016-11-09 09:32  fuyun_boy  阅读(1408)  评论(0编辑  收藏  举报

导航