http://www.elijahqi.win/archives/3123
Description
在加里敦中学的小明最近爱上了数学竞赛,很多数学竞赛的题都是与序列的连续和相关的。
所以对于一个序列,求出它们所有的连续和来说,小明觉得十分的简单。但今天小明遇到了
一个序列和的难题,这个题目不仅要求你快速的求出所有的连续和,还要快速的求出这些连
续和的异或值。小明很快的就求出了所有的连续和,但是小明要考考你,在不告诉连续和的
情况下,让你快速求是序列所有连续和的异或值。
Input
第一行输入一个n,表示这序列的数序列 第二行输入n个数字a1,a2…an代表这个序列
0<=a1,a2,…an,0<=a1+a2…+an<=10^6
1<=n <= 10^5
Output
输出这个序列所有的连续和的异或值
Sample Input
3
1 2 3
Sample Output
0
HINT
Source
树状数组:
仍然预处理前缀和
考虑按位维护答案 如果只考虑这一位的话 那么我考虑枚举每个前缀作为终点 然后再去枚举前面的前缀作为起点的情况 那考虑竖式加减法的操作 考虑我当前这一位如果是1 那么我就需要在前面找一个当前这一位是0的位置 并且(0~k-1)位所构成的数需要小于等于我(0~k-1)位所构成的数 或者 找前面第k位是1 并且(0~k-1)位构成的数比我(0~k-1)位所构成的数要大 都是答案
另外一种情况如果我这一位是1 同理
那么我就需要在前面找一个当前这一位是1的位置 并且(0~k-1)位所构成的数需要小于等于我(0~k-1)位所构成的数 或者 找前面第k位是0 并且(0~k-1)位构成的数比我(0~k-1)位所构成的数要大 都是答案
考虑快速维护这个有多少个 就使用权值树状数组即可 可能会出现0等情况 提前判断+1即可
注意存在前缀和为0的情况..
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
inline char gc(){
static char now[1<<16],*S,*T;
if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
return x*f;
}
const int N=1e6+100;
int s[2][N],ans,sum[N],lim,n;
inline void add(int op,int x,int v){++x;
while(x<=lim+5) s[op][x]+=v,x+=x&-x;
}
inline int query(int op,int x){static int t;++x;
t=0;while(x) t+=s[op][x],x-=x&-x;return t&1;
}
inline int get(int op,int x,int y){return (query(op,y)-query(op,x)+2)&1;}
inline void clear(){
for (int owo=0;owo<=1;++owo)
for (int i=1;i<=lim+5;++i) s[owo][i]=0;
}
int main(){
freopen("bzoj4888.in","r",stdin);
n=read();for (int i=1;i<=n;++i) sum[i]=sum[i-1]+read();
lim=sum[n];int t=0;while(lim) ++t,lim>>=1;lim=sum[n];
for (int k=0;k<t;++k){static int bin,lst,cnt;
bin=1<<k;cnt=0;
for (int i=0;i<=n;++i){lst=sum[i]&(bin-1);
if (sum[i]&bin){
cnt+=query(0,lst);cnt+=get(1,lst,bin-1);add(1,lst,1);
}else{
cnt+=query(1,lst);cnt+=get(0,lst,bin-1);add(0,lst,1);
}cnt&=1;
}ans+=cnt*bin;clear();
}printf("%d\n",ans);
return 0;
}
考虑每种权值出现的情况 设sn表示所有权值的前缀和 那么这个显然可以用所有前缀表示所有的区间和 那么设 A(x)=1+x1+x2+...xsn A ( x ) = 1 + x 1 + x 2 + . . . x s n B(x)=1+x1+x2+...xsn B ( x ) = 1 + x 1 + x 2 + . . . x s n 最后答案是 ∑j=1n∑i=1sn−ja[i]∗b[i+j] ∑ j = 1 n ∑ i = 1 s n − j a [ i ] ∗ b [ i + j ] 于是参考快速傅里叶2 那么直接将其中一个串反过来ntt即可 但是蒟蒻我没有优秀的常数bzoj过不去..
#include<cstdio>
#include<cctype>
#include<algorithm>
#define ll long long
using namespace std;
inline char gc(){
static char now[1<<16],*S,*T;
if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
return x*f;
}
const int mod=998244353;
const int g=3;
const int N=1e6+20;
int a[N<<2],b[N<<2],R[N<<2];
int s[110000],nn,n,m;
ll inv;
inline int ksm(ll b,int t){static ll tmp;
tmp=1;for (;t;b=b*b%mod,t>>=1)if (t&1) tmp=tmp*b%mod;return tmp;
}
inline int inc(int x,int v){x+=v;return x>=mod?x-mod:x;}
inline int dec(int x,int v){x-=v;return x<0?x+mod:x;}
inline void ntt(int *x,int f){
for (int i=0;i<n;++i) if (i<R[i]) swap(x[i],x[R[i]]);
for (int i=1;i<n;i<<=1){
int wn=ksm(g,f==1?(mod-1)/(i<<1):mod-1-(mod-1)/(i<<1));
for (int j=0;j<n;j+=i<<1){
ll w=1,t1,t2;int tmp1,tmp2;
for (int k=0;k<i;++k,w=w*wn%mod){
t1=x[j+k],t2=w*x[j+k+i]%mod;
tmp1=inc(t1,t2);tmp2=dec(t1,t2);
x[j+k]=tmp1;x[j+k+i]=tmp2;
}
}
}
if (f==-1) for (int i=0;i<n;++i) x[i]=inv*x[i]%mod;
}
int main(){
freopen("bzoj4888.in","r",stdin);
nn=read();
for (int i=1;i<=nn;++i) s[i]=s[i-1]+read();
for (int i=0;i<=nn;++i) ++a[s[i]],++b[s[nn]-s[i]];
m=s[nn]<<1;int l=0;for (n=1;n<m;n<<=1,++l);inv=ksm(n,mod-2);
for (int i=0;i<n;++i) R[i]=(R[i>>1]>>1)|(i&1)<<l-1;
ntt(a,1);ntt(b,1);
for (int i=0;i<n;++i) a[i]=(ll)a[i]*b[i]%mod;
ntt(a,-1);int ans=0;
for (int i=s[nn];i<=s[nn]<<1;++i) if (a[i]&1) ans^=i-s[nn];
printf("%d\n",ans);
return 0;
}