• 树链剖分求LCA


    在讲树链剖分之前,让我们看看求LCA可以用什么算法:

    Tarjian   倍增   (暂时只想到这么一些)

    倍增求LCA理论复杂度O(n*m*logn)

    对于Tarjian求LCA的理论复杂度是O(n*m)按理说复杂度比倍增优秀,但不知道为何在洛谷上交要加快读才能勉强卡过

    个人认为Tarjian写LCA很恼火,就是dfs,感觉很蠢而且还要离线操作,弄得人不舒服

    倍增是最好写的,但是常数不小

    最优秀的就是树链剖分了,理论复杂度最大为O(logn*m),带个2的常数

    既然如此优越,那就学习一下啊

    【如何用树链剖分求LCA】

    1、首先判断当前两个节点是不是拥有同样的链顶,如果有同样的链顶,那么深度小的节点一定LCA(想一想为什么)

    2、如果不是同链顶,我们就对链顶深度大的点进行操作,让其跳到链顶的父亲节点(之所以要跳的父节点是为了防止当前节点的链顶就是自己)

    3、然后回到1进行判断,直到同链顶

    是不是感觉还是很好理解的,如果还是不理解,那就拿下面的图手推一下

     1 int lca(int a,int b)
     2 {
     3     while(top[a]!=top[b])
     4     {
     5         if(dep[top[a]]>dep[top[b]]) a=fa[top[a]];
     6         else b=fa[top[b]];
     7     }
     8     if(dep[a]<dep[b]) return a;
     9     else return b;
    10 }

    证明其复杂度:

    我们单次操作只需要对两条链进行操作,对吧,这就是常数2

    我们每条链跳的次数不会超过logn次,很显然(树的优越性质),如果就是一条长链的话,那么很舒服一次性就跳到了链顶并不需要logn的复杂度

    【代码实现】

     1 #include<cstdio>
     2 #include<vector>
     3 using namespace std;
     4 const int maxn=500000;
     5 int top[maxn+5],fa[maxn+5],son[maxn+5],siz[maxn+5],dep[maxn+5],head[maxn+5];
     6 struct sd{
     7     int to,next;
     8 }edge[2*maxn+10];
     9 int cnt;
    10 void add(int a,int b)
    11 {
    12     edge[++cnt].to=b;
    13     edge[cnt].next=head[a];
    14     head[a]=cnt;
    15 }
    16 int dfs(int now,int ff,int deep)
    17 {
    18     fa[now]=ff,dep[now]=deep,siz[now]=1;
    19     int ms=-1,maxx=-1;
    20     for(int i=head[now];i!=0;i=edge[i].next)
    21     {
    22         if(edge[i].to!=ff)
    23         {
    24             int gg=dfs(edge[i].to,now,deep+1);
    25             if(maxx<gg)
    26             { maxx=gg,ms=edge[i].to; }
    27             siz[now]+=gg;
    28         }
    29     }
    30     if(ms==-1) son[now]=now;
    31     else son[now]=ms;
    32     return siz[now];
    33 }
    34 void dfs2(int now,int tt)
    35 {
    36     top[now]=tt;
    37     if(son[now]==now) return;
    38     dfs2(son[now],tt);
    39     for(int i=head[now];i!=0;i=edge[i].next)
    40     {
    41         if(edge[i].to!=fa[now]&&edge[i].to!=son[now])
    42             dfs2(edge[i].to,edge[i].to);
    43     }
    44 }
    45 int lca(int a,int b)
    46 {
    47     while(top[a]!=top[b])
    48     {
    49         if(dep[top[a]]>dep[top[b]]) a=fa[top[a]];
    50         else b=fa[top[b]];
    51     }
    52     if(dep[a]<dep[b]) return a;
    53     else return b;
    54 }
    55 int main()
    56 {
    57     int n,m,root;
    58     scanf("%d%d%d",&n,&m,&root);
    59     for(int i=1;i<n;i++)
    60     {
    61         int a,b;
    62         scanf("%d%d",&a,&b);
    63         add(a,b);add(b,a);
    64     }
    65     siz[root]=dfs(root,0,1);
    66     dfs2(root,root);
    67     for(int i=1;i<=m;i++)
    68     {
    69         int a,b;
    70         scanf("%d%d",&a,&b);
    71         printf("%d
    ",lca(a,b));
    72     }
    73     return 0;
    74 }

    最后温馨提示一下,对于存边的话,个人建议还是要用链式前向星,会比vector快不少的,不然洛谷上过不了

     

  • 相关阅读:
    并不对劲的bzoj3932: [CQOI2015]任务查询系统
    并不对劲的bzoj4868: [Shoi2017]期末考试
    并不对劲的bzoj1853:[SCOI2010]幸运数字
    并不对劲的bzoj4199: [Noi2015]品酒大会
    并不对劲的bzoj1500: [NOI2005]维修数列
    并不对劲的hdu4777
    并不对劲的线段树套平衡树
    44.Linked List Cycle II(环的入口节点)
    43.Word Break(看字符串是否由词典中的单词组成)
    42.Flatten Binary Tree to Linked List
  • 原文地址:https://www.cnblogs.com/genius777/p/8719201.html
Copyright © 2020-2023  润新知