【在线】

1.倍增法

现将深度较大的跳至与深度较小的统一深度。预处理$fa[u][i]$表示$u$往上跳$2^i$个单位后的祖先,则就可以像快速幂一样,将移动的步数化为二进制,如果第$i$位为$1$,那么向上跳$2^i$次方,即$if(1 << i \& d) u = fa[u][i]$。跳至统一深度后,若两点重合,则返回两点的任意一个。若不重合,再一个一个一起往上跳,直到重合。

复杂度为$O(N*logN) $

【code】求两点距离

#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
using namespace std; const int N = ;
int dep[N], dis[N];
int ecnt, adj[N], go[N << ], len[N << ], nxt[N << ];
int fa[N][], Log[N], n, m; inline void addEdge(const int &u, const int &v, const int &l){
nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v, len[ecnt] = l;
nxt[++ecnt] = adj[v], adj[v] = ecnt, go[ecnt] = u, len[ecnt] = l;
} inline void Init_Log(){
Log[] = -;
for(int i = ; i <= n; i++) Log[i] = Log[i >> ] + ;
} inline void dfs(const int &u, const int &f, const int &l){
dep[u] = dep[f] + ;
dis[u] = dis[f] + l;
fa[u][] = f;
for(int i = ; fa[u][i]; i++)
fa[u][i + ] = fa[fa[u][i]][i];
for(int e = adj[u]; e; e = nxt[e]){
int v = go[e];
if(v == f) continue;
dfs(v, u, len[e]);
}
} inline int lca(int u, int v){
if(dep[u] < dep[v]) swap(u, v);
int delta = dep[u] - dep[v];
for(int i = Log[delta]; i >= ; i--)
if( << i & delta) u = fa[u][i];
if(u == v) return u;
for(int i = Log[dep[u]]; i >= ; i--)
if(fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
return fa[u][];
} int main(){
scanf("%d%d", &n, &m);
Init_Log();
for(int i = ; i < n; i++){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
addEdge(x, y, z);
}
dfs(,,);
for(int i = ; i <= m; i++){
int x, y;
scanf("%d%d", &x, &y);
int L = lca(x, y);
cout<<(dis[x] - dis[L]) + (dis[y] - dis[L])<<endl;
}
return ;
}

2.树链剖分

  树链剖分

  同样,将点往上跳,不过树链剖分后可以直接从重链尾部跳到重链顶部甚至下一条重链的尾部,直到两点在同一重链上,先判重合,否则就是现在深度较小的点。

  复杂度O(mlog2 n)

  【code】求两点距离

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std; const int N = 1e5 + ;
const int oo = 0x3f3f3f3f; int dep[N], sze[N], top[N], son[N], pos[N], idx[N], val[N], fa[N];
int ecnt, adj[N], go[N << ], nxt[N << ], tot, len[N << ];
int n, m, dis[N]; inline int Re(){
int i = , f = ; char ch = getchar();
for(; (ch < '' || ch > '') && ch != '-'; ch = getchar());
if(ch == '-') f = -, ch = getchar();
for(; ch >= '' && ch <= ''; ch = getchar())
i = (i << ) + (i << ) + (ch - '');
return i * f;
} inline void Wr(int x){
if(x < ) putchar('-'), x = -x;
if(x > ) Wr(x / );
putchar(x % + '');
} inline void addEdge(const int &u, const int &v, const int &l){
nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v, len[ecnt] = l;
nxt[++ecnt] = adj[v], adj[v] = ecnt, go[ecnt] = u, len[ecnt] = l;
} inline void dfs1(const int &u, const int &f, const int &l){
dep[u] = dep[f] + ;
dis[u] = dis[f] + l;
fa[u] = f;
sze[u] = ;
for(int e = adj[u]; e; e = nxt[e]){
int v = go[e];
if(v == f) continue;
dfs1(v, u, len[e]);
sze[u] += sze[v];
if(sze[v] > sze[son[u]]) son[u] = v;
}
} inline void dfs2(const int &u, const int &f){
if(son[u]){ //先查重儿子, 保证重链连续
top[son[u]] = top[u];
idx[pos[son[u]] = ++tot] = son[u];
dfs2(son[u], u);
}
for(int e = adj[u]; e; e = nxt[e]){
int v = go[e];
if(v == f || v == son[u]) continue;
top[v] = v;
idx[pos[v] = ++tot] = v;
dfs2(v, u);
}
} inline int lca(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
if(u == v) return u;
if(dep[u] < dep[v]) return u;
else return v;
} int main(){
// freopen("h.in", "r", stdin);
n = Re(), m = Re();;
for(int i = ; i < n; i++){
int a = Re(), b = Re(), c = Re();
addEdge(a, b, c);
}
dfs1(, , );
pos[] = top[] = idx[] = tot = , dep[] = -;
dfs2(, );
for(int i = ; i <= m; i++){
int a = Re(), b = Re();
int L = lca(a, b);
Wr(dis[a] + dis[b] - * dis[L]), putchar('\n');
}
return ;
}

【离线】

【tarjan】

  奇妙的算法。但要求必须离线,先记录下所有的询问,再挨个找到答案。

  用$dfs$的思想,现将子树扫描完,再返回根节点,进入下一颗子树。

  tarjan求lca则每扫描完一颗子树,就将他与根节点的并查集进行合并,然后处理有关询问(可能现在还没法回答)。

  如下图所示:比如我要查找$(4, 5), (4, 6)$的$lca$

  扫描完$4$这颗子树后,4的并查集祖先已经设置为2,就可以开始尝试处理$4$中 的询问了

但是,处理4-6, 4-5询问时,发现5、6还没被访问到,所以回答失败,继续dfs。

        

  扫描完5,处理询问,发现4已经访问过,而我dfs时从4返回到2,4的并查集祖先已经设置为2,然后我跨过2到达5,所以4和5的祖先一定就是getAnc(4) = 2.

  4-6同理,,2的祖先被设置为1,那么4通过并查集的维护最终祖先也变为1,跨过1后到达6,那么4-6的lca一定为1.

  看懂了这个算法,就明白为什么必须要求离线了。

  【code】求两点lca

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
using namespace std; #define mp make_pair
const int N = ;
int ecnt, adj[N], go[N << ], nxt[N << ], len[N << ];
int n, m, qa[N], qb[N], qans[N];
vector<pair<int, int> > vq[N];
int anc[N], dis[N];
bool vst[N]; int read(){
int i=,f=;char ch;
for(ch=getchar();(ch<''||ch>'')&&ch!='-';ch=getchar());
if(ch=='-') {f=-;ch=getchar();}
for(;ch>=''&&ch<='';ch=getchar()) i=(i<<)+(i<<)+(ch^);
return f*i;
} inline void wr(int x){
if(x < ) putchar('-'), x = -x;
if(x > ) wr(x / );
putchar(x % + '');
} inline void addEdge(int u, int v, int l){
nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v, len[ecnt] = l;
nxt[++ecnt] = adj[v], adj[v] = ecnt, go[ecnt] = u, len[ecnt] = l;
} inline int getAnc(int u){
return (u == anc[u]) ? u : (anc[u] = getAnc(anc[u]));
} inline void Merge(int u, int v){
int fu = getAnc(u), fv = getAnc(v);
if(fu != fv) anc[fu] = fv;
} inline void tarjan(int u, int f, int d){
dis[u] = dis[f] + d;
for(int i = adj[u]; i; i = nxt[i]){
if(go[i] == f) continue;
if(!vst[go[i]]){
tarjan(go[i], u, len[i]);
Merge(go[i], u);
anc[getAnc(u)] = u;
}
}
vst[u] = true;
for(int i = ; i < vq[u].size(); i++){
if(vst[vq[u][i].first])
qans[vq[u][i].second] = getAnc(vq[u][i].first);
}
} int main(){
n = read(), m = read();
for(int i = ; i < n; i++){
int a, b, c;
a = read(), b = read(), c = read();
addEdge(a, b, c);
}
for(int i = ; i <= m; i++){
int u, v;
u = read(), v = read();
qa[i] = u, qb[i] = v;
vq[u].push_back(mp(v, i)), vq[v].push_back(mp(u, i));
}
for(int i = ; i <= n; i++) anc[i] = i;
tarjan(, , );
for(int i = ; i <= m; i++){
int ans = dis[qa[i]] + dis[qb[i]] - * dis[qans[i]];
wr(ans), putchar('\n');
}
return ;
}

【LCA最近公共祖先】在线离线的更多相关文章

  1. LCA 最近公共祖先 tarjan离线 总结 结合3个例题

    在网上找了一些对tarjan算法解释较好的文章 并加入了自己的理解 LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点.也就是说,在两个点通 ...

  2. LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现

    首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵 ...

  3. 求LCA最近公共祖先的离线Tarjan算法_C++

    这个Tarjan算法是求LCA的算法,不是那个强连通图的 它是 离线 算法,时间复杂度是 O(m+n),m 是询问数,n 是节点数 它的优点是比在线算法好写很多 不过有些题目是强制在线的,此类离线算法 ...

  4. LCA最近公共祖先 Tarjan离线算法

    学习博客:  http://noalgo.info/476.html 讲的很清楚! 对于一颗树,dfs遍历时,先向下遍历,并且用并查集维护当前节点和父节点的集合.这样如果关于当前节点(A)的关联节点( ...

  5. lca(最近公共祖先(离线))

    转自大佬博客 : https://www.cnblogs.com/JVxie/p/4854719.html   LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 首先是最近公共祖先 ...

  6. lca最近公共祖先与树上倍增。

    https://vjudge.net/contest/295298#problem/A lca 的题目 求任意两点的距离. A题是在线算法,用st表rmq来实现. https://blog.csdn. ...

  7. LCA 近期公共祖先 小结

    LCA 近期公共祖先 小结 以poj 1330为例.对LCA的3种经常使用的算法进行介绍,分别为 1. 离线tarjan 2. 基于倍增法的LCA 3. 基于RMQ的LCA 1. 离线tarjan / ...

  8. lca 最近公共祖先

    http://poj.org/problem?id=1330 #include<cstdio> #include<cstring> #include<algorithm& ...

  9. Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载)

    Tarjan算法应用 (割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)问题)(转载) 转载自:http://hi.baidu.com/lydrainbowcat/blog/item/2 ...

  10. LCA(最近公共祖先)模板

    Tarjan版本 /* gyt Live up to every day */ #pragma comment(linker,"/STACK:1024000000,1024000000&qu ...

