树状数组&线段树
树状数组&线段树:解决连续区间动态查询问题
如:
- 单点修改/查询
- 单点修改/区间查询
- 区间修改/单点查询
- 区间修改/区间查询
- 区间最值/区间求和
基本上树状数组可以解决大多数问题,但是对于复杂的区间问题还是不行,
而树状数组能做到的线段树也能做到的,反之则不行。
下面我们先看这样一个【单点修改/区间查询】的题目:P3374 【模板】树状数组 1
【题目描述】
已知一个数列,你需要进行下面两种操作:
- 将某一个数加上 x
- 求出某区间每一个数的和
【输入格式】
第一行包含两个正整数 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;
}
线段树:解决连续区间动态查询问题
- 建树
- 单点/区间查询
- 单点/区间修改
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探