尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

【P3128】树上路径点权加1|倍增LCA + 树上差分

【P3128】树上路径点权加1|倍增LCA + 树上差分
📅 发布时间:2026/7/15 19:54:21

【P3128】树上路径点权加1|倍增LCA + 树上差分

一、题目题意

题意简述

  1. 给定一棵 $N$ 个节点的无根树($N-1$ 条双向边,全部牛棚连通);
  2. 共 $K$ 次操作,每次给出两点 $s,t$,将树上 $s$ 到 $t$ 整条路径上所有点的流量 $+1$;
  3. 求所有节点中,流量数值的最大值。

核心规则

一条路径 $s \to t$ 包含:起点$s$、终点$t$、两点之间所有中转牛棚,全部计数+1。

数据范围

$2\le N \le 5\times 10^4,\ 1\le K \le 1\times 10^5$,暴力遍历路径会超时,必须使用树上差分优化。

二、两大核心算法原理

1. 倍增LCA

快速求出树上两点 $s,t$ 的最近公共祖先 $l=\text{lca}(s,t)$,拆分路径:

$$s \to t = s \to l + t \to l - l$$

2. 树上点差分(路径区间+1模板)

若要给路径 $s \to t$ 所有点权值 $+1$,设 $l=\text{lca}(s,t)$,树上差分修改规则:

power[s] += 1
power[t] += 1
power[l] -= 1
power[ fa[l][0] ] -= 1

原理:子树回溯求和时,仅 $s\sim t$ 路径上的点会被多累计1次,其余点抵消为0。全部操作完成后,执行一次后序DFS子树累加,power[u] 即为节点 $u$ 总流量。
请添加图片描述

三、完整解题步骤

步骤1:建图

读取节点数 $N$、路径操作次数 $m$;循环读入 $N-1$ 条双向边,用链式前向星存储无向树。

步骤2:DFS预处理倍增LCA数组

从根节点1出发深搜:

  1. 记录每个节点深度 dep[u];
  2. 预处理倍增祖先数组 fa[u][i],fa[u][i] 代表 $u$ 向上跳 $2^i$ 步的祖先,用于快速LCA查询。

步骤3:批量处理K条路径操作

循环 $m$ 次,每组读入 $s,t$:

  1. 调用lca(s,t)求出两点公共祖先$l$;
  2. 套用树上差分四点修改公式,更新power数组标记。

步骤4:后序DFS统计子树贡献,求最大值

第二次DFS遍历整棵树:

  1. 递归处理所有子节点,将子节点power[v]累加到父节点power[u];
  2. 遍历过程同步维护全局最大值ans;

步骤5:输出答案

打印全局最大流量ans。

四、完整AC代码

#include <cstdio>
#include <algorithm>
using namespace std;const int N = 50005, M = 2 * N;
int h[N], to[M], ne[M], tot;
int dep[N], fa[N][22];
int n, m, ans, power[N];void add(int x, int y) { // 链式前向星双向加边to[++tot] = y;ne[tot] = h[x];h[x] = tot;
}void dfs(int u, int f) { // 倍增LCA预处理DFSdep[u] = dep[f] + 1;fa[u][0] = f;for (int i = 0; fa[u][i]; ++i)fa[u][i + 1] = fa[fa[u][i]][i];for (int i = h[u]; i; i = ne[i])if (to[i] != f) dfs(to[i], u);
}int lca(int u, int v) { // 倍增求两点最近公共祖先if (dep[u] > dep[v]) swap(u, v);// 将v跳到与u同一深度for (int i = 20; i >= 0; --i)if (dep[u] <= dep[v] - (1 << i)) v = fa[v][i];if (u == v) return u;// 同步向上跳跃for (int i = 20; i >= 0; --i)if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];return fa[u][0];
}void dfs2(int u, int f) { // 后序子树求和,统计最大流量for (int i = h[u]; i; i = ne[i]) {int v = to[i];if (v == f) continue;dfs2(v, u);power[u] += power[v];}ans = max(ans, power[u]);
}int main() {scanf("%d%d", &n, &m);for (int i = 1, x, y; i < n; ++i) {scanf("%d%d", &x, &y);add(x, y); add(y, x);}dfs(1, 0);// 处理m次路径区间+1for (int i = 1, x, y; i <= m; ++i) {scanf("%d%d", &x, &y);int l = lca(x, y);++power[x]; ++power[y];--power[l]; --power[fa[l][0]];}dfs2(1, 0);printf("%d\n", ans);return 0;
}

