【题解】P4751 【模板】动态 DP(加强版)(Global Balanced Binary Tree)
前置P4719 【模板】动态 DP - 洛谷默认你已经会线段树版本的动态 DP【题解】P4719 【模板】动态 DP线段树版本-CSDN博客0.回顾前面我们利用树链剖分将树变成了若干条重链再对每条链独立建立线段树。线段树的每个叶子维护一个转移矩阵。修改时先在线段树内更新然后通过链与链之间的轻边向上“跳”。这个算法时间复杂度单次第一个来自线段树内部维护链信息的复杂度。第二个来自修改点后沿着轻边向上跳的路径长度因为保证轻边数量是的。那么既然我们无法避免轻边的数量那能不能想办法把线段树的那个优化掉。从而达到理论最优的呢1.介绍 GBBT全局平衡二叉树Global Balanced Binary Tree, GBBT是一种巧妙的静态数据结构它被设计用来解决动态DP问题可以将传统树链剖分加线段树的复杂度优化到理想的同时兼具 LCT 的高效和树剖的实现便捷性。以下为该算法流程首先进行一次树链剖分找出每个点的重儿子。对于每个点定义一个权值也就是该点的轻子树大小之和 1。对于每条重链上的节点序列按照它们的值寻找带权中点。即左右两侧权值和尽可能平衡的点将该点作为这棵二叉搜索树的根然后递归处理左右序列。那为什么这能保证树高是走轻边根据树链剖分的性质从任意节点向上。每经过一条轻边所在子树大小至少翻倍因此轻边数量是。走重边在 GBBT 内向上走一条重边时由于我们的带权中点选取策略。当前节点在GBBT 中的子树大小至少翻倍因此重边数量也是。2.代码#include bits/stdc.h using namespace std; #define int long long // 最方便 const int N 1e6 10; const int inf 0x3f3f3f3f; int n, m; int a[N]; vectorint G[N]; int fa[N], siz[N], son[N]; // 原树父亲、子树大小、重儿子 int f[N][2]; // 考虑节点 x 不选 / 选 的 DP 贡献值 // 和线段树不同的是我们新建了 g 数组来辅助处理 // 实际逻辑和线段树版本是一样的g 数组不起任何实际作用只帮助理解 int g[N][2]; // 只考虑节点 x 的所有轻儿子不包括重儿子时 x 不选/选的 DP 贡献值 int gfa[N]; // GBBT 中的父亲 int ls[N], rs[N]; // GBBT 左右儿子 struct Matrix { int g[2][2]; Matrix() { memset(g, -0x3f, sizeof(g)); } void change(int g0, int g1) { g[0][0] g[0][1] g0; g[1][0] g1; g[1][1] -inf; } Matrix operator * (const Matrix b) const { Matrix c; for (int i 0; i 2; i ) for (int j 0; j 2; j ) for (int k 0; k 2; k ) c.g[i][j] max(c.g[i][j], g[i][k] b.g[k][j]); return c; } } mt[N], tr[N]; // 第一次 DFS求 fa, siz, son, 静态 DP void dfs(int x) { siz[x] 1; f[x][1] a[x]; g[x][0] 0; // 初始化轻儿子贡献 g[x][1] a[x]; for (int y : G[x]) if (y ! fa[x]) { fa[y] x; dfs(y); siz[x] siz[y]; if (siz[y] siz[son[x]]) { son[x] y; } f[x][0] max(f[y][0], f[y][1]); f[x][1] f[y][0]; } } int b[N], bs[N]; // b[]重链节点序列按深度从小到大bs[]顺着重链权值前缀和 // GBBT 重链内处理 // 传参当前处理的重链节点序列区间 [l, r] // 返回值构建出的子树的根节点编号 int g_build(int l, int r) { // 递归边界区间只有一个节点直接作为叶子返回 if (l r) { tr[b[l]] mt[b[l]]; // 该节点的矩阵就是它自己的转移矩阵 return b[l]; } // 二分寻找带权中点 int L l, R r, pm l; int len bs[r] - bs[l - 1]; // len当前区间所有节点的权值总和 // 二分查找带权中点让左半部分的权值和尽量接近总权值的一半 // 这样能保证左右子树平衡路径长度 O(log n) while (L R) { int mid (L R) 1; // 检查 [l, mid] 区间的权值和是否 ≤ 总权值的一半 if ((bs[mid] - bs[l - 1]) * 2 len) { L mid 1; // 可以继续右移让左半更大 pm mid; } else { R mid - 1; // 左半太大需要左移 } } // 以带权中点 pm 作为根节点 int rt b[pm]; // 取出中点对应的节点编号作为根 tr[rt] mt[rt]; // 初始矩阵为当前节点的转移矩阵 // 递归构建左右子树 // 构建左子树[l, pm - 1]深度更浅的节点 if (l pm - 1) { ls[rt] g_build(l, pm - 1); // 递归构建左儿子指向子树根 gfa[ls[rt]] rt; // 设置左儿子在 GBBT 中的父亲为 rt tr[rt] tr[ls[rt]] * tr[rt]; // 矩阵乘法顺序左子树浅层× 当前节点 // 因为中序遍历是从浅到深所以左子树在前 } // 构建右子树[pm 1, r]深度更深的节点 if (r pm 1) { rs[rt] g_build(pm 1, r); // 递归构建右儿子指向子树根 gfa[rs[rt]] rt; // 设置右儿子在 GBBT 中的父亲为 rt tr[rt] tr[rt] * tr[rs[rt]]; // 矩阵乘法顺序当前节点 × 右子树深层 // 中序遍历顺序左 - 中 - 右所以右子树在后 } return rt; // 返回当前子树的根节点 } // 处理整棵树轻链间的连接和重链节点序列和前缀和 int build(int u) { int v u, tp 0; // 处理所有轻子树并计算当前节点的轻儿子贡献使用静态 f while (v) { int g0 0, g1 a[v]; // g0 不选 vg1 强制选 v for (int y : G[v]) { if (y ! fa[v] y ! son[v]) { g0 max(f[y][0], f[y][1]); g1 f[y][0]; } } g[v][0] g0; g[v][1] g1; mt[v].change(g0, g1); // 递归构建轻子树的 GBBT并通过 gfa 连接到 v for (int y : G[v]) { if (y ! fa[v] y ! son[v]) { int lcrt build(y); gfa[lcrt] v; } } v son[v]; // 下一个重儿子 } // 收集当前重链节点 while (u) { b[ tp] u; bs[tp] bs[tp - 1] siz[u] - siz[son[u]]; u son[u]; } int RT g_build(1, tp); // 有完整重链后处理 return RT; } // 判断节点 x 是否通过轻边连接到父亲是 1不是 0 // 在 GBBT 中每个节点都有 gfa[x] 指向它的父节点 // 可能是同一条重链内的父亲也可能是轻边连接的父链 // 但只有和父亲在同一条重链上才会是父亲的左 / 右子节点 inline bool isLight(int u) { return gfa[u] ls[gfa[u]] ! u rs[gfa[u]] ! u; } // 动态 DP 更新 void update(int u, int k) { g[u][1] k - a[u]; a[u] k; mt[u].change(g[u][0], g[u][1]); // 沿 GBBT 向上更新 while (u) { Matrix old tr[u]; // 重新计算当前节点的矩阵 // 当前节点的矩阵 左儿子矩阵 × 当前节点val × 右儿子矩阵 tr[u] mt[u]; if (ls[u]) tr[u] tr[ls[u]] * tr[u]; if (rs[u]) tr[u] tr[u] * tr[rs[u]]; // 处理轻边更新 // 如果 u 是通过轻边连接到父节点的即 u 是一条重链的根 // 那么 u 这棵子树的 DP 值发生了变化需要更新父节点的 g 值 if (isLight(u)) { // 计算 u 子树在 不选 / 选根节点 时的最大权值即整条重链的 DP 结果 // 注意old 和 tr 中的 g[0][0] 表示子树根不选时的最大权 // g[1][0] 表示子树根选时的最大权 int oldAns max(old.g[0][0], old.g[1][0]); // 更新前的整棵子树 DP 值 int newAns max(tr[u].g[0][0], tr[u].g[1][0]); // 更新后的整棵子树 DP 值 // 更新父节点的 g[0]父节点不选时这个轻儿子可任意 // 父节点不选时轻儿子贡献增加newAns - oldAns g[gfa[u]][0] newAns - oldAns; // 更新父节点的 g[1]父节点选时这个轻儿子必须不选 // 父节点选时轻儿子必须不选所以只取 g[0][0]根节点不选的情况 g[gfa[u]][1] tr[u].g[0][0] - old.g[0][0]; // 因为父节点的g值变了需要重新构造父节点的转移矩阵 mt[gfa[u]].change(g[gfa[u]][0], g[gfa[u]][1]); } // 继续向上跳到 GBBT 中的父节点 u gfa[u]; } } signed main() { ios::sync_with_stdio(false); cin.tie(0); cin n m; for (int i 1; i n; i) cin a[i]; for (int i 1; i n; i) { int u, v; cin u v; G[u].push_back(v); G[v].push_back(u); } dfs(1); // 第一次 DFS求 fa, siz, son, 静态 DP int root build(1); int last -1; while (m--) { int x, y; cin x y; if (last ! -1) { // 洛谷模版题要求强制在线异或 x ^ last; } update(x, y); int ans max(tr[root].g[0][0], tr[root].g[1][0]); cout ans \n; last ans; } return 0; }3.到底和线段树比快哪了

