前缀知识
lowbit=x&(-x)
lowbit表示的是转换成二进制取第一个一和后面的0得到的数,树状数组经常用到lowbit(原理我也不知道)
树状数组只支持单点修改和前缀查询,其他可以通过拓展
时间复杂度分析:
单点修改O(log n)
前缀和查询O(log n)
树状数组操作:
1.lowbit();
2.modify(x,d);//用于将点x加上数d
3.query(int x);//表示算出到x的前缀和
经典例题
1.洛谷P3374
裸的树状数组,直接按照需要的操作进行单点修改和前缀和查询,在利用前缀和的性质,来算出区间答案
cpp代码如下(代码中,a[i]表示一开始第i项的初始值,s[i]表示编号为i的数在维护的树状数组里,每个编号的数
#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
using namespace std;
int a[500005],n,m,s[500005];
int lowbit(int x)
{
return x&(-x);
}
void modify(int x,int d)
{
for(int i=x;i<=n;i+=lowbit(i))
{
s[i]+=d;
}
}
int qusry(int x)//返回前x项的前缀和
{
int sum=0;
for(int i=x;i;i-=lowbit(i))
{
sum+=s[i];
}
return sum;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
modify(i,a[i]);
}
for(int i=1;i<=m;i++)
{
int xx,x,y,k;
cin>>xx;
if(xx==1)
{
scanf("%d%d",&x,&k);
modify(x,k);
}
if(xx==2)
{
scanf("%d%d",&x,&y);
cout<<qusry(y)-qusry(x-1)<< endl;
}
}
return 0;
}
2.洛谷P3368
其实就是由单点修改,变成了区间修改,根据差分数组,只需要由单点修改改成双点修改就可以了
cpp代码如下(带注释)
#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include<algorithm>
#include<cmath>
#include<stdlib.h>
using namespace std;
int n,m,a[500001],s[500001],c[500001];//a数组表示第i项的初始值
int lowbit(int x)
{
return x&(-x);
}
void modify(int x,int d)
{
for(int i=x;i<=n;i+=lowbit(i))
{
c[i]+=d;
}
}
int qusry(int x)
{
int sum=0;
for(int i=x;i;i-=lowbit(i))
{
sum=sum+c[i];
}
return sum;
}
//以上皆为树状数组正常操作
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);//读入初始值
}
for(int i=1;i<=n;i++)
{
s[i]=a[i]-a[i-1];//维护一个差分数组
modify(i,s[i]);//将差分数组形成一个新的树状数组
}
for(int i=1;i<=m;i++)
{
int xx,x,y,k;
scanf("%d",&xx);
if(xx==1)
{
scanf("%d%d%d",&x,&y,&k);
modify(x,k);
modify(y+1,-k);
//根据差分数组,只需要将他的头指针+k,(尾指针+1)的指针的值-k,就可以将x~y区间的值都加上k
}
if(xx==2)
{
scanf("%d",&x);
cout<<qusry(x)<< endl;//查询的这个数就为他的差分数组的前缀和
}
}
return 0;
}