Tarjan(离线)算法
思路:
1.任选一个点为根节点,从根节点开始。
2.遍历该点u所有子节点v,并标记这些子节点v已被访问过。
3.若是v还有子节点,返回2,否则下一步。
4.合并v到u上。
5.寻找与当前点u有询问关系的点v。
6.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。
1、POJ 1330 Nearest Common Ancestors
题意:给出一颗有根树(外向树),再给出有向边。询问一次,求两点的最近公共祖先。
思路:最最基础的LCA题目,而且只询问一次。对于外向树,记录入度,入度为0的则为根。
①tarjan离线算法实现
1 #include<iostream> 2 #include<cstdio> 3 #include<vector> 4 #include<memory.h> 5 using namespace std; 6 const int maxn = 10010; 7 const int maxq = 10; 8 vector<int> node[maxn];//邻接表 9 int q1, q2, ans; 10 int n, qnum; 11 int index[maxn];//入度 12 int pre[maxn];//并查集 13 bool vis[maxn];//标记是否访问过 14 pair<int, int>queq[maxq];//保存查询顺序 15 int f[maxn];//保存临时祖先 16 int Find(int x) 17 { 18 int r = x; 19 while (pre[r] != r) 20 { 21 r = pre[r]; 22 } 23 int i = x, j; 24 while (i != r) 25 { 26 j = pre[i]; 27 if (j != r) pre[i] = r; 28 i = j; 29 } 30 return r; 31 } 32 void Join(int root, int child) 33 { 34 int rx = Find(root), ry = Find(child); 35 if (rx != ry) pre[child] = root; 36 } 37 void LCA(int root) 38 { 39 f[Find(root)] = root; 40 vis[root] = true;//标记被访问过 41 int sz = node[root].size(); 42 for (int i = 0; i < sz; i++) 43 {//访问所有root子节点 44 if (!vis[node[root][i]]) 45 { 46 LCA(node[root][i]);//继续往下遍历 47 Join(root, node[root][i]);//合并 48 f[Find(root)] = root; 49 } 50 } 51 if (q1 == root&&vis[q2]) ans = f[Find(q2)]; 52 else if (q2 == root&&vis[q1]) ans = f[Find(q1)]; 53 } 54 void Init()//初始化 55 { 56 for (int i = 0; i <= n; i++) 57 { 58 node[i].clear(); 59 pre[i] = i; 60 } 61 memset(vis, 0, sizeof(vis)); 62 memset(f, 0, sizeof(f)); 63 memset(index, 0, sizeof(index)); 64 } 65 int main() 66 { 67 int t; 68 scanf("%d", &t); 69 while (t--) 70 { 71 scanf("%d", &n); 72 Init(); 73 for (int i = 1; i <= n - 1; i++) 74 { 75 int u, v; 76 scanf("%d%d", &u, &v); 77 node[u].push_back(v);//有向图 78 index[v]++; 79 } 80 scanf("%d%d", &q1, &q2); 81 int root = 1; 82 for (; root <= n; root++) if (!index[root]) break;//入度为0的为根 83 LCA(root); 84 printf("%d", ans); 85 printf("\n"); 86 } 87 return 0; 88 }