P3917 异或序列(位运算)

P3917 异或序列(位运算)

传送门

题意:给定序列求所有区间异或和的和。

思路:经典异或题,显然需要按照位来计算贡献。我们需要先预处理前缀异或和,这样便于计算区间。对于当前位ii,我们只需看这个区间的异或和的该位是否为1,也就是该区间该位为1的个数为奇数,记cnt[j]cnt[j]为前jj个数异或和位为11的个数,显然对于区间[l,r][l,r]我们只需让cnt[r]cnt[l1]cnt[r]\oplus cnt[l-1]为奇数,即cnt[l1],cnt[r]cnt[l-1],cnt[r]奇偶性不同即可。

所以统计前缀和有多少个1,每个1都可以与剩下的0组成区间,对于[0,r][0,r],显然0的个数是r+1cnt[r]r+1-cnt[r]。利用乘法原理再乘上2i2^i 即是该位的贡献。

时间复杂度:O(32n)O(32n)

#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:不用预处前缀异或和,直接计算区间为奇数的个数,类似dpdp的思想,

如果a[i]a[i]的第jj位为1,进行交换0,10,1片段,sum=isumsum=i-sum, 因为之前的以i1sum第i-1个数的结尾的区间个数为sum,再加上该位的11,区间1变成偶数个,所以应该取区间为00的部分作为贡献。 时间复杂度: O(32n)O(32n)

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章