題目描述
在加里敦中學的小明最近愛上了數學競賽,很多數學競賽的題目都是與序列的連續和相關的。所以對於一個序列,求出它們所有的連續和來說,小明覺得十分的簡單。但今天小明遇到了一個序列和的難題,這個題目不僅要求你快速的求出所有的連續和,還要快速的求出這些連續和的異或值。小明很快的就求出了所有的連續和,但是小明想考考你,在不告訴連續和的情況下,讓你快速的求出序列所有的連續和的異或值。
輸入格式
第一行輸入一個\(n\),表示這序列的數字個數。
第二行輸入\(n\)個數字\(a_1,a_2,a_3\)...\(a_n\),代表這個序列。
\(0≤a_1,a_2,\)...\(,a_n,0≤a_1+a_2+\)...\(+a_n≤10^6\)。
輸出格式,
輸出這個序列所有的連續和的異或值。
數據范圍
對於\(20\%\)的數據,\(1≤n≤1000\)
對於\(100\%\)的數據,\(1≤n≤10^5\)
直接暴力\(O(n^2)\),可以獲得\(20\)分
考慮按位枚舉,題目保證了\(0≤a_1+a_2+\)...\(+a_n≤10^6\)
那么顯然,我們最多只需要枚舉\(20\)位
對於枚舉的每一位,我們希望快速地算出有多少和在這一位上有貢獻\(1\)
顯然,一個和我們可以通過前綴和的預處理,\(\sum_{i=l}^r=sum[r]-sum[l-1]\)
當我們知道了當前\(x\),\(sum[x]\)的第\(i\)位為\(1\)的時候,我們要知道以這一位為結束區間有多少貢獻為\(1\)的
顯然只有兩種情況,當\(sum[y](y<x)\)的第\(i\)位也是\(1\)的時候,\(y\)的后面幾位必須比\(x\)大才可以使得\(x\)的前面退位,使得區間\(\sum_{i=y-1}^x\)產生貢獻
當\(sum[y](y<x)\)的第\(i\)位也是\(0\)的時候,\(y\)的后面幾位必須比\(x\)小才可以保住\(x\)的貢獻
而滿足這樣的兩個條件,我們可以用樹狀數組來簡單地維護一下
\(x\)的第\(i\)位為\(0\)的時候,一樣的分析一下就好了
還有一個細節就是,計算貢獻的時候,可能答案會超過int,那就讓他自然溢出好了,就是最后計算的時候注意取模的正負即可
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<vector>
#include<cmath>
#include<map>
#define LL long long
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x){
char c=nc();int b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void read(LL &x){
char c=nc();LL b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline int read(char *s)
{
char c=nc();int len=0;
for(;!((c>='A' && c<='Z')||(c>='a' && c<='z'));c=nc()) if (c==EOF) return 0;
for(;((c>='A' && c<='Z')||(c>='a' && c<='z'));s[len++]=c,c=nc());
s[len++]='\0';
return len;
}
inline void read(char &x){
for (x=nc();!(x=='?' || x=='+' || x=='-');x=nc());
}
int wt,ss[19];
inline void print(int x){
if (x<0) x=-x,putchar('-');
if (!x) putchar(48); else {
for (wt=0;x;ss[++wt]=x%10,x/=10);
for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
if (x<0) x=-x,putchar('-');
if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}
int n,a[100010],p2[100],c0[2000010],c1[2000010],ans[100];
const int M=20;
void change0(int x,int y)
{
x++;
while (x<=p2[M]) c0[x]+=y,x+=x&(-x);
}
void change1(int x,int y)
{
x++;
while (x<=p2[M]) c1[x]+=y,x+=x&(-x);
}
int query0(int x)
{
x++;
int res=0;
while (x>0) res+=c0[x],x-=x&(-x);
return res;
}
int query1(int x)
{
x++;
int res=0;
while (x>0) res+=c1[x],x-=x&(-x);
return res;
}
int main()
{
read(n);
for (int i=1;i<=n;i++)
read(a[i]);
p2[0]=1;
for (int i=1;i<=M;i++) p2[i]=2*p2[i-1];
memset(ans,0,sizeof(ans));
for (int i=1;i<=M;i++)
{
int s=0;
for (int j=1;j<=n;j++)
{
s+=a[j];
if ((s&p2[i-1])!=0) ans[i]++;
if ((s&p2[i-1])==0)
{
ans[i]+=query0(p2[i-1]-1)-query0(s%p2[i-1]);
ans[i]+=query1(s%p2[i-1]);
}
else
{
ans[i]+=query0(s%p2[i-1]);
ans[i]+=query1(p2[i-1]-1)-query1(s%p2[i-1]);
}
if ((s&p2[i-1])==0) change0(s%p2[i-1],1);else change1(s%p2[i-1],1);
}
s=0;
for (int j=1;j<=n;j++)
{
s+=a[j];
if ((s&p2[i-1])==0) change0(s%p2[i-1],-1);else change1(s%p2[i-1],-1);
}
}
int res=0,s=1;
for (int i=1;i<=M;i++)
res+=s*abs(ans[i]%2),s*=2;
print(res),puts("");
return 0;
}
