P3760 [TJOI2017] 异或和

[TJOI2017] 异或和

题目link

题目描述

在加里敦中学的小明最近爱上了数学竞赛,很多数学竞赛的题都是与序列的连续和相关的。 所以对于一个序列,求出它们所有的连续和来说,小明觉得十分的简单。

但今天小明遇到了一个序列和的难题,这个题目不仅要求你快速的求出所有的连续和(即字串和),还要快速的求出这些连续和的异或值。

小明很快的就求出了所有的连续和,但是小明要考考你,在不告诉连续和的情况下,让你快速求是序列所有连续和的异或值。

输入格式

第一行输入一个 \(n\),表示这序列的数序列
第二行输入 \(n\) 个非负整数 \(a_1,a_2 \dots a_n\) 代表这个序列

输出格式

输出这个序列所有的连续和的异或值

样例 #1

样例输入 #1

3
1 2 3

样例输出 #1

0

提示

【样例解释】

序列 \(1\) \(2\) \(3\)\(6\) 个连续和,它们分别是 \(1\) \(2\) \(3\) \(3\) \(5\) \(6\),则 \(1 \text{ xor } 2 \text{ xor } 3 \text{ xor } 3 \text{ xor } 5 \text{ xor } 6 = 0\)

【数据范围】

对于 \(20\%\) 的数据,\(1\le n \le 100\)
对于 \(100\%\) 的数据,\(1\le n \le 10^5\)\(\sum\limits_{i=1}^na_i \le 10^6\)


对于求答案的异或和这类问题,一般要对每一位分别考虑,看这一位出现的次数是否是奇数次,然后最终计算答案。

考虑如何计算第 \(k\) 位的贡献,题目要求所有连续子序列的和,那么快速的计算一端区间的和可以用前缀和来计算,对于一段区间 \([i, j]\) 的和,可以使用 \(s_j - s_{i-1}\) 来计算。如果 \(s_j - s_i\) 的第 \(k\) 位为 \(1\) ,那么可以有两种情况:

  • 如果 \(s_j\) 的低 \(k-1\) 位是大于等于 \(s_i\) 的低 \(k-1\) 位,那么当 \(s_j\) 的第 \(k\) 位和 \(s_i\) 的第 \(k\) 位不同的时候,\([i, j]\) 的区间和的第 \(k\) 位是 \(1\)
  • 如果 \(s_j\) 的低 \(k-1\) 位是小于 \(s_i\) 的低 \(k-1\) 位,那么当 \(s_j\) 的第 \(k\) 位和 \(s_i\) 的第 \(k\) 位相同的时候,\([i, j]\) 的区间和的第 \(k\) 位是 \(1\)

证明可以考虑手摸几个数据,注意 \(s_j\) 一定是 大于等于 \(s_i\)

然后可以使用两个权值树状数组,分别考虑第 \(k\) 位的为 \(1\)\(0\) 的时候,低 \(k-1\) 位的大小。

CODE

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

#define rep(i, a, b) for (int i(a); i <= b; ++ i ) 
#define dec(i, a, b) for (int i(b); i >= a; -- i ) 

template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }

template <typename T>
class fenwick {
 public:
  vector<T> fenw;
  int n;
 
  fenwick(int _n) : n(_n) {
    fenw.resize(n);
  }
 
  void modify(int x, T v) {
    while (x < n) {
      fenw[x] += v;
      x |= (x + 1);
    }
  }
 
  T get(int x) {
    T v{};
    while (x >= 0) {
      v += fenw[x];
      x = (x & (x + 1)) - 1;
    }
    return v;
  }

  T get(int l, int r) {
    return get(r) - get(l - 1);
  }

};

constexpr int N = 1E5 + 10;

int n, a[N], s[N];

void solve() {
  cin >> n;
  rep (i, 1, n) cin >> a[i], s[i] = s[i - 1] + a[i];  
  int ans = 0; 
  int m = 1000010;
  for (int i = 0; i < 20 && (1 << i) <= s[n]; i ++ ) {
    vector<fenwick<int> > f(2, fenwick<int>(m));
    int now = 0;
    f[0].modify(0, 1);
    for (int j = 1; j <= n; j ++ ) {
      int bit = s[j] >> i & 1; //当前位是 1 or 0
      int low = s[j] & ((1 << i) - 1);
      auto sum = f[bit ^ 1].get(low) + f[bit].get(low + 1, m - 1);
      now ^= (sum & 1);
      f[bit].modify(low, 1);
    }
    if (now & 1) ans |= 1 << i;
  }

  cout << ans << "\n";
}

int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  solve();
  return 0;
}

posted @ 2022-09-08 10:25  ccz9729  阅读(52)  评论(0编辑  收藏  举报