树状数组是一种结合了二进制的一种数据结构,区间修改的复杂度跟普通的相比n减少到logn 。
主要是两个函数:1.update区间修改函数 2.query区间询问函数
有点类似前缀和
比如a数组代表单点数据,s数组代表树状数组数据
s[4]=a1+a2+a3+a4这是和前缀和数组一样的,但是
s[6]却等于a[5]+a[6],如下图,这是因为6的二进制码为110,去掉高位1剩余一个0,它的和就为2^1=2个数的和,且为最近两个数的和,如果是s[10],二进制码为1010,去掉高位1以及夹杂的0,也是剩下一个0,所以它的和为a[9]+a[10]。
#include
//
using namespace std;
#define lowbit(x) ((x)&(-x))
const int N=5e5+10;
int tree[N],a[N],n,m;
//构造一个树状数组,x为下标,k为数
void update(int x,int k)
{
while(x<=n)
{
tree[x]+=k;
x+=lowbit(x);
}
}
void build()
{
for(int i=1;i<=n;i++)
update(i,a[i]);
}
int query(int x)
{
int sum=0;
while(x)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build();
int flag,x,y;
for(int i=1;i<=m;i++)
{
scanf("%d %d %d",&flag,&x,&y);
if(flag==1) update(x,y);
else printf("%d\n",query(y)-query(x-1));
}
return 0;
}
解决的问题:区间更新时可能会超时
将树状数组的基础元素转化为差分数组,众所周知,将区间[x,y]的元素都加上k,那么它的差分数组的变化只是为cha[x]+k,cha[y+1]-k,可以证明,
我举个例子,比如1 1 1 1 1这5个数,差分数组为1 0 0 0 0,想要将[2,4]都+1,即1 2 2 2 1,差分数组变为 1 1 0 0 -1,符合变化,最后再由于树状数组本身就是求区间和,最后输出时查询单点的值,也就是相当于上一个模板中求区间和,所以可由差分数组再求出单点的值。
例题:
#include
//
using namespace std;
#define lowbit(x) ((x)&(-x))
const int N=5e5+10;
int tree[N],a[N],n,m;
//构造一个树状数组,x为下标,k为数
void update(int x,int k)
{
while(x<=n)
{
tree[x]+=k;
x+=lowbit(x);
}
}
void build()
{
for(int i=1;i<=n;i++)
update(i,a[i]);
}
int query(int x)
{
int sum=0;
while(x)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
int main()
{
cin>>n>>m;
int last=0,now;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
now=a[i];
a[i]=now-last;
last=now;
}
build();
int flag,x,y,k;
for(int i=1;i<=m;i++)
{
scanf("%d",&flag);
if(flag==1)
{
scanf("%d %d %d",&x,&y,&k);
update(x,k);
update(y+1,-k);
}
else
{
scanf("%d",&k);
printf("%d\n",query(k));
}
}
return 0;
}
用到了离散化的方法
解题思路:这题求的是一个数字的之前出现的比这个数字小的和的次数,如果将数放入桶中,相当于求这个数之前的前缀和,所以可以用到树状数组来求前缀和,但是如果用桶的话,数据太大,装不下,就得用到数据离散化的方法
离散化:先用结构体标好顺序,然后再排序,然后再按顺序给数组赋值,第几大,并且数组下标得按顺序来,最后再按数组下标来进行树状数组的更新操作。
#include
//
using namespace std;
#define lowbit(x) ((x)&(-x))
const int N=5e5+10;
//maptree;
int tree[N],n,m,rank[N];
struct node
{
int num,val;
}a[N];
//构造一个树状数组,x为下标,k为数
void update(int x,int k)
{
while(x<=n)
{
tree[x]+=k;
x+=lowbit(x);
}
}
int query(int x)
{
int sum=0;
while(x)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
bool cmp(node x,node y)
{
if(x.val==y.val)return x.num<y.num;
return x.val<y.val;
}
int main()
{
cin>>n;
long long sum=0;
int x;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].val);
a[i].num=i;
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++)
rank[a[i].num]=i;
for(int i=1;i<=n;i++)
{
update(rank[i],1);
sum+=i-query(rank[i]);
}
printf("%lld",sum);
return 0;
}