Tarjan算法求LCA(最近公共祖先)

来源:互联网 发布:淘宝网商品怎么分期购 编辑:程序博客网 时间:2024/05/26 02:19

LCA的离线算法。复杂度为O(n+q)。

这个算法充分利用了dfs树的结构。

对于每个节点u,关于它的询问(u,v)只有两种。(假设先dfs(u)后dfs(v))

1、v在u的子树内。

此时LCA(u,v) = u.

2、v不在u的子树内。

⑴假设v在u的父亲的另一棵子树内。

此时LCA(u,v) = father[u].

⑵如果不满足条件⑴,则v可能在u的父亲的父亲的另一棵子树内。

而此时LCA(u,v) = father[ father[u] ].

⑶……

观察一下,是不是发现了什么呢?

没错,不论是哪种情况,LCA(u,v)都与u和father[ ]有某种关系。我们能不能抓住这种关系呢?

我们继续观察,一直向上取father[ ],貌似和并查集的FIND操作很像呢。


我们用并查集的角度依次考虑上面的情况试试看。

1、v在u的子树内。

此时dfs(u)还在栈中,没有执行完,此时没有向上取father[ ],说明此时u是根。

2、v不在u的子树内。

⑴假设v在u的父亲的另一棵子树内。

此时的dfs(u)已经执行完并出栈。此时向上取了一次father[ ],说明此时u的父亲是根。

⑵如果不满足条件⑴,则v可能在u的父亲的父亲的另一棵子树内。

同理,此时dfs(u的父亲)也已经执行完并出栈。此时向上取了两次father[ ],说明此时u的父亲的父亲是根。

⑶……


综上,我们只要保证当dfs(u)在栈中的时候,u是根;当dfs(u)不在栈中的时候,father[u]是根就行了。

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <algorithm>  
  4. using namespace std;  
  5.   
  6. #define FileIn  freopen("in.ads","r",stdin)  
  7. #define FileOut freopen("out.ads","w",stdout)  
  8.   
  9. #define N 155  
  10. #define M 22555  
  11.   
  12. struct Vertex  
  13. {  
  14.     int head;  
  15. }V[N],Qv[N];  
  16.   
  17. struct Edge  
  18. {  
  19.     int v,next;  
  20. }E[M],Qe[M];  
  21.   
  22. int top,pre[N];  
  23.   
  24. bool used[N];  
  25.   
  26. void Init()  
  27. {  
  28.     top = 0;  
  29.     memset(V,-1,sizeof(V));  
  30.     memset(Qv,-1,sizeof(Qv));  
  31.     memset(pre,-1,sizeof(pre));  
  32.     memset(used,0,sizeof(used));  
  33. }  
  34.   
  35. int Root(int x)  
  36. {  
  37.     if(pre[x] != -1)  
  38.         return pre[x] = Root(pre[x]);  
  39.     else  
  40.         return x;  
  41. }  
  42.   
  43. void Union(int a,int b)  
  44. {  
  45.     int r1 = Root(a);  
  46.     int r2 = Root(b);  
  47.     if(r1 != r2)  
  48.         pre[r2] = r1;  
  49. }  
  50.   
  51. void Add_Edge(int u,int v)  
  52. {  
  53.     E[top].v = v;  
  54.     E[top].next = V[u].head;  
  55.     V[u].head = top++;  
  56. }  
  57.   
  58. void Add_Qedge(int u,int v)  
  59. {  
  60.     Qe[top].v = v;  
  61.     Qe[top].next = Qv[u].head;  
  62.     Qv[u].head = top++;  
  63. }  
  64.   
  65. void Tarjan(int u)  
  66. {  
  67.     used[u] = true;  
  68.     for(int i=Qv[u].head;i!=-1;i=Qe[i].next)  
  69.     {  
  70.         int v = Qe[i].v;  
  71.         if(used[v])  
  72.             printf("The LCA of (%d,%d) is -> %d\n",u,v,Root(v));  
  73.     }  
  74.     for(int i=V[u].head;i!=-1;i=E[i].next)  
  75.     {  
  76.         int v= E[i].v;  
  77.         if(used[v])  
  78.             continue;  
  79.         Tarjan(v);  
  80.         Union(u,v);  
  81.     }  
  82. }  
  83.   
  84. int main()  
  85. {  
  86.     FileIn;  
  87.     int n,m,u,v,Q;  
  88.     while(~scanf("%d%d",&n,&m))  
  89.     {  
  90.         Init();  
  91.         while(m--)  
  92.         {  
  93.             scanf("%d%d",&u,&v);  
  94.             Add_Edge(u,v);  
  95.             Add_Edge(v,u);  
  96.         }  
  97.         scanf("%d",&Q);  
  98.         while(Q--)  
  99.         {  
  100.             scanf("%d%d",&u,&v);  
  101.             Add_Qedge(u,v);  
  102.             Add_Qedge(v,u);  
  103.         }  
  104.         Tarjan(1);  
  105.     }  
  106.     return 0;  
  107. }  

转自http://blog.csdn.net/dgq8211/article/details/7828478
0 0