📌  相关文章
📜  范围更新查询与二进制数组中的 1 异或。(1)

📅  最后修改于: 2023-12-03 15:27:47.176000             🧑  作者: Mango

范围更新查询与二进制数组中的 1 异或

本文主要介绍两个常见的技巧:范围更新查询和二进制数组中的 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;
}
二进制数组中的 1 异或

二进制数组指的是只包含 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 异或这两个常见的技巧及对应的实现代码。在实际开发中,我们可以将这些代码封装成函数,在需要时进行调用,以提高开发效率。