ST表

ST表

  • 用于解决可重复贡献问题

    可重复贡献问题,是指对于运算 \(\operatorname{opt}\) ,满足 \(x \operatorname{opt} x=x\),则对应的区间询问就是一个可重复贡献问题。例如,最大值有 \(\max(x,x)=x\)\(\gcd\)\(\gcd(x,x)=x\),所以 \(\operatorname{RMQ}\) 和区间 \(\gcd\) 就是一个可重复贡献问题。像区间和就不具有这个性质,如果求区间和时采用的预处理区间重叠了,则会导致重复部分计算两次,这是我们所不愿意看到的。另外,\(\operatorname{opt}\) 还必须满足结合律才能使用ST表求解。

    RMQ,RMQ 是英文 Range Maximum/Minimum Query 的缩写,表示区间最大(最小)值。

  • 我们能使用至多两个预处理过的区间来覆盖询问区间,也就是说询问时的时间复杂度可以被降至 \(O(1)\)

预处理

  • 使用 \(f(i,j)\) 代表 \(i\)\(i+2^j-1\) 的最值,显然 \(f(i,0)=a_i\)
  • \(f(i,j)=\operatorname{opt}(f(i,j-1),f(i+2^{j-1},j-1))\)
  • 以上就是预处理部分,复杂度为 \(O(n \log n)\)

查询

  • 对于每个询问,可以把它分成两部分:\(f(l,l+2^s-1)\)\(f(r-2^s+1,r)\) ,其中 \(s=\lfloor \log_2(r-l+1)\rfloor\)

  • \(\log\) 可以建一个 \(\log_2x\) 的表,这样就不需要STL的log函数了

    \(\log_2x+1=\log_2x+\log_22=\log_2(2x)\)

  • 两部分的结果的 \(\operatorname{opt}\) 就是最终结果

模板

int Log[maxN];
int f[maxN][22];

//这里多定义一个opt函数是为了便于修改去解决其他的可重复贡献问题
inline int opt(int x,int y){
    return max(x,y);
}

void pre(){
    //预处理log值
    Log[1] = 0; Log[2] = 1;
    for(int i = 3;i<maxN;i++){
        Log[i] = Log[i/2]+1;
    }
    //预处理f值
    for(int j = 1;j<=21;j++){
        for(int i = 1;i + (1 << j) - 1 <= n;i++){
            f[i][j] = opt(f[i][j-1],f[i+(1<<j-1)][j-1]);
        }
    }
}

int ask(int l,int r){
    int s = Log[r - l + 1];
    return opt(f[x][s],f[y - (1 << s) + 1][s]);
}
posted @ 2022-02-15 14:18  Burnling  阅读(77)  评论(0编辑  收藏  举报