单点与区间的更新与查询(模板)

下文为了方便起见,
1/ 单点(single point 简写 s)
2/区间 (distance 简写 d)
3/更新 (update 简写 u)
4/查询(require 简写 r)
比如单点更新,单点查询,我们用SUSR

单点更新,单点查询(SUSR)

传统数组就可以做,不说了

单点更新,区间查询(SUDR)

这个用线段树和树状数组都能完成,线段树的模板,已经在我另一篇博客里,下面献上树状数组的模板,关于树状数组的原理分析自行了解。

class TreeArray_for_SUDR:  # when using, index start from 0
    def __init__(self, nums: List[int]):
        self.n = len(nums)
        self.arr = [0] * (self.n + 1)
        for i in range(self.n):
            self.update(i, nums[i])
    def __lowbit(self, i) -> int:
        return i & (-i)
    def update(self, i, k) -> int:  # add k in index_i
        i += 1
        while (i <= self.n):
            self.arr[i] += k
            i += self.__lowbit(i)
    def getSum(self, i) -> int:   # get the sum from 0 to i
        i += 1
        res = 0
        while (i > 0):
            res += self.arr[i]
            i -= self.__lowbit(i)
        return res

区间更新,单点查询(DUSR)

如果题目是让你把x~y区间内的所有值全部加上k或者减去k,然后查询操作是问某个点的值,这种时候该怎么做呢。如果是像上面的树状数组来说,就必须把x-y区间内每个值都更新,这样的复杂度肯定是不行的,这个时候,就不能再用数据的值建树了,这里我们引入差分,利用差分建树。

假设我们规定A[0] = 0;

则有 A[i] = Σij = 1D[j];(D[j] = A[j] - A[j-1]),即前面i项的差值和,这个有什么用呢?例如对于下面这个数组

A[] = 1 2 3 5 6 9
D[] = 1 1 1 2 1 3
如果我们把[2,5]区间内值加上2,则变成了

A[] = 1 4 5 7 8 9
D[] = 1 3 1 2 1 1
发现了没有,当某个区间[x,y]值改变了,区间内的差值是不变的,只有D[x]和D[y+1]的值发生改变,至于为什么我想我就不用解释了吧。

所以我们就可以利用这个性质对D[]数组建立树状数组

代码模板为:

#ConstantPalindromeSum => CPS, 指区间查询
class TreeArray_CPS_for_DUSR:
    def __init__(self, nums: List[int]):
        self.n = len(nums)
        self.arr = [0] * (self.n + 2)
        for i in range(self.n):
            self.__updateNum(i, nums[i]-nums[i-1] if i!=0 else nums[i])
        print(self.arr)
    def __lowbit(self, i) -> int:
        return i & (-i)
    def updateDistance(self, from_i: int, to_i: int, k: int) -> None: #add k from from_i to to_i
        self.__updateNum(from_i, k)
        self.__updateNum(to_i+1, -k)
    def __updateNum(self, i, k) -> int:  # add k in index_i
        i += 1
        while (i <= self.n):
            self.arr[i] += k
            i += self.__lowbit(i)
    def getNum(self, i) -> int:  # get  in i
        i += 1
        res = 0
        while i > 0:
            res += self.arr[i]
            i -= self.__lowbit(i)
        return res

区间更新,区间查询(DUDR)

上面我们说的差值建树状数组,得到的是某个点的值,那如果我既要区间更新,又要区间查询怎么办。这里我们还是利用差分,由上面可知
∑ni = 1A[i] = ∑ni = 1 ∑ij = 1D[j];
则A[1]+A[2]+…+A[n]
= (D[1]) + (D[1]+D[2]) + … + (D[1]+D[2]+…+D[n])
= nD[1] + (n-1)D[2] +… +D[n]
= n * (D[1]+D[2]+…+D[n]) - (0
D[1]+1
D[2]+…+(n-1)D[n])
所以上式可以变为∑ni = 1A[i] = n
∑ni = 1D[i] - ∑ni = 1( D[i](i-1) );
如果你理解前面的都比较轻松的话,这里也就知道要干嘛了,维护两个数状数组,sum1[i] = D[i],sum2[i] = D[i]
(i-1);

附上自己整理的代码模板:

