换根DP入门:洛谷P3478深度和最大节点求解详解

洛谷 P3478(POI 2008 STA-Station)是我见过最适合入门换根DP的题目,没有之一。题面非常直白:给你一棵树,找出一个节点来做根,让所有节点的深度之和最大。如果数据范围小,直接每个点跑一遍DFS就完事,但这题的数据规模接近百万量级,O(n^2) 根本跑不动。换根DP的做法,就是先随便选一个根把答案算出来,再通过一个 O(1) 的递推式把答案“搬”到相邻节点上,整棵树每个点都能在 O(n) 时间内得到答案。这篇文章就沿着这条主线,把状态定义、公式推导、两份可提交的代码,以及那些容易被坑的细节一次说清楚。适合刚学完树形DP、但遇到“每个点都要当一次根”这类问题容易懵的选手,也适合想系统理解换根DP套路的人。

1. 项目概述:旧题新解,换根DP的经典模板

1.1 题目到底在问什么

题目给出一棵无根树,要求我们选一个点作为根,计算出这棵树上所有节点的深度之和。这里“深度”可以定义为根节点为 0,也可以定义为根节点为 1,题目本身不会因为这个基准不同而改变答案对应的那个节点,后面推导公式的时候这点会自然体现出来。

举个例子,假设树长这样:

  • 1-2
  • 2-3
  • 3-4
  • 2-5

如果拿 1 当根,深度从 0 开始算,那么各节点深度分别是 0、1、2、3、2,总和是 8。如果拿 4 当根,深度分别是 3、2、1、0、3,总和是 9。可以看到不同的根,答案会差不少。题目的核心就是:在所有 n 个候选根里,找出让深度总和最大的那个点。

这种问题之所以值得专门讲,是因为它不像普通树形DP那样只求一个固定根的答案,而是要求“所有可能根”的状态。如果对每个点都重新DFS一次,百万级别的节点数是绝对不可行的,换根DP就是为此设计的。

1.2 为什么不能枚举每个根

很多新手拿到题的第一反应就是:这还不简单,n 次 DFS,每次统计深度和,取最大。这种思路在 n ≤ 2000 时没什么问题,但放到百万量级就完全崩了。

简单估算一下,假设树是一条链,每次DFS都要遍历全部 n 个节点,做 n 次就是 n 的平方量级。n = 100000 时已经是一百亿次操作,n = 1000000 时更是一万亿次,这显然不是几秒能跑完的事。就算用再快的读入优化、再紧凑的邻接表,也救不回来。

所以我们必须换一种思路:第一次DFS先算出一个根(比如节点 1)的答案,第二次DFS时利用父子关系,在常数时间内把父节点的答案“调整”成子节点的答案。因为树上任意两点之间只有一条路径,根从 u 换到相邻的 v 时,变化的只是所有节点的深度,而且这种变化是可以批量计算的。这个“调整”的过程,就是换根DP的核心。

2. 核心细节解析:状态定义与转移方程推导

2.1 第一次DFS:预处理子树大小和初始答案

做换根DP前,我们先约定两个基本状态:

  • sz[u]:以节点 1 为根时,u 的子树大小。注意这里说的“子树”,方向是固定以 1 为参照系的。
  • dp[u]:以 u 为根时,整棵树所有节点的深度之和。

第一次DFS从 1 出发,做三件事:给每个节点记录深度、递归算子树大小、累加得到dp[1]。

这里深度基准选 0 还是 1 都可以,只要第一次DFS和后面公式里的基准保持一致就行。因为换根公式是相邻两个答案之间的差值关系,基准常数会在差值中抵消掉,最终不影响“哪个点最大”的结论。

以 1 为根跑完第一次DFS后,我们得到了一棵有向的“父子树”。每个节点 u 的sz[u]都明确了:这棵子树里一共有多少个节点,深度也明确了:从 1 到每个节点的距离。dp[1]就是所有节点到 1 的距离总和。有了这份基础数据,才能开始“搬家”。

2.2 换根公式:答案如何从父节点迁移到子节点

这是整道题最核心的一步。假设现在知道dp[u],想把根从 u 换到它的某个儿子 v,问dp[v]等于多少。

