【题解】可持久化区间仿射区间和(Persistent Range Affine Range Sum) 很好小朋友们我们被要求写一个可持久化线段树支持区间修改、区间复制和输出区间和。懒标记是必须的但可持久化线段树一般不太支持 pushdown尤其在本题中。在进入子节点之前我们需要克隆当前节点因为当前节点可能被其他版本共享。然后才能将其懒标记下推到新克隆出的子节点最后将当前节点的懒标记重置。这样会导致不必要的新节点开销。于是我们不考虑 pushdown而是在递归函数中同时传进祖先变化即懒标记。线段树维护五个参数分别是左右子节点、区间总和 b、c 懒标记。1处理操作 0我们构建一个 change 函数对版本 p 的区间 [l, r] 施加变换 x - b*x c返回新版本的根节点。可持久化的精髓是“改了才新建”编写时请注意祖先变化的传递。2处理操作 1一个个复制绝对是不明智的我们可以先构建一个 crossover 函数。合并两个版本 x 和 y返回一个新版本其前 at 个元素来自 x后 len - at 个元素来自 y。这样题目的 [l, r] 可以变成先处理 [1, r] 的复制接着把原来的 [1, l - 1] 复制回去。3处理操作 2正常的 query 函数即可。另注意本题的边界和 base 十分复杂我用的是统一 base-1。#include bits/stdc.h using namespace std; typedef long long LL; const LL P 998244353; // 模数 const int N 1e5 10; // 数组最大长度 #define lc(p) tr[p].lc // 左孩子下标 #define rc(p) tr[p].rc // 右孩子下标 #define MID ((L R) 1) // 当前区间中点 /* 线段树节点结构 lc, rc : 左右孩子指针下标 sum : 当前节点代表的区间和已应用该节点的懒标记 lazy_b, lazy_c : 懒标记表示对子区间施加的仿射变换 x - lazy_b * x lazy_c */ struct node { int lc, rc; LL sum; LL lazy_b, lazy_c; }; vectornode tr; // 动态存储所有节点0 号节点为空节点 LL a[N]; // 原始数组1‑based // 新建一个节点懒标记初始为恒等变换 (1, 0) int newd() { tr.push_back({0, 0, 0, 1, 0}); return (int)tr.size() - 1; } // 克隆节点 p复制其所有信息 int clone_node(int p) { tr.push_back(tr[p]); return (int)tr.size() - 1; } /* 将仿射变换 x - b*x c 应用到节点 p 所代表的整个区间长度为 len 同时更新该节点的懒标记使其与原有懒标记复合。 注意应用顺序为 新变换 作用于 旧变换即最终变换为 b*(old(x)) c。 */ void modify(int p, LL b, LL c, LL len) { if (p 0) return; tr[p].sum (b * tr[p].sum c * len) % P; tr[p].lazy_b b * tr[p].lazy_b % P; tr[p].lazy_c (b * tr[p].lazy_c % P c) % P; } // 复合变换返回 g(f())即先 f 后 g pairLL, LL compose(LL g_b, LL g_c, LL f_b, LL f_c) { return { (g_b * f_b) % P, (g_b * f_c g_c) % P }; } // 用左右子树的 sum 更新当前节点的 sum void pushup(int p) { tr[p].sum (tr[lc(p)].sum tr[rc(p)].sum) % P; } // 建树p 为引用返回根节点下标 void build(int p, int L, int R) { p newd(); if (L R) { tr[p].sum a[L] % P; return; } build(lc(p), L, MID); build(rc(p), MID 1, R); pushup(p); } /* 对版本 p 的区间 [l, r] 施加变换 x - b*x c返回新版本的根节点。 参数 prop_b, prop_c 表示从根到当前节点路径上所有祖先懒标记复合后的变换 需要在访问当前区间时应用。 本函数采用路径复制只修改必要的节点保留未修改部分。 */ int change(int p, int L, int R, int l, int r, LL b, LL c, LL prop_b, LL prop_c) { // 完全不相交克隆当前节点并应用祖先变换然后返回 if (r L || R l) { int np clone_node(p); modify(np, prop_b, prop_c, R - L 1); return np; } // 完全覆盖克隆当前节点整体施加 “祖先变换 当前变换” if (l L R r) { auto t compose(b, c, prop_b, prop_c); // 先 prop后 (b,c) int np clone_node(p); modify(np, t.first, t.second, R - L 1); return np; } // 部分重叠创建新节点懒标记为恒等 int np newd(); // 传递给子节点的祖先变换 prop ∘ p.lazy先 p.lazy后 prop auto t compose(prop_b, prop_c, tr[p].lazy_b, tr[p].lazy_c); // 左子区间有重叠递归处理 if (l MID) { lc(np) change(lc(p), L, MID, l, r, b, c, t.first, t.second); } else { // 左子区间没有重叠克隆并应用祖先变换 if (lc(p) ! 0) { int lc clone_node(lc(p)); modify(lc, t.first, t.second, MID - L 1); lc(np) lc; } } // 右子区间有重叠递归处理 if (r MID 1) { rc(np) change(rc(p), MID 1, R, l, r, b, c, t.first, t.second); } else { if (rc(p) ! 0) { int rc clone_node(rc(p)); modify(rc, t.first, t.second, R - (MID 1) 1); rc(np) rc; } } pushup(np); return np; } // 封装 update - change初始祖先变换为恒等 int update(int p, int L, int R, int l, int r, LL b, LL c) { return change(p, L, R, l, r, b, c, 1, 0); } /* 合并两个版本 x 和 y返回一个新版本其前 at 个元素来自 x后 len - at 个元素来自 y。 参数 xb,xc 和 yb,yc 分别为两个版本当前需要应用的祖先变换。 具体规则 - at 0 全部取 y - at len 全部取 x - 否则根据 at 与左子树长度的关系递归处理左右子树。 */ int crossover(int x, int y, int len, int at, LL xb, LL xc, LL yb, LL yc) { if (at 0) { // 全部来自 y if (y 0) return 0; int ny clone_node(y); modify(ny, yb, yc, len); return ny; } if (len at) { // 全部来自 x if (x 0) return 0; int nx clone_node(x); modify(nx, xb, xc, len); return nx; } int midl (len 1) 1; // 左子树长度尽量平衡 // 为啥是上取整len R - L 1 // MID (R L) / 2 // midl MID - L 1 // 2 * midl R L - 2 * L 2 * 1 // midl R - L 1 LL nx_b, nx_c, ny_b, ny_c; // 将 x 的祖先变换与其自身懒标记复合得到传递给左/右子树的变换 if (x ! 0) { auto res compose(xb, xc, tr[x].lazy_b, tr[x].lazy_c); nx_b res.first; nx_c res.second; } else { nx_b xb; nx_c xc; } if (y ! 0) { auto res compose(yb, yc, tr[y].lazy_b, tr[y].lazy_c); ny_b res.first; ny_c res.second; } else { ny_b yb; ny_c yc; } int left_x (x 0) ? 0 : lc(x); int left_y (y 0) ? 0 : lc(y); int right_x (x 0) ? 0 : rc(x); int right_y (y 0) ? 0 : rc(y); // 左子树需要的前 at 个元素来自 x int lc crossover(left_x, left_y, midl, at, nx_b, nx_c, ny_b, ny_c); // 右子树需要的前 at - midl 个元素来自 x int rc crossover(right_x, right_y, len - midl, at - midl, nx_b, nx_c, ny_b, ny_c); int res newd(); lc(res) lc; rc(res) rc; pushup(res); return res; } /* 操作 1 的封装将版本 y 的区间 [l, r] 复制到版本 x 的对应位置。 通过两次交叉实现 1. tmp x[0 : l - 1] y[l : n] 2. 结果 tmp[0 : r] x[r : n] x[0 : l - 1] y[l : r] x[r : n] */ int update_crossover_twice(int x, int y, int l, int r, int total_len) { int tmp crossover(x, y, total_len, l - 1, 1, 0, 1, 0); return crossover(tmp, x, total_len, r, 1, 0, 1, 0); } /* 查询版本 p 中区间 [l, r] 的和。 参数 b, c 表示当前路径上所有祖先懒标记复合后的变换 在完全覆盖时直接应用到当前节点的 sum 上。 */ LL query(int p, int L, int R, int l, int r, LL b, LL c) { if (r L || R l) { return 0; } if (l L R r) { return (b * tr[p].sum c * (R - L 1)) % P; } // 传递给子节点的祖先变换 (b,c) ∘ p.lazy先 p.lazy后当前祖先 auto t compose(b, c, tr[p].lazy_b, tr[p].lazy_c); LL res 0; res query(lc(p), L, MID, l, r, t.first, t.second); res query(rc(p), MID 1, R, l, r, t.first, t.second); return res % P; } int main() { ios::sync_with_stdio(false); cin.tie(0); int n, Q; cin n Q; for (int i 1; i n; i ) cin a[i]; tr.reserve(20000000); // 预留节点空间 tr.push_back({0, 0, 0, 1, 0}); // 0 号节点作为空节点 int rt; build(rt, 1, n); vectorint roots(Q 1); // roots[i] 存储版本 A_i 的根节点 roots[0] rt; // 初始版本 A_{-1} for (int i 1; i Q; i) { int opt; cin opt; // 输入中 k, s 为 0‑based 版本编号-1 表示初始版本所以用 k1 索引 roots if (opt 0) { // 区间仿射变换 int k, l, r; LL b, c; cin k l r b c; l ; // 输入区间为 [l, r) 的 0‑based 下标转为 1‑based int src roots[k 1]; roots[i] update(src, 1, n, l, r, b, c); } else if (opt 1) { // 区间从另一版本复制 int k, s, l, r; cin k s l r; l ; int src_k roots[k 1]; int src_s roots[s 1]; roots[i] update_crossover_twice(src_k, src_s, l, r, n); } else { // 区间求和查询 int k, l, r; cin k l r; l ; int src roots[k 1]; cout query(src, 1, n, l, r, 1, 0) \n; roots[i] roots[i - 1]; } } return 0; }