class TreeArray_CPS_for_DUDR1:  # when using, index start from 0
    def __init__(self, nums: List[int]):
        self.n = len(nums)
        self.arr1 = [0] * (self.n + 1)
        self.arr2 = [0] * (self.n + 1)
        for i in range(self.n):
            self.updateFromStart(i, nums[i] - nums[i - 1] if i != 0 else nums[i])

    def __lowbit(self, i: int) -> int:
        return i & (-i)

    def updateFromStart(self, i: int, k: int) -> int:  # add k to index_i
        i += 1
        x = i
        while (i <= self.n):
            self.arr1[i] += k
            self.arr2[i] += k * (x - 1)
            i += self.__lowbit(i)

    def updateDistancePoints(self, from_i: int, to_i: int, k: int) -> None:  # add k from from_i to to_i
        self.updateFromStart(from_i, k)
        self.updateFromStart(to_i + 1, -k)

    def getSumInDistance(self, from_i: int, to_i: int) -> int:  # get the sum from from_i to to_i
        return self.getSumFromStart(to_i) - (self.getSumFromStart(from_i - 1) if from_i != 0 else 0)

    def getSumFromStart(self, i: int) -> int:  # get the sum from 0 to i
        i += 1
        res, x = 0, i
        while (i > 0):
            res += x * self.arr1[i] - self.arr2[i]
            i -= self.__lowbit(i)
        return res

更新版本,来自力扣的某位dalao,因为写得实在太棒了!!!先不问自取,若dalao看到请联系,我在做进一步的处理!!

class TreeArray_CPS_for_DUDR:
    def __init__(self, n):
        self.n = n
        self.t1 = [0] * (n + 1)
        self.t2 = [0] * (n + 1)

    def add(self, t, x, k):
        while x <= self.n:
            t[x] += k
            x += (x & (-x))

    def query(self, t, x):
        c = 0
        while x:
            c += t[x]
            x -= (x & (-x))
        return c

    def add_range(self, begin, end, k):
        self.add(self.t1, begin, k)
        self.add(self.t1, end, -k)
        self.add(self.t2, begin, (begin - 1) * k)
        self.add(self.t2, end, - (end - 1) * k)

    def query_sum(self, x):
        return self.query(self.t1, x) * x - self.query(self.t2, x)

    def query_range(self, begin, end):
        return self.query_sum(end - 1) - self.query_sum(begin - 1)

测试:
数据准备,随机生成一个5000大小的数组,然后随机生成10000次操作,如下图所示:

import random as r
length = 5000
arr = [0] * length
for i in range(length):
    arr[i] = r.randint(1, 50000)
oper = []
time = 100000
for i in range(time):
    from_i, to_i = 95, 1
    while from_i > to_i:
        from_i = r.randint(0, length - 1)
        to_i = r.randint(0, length - 1)
        oper.append([from_i, to_i])
tcf = TreeArray_CPS_for_DUDR(arr)

测试代码:

import time as t
# 计算时间消耗,特别是对程序运行时间消耗
start = t.time()
tcf = TreeArray_CPS_for_DUDR(arr)
for _ in range(time):
    tcf.updateDistancePoints(oper[_][0], oper[_][1], _)
    tcf.getSumInDistance(oper[_][0], oper[_][1])
end = t.time()
print("树状数组+差分数组循环运时间:%.2f秒" % (end - start))

# 计算时间消耗,特别是对程序运行时间消耗
b = Baoli(arr)
start = t.time()
for _ in range(time):
    b.updateDistancePoints(oper[_][0], oper[_][1], _)
    b.getSumInDistance(oper[_][0], oper[_][1])
end = t.time()
print("暴力循环运行时间:%.2f秒" % (end - start))

最后结果:

树状数组+差分数组循环运时间:1.41秒

暴力循环运行时间:16.40

线段树求区间最大值(模板)

class SegTree {
public:
    vector<int> arr;
    int n;
    SegTree(int n){
        this->n = n;
        this->arr = vector<int>(4*n, INT_MIN);
    }
    void update(int index, int val){
        update(1, 1, n, index, val);
    }
    void update(int id, int l, int r, int index, int val){
        if(l>r || index<l || r<index) return;
        if(l<=index && index<=r)
            arr[id] = max(arr[id], val);
        if(l==r)    return;
        int mid = (l+r) / 2;
        update(id<<1, l, mid, index, val);
        update((id<<1)+1, mid+1, r, index, val);
    }
    int query(int l, int r){
        return query(1, 1, n, l, r);
    }
    int query(int id, int l, int r, int ql, int qr){
        if(r<ql || qr<l) return INT_MIN;
        if(l==r || ql<=l && r<=qr)    return arr[id];
        int mid = (l+r) / 2;
        int l_max = query(id<<1, l, mid, ql, qr); 
        int r_max = query((id<<1)+1, mid+1, r, ql, qr);
        return max(l_max, r_max);
    }
};

引用:
https://www.cnblogs.com/xenny/p/9739600.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值