一、最近公共祖先(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,E>
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问题的转化方法。
易知,转化后得到的数列长度为树的结点数的两倍加一,所以转化后的RMQ问题与LCA问题的规模同次。
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define maxn 100010 int cnt,id; int head[maxn]; bool vis[maxn]; int dep[maxn*2+1], E[maxn*2+1], R[maxn]; int f[maxn*2+1][20],d[50]; //f[] is RMQ, d[i] is 2^i struct Edge { int to,next,weight; }edges[maxn]; //邻接表 void init() { cnt=id=0; memset(vis,false,sizeof(vis)); memset(head,-1,sizeof(head)); } void insert(int a, int b, int weight) { edges[cnt].to=b; edges[cnt].next=head[a]; edges[cnt].weight=weight; head[a]=cnt++; } void DFS(int u, int d) { vis[u]=1; R[u]=id;E[id]=u;dep[id++]=d; for(int i = head[u]; i != -1; i=edges[i].next) { int v=edges[i].to; if(!vis[v]) { DFS(v,d+1); E[id]=u;dep[id++]=d; } } } void InitRMQ(const int &id,int n) { d[0]=1; for(int i = 1; i < n; i++)d[i]=2*d[i-1]; for(int i = 0; i < id; i++)f[i][0]=i; int k=int(log(double(n))/log(2.0))+1; for(int j = 1; j < k; j++) for(int i = 0; i < id; i++) { if(i+d[j-1]-1<id) f[i][j]=dep[f[i][j-1]]>dep[f[i+d[j-1]][j-1]]?f[i+d[j-1]][j-1]:f[i][j-1]; else break; } } int Query(int x, int y) { int k; k=int(log(double(y-x+1))/log(2.0)); return dep[f[x][k]]>dep[f[y-d[k]+1][k]]?f[y-d[k]+1][k]:f[x][k]; } void Answer() { int Q;scanf("%d",&Q); for(int i = 0; i < Q; i++) { int x,y; scanf("%d%d",&x,&y); //查询x,y的LCA x=R[x];y=R[y]; if(x>y)swap(x,y); printf("%d\n",E[Query(x,y)]); } } int main() { int t;scanf("%d",&t); while(t--) { init(); int n,m;scanf("%d%d",&n,&m); for(int i = 0; i < m; i++) { int a,b,c;scanf("%d%d%d",&a,&b,&c); insert(a,b,c); } DFS(1,0); InitRMQ(id,n); Answer(); } return 0; }