1. 程式人生 > >Luogu3760 TJOI2017 異或和 位運算、樹狀陣列

Luogu3760 TJOI2017 異或和 位運算、樹狀陣列

傳送門

題意:給出一個長度為$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 }