LOJ6285. 數列分塊入門 9 - 離線區間眾數 -- 回滾莫隊
阿新 • • 發佈:2021-11-08
LOJ6285. 數列分塊入門 9 - 離線區間眾數 -- 回滾莫隊
最近正在通過這個這個題集學習分塊,做到最後一題求區間眾數的時候百思不得其解,無奈去網上搜索題解。在搜尋的過程中,許多部落格都提到了一種叫做回滾莫隊的演算法,於是又去花了半天時間學習新技術,終於用回滾莫隊來做出了這道題
題目連結 : #6285. 數列分塊入門 9 - 題目 - LibreOJ (loj.ac)
回滾莫隊學習參考資料 :『回滾莫隊及其簡單運用』
程式碼
#include<bits/stdc++.h> #include<unordered_map> using namespace std; const int maxn = 1e5; struct Query { int l, r, id; }q[maxn+10]; int a[maxn + 10]; int st[maxn + 10]; int ed[maxn + 10]; int bel[maxn + 10]; void init(int n) { for (int i = 1; i <= n; i++) cin >> a[i]; int sq; sq = sqrt(n); for (int i = 1; i <= n; i++) { st[i] = n / sq * (i - 1) + 1; ed[i] = n / sq * i; } ed[sq] = n; for (int i = 1; i <= sq; i++) { for (int 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; } int ans[maxn + 10]; unordered_map<int, int> cnt1; void add(int p, int& ans) { cnt1[a[p]]++; if (cnt1[a[p]] > cnt1[ans]) ans = a[p]; else if (cnt1[a[p]] == cnt1[ans]) ans = min(a[p], ans); } void del(int p) { cnt1[a[p]]--; } void solve() { int n; cin >> n; init(n); for (int i = 1; i <= n; i++) { cin >> q[i].l >> q[i].r; q[i].id = i; } sort(q + 1, q + 1 + n, cmp); int l = 1, r = 0; int curblo = 0; int Max = 0; for (int i = 1; i <= n; i++) { if (bel[q[i].l] == bel[q[i].r]) { unordered_map<int, int> cnt2; for (int j = q[i].l; j <= q[i].r; j++) cnt2[a[j]]++; int t = 0; for (int j = q[i].l; j <= q[i].r; j++) if (cnt2[a[j]] > cnt2[t]) t = a[j]; else if (cnt2[a[j]] == cnt2[t]) t = min(a[j], t); 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++); Max = 0; curblo = bel[q[i].l]; } while (r < q[i].r) add(++r, Max); int t = Max; int l2 = l; while (l2 > q[i].l) add(--l2, t); ans[q[i].id] = t; while (l2 < l) del(l2++); } for (int i = 1; i <= n; i++) { cout << ans[i] << '\n'; } } int main() { ios::sync_with_stdio(false); cin.tie(0); solve(); return 0; }