题目描述
输入一串数字,给你 M个询问,每次询问就给你两个数字X,Y ,要求你说出 X 到 Y 这段区间内的最大数。
输入格式
第一行两个整数n,m 表示数字的个数和要询问的次数;
接下来一行为 n 个数;
接下来 m行,每行都有两个整数 x,y。
输出格式
输出共M行,每行输出一个数。
样例
样例输入
10 2
3 2 4 5 6 8 1 2 9 7
1 4
3 8
样例输出
5
8
数据范围与提示
对于全部数据1<=n<=1e5,1<=m<=le6, 1<=x<=y<=n。数字不超过 C/C++
的 int
范围。
思路:可用线段树写 nlogn + mlogn,这里可用ST算法求解,复杂度 O(n*logn),询问时O(1),ST算法适用于没有修改的操作且询问次数较多的的情况。
ST算法,其实就是动态规划,dp[i][j] 表示起始位为i,后2^j位的最大值,递推方程式dp[i][j] = Max(dp[i][j-1],dp[i+2^(j-1)][j-1]),区间长度2^j,分成两个2^(j-1),意思是要求一个数的后2^j位的最大值,需要先求出一个数后2^(j-1)位的最大值 (在这个数+2^(j-1)次方后) ,询问求最终结果时,// 要求最大的x满足 2^x<=r-l+1,x = log2(r-l+1),[i,j]的最大值 = max(dp[l][x],dp[r-2^x+1][x]);就算有dp[i][x] 和 dp[r-2^x+1][x] 有交集,也不影响 [l,r]中的最大值。
下面在递推时,还得要注意当 i+2^j-1 超过 n了,就不用求了,代码中有解释
代码:
//静态的RQM问题,可用ST算法,求解,时间复杂度N*logN,每次询问 O(1),空间复杂度N*logN
#include<bits/stdc++.h>
using namespace std;
const int Max = 1e5+10,LogN = 19;
int a[Max],n,k;
int Log[Max];
int dp[Max][LogN+5],c[LogN+5];
// 2^(j-1) = 1<<j; 若运算少的话,可以用左移运算符代替2的次方,运算多的话,就算了,怕卡时长。
//dp[i][j] 表示起始位为i,后2^j位的最大值,递推方程式dp[i][j] = Max(dp[i][j-1],dp[i+2^(j-1)][j-1])
//区间长度2^j,分成两个2^(j-1),意思是要求一个数的后2^j位的最大值,需要求出一个数后2^(j-1)位的最大值
//(在这个数+2^(j-1)次方后)
int main()
{
scanf("%d%d",&n,&k);
for(int i = 1;i<=n;i++)
scanf("%d",&a[i]);
Log[0] = -1; // log N 向下取整的递推方式;// 如向上取整 Log[0] = 0;
for(int i = 1;i<=n;i++)
dp[i][0] = a[i],Log[i] = Log[i>>1] + 1;
c[0] = 1;
for(int i = 1;i<=LogN;i++)
c[i] = c[i-1] * 2;
for(int j = 1;j<=LogN;j++){
//求 i后面2^j位的最大值,首先要保证i+2^j不超多n,超过就没有意义了,不一定要把每一个dp[i][j]都求不来,
//i+2^j超过n的,就算求了在询问时,也用不上。
for(int i = 1;i+c[j]-1<=n;i++) // 这里一定要注意减一,dp[i][j] 表示的是算上i后面的2^j位的最大值。
dp[i][j] = max(dp[i][j-1],dp[i+c[j-1]][j-1]);
}
int x,y;
while(k--)
{
int x,y;
scanf("%d%d",&x,&y);
int tt = Log[y-x+1]; // 要求最大的x满足 2^x<=r-l+1,所以log[y-x-1]求得log2(y-x-1)向下取整。
printf("%d\n",max(dp[x][tt],dp[y-c[tt]+1][tt])); // 就算有重叠的部分也不影响最大值的
}
return 0;
}