您的位置:首页 > 其它

【模板】树状数组

2017-09-05 23:56 246 查看

树状数组可以解决什么样的问题

对于包含n个元素的整数数组a,每次可以

1. C(i, j): 修改一个元素a[i] = j

2. Q(i): 询问前缀Si=a1+a2+…+ai的值

lowbit值

在说树状数组之前,我们不得不说一下lowbit值

设c[i]=a[i-2k+1]+…+a[i],其中k为i在二进制下末尾0的个数

令LOWBIT(i)=2^k

例如, i=1001010110010000, 则k=4

因为补码的原理是:正数变负数时,按位取反 末位加一

所以对于正数x 我们不难得到lowbit公式: lowbit(x)=x and -x

树状数组是一个动态维护前缀和的数据结构

网上最“火”的图应该是这个



[b]修改操作[/b]

当修改c[x]时, 可能有很多c随之修改

例如,对于x=76=01001010,可以得到:

p1= 01001010

p2= 01001100

p3= 01010000

p4= 01100000

p5= 10000000

所以

•P1=x

•Pi+1=Pi+LOWBIT(Pi)

则需要依次修改C[p1],C[p2],…

[b]询问操作[/b]

如何计算A[1]+…+A[x]?

首先累加C[x], 因为它的定义是以x结尾的连续和,它的连加起点是C[i-LOWBIT(i)+1]

因此问题转化为了求A[1]+…+A[i-LOWBIT(i)]

由此, 我们得到递推式

•P1=x

•Pi+1=Pi-LOWBIT(Pi)

则只需要累加C[p1], C[p2], …

清晰一点就像这样



【模板】树状数组 1 洛谷P3374

裸树状数组代码:

#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define MAXN 500050
using namespace std;
int tree[MAXN];
int n,m;
inline void add(int x,int num)
{
while(x<=n)
{
tree[x]+=num;
x+=lowbit(x);
}
}
inline int search(int x)
{
int re=0;
while(x)
{
re+=tree[x];
x-=lowbit(x);
}
return re;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
int x;
scanf("%d",&x);
add(i,x);
}
for(int i=1;i<=m;++i)
{
int opt,x,y;
scanf("%d%d%d",&opt,&x,&y);
if(opt==1)        add(x,y);
else            printf("%d\n",search(y)-search(x-1));
}
return 0;
}


【模板】树状数组 2 洛谷P3368

裸树状数组+差分

#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define MAXN 500050
using namespace std;
int tree[MAXN];
int a[MAXN];
int n,m;
inline void add(int x,int num)
{
for(int i=x;i<=n;i+=i&-i)
{
tree[i]+=num;
}
}
inline int search(int x)
{
int re=0;
while(x)
{
re+=tree[x];
x-=lowbit(x);
}
return re;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)    scanf("%d",&a[i]),add(i,a[i]-a[i-1]);
for(int i=1;i<=m;++i)
{
int opt,x,y,k;
scanf("%d",&opt);
if(opt==1)
{
scanf("%d%d%d",&x,&y,&k);
add(y+1,-k);
add(x,k);
}
else
{
scanf("%d",&x);
printf("%d\n",search(x));
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: