倍增即为划分上的成倍增长:
试求这样一个问题,有一个长度为N的数列A然后进行若干次询问,每次给出一个值T,求出最大的k满足从A[i]到A[k]的和小于T,同时要求的回答必须在线。
bf:每次枚举直到大于T为止.时间复杂度为n
二分:因为前缀和具有单调性,我们先预处理前缀和,然后二分k值这样复杂度为log(n)。
倍增:在每次T都很小的时候二分的优化,从开始枚举区间长度。
ST表
输入一串数字,给你 MM 个询问,每次询问就给你两个数字 X,Y,要求你说出 X 到 Y这段区间内的最大数。
输入格式
第一行两个整数 N,M 表示数字的个数和要询问的次数;
接下来一行为 N 个数;
接下来 M行,每行都有两个整数 X,Y
输出格式
输出共 M 行,每行输出一个数。
数据范围
1≤N≤105
1≤M≤106
1≤X≤Y≤N
数列中的数字均不超过2^31−1
输入样例:
10 2
3 2 4 5 6 8 1 2 9 7
1 4
3 8
输出样例:
5
8
#include <bits/stdc++.h>
#define PII pair<int,int>
#define ll long long
using namespace std;
const int N = 100005;
int a[N],f[N][20],k[N];
int n,M;
void init(){
k[1] = 0,k[2] = 1;
for(int i = 3; i <= N; ++i){
k[i] = k[i / 2] + 1;
}
}
void ST_prework(){
for(int i = 1; i <= n; ++i)f[i][0] = a[i];
int t = log(n) / log(2) + 1;
for(int j = 1; j < t; ++j){
for(int i = 1; i <= n - (1 << j) + 1; ++i){
f[i][j] = max(f[i][j - 1],f[i + (1 << (j - 1))][j - 1]);
}
}
}
int calc(int l, int r){
int len = r - l + 1;
return max(f[l][k[len]],f[r - (1 << k[len]) + 1][k[len]]);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
init();
cin >> n >> M;
for(int i = 1; i <= n; ++i){
cin >> a[i];
}
ST_prework();
while(M--){
int x,y;
cin >> x >> y;
cout << calc(x,y) << '\n';
}
return 0;
}