zoukankan      html  css  js  c++  java
  • 每日算法——新型在线LCA

    在线LCA一般大家都会用倍增吧,时间复杂度O(nlogn),空间复杂度O(nlogn),都是非常严格的复杂度限制,并且各种边界处理比较麻烦,有没有更快更好的办法呢?

    我们发现,在树链剖分时,我们不经意的找到了LCA,能不能用这种方法找LCA呢?

    答案是肯定的,使用轻重链剖分达到的LCA,时间复杂度最坏为O(logn),预处理是O(n)的dfs,比起每次处理严格O(nlogn),预处理O(nlogn)的倍增看起来好了很多,下面我们就用实验测量一下。

    使用一个随机数据生成器生成了99组100000个点100000次询问的LCA,测试结果如下:

    测试环境:intel I5-4200M 2.5GHz*2 windows7 VMware虚拟机

    测试软件:cena 0.8

    测试结果:

    可以看到,树链剖分的代码比倍增有明显的优势,但是优势并不是特别大,平均每个点快了0.1秒左右。没有快太多的原因还是因为常数较大,劣处是代码量大了大约三十行。事实上,本人认为树链剖分比较好想,边界容易。

    代码:

    倍增:by Ommy_Zhang

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <cstring>
     4 #include <algorithm>
     5 using namespace std;
     6 #define MAXN 101010
     7 #define MAXM 202020
     8 #define TC 2000000
     9 int n,m,u,v,k,lca,lastans;
    10 int head[MAXN],next[MAXM],go[MAXM],cnt;
    11 int father[MAXN][20],deep[MAXN];
    12  
    13 void add(int a,int b)
    14 {
    15     go[++cnt]=b;
    16     next[cnt]=head[a];
    17     head[a]=cnt;
    18 }
    19 void dfs(int x)
    20 {
    21     for(int k=0;father[x][k];++k)
    22         father[x][k+1]=father[ father[x][k] ][k];
    23     for(int e=head[x];e;e=next[e])
    24         if(go[e]!=father[x][0])
    25         {
    26             deep[go[e]]=deep[x]+1;
    27             father[go[e]][0]=x;
    28             dfs(go[e]);
    29         }
    30 }
    31 int get_lca(int a,int b)
    32 {
    33     if(deep[a] < deep[b])
    34     {
    35         int t=a;
    36         a=b;
    37         b=t;
    38     }
    39     int d=deep[a]-deep[b];
    40     for(int k=0;k<20;++k)
    41         if((d>>k)&1)
    42             a=father[a][k];
    43     if(a==b)  return a;
    44      
    45     for(int k=19;k>=0;--k)
    46         if(father[a][k]!=father[b][k])
    47         {
    48             a=father[a][k];
    49             b=father[b][k];
    50         }
    51     return father[a][0];
    52      
    53 }
    54 int main()
    55 {
    56     freopen ("LCA.in","r",stdin);
    57     freopen ("LCA.out","w",stdout);
    58     int n;
    59     scanf("%d",&n);
    60     int j,k;
    61     for (int i=1;i<n;++i)
    62     {
    63         scanf("%d%d",&j,&k);
    64         add(j,k);
    65         add(k,j);
    66     }
    67     dfs(1);
    68     int m;
    69     scanf("%d",&m);
    70     for (int i=1;i<=m;++i)
    71     {
    72         scanf("%d%d",&j,&k);
    73         printf("%d
    ",get_lca(j,k));
    74     }
    75     return 0;
    76 }
    View Code

    树链剖分:by SymenYang

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #define maxn 100010
    using namespace std;
    struct edge
    {
        int to;
        edge* next;
    }ed[300000];
    
    edge* head[100010];
    int cnt=-1;
    void add(int j,int k)
    {
        edge* q=&ed[++cnt];
        q->to=k;
        q->next=head[j];
        head[j]=q;
    }
    int fa[maxn];
    int top[maxn];
    int dep[maxn];
    edge* wei[maxn];
    int size[maxn];
    void dfs(int now,int de,int last)
    {
        dep[now]=de;
        size[now]=1;
        fa[now]=last;
        int maxx=0;
        for (edge* q=head[now];q!=NULL;q=q->next)
        {
            if (q->to!=last)
            {
                dfs(q->to,de+1,now);
                if (size[q->to]>maxx)
                {
                    wei[now]=q;
                }
                size[now]+=size[q->to];
            }
        }
        return;
    }
    
    void dfs2(int now,int last,int to)
    {
        top[now]=to;
        if (wei[now])
            dfs2(wei[now]->to,now,to);
        for (edge* q=head[now];q!=NULL;q=q->next)
        {
            if (q->to!=last&&q!=wei[now])
            {
                dfs2(q->to,now,q->to);
            }
        }
        return;
    }
    
    int get_lca(int a,int b)
    {
        while (top[a]!=top[b])
        {
            if (dep[top[a]]<dep[top[b]]) a^=b^=a^=b;
            a=fa[top[a]];
        }
        return dep[a]<dep[b]? a:b;
    }
    
    int main()
    {
        freopen ("LCA.in","r",stdin);
        freopen ("LCA.out","w",stdout);
        int n;
        scanf("%d",&n);
        int j,k;
        for (int i=1;i<=n;++i) head[i]=NULL;
        for (int i=1;i<n;++i)
        {
            scanf("%d%d",&j,&k);
            add(j,k);
            add(k,j);
        }
        dfs(1,1,0);
        dfs2(1,0,1);
        int m;
        scanf("%d",&m);
        for (int i=1;i<=m;++i)
        {
            scanf("%d%d",&j,&k);
            printf("%d
    ",get_lca(j,k));
        }
        return 0;
    }
    View Code
  • 相关阅读:
    python中实现mysql连接池
    flask简单的路由分发
    用进程池创建子进程
    用类创建子进程
    用函数创建子进程
    fiddler抓包工具使用
    requests库的小技巧
    requests库的get请求,带有cookies
    requests库的post请求
    Android下Json数据解析
  • 原文地址:https://www.cnblogs.com/SymenYang/p/3683632.html
Copyright © 2011-2022 走看看