算竞一题中的代码设计与技巧解析 _
以这一题为例需要对节点 1 求 两次 dijkstra怎么使得代码写的简洁ac 代码如下我们来一一解析void solve() { int n(q_), m(q_); using ED arrayint, 2; using GRAPH vectorvectorED; GRAPH tr(n 1); GRAPH rtr(n 1); ffp(i, 1, m) { int u(q_), v(q_), w(q_); tr[u].push_back({ w,v });//tr是树边本人的习惯 rtr[v].push_back({ w,u });//rtr是反图 } auto dij [](int st, GRAPH gp)-ll { priority_queueED, vectorED, greaterEDdui; vectorintdis(n 1, iINF); dis[st] 0; dui.push({ 0,st }); while (dui.size()) { auto [w, now] dui.top(); dui.pop(); if (dis[now] w)continue; for (auto [nxtw, nxt] : gp[now]) { if (nxtw dis[now] dis[nxt])continue; dis[nxt] nxtw dis[now]; dui.push({ nxtw dis[now],nxt }); } } return accumulate(dis.begin() 1, dis.end(), 0ll); }; cout dij(1, tr) dij(1, rtr) endl; return; }【初始化与快读】首先我们使用n(q_), m(q_)对变量进行初始化。这里的q_是封装好的快读对象Fast I/O用于高效读取整数。值得一提的是n(q_)使用的是直接初始化Direct Initialization而我们常见的n q_属于复制初始化Copy Initialization。虽然在基础类型上二者生成的汇编指令并无差异但直接初始化更具 Modern C 的风格。【类型别名优化】为了提升代码的可读性与编写效率我们使用using ED arrayint, 2为边的存储结构定义别名同理定义了GRAPH。这样不仅减少了冗余代码也便于后续维护。【存图细节】在读入边权并建图时有一个关键细节tr[u].push_back({ w, v })。请注意我们将权值 w放在了array的首位。 这是为了配合后续的priority_queue。由于 STL 的容器默认按字典序比较即先比较第一个元素将权值置于首位可以直接利用默认的比较规则实现“按权值排序”无需额外编写比较函数。【Dijkstra 的封装】我们使用 Lambda 表达式封装了 Dijkstra 算法。在传参时图结构GRAPH必须使用引用传递建议为const引用以避免在函数调用时发生巨大的内存拷贝开销防止 TLE超时。【累加器的陷阱】计算最终结果时我使用了numeric库中的accumulate函数。这里潜藏着一个常见的溢出陷阱accumulate的第三个参数决定了累加过程的数据类型。 如果传入0编译器会将其推导为int类型进行累加这在处理大数时会导致溢出。因此必须显式传入0ll即long long类型的 0以确保计算过程使用长整型。【I/O 细节】最后输出时使用\n替代endl。endl会强制刷新缓冲区在大规模输出场景下可能导致显著的性能损耗。