st表是用来求解给定区间的最大值或最小值,算法步骤大致分成两部分。
1.离线预处理:时间复杂度为o(nlog(n))
预处理使用的是倍增和动态规划的思想。不知道倍增的同学可以先看看这个博客戳我
2.在线查询:时间复杂度为o(1);
具体算法详解可以看看这个博客:戳我戳我
在你理解了这个算法后,我们来看看这个题目:题目链接
Frequent values
Description:
You are given a sequence of n integers a1 , a2 , … , an in non-decreasing order. In addition to that, you are given several queries consisting of indices i and j (1 ≤ i ≤ j ≤ n). For each query, determine the most frequent value among the integers ai , … , aj.
Input:
The input consists of several test cases. Each test case starts with a line containing two integers n and q (1 ≤ n, q ≤ 100000). The next line contains n integers a1 , … , an (-100000 ≤ ai ≤ 100000, for each i ∈ {1, …, n}) separated by spaces. You can assume that for each i ∈ {1, …, n-1}: ai ≤ ai+1. The following q lines contain one query each, consisting of two integers i and j (1 ≤ i ≤ j ≤ n), which indicate the boundary indices for the
query.
The last test case is followed by a line containing a single 0.
Output:
For each query, print one line with one integer: The number of occurrences of the most frequent value within the given range.
Sample Input:
10 3
-1 -1 1 1 1 1 3 10 10 10
2 3
1 10
5 10
0
Sample Output:
1
4
3
题意:给定一个递增的数列,每次询问你一个区间,求在这个区间出现的频繁次数最多的元素有多少次。
思路:st表是用来求区间最值的,所以对于这道题,我们可以先将原数列转化一下。
比如这组样例:-1 -1 1 1 1 1 3 10 10 10 我们可以转化为:1 2 1 2 3 4 1 1 2 3 将元素出现的次数用另一个数组b保存, 这样我每次求区间次数最大值,就是对b数组求解区间最大值。现在询问区间1 10,显而易见是4,在这段区间1出现了四次; 那假如是5 10呢,这段区间的b数组为3 4 1 1 2 3 这个时候答案就不能是4了,1只出现了两次,而10出现了三次。所以对于开头部分的元素,我们要另外再处理一下,要先将这个区间里第一个元素的个数求出来,再求剩下区间的最大值。所以对于区间5 10,先找出第一个元素的个数发现是两个,再对剩下的区间7 10求最大值,发现是三个。所以最后答案就是3。
int t = l;//从区间左端开始
while(t<=r && num[t]==num[t-1]) t++;//求第一个元素的个数
max_rmq(t,r);//再求区间t到r最大值
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1e5+10;
int a[maxn],b[maxn],mx[maxn][20];
int n,m;
int max_rmq(int l,int r)
{
if(l>r) return 0;
int k = log((double)(r-l+1))/log(2.0);
return max(mx[l][k],mx[r-(1<<k)+1][k]);
}
void init()
{
for(int i=1;i<=n;++i) mx[i][0] = b[i];
int k = log((double)(n+1))/log(2.0);
for(int i=1;i<=k;++i)
for(int j=1;j+(1<<i)-1<=n;++j)
mx[j][i] = max(mx[j][i-1],mx[j+(1<<(i-1))][i-1]);
}
int main()
{
while(scanf("%d",&n)&&n){
scanf("%d",&m);
for(int i=1;i<=n;++i){
b[i]=0;
scanf("%d",&a[i]);
if(i==1) b[i]=1;//预处理b数组
else if(a[i]==a[i-1]) b[i]=b[i-1]+1;
else b[i]=1;
}
init();
int l,r,id;
while(m--){
scanf("%d%d",&l,&r);
id = upper_bound(a+1,a+1+n,a[l])-a;//我的做法是二分第一个大于第一个元素的下标
printf("%d\n",max(max_rmq(id,r),min(r-l+1,id-l)));//但是要注意这个下标不能超过l
}
}
}