树状数组可以解决查询前缀和/区间和问题。
模板(单点修改)
int lowbit(int i) { return i & -i;//或者是return i-(i&(i-1));表示求数组下标二进制的非0最低位所表示的值 } void add(int i,int val)//更新单节点的值 { while(i<=n){ a[i]+=val; i+=lowbit(i);//由叶子节点向上更新a数组,从左往右更新 } } int sum(int i)//求和节点的值 { int ret=0; while(i>0){ ret+=a[i];//从右往左区间求和 i-=lowbit(i); } return ret; }
区间修改的理论依据是差分数组,维护的数组有两个,一个是差分数组c1。还有个是c2是pos*value;
模板 :
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll N=5E5+7; ll d[N]; ll arr[N]; ll n,m; ll c1[N],c2[N]; ll lowbit(ll x){ return x&(-x); } void add(ll x,ll value){ ll x1=x; while(x<=n){ c1[x]+=value; c2[x]+=x1*value; x+=lowbit(x); } } ll sum(ll x) { ll res=0; ll x1=x; while(x){ res+=(x1+1)*c1[x]-c2[x]; x-=lowbit(x); } return res; } ll query(ll x,ll y){ return sum(y)-sum(x-1); } int main(){ scanf("%d%d",&n,&m); for(ll i=1;i<=n;i++) scanf("%lld",&arr[i]); arr[0]=0; for(ll i=1;i<=n;i++){ add(i,arr[i]-arr[i-1]); } for(ll i=1;i<=m;i++){ int k; scanf("%d",&k); if(k==1){ ll x,y,value; scanf("%lld%lld%lld",&x,&y,&value); add(x,value); add(y+1,-value); } else { ll pos; scanf("%lld",&pos); printf("%lld\n",query(pos,pos)); } } return 0; }
lowbit返回的是i的二进制最右端1所对应的值。
区间和[x,y]:可以通过sum(y)-sum(x-1)来实现。
注:树状数组可以解决的一般线段树都可以解决,树状数组只能是区间加减单点加减。