#10119. 「一本通 4.2 例 1」数列区间最大值(ST算法求解静态的RQM问题)

题目描述

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

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值