严格根号带修 RMQ
其实很简单,把之前随机数据的解法中维护块内数据的数据结构换成约束 RMQ,这样子复杂度 严格 单点修改 \(O(\sqrt n)\),区间查询 \(O(1)\),线性空间。
唯一的问题是常数太大了,有 \(4\) 倍常数。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2000;
const int MAXM = 20;
const int warma = 1000;
int a[1000001];
int INPUT[MAXN];
int cnt;
struct RMQ {
int mx;
int N, A[MAXN];
int blockSize;
int S[MAXN][MAXM], Pow[MAXM], Log[MAXN];
int Belong[MAXN], Pos[MAXN];
int Pre[MAXN], Sub[MAXN];
int F[MAXN];
void buildST() {
int cur = 0, id = 1;
Pos[0] = -1;
for (int i = 1; i <= N; ++i) {
S[id][0] = std::max(S[id][0], A[i]);
Belong[i] = id;
if (Belong[i - 1] != Belong[i])
Pos[i] = 0;
else
Pos[i] = Pos[i - 1] + 1;
if (++cur == blockSize) {
cur = 0;
++id;
}
}
if (N % blockSize == 0) --id;
Pow[0] = 1;
for (int i = 1; i < MAXM; ++i) Pow[i] = Pow[i - 1] * 2;
for (int i = 2; i <= id; ++i) Log[i] = Log[i / 2] + 1;
for (int i = 1; i <= Log[id]; ++i) {
for (int j = 1; j + Pow[i] - 1 <= id; ++j) {
S[j][i] = std::max(S[j][i - 1], S[j + Pow[i - 1]][i - 1]);
}
}
}
void buildSubPre() {
for (int i = 1; i <= N; ++i) {
if (Belong[i] != Belong[i - 1])
Pre[i] = A[i];
else
Pre[i] = std::max(Pre[i - 1], A[i]);
}
for (int i = N; i >= 1; --i) {
if (Belong[i] != Belong[i + 1])
Sub[i] = A[i];
else
Sub[i] = std::max(Sub[i + 1], A[i]);
}
}
void buildBlock() {
static int S[MAXN], top;
for (int i = 1; i <= N; ++i) {
if (Belong[i] != Belong[i - 1])
top = 0;
else
F[i] = F[i - 1];
while (top > 0 && A[S[top]] <= A[i]) F[i] &= ~(1 << Pos[S[top--]]);
S[++top] = i;
F[i] |= (1 << Pos[i]);
}
}
void init() {
for(int i=1;i<=1001;i++){
A[i]=Log[i]=Belong[i]=Pos[i]=Pre[i]=Sub[i]=F[i]=0;
}
for(int i=1;i<=20;i++){
Pow[i]=0;
}
for(int i=1;i<=101;i++){
for(int j=1;j<=11;j++){
S[i][j]=0;
}
}
mx=0;
for (int i = 1; i <= N; ++i) A[i]=INPUT[i],mx=max(mx,A[i]);
blockSize = max( (int)(log2(N) * 1.5),1);
buildST();
buildSubPre();
buildBlock();
}
int queryMax(int l, int r) {
int bl = Belong[l], br = Belong[r];
if (bl != br) {
int ans1 = 0;
if (br - bl > 1) {
int p = Log[br - bl - 1];
ans1 = std::max(S[bl + 1][p], S[br - Pow[p]][p]);
}
int ans2 = std::max(Sub[l], Pre[r]);
return std::max(ans1, ans2);
} else {
return A[l + __builtin_ctz(F[r] >> Pos[l])];
}
}
} R[MAXN];
void init(){
for(int i=1;i<=cnt;i++){
INPUT[i]=R[i].mx;
}
R[cnt+1].N=cnt;
R[cnt+1].init();
}
int query(int l,int r){
int bl=l/warma+1;
l%=warma;
if(l==0){
bl--;
l+=warma;
}
int br=r/warma+1;
r%=warma;
if(r==0){
br--;
r+=warma;
}
if(bl==br){
return R[bl].queryMax(l,r);
}
else if(bl+1==br){
return max(R[bl].queryMax(l,R[bl].N),R[br].queryMax(1,r));
}
else{
return max(max(R[bl].queryMax(l,R[bl].N),R[br].queryMax(1,r)),R[cnt+1].queryMax(bl+1,br-1));
}
}
void add(int pos,int val){
a[pos]+=val;
int bpos=pos/warma+1;
if(pos%warma==0){
bpos--;
}
int len=0;
for(int i=(bpos-1)*warma+1;i<=min(n,bpos*warma);i++){
len++;
INPUT[i-(bpos-1)*warma]=a[i];
}
R[bpos].N=len;
R[bpos].init();
init();
}
int n,m;
int last=1;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
if(i%warma==0){
R[++cnt].N=(i-last+1);
for(int j=last;j<=i;j++){
INPUT[j-last+1]=a[j];
}
R[cnt].init();
last=i+1;
}
}
if(last<=n){
R[++cnt].N=(n-last+1);
for(int j=last;j<=n;j++){
INPUT[j-last+1]=a[j];
}
R[cnt].init();
last=n+1;
}
init();
while(m--){
int l,r;
cin>>l>>r;
cout<<query(l,r)<<'\n';
}
/*
scanf("%d%d", &R.N, &M);
R.init();
for (int i = 0, l, r; i < M; ++i) {
scanf("%d%d", &l, &r);
printf("%d\n", R.queryMax(l, r));
}
*/
return 0;
}