首页 > 其他分享 >RMQ

RMQ

时间:2024-02-22 23:44:10浏览次数:17  
标签:RMQ int 复杂度 small query block

区间最大最小值(RMQ)

st 表

利用 min,max区间合并是可重的,倍增预处理

时间复杂度 \(O(n \log n+ q)\)

空间复杂度 \(O(n\log n)\)

线段树

二进制划分区间

时间复杂度 \(O(n \log n)\)

method of four russians

建立笛卡尔树,求欧拉序,序列中相邻的两个元素绝对值为 1,分块RMQ,状压,然后可以 \(O(n)-O(1)\)。

然而位运算需要一定复杂度,有常数,跑起来甚至比st表慢。

看看就行。

// P3793 lxl 毒瘤题
#include<bits/stdc++.h>
using namespace std;
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;
}
const int N=20000001;
int n,m,s;
int a[N];
struct Node{
    int ls,rs;
    #define lc(p) t[p].ls
    #define rc(p) t[p].rs
}t[N];
int rt;
const int M=3076924+10;
int dfc,B,cntB,euler[N<<1],dep[N<<1],lg[N<<1];
int block[N<<1],L[M],R[M],dif[M],f[M][23],g[M][23];
int idx[N],c[1<<15];
int stk[N],tp;
void build_Cartesian(){
    stk[tp=1]=0;
    a[0] = 2e9;
    for(int i=1;i<=n;++i){
        while(a[stk[tp]]<a[i])tp--;
        int pos=stk[tp];
        lc(i) = rc(pos);
        rc(pos) = i;
        stk[++tp]=i;
    }
    rt=stk[2];
}
void get_Euler(int x,int Dep){
    euler[++dfc]=x;
    dep[dfc]=Dep;
    idx[x]=dfc;
    if(lc(x)){
        get_Euler(lc(x),Dep+1);
        euler[++dfc]=x;
        dep[dfc]=Dep;
    }
    if(rc(x)){
        get_Euler(rc(x),Dep+1);
        euler[++dfc]=x;
        dep[dfc]=Dep;
    }
}
void divide(){
    for(int i=1;i<=cntB;++i){
        L[i]=(i-1)*B+1;
        R[i]=min(i*B,dfc);
        for(int j=L[i];j<=R[i];++j)block[j]=i;
    }
}
void small_init(){
    for(int i=1;i<=cntB;i++){
        for(int j=L[i];j<=R[i];++j)
            if(j>L[i]&&dep[j-1]>dep[j])
                dif[i]|=(1<<j-L[i]-1);
    }
    for(int i=0;i<(1<<B-1);++i){
        int sum=0,mn=0;
        c[i]=0;
        for(int j=1;j<B;++j){
            sum+=(i>>(j-1)&1)?-1:1;
            if(mn>sum)mn=sum,c[i]=j;
        }
    }
}
void block_init(){
    for(int i=1;i<=cntB;i++){
        f[i][0]=2e9;
        for(int j=L[i];j<=R[i];++j){
            if(dep[j]<f[i][0]){
                f[i][0]=dep[j];
                g[i][0]=j;
            }
        }
    }
    for(int j=1;j<=lg[cntB];++j){
        for(int i=1,I=cntB-(1<<j)+1;i<=I;++i){
            int t=i+(1<<(j-1));
			if(f[i][j-1]<f[t][j-1]){
                f[i][j]=f[i][j-1];
                g[i][j]=g[i][j-1]; 
            }else{
                f[i][j]=f[t][j-1];
                g[i][j]=g[t][j-1];
            }
        }
    }
}
int small_query(int l,int r){
    int p=block[l],S=(dif[p]>>(l-L[p]))&((1<<r-l)-1);
    return c[S]+l;
}
int block_query(int l,int r){
    int k=log2(r-l+1);
    return f[l][k]<=f[r-(1<<k)+1][k]?g[l][k]:g[r-(1<<k)+1][k];
}
int query(int l,int r){
    if(l>r)swap(l,r);//确实有序数对,在欧拉序上不一定有序,因为欧拉序不满足二叉搜索树遍历
    if(block[l]==block[r])return small_query(l,r);
    else{
        int res1=small_query(l,R[block[l]]),res2=small_query(L[block[r]],r);
        int ans=(dep[res1]<dep[res2]?res1:res2);
        if(block[l]+1<=block[r]-1){
            int res3=block_query(block[l]+1,block[r]-1);
            if(dep[res3]<dep[ans])ans=res3;
        }
        return ans;
    }
}
void Read(int &a){
    #define gc getchar()
    char c=gc;a=0;
    while(c<'0'||c>'9')c=gc;
    while(c>='0'&&c<='9')a=a*10+c-'0',c=gc;
}
int main(){
//    freopen("in.in","r",stdin);
//    freopen("o.out","w",stdout);
    Read(n);Read(m);Read(s);
    srand(s);
    for(int i=1;i<=n;++i)a[i]=read();
    lg[0]=-1;
    for(int i=1;i<=n<<1;++i)lg[i]=lg[i>>1]+1;
    build_Cartesian();
    get_Euler(rt,1);
    B=ceil(log2(dfc)/2+0.1),cntB=(dfc-1)/B+1;
    divide();
    small_init();
    block_init();
    unsigned long long ans=0;
    for(int i=1;i<=m;++i){
        int l=read()%n+1,r=read()%n+1;
        if(l>r)swap(l,r);
        int tmp=query(idx[l],idx[r]);
        ans += a[euler[tmp]];
    }
    printf("%llu\n",ans);
    cerr<<(double)clock()/CLOCKS_PER_SEC<<"s"<<endl;
    return 0;
}