不要试图重新计算每个节点的深度,那是笨办法。我们只需要看:换根之后,哪些节点深度变了,变了多少。

  • 以 1 为根时,v 的子树大小为sz[v]。这些节点在 u 为根时,v 的子树内所有节点都在 v 这一侧,它们的深度都比原来少了 1,所以总共减少了sz[v]。
  • 剩下的部分呢?除了 v 子树内的节点,其他节点在换根后都离根更远了。具体来说,u 和 u 那边所有的节点,深度都比原来多了 1。这部分节点数量是n - sz[v],所以总共增加了n - sz[v]。

于是可以得到:

dp[v] = dp[u] - sz[v] + (n - sz[v]) = dp[u] + n - 2 * sz[v]

这个式子就是换根DP的命根子。后面所有代码都是在反复用这个公式。

这里有个很容易绕晕的点:公式里的sz[v]是第一次DFS以 1 为根时算出的子树大小,不是“以 u 为根时 v 的子树大小”。因为第一次DFS已经固定了整棵树的父子方向,第二次DFS只是沿着这个方向从上往下推,sz数组不会改变。很多初学者在这里会突然怀疑:“根都换成 v 了,v 的子树不就应该变成反方向了吗?”其实不必纠结,sz[v]只是一个预先算好的静态信息,它告诉你:如果根从 u 移到 v,哪些节点深度会减少。减少的永远是以 1 为根时 v 子树里的那些节点。

2.3 手算验证公式到底怎么用

光看公式容易发虚,拿前面那棵树实际算一遍。

先以 1 为根DFS,得到:

  • sz[1] = 5, sz[2] = 4, sz[3] = 2, sz[4] = 1, sz[5] = 1
  • dp[1] = 0 + 1 + 2 + 3 + 2 = 8

从 1 换到 2:

dp[2] = dp[1] + n - 2 * sz[2] = 8 + 5 - 2 * 4 = 5

手动以 2 为根算一下深度总和:节点深度为 1、0、1、2、1,总和确实是 5。公式正确。

从 2 换到 3:

dp[3] = dp[2] + n - 2 * sz[3] = 5 + 5 - 2 * 2 = 6

从 3 换到 4:

dp[4] = dp[3] + n - 2 * sz[4] = 6 + 5 - 2 * 1 = 9

最后答案是 9,对应节点 4,跟最开始手动枚举的结果一致。整棵树的答案表如下:

根节点深度总和
18
25
36
49
58

最大值出现在节点 4,所以输出 4。如果没有换根公式,这五个数每个都要重新DFS一遍才能得到;有了公式,只需要一次预处理的 O(n) 加一次 O(1) 的递推,快得多。

3. 实操过程:从零写出这份AC代码

3.1 建图选型:邻接表还是链式前向星

写树题第一步永远是怎么存图。常见的方案有两种:vector邻接表和链式前向星。

对于这道题,vector写法读起来最友好,代码也最少。但要注意,如果 n 真的到百万级,vector的扩容和遍历开销会比链式前向星高一些,而且每个节点都要维护一个动态数组,内存碎片和 cache 命中率都不如静态数组。竞赛里稳妥的做法是用链式前向星,也就是用静态数组模拟邻接表。

链式前向星本质上就是用一个head数组记录每个点第一条边的下标,再用next把同一个点的所有边串起来。加边和遍历都非常直接,内存是预先开好的连续数组,性能稳定。

如果只是想快速理解换根DP的逻辑,vector版本完全够用。但如果想在 POI 原题这种大数据范围下求稳,建议用链式前向星。下面先把完整代码给出来,再解释每个部分。

3.2 完整可提交代码

#include <bits/stdc++.h> using namespace std; const int MAXN = 1000005; struct Edge { int to, next; } edge[MAXN << 1]; int head[MAXN], tot; int n; int sz[MAXN]; long long dp[MAXN]; void addEdge(int u, int v) { edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; } void dfs1(int u, int fa, int depth) { sz[u] = 1; dp[1] += depth; for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (v == fa) continue; dfs1(v, u, depth + 1); sz[u] += sz[v]; } } void dfs2(int u, int fa) { for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (v == fa) continue; dp[v] = dp[u] + n - 2LL * sz[v]; dfs2(v, u); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> n; memset(head, -1, sizeof(head)); for (int i = 1; i < n; ++i) { int a, b; cin >> a >> b; addEdge(a, b); addEdge(b, a); } dfs1(1, 0, 0); dfs2(1, 0); int ans = 1; for (int i = 2; i <= n; ++i) { if (dp[i] > dp[ans]) ans = i; } cout << ans << '\n'; return 0; }

代码核心逻辑并不复杂。dfs1里每次递归到一个新节点,先把当前深度累加到dp[1],再递归处理子节点,子节点返回后把子树的节点数累加到当前节点。dfs2就纯粹是套公式,从 1 开始一路往下更新各个点的答案。

注意最后一轮找答案,ans初始设为 1,更新时用的是dp[i] > dp[ans]而不是>=。这样如果存在多个相同最大值的解,最终会保留编号最小的那个,题目如果要求输出编号最小的点,这个细节可以帮你少踩一个坑。

3.3 复杂度与空间核算

整个算法只有两次遍历,每次每个节点和每条边都只会被访问一次,所以时间复杂度是 O(n)。空间上:

  • sz数组:int 类型,约 4MB(百万级)
  • dp数组:long long,约 8MB
  • head数组:int,约 4MB
  • edge数组:每条边存 to 和 next 两个 int,无向边要存两条,共 2*(n-1) 条边,约 16MB

合计大约 32MB,对现代竞赛评测机来说是很友好的。这也是为什么我们要用静态数组而不是反复动态开辟内存,vector的默认扩容策略在百万节点下会多出不少不必要的开销。

4. 常见问题与排查技巧实录

4.1 爆 int:答案不一定是 int 能装下的

这是新手最容易犯的错,没有之一。深度总和最大值出现在树退化成一条链的时候,如果根选在链的端点,所有节点深度之和大概是 0+1+2+...+(n-1),也就是 n*(n-1)/2。n 到百万级别时,这个值大约是 5×10^11,远超 int 的范围。

所以在代码里,dp数组、dp[1]的累加、换根公式中的dp[v] = dp[u] + n - 2LL * sz[v],都必须用 long long。尤其是公式里的2LL * sz[v],如果写成2 * sz[v],两个 int 相乘可能先溢出再转 long long,结果就错了。

4.2 递归栈溢出与迭代改写

树题的递归深度最坏情况下就是链长,n 很大时递归深度可能达到百万。本地环境或者部分评测机的栈空间不够,就会出现段错误。这个问题在 Windows 上尤其明显,默认栈只有 1MB 左右。

解法有两种。第一种是在代码里加栈空间扩增指令,但这种方法依赖编译器,不同环境不通用。第二种是干脆把 DFS 改成迭代,这里给一个迭代版本的思路。

第一次DFS可以借助栈模拟递归,用三元组(u, fa, state)表示节点 u、父节点 fa、当前状态。state 为 0 表示刚进入节点需要统计深度,state 为 1 表示子树已经处理完,要回溯累加sz:

void dfs1_iter(int root) { stack<tuple<int, int, int>> st; st.emplace(root, 0, 0); while (!st.empty()) { auto [u, fa, state] = st.top(); st.pop(); if (state == 0) { sz[u] = 1; dp[1] += (fa ? dep[fa] + 1 : 0); st.emplace(u, fa, 1); for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (v == fa) continue; st.emplace(v, u, 0); } } else { if (fa) sz[fa] += sz[u]; } } }

第二次DFS其实不需要递归,用队列从根开始往下推就行,因为父节点的dp值一定先算好,只要保证从根到叶子的顺序即可:

queue<int> q; q.push(1); while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (v == fa[u]) continue; fa[v] = u; dp[v] = dp[u] + n - 2LL * sz[v]; q.push(v); } }

实际做题时,如果题目明确是百万级且有链状数据,建议直接用迭代版本,省心很多。递归版本在多数 Linux 评测机上靠 8MB 栈或许能过,但没必要赌这种运气。

4.3 常见问题速查表

