📜  门| GATE-CS-2007 |第 62 题(1)

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

题目描述

给定一个由$n$个元素组成的数组$A$,处理以下两个操作:

  1. 查询:查询区间$[L, R]$中所有元素的和。
  2. 修改:将数组中某个元素$A_i$的值修改为$v$。

使用分块算法实现该数据结构。

输入格式

第一行包含两个整数$n$和$m$,表示数组$A$的大小和操作次数。

第二行包含$n$个整数,表示数组$A$的初始值。

接下来$m$行,每行描述一个操作,格式为以下两种之一:

  1. 1 l r - 表示查询区间$[l, r]$中所有元素的和。
  2. 2 x v - 表示将$A_x$的值修改为$v$。
输出格式

对于每个查询操作输出查询结果。

输入样例
6 7
1 2 3 4 5 6
1 2 5
2 3 6
1 1 6
2 3 8
1 1 6
1 3 5
1 2 4
输出样例
14
20
15
9

解题思路

使用分块算法解决该问题,将数组分成$\sqrt{n}$份。

对于查询操作,由于数组中某些元素已经被修改过了,所以要在查询前进行所有修改操作。

然后对于查询的区间$[L, R]$,先查看L和R所在块是否相同,如果相同,则在该块中暴力求解。如果不同,则先在起始块中从L开始暴力求出L所在块到末尾的所有元素的和,再在结束块中从开头到R位置的元素进行暴力求解,最后在中间的若干块中求出所有元素的和。

对于修改操作,只需要修改对应位置的元素值即可。

算法实现

from math import sqrt, ceil

class Block:
    def __init__(self, data, size):
        self.data = data                 # 块中的元素
        self.size = size                 # 块的大小
        self.sums = [0] * self.size      # 前缀和数组

        # 初始化块的前缀和数组
        for i in range(size):
            self.sums[i] = self.data[i] + (self.sums[i-1] if i-1>=0 else 0)

    def update(self, index, value):
        # 修改块中某个元素值后,同步更新前缀和数组
        self.sums[index % self.size] += value - self.data[index % self.size]
        self.data[index % self.size] = value

    def query(self, left, right):
        # 查询块中两个位置之间的所有元素的和
        left_index, right_index = left % self.size, right % self.size
        if left_index <= right_index:
            return self.sums[right_index] - (self.sums[left_index - 1] if left_index>0 else 0)
        else:
            return self.sums[-1] - (self.sums[left_index - 1] if left_index>0 else 0) + self.sums[right_index]

class BlockArray:
    def __init__(self, data):
        self.data = data
        self.block_size = ceil(sqrt(len(data)))
        self.blocks = [None] * self.block_size

        # 初始化每个块
        for i in range(self.block_size):
            left = self.block_size * i
            right = left + self.block_size - 1
            right = min(right, len(data)-1)
            self.blocks[i] = Block(data[left:right+1], right-left+1)

    def update(self, index, value):
        # 修改某个元素值
        self.data[index] = value
        self.blocks[index//self.block_size].update(index, value)

    def query(self, left, right):
        # 查询区间[left, right]的所有元素的和
        res = 0
        left_block, left_index = left // self.block_size, left % self.block_size
        right_block, right_index = right // self.block_size, right % self.block_size

        # 如果查询区间在同一个块中,则暴力查询即可
        if left_block == right_block:
            res = self.blocks[left_block].query(left_index, right_index)
        else:
            res += self.blocks[left_block].query(left_index, -1)
            res += self.blocks[right_block].query(0, right_index)
            for i in range(left_block+1, right_block):
                res += self.blocks[i].sums[-1]
            res += self.data[left_block*self.block_size-1] if left_index==0 and left_block>0 else 0
            res += self.data[right_block*self.block_size] if right_index==self.block_size-1 and right_block<len(self.blocks)-1 else 0

        return res


if __name__ == '__main__':
    n, m = map(int, input().split())
    data = list(map(int, input().split()))
    block_array = BlockArray(data)
    for _ in range(m):
        op = list(map(int, input().split()))
        if op[0] == 1:
            print(block_array.query(op[1]-1, op[2]-1))
        elif op[0] == 2:
            block_array.update(op[1]-1, op[2])