CF - 617E 莫队算法 + 分块

题意:

给出n个数的序列,给出m个区间[L,R]的询问,问在[L,R]中有多少段子区间的异或和等于k。

思路:

莫队算法,先保存前缀异或和,然后对于每次从[L,R]转移到[L,R+1]只需要O(1)的时间,只需要知道sum[R^k]有多少即可,因为每个数都不大,可以直接开一个数组保存。另外这题的莫队需要分块处理,否则还是T。

代码:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 10;
const int MAXA = 2e6 + 10;

ll cur;
int n, m, k;
int cnt[MAXA], sum[MAXN], p[MAXN];
ll ans[MAXN];

void add(int pos) {
	cur += cnt[sum[pos] ^ k]; 
	cnt[sum[pos]]++;
}

void del(int pos) {
	cnt[sum[pos]]--;
	cur -= cnt[sum[pos] ^ k];
}

struct Query {
	int l, r, id;
	bool operator < (const Query &rhs) const {
		return p[l] == p[rhs.l] ? r < rhs.r : p[l] < p[rhs.l];
	}
}q[MAXN];

int main() {
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &sum[i]);
		sum[i] ^= sum[i - 1];
	}
	int block = ceil(sqrt(n));
	for (int i = 1; i <= n; i++) p[i] = (i - 1) / block;
	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);
	cnt[0] = 1;
	add(1);
	int l = 1, r = 1;
	for (int i = 1; i <= m; i++) {
		while (r > q[i].r) {
			del(r);
			r--;
		}
		while (r < q[i].r) {
			++r;
			add(r);
		}
		while (l > q[i].l) {
			--l;
			add(l - 1);
		}
		while (l < q[i].l) {
			del(l - 1);
			l++;
		}
		ans[q[i].id] = cur;
	}
	for (int i = 1; i <= m; i++)
		printf("%lld\n", ans[i]);
	return 0;
}

你可能感兴趣的:(莫队算法)