算法:最近公共祖先(LCA)

有根树中,每一个点都有好几个祖先(在往根节点走的过程中遇到的都是它的祖先),一般化,把本身也称为它的祖先

对于两个点,离它们最近的一个公共祖先被称为最近公共祖先

法一:向上标记法(暴力做法)O(n)不常用

对于其中一个点,在走到根节点的过程中标记走过的点,然后另一个点开始往根节点走,走到第一个被标记过的点即为这两个点的最近公共祖先

法二:倍增法

预处理每个点向上走2^k步的节点,f[i,j]表示从i开始,向上走2^j步所能走到的节点(j大于等于0,j小于等于logn)

通过递推的方式来求

当j为0时,f(i,j)=i的父节点

当j大于0时,f(i,j)=f(f(i,j-1),j-1)

depth[i]表示深度

哨兵:如果从i开始跳2^j步会跳过根节点,那么f[i,j]=0,depth[0]=0

二进制拼凑:

比如已经有1,2,4,8,16

要想拼凑11,首先从大往小看,11小于16,不可行,11大于8,那么由于从大到小8是第一个出现,那么8可行,11减去8得3,第一个满足小于等于3的是2,所以2可行,3减2得1,第一个满足小于等于1的是1,所以1可行

利用这个思想可以把x和y跳到一个相同的位置上

x和y相差depth[x]-depth[y]步,用2的次幂拼凑出它们之间的距离

如果depth[f(x,k)]大于等于depth[y],那么表示跳2^k步之后到达的点还是在y的下面的,那么就可以作为拼凑的一部分,可以跳2^k步

当f(a,k)等于f(b,k)时,不一定到达的是最近的公共祖先,有可能到达的是最近公共祖先的上面一个点

当f(a,k)不等于f(b,k)时,说明还没有走到a,b的最近公共祖先

预处理O(nlogn)

查询 O(logn)

步骤:

1.先将两个点跳到同一层(距根节点的距离相同)

2.如果两个点此时不在同一个位置,那么让两个点同时往上跳,一直跳到它们的最近公共祖先的下一层(即它们的最近公共祖先的儿子节点)

例1:祖孙询问 

 

 