标签:RMQ,int,复杂度,small,query,block
From: https://www.cnblogs.com/life-of-a-libertine/p/18028441

相关文章

  • RMQ问题
    #include<bits/stdc++.h>usingnamespacestd;#defineintlonglongchar*p1,*p2,buf[100000];#definenc()(p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)intread(){intx=0,f=1;charch=nc();while(ch<48......
  • ST表 RMQ(区间最大/最小值查询)问题
    主要应用倍增思想预处理:O(nlogn)查询:O(1)f[i][j]是以i为起点,长度为2j的区间中的最大值(一个点一个单位长度,不是一条线段)区间终点:i+2j-1<=n区间长度的指数k=log2(r-l+1),只有当r-l+1为2n-1时是恰好分割,其他时候有重叠,但问题不大代码 #include<iostream>#include<cstring>#......
  • #P1042. 静态RMQ[ST表模板]
    题意是:给定一个长度为N的数列,和M次询问,求出每一次询问的区间内数字的最大值。ST表的基本功能是对区间进行查询,其核心使用的是倍增的思想f[i][k]:意思是从第i个数开始往后2^k个数f[i][k]=max(f[i][k-1],f[i+2^k-1][k-1])求【l,r】区间max(f[i][k],f[r-2^k+1][k])#define......
  • CF803G Periodic RMQ Problem
    题目描述给你一个序列\(a\)让你支持\(1\)\(l\)\(r\)\(x\)区间赋值\(2\)\(l\)\(r\)询问区间最小值我们觉得这个问题太水了,所以我们不会给你序列\(a\)而是给你序列一个长度为\(n\)的序列\(b\),把\(b\)复制粘贴\(k\)次就可以得到\(a\)\(n\le10^5,k\le10^4,q\le10......
  • RMQ模板
    #include<cstdio>#include<cstring>#include<algorithm>#include<cmath>usingnamespacestd;constintN=200010,M=18;intn,m;intw[N];intlg[N];intf[N][M];//查询区间最值//预处理f数组voidinit(){ lg[1]=0; for(inti=2;i&l......
  • P6109 [Ynoi2009] rprmq1 题解-猫树+Segment Tree Beats
    20231025P6109[Ynoi2009]rprmq1题解-猫树+SegmentTreeBeats不愧是学长出的题。。。让我更深刻地理解了猫树。Statement传送门有一个\(n\timesn\)的矩阵\(a\),初始全是\(0\),有\(m\)次修改操作和\(q\)次查询操作,先进行所有修改操作,然后进行所有查询操作。一次修......
  • RMQ
    P3865【模板】ST表利用倍增f[i][j]表示,范围[i,i+2^(j)-1]内的最大值是多少点击查看代码#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+10;intf[N][22];intmain(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); intn,m; cin>>n>>......
  • 分块+ST的RMQ
    期望\(O(n)-O(1)的RMQ\)#include<bits/stdc++.h>#defineintlonglong#defineF(i0,i1,i2)for(inti0=(i1);i0<=(i2);++i0)usingnamespacestd;inlineintrd(){ intx=0,f=0;charch=getchar(); while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar(......
  • RockerMq发送消息之顺序消息
    顺序消息        消息有序指的是可以按照消息的发送顺序来消费(FIFO)。RocketMQ可以严格的保证消息有序,可以分为分区有序或者全局有序。        顺序消费的原理解析,在默认的情况下消息发送会采取RoundRobin轮询方式把消息发送到不同的queue(分区队列);而消费消......
  • 基于Docker安装RockerMQ
    1、拉取RockerMQ镜像dockerpullapache/rocketmq2、创建namesrv服务mkdir-p/usr/local/rocketmq/data/namesrv/logs/usr/local/rocketmq/data/namesrv/store3、构建namesrv容器 dockerrun-d\--restart=always\--namermqnamesrv\--privileged=true\-p98......