有人向我反映在代码中写题解会令读者提升血压,我认为这种方式的优缺点如下:

优点

  • 写起来方便,不需要切换界面
  • 便于写一段解释,放一段代码
  • 在代码中也便于顺着上面的注释继续讲解
  • 可以作为写代码的时候使自己思路清晰的一种手段

缺点

  • 非常丑
  • 不支持 LaTeX\LaTeX 公式
  • 容易写着写着变成注释的样子

改进方向

理论上如果使用一个更有表达力的语言,便可以基于语言本身生成更多的解释内容,甚至可以生成可以互动的算法演示。我最近在学习的 F* 表达力高于大部分语言,但依然无法达到这种程度。

所以现在呢?

反正没人看,不如

#include <bits/stdc++.h>
#define long long long
#define fori(n) for (int i = 0; i < n; i++)
#define forj(n) for (int j = 0; j < n; j++)
#define rofi(n) for (int i = n - 1; i >= 0; i--)
#define rofj(n) for (int j = n - 1; j >= 0; j--)
using namespace std;
/* REQUIRED */ const int N = 1e5 + 4;

// 可以发现,对区间异或后求区间加的结果,和区间内具体的数有关
// 例如 3, 2 对 1 异或后,得到 2, 3,区间加结果不变
// 但是 2, 2, 1 则会变为 3, 3, 0,区间加结果改变
// 因此我们需要额外存储每个二进制位置的权值

// 比如对 3, 2,区间信息为 [...21],区间长度为 2
// 对 1 异或后就得到 [...2(2-1)] = [...21]
// 对 2, 2, 1,区间信息为 [...21],区间长度为 3
// 对 1 异或后就得到 [...2(3-1)] = [...22]
// 要计算区间加,对权值进行加权和即可

// 构建线段树,注意
// 1. 每个数据是长度为 20 的数组
// 2. 区间求和可以先将数组求成值,然后再求和
// 3. 求和时小心爆 int
// 4. 区间异或时懒标记只存储要异或的数,这样参数和异或初次调用一致
namespace seg {
/* REQUIRED */ const int N = 1e5 + 4;
/* REQUIRED */ int n;
int a[N << 2][20];
int t[N << 2];
inline int mid(int l, int r) { return l + (r - l) / 2; }
inline void pull(int p) { fori(20) a[p][i] = a[p << 1][i] + a[p << 1 | 1][i]; }
inline void build(int *b, int l = 0, int r = n, int p = 1) {
  if (r - l == 1) {
    // 依次取原数组的每一位
    fori(20) a[p][i] = b[l] >> i & 1;
  } else {
    int m = mid(l, r);
    build(b, l, m, p << 1);
    build(b, m, r, p << 1 | 1);
    pull(p);
  }
}
inline void push(int l, int r, int p) {
  if (r - l != 1) {
    int m = mid(l, r);
    fori(20) if (t[p] >> i & 1) {
      a[p << 1][i] = m - l - a[p << 1][i];
      a[p << 1 | 1][i] = r - m - a[p << 1 | 1][i];
    }
    t[p << 1] ^= t[p];
    t[p << 1 | 1] ^= t[p];
  }
  t[p] = 0;
}
inline void add(int lq, int rq, int b, int l = 0, int r = n, int p = 1) {
  if (r <= l)
    return;
  push(l, r, p);
  if (lq <= l && rq >= r) {
    fori(20) if (b >> i & 1) a[p][i] = r - l - a[p][i];
    t[p] ^= b;
  } else {
    int m = mid(l, r);
    if (lq < m)
      add(lq, rq, b, l, m, p << 1);
    if (rq > m)
      add(lq, rq, b, m, r, p << 1 | 1);
    pull(p);
  }
}
inline long query(int lq, int rq, int l = 0, int r = n, int p = 1) {
  if (r <= l)
    return 0;
  long f = 0;
  push(l, r, p);
  if (lq <= l && rq >= r) {
    // 这个 1ll 有点难查,以后还是老老实实用 long long
    fori(20) f += 1ll * a[p][i] << i;
  } else {
    int m = mid(l, r);
    if (lq < m)
      f += query(lq, rq, l, m, p << 1);
    if (rq > m)
      f += query(lq, rq, m, r, p << 1 | 1);
  }
  return f;
}
} // namespace seg

// 题目变量
int n, m, a[N], op, l, r, x;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cout.tie(nullptr);
  cin >> n;
  seg::n = n;
  fori(n) cin >> a[i];
  seg::build(a);
  cin >> m;
  fori(m) {
    cin >> op >> l >> r;
    if (op == 1) {
      cout << seg::query(l - 1, r) << endl;
    } else {
      cin >> x;
      seg::add(l - 1, r, x);
    }
  }
  return 0;
}