【[TJOI2017]异或和】

这道题挺神仙的,毕竟这个异或是需要进位的

看到区间和我们很自然的就想到了前缀和

于是处理一下前缀和答案就变成了这个样子

\[⊕\sum_{i=1}^n\sum_{j=1}^{i}pre_i-pre_{j-1} \]

众所周知异或是应该按位处理的,但是这里是减法,所以还有进位需要处理

瞬间就感觉没有办法处理了

但是还是应该按位处理的,我们应该按位考虑这一位上最后的答案是什么

非常显然的一点是我们在考虑某一位的时候并不需要顾忌更靠前的位置,只需要考虑后面的位置就好了

所以我们按位考虑每一位的答案,用一个权值树状数组维护这个位置后面的数是什么

分情况讨论一下

  1. 如果这一位上是\(1\),那么后面的数无论多小都无需向前面的数借位,统计一下后面的数比他小的有多少个\(0\),比它大的有多少个\(1\)就好了

  2. 这一位是\(0\),那么后面的统计一下后面的数比他小的有多少个\(1\),同时往前最靠近的那一个\(1\),把这一位借过来之后在考虑影响就好了

复杂度\(O(nlog^2m)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define re register
#define maxn 100005
#define lowbit(x) ((x)&(-x))
#define LL long long
inline int read()
{
	char c=getchar();
	int x=0;
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9')
		x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
int a[maxn],pre[maxn];
int c[2][1000005];
LL ans=0;
int N;
inline void add(int op,int x)
{
	for(re int i=x;i<=N;i+=lowbit(i))
		c[op][i]++;
}
inline LL ask(int op,int x)
{
	LL ans_ask=0;
	for(re int i=x;i;i-=lowbit(i))
		ans_ask+=c[op][i];
	return ans_ask;
}
int n;
int main()
{
	n=read();
	for(re int i=1;i<=n;i++) a[i]=read();
	for(re int i=1;i<=n;i++) pre[i]=pre[i-1]+a[i];
	LL _0=1,_1=0;
	for(re int i=1;i<=n;i++)
	{
		if(pre[i]&1ll) ans+=_0,_1++;
			else ans+=_1,_0++;
	}
	if(ans&1ll) ans=1;
		else ans=0;
	for(re LL t=1;(1<<t)<=pre[n];t++)
	{
		LL now=0;
		memset(c,0,sizeof(c));
		N=(1ll<<(t))+1;
		add(0,1);
		for(re int i=1;i<=n;i++)
		{
			int tt=pre[i]%(1ll<<t)+1;
			if(pre[i]&(1ll<<t))
			{
				now+=ask(0,tt);
				now+=ask(1,N)-ask(1,tt);
			}
			else
			{
				now+=ask(1,tt);
				for(re int j=t+1;(1ll<<j)<=pre[i];j++)
				if(pre[i]&(1ll<<j))
				{
					int p=(1ll<<j)/(1ll<<t);
					now+=ask(((p-1)&1ll)^1ll,N)-ask(((p-1)&1ll)^1ll,tt);
					break;
				}
			}
			if(pre[i]&(1ll<<t)) add(1,tt);
				else add(0,tt);
		}
		if(now&1ll) ans+=(1ll<<t);
	}
	std::cout<<ans;
	return 0;
}
posted @ 2019-01-01 19:54  asuldb  阅读(190)  评论(0编辑  收藏  举报