Sequence NOIp2013-Training Series #4

Description

【分析】(感谢nodgd同学)

        所有的v[i]转化为二进制之后,我们发现无论怎么异或,不同位的答案是互不影响的
        于是我们不妨一位一位的讨论:
            假设所有a[i]的二进制第j位组成一个只含有0,1的序列b[i],那么b[i]的从l到r的区间的异或的值只与b[i]从l到r的"1"的个数有关。
            假设b[i]的前缀和为sum[i]
                    ·如果sum[i]是奇数,那么任意一个j<i的偶数sum[j]都可以使b[i]的区间从j+1到i是奇数个1;
                    ·如果sum[i]是偶数,那么任意一个j<i的奇数sum[j]都可以使b[i]的区间从j+1到i是奇数个1;
        于是我们只需要统计sum[i]有多少个奇数多少个偶数就能够求出,以i结尾的区间有多少个异或答案为1
        由于这是二进制的某一位,所以在加答案的时候记得要左移多少位


【代码】

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN=100005;
int N,v[MAXN];
void _in(int &x)
{
	char t=getchar();
	while(t<'0'||'9'<t) t=getchar();
	for(x=0;'0'<=t&&t<='9';x=x*10+t-'0',t=getchar());
}
void _init()
{
	_in(N);
	for(int i=1;i<=N;i++)
		_in(v[i]);
}
void _solve()
{
	long long ans=0;
	for(int j=0;j<=30;j++) //v[i]<=10^9,二进制不超过30位
	{
		long long s=(1<<j);   //讨论二进制的第j位
		long long c1=0,c2=1,sum=0;
		//一开始前缀和为0。为了省空间没有开sum数组
		//c1表示已经有多少个奇数的sum[i]
		//c2表示已经有多少个偶数的sum[i],由于sum[0]=0是偶数,所以初值为1
		for(int i=1;i<=N;i++)   //直接扫一遍所有v[i]
		{
			if((v[i]&s)) sum++;
				//如果a[i]这一位是"1" 那么sum[i]=sum[i-1]+1;否则不变
			if(sum&1)   //sum[i]是奇数的情况
			{
				c1++;
				ans+=c2*s;   //与前面c2个sum[i]偶数,可以异或成"1"
			}
			else      //sum[i]是偶数的情况
			{
				c2++;
				ans+=c1*s;     //与前面c1个sum[i]奇数,可以异或成"1"
			}
		}
	}
	cout<<ans<<endl;
}
int main()
{
	_init();
	_solve();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值