zoukankan      html  css  js  c++  java
  • ST表略解

    题面

    给定一个长度为(N)的数列,和(M)次询问,求出每一次询问的区间内数字的最大值。

    对于30%的数据,满足: (1≤N,M≤10)
    对于70%的数据,满足: (1≤N,M≤10^5)
    对于100%的数据,满足: (1≤N≤10^5,1≤M≤10^6,a_i∈[0,10^9],1≤li≤ri≤N)

    线段树裸体啊。
    但是这道题目线段树明显过不去这道题。所以我们要另辟蹊径我们观察题目,题目并没有要求我们进行更改操作,所以st表就派上用场了。

    前置要求

    1. 倍增(如果不会戳这)
    2. dp(如果不会戳这)

    正文

    如果你会上面的内容了,那么你可以开始学st表了
    st表实际上就是一个倍增+dp
    我们令(f[i][j])([i,i+2^j-1])内的最大值
    我们可以将([i,i+2^j-1])分成两半,即([i,i+2^{j-1}-1])([i+2^{j-1},i+2^j-1])
    两段的长度都为(2^j)
    于是我们可以写出转移方程f[i][j]=min(f[i][j-1],f[i+1<<(j-1)-1][j-1])
    注意需要预处理一下(f[i][0])的值

    那么询问怎么办?
    对于一段询问的区间([l,r])
    我们可以算出从l到r最少需要加上2的多少次方,即k=log(r-l+1)/log(2)
    这里运用到了换底公式,也可以直接写成k=log2(r-l+1)

    code

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int read(){
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9') c=='-'?f=-1,c=getchar():c=getchar();
        while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
        return x*f;
    }
    int f[100001][21],a[100001],n=read(),m=read(),x,y;
    void init(){
        for(int i=1;i<=n;i++)
            f[i][0]=a[i];
        for(int j=1;j<19;j++)
            for(int i=1;i+(1<<j)-1<=n;i++)
                f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    }
    int find(int x,int y){
        int k=log(y-x+1)/log(2);
        return max(f[x][k],f[y-(1<<k)+1][k]);
    }
    int main(){
        for(int i=1;i<=n;i++)
            a[i]=read();
        init();
        while(m--)
            x=read(),y=read(),printf("%d
    ",find(x,y));
        return 0;
    }
    
  • 相关阅读:
    [POI2014]KUR-Couriers
    MySQL有哪些索引
    索引的设计原则
    explain参数之extra
    explain参数之type
    explain参数之select_type
    如何查询最后一行的记录
    为什么MySQL自增id不连续?
    MySQL字符集
    MySQL有哪些优化策略?
  • 原文地址:https://www.cnblogs.com/hbxblog/p/9873469.html
Copyright © 2011-2022 走看看