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基础——从数组到集合之间关键字的区别!!!!
1.&& 和 &区别和联系: 相同点 : 结果是一样的. 不同点 :如果使用双&号判断,如果说条件一为false,不会判断条件二,但是单&号会继续判 ...
- UVA-1572 Self-Assembly(拓扑排序判断有向环)
题目: 给出几种正方形,每种正方形有无穷多个.在连接的时候正方形可以旋转.翻转. 正方形的每条边上都有一个大写英文字母加‘+’或‘-’.00,当字母相同符号不同时,这两条边可以相连接,00不能和任何边 ...
- 39页第3题 求x的n次幂
/*计算x的n次幂*/ #include<stdio.h> main(void) { int i,n; double x,y; printf("Enter x:");/ ...
- knockout.js--基本用法
1,HTML元素的面向对象的赋值,数据绑定 text绑定:为p,span,div,td等加text属性值(即元素内部显示的文本), value绑定:为input添加value属性值, attr绑定:为 ...
- stm32实现iap远程固件更新
前提 想来做iap升级了,应该不是什么新手. 下面的程序需要用到一些简单的功能 串口收发数据开关总中断虽然本文标题是实现远程固件更新,但是具体远程方案本文不做详细说明,重点在于介绍mcu接收到新的固件 ...
- 7-26 Windows消息队列
7-26 Windows消息队列(25 分) 消息队列是Windows系统的基础.对于每个进程,系统维护一个消息队列.如果在进程中有特定事件发生,如点击鼠标.文字改变等,系统将把这个消息加到队列当中. ...
- FJoi2017 1月20日模拟赛 恐狼后卫(口糊动规)
Problem 1 恐狼后卫(wolf.cpp/c/pas) [题目描述] 著名卡牌游戏<石炉传说>中有一张随从牌:恐狼后卫.恐狼后卫的能力是使得相邻随从的攻击力提高. 现在有n张恐狼后卫 ...
- 【C++】实现记录软件计时时间
利用getTickCount()和getTickFrequency()函数实现计时 double time0 = static_cast<double>(getTickCount()); ...
- android获取年月日时分秒
Calendar calendar=Calendar.getInstance(); //获取当前时间,作为图标的名字 String year=calendar.get(Calendar.YEAR)+& ...
- - > 动规讲解基础讲解四——矩阵取数
给定一个m行n列的矩阵,矩阵每个元素是一个正整数,你现在在左上角(第一行第一列),你需要走到右下角(第m行,第n列),每次只能朝右或者下走到相邻的位置,不能走出矩阵.走过的数的总和作为你的得分,求最大 ...