树状数组——插段求点

题目链接:https://www.luogu.org/problemnew/show/P3368

(洛谷的模板题)

 

求某一点 a [ i ] 的方法:差分累加原理。 

设:

原数组a,存储第i个数据的原始值

差分原始数组c:c[i]存储的是,a[i]-a[i-1]的值

差分数组的树状数组:cx。存储的是,c数组的阶段和,便于c数组求和。

 

对于每个a[i] ,计算方法是:

c[i] = a[i] - a[i-1]

c[i-1] = a[i-1] - a[i-2]

c[i-2] = a[i-2] - a[i-3]

……

c[1] = a[1] - a[0]

 

左右分别累加,可得:a[i] - a[0] = c[1] + c[2] + …… + c[i]

根据习惯,我存储数值从a[1] 开始存起 , 所以a[0] =0 

所以,a[i] = c[1] + c[2] + …… + c[i] = sum ( i )

求a[i]的问题,就转化为,求c数组的和的问题

引入c数组的树状数组cx,简化求和过程.

 

a数组中,若区间 [ l , r ] 中的数全部 +k,只需要在 c 数组中,把 c [ l ] +k , 把 c [r+l] -k , 即可 。

这样变化之后的c数组,仍可以按原来方式表示a数组。

因为:若把 c [ l ] +k , 根据累加原则,会导致由此c数组表示出来的a数组中,x下标之后的数,全部+k。为了避免让r以后的数也加k,所以同理,让-k去消掉。

 

基本原理已经有了,在具体操作的实现上,要应用树状数组,构建c的树状数组cx。

每次c[x]+k的时候,把cx数组中能管理到x的位置,全部+k。

每次c[x]单点更新,也要注意更新cx数组。

 

 

#include <iostream>
#include <cstdio>
using namespace std;
int c[500005];//c[i]=a[i]-a[i-1];
int cx[500005];//存储的是:差数组的,阶段“和”
int n;
int lowbit(int x)
{
    return x&(-x);
}
void upgrade(int*arr,int x,int k)//每次改动,只在cx上改动
{
    int o=x;
    while(o<=n)
    {
        arr[o]+=k;
        o+=lowbit(o);
    }
    return ;
}
int sum(int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=cx[x];
        x-=lowbit(x);
    }
    return ans;
}
int main(){
    int k;
    cin>>n>>k;
    c[0]=0;
    int tmp[2];
    tmp[0]=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&tmp[1]);
        int tmpx=tmp[1]-tmp[0];//其实c原数组都不用全部保存,直接构建cx即可;因为构建了cx之后,c的任务就结束了。
        //很多时候,原数组(a[i]也好,这里的c[i]也好),都是不存储的。
        upgrade(cx,i,tmpx);//构建cx数组
        tmp[0]=tmp[1];
    }
    
    while(k--)
    {
        int flag;
        scanf("%d",&flag);
        if(flag==1)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            upgrade(cx,l,k);
            upgrade(cx,r+1,-k);//注意哦,这里是,“r+1”
            
        }
        else
        {
            int x;
            scanf("%d",&x);
            printf("%d\n",sum(x));
        }
    }
}

 

 

 

 

posted on 2018-04-24 10:22  _isolated  阅读(92)  评论(0)    收藏  举报