AC代码: 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=40010,M=2*N;
int h[N],e[M],ne[M],idx;
int depth[N],fa[N][16];
int q[N];
int n,m;
void add(int a,int b){e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void bfs(int root){memset(depth,0x3f,sizeof depth);depth[0]=-1,depth[root]=0;int hh=0,tt=0;q[0]=root;while(hh<=tt){int t=q[hh++];for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];if(depth[j]>depth[t]+1){depth[j]=depth[t]+1;q[++tt]=j;fa[j][0]=t;//j跳一步跳到t点,即t为j的父节点for(int k=1;k<=15;k++) fa[j][k]=fa[fa[j][k-1]][k-1];//预处理点j跳2^0,2^1,...2^15跳到哪个点}}}
}
int lca(int a,int b){if(depth[a]<depth[b]) swap(a,b);//使得a在b的下面for(int k=15;k>=0;k--){if(depth[fa[a][k]]>=depth[b]) a=fa[a][k];//如果a跳2^k步还在b的下面,那么就可以跳,哨兵的好处在于当跳2^k步后跳出根节点,那么depth为0}//此时a和b已经在同一层了,即距离根节点的距离是相同的if(a==b) return a;//如果a等于b,那么a就是a和b的最近公共祖先//否则a和b同时往上跳for(int k=15;k>=0;k--){//如果跳到的点不是同一个点,那么说明还没有跳到最近公共祖先//哨兵的第二个好处在于当跳出根节点时,跳到的点为0,那么两者就相同了if(fa[a][k]!=fa[b][k]){a=fa[a][k];b=fa[b][k];}}//此时a和b跳到了它们最近公共祖先的儿子节点return fa[a][0];
}
int main()
{cin>>n;int root=0;memset(h,-1,sizeof h);for(int i=0;i<n;i++){int a,b;cin>>a>>b;if(b==-1) root=a;else add(a,b),add(b,a);}bfs(root);cin>>m;while(m--){int a,b;cin>>a>>b;int p=lca(a,b);if(p==a) cout<<1<<endl;else if(p==b) cout<<2<<endl;else cout<<0<<endl;}return 0;
}

vector,queue版: 

#include<bits/stdc++.h>
#define endl '\n'
//#define int long long
using namespace std;
const int N=4e4+10,M=17;
int fa[N][M];
int depth[N];
int n,m;
vector<vector<int>>e(N);
queue<int>q;
void bfs(int root){memset(depth,0x3f,sizeof depth);depth[0]=-1,depth[root]=0;q.push(root);while(q.size()){int t=q.front();q.pop();for(auto v:e[t]){if(depth[v]>depth[t]+1){depth[v]=depth[t]+1;q.push(v);fa[v][0]=t;for(int k=1;k<M;k++) fa[v][k]=fa[fa[v][k-1]][k-1];}}}
}
int lca(int a,int b){if(depth[a]<depth[b]) swap(a,b);for(int k=16;k>=0;k--){if(depth[fa[a][k]]>=depth[b]) a=fa[a][k];}if(a==b) return a;for(int k=16;k>=0;k--){if(fa[a][k]!=fa[b][k]){a=fa[a][k];b=fa[b][k];}}return fa[a][0];
}
void solve() {cin>>n;int root;for(int i=0;i<n;i++){int a,b;cin>>a>>b;if(b==-1) root=a;else {e[a].push_back(b);e[b].push_back(a);}}bfs(root);cin>>m;while(m--){int x,y;cin>>x>>y;int l=lca(x,y);if(l==x) cout<<1<<endl;else if(l==y) cout<<2<<endl;else cout<<0<<endl;}
}
int main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
//    cin>>t;while(t--) {solve();}return 0;
}

法三:Tarjan----离线求LCA O(n+m)(向上标记法的优化)

在线:对于每一个询问单独处理

离线:对于所有询问,全部存起来,一起处理,再一起输出

在深度优先遍历时,将所有点分三大类:(1)已经遍历过,且回溯过的点 (2)正在搜索的分支 (3)还未搜索到的点

tarjan在存储询问的时候,正反都会存一次,当lca(u,v)等于u,v其中一个时,lca会求两次,其它情况lca求一次,正反都存一次的目的是为了防止漏求lca

例:

AC代码: 

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
typedef pair<int,int>PII;
const int N=20010,M=2*N;
int n,m;
int h[N],e[M],w[M],ne[M],idx;
int dist[N];
int p[N];
int res[N];
int vis[N];
vector<PII>query[N];//first存查询的另外一个点,second存查询编号
void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int fa){for(int i=h[u];i!=-1;i=ne[i]){int j=e[i];if(j==fa) continue;dist[j]=dist[u]+w[i];dfs(j,u);}
}
int find(int x){if(p[x]!=x) p[x]=find(p[x]);return p[x];
}
void tarjan(int u){vis[u]=1;//入u时,标记ufor(int i=h[u];i!=-1;i=ne[i]){int j=e[i];if(!vis[j]){tarjan(j);p[j]=u;//回u时,v指向u,回u表示u往下搜完其中的一条路回到u}}//离u时,枚举LCA//离u表示u的子树已经全部搜完了for(auto item:query[u]){int y=item.first,id=item.second;if(vis[y]){int anc=find(y);res[id]=dist[u]+dist[y]-dist[anc]*2;}}
}
int main()
{cin>>n>>m;memset(h,-1,sizeof h);for(int i=0;i<n-1;i++){int a,b,c;cin>>a>>b>>c;add(a,b,c),add(b,a,c);}for(int i=0;i<m;i++){int a,b;cin>>a>>b;if(a!=b){query[a].push_back({b,i});query[b].push_back({a,i});}}for(int i=1;i<=n;i++) p[i]=i;dfs(1,-1);tarjan(1);for(int i=0;i<m;i++) cout<<res[i]<<endl;return 0;
}

 

 

