LCA——Tarjan
LCA,求最近公共祖先。
给一棵树,树上的节点的祖先就是该节点的父节点、父节点的父节点……直到根节点(包括该节点),而两个节点的公共祖先就是一个节点既是两个节点中的一个节点的祖先,又是另一个节点的祖先,那最近公共祖先就是两个节点的公共祖先中离它们最近的一个。
现在求LCA,我只学会了Tarjan,所以我就讲一下Tarjan的思路(Tarjan是离线的)。
1.从根节点开始,查找它的所有子节点。
2.在查找完毕后,记该节点已被访问。
3.查找所有与它有关系的节点(即两个节点是被查询最近公共祖先的节点),若是查到的节点已被访问,则向上找父亲,直到找到未修改父亲的节点,这就是这两个节点的最近公共祖先。
4.将它的父节点记做他的父亲(开始该节点的父亲是该节点,是一个并查集)。
下面就是代码:
#include<iostream> #define ll long long using namespace std; ll read(){ ll x=0,f=1; char a=getchar(); while(a<'0'||a>'9'){ if(a=='-')f=-1; a=getchar(); } while(a>='0'&&a<='9'){ x*=10; x+=a-'0'; a=getchar(); } return x*f; } ll n,m,s; ll head[500010],tot,head_cha[500010],tot_cha; ll f[500010],vis[500010],ans[500010]; struct node{ ll to; ll nxt; }edge[1000010]; struct nod{ ll to; ll nxt; ll id; }edge_cha[1000010]; void build(ll u,ll v){ edge[++tot].to=v; edge[tot].nxt=head[u]; head[u]=tot; } void build_cha(ll u,ll v){ edge_cha[++tot_cha].to=v; edge_cha[tot_cha].nxt=head_cha[u]; head_cha[u]=tot_cha; edge_cha[tot_cha].id=(tot_cha+1)/2; edge_cha[++tot_cha].to=u; edge_cha[tot_cha].nxt=head_cha[v]; head_cha[v]=tot_cha; edge_cha[tot_cha].id=(tot_cha+1)/2; } ll cha(ll v){ if(v==f[v])return v; return f[v]=cha(f[v]); } void search(ll u,ll fa){ ll t=head[u]; while(t!=0){ ll v=edge[t].to; if(v!=fa){ search(v,u); } t=edge[t].nxt; } vis[u]=1; t=head_cha[u]; while(t!=0){ ll v=edge_cha[t].to; if(vis[v]){ ans[edge_cha[t].id]=cha(v); } t=edge_cha[t].nxt; } f[u]=fa; } int main(){ n=read();m=read();s=read(); for(int i=1;i<=n;i++)f[i]=i; for(int i=1;i<n;i++){ ll u,v; u=read(); v=read(); build(u,v); build(v,u); } for(int i=1;i<=m;i++){ ll u,v; u=read(); v=read(); build_cha(u,v); } search(s,s); for(int i=1;i<=m;i++){ cout<<ans[i]<<endl; } return 0; }