「CQOI 2018」异或序列

传送门


problem

已知一个长度为 n n n 的整数数列 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,给定查询参数 l , r l,r l,r,问在 a l , a l + 1 , . . . , a r a_l,a_{l+1},...,a_r al,al+1,...,ar 区间内,有多少子序列满足异或和等于 k k k。也就是说,对于所有的 x , y ( l ≤ x ≤ y ≤ r ) x,y (l ≤ x ≤ y ≤ r) x,y(lxyr),能够满足 a x ⊕ a x + 1 ⊕ ⋯ ⊕ a y = k a_x \oplus a_{x+1} \oplus \cdots \oplus a_y = k axax+1ay=k x , y x,y x,y 有多少组。

数据范围: 1 ≤ n , m ≤ 1 0 5 1 ≤ n, m ≤ 10^5 1n,m105 0 ≤ k , a i ≤ 1 0 5 0 ≤ k, a_i ≤ 10^5 0k,ai105


solution

这题其实挺简单的。

我们先求一个前缀异或和,把问题转化成求 [ l − 1 , r ] [l-1,r] [l1,r] 中有多少异或和为 k k k 的点。

注意到 k k k 是全局的,也即,每次询问不是完全独立的,我们也许可以从上一次询问得到一些信息来优化复杂度。

这便启示我们考虑莫队算法了。

维护一个桶(记作 c n t cnt cnt),插入一个数 x x x 时,我们在 k ⊕ x k\oplus x kx 位置 + 1 +1 +1,同时把 c n t x cnt_x cntx 加进答案里。删除反着做即可。

时间复杂度 O ( n n ) O(n\sqrt n) O(nn )


code

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,m,k,S,Xor[N],in[N],ans[N],cnt[N];
struct Query{int l,r,id;}Q[N];
bool operator<(const Query &p,const Query &q){
	return (in[p.l]==in[q.l])?p.r<q.r:in[p.l]<in[q.l];
}
void Del(int pos,int &now)  {now-=cnt[Xor[pos]],cnt[k^Xor[pos]]--;}
void Add(int pos,int &now)  {now+=cnt[Xor[pos]],cnt[k^Xor[pos]]++;}
int main(){
	scanf("%d%d%d",&n,&m,&k),S=sqrt(n);
	for(int i=1,x;i<=n;++i)  scanf("%d",&x),Xor[i]=Xor[i-1]^x;
	for(int i=1;i<=m;++i)  scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].l--,Q[i].id=i;
	for(int i=1;i<=n;++i)  in[i]=(i-1)/S+1;
	sort(Q+1,Q+m+1);
	int L=1,R=0,now=0;
	for(int i=1;i<=m;++i){
		while(L<Q[i].l)  Del(L++,now);
		while(L>Q[i].l)  Add(--L,now);
		while(R<Q[i].r)  Add(++R,now);
		while(R>Q[i].r)  Del(R--,now);
		ans[Q[i].id]=now;
	}
	for(int i=1;i<=m;++i)  printf("%d\n",ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值