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;
}