用ST算法解决RMQ(区间最值问题)
在解决CF上的6E Exposition时,用到了RMQ+二分的方法。学习了用ST来快速解决RMQ问题,因此做一个小记
建表
用DP的方式来建ST。
dp[i][j]表示从第i个数起,往后2^j个数中的最大(或最小)值。如dp[1][3]则表示区间内第1个数到第8个数中的最值。
则dp[i][0]初始化为a[i],即第i个数本身。
而dp[i][j]则分成两部分,dp[i][j-1] 和 dp[i+(1<<(j-1))][j-1],最值由这两个部分的最值比较之后获得。于是得到状态转移方程
建表代码如下
//dp[i][j]表示第i个数起,往后2^j个数的区间内的最值
int dp_max[N][32];
int dp_min[N][32];
void max_ST(int n)
{
int i,j;
for (i=1;i<=n;++i)
{
dp_max[i][0]=a[i];
}
for (j=1;(1<<j)<=n;++j)
{
for (i=1;i+(1<<j)-1<=n;++i)
{
dp_max[i][j]=max(dp_max[i][j-1],dp_max[i+(1<<(j-1))][j-1]);
}
}
return;
}
void min_ST(int n)
{
int i,j;
for (i=1;i<=n;++i)
{
dp_min[i][0]=a[i];
}
for (j=1;(1<<j)<=n;++j)
{
for (i=1;i+(1<<j)-1<=n;++i)
{
dp_min[i][j]=min(dp_min[i][j-1],dp_min[i+(1<<(j-1))][j-1]);
}
}
return;
}
查询
若要查询i到j之间的最值RMQ(i,j),可取两段dp,使其恰好覆盖这个区间。
取k=log2 (j-i+1),则该区最值由dp[i][k]和dp[r-(1<<k)+1][k]比较后获得。
查询代码如下
int RMQ_min(int l,int r)
{
int k=0;
while ((1<<(k+1))<=r-l+1) //即 令k=log2(r-l+1)
++k;
return min(dp_min[l][k],dp_min[r-(1<<k)+1][k]);
}
int RMQ_max(int l,int r)
{
int k=0;
while ((1<<(k+1))<=r-l+1) //即 令k=log2(r-l+1)
++k;
return max(dp_max[l][k],dp_max[r-(1<<k)+1][k]);
}