倍增:ST与LCA
倍增算法之:ST表 与 RMQ
讲解:
倍增思想,就是每次在原基础上往前“跳”
RMQ参考 https://blog.csdn.net/qq_31759205/article/details/75008659
RMQ 问题,即区间最值查询问题,通常的做法(我会的做法)有 暴力、线段树……
这里介绍一种比较高效的算法:ST表。
ST 表可以做到
预处理:
- 设
是要求区间最值的序列, 表示从第 个数起连续 个数中的最大值。(DP状态)
for example:
a={3,2,4,5,6,8,1,2,9,7};
表示从第一个数开始,长度为 的最大值,其实就是这个数 。
- 很容易看出
。(DP的初始值) - 我们把
平均分成 段(因为 到 之间一定有偶数个数),从 到 为一段, 到 为一段,每段长度都是 。我们可以得出状态转移方程: 。
查询:
- 假如我们要查询的区间为
到 ,那么我们需要找到覆盖整个闭区间(左边界取 ,右边界取 )的最小幂(可以重叠)
比如我们要查询 1,2,3,4,5 中的最大值,我们可以先查询 1,2,3,4 中的最大值,再查询 2,3,4,5 中的最大值,最后取 max
- 因为这个区间长度为
,所以我们就可以取 则有 .
洛谷模板题传送门:ST 表 && RMQ 问题
代码:
#include<bits/stdc++.h> using namespace std; int n,m; int f[100100][100]; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-')f=-1;ch=getchar(); } while(ch>='0'&&ch<='9'){ x=x*10+ch-48;ch=getchar(); } return x*f; } int main() { n=read();m=read(); for(int i=1;i<=n;i++) { f[i][0]=read(); } for(int j=1;(1<<j)<=n;j++) { for(int i=1;i+(1<<j)-1<=n;i++) { f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); } } while(m--) { int k=0; int l,r; l=read();r=read(); while((1<<(k+1))<=r-l+1)k++; int ans=max(f[l][k],f[r-(1<<k)+1][k]); printf("%d\n",ans); } return 0; }
倍增求 LCA
讲解:
LCA,即为最近公共祖先。
我们可以记录一个深度
定义
前面也用到了一个倍增的常用方法:
则状态转移方程为
代码:
#include<bits/stdc++.h> using namespace std; const int maxn=500100; const int maxm=500100; int n,m,root; int depth[maxn];//表示第i个节点的深度 int en=0; int fir[maxn]; int f[maxn][25]; struct edge{ int v,next; }ed[maxm*2]; void add_edge(int u,int v) { en++; ed[en].v=v; ed[en].next=fir[u]; fir[u]=en; } void dfs(int now,int fa)//当前节点为now,fa是他的父亲节点 { depth[now]=depth[fa]+1; f[now][0]=fa;//从这个点向上跳2^0=1步,到达父亲 for(int i=1;i<=20;i++) { f[now][i]=f[f[now][i-1]][i-1];//转移方程 } for(int i=fir[now];i;i=ed[i].next)//遍历所有出边 if(ed[i].v!=fa) dfs(ed[i].v,now); } int get_lca(int a,int b) { if(depth[a]<depth[b])swap(a,b);//把 a 变成深度更深的点 for(int i=20;i>=0;i--)//a 向上跳,直到与 b 深度一致. { if(depth[f[a][i]]>=depth[b])//没有跳过 a=f[a][i]; } //求lca if(a==b) return a; for(int i=20;i>=0;i--) { if(f[a][i]!=f[b][i])//说明两个点同时向上跳了 2^i 步后仍然没有重合,没有跳到lca a=f[a][i],b=f[b][i]; } return f[a][0];//返回他们的父节点 } int main() { cin>>n>>m>>root; depth[root]=0; for(int i=1;i<n;i++) { int p1,p2; cin>>p1>>p2; add_edge(p1,p2); add_edge(p2,p1); } dfs(root,0); for(int i=1;i<=m;i++) { int a,b; cin>>a>>b; int p=get_lca(a,b); cout<<p<<"\n"; } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效