区间更新,单点查询的两种做法(树状数组和差分)

差分

差分是一种和前缀和相对的策略,可以当做是求和的逆运算。
定义为:
f ( n ) = { a i − a i − 1 , n > 2 a i , n = 1 f(n) = \begin{cases} a_i - a_{i-1}, &n>2\\ a_i, &n = 1 \end{cases} f(n)={aiai1,ai,n>2n=1

它具有一下这些性质:

  • bi的前缀和是ai的值。
  • 若在序列a的[l,r]区间增加一个数d,对b的表现为在b[l] +d,b[r+1]-d。(通过这个性质可以实现区间修改,单点查询)

具体:OIWIKI

参靠例题:1674. 使数组互补的最少操作次数

给你一个长度为 偶数 n 的整数数组 nums 和一个整数 limit 。每一次操作,你可以将 nums 中的任何整数替换为 1 到 limit 之间的另一个整数。

如果对于所有下标 i(下标从 0 开始),nums[i] + nums[n - 1 - i] 都等于同一个数,则数组 nums 是 互补的 。例如,数组 [1,2,3,4] 是互补的,因为对于所有下标 i ,nums[i] + nums[n - 1 - i] = 5 。

返回使数组 互补 的 最少 操作次数。

示例 1:

输入:nums = [1,2,4,3], limit = 4
输出:1
解释:经过 1 次操作,你可以将数组 nums 变成 [1,2,2,3](加粗元素是变更的数字):
nums[0] + nums[3] = 1 + 3 = 4.
nums[1] + nums[2] = 2 + 2 = 4.
nums[2] + nums[1] = 2 + 2 = 4.
nums[3] + nums[0] = 3 + 1 = 4.
对于每个 i ,nums[i] + nums[n-1-i] = 4 ,所以 nums 是互补的。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-moves-to-make-array-complementary
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

可以看出,对于每对数存在一个变化一次的区间[L,R],在这个区间外的即需要变化两次,在nums[i]+nums[n-i-1]这个位置的不需要变化。因为总的范围是[2,2limit],所以可以对每个数在区间上做修改,最后再扫描[2,2limit],获取变化到这个数的操作次数,不断维护最小值即可得到答案。

利用差分的做法如下:

const int MAX = 2e5+10;
int dat[MAX],N;
class Solution {
public:
    int minMoves(vector<int>& nums, int limit) {
        N = limit*2;
        for(int i=0;i<=N;i+=1){
            dat[i] = 0;
        }
        int len = nums.size();
        for(int i = 0;i<nums.size()/2;i+=1){
            int l = 1+min(nums[i],nums[len-i-1]);
            int r = limit+max(nums[i],nums[len-i-1]);
            int s = nums[i]+nums[len-i-1];
            dat[l] -= 1;
            dat[r+1] +=1;
            dat[s] -= 1;
            dat[s+1] += 1;
        }
        int res = len,now = len;//now是默认每对数都发生两次变化。
        for(int i=2;i<=2*limit;i+=1){
            now += dat[i];

            res = min(res,now);
        }

        return res;
    }
};

利用树状数组的解法如下:

const int MAX = 2e5+10;
int dat[MAX],N;
class BinaryIndexTree{
public:
    void add(int p,int v){
        while(p <= N){
            dat[p] += v;
            p += p&-p;
        }

    }
    int sum(int p){
        int res = 0;
        while(p > 0){
            res += dat[p];
            p -= p&-p;
        }
        return res;
    }
};
class Solution {
public:
    int minMoves(vector<int>& nums, int limit) {
        N = limit*2;
        for(int i=0;i<=N;i+=1){
            dat[i] = 0;
        }
        BinaryIndexTree bit;

        int len = nums.size();
        bit.add(2,len);
        bit.add(2*limit+1,-len);

        for(int i = 0;i<nums.size()/2;i+=1){
            int l = 1+min(nums[i],nums[len-i-1]);
            int r = limit+max(nums[i],nums[len-i-1]);
            int s = nums[i]+nums[len-i-1];
            bit.add(l,-1);
            bit.add(r+1,+1);

            bit.add(s,-1);
            bit.add(s+1,+1);
        }
        int res = len;
        for(int i = 2;i<=2*limit;i+=1){
            res = min(res,bit.sum(i));
      //      cout << bit.sum(i) << endl;
        }
        return res;
    }
};
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值