RMQ 解决区间查询问题

 线段树写法不管,比较灵活。这里主要讨论DP实现。

其实单纯说RMQ解决的是区间最值查询是不准确的,只要满足一个区间的信息可以从它的覆盖区间获得(即[L,R]<=[L,r],[l,R] (l<=r) ,允许两个子区间重合)即可使用。重合不影响最值判断,所以最值查询是可以用RMQ的,其次如同区间gcd,重合区间也是不影响求值的。一样可以用RMQ。

下面用RMQ的实现来解释上述结论:

dp[i][j]表示以i起始,长度为2^j的线段信息。它可以划分为dp[i][j-1],dp[i+2^(j-1)][j-1],所以状态转移就出来,dp[i][j]=某种计算(dp[i][j-1],dp[i+2^(j-1)][j-1];

查询的话,如果像线段树那样用递归分解区间求解,复杂度log级,因为是严格分离区间,所以不存在之前说的重合影响(所以线段树适用范围大啊)。而DP实现要求在O(1)时间给出答案,所以我们可以把查询区间分解为两个尽可能大的子区间(允许覆盖),这有个很简单的写法,对于区间L,R的查询,求一下k=log2(R-L+1),两个子区间为dp[L][k],dp[R-2^k+1][k];

以区间gcd为例:

LL lg2(LL p)//计算log2(n)
{
    return (LL)(log(p) / log(2));
}
const LL len = 100005;//数据长度
const LL bitlen = 25;//需要的位数
LL n;
LL dp[len][bitlen];//dp数组
LL bit[bitlen];//预处理2^n
void initRMQ()//初始化
{
    for (LL j = 1; bit[j] < n; j ++)
        for (LL i = 0; i+bit[j] <= n; i++)
            dp[i][j] = gcd(dp[i][j - 1], dp[i + bit[j - 1]][j - 1]);
}
LL query(LL l, LL r)
{
    LL mig = lg2(r - l + 1.0);
    return gcd(dp[l][mig], dp[r - bit[mig] + 1][mig]);
}

对于需要增删改的操作,显然是线段树比较合适,对于有大量查询操作的题目,RMQ可以有效降低时间(最近遇到的这类题目对时间空间要求都很高,写代码写丑一点就挂了。提醒自己多注意)。

 

posted @ 2017-07-12 16:56  Luke_Ye  阅读(586)  评论(0编辑  收藏  举报