给一个序列
询问这个序列
1:所有子区间的异或值的和
2:所有子区间的和的异或值
第一个操作,拆二进制位,枚举右端点r,记录这个位前r个数字0/1的个数,右端点转移O(1)
第二个操作比较复杂,对于每个右端点要询问sum[r]-sum[l-1]mod(2^(k+1))>=2^k的左端点个数(的奇偶性)
题解用了树状数组维护,参考skywalkert,题解里对于模不等式的解释很巧妙,复习的时候多看看吧
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<stack>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mem(a) memset(a,0,sizeof a)
#define FOR(a) for(int i=1;i<=a;i++)
const int inf =0x3f3f3f3f;
const int maxn=1e5+7;
const int mod=998244353;
int n;
int a[maxn],x[maxn],bit[maxn],ans1;
ll s[maxn],p[maxn],ans2;
void inc(int &x,int y){
x+=y;if(x>=mod)x-=mod;
}
void add(int x){
for(;x<=n;x+=~x&x+1)
bit[x]^=1;
}
int sum(int x){
int ret=0;
for(;x>=0;x-=~x&x+1)
ret^=bit[x];
return ret;
}
int idx(ll val){
//return lower_bound(p,p+n,val)-p;
int L=-1,R=n;
while(L<R){
int M=L+R+1>>1;
if(p[M]<=val)L=M;
else R=M-1;
}
return L;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
x[i]=x[i-1]^a[i];
s[i]=s[i-1]+a[i];
}
//异或的和
for(int k=0,powk=1;k<30;++k,inc(powk,powk)){ //枚举指数
int cnt[2]={},tmp=0;
for(int i=0;i<=n;i++){
inc(tmp,cnt[((x[i]>>k)&1)^1]);
++cnt[(x[i]>>k)&1];
}
inc(ans1,1ll*powk*tmp%mod);
}
//和的异或
for(int k=0;(1ll<<k)<=s[n];++k){
int tmp=0;
for(int i=0;i<=n;i++){
p[i]=s[i]&((1ll<<k+1)-1); //取模
}
sort(p,p+n+1);
memset(bit,0,sizeof bit);
for(int i=0;i<=n;i++){
ll now=s[i]&((1ll<<k+1)-1);
add(idx(now));
tmp^=sum(idx(now-(1ll<<k)))^sum(idx(now+(1ll<<k)))^sum(idx(now));
}
if(tmp)
ans2|=1ll<<k;
}
printf("%d %lld\n",ans1,ans2);
}