001 树状数组基础

数据结构-——树状数组杂谈

  首先,明确树状数组的功能,简单来说,树状数组就是一种支持单点修改,区间查询的数据结构。树状数组的工作基于唯一分解性质,通过将数组拆分成2的次幂大小的区间来完成高效查询操作,可以高效解决前缀和和逆序对问题。

  工作机理:区间P[1,x]可以拆分成logx个小区间,如上所述,以2的次幂为大小的区间。知晓了基本要求,那么如何确定这些小区间呢?引入lowbit运算,简单的说,lowbit运算就是取出指定数的最低(1)位。举个例子,对5(101)进行lowbit运算,取出(001);对8(1000)进行lowbit运算,取出(1000)。lowbit的运算规则lowbit(x)=(x)&(-x)。通过lowbit运算,我们可以得到每个小区间,具体的说

while(x>0)
{
  printf("%d %d",x-(x&-x)+1,x);
  x-=x&-x;        
}

  存在c[x]数组,存储前缀和,c[x]运算机理如图所示。

  上面提到,树状数组支持区间查询,具体来说,查询操作

long long query(int x)
{
    long long ans=0;
    for(;x;x-=(x&-x)) ans+=c[x];
    return ans;
}

  单点修改

void add(int  x,int k)
{
    for(;x<=n;x+=(x&-x)) c[x]+=k;
}

  初始化操作:按需即可,若是初始化前缀和,那么直接对每个点单点操作即可,即可建起树状数组

树状数组工作原理(图片来源于oi-wiki)

image

https://www.luogu.com.cn/problem/P3374 例题代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
long long a[N],c[N],b[N],n,m;
void add(int  x,int k)
{
    for(;x<=n;x+=(x&-x)) c[x]+=k;
}
long long query(int x)
{
    long long ans=0;
    for(;x;x-=(x&-x)) ans+=c[x];
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin>>n>>m;
    for(int i=1;i<=n;i++) 
        cin>>a[i],add(i,a[i]);
    for(int i=1;i<=m;i++)
    {
        int op;
        cin>>op;
        if(op==1)
        {
            int x,k;
            cin>>x>>k;
            add(x,k);
        }
        else 
        {
            int x,y;
            cin>>x>>y;
            cout<<query(y)-query(x-1)<<"\n";//注意查询值
        }
    }

    return 0;
}

 

   探讨:那么树状数组能不能实现区间修改呢?答案很简单,不可以。一般来说,区间修改都要使用线段树,那么我们能不能通过鬼脑想到怎么完成树状数组的区间修改呢,答案是可以的。也就是说,我们可以用树状数组来维护一个差分数组。

   这道变式题也就是区间修改,单点查询。

P3368 【模板】树状数组 2

  采用精妙的差分数组的形式就可以完成树状数组区间的修改,简单来说,就是把原数组的值不加入c[x]当中,转而维护增加的值。这样就可以搞定区间修改的难题。

  

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
long long a[N],c[N],n,m;
void add(int x,int y)
{
    for(;x<=n;x+=(x&-x))
    {
        c[x]+=y;
    }
}
long long query(int x)
{
    long long ans=0;
    for(;x;x-=(x&-x))
        ans+=c[x];
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=m;i++)
    {
        int op;
        cin>>op;
        if(op==1)
        {
            int x,y,k;
            cin>>x>>y>>k;
            add(x,k);
            add(y+1,-k);
        }
        if(op==2)
        {
            int x;
            cin>>x;
            cout<<a[x]+query(x)<<"\n";
        }
    }
    return 0;
}

 

posted @ 2026-08-12 13:13  Noname_min  阅读(4)  评论(0)    收藏  举报