P3379 LCAtarjan
看到题解基本上都是倍增做法,找了半天tarjan的代码都没找到QAQ,我就提供一个tarjan代码好了
tarjan我个人理解就是:从叶到根依次选取节点(用dfs+回溯),然后把选取的节点查询子树,那么子树中的所有节点的公共祖先都是这个节点,那么查询的第一个的子树节点有包括(u,v)便是(u,v)的最近公共祖先。
tarjan具体做法请看这个博客
我认为tarjan做法真的很巧妙,每次选取节点时利用并查集把所有的点的公共祖先转移了。
我这个代码是用vector储存边,用前向星(结构体Edge)储存的询问问题和问题答案,然后每个问题储存两次,那么肯定会有一个答案为0。当然也可以直接再开一个数组来储存答案。
#include <bits/stdc++.h>
using namespace std;
#define ll int
#define pc(a) putchar(a)
#define rg register
const ll maxn=500001;
const ll maxm=500001;
void gc(char &a)
{
a=getchar();
while(a==' '||a=='\n') a=getchar();
}
ll read(){
char c;ll x=0;bool flag=0;gc(c);
while(c<'0'||c>'9'){if(c=='-') flag=1;gc(c);}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48),c=getchar();}
return flag?-x:x;
}
void pr(ll x){
if(x<0){x=-x;pc('-');}
if(x>9) pr(x/10);
pc(x%10+48);
}
//----------快读-------------
struct Edge
{
ll v,nx,dis;//dis为答案
Edge(ll v,ll nx):v(v),nx(nx){this->dis=0;}
Edge(){}
}edge[maxm<<1];
ll head[maxn],en;
void edgepush(ll u,ll v)
{
edge[++en]=Edge(v,head[u]);
head[u]=en;
}
ll fa[maxn],n,m,e;
vector<ll> tree[maxn];
bool flag[maxn];
int find(ll u){
return u==fa[u]?u:fa[u]=find(fa[u]);
}//超短并查集
void dfs(ll u,ll f)
{
fa[u]=u;
for(int v,i=0;i<tree[u].size();i++)
{
v=tree[u][i];
if(f==v) continue;//因为是无向图,防止死循环
dfs(v,u);
fa[find(v)]=u;
}//遍历子树
flag[u]=1;
for(int v,i=head[u];i;i=edge[i].nx)
{
v=edge[i].v;
if(flag[v]&&!edge[i].dis)
edge[i].dis=find(v);
}//查询答案
}
int main()
{
n=read(),m=read(),e=read();
for(int u,v,i=1;i<n;i++)
{
u=read(),v=read();
tree[u].push_back(v);
tree[v].push_back(u);
}//建树
for(int u,v,i=1;i<=m;i++)
{
u=read(),v=read();
edgepush(u,v);
edgepush(v,u);
}//建问题
dfs(e,-1);//遍历
for(int i=1;i<=(m*2);i++)
if(edge[i].dis)
{
pr(edge[i].dis);
pc('\n');//换行
}
}