📌  相关文章
📜  国际空间研究组织 | ISRO CS 2007 |问题2(1)

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

国际空间研究组织 | ISRO CS 2007 | 问题2

题目描述

给定一个长度为 n 的数组 a,其中 1 ≤ n ≤ 10^5,-10^9 ≤ a_i ≤ 10^9,你需要支持以下两种操作:

  1. 将某个位置 i 上的数修改为 x(-10^9 ≤ x ≤ 10^9)
  2. 求区间 [l,r] 中所有数的和和积(保证 l ≤ r)
思路解析

对于此题,我们需要支持区间修改和区间查询操作。对于支持动态区间查询操作的问题,经常可以采用线段树的方式进行解答。

我们可以首先建立一棵线段树,将所有数值保存在叶子结点中。初始化时,将所有叶子结点初始化为数组 a 中的值。每个非叶子结点保存的是其左右子节点的和和积,由此便可高效完成区间查询操作。

对于修改操作,我们需要从根节点开始,沿着树往下走,将受影响的节点的值修改为新值。对于每个节点上保存的区间,若其覆盖区间与修改区间不相交,则无需修改该区间内的值;若相交,则需要分别递归到其左右子节点上修改。修改结束后,我们需要重新对每个节点保存的值进行更新。

具体代码实现过程中,我们可以采用递归或非递归两种方式。此外,在建立线段树时,我们可以多申请一些节点以避免越界等问题。

下面是一个采用递归方式实现区间修改和查询的示例代码(使用 C++ 语言):

struct Node {
    int l, r;
    ll sum, mul;
} tree[N * 4 + 5];

void build(int p, int l, int r) {
    tree[p].l = l, tree[p].r = r, tree[p].mul = 1;
    if (l == r) {
        tree[p].sum = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(p << 1, l, mid);
    build(p << 1 | 1, mid + 1, r);
    tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
    tree[p].mul = tree[p << 1].mul * tree[p << 1 | 1].mul;
}

void pushdown(int p) {
    tree[p << 1].mul *= tree[p].mul;
    tree[p << 1 | 1].mul *= tree[p].mul;
    tree[p << 1].sum *= tree[p].mul;
    tree[p << 1 | 1].sum *= tree[p].mul;
    tree[p].mul = 1;
}

void update(int p, int x, ll k) {
    if (tree[p].l == x && tree[p].r == x) {
        tree[p].sum = k;
        return;
    }
    pushdown(p);
    int mid = (tree[p].l + tree[p].r) >> 1;
    if (x <= mid) update(p << 1, x, k);
    if (x > mid) update(p << 1 | 1, x, k);
    tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
}

void modify(int p, int l, int r, ll k) {
    if (tree[p].l == l && tree[p].r == r) {
        tree[p].mul *= k;
        tree[p].sum *= k;
        return;
    }
    pushdown(p);
    int mid = (tree[p].l + tree[p].r) >> 1;
    if (r <= mid) modify(p << 1, l, r, k);
    else if (l > mid) modify(p << 1 | 1, l, r, k);
    else modify(p << 1, l, mid, k), modify(p << 1 | 1, mid + 1, r, k);
    tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
    tree[p].mul = tree[p << 1].mul * tree[p << 1 | 1].mul;
}

ll query_sum(int p, int l, int r) {
    if (tree[p].l == l && tree[p].r == r) return tree[p].sum;
    pushdown(p);
    int mid = (tree[p].l + tree[p].r) >> 1;
    if (r <= mid) return query_sum(p << 1, l, r);
    if (l > mid) return query_sum(p << 1 | 1, l, r);
    return query_sum(p << 1, l, mid) + query_sum(p << 1 | 1, mid + 1, r);
}

ll query_mul(int p, int l, int r) {
    if (tree[p].l == l && tree[p].r == r) return tree[p].mul;
    pushdown(p);
    int mid = (tree[p].l + tree[p].r) >> 1;
    if (r <= mid) return query_mul(p << 1, l, r);
    if (l > mid) return query_mul(p << 1 | 1, l, r);
    return query_mul(p << 1, l, mid) * query_mul(p << 1 | 1, mid + 1, r);
}
总结

本题考察了对区间修改和查询操作的理解和实现能力。通过采用线段树的方式,我们能够高效地完成这些操作。对于其它涉及动态区间查询的问题,类似的思路也可以很好地进行拓展应用。