Nearest Common Ancestors
Time Limit: 1 Sec Memory Limit: 128 MBDescription
给定N个节点的一棵树,有K次查询,每次查询a和b的最近公共祖先。
样例中的16和7的公共祖先(LCA:Least Common Ancestors)是4。
Input
第一行两个整数N(1 < N <= 105)、K(1 <= K <= 105)
第2~N行,每行两个整数a、b(1 <= a,b <= N),表示a是b的父亲。
第N+1~N+K+1行,每行两个整数a、b(1 <= a,b <= N),表示询问a和b的最近公共祖先是谁。
Output
输出K行,第i行表示第i个查询的最近公共祖先是谁。
Sample Input
16 1
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 11
6 12
16 7
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 11
6 12
16 7
Sample Output
4
HINT
30%数据 N<=20,K<=5。小数据,方便调试
50%数据 N<=1000,K<=1000。中数据,暴力可过
100%数据 1 < N <= 105,1 <= K <= 105。大数据,请使用树上倍增、LCA转RMQ&ST、离线Tarjan、树链剖分求LCA
版本1:不带启发式合并 O(2*n+nlogn)
#include<stdio.h>
int n,t;
int head[100001];
int next[200001];
int to[200001];
int ask_head[100001];
int ask_next[200001];
int ask_to[200001];
int fa[100001];
int ans[100001];
bool vis[100001];
bool is[100001];
int idx,idx2,all_fa;
int find(int p)
{
if(fa[p]==p)
return p;
fa[p]=find(fa[p]);
return fa[p];
}
void merge(int a,int b)
{
int fa_a=find(a);
int fa_b=find(b);
if(fa_a==fa_b)
return;
fa[fa_b]=fa_a;
}
void dfs(int from,int p)
{
for(int i=head[p];i;i=next[i])
if(to[i]!=from)
dfs(p,to[i]);
for(int i=ask_head[p];i;i=ask_next[i])
if(vis[ask_to[i]])
ans[(i+1)/2]=find(ask_to[i]);
merge(from,p);
vis[p]=true;
}
int main()
{
scanf("%d%d",&n,&t);
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
next[++idx]=head[a];
head[a]=idx;
to[idx]=b;
is[b]=true;
}
for(int i=1;i<=n;i++)
if(!is[i])
all_fa=i;
for(int i=1;i<=t;i++)
{
int a,b;
scanf("%d%d",&a,&b);
if(a==b)
ans[i]=a;
ask_next[++idx2]=ask_head[a];
ask_head[a]=idx2;
ask_to[idx2]=b;
ask_next[++idx2]=ask_head[b];
ask_head[b]=idx2;
ask_to[idx2]=a;
}
dfs(0,all_fa);
for(int i=1;i<=t;i++)
printf("%d\n",ans[i]);
}
版本2:带启发式合并 O(2*n+α*n)
#include<stdio.h>
int n,t;
int head[100001];
int next[200001];
int to[200001];
int ask_head[100001];
int ask_next[200001];
int ask_to[200001];
int fa[100001];
int s[100001];
int ans[100001];
int rel_fa[100001];
bool vis[100001];
bool is[100001];
int idx,idx2,all_fa;
int find(int p)
{
if(fa[p]==p)
return p;
fa[p]=find(fa[p]);
return fa[p];
}
void merge(int a,int b)
{
int fa_a=find(a);
int fa_b=find(b);
if(fa_a==fa_b)
return;
if(s[fa_a]>s[fa_b])
s[fa_a]+=s[fa_b],fa[fa_b]=fa_a,rel_fa[fa_a]=a;
else
s[fa_b]+=s[fa_a],fa[fa_a]=fa_b,rel_fa[fa_b]=a;
}
void dfs(int from,int p)
{
for(int i=head[p];i;i=next[i])
if(to[i]!=from)
dfs(p,to[i]);
vis[p]=true;
for(int i=ask_head[p];i;i=ask_next[i])
if(vis[ask_to[i]])
ans[(i+1)/2]=rel_fa[find(ask_to[i])];
merge(from,p);
}
int main()
{
scanf("%d%d",&n,&t);
for(int i=1;i<=n;i++)
rel_fa[i]=i,fa[i]=i,s[i]=1;
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
next[++idx]=head[a];
head[a]=idx;
to[idx]=b;
is[b]=true;
}
for(int i=1;i<=n;i++)
if(!is[i])
all_fa=i;
for(int i=1;i<=t;i++)
{
int a,b;
scanf("%d%d",&a,&b);
ask_next[++idx2]=ask_head[a];
ask_head[a]=idx2;
ask_to[idx2]=b;
ask_next[++idx2]=ask_head[b];
ask_head[b]=idx2;
ask_to[idx2]=a;
}
dfs(0,all_fa);
for(int i=1;i<=t;i++)
printf("%d\n",ans[i]);
}