2024.11.3 鮮花
淺談 RMQ
?? ??? II
-
?? ??? ?? ?? ?? ??? ??
-
在某個僻靜村莊胡同的破舊建筑里
-
?? ?? ???? ?? ??? ??
-
開門進去便會見到一個小劇場
-
?? ?? ???? ??? ??? ???
-
一個手藝不錯的人偶師演完戲離開的時候
-
???? ?? ?? ?? ?????
-
人偶們便會留在劇場的倉庫里
-
?? ?? ?? ?? ??? ??? ??
-
每當夜幕降臨 人們離開劇場之時
-
???? ?? ?? ?? ?????
-
人偶們便會小心翼翼地開始行動
-
"??? ?? ???, ??? ?????"
-
"今天也很辛苦了, 來開個派對吧"
-
?? ??? ?? ?? ?? ?? ???
-
然后在無人的舞臺上點亮閃爍的燈光
-
??? ?? ??? ??? ?? ?? ???
-
軍樂隊的士兵人偶咚咚地敲著鼓
-
?? ???? ??? ?? ??? ??? ???
-
其他的人偶也在隨著節奏起舞歌唱
-
??? ?? ??? ??? ?? ???
-
啦啦噠 興致勃勃的這里是場秘密舞會
-
???? ???? ?? ?? ?? ????
-
先暫時忘掉那些勞累的事來一起享受一下吧?
-
????? ?? ??? ??? ????
-
啦啦啦噠噠 歡迎來到這場秘密人偶劇
-
?? ??? ?? ?? ?? ?? ???
-
在這里放下束縛你的一切來盡情共舞吧
-
??? ?? ????? ??? ?? ???
-
沒有人會詢問你到底是什么樣的存在
-
??? ?? ?? ?? ??? ?? ???
-
就將你自己托付給那人偶的身軀和音樂吧
-
????? ?? ??? ??? ???
-
啦啦啦噠噠 一起歌唱吧 就在這秘密人偶劇
-
?? ??? ?? ??? ??? ?? ???
-
在某個僻靜村莊胡同里游蕩的小幽靈們
-
?? ?? ???? ?? ??? ???
-
那些無處可歸的亡靈們也在街上到處游蕩
-
?? ??? ?? ???? ??? ???
-
他們無法拋棄對生活的無盡向往
-
?? ?? ??? ?? ?? ?????
-
便都藏在了小劇場的人偶里
-
?? ?? ?? ?? ??? ??? ??
-
每當夜幕降臨 人們離開劇場之時
-
???? ?? ?? ?? ?????
-
人偶們便會小心翼翼地開始行動
-
"??? ?? ??? ??? ?? ? ??"
-
"只要盡情地跳舞玩耍就能夠忘記對生活的向往"
-
?? ??? ???? ?? ??? ??
-
于是每天晚上都會舉行他們自己的小慶典
-
??? ?? ??? ?? ??? ????
-
就算人偶的身體不好使 扭到腳便會摔倒
-
?? ??? ????? ??? ??? ???
-
但倘若能與他人共度時光 也會是很愉快的事啊
-
??? ?? ??? ??? ?? ???
-
啦啦噠 興致勃勃的這里是場秘密舞會
-
???? ???? ?? ?? ?? ????
-
先暫時忘掉那些勞累的事來一起享受一下吧?
-
????? ?? ??? ??? ????
-
啦啦啦噠噠 歡迎來到這場秘密人偶劇
-
?? ??? ?? ?? ?? ?? ???
-
在這里放下束縛你的一切來盡情共舞吧
-
??? ?? ????? ??? ?? ???
-
沒有人會詢問你到底是什么樣的存在
-
??? ?? ?? ?? ??? ?? ???
-
就將你自己托付給那人偶的身軀和音樂吧
-
????? ?? ??? ??? ????
-
啦啦啦噠噠 夜晚消逝 太陽又照常升起
-
??? ?? ??? ??? ?????
-
派對會落下帷幕 人偶也會逐漸停下來
-
??? ?? ?? ??? ????? ?????
-
但若能從痛苦的記憶中抹除一些的話
-
?? ??? ????? ???? ????
-
也很期待你明天晚上還會再來吧?
-
????? ?? ??? ??? ???
-
啦啦啦噠噠 一起歌唱吧 就在這秘密人偶劇

