关于st表的推导

#include <bits/stdc++.h>

using namespace std;
const int maxn=1e6+7;
int st[maxn][32];
int a[maxn],n;
void init(){
    int i,j;
    //st[i][j]表示i到i+2^j-1区间的最小值
    //先预处理区间长度为1的
    for(i=0;i<n;++i)  st[i][0]=a[i];
    for(i=0;i<n;++i){
        for(j=1;i+2^(j)-1<n;++j){
            //i~i+2^(j-1)-1
            //i+2^(j-1)~i+2^(j-1)+2^(j-1)-1=>i+2^j-1;
            //一定要发现这个显然的事实就是
            //2^(j-1)+2^(j-1)=2^j;
            st[i][j]=min(s[i][j-1],s[i+2^(j-1)][j-1]);
        }
    }
}
int queryMin(int l,int r){
    int len=r-1+1;
    int index=log(len);
    //st[l][index] l~l+2^(index)-1
    //2^(index)<=(r-l+1); l+2^(index)-1<=r
    //r-(l+2^(index)-1)>=0 还差多少元素没放进来
    //x+LEN=l+2^(index)-1+(r-(l+2^(index)-1));
    //x+2^(index)-1=r;//区间长度固定。。起点是多少才能正好跑到r,列一个简单的方程才能解决
    //x=r+1-(2^(index));
    return min(st[l][index],st[r+1-(2^(index))][index]);
}
int main(){
    while(~scanf("%d",&n)){
        int i,q,l,r;
        for(i=0;i<n;++i){
            scanf("%d",a+i);
        }
        init();
        scanf("%d",&q);
        for(i=0;i<q;++i){
            scanf("%d%d",&l,&r);
            printf("%d\n",query(l,r));
        }
    }
    return 0;
}

上面这个^符号代表幂次。。而c++里只有异或。。这就是为什么这是一个伪代码的意思

先来一个终极伪代码

推导过程如上。。

下面给一个真正的的代码

#include <bits/stdc++.h>

using namespace std;
const int maxn=1e6+7;
int st[maxn][32];
int a[maxn],n;
void init(){
    int i,j;
    //st[i][j]表示i到i+2^j-1区间的最小值
    //先预处理区间长度为1的
    for(i=0;i<n;++i)  st[i][0]=a[i];
    for(i=0;i<n;++i){
        for(j=1;i+(1<<j)-1<n;++j){//这里有一个优化。。本来是小于32的。。问题规模较小是只是相当于一个常数的优化
            //i~i+2^(j-1)-1
            //i+2^(j-1)~i+2^(j-1)+2^(j-1)-1=>i+2^j-1;
            //一定要发现这个显然的事实就是
            //2^(j-1)+2^(j-1)=2^j;
            st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
        }
    }
}
int queryMin(int l,int r){
    int k=log(r-l+1);
    //st[l][index] l~l+2^(index)-1
    //2^(index)<=(r-l+1); l+2^(index)-1<=r
    //r-(l+2^(index)-1)>=0 还差多少元素没放进来
    //x+LEN=l+2^(index)-1+(r-(l+2^(index)-1));
    //x+2^(index)-1=r;//区间长度固定。。起点是多少才能正好跑到r,列一个简单的方程才能解决
    //x=r+1-(2^(index));
    return min(st[l][k],st[r+1-(1<<k)][k]);
}
int main(){
    while(~scanf("%d",&n)){
        int i,q,l,r;
        for(i=0;i<n;++i){
            scanf("%d",a+i);
        }
        init();
        scanf("%d",&q);
        for(i=0;i<q;++i){
            scanf("%d%d",&l,&r);
            printf("%d\n",queryMin(l-1,r-1));
        }
    }
    return 0;
}

还有一个对于新手来说理解的坑。。那就是int x=log(val)实际上是对log的值向下取整。。这一点非常重要
只有这个成立我们注释里的推导才会成立。。另外有一些没用的推导。。但是我没有删掉。。这是因为想记录一下我全部的思考过程

时间: 2024-10-18 15:55:48

关于st表的推导的相关文章

RMQ问题 - ST表的简单应用

