一般和异或相关的求和都是一位一位来的。这题也一样。
首先看第一问。令sum[i]=a[1]^a[2]^...^a[i],那么xor(l,r)=sum[l-1]^sum[r],考虑每一位对答案带来的影响。假设现在考虑二进制第k位(从低到高)对答案的影响。
对于sum[x],它的第k位对答案的影响为2^k*t,其中t为1..x-1中sum[]值二进制第k位与sum[x]不同的数的个数。那么在k确定的情况下O(N)扫一遍即可。时间复杂度O(Nmax{log A})。
然后看第二问,继续考虑第k位对答案的影响。令sum[i]=sum[i-1]+a[i],则sum(l,r)=sum[r]-sum[l-1],那么第k位对答案有影响当且仅当存在奇数对(x,y),满足x<=y且sum[x-1]和sum[y]的第k为不同。考虑在mod (2^(k+1))的意义下,任意;两个sum[l]和sum[r],它们的第k为不同,当且仅当sum[r]-sum[l]>=2^k,或者sum[r]+2^k+1-sum[l]>=2^k。因此只需要考虑有多少对(x,y),x<=y,满足sum[y]-sum[x-1]>=2^k 或者sum[y]<sum[x-1] 且 sum[x-1]-sum[y]<=2^k即可,用树状数组维护即可。时间复杂度O(NlogNlog sum|A|)。
AC代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define N 100005
#define mod 998244353
using namespace std;
int n,cnt,a[N],c[N]; ll m,sum[N],num[N],hash[N],p[N];
void ins(int x,int t){
for (; x<=cnt; x+=x&-x) c[x]^=t;
}
int getxor(int x){
int t=0; for (; x; x-=x&-x) t^=c[x];
return t;
}
int find(ll x){
if (x<0) return 0; int l=1,r=cnt+1;
while (l+1<r){
int mid=(l+r)>>1;
if (hash[mid]<=x) l=mid; else r=mid;
}
return l;
}
int main(){
scanf("%d",&n); int i; ll k,ans=0;
for (i=1; i<=n; i++){
scanf("%d",&a[i]); sum[i]=sum[i-1]^a[i];
m=max(m,sum[i]);
}
for (k=1; k<=m; k<<=1){
c[0]=1; c[1]=0;
for (i=1; i<=n; i++){
int tmp=(sum[i]&k)?1:0;
ans=(ans+k*c[tmp^1]%mod)%mod; c[tmp]++;
}
}
printf("%lld ",ans); ans=0;
for (i=1; i<=n; i++) sum[i]=sum[i-1]+a[i];
for (k=1; k<=sum[n]; k<<=1){
for (i=0; i<=n; i++) num[i]=p[i]=sum[i]&((k<<1)-1);
sort(num,num+n+1); hash[cnt=1]=num[0];
memset(c,0,sizeof(c));
for (i=1; i<=n; i++)
if (num[i]!=num[i-1]) hash[++cnt]=num[i];
int tmp=0;
for (i=0; i<=n; i++){
tmp^=getxor(find(p[i]-k))^getxor(find(p[i]))^getxor(find(p[i]+k));
ins(find(p[i]),1);
}
if (tmp) ans|=k;
}
printf("%lld\n",ans);
return 0;
}
by lych
2016.2.27