倍增与ST算法

倍增

很好理解,就是成倍增加,相当于每次乘2,所以倍增通常可以与二进制位运算联系起来。倍增法的特点是需要提前计算出第 1 、 2 、 4 、 … 、 2 k 1、2、4、…、2^k 1242k 个跳板,这要求数据是静态不变的,不是动态变化的。如果数据发生了变化,所有跳板要重新计算,跳板就失去了意义。
   倍增法的经典应用有:矩阵快速幂、后缀数组、ST算法,LCA(最近公共祖先)。本节介绍相对简单的ST算法,另外也给出了一个应用倍增的经典题。

ST算法

ST算法主要是解决静态RMQ问题也就是不改变值的情况下访问区间最大,最小值。主要思想是大区间找最值,小区间合并成大区间,结果就是分出来的小区间最值再求结果。有点绕口,举个例子:

要求1-20,那么我们可以分解成 min(或max)(dp[1][16],dp[17][20]);
能分解成min(或max)(dp[1][16],dp[17][20]); 这样当然也就是可以分解成这样:
min(或max)(dp[1][16],dp[20-16+1][20]);
就是有重复的,因为我们要求的最值所以重复不会影响结果。
dp[i][j]表示i到j存的最小/大值。
为什么是16那?不可以是10,14,15吗?
当然可以只不过用16会更优化 因为16是2的倍数,那么我们就可以引入倍增了。
ST思想:
 (1)把整个数列分为很多小区间,并提前计算出每个小区间的最值;
 (2)对任意一个区间最值查询,找到覆盖它的两个小区间,用两个小区间的最值算出答案。

用下罗老师的图这就很nice当然罗老师写的更好。
首先我们先构造ST数组,也就是区间存放的最值。区间长度为1的最值,前面我们说倍增起到跳板的作用所以我们可以从 2 0 2^0 20跳到 2 1 2^1 21(也就是21平均分成两段,用到动态规划的思想)这就是区间长度为 2 1 2^1 21的最值,这样我们就可以跳到 2 2 2^2 22

直到不能在跳为止(1<<k)>n不能跳了,比原数组长度都长。

在这里插入图片描述
这样我们就可以找出递归表达式:
dp[l][k]=max(dp[l][ k - 1],dp[ r - 1<<( k - 1) + 1][ k - 1 ]);
长度为 1 < < ( k − 1 ) 1<<(k-1) 1<<(k1)终点为 r r r的起点是 r − 1 < < ( k − 1 ) + 1 r - 1<<( k - 1) + 1 r1<<(k1)+1
l l l是左端点, k k k是二进制位数也即是长度(2的k次方)。 r r r是右端点。

P2880 USACO07JANBalanced Lineup G

改天加解释

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+7;

ll n,m;
ll d[50010][100],p[50001][100];
ll a[maxn],b[100];

int main() {
    
    
    scanf("%lld%lld",&n,&m);
    for(int i=1; i<=n; i++)scanf("%lld",&a[i]),d[i][0]=p[i][0]=a[i];
    b[0]=1;
    ll flag=0;
    for(int i=1; i<64; i++) {
    
    
        b[i]=b[i-1]*2;
        if(b[i]>=n) {
    
    
            flag=i;
            break;
        }
    }

    for(int i=1; i<=flag; i++) {
    
    
        for(int j=1;j<=n-b[i]+1;j++){
    
    
            d[j][i]=min(d[j][i-1],d[j+b[i-1]][i-1]);
            p[j][i]=max(p[j][i-1],p[j+b[i-1]][i-1]);
        }
    }

    while(m--) {
    
    
        ll l,r;
        scanf("%lld%lld",&l,&r);
        ll len=r-l+1;
        ll k;
        for(int i=0;i<=flag;i++){
    
    
            if(b[i]<=len){
    
    
                k=i;
            }else break;
        }
        ll minn=min(d[l][k],d[r-b[k]+1][k]);
        ll maxx=max(p[l][k],p[r-b[k]+1][k]);
        printf("%lld\n",maxx-minn);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45911397/article/details/111085611
今日推荐