倍增 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+2j1)。 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][j1]][j1]

  • 含义:第 i i i个元素倍增 j j j组,等于其倍增 j − 1 j-1 j1组后再倍增 j − 1 j-1 j1组,即 i + 2 j = ( i + 2 j − 1 ) + 2 j − 1 i+2^j=(i+2^{j-1})+2^{j-1} i+2j=(i+2j1)+2j1。特别地,当 j = 0 j=0 j=0时,表示其倍增 0 0 0组,倍增区间长度为 2 0 2^0 20,即为 i i i本身

ST表倍增分块

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表鸽巢原理

初始化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+2k1)内的最值和区间 [ s + 2 k − 1 , s + 2 k ) [s+2^{k-1},s+2^k) [s+2k1,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][k1],dp[s+(1(k1))][k1])

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=rl+1。根据鸽巢原理,将查询区间分为 [ l , r ′ ] [l,r'] [l,r] [ l ′ , r ] [l',r] [l,r]两个等长小区间,其长度均为 x x x( x ≤ l e n x\le len xlen 2 x ≥ l e n 2x\ge len 2xlen),确保两个小区间都能完全覆盖,且对于 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+2k1 l ′ = r − 2 k + 1 l'=r-2^k+1 l=r2k+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(1k)+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]);
}
posted @   椰萝Yerosius  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示