回滚莫队小记

前言

回滚莫队这个名字我几百年前就听过了,一直以为很高大上,但是今天学习后才发现非常的简单~就写篇博客吧

介绍

回滚莫队是什么?有什么用处?

首先考虑这样一个问题:求区间内相同颜色的距离最大值。莫队可以写吧,但好像有点问题,删除元素时不好维护哇 qwq,若是最大值,就得知道次大值,然后次次大值,次次次大值……

于是回滚莫队诞生啦!

名字里有个“回滚”,它到底回滚在哪里呢?

我知道你很急,但是别急,类似普通莫队,先将询问排序,以左端点所在的块的位置为第一关键字,再以右端点为第二关键字排序。

接下来就是一个个处理每一个块里的询问了(这里处理的是左端点在这个块内的询问),按照排序的顺序来处理。我们会发现右端点是递增的,而左端点是在这个块内左右横跳。下面给个图:

如上图所示,序列已分为若干个块。设当前要处理的块为 \(B\)(黄色区域),然后有若干个询问(灰色线条),设 \(B\) 的右端点为 \(br\),当前询问 \([ql,qr]\)

那么我们初始化 \(l=br+1,r=br\),接下来 \(r\) 调整至 \(qr\),记录答案,再将 \(l\) 拓展至 \(ql\),答案找出。然后将 \(l\) 退回至 \(br+1\),答案回到记录的那个版本,清空原本 \(l\)\(br\) 的贡献,等待下一次询问,这就是回滚莫队的“回滚”过程了。

可见,回滚莫队的主要思想就是不删除,只加入,避免看普通莫队存在的问题了。

下面给几道例题:

歴史の研究

模版题,只需维护每个数的出现次数。

#include <bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pb push_back
#define mk make_pair
#define ll long long
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;

inline int read() {
	int x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9') f = c == '-' ? -1 : f, c = getchar();
	while (c >= '0' && c <= '9') x = (x<<3)+(x<<1)+(c^48), c = getchar();
	return x*f;
}

inline void write(int x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x/10);
	putchar('0'+x%10);
}

const int N = 1e5+5;
int n, m, b, bn, tot, a[N], a_[N], _[N], be[N], cnt[N];

struct que {
	int l, r, id;
	bool operator < (const que &x) const { return (be[l]^be[x.l]) ? be[l] < be[x.l] : r < x.r; }
} q[N];

int calc(int l, int r) {
	int ans = 0, cnt[N] = {0};
	for (int i = l; i <= r; ++i) ++cnt[a[i]];
	for (int i = l; i <= r; ++i) ans = max(ans, cnt[a[i]]*a_[a[i]]);
	return ans;
}

signed main() {
	n = read(), m = read(), b = sqrt(n), bn = n/b+(n%b != 0);
	for (int i = 1; i <= n; ++i) a[i] = a_[i] = read(), be[i] = (i-1)/b+1;
	sort(a_+1, a_+n+1);
	tot = unique(a_+1, a_+n+1)-a_-1;
	for (int i = 1; i <= n; ++i) a[i] = lower_bound(a_+1, a_+tot+1, a[i])-a_;
	for (int i = 1; i <= m; ++i) q[i].l = read(), q[i].r = read(), q[i].id = i;
	sort(q+1, q+m+1);
	for (int i = 1, j = 1; j <= bn; ++j) {
		int br = min(j*b, n), l = br+1, r = br, ans = 0;
		for (; be[q[i].l] == j; ++i) {
			if (be[q[i].l] == be[q[i].r]) { _[q[i].id] = calc(q[i].l, q[i].r); continue; }
			while (r < q[i].r) {
				++r;
				++cnt[a[r]];
				ans = max(ans, cnt[a[r]]*a_[a[r]]);
			}
			int tmp = ans;
			while (l > q[i].l) {
				--l;
				++cnt[a[l]];
				ans = max(ans, cnt[a[l]]*a_[a[l]]);
			}
			_[q[i].id] = ans, ans = tmp;
			while (l <= br) {
				--cnt[a[l]];
				++l;
			}
		}
		while (r > br) --cnt[a[r]], --r;
	}
	for (int i = 1; i <= m; ++i) write(_[i]), enter;
	return 0;
}

P5906 【模板】回滚莫队&不删除莫队

这个题是介绍中给的一个例子,维护最后一次出现的位置和第一次出现的位置即可。

#include <bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mk make_pair
#define ll long long
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;

inline int read() {
	int x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9') f = c == '-' ? -1 : f, c = getchar();
	while (c >= '0' && c <= '9') x = (x<<3)+(x<<1)+(c^48), c = getchar();
	return x*f;
}

inline void write(int x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x/10);
	putchar('0'+x%10);
}

const int N = 2e5+5;
int n, m, b, bn, tot, a[N], a_[N], _[N], be[N], st[N], ed[N];

struct que {
	int l, r, id;
	bool operator < (const que &x) const { return (be[l]^be[x.l]) ? be[l] < be[x.l] : r < x.r; }
} q[N];

int calc(int l, int r) {
	int ans = 0, lst[N];
	for (int i = l; i <= r; ++i) lst[a[i]] = 0;
	for (int i = l; i <= r; ++i) lst[a[i]] ? ans = max(ans, i-lst[a[i]]) : lst[a[i]] = i;
	return ans;
}

int main() {
	n = read(), b = sqrt(n), bn = n/b+(n%b != 0);
	for (int i = 1; i <= n; ++i) a[i] = a_[i] = read(), be[i] = (i-1)/b+1;
	sort(a_+1, a_+n+1);
	tot = unique(a_+1, a_+n+1)-a_-1;
	for (int i = 1; i <= n; ++i) a[i] = lower_bound(a_+1, a_+tot+1, a[i])-a_;
	m = read();
	for (int i = 1; i <= m; ++i) q[i].l = read(), q[i].r = read(), q[i].id = i;
	sort(q+1, q+m+1);
	for (int i = 1, j = 1; j <= bn; ++j) {
		int br = min(j*b, n), l = br+1, r = br, ans = 0;
		vector <int> cl;
		for (; be[q[i].l] == j; ++i) {
			if (be[q[i].l] == be[q[i].r]) { _[q[i].id] = calc(q[i].l, q[i].r); continue; }
			while (r < q[i].r) {
				++r;
				ed[a[r]] = r;
				if (!st[a[r]]) st[a[r]] = r, cl.pb(a[r]);
				ans = max(ans, r-st[a[r]]);
			}
			int tmp = ans;
			while (l > q[i].l) {
				--l;
				if (ed[a[l]]) ans = max(ans, ed[a[l]]-l);
				else ed[a[l]] = l;
			}
			_[q[i].id] = ans, ans = tmp;
			while (l <= br) {
				if (ed[a[l]] == l) ed[a[l]] = 0;
				++l;
			}
		}
		for (int x:cl) st[x] = ed[x] = 0;
	}
	for (int i = 1; i <= m; ++i) write(_[i]), enter;
	return 0;
}
posted @ 2024-01-30 23:19  123wwm  阅读(33)  评论(0)    收藏  举报