hicocoder1067 最近公共祖先·二(tarjan算法)

tarjan算法是处理最近公共祖先问题的一种离线算法。

算法思路:

先将所有的询问搜集起来。

然后对树进行dfs,在dfs的过程中对节点进行着色。当到达某个节点x的时候,给x着色为灰色,离开x的时候,着色为黑色。

当到达x并将其着色为灰色后,处理与x相关联的所有询问:

(这里有一个显然的事实:所有的灰色节点都是x的祖先)

(1)若询问的另一个节点y是灰色,那么该询问的结果为y;

(2)若询问的另一个节点y是黑色,那么询问结果应为离y最近的y的灰色祖先(因为所有灰色节点都是x的祖先,所以离y最近的y的灰色祖先就是x与y的最近公共祖先了);

关键在于如何找到离y最近的y的灰色祖先:利用并查集,维护每个节点所属的集合,当某个节点被标记为黑色,就将该节点所在的子树合并到其父节点所属的集合。而灰色节点所属的集合编号就是该节点编号本身。这样我们利用并查集的查找操作就能快速的找到任意黑色节点的最近灰色祖先。

(3)若询问的另一个节点y还没被着色,那么暂不处理,留待访问到y节点的时候再处理。

我的代码:

  1 #include <iostream>
  2 #include <string>
  3 #include <map>
  4 #include <vector>
  5 #include <cstring>
  6
  7 using namespace std;
  8
  9 #define MAXN 100005
 10
 11 map<string, int> mp;
 12 string name[2*MAXN];
 13 vector<int> v[2*MAXN];
 14 vector<int> query[2*MAXN];
 15 map<pair<int, int>, int> qid;
 16 string ans[MAXN];
 17 int color[2*MAXN];
 18 bool flag[MAXN];
 19 int n, m, cnt;
 20 vector<int> v1[MAXN];
 21
 22 struct unionFindSet
 23 {
 24     int st[2*MAXN];
 25     void init()
 26     {
 27         for(int i=0; i<2*MAXN; ++i)   st[i] = i;
 28     }
 29     int findSet(int x)
 30     {
 31         if(x==st[x]) return x;
 32         return st[x] = findSet(st[x]);
 33     }
 34     void unionSet(int x, int y)
 35     {
 36         int sx = findSet(x), sy = findSet(y);
 37         st[sx] = sy;
 38     }
 39 }ufs;
 40
 41 void init()
 42 {
 43     cnt = 0;
 44     for(int i=0; i<2*MAXN; ++i) v[i].clear();
 45     mp.clear();
 46     for(int i=0; i<2*MAXN; ++i) query[i].clear();
 47     qid.clear();
 48     memset(color, 0x11, sizeof(color));
 49     memset(flag, 0, sizeof(flag));
 50     for(int i=0; i<MAXN; ++i) v1[i].clear();
 51     ufs.init();
 52 }
 53
 54 void handleRepetition(int x)
 55 {
 56     for(int i=0; i<v1[x].size(); ++i)
 57     {
 58         ans[v1[x][i]] = ans[x];
 59         handleRepetition(v1[x][i]);
 60     }
 61 }
 62
 63 void solute(int x, int y)
 64 {
 65     if(flag[qid[pair<int, int>(x,y)]]) {}
 66     else if(color[y]==0)
 67     {
 68         ans[qid[pair<int, int>(x,y)]] = name[y];
 69         flag[qid[pair<int, int>(x,y)]] = true;
 70     }
 71     else if(color[y]==1)
 72     {
 73         ans[qid[pair<int, int>(x,y)]] = name[ufs.findSet(y)];
 74         flag[qid[pair<int, int>(x,y)]] = true;
 75     }
 76     if(color[y]>=0) handleRepetition(qid[pair<int, int>(x,y)]);
 77 }
 78
 79 void tarjan(int x)
 80 {
 81     color[x] = 0;
 82     for(int i=0; i<v[x].size(); ++i)
 83     {
 84         tarjan(v[x][i]);
 85         ufs.unionSet(v[x][i], x);
 86     }
 87     for(int i=0; i<query[x].size(); ++i)    solute(x, query[x][i]);
 88     color[x] = 1;
 89 }
 90
 91 int main()
 92 {
 93     string name1, name2;
 94     while(cin>>n)
 95     {
 96         init();
 97         while(n--)
 98         {
 99             cin>>name1>>name2;
100             if(mp.find(name1)==mp.end())
101             {
102                 mp[name1] = cnt;
103                 name[cnt++] = name1;
104             }
105             if(mp.find(name2)==mp.end())
106             {
107                 mp[name2] = cnt;
108                 name[cnt++] = name2;
109             }
110             v[mp[name1]].push_back(mp[name2]);
111         }
112         cin>>m;
113         int id = 0;
114         while(m--)
115         {
116             cin>>name1>>name2;
117             query[mp[name1]].push_back(mp[name2]);
118             query[mp[name2]].push_back(mp[name1]);
119             int tmp = -1;
120             if(qid.find(pair<int, int>(mp[name1], mp[name2]))!=qid.end())
121                 tmp = qid[pair<int, int>(mp[name1], mp[name2])];
122             qid[pair<int, int>(mp[name1], mp[name2])] = id++;
123             qid[pair<int, int>(mp[name2], mp[name1])] = id-1;
124             if(tmp!=-1) v1[id-1].push_back(tmp);
125         }
126         tarjan(0);
127         for(int i=0; i<id; ++i) cout<<ans[i]<<endl;
128     }
129
130     return 0;
131 }

