O(n)-O(1) 线性 RMQ 学习笔记
O(n)-O(1) 线性 RMQ 学习笔记
而我们正常 ST 表处理 RMQ 只能做到
用四毛子算法可以做到
四毛子算法:对原序列分块,块长为
但四毛子算法还是不够优秀。
那么下面我们介绍线性 RMQ 算法(也叫标准 RMQ 算法)。
1.建笛卡尔树
我们对原序列建出笛卡尔树,至于是大根还是小根依据具体题目。
我们可以使用单调栈
笛卡尔树的性质:区间
于是区间最值转化为了求
2.欧拉序转化 LCA
对笛卡尔树建出欧拉序。
欧拉序:初始一个空的序列,在
欧拉序的性质:设
这是一个
以上的主要思想就是把一般 RMQ 问题转化为
3. RMQ 问题
做法 1
设
我们采用 分块 + ST 表 + 散块状压 来解决这个问题,其中 分块 + ST 表 就是四毛子算法的思想。
取块长
对于每个块之间做 ST 表,时间复杂度
而对于散点,我们并不能预处理每个块的前缀和后缀最值,因为可能出现左端点和右端点在同一个块里的情况。
我们对每个块的每个前缀以原数组的值维护一个单调栈,单调栈可以用
做法 2
考虑 CSP-S2021第一轮 中的做法。
还是分块,块长我们取
而对于块内的 RMQ,一个块内的差分数组只有
例题
直接用朴素的四毛子是过不了的。
我们可以考虑线性 RMQ,但这里提供一种不用线性 RMQ 的做法,常数较优秀。
考虑用一种另类的四毛子算法,对于散点用前缀和后缀最值处理。
然而对于两端点在同一个块内的情况,因为数据随机,所以两端点在一个块内的概率是
总的复杂度就是
建议取
这道题的代码:
#include<bits/stdc++.h> using namespace std; namespace IO{ const int sz=1<<22; char a[sz+5],b[sz+5],*p1=a,*p2=a,*t=b,p[105]; inline char gc(){ return p1==p2?(p2=(p1=a)+fread(a,1,sz,stdin),p1==p2?EOF:*p1++):*p1++; } template<class T> void read(T& x){ x=0; char c=gc(),fushu=0; for(;c<'0'||c>'9';c=gc())if(c=='-')fushu=1; for(;c>='0'&&c<='9';c=gc()) x=x*10+(c-'0'); if(fushu)x=-x; } inline void flush(){fwrite(b,1,t-b,stdout),t=b; } inline void pc(char x){*t++=x; if(t-b==sz) flush(); } template<class T> void write(T x,char c='\n'){ if(x<0) pc('-'), x=-x; if(x==0) pc('0'); int t=0; for(;x;x/=10) p[++t]=x%10+'0'; for(;t;--t) pc(p[t]); pc(c); } struct F{~F(){flush();}}f; }; #define fo(i,l,r) for(int i=(l);i<=(r);++i) #define fu(i,l,r) for(int i=(l);i<(r);++i) #define fd(i,r,l) for(int i=(r);i>=(l);--i) #define IOS ios::sync_with_stdio(0); cin.tie(0) #define filein(x) freopen(x,"r",stdin) #define fileout(x) freopen(x,"w",stdout) #define usefile(x) freopen(x ".in","r",stdin); freopen(x ".out","w",stdout) #define ll long long #define ld long double #define ull unsigned long long //using IO::read; //using IO::write; 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=2e7+5; int n,m,SEED; ull ANS; const int B=4472; int a[N],b[N],c[N],ans[13][N/B+2],lg[B+10]; signed main(){ // usefile("rmq"); cin>>n>>m>>SEED; srand(SEED); fo(i,1,n){ a[i]=read(); int t=i/B; if(a[i]>ans[0][t])ans[0][t]=a[i]; b[i]=a[i]; if((i-1)/B==i/B)b[i]=max(b[i-1],b[i]); } fd(i,n,1){ c[i]=a[i]; if((i+1)/B==i/B)c[i]=max(c[i+1],c[i]); } fo(i,2,n/B+1)lg[i]=lg[i>>1]+1; fo(j,1,12)fo(i,0,n/B-(1<<j)+1)ans[j][i]=max(ans[j-1][i],ans[j-1][i+(1<<j-1)]); fo(i,1,m){ int l=read()%n+1,r=read()%n+1; if(l>r)swap(l,r); int as=0; if(l/B==r/B){ fo(j,l,r)as=max(as,a[j]); } else{ int L=l/B+1,R=r/B-1; int len=R-L+1; if(len>0)as=max(ans[lg[len]][L],ans[lg[len]][R-(1<<lg[len])+1]); as=max(as,max(c[l],b[r])); } ANS+=as; } cout<<ANS; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示