洛谷上的lca模板题——传送门

1.tarjan求lca

学了求lca的tarjan算法(离线),在洛谷上做模板题,结果后三个点超时。

又把询问改成链式前向星,才ok。

这个博客,tarjan分析的很详细。

附代码——

#include <cstdio>
#include <cstring> const int maxn = ; int n, m, cnt, s, cns;
int x, y, z[maxn];//z是x和y的lca
int f[maxn], head[maxn], from[maxn];
bool vis[maxn];
struct node
{
int to, next;
}e[ * maxn];
struct Node
{
int to, next, num;
}q[ * maxn]; inline int read()//读入优化
{
int x = , f = ;
char ch = getchar();
while(ch < '' || ch > '')
{
if(ch == '-') f = -;
ch = getchar();
}
while(ch >= '' && ch <= '')
{
x = x * + ch - '';
ch = getchar();
}
return x * f;
} inline void ask(int u, int v, int i)//储存待询问的结构体,也是链式前向星优化
{
q[cns].num = i;//num表示第几次询问
q[cns].to = v;
q[cns].next = from[u];
from[u] = cns++;
} inline void add(int u, int v)//
{
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt++;
} inline int find(int a)
{
return a == f[a] ? a : f[a] = find(f[a]);//路径压缩优化
} /*inline void Union(int a, int b)
{
int fx = find(a), fy = find(b);
if(fx == fy) return;
f[fy] = fx;
}*/ inline void tarjan(int k)
{
int i, j;
vis[k] = ;
f[k] = k;
for(i = head[k]; i != -; i = e[i].next)
if(!vis[e[i].to])
{
tarjan(e[i].to);
//Union(k, e[i].to);
f[e[i].to] = k;
}
for(i = from[k]; i != -; i = q[i].next)
if(vis[q[i].to] == )
z[q[i].num] = find(q[i].to);
} int main()
{
int i, j, u, v;
n = read();
m = read();
s = read();
memset(head, -, sizeof(head));
memset(from, -, sizeof(from));
for(i = ; i <= n - ; i++)
{
u = read();
v = read();
add(u, v);//注意添加两遍
add(v, u);
}
for(i = ; i <= m; i++)
{
x = read();
y = read();
ask(x, y, i);//两遍
ask(y, x, i);
}
tarjan(s);
for(i = ; i <= m; i++) printf("%d\n", z[i]);
return ;
}

进过培训,修改了代码

 # include <iostream>
# include <cstdio>
# include <cstring>
# include <string>
# include <cmath>
# include <vector>
# include <map>
# include <queue>
# include <cstdlib>
# define MAXN
using namespace std; inline int get_num() {
int k = , f = ;
char c = getchar();
for(; !isdigit(c); c = getchar()) if(c == '-') f = -;
for(; isdigit(c); c = getchar()) k = k * + c - '';
return k * f;
} int n, m, s;
int fa[MAXN], qx[MAXN], qy[MAXN], ans[MAXN], f[MAXN];
vector <int> vec[MAXN], q[MAXN]; inline int find(int x)
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
} inline void dfs(int u)
{
int i, v;
fa[u] = u;
for(i = ; i < vec[u].size(); i++)
{
v = vec[u][i];
if(f[u] != v) f[v] = u, dfs(v);
}
for(i = ; i < q[u].size(); i++)
if(f[v = u ^ qx[q[u][i]] ^ qy[q[u][i]]])
ans[q[u][i]] = find(v);
fa[u] = f[u];
} int main()
{
int i, x, y;
n = get_num();
m = get_num();
s = get_num();
for(i = ; i < n; i++)
{
x = get_num();
y = get_num();
vec[x].push_back(y);
vec[y].push_back(x);
}
for(i = ; i <= m; i++)
{
qx[i] = get_num();
qy[i] = get_num();
q[qx[i]].push_back(i);
q[qy[i]].push_back(i);
}
dfs(s);
for(i = ; i <= m; i++) printf("%d\n", ans[i]);
return ;
}

其实上面两个代码有些重复运算,请手动把求lca的过程放到dfs上面(也就是遍历到这个节点就求lca,而不是遍历完再求)

2.倍增求lca

下面是求lca的倍增算法(在线)

1. DFS预处理出所有节点的深度和父节点

inline void dfs(int u)
{
int i;
for(i=head[u];i!=-;i=next[i])
{
if (!deep[to[i]])
{
deep[to[i]] = deep[u]+;
p[to[i]][] = u; //p[x][0]保存x的父节点为u;
dfs(to[i]);
}
}
}

dfs预处理

2. 初始各个点的2^j祖先是谁 ,其中 2^j (j =0...log(该点深度))倍祖先,1倍祖先就是父亲,2倍祖先是父亲的父亲......。

void init()
{
int i,j;
//p[i][j]表示i结点的第2^j祖先
for(j=;(<<j)<=n;j++)
for(i=;i<=n;i++)
if(p[i][j-]!=-)
p[i][j]=p[p[i][j-]][j-];//i的第2^j祖先就是i的第2^(j-1)祖先的第2^(j-1)祖先
}

