注:代码经过简单修改即可适用于 洛谷P3379
代码洛谷题目范围 为 n = 5e5,以下代码内部使用的 vector push_back 和 遍历 vector 导致本题部分代码 Time Limit
可以使用如下代码替换 add建边,和 循环
int cnt = -1, head[maxn]; struct node{ int nxt, to; }; node Edge[maxn*2]; void add(int u,int v){ Edge[++cnt].nxt = head[u]; Edge[cnt].to = v; head[u] = cnt; } // 主程序内需要初始化 memset(head, -1, sizeof(head));
for(int i=head[u];i!=-1;i=Edge[i].nxt){ int v = Edge[i].to; }
为什么要学习LCA算法
LCA :两个树上节点,逐层向上寻找它们的父节点,第一次查找到的 "相同 (祖先) 节点"
查询q次,每次给出两个节点,向上递归查找,最坏情况下是 O(n* q) 的时间复杂度
要降低查询的时间复杂度
共有三种算法:(在线:来一个查询操作,执行一次query;离线:读入全部查询操作,一次性计算所有结果)
1. LCA tarjan算法 O(n +q) 离线算法
2. dfs + ST(RMQ) 算法 O(n * logn) 在线算法, RMQ预处理后可以做到 O(1) 查询
3. 倍增算法 O(n * logn) 在线算法,O(n * logn)处理数据,O(logn)查询
-
算法一:LCA tarjan算法
算法原理
dfs 过程:
1. 查询当前 节点u,是否包含未遍历 子节点v,包含继续向下递归查找,回溯时 更新 节点v的 fa[v] = u
2. 遍历完当前节点所有子树,
在查询集合中,查找是否在 在包括当前节点u 的所有查询中:
如果这个查询(u , v)的另一个节点v 被遍历过,那么当前这个查询(u, v)的答案是 v节点所在并查集里的根节点
如上图 查询 (8 , 11) 的 LCA,因为2号节点 的 fa[2] 并未更新,还是初始值 2 ,
依次查到 5节点,10节点 11节点时,将它们放入绿框内,(fa[2] = 2不变,
之前所有绿框内的 节点,根据并查集,都会维护在2号节点下,他们所属并查集的根节点 为2
查到11节点,这个查询(8 , 11