树状数组(区间更新)差分思想

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

1.将某区间每一个数数加上x

2.求出某一个数的和

题:

 

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

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

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

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x 含义:输出第x个数的值

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 复制

5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4

输出样例#1: 复制

6
10

说明

时空限制:1000ms,128M

数据规模:

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

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

对于100%的数据:N<=500000,M<=500000

样例说明:

故输出结果为6、10

关于差分:

设数组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数组变为a[]={1,8,10,7,10},b数组变为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;

这个题目是树状数组的一个拓展,在树状数组中可以用前 i 项的和来表示第 i 个数.

那么当对 x ~ y 的区间进行修改的时候需要在树状数组中的第 x 个位置 + k, 第 y + 1 个位置 -k

这样便维护了这个树状数组

输出时候直接输出查询即可

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <list>
#include <deque>
#include <vector>
#include <map>
#define LL long long
int lowbit(int x)
{
    return x&(-x);
}
const int MAXN =1e6+7;
const long long MAX=0x7f7f7f3f;
using namespace std;
void read(int &x){
	char ch = getchar();x = 0;
	for (; ch < '0' || ch > '9'; ch = getchar());
	for (; ch >='0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
}
long long  arr[MAXN]={0},c[MAXN]={0};
long long  n,m,k;
long long  sum(long long  x,long long  c[])
{
   long long ret=0;
    for(int i=x;i>0;i-=lowbit(i))
    {
        ret+=arr[i];
        //ret+=c[i];
    }
    return ret;
}/*
void updata(int x,int val,int n)
{
   for(int i=x;i<=n;i+=lowbit(i))
    {
        c[i]+=val;
       // x+=lowbit((x));
    }
}*/
void updata(long long x,long long  val)
{
   // int i=l;
    for(int i=x;i<=n;i+=lowbit(i))
    {
       // c[i]+=val;
        arr[i]+=val;
    }
}
int main()
{
    ios::sync_with_stdio(false);

   // int n,m,k;
    cin>>n>>m;
    long long num;
    long long last=0,now;
    for(int i=1;i<=n;i++)
    {
       // cin>>arr[i];
        //updata(i,arr[i]);
        cin>>now;
        updata(i,now-last);
        last=now;
    }
    while(m--)
    {
        int l,r,val;
        int x;
        cin>>k;
        if(k==1)
        {
            cin>>l>>r>>val;
            updata(l,val);
            updata(r+1,-val);
        }
        else
        {
            cin>>x;
            //int ans=sum(r,c,n)-sum(l-1,c,n);
           // cout<<sum(r,c,n)<<" "<<sum(l,c,n)<<endl;
           cout<<sum(x,c)<<endl;
        }
    }
    return 0;
}

转载:https://www.luogu.org/problemnew/solution/P3368 

 

### 树状数组区间修改实现方法 #### 单点更新与前缀和概念 树状数组(IndexTree),作为一种高效处理单点更新和前缀和查询的数据结构,在面对“单点修改+区间查询”的场景下表现优异[^2]。然而,当涉及到区间的批量操作时,则需引入差分的概念来辅助完成。 #### 差分思想的应用 为了有效地执行区间修改并保持后续查询的准确性,可以利用差分数组这一技巧。具体而言,在进行\[l,r\]范围内的增量\( k \)调整时,并不需要直接作用于该段内每一个元素上;而是仅改变边界位置上的数值——即令`c1[l] += k`以及`c1[r + 1] -= k`。这样的做法使得实际影响被局限在两个特定索引处,从而大大简化了计算过程[^3]。 #### Go语言中的实践案例 下面给出一段基于Go编写的代码片段用于展示如何通过上述理论指导下的算法逻辑去构建支持区间修改功能的树状数组: ```go // 更新函数:负责应用差分变化至指定节点及其父辈们身上 func (t *TreeArray) update(index int, delta int) { for ; index <= n; index += lowbit(index){ t.treeArray[index] += delta; } } // 执行区间修改的具体方式如下所示: leftEnd := l // 修改起始位置 rightStart := r + 1 // 修改结束后的第一个位置 update(leftEnd , value); // 对左端点增加value if rightStart <= n{ update(rightStart ,-value); // 对右端点之后的第一个位置减少value } ``` 在此基础上,如果想要获取某个具体的值或某段连续子序列之和,则依然可以通过调用之前定义好的`prefixQuery()`来进行间接求解[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值