初始化

3.从深度大的节点上升至深度小的节点同层,如果此时两节点相同直接返回此节点,即lca。

否则,利用倍增法找到最小深度的 p[a][j]!=p[b][j],此时他们的父亲p[a][0]即lca。

int lca(int a,int b)//最近公共祖先
{
int i,j;
if(deep[a]<deep[b])swap(a,b);
for(i=;(<<i)<=deep[a];i++);
i--;
//使a,b两点的深度相同
for(j=i;j>=;j--)
if(deep[a]-(<<j)>=deep[b])
a=p[a][j];
if(a==b)return a;
//倍增法,每次向上进深度2^j,找到最近公共祖先的子结点
for(j=i;j>=;j--)
{
if(p[a][j]!=-&&p[a][j]!=p[b][j])
{
a=p[a][j];
b=p[b][j];
}
}
return p[a][];
}

倍增求lca

最后是完整代码,为了节约时间,就没有把p数组初始化为-1.

#include <cstdio>
#include <cstring>
#include <iostream> const int maxn = ;
int n, m, cnt, s;
int next[ * maxn], to[ * maxn], head[ * maxn], deep[maxn], p[maxn][]; inline void add(int x, int y)
{
to[cnt] = y;
next[cnt] = head[x];
head[x] = cnt++;
} inline void dfs(int i)
{
int j;
for(j = head[i]; j != -; j = next[j])
if(!deep[to[j]])
{
deep[to[j]] = deep[i] + ;
p[to[j]][] = i;
dfs(to[j]);
}
} inline void init()
{
int i, j;
for(j = ; ( << j) <= n; j++)
for(i = ; i <= n; i++)
p[i][j] = p[p[i][j - ]][j - ];
} inline int lca(int a, int b)
{
int i, j;
if(deep[a] < deep[b]) std::swap(a, b);
for(i = ; ( << i) <= deep[a]; i++);
i--;
for(j = i; j >= ; j--)
if(deep[a] - ( << j) >= deep[b])
a = p[a][j];
if(a == b) return a;
for(j = i; j >= ; j--)
if(p[a][j] != p[b][j])
{
a = p[a][j];
b = p[b][j];
}
return p[a][];
} int main()
{
int i, j, x, y;
memset(head, -, sizeof(head));
scanf("%d %d %d", &n, &m, &s);
for(i = ; i <= n - ; i++)
{
scanf("%d %d", &x, &y);
add(x, y);
add(y, x);
}
deep[s] = ;
dfs(s);
init();
for(i = ; i <= m; i++)
{
scanf("%d %d", &x, &y);
printf("%d\n", lca(x, y));
}
return ;
}

经过培训,又改了改代码。

 # include <iostream>
