题目
T组样例,N(2<=N<=1e4)个节点,
给出N-1条有向边u->v后,询问一组u和v的lca
思路来源
《挑战程序竞赛》P330-331
题解
lca裸题,用RMQ+ST就好了,其实倍增也是应该掌握的
由于只有一组询问,所以预处理欧拉序dfn
id用来记录dfs序中第一次访问的时间戳
不妨id[u]<id[v],那么lca(u,v)=dfn[[id[u],id[v]中dep值最小的]
心得
大一选拔赛的时候当时不会lca,CCCC的时候还不会lca
现在敲了几道RMQ的ST题之后,终于会魔改ST的板子存下标了
白书的板子太简洁,有时实在不是一件好事啊……
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
const int maxn=2e4+10;
vector<int>E[maxn];
int T,n,u,v;
int dfn[2*maxn],tot;//欧拉序
int dep[2*maxn];//各顶点深度
int id[maxn];//各顶点第一次出现下标
int dp[maxn][20];
bool vis[maxn];
void dfs(int u,int fa,int d)
{
id[u]=++tot;
dfn[tot]=u;
dep[tot]=d;
for(int i=0;i<E[u].size();++i)
{
int v=E[u][i];
if(v==fa)continue;
dfs(v,u,d+1);
dfn[++tot]=u;
dep[tot]=d;
}
}
void ST()
{
for(int i=1;i<=tot;++i)
dp[i][0]=i;//存的是最小值的下标
for(int len=1;(1<<len)<=tot;++len)
{
for(int l=1;l+(1<<len)-1<=tot;++l)
{
if(dep[dp[l][len-1]]<dep[dp[l+(1<<(len-1))][len-1]])dp[l][len]=dp[l][len-1];
else dp[l][len]=dp[l+(1<<(len-1))][len-1];
}
}
}
int RMQ(int l,int r)//返回最小值下标
{
int len=log(r-l+1)/log(2);
if(dep[dp[l][len]]<dep[dp[r-(1<<len)+1][len]])return dp[l][len];
else return dp[r-(1<<len)+1][len];
}
int lca(int u,int v)
{
int mn=min(id[u],id[v]);
int mx=max(id[u],id[v]);
return dfn[RMQ(mn,mx)];
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
E[i].clear();
memset(vis,0,sizeof vis);
memset(id,0,sizeof id);
memset(dep,0,sizeof dep);
memset(dfn,0,sizeof dfn);
for(int i=1;i<n;++i)
{
scanf("%d%d",&u,&v);
E[u].push_back(v);
vis[v]=1;
}
for(int i=1;i<=n;++i)
{
if(!vis[i])
{
dfs(i,-1,0);
break;
}
}
ST();
scanf("%d%d",&u,&v);
printf("%d\n",lca(u,v));
}
return 0;
}