问题背景
对于包含n个元素的整数数组a,每次可以
1. C(i, j): 修改一个元素a[i] = j //单点修改
2. Q(i): 询问前缀Si=a1+a2+…+ai的值 // 区间查询
lowbit值
在说树状数组之前,我们不得不说一下lowbit值
- 公式 : lowbit(x)=x and -x
- 含义 : 令LOWBIT(i)=2^k 其中k为i在二进制下末尾0的个数
例如 : 6--->110 k=1 lowbit(6)= 2^1=2
树状数组是一个动态维护前缀和的数据结构
单点修改————沿红线向后
求前缀和————沿绿线向前
P3374 【模板】树状数组 1 https://www.luogu.org/problemnew/show/P3374
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
int n,m;
int a[500050]={0},c[500050]={0};
int lowbit(int x)
{
return x&(-x);
}
void add(int i,int x)
{
for(i;i<=n;i+=lowbit(i)) c[i]+=x;
}
int getsum(int i)
{
int re=0;
for(i;i>0;i-=lowbit(i)) re+=c[i];
return re;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),add(i,a[i]);
for(int i=1;i<=m;i++)
{
int c,x,y;
scanf("%d%d%d",&c,&x,&y);
if(c==1) add(x,y);
else printf("%d\n",getsum(y)-getsum(x-1));
}
return 0;
}
P3368 【模板】树状数组 2 https://www.luogu.org/problemnew/show/P3368
来介绍一下差分
设数组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;
所以 ,树状数组 本质上只能 单点修改,求前缀和
但与差分结合后 可以在结果上等价于 区间修改 ,查找单点
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
int n,m;
int a[500050]={0},c[500050]={0};
int lowbit(int x)
{
return x&(-x);
}
void add(int i,int x)
{
for(i;i<=n;i+=lowbit(i)) c[i]+=x;
}
int getsum(int i)
{
int re=0;
for(i;i>0;i-=lowbit(i)) re+=c[i];
return re;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=n;i>=1;i--) a[i]-=a[i-1],add(i,a[i]);
for(int i=1;i<=m;i++)
{
int c;
scanf("%d",&c);
if(c==1)
{
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
add(x,k);
add(y+1,-k);
}
else
{
int x;
scanf("%d",&x);
printf("%d\n",getsum(x));
}
}
return 0;
}