Description
给出序列 A 1 , A 2 , ⋯ , A N A_1,A_2,\cdots,A_N A1,A2,⋯,AN,求
∑ 1 ≤ i ≤ j ≤ N A i ⨁ A i + 1 ⨁ ⋯ ⨁ A j \sum_{1\le i\le j\le N} A_i\bigoplus A_{i+1}\bigoplus\cdots\bigoplus A_j ∑1≤i≤j≤NAi⨁Ai+1⨁⋯⨁Aj
的值。其中, ⨁ \bigoplus ⨁表示按位异或。
Input
第1 行,1 个整数 N N N。
第2 行, N N N 个整数 A 1 , A 2 , ⋯ , A N A_1,A_2,\cdots,A_N A1,A2,⋯,AN。
Output
一个数,为表达式的值。
Solution
1.可以先将n个数拆分成二进制,处理后的数组进行前缀异或处理,得到前缀异或数组。
2.得到处理后,对于区间[L, R]
的异或值,我们可以用XOR[L-1] ^ XOR[R]
得到
3.基于此理论,对于每一位,该位上对答案的贡献即为[0, n]
上0的个数与1的个数与该位的基值之积
解释:
基于基本知识 0 ^ 1 = 1,求出0与1的个数,也就求出了前缀异或区间为0和1的区间,这两个区间作异或处理,得到的值为1,对答案有贡献。
特殊处理:
包含0这个特殊下标,需要将其加入到答案中,
不然会忽略掉所有1区间前方的异或值为1的区间。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MOD 1000000007
#define intmax 2147483647
#define memmax 0x7fffffff
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = x * 10 + ch - 48;
ch = getchar();
}
return x * f;
}
ll n;
ll a[100005];
ll num[100005][35];
void solve()
{
n = read();
for (int i = 1; i <= n; i++)
{
a[i] = read();
for (int j = 0; j < 32; j++)
num[i][j] = (a[i] >> j) & 1;
}
// 构建前缀异或数组
for (int i = 1; i <= n; i++)
for (int j = 0; j < 32; j++)
num[i][j] ^= num[i - 1][j];
ll ans = 0;
ll base = 1;
for (int j = 0; j < 32; j++)
{
ll num0 = 0, num1 = 0;
for (int i = 0; i <= n; i++)
{
if (num[i][j])
num1++;
else
num0++;
}
ans += num0 * num1 * base;
base <<= 1;
}
cout << ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
solve();
}