相关新闻

【三年面试五年模拟】2026-08-01_米哈游_AI Agent开发提前批一面面经全解析

【三年面试五年模拟】2026-08-01_米哈游_AI Agent开发提前批一面面经全解析

写在前面 欢迎大家关注Rocky的公众号:WeThinkIn 欢迎大家关注Rocky的知乎:Rocky Ding 《三年面试五年模拟》AIGC/LLM/AI Agent算法工程师/开发工程师求职面试秘籍独家资源:【三年面试五年模拟】WeThinkIn/AIGC-Interview-Book,欢迎…

2026/10/14 4:26:19 阅读更多 →
大模型微调显存不足实战全套技巧

大模型微调显存不足实战全套技巧

大模型微调显存不足实战全套技巧 本资源是面向 QLoRA/LoRA 大模型微调场景的落地实操手册,完整解决 24G/40G 单卡、多卡 DDP 训练下 OOM 爆显存、训练卡顿、仅能使用极小 batch 等行业高频痛点,覆盖显存诊断、多级显存压缩优化、完整训练工程、训练后模型合并、高性能推理、…

2026/10/14 4:26:20 阅读更多 →
免费开源的像素画工具LibreSprite,为什么我用完就想卸载付费软件

免费开源的像素画工具LibreSprite,为什么我用完就想卸载付费软件

