树状数组
先给一张树状数组的图片吧,有图有真相。
树状数组就长这样↑,因为看上去像一棵树,所以就叫树状数组了。我们用
c
数组来存储
如何实现
lowbit
先来了解一个概念
lowbit
。从字面意思上理解:就是最低的二进制位。在树状数组中,这个的意思是:把一个整数化成二进制,最低的“1”表示的数是多少。
例如:如果要求
lowbit(10)
我们可以知道,10化成二进制是1010,最低的“1”是倒数第二位,就是代表2,所以
lowbit(10)=2
。
这里有个快速求
lowbit
的方法。
lowbit(x)=x&-x
存储方式
令
c
是一个树状数组,那么
修改
这里的修改指的是单点修改。
修改
a[i]
的话,我们就需要,修改所有包含了
a[i]
的树状数组元素。就是
c[i+(lowbit(i))]
,然后令
i+lowbit(i)=i
,再修改
c[i+(lowbit(i))]
,一直到大于总数
n
为止。
所以我们可以写出代码:
void invise(int k,int delta)
{
a[k]+=delta;
while(k<=n)
{
c[k]+=delta;
k+=lowbit(k);
}
}
查询
这里的查询指的是区间查询。
区间查询只能查询
查询我们需要把[1,k]这个区间分成不想交没有空隙的小区间。
我们要查询
c[i−lowbit(i)]
,同样的,令
i−lowbit(i)=i
,再查询
c[i−lowbit(i)]
。我们可以写出代码:
int getsum( int k)
{
int t=0;
while (k>0)
{
t+=c[k];
k- =lowbit(k);
}
return t;
}
单点修改,区间查询
就是最简单的树状数组的板子。
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int num=0;
char c=' ';
bool flag=true;
for(;c>'9'||c<'0';c=getchar())
if(c=='-')
flag=false;
for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
return flag ? num : -num;
}
#define maxn 508000
int n,m,a[maxn],d[maxn];
int lowbit(int i)
{
return i&(-i);
}
void revise(int i,int x)
{
while(i<=n)
{
d[i]+=x;
i+=lowbit(i);
}
}
long long find(int i)
{
long long ans=0;
while(i)
{
ans+=d[i];
i-=lowbit(i);
}
return ans;
}
void init()
{
n=read();m=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
revise(i,a[i]);
}
for(int i=1;i<=m;i++)
{
int c=read();
int l=read();
int r=read();
if(c==2)
printf("%lld\n",find(r)-find(l-1));
if(c==1)
revise(l,r),a[l]+=r;
}
}
int main()
{
init();
return 0;
}
区间修改,单点查询
这里要运用差分数组。但是不是纯差分。因为它是多次修改多次查询,算法复杂度会下降到
O(n2)
。
差分数组这样计算
cf[i]=a[i]−a[i−1]
,所以对差分数组求前缀和就是
a
<script id="MathJax-Element-23" type="math/tex">a</script>数组中的元素了。所以我们可以在差分数组的基础上维护树状数组。
对原数组的区间修改就成为了对差分数组的单点修改,对原数组的单点查询就称为了对差分数组的区间查询。
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int num=0;
char c=' ';
bool flag=true;
for(;c>'9'||c<'0';c=getchar())
if(c=='-')
flag=false;
for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
return flag ? num : -num;
}
#define maxn 530000
int a[maxn];int n,m;
int cf[maxn];
int d[maxn];
int lowbit(int i)
{
return i&-i;
}
void invice(int i,int x)
{
while(i<=n)
{
d[i]+=x;
i+=lowbit(i);
}
}
long long find(int i)
{
long long ans=0;
while(i)
{
ans+=d[i];
i-=lowbit(i);
}
return ans;
}
void init()
{
n=read();
m=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
cf[i]=a[i]-a[i-1];
invice(i,cf[i]);
}
for(int i=1;i<=m;i++)
{
int t=read();
if(t==1)
{
int x=read();
int y=read();
int k=read();
invice(x,k);
invice(y+1,-k);
}
if(t==2)
{
int x=read();
printf("%lld\n",find(x));
}
}
}
int main()
{
init();
return 0;
}
例题——星星(HLoj532)
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int num=0;
char c=' ';
bool flag=true;
for(;c>'9'||c<'0';c=getchar())
if(c=='-')
flag=false;
for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
return flag ? num : -num;
}
#define maxn 60050
#define max_location 32001
struct node
{
int x,y;
}a[maxn];
int d[maxn];
int lowbit(int i)
{
return i&-i;
}
int n;
void revise(int k,int delta)
{
while(k<=max_location)
{
d[k]+=delta;
k+=lowbit(k);
}
}
int find(int k)
{
int ans=0;
while(k)
{
ans+=d[k];
k-=lowbit(k);
}
return ans;
}
void init()
{
n=read();
for(int i=1;i<=n;i++)
{
a[i].x=read()+1;
a[i].y=read()+1;
revise(a[i].x,1);
printf("%d\n",find(a[i].x)-1);
}
}
int main()
{
init();
return 0;
}
二维树状数组——打鼹鼠HLoj534
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int num=0;
char c=' ';
bool flag=true;
for(;c>'9'||c<'0';c=getchar())
if(c=='-')
flag=false;
for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
return flag ? num : -num;
}
#define maxn 1025
int d[maxn][maxn];
int n,m;
int lowbit(int x)
{
return x&-x;
}
void invise(int x,int y,int k)
{
while(x<=n)
{
int ty=y;
while(ty<=n)
{
d[x][ty]+=k;
ty+=lowbit(ty);
}
x+=lowbit(x);
}
}
long long find(int x,int y)
{
long long ans=0;
while(x)
{
int ty=y;
while(ty)
{
ans+=d[x][ty];
ty-=lowbit(ty);
}
x-=lowbit(x);
}
return ans;
}
void init()
{
n=read();
m=read();
int a,b,c,d;
while(m!=3)
{
if(m==1)
{
a=read()+1;
b=read()+1;
c=read();
invise(a,b,c);
}
if(m==2)
{
a=read()+1;
b=read()+1;
c=read()+1;
d=read()+1;
printf("%lld\n",find(a-1,b-1)+find(c,d)-find(a-1,d)-find(c,b-1));
}
m=read();
}
}
int main()
{
init();
return 0;
}