这貌似是一个很神奇的新科技。

由乃救爷爷

给你一个长为 \(n\) 的序列,你需要回答 \(q\) 次询问

每次询问给出 \(l,r\),求 \(\max\limits_{i=l}^ra_i\)。

其中,\(n,m\le 2\times 10^7\)

看起来好像是一个比较模板的 RMQ,事实也确实如此。

不过一般解决 RMQ 的方法都无法在正确的时间复杂度内解决这个问题,例如线段树:预处理 \(O(n\log n)\),单次询问 \(O(\log n)\),无论预处理还是解决询问都会超时。再比如 ST 表:预处理 \(O(n\log n)\),单次询问 \(O(1)\),预处理也会超时。

这里就要讲一讲一个很强的东西了:基于整数状压的线性 RMQ。

前置:

  1. ST 表(必须会)
  2. 状态压缩(必须会)
  3. 分块(因为比较简单不强求)
  4. 单调栈(因为比较简单不强求)

算法介绍

首先,一个降低常数的办法是,先分块(假设块长为 \(B\)),记录每个块的最大值,然后把每个块内最大值当成一个元素,重新构成一个序列 \(b\),在这个序列上建立 ST 表。

对于一次询问 \(l,r\),假设 \(l\) 属于 \(p\) 块,\(r\) 属于 \(q\) 块。

  • 如果 \(p\ne q\):那么 \([l,r]\) 内的最大值即为 \(b_{p+1\sim q-1}\) 的最大值与剩余两边部分的最大值,求解的时间复杂度为 \(O(\frac{n}{B}\log{\frac{n}{B}}+B)\)。

  • 如果 \(p=q\):那么直接在块内暴力查找,时间复杂度 \(O(B)\)。

此时的时间复杂度足以通过本题,但依然没有到达线性的要求,我们进行进一步强化。

假设 \(B=\log n\),对序列 \(a\) 分块后,再建立 ST 表的时间复杂度为 \(O(\frac{n}{\log n}\log{\frac{n}{\log n}})\approx O(n)\)。然后我们在记录每个块内前后缀最大值(时间复杂度 \(O(n)\))。依然假设对于一次询问 \(l,r\),\(l\) 属于 \(p\) 块,\(r\) 属于 \(q\) 块。

  • 如果 \(p\ne q\):则询问可以拆成“后缀 + 大区间 + 前缀”的模式,可以 \(O(1)\) 求解。

  • 如果 \(p=q\):此时就比较难绷了,好像依然需要暴力查找。

首先有一个性质,就是 \(B\) 很小,不超过 \(30\)(因为 \(B=\log n\))。

假设我们现在确定了右边界 \(r\),但是未确定左边界 \(l\)。此时有哪些候选项可能成为 \([l,r]\) 区间内的最大值?显然,候选项有多个,假设分别为 \(p_1,p_2,\dots p_k\)。思考每个 \(p_i\) 都需要满足的条件。

显然,对于任意 \(i<j\),如果 \(a_i\le a_j\),则 \(a_i\) 一定不可能成为区间 \([l,r]\) 最大值。假设存在区间 \([l,r]\) 的最大值为 \(a_i\),那么显然可以用 \(a_j\) 代替 \(a_i\)。原因很简单:如果区间 \([l,r]\) 包含 \(i\),则其一定也包含 \(j\)。但是对于 \(i<j,a_i>a_j\)。\(a_j\) 依然可能成为区间最大值(可能存在 \(i<l\le j\) 的情况)。

我们维护一个单调栈,使得栈底到栈顶单调递减,每把一个数入栈,就进行一次状态压缩,为在栈中的数对应的位置为 \(1\),反之为 \(0\)。假设当前将 \(r\) 入栈,则把状态压缩后的结果储存在 \(val_r\) 中,\(val_r\) 中所有为 \(1\) 的位置即为可能成为 \([l,r]\) 最大值的候选项(\(l\le r\))。