相关新闻

最新新闻

深入解析贵港网站建设公司如何选择与贵港网站建设公司如何打造高性价比数字形象贵港网站建设公司在数字化转型中的关键作用贵港网站建设公司未来趋势分析

深入解析贵港网站建设公司如何选择与贵港网站建设公司如何打造高性价比数字形象贵港网站建设公司在数字化转型中的关键作用贵港网站建设公司未来趋势分析

在这个互联网触角无处不在的时代,很多人可能会产生一种错觉,觉得建站就是找几个技术员写几行代码,或者随便找个模板套一套,花个三五千块钱就能搞定。但在广西贵港,尤其是在当地经济结构正在发生深刻变化、传统制造业和农业正在寻求数字化转型的背景下,这种想法其实是相当…

2026/8/13 1:03:43
计算机技术与科学毕业设计2026开题大全

计算机技术与科学毕业设计2026开题大全

文章目录🚩 1 前言1.1 选题注意事项1.1.1 难度怎么把控?1.1.2 题目名称怎么取?1.2 选题推荐1.2.1 起因1.2.2 核心- 如何避坑(重中之重)1.2.3 怎么办呢?🚩2 选题概览🚩 3 项目概览题目1 : 基于协同过滤的电影…

2026/8/13 1:03:43
计科毕设容易的题目思路