[BJOI2018] 求和 - 洛谷

该题是点前缀和

AC代码:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=3e5+10,mod=998244353;
int depth[N],fa[N][22];
int mi[60];//mi[j]表示depth[v]的j次幂
int s[N][60];//s[v][j]表示从根节点到v的路径节点的深度的j次幂之和
int n,m;
int root;
vector<vector<int>>e(N);
queue<int>q;
void bfs(int root){memset(depth,0x3f,sizeof depth);depth[0]=-1,depth[root]=0;q.push(root);while(q.size()){int t=q.front();q.pop();for(auto v:e[t]){if(depth[v]>depth[t]+1){depth[v]=depth[t]+1;q.push(v);fa[v][0]=t;for(int k=1;k<=20;k++) fa[v][k]=fa[fa[v][k-1]][k-1];for(int j=1;j<=50;j++) mi[j]=mi[j-1]*depth[v]%mod;for(int j=1;j<=50;j++) s[v][j]=(mi[j]+s[t][j])%mod;}}}
}
int lca(int a,int b){if(depth[a]<depth[b]) swap(a,b);for(int k=20;k>=0;k--){if(depth[fa[a][k]]>=depth[b]) a=fa[a][k];}if(a==b) return a;for(int k=20;k>=0;k--){if(fa[a][k]!=fa[b][k]){a=fa[a][k];b=fa[b][k];}}return fa[a][0];
}
void solve() {cin>>n;for(int i=0;i<n-1;i++){int a,b;cin>>a>>b;e[a].push_back(b);e[b].push_back(a);}root=1;mi[0]=1;bfs(root);cin>>m;for(int i=0;i<m;i++){int a,b,k;cin>>a>>b>>k;int l=lca(a,b);cout<<(s[a][k]+s[b][k]-s[l][k]-s[fa[l][0]][k]+2*mod)%mod<<endl;}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
//    cin>>t;while(t--) {solve();}return 0;
}

 树上差分

 

 

[USACO15DEC] Max Flow P - 洛谷

AC代码:

#include<bits/stdc++.h>
#define endl '\n'
//#define int long long
using namespace std;
const int N=5e4+10;
int depth[N],fa[N][22];
int diff[N];//差分数组
int n,k;
int root;
int ans;
vector<vector<int>>e(N);
queue<int>q;
void bfs(int root){memset(depth,0x3f,sizeof depth);depth[0]=-1,depth[root]=0;q.push(root);while(q.size()){int t=q.front();q.pop();for(auto v:e[t]){if(depth[v]>depth[t]+1){depth[v]=depth[t]+1;q.push(v);fa[v][0]=t;for(int k=1;k<=20;k++) fa[v][k]=fa[fa[v][k-1]][k-1];}}}
}
void dfs(int u,int fa){for(auto v:e[u]){if(v==fa) continue;dfs(v,u);diff[u]+=diff[v];}ans=max(ans,diff[u]);
}
int lca(int a,int b){if(depth[a]<depth[b]) swap(a,b);for(int k=20;k>=0;k--){if(depth[fa[a][k]]>=depth[b]) a=fa[a][k];}if(a==b) return a;for(int k=20;k>=0;k--){if(fa[a][k]!=fa[b][k]){a=fa[a][k];b=fa[b][k];}}return fa[a][0];
}
void solve() {cin>>n>>k;for(int i=0;i<n-1;i++){int a,b;cin>>a>>b;e[a].push_back(b);e[b].push_back(a);}root=1;bfs(root);while(k--){int a,b;cin>>a>>b;int l=lca(a,b);diff[a]++,diff[b]++;diff[l]--,diff[fa[l][0]]--;}dfs(1,0);cout<<ans<<endl;
}
int main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
//    cin>>t;while(t--) {solve();}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/148644.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Studio实现简易计算器(带横竖屏,深色浅色模式,更该按钮颜色,selector,style的使用)

