题目描述
给定一个长度为 N 的数列,和 M 次询问,求出每一次询问的区间内数字的最大值。
输入
第一行包含两个整数 N,M,分别表示数列的长度和询问的个数。
第二行包含 N 个整数(记为 ai),依次表示数列的第 i 项。
接下来 M 行,每行包含两个整数 li,ri,表示查询的区间为 [li,ri]。
输出
输出包含 M 行,每行一个整数,依次表示每一次询问的结果。
样例输入1
8 8
9 3 1 7 5 6 0 8
1 6
1 5
2 7
2 6
1 8
4 8
3 7
1 8
样例输出1
9
9
7
7
9
8
7
9
提示/说明
对于 30% 的数据,满足 1≤N,M≤10。
对于 70% 的数据,满足 1≤N,M≤105。
对于 100% 的数据,满足 1≤N≤105,1≤M≤2×106,ai∈[0,109],1≤li≤ri≤N。
标签
普及+/提高 其他 线段树 ST表,稀疏表 倍增
分析
本题是一个求区间最值问题的ST表模板题
先初始化dp,再用在线算法计算每一对L,R的区间最大值结果
#include<iostream>
#include<cmath>
using namespace std;
// 定义数组的最大长度
#define MAX 2000001
// 定义动态规划数组,dp[i][j] 表示从i开始,长度为2^j的区间的最大值
int n,m,dp[MAX][30],a[MAX];
//ST表求区间最值问题,本题求最大值
int main(){
cin.tie();
cout.tie();
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
// 初始化dp数组,单个元素的区间最大值就是它自己
for(int i=1;i<=n;i++){
dp[i][0]=a[i];
}
// 开始填充dp数组,j是区间的指数,表示2^j的长度
for(int j=1; (1<<j) <= n; j++){ // 这里的(1<<j)相当于pow(2,j),代表2的j次方,位运算更快
for(int i=1; i+(1<<j)-1 <= n; i++){ // 确保区间在数组范围内
// dp[i][j] 是由两个长度为2^(j-1)的区间合并而来,取这两个区间的最大值
dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
// 开始处理m次查询
//在线算法,随输入输出
while(m--){
int l,r,ans;
cin>>l>>r;
// 查找能覆盖区间[l,r]的最小的2^k(k为整数),使得2^k<=r-l+1
int k=log2(r-l+1);
// 区间[l,r]被拆分为两个区间:[l, l+2^k-1]和[r-2^k+1, r]
// 取这两个区间的最大值作为[l,r]的最大值
ans=max(dp[l][k],dp[r-(1<<k)+1][k]);
cout<<ans<<'\n';
}
return 0;
}
//本文为张.tiger.予诺原创,请不要抄袭,抄袭==欺骗自己