ST算法

在RMQ问题(区间最值问题)中,著名的ST算法就是倍增的产物,给定一个长度为N的数列A,ST算法能在O(NlogN)的时间预处理后,以O(1)的时间复杂度在线回答“数列A中下标在l~r之间的数的最大值是多少”这样的区间最值问题。

以下是ST算法的实现:

首先,我们用f[i][j]来表示在序列a中,第i位数字以后数2j个数之中的最大值,那么就可以得到f[i][0]=a[i]。

运用DP找状态方程:

for(int j = 1;j <= ( log2(n) ); j++)
	for(int i = 1;i <= n - (1 << j) + 1; i++)
		f[i][j] = max(f[i][j-1],f[i+(1<<(j-1))][j-1]);

f[i][j]表示的区间为[i,i+2j]中的最大值,而2j=2*2j-1=2j-1+2j-1,因此,我们可以把区间[i,i+2j]分成[i,i+2j-1]和[i+2j-1+1,2j]。既然已经可以把区间划分成两份,那么DP的思路便很明了了,f[i][j]的值便可以求出来。
但是,问题所询问的区间并不一定恰好是2的幂次方,有可能会超出区间。
所以,设l为区间长度,在查询时我们可以求出一个k=log2(l),向下取整。
那么就有:l/2<=2k<=l
max(f[i][k],f[i+2j-2k][k])便是所求区间的最大值。
下面来一道洛谷的例题:

题目描述

Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题。

对于一段妹子们,他们想让你帮忙求出这之内美丽度\in [a,b]∈[a,b]的妹子的美丽度的种类数。

为了方便,我们规定妹子们的美丽度全都在[1,n][1,n]中。
给定一个长度为n(1 \le n \le 100000)n(1≤n≤100000)的正整数序列s(1 \le si \le n)s(1≤si≤n),对于m(1 \le m \le 1000000)m(1≤m≤1000000)次询问l,r,a,b,每次输出s_l \cdots s_rsl​⋯sr​中,权值\in [a,b]∈[a,b]的权值的种类数。

输入格式

第一行包括两个整数n,m(1 \le n \le 100000,1 \le m \le 1000000)n,m(1≤n≤100000,1≤m≤1000000),表示数列ss中的元素数和询问数。

第二行包括nn个整数s1…sn(1 \le si \le n)s1…sn(1≤si≤n)。

接下来mm行,每行包括44个整数l,r,a,b(1 \le l \le r \le n,1 \le a \le b \le n)l,r,a,b(1≤l≤r≤n,1≤a≤b≤n),意义见题目描述。

保证涉及的所有数在C++的int内。保证输入合法。

输出格式

对每个询问,单独输出一行,表示s_l \cdots s_rsl​⋯sr​中权值\in [a,b]∈[a,b]的权值的种类数。

输入输出样例

输入 #1复制

10 10
4 4 5 1 4 1 5 1 2 1
5 9 1 2
3 4 7 9
4 4 2 5
2 3 4 7
5 10 4 4
3 9 1 1
1 4 5 9
8 9 3 3
2 2 1 6
8 9 1 4

输出 #1复制

2
0
0
2
1
1
1
0
1
2

说明/提示

【样例的部分解释】

5 9 1 2 子序列为4 1 5 1 2
在[1,2]里的权值有1,1,2,有2种,因此答案为2。

3 4 7 9
子序列为5 1 在[7,9]里的权值有5,有1种,因此答案为1。

4 4 2 5
子序列为1
没有权值在[2,5]中的,因此答案为0。

2 3 4 7
子序列为4 5
权值在[4,7]中的有4,5,因此答案为2。

建议使用输入/输出优化。

#include<iostream>
#include<cmath>
using namespace std;
long f[100001][40];
int n,m;
int main(){
    cin>>n>>m;
    int temp;
    for(int i=1;i<=n;i++){
        cin>>temp;
        f[i][0]=temp;
    }
    for(int k=1;k<=(int)log2(n);k++){
        for(int i=1;i<=n-(1<<k)+1;i++){
            f[i][k]=max(f[i][k-1],f[i+(1<<(k-1))][k-1]);
        }
    }
    int l,r;
    for(int i=1;i<=m;i++){
        cin>>l>>r;
        int k=log2(r-l+1);
       cout<<max(f[l][k],f[r-(1<<k)+1][k])<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值