目录 前言 运行结果&#xff1a; 运行截屏&#xff08;p50e&#xff09; apk文件 源码文件 项目结构 总览 MainActivity.java drawable 更改图标的方法&#xff1a; blackbutton.xml bluebuttons.xml greybutton.xml orangebuttons.xml whitebutton.xml layout 布…

Lagrange插值法实验:求拉格朗日插值多项式和对应x的近似值matlab实现(内附代码)

一、实验要求 已知函数表&#xff1a; 求出Lagrange 插值多项式&#xff0c;并计算x1.2处的y的近似值。 二、MATLAB代码 求解多项式&#xff1a; X input(请输入横坐标向量X:\nX); % 获取用户输入的横坐标向量 Y input(请输入纵坐标向量Y:\nY); % 获取用户输入的纵坐标…

简单走近ChatGPT

目录 一、ChatGPT整体背景认知 &#xff08;一&#xff09;ChatGPT引起关注的原因 &#xff08;二&#xff09;与其他公司的竞争情况 二、NLP学习范式的发展 &#xff08;一&#xff09;规则和机器学习时期 &#xff08;二&#xff09;基于神经网络的监督学习时期 &…

竞赛 机器学习股票大数据量化分析与预测系统 - python 竞赛

文章目录 0 前言1 课题背景2 实现效果UI界面设计web预测界面RSRS选股界面 3 软件架构4 工具介绍Flask框架MySQL数据库LSTM 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 机器学习股票大数据量化分析与预测系统 该项目较为新颖&am…

2023计算机保研——双非上岸酒吧舞

我大概是从22年10月份开始写博客的&#xff0c;当时因为本校专业的培养方案的原因&#xff0c;课程很多&#xff0c;有些知识纸质记录很不方便&#xff0c;于是选择了打破了自己的成见使用博客来记录学习生活。对于我个人而言&#xff0c;保研生活在前一大半过程中都比较艰难&a…

System Generator学习——时间和资源分析

文章目录 前言一、目标二、步骤三、步骤 1 &#xff1a;系统生成器的时序分析1、时序分析2、解决时间违规问题 四、步骤 2 &#xff1a;系统生成器中的资源分析总结 前言 在本节实验中&#xff0c;你将学习如何通过在 Simulink 中进行仿真来验证设计的功能&#xff0c;以确保在…

MapStruct初窥门径

一、介绍 MapStruct相比于BeanUtils性能更高&#xff0c;能够实现DO&#xff0c;DTO&#xff0c;VO之间的转换&#xff0c;达到解耦合的目的 二、使用前提 添加依赖 <dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifa…

学习记忆——宫殿篇——记忆宫殿——数字编码——扑克牌记忆

扑克牌我们可以通过以下3点进行识记&#xff1a; 1、先把扑克牌进行编码转换 2、确定要进行记忆的记忆宫殿 3、把扑克牌与记忆宫殿一一对应 首先54张扑克牌除去大小王后剩下52张&#xff0c;因为世界赛不需要记忆大小王。52张扑克牌都有对应的编码&#xff0c;每2张扑克牌对应…

JVM篇---第三篇

系列文章目录 文章目录 系列文章目录一、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?二、Java内存结构三、说说对象分配规则一、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文…

如何将图片存到数据库(以mysql为例), 使用ORM Bee更加简单

