📅  最后修改于: 2023-12-03 15:27:47.176000             🧑  作者: Mango
本文主要介绍两个常见的技巧:范围更新查询和二进制数组中的 1 异或。这两个技巧在算法竞赛和实际开发中都经常被使用。
范围更新查询是指在一个数组中,将某一区间内的元素都加上一个固定的值,然后查询某一个位置的元素值。这个问题可以使用线段树来解决。
下面是一个示例代码片段,其实现了对一个数组进行单点修改和区间查询的功能:
const int maxn = 100010;
int n, m;
int a[maxn];
struct Node {
int l, r;
int sum, lazy;
}tr[maxn * 4];
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u) {
auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
if (root.lazy) {
left.lazy += root.lazy;
right.lazy += root.lazy;
left.sum += (left.r - left.l + 1) * root.lazy;
right.sum += (right.r - right.l + 1) * root.lazy;
root.lazy = 0;
}
}
void build(int u, int l, int r) {
if (l == r) {
tr[u] = {l, r, a[l], 0};
} else {
tr[u] = {l, r};
int mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int x, int c) {
if (tr[u].l == x && tr[u].r == x) {
tr[u].sum += c;
return;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (x <= mid) modify(u << 1, x, c);
else modify(u << 1 | 1, x, c);
pushup(u);
}
int query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) {
return tr[u].sum;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1, res = 0;
if (l <= mid) res += query(u << 1, l, r);
if (r > mid) res += query(u << 1 | 1, l, r);
return res;
}
二进制数组指的是只包含 0 和 1 的数组。我们可以使用一棵线段树来实现异或操作。
异或操作的本质是将两个二进制数的相同位取反,不同位不变。因此,我们可以设立一个懒惰标记,每次异或操作就将该标记置为 1。在 pushdown 操作时,如果该节点的懒惰标记为 1,那么就将其子节点的懒惰标记取反,并将其子节点的值与 1 异或,实现递归下去的异或操作。
下面是一个示例代码片段,其实现了对一个二进制数组进行单点修改和区间查询的功能:
const int maxn = 200010;
int n, m;
int a[maxn];
struct Node {
int l, r;
int sum, lazy;
}tr[maxn * 4];
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u) {
auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
if (root.lazy) {
left.lazy ^= 1;
right.lazy ^= 1;
left.sum = left.r - left.l + 1 - left.sum;
right.sum = right.r - right.l + 1 - right.sum;
root.lazy = 0;
}
}
void build(int u, int l, int r) {
if (l == r) {
tr[u] = {l, r, a[l], 0};
} else {
tr[u] = {l, r};
int mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int x) {
if (tr[u].l == x && tr[u].r == x) {
tr[u].sum ^= 1;
return;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (x <= mid) modify(u << 1, x);
else modify(u << 1 | 1, x);
pushup(u);
}
int query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) {
return tr[u].sum;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1, res = 0;
if (l <= mid) res += query(u << 1, l, r);
if (r > mid) res += query(u << 1 | 1, l, r);
return res;
}
以上介绍了范围更新查询和二进制数组中的 1 异或这两个常见的技巧及对应的实现代码。在实际开发中,我们可以将这些代码封装成函数,在需要时进行调用,以提高开发效率。