原帖在这:http://blog.csdn.net/crazy_ac/article/details/7796497
LCA可以离线tarjan去求,也可以在线倍增去求。
记二维数组p[u][i]表示u的2^i个祖先是谁,i==0就是父节点。那么可以通过 p[u][i]=p[p[u][i-1]][i-1]来递推出所有的情况,然后再求lca(x,y)的时候,根据x,y的深度,先将深度大的点通过p[][放缩到与x相同的深度,做法是用二进制表示出深度差,然后从低到高扫描,若第i位为1,那么b=p[b][i],否则不变即第i位为1时,b跳转到其2^i个祖先处,看起来有点抽象,不过画个图或者单步监视一下就明白了。放缩到统一深度后,直接枚举一下答案就出来了...
这里放一个poj1330的模板,题意就是给一个数,然后给一个查询求LCA。
#include <iostream> #include <cstdio> #include <cmath> #include <cstdio> #include <cstring> #include <vector> using namespace std; typedef long long ll; const int POW = 16; const int maxn=10000; vector<int> edge[maxn]; int p[maxn][16]; int d[maxn]; int n,m,k; void dfs(int u,int fa){ d[u]=d[fa]+1; p[u][0]=fa; for(int i=1;i<POW;i++) p[u][i]=p[p[u][i-1]][i-1]; int sz=edge[u].size(); for(int i=0;i<sz;i++){ int v=edge[u][i]; if(v==fa) continue; dfs(v,u); } } int lca( int a, int b ){ if( d[a] > d[b] ) a ^= b, b ^= a, a ^= b; if( d[a] < d[b] ){ int del = d[b] - d[a]; for( int i = 0; i < POW; i++ ) if(del&(1<<i)) b=p[b][i]; } if( a != b ){ for( int i = POW-1; i >= 0; i-- ) if( p[a][i] != p[b][i] ) a = p[a][i] , b = p[b][i]; a = p[a][0], b = p[b][0]; } return a; } int ideg[maxn]; int main() { // freopen("in.txt","r",stdin); int tt; scanf("%d",&tt); while (tt--) { memset(d,0,sizeof d); memset(p,0,sizeof p); memset(ideg,0,sizeof ideg); int x,y; scanf("%d",&n); for (int i=1; i<=n; i++) edge[i].clear(); for (int i=1; i<n; i++) { scanf("%d%d",&x,&y); ideg[y]++; edge[x].push_back(y); edge[y].push_back(x); } int root; for (int i=1; i<=n; i++) if (ideg[i]==0) root=i; dfs(root,-1); scanf("%d%d",&x,&y); cout<<lca(x,y)<<endl; } return 0; }