五、代码分段详解

1. 全局常量与数组

const int N = 50005, M = 2 * N;
int h[N], to[M], ne[M], tot;
int dep[N], fa[N][22];
int n, m, ans, power[N];
  • N=5e4:节点上限;M=2*N 双向边开双倍空间;
  • fa[N][22]:倍增数组,$2^{20}$ 覆盖最大深度;
  • dep[]:节点深度;power[]:树上差分数组,记录流量标记;
  • ans:保存全局最大流量。

2. 链式前向星加边

void add(int x, int y) {to[++tot] = y;ne[tot] = h[x];h[x] = tot;
}

无向树每条边存两次,构建双向连通关系。

3. 预处理倍增数组 dfs

void dfs(int u, int f) {dep[u] = dep[f] + 1;fa[u][0] = f;for (int i = 0; fa[u][i]; ++i)fa[u][i + 1] = fa[fa[u][i]][i];for (int i = h[u]; i; i = ne[i])if (to[i] != f) dfs(to[i], u);
}
  1. 初始化父节点fa[u][0]与深度;
  2. 递推倍增祖先:$2^{i+1}$ 祖先 = $2^i$ 祖先的 $2^i$ 祖先;
  3. 递归遍历子树。

4. 倍增LCA函数

int lca(int u, int v) {if (dep[u] > dep[v]) swap(u, v);for (int i = 20; i >= 0; --i)if (dep[u] <= dep[v] - (1 << i)) v = fa[v][i];if (u == v) return u;for (int i = 20; i >= 0; --i)if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];return fa[u][0];
}

标准倍增LCA流程:拉平深度→同步上跳→返回公共祖先。

5. 子树求和dfs2

void dfs2(int u, int f) {for (int i = h[u]; i; i = ne[i]) {int v = to[i];if (v == f) continue;dfs2(v, u);power[u] += power[v];}ans = max(ans, power[u]);
}

后序遍历,先算所有子节点,再把子节点流量累加到父节点;遍历同时更新全局最大值。

6. main主函数流程

  1. 读入 $n,m$,循环建双向树;
  2. 根1执行DFS预处理倍增;
  3. 循环处理每条路径,LCA配合树上差分打标记;
  4. DFS2后序合并子树流量,得到每个节点总流量;
  5. 输出最大流量ans。

六、题型总结

  1. 模型定位:倍增LCA + 树上点差分,树上路径区间更新经典模板;

  2. 树上差分记忆公式:路径$s \to t$ 全体+1

$$power[s]++,power[t]++,power[lca]--,power[fa[lca][0]]--$$

  1. 复杂度分析

    • 预处理LCA:$O(N\log N)$;
    • $m$ 次路径操作:每次LCA $O(\log N)$,总 $O(m\log N)$;
    • 子树求和DFS:$O(N)$;
      数据范围 $5\times104、1\times105$ 完全无超时;
  2. 拓展区分

    • 点差分:路径所有点修改,使用本题四标记公式;
    • 边差分:路径所有边修改,仅需修改两点+LCA两处标记。

相关新闻

  • 亲身到店探访长沙劳力士官方售后服务中心|官方地址及24小时售后电话(2026年7月最新) - 劳力士官方服务中心
  • TK1终端优化五步法:从卡顿到秒响应的实操指南
  • 长沙岳麓区保洁收纳|维小达|日常保洁、深度保洁、开荒保洁、窗户保洁、收纳整理、暖气清洗、家电清洗全屋家政一站式清洁收纳养护服务 - 一点传媒

最新新闻

  • 普尚云课堂 | 射频底层干货:幅度、频率、相位通俗解析一文讲透
  • 嵌入式开发中GPMC时序配置详解:从原理到实战
  • @Transactional 原理与事务失效场景
  • Java NIO内存管理机制剖析
  • 效率的馈赠与创意的觉醒:AI时代的人文主义复兴
  • MMDetection(简称 mmdet)是 OpenMMLab 开发的开源目标检测框架,基于 PyTorch 构建

日新闻

  • 告别启动盘残留:用Diskpart彻底清除U盘EFI分区与恢复完整空间
  • 2026 年宜春诚信的塑料缠绕膜厂家哪个好,缠绕膜背后的秘密:你不知道的成本陷阱 - 领域鉴赏官
  • Arch ECS 入门指南:10分钟掌握C#高性能数据驱动架构

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号