题目描述
输入一串数字,给你 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 范围。
解析:
求区间最值的模板问题,用ST算法实现,总的时间复杂度O(nlogn+Q) 。
倍增思想:
f[i][j]表示i开始的连续2^j 个点的最大值。
因此 f[i][0]表示i开始的连续 1 个点的最大值,a[i] 。
因此 f[i][1]表示i开始的连续 2 个点的最大值,max{ a[i] , a[ i+1 ] }。
因此 f[i][2]表示i开始的连续 4 个点的最大值,max{ a[i] , a[ i+1 ] ,a[i+2] , a[ i+3] }。
f [ i ] [ log(n) / log(2) ] 开始连续n个点的最大值 a[i] ~ a[i+n-1] (i+n-1 <=n)
代码:
当输入输出数据的规模达到10的6次方时,就需要用scanf和pritf输入输出。此时用cin和cout是绝对会超时的。
#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
const int maxn=100100;
int n,m;
int f[maxn][20];
int a[maxn];
void fun() //时间复杂度O(nlogn)
{
int k=log(n)/log(2); //深度
for(int i=1;i<=n;i++) //初始化
{
f[i][0]=a[i];
}
for(int j=1;j<=k;j++)
{
for(int i=1;i+(1<<j)-1 <=n;i++)//长度为2^j的区间 [i , i+2^j-1 ]
{
f[i][j] = max(f[i][j-1] ,f[i+(1<<(j-1))][j-1]);
}
}
printf("---f[][]----\n");
for(int i=1;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
if(i+(1<<j) >n+1 )continue;
printf("f[%d][%d]:%d ",i,j,f[i][j]);
}
printf("\n");
}
}
int qwt(int x,int y) //时间复杂度O(1)
{
int k=log(y-x+1) / log(2); // 2^k <= y-x+1 ; 2^k * 2 >y-x+1
return max(f[x][k],f[y-(1<<k)+1][k]); //x+2^k-1<=y,y-2^k+1>=x,
//所以区间[x,x+2^k-1]和[y-2^k+1,y]是正好相切或者有部分重合,
//也就是说两个区间是无缝衔接的,这样求两个区间最值的最值就是所求答案。
}
int main()
{
//当输入输出数据的规模达到10的6次方时,就需要用scanf和pritf输入输出。
//此时用cin和cout会超时。
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
fun();
int x,y;
for(int i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
printf("%d\n",qwt(x,y));
}
return 0;
}
测试:
10 2
3 2 4 5 6 8 1 2 9 7
---f[][]----
f[1][0]:3 f[1][1]:3 f[1][2]:5 f[1][3]:8
f[2][0]:2 f[2][1]:4 f[2][2]:6 f[2][3]:9
f[3][0]:4 f[3][1]:5 f[3][2]:8 f[3][3]:9
f[4][0]:5 f[4][1]:6 f[4][2]:8
f[5][0]:6 f[5][1]:8 f[5][2]:8
f[6][0]:8 f[6][1]:8 f[6][2]:9
f[7][0]:1 f[7][1]:2 f[7][2]:9
f[8][0]:2 f[8][1]:9
f[9][0]:9 f[9][1]:9
f[10][0]:7
1 4
5
3 8
8