Be careful about stack overflow.
求LCA最近公共祖先有3种方法:
1:在线算法 dfs序+线段树(或dfs序+rmq,rmq要更快,本人rmq较弱所以就用线段数将就一下)
2:离线算法 tarjan
3:在线算法 树链剖分(这个是非常规求法,这篇博客就不讲了,想了解的:传送门)
1. dfs序+线段树
dfs深搜得到dfs序,当询问节点l和r的LCA时,只需用线段树在dfs序中找到深度最小的节点,此节点就是LCA
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int inf=0x3f3f3f3f;
vector<int>vec[300005];
int dfsx[600005],dfsx2[600005],dfsn,first[300005],tree[2400005],tree2[2400005],dist[300005],vis[300005],n;///tree2[]为查询树
void dfs(int x)///求dfs序
{
vis[x]=1;
dfsx[++dfsn]=x;
dfsx2[dfsn]=dist[x];
first[x]=dfsn;
for(int i=0;i<vec[x].size();i++){
int v=vec[x][i];
if(vis[v]==0){
dist[v]=dist[x]+1;
dfs(v);
dfsx[++dfsn]=x;
dfsx2[dfsn]=dist[x];
}
}
}
void up(int root)///线段树 合并
{
int ll=tree[root<<1],rr=tree[root<<1|1];
if(dfsx2[ll]>dfsx2[rr]){tree[root]=rr;return;}
tree[root]=ll;
}
void maketree(int l,int r,int root)///线段树 建树
{
if(l==r){tree[root]=l;return;}
int mid=(l+r)>>1;
maketree(l,mid,root<<1);
maketree(mid+1,r,root<<1|1);
up(root);
}
void up2(int root)///线段树 合并
{
int ll=tree2[root<<1],rr=tree2[root<<1|1];
if(dfsx2[ll]>dfsx2[rr]){tree2[root]=rr;return;}
tree2[root]=ll;
}
void zhao(int l,int r,int root,int ll,int rr)///线段树 查找
{
if(r<ll||l>rr){tree2[root]=0;return;}
if(l>=ll&&r<=rr){
tree2[root]=tree[root];
return;
}
int mid=(l+r)>>1;
zhao(l,mid,root<<1,ll,rr);
zhao(mid+1,r,root<<1|1,ll,rr);
up2(root);
}
void init()
{
for(int i=1;i<=n;i++){
vis[i]=0;
vec[i].clear();
}
}
int main()
{
dfsx2[0]=inf;
while(scanf("%d",&n)!=EOF){
init();
int a,b;
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
vec[a].push_back(b);
vec[b].push_back(a);
}
dfsn=0;
dist[1]=1;
dfs(1);
maketree(1,dfsn,1);
int m;
scanf("%d",&m);
while(m--){
scanf("%d%d",&a,&b);
int l=first[a],r=first[b];
if(l>r)swap(l,r);
zhao(1,dfsn,1,l,r);
printf("%d\n",dfsx[tree2[1]]);
}
}
return 0;
}
2.离线算法 tarjan
先将询问保存,然后dfs遍历,从子节点返回时要用并查集合并,当遍历到一个节点u时,查看所有的“询问对”u<->v,当v已经被dfs访问并标记时,此“询问对”u<->v的LCA就为fa[v]。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=40010;
struct note
{
int u,v,w,lca,next;
}edge[maxn*2],edge1[805];
int head[maxn],ip,head1[maxn],ip1;
int m,n;
int father[maxn],vis[maxn],ance[maxn],dir[maxn];
void init()
{
memset(vis,0,sizeof(vis));
memset(dir,0,sizeof(dir));
memset(head,-1,sizeof(head));
memset(head1,-1,sizeof(head1));
ip=ip1=0;
}
void addedge(int u,int v,int w)
{
edge[ip].v=v,edge[ip].w=w,edge[ip].next=head[u],head[u]=ip++;
}
void addedge1(int u,int v)
{
edge1[ip1].u=u,edge1[ip1].v=v,edge1[ip1].lca=-1,edge1[ip1].next=head1[u],head1[u]=ip1++;
}
int Find(int x)///路径压缩
{
if(father[x]==x)
return x;
return father[x]=Find(father[x]);
}
void Union(int x,int y)///x,y合并
{
x=Find(x);
y=Find(y);
if(x!=y)
father[y]=x;
}
void tarjan(int u)
{
vis[u]=1;
ance[u]=father[u]=u;
for(int i=head[u];i!=-1;i=edge[i].next)///往下深搜
{
int v=edge[i].v;
int w=edge[i].w;
if(!vis[v])
{
dir[v]=dir[u]+w;
tarjan(v);
Union(u,v);
}
}
for(int i=head1[u];i!=-1;i=edge1[i].next)///
{
int v=edge1[i].v;
if(vis[v])///v已经被访问并标记,可以得出此询问的结果
{
edge1[i].lca=edge1[i^1].lca=ance[Find(v)];///此询问对u,v和询问对v,u都会得出答案
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addedge1(u,v);
addedge1(v,u);
}
dir[1]=0;
tarjan(1);
for(int i=0;i<m;i++)
{
int s=i*2,u=edge1[s].u,v=edge1[s].v,lca=edge1[s].lca;
printf("%d\n",dir[u]+dir[v]-2*dir[lca]);
}
}
return 0;
}