tarjan解决LCA

tarjan他老人家比较nbbbb(破音),所以实际上有好几个tarjan算法,这里是解决树上最近公共祖先lca的算法

推荐下一个b站视频,emmm有些长,建议2倍速看

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值