简单说就是求区间最值问题,若是简单的单次询问或者是区间长度很短的询问,可以用暴力的方法来实现,但面对大数据的时候此方法必然超时,这里介绍O(nlogn)预处理,O(1)查询的ST算法。
ST的高效在于O(nlogn)的时间预处理,O(1)的时间来查询。其主要思想就是将所求的区间化为两个小区间,这两个区间的长度正好是2的k次幂,总长度正好覆盖[l,r],得到的结果就是所求答案。
首先要清楚为什么是分成两段,而不是3段,4段,首先分成两段的话是好写的,比那些处理3段以及n段毕竟简单。在logn的时间复杂度下,以2为底或者以其他数为底的话,时间复杂度差距也不是那么明显,所以选择了分成两段,便于位运算以及其他方面的简便操作。
首先确定dp[i,j]表示以第i个数开始,长度为2^j的区间长度的区间最值。
易知dp[i][0] = s[i].即表示以i为起点,长度为1的区间的最值就是当前的数值。
然后可以考虑从当前一个点,以2的指数倍向一侧扩展,可以得到以当前点为起点,延申到边界的数组最值。
考虑O(nlogn)的时间复杂度,即以当前点为起始点,向右侧开始维护最值,从2^1开始,因为2^k一定是偶数,所以一定可以将2^k拆分为两个2^(k-1),即将区间[l,r]分为[l,l+2^(k-1)-1]跟[l+2^(k-1),r],将区间长度从小到大开始枚举,这样一定可以得到以当前起点一直到n的的每一个区间段内的最大值。
ll maxx[N][20],minn[N][20],n,s[N];
void init()
{
for(int i = 1; i <= n; i++)
maxx[i][0] = minn[i][0] = s[i];
int k = (int)(log(n*1.0)/log(2.0));
for(int i = 1; i <= k; i++)
for(int j = 1; j <= n; j++)
{
maxx[j][i] = maxx[j][i-1];
if(j+(1<<(i-1)) <= n) maxx[j][i] = max(maxx[j][i],maxx[j+(1<<(i-1))][i-1]);
minn[j][i] = minn[j][i-1];
if(j+(1<<(i-1)) <= n) minn[j][i] = min(minn[j][i],minn[j+(1<<(i-1))][i-1]);
}
}
对于当前区间[l,r],如何表示为以某个点为起点,长度是2^k的区间。
取k=[log2(j-i+1)],则有:RMQ(i, j) = max(dp[i,k], dp[j-2^k+1,k])。
考虑k的取值问题,k一定可以将整个区间都覆盖吗?
[l,r]分成2份,一部分是[l,i+2^(k-1)-1],一部分是[j-2^(k-1)+1,r],若是要完成覆盖,则应该有j-2^(k-1)+1 <= i+2^(k-1)-1,移项合并同类项得到2^k >= j-i+1,可以验证上述k取值是成立的。
分成两个区间后,在两个区间取最值就可以了。
ll rmq_min(ll l,ll r)
{
ll m = (ll)(log((r-l+1)*1.0)/log(2.0));
return min(minn[l][m],minn[r-(1<
/*
Look at the star
Look at the shine for U
*/
#include
#define ll long long
#define PII pair
#define sl(x) scanf("%lld",&x)
using namespace std;
const int N = 1e6+5;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1);
ll inv(ll b){if(b==1)return 1; return (mod-mod/b)*inv(mod%b)%mod;}
ll fpow(ll n,ll k){ll r=1;for(;k;k>>=1){if(k&1)r=r*n%mod;n=n*n%mod;}return r;}
ll maxx[N][20],minn[N][20],n,s[N];
void init()
{
for(int i = 1; i <= n; i++)
maxx[i][0] = minn[i][0] = s[i];
int k = (int)(log(n*1.0)/log(2.0));
for(int i = 1; i <= k; i++)
for(int j = 1; j <= n; j++)
{
maxx[j][i] = maxx[j][i-1];
if(j+(1<<(i-1)) <= n) maxx[j][i] = max(maxx[j][i],maxx[j+(1<<(i-1))][i-1]);
minn[j][i] = minn[j][i-1];
if(j+(1<<(i-1)) <= n) minn[j][i] = min(minn[j][i],minn[j+(1<<(i-1))][i-1]);
}
}
ll rmq_min(ll l,ll r)
{
ll m = (ll)(log((r-l+1)*1.0)/log(2.0));
return min(minn[l][m],minn[r-(1<