回滚莫队初步--JOISC 2014 Day1 历史研究
回滚莫队
莫队算法的核心在于在区间变化时快速的计算新的结果。但是,当我们的序列(或别的什么集合)只能方便地实现增加或删除中的一种操作,另一种操作难以实现时,这时候就需要使用莫队算法的一个变型,回滚莫队,来实现需求。
前置知识:普通莫队,分块
思路
以只能加不能减的回滚莫队举例
回滚莫队的关键之处在于,对于所有的询问,先将左端点按其所属块进行升序排序(从小到大),块相同时按右端点升序进行排序。
对于左右端点在同一块中的询问,暴力查询即可,复杂度\(O(\sqrt n)\)
每当左端点改变时,进行初始化,把\(l,r\)重置到左区间块的尾部,分别为ed[bel[q[i].l]]
和ed[bel[q[i].l]] + 1
(注意\(l\)比\(r\)大),并清除之前的查询记录。
对于左端点在同一个块内的查询,右端点递增(因为排过序了),区间右端点不断向右递增并和普通莫队一样记录修改。对于左端点,先记录左端点不动时的答案,使用一个临时左端点变量例如l2
和一个临时答案变量例如t
,临时左端点移动,记录修改并更新答案。
当完成这次查询时,直接让临时左端点和临时答案变量归位,完成“回滚”
对于只能减不能加的询问也是类似,同样将左端点按其所属块进行升序排序(从小到大),但块相同时按右端点降序进行排序
左端点变化时,将\(r\)初始化为\(n\),将\(l\)初始化为左端点块的起点
后面的操作类似于只加不减的回滚莫队
可以证明,回滚莫队的时间复杂度为\(O(n\sqrt n)\)
例题
#2874. 「JOISC 2014 Day1」历史研究 - 题目 - LibreOJ (loj.ac)
回滚莫队模板题
记录x有两种方案,一种是读入后进行离散化,另一种是使用unordered_map(用map会导致超时)
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
using ll = long long;
const ll maxn = 1e5;
ll n;
ll sq;
ll st[maxn + 10];
ll ed[maxn + 10];
ll a[maxn + 10];
ll bel[maxn + 10];
ll ans[maxn + 10];
struct Query {
ll l, r, id;
}q[maxn+10];
void init(ll n) {
for (ll i = 1; i <= n; i++)
cin >> a[i];
sq = sqrt(n);
for (ll i = 1; i <= sq; i++) {
st[i] = n / sq * (i - 1) + 1;
ed[i] = n / sq * i;
}
ed[sq] = n;
for (ll i = 1; i <= sq; i++) {
for (ll j = st[i]; j <= ed[i]; j++)
bel[j] = i;
}
}
bool cmp(Query a, Query b) {
if (bel[a.l] != bel[b.l])
return bel[a.l] < bel[b.l];
else
return a.r < b.r;
}
unordered_map<ll, ll> cnt;
void add(ll p, ll& ans) {
cnt[a[p]]++;
ans = max(ans, cnt[a[p]] * a[p]);
}
void del(ll p) {
cnt[a[p]]--;
}
void solve() {
ll n,Q;
cin >> n>>Q;
init(n);
for (ll i = 1; i <= Q; i++) {
cin >> q[i].l >> q[i].r;
q[i].id = i;
}
sort(q + 1, q + Q + 1, cmp);
ll l = 1, r = 0;
ll curblo = 0;
ll Max = 0;
for (ll i = 1; i <= Q; i++) {
if (bel[q[i].l] == bel[q[i].r]) {
unordered_map<ll, ll> cnt1;
for (ll j = q[i].l; j<= q[i].r; j++)
cnt1[a[j]]++;
ll t = 0;
for (ll j = q[i].l; j <= q[i].r; j++)
t = max(t, cnt1[a[j]] * a[j]);
for (ll j = q[i].l; j <= q[i].r; j++)
cnt1[a[j]]--;
ans[q[i].id] = t;
continue;
}
if (curblo != bel[q[i].l]) {
while (r > ed[bel[q[i].l]])
del(r--);
while (l < ed[bel[q[i].l]] + 1)
del(l++);
curblo = bel[q[i].l];
Max = 0;
}
while (r < q[i].r)
add(++r, Max);
ll t = Max;
ll l2 = l;
while (l2 > q[i].l)
add(--l2, t);
ans[q[i].id] = t;
while (l2 < l)
del(l2++);
}
for (ll i = 1; i <= Q; i++) {
cout << ans[i] << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
solve();
return 0;
}