有一个超级好的LCA的讲解非常好:LCA
入门的博客可以看那个,接下来我也会将一下。
下面是相关的例题:
目前我就学了两类题:
1。求两点的最近公共祖先。例题:Nearest Common Ancestors,Closest Common Ancestors。
2。求两点在树上的最短路。例题:Connections between cities,How far away ?,CD操作,Distance Queries。
【LCA】
LCA就是求在一棵树上的两点的最近公共祖先。
它分为两种算法,在线算法和离线算法。
在线算法就是先做预处理,它问你一个问题你就回答一个问题。
离线算法就是他全部问完后,你再将这些答案按顺序输出即可。
我们这里讲离线算法(在线还不会)。
先说一下思想,就是深搜,如果要求最短路,就直接计算该点到根节点的路径长度,返回的过程中,将经过的点放入一个并查集中,以此来判断他询问的两点的最近公共祖先是谁,找到后更新ans[w]=dis[v]+dis[u]-2*dis[f];(不懂就来模拟一下代码吧~~)。
先以最近公共祖先为例:
代码:
void Trajan(int u)//u是根节点
{
fath[u]=u;//初始化并查集的fath数组
vis[u]=1;//记录这个点u被访问过,有可能要建立无向图
for(int i=firs1[u];i!=-1;i=edge[i].nex)//遍历与u有关的所有的询问,这里是firs1数组
{
int v=edge[i].v,w=edge[i].w;//这里的w是用来记录这个询问是带几个询问。
if(vis[v])
{
int f=find(v);//查找他们的公共祖先,并查集在这里运用的非常巧妙,可以好好想想
ans[w]=f;//ans数组用来记录第w个询问的公共祖先f。
}
}
for(int i=firs2[u]; i!=-1; i=edge[i].nex)//向下一直深搜,这里用的是firs2数组
{
int v=edge[i].v;
if(!vis[v])
{
Trajan(v);
fath[v]=u;//将u加入到并查集中。
}
}
}
下面是求最短路的代码:
void Trajan(int u)//u代表根节点
{
fath[u]=u;//初始化并查集的fath数组
vis[u]=1;//记录被访问过
for(int i=first2[u];~i;i=edge[i].nex)//别看错了,是first2数组
{
int v=edge[i].v,w=edge[i].w;//这里的w是用来记录第几个询问的,下面的w是记录该边的路径长度的。
if(vis[v])
{
int f=find(v);
ans[w]=dis[v]+dis[u]-2*dis[f];
//因为都是到根节点的距离,所以要减去公共的路径。
}
}
for(int i=first1[u];~i;i=edge[i].nex)//用first1数组
{
int v=edge[i].v,w=edge[i].w;//w代表路径长度
if(!vis[v])
{
dis[v]=dis[u]+w;//记录v到根节点的距离
Trajan(v);
fath[v]=u;//更新并查集,并查集记录的是深搜返回时的节点的关系
}
}
}
更新:
这是新学的在线LCA的算法:倍增。
其实倍增我还没有完全吃透,所以就先给出代码和相关博客了。
相关博客:最近公共祖先:LCA及其用倍增实现 +POJ1986
以这道题为例:Distance Queries
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=200090;
int firs[maxn],dis[maxn],deep[maxn],fath[maxn],dp[maxn][22];
int N;
struct node
{
int v,w,nex;
}edge[maxn*10];
void build_edge(int u,int v,int w)
{
edge[N]=(node){v,w,firs[u]};
firs[u]=N++;
edge[N]=(node){u,w,firs[v]};
firs[v]=N++;
}
int init()
{
memset(firs,-1,sizeof(firs));
memset(dis,0,sizeof(dis));
memset(deep,0,sizeof(deep));
memset(dp,0,sizeof(dp));
N=0;
}
void dfs(int u)
{
dp[u][0]=fath[u];
for(int i=1;i<20;i++)
{
dp[u][i]=dp[dp[u][i-1]][i-1];
}
for(int i=firs[u];~i;i=edge[i].nex)
{
int v=edge[i].v,w=edge[i].w;
if(fath[u]==v) continue;
deep[v]=deep[u]+1;
dis[v]=dis[u]+w;
fath[v]=u;
dfs(v);
}
}
int lca(int u,int v)
{
if(deep[u]<deep[v])
swap(u,v);
for(int i=19;i>=0;i--)
{
if(deep[dp[u][i]]>=deep[v])
u=dp[u][i];
}
if(u==v) return u;
for(int i=19;i>=0;i--)
{
if(dp[u][i]!=dp[v][i])
{
u=dp[u][i];
v=dp[v][i];
}
}
return dp[u][0];
}
int main()
{
int n,m,q;
while(~scanf("%d%d",&n,&m))
{
init();
int u,v,w;
char a;
for(int i=0;i<m;i++)
{
scanf("%d%d%d%*c%*c",&u,&v,&w);
build_edge(u,v,w);
}
fath[1]=1;
dfs(1);
scanf("%d",&q);
for(int i=0;i<q;i++)
{
scanf("%d%d",&u,&v);
printf("%d\n",dis[u]+dis[v]-2*dis[lca(u,v)]);
}
}
return 0;
}
好好看看吧,有错误请留言~~。