在线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\n",get_lca(j,k)); 74 } 75 return 0; 76 }
树链剖分: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\n",get_lca(j,k)); } return 0; }