P5906 回滚莫队&不删除莫队 (回滚莫队)

题目链接: P5906 回滚莫队&不删除莫队

大致题意

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

序列中两个元素的间隔距离指的是两个元素下标差的绝对值(ctrl+c, ctrl+v)

解题思路

回滚莫队

考虑信息维护: 我们需要记录每个值出现的最左和最右位置.

下文 [ L , R ] [L, R] [L,R]为莫队维护区间, [ l , r ] [l, r] [l,r]为查询区间.

由于 l l l在一个块内时, r r r单调递增的, 因此我们会不断更新最右位置. 贡献为: 最左位置和当前位置的距离.

考虑暴力维护 [ l , L − 1 ] [l, L - 1] [l,L1]的部分, 当 v a l val val第一次出现在 [ l , L − 1 ] [l, L - 1] [l,L1]时, 贡献为: 最右位置和当前位置的距离. 否则为: 最左位置和当前位置的距离.

我们发现这部分我们会改变最左位置的信息, 因此我们应当备份 [ L , R ] [L, R] [L,R]区间最左位置的信息.


为什么我的代码比别人高一倍的常数QAQ

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 2E5 + 10; int B; 
int w[N];
vector<int> v(1, -0x3f3f3f3f); // 离散化
int find(int x) { return lower_bound(v.begin(), v.end(), x) - v.begin(); }


struct mo {
	int l, r, id;
	bool operator< (const mo& t) const {
		if (l / B != t.l / B) return l < t.l;
		return r < t.r;
	}
}; vector<mo> area;


int res[N];
int lpos[N], rpos[N], blpos[N], dis;
void add(int x) { //向右添加
	if (lpos[w[x]]) dis = max(dis, x - lpos[w[x]]), rpos[w[x]] = x;
	else lpos[w[x]] = rpos[w[x]] = x;
}
void bruth(int l, int r, int id) {
	for (int i = l; i <= r; ++i) add(i);
	res[id] = dis;

	for (int i = l; i <= r; ++i) lpos[w[i]] = rpos[w[i]] = 0;
	dis = 0;
}
int main()
{
	int n; scanf("%d", &n);
	B = sqrt(n);
	rep(i, n) scanf("%d", &w[i]), v.push_back(w[i]);
	sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end());
	rep(i, n) w[i] = find(w[i]), blpos[w[i]] = -1;

	int m; cin >> m;
	rep(i, m) {
		int l, r; scanf("%d %d", &l, &r);
		area.push_back({ l, r, i });
	}
	sort(area.begin(), area.end());

	int L = 1, R = 0, last = -1;
	for (auto& [l, r, id] : area) {
		if (l / B != last) {
			for (int i = L; i <= R; ++i) {
				lpos[w[i]] = rpos[w[i]] = 0;
			}
			dis = 0;

			L = (l / B + 1) * B, R = L - 1, last = l / B;
		}

		if (l / B == r / B) {
			bruth(l, r, id);
			continue;
		}

		while (R < r) add(++R);
		int bdis = dis;
		for (int i = l; i <= L - 1; ++i) {
			if (blpos[w[i]] == -1) { //没有备份过
				blpos[w[i]] = lpos[w[i]]; //备份
				lpos[w[i]] = i;
				if (rpos[w[i]]) dis = max(dis, rpos[w[i]] - i);
			}
			else dis = max(dis, i - lpos[w[i]]);
		}
		res[id] = dis;

		for (int i = l; i <= L - 1; ++i) {
			if (blpos[w[i]] != -1) {
				lpos[w[i]] = blpos[w[i]], blpos[w[i]] = -1;
			}
		}
		dis = bdis;
	}

	rep(i, m) printf("%d\n", res[i]);

	return 0;
}

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值