Loading

回滚莫队(不删除莫队)

AT1219 歴史の研究 - 洛谷

题意

查询区间 \([l,r]\) 内一个数乘上它在区间出现次数的最大值

使用莫队的时候进行增加操作的时候会很简单,但是在删除操作的时候不是那么好维护的时候,可以使用不删除的莫队(回滚莫队)

还是相同的思路,先把询问排序

然后对于左端点在同一个块的询问来说

如图

如果右端点也在块内,则暴力计算

否则左端点从下一个块的左边开始,右端点单调向右移动。

左端点在块内反复进行回滚操作。

这样就在保证时间复杂度还是 \(O(n\sqrt n)\) 的情况下避免了删除操作

/*
 * @Author: zhl
 * @Date: 2020-11-19 10:38:35
 */
 #include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
int n, m, len, cnt[N], nums[N], w[N], ID[N];
ll ans[N];
struct Query {
	int id, l, r;
	bool operator < (const Query& rhs)const {
		int al = ID[l], bl = ID[rhs.l];
		if (al != bl)return al < bl;
		return r < rhs.r;
	}
}q[N];

void add(int x, ll& res) {
	cnt[x]++;
	res = max(res, 1ll * cnt[x] * nums[x]);
}

int main() {
	scanf("%d%d", &n, &m);
	int numID = 0;
	for (int i = 1; i <= n; i++)scanf("%d", w + i), nums[++numID] = w[i];

	sort(nums + 1, nums + 1 + n);
	numID = unique(nums + 1, nums + 1 + n) - nums - 1;
	len = sqrt(n);
	for (int i = 1; i <= n; i++)ID[i] = i / len;
	for (int i = 1; i <= n; i++)w[i] = lower_bound(nums + 1, nums + 1 + numID, w[i]) - nums;

	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &q[i].l, &q[i].r);
		q[i].id = i;
	}

	sort(q + 1, q + 1 + m);

	for (int x = 1; x <= m;) {
		int y = x;
		while (y <= m and ID[q[y].l] == ID[q[x].l]) y++;

		//块内暴力
		int right = len * ID[q[x].l] + len;
		//int right = len * ID[q[y].l]; 这样不对,y不一定比x大
		
		while (x < y and q[x].r <= right - 1) {
			ll res = 0;
			for (int i = q[x].l; i <= q[x].r; i++) add(w[i], res);
			ans[q[x].id] = res;
			for (int i = q[x].l; i <= q[x].r; i++) cnt[w[i]]--;
			x++;
		}

		//块外
		
		int l = right, r = right - 1;
		ll res = 0;
		while (x < y) {
			int ql = q[x].l, qr = q[x].r;
			while (r < qr)add(w[++r], res);
			ll _res = res;
			while (l > ql)add(w[--l], res);
			ans[q[x].id] = res;
			while (l < right) cnt[w[l++]] --;
			res = _res;
			x++;
		}
		memset(cnt, 0, sizeof cnt);
	}
	for (int i = 1; i <= m; i++)printf("%lld\n", ans[i]);
}

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

给定一个序列,多次询问一段区间 \([l,r]\),求区间中相同的数的最远间隔距离

序列中两个元素的间隔距离指的是两个元素下标差的绝对值

这个说是模板题,其实上一道题更模板。

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10;
int w[N], ans[N], n, m, nums[N], ID[N], len;
int fir[N], last[N];
int mfir[N], mlast[N], mpos[N], mcnt, vis[N], vscnt;
struct Query {
	int id, l, r;
	bool operator < (const Query& b)const {
		if (ID[l] != ID[b.l])return ID[l] < ID[b.l];
		return r < b.r;
	}
}q[N];


void add(int pos, int val, int& res) {
	if (!fir[val]) fir[val] = pos;
	else fir[val] = min(fir[val], pos);

	if (!last[val])last[val] = pos;
	else last[val] = max(last[val], pos);

	res = max(res, last[val] - fir[val]);
}
int main() {
	scanf("%d", &n);
	int numID = 0;
	for (int i = 1; i <= n; i++)scanf("%d", w + i), nums[++numID] = w[i];

	sort(nums + 1, nums + 1 + n);
	numID = unique(nums + 1, nums + 1 + n) - nums - 1;

	for (int i = 1; i <= n; i++)w[i] = lower_bound(nums + 1, nums + 1 + numID, w[i]) - nums;

	len = sqrt(n);
	for (int i = 1; i <= n; i++)ID[i] = i / len;

	scanf("%d", &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &q[i].l, &q[i].r); q[i].id = i;
	}

	sort(q + 1, q + 1 + m);

	for (int x = 1; x <= m;) {
		int y = x;
		while (y <= m and ID[q[y].l] == ID[q[x].l])y++;
		int right = ID[q[x].l] * len + len;

		while (x < y and q[x].r <= right - 1) {
			int res = 0, mcnt = 0; vscnt++;
			for (int i = q[x].l; i <= q[x].r; i++) {
				if (vis[w[i]] != vscnt) {
					vis[w[i]] = vscnt;
					mpos[++mcnt] = w[i];
					mfir[mcnt] = fir[w[i]];
					mlast[mcnt] = last[w[i]];
				}
				add(i, w[i], res);
			}
			ans[q[x].id] = res;
			for (int i = 1; i <= mcnt; i++) {
				fir[mpos[i]] = mfir[i];
				last[mpos[i]] = mlast[i];
			}
			x++;
		}

		int l = right, r = right - 1;
		int res = 0;

		while (x < y) {
			int ql = q[x].l, qr = q[x].r;
			while (r < qr)r++, add(r, w[r], res);
			int _res = res;
			mcnt = 0; vscnt++;
			while (l > ql) {
				l--;
				if (vis[w[l]] != vscnt) {
					vis[w[l]] = vscnt;
					mpos[++mcnt] = w[l];
					mfir[mcnt] = fir[w[l]];
					mlast[mcnt] = last[w[l]];
				}
				add(l, w[l], res);
			}
			ans[q[x].id] = res;
			for (int i = 1; i <= mcnt; i++) {
				fir[mpos[i]] = mfir[i];
				last[mpos[i]] = mlast[i];
			}
			l = right; res = _res;
			x++;
		}
		memset(fir, 0, sizeof fir); memset(last, 0, sizeof last);
	}
	for (int i = 1; i <= m; i++)printf("%d\n", ans[i]);
}

posted @ 2020-11-24 20:21  —O0oO-  阅读(216)  评论(0编辑  收藏  举报