1. 程式人生 > 其它 >回滾莫隊初步--JOISC 2014 Day1 歷史研究

回滾莫隊初步--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;
}