题目链接:http://poj.org/problem?id=1330
题目大意:
给你一个树,问任意节点的最近公共祖先。
解题思路:
LCA问题。用RMQ解决。~。~不过我的办法比较2,因为我需要另外找根,另外找level数组也需要遍历。应该可以优化。
代码如下:
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> #include<cmath> using namespace std; #define N 10010 #define M 20010 int visit[M], level[M], first[N]; //深搜步骤、层、首次出现 int dp[20][N]; //RMQ-dp处理 vector<int>son[N]; //记录孩子 int in[N]; //入度 int top; int res[N]; //结果 void DFS(int ceng, int now) { if(first[now] == -1) first[now] = top; level[top] = ceng; visit[top++] = now; if(son[now].size() == 0) //终止条件 return ; for(int i = 0; i < son[now].size(); ++i) { DFS(ceng + 1, son[now][i]); level[top] = ceng; visit[top++] = now; } } void RMQ(int n) //预处理 { for(int i = 1; i <= n; ++i) dp[0][i] = level[i]; for(int i = 1; (1 << i) <= n; ++i) for(int j = 1; j <= n + 1 - (1 << i); ++j) dp[i][j] = min(dp[i - 1][j], dp[i - 1][j + (1 << i >> 1)]); } int ac(int start, int end) //返回最近公共祖先 { int k = (int)(log((double)(end - start + 1.0)) / log(2.0)); int temp = min(dp[ k ][ start ], dp[ k ][ end - (1 << k) + 1 ]); for(int i = start; i <= end; ++i) //找下标 if(level[i] == temp) return visit[i]; } void init(int n) { top = 1; memset(in, 0, sizeof(in)); memset(first, -1, sizeof(first)); memset(res, 0, sizeof(res)); for(int i = 1; i <= n; ++i) son[i].clear(); } int main() { int ncase; int vnum; int a, b; int root; int query, start, end; scanf("%d", &ncase); while(ncase--) { scanf("%d", &vnum); init(vnum); //初始化 for(int i = 1; i < vnum; ++i) //读入图 { scanf("%d %d", &a, &b); son[a].push_back(b); in[b]++; //入度+1 } for(root = 1; in[root]; ++root); //找根 DFS(0, root); //深搜 RMQ(2 * vnum - 1); //O(nlogn)预处理 scanf("%d %d", &start, &end); start = first[start], end = first[end]; //找到在level中的位置 if(start > end) //保证前小后大 swap(start, end); printf("%d\n", ac(start, end)); } return 0; }