1. 程式人生 > >[TJOI2017]異或和

[TJOI2017]異或和

nbsp can ans add 今天 namespace ios 格式 cout

題目描述

在加裏敦中學的小明最近愛上了數學競賽,很多數學競賽的題都是與序列的連續和相關的。所以對於一個序列,求出它們所有的連續和來說,小明覺得十分的 簡單。但今天小明遇到了一個序列和的難題,這個題目不僅要求你快速的求出所有的連續和,還要快速的求出這些連續和的異或值。小明很快的就求出了所有的連續 和,但是小明要考考你,在不告訴連續和的情況下,讓你快速求是序列所有連續和的異或值。

輸入輸出格式

輸入格式:

第一行輸入一個n,表示這序列的數序列 第二行輸入n個數字a1,a2...an代表這個序列

0<=a1,a2,...an,0<=a1+a2...+an<=10^6

輸出格式:

輸出這個序列所有的連續和的異或值

輸入輸出樣例

輸入樣例#1:
3
1 2 3
輸出樣例#1:
0

說明

【樣例解釋】

序列1 2 3有6個連續和,它們分別是1 2 3 3 5 6,則1 xor 2 xor 3 xor 3 xor 5 xor 6 = 0

【數據範圍】

對於20%的數據,1<=n<=100

對於100%的數據,1<=n <= 10^5

一般這種異或都是按位一位一位做的

對於某第k位,如果為1,那麽說明

所有連續和異或的這第k位為1

如果滿足這第k位為1的(s[i]-s[j])有cnt個

如果cnt為奇數,那麽說明答案的第k位也等於1

如何求出第k位為1的(i,j)對數?

如果sum[i]第k位為1:

為了使第k位為1,要麽sum[j]第k位為0且sum[j]前k-1位小於sum[i]前k-1位的大小

原因是如果紅色條件不成立,進位後就變成了0

還有就是sum[j]第k位為1且sum[j]前k-1位大於sum[i]前k-1位的大小

同理,也是進位的問題

那麽紅色部分要求滿足大小關系的對數,用兩個樹狀數組就行

第k位為0同理

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 int
c[1000001][2],s[100001],a[100001],ans; 7 int pw[21],n; 8 void add(int x,int y) 9 { 10 while (x<=1000000) 11 { 12 c[x][y]++; 13 x+=(x&(-x)); 14 } 15 } 16 int query(int x,int y) 17 { 18 int sum=0; 19 while (x) 20 { 21 sum+=c[x][y]; 22 x-=(x&(-x)); 23 } 24 return sum; 25 } 26 int main() 27 {int i,j,flag,cnt; 28 cin>>n; 29 for (i=1;i<=n;i++) 30 { 31 scanf("%d",&s[i]); 32 s[i]+=s[i-1]; 33 } 34 pw[0]=1; 35 for (i=1;i<=20;i++) 36 pw[i]=pw[i-1]*2; 37 for (i=0;i<=20;i++) 38 if (pw[i]<=s[n]) 39 { 40 memset(c,0,sizeof(c)); 41 flag=0; 42 add(1,0); 43 for (j=1;j<=n;j++) 44 { 45 int tmp=s[j]&pw[i]; 46 if (tmp) cnt=query(a[j]+1,0)+query(1000001,1)-query(a[j]+1,1); 47 else cnt=query(a[j]+1,1)+query(1000001,0)-query(a[j]+1,0); 48 if (cnt%2==1) flag^=1; 49 add(a[j]+1,(bool)tmp); 50 if (tmp) a[j]|=pw[i]; 51 } 52 if (flag) ans|=(pw[i]); 53 } 54 cout<<ans; 55 }

[TJOI2017]異或和