免费开源的像素画工具LibreSprite,为什么我用完就想卸载付费软件 【免费下载链接】LibreSprite Animated sprite editor & pixel art tool -- Fork of the last GPLv2 commit of Aseprite 项目地址: https://gitcode.com/gh_mirrors/li/LibreSprite 如果…

2026/10/7 7:59:27 阅读更多 →

最新新闻

考研数据结构复试高频考点、手写代码与面试应对策略

考研数据结构复试高频考点、手写代码与面试应对策略

初试成绩出来之后,很多同学会陷入一种“不知道该干什么”的状态。刷题吧,好像复试不考这么细;看论文吧,又不知道从哪看起;真要准备机试,又发现手生的厉害。我自己当年备考“考研数据结构复试题目整理”的时…

2026/10/14 4:25:51 阅读更多 →
考虑主从配电网分布式优化的ADMM算法Matlab实现与串并行对比

考虑主从配电网分布式优化的ADMM算法Matlab实现与串并行对比

最近在推进一个关于主从配电网分布式优化控制的课题时,核心目标是用交替方向乘子法(ADMM)把主网和多个配电子网之间的协同优化拆开做,整个算法在Matlab里落地,并对比了串行和并行两种执行机制下的收敛行为。从模型搭建…

2026/10/14 4:25:51 阅读更多 →
2026 AI 论文工具排行榜|按「国内毕业论文适配度」打分,适合盲审抽检环境

2026 AI 论文工具排行榜|按「国内毕业论文适配度」打分,适合盲审抽检环境

前言 很多海外 AI 工具在国外学术写作里表现亮眼,但拿到国内本科、硕士毕业论文场景,就会水土不服。国内高校论文有一套独立规则:GB/T7714 参考文献格式、固定 Word 排版规范、AI 痕迹检测、参考文献溯源抽查、逻辑盲审。 本次排行榜不再看通…

2026/10/14 4:25:51 阅读更多 →
一次GC频繁问题排查:从JFR到代码优化的完整实践

一次GC频繁问题排查:从JFR到代码优化的完整实践

大概一个多月前,我接到一条让我头疼不已的告警:某个订单查询接口的 P99 延迟从平时的 80ms 一路飙到 800ms 以上,CPU 使用率每隔几分钟就像过山车一样冲到接近 100%。一开始我照例查了流量、数据库慢查询、第三方依赖,全都没有异常…

2026/10/14 4:25:51 阅读更多 →
pdf.js在线查看PDF工具:渲染原理、避坑指南与性能优化实践

pdf.js在线查看PDF工具:渲染原理、避坑指南与性能优化实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 4:25:51 阅读更多 →
Dropout 正则化原理与实战:从过拟合到泛化能力提升

Dropout 正则化原理与实战:从过拟合到泛化能力提升

1. 为什么一个"随手关掉神经元"的操作能成为深度学习的基石第一次在代码里写下nn.Dropout(p0.5)的时候,我其实没太当回事——不就是训练时随机扔掉一半神经元嘛,听起来像个正则化的小把戏。直到有一次复现一篇图像分类论文,模型在训…

2026/10/14 4:24:51 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →