原题链接
按每个数的二进制下一位一位计算贡献。
对于第(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;
}