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