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

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

题目链接:luogu P5906

题目大意

给你一个序列,多次询问每次问一个区间,求里面相同的数的最远间隔距离。

思路

考虑莫队,发现加入一个点好处理,但是删去不好处理。

考虑莫队能不能不删去,那我们考虑把删去变成从头再加。
啊当然不行,那能不能选一个节点,每次改左边就从这个节点从新向左边扩展。

距离来说就是对于一个块 x x x,我们枚举作为左端点所在块,那我们按右端点排序,每次确定好右端点,我们就把左端点从这个块的右端开始往左移,从而来加进去。

然后在这题里面你要维护的就是最左的某个数和最右的某个数(用桶来维护)。
然后记得还原操作不要用 memset 而是按之前贡献的路取消回去。
(也可以拿一个桶记录修改了什么地方)

具体操作看代码。

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 2e5 + 100;
int n, a[N], B, m, blo[N], bl[N], br[N];
int lst[N], ans[N], Rlst[N], Llst[N];
int clr[N], b[N], bn;
struct Quest {
	int l, r, id;
}q[N];

bool cmp(Quest x, Quest y) {
	if (blo[x.l] != blo[y.l]) return blo[x.l] < blo[y.l];
	return x.r < y.r;
}

int clac(int l, int r) {
	int re = 0;
	for (int i = l; i <= r; i++)
		if (!lst[a[i]]) lst[a[i]] = i;
			else re = max(re, i - lst[a[i]]);
	for (int i = l; i <= r; i++) lst[a[i]] = 0;
	return re;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	
	//离散
	memcpy(b, a, sizeof(b)); sort(b + 1, b + n + 1); bn = unique(b + 1, b + n + 1) - b - 1;
	for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + bn + 1, a[i]) - b; 
	
	B = sqrt(n);
	for (int i = 1; i <= n; i++) {
		blo[i] = (i - 1) / B + 1;
		if (!bl[blo[i]]) bl[blo[i]] = i;
		br[blo[i]] = i;
	}
	
	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 + m + 1, cmp);
	
	int now = 1;
	for (int i = 1; i <= blo[n]; i++) {
		int mid = br[i], l = mid + 1, r = mid, val = 0;
		while (now <= m && blo[q[now].l] == i) {
			if (blo[q[now].r] == i) {
				ans[q[now].id] = clac(q[now].l, q[now].r);
				now++; continue;
			}
			while (r < q[now].r) {
				r++;
				Rlst[a[r]] = r;
				if (!Llst[a[r]]) Llst[a[r]] = r, clr[++clr[0]] = a[r];
					else val = max(val, r - Llst[a[r]]);
			}
			int tmp = val;
			while (l > q[now].l) {
				l--;
				if (!Rlst[a[l]]) Rlst[a[l]] = l;
					else val = max(val, Rlst[a[l]] - l);
			}
			while (l < mid + 1) {
				if (Rlst[a[l]] == l) Rlst[a[l]] = 0;
				l++;
			}
			ans[q[now].id] = val; val = tmp;
			now++;
		}
		for (int j = 1; j <= clr[0]; j++) Llst[clr[j]] = Rlst[clr[j]] = 0; clr[0] = 0;
	}
	
	for (int i = 1; i <= m; i++) printf("%d\n", ans[i]);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值