【ST稀疏表】20. 数列区间最大值

题目描述

输入一串数字,给你 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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值