ABC365E 题解

考虑拆位计算,算每一位的答案。

那原题就转化成在 0,10,1 序列上的求值。

对于快速求一段区间的异或和,显然想到先对每一位做个前缀异或和。

举样例 22 的第三位为例子:

0,1,1,1,0,0,10,1,1,1,0,0,1

做完前缀异或和后变为:

0,1,0,1,1,1,00,1,0,1,1,1,0

显然异或和为 00 是不会对答案有贡献。因为这是个 0,10,1 序列,所以对于每一个数,对答案的贡献是这个数之前与其值不同的个数。

例如:第 44 个数,它前面有 22 个与其不同(第 11 和第 33 个),所以贡献是 22(区间是 [2,4],[4,4][2,4],[4,4])。

然后就可以算了。

#include<bits/stdc++.h>
using namespace std;
inline void ri(int &_a){
	int res=0,f=1;
	char c;
	for(;(c=getchar())<'0'||c>'9';c=='-'?f=-f:0);
	while(c>='0' && c<='9') res=(res<<1) + (res<<3) + c-'0',c=getchar();
	_a=res*f;
}
inline void rll(long long &_a){
	long long res=0ll,f=1;
	char c;
	for(;(c=getchar())<'0'||c>'9';c=='-'?f=-f:0);
	while(c>='0' && c<='9') res=(res<<1) + (res<<3) + c-'0',c=getchar();
	_a=res*f;
}
/*-----------------*/
int n;
long long a[200010],ans;
int main(){
	ri(n);
	for(int i=1;i<=n;i++) rll(a[i]);
	for(int i=0;i<40;i++){
		int p=0,q=0,now=0;
		long long sum=0;
		for(int j=1;j<=n;j++){
			int up=now;
			now^=(a[j]&1);
			sum+=(now?p:q);
			a[j]>>=1;
			up?q++:p++;
		}
		ans+=1ll*sum*(1ll<<i);
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2024-08-09 20:53  fengxiaoyi  阅读(26)  评论(0)    收藏  举报  来源