题意
解法
单独看每一位。记录数组s[i]
为第k位的前缀异或和。假设区间[i,j]
可以产生贡献,那么是s[i-1]^s[j]=1
,产生的贡献是 (1<<k)*(j-i+1)
。
当考虑第k位的时候,我们可以这样计算贡献:
划分集合:区间右端点为j
(1<=j<=n).
讨论
1.当s[j]为1的时候,s[i]必须为0(i<j),(注意这个区间是[i+1,j]
),假设有x个s[i]为偶数,那此时贡献为
x*j-sum (sum表示所有i<j中, s[i]=0时i的总和)
2.当s[j]为0的时候,与上述相同
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
ll n;
ll a[N];
ll s[N];//前缀异或和
ll cnt[N][2];//表示前i个数字,s[j]为0的下标总和
const ll mol=998244353;
void solve() {
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
ll ans=0;
for(ll j=0;j<32;j++){
ll quan=(1ll<<j);
ll s0=1,s1=0;//表示前i个数字,0和1的个数,注意s0初始化为1
for(ll i=1;i<=n;i++){
if(a[i]>>j&1){
s[i]=1;
}
else s[i]=0;
s[i]^=s[i-1]; //计算前缀异或和
//计算答案
if(s[i]){
ans=(ans+quan*(s0*i%mol-cnt[i-1][0]%mol+mol)%mol)%mol;
}
else{
ans=(ans+quan*(s1*i%mol-cnt[i-1][1]%mol+mol)%mol)%mol;
}
//更新cnt和s0,s1
if(s[i]){
s1++;
cnt[i][1]=cnt[i-1][1]+i;
cnt[i][0]=cnt[i-1][0];
}
else{
s0++;
cnt[i][0]=cnt[i-1][0]+i;
cnt[i][1]=cnt[i-1][1];
}
cnt[i][0]%=mol;
cnt[i][1]%=mol;
}
}
cout<<ans<<endl;
}
int main() {
int t=1;
while(t--)
solve();
}