ST表——求区间[l,r]之间的最大值

  1. 讲解

  2. 板子题

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,l,r;
    int a[180010];
    int maxn[180010][50];
    int minn[180010][50];
    ///  f(i,j)=从ai开始的2^j个数里面
    ///  maxn[i][j]=[i,i+2^j-1]里面的最大值
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; i++)
        {
            scanf("%d",a+i);
            maxn[i][0]=a[i];
            minn[i][0]=a[i];
        }
        for(int j=1; j<=21; j++)
        {
            for(int i=1; (i+(1<<j)-1)<=n; i++)
            {
                maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);
                minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
            }
        }
        while(m--)
        {
            scanf("%d%d",&l,&r);
            int len=log2(r-l+1);
            int ans1=max(maxn[l][len],maxn[r-(1<<len)+1][len]);///len->2^(len-1)
            int ans2=min(minn[l][len],minn[r-(1<<len)+1][len]);
           // cout<<l<<" "<<r<<" "<<len<<" "<<maxn[l][len]<<" "<<maxn[r-(1<<len)+1][len]<<endl;
           printf("%d\n",ans1-ans2);
        }
        return 0;
    }
    

    3.链接:https://ac.nowcoder.com/acm/contest/11256/K
    来源:牛客网

    示例1

    输入

    5 1
    1 2 3 4 5
    2

    输出

    3
    
    #pragma GCC optimize(1)
    #pragma GCC optimize(2)
    #pragma GCC optimize(3,"Ofast","inline")
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,k,a;
    int maxn[100005][30];
    int minn[100005][30];
    int lg[100005];
    ///  f(i,j)=从ai开始的2^j个数里面
    ///  maxn[i][j]=[i,i+2^j-1]里面的最大值
    bool check(int l,int r,int k)
    {
        int len=lg[r-l+1];
        int ans1=max(maxn[l][len],maxn[r-(1<<len)+1][len]);///len->2^(len-1)
        int ans2=min(minn[l][len],minn[r-(1<<len)+1][len]);
        if(ans1-ans2>k) return true;
        return false;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        cin>>n>>m;
        for(int i=1; i<=n; i++)
        {
            cin>>a;
            maxn[i][0]=a;
            minn[i][0]=a;
            if(i>=2) lg[i]=lg[i/2]+1;
        }
        for(int j=1; j<=21; j++)///构造st表
        {
            for(int i=1; (i+(1<<j)-1)<=n; i++)
            {
                maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);
                minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
            }
        }
        while(m--)
        {
            cin>>k;
            ll ans=0;
            for(int i=1,j=1; i<=n; i++)
            {
                while(!check(i,j,k)&&j>=i&&j<=n)///找最贴近的i
                {
                    j++;
                }
                if(j<=n)
                {
                    ans+=n-j+1;
                    // printf("yes\n");
                }
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ST是一种用于快速查询区间最值的数据结构。它的核心思想是对区间进行预处理,将区间内的最值信息存储在一个二维数组中,然后利用这个数组进行查询。以下是一个最大值ST模板代码: ``` const int MAXN = 100005; const int MAXLOGN = 20; int a[MAXN]; int st[MAXN][MAXLOGN]; void init(int n) { for (int i = 1; i <= n; i++) { st[i][0] = a[i]; } for (int j = 1; (1 << j) <= n; j++) { for (int i = 1; i + (1 << j) - 1 <= n; i++) { st[i][j] = max(st[i][j-1], st[i+(1<<(j-1))][j-1]); } } } int query(int l, int r) { int k = log2(r-l+1); // k为最大的2的幂次方,使得2^k <= r-l+1 return max(st[l][k], st[r-(1<<k)+1][k]); } int main() { int n, q; cin >> n >> q; for (int i = 1; i <= n; i++) { cin >> a[i]; } init(n); while (q--) { int l, r; cin >> l >> r; cout << query(l, r) << endl; } return 0; } ``` 这段代码中,init函数用于初始化ST,query函数用于查询区间最大值。具体来说,init函数的实现如下: 1. 将a[i]的值存储到st[i][0]中,区间[i,i]的最大值为a[i]。 2. 对于每个j,计算区间[i,i+2^j-1]的最大值,存储在st[i][j]中。可以发现,区间[i,i+2^j-1]可以拆分为两个长度为2^(j-1)的子区间,即区间[i,i+2^(j-1)-1]和区间[i+2^(j-1),i+2^j-1]。因此,区间[i,i+2^j-1]的最大值等于区间[i,i+2^(j-1)-1]的最大值区间[i+2^(j-1),i+2^j-1]的最大值中较大的一个。 query函数的实现也比较简单,首先计算k,然后查询区间[l,r]的最大值,等价于查询区间[l,l+2^k-1]的最大值区间[r-2^k+1,r]的最大值中较大的一个。 最小值的ST模板代码与最大值的类似,只需要将max改为min即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值