区间最大最小值(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