考慮優秀的復雜度算法。
四毛子:
大體分為四步:
-
建立笛卡爾樹,復雜度 \(O(n)\)。
-
維護一個以歐拉序為下標深度為值的數組,復雜度 \(O(n)\)。
考慮性質,發現相鄰兩個之間的差之多為一,問題變成了 \(\pm1\) RMQ,考慮維護。
-
將原序列分塊,塊長為 \(\log(n)\),對于每塊求出最值,前后綴最值,塊間用 ST 表維護,復雜度 \(O(n)\),于是我們可以 \(O(1)\) 查詢端點在不同塊之間的詢問。
-
考慮端點在同一塊的詢問,發現對于一個左端點固定的長度至多為 \(\log(n)\) 的序列,其至多有 \(\sum\limits_{i=1}^{\log(n)} 2^{i-1} \le n\) 種方案,所以暴力預處理出所有情況,預處理塊間差分,每次查表即可。復雜度 \(O(n)+O(1)\)
發現其實現難度和代碼常數過于巨大,于是有了一下簡單做法。
二毛子:
名字取自 P3793 由乃救爺爺 的一篇題解。
考慮直接去掉四毛子的前兩步,考慮維護塊間。
其實直接再上一個 ST 表就可以做到 \(O(n\log\log n)+O(1)\) 的,常數也不小,但是實現難度大大降低。
發現長度 \(\log n\) 一般不會超過 \(64\),考慮狀壓。
具體的,用單調棧維護最值,用 unsigned long long 壓棧內元素狀態,查詢時用位運算拆掉 \(<l\) 做 \(lowbit\) 即可。
復雜度依然是 \(O(n)+O(1)\),并且因為位運算和 \(n\) 的大小的原因,常數巨小。
然而還有一種暴力:
對于塊內的詢問直接暴力,發現其基本不會落在這個區間,也可以通過微調塊長來避免。
既然暴力就暴力到底,直接每 \(\sqrt{n}\) 分一塊,每次直接暴力即可,期望是 \(O(n)+O(1)\) 的,通過微調塊長依然不太會被卡,常數也很小。
放一下由乃救爺爺的代碼,均沒卡常:
暴力分塊 17.12 s
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
using llf=long double;
using ull=unsigned long long;
#define endl '\n'
#ifdef LOCAL
FILE *InFile=freopen("in_out/in.in","r",stdin),*OutFile=freopen("in_out/out.out","w",stdout);
#else
FILE *InFile=stdin,*OutFile=stdout;
#endif
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+3,L=4480;
int n,m,s,c[N],id[N],bg[L],ed[L],cma[L][L],pm[N],nm[N],bl,B; unsigned long long ans;
int main(){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
cin>>n>>m>>s; srand(s); B=max(2,int(sqrt(n)));
bg[++bl]=1; for(int i=1;i<=n;++i) c[i]=read(),((i%B==0)&&(ed[bl]=i-1,bg[++bl]=i)),id[i]=bl; ed[bl]=n;
for(int i=1;i<=bl;++i){
int ma=0; for(int j=bg[i];j<=ed[i];++j) ma=max(ma,c[j]),pm[j]=ma;
ma=0; for(int j=ed[i];j>=bg[i];--j) ma=max(ma,c[j]),nm[j]=ma;
cma[i][i]=ma;
}
for(int i=1;i<=bl;++i){
int ma=cma[i][i];
for(int j=i-1;j;--j) ma=max(ma,cma[j][j]),cma[j][i]=ma;
}
for(int i=1;i<=m;++i){
int l=read()%n+1,r=read()%n+1; if(l>r) swap(l,r);
if(id[l]==id[r]){
int ma=0; for(int i=l;i<=r;++i) ma=max(ma,c[i]);
ans+=ma;
}else ans+=max({cma[id[l]+1][id[r]-1],nm[l],pm[r]});
}
cout<<ans<<endl;
}
狀壓 11.24 s
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
using llf=long double;
using ull=unsigned long long;
#define endl '\n'
#ifdef LOCAL
FILE *InFile=freopen("in_out/in.in","r",stdin),*OutFile=freopen("in_out/out.out","w",stdout);
#else
FILE *InFile=stdin,*OutFile=stdout;
#endif
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+3,B=64,L=N/B+3;
#define id(p) (p+63>>6)
#define bl(i) (i-1<<6|1)
#define br(i) (i<<6)
int n,m,s,c[N],pm[N],nm[N],bln; unsigned long long ans,sta[N];
class St{
private: const static int LG=19; int o[LG+3][L];
public:
int &operator[](int p){return o[0][p];}
void In(int l){for(int i=1;i<=LG;++i) for(int j=1;j+(1<<i)-1<=l;++j) o[i][j]=max(o[i-1][j],o[i-1][j+(1<<i-1)]);}
int operator()(int l,int r){if(l>r) return 0; int k=__lg(r-l+1); return max(o[k][l],o[k][r-(1<<k)+1]);}
}st;
void In(){
int stk[N],*top;
for(int i=1;i<=bln;++i){
int ma=0; for(int j=bl(i);j<=br(i);++j) ma=max(ma,c[j]),pm[j]=ma;
ma=0; for(int j=br(i);j>=bl(i);--j) ma=max(ma,c[j]),nm[j]=ma;
st[i]=ma; top=stk;
for(int j=bl(i);j<=br(i);++j){
if(j!=bl(i)) sta[j]=sta[j-1];
while(top!=stk&&c[*top]<=c[j]) sta[j]^=1ull<<(*top--)-bl(i);
*++top=j,sta[j]|=1ull<<j-bl(i);
}
} st.In(bln);
}
int Ma(int l,int r){
int il=id(l),ir=id(r);
if(il==ir) return c[l+__builtin_ctzll(sta[r]>>l-bl(il))];
else return max({st(il+1,ir-1),nm[l],pm[r]});
}
int main(){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
cin>>n>>m>>s; srand(s);
for(int i=1;i<=n;++i) c[i]=read(); bln=id(n),In();
for(int i=1;i<=m;++i){int l=read()%n+1,r=read()%n+1; if(l>r) swap(l,r); ans+=Ma(l,r);}
cout<<ans;
}
Kaguya 和 critno 他們好像搞了個支持修改的,但是做法過于優秀,感覺只有 YY 的作用了。
P


本文來自博客園,作者:xrlong,轉載請注明原文鏈接:http://www.rzrgm.cn/xrlong/p/18522436
版權聲明:本作品采用 「署名-非商業性使用-相同方式共享 4.0 國際」許可協議(CC BY-NC-SA 4.0) 進行許可。

浙公網安備 33010602011771號