现象可能原因解决办法
样例能过但大数据点超时使用了 O(n^2) 枚举根换根DP,两次遍历
答案输出负数或溢出int 溢出dp和公式用 long long
递归写法在本地段错误系统栈太小改成迭代栈,或扩栈指令
多组解输出编号错误更新方案时用了>=改成>,保留最小编号
建图后遍历不到某些边head未初始化使用memset(head, -1, sizeof(head))
树的父子方向混乱没有正确跳过父节点DFS参数中记录fa,遍历时if (v == fa) continue

4.4 关于“深度基准”的坑

换根公式本身不关心深度从 0 还是 1 开始,两个基准下算出来的dp值差了一个常数 n,最大值对应的节点不会变。但如果你第一次DFS用深度 0 初始化,第二次DFS又突然用深度 1 的思路来理解公式,容易在推导时把自己绕进去。

建议从一开始就固定一个基准:根深度为 0。这样dp[1]的物理意义就是“所有节点到 1 的距离总和”,换根公式dp[v] = dp[u] + n - 2*sz[v]可以直接解释为距离变化,不用再额外加偏移。

5. 从 P3478 延伸出去的换根DP套路

5.1 什么样的题需要换根DP

做了这道题之后,可以把换根DP的识别方法沉淀下来。通常满足下面几个特征:

  • 题面要求“每个点作为根时”或者“每个点作为起点时”的某种指标
  • 朴素做法是枚举每个点做完整树遍历
  • 指标能够从相邻点的答案加上子树的某些信息推导出来

最典型的就是求深度和、距离和、最大深度、最小深度这一类树上统计问题。如果只是求某一个固定根的答案,那普通树形DP就够了。一旦要求“所有点”,就要考虑能不能用一次预处理+一次递推完成。

5.2 几个常见扩展方向

换根DP不只是能求深度和。如果题目要求“所有点对之间的距离总和”,也可以用类似思路,只不过第一次DFS除了记录子树大小,通常还要记录子树内所有节点到当前根的距离和,然后换根时多做一次累加。

如果边权不是 1,而是带权值 w,公式就变成dp[v] = dp[u] + w * (n - 2*sz[v])。原理完全一样:v 子树内的节点离根近了 w,其他节点离根远了 w。

如果题目要求“哪个点作为根时,到所有点的最远距离最小”,那就需要在换根时维护每个点为根时到子树内的最远距离和子树外的最远距离,第一次DFS求子树内信息,第二次DFS用父节点信息更新子树外信息,本质上也是换根DP的变种。

之所以说 P3478 是入门经典,是因为它剥掉了所有花哨的包装,把换根DP最核心的“答案如何从父节点搬到子节点”这件事直接展示了出来。把这个公式吃透,后续遇到带权、求最值、求距离和的题目,思路会顺畅很多。

5.3 个人实操体会

我个人第一次做这道题时,其实是先把递归版本写好并本地跑通,然后才去思考迭代写法。我的建议是:学的时候可以先用最直观的vector+ 递归,把逻辑理清楚;真正提交大数据范围前,再改成链式前向星 + 迭代。不要一上来就同时追求代码性能和逻辑简洁,那样容易在写代码时犯低级错误。

另外有个很实用的调试技巧:小范围数据下,可以写一个暴力枚举根的代码,跟换根DP跑出来的dp数组逐一对拍。P3478 这种题,只要dp数组所有值能对上暴力结果,基本上就说明公式本身没写错,剩下的问题只可能在读入、建图这些边角上。

最后分享一个小技巧:如果你发现换根DP推出来的公式总是不对,试着把size的概念和“深度变化方向”绑在一起想,而不是死记公式。当根从 u 变成 v 时,v 的子树是深度变小的那部分,其他部分是深度变大的那部分,变化的差值就是两边节点数量的差。理解了这一点,就算面试或者比赛中忘了具体公式,也能在 30 秒内重新推出来。

做题不是背公式,而是理解公式怎么来的。P3478 教给我的,正是这件事。

相关新闻

小波时频图 + SwinTransformer:轴承故障诊断实战全解析

小波时频图 + SwinTransformer:轴承故障诊断实战全解析

在旋转机械故障诊断这个方向上&#xff0c;轴承的振动信号分析永远是绕不开的话题。我之前做过几轮基于传统特征提取的项目&#xff0c;也试过直接喂一维信号给卷积分网络&#xff0c;最终在实际项目里稳定复现、效果也最让我放心的方案&#xff0c;是小波时频图加SwinTransfor…

