树状数组: 每个节点储存所有子节点和该节点对应的值之和,每个节点的值的求解有一个lowbit()函数的关系。
复杂度: 更新单点信息O(logn),求区间和O(logn).
树状数组能解决的,线段树基本都能解决。线段树
核心函数:
ll A[600005],n;
//A数组存的是所有子节点加上自身的和,不同实现可以有不同含义
ll lowbit(ll X){return X&(-X);}
void update(ll I,ll K){ //在i位置加上k
while(I <= n){
A[I] += K;
I += lowbit(I);
}
}
ll getsum(ll I){ //求1 - i的和
ll res = 0;
while(I > 0){
res += A[I];
I -= lowbit(I);
}
return res;
}
单点修改,区间查询:
int main()
{
cin>>n>>w;
for(ll i=1;i<=n;i++){
ll tmp;cin>>tmp;
update(i,tmp);//初始赋值
}
char op;
ll x,y;
for(ll i=1;i<=w;i++){
cin>>op>>x>>y;
if(op=='1'){
update(x,y);//在x位置加上y
}
else{
//求区间[x,y]的所有数之和
ll tot1=getsum(x-1),tot2=getsum(y);
cout<<tot2-tot1<<"\n";
}
}
return 0;
}
区间修改,单点查询:
看到区间修改单点查询,很容易想到差分数组(O(1) 时间修改区间,但O(n) 时间查询单点)。差分数组的查询比较慢,当问题的查询比较多时,可以用树状数组优化(因为树状数组更新单点和查询区间都是logn的)。
int main()
{
cin>>n>>w;
for(ll i=1;i<=n;i++){
cin>>a[i];
update(i,a[i]-a[i-1]);//用A[]存差分数组信息
}
char op;
ll x,y,k;
for(ll i=1;i<=w;i++){
cin>>op;
if(op=='1'){
cin>>x>>y>>k;
update(x,k);
update(y+1,-k);
}
else{
cin>>x;
ll ans=getsum(x);
cout<<ans<<"\n";
}
}
return 0;
}
区间修改,区间查询:
这个功能和线段树比较重复了,只是树状数组的常数时间更小一些,树状数组更好编写一些。
例题:A Simple Problem with Integers
//和之前的不一样,现在存两个数组
ll sum1[500005]; //(D[1] + D[2] + ... + D[n])
ll sum2[500005]; //(1*D[1] + 2*D[2] + ... + n*D[n])
ll lowbit(ll X){
return X&(-X);
}
void update(ll I,ll K){
ll x = I; //因为x不变,所以得先保存i值
while(I <= n){
sum1[I] += K;
sum2[I] += K * (x-1);
I += lowbit(I);
}
}
ll getsum(ll I){ //求前缀和
ll res = 0, x = I;
while(I > 0){
res += x * sum1[I] - sum2[I];
I -= lowbit(I);
}
return res;
}
int main()
{
cin>>n>>w;
for(ll i=1;i<=n;i++){
cin>>a[i];
update(i,a[i]-a[i-1]);//类似前面差分数组
}
char op;
ll x,y,k;
for(ll i=1;i<=w;i++){
cin>>op;
if(op=='C'){
cin>>x>>y>>k;
update(x,k);
update(y+1,-k);
}
else{
cin>>x>>y;
ll ans=getsum(y)-getsum(x-1);//区间查询
cout<<ans<<"\n";
}
}
return 0;
}
版权声明:本文为Khasehemwy原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。