题意:给出一个长度为$N$的非负整数序列,求其中所有连续区间的区间和的异或值。$N \leq 10^5$,所有元素之和$\leq 10^6$
设序列的前缀和为$s_i$,特殊地,$s_0=0$
因为最后答案是一个异或值,所以我们考虑按位计算答案,也就是计算所有区间和中某一位上的$1$的个数。
考虑区间$[j+1,i]$在第$t$位上是否产生贡献,假设$s_i$的第$0$到$t-1$位的数字为$x$,$s_j$的第$0$到$t-1$位的数字为$y$,分类讨论:
①如果$s_i$的第$t$位为$1$,则$s_j$要么$t$位为$0$且$y<x$,要么$t$位为$1$且$y>x$
②如果$s_i$第$t$位为$0$,情况与上面相反。
可以发现不论什么情况,会产生贡献的$s_j$在$0-t$位上的取值表现为一段区间。于是使用树状数组维护这一段区间,每一次计算$s_i$对应的答案,然后把$s_i \text{& (1 << t) - 1}$丢入树状数组即可。复杂度$O(nlognlog10^6)$
1 #include<bits/stdc++.h>
2 using namespace std;
3
4 inline int read(){
5 int a = 0;
6 char c = getchar();
7 while(!isdigit(c))
8 c = getchar();
9 while(isdigit(c)){
10 a = (a << 3) + (a << 1) + (c ^ '0');
11 c = getchar();
12 }
13 return a;
14 }
15
16 const int MAXN = 1e5 , MAXM = 2e6;
17 int treeNum[MAXM + 10];
18 int num[MAXN + 10] , N;
19
20 inline int lowbit(int now){
21 return now & -now;
22 }
23
24 inline void add(int now){
25 now++;
26 while(now < MAXM){
27 treeNum[now]++;
28 now += lowbit(now);
29 }
30 }
31
32 inline int getSum(int now){
33 now++;
34 int sum = 0;
35 while(now){
36 sum += treeNum[now];
37 now -= lowbit(now);
38 }
39 return sum;
40 }
41
42 int main(){
43 N = read();
44 int ans = 0;
45 for(int i = 1 ; i <= N ; i++)
46 num[i] = read() + num[i - 1];
47 for(int i = 0 ; i < 20 ; i++){
48 memset(treeNum , 0 , sizeof(treeNum));
49 int cnt = 0;
50 for(int j = 0 ; j <= N ; j++){
51 if(num[j] & (1 << i))
52 cnt = (cnt + getSum((1 << i + 1) - 1) - getSum(num[j] & (1 << i + 1) - 1) + getSum(num[j] & (1 << i) - 1)) & 1;
53 else
54 cnt = (cnt + getSum(((num[j] & (1 << i) - 1) | 1 << i)) - getSum(num[j] & (1 << i) - 1)) & 1;
55 add(num[j] & (1 << i + 1) - 1);
56 }
57 ans += cnt * (1 << i);
58 }
59 cout << ans;
60 return 0;
61 }