tarjan他老人家比较nbbbb(破音),所以实际上有好几个tarjan算法,这里是解决树上最近公共祖先lca的算法
tarjan是离线算法,也就是多个提问只能先存起来,然后一次性解决后输出,不能做到即问即答
具体思想是dfs+并查集
因为dfs的时候,一定会经过两点的lca,具体画个图就很清楚,所以在dfs的时候,对于当前点u看是否有关于它的提问
如果有,那么是否已经访问过它的一个点了,如果访问过,那么Find(已访问过的点)就是这个询问的lca
题目是洛谷p3379,裸的板子
所以代码(不开o2会超时,e小声)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+7;
vector<int> G[maxn],Q[maxn];
int ans[maxn][3],p[maxn],n,m,s,x,y;
bool vis[maxn];
void init(){for(int i=0;i<maxn;i++)p[i] = i;}
int Find(int x){return x == p[x]?x:p[x] = Find(p[x]);}
void tarjan(int u){
vis[u] = true;
for(auto i : Q[u]){
if((ans[i][0] == u && vis[ans[i][1]]) || (ans[i][1] == u && vis[ans[i][0]]))
ans[i][2] = Find(ans[i][0] == u?ans[i][1]:ans[i][0]);
}
for(auto v : G[u]){
if(vis[v])continue;
tarjan(v);
p[v] = u;
}
}
signed main(){
init();
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;i++)scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x);
for(int i=1;i<=m;i++)scanf("%d%d",&ans[i][0],&ans[i][1]),Q[ans[i][0]].push_back(i),Q[ans[i][1]].push_back(i);
tarjan(s);
for(int i=1;i<=m;i++)printf("%d\n",ans[i][2]);
return 0;
}
o2会超时,这你能忍?
链式前向星优化、(笑)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+7;
struct node{int val,next;}E[maxn<<2];
int x,y,n,m,s,tot = 1,ans[maxn][3],p[maxn],Q[maxn],head[maxn];
bool vis[maxn];
void init(){for(int i=0;i<maxn;i++)p[i] = i;}
void add(int u,int v,int flag){/// 0 -> edge
if(flag)E[tot] = {v,Q[u]},Q[u] = tot++;
else E[tot] = {v,head[u]},head[u] = tot++;
}
int Find(int x){return x == p[x] ? x:p[x] = Find(p[x]);}
void tarjan(int u){
vis[u] = true;
for(int v=Q[u];v;v = E[v].next){
int i = E[v].val;
if((ans[i][0] == u && vis[ans[i][1]]) || (ans[i][1] == u && vis[ans[i][0]]))
ans[i][2] = Find(ans[i][0] == u?ans[i][1]:ans[i][0]);
}
for(int i=head[u];i;i = E[i].next){
int v = E[i].val;
if(vis[v])continue;
tarjan(v);
p[v] = u;
}
}
signed main(){
init();
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y,0),add(y,x,0);
for(int i=1;i<=m;i++)scanf("%d%d",&ans[i][0],&ans[i][1]),add(ans[i][0],i,1),add(ans[i][1],i,1);
tarjan(s);
for(int i=1;i<=m;i++)printf("%d\n",ans[i][2]);
return 0;
}
压行代码 ( 笑、写着玩的
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+7;
int ans[maxn][3],head[2][maxn],Fa[maxn],n,m,s,tot = 1;
bool vis[maxn];
pair<int,int> E[maxn<<2];
void add(int u,int v,int flag){E[tot] = make_pair(v,head[flag][u]),head[flag][u] = tot++;}
int Find(int x){return x == Fa[x] ? x:Fa[x] = Find(Fa[x]);}
void init(){for(int i=0;i<maxn;i++)Fa[i] = i;}
void tarjan(int u){
vis[u] = true;
for(int i=head[1][u],j = E[i].first;i;i=E[i].second,j = E[i].first)
if((ans[j][0] == u && vis[ans[j][1]]) || (ans[j][1] == u && vis[ans[j][0]]))
ans[j][2] = Find(ans[j][0] == u?ans[j][1]:ans[j][0]);
for(int i=head[0][u],j = E[i].first;i;i=E[i].second,j = E[i].first)if(!vis[j])
tarjan(j),Fa[j] = u;
}
signed main(){
init();
scanf("%d%d%d",&n,&m,&s);
for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),add(x,y,0),add(y,x,0);
for(int i=1;i<=m;i++)scanf("%d%d",&ans[i][0],&ans[i][1]),add(ans[i][0],i,1),add(ans[i][1],i,1);
tarjan(s);
for(int i=1;i<=m;i++)printf("%d\n",ans[i][2]);
return 0;
}