标题:[转载] 关于LCA和RMQ问题及其互相转化 出处:Felix021 时间:Fri, 01 Aug 2008 19:52:32 +0000 作者:felix021 地址:https://www.felix021.com/blog/read.php?1065 内容: from http://hi.baidu.com/trooper/blog/item/ad377fd9ee6d072d10df9bc2.html p.s. Felix终于看懂了, 建议先看看RMQ的实现算法之sparse table 一、最近公共祖先(Least Common Ancestors) 对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。 这里给出一个LCA的例子: 例一 对于T= V={1,2,3,4,5} E={(1,2),(1,3),(3,4),(3,5)} 则有: LCA(T,5,2)=1 LCA(T,3,4)=3 LCA(T,4,5)=3 二、RMQ问题(Range Minimum Query) RMQ问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在[i,j]里的最小值下标。这时一个RMQ问题的例子: 例二 对数列:5,8,1,3,6,4,9,5,7 有: RMQ(2,4)=3 RMQ(6,9)=6 RMQ问题与LCA问题的关系紧密,可以相互转换,相应的求解算法也有异曲同工之妙。 下面给出LCA问题向RMQ问题的转化方法。 对树进行深度优先遍历,每当“进入”或回溯到某个结点时,将这个结点的值存入数组E最后一位。同时记录结点i在数组中第一次出现的位置(事实上就是进入结点i时记录的位置),记做R[i]。如果结点E[i]的深度记做D[i],易见,这时求LCA(T,u,v),就等价于求 E[RMQ(D,R[u],R[v])],(R[u] #include #define MAX 905 int R[MAX], L[2 * MAX], E[2 * MAX], num[MAX], d[2 * MAX][12], count[MAX]; int *link[MAX], tot; bool v[MAX]; //数列E[i]为:1,2,1,3,4,3,5,3,1 //R[i]为:1,2,4,5,7 //L[i]为:0,1,0,1,2,1,2,1,0 //E存放深度搜索时访问的节点的标号 //R存放各个节点的访问的时间 //L访问节点的深度 int DFS(int x, int d) { int i; if (!v[x]) { v[x] = 1; R[x] = tot; } L[tot] = d; E[tot++] = x; for (i = 0; i < num[x]; i++) { DFS(link[x][i], d + 1); L[tot] = d; E[tot++] = x; } return 0; } int RMQ(int v1, int v2) { int k = 0; while ((1 << (k + 1)) < (v2 - v1 + 1)) k++; if (L[d[v1][k]] < L[d[v2 - (1 << k) + 1][k]]) return E[d[v1][k]]; else return E[d[v2 - (1 << k) + 1][k]]; } int LCA(int v1, int v2) { if (R[v1] < R[v2]) return RMQ(R[v1], R[v2]); else return RMQ(R[v2], R[v1]); } int main() { int n, i, j, v1, v2, m; char c; bool root[MAX]; while (cin >> n) { memset(root, 0, sizeof(root)); for (i = 0; i < n; i++) { cin >> v1; cin >> c; while (c != ':') cin >> c; cin >> c; while (c != '(') cin >> c; cin >> num[v1 - 1]; link[v1 - 1] = new int[num[v1 - 1]]; cin >> c; while (c != ')') cin >> c; for (j = 0; j < num[v1 - 1]; j++) { cin >> v2; root[v2 - 1] = 1; link[v1 - 1][j] = v2 - 1; } } for (i = 0; i < n; i++) if (!root[i]) break; memset(v, 0, sizeof(v)); tot = 0; DFS(i, 0); for (i = 0; i < n; i++) delete[]link[i]; for (j = 0; (1 << j) <= tot; j++) for (i = 0; i + (1 << j) <= tot; i++) { if (j == 0) d[i][j] = i; else { if (L[d[i][j - 1]] < L[d[i + (1 << (j - 1))][j - 1]]) d[i][j] = d[i][j - 1]; else d[i][j] = d[i + (1 << (j - 1))][j - 1]; } } cin >> m; memset(count, 0, sizeof(count)); for (i = 0; i < m; i++) { cin >> c; while (c != '(') cin >> c; cin >> v1 >> c >> v2; v1--; v2--; count[LCA(v1, v2)]++; cin >> c; while (c != ')') cin >> c; } for (i = 0; i < n; i++) if (count[i]) cout << i + 1 << ':' << count[i] << endl; } return 0; } Program @ TrackBack : http://post.blog.hexun.com/bloodybat/trackback.aspx?articleid=2832372&key=632785863675270000 Generated by Bo-blog 2.1.0