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