对于查询 \([l,r]\) 中的最大值,我们取出 \(val_r\),此时小于 \(l\) 的位置的候选项均不合法,排除不合法候选项后,取出当前最靠后的一个 \(1\),即为区间 \([l,r]\) 的最大值,他满足其到 \(r\) 中的任意数都小于它,它到 \(l\) 中的任意数都不大于他。

因为要状态压缩,所有必须要求区间长度很小,而因为块长 \(B\le 30\),所以很适合对每个块进行一遍上述过程,预处理 \(O(n)\),查询 \(O(1)\)。

综上,对于任意情况,我们都可以做到趋近于线性求出区间最大值。还有一个小技巧是:将快长直接赋值 \(64\)。方便各种运算,时间复杂度也不会很劣,甚至跑的更快。

#include <iostream>
#include <cstdio>
using namespace std;
const int N=2e7+10,M=4e5+10;
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;
}
#define L(i) ((i-1)<<6|1)
#define R(i) (i<<6)
#define bel(i) (i+63>>6)
#define lowbit(i) (i&-i)
struct RMQ{
int n,f[21][M],pre[N],suf[N],stk[65],top,len,tot,logn[M],a[N];
unsigned long long val[N];
void init(){
len=64,tot=bel(n),logn[0]=-1;
for(int i=1;i<=tot;i++)logn[i]=logn[i>>1]+1;
for(int i=1;i<=tot;i++){
pre[L(i)]=a[L(i)],suf[R(i)]=a[R(i)];
for(int j=L(i)+1;j<=R(i);j++)pre[j]=max(pre[j-1],a[j]);
for(int j=R(i)-1;j>=L(i);j--)suf[j]=max(suf[j+1],a[j]);
f[0][i]=max(f[0][i],pre[R(i)]);
}
for(int i=1;i<=20;i++){
for(int j=1;j+(1<<i)-1<=tot;j++){
f[i][j]=max(f[i-1][j],f[i-1][j+(1<<(i-1))]);
}
}
for(int i=1;i<=tot;i++){
top=0;
for(int j=L(i);j<=R(i);j++){
if(j>L(i))val[j]=val[j-1];
while(top&&a[j]>=a[stk[top]])val[j]^=(1ull<<(stk[top--]-L(i)));
stk[++top]=j,val[j]|=(1ull<<(j-L(i)));
}
}
}
int ask(int l,int r){
int p=bel(l),q=bel(r);
if(p==q)return a[l+__builtin_ctzll(val[r]>>(l-L(p)))];
int ans=max(suf[l],pre[r]);
if(q-p>1){
int t=logn[q-p-1];
ans=max(ans,max(f[t][p+1],f[t][q-(1<<t)]));
}
return ans;
}
}st;
int n,m,s;
unsigned long long res;
int main(){
scanf("%d %d %d",&n,&m,&s);
srand(s);
for(int i=1;i<=n;i++)st.a[i]=read();
st.n=n;st.init();
while(m--){
int l=read()%n+1,r=read()%n+1;
if(l>r)swap(l,r);
res+=st.ask(l,r);
}
printf("%llu\n",res);
}

这种做法只有在序列长度 \(\ge 10^6\) 才会很快。