2017-08-26 22:25:57 writer:pprp 题意很简单,给你一串数字,问你给定区间中最大值减去给定区间中的最小值是多少? 用ST表即可实现 一开始无脑套模板,找了最大值,找了最小值,分别用两个函数实现,实际上十分冗余 所以TLE了 之后改成一个函数中同时处理最大值和最小值,就可以了 AC代码如下: /* @theme:poj 3264 @writer:pprp @declare:ST表(sparse table)稀疏表,用动态规划的思想来解决RMQ问题: @date:2017

[模板]ST表浅析

ST表,稀疏表,用于求解经典的RMQ问题.即区间最值问题. Problem: 给定n个数和q个询问,对于给定的每个询问有l,r,求区间[l,r]的最大值.. Solution: 主要思想是倍增和区间dp. 状态:dp[i][j] 为闭区间[i,i+2^j-1]的最值. 这个状态与转移方程的关系很大,即闭区间的范围涉及到了转移方程的简便性. 转移方程:dp[i][j]=max(dp[i][j-1],dp[i+2^(j-1)][j-1]). 这是显然的,但这里有个细节:第一个项的范围为[i,i+2^

【模板】ST表 洛谷P1816 忠诚

P1816 忠诚 题目描述 老管家是一个聪明能干的人.他为财主工作了整整10年,财主为了让自已账目更加清楚.要求管家每天记k次账,由于 管家聪明能干,因而管家总是让财主十分满意.但是由于一些人的挑拨,财主还是对管家产生了怀疑.于是他决定用一种特别的方法来判断管家的忠诚,他把每次的 账目按1,2,3…编号,然后不定时的问管家问题,问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题. 输入输出格式 输入格式: 输入中第一行有两个数m,n表示有m(m<=100000

GCD(st表)

GCD Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 3432    Accepted Submission(s): 1227 Problem Description Give you a sequence of N(N≤100,000) integers : a1,...,an(0<ai≤1000,000,000). There a

UVA 11475 Extend to Palindrome(后缀数组+ST表)

[题目链接] http://acm.hust.edu.cn/vjudge/problem/27647 [题目大意] 给出一个字符串,要求在其后面添加最少的字符数,使得其成为一个回文串.并输出这个回文串. [题解] 用拼接符将原字符串倒序相接,做一遍后缀数组,查询两串相应位置的LCP就是以该点为中心的回文串长度的一半分,奇偶求出所有后缀回文串,保留最长的,则补充部分为剩下的前缀的倒置.至于查询两串的LCP我们可以在height数组建立ST表查询. [代码] #include <cstdio> #

lca最近公共祖先(st表)

大体思路 1.求出每个元素在树中的深度 2.用st表预处理的方法处理出f[i][j],f[i][j]表示元素i上方第2^j行对应的祖先是谁 3.将较深的点向上挪,直到两结点的深度相同 4.深度相同后,祖先可能就在上方,再走几步就到了,于是两个点同时向上移 具体的方法和代码贴在下面 ↓ 具体来看 1.求出每个元素在树中的深度 //求每个节点在树中的深度 void dfs(int pos,int pre)//pre是pos的父节点 { for(int i=0;i<v[pos].size;i++)//

【BZOJ-3956】Count ST表 + 单调栈

3956: Count Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 173  Solved: 99[Submit][Status][Discuss] Description Input Output Sample Input 3 2 0 2 1 2 1 1 1 3 Sample Output 0 3 HINT M,N<=3*10^5,Ai<=10^9 Source CH Round#64 MFOI杯水题欢乐赛day1 By Gromah So

luoguP1890 gcd区间 [st表][gcd]

题目描述 给定一行n个正整数a[1]..a[n]. m次询问,每次询问给定一个区间[L,R],输出a[L]..a[R]的最大公因数. 输入输出格式 输入格式: 第一行两个整数n,m. 第二行n个整数表示a[1]..a[n]. 以下m行,每行2个整数表示询问区间的左右端点. 保证输入数据合法. 输出格式: 共m行,每行表示一个询问的答案. 输入输出样例 输入样例#1: 5 3 4 12 3 6 7 1 3 2 3 5 5 输出样例#1: 1 3 7 说明 对于30%的数据,n <= 100, m

说一说ST表 讲一讲水题

ST表 一.算法介绍 如何快速求解RMQ问题呢?暴力复杂度O(n),线段树复杂度O(n)~O(logn),要是数据规模达到10^7或者更高呢?我们需要一种可以做到O(1)查询的算法,这时就可以用到ST表. 我们用 f[i][j] 表示从 j 位置开始往右 2^i 个数内的最大值,用 g[i][j] 表示从j位置开始往左 2^i 个数内的最大值.所以 f[0][j] , g[0][j] 就为 j 位置上的数,可以在预处理中O(n)处理掉. 接下来我们要求出每个位置的每个 2^i 区间的最大值.可以