计科毕设容易的题目思路

文章目录🚩 1 前言1.1 选题注意事项1.1.1 难度怎么把控?1.1.2 题目名称怎么取?1.2 选题推荐1.2.1 起因1.2.2 核心- 如何避坑(重中之重)1.2.3 怎么办呢?🚩2 选题概览🚩 3 项目概览题目1 : 图像隐写算法研究与…

2026/8/13 1:03:43
网站建设用什么语言:从前端交互到后端逻辑的全景深度解析与选型实战指南

网站建设用什么语言:从前端交互到后端逻辑的全景深度解析与选型实战指南

大家好,我是你们的网站技术顾问。今天咱们不整那些虚头巴脑的黑话,也不搞什么高大上的学术报告,就坐下来喝杯茶,实实在在聊聊一个老生常谈却又让无数创业者、老板以及刚入行的程序员头秃的问题:网站建设用什么语言。这个问题听起来简单,好像只要问百度或者谷歌,随便弹出…

2026/8/13 1:03:43
2026年5月系统集成项目管理工程师应用技术真题(第一批)

2026年5月系统集成项目管理工程师应用技术真题(第一批)

文章目录期望货币价值EMV。ETC(BAC-EV) /CPI沟通管理。管理团队过程的主要工作。范围管理。控制范围的输入。将项目工作分解为工作包时需要开展的活动12.9 实施采购【项目采购管理】。采购形式。采购文档。索赔管理。期望货币价值EMV。ETC(BAC-EV) /CPI (一)(20分)某公司承接了…

2026/8/13 0:58:43
重新定义树莓派启动盘制作:Raspberry Pi Imager的技术革新之路

重新定义树莓派启动盘制作:Raspberry Pi Imager的技术革新之路

重新定义树莓派启动盘制作:Raspberry Pi Imager的技术革新之路 【免费下载链接】rpi-imager The home of Raspberry Pi Imager, a user-friendly tool for creating bootable media for Raspberry Pi devices. 项目地址: https://gitcode.com/gh_mirrors/rp/rpi-i…

2026/8/13 0:58:43