回滾莫隊初步--JOISC 2014 Day1 歷史研究
阿新 • • 發佈:2021-11-10
回滾莫隊
莫隊演算法的核心在於在區間變化時快速的計算新的結果。但是,當我們的序列(或別的什麼集合)只能方便地實現增加或刪除中的一種操作,另一種操作難以實現時,這時候就需要使用莫隊演算法的一個變型,回滾莫隊,來實現需求。
前置知識:普通莫隊,分塊
思路
以只能加不能減的回滾莫隊舉例
回滾莫隊的關鍵之處在於,對於所有的詢問,先將左端點按其所屬塊進行升序排序(從小到大),塊相同時按右端點升序進行排序。
對於左右端點在同一塊中的詢問,暴力查詢即可,複雜度\(O(\sqrt n)\)
每當左端點改變時,進行初始化,把\(l,r\)重置到左區間塊的尾部,分別為ed[bel[q[i].l]]
和ed[bel[q[i].l]] + 1
對於左端點在同一個塊內的查詢,右端點遞增(因為排過序了),區間右端點不斷向右遞增並和普通莫隊一樣記錄修改。對於左端點,先記錄左端點不動時的答案,使用一個臨時左端點變數例如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; }