如何将图片存到数据库 1. 创建数据库: 2. 生成Javabean public class ImageExam implements Serializable {private static final long serialVersionUID 1596686274309L;private Integer id;private String name; // private Blob image;private InputStream image; //将In…

【Java】抽象类案例

需求&#xff1a;加入我们在开发一个系统时 需要对员工&#xff08;Employee&#xff09;类进行设计&#xff0c;员工包含3个属性&#xff1a;姓名、工号&#xff08;number&#xff09;以及工资&#xff08;salary&#xff09;。 经理&#xff08;Manager&#xff09;也是员工…

【物联网】STM32的中断机制不清楚?看这篇文章就足够了

在嵌入式系统中&#xff0c;中断是一种重要的机制&#xff0c;用于处理来自外部设备的异步事件。STM32系列微控制器提供了强大的中断控制器&#xff0c;可以方便地处理各种外部中断和内部中断。本文将详细介绍STM32中断的结构和使用方法。 文章目录 1. 什么叫中断2. 中断优先级…

怒刷LeetCode的第23天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;贪心算法 方法二&#xff1a;动态规划 方法三&#xff1a;回溯算法 方法四&#xff1a;并查集 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;排序和遍历 方法二&#xff1a;扫描线算法 方法…

给列起别名(关键字:as)

MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 语法格式: select 列名1 as 别名1, 列名2 as 别名2, 列名n as 别名n from 表名; 说明&#xff1a;可以省略as&#xff0c;列名和别名之间使用空格…

多目标跟踪框架boxmot介绍

引言 boxmot由mikel brostrom开发&#xff0c;用于目标检测&#xff0c;分割和姿态估计模型的SOTA&#xff08;state of art&#xff09;跟踪模块&#xff0c;现已加入python第三方库 PYPI&#xff0c;可用pip包管理器进行安装。 boxmot所支持的跟踪器采用外观特征识别方法&am…

【动手学深度学习-Pytorch版】Transformer代码总结

本文是纯纯的撸代码讲解&#xff0c;没有任何Transformer的基础内容~ 是从0榨干Transformer代码系列&#xff0c;借用的是李沐老师上课时讲解的代码。 本文是根据每个模块的实现过程来进行讲解的。如果您想获取关于Transformer具体的实现细节&#xff08;不含代码&#xff09;可…

国庆中秋特辑(七)Java软件工程师常见20道编程面试题

以下是中高级Java软件工程师常见编程面试题&#xff0c;共有20道。 如何判断一个数组是否为有序数组&#xff1f; 答案&#xff1a;可以通过一次遍历&#xff0c;比较相邻元素的大小。如果发现相邻元素的大小顺序不对&#xff0c;则数组不是有序数组。 public boolean isSort…

【Unity】两种方式实现弹跳平台/反弹玩家(玩家触发与物体自身触发事件实现蹦床的物理效果)

一、声明 只实现物理反弹的效果&#xff0c;不实现蹦床会有的视觉拉伸效果&#xff0c;请自行找相关代码 二、实现 经过我的实践&#xff0c;我发现要想实现一个平台反弹的效果&#xff0c;要么就选择给player添加一个物理材质&#xff08;平台加了没用&#xff09;&#xff0…

arduino嵌入式1,LED闪烁案例

CVE系列在等等吧&#xff0c;环境我有点懒得搭建了 文章目录 前言一、anduino是什么玩意儿&#xff1f;二、使用步骤1.找蓝图/画蓝图2.写入数据成果 总结 前言 最近在学习嵌入式开发&#xff0c;我的单片机到了&#xff0c;然后我就沉迷于嵌入式开发的环境中 提示&#xff1a;…

Linux嵌入式学习之Ubuntu入门(六)shell脚本详解

系列文章内容 Linux嵌入式学习之Ubuntu入门&#xff08;一&#xff09;基本命令、软件安装、文件结构、编辑器介绍 Linux嵌入式学习之Ubuntu入门&#xff08;二&#xff09;磁盘文件介绍及分区、格式化等 Linux嵌入式学习之Ubuntu入门&#xff08;三&#xff09;用户、用户组…