[TJOI2017] 異或和


題面

 

    由於本題求和的優先級高於異或,所以最后其實是一坨求和之后的數再異或起來,於是我們就可以拆位做啦。

    然后原問題簡化成了:對於每一位,有多少區間和在這一位上是1。

    假如現在處理到 2^i 這位,然后我們枚舉區間右端點j,想要可以快速找到所有左端點l,滿足 sum[j] - sum[l-1] 在 2^i 這位為1(為0不用管,對異或無影響)。

    可以發現 sum[j] > sum[l-1] 必然成立,不妨設 lef[x] 為每個位置的數 僅保留后 i 位 得到的數 (即2^0,2^1,.....,2^(i-1)這些位),那么可以得到:

        lef[x] >= lef[l-1] 時,當且僅當 sum[j]^sum[l-1] 在 2^i 這位為1時,j與l-1是一個我們需要的區間;

        類似的,lef[x]<lef[l-1]時,當且僅當 sum[j]^sum[l-1] 在 2^i 這位為0時,j與l-1是一個我們需要的區間;

 

然后這就是個樹狀數組題了23333(后面還有個細節,因為樹狀數組里面最小的下標是1,所以要對下標做一些修改)

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5,M=1e6+5;

inline int read(){
    int x=0; char ch=getchar();
    for(;!isdigit(ch);ch=getchar());
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x;
}

int n,a[N],f[2][M],ans,i,now,oe; 

inline void update(int T,int x){
	for(;x<=i;x+=x&-x) f[T][x]^=1;
}

inline int query(int T,int x){
	int an=0;
	for(;x;x-=x&-x) an^=f[T][x];
	return an;
}

int main(){
	n=read();
	for(i=1;i<=n;i++) a[i]=read()+a[i-1];
	
	for(i=1,now=0,oe;i<=a[n];now|=i,i<<=1){
		memset(f,0,sizeof(f)),oe=0;
		
		update(0,1);
		for(int j=1,u,p;j<=n;j++){
			u=(a[j]&i)?1:0,p=(a[j]&now)+1;
			oe^=query(u^1,p)^query(u,i)^query(u,p);
			update(u,p);
		}
		
		ans+=i*oe;
	}
	
	printf("%d\n",ans);
	return 0;
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM