zoukankan      html  css  js  c++  java
  • 树链剖分求LCA

    这里先推荐两道练习的裸题

    首先是求点

    【codevs4605】 LCA

    就是求两个点的公共祖先,每次询问xor上上一个询问的答案。

    先是两遍DFS:

    dfs1:把dep、siz、son求出来

    dfs2:求出top和w

    siz[v]表示以v为根的子树的节点数

    dep[v]表示v的深度(根深度为1)

    top[v]表示v所在的链的顶端节点

    fa[v]表示v的父亲

    son[v]表示与v在同一重链上的v的儿子节点

    w[v]结点编号

     1 int lca(int x,int y)
     2 {
     3   while (top[x]!=top[y])
     4   {
     5     int a=fa[top[x]];
     6     int b=fa[top[y]];
     7     if (dep[a]<dep[b])
     8       swap(a,b),swap(x,y);
     9     x=fa[top[x]];
    10   }
    11   return dep[x]<dep[y] ? x : y;
    12 }

    下面来分析这个算法。

     

    1:如果top[a]==top[b],说明a和b在同一条重链上,显然它们中深度较小的点即为它们的最近公共祖先。

    2:如果top[a]!=top[b],(说明a,b在不同的重链上)且a的深度较大,则此时a,b的LCA不可能在a所在的重链上。

    因为如果a,b的LCA在a所在重链上,那么top[a]显然也为a,b的公共祖先,则若dep[up[a]]]>dep[b],则显然不可能,若dep[up[a]]]<=dep[b],则设dep[up[a]]]为d,因为d>=dep[b],所以我沿着b向上搜索,在深度d处也可以找到一个点C为b的祖先,又因为a,b不在同一条重链上,所以top[a]!=C,这就意味着在同一深度,b有两个不同的祖先,这是不可能的(因为是一棵树),所以,LCA不可能在a所在的重链上。所以我们可以将a上升到up[a]的父节点,这时a,b的LCA没有变化。

    3:若果top[a]!=top[b],且b的深度较大,同理我们可将b上升到up[b]的父节点。

    4: a,b不停地上升,所以一定可以找到a,b的LCA。

    因为我们知道,在树中任意一点到根的路径上,重链数不会超过(logn),所以我们可以在O(logn)的时间复杂度内,将a,b的LCA求出来。

    (证明来自:http://www.xuebuyuan.com/552070.html)

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<cstdio>
     6 #include<cmath>
     7 using namespace std;
     8 
     9 #define N 100010
    10 
    11 struct edge
    12 {
    13     int to,next;
    14 }e[N<<1];
    15 int head[N<<1];
    16 int cnt;
    17 
    18 int fa[N],dep[N],son[N],top[N],siz[N],pos[N];
    19 
    20 int n,m;
    21 
    22 int x,y;
    23 int ans;
    24 
    25 int root;
    26 
    27 void link(int x,int y)
    28 {
    29     e[++cnt]=(edge){x,head[y]};
    30     head[y]=cnt;
    31 }
    32 
    33 void dfs(int x,int d)
    34 {
    35     dep[x]=d;
    36     siz[x]++;
    37     for (int i=head[x];i;i=e[i].next)
    38     {
    39         dfs(e[i].to,d+1);
    40         siz[x]+=siz[e[i].to];
    41         if (siz[e[i].to]>siz[son[x]])
    42             son[x]=e[i].to;
    43     }
    44 }
    45 
    46 void dfs2(int x,int d)
    47 {
    48     pos[x]++;
    49     top[x]=d;
    50     if (son[x])
    51         dfs2(son[x],d);
    52     for (int i=head[x];i;i=e[i].next)
    53         if (e[i].to!=son[x])
    54             dfs2(e[i].to,e[i].to);
    55 }
    56 
    57 int lca(int x,int y)
    58 {
    59     while (top[x]!=top[y])
    60     {
    61         int a=fa[top[x]];
    62         int b=fa[top[y]];
    63         if (dep[a]<dep[b])
    64             swap(a,b),swap(x,y);
    65         x=fa[top[x]];
    66     }
    67     return dep[x]<dep[y] ? x : y;
    68 }
    69 
    70 int main()
    71 {
    72     scanf("%d",&n);
    73     for (int i=1;i<=n;i++)
    74     {
    75         scanf("%d",&fa[i]);
    76         if (!fa[i])
    77         {
    78             root=i;
    79             continue;
    80         }
    81         link(i,fa[i]);
    82     }
    83     dfs(root,1);
    84     cnt=0;
    85     top[root]=root;
    86     dfs2(root,root);
    87     scanf("%d",&m);
    88     while (m--)
    89     {
    90         scanf("%d%d",&x,&y);
    91         x^=ans;
    92         y^=ans;
    93         ans=lca(x,y);
    94         printf("%d
    ",ans);
    95     }
    96     return 0;
    97 }
    codevs4605

    然后是求值

    【codevs2370】 小机房的树

    大意:求树上两点距离

    我写了树链剖分

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<cstdio>
     6 #include<cmath>
     7 using namespace std;
     8 
     9 #define N 100010
    10 
    11 struct edge
    12 {
    13 int to,next;
    14 }e[N<<1];
    15 int head[N<<1];
    16 int cnt;
    17 
    18 int fa[N],dep[N],son[N],top[N],siz[N],pos[N];
    19 
    20 int n,m;
    21 
    22 int x,y;
    23 int ans;
    24 
    25 int root;
    26 
    27 void link(int x,int y)
    28 {
    29   e[++cnt]=(edge){x,head[y]};
    30   head[y]=cnt;
    31 }
    32 
    33 void dfs(int x,int d)
    34 {
    35   dep[x]=d;
    36   siz[x]++;
    37   for (int i=head[x];i;i=e[i].next)
    38   {
    39     dfs(e[i].to,d+1);
    40     siz[x]+=siz[e[i].to];
    41     if (siz[e[i].to]>siz[son[x]])
    42       son[x]=e[i].to;
    43   }
    44 }
    45 
    46 void dfs2(int x,int d)
    47 {
    48   pos[x]++;
    49   top[x]=d;
    50   if (son[x])
    51     dfs2(son[x],d);
    52   for (int i=head[x];i;i=e[i].next)
    53     if (e[i].to!=son[x])
    54       dfs2(e[i].to,e[i].to);
    55 }
    56 
    57 int lca(int x,int y)
    58 {
    59   while (top[x]!=top[y])
    60   {
    61     int a=fa[top[x]];
    62     int b=fa[top[y]];
    63     if (dep[a]<dep[b])
    64       swap(a,b),swap(x,y);
    65     x=fa[top[x]];
    66   }
    67   return dep[x]<dep[y] ? x : y;
    68 }
    69 
    70 int main()
    71 { 
    72   scanf("%d",&n);
    73   for (int i=1;i<=n;i++)
    74   {
    75     scanf("%d",&fa[i]);
    76     if (!fa[i])
    77     {
    78       root=i;
    79       continue;
    80     }
    81     link(i,fa[i]);
    82   }
    83   dfs(root,1);
    84   cnt=0;
    85   top[root]=root;
    86   dfs2(root,root);
    87   scanf("%d",&m);
    88   while (m--)
    89   {
    90     scanf("%d%d",&x,&y);
    91     x^=ans;
    92     y^=ans;
    93     ans=lca(x,y);
    94     printf("%d
    ",ans);
    95   }
    96   return 0;
    97 }
    codevs2370

    复杂度O(qlogn),还是比较快的。
  • 相关阅读:
    MySQL的安装问题
    初识二分法
    PK赛 lower_bound( )和upper_bound( )的应用
    记录
    "双指针"去重有序数组
    归并排序(循序渐进中......)
    [2021.4.20打卡]LeetCode781. 森林中的兔子
    杂记...(持续更新)
    [未完待续](c++实现)八数码Ⅱ
    [回忆向]快速排序(降序) 感悟
  • 原文地址:https://www.cnblogs.com/yangjiyuan/p/5399655.html
Copyright © 2011-2022 走看看