回滚莫队学习笔记
回滚莫队,用来处理增加好维护,删除不好维护的情况
- 参考自:oiwiki回滚莫队
- 算法流程:考虑按值域分块,对每个询问\([l,r]\),以\(l\)所在的块为第一关键字,\(r\)为第二关键字排序
- 若\([l,r]\)在同一块内,暴力即可
- 否则\(:\)
- 若上一个询问\(l\),所在的块和这个不同,暴力的重构即可,这个过程最多只有\(O(\sqrt n)\)次,每次最多\(O(n)\),所以\(O(n \sqrt n)\)
- 否则我们将\(l,r\)暴力拓展,回答询问,并在回答询问后将\(l\)回滚到其所在的块的最右段\(+1\),那么撤销和拓展的时间复杂度都是\(O(\sqrt n)\),\(m\)次询问,总共就是\(O(m \sqrt n)\)
- 所以总时间复杂度就是\(O(n \sqrt n + m \sqrt n)\)
例题:AT1219 歴史の研究
- 模板题没什么好说的
- 代码还是挺好写的
/*AT1219 歴史の研究*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int read(){
char c = getchar();
int x = 0;
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48,c = getchar();
return x;
}
const int _ = 2e5 + 7;
int b[_],L[_],R[_];
struct Query{
int l,r,id;
bool operator < (const Query &A)const{
if(b[l] == b[A.l]) return r < A.r;
return b[l] < b[A.l];
}
}Q[_];
ll ans[_];int a[_],tmp[_],rk[_];
int cnt[_];
int n,m;
int main(){
n = read(),m = read();
for(int i = 1; i <= n; ++i) tmp[i] = read(),a[i] = tmp[i];
sort(tmp+1,tmp+n+1) ;
int k = unique(tmp + 1,tmp + n + 1) - tmp - 1;
for(int i = 1; i <= n; ++i) rk[i] = lower_bound(tmp + 1,tmp + k + 1,a[i]) - tmp;
int t = sqrt(n);
for(int i = 1; i <= t; ++i){
L[i] = R[i-1] + 1;
R[i] = L[i] + t - 1;
}
if(R[t] != n){
t++;L[t] = R[t-1] + 1;R[t] = n;
}
for(int i = 1; i <= t; ++i)
for(int j = L[i]; j <= R[i]; ++j) b[j] = i;
for(int i = 1; i <= m; ++i){
Q[i].l = read(),Q[i].r = read();
Q[i].id = i;
}
sort(Q+1,Q+m+1);memset(tmp,0,sizeof(tmp));
int tp = 1;
for(int i = 1; i <= t; ++i){
int l = R[i],r = R[i] + 1;
memset(cnt,0,sizeof(int) * (k + 1));
ll v = 0;
while(b[Q[tp].l] == i && tp <= m){
int ql = Q[tp].l,qr = Q[tp].r;
if(b[ql] == b[qr]){
ll ret = 0;
for(int j = ql; j <= qr; ++j){
tmp[rk[j]]++;
ret = max(ret,1ll * tmp[rk[j]] * a[j]);
}
ans[Q[tp].id] = ret;
for(int j = ql; j <= qr; ++j) tmp[rk[j]] = 0;
}
else{
while(r <= qr){
cnt[rk[r]]++;
v = max(v,1ll * cnt[rk[r]] * a[r]);
r++;
}
ll ret = 0;
l = R[i];
while(l >= ql){
cnt[rk[l]]++;
ret = max(ret,1ll * cnt[rk[l]] * a[l]);
l--;
}
ans[Q[tp].id] = max(ret,v);
for(int j = ql; j <= R[i]; ++j) cnt[rk[j]]--;
}
tp++;
}
}
for(int i = 1; i <= m; ++i) printf("%lld\n",ans[i]);
return 0;
}