随机推荐

  1. Java基础学习总结(52)——Liunx系统Centos上搭建Java开发环境

    一.安装jdk 1.查看Linux自带的JDK是否已安装 [plain] view plain copy print? java –version 如果出现openjdk,最好还是先卸载掉openjd ...

  2. amazeui学习笔记--css(HTML元素1)--按钮Button

    amazeui学习笔记--css(HTML元素1)--按钮Button 一.总结 1.button的基本使用:a.am-btn 在要应用按钮样式的元素上添加 .am-btn,b.颜色 再设置相应的颜色 ...

  3. python opencv —— io(帧、图像、视频的读取与保存)

    0. VideoCapture VideoCapture:构造函数: 常见成员函数: open:打开视频文件,或者捕获视频设备,该函数会首先调用 release() 函数以关闭已打开的文件或设备: P ...

  4. Altium Designer一些问题

    1,当你重新导入原理图的时候,会提示删除类 2,不要粘贴和赋值pcb中的原件,否则飞线可能会消失

  5. UVA 11090 Going in Cycle!!(Bellman-Ford推断负圈)

    题意:给定一个n个点m条边的加权有向图,求平均权值最小的回路. 思路:使用二分法求解.对于每个枚举值mid,推断每条边权值减去mid后有无负圈就可以. #include<cstdio> # ...

  6. 更改jdk所用内存空间

    在做项目是有时候会遇到内存jvm内存不够用的情况,在myeclipse是这样设置的. -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=128m

  7. js进阶ajax的XMLHttpRequest对象的status和statustext属性(如果ajax和php联合使用的话:open连接服务器的第二个参数文件路径改成请求php的url即可)

    js进阶ajax的XMLHttpRequest对象的status和statustext属性(如果ajax和php联合使用的话:open连接服务器的第二个参数文件路径改成请求php的url即可) 一.总 ...

  8. vuejs及相关工具介绍

    轻量级前端mvm的框架 图片.png 对es6语法的简单描述 图片.png 融合了react和angular的优点,组件化和灵活应用和指令,在国际上是一款极有潜力的前端框架. 1.双向绑定 两段相加得 ...

  9. 【32.22%】【codeforces 602B】Approximating a Constant Range

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  10. php实现Bloom Filter

    Bloom Filter(BF) 是由Bloom在1970年提出的一种多哈希函数映射的高速查找算法,用于高速查找某个元素是否属于集合, 但不要求百分百的准确率. Bloom filter通经常使用于爬 ...