洛谷P3379 【模板】最近公共祖先(LCA)
题目描述
如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
输入输出格式
输入格式:
第一行包含三个正整数(N)、(M)、(S),分别表示树的结点个数、询问的个数和树根结点的序号。
接下来(N-1)行每行包含两个正整数(x)、(y),表示(x)结点和(y)结点之间有一条直接连接的边(数据保证可以构成树)。
接下来(M)行每行包含两个正整数(a)、(b),表示询问(a)结点和(b)结点的最近公共祖先。
输出格式:
输出包含(M)行,每行包含一个正整数,依次为每一个询问的结果。
说明
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=10,M<=10
对于70%的数据:N<=10000,M<=10000
对于100%的数据:N<=500000,M<=500000
今天(2018.6.6)刚学离线的tarjan做法,复杂度近似为(O(n))
主要思想:利用(dfs)的遍历顺序,来实时更新每个时间点某个点的祖先。
我们在遍历到某个节点(i)时将这个节点标记已经访问,在 回溯到它父亲时 更新它的祖先(注意时间顺序)。当((i,j))是某个询问时,若(j)还未访问,则不执行操作;若(j)已经访问,此时(j)的祖先即为((i,j))的最近公共祖先。
对于为什么,自己手动模拟一下其实不难明白。
对于祖先关系,我们用并查集维护即可。
code:
#include <cstdio>
const int N=500010;
int n,m,s;
struct Edge
{
int to,next;
}g[N*2],edge[N*2];
int head1[N],head2[N],cnt1=0,cnt2=0;
void add1(int u,int v)
{
g[++cnt1].to=v;g[cnt1].next=head1[u];head1[u]=cnt1;
}
void add2(int u,int v)
{
edge[++cnt2].to=v;edge[cnt2].next=head2[u];head2[u]=cnt2;
}
int ans[N],used[N],f[N];
int find(int x)
{
return f[x]=f[x]==x?x:find(f[x]);
}
void merge(int x,int y)//y±»ºÏ²¢
{
f[find(y)]=f[find(x)];
}
void tarjan(int now)
{
used[now]=1;
for(int i=head1[now];i;i=g[i].next)
{
int v=g[i].to;
if(!used[v])
{
used[v]=1;
tarjan(v);
merge(now,v);
}
}
for(int i=head2[now];i;i=edge[i].next)
{
int v=edge[i].to;
if(used[v])
ans[i+1>>1]=find(v);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
int u,v;
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
add1(u,v);add1(v,u);
}
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add2(u,v);add2(v,u);
}
tarjan(s);
for(int i=1;i<=m;i++)
printf("%d
",ans[i]);
return 0;
}
今天复习倍增的,贴一下代码
#include <cstdio>
#include <iostream>
const int N=500010;
int n,m,r;
int head[N],to[N<<1],next[N<<1],cnt;
void add(int u,int v)
{
to[++cnt]=v;next[cnt]=head[u];head[u]=cnt;
}
int f[N][21],dep[N];
void dfs(int now,int fa)
{
for(int i=head[now];i;i=next[i])
{
int v=to[i];
if(v!=fa)
{
f[v][0]=now;
dep[v]=dep[now]+1;
dfs(v,now);
}
}
}
int query(int x,int y)
{
if(dep[x]<dep[y]) std::swap(x,y);
for(int i=20;i>=0;i--)
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
void init()
{
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
}
int main()
{
int u,v,a,b;
scanf("%d%d%d",&n,&m,&r);
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dep[r]=1;
dfs(r,0);
init();
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
printf("%d
",query(a,b));
}
return 0;
}