# include <cstdio>
# include <cstring>
# include <string>
# include <cmath>
# include <vector>
# include <map>
# include <queue>
# include <cstdlib>
# define MAXN
using namespace std; inline int get_num() {
int k = , f = ;
char c = getchar();
for(; !isdigit(c); c = getchar()) if(c == '-') f = -;
for(; isdigit(c); c = getchar()) k = k * + c - '';
return k * f;
} int n, m, s;
int f[MAXN][], deep[MAXN];
vector <int> vec[MAXN]; inline void dfs(int u)
{
int i, v;
deep[u] = deep[f[u][]] + ;
for(i = ; f[u][i]; i++) f[u][i + ] = f[f[u][i]][i];
for(i = ; i < vec[u].size(); i++)
{
v = vec[u][i];
if(!deep[v]) f[v][] = u, dfs(v);
}
} inline int lca(int x, int y)
{
int i;
if(deep[x] < deep[y]) swap(x, y);
for(i = ; i >= ; i--)
if(deep[f[x][i]] >= deep[y])
x = f[x][i];
if(x == y) return x;
for(i = ; i >= ; i--)
if(f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
return f[x][];
} int main()
{
int i, x, y;
n = get_num();
m = get_num();
s = get_num();
for(i = ; i < n; i++)
{
x = get_num();
y = get_num();
vec[x].push_back(y);
vec[y].push_back(x);
}
dfs(s);
for(i = ; i <= m; i++)
{
scanf("%d %d", &x, &y);
printf("%d\n", lca(x, y));
}
return ;
}

3.树剖法求lca

 # include <iostream>
# include <cstdio>
# include <cstring>
# include <string>
# include <cmath>
# include <vector>
# include <map>
# include <queue>
# include <cstdlib>
# define MAXN
using namespace std; inline int get_num() {
int k = , f = ;
char c = getchar();
for(; !isdigit(c); c = getchar()) if(c == '-') f = -;
for(; isdigit(c); c = getchar()) k = k * + c - '';
return k * f;
} int n, m, s;
int f[MAXN], size[MAXN], top[MAXN], son[MAXN], deep[MAXN];
vector <int> vec[MAXN]; inline void dfs1(int u)
{
int i, v;
size[u] = ;
deep[u] = deep[f[u]] + ;
for(i = ; i < vec[u].size(); i++)
{
v = vec[u][i];
if(!deep[v])
{
f[v] = u;
dfs1(v);
size[u] += size[v];
if(size[son[u]] < size[v]) son[u] = v;
}
}
} inline void dfs2(int u, int tp)
{
int i, v;
top[u] = tp;
if(!son[u]) return;
dfs2(son[u], tp);
for(i = ; i < vec[u].size(); i++)
{
v = vec[u][i];
if(v != son[u] && v != f[u]) dfs2(v, v);
}
} inline int lca(int x, int y)
{
while(top[x] != top[y])
{
if(deep[top[x]] < deep[top[y]]) swap(x, y);
x = f[top[x]];
}
if(deep[x] > deep[y]) swap(x, y);
return x;
} int main()
{
int i, x, y;
n = get_num();
m = get_num();
s = get_num();
for(i = ; i < n; i++)
{
x = get_num();
y = get_num();
vec[x].push_back(y);
vec[y].push_back(x);
}
dfs1(s);
dfs2(s, s);
for(i = ; i <= m; i++)
{
x = get_num();
y = get_num();
printf("%d\n", lca(x, y));
}
return ;
}

lca最近公共祖先(模板)的更多相关文章

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

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

  2. LCA最近公共祖先模板(求树上任意两个节点的最短距离 || 求两个点的路进(有且只有唯一的一条))

    原理可以参考大神 LCA_Tarjan (离线) TarjanTarjan 算法求 LCA 的时间复杂度为 O(n+q) ,是一种离线算法,要用到并查集.(注:这里的复杂度其实应该不是 O(n+q)  ...

  3. LCA最近公共祖先模板代码

    vector模拟邻接表: #include<iostream> #include<cstdio> #include<cstring> #include<cma ...

  4. lca最短公共祖先模板(hdu2586)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 #include<iostream> #include<cstdio> ...

  5. LCA(最近公共祖先)之倍增算法

    概述 对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. 如图,3和5的最近公共祖先是1,5和2的最近公共祖先是4 在本篇中我们先介 ...

  6. lca 最近公共祖先

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

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

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

  8. CodeVs.1036 商务旅行 ( LCA 最近公共祖先 )

    CodeVs.1036 商务旅行 ( LCA 最近公共祖先 ) 题意分析 某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间. 假设有N个城镇,首都编号为1,商人从 ...

  9. LCA近期公共祖先

    LCA近期公共祖先 该分析转之:http://kmplayer.iteye.com/blog/604518 1,并查集+dfs 对整个树进行深度优先遍历.并在遍历的过程中不断地把一些眼下可能查询到的而 ...

  10. LCA 近期公共祖先 小结

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

随机推荐

  1. Visual Studio 2017正式版离线安装及介绍

    Visual Studio 2017 RTM正式版离线安装及介绍. 首先至官网下载:https://www.visualstudio.com/zh-hans/downloads/ VS 2017 正式 ...

  2. Java设计模式之《代理模式》及应用场景

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6525527.html 代理模式算是我接触较早的模式,代理就是中介,中间人.法律上也有代理, ...

  3. AlloyTouch.js 源码 学习笔记及原理说明

    alloyTouch这个库其实可以做很多事的, 比较抽象, 需要我们用户好好的思考作者提供的实例属性和一些回调方法(touchStart, change, touchMove, pressMove, ...

  4. 从depth buffer中构建view-space position

    观察透视投影矩阵: 对于x和y,矩阵变换只是一个缩放系数,那么逆变换就是缩放系数的倒数,所以 设Xndc Yndc为NDC空间中的XY坐标,Xview Yview Zview为view space中的 ...

  5. CentOS 7安装配置FTP服务器

    CentOS 7下FTP服务器的安装配置. 假设我们有以下要求 路径 权限 备注 /ftp/open 公司所有人员包括来宾均可以访问 只读 /ftp/private 仅允许Alice.Jack.Tom ...

  6. 476. Number Complement

    题目 Given a positive integer, output its complement number. The complement strategy is to flip the bi ...

  7. VueJs学习路线

    对于这个东西,我本人也是初学者,收集一些学习资源的链接,用于个人资源的学习,也分享给大家 lavyun的博客 里面有一些给初学者的建议,学习路线 http://www.cnblogs.com/smar ...

  8. 'utf8' codec can't decode byte 0xd1 in position 931: invalid continuation byte解决方法

    有时候,我得到这样的字符œ导致的UnicodeDecodeError错误. 我需要能够使串的UTF-8有或没有这些字符. 在工作中,经常遇到,读取一个文件,或者是从网页获取一个问题,明明看着是gb23 ...

  9. 使用Java语言开发微信公众平台(四)——图文消息的发送与响应

    在上一篇文章中,我们实现了被关注回复与关键词回复功能.在用户关注的时候自动推送功能菜单,并根据用户输入的关键词,回复特定信息.但是,我们只能回复文本消息给用户,如何才回复一条图文消息呢?本周,我们一起 ...

  10. java开发中获取路径的一些方式

    1.servlet开发获取WebContent(项目)的绝对路径: System.out.println(getServletContext().getRealPath("")); ...