题目链接:http://hihocoder.com/problemset/problem/1067

时间: 2024-10-05 16:38:09

hicocoder1067 最近公共祖先·二(tarjan算法)的相关文章

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先

POJ 1330 LCA最近公共祖先 离线tarjan算法

题意要求一棵树上,两个点的最近公共祖先 即LCA 现学了一下LCA-Tarjan算法,还挺好理解的,这是个离线的算法,先把询问存贮起来,在一遍dfs过程中,找到了对应的询问点,即可输出 原理用了并查集和dfs染色,先dfs到底层开始往上回溯,边并查集合并 一边染色,这样只要询问的两个点均被染色了,就可以输出当前并查集的最高父亲一定是LCA,因为我是从底层层层往上DSU和染色的,要么没被染色,被染色之后,肯定就是当前节点是最近的 #include <iostream> #include <

最近公共祖先 LCA Tarjan算法

来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html 对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点. 0 | 1 /   \ 2      3 比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点. 在求解最近公共祖先为问

我对最近公共祖先LCA(Tarjan算法)的理解

LCA 最近公共祖先 Tarjan(离线)算法的基本思路及我个人理解 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵树上距离最近的公共祖先节点. 所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径. 有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢? 答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而

【算法】树上公共祖先的Tarjan算法

最近公共祖先问题 树上两点的最近公共祖先问题(LCA - Least Common Ancestors) 对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u和v的祖先且x的深度尽可能大.在这里,一个节点也可以是它自己的祖先. 例如,如图,在以A为根的树上 节点 8 和 9 的LCA为 4 节点 8 和 5 的LCA为 2 节点 8 和 3 的LCA为 1 在线算法与离线算法 区别就在于是同时处理询问后输出还是边询问边输出 在线算法是读入一组询问,查询一次后紧

hihoCoder #1067 : 最近公共祖先&#183;二 [ 离线LCA tarjan ]

传送门: #1067 : 最近公共祖先·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个人的所有共同祖先中辈分最低的一个是谁.远在美国的他们利用了一些奇妙的技术获得了国内许多人的相关信息,并且搭建了一个小小的网站来应付来自四面八方的请求. 但正如我们所能想象到的……这样一个简单的算法并不能支撑住非常大的访问量,所以摆在小Hi和小Ho面前的无非两种选择: 其一是

hihoCoder_#1067_最近公共祖先&#183;二(LCA模板)

#1067 : 最近公共祖先·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣--或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个人的所有共同祖先中辈分最低的一个是谁.远在美国的他们利用了一些奇妙的技术获得了国内许多人的相关信息,并且搭建了一个小小的网站来应付来自四面八方的请求. 但正如我们所能想象到的--这样一个简单的算法并不能支撑住非常大的访问量,所以摆在小Hi和小Ho面前的无非两种选择: 其一是购买更为昂

LCA(最近公共祖先)--tarjan离线算法 hdu 2586

HDU 2586 How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11320    Accepted Submission(s): 4119 Problem Description There are n houses in the village and some bidirectional roads c

[图论] LCA(最近公共祖先)Tarjan 离线算法

很好的参考资料:http://taop.marchtea.com/04.04.html    下面的配图和部分文字转载于此文章 离线算法就是指统一输入后再统一输出,而不是边输入边实时输出.Tarjan算法的复杂度为O(N+Q),Q为询问的次数. 由于是离线算法,所以要保存输入的信息,次序问题. 若两个结点u.v分别分布于某节点t 的左右子树,那么此节点 t即为u和v的最近公共祖先.更进一步,考虑到一个节点自己就是LCA的情况,得知: ?若某结点t 是两结点u.v的祖先之一,且这两结点并不分布于该