AcWing算法提高课 线段树
操作:
pushup:由子节点算父节点信息
build:建立线段树,将一段区间初始化为线段树
modify:修改,修改某个点,或修改某个区间(需要使用pushdown)
query:查询,查询某一段区间的信息
pushdown:父节点修改传递到子节点修改(懒标记)
原理:
满二叉树:
节点编号:
一般数组开4n的大小
模板:
仅支持单点修改:(以记录最大值为例)

const int N = 200010; struct Node { int l, r; int val; }tr[N * 4]; //由子节点信息计算父节点信息,以最大值为例 void pushup(int u) { tr[u].val = max(tr[2 * u].val, tr[2 * u + 1].val); } void build(int u, int l, int r) { if (l == r) { tr[u] = { l,r,0 }; } else { tr[u].l = l; tr[u].r = r; int mid = (l + r) / 2; build(2 * u, l, mid); build(2 * u + 1, mid + 1, r); pushup(u); } } int query(int u, int l, int r) { if (l > r) return 0; if (tr[u].l >= l && tr[u].r <= r) { return tr[u].val; } int mid = (tr[u].l + tr[u].r) / 2; int res = 0; if (l <= mid) res = query(2 * u, l, r); if (r > mid) res = max(res, query(2 * u + 1, l, r)); return res; } void modify(int u, int x, int val)//修改x的信息 { if (tr[u].l == x && tr[u].r == x) tr[u].val = val; else { int mid = tr[u].l + tr[u].r >> 1; if (x <= mid) modify(2 * u, x, val); else modify(2 * u + 1, x, val); pushup(u);//注意更新 } } build(1, 1, n); for (int i = 1; i <= n; i++) { int a; cin >> a; modify(1, i, a); }
例题:
245. 你能回答这些问题吗(线段树维护连续子区间最大值)

#include<bits/stdc++.h> #include<unordered_set> using namespace std; typedef long long LL; struct Node { int l,r; int imax; int lmax, rmax; int sum; }tr[500010*4]; int n, m, nums[500010]; void pushup(int u) { int l = 2 * u, r = 2 * u + 1; tr[u].imax = max({ tr[l].rmax + tr[r].lmax, tr[l].imax , tr[r].imax }); tr[u].lmax = max(tr[l].lmax, tr[l].sum + tr[r].lmax); tr[u].rmax = max(tr[r].rmax, tr[r].sum + tr[l].rmax); tr[u].sum = tr[l].sum + tr[r].sum; } void build(int u, int l, int r) { if (l == r) { tr[u] = { l,r,nums[l],nums[l] ,nums[l] ,nums[l] }; } else { tr[u].l = l; tr[u].r = r; int mid = l + r >> 1; build(u *2, l, mid); build(u *2 + 1, mid + 1, r); pushup(u); } } void modify(int u, int i,int v) { if (tr[u].l == i && tr[u].r == i) { tr[u] = { i,i,v,v,v,v }; } else { int mid = tr[u].l + tr[u].r >> 1; if (i <= mid) modify(2 * u, i, v); else modify(2 * u + 1, i, v); pushup(u); } } Node query(int u, int l, int r) { if (tr[u].l >= l && tr[u].r <= r) { return tr[u]; } else { int mid = tr[u].l + tr[u].r >> 1; if (r <= mid) return query(2 * u, l, r); else if (l > mid) return query(2 * u + 1, l, r); else { auto left= query(2 * u, l, r); auto right= query(2 * u + 1, l, r); Node res; //res.l = l; res.r = r; res.imax = max({ left.rmax + right.lmax, left.imax , right.imax }); res.lmax = max(left.lmax, left.sum + right.lmax); res.rmax = max(right.rmax, right.sum + left.rmax); res.sum = left.sum + right.sum; return res; } } } void YD() { cin >> n >> m; for (int i = 1; i <= n; i++) cin >> nums[i]; build(1, 1, n); int k, x, y; while (m--) { cin >> k >> x >> y; if (k == 1) { if (x > y) swap(x, y); cout << query(1, x, y).imax << endl; } else { modify(1, x, y); } } } int main() { YD(); return 0; }
注意,由于左移、右移操作的优先级比加法低,所以要么2*u+1,要么u>>1|1,不能把>>和+混用
AcWing 246. 区间最大公约数 (线段树维护最大公约数,区间整体加一个数)
此题虽然做了区间修改,但仅会区间整体加一个数,可以转化为差分数组的单点修改,所以不用懒标记
数组的最大公约数和差分数组的最大公约数也有转化的关系
故只需要对差分数组进行单点修改和区间查询
记录的值为差分数组的最大公约数和区间和(差分的前缀和用于求单点的值)

#include<bits/stdc++.h> #include<unordered_set> using namespace std; typedef long long LL; const int N = 500010; int n,m; LL nums[N]; struct Node { int l, r; LL sum, d; }tr[N*4]; LL GCD(LL a, LL b) { return b == 0 ? a : GCD(b, a % b); } void pushup(int u) { tr[u].sum = tr[2 * u].sum + tr[2 * u + 1].sum; tr[u].d= GCD(tr[2 * u].d , tr[2 * u + 1].d); } void build(int u, int l, int r) { if (l == r) { LL t = nums[l] - nums[l - 1]; tr[u] = { l,r,t,t }; } else { tr[u].l = l; tr[u].r = r; int mid = l + r >> 1; build(u * 2, l, mid); build(u * 2 + 1, mid + 1, r); pushup(u); } } void modify(int u, int i, LL v) { if (tr[u].l == i && tr[u].r == i) { //单点 tr[u].sum += v; tr[u].d += v; } else { int mid = tr[u].l + tr[u].r >> 1; if (i <= mid) modify(2 * u, i, v); else modify(2 * u+1, i, v); pushup(u); } } Node query(int u, int l, int r) { if (l <= tr[u].l && r >= tr[u].r) { return tr[u]; } else { int mid = tr[u].l + tr[u].r >> 1; if (mid >= r) return query(2 * u, l, r); else if (mid < l) return query(2 * u + 1, l, r); else { Node left= query(2 * u, l, r); Node right= query(2 * u + 1, l, r); Node res; res.sum = left.sum + right.sum; res.d = GCD(left.d, right.d); return res; } } } void YD() { cin >> n >> m; for (int i = 1; i <= n; i++) cin >> nums[i]; build(1, 1, n); int l, r; LL d; char op; while (m--) { cin >> op >> l >> r; if (op == 'Q') { auto left = query(1, 1, r); LL tmp = left.sum; if (l + 1 <= r) { tmp = GCD(tmp, query(1, l + 1, r).d); } cout << abs(tmp)<< endl; } else { LL v; cin >> v; modify(1, l, v); if(r+1<=n) modify(1, r + 1, -v); } } } int main() { YD(); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人