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;
ll n;
void solve()
{
cin >> n;
vector<ll>a(n + 1);
vector num(n + 1, vector<ll>(32));
for(int i=1; i<=n; i++)
{
cin >> a[i];
// check corresponding digit
for(int j=0; j<32; j++)
num[i][j] = (a[i] >> j) & 1;
}
// prefix
for(int i=1; i<=n; i++)
for(int j=0; j<32; j++)
num[i][j] ^= num[i-1][j];
// answer, current digit
ll ans = 0, base = 1;
for(int j=0; j<32; j++)
{
ll num0 = 1, num1 = 0;
for(int i=1; i<=n; i++)
{
if(num[i][j])
num1++;
else
num0++;
}
ans += num0 * num1 * base;
base <<= 1;
}
cout << ans << '\n';
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
solve();
return 0;
}
}