有人向我反映在代码中写题解会令读者提升血压,我认为这种方式的优缺点如下:
优点
- 写起来方便,不需要切换界面
- 便于写一段解释,放一段代码
- 在代码中也便于顺着上面的注释继续讲解
- 可以作为写代码的时候使自己思路清晰的一种手段
缺点
- 非常丑
- 不支持 公式
- 容易写着写着变成注释的样子
改进方向
理论上如果使用一个更有表达力的语言,便可以基于语言本身生成更多的解释内容,甚至可以生成可以互动的算法演示。我最近在学习的 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;
}