O(n)-O(1) RMQ的更多相关文章

  1. BZOJ 3489: A simple rmq problem

    3489: A simple rmq problem Time Limit: 40 Sec  Memory Limit: 600 MBSubmit: 1594  Solved: 520[Submit] ...

  2. UVA 11235Frequent values(RMQ)

    训练指南P198 题意:给出一个非降序排列的整数数组a1, a2…… an,你的任务是对于一系列询问(i,j),回答ai, ai+1 ……aj 中出现的次数最多的次数 这题不仅学到了rmq的应用还学到 ...

  3. 51nod1174(RMQ)

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1174 题意:中文题诶- 思路:RMQ模板题 关于RMQ: h ...

  4. 2016 ACM/ICPC Asia Regional Dalian Online 1008 Function 二分+RMQ

    Time Limit: 7000/3500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Submissio ...

  5. Gym 100646 F Tanks a Lot RMQ

    Problem F: Tanks a Lot Imagine you have a car with a very large gas tank - large enough to hold what ...

  6. (RMQ版)LCA注意要点

    inline int lca(int x,int y){ if(x>y) swap(x,y); ]][x]]<h[rmq[log[y-x+]][y-near[y-x+]+]])? rmq[ ...

  7. 洛谷P2412 查单词 [trie树 RMQ]

    题目背景 滚粗了的HansBug在收拾旧英语书,然而他发现了什么奇妙的东西. 题目描述 udp2.T3如果遇到相同的字符串,输出后面的 蒟蒻HansBug在一本英语书里面找到了一个单词表,包含N个单词 ...

  8. POJ3368Frequent values[RMQ 游程编码]

    Frequent values Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 17581   Accepted: 6346 ...

  9. [tem]RMQ(st)

    倍增思想 代码中有两个测试 #include <iostream> #include <cmath> using namespace std; const int N=1e5; ...

  10. 二维RMQ

    求二维ST表 ;k<=;k++) ;l<=;l++) ;i<=n;i++) ;j<=m;j++){ <<(l-)),m+),tx=min(n+,i+(<< ...

随机推荐

  1. Electron35-DeepSeek桌面端AI系统|vue3.5+electron+arco客户端ai模板

    2025跨平台ai实战electron35+vite6+arco仿DeepSeek/豆包ai流式打字聊天助手. electron-deepseek-chat:实战ai大模型对话,基于vue3.5+el ...

  2. ESP-IDF教程1 开发环境

    1.开发环境 对于 ESP32 系列芯片的开发环境主要有如下几种方式: ESP-IDF(Espressif IoT Development Framework) Arduino PlatformIO ...

  3. CF1370C题解

    本蒟蒻的第二篇题解,找题归功于教练 题目传送门 这道题目找好了规律很简单: 具体思路: 题目大意: 有一个正整数 nnn.两名玩家轮流操作.每次操作可以执行以下一种: 将 nnn 除以一个 nnn 的 ...

  4. 最新最完整的iphone开发ios开发证书/发布证书/推送证书/企业证书和描述文件制作方法与教程

    本文介绍最新的最全的如何制作 iOS 证书(开发证书.发布证书.推送证书,企业证书)和iphone配置描述文件,用于iphone和ipad开发构建 IOS App 应用.如果嫌麻烦,可以使用懒人工具a ...

  5. Linux各种服务配置开机自启

    一.Linux配置redis开机自启 (1)到redis配置文件中找到conf文件:vi redis.conf (2)daemonize no 修改为:daemonize yes (3)cd /etc ...

  6. 【UEFI】DXE阶段从概念到代码

    总述 DXE(Driver Execution Environment)阶段,是执行大部分系统初始化的阶段,也就是说是BIOS发挥作用,初始化整个主板的主战场.在这个阶段我们可以进行大量的驱动工作. ...

  7. C++11 Lambda表达式(匿名函数)详解

    使用STL时,往往会大量用到函数对象,为此要编写很多函数对象类.而有的函数对象类只用定义一个对象,而且这个对象也只使用一次,那编写这样一个函数对象就很浪费了.而且有时这定义函数对象类的地方和使用函数对 ...

  8. SSI注入

    .stm,.shtm和.shtml后缀文件中可以如此执行命令 <!--#exec cmd="ls"-->

  9. wireshark 抓包查看包得明文消息

    转载注明出处: 最近在进行一些网络消息得定位,发现可以用wireshark查看网络包得消息内容,特此记录 需要注意得是,需要将wireshark更新到最新得版本,如果是老版本有可能不支持. 使用tcp ...

  10. 让AI操作powershell会发生什么

    AI-win11-管理员 (中文) 具体项目:https://github.com/liluoyi666/AI-win11-Administrator.git 项目概述 本项目旨在让大语言模型(LLM ...