P3917 异或序列(位运算)
题意:给定序列求所有区间异或和的和。
思路:经典异或题,显然需要按照位来计算贡献。我们需要先预处理前缀异或和,这样便于计算区间。对于当前位,我们只需看这个区间的异或和的该位是否为1,也就是该区间该位为1的个数为奇数,记为前个数异或和位为的个数,显然对于区间我们只需让为奇数,即奇偶性不同即可。
所以统计前缀和有多少个1,每个1都可以与剩下的0组成区间,对于,显然0的个数是。利用乘法原理再乘上 即是该位的贡献。
时间复杂度:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
int a[N],n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]^=a[i-1];
ll ans=0;
for(int i=0,x=(1<<i);i<32;i++,x<<=1){
int cnt=0;
for(int j=1;j<=n;j++)
if((a[j]>>i)&1) cnt++;
ans+=1LL*cnt*(n+1-cnt)*x;
}
printf("%lld\n",ans);
return 0;
}
思路2:不用预处前缀异或和,直接计算区间为奇数的个数,类似的思想,
如果的第位为1,进行交换片段,, 因为之前的以,再加上该位的,区间1变成偶数个,所以应该取区间为的部分作为贡献。 时间复杂度:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first
#define se second
int a[N],n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
ll ans=0;
for(int i=0,x=(1<<i);i<32;i++,x<<=1){
ll sum=0,cnt=0;
for(int j=1;j<=n;j++){
if((a[j]>>i)&1) sum=j-sum;
cnt+=sum;
}
ans+=cnt*x;
}
printf("%lld\n",ans);
return 0;
}