LCA__st算法&&树上倍增
st表
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int go[];
int nxt[];
int adj[];
int pos[];
int st[][];
int dep[];
int aa[];
int lg[];
int ecnt,cnt,m,n,s,x,y,a,b;
void add(int u,int v) { //建链前
go[++ecnt]=v;
nxt[ecnt]=adj[u];
adj[u]=ecnt;
}
void dfs(int u,int pre) {
dep[u]=dep[pre]+; //首先预处理出深度
aa[++cnt]=u; //DFS序
pos[u]=cnt; //第几个出现的
for(int e=adj[u]; e; e=nxt[e]) {
if(go[e]!=pre) {
dfs(go[e],u);
aa[++cnt]=u;
}
}
}
int MIN(int p,int q) {
if(dep[p]<dep[q]) return p;
else return q;
}
void build() {
for(int i=; i<=cnt; i++) {
st[i][]=aa[i];
}
for(int i = , j = ; i <= cnt; i++){
if( << (j + ) == i) j++;
lg[i] = j;
}
for(int j=; j<=lg[cnt]; j++) {
for(int i=; i + ( << j) - <=cnt; i++) {
st[i][j]=MIN(st[i][j-],st[i+( << (j - ))][j-]);
}
}
}
/*
读入两个节点,查询它们第一次出现的位置
在这两个位置之间的区间查询最小深度的节点,该节点即为最近公共祖先
*/
int lca(int l,int r) {
int xx=pos[l];
int yy=pos[r];
if(xx>yy) swap(xx,yy);
int k=lg[yy-xx+];
return MIN(st[xx][k],st[yy-( << k)+][k]);
}
template <class T> //快速读入
void read(T &x){
char c;
bool op = ;
while(c = getchar(), c < '' || c > '')
if(c == '-') op = ;
x = c - '';
while(c = getchar(), c >= '' && c <= '')
x = x * + c - '';
if(op) x = -x;
}
template <class T> //快速输出
void write(T x){
if(x < ) putchar('-'), x = -x;
if(x >= ) write(x / );
putchar('' + x % );
}
int main() {
//scanf("%d%d%d",&n,&m,&s);
read(n), read(m), read(s);
for(int i=; i<=n-; i++) {
//scanf("%d%d",&x,&y);
read(x), read(y);
add(x,y);
add(y,x);
}
dfs(s,);
build();
for(int i=; i<=m; i++) {
//scanf("%d%d",&a,&b);
//printf("%d\n",lca(a,b));
read(a), read(b);
write(lca(a, b)), putchar('\n');
}
return ;
}
树上倍增和st表的思路一样,只是实现方法不同
/*
倍增求LCA:
father【i】【j】表示节点i往上跳2^j次后的节点
可以转移为
father【i】【j】=father【father【i】【j-1】】【j-1】
(此处注意循环时先循环j,再循环i)
然后dfs求出各个点的深度depth
整体思路:
先比较两个点的深度,如果深度不同,先让深的点往上跳,浅的先不动,
等两个点深度一样时,if 相同 直接返回,if 不同 进行下一步;如果不同
,两个点一起跳,j从大到小枚举(其实并不大),如果两个点都跳这么多后
,得到的点相等,两个点都不动(因为有可能正好是LCA也有可能在LCA上方)
,知道得到的点不同,就可以跳上来,然后不断跳,两个点都在LCA下面那层,
所以再跳1步即可,当father【i】【j】中j=0时即可,就是LCA,返回值结束
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
vector <int> g[];
int father[][]={};
int depth[]={};
int n,m;
bool visit[]={false};
int root;
void dfs(int u)//深搜出各点的深度,存在depth中
{
int i;
visit[u]=true;
for (i=;i<g[u].size();i++)
{
int v=g[u][i];
if ( !visit[v] )
{
depth[v]=depth[u]+;
dfs(v);
}
}
}
void bz()//fa数组的预处理
{
int i,j;
for (j=;j<=;j++)
for (i=;i<=n;i++)
father[i][j]=father[father[i][j-]][j-];
}//倍增,处理father数组,详情参照上述讲解
int LCA(int u,int v)
{
if ( depth[u]<depth[v] )
{
int temp=u;
u=v;
v=temp;
}//保证深度大的点为u,方便操作
int dc=depth[u]-depth[v];
int i;
for (i=;i<;i++)//值得注意的是,这里需要从零枚举
{
if ( (<<i) & dc)//从大到小二分
u=father[u][i];//意思是跳2^j步不一样,就跳,否则不跳
}
//上述操作先处理较深的结点,使两点深度一致
if (u==v) return u;//如果深度一样时,两个点相同,直接返回
for (i=;i>=;i--)//从大到小二分。
{
if (father[u][i]!=father[v][i])//跳2^j步不一样,就跳,否则不跳
{
u=father[u][i];
v=father[v][i];
}
}
u=father[u][];//上述过程做完,两点都在LCA下一层,所以走一步即可
return u;
}
int main()
{
int i,j;
scanf("%d",&n);
for (i=;i<=n;i++)
g[i].clear();
for (i=;i<n;i++)
{
int a,b;
int root;
scanf("%d%d",&a,&b);
g[a].push_back(b);
father[b][]=a;//a^0为1,所以fa[a][0]代表a向上走一格
if (father[a][]==)//如果节点的根为0,证明该节点为根节点
root = a;
}
depth[root]=;
dfs(root);
bz();
int x,y;
scanf("%d%d",&x,&y);
printf("%d",LCA(x,y));
return ;
}
LCA__st算法&&树上倍增的更多相关文章
- [算法]树上倍增求LCA
LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 然后把深度更深的那一个点(4 ...
- bzoj 3569 DZY Loves Chinese II 随机算法 树上倍增
题意:给你一个n个点m条边的图,有若干组询问,每次询问会选择图中的一些边删除,删除之后问此图是否联通?询问之间相互独立.此题强制在线. 思路:首先对于这张图随便求一颗生成树,对于每一条非树边,随机一个 ...
- 【bzoj4940】[Ynoi2016]这是我自己的发明 DFS序+树上倍增+莫队算法
题目描述 给一个树,n 个点,有点权,初始根是 1. m 个操作,每次操作: 1. 将树根换为 x. 2. 给出两个点 x,y,从 x 的子树中选每一个点,y 的子树中选每一个点,如果两个点点权相等, ...
- 最近公共祖先算法LCA笔记(树上倍增法)
Update: 2019.7.15更新 万分感谢[宁信]大佬,认认真真地审核了本文章,指出了超过五处错误捂脸,太尴尬了. 万分感谢[宁信]大佬,认认真真地审核了本文章,指出了超过五处错误捂脸,太尴尬了 ...
- 关于树论【LCA树上倍增算法】
补了一发LCA,表示这东西表面上好像简单,但是细节真挺多. 我学的是树上倍增,倍增思想很有趣~~(爸爸的爸爸叫奶奶.偶不,爷爷)有一个跟st表非常类似的东西,f[i][j]表示j的第2^i的祖先,就是 ...
- 树上倍增求LCA及例题
先瞎扯几句 树上倍增的经典应用是求两个节点的LCA 当然它的作用不仅限于求LCA,还可以维护节点的很多信息 求LCA的方法除了倍增之外,还有树链剖分.离线tarjan ,这两种日后再讲(众人:其实是你 ...
- 两种lca的求法:树上倍增,tarjan
第一种:树上倍增 f[x,k]表示x的2^k辈祖先,即x向根结点走2^k步达到的结点. 初始条件:f[x][0]=fa[x] 递推式:f[x][k]=f[ f[x][k-1] ][k-1] 一次bfs ...
- LCA树上倍增
LCA就是最近公共祖先,比如 节点10和11的LCA就是8,9和3的LCA就是3. 我们这里讲一下用树上倍增来求LCA. 大家都可以写出暴力解法,两个节点依次一步一步往上爬,直到爬到了相同的一个节点. ...
- 【CodeForces】983 E. NN country 树上倍增+二维数点
[题目]E. NN country [题意]给定n个点的树和m条链,q次询问一条链(a,b)最少被多少条给定的链覆盖.\(n,m,q \leq 2*10^5\). [算法]树上倍增+二维数点(树状数组 ...
随机推荐
- java虚拟机(二)--类加载机制和双亲委派模型
一.类的生命周期 加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(Initialization).使用(Using).卸 ...
- Linux常用命令——链接命令
链接命令:ln ln -s [原文件] [目标文件] 命令英文原意:link 功能描述:生成链接文件 选项:-s 创建软链接,也叫符号链接 硬链接特征: 1.拥有相同的i节点和存储block块,可以看 ...
- 洛谷——P3275 [SCOI2011]糖果
P3275 [SCOI2011]糖果 差分约束模板题,基本思路就是$d[v]+w[v,u]<=d[u]$,$Spfa$更新方法, 有点套路的是要建立原点,即图中不存在的点来向每个点加边,但同样这 ...
- 39页第7题 计算2的i次方之和
/*计算2的i次方之和*/ #include<stdio.h> #include<math.h>/*调用math.h文件中的函数*/ int main(void) { int ...
- db2 group by的疑惑。
按借据号分组,显示每组的条数:
- Python关于导入模块的一些感想:
写项目的时候,碰到这种情况 程序业务为core,里面有两个目录,core1 和core2 core1中有三个模块,business main main1 程序入口为bin目录下的project ...
- 虚拟机上CentOS-6.9-x86_64系统安装教程
最近想学学Linux系统如何使用,于是想用VM安装虚拟机学习一下. linux系统比较多,我这里用的是CentOS-6.9-x86_64 一.下载系统 下载地址:https://www.centos. ...
- python爬取豆瓣小组700+话题加回复啦啦啦python open file with a variable name
需求:爬取豆瓣小组所有话题(话题title,内容,作者,发布时间),及回复(最佳回复,普通回复,回复_回复,翻页回复,0回复) 解决:1. 先爬取小组下,所有的主题链接,通过定位nextpage翻页获 ...
- qemu-guest-agent简介
经常使用vmWare的同学都知道有vmware-tools这个工具,这个安装在vm内部的工具,可以实现宿主机与虚拟机的通讯,大大增强了虚拟机的性能与功能, 如vmware现在的Unity mode下可 ...
- codevs1001 舒适的线路
题目描述 Description Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光.Z小镇附近共有N(1<N≤500)个景点(编号为1,2,3,…,N),这些景点被M(0<M≤ ...