BZOJ4017 小Q的无敌异或 好题

给一个序列

询问这个序列

1:所有子区间的异或值的和

2:所有子区间的和的异或值

第一个操作,拆二进制位,枚举右端点r,记录这个位前r个数字0/1的个数,右端点转移O(1)

第二个操作比较复杂,对于每个右端点要询问sum[r]-sum[l-1]mod(2^(k+1))>=2^k的左端点个数(的奇偶性)

题解用了树状数组维护,参考skywalkert,题解里对于模不等式的解释很巧妙,复习的时候多看看吧

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<string.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<stack>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mem(a) memset(a,0,sizeof a)
#define FOR(a) for(int i=1;i<=a;i++)
 
const int inf =0x3f3f3f3f; 
const int maxn=1e5+7;
const int mod=998244353;

int n;
int a[maxn],x[maxn],bit[maxn],ans1;
ll s[maxn],p[maxn],ans2;

void inc(int &x,int y){
	x+=y;if(x>=mod)x-=mod;
}

void add(int x){
	for(;x<=n;x+=~x&x+1)
		bit[x]^=1;
}
int sum(int x){
	int ret=0;
	for(;x>=0;x-=~x&x+1)
		ret^=bit[x];
	return ret;
}
int idx(ll val){
	//return lower_bound(p,p+n,val)-p;
	int L=-1,R=n;
	while(L<R){
		int M=L+R+1>>1;
		if(p[M]<=val)L=M;
		else R=M-1;
	}
	return L;	
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		x[i]=x[i-1]^a[i];
		s[i]=s[i-1]+a[i];
	}	
	//异或的和
	for(int k=0,powk=1;k<30;++k,inc(powk,powk)){	//枚举指数
		int cnt[2]={},tmp=0;
		for(int i=0;i<=n;i++){
			inc(tmp,cnt[((x[i]>>k)&1)^1]);
			++cnt[(x[i]>>k)&1];
		}
		inc(ans1,1ll*powk*tmp%mod);
	}

	//和的异或
	for(int k=0;(1ll<<k)<=s[n];++k){
		int tmp=0;
		for(int i=0;i<=n;i++){
			p[i]=s[i]&((1ll<<k+1)-1);	//取模
		}
		sort(p,p+n+1);
		memset(bit,0,sizeof bit);
		
		for(int i=0;i<=n;i++){
			ll now=s[i]&((1ll<<k+1)-1);
			add(idx(now));
			tmp^=sum(idx(now-(1ll<<k)))^sum(idx(now+(1ll<<k)))^sum(idx(now));
		}

		if(tmp)
			ans2|=1ll<<k;
	}

	printf("%d %lld\n",ans1,ans2);
}
 

posted @ 2017-11-10 00:47  Drenight  阅读(215)  评论(0编辑  收藏  举报