P10401 「XSOI-R1」区间操作 题解

题目传送门

题目大意:

给定一个长度为 \(n\) 的序列,有 \(q\) 次询问,每次询问一个区间 \([l,r]\),需要输出该区间内每个前缀的和的异或和。

很容易想到用前缀和维护序列 \(a\),异或和暴力处理,但这样的时间复杂度是 \(O(nq)\) 的,只能通过测试点 \(0,1,3\)

又考虑到这是个静态区间问题,想到莫队。

思考了一会儿发现左端点不好维护,但右端点可以 \(O(1)\) 维护,于是借用了莫队的思想(其实就是普通的双指针),将询问按左端点为第一关键字,右端点按第二关键字升序排序。

左端点与上一个询问相同时,只需右移右端点就行了。

左端点与上一个询问不同时,暴力计算。

这样的最坏时间复杂度是 \(O(n^2)\)可以水过此题

总之,莫队的思想还是很重要的。

\(\texttt{Code:}\)

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 10010, M = 1000010;

int n, m;
long long s[N];

struct node{
	int id, l ,r;
}q[M];
long long ans[M];

bool cmp(node x, node y) {
	if(x.l == y.l) return x.r < y.r;
	return x.l < y.l;
}

void add(int x, int l, long long &res) {
	res ^= (s[x] - s[l - 1]);
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) {
		scanf("%lld", &s[i]);
		s[i] += s[i - 1];
	}
	int id, l, r;
	for(int i = 1; i <= m; i++) {
		scanf("%d%d", &l, &r);
		q[i] = {i, l, r};
	} 
	sort(q + 1, q + m + 1, cmp);
	long long res;
	for(int i, k = 1; k <= m; k++) {
		id = q[k].id, l = q[k].l, r = q[k].r;
		if(k == 1 || l != q[k - 1].l) {
			res = 0;
			for(int j = l; j <= r; j++) 
				res ^= (s[j] - s[l - 1]);
			i = q[k].r;
		}
		else while(i < r) add(++i, q[k - 1].l, res); //幼年的莫队 
		ans[id] = res;
	}
	for(int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
	return 0;
}
posted @ 2024-07-23 14:40  Brilliant11001  阅读(10)  评论(0编辑  收藏  举报