[TJOI2017]异或和
题目描述
在加里敦中学的小明最近爱上了数学竞赛,很多数学竞赛的题都是与序列的连续和相关的。所以对于一个序列,求出它们所有的连续和来说,小明觉得十分的 简单。但今天小明遇到了一个序列和的难题,这个题目不仅要求你快速的求出所有的连续和,还要快速的求出这些连续和的异或值。小明很快的就求出了所有的连续 和,但是小明要考考你,在不告诉连续和的情况下,让你快速求是序列所有连续和的异或值。
输入输出格式
输入格式:第一行输入一个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 }