洛谷3917 异或序列

原题链接

按每个数的二进制下一位一位计算贡献。
对于第(k)位,计算前缀异或和(S[1 o n] (S[0] = 0)),则求这一位所能产生贡献的次数即是求有多少区间满足异或值为(1),即求有多少([i,j] (1leqslant ileqslant j leqslant n))满足(S[i - 1] oplus S[j] = 1 (oplus ext{表示异或}))
有结论:满足要求的区间个数等于(S[0 o n])(1)的个数与(0)的个数的乘积。
而对于第(k)位,单个区间所产生贡献值为(2^k)
最后将其所有贡献累加起来即可。
时间复杂度(O(n imes log_2MAXNUM))

#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int a[N];
inline int re()
{
	int x = 0;
	char c = getchar();
	bool p = 0;
	for (; c < '0' || c > '9'; c = getchar())
		p |= c == '-';
	for (; c >= '0' && c <= '9'; c = getchar())
		x = x * 10 + c - '0';
	return p ? -x : x;
}
inline int maxn(int x, int y)
{
	return x > y ? x : y;
}
int main()
{
	int i, j, n, ma = 0, o, k, s_1, s_0;
	ll s = 0;
	n = re();
	for (i = 1; i <= n; i++)
	{
		a[i] = re();
		ma = maxn(ma, a[i]);
	}
	o = log2(ma);
	for (j = 0; j <= o; j++)
	{
		s_1 = k = 0;
		s_0 = 1;
		for (i = 1; i <= n; i++)
		{
			k ^= (a[i] >> j) & 1;
			k ? s_1++ : s_0++;
		}
		s += 1LL * s_1 * s_0 * (1 << j);
	}
	printf("%lld", s);
	return 0;
}
原文地址:https://www.cnblogs.com/Iowa-Battleship/p/9829500.html