📌  相关文章
📜  范围内最大奇数除数的异或查询(1)

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

范围内最大奇数除数的异或查询

有一个长度为 $n$ 的数组 $a$,支持以下两种操作:

  1. 修改数组中的一个元素 $a_i$。

  2. 查询区间 $[l,r]$ 内最大的奇数除数,并将其与当前答案进行异或操作。

为了完成这个任务,我们可以使用线段树来维护区间信息。我们需要用 $odd_{i}$ 表示一个区间中偶数数量为 $i$ 的子区间最右端点的位置。

在进行修改操作时,我们只需要将相应叶子节点的 $odd$ 数组值清零,并重新更新它的父亲节点的 $odd$ 值。修改操作的时间复杂度为 $O(log_2 n)$。

在查询操作中,我们可以递归地向下搜索区间,并使用 $odd$ 数组的二分查找来确定最大奇数除数的位置。当一个区间中所有的数都是偶数时,最大奇数除数为 $-1$。查询操作的时间复杂度为 $O(log_2 n)$。

下面是代码片段:

const int MAXN = 100005;

int n;
int a[MAXN];
int odd[4 * MAXN];

void build(int i, int l, int r) {
    if (l == r) {
        if (a[l] % 2 != 0) {
            odd[i] = l - 1;
        }
        return;
    }
    int mid = (l + r) / 2;
    build(2 * i, l, mid);
    build(2 * i + 1, mid + 1, r);

    int l_odd = odd[2 * i];
    int r_odd = odd[2 * i + 1];

    if (l_odd == -1 && r_odd == -1) {
        odd[i] = -1;
    } else if (l_odd == -1) {
        odd[i] = r_odd;
    } else if (r_odd == -1) {
        odd[i] = l_odd;
    } else {
        odd[i] = max(l_odd, r_odd);
    }
}

void modify(int i, int l, int r, int x, int delta) {
    if (l == r) {
        if (a[x] % 2 != 0) {
            odd[i] = x - 1;
        } else {
            odd[i] = -1;
        }
        return;
    }
    int mid = (l + r) / 2;
    if (x <= mid) {
        modify(2 * i, l, mid, x, delta);
    } else {
        modify(2 * i + 1, mid + 1, r, x, delta);
    }

    int l_odd = odd[2 * i];
    int r_odd = odd[2 * i + 1];
    if (l_odd == -1 && r_odd == -1) {
        odd[i] = -1;
    } else if (l_odd == -1) {
        odd[i] = r_odd;
    } else if (r_odd == -1) {
        odd[i] = l_odd;
    } else {
        odd[i] = max(l_odd, r_odd);
    }
}

int query(int i, int l, int r, int ql, int qr) {
    if (ql <= l && qr >= r) {
        return odd[i];
    }
    int mid = (l + r) / 2;
    int res = -1;
    if (ql <= mid) {
        res = max(res, query(2 * i, l, mid, ql, qr));
    }
    if (qr > mid) {
        res = max(res, query(2 * i + 1, mid + 1, r, ql, qr));
    }
    return res;
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    memset(odd, -1, sizeof(odd));
    build(1, 1, n);

    int q;
    cin >> q;
    int ans = 0;
    for (int i = 1; i <= q; i++) {
        int type;
        cin >> type;
        if (type == 1) {
            int x, delta;
            cin >> x >> delta;
            a[x] += delta;
            modify(1, 1, n, x, delta);
        } else {
            int l, r;
            cin >> l >> r;
            int pos = query(1, 1, n, l, r);
            if (pos == -1) {
                ans ^= 0;
            } else {
                ans ^= a[pos] / 2;
            }
        }
    }
    cout << ans << endl;
}

此代码片段的时间复杂度为 $O(q log_2 n)$,其中 $q$ 为操作数,$n$ 为数组长度。