Codeforces 703D (区间内出现偶数次的数的异或和)

在这里插入图片描述
题意:给定你一个长度为n的序列,然后m个询问,每次询问给出L和R,问你在这个区间内出现次数为偶数次的数(每个数只取一个),他们的异或和为多少。

思路:出现次数为偶数次这些异或都会变成0,所以我们直接求区间异或和,求出来的出现次数为奇数词的数的异或和,假如我们能求得这个区间内不同的数的异或和,那他们二者异或一下就可以得到我们想要的答案了。
然后问题就变成了怎么求区间内不同数的异或和呢。m个询问,我们可以离线处理,按照右端点从小到大的顺序排序,然后每次只去更新距离当前右端点r最近的值的位置,再把这个位置之间的位置的值置为0,这样每次区间内我们算的就是一个唯一存在的值,又因为离线处理,按照右端点扫一遍,复杂度是可以的。
这样我们就用一个树状数组维护这个特殊的区间异或和即可。

代码:

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 1e6 + 7;
//对于这种m个询问的题目 可以多想一下 能不能离线搞 这个题就是离线之后 按右端点找就可
int pre[MAXN];//当前数的上一次出现的位置
map<int,int>mp;//记录一下每个数出现的位置
int sxor[MAXN],n,m,ans[MAXN],a[MAXN],c[MAXN];

struct Q{//存询问
	int l,r,id;
}q[MAXN];
bool cmp(Q a,Q b){ return a.r < b.r; }
bool ccmp(Q a,Q b){ return a.id < b.id; }

int lowbit(int x){ return x & (-x); }
void update(int p,int v){ while(p <= n){ c[p] ^= v;p += lowbit(p);  } }
int query(int p){
	int ans = 0;
	while(p){ ans ^= c[p];p -= lowbit(p); }
	return ans;
}

int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;i ++){
		scanf("%d",&a[i]);
		pre[i] = mp[a[i]];//存一下当前位置的值上一次出现的位置是哪里
		mp[a[i]] = i;
	}
	// for(int i = 1;i <= n;i ++) cout<<pre[i]<<' '; cout<<'\n';
	for(int i = 1;i <= n;i ++) sxor[i] = sxor[i-1] ^ a[i];
	scanf("%d",&m);
	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,cmp);//按右端点的大小排序 这样保证扫一遍即为答案
	int L,R = 1;
	for(int i = 1;i <= m;i ++){
		while(R <= q[i].r){
			if(pre[R]){//前面出现过的话 我就把它置为0 也就是再异或一下
				update(pre[R],a[R]);
			}
			update(R,a[R]);
			R++;
		}
		L = q[i].l;
		int t = sxor[R-1] ^ sxor[L-1];
		// cout<<R-1<<' '<<L<<endl;
		// cout<<query(R-1)<<"***"<<query(L-1)<<endl;
		ans[q[i].id] = t ^ query(R-1) ^ query(L-1);

	}
	sort(q + 1,q + 1 + m,ccmp);
	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、付费专栏及课程。

余额充值