树状数组&线段树

树状数组&线段树:解决连续区间动态查询问题

如:

  • 单点修改/查询
  • 单点修改/区间查询
  • 区间修改/单点查询
  • 区间修改/区间查询
  • 区间最值/区间求和

基本上树状数组可以解决大多数问题,但是对于复杂的区间问题还是不行,
而树状数组能做到的线段树也能做到的,反之则不行。


下面我们先看这样一个【单点修改/区间查询】的题目:P3374 【模板】树状数组 1

【题目描述】
已知一个数列,你需要进行下面两种操作:

  1. 将某一个数加上 x
  2. 求出某区间每一个数的和

【输入格式】
第一行包含两个正整数 n,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。
接下来 m 行每行包含 3 个整数,表示一个操作,具体如下:
1 x k 含义:将第 x 个数加上 k
2 x y 含义:输出区间 [x,y] 内每个数的和

【输出格式】 输出包含若干行整数,即为所有操作 2 的结果。

输入样例:

5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4

输出样例:

14
16

【数据范围】
对于 30% 的数据,1 ≤n ≤8, 1 ≤m ≤10;
对于 70% 的数据,1 ≤n, m ≤10^4;
对于 100% 的数据,1 ≤n, m ≤5 ×10^5.

很明显这个题目如果直接暴力枚举,时间为 O(n^2) 不行,那么我们需要想 nlogn的方法。

树状数组

#include<iostream>
#define ll long long
using namespace std;
const int N=1e5+10;
ll arr[N], c[N], n, m;

// 2^k 的最低位 
int lowbit(int x){
    return x&(-x);
}
//第 i个元素的值增加 x
void add(int i,int x){
    while(i<=n){
        c[i] += x;
        i += lowbit(i);
    }
}
//查询 arr[1~i]的和
ll query(int i){
    ll sum=0;
    while(i>0){
        sum += c[i];
        i -= lowbit(i);
    }
    return sum;
}
int main(){
//    freopen("data.in", "r", stdin);
    cin>>n>>m;
    for(int i=1; i<=n; i++) cin>>arr[i];
    for(int i=1; i<=n; i++) add(i, arr[i]);
    for(int i=1; i<=m; i++){
       int k,a,b; cin>>k>>a>>b;
       if(k==1) add(a, b);
       else cout<<query(b)-query(a-1)<<endl;
    }
    return 0;
}

线段树:解决连续区间动态查询问题

  1. 建树
  2. 单点/区间查询
  3. 单点/区间修改
#include<iostream>
#define ll long long
using namespace std;
const int N=1e5+10;
int tree[N*4], arr[N];
//线段树的建立
void build(int l, int r, int i){
    if(l==r){
        tree[i]==arr[l]; return;
    }
    int mid=(l+r)>>1;
    build(l, mid, i<<1);
    build(mid+1, r, i<<1|1);
    tree[i]=max(tree[i<<1], tree[i<<1|1]);
    return ;
}

//线段树的单点更新  a[x]=y
void updata(int l, int r, int i, int x, int y){
    if(l==r && l==x){
        tree[i]=y; return;
    }
    int mid=(l+r)>>1;
    if(mid>=x) updata(l, mid, i<<1, x, y);
    else updata(mid+1, r, i<<1|1, x, y);
    tree[i]=max(tree[i<<1], tree[i<<1|1]);
    return ;
}

//线段树的区间更新

//线段树的单点查询

//线段树的区间查询
void query(int l, int r, int i, int a, int b){
    if(l>=a && r<=b){
        ans=max(ans, tree[i]); return ;
    }
    int mid=(l+r)>>1;
    if(mid>=a) query(l, mid, i<<1, a, b);
    if(mid<b) query(mid+1, r, i<<1|1, a, b);
    return ;
}
int main(){
    int n,m; while(~scanf("%d%d", &n, &m)){
        for(int i=1; i<=n; i++) scanf("%d", &arr[i]);
        build(1, n, 1);
        while(m--){
            char op; int x,y;
            scanf(" %c%d%d", &op, &x, &y);
            if(op=='Q') ans=-1, query(1, n, 1, x, y), printf("%d\n", ans);
            if(op=='U') updata(1, n, 1, x, y);
        }
    }
    return 0;
}
posted @   HelloHeBin  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示