超全树链剖分模板

news/2025/12/9 21:09:41/文章来源:https://www.cnblogs.com/Zmeiying/p/19328396
#include<bits/stdc++.h>
#define ls (rt << 1)        // 左儿子
#define rs (rt << 1 | 1)    // 右儿子
using namespace std;const int N = 1e5 + 5;   // 最大节点数
const int M = 2e5 + 5;   // 最大边数(无向边*2)// 查询类型常量
const int QUERY_SUM = 0;    // 区间和
const int QUERY_MAX = 1;    // 区间最大值
const int QUERY_MIN = 2;    // 区间最小值
// 操作类型常量
const int OP_ADD = 0;       // 区间加操作
const int OP_SET = 1;       // 区间覆盖操作// 前向星结构体
struct edge {int to;          // 边的终点int nxt;         // 下一条边
} e[M];// 基本变量
int n, root;           // 节点数, 根节点
int tim;               // 时间戳,用于DFS序
int head[N];        // 前向星头数组
int tot;               // 边计数器
// 树链剖分数组
int fa[N];          // fa[x]: 节点x的父亲
int dep[N];         // dep[x]: 节点x的深度
int siz[N];         // siz[x]: 节点x的子树大小
int son[N];         // son[x]: 节点x的重儿子
int top[N];         // top[x]: 节点x所在重链的顶部结点
int dfn[N];         // dfn[x]: 节点x的DFS序
int rnk[N];         // rnk[x]: DFS序x对应的节点编号
// 原树节点权值
int val[N];         // val[x]: 节点x的原始权值// 线段树部分
struct SegTree {int sum;           // 区间和int max;           // 区间最大值int min;           // 区间最小值int lazy;          // 懒标记(区间加)int set_tag;       // 覆盖标记 (区间覆盖)bool has_set;      // 是否有覆盖标记
} seg[N << 2];namespace TreeDecomposition {// ========== 前向星部分 ==========// 添加边void adde(int u, int v) {e[++tot].to = v;e[tot].nxt = head[u];head[u] = tot;}// 添加无向边void plus_adde(int u, int v) {adde(u, v);adde(v, u);}// ========== 线段树部分 ==========// 合并子节点信息void pushup(int rt) {seg[rt].sum = seg[ls].sum + seg[rs].sum;seg[rt].max = max(seg[ls].max, seg[rs].max);seg[rt].min = min(seg[ls].min, seg[rs].min);}// 下传懒标记void pushdown(int rt, int l, int r) {int mid = (l + r) >> 1;// 优先处理覆盖标记if (seg[rt].has_set) {int set_val = seg[rt].set_tag;int lenL = mid - l + 1;int lenR = r - mid;// 更新左子树seg[ls].sum = set_val * lenL;seg[ls].max = seg[ls].min = set_val;seg[ls].lazy = 0;          // 覆盖操作清空加标记seg[ls].set_tag = set_val;seg[ls].has_set = true;// 更新右子树seg[rs].sum = set_val * lenR;seg[rs].max = seg[rs].min = set_val;seg[rs].lazy = 0;          // 覆盖操作清空加标记seg[rs].set_tag = set_val;seg[rs].has_set = true;seg[rt].has_set = false;seg[rt].set_tag = 0;}// 再处理加标记if (seg[rt].lazy != 0) {int lazy = seg[rt].lazy;int lenL = mid - l + 1;int lenR = r - mid;// 更新左子树seg[ls].sum += lazy * lenL;seg[ls].max += lazy;seg[ls].min += lazy;seg[ls].lazy += lazy;// 更新右子树seg[rs].sum += lazy * lenR;seg[rs].max += lazy;seg[rs].min += lazy;seg[rs].lazy += lazy;seg[rt].lazy = 0;}}// 构建线段树void buildSegTree(int rt, int l, int r) {seg[rt].lazy = 0;seg[rt].has_set = false;if (l == r) {int node = rnk[l];  // 通过rnk找到对应节点seg[rt].sum = seg[rt].max = seg[rt].min = val[node];return;}int mid = (l + r) >> 1;buildSegTree(ls, l, mid);buildSegTree(rs, mid + 1, r);pushup(rt);}// 统一更新函数void updateSeg(int rt, int l, int r, int L, int R, int add_val, int set_val, int op_type) {if (L <= l && r <= R) {if (op_type == OP_SET) {  // 区间覆盖seg[rt].sum = set_val * (r - l + 1);seg[rt].max = seg[rt].min = set_val;seg[rt].set_tag = set_val;seg[rt].has_set = true;seg[rt].lazy = 0;  // 覆盖操作清空加标记} else {  // 区间加seg[rt].sum += add_val * (r - l + 1);seg[rt].max += add_val;seg[rt].min += add_val;seg[rt].lazy += add_val;}return;}pushdown(rt, l, r);int mid = (l + r) >> 1;if (L <= mid) updateSeg(ls, l, mid, L, R, add_val, set_val, op_type);if (R > mid) updateSeg(rs, mid + 1, r, L, R, add_val, set_val, op_type);pushup(rt);}// 区间查询int querySeg(int rt, int l, int r, int L, int R, int type) {if (L <= l && r <= R) {if (type == QUERY_SUM) return seg[rt].sum;else if (type == QUERY_MAX) return seg[rt].max;else return seg[rt].min;}pushdown(rt, l, r);int mid = (l + r) >> 1;if (type == QUERY_SUM) {int res = 0;if (L <= mid) res += querySeg(ls, l, mid, L, R, type);if (R > mid) res += querySeg(rs, mid + 1, r, L, R, type);return res;} else if (type == QUERY_MAX) {int res = INT_MIN;if (L <= mid) res = max(res, querySeg(ls, l, mid, L, R, type));if (R > mid) res = max(res, querySeg(rs, mid + 1, r, L, R, type));return res;} else { // QUERY_MINint res = INT_MAX;if (L <= mid) res = min(res, querySeg(ls, l, mid, L, R, type));if (R > mid) res = min(res, querySeg(rs, mid + 1, r, L, R, type));return res;}}// ========== 树链剖分部分 ==========// 初始化void init(int _n, int _root = 1) {n = _n;root = _root;tot = 0;tim = 0;memset(head, 0, sizeof(head));memset(son, 0, sizeof(son));memset(val, 0, sizeof(val));}// 设置节点原始权值void setVal(int u, int value) {val[u] = value;}// 第一次DFS:预处理父亲、深度、子树大小、重儿子void dfs1(int u, int f) {//u为当前节点,f父亲fa[u] = f;//标记每个点的父亲 dep[u] = dep[f] + 1;//标记每个点的深度 siz[u] = 1;//标记每个非叶子节点的子树大小(记自己) son[u] = 0;//记录重儿子的儿子数 for (int i = head[u]; i; i = e[i].nxt) {int v = e[i].to;if (v == f) continue;//若为父亲则continue dfs1(v, u);//dfs其儿子 siz[u] += siz[v];//把它的儿子数加到它身上 if (siz[v] > siz[son[u]]) {son[u] = v;//标记每个非叶子节点的重儿子编号 }}}// 第二次DFS:预处理DFS序、重链顶端void dfs2(int u, int t) {top[u] = t;dfn[u] = ++tim;rnk[tim] = u;  // 记录DFS序到节点的映射// 优先遍历重儿子,保证重链上的DFS序连续if (son[u]) {dfs2(son[u], t);}// 遍历轻儿子for (int i = head[u]; i; i = e[i].nxt) {int v = e[i].to;if (v == fa[u] || v == son[u]) continue;//等于父亲或自己都不行dfs2(v, v);  // 轻儿子作为新重链的顶端}}// 构建树链剖分void build() {tim = 0;dfs1(root, 0);dfs2(root, root);buildSegTree(1, 1, n);  // 构建线段树}
}namespace Debug{ // 获取查询接口int getFa(int x) { return fa[x]; }int getDep(int x) { return dep[x]; }int getSiz(int x) { return siz[x]; }int getSon(int x) { return son[x]; }int getTop(int x) { return top[x]; }int getDfn(int x) { return dfn[x]; }int getRnk(int x) { return rnk[x]; }// 打印信息(调试用)void debug() {cout << "节点信息:" << endl;cout << "id\tfa\tdep\tsiz\tson\ttop\tdfn\tval" << endl;for (int i = 1; i <= n; i++) {cout << i << "\t" << fa[i] << "\t" << dep[i] << "\t" << siz[i] << "\t" << son[i] << "\t" << top[i] << "\t" << dfn[i] << "\t" << val[i] << endl;}}
}namespace funtion{// ========== 树链剖分 + 线段树操作 ==========// 查询LCA(最近公共祖先)int lca(int u, int v) {while (top[u] != top[v]) {if (dep[top[u]] < dep[top[v]]) swap(u, v);u = fa[top[u]];}return dep[u] < dep[v] ? u : v;}// 路径修改:u到v路径上所有节点加valvoid updatePathAdd(int u, int v, int val) {while (top[u] != top[v]) {if (dep[top[u]] < dep[top[v]]) swap(u, v);TreeDecomposition::updateSeg(1, 1, n, dfn[top[u]], dfn[u], val, 0, OP_ADD);u = fa[top[u]];}if (dep[u] > dep[v]) swap(u, v);TreeDecomposition::updateSeg(1, 1, n, dfn[u], dfn[v], val, 0, OP_ADD);}// 路径修改:u到v路径上所有节点设置为val(覆盖)void updatePathSet(int u, int v, int val) {while (top[u] != top[v]) {if (dep[top[u]] < dep[top[v]]) swap(u, v);TreeDecomposition::updateSeg(1, 1, n, dfn[top[u]], dfn[u], 0, val, OP_SET);u = fa[top[u]];}if (dep[u] > dep[v]) swap(u, v);TreeDecomposition::updateSeg(1, 1, n, dfn[u], dfn[v], 0, val, OP_SET);}// 路径查询:查询u到v路径上指定类型的信息int queryPath(int u, int v, int type) {int res;if (type == QUERY_SUM) res = 0;else if (type == QUERY_MAX) res = INT_MIN;else res = INT_MAX;while (top[u] != top[v]) {if (dep[top[u]] < dep[top[v]]) swap(u, v);int cur = TreeDecomposition::querySeg(1, 1, n, dfn[top[u]], dfn[u], type);if (type == QUERY_SUM) res += cur;else if (type == QUERY_MAX) res = max(res, cur);else res = min(res, cur);u = fa[top[u]];}if (dep[u] > dep[v]) swap(u, v);int cur = TreeDecomposition::querySeg(1, 1, n, dfn[u], dfn[v], type);if (type == QUERY_SUM) res += cur;else if (type == QUERY_MAX) res = max(res, cur);else res = min(res, cur);return res;}// 子树修改:以u为根的子树所有节点加valvoid updateSubtreeAdd(int u, int val) {int l = dfn[u], r = dfn[u] + siz[u] - 1;TreeDecomposition::updateSeg(1, 1, n, l, r, val, 0, OP_ADD);}// 子树修改:以u为根的子树所有节点设置为valvoid updateSubtreeSet(int u, int val) {int l = dfn[u], r = dfn[u] + siz[u] - 1;TreeDecomposition::updateSeg(1, 1, n, l, r, 0, val, OP_SET);}// 子树查询:查询以u为根的子树指定类型的信息int querySubtree(int u, int type) {int l = dfn[u], r = dfn[u] + siz[u] - 1;return TreeDecomposition::querySeg(1, 1, n, l, r, type);}// 单点修改:节点u加valvoid updateNodeAdd(int u, int val) {TreeDecomposition::updateSeg(1, 1, n, dfn[u], dfn[u], val, 0, OP_ADD);}// 单点修改:节点u设置为val(覆盖)void updateNodeSet(int u, int val) {TreeDecomposition::updateSeg(1, 1, n, dfn[u], dfn[u], 0, val, OP_SET);}// 单点查询int queryNode(int u, int type) {return TreeDecomposition::querySeg(1, 1, n, dfn[u], dfn[u], type);}
}using namespace TreeDecomposition;
using namespace funtion;
using namespace Debug;// void task();int main(){cin.tie(0)->sync_with_stdio(0);task();return 0;
}// 使用示例
void task() {int n = 8;  // 节点数init(n, 1); // 初始化,根节点为1// 构建一棵树(示例)plus_adde(1, 2);plus_adde(1, 3);plus_adde(2, 4);plus_adde(2, 5);plus_adde(3, 6);plus_adde(5, 7);plus_adde(6, 8);// 设置节点权值for (int i = 1; i <= n; i++) {setVal(i, i);  // 初始权值等于节点编号}// 构建树链剖分(包含线段树)build();// 调试输出debug();// 查询示例cout << "\n=== 初始查询 ===" << endl;cout << "路径1-7的和: " << queryPath(1, 7, QUERY_SUM) << endl;cout << "路径1-7的最大值: " << queryPath(1, 7, QUERY_MAX) << endl;cout << "路径1-7的最小值: " << queryPath(1, 7, QUERY_MIN) << endl;// 测试区间加操作cout << "\n=== 测试区间加操作 ===" << endl;cout << "路径2-6所有节点加10" << endl;updatePathAdd(2, 6, 10);cout << "路径2-6的和(加10后): " << queryPath(2, 6, QUERY_SUM) << endl;cout << "路径2-6的最大值(加10后): " << queryPath(2, 6, QUERY_MAX) << endl;// 测试覆盖操作cout << "\n=== 测试覆盖操作 ===" << endl;cout << "路径3-8所有节点设置为100" << endl;updatePathSet(3, 8, 100);cout << "路径3-8的和(覆盖后): " << queryPath(3, 8, QUERY_SUM) << endl;cout << "路径3-8的最小值(覆盖后): " << queryPath(3, 8, QUERY_MIN) << endl;// 测试混合操作(覆盖后加)cout << "\n=== 测试混合操作 ===" << endl;cout << "节点2的子树设置为50" << endl;updateSubtreeSet(2, 50);cout << "节点2的子树和(设置后): " << querySubtree(2, QUERY_SUM) << endl;cout << "节点2的子树加20" << endl;updateSubtreeAdd(2, 20);cout << "节点2的子树和(再加20后): " << querySubtree(2, QUERY_SUM) << endl;// 测试单点操作cout << "\n=== 测试单点操作 ===" << endl;cout << "节点4设置为200" << endl;updateNodeSet(4, 200);cout << "节点4的值: " << queryNode(4, QUERY_SUM) << endl;cout << "节点4加30" << endl;updateNodeAdd(4, 30);cout << "节点4的值(加30后): " << queryNode(4, QUERY_SUM) << endl;// 查询LCAcout << "\n=== LCA查询 ===" << endl;cout << "LCA(4, 7) = " << lca(4, 7) << endl;cout << "LCA(8, 7) = " << lca(8, 7) << endl;// 验证懒标记正确性cout << "\n=== 验证懒标记正确性 ===" << endl;cout << "最终路径1-7的和: " << queryPath(1, 7, QUERY_SUM) << endl;cout << "最终路径1-7的最大值: " << queryPath(1, 7, QUERY_MAX) << endl;cout << "最终路径1-7的最小值: " << queryPath(1, 7, QUERY_MIN) << endl;return ;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/995548.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025托福培训机构选择指南:精准匹配你的提分需求

2025托福培训机构选择指南:精准匹配你的提分需求面对众多的托福培训机构,很多考生都在思考同一个问题:如何找到真正适合自己的备考伙伴?一个明智的选择,并非盲目追随广告或模糊的排名,而是始于对自身“目标分数、…

python 类的repr函数

class Human:def __init__(self,name,age):self.name,self.age=name,agedef __str__(self):return f{self.age}岁的{self.name}def __repr__(self):return Human(+repr(self.name)+,+repr(self.age)+)h1=Human("张…

2025托福辅导机构优选指南:从口碑到提分的全方位攻略

2025托福辅导机构优选指南:从口碑到提分的全方位攻略(一)无老师托福:高端封闭班代表性品牌 推荐指数:★★★★★ 作为上海托福培训高端封闭班领域的标杆品牌之一,无老师托福拥有14年留学教育积淀,专注出国考培与…

51单片机:数码管

数码管简介数码管每段其本质就是个LED灯,只需要控制特定的LED灯亮就能显示数据。普中开发版所使用的是两个并在一起共阴极连接的“4位数码管”,可以同时显示8个数字。数码管的显示可以分成静态显示和动态显示,这里先…

江西过碳酸钠生产厂、浙江过碳酸钠生产厂名单精选

2025年全球过碳酸钠产能预计突破120万吨,这种被称为“固体双氧水”的高效氧化剂,因环保特性在多领域需求激增。从日化洗涤的去污漂白,到纺织印染的固色处理,再到污水处理中的污染物降解,其应用场景持续拓宽。江西…

江西成膜助剂生产厂、浙江成膜助剂生产厂家精选名单

在环保政策持续收紧与水性涂料市场蓬勃发展的双重驱动下,成膜助剂作为涂料工业的“关键助剂”,其市场需求在2025年迎来新的增长拐点。成膜助剂通过降低乳液最低成膜温度,改善漆膜耐候性与可擦洗性,成为水性涂料生产…

使用VSCode开发ESP32单片机基于MicroPython-12.8

Vscode搭建环境,最好是分隔开,增加一个新的ESP32的配置文件。安装插件,Python和RT-Thread MicroPython。 安装RT-Thread MicroPython后,记得将命令行默认打开更改为powershell新建工程创建新的MicroPython工程创建…

DBLens 连接数怎么限制?免费 3 个,订阅随便加

DBLens 连接数怎么限制?免费 3 个,订阅随便加 如果你平时在 Linux / macOS / Windows 上做 MySQL(或 MariaDB)开发、运维、测试,DBLens 可能已经进入你的候选清单:原生体验顺滑、功能聚焦在高频开发场景,并把 A…

过碳酸钠选购指南:优质厂家推荐及欧盟标准供应商盘点

过碳酸钠作为高效环保的氧系漂白剂,广泛应用于洗涤、纺织、水处理等多个领域。2025年,随着市场需求的持续攀升,消费者和企业采购者对过碳酸钠的质量要求愈发严格,本文将聚焦这些关键需求,为大家详细介绍过碳酸钠及…

轮询相关算法

普通轮询 n:请求的编号 x:服务器数量 i:请求的服务器编号 i = n % x 加权轮询 最大公约数算法(Weighted Round-Robin, WRR) 随着每一轮遍历,降低“门槛”(Current Weight),只有权重大于等于当前门槛的服务器才能…

数据仓库和数据集市之ODS、CDM、ADS、DWD、DWS - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

成膜助剂代理商有哪些?成膜助剂全攻略:成膜助剂进口CIF价格供应商

在涂料、胶粘剂等行业绿色转型的浪潮中,成膜助剂作为保障涂膜连续性与稳定性的核心原料,其供应品质、渠道稳定性及价格透明度直接影响下游产业发展。2025年,随着新国标对VOCs排放、沸点等指标的升级要求,市场对优质…

过碳酸钠供应商大全:实力厂家、制造商及优质批发商推荐指南

过碳酸钠作为兼具漂白、杀菌功能的环保型化工原料,在洗涤、纺织、水处理等领域应用愈发广泛。2025年,随着环保政策收紧与下游需求升级,优质过碳酸钠供应商、制造商及批发商的选择成为采购核心命题。本文将聚焦行业标…

完整教程:读后感:《解析极限编程:拥抱变化》

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

托福备考黄金期,如何精准锁定高性价比机构?

托福备考黄金期,如何精准锁定高性价比机构?一、托福备考黄金期,如何精准锁定高性价比机构? 在留学竞争白热化的 2025 年,托福成绩已成为海外名校的核心筛选指标。数据显示,国内托福考生平均备考周期缩短至 3-6 个…

华为fusion-compute-8.x安装

华为FusionCompute 8.x部署文档存储工程师编辑于 2023年04月06日 05:07 收录于文集虚拟化 13篇 一、IP地址规划 二、安装CNA 1.启动安装镜像 2.进入配置界面 3.编辑安装盘设置,选择安装磁盘,设置swap分区大小。 …

2025年12月广州番禺佛山网站建设,营销网站建设,网站建设推广公司品牌推荐,定制能力与交付效率三维测评

引言在 2025 年 12 月的广州番禺及佛山地区,网站建设、营销网站建设以及网站建设推广行业竞争激烈。为了帮助企业客户在众多公司中挑选出最适合的合作伙伴,我们依据国内相关行业协会测评权威数据和白皮书内容,从定制…

2025托福培训机构怎么选?6大高性价比机构测评+避坑指南

2025托福培训机构怎么选?6大高性价比机构测评+避坑指南一、2025 托福备考新趋势:选对机构是提分关键 在如今这个全球化进程不断加速的时代,托福成绩的重要性愈发凸显,它不仅是留学申请时不可或缺的敲门砖,更是职场…

2025雅思一对一机构推荐排行榜:精准提分攻略,考研必看!

2025雅思一对一机构推荐排行榜:精准提分攻略,考研必看!一、雅思备考痛点解析:为什么选择一对一机构? 在雅思备考的漫漫征途上,许多同学都历经坎坷。有的同学选择自学,在浩如烟海的学习资料中艰难摸索,没有清晰…

2025 雅思报班全攻略:红榜机构测评 + 避坑指南,帮你精准选对课程

2025 雅思报班全攻略:红榜机构测评 + 避坑指南,帮你精准选对课程一、雅思备考为什么建议报班?—— 用数据告诉你报班优势 (一)自学 vs 报班效果对比 自学适合基础扎实(模考 6.5+)、自律性强的考生,提分周期长(…