【分治】线段树 SegmentTree
算法描述
线段树是一种能够处理区间修改和区间查询的数据结构。
顾名思义,线段树就是一种存储着线段数据的树形结构。它的每个节点都表示一个线段区间,每个节点的孩子节点存储的就是该区间的左半段和右半段。每个线段区间都存储着一个值,一般是区间和,也有可能是区间最大/最小值。
算法实现
线段树使用数组实现,根节点编号为
ll lc(ll p){return p << 1;}
ll rc(ll p){return p << 1 | 1;}
初始化
从根节点开始,不断的分割区间直到该区间只剩单个数,然后开始向上汇总。传入两个变量 l
和 r
, 表示当前节点表示的线段。
void pushUp(ll p){
sum[p] = sum[lc(p)] + sum[rc(p)];
}
void build(ll p, ll l, ll r){
if(l == r){
sum[p] = a[l];
return;
}
ll mid = (l + r) >> 1;
build(lc(p), l, mid);
build(rc(p), mid + 1, r);
pushUp(p);
}
区间查询
从根节点开始,如果当前的线段不被查询的区间完全包含就一直分两段尝试,直到它被完全包含了,返回该线段的值然后继续递归其他的。
ll Query(ll p, ll l, ll r, ll ql, ll qr){
if(ql <= l && qr >= r){
return sum[p];
}
pushDown(p, l, r);
ll mid = (l + r) >> 1, ans = 0;
if(ql <= mid){
ans += Query(lc(p), l, mid, ql, qr);
}
if(qr > mid){
ans += Query(rc(p), mid + 1, r, ql, qr);
}
return ans;
}
区间修改
如果每次修改都从上到下全改一遍,复杂度得
void Update(ll p, ll l, ll r, ll ql, ll qr, ll d){
if(ql <= l && qr >= r){
sum[p] += d * (r - l + 1);
tag[p] += d;
return;
}
pushDown(p, l, r);
ll mid = (l + r) >> 1;
if(ql <= mid){
Update(lc(p), l, mid, ql, qr, d);
}
if(qr > mid){
Update(rc(p), mid + 1, r, ql, qr, d);
}
pushUp(p);
}
复杂度估算
由于线段树原理是每个区间分两段,再将两端区间分别分段以此类推,又有
总代码
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const ll MAXN = 1e5 + 5;
ll sum[4 * MAXN], a[MAXN], tag[4 * MAXN];
ll lc(ll p){return p << 1;}
ll rc(ll p){return p << 1 | 1;}
void pushUp(ll p){
sum[p] = sum[lc(p)] + sum[rc(p)];
}
void build(ll p, ll l, ll r){
if(l == r){
sum[p] = a[l];
return;
}
ll mid = (l + r) >> 1;
build(lc(p), l, mid);
build(rc(p), mid + 1, r);
pushUp(p);
}
void moveTag(ll p, ll l, ll r, ll t){
sum[p] += t * (r - l + 1);
tag[p] += t;
}
void pushDown(ll p, ll l, ll r){
ll mid = (l + r) >> 1;
moveTag(lc(p), l, mid, tag[p]);
moveTag(rc(p), mid + 1, r, tag[p]);
tag[p] = 0;
}
void Update(ll p, ll l, ll r, ll ql, ll qr, ll d){
if(ql <= l && qr >= r){
sum[p] += d * (r - l + 1);
tag[p] += d;
return;
}
pushDown(p, l, r);
ll mid = (l + r) >> 1;
if(ql <= mid){
Update(lc(p), l, mid, ql, qr, d);
}
if(qr > mid){
Update(rc(p), mid + 1, r, ql, qr, d);
}
pushUp(p);
}
ll Query(ll p, ll l, ll r, ll ql, ll qr){
if(ql <= l && qr >= r){
return sum[p];
}
pushDown(p, l, r);
ll mid = (l + r) >> 1, ans = 0;
if(ql <= mid){
ans += Query(lc(p), l, mid, ql, qr);
}
if(qr > mid){
ans += Query(rc(p), mid + 1, r, ql, qr);
}
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll n, m;
cin >> n >> m;
for(ll i = 1; i <= n; i ++){
cin >> a[i];
}
build(1, 1, n);
while(m --){
ll o, x, y, k;
cin >> o;
if(o == 1){
cin >> x >> y >> k;
Update(1, 1, n, x, y, k);
}else{
cin >> x >> y;
cout << Query(1, 1, n, x, y) << "\n";
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具