news 2026/4/20 14:34:31

seg tree|dijk

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
seg tree|dijk

lc3650

Dijkstra求从0到n-1的单源最短路,邻接表构建时无向边双向权重不同(x→y为原权值,y→x为原权值2倍)

小根堆+懒更新实现最短路求解,到达终点时直接返回最短距离

class Solution {
public:
int minCost(int n, vector<vector<int>>& edges) {
vector<vector<pair<int, int>>> g(n); // 邻接表
for (auto& e : edges) {
int x = e[0], y = e[1], wt = e[2];
g[x].emplace_back(y, wt);
g[y].emplace_back(x, wt * 2);
}

vector<int> dis(n, INT_MAX);
// min heap(起点到节点 x 的最短路长度,节点 x)
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
dis[0] = 0; // 起点到自己的距离是 0
pq.emplace(0, 0);

while (!pq.empty()) {
auto [dis_x, x] = pq.top();
pq.pop();
if (dis_x > dis[x])
// x 之前出堆过
continue;
if (x == n - 1) // 到达终点
return dis_x;

for (auto& [y, wt] : g[x]) {
auto new_dis_y = dis_x + wt;
if (new_dis_y < dis[y]) {
dis[y] = new_dis_y;
// 更新 x 的邻居的最短路
// 懒更新堆:只插入数据,不更新堆中数据
// 相同节点可能有多个不同的 new_dis_y,除了最小的 new_dis_y,其余值都会触发上面的 continue
pq.emplace(new_dis_y, y);
}
}
}
return -1;
}
};

我悟了(

// 模板来源 https://leetcode.cn/circle/discuss/mOr1u6/
template<typename T, typename F>
class LazySegmentTree {
// 注:也可以去掉 template<typename T, typename F>,改在这里定义 T 和 F
// using T = pair<int, int>;
// using F = pair<int, int>;

// 懒标记初始值
const F TODO_INIT = 0; // **根据题目修改**

struct Node {
T val;
F todo;
};

int n;
vector<Node> tree;

// 合并两个 val
T merge_val(const T& a, const T& b) const {
return a + b; // **根据题目修改**
}

// 合并两个懒标记
F merge_todo(const F& a, const F& b) const {
return a + b; // **根据题目修改**
}

// 把懒标记作用到 node 子树(本例为区间加)
void apply(int node, int l, int r, F todo) {
Node& cur = tree[node];
// 计算 tree[node] 区间的整体变化
cur.val += todo * (r - l + 1); // **根据题目修改**
cur.todo = merge_todo(todo, cur.todo);
}

// 把当前节点的懒标记下传给左右儿子
void spread(int node, int l, int r) {
Node& cur = tree[node];
F todo = cur.todo;
if (todo == TODO_INIT) { // 没有需要下传的信息
return;
}
int m = (l + r) / 2;
apply(node * 2, l, m, todo);
apply(node * 2 + 1, m + 1, r, todo);
cur.todo = TODO_INIT; // 下传完毕
}

// 合并左右儿子的 val 到当前节点的 val
void maintain(int node) {
tree[node].val = merge_val(tree[node * 2].val, tree[node * 2 + 1].val);
}

// 用 a 初始化线段树
// 时间复杂度 O(n)
void build(const vector<T>& a, int node, int l, int r) {
Node& cur = tree[node];
cur.todo = TODO_INIT;
if (l == r) { // 叶子
cur.val = a[l]; // 初始化叶节点的值
return;
}
int m = (l + r) / 2;
build(a, node * 2, l, m); // 初始化左子树
build(a, node * 2 + 1, m + 1, r); // 初始化右子树
maintain(node);
}

void update(int node, int l, int r, int ql, int qr, F f) {
if (ql <= l && r <= qr) { // 当前子树完全在 [ql, qr] 内
apply(node, l, r, f);
return;
}
spread(node, l, r);
int m = (l + r) / 2;
if (ql <= m) { // 更新左子树
update(node * 2, l, m, ql, qr, f);
}
if (qr > m) { // 更新右子树
update(node * 2 + 1, m + 1, r, ql, qr, f);
}
maintain(node);
}

T query(int node, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) { // 当前子树完全在 [ql, qr] 内
return tree[node].val;
}
spread(node, l, r);
int m = (l + r) / 2;
if (qr <= m) { // [ql, qr] 在左子树
return query(node * 2, l, m, ql, qr);
}
if (ql > m) { // [ql, qr] 在右子树
return query(node * 2 + 1, m + 1, r, ql, qr);
}
T l_res = query(node * 2, l, m, ql, qr);
T r_res = query(node * 2 + 1, m + 1, r, ql, qr);
return merge_val(l_res, r_res);
}

public:
// 线段树维护一个长为 n 的数组(下标从 0 到 n-1),元素初始值为 init_val
LazySegmentTree(int n, T init_val = 0) : LazySegmentTree(vector<T>(n, init_val)) {}

// 线段树维护数组 a
LazySegmentTree(const vector<T>& a) : n(a.size()), tree(2 << bit_width(a.size() - 1)) {
build(a, 1, 0, n - 1);
}

// 用 f 更新 [ql, qr] 中的每个 a[i]
// 0 <= ql <= qr <= n-1
// 时间复杂度 O(log n)
void update(int ql, int qr, F f) {
update(1, 0, n - 1, ql, qr, f);
}

// 返回用 merge_val 合并所有 a[i] 的计算结果,其中 i 在闭区间 [ql, qr] 中
// 0 <= ql <= qr <= n-1
// 时间复杂度 O(log n)
T query(int ql, int qr) {
return query(1, 0, n - 1, ql, qr);
}
};

