树状数组是什么?
树状数组,就是长得像一棵树的数组(如下图,长方形为原数组,圆形为树状数组,数字表示编号)。
树状数组为什么长成这样?
先引入一个概念,lowbit(x) 即为二进制下 x 为 1 的最低位所代表的数字,
例如 lowbit(5)=1 , lowbit(6)=2 , lowbit(4)=4 。求 lowbit(x) 用 x&(-x) 即可。
显然,如图,如果树状数组用 tree[i] 表示的话,tree[i] 会对 tree[i+lowbit(i)] 产生贡献,
这样建树就会使各种区间操作的复杂度从 O(n) 降到 log 级别。
树状数组有什么作用?
可以用来快速的求区间和,区间最值等,
而且相比线段树通常空间更小,时间更快(常数小)。
下面就介绍一下几种不同功能的写法。
1.单点修改+区间查询。
这个很简单,直接树状数组维护原数组即可(代码中含注释)。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a[500005],tree[500005],x,u,v;
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int k)//显然,x点对 x+lowbit(x) 有贡献,那么就可以递归下去直到 x 加到 x>n
{
while(x<=n)
{
tree[x]+=k;
x+=lowbit(x);
}
}
int ser(int x)//求sum 1~x
{
int ans=0;
while(x>0)
{
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i],add(i,a[i]);
for(int i=1;i<=m;i++)
{
cin>>x>>u>>v;
if(x==1)
{
add(u,v);
}
else
{
cout<<ser(v)-ser(u-1)<<endl;
}
}
}
2.区间修改+单点查询。
因为树状数组只支持单点修改,所以我们需要把区间操作转化成单点操作,
所以这里我们引入差分数组F,这样就可以把一个区间操作转变成两单点操作,
而单点查询就是求 sum(F1~Fi),所以我们只需要用树状数组来维护一个差分数组即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a[500005],tree[500005],x,u,v,w;
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int k)
{
while(x<=n)
{
tree[x]+=k;
x+=lowbit(x);
}
}
int ser(int x)
{
int ans=0;
while(x>0)
{
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i],add(i,a[i]-a[i-1]);
for(int i=1;i<=m;i++)
{
cin>>x;
if(x==1)
{
cin>>u>>v>>w;
add(u,w);
add(v+1,-w);
}
else
{
cin>>v;
cout<<ser(v)<<endl;
}
}
}
3.区间修改+区间查询。
可以证明只需要在维护一个差分数组的基础上再维护一个新数组,
推理过程可以看这个大佬
代码如下。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a[1000005],tree[1000005],tree2[1000005],x,b[1000005],c[1000005],d[1000005],u,v,w;
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int k)
{
while(x<=n)
{
tree[x]+=k;
x+=lowbit(x);
}
}
void add2(int x,int k)
{
while(x<=n)
{
tree2[x]+=k;
x+=lowbit(x);
}
}
int ser(int x)
{
int ans=0;
while(x>0)
{
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
int ser2(int x)
{
int ans=0;
while(x>0)
{
ans+=tree2[x];
x-=lowbit(x);
}
return ans;
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i],b[i]=a[i]-a[i-1],add(i,b[i]),add2(i,(i-1)*b[i]);
for(int i=1;i<=m;i++)
{
cin>>x;
if(x==1)
{
cin>>u>>v>>w;
add(u,w);
add(v+1,-w);
add2(u,(u-1)*w);
add2(v+1,-v*w);
}
else
{
cin>>u>>v;
cout<<(v*ser(v)-ser2(v))-((u-1)*ser(u-1)-ser2(u-1))<<endl;
}
}
}
4.单点修改+单点查询
这个用什么树状数组?直接暴力!