P3760 [TJOI2017] 异或和

[TJOI2017] 异或和

题目link

题目描述#

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

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

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

输入格式#

第一行输入一个 n,表示这序列的数序列
第二行输入 n 个非负整数 a1,a2an 代表这个序列

输出格式#

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

样例 #1#

样例输入 #1#

3
1 2 3

样例输出 #1#

0

提示#

【样例解释】

序列 1 2 36 个连续和,它们分别是 1 2 3 3 5 6,则 1 xor 2 xor 3 xor 3 xor 5 xor 6=0

【数据范围】

对于 20% 的数据,1n100
对于 100% 的数据,1n105i=1nai106


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

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

  • 如果 sj 的低 k1 位是大于等于 si 的低 k1 位,那么当 sj 的第 k 位和 si 的第 k 位不同的时候,[i,j] 的区间和的第 k 位是 1
  • 如果 sj 的低 k1 位是小于 si 的低 k1 位,那么当 sj 的第 k 位和 si 的第 k 位相同的时候,[i,j] 的区间和的第 k 位是 1

证明可以考虑手摸几个数据,注意 sj 一定是 大于等于 si

然后可以使用两个权值树状数组,分别考虑第 k 位的为 10 的时候,低 k1 位的大小。

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 @   ccz9729  阅读(57)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
主题色彩