当前位置: 首页 > news >正文

【笔记】最近公共祖先 Tarjan 算法

该算法可以离线求最近公共祖先,大幅节省时间复杂度(\(O(n \log n)\)->\(O(n+m)\))。

缺点是如果题目要求强制在线那么就用不了了。

具体实现是这样的:把原树用双向边存起来,然后把每一对要求 LCA 的两个点在一个新图上存一个双向边(称为查询边)。

对原图进行一次 DFS 遍历,遍历过程中使用并查集存储每个节点的祖先。我们在遍历过程中往下遍历时节点的祖先是它本身,遍历完这个节点的子树后回溯时是它的父亲。回溯过程中遍历到以 \(v\) 为根的子树时,如果 \(i\) 节点恰好访问过,而并查集中存储的又正是 \(v\) 的最近祖先,那么 \(v\) 的最近祖先就恰好是两个点的最近公共祖先。将查询边中 \(v\)\(i\) 节点的 LCA 设为并查集中存储 \(v\) 的祖先(即 \(v\) 的父亲)即可。

时间复杂度涉及 \(3\) 个要素,一个是 DFS 的时间复杂度 \(O(n)\),一个是并查集的初始化 \(O(n)\),一个是路径压缩优化过后的并查集时间复杂度 \(O(a(n))\),其中 \(a\) 表示的是阿克曼函数的反函数,增长极其缓慢,常数最大到 \(4\) 左右。总时间复杂度 \(O(n+m)\) 左右。去掉了倍增法的 \(\log\),但是常数会比倍增法大。

在 LCA 模板题中,可以看到两次提交差别非常大:

在线算法倍增:

离线算法 Tarjan:

可以注意到因为常数较大导致前面的小点跑得反而慢,但是后面的大点快了将近 2 倍。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=5e5+10;
struct Node{int to,nxt,LCA;
}e[2*maxn],q[2*maxn];
int n,m,s,D;
int tot,h[maxn],hq[maxn];
int fa[maxn];
bool vis[maxn];
void Add(int u,int v){tot++;e[tot].to=v;e[tot].nxt=h[u];h[u]=tot;
}
void Addq(int u,int v){tot++;q[tot].to=v;q[tot].nxt=hq[u];hq[u]=tot;
}
int Find(int x){if(fa[x]==x) return x;else return fa[x]=Find(fa[x]);
}
void tarjan(int u){fa[u]=u;vis[u]=1;for(int i=h[u];i!=-1;i=e[i].nxt){int v=e[i].to;if(!vis[v]){tarjan(v);fa[v]=u;}}for(int i=hq[u];i!=-1;i=q[i].nxt){int v=q[i].to;if(vis[v]){if(i%2==0) q[i-1].LCA=q[i].LCA=Find(v);else q[i+1].LCA=q[i].LCA=Find(v);}}
}
int main(){memset(h,-1,sizeof(h));memset(hq,-1,sizeof(hq));scanf("%d%d%d",&n,&m,&s);for(int i=1;i<=n-1;i++){int x,y;scanf("%d%d",&x,&y);Add(x,y);Add(y,x);}	tot=0;for(int i=1;i<=m;i++){int a,b;scanf("%d%d",&a,&b);Addq(a,b);Addq(b,a);}tarjan(s);for(int i=1;i<=2*m;i+=2){printf("%d\n",q[i].LCA);}return 0;
}
http://www.rkmt.cn/news/89338.html

相关文章:

  • Spring的DI依赖注入(配置文件方式)
  • Office Tool Plus v10.29.50 office安装激活一条龙
  • 如何编写优美的代码:从工匠到艺术家的修炼之路
  • AI搜索焦虑自救指南:一份面向2026年的系统化追赶方案
  • 告别文件整理拖延症!快速找关键字 TXT + 批量复制到目标文件夹,躺平搞定
  • 《追问者宪章》完整版
  • 视频剪辑软件电脑版排行榜,2025年度前十名软件推荐
  • Error occurred during initialization of VMCould not reserve enough space for object heap
  • 东芝与Quantum Corridor实现量子安全网络通信重大突破
  • Qt Creator中pro文件添加外部动态库的方法
  • 芯祥联科技SNMP协议栈产品形态
  • 【笔记】线段树
  • 基于java的SpringBoot/SSM+Vue+uniapp的篮球管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
  • P3258 [JLOI2014] 松鼠的新家
  • K8S 中使用 YAML 安装 ECK
  • 23、深入解析 fwsnort 与 psad 的协同防御机制
  • 光伏板太阳能充电MATLAB仿真探索
  • 基于SpringBoot的高校HIV预防宣传系统毕业设计项目源码
  • 创维LB2004_瑞芯微RK3566_2G+32G_删除移动定制_安卓11_原生桌面_线刷固件包-方法4
  • 详细介绍:【分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
  • 【Java避坑】为什么我的 String a == b 返回 false?一文搞懂 Java 中的 == 与 equals
  • Java面试三连击:原理拆解+实战避坑
  • 【题解】Luogu P11854 [CSP-J2022 山东] 宴会
  • 代码源挑战赛 Round 41
  • 详细介绍:NumPy / pandas 类型选型、内存占用与性能优化
  • 告别选择困难!2025年远程控制软件场景化终极横评
  • 青少年编程学习:考级与竞赛如何平衡
  • 2025 Autel MaxiVCI V150 Wireless Dongle: CAN FD/DOIP for Autel 900 Series Scanners
  • 【题解】Luogu P8269 [USACO22OPEN] Visits S
  • Ubuntu环境中LLaMA Factory 的部署与配置—构建大语言模型微调平台 - 实践