2026/10/20 13:20:14 阅读更多 →
Linux常用命令实战指南:从文件操作到排查技巧的完整梳理

Linux常用命令实战指南:从文件操作到排查技巧的完整梳理

刚接触 Linux 的时候&#xff0c;我也找过一份所谓“Linux 常见命令集合”&#xff0c;复制到笔记里当作字典来背。结果可想而知&#xff1a;背了三天&#xff0c;连文件权限数字对应的含义都记不牢。后来在实际项目里摸爬滚打&#xff0c;才慢慢明白&#xff0c;命令不是背出来…

2026/10/20 13:19:13 阅读更多 →
从零构建可维护项目:以rea为例的架构设计与实操指南

从零构建可维护项目:以rea为例的架构设计与实操指南

1. 从“rea”这个标题说起&#xff1a;一个极简命名背后的完整项目思维第一次看到“rea”这个标题的时候&#xff0c;我脑子里蹦出来的第一反应是——这大概率又是一个被随手命名的项目。做技术的人都有这个毛病&#xff0c;项目文件夹建好的那一刻&#xff0c;名字往往是最后才…

2026/10/20 13:19:13 阅读更多 →

最新新闻

链表环与相交的底层原理:从内存地址到工程可靠性

链表环与相交的底层原理:从内存地址到工程可靠性

1. 这不是一道“刷题”题&#xff0c;而是数据结构底层思维的试金石“链表是否有环&#xff0c;是否相交”——看到这八个字&#xff0c;很多人第一反应是&#xff1a;哦&#xff0c;又是LeetCode第141、142、160题。翻出快慢指针模板&#xff0c;背下相遇点公式&#xff0c;提…

2026/10/20 13:57:57 阅读更多 →
AI辅助视频剪辑实操:把3小时剪片压缩到45分钟的时间账单

AI辅助视频剪辑实操:把3小时剪片压缩到45分钟的时间账单

经常有人问我&#xff1a;“为什么我剪一条视频要两三个小时&#xff0c;别人半小时就搞定了&#xff1f;”我一般不会直接回答&#xff0c;而是反过来问一句&#xff1a;“你剪视频的时候&#xff0c;是不是一直在手动做字幕&#xff1f;”对方往往一愣&#xff0c;然后陷进沉…

2026/10/20 13:57:57 阅读更多 →
DDS协议实战:分布式仿真系统实时数据分发与调优指南

DDS协议实战:分布式仿真系统实时数据分发与调优指南

做分布式仿真和实时系统这一行&#xff0c;通信协议往往是决定项目成败的“隐形变量”。我真正开始系统研究DDS&#xff08;Data Distribution Service&#xff0c;数据分发服务&#xff09;这个协议&#xff0c;是因为一个分布式仿真项目——多个异构节点要在局域网内实时同步…

2026/10/20 13:57:57 阅读更多 →
Python数据分析项目实战:从源码拆解到二次开发

Python数据分析项目实战:从源码拆解到二次开发

简介&#xff1a;这份资源面向希望系统掌握Python数据分析与可视化的学习者&#xff0c;覆盖从编程基础到实战项目的完整技能链路&#xff0c;适合高校学生、转行者及需要处理结构化数据的从业者。压缩包共162个文件&#xff0c;约31.27MB&#xff0c;以67个ipynb实验代码、11个…

2026/10/20 13:57:57 阅读更多 →
AI写教材如何避免重复?低查重工具嵌入创作流程的实战指南

AI写教材如何避免重复?低查重工具嵌入创作流程的实战指南

如果有人觉得“AI写教材”就是输入标题、点一下生成、再复制粘贴&#xff0c;那就大错特错了。真正做过教材或讲义的人都知道&#xff0c;教材创作最耗时的不是“写”&#xff0c;而是让内容准确、成体系、可教学&#xff0c;同时还要保证表达足够原创、不跟已有资料大面积重合…

2026/10/20 13:57:57 阅读更多 →
AO环境光遮蔽合成指南:黑线与白底之间的数值差异与混合策略

AO环境光遮蔽合成指南:黑线与白底之间的数值差异与混合策略

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

2026/10/20 13:56:50 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →