借助倍增和动态规划可以实现O(1)的时间复杂度的查询
预处理: ①区间DP 转移方程 f[i][j] = min(MAX同理)(f[i][j - 1],f[i ][j - 1]) f[i][j]表示从i位置开始的后2^j个数中的最大值
用f[i][j]表示从j到j 2i-1的最小值(长度显然为2i)。 任意一段的最小值显然等于min(前半段最小值,后半段最小值)。 那么f[i][j]如何用其他状态来继承呢? j到j 2i-1的长度为2i,那么一半的长度就等于2^(i-1)。 那么前半段的状态表示为f[i-1][j]。 后半段的长度也为2(i-1),起始位置为j 2(i-1)。 那么后半段的状态表示为f[i-1][j 2^(i-1)]。
②不过区间在增加时,每次并不是增加一个长度,而是基于倍增思想,用二进制右移,每次增加2^i个长度 ,最多增加logn次
这样预处理了所有2的幂次的小区间的最值
查询: ③对于每个区间,分成两段长度为的区间,再取个最值(这里的两个区间是可以有交集的,因为重复区间并不影响最值)
比如3,4,6,5,3一种分成3,4,6和6,5,3,另一种分成3,4,6和5,3,最大值都是6,没影响。
首先明确 2^log(a)>a/2 这个很简单,因为log(a)表示小于等于a的2的最大几次方。比如说log(4)=2,log(5)=2,log(6)=2,log(7)=2,log(8)=3,log(9)=3……. 那么我们要查询x到y的最小值。设len=y-x 1,t=log(len),根据上面的定理:2t>len/2,从位置上来说,x 2t越过了x到y的中间! 因为位置过了一半,所以x到y的最小值可以表示为min(从x往后2t的最小值,从y往前2t的最小值),前面的状态表示为f[t][x] 设后面(从y往前2t的最小值)的初始位置是k,那么k 2t-1=y,所以k=y-2t 1,所以后面的状态表示为f[t][y-2t 1] 所以x到y的最小值表示为f(f[t][x],f[t][y-2^t 1]),所以查询时间复杂度是O(1)
④所以O(nlogn)预处理,O(1)查询最值 但不支持修改 预处理时间复杂度O(nlogn),查询时间O(1)。
代码语言:javascript复制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 ST_query(int l, int r) {
int k = log(r - l 1) / log(2);
return max(f[l][k], f[r - (1<<k) 1][k]);
}