浅谈 倍增

命题描述

给定一个长度为 n n n 的序列, m m m 次询问区间最大值

分析

上面的问题肯定可以暴力对吧。
但暴力肯定不是最优对吧,所以我们直接就不考虑了。。。

于是引入:倍增

首先,倍增是个什么东西?

在这里转一篇写的超棒的blog,点我。要是这都没看懂你就连小白兔都不如我就无语了。

总的来说,其实就是倒着运用二分的思想,从需求小的慢慢倍增把答案更新到需求大的

ST表就是一种常见的倍增思想的运用

关于ST表

ST表和树状数组线段树这两种算法一样,是一种用于解决 R M Q ( R a n g e M i n i m u m / M a x i m u m Q u e r y ) RMQ(Range Minimum/Maximum Query) RMQRangeMinimum/MaximumQuery多次区间查询问题的离线算法

ST表的主要思想是构建一个二维数组 s t [ i ] [ j ] st[i][j] st[i][j],这个二维数组表示需要查询的数组的从下标 i i i 到下标 2 j / 2 2^j / 2 2j/2 的最值,这里以最大值为例。超像 d p dp dp 的说~

我们可以把 [ i , j ] [i, j] [i,j] 拆成数量相等的两半,在求出这两部分的最大值即可,故:

st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1];

再枚举一下 i , j i, j i,j 就可以了。点儿都不高级


接下来,该怎么实现查询呢?

如果查询的区间长度刚好是 2 k ( k 为 整 数 ) 2^k(k为整数) 2kk,直接输出 s t [ l ] [ l o g ( r − l + 1 ) / l o g ( 2 ) ] st[l][log(r - l + 1) / log(2)] st[l][log(rl+1)/log(2)] 即可;

如果不是,也很简单,我们还是将所给区间分为两部分。
首先规定在 s t [ i ] [ j ] st[i][j] st[i][j] i = l i = l i=l j = k j = k j=k,区间长度 l e n = r − l + 1 len = r - l + 1 len=rl+1
会发现最大的 k k k 应满足 2 k < = l e n 2^k <= len 2k<=len (这样以 l l l 开头的ST表数据覆盖需要查询的区间中的数最多)

所以 k = ( i n t ) ( l o g ( l e n ) / l o g ( 2 ) ) k = (int) (log(len)/log(2)) k=(int)(log(len)/log(2))
显然查询时的区间 [ l , r ] [l,r] [l,r] 分成的右边部分的左端点 x = r + 1 − 2 p x = r + 1 - 2^p x=r+12p

具体实现
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

const int MAXN = 100005;
int st[MAXN][41];

void read(int &a) { // 读优
	int k = 1;
	a = 0;
	char s = getchar();
	while(s < '0' || s > '9') {
		if(s == '-') k = -1;
		s = getchar();
	}
	while(s >= '0' && s <= '9') { 
		a = a * 10 + (s - '0');
		s = getchar();
	} 
	a *= k;
}

int main() {
	int n, m;
	read(n); read(m);
	for(int i = 1; i <= n; i++) read(st[i][0]);
	
	for(int j = 1; j <= (int)(log(n) / log(2)); j++)
		for(int i = 1; i + (1 << j) - 1 <= n; i++)
			st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
			
	for(int i = 1; i <= m; i++) {
		int l, r;
		read(l); read(r);
		int k = (int)(log(r - l + 1) / log(2));
        printf("%d\n", max(st[l][k], st[r - (1 << k) + 1][k]));
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值