Luogu3760 TJOI2017 異或和 位運算、樹狀陣列
阿新 • • 發佈:2018-11-07
題意:給出一個長度為$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 }