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\). [算法]树上倍增+二维数点(树状数组 ...
随机推荐
- Spring框架系列(九)--MyBatis面试题(转载)
1.什么是Mybatis? 1.Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动.创建 连接.创建statement ...
- jenkins自动部署测试环境
构建脚本如下: echo "当前目录":$(pwd)echo "当前时间":$(date +%Y-%m-%d_%H:%M)find ./ -type f -na ...
- 【转载】dubbo约束的引入(解决eclipse中报错问题)
在采用分布式系统架构时,我们会经常使用到阿里巴巴的dubbo的分布式框架. 在相关xml配置了dubbo的约束依赖后,即使能上网eclipse.myeclipse等IDE也是无法识别dubbo的相关约 ...
- C++STL快速入门学习
C++ STL中最基本以及最常用的类或容器无非就是以下几个: string vector set list map 下面就依次介绍一下它们,并给出一些最常见的使用方法,做到最快入门. string 首 ...
- Go:闭包
闭包就是一个函数和与其相关的引用环境组合的一个整体(实体). package main import "fmt" func add() func(int) int { i := 0 ...
- vs2012+ winform+.net4.0发布如何在xp上运行
今天在英文版vs2013打包发布4.0(非4.0 client)的winform时,遇到了在xp上无法运行的情况,.net framework 4.0在xp上已安装.在打包前,winform工程,即菜 ...
- [bzoj1500][NOI2005 维修数列] (splay区间操作)
Description Input 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目. 第2行包含N个数字,描述初始时的数列. 以下M行,每 ...
- SRAM的简单概念
CY7C138 版权声明:本文为博主原创文章,未经博主允许不得转载.
- 返回通知&异常通知&环绕通知
[返回通知] LoggingAspect.java: @Aspect @Component public class LoggingAspect { /* * 在方法正常执行后执行的通知叫返回通知 * ...
- java方法的虚分派和方法表
java:方法的虚分派(virtual dispatch)和方法表(method table) Java方法调用的虚分派 虚分配(Virtual Dispatch) 首先从字节码中对方法的调用说起.J ...