树状数组——插段求点
题目链接: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)); } } }
浙公网安备 33010602011771号