倍增 ST表
倍增
倍增是与二分相反的算法,其核心思想是每次扩大一倍,以 2 n 2^n 2n的速度极大扩展空间
-
原理:任意整数均可被分解为若干个以2为底的幂项和。最经典的倍增是 2 i 2^i 2i(实际应为 e i e^i ei)
-
流程:定义倍增表 g [ i ] [ j ] g[i][j] g[i][j],其中 i i i代表起始元素索引, j j j代表倍增组数(该组对应区间长度(包含原元素本身)为 2 j 2^j 2j,该元素在该组中倍增后的位置为 i + 2 j − 1 i+2^j-1 i+2j−1)。 n n n个元素最多可倍增 log 2 n \log_2n log2n组(经验值在20左右)。
-
倍增递推式: g [ i ] [ j ] = g [ g [ i ] [ j − 1 ] ] [ j − 1 ] g[i][j]=g[g[i][j-1]][j-1] g[i][j]=g[g[i][j−1]][j−1]
-
含义:第 i i i个元素倍增 j j j组,等于其倍增 j − 1 j-1 j−1组后再倍增 j − 1 j-1 j−1组,即 i + 2 j = ( i + 2 j − 1 ) + 2 j − 1 i+2^j=(i+2^{j-1})+2^{j-1} i+2j=(i+2j−1)+2j−1。特别地,当 j = 0 j=0 j=0时,表示其倍增 0 0 0组,倍增区间长度为 2 0 2^0 20,即为 i i i本身
void init(){
for(int i=0;i<n;i++) g[i][0]=i;
for(int j=1;j<=log2(n);j++)//倍增轮数,或写成(1<<j)<=n
for(int i=0;i<n;i++)//逐步处理每组组内倍增
g[i][j]=g[g[i][j-1]][j-1];
}
- 优化:打表计算所有用到的 log 2 \log_2 log2
extern vector<int>l2(n+1);
void calc(){
l2[0]=-1;
for(int i=1;i<=n;i++) l2[i]=l2[i>>1]+1;
}
倍增法具有极其广泛的应用,可用于求解ST表、LCA等问题。
ST表
可重复贡献性问题:对于运算 opt \texttt{opt} opt,若满足 x opt x = x x\ \texttt{opt}\ x\ =\ x x opt x = x,即重复该运算对答案无影响,则 opt \texttt{opt} opt称为可重复贡献性运算,其对应的区间询问就是一个可重复贡献问题。常见的 opt = max/min , gcd \texttt{opt}={\texttt{max/min},\texttt{gcd}} opt=max/min,gcd等,但 opt ≠ ∑ , ∏ \texttt{opt}\ne \sum,\prod opt=∑,∏等。
可重复贡献性常用于两个交集 ≠ ∅ \ne \varnothing =∅的区间合并。若大区间能被 n n n个小区间所覆盖,则 opt \texttt{opt} opt(大区间)= opt \texttt{opt} opt(小区间1, ⋯ \cdots ⋯ ,小区间 n n n)。
ST表用于高效解决具有可重复贡献性问题的数据结构,其基于倍增的思想。
ST表求解静态区间RMQ问题
问题:给定长度为 n n n的静态序列(无任何修改操作),有 m m m次询问,每次给定 L , R L,R L,R,查询序列区间 [ L , R ] [L,R] [L,R]内的最值,必须以 O ( 1 ) O(1) O(1)复杂度回答每次询问。
初始化ST表 O ( n log 2 n ) O(n\log_2n) O(nlog2n)
流程:定义 d p [ s ] [ k ] dp[s][k] dp[s][k]表示区间 [ s , s + 2 k ) [s,s+2^k) [s,s+2k)内的最值,根据鸽巢原理, [ s , s + 2 k ) [s,s+2^k) [s,s+2k)的区间最值必定为 最值 ( 最值( 最值(区间 [ s , s + 2 k − 1 ) [s,s+2^{k-1}) [s,s+2k−1)内的最值和区间 [ s + 2 k − 1 , s + 2 k ) [s+2^{k-1},s+2^k) [s+2k−1,s+2k)内的最值 ) ) )。
状态转移方程式: d p [ s ] [ k ] = R M Q ( d p [ s ] [ k − 1 ] , d p [ s + ( 1 ≪ ( k − 1 ) ) ] [ k − 1 ] ) dp[s][k]=RMQ(dp[s][k-1],dp[s+(1\ll(k-1))][k-1]) dp[s][k]=RMQ(dp[s][k−1],dp[s+(1≪(k−1))][k−1])
extern int dp[MAX][log2(n)+10];
int opt(int,int);
void init(){
for(int i=0;i<n;i++) dp[i][0]=a[i];
for(int j=1;j<=log2(n);j++)
for(int i=0;i+(1<<j)-1<n;i++)
dp[i][j]=opt(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
查询任意区间最值 O ( 1 ) O(1) O(1)
原理:设序列区间 [ L , R ] [L,R] [L,R],给定查询区间 [ l , r ] [l,r] [l,r],区间长度为 l e n = r − l + 1 len=r-l+1 len=r−l+1。根据鸽巢原理,将查询区间分为 [ l , r ′ ] [l,r'] [l,r′]和 [ l ′ , r ] [l',r] [l′,r]两个等长小区间,其长度均为 x x x( x ≤ l e n x\le len x≤len且 2 x ≥ l e n 2x\ge len 2x≥len),确保两个小区间都能完全覆盖,且对于 d p [ ] [ k ] dp[][k] dp[][k],需确保 2 k = x 2^k=x 2k=x, k = log 2 ( l e n ) k=\log_2(len) k=log2(len),由此可知 r ’ = l + 2 k − 1 r’=l+2^k-1 r’=l+2k−1, l ′ = r − 2 k + 1 l'=r-2^k+1 l′=r−2k+1。
状态转移方程式: R M Q ( d p [ l ] [ k ] , d p [ r − ( 1 ≪ k ) + 1 ] [ k ] ) RMQ(dp[l][k],dp[r-(1\ll k)+1][k]) RMQ(dp[l][k],dp[r−(1≪k)+1][k])
extern int dp[MAX][log2(n)+10],l2[n];
extern int l,r;
int opt(int,int);
int query(){
int k=l2[r-l+1];
return opt(dp[l][k],dp[r-(1<<k)+1][k]);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具