scu 3908 meetplace (LCA--> 倍增法)

 给你一颗树,根节点为节点1。然后又m个询问,求节点:(x,y)的最近公共祖先。
 这是一个经典的LCA问题,到网上看了很多资料。自己写了一个:在线算法-->倍增法


 基本思想是:

 d[i] 表示 i节点的深度, p[i,j] 表示 i 的 2^j 倍祖先
 那么就有一个递推式子 p[i,j]=p[p[i,j-1],j-1]   
 这样子一个O(NlogN)的预处理求出每个节点的 2^k 的祖先   
 然后对于每一个询问的点对a, b的最近公共祖先就是:  
  

 先判断是否 d[x] < d[y] ,如果是的话就交换一下(保证 x 的深度大于 y 的深度)
 然后把 x 调到与 y 同深度, 同深度以后再把a, b 同时往上调,调到有一个最小的j
 满足p[x,j]!=p[y,j] (x,y是在不断更新的), 最后再把(x,y)往上调 (x=p[x,0], y=p[y,0])
 一个一个向上调直到x = y, 这时 x或y 就是他们的最近公共祖先

  3908 C++ (G++-3)  Accepted  8ms 704
  2011-03-22 14:11:16

#include #include #include using namespace std; #define max_size 1010 int d[max_size], p[max_size][10]; int head[max_size]; int cnt; //构造树时用到的机构体,看过一个大牛用的,感觉很好 struct Edge { int v; int pre; }eg[max_size]; //建树的函数 void add(int x, int y) { eg[cnt].v = y; eg[cnt].pre = head[x]; head[x] = cnt++; } //dfs()初始整颗数,算出d[1-n], p[1-n][j]; void dfs(int k) { if (head[k] == 0) { return ; } int m, x, i, j; for (i = head[k]; i != 0; i = eg[i].pre) { x = eg[i].v; p[x][0] = k; m = k; d[x] = d[k]+1; for (j = 0; p[m][j] != 0; j++) { p[x][j+1] = p[m][j]; //利用公式 p[x][j] = p[p[x][j-1]][j-1],这里的m就是p[x][j-1]; m = p[m][j]; } dfs(x); } } int find_lca(int x, int y) { int m, k; if (x == y)return x; if (d[x] < d[y]) { m = x; x = y; y = m; } m = d[x] - d[y]; k = 0; while (m) //将x的深度调到和y的深度一样 { if (m&1) x = p[x][k]; m >>= 1; k++; } if (x == y)return x; k = 0; // 向上调节,找最近公共祖先, 算法的核心,相当于一个二分查找。 while (x != y) { if (p[x][k] != p[y][k] || p[x][k] == p[y][k] && k == 0) //如果p[x][k]还不相等,说明节点p[x][k]还在所求点的下面,所以继续向上调节 //如果相等了,并且就是他们父节点,则那个节点一定就是所求点。 { x = p[x][k]; y = p[y][k]; k++; } else//如果p[x][k] = p[y][k],可以说明p[x][k]一定是x和y的共祖先,但不一定是最近的。 //所以向下找看还有没有更近的公共祖先 { k--; } } return x; } int main() { int i, n, m, x, y; while (cin >> n >> m) { memset(head, 0, sizeof(head)); memset(p, 0, sizeof(p)); memset(d, 0, sizeof(d)); cnt = 1; for (i = 2; i <= n; i++) { scanf("%d", &x); add(x, i); } dfs(1); for (i = 0; i < m; i++) { scanf("%d%d", &x, &y); printf("%d/n", find_lca(x, y)); } } return 0; }  

你可能感兴趣的:(acm_LCA,算法,struct,c)