Tarjan离线求LCA
上次貌似在处理RMQ问题时提起了倍增,这玩意可以求动态LCA(比较牛),现在我们在讲dfs时间戳的时候发现了一个求静态LCA的算法
何为时间戳?
在这里我们定义两个数组f和d,在深度优先搜索时记录这两个的值——f表示对这个店开始搜索的时间,d表示对这个店完成搜索的时间
我们假设所有操作花费的都是单位时间,就可以得到一棵树的完整时间戳啦。
这东西有什么用呢?
对于树上任意两个和它们的LCA间的时间戳关系
然后我们把所有询问都存下来
我们发现其实两个点的LCA就是一个最“下面”的左边包含u,右边包含v的点。
其实我们发现就是第一个还保持在u通往根的灰色状态的点(如果u,v属于同一个根的话)
所以我们在dfs时顺带处理一下第一个灰色节点就行了
模板题:POJ1330
#include<iostream> #include<cstdio> #include<algorithm> const int N=10005; using namespace std; int n,i,u,v,a,b,rep[N],head[N],num,next[N],point[N],T; bool flag[N],FLAG[N]; void make_way(int u,int v) { point[++num]=v; next[num]=head[u]; head[u]=num; } int getrep(int u) { if(rep[u]==u) { return u; } return rep[u]=getrep(rep[u]); } void Init() { scanf("%d",&n); num=0; for(int i=1;i<=n;i++) head[i]=0,rep[i]=i,flag[i]=FLAG[i]0; for(int i=1;i<n;i++) { scanf("%d %d",&u,&v); FLAG[v]=1; make_way(u,v); } scanf("%d %d",&a,&b); } int findrt() { for(int i=1;i<=n;i++) if(!FLAG[i]) { return i; } } void LCA(int u) { for(int i=head[u];i;i=next[i]) { LCA(point[i]); //printf("i:%d rep:%d",point[i],rep[point[i]]); rep[rep[point[i]]]=u; } flag[u]=1; if(a==u&&flag[b]) { cout<<getrep(b)<<endl; }else { if(b==u&&flag[a]) cout<<getrep(a)<<endl; } } int main() { scanf("%d",&T); while(T--) { Init(); LCA(findrt()); } }