[题解]Much of surprise that be moved to you

题目传送门

题目大意

给出一个长度为 n n n的序列 a a a,有 m m m次操作。对于每次操作,求区间 [ l , r ] [l,r] [l,r]中,出现偶数次的数的异或和。

数据范围

  • Subtask 1 (10 pts): \text{Subtask 1 (10 pts):} Subtask 1 (10 pts):保证测试点数据是样例 1 \text{1} 1
  • Subtask 2 (20 pts): \text{Subtask 2 (20 pts):} Subtask 2 (20 pts): 1 ≤ n , m ≤ 5 × 1 0 3 , 1 ≤ a i ≤ 10. 1 \leq n,m \leq 5 \times 10 ^ 3, 1 \leq a_i \leq 10. 1n,m5×103,1ai10.
  • Subtask 3 (30 pts): \text{Subtask 3 (30 pts):} Subtask 3 (30 pts): 1 ≤ n , m ≤ 5 × 1 0 4 , 1 ≤ a i ≤ 1 0 6 . 1 \leq n,m \leq 5 \times 10 ^ 4, 1 \leq a_i \leq 10^6. 1n,m5×104,1ai106.
  • Subtask 4 (40 pts): \text{Subtask 4 (40 pts):} Subtask 4 (40 pts): 1 ≤ n , m ≤ 1.9 × 1 0 6 , 1 ≤ a i ≤ 1 0 9 . 1 \leq n,m \leq 1.9 \times 10^6, 1 \leq a_i \leq 10^9. 1n,m1.9×106,1ai109.

0x0.骗分( 10 pts \text{10 pts} 10 pts

输出样例 1 1 1即有 10 pts \text{10 pts} 10 pts.
代码省略。

printf("%d",。。。省略);

0x1.关于暴力解法( 30 pts \text{30 pts} 30 pts

类似计数方法,暴力判断即可,在线输出.

代码如下(by qzcbw):

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m,a[1000039],ans,b[1000039],maxn;
int main(){
	scanf("%d%d",&n,&m);int i,j;
	for(i=1;i<=n;i++){
		scanf("%d",&a[i]);
		maxn=max(maxn,a[i]);
	}int x,y;
	for(i=1;i<=m;i++){
		ans=0;
		for(j=1;j<=maxn;j++)b[j]=0;
		scanf("%d%d",&x,&y);
		for(j=x;j<=y;j++)b[a[j]]++;
		for(j=1;j<=maxn;j++)if((b[j]%2==0)&&(b[j]!=0)) ans=ans^j;
		printf("%d\n",ans);
	}
	return 0;
}

时间复杂度分析

平均时间复杂度为 O ( m Maxn ) O(\text{m Maxn}) O(m Maxn)(其中 Maxn \text{Maxn} Maxn 为序列 a a a的最大数值)

0x2. 关于优化的解法( 60 pts \text{60 pts} 60 pts)

莫队 或 玄学数据结构 或 类似正解的方法

示例代码略:

莫队离散化 + 前驱思想。

时间复杂度分析

平均时间复杂度为 O ( m n ) O(m\sqrt{n}) O(mn ).

0x3. 关于正解( 100 pts \text{100 pts} 100 pts)

正解算法: 离线 + 树状数组
对于正解有以下结论:( ⨁ \bigoplus 为异或运算)

  1. 区间内出现偶数次的异或和 = = = 区间内出现奇数次的数的异或和 ⨁ \bigoplus 区间内出现过的数的异或和
  2. 区间内出现奇数次的数的异或和 = = = 区间所有数的异或和

所以,区间内出现偶数次的数异或和 = = = 区间所有数的异或和 ⨁ \bigoplus 区间内出现过的数的异或和, 于是问题就转化成了区间内出现过数的异或和。

利用树状数组维护区间内不同数的异或和, 注意先以左端点先排序。
如果,当前的数之前出现过,则将他之前位置上的影响删除,利用异或的性质,偶数次数操作可以消除对该区间的影响。

然后不管之前什么操作,再把它在当前位置的影响插入,并更新到最后的答案数组。

之前这些操作类似于莫队的某些操作,但没有莫队的某些操作那么繁琐。

map \text{map} map可能会慢一些,那么就使用 unordered_map \text{unordered\_map} unordered_map吧。

代码如下:(by vibrant72)

#include<cstdio>
#include<algorithm>
#include<tr1/unordered_map>
#define re register
#define inl inline
using namespace std;
using namespace tr1;
typedef long long ll;
const int maxn = 2e6 + 5;
int a[maxn], tree[maxn], ans[maxn], sum[maxn], n, q;
struct node {int l, r, id;} op[maxn];
unordered_map<int, int> mm;
inl int cmp(re node x, node y){return x.r < y.r;}
inl int lowbit(re int x) {return x&(-x);}
inl void update(re int pos, int val) {
	while(pos < maxn) {
		tree[pos] ^= val;
		pos += lowbit(pos);
	}
}
inl int query(re int pos) {
	int res = 0;
	while(pos) {
		res ^= tree[pos];
		pos -= lowbit(pos);
	}return res;
}
int main() {
	scanf("%d%d",&n,&q);
	sum[0] = 0;re int i,p=1;
	for(i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		sum[i] = sum[i-1]^a[i];
	}
	for(i = 1; i <= q; i++)scanf("%d%d", &op[i].l, &op[i].r),op[i].id = i;
	sort(op+1, op+1+q,cmp);
	for(i = 1; i <= q; i++) {
		while(p <= n && p <= op[i].r) {
			if(mm[a[p]])update(mm[a[p]], a[p]);
			update(p,a[p]),mm[a[p]] = p,p++;
		}ans[op[i].id] = sum[op[i].r]^sum[op[i].l-1]^query(op[i].r)^query(op[i].l-1);
	}
	for(i = 1; i <= q; i++)printf("%d\n", ans[i]);
	return 0;
}

时间复杂度分析

平均时间复杂度为 O ( m log ⁡ n ) O(m \log n) O(mlogn),稳过 1.9 × 1 0 6 1.9 \times 10^6 1.9×106的数据平均 1 s 1s 1s左右。

出题总结

这道题的思路其实容易想到,但如果没想到,可以得到的部分分挺多的(虽然用了子任务),思维难度不高,代码难度不大,因为正解是学过的数据结构,所以出出来(借鉴原题)不会被喷吧。

出题参考借鉴资料

  1. CF703D
  2. 区间出现偶数次的数的异或和

Thinks.

by vibrant72 on Oct. 1st 2020 Thursday.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值