浅谈RMQ

  • RMQ是一类求区间极值的问题
  • 有一种 \(O\left(nlogn\right)\) 的解法,用倍增实现

倍增算法 变量的定义

  • \(A_i\) : 原数组
  • \(f_{i,j}\) : 以 \(i\) 为起点(包括 \(i\)),向右 \(2^j\) 个位置的极值,以最大值为例

倍增算法 实现

【预处理】

  • \(f_{i,0}=A_i\)
  • \(f_{i,j}=max\left(f_{i,j-1},f_{i+2^{j-1},j-1}\right)\ (j>0)\)
  • 时间复杂度 \(O\left(nlogn\right)\)
for (int j=1,R=log2(n);j<=R;j++)
    for (int i=1;i+(1<<j)-1<=n;i++)
        f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);

【询问】

  • \(x=log_2(r-l+1)\),\(max(f_{l,x},f_{r-2^x+1,x})\)
  • 时间复杂度 \(O(1)\)
int Query(int l,int r){
    int x=log2(r-l+1);
    return max(f[l][x],f[r-(1<<x)+1][x]);
}

当然还有线段树的做法,就是询问时间复杂度是 \(O(logn)\),还能够支持修改

线段树 变量的定义

  • \(A_i\) 原数组
  • \(tr_i\)\(i\) 个节点

线段树 实现

【预处理 建树】

void build(int u,int l,int r){
    if (l==r){tr[u]=A[l];return;}
    int mid=l+r>>1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
    tr[u]=max(tr[u<<1],tr[u<<1|1]);
}

【询问】

int Query(int u,int l,int r,int lx,int rx){
    if (l==lx&&r==rx) return tr[u];
    int mid=l+r>>1;
    if (rx<=mid) return Query(u<<1,l,mid,lx,rx); else
    if (mid<lx) return Query(u<<1|1,mid+1,r,lx,rx); else
        return max(Query(u<<1,l,mid,lx,mid),Query(u<<1|1,mid+1,r,mid+1,rx));
}

【修改】

void modify(int u,int l,int r,int pos,int k){
    if (l==r) tr[l]=k;
    int mid=l+r>>1;
    if (pos<=mid) modify(u<<1,l,mid,pos,k); else
        modify(u<<1|1,mid+1,r,pos,k);
    tr[u]=max(tr[u<<1],tr[u<<1|1]);
}
posted @ 2018-03-19 19:06 xay5421 阅读(...) 评论(...) 编辑 收藏