回滚莫队
有些问题不支持从序列中删除元素。回滚莫队通过调整扩展的顺序,并增加一些常数,使得删除操作变为回滚(撤销)操作,可以使用栈维护。
回滚莫队和普通莫队的排序方式基本相同,但不能使用奇偶块优化。这样 \(blo[l]\) 相等的所有询问的右端点 \(r\) 就会单调递增。
例题
题意
给定一个长为 \(n\) 序列。有 \(q\) 次询问,每次询问给定一个区间 \([l,r]\),询问 \(\max_{i\in V}\{i\times cnt(i)\}\)。
这种最值形式不易实现删除操作,考虑如何回滚。对于 \(r-l+1\le \sqrt n\) 的询问,直接暴力即可。
考虑左右端点分居两块的情况。记 ed[i]
表示 \(i\) 块的结束位置,初始时 c_l = ed[blo[l]]
,c_r = c_l - 1
。扩展时总是先处理 \(r\),然后处理 \(l\),得到答案后再将 \(l\) 回滚到 ed[blo[l]]
。

容易发现时间复杂度仍为 \(O(n\sqrt m)\),且在块长 \(blen=\frac{n}{\sqrt m}\) 时最优。
模板代码
| #include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int N = 1E5 + 10;
int blen, block[N], ed[N];
struct Query {
int l, r, id;
inline bool operator<(const Query& other) const {
if(block[l] != block[other.l]) return block[l] < block[other.l];
return r < other.r;
}
} q[N];
int n, m;
int a[N], num[N], nn = 0;
ll ans[N];
void lisanhua() {
for(int i = 1; i <= n; i++) {
num[++nn] = a[i];
}
sort(num + 1, num + 1 + nn);
nn = unique(num + 1, num + 1 + nn) - (num + 1);
for(int i = 1; i <= n; i++) {
a[i] = lower_bound(num + 1, num + 1 + nn, a[i]) - num;
}
}
ll cnt[N];
int cl, cr;
ll c_ans;
ll buf[N][2], top;
inline void clear() {
c_ans = 0;
// cout << "clear" << endl;
for(int i = 1; i <= nn; i++) cnt[i] = 0;
}
inline void clear_buf() {
// cout << "clear buf" << endl;
top = 0;
}
inline void add(int pos) {
// cout << "add: " << pos << endl;
cnt[a[pos]] += num[a[pos]];
buf[++top][0] = a[pos];
buf[top][1] = c_ans;
if(cnt[a[pos]] > c_ans) c_ans = cnt[a[pos]];
}
inline void roll_back() {
// cout << "roll back" << endl;
cnt[buf[top][0]] -= num[buf[top][0]];
c_ans = buf[top--][1];
}
ll bl[N];
inline ll baoli(int l, int r) {
ll res = 0;
for(int i = l; i <= r; i++) {
bl[a[i]] += num[a[i]];
if(bl[a[i]] > res) res = bl[a[i]];
}
for(int i = l; i <= r; i++) {
bl[a[i]] = 0;
}
return res;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
blen = n / sqrt(m);
for(int i = 1; i <= n; i++) block[i] = (i + blen - 1) / blen;
for(int i = 1; i <= n; i++) ed[block[i]] = i;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
lisanhua();
for(int i = 1; i <= m; i++) {
cin >> q[i].l >> q[i].r;
q[i].id = i;
}
for(int i = 1; i <= m; i++) {
int l = q[i].l, r = q[i].r;
if(block[l] == block[r]) {
ans[i] = baoli(l, r);
}
}
sort(q + 1, q + 1 + m);
for(int i = 1; i <= m; i++) {
int l = q[i].l, r = q[i].r;
if(block[l] == block[r]) {
continue;
}
if(block[l] != block[cl]) {
cl = ed[block[l]], cr = cl - 1;
clear();
}
while(cr < q[i].r) add(++cr);
clear_buf();
while(cl > q[i].l) add(--cl);
ans[q[i].id] = c_ans;
while(cl != ed[block[cl]]) roll_back(), ++cl;
}
for(int i = 1; i <= m; i++) {
cout << ans[i] << '\n';
}
return 0;
}
|
思路同上。
代码
| #include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 2E5 + 10;
const int INF = (int)0x3f3f3f3f3f3f3f3f;
int blen, blo[N], ed[N];
struct Query {
int l, r, id;
inline bool operator<(const Query& other) const {
if(blo[l] != blo[other.l]) return blo[l] < blo[other.l];
return r < other.r;
}
} q[N];
int n, m, nn;
int a[N], ans[N];
void lisanhua() {
int num[N];
for(int i = 1; i <= n; i++) num[++nn] = a[i];
sort(num + 1, num + 1 + nn);
nn = unique(num + 1, num + 1 + nn) - (num + 1);
for(int i = 1; i <= n; i++) a[i] = lower_bound(num + 1, num + 1 + nn, a[i]) - num;
}
int b_mn[N], b_mx[N];
int baoli(int l, int r) {
int res = 0;
for(int i = l; i <= r; i++) {
b_mn[a[i]] = min(b_mn[a[i]], i);
b_mx[a[i]] = max(b_mx[a[i]], i);
res = max(res, b_mx[a[i]] - b_mn[a[i]]);
}
for(int i = l; i <= r; i++) {
b_mn[a[i]] = INF;
b_mx[a[i]] = 0;
}
return res;
}
int c_l, c_r, c_ans;
int mn[N], mx[N];
int buf[N][4], nb;
void clear_buf() {
nb = 0;
}
void clear() {
nb = 0;
c_ans = 0;
for(int i = 1; i <= nn; i++) mn[i] = INF, mx[i] = 0;
}
void update(int pos) {
buf[++nb][0] = a[pos];
buf[nb][1] = mn[a[pos]];
buf[nb][2] = mx[a[pos]];
buf[nb][3] = c_ans;
mn[a[pos]] = min(mn[a[pos]], pos);
mx[a[pos]] = max(mx[a[pos]], pos);
c_ans = max(c_ans, mx[a[pos]] - mn[a[pos]]);
}
void roll_back() {
int pos = buf[nb][0];
mn[pos] = buf[nb][1];
mx[pos] = buf[nb][2];
c_ans = buf[nb--][3];
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
cin >> m;
for(int i = 1; i <= m; i++) {
cin >> q[i].l >> q[i].r;
q[i].id = i;
}
blen = max(1, (int)(n / sqrt(m)));
for(int i = 1; i <= n; i++) blo[i] = (i + blen - 1) / blen;
for(int i = n; i >= 1; i--) ed[i] = (blo[i] == blo[i + 1]) ? ed[i + 1] : i;
lisanhua();
sort(q + 1, q + 1 + m);
for(int i = 1; i <= nn; i++) mn[i] = b_mn[i] = INF;
for(int i = 1; i <= m; i++) {
int l = q[i].l, r = q[i].r;
if(blo[l] == blo[r]) {
ans[q[i].id] = baoli(l, r);
continue;
}
if(blo[l] != blo[c_l]) {
clear();
c_l = ed[l], c_r = c_l - 1;
}
while(c_r < r) update(++c_r);
clear_buf();
while(c_l > l) update(--c_l);
ans[q[i].id] = c_ans;
while(c_l < ed[c_l]) roll_back(), ++c_l;
}
for(int i = 1; i <= m; i++) {
cout << ans[i] << '\n';
}
return 0;
}
|