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;
}