• ST表


    ST表

    什么是ST表

    ST表是一种神奇的数据结构,它虽说有它的短板——不支持修改,但它的特点同样很鲜明——短小精悍,能做到 $ O (n log n) $的预处理, $ O(1) $ 的单次查询。

    实现方法:

    ST表分为预处理和区间查询两个部分,实现起来十分简单。

    预处理的预处理:

    由于我们需要每次倍增的运算,所以一定会涉及到多次计算 $ 2^i $ 和 $ log i $ ,所以可以对这两个数组进行预处理。
    实现方案十分简单,就直接放代码了

    num[0] = 1,lg[0] = -1;
    for(int i = 1 ; i <= 30 ; i++)
        num[i] = num[i - 1] * 2;
    for(int i = 1 ; i <= n ; i++)
        lg[i] = lg[i / 2] + 1;
    

    真正的预处理:

    设 $ f[i][j] $ 表示从第i号节点到第 $ i+2^j-1 $ 号节点的最大值,即从 $ i $ 号节点开始往后数共 $ 2^j $ 个节点中的最大值。
    例如 $ f[i][1] $ 指的就是第 $ i $ 号节点和第 $ i+1 $ 号节点 的最大值。
    但是暴力的来构造肯定是不行的呀,我们考虑利用性质进行二分。我们可以逐层循环j和i,当我们循环到要处理f[i][j]时,我们其实就是要求这个区间 $ i $ 到 $ i+2^j - 1 $ 中的最大值。
    这时我们发现,由于区间长度为2^j,所以是可以分割成左右两部分的,即 $ i $ 到 $ i + 2^{j-1}-1$ 和 $ i + 2 ^ {j-1} $ 到 $ i+2^j-1 $
    所以我们原区间的最大值就变成了两个区间的最大值的最大值,代码如下:

    for(int i = 1 ; i <= lg[n] ; i++) {
            for(int j = 1 ; j <= n ; j++) {
                if(j + num[i] - 1 <= n) 
                    f[j][i] = max(f[j][i - 1],f[j + num[i - 1]][i - 1]);
            }
        }
    

    区间查询:

    首先,先说一个定理: $ 2^{log x} > frac{x}{2} $ 。
    这个定理告诉我们,任意一个长度为 $ x $ 的区间,都能被两端长度为 $ 2^{log x} $ 的区间所覆盖 。
    所以我们的查询也变得很简单了,假如我们要查询区间 $ [l,r] $ 的最大值,很显然,这段区间长度 $ len=l-r+1 $ 。
    那么我们只要求出以 $ l $ 为首,长度为 $ 2^{log len} $ 的区间的最大值和以 $ r $ 为尾,长度为 $ 2^{log len} $ 的区间的最大值,就一定包括了整个区间的最大值。
    所以我们可以知道 $ [l,r] $ 中的最大值为 $ max(f[l][log len],f[r -2^{log len}+1][log len]) $ 。

    例题:luogu P3865

    题意:

    给你一个长度为 $ n $ 的的数列和 $ m $ 组询问,询问序列中的最大值。

    CODE:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    
    using namespace std;
    
    #define LL long long
    const int N = 1e6 + 100;
    
    int f[N][30],num[N];
    int m,n,ans,lg[N];
    
    int main() {
        scanf("%d%d",&n,&m);
        for(int i = 1 ; i <= n ; i++)
            scanf("%d",&f[i][0]);
        num[0] = 1,lg[0] = -1;
        for(int i = 1 ; i <= 30 ; i++)
            num[i] = num[i - 1] * 2;
        for(int i = 1 ; i <= n ; i++)
            lg[i] = lg[i / 2] + 1;
        for(int i = 1 ; i <= lg[n] ; i++) {
            for(int j = 1 ; j <= n ; j++) {
                if(j + num[i] - 1 <= n) 
                    f[j][i] = max(f[j][i - 1],f[j + num[i - 1]][i - 1]);
            }
        }
        for(int i = 1 ; i <= m ; i++) {
            int l,r;
            scanf("%d%d",&l,&r);
            int cnt = lg[r - l + 1];
            ans = max(f[l][cnt],f[r - num[cnt] + 1][cnt]);
            printf("%d 
    ",ans);
        }
        //system("pause");
        return 0;
    }
    
  • 相关阅读:
    MySql 数据类型
    MySql 数据库的增删改
    MySql 联合查询
    Mysql 库的管理 --->>>>DDL
    MySql 子查询
    MySql 分页查询
    sql99语法的连接查询
    MySql 连接查询
    MySql 分组函数
    jQ处理页面中尺寸过大的图片
  • 原文地址:https://www.cnblogs.com/Repulser/p/11503033.html
Copyright © 2020-2023  润新知