例题:https://www.acwing.com/problem/content/1272/
ST表类似于dp。
定义st[i][j]表示以i为起点,长度位2^j的一段区间,即[ i , i + 2^j - 1 ]。
而这个区间又可以被拆分为[i,i+2^(j-1)-1]+[ i + 2 ^ ( j - 1 ) , i + 2 ^ j - 1 ]这两个区间可以这样表示st[i][j-1]和st[i+(1<<(j-1))][j-1]
所以
st[i][j] = m(st[i][j-1],st[i+(1<<(j-1))][j-1]);
然后枚举长度和端点就可以以O(nlogn)转移状态了。特别的st[i][0]=arr[i]
查询:
设查询区间为[x,y]。将[x,y]分为两个带有重叠的子区间即[x,k1]+[k2,y]。其中k1>=k2。
怎样拆分呢?取log(y-x)向下取整,设为k,将[x,y]分为[x,x+2^k-1]+[y-2^k+1,y]。
我们可以做一下差即y-2^k+1-x-2^k+1=y-x-2^(k+1)+2。 结果一定是小于等于0的。
所以答案为:
m(st[x][k],st[y-(1<<k)+1][k])
例题code:
#include<bits/stdc++.h> using namespace std; const int N = 1E5 + 7; int st[N][40]; int n,m; int Log[N]; int arr[N]; void ST(){ // Log[1] = 0;//预处理log函数 // for(int i = 2;i <= n+1;i++) Log[i] = Log[i/2]+1; for(int i = 1;i <= n;i++) st[i][0] = arr[i]; for(int j = 1; (1<<j) <= n;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]); } } } int main(){ inint(); ios::sync_with_stdio(0); cin>>n>>m; for(int i=1;i<=n;i++) cin>>arr[i]; ST(); for(int i=1;i<=m;i++){ int x,y; cin>>x>>y; int k= Log[y-x+1]; cout<<max(st[x][k],st[y-(1<<k)+1][k])<<endl; } return 0; }
记录一个求O(n)求log的方法
log[i]=log[i/2]+1 当i刚好的i的倍数时,想当然log[i]=log[i/2]+1。
当i不是i的倍数时,i/2刚好舍去余数,向下取整。。。秒~~