信奥赛C++提高组csp-s之倍增算法
倍增算法核心思想讲解
1. 什么是倍增?
“倍增”,顾名思义,就是成倍地增加。它的核心思想是:不是一步一步地处理问题,而是将每一步的“步长”以2的幂次(1, 2, 4, 8…)进行跳跃式处理。
2. 为什么倍增有效?
- 高效性:通过二进制划分,可以将一个线性过程的时间复杂度从 O(N) 优化到 O(logN)。
- 可行性:任何整数都可以用二进制数表示。这意味着,从起点到终点的任意一个长度,我们都可以通过
2^k1 + 2^k2 + ...的跳跃方式到达。 - 预处理:倍增算法通常需要先进行预处理,计算出一些“跳跃点”的信息,以便在查询时能够快速组合。
3. 倍增的通用步骤
- 定义状态:
f[i][j]通常表示从i这个点出发,走2^j步(或者进行2^j次操作)后所到达的状态。这个“状态”可以是到达的位置、区间的最值、区间和等。 - 预处理(DP填充):这是算法的关键。我们利用递推关系来填充这个
f数组(也称为ST表、DP表)。- 边界条件:
f[i][0]表示从i走 1 (2^0) 步后的状态。这是初始的、已知的数据。 - 递推公式:
f[i][j] = f[ f[i][j-1] ][j-1]。这个公式是倍增的灵魂!它的意思是:从i走2^j步到达的点,等价于从i先走2^(j-1)步,到达一个中间点f[i][j-1],然后再从这个中间点走2^(j-1)步。
- 边界条件:
- 查询/执行:对于一次查询,比如“从点
u走k步会到哪?”,我们将k分解成二进制。例如k = 13 = 8 + 4 + 1(二进制1101)。那么我们就依次走 8 步、4 步、1 步。每一步的跳跃都可以直接从我们预处理好的f数组中 O(1) 获取。
案例:ST 表 & RMQ 问题
题目描述
给定一个长度为N NN的数列,和 $ M $ 次询问,求出每一次询问的区间内数字的最大值。
输入格式
第一行包含两个整数N , M N,MN,M,分别表示数列的长度和询问的个数。
第二行包含N NN个整数(记为a i a_iai),依次表示数列的第i ii项。
接下来M MM行,每行包含两个整数l i , r i l_i,r_ili,ri,表示查询的区间为[ l i , r i ] [l_i,r_i][li,ri]。
输出格式
输出包含M MM行,每行一个整数,依次表示每一次询问的结果。
输入输出样例 #1
输入 #1
8 8 9 3 1 7 5 6 0 8 1 6 1 5 2 7 2 6 1 8 4 8 3 7 1 8输出 #1
9 9 7 7 9 8 7 9说明/提示
对于30 % 30\%30%的数据,满足1 ≤ N , M ≤ 10 1\le N,M\le 101≤N,M≤10。
对于70 % 70\%70%的数据,满足1 ≤ N , M ≤ 10 5 1\le N,M\le {10}^51≤N,M≤105。
对于100 % 100\%100%的数据,满足1 ≤ N ≤ 10 5 1\le N\le {10}^51≤N≤105,1 ≤ M ≤ 2 × 10 6 1\le M\le 2\times{10}^61≤M≤2×106,a i ∈ [ 0 , 10 9 ] a_i\in[0,{10}^9]ai∈[0,109],1 ≤ l i ≤ r i ≤ N 1\le l_i\le r_i\le N1≤li≤ri≤N。
AC代码
#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+10;// 定义最大数组长度intn,m;// n: 数列长度, m: 询问个数inta[N];// 存储原始数列intst[N][17];// ST表,st[i][j]表示从位置i开始,长度为2^j的区间最大值intmain(){cin>>n>>m;// 读入数列并初始化ST表第一层for(inti=1;i<=n;i++){scanf("%d",&a[i]);st[i][0]=a[i];// 长度为1的区间最大值就是元素本身}// 构建ST表for(intj=1;j<=17;j++){// j表示区间长度为2^jfor(inti=1;i+(1<<j)-1<=n;i++){// i为区间起点// 将区间[i, i+2^j-1]分成两个长度为2^(j-1)的子区间// 取两个子区间最大值的较大者作为当前区间的最大值st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);}}// 处理每个查询while(m--){intl,r;scanf("%d%d",&l,&r);// 计算区间长度的对数(向下取整)ints=log2(r-l+1);// 查询区间最大值:将区间分成可能有重叠的两部分// [l, l+2^s-1] 和 [r-2^s+1, r]intans=max(st[l][s],st[r-(1<<s)+1][s]);printf("%d\n",ans);}return0;}功能分析
使用ST表(Sparse Table)解决区间最大值查询(RMQ)。
核心思想:
- 预处理:构建一个二维数组
st,其中st[i][j]存储从位置i开始,长度为2^j的区间内的最大值 - 查询:对于任意区间
[l, r],可以将其分解为两个可能有重叠的区间,取这两个区间最大值的较大者
算法步骤:
初始化:
st[i][0] = a[i](长度为1的区间最大值就是元素本身)
构建ST表:
- 使用动态规划思想,
st[i][j] = max(st[i][j-1], st[i+2^(j-1)][j-1]) - 即将大区间分成两个小区间,取两者的最大值
- 使用动态规划思想,
查询处理:
- 对于区间
[l, r],计算s = log2(r-l+1)(区间长度的对数) - 查询结果 =
max(st[l][s], st[r-2^s+1][s])
- 对于区间
复杂度分析:
- 预处理:O(N log N)
- 单次查询:O(1)
- 总复杂度:O(N log N + M)
优势:
- 查询速度极快(O(1)),适合处理大量查询
- 代码简洁,实现相对容易
适用场景:
- 静态数据(数据不修改)
- 查询次数远大于数据修改次数
- 需要快速响应大量区间查询
更多系列知识,请查看专栏:《信奥赛C++提高组csp-s知识详解及案例实践》:
https://blog.csdn.net/weixin_66461496/category_13113932.html
各种学习资料,助力大家一站式学习和提升!!!
#include<bits/stdc++.h>usingnamespacestd;intmain(){cout<<"########## 一站式掌握信奥赛知识! ##########";cout<<"############# 冲刺信奥赛拿奖! #############";cout<<"###### 课程购买后永久学习,不受限制! ######";return0;}1、csp信奥赛高频考点知识详解及案例实践:
CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转
CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转
信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html
2、csp信奥赛冲刺一等奖有效刷题题解:
CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转
CSP信奥赛C++一等奖通关刷题题单及题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转
3、GESP C++考级真题题解:
GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转
GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转
GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html
4、CSP信奥赛C++竞赛拿奖视频课:
https://edu.csdn.net/course/detail/40437 点击跳转
· 文末祝福 ·
#include<bits/stdc++.h>usingnamespacestd;intmain(){cout<<"跟着王老师一起学习信奥赛C++";cout<<" 成就更好的自己! ";cout<<" csp信奥赛一等奖属于你! ";return0;}