回滚莫队初步--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;
}
posted @ 2021-11-10 15:39  icey_z  阅读(106)  评论(0)    收藏  举报