每日算法——新型在线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
原文地址:https://www.cnblogs.com/SymenYang/p/3683632.html