题解 P3379 【【模板】最近公共祖先(LCA)】
Salamander · · 个人记录
这题其实用tarjan可以过的,我加了读入优化,其实不加也可以过。
注意储存数据的时候用链表储存边和询问,dfs遍历的时候注意不要遍历到父亲上去。
特别注意询问的储存,一个询问要用链表储存两遍,然后回答的时候只要回答了其中一个就要把两个都去掉。
原理不懂的可以去这里http://www.cnblogs.com/JVxie/p/4854719.html
#include<bits/stdc++.h>
using namespace std;
template<typename Type>inline void read(Type &xx)
{
Type f=1;char ch;xx=0;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())xx=xx*10+ch-'0';
xx*=f;
}
struct edge
{
int to,next;
}e[1000001];//边的储存
struct questions
{
int to,next,same,num;
bool flag;
questions(){flag=false;}
}q[1000001];//询问的储存,flag=false表示还没回答,num表示是第几个询问,same储存与这个询问相同的询问序号。
bool b[500001];
int head[500001],que[500001],father[500001];
int n,m,s,nume=0,numq=0,ans[500001];
void add_edge(int x,int y)
{
e[++nume].to=y;
e[nume].next=head[x];
head[x]=nume;
e[++nume].to=x;
e[nume].next=head[y];
head[y]=nume;
}
void add_que(int x,int y,int k)
{
q[++numq].to=y;
q[numq].same=numq+1;
q[numq].next=que[x];
q[numq].num=k;
que[x]=numq;
q[++numq].to=x;//询问要储存到两个点的链表序列里,删的时候也要一起删
q[numq].same=numq-1;
q[numq].next=que[y];
q[numq].num=k;
que[y]=numq;
}
int find(int x)//并查集
{
if(father[x]!=x)father[x]=find(father[x]);
return father[x];
}
void unionn(int x,int y)//并查集
{
father[find(y)]=find(x);
}
void LCA(int point,int f)//point是当前搜索节点,f是它的父亲
{
for(int i=head[point];i!=0;i=e[i].next)//遍历与point相连的所有边
if(e[i].to!=f&&!b[e[i].to])
{
LCA(e[i].to,point);
unionn(point,e[i].to);//合并
b[e[i].to]=1;
}
for(int i=que[point];i!=0;i=q[i].next)//遍历与point相关的询问
if(!q[i].flag&&b[q[i].to])//如果另一个点遍历过了并且该询问没有回答过
{
ans[q[i].num]=find(q[i].to);//记录下答案
q[i].flag=1;
q[q[i].same].flag=1;//把两个点上的询问都去掉
}
}
int main()
{
read(n);read(m);read(s);
for(int i=1,x,y;i<=n-1;i++)
{
father[i]=i;
read(x);read(y);
add_edge(x,y);
}
father[n]=n;
for(int i=1,x,y;i<=m;i++)
{
read(x);read(y);
add_que(x,y,i);
}
LCA(s,0);
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}