20260112树状数组总结

引子

树状数组是一种支持单点修改和区间查询码量低常数小的数据结构。

任何数字都可以表示为不超过logn个2的幂次之和,例如7=4+2+1,这一特性就是树状数组的核心理论。

关键在于设计一种数据结构,使得任意前缀和都能由logn个区间和表示以及每个位置最多被logn个区间覆盖,这样就能实现单点修改和区间查询。

树状数组通过lowbit运算实现这一目标。lowbit定义为数字二进制表示中最右边的1所代表的值,可通过x&-x快速计算。例如6的lowbit是2(110)、8的lowbit是8(1000)

在具体实现中,ti存储以 i 为右端点长度为lowbit(i)的区间和,前缀和计算采用递推的方式sum(7)=t[7]+t[6]+t[4],其中6=7-lowbit(7)4=6-lowbit(6)

单点更新时,由于ti包含位置 i,所以只需要沿着lowbit递增的方向更新所有相关区间就行了,同样利用lowbit实现高效处理。

B3612 【深进1.例1】求区间和

虽然这道题用前缀和比树状数组快一倍,但如果用树状数组写的话呃比模板题会简单一些。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;//树状数组参考模板
int s[100005],n;
int lowbit(int x){//lowbit函数
    return x&(-x);
}
void add(int i,int x){//递增
    for(;i<=n;i+=lowbit(i))s[i]+=x;
}
int sum(int i){//求区间和
    int sm=0;
    for(;i>0;i-=lowbit(i))sm+=s[i];
    return sm;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        add(i,x);//递增
    }
    int m;
    cin>>m;
    while(m--){
        int l,r;
        cin>>l>>r;
        cout<<sum(r)-sum(l-1)<<endl;//前缀和
    }
    return 0;
}

P3374 【模板】树状数组 1

单点修改和区间查询。

这里说明一下树状数组是可以中途单点修改的。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int s[500005],n;
int lowbit(int x){
    return x&(-x);
}
void add(int i,int x){
    for(;i<=n;i+=lowbit(i))s[i]+=x;
}
int sum(int i){
    int sm=0;
    for(;i>0;i-=lowbit(i))sm+=s[i];
    return sm;
}
int main(){
    int m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        add(i,x);
    }
    while(m--){
        int opt,x,y;
        cin>>opt>>x>>y;
        if(opt==1){//这里判断一下是单点修改还是区间查询
            add(x,y);
        }else{
            cout<<sum(y)-sum(x-1)<<endl;//还是前缀和
        }
    }
    return 0;
}

P3368 【模板】树状数组 2

区间修改和单点查询。

首先直接x到y都递增一次会发现TLE70分,于是考虑用树状数组维护一个差分数组,三函数一样,输入a数组时add(ai-ai-1);要区间修改时add(l,k)add(r+1,k),还是差分;单点查询时输出sum(x),相当于差分之后进行一次前缀和。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int a[500005],s[500005],n;
int lowbit(int x){
    return x&(-x);
}
void add(int i,int x){
    for(;i<=n;i+=lowbit(i))s[i]+=x;
}
int sum(int i){
    int sm=0;
    for(;i>0;i-=lowbit(i))sm+=s[i];
    return sm;
}
int main(){
    int m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        add(i,a[i]-a[i-1]);
    }
    while(m--){
        int opt;
        cin>>opt;
        if(opt==1){
            int x,y,k;
            cin>>x>>y>>k;
            add(x,k);
            add(y+1,-k);
        }else{
            int x;
            cin>>x;
            cout<<sum(x)<<endl;
        }
    }
    return 0;
}
相关推荐
鸿芯微控科技2 分钟前
MFC质量流量控制器流量不准怎么排查?设定值、反馈值与参考流量对比,附Python代码
c++·python·mfc
CVer儿9 分钟前
c++二级指针
开发语言·c++
极序时代GEO品牌优化33 分钟前
杭州极序时代|面向地域、设备与用户画像的动态GEO
人工智能·python·算法·极序时代geo
mlidongfeng1 小时前
[AI][C++26] SIMD 编程模型思考
开发语言·c++·人工智能
梓䈑1 小时前
【用 Vibe Coding 实现的 C++17 在线判题系统】OpenCode 入门指南:从环境搭建到上下文管理
c++·ai编程
j7~1 小时前
【Linux】二十七.线程篇四《Linux多线程编程:线程互斥(互斥量的底层到封装)、线程安全和冲入、死锁》---详解
linux·开发语言·c++·线程安全·死锁·线程互斥·重入
啦啦啦啦啦zzzz1 小时前
贪心算法和动态规划
c++·算法·贪心算法·动态规划
happyprince1 小时前
篇3:bitsandbytes-深刻观-哲学与升华
人工智能·算法
我不是懒洋洋1 小时前
从零实现一个分布式数据集成:Airbyte的核心设计
c++
应用市场1 小时前
红绿灯背后的算法:从 Webster 配时到 Max-Pressure 与强化学习
算法