『分块·莫队基础』[codeforces 617E]XOR and Favorite Number

题目描述

dydxh又没有强制在线,于是mzx又可以开心地水过了,不过他发现,在线貌似不可做?

给定长度为n的数列以及正整数,有m个询问每次询问给定两个数l,r,求 l ≤ i ≤ j ≤ r l≤i≤j≤r lijr,且 a ( i ) x o r a ( i + 1 ) x o r a ( i + 2 ) x o r . . . x o r a ( j ) = k 的 ( i , j ) a(i) xor a(i+1) xor a(i+2) xor ... xor a(j)=k的(i,j) a(i)xora(i+1)xora(i+2)xor...xora(j)=k(i,j)的个数。

题解

根据莫队算法的原理,我们应该讲每一个询问进行分块。分块的方法是:将一个长度为 n n n的序列分成 n \sqrt n n 块,若询问的左端点在相同的块上,就根据右端点从小到大排序;否则按左端点的块进行从小到大排序。

然后再回到此题,我们需要找到 S l   x o r   S r   =   k S_{l}\ xor\ S{r}\ =\ k Sl xor Sr = k中有多少对 ( l , r ) (l,r) (l,r).显然 q i . l ≤ q i . r q_i.l\leq q_i.r qi.lqi.r. S i S_i Si表示原序列中前 i i i个数的异或和。

假设我们已经知道了 ( l , r ) (l,r) (l,r)的答案,我们如何更新其它的答案呢:

  • 更新区间 ( l + 1 , r ) (l+1,r) (l+1,r),那么合法的左区间位置由 [ l − 1 , r − 1 ] [l-1,r-1] [l1,r1]变成了 [ l , r ] [l,r] [l,r],因此减去第 l − 1 l-1 l1个位置的影响即可。
  • 跟新区间 ( l − 1 , r ) (l-1,r) (l1,r),那么合法的左区间位置由 [ l − 1 , r − 1 ] [l-1,r-1] [l1,r1]变成了 [ i − 2 , r − 1 ] [i-2,r-1] [i2,r1],添加第 l − 2 l-2 l2个位置的影响即可。
  • 右区间的更新方式同理。

还有一个值得注意的地方:当 k = 0 k=0 k=0且在执行某一个删除操作的时候,会多减一个自己本身产生的影响;所以每一次进行删除操作得时候,便需要重新对答案进行 + 1 +1 +1操作才行。

代码如下:

#include <bits/stdc++.h>
#define int long long
using namespace std;

int n,m,k,T;
struct node
{
	int l,r,id,p;
	friend bool operator < (node p1, node p2) {
		if (p1.p == p2.p) return p1.r < p2.r;
		return p1.p < p2.p;
	}
} q[200000];
int ans = 0;
int a[200000];
int Ans[200000];
int cnt[2000000];

inline int read(void)
{
	int s = 0, w = 1;char c = getchar();
	while (c<'0' || c>'9') {if (c == '-') w = -1; c = getchar();}
	while (c>='0' && c<='9') s = s*10+c-48,c = getchar();
	return s*w;
}

void init(void)
{
	n = read(), m = read(), k = read(), T = sqrt(n);
	for (int i=1;i<=n;++i) a[i] = read(), a[i] = a[i-1] ^ a[i];
	for (int i=1;i<=m;++i) 
	{
		q[i].l = read();
		q[i].r = read();
		q[i].id = i;
		q[i].p = (q[i].l + T - 1) / T;//表示第i个询问所属的块的编号
	}
	sort(q+1, q+m+1);
	return;
}

void change(int x,int del)
{
	ans += cnt[x ^ k] * del;
	cnt[x] += del;
	if (del == -1 && k == 0) ans ++;
	return;
}

void work(void)
{
	int L , R;
	L = R = 1;
	cnt[a[1]] ++;cnt[0] ++;
	ans = (a[1] == k);
	for (int i=1;i<=m;++i)
	{
		while (L < q[i].l) change(a[L-1],-1), L ++;
		while (L > q[i].l) L --, change(a[L-1],1);
		while (R > q[i].r) change(a[R],-1), R --;
		while (R < q[i].r) R ++, change(a[R],1);
		Ans[q[i].id] = ans;
	}
	for (int i=1;i<=m;++i) printf("%lld\n", Ans[i]);
	return;
}

signed main(void)
{
	freopen("2011.in","r",stdin);
	freopen("2011.out","w",stdout);
	init();	
	work();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值