异或sum

题目

题意

在这里插入图片描述

解法

单独看每一位。记录数组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();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值