[模板] 差分树状数组 - 区间操作&元素求值

【模板】树状数组 2 - 区间操作&元素求值

P3368 【模板】树状数组 2 - 洛谷

首先:树状数组 - 区间求值&元素操作

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数数加上x
  2. 求出某一个数的值。

输入格式

第一行包含两个整数NM,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含24个整数,表示一个操作,具体如下:

  • 操作1:格式:1 x y k含义:将区间[x,y]内每个数加上k
  • 操作2:格式:2 x含义:输出第x个数的值。

输出格式

输出包含若干行整数,即为所有操作2的结果。

输入输出样例

输入输出
5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4
6
10

说明/提示

样例1解释:

故输出结果为610

数据规模与约定

对于30%的数据:N≤8,M≤10

对于70%的数据:N≤10000,M≤10000

对于100%的数据:1≤N,M≤5000001≤x,y≤n,保证任意时刻序列中任意元素的绝对值都不大于2E30

思想

引用:题解 P3368 【模板】树状数组 2】 - Lyp10000 的博客 - 洛谷博客

设数组a[] = {1,6,8,5,10},那么差分数组b[] = {1,5,2,-3,5}

也就是说b[i]=a[i]-a[i-1], (a[0] = 0),那么a[i]=b[1]+...+b[i]

假如区间[2,4]都加上2的话

  • a[]数组变为{1,8,10,7,10}
  • b[]数组变为{1,7,2,-3,3}

b[]数组只有b[2]b[5]变了,因为区间[2,4]是同时加上2的,所以在区间内b[i]-b[i-1]是不变的.

所以对区间[x,y]进行修改,只用修改b[x]b[y+1]:

  • b[x]=b[x]+k
  • b[y+1]=b[y+1]-k

代码

#include <cstdio>

typedef int datatype;

struct bintree_base
{
   //树状数组指针
   datatype *a;

   //元素数量
   int n;

   //初始化
   explicit bintree_base(const int &_n){
      n = _n;
      a = new datatype[n + 1]();
   }
   ~bintree_base(){
      delete[] a;
   }
   datatype lowbit(const datatype &num){
      return num & (-num);
   }
   //求从1到index的和
   datatype accumulate(int index){
      datatype ans = 0;
      while (index)
         ans += a[index], index -= lowbit(index);
      return ans;
   }
   //求区间和
   datatype accumulate(const int &begin, const int &end){
      return accumulate(end) - accumulate(begin - 1);
   }
   //在index位置上+value并更新到树上
   void add(int index, const datatype &value){
      while (index <= n)
         a[index] += value, index += lowbit(index);
   }
};

//差分树状数组,继承自基本树状数组
struct bintree_diff : public bintree_base
{ 
protected:
   typedef bintree_base super;

public:
   explicit bintree_diff(const int &_n) : super(_n){};
   //更改某一位置的某个值
   void assign(int index, const datatype &value){ 
      add(index, index + 1, -super::accumulate(index));
      add(index, index + 1, value);
   }
   //在[head,tail)区间+value
   void add(int head, int tail, datatype value){ 
      super::add(head, value);
      super::add(tail, -value);
   }
   //index位置的值
   datatype at(const int &index){ 
      return super::accumulate(index);
   }
};

int main(){
   int n, m, tmp1, tmp2 = 0, tmp3, tmp4;
   scanf("%d%d", &n, &m);
   bintree_diff bt(n);
   for (int i = 1; i <= n; ++i){
      scanf("%d", &tmp1);
      bt.assign(i, tmp1);
   }
   for (int i = 0; i < m; ++i){
      scanf("%d", &tmp1);
      switch (tmp1){
      case 1:
         scanf("%d%d%d", &tmp2, &tmp3, &tmp4);
         //左闭右开,tmp3+1
         bt.add(tmp2, tmp3 + 1, tmp4); 
         break;
      case 2:
         scanf("%d", &tmp2);
         printf("%d\n", bt.at(tmp2));
         break;
      }
   }
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值