RMQ

RMQ

一个高效的用于查询区间最大/最小值的方法,其需要 O ( n l o g n ) O(nlogn) O(nlogn)的时间复杂度进行预处理,之后对于每次的区间查询的复杂度为O(1)。

预处理

采用DP的思想(也可以说分治法). 设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示从 i i i开始的连续 2 j 2^j 2j个数的最大值,显然初始值 d p [ i ] [ 0 ] = a [ i ] dp[i][0]=a[i] dp[i][0]=a[i], 而对于每个 d p [ i ] [ j ] dp[i][j] dp[i][j]可以划分为区间 [ i , i + 2 j − 1 − 1 ] [i,i+2^{j-1}-1] [i,i+2j11] [ i + 2 j − 1 , i + 2 j − 1 ] [i+2^{j-1},i+2^j-1] [i+2j1,i+2j1],则 d p [ i ] [ j ] = m a x ( d p [ i ] [ j − 1 ] , d p [ i + ( 1 < < ( j − 1 ) ) ] [ j − 1 ] ) dp[i][j]=max(dp[i][j-1], dp[i+(1<<(j-1))][j-1]) dp[i][j]=max(dp[i][j1],dp[i+(1<<(j1))][j1])。这样只要先枚举 j j j的大小,然后再对每个 i i i进行计算即可。

查询

对于我们要查询的区间 [ l , r ] [l, r] [l,r],我们计算 k = l o g 2 ( r − l + 1 ) k=log_2(r-l+1) k=log2(rl+1), 然后将其分成两成两个区间,则答案为 d p [ l ] [ r ] = m a x ( d p [ l ] [ k ] , d p [ r − ( 1 < < k ) + 1 ] [ k ] ) dp[l][r]=max(dp[l][k],dp[r-(1<<k)+1][k]) dp[l][r]=max(dp[l][k],dp[r(1<<k)+1][k])

实战:

传送门:
https://ac.nowcoder.com/acm/contest/371/B

这是一道裸的RMQ,一开始看到果断用线段树维护查询,然后TLE了几发后发现查询的次数高达10^7, 一下子傻逼了(qaq)。重新分析问题可以知道,查询只能在 O ( 1 ) O(1) O(1)的时间里实现。所以这就需要RMQ大法了。然后一顿狂操代码之和AC了。(纪念一下)。

AC代码

//  小学生一发的刷题之路
//
//  维护编号的RMQ
//
//

#include 
#include 
#include 
#include 
#include 
#include 
#include                 //双向队列;
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const double PI=acos(-1.0);
const double eps=1e-8;
const int maxn=1e5+5;
const int maxm=1e3+5;
const ll mod=1e9+7;
const int INF=1e8;
template<class T>
inline void read(T &ret){       //快速输入模版;
    ret=0;
    int f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        ret=ret*10+c-'0';
        c=getchar();
    }
    ret*=f;
}
template <class T>
inline void out(T ret){     //快速输出模版;
    if(ret>9)
    {
        out(ret/10);
    }
    putchar(ret%10+'0');
}
struct node{
    int x,num,id;
    node(){};
//    bool operator<(const node& tmp)const{
//        if(tmp.x==x){
//            return tmp.num
//        }
//        return tmp.x
//    }
}a[maxn];
int n,m,seed,seedx,seedy;
int dp[maxn][20],maps[maxn];

bool cmp(node a,node b){
    if(a.x==b.x){
        return a.num>b.num;
    }
    return a.x<b.x;
}

inline int max(int x,int y){
    return x>y?x:y;
}

void init_RMQ(int n){
    //dp[i][j]从i开始到i+2^j的最大值;
    for(int i=1;i<=n;i++){
        dp[a[i].num][0]=a[i].id;
    }
    
    for(int j=1;(1<<j)<=n;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }
}

int query_RMQ(int l,int r){
    int k=0;
    while(1<<(k+1)<=r-l+1){
        k++;
    }
    return max(dp[l][k],dp[r-(1<<k)+1][k]);
}

int main()
{
    scanf("%d %d",&n,&m);
    scanf("%d %d %d",&seed,&seedx,&seedy);
    unsigned tmp=seed;
    for(int i=1;i<=n;i++){
        tmp^=tmp<<13;
        tmp^=tmp>>17;
        tmp^=tmp<<5;
        a[i].num=i;
        a[i].x=tmp%100;
    }
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++){
        a[i].id=i;
        maps[i]=a[i].num;
    }
    init_RMQ(n);
//    for(int i=1;i<=n;i++){
//        cout<
//    }
//    cout<
    
    unsigned tmpx=seedx,tmpy=seedy;
    int lastans=0,ans=0,L,R;
    for(int i=1;i<=m;i++){
        tmpx^=tmpx<<13;
        tmpx^=tmpx>>17;
        tmpx^=tmpx<<5;
        tmpy^=tmpy<<13;
        tmpy^=tmpy>>17;
        tmpy^=tmpy<<5;
        L=(tmpx^lastans)%n+1;
        R=(tmpy^lastans)%n+1;
        if(L>R) swap(L,R);
        lastans=query_RMQ(L,R);
        lastans=maps[lastans];
        ans^=lastans;
    }
    printf("%d\n",ans);
    return 0;
}

新的开始,每天都要快乐哈。
在这里插入图片描述

你可能感兴趣的:(算法类)