题目大意
给出一个长度为 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. 1≤n,m≤5×103,1≤ai≤10.
- 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. 1≤n,m≤5×104,1≤ai≤106.
- 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. 1≤n,m≤1.9×106,1≤ai≤109.
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
⨁为异或运算)
- 区间内出现偶数次的异或和 = = = 区间内出现奇数次的数的异或和 ⨁ \bigoplus ⨁ 区间内出现过的数的异或和
- 区间内出现奇数次的数的异或和 = = = 区间所有数的异或和
所以,区间内出现偶数次的数异或和 = = = 区间所有数的异或和 ⨁ \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左右。
出题总结
这道题的思路其实容易想到,但如果没想到,可以得到的部分分挺多的(虽然用了子任务),思维难度不高,代码难度不大,因为正解是学过的数据结构,所以出出来(借鉴原题)不会被喷吧。
出题参考借鉴资料
Thinks.
by vibrant72 on Oct. 1st 2020 Thursday.