题意
第一行输入T,有T组数据。
对于每组数据,给出一棵树,先输入n,然后n-1行,每行两个数a,b,表示a是b的父亲;第n行输入两个数A,B表示询问A和B的最近公共祖先。
题解
LCA模板题。建议先学学LCA
有两种方法,分别是Tarjan和倍增,这里说一说倍增。
LCA_倍增是LCA的在线算法,时间和空间复杂度分别是O((n+q)log n)和O(n log n)。
对于这个算法,我们从最暴力的算法开始:
①如果a和b深度不同,先把深度调浅,使他变得和浅的那个一样
②现在已经保证了a和b的深度一样,所以我们只要把两个一起一步一步往上移动,直到他们到达同一个节点,也就是他们的最近公共祖先了。
O(nq)的暴力实现
#include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <vector> using namespace std; const int N=10000+5; vector <int> son[N]; int T,n,depth[N],fa[N],in[N],a,b; void dfs(int prev,int rt){ depth[rt]=depth[prev]+1; fa[rt]=prev; for (int i=0;i<son[rt].size();i++) dfs(rt,son[rt][i]); } int LCA(int a,int b){ if (depth[a]>depth[b]) swap(a,b); while (depth[b]>depth[a]) b=fa[b]; while (a!=b) a=fa[a],b=fa[b]; return a; } int main(){ scanf("%d",&T); while (T--){ scanf("%d",&n); for (int i=1;i<=n;i++) son[i].clear(); memset(in,0,sizeof in); for (int i=1;i<n;i++){ scanf("%d%d",&a,&b); son[a].push_back(b); in[b]++; } depth[0]=-1; int rt=0; for (int i=1;i<=n&&rt==0;i++) if (in[i]==0) rt=i; dfs(0,rt); scanf("%d%d",&a,&b); printf("%d\n",LCA(a,b)); } return 0; }
而实际上,一步一步往上移动太慢,我们可以做一个预处理:
fa[i][j]表示节点i往上走2^j次所到达的祖先,那么不难写出转移方程:
fa[i][0]=father[i],fa[i][j]=fa[fa[i][j-1]][j-1]
然后在求LCA的时候,有这样一个性质:(假设a和b深度一样)
设anst[x][y]为节点x网上走y步到达的祖先,对于一个k,如果anst[a][k]==anst[b][k],那么对于k‘(k‘>k),一定有anst[a][k‘]==anst[b][k‘];对于一个k,如果anst[a][k]!=anst[b][k],那么对于k‘(k‘<k),一定有anst[a][k‘]!=anst[b][k‘],而且LCA(a,b)=LCA(anst[a][k],anst[b][k])。
于是求法就渐渐的现行了:
1. 把a和b移到同一深度(设depth[x]为节点x的深度),假设depth[a]<=depth[b],所以我们的目的是把b向上移动i=(depth[b]-depth[a])层,那么,由于之前有预处理的fa数组,我们把i写成二进制形势,然后利用fa数组来在log n的复杂度中完成;
2. 寻找a和b的LCA下一层的两个祖先。利用之前的那个性质,再利用倍增,如果a和b的第2^k个祖先不是同一个,那么把a改为fa[a][k],b改为fa[b][k],k减1;否则直接k减1;当然在这之前要实现确定k的最大值,从大往小处理下去。最终的结果就是fa[a][0]或者fa[b][0]。
注意点:如果a和b在调节深度之后已经是同一个祖先的,那么直接返回a或者b。
LCA倍增算法&POJ1330标程
#include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <vector> using namespace std; const int N=10000+5; vector <int> son[N]; int T,n,depth[N],fa[N][17],in[N],a,b; void dfs(int prev,int rt){ depth[rt]=depth[prev]+1; fa[rt][0]=prev; for (int i=1;(1<<i)<=depth[rt];i++) fa[rt][i]=fa[fa[rt][i-1]][i-1]; for (int i=0;i<son[rt].size();i++) dfs(rt,son[rt][i]); } int LCA(int a,int b){ if (depth[a]>depth[b]) swap(a,b); for (int i=depth[b]-depth[a],j=0;i>0;i>>=1,j++) if (i&1) b=fa[b][j]; if (a==b) return a; int k; for (k=0;(1<<k)<=depth[a];k++); for (;k>=0;k--) if ((1<<k)<=depth[a]&&fa[a][k]!=fa[b][k]) a=fa[a][k],b=fa[b][k]; return fa[a][0]; } int main(){ scanf("%d",&T); while (T--){ scanf("%d",&n); for (int i=1;i<=n;i++) son[i].clear(); memset(in,0,sizeof in); for (int i=1;i<n;i++){ scanf("%d%d",&a,&b); son[a].push_back(b); in[b]++; } depth[0]=-1; int rt=0; for (int i=1;i<=n&&rt==0;i++) if (in[i]==0) rt=i; dfs(0,rt); scanf("%d%d",&a,&b); printf("%d\n",LCA(a,b)); } return 0; }