Sum of XOR Functions—CF1879D
参考文章
这个题真的很难,比我以前写过的1700分的题都难。
由于这个是edu场的题,网上的题解很少,找个好长时间,最后找到一个比较好理解的题解。
思路
这道题用了“拆位法”。jls几乎没思考就写出来了,但我都没有听说过这个方法,还是写的题太少了。
题目中涉及到位运算,不难发现,不同的二进制位之间对答案的贡献是互不影响的。所以我们就可以考虑使用拆位法。
如此,对于
n
n
n 个元素二进制中的第
k
k
k 位,把它们按顺序组成一个
01
01
01 字符串,
a
a
a 数组二进制第
k
k
k 位的总贡献就是 now_res
,其中 now_res
= “所有包含奇数个
1
1
1 的区间”的区间长度之和。
易知:一个区间可以看作是两个前缀相减的结果,因此,如果我们要求的区间 [ l , r ] [l,~r] [l, r] 需要满足有奇数个 1 1 1,那么需要满足: [ 1 , l ] [1,~l] [1, l] 有偶数个 1 1 1 且 [ 1 , r ] [1,~r] [1, r] 有奇数个 1 1 1,或者是满足 [ 1 , l ] [1,~l] [1, l] 有奇数个 1 1 1 且 [ l , r ] [l,~r] [l, r] 有偶数个 1 1 1;
C o d e Code Code
#include <bits/stdc++.h>
#define int long long
#define sz(a) ((int)a.size())
#define all(a) a.begin(), a.end()
using namespace std;
using PII = pair<int, int>;
using i128 = __int128;
const int N = 3e5 + 10;
const int mod = 998244353;
int n;
int a[N];
void solve() {
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
int res = 0;
for (int k = 0; k <= 30; k ++) { // 因为不同位互相独立,所以可以分别计算每一位的贡献
int num[2] = {1, 0}, sum[2] = {0, 0};
int parity = 0;
int now_res = 0;
for (int i = 1; i <= n; i ++) {
parity = (parity + (a[i] >> k & 1)) % 2;
now_res = (now_res + num[1 - parity] * i - sum[1 - parity]) % mod;
num[parity] ++;
sum[parity] += i;
}
res = (res + now_res * (1 << k) % mod) % mod;
}
cout << " ";
cout << res << "\n";
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T; cin.get();
while (T --) solve();
return 0;
}