如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先.
输入格式:
第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。
接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。
接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。
输出格式:
输出包含M行,每行包含一个正整数,依次为每一个询问的结果.
输入样例#1:
5 5 4 3 1 2 4 5 1 1 4 2 4 3 2 3 5 1 2 4 5
输出样例#1:
4 4 1 4 4思路的话可以见我的转载博文:传送门
tarjan方法
这是我的代码实现~
%:pragma GCC optmize(3)//o3优化,尽量不要开
#include<bits/stdc++.h>
#define maxn 1100010
#define maxm 1100130
using namespace std;
int n,m,ss;
struct aaa{int to,next;}edge[maxn];
struct bbb{int same,to,next,num;bool flag;bbb(){flag=0;}}s[maxm];
int head[maxn],shead[maxm],pre[maxn],book[maxn],ans[maxm];
int cnt=0;
inline int read()//注意快读
{
char c=getchar();
int num=0;
while(!isdigit(c)) c=getchar();
while(isdigit(c))
{
num=num*10+c-'0';
c=getchar();
}
return num;
}
void add_edge(int u,int v)
{
edge[++cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt;
edge[++cnt].to=u;
edge[cnt].next=head[v];
head[v]=cnt;
}
int cntt=0;
void add_search(int u,int v,int w)
{
s[++cntt].to=v;
s[cntt].next=shead[u];
s[cntt].num=w;
shead[u]=cntt;
s[++cntt].to=u;
s[cntt].num=w;
s[cntt].next=shead[v];
shead[v]=cntt;
}
int find(int x)
{
if(pre[x]==x) return x;
else return pre[x]=find(pre[x]);
}
int merge(int x,int y)//注意谁和谁合并
{
int fx=find(x),fy=find(y);
if(pre[fy]!=fx)
{
pre[fy]=fx;
return 1;
}
return 0;
}
void tarjan(int point,int f)
{
for(int i=head[point];i;i=edge[i].next)
if(edge[i].to!=f&&!book[edge[i].to])//不能返回之前走过的点——父节点
{
tarjan(edge[i].to,point);
merge(point,edge[i].to);
book[edge[i].to]=1;
}
for(int i=shead[point];i;i=s[i].next)
if(book[s[i].to]&&!s[i].flag)
{
ans[s[i].num]=find(s[i].to);
s[i].flag=1;//走过的点标记
int tt=i;if(tt%2)tt++;else tt--;
s[tt].flag=1;//因为是双向储存所以两边都要去掉
}
}
int main()
{
scanf("%d%d%d",&n,&m,&ss);
for(int i=1;i<n;i++)
{
int t1,t2;
t1=read();t2=read();
add_edge(t1,t2);
}
for(int i=1;i<=n;i++)
pre[i]=i;
for(int i=1;i<=m;i++)
{
int t1,t2;
t1=read();t2=read();
add_search(t1,t2,i);
}
tarjan(ss,0);//虚构一个父节点
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
倍增的思想
真是巧妙,巧妙的我晕晕转~
这是我的代码~以前的代码风格不是特别好,于是又写了一遍
#include<bits/stdc++.h>
#define maxn 600000
using namespace std;
int n,m,s,cnt=0;
int book[maxn],p[maxn][25],deep[maxn],head[maxn];
struct Edge{int to,next;}e[maxn*4];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int x,int fa)
{
book[x]=1;
for(int i=head[x];i;i=e[i].next)
if(!book[e[i].to])
{
deep[e[i].to]=deep[x]+1;
p[e[i].to][0]=x;
dfs(e[i].to,x);
}
}
void init()
{
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i<=n;i++)
p[i][j]=p[p[i][j-1]][j-1];
}
int lca(int a,int b)
{
if(deep[a]<deep[b]) swap(a,b);//注意是深度
int i=0;
int depth=deep[a]-deep[b];
while((1<<i)<=n) i++;i--;
for(int j=i;j>=0;j--)
if((1<<j)&depth) //同样注意是depth寻找二进制位上的1
a=p[a][j];
if(a==b) return a;
for(int j=i;j>=0;j--)
{
if(p[a][j]!=p[b][j])
{
a=p[a][j];
b=p[b][j];
}
}
return p[a][0];
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;i++)
{
int t1,t2;
scanf("%d%d",&t1,&t2);
add(t1,t2);
add(t2,t1);
}
deep[s]=0;//这里的深度无论初始化多少都无所谓的
dfs(s,0);
init();
for(int i=1;i<=m;i++)
{
int t1,t2;
scanf("%d%d",&t1,&t2);
printf("%d\n",lca(t1,t2));
}
return 0;
}