hdu--2586--lca_tarjan<证明tarjan这个人很流弊>

开篇 敬仰下 大师-tarjan 发明的这些算法太流弊了=-=使用这个lca_tarjan之前 可以先去学习下使用tarjan解决scc强连通问题

我本来是去做到hdu-4912-发现做不来=-= 去网上搜了下 都说是神马lca的....那就去学下吧 而且很多也将这题当做入门题..

lca分离线和在线 这边我介绍的是离线算法 是要先将所有的询问一起处理 而不是单个单个地去处理  处理完成之后 你自己选择一种方式将答案存储下来

然后再进行一遍输出答案

我再网上没有找到一篇好的博客来推荐给大家 所以这边我就不写 传送门的故事了 -.-

学习下它 对于你的思想 是很有帮助的 很多时候 都是这样

这边我们需要用到 并查集来优化这个算法

我们结合下图 进行分析

现在 我们以上图为例 建立的边<无向>

1<-->2   2<-->3   2<--->4   3<--->5   5<--->7   2<--->4  4<--->6  1<--->8  8<--->9

我们的询问就是

lca:<2,3>  ,   <5,6>    ,    <7,4>      ,    <9,3>

这4组查询结果也算都是有一些特点的吧  虽然我们很容易地可以从上图中得到结果

2 3肯定是2啊 因为3是2的子树下的结点

5 6就是2了 他们同时是2的子树的结点 你也当然会说他们也是1的子树的结点啊 但是2的深度更大 离根结点更远

还有2个就先不说了.....

既然 这是树 那我们解决的时候首先就自己确定一个根结点 按习惯上 我们都喜欢默认1当做根结点 当然你也可以2 3 ……n

草。。我突然发现 不会分析下去了..

就是要注意下 假设你当前访问到的结点是u 现在有一个结点是v与u相邻 那么假如v没有被访问过 那就再lca_tarjan(v)即去访问v 这边的后续操作要明白 既然u与v相邻 那我们可以将他们看成是在同一棵子树上的 所以我们将v所在的集合并在u所在的集合之下 这时 就需要用到并查集了 

很多人会习x = find(u)  y = find(v) father[y] = x;

但其实 我觉得 直接father[v] = u就足够了 因为你find的结果肯定是自身结点 如若不是 那就是成环了 这样本来就不可以使用tarjan去解决了

//我也不确定上面我的理解是否正确 如若错误 一定要告诉我啊...

当然 在我们接下去询问求u v的lca的时候 是一定要用 find(v)的 因为v是比u先遍历到的 离根结点更近 因为可能会出现如 上图询问《5,6>这样的情况 这时候 father[5]=3 这是没有错的 但<5,6>的lca就是2了 所以我们这边必须是要find(v)的 就是用来针对这种情况 假如这棵子树的根结点是A 假设只有B C与它相连 而我们是询问B C这2棵子树下的lca 而我们递归回溯过来更新father[]是更新到直接与它相邻的为止 so 需要进行find操作 在2棵不同的子树情况下

这边最好去理解下 子树的概念 这在数据结构有涉及到 这个可以是递归定义的

可能有些 言不达意 =-= 都是自我理解 大家可以多参考一些博客 然后自己模拟画画 就是了

  1 #include <iostream>
  2 #include <cstring>
  3 using namespace std;
  4 
  5 int cnt1 , cnt2;
  6 const int size1 = 40010;
  7 const int size2 = 210;
  8 struct graph
  9 {
 10     int from;
 11     int to;
 12     int val;
 13     int next;
 14     int lca;
 15 };
 16 graph edge[size1*2];
 17 int Ehead[size1];
 18 graph node[size2*2];
 19 int Nhead[size1];
 20 bool vis[size1];
 21 int dist[size1];
 22 int father[size1];
 23 
 24 void init( )
 25 {
 26     memset( Ehead , -1 , sizeof(Ehead) );
 27     memset( Nhead , -1 , sizeof(Nhead) );
 28     memset( dist , 0 , sizeof(dist) );
 29     memset( vis , false , sizeof(vis) );
 30 }
 31 
 32 int find( int x )
 33 {
 34     return x == father[x] ? x : father[x] = find( father[x] );
 35 }
 36 
 37 
 38 void addEdge( int from , int to , int val )
 39 {
 40     edge[cnt1].to = to;
 41     edge[cnt1].val = val;
 42     edge[cnt1].next = Ehead[from];
 43     Ehead[from] = cnt1 ++;
 44 }
 45 
 46 void addQueryEdge( int from , int to )
 47 {
 48     node[cnt2].from = from;
 49     node[cnt2].to = to;
 50     node[cnt2].next = Nhead[from];
 51     Nhead[from] = cnt2 ++;
 52 }
 53 
 54 void lca_tarjan( int u , int val )
 55 {
 56     int v , x , y;
 57     vis[u] = true;
 58     dist[u] = val;
 59     father[u] = u;
 60     for( int i = Ehead[u] ; ~i ; i = edge[i].next )
 61     {
 62         v = edge[i].to;
 63         if( !vis[v] )
 64         {
 65             lca_tarjan( v , edge[i].val + val );
 66             x = find(u);
 67             y = find(v);
 68             if( x!=y )
 69             {
 70                 father[y] = x;
 71             }
 72         }
 73     }
 74     for( int i = Nhead[u] ; ~i ; i = node[i].next )
 75     {
 76         v = node[i].to; 
 77         if( vis[v] )
 78         {
 79             node[i].lca = node[i^1].lca = find(v);
 80         }
 81     }
 82 }
 83 
 84 int main()
 85 {
 86     cin.sync_with_stdio(false);
 87     int t , from , to , val , ans , id;
 88     int n , m;
 89     cin >> t;
 90     while( t-- )
 91     {
 92         init( );
 93         cin >> n >> m;
 94         cnt1 = cnt2 = 0;
 95         for( int i = 1 ; i<n ; i++ )
 96         {
 97             cin >> from >> to >> val;
 98             addEdge( from , to , val );
 99             addEdge( to , from , val );
100         }
101         for( int i = 0 ; i<m ; i++ )
102         {
103             cin >> from >> to;
104             addQueryEdge( from , to );
105             addQueryEdge( to , from );
106         }
107         lca_tarjan( 1 , 0 );
108         for( int i = 0 ; i<m ; i++ )
109         {
110             id = i * 2;
111             ans = dist[ node[id].from ] + dist[ node[id].to ] - 2*dist[ node[id].lca ];
112             cout << ans << endl;
113         }
114     }
115     return 0;
116 }
View Code

忘了一个很重要的...lca是基于 无向无环图的 也可以说多叉树吧 我是觉得差不多的

today:

  我们最大的敌人就是自己

  我们的恐惧来源于对自己的否定

  信心才是最重要的

just follow your heart
原文地址:https://www.cnblogs.com/radical/p/3945072.html