线段树 ----洛谷p3372
问题描述:
已知一个数列,对数列进行两种操作:1,对数列某个区间中的所有数加d;2,查询数列某区间的区间和
输入:
第一行输入两个整数n和m,分别代表数列中元素个数和对数列的操作次数,第二行输入n个用空格隔开的整数,接下来的m行输入3或4个整数,表示m种操作:
(1)1 L R d:表示对区间[L,R]中的所有数字加d;
(2)2 L R:表示查询并输出[L,R]区间的区间和;
输出:
输出所有操作2的区间和
数据范围:
1<=m,n<=10e5, 数列中元素取值为[-2e63,2e63];
线段树:
使用线段树首先要明确树状数组的叶子节点和子树根节点代表什么,叶子节点自然是代表数组中的元素,由于本题需要解决的都是有关区间的问题,所以子树根节点自然就是区间和,但本题还涉及到区间修改,如果直接再区间中修改,那么每次修改都需要从该修改区间向下传递修改信息直到叶子节点,但查询操作并不是每次修改后都需要查询,显然这将会进行很多不必要的操作增加时间复杂度,有什么可以一劳永逸的呢,最简单直观的方法就是先不修改,直到读取到查询操作的时候再将修改信息传递,对,这就是离线操作和在线操作的区别,也就是线段树中常用的lazy_tag操作,接下来直接看代码:
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 1e5 + 10;
ll a[N]; //记录数列元素
ll tree[N << 2]; //线段树
ll tag[N << 2];
ll ls(ll p) { return p << 2; }
ll rs(ll p) { return p << 2 | 1; }
void pushup(ll p) {
tree[p] = tree[ls(p)] + tree[rs(p)];
}
//建立线段树
void build(ll p, ll pl, ll pr) {
//调用build(1,1,n);
tag[p] = 0; //由于线段树结构的特性,数组中有很多空位置,我们不去使用那些空位,只更新线段树用到的位置
if (pl == pr) {
tree[p] = a[pl]; return; //此时p=pl=pr,不明白的可以看看线段树的图解
}
int mid = (pl + pr) >> 2;
//搜索算法
build(ls(p), pl, mid);
build(rs(p), mid + 1, pr);
//直到叶子节点,递归回根节点的路上更新沿路区间和,即tree[p]的值
pushup(p);
}
//lazy_tag算法
void addtag(ll p,ll pl,ll pr,ll d) {
tag[p] += d;
tree[p] += (pl - pr + 1) * d;
}
//向下传递函数
void pushdown(ll p, ll pl, ll pr) {
if (tag[p]) {
//如果子节点有tag[p],则一直传递直到叶子节点
int mid = (pl + pr);
addtag(ls(p), pl, mid,tag[p]);
addtag(rs(p), mid + 1, pr,tag[p]);
tag[p] = 0;
}
}
//更新操作
void update(ll L, ll R, ll p, ll pl, ll pr, ll d) {
if (L <= pl && R >= pr) {
addtag(p, pl, pr, d);
return;
}
int mid = (pl + pr) >> 1; //这个是左中位数,所以下面的等号使用是有讲究的,原理类似于二分法,感兴趣的同学可自行查阅
if (L <= mid) update(L, R, ls(p), pl, mid, d);
if (R > mid) update(L, R, rs(p), mid + 1, pr, d);
//更新之后任然需要将更新信息传给与更新路径相关的节点
pushup(p);
}
//查询操作
ll query(ll L, ll R, ll p, ll pl, ll pr) {
if (L <= pl && R >= pr) {
return tree[p];
}
//将tag标记传递下去
pushdown(p, pl, pr);
ll res = 0;
int mid = (pl + pr) >> 1;
if (L <= mid) res += query(L, R, ls(p), pl, mid);
if (R > mid) res += query(L, R, rs(p), mid + 1, pr);
return res;
}
int main() {
ll n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
build(1, 1, n);
while (m--) {
ll q, l, r, d; cin >> q;
if (q == 1) {
cin >> l >> r >> d;
update(l, r, 1, 1, n, d);
}
if (q == 2) {
cin >> l >> r;
query(l, r, 1, 1, n);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】