int main() {
LazySegmentTree<long long, long long> t(8); // 默认值为 0
t.update(3, 5, 100);
t.update(4, 6, 10);
cout << t.query(0, 7) << endl;

vector<long long> nums = {3, 1, 4, 1, 5, 9, 2, 6};
LazySegmentTree<long long, long long> t2(nums);
t2.update(3, 5, 1);
t2.update(4, 6, 1);
cout << t2.query(0, 7) << endl;
return 0;
}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 20:53:54

3个突破性前端资源瘦身技巧:从500KB到50KB的效率倍增方案

3个突破性前端资源瘦身技巧&#xff1a;从500KB到50KB的效率倍增方案 【免费下载链接】Font-Awesome The iconic SVG, font, and CSS toolkit 项目地址: https://gitcode.com/GitHub_Trending/fo/Font-Awesome 核心价值&#xff1a;通过精准优化技术&#xff0c;让前端资…

作者头像 李华
网站建设 2026/4/17 23:30:36

黑苹果配置终极指南:4阶段精准定位你的macOS系统版本

黑苹果配置终极指南&#xff1a;4阶段精准定位你的macOS系统版本 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 黑苹果配置过程中&#xff0c;macOS版…

作者头像 李华
网站建设 2026/4/19 2:05:40

数据工作流编排工具选型指南:Mage实战与架构解析

数据工作流编排工具选型指南&#xff1a;Mage实战与架构解析 【免费下载链接】data-engineer-handbook Data Engineer Handbook 是一个收集数据工程师学习资料的项目。 - 提供数据工程师所需的知识、工具和资源&#xff0c;帮助数据工程师学习和成长。 - 特点&#xff1a;涵盖数…

作者头像 李华
网站建设 2026/4/19 17:26:26

7步掌握raylib跨平台开发:从环境配置到性能优化

7步掌握raylib跨平台开发&#xff1a;从环境配置到性能优化 【免费下载链接】raylib raysan5/raylib 是一个用于跨平台 C 语言游戏开发库。适合在进行 C 语言游戏开发时使用&#xff0c;创建 2D 和 3D 图形应用程序。特点是提供了丰富的图形和音频处理功能、易于使用的 API 和多…

作者头像 李华
网站建设 2026/4/17 17:42:52

OpCore Simplify:探索黑苹果配置工具的智能解决方案

OpCore Simplify&#xff1a;探索黑苹果配置工具的智能解决方案 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 对于初次接触黑苹果的用户来说&#x…

作者头像 李华
网站建设 2026/4/17 16:03:34

零基础高效完成黑苹果安装:OpenCore Simplify自动化配置指南

零基础高效完成黑苹果安装&#xff1a;OpenCore Simplify自动化配置指南 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify OpenCore Simplify是一款专为…

作者头像 李华