0%

[TJOI2017]异或和

题面描述

连续和的异或值。

题解

题目实际要求出

\[ \begin{aligned} ans\ =\ \oplus \sum_{i=1}^n\sum_{j=0}^{i-1}sum_i-sum_j \end{aligned} \]

然后,我看了一下二进制加法器后直接弃疗 实际上,几乎所有二进制题都是按位考虑的

我们把它改写成按位考虑的形式

\[ \begin{aligned} ans = \sum_{k=0}^{2^k\le sum_n} ( \sum_{i=1}^n\sum_{j=0}^{i-1}(sum_i-sum_j)>>k\& 1\ (mod 2) )<<k \end{aligned} \]

按位考虑后实际上就是对于 \(sum_i\) 而言, 前面有多少 \(sum_j\)\(sum_i\) 差在 \(k\)\(1\)

\(sum_i\) 这一位为 \(1\) 则差仍为此的情况必定为 \(sum_j\) 这一位为 \(0\) 且之前没有借位

另一种情况恰好相反

所以问题在于是否借位,考虑竖式计算的过程,发现 当且仅当 \(sum_i \&(2^k-1)\ \ge\ sum_j\&(2^k-1)\) 不会借位

用权值树状数组维护一下就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
typedef long long int64;
using namespace std;
inline int read(int f = 1, int x = 0, char ch = ' ')
{
while(!isdigit(ch = getchar())) if(ch == '-') f = -1;
while(isdigit(ch)) x = x*10+ch-'0', ch = getchar();
return f*x;
}
const int N = 2e6+5;
int n, m, sum[N];
struct Fenwick
{
int64 c[N];
void clear() { memset(c, 0, sizeof(c)); }
void add(int x) { ++x; for( ; x <= m+1; x += x&-x) ++c[x]; }
int64 ask(int x) { int ret = 0 ; ++x; for( ; x; x -= x&-x) ret += c[x]; return ret; }
int64 query(int l, int r) { return ask(r)-ask(l-1); }
}t[2];
int ans;
int main()
{
n = read();
for(int i = 1; i <= n; ++i) sum[i] = sum[i-1]+read();
for(int k = 0; (1<<k) <= sum[n]; ++k)
{
m = (1<<k)-1; int64 p = 0;
t[0].clear(); t[1].clear();
for(int i = 0; i <= n; ++i)
{
int c = (sum[i]>>k&1), d = (sum[i]&m);
p += t[c^1].query(0, d)+t[c^0].query(d+1, m);
t[c].add(d);
}
ans += (1<<k)*(p%2);
}
printf("%d\n", ans);
return 0;
}