题解 | 信息学奥赛一本通 - 数列区间最大值

数列区间最大值

https://ac.nowcoder.com/acm/contest/966/A

本题是经典的静态序列区间最大值问题 (RMQ),可以用ST表等数据结构解决

其中ST表较为优秀,它只需要Onlogn的预处理时间,查询为O1

我们令st[i][j]表示序列的第i个元素到中的最大值,然后从1到logn遍历j,预处理所有的st

其中,这是一个倍增的过程

那么接下来对于每个询问区间,都可以有两个st表中存下来的区间合并来求出最值。

#include<iostream>
#include<cstdio>
using namespace std; 
const int maxn=1e5+10;
int n,m;
const int ch_top=4e7;
char ch[ch_top],*now_r=ch-1,*now_w=ch-1;
int mx[maxn][22];
int lg[maxn];
int max(int a,int b){return a>b?a:b;}
inline int query(int l,int r)
{
    int k=lg[r-l+1];
    return max(mx[l][k],mx[r-(1<<k)+1][k]);
}
int main()
{
    lg[0]=-1;for(int i=1;i<maxn;i++)lg[i]=lg[i>>1]+1;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&mx[i][0]);
    for(int j=1;j<=22;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
    while(m--){int l,r;scanf("%d%d",&l,&r);printf("%d\n",query(l,r));}
}

另外,区间RMQ有O(n)-O(1)的解法,为笛卡尔树或者分块st表。

//笛卡尔树
//题目链接 https://www.luogu.org/problem/P3793
#include<bits/stdc++.h>

const int maxn=2e7+10;
int n,m,a[maxn],ls[maxn],rs[maxn],stk[maxn],tp=0,rt,RAND;

namespace GenHelper
{
    unsigned z1,z2,z3,z4,b;
    unsigned rand_()
    {
    b=((z1<<6)^z1)>>13;
    z1=((z1&4294967294U)<<18)^b;
    b=((z2<<2)^z2)>>27;
    z2=((z2&4294967288U)<<2)^b;
    b=((z3<<13)^z3)>>21;
    z3=((z3&4294967280U)<<7)^b;
    b=((z4<<3)^z4)>>12;
    z4=((z4&4294967168U)<<13)^b;
    return (z1^z2^z3^z4);
    }
}
void srand(unsigned x)
{using namespace GenHelper;
z1=x; z2=(~x)^0x233333333U; z3=x^0x1234598766U; z4=(~x)+51;}
int read()
{
    using namespace GenHelper;
    int a=rand_()&32767;
    int b=rand_()&32767;
    return a*32768+b;
}

inline int que(int l,int r){
    int x=stk[1];
    while(1){
        if(l<=x&&x<=r) return a[x];
        if(x>r)x=ls[x];
        else x=rs[x];
    }
}

inline void Swap(int &l,int &r){
    l^=r;r^=l,l^=r;
}

int main(){
    scanf("%d%d%d",&n,&m,&RAND);srand(RAND);
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++){
        while(tp&&a[stk[tp]]<a[i]) ls[i]=stk[tp--];
        rs[stk[tp]]=i;stk[++tp]=i;
    }
    unsigned long long ans=0;
    while(m--){
        int l=read()%n+1,r=read()%n+1;
        if(l>r) Swap(l,r);
        ans+=que(l,r);
    }
    return printf("%llu\n",ans),0;
}
// 分块ST表
#include<bits/stdc++.h>

namespace GenHelper{
    unsigned z1,z2,z3,z4,b;
    unsigned rand_()
    {
    b=((z1<<6)^z1)>>13;
    z1=((z1&4294967294U)<<18)^b;
    b=((z2<<2)^z2)>>27;
    z2=((z2&4294967288U)<<2)^b;
    b=((z3<<13)^z3)>>21;
    z3=((z3&4294967280U)<<7)^b;
    b=((z4<<3)^z4)>>12;
    z4=((z4&4294967168U)<<13)^b;
    return (z1^z2^z3^z4);
    }
}
inline void srand(unsigned x){using namespace GenHelper;z1=x; z2=(~x)^0x233333333U; z3=x^0x1234598766U; z4=(~x)+51;}
inline int read(){
    using namespace GenHelper;
    int a=rand_()&32767;
    int b=rand_()&32767;
    return a*32768+b;
}

const int maxn=2e7+10;
const int SIZ=4750,NUM=maxn/SIZ+10;
int n,m,s[maxn];
int st[15][NUM],lg2[NUM];
unsigned long long ans;

inline void Swap(register int &l,register int &r){
    int t=l;
    l=r;r=t;
}
inline int Min(register int a,register int b){
    return a<b?a:b;
}

inline int Max(register int a,register int b){
    return a>b?a:b;
}

int lmax[maxn],rmax[maxn];
int l[NUM],r[NUM],pos[maxn];
unsigned RAND;

inline int que(register int l,register int r){
    if(l>=r) return 0;
    int t=lg2[r-l];
    return Max(st[t][l],st[t][r-(1<<t)]);
}


int main(){
    scanf("%d%d%u",&n,&m,&RAND);srand(RAND);
    for(int i=1;i<=n;i++) s[i]=read(),pos[i]=i/SIZ+1;
    const int B=pos[n];
    lg2[0]=-1;
    for(register int i=1;i<=B;++i)lg2[i]=lg2[i>>1]+1;
    for(register int i=1;i<=B;i++){
        l[i]=(i-1)*SIZ;r[i]=l[i]+SIZ-1;
    }
    l[1]=1,r[B]=n;
    for(register int i=1,now=1,lst=0;i<=n;i++){
        lmax[i]=lst=Max(s[i],lst);
        if(i>=r[now]) st[0][now]=lmax[i],lst=0,++now;
    }
    for(register int i=n,now=B,lst=0;i>=0;i--){
        rmax[i]=lst=Max(s[i],lst);
        if(i<=l[now]) lst=0,now--;
    }
    for(register int i=1,pw=1;i<=14;i++,pw<<=1){
        for(register int j=1;j<=B;j++)
            st[i][j]=Max(st[i-1][j],st[i-1][Min(j+pw,B)]);
    }
    while(m--){
        register int l=read()%n+1,r=read()%n+1;
        if(l>r) Swap(l,r);
        register int pl=pos[l],pr=pos[r];
        if(pl^pr) ans+=Max(Max(rmax[l],lmax[r]),que(pl+1,pr));
        else {
            register int res=0;
            for(register int i=l;i<=r;i++)
                res=Max(res,s[i]);
            ans+=res;
        }
    }
    printf("%llu\n",ans);
    return 0;
}
全部评论

相关推荐

头像
04-26 15:05
已编辑
腾讯_后端开发
小红书 iOS社区技术 年薪52w+包三餐大小周
点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务