lca模板总结
倍增:初始化O(nlogn),查询O(logn)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int D = 25;
const int maxn = 500000 + 10;
int k,head[maxn],d[maxn],f[maxn][D + 5];
struct node{
int v,nxt;
}e[maxn << 1];
void adde(int u,int v){
e[k].v = v;
e[k].nxt = head[u];
head[u] = k++;
}
void dfs(int now,int fa)
{
d[now] = d[fa] + 1; f[now][0] = fa;
for(int i = head[now]; i != -1; i = e[i].nxt)
if(e[i].v != fa)
dfs(e[i].v,now);
}
int lca(int u,int v)
{
if(d[u] < d[v]) swap(u,v);
for(int i = D; i >= 0; --i)
if(d[v] <= d[f[u][i]]) u = f[u][i];
if(u == v) return u;
for(int i = D; i >= 0; --i)
if(f[u][i] != f[v][i])
{
u = f[u][i];
v = f[v][i];
}
return f[u][0];
}
int main(){
int n,m,s,a,b;
memset(head,-1,sizeof(head));
scanf("%d%d%d",&n,&m,&s);
for(int i = 1; i < n; ++i){
scanf("%d%d",&a,&b);
adde(a,b);adde(b,a);
}
dfs(s,0);//!!
for(int j = 1; j <= D; ++j)
for(int i = 1; i <= n; ++i)
f[i][j] = f[f[i][j - 1]][j - 1];//!!
while(m--){
scanf("%d%d",&a,&b);
printf("%d\n",lca(a,b));
}
return 0;
}
tarjan:O(n+q)
1.任选一个点为根节点,从根节点开始。
2.遍历该点u所有子节点v,并标记这些子节点v已被访问过。
3.若是v还有子节点,返回2,否则下一步。
4.合并v到u上。
5.寻找与当前点u有询问关系的点v。
6.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。
Tarjan(u)//marge和find为并查集合并函数和查找函数
{
for each(u,v) //访问所有u子节点v
{
Tarjan(v); //继续往下遍历
marge(u,v); //合并v到u上
标记v被访问过;
}
for each(u,e) //访问所有和u有询问关系的e
{
如果e被访问过
u,e的最近公共祖先为find(e);//只能是find(e)!!
}
}
struct node{
int v,nxt;
}e[maxn << 1];
struct query{
int v,w,nxt;
}q[maxn << 1];
void Adde(int u,int v){
e[k].v = v;
e[k].nxt = head[u];
head[u] = k++;
}
void Addq(int u,int v){
q[cnt].v = v;
q[cnt].nxt = headq[u];
headq[u] = cnt++;
}
int Find(int u){
return (fa[u] == u) ? u : fa[u] = Find(fa[u]);
}
void Tarjan(int u){
for(int i = head[u]; i != -1; i = e[i].nxt){
if(!vis[e[i].v]){
vis[e[i].v] = 1;
Tarjan(e[i].v);
fa[e[i].v] = u;
}
}
for(int i = headq[u]; i != -1; i = q[i].nxt){
if(vis[q[i].v]){
q[i].w = q[i ^ 1].w = Find(q[i].v);
}
}
}
int main(){
int n,m,s,a,b;
memset(head,-1,sizeof(head));
memset(headq,-1,sizeof(headq));
n = read();m = read();s = read();
for(int i = 1; i <= n; ++i) fa[i] = i;
for(int i = 1; i < n; ++i){
a = read();b = read();
Adde(a,b);Adde(b,a);
}
for(int i = 1; i <= m; ++i){
a = read();b = read();
Addq(a,b);Addq(b,a);
}
vis[s] = 1;//!!
Tarjan(s);
for(int i = 0;i < cnt; i += 2){
printf("%d\n",q[i].w);
}
return 0;
}
ST表.初始化O(nlogn),查询O(1)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int read(){
char ch;int op=1;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') op=-1;
int ret=ch-'0';
while((ch=getchar())>='0'&&ch<='9') ret=ret*10+ch-'0';
return ret*op;
}
const int N=1500000+10;
const int D=20;
double lg2;
int n,m,kk,idx,s;
int head[N],depth[N],euler[N],first[N],dist[N],ST[N][22];
struct edge{
int v,nxt;
}e[N<<1];
void adde(int u,int v){
e[kk].v=v;
e[kk].nxt=head[u];
head[u]=kk++;
}
int calc(int x,int y){
return depth[x]<depth[y]?x:y;
}
void dfs(int u,int fa,int d){
euler[++idx]=u;depth[idx]=d;first[u]=idx;
for(int i=head[u];i!=-1;i=e[i].nxt){
int v=e[i].v;
if(v!=fa){
dfs(v,u,d+1);
euler[++idx]=u;depth[idx]=d;
}
}
}
void init(int root){
idx=0;dist[root]=0;
dfs(root,0,1);
for(int i=1;i<=idx;++i) ST[i][0]=i;
for(int j=1;(1<<j)<=idx;++j)
for(int i=1;i+(1<<j)-1<=idx;++i)
ST[i][j]=calc(ST[i][j-1],ST[i+(1<<(j-1))][j-1]);
}
int Lca(int x,int y){
int l=first[x],r=first[y];
if(l>r) swap(l,r);
int k=log(r-l+1)/log(2);
return euler[calc(ST[l][k],ST[r-(1<<k)+1][k])];
}
int main(){
int a,b;
lg2=log(2);
n=read();m=read();s=read();
memset(head,-1,sizeof(head));
for(int i=1;i<n;++i){
a=read();b=read();
adde(a,b);adde(b,a);
}
init(s);
while(m--){
a=read();b=read();
printf("%d\n",Lca(a,b));
}
return 0;
}
树链剖分求lca
详见树剖总结。