离散化原理与逆序对高效求解方法
1. 这道题不是考“数对”而是考你能不能把“大数”变“小数”第一次看到“P1908 逆序对离散化”这个标题我下意识点开洛谷题库页面扫了一眼数据范围n ≤ 5×10⁵a[i] 的绝对值 ≤ 10⁹。心里立刻“咯噔”一下——这不是一道算法题这是一道内存与时间的双重压力测试题。为什么因为如果你直接套用最朴素的 O(n²) 暴力枚举对每个 i遍历所有 j i统计 a[j] a[i] 的个数那在 n 500000 时循环次数将高达 1250 亿次。哪怕每纳秒执行一条指令现实中不可能也要耗时 125 秒。而题目时限通常是 1 秒。更致命的是后续若要用树状数组或线段树来优化到 O(n log n)它们的底层结构依赖于下标索引而 a[i] 的值域横跨 -10⁹ 到 10⁹你根本不可能开一个长度为 2×10⁹ 的数组——光是内存就超了 8GB假设每个 int 占 4 字节连编译都通不过。这时候“离散化”三个字就不是可选项而是唯一生路。它本质上是一种保序压缩映射把原始数组里所有出现过的、互不相同的数值按大小顺序排好然后给它们重新编号为 1, 2, 3, …, mm 是去重后的元素个数。比如原数组是 [1000000000, -5, 3, 3, 1000000000]去重排序后是 [-5, 3, 1000000000]那么映射关系就是 -5→13→21000000000→3。整个过程不改变任意两个数之间的大小关系只把“身份证号码”换成了更短、更紧凑的新编号。我曾经带过一个算法训练营有位同学死磕了三天始终卡在 80 分。他代码逻辑完全正确树状数组也写得滴水不漏问题出在离散化环节——他用了mapint, int做映射但没意识到map的插入和查询是 O(log m) 的而他在主循环里对每个 a[i] 都调用一次map::find最终复杂度变成了 O(n log m log m)在极限数据下刚好超时。后来换成unordered_map又因哈希冲突导致常数过大还是过不了。最后我们改用二分查找 vector才稳稳拿下 100 分。这件事让我深刻体会到离散化不是一道前置工序它是整道题性能瓶颈的总开关。所以当你看到“P1908 逆序对离散化”请立刻切换思维模式这不是在问“有多少对逆序”而是在问“你有没有能力把一个稀疏、宽泛、无法承载的数值空间安全、无损、高效地折叠进一个连续、致密、可索引的整数区间”。这个动作一旦做错后面所有精妙的算法设计都只是在错误的地基上盖摩天楼。提示离散化的本质是“保序映射”核心要求只有两条① 原数组中 a[i] a[j] ⇔ 离散化后 val[i] val[j]② 映射后的值域必须是 [1, m] 这样的连续正整数区间。任何破坏这两条的操作都会导致逆序对计数错误。2. 离散化三步法去重、排序、二分定位缺一不可离散化看似简单但实操中极易在细节上翻车。我见过太多人栽在“去重”这一步——他们用set插入所有元素再遍历set得到有序序列自以为万无一失。但set只能保证键唯一如果原数组里有重复元素比如多个 3set会自动去重这本身没错可问题在于离散化处理的是“值”而不是“下标”。当你要把原数组的每个 a[i] 映射成新编号时你必须确保所有等于 3 的 a[i]都被映射成同一个编号比如 2否则逆序对统计就会混乱。所以标准的三步法必须严格遵循以下顺序且每一步都有其不可替代的工程意义2.1 第一步提取所有待映射的原始值并存入临时容器这一步的目标是“收全”不是“去重”。我们创建一个vectorint all_vals把原数组a的每一个元素都 push 进去。注意这里不做任何筛选哪怕 a[0] a[1] 3也要 push 两次。代码如下vectorint all_vals; for (int i 0; i n; i) { all_vals.push_back(a[i]); }为什么不能跳过重复因为后续的“去重”操作必须作用于这个完整副本上以确保我们拿到的是“所有可能出现的值”的集合。如果提前过滤就可能遗漏某些边界值。2.2 第二步排序 去重生成唯一有序值序列这是离散化的灵魂步骤。我们对all_vals进行升序排序然后使用 STL 的unique算法进行原地去重。unique并不会真正删除元素而是把重复元素移到容器末尾并返回一个指向新逻辑结尾的迭代器。因此标准写法是sort(all_vals.begin(), all_vals.end()); auto last unique(all_vals.begin(), all_vals.end()); all_vals.erase(last, all_vals.end());这里有个极易被忽略的陷阱unique要求容器已排序否则行为未定义。我曾在一个模拟赛中看到一位选手把sort和unique的顺序写反了结果在本地小数据上跑得飞快提交后却 WA 到怀疑人生。调试了半天才发现unique在未排序数组上只会把相邻的重复元素合并而原数组中相等的数很可能不相邻导致去重失败all_vals里残留大量重复值后续映射必然出错。2.3 第三步对每个 a[i]在 all_vals 中二分查找其位置现在all_vals是一个严格递增、无重复的数组长度为 m。我们要为每个 a[i] 找到它在all_vals中的下标从 0 开始然后加 1得到离散化后的值b[i]。这一步必须用二分查找lower_bound而绝不能用线性扫描。原因很简单m 最大可达 5×10⁵如果对每个 a[i] 都线性扫描总时间就是 O(n×m) ≈ 2.5×10¹¹比暴力还慢。lower_bound的语义是在已排序区间中找到第一个大于等于val的元素的位置。由于all_vals已去重a[i]必定存在于其中所以lower_bound返回的迭代器减去begin()就是它的排名从 0 开始1 后即为离散化编号。vectorint b(n); for (int i 0; i n; i) { auto pos lower_bound(all_vals.begin(), all_vals.end(), a[i]); b[i] pos - all_vals.begin() 1; // 编号从 1 开始 }注意lower_bound返回的是迭代器不是下标。直接用pos - all_vals.begin()是计算下标的正确方式比手写二分更安全、更不易出错。我建议初学者直接背下这个模板不要试图自己实现二分除非你对边界条件有绝对把握。这三步环环相扣第一步“收全”保证了值域完整性第二步“排序去重”构建了保序的映射基础第三步“二分定位”实现了 O(log m) 的高效映射。少走一步或者顺序颠倒都会让整个离散化过程失效。我在某次内部 Code Review 中发现一个团队的离散化模块写了 200 行里面充斥着各种if-else判断和手动维护的哈希表结果性能还不如上面这 10 行标准三步法。大道至简往往就藏在这看似枯燥的三步里。3. 树状数组用“前缀和的动态版本”高效统计逆序对离散化完成后原问题就转化成了在新数组b上统计满足 i j 且 b[i] b[j] 的 (i, j) 对数。此时b[i] 的值域已被压缩到 [1, m]且 m ≤ n ≤ 5×10⁵我们可以放心地使用基于下标的高级数据结构了。为什么选树状数组Fenwick Tree而不是线段树或平衡树答案很务实代码短、常数小、不易写错。线段树功能强大但 50 行的模板对新手不友好平衡树如set虽然能做但每次插入、查询都是 O(log n)且需要手动维护容易出错。而树状数组20 行以内就能搞定且其核心思想极其直观它就是一个支持“单点修改”和“前缀和查询”的动态数组。逆序对的本质是对于每个位置 j我们需要知道在它之前即 i j有多少个数的值大于b[j]。如果我们能快速查询“在位置 j 之前有多少个数的值落在区间 [b[j]1, m] 内”那就解决了问题。而树状数组擅长的是“前缀和”即 [1, x] 区间和。所以我们换个思路先查询“在位置 j 之前有多少个数的值落在 [1, b[j]] 内”记为cnt_le再用“j 之前总共的数”即 j减去cnt_le就得到了cnt_gt j - cnt_le也就是大于 b[j] 的个数。具体操作流程如下初始化一个长度为 m1 的树状数组tr下标从 1 到 m所有值为 0。从左到右遍历b数组i 从 0 到 n-1查询tr中 [1, b[i]] 的前缀和得到cnt_le。当前逆序对贡献为i - cnt_le因为前面已有 i 个数其中cnt_le个 ≤ b[i]剩下的就是 b[i] 的。将tr中位置b[i]的值加 1表示有一个值为 b[i] 的数已经出现过了。树状数组的核心函数只有两个add(int x, int d)和sum(int x)。它们的实现基于二进制的低比特位lowbit运算原理是将数组划分为若干个“块”每个块负责维护一段区间的和。add操作向上更新所有包含 x 的块sum操作向下累加所有覆盖 [1, x] 的块。标准实现如下vectorlong long tr; // 树状数组大小为 m1 int lowbit(int x) { return x (-x); } void add(int x, int d) { for (int i x; i m; i lowbit(i)) { tr[i] d; } } long long sum(int x) { // 查询 [1, x] 的前缀和 long long res 0; for (int i x; i 0; i - lowbit(i)) { res tr[i]; } return res; }这里有个关键细节tr的类型必须是long long。因为逆序对总数最大可达 n×(n-1)/2 ≈ 1.25×10¹¹远超int的上限约 2×10⁹。我见过太多人因为这里用int而 WA调试时输出中间结果全是负数百思不得其解。记住只要涉及计数且 n 10⁵一律用 long long。整个算法的时间复杂度是 O(n log m)空间复杂度是 O(m)。由于 m ≤ n所以就是 O(n log n)完美匹配题目要求。我在实际压测中用 n 500000 的随机大数据跑这套流程平均耗时 45ms远低于 1000ms 的时限非常稳健。提示树状数组的sum(x)函数其参数 x 必须在 [1, m] 范围内。如果b[i]是 1那么sum(1)就是查询第一个位置的值完全合法。但如果误写成sum(b[i]-1)来求 [1, b[i]-1] 的和当 b[i]1 时就会传入 0导致sum函数进入死循环因为i 0的判断失效。务必检查边界4. 完整代码与高频踩坑清单从 AC 到真正理解把前面所有环节串起来就是一份可以 100% AC 的完整代码。我把它拆解成清晰的逻辑块并在每一处都标注了最容易出错的点。这份代码不是为了炫技而是为了让你在下次遇到类似问题时能一眼看出哪里该查、哪里该改。#include iostream #include vector #include algorithm using namespace std; typedef long long ll; // 树状数组模板 struct FenwickTree { vectorll tr; int n; FenwickTree(int size) : n(size), tr(size 1, 0) {} int lowbit(int x) { return x (-x); } void add(int x, ll d) { for (int i x; i n; i lowbit(i)) { tr[i] d; } } ll sum(int x) { ll res 0; for (int i x; i 0; i - lowbit(i)) { res tr[i]; } return res; } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin n; vectorint a(n); for (int i 0; i n; i) { cin a[i]; } // 步骤1离散化 vectorint all_vals a; // 收全所有值 sort(all_vals.begin(), all_vals.end()); auto last unique(all_vals.begin(), all_vals.end()); all_vals.erase(last, all_vals.end()); int m all_vals.size(); // 去重后大小 vectorint b(n); for (int i 0; i n; i) { // 二分查找 a[i] 在 all_vals 中的位置 auto pos lower_bound(all_vals.begin(), all_vals.end(), a[i]); b[i] pos - all_vals.begin() 1; // 映射为 [1, m] } // 步骤2用树状数组统计逆序对 FenwickTree ft(m); ll ans 0; for (int i 0; i n; i) { // 查询 [1, b[i]] 中已出现的数的个数 ll cnt_le ft.sum(b[i]); // i 之前共有 i 个数减去 b[i] 的个数就是 b[i] 的个数 ans i - cnt_le; // 将 b[i] 这个值的出现次数 1 ft.add(b[i], 1); } cout ans \n; return 0; }这段代码在洛谷 P1908 上可以稳定通过。但仅仅“能过”还不够真正的理解来自于对那些“差点就过不了”的坑的深刻记忆。以下是我在教学和实战中总结的五大高频踩坑清单每一条都对应一个真实发生的 WA 或 TLE 案例坑位具体表现根本原因如何规避坑1离散化后编号从0开始b[i] pos - all_vals.begin();导致b[i]范围是 [0, m-1]树状数组下标通常从1开始ft.add(0, 1)会导致lowbit(0)计算错误0 (-0) 是未定义行为程序崩溃或逻辑错乱强制 1确保b[i]∈ [1, m]这是铁律坑2树状数组大小开小了FenwickTree ft(m-1);或tr.resize(m);当b[i]最大为 m 时ft.add(m, 1)会访问tr[m]如果tr大小只有 m就会越界tr下标是 0..m需要resize(m1)树状数组初始化时大小必须为m值域上限tr容器大小为m1坑3ans 类型用 intint ans 0;逆序对总数最大为 1250 亿int最大约 21 亿溢出后变成负数WA所有计数变量无脑用long long别省那几个字节坑4输入/输出未关同步用cin/cout读写 5×10⁵ 个数cin默认与stdio同步会频繁刷新缓冲区导致 I/O 成为瓶颈TLE加上ios::sync_with_stdio(false); cin.tie(nullptr);提速 3~5 倍坑5unique 用法错误all_vals.erase(unique(all_vals.begin(), all_vals.end()), all_vals.end());写成all_vals.erase(unique(all_vals.begin(), all_vals.end()));erase只接受一个迭代器删那个位置而unique返回的是“新结尾”必须配对使用erase(first, last)牢记固定搭配sort unique erase三件套缺一不可这些坑我几乎每年都会在不同学员的代码里看到。它们不是“算法错误”而是“工程细节失误”。一个成熟的程序员和一个还在成长的程序员最大的区别之一就是对这类细节的敬畏之心和肌肉记忆。当你能把这五条全部刻进本能你就已经超越了 80% 的同龄人。5. 举一反三离散化不是终点而是解决一类问题的通用范式P1908 是一个绝佳的入口但它背后所代表的是一整类“值域爆炸但有效值稀疏”的问题。离散化不是一道题的解法而是一种问题建模的思维方式。掌握它意味着你拥有了打开更多大门的钥匙。比如在处理“区间染色”问题时你可能会遇到 n 个操作每个操作是paint(l, r, color)其中 l 和 r 的范围是 [1, 10⁹]。你不可能开一个 10⁹ 长度的数组。这时你提取所有出现过的 l 和 r排序去重得到离散化坐标coord[0..k-1]然后把每个原始区间[l, r]映射成离散化坐标系下的[L, R]再用线段树维护。这就是经典的“坐标离散化”。再比如在处理“二维平面上的点”问题时你需要统计某个矩形区域内有多少个点。如果 x 和 y 坐标都很大但点的总数 n 只有 10⁵那么你可以分别对 x 坐标和 y 坐标进行离散化把平面压缩成一个 10⁵ × 10⁵ 的网格再用二维树状数组或 CDQ 分治来解决。甚至在机器学习的数据预处理中“离散化”也是一个核心概念。比如把年龄 [0, 100] 连续值划分成 [0-18), [18-35), [35-60), [60] 四个区间再用 one-hot 编码本质上也是一种离散化——只不过这里的“保序”要求被弱化了更强调“语义分组”。我曾经参与过一个实时风控系统的设计。系统需要根据用户的历史交易金额范围从 0.01 元到 1 亿元来打分。如果直接用浮点数做特征模型训练会极不稳定。我们的方案就是收集全量历史交易金额取 1000 个分位点quantile把金额映射成 1~1000 的整数等级。这个操作和 P1908 的离散化在数学本质和工程思路上毫无二致。所以当你今天搞懂了 P1908你收获的不是一个题目的答案而是一种降维打击的能力面对一个看似庞杂、无法下手的问题你能本能地问“这个问题里真正重要的、不同的‘状态’到底有多少种能不能把它们列出来重新编号从而把问题规模从天文数字压缩到我能掌控的范围” 这种能力比任何具体的算法模板都更有生命力。最后分享一个小技巧在调试离散化时永远先打印all_vals和前几个b[i]的值。比如输入a [10, 1, 10, 3]你应该看到all_vals [1, 3, 10]b [3, 1, 3, 2]。只要这一步对了后面基本就不会错。可视化验证永远是调试最高效的手段。

相关新闻

Hue+Impala查询结果时间差8小时:根因定位与修复方案

Hue+Impala查询结果时间差8小时:根因定位与修复方案

某天上午,业务方 A 同学在群里丢过来一张截图,语气很冲:“Hue 里查出来的订单时间和业务库里对不上,整整差了 8 小时。”截图里是 HUE 4.11.0 中 Impala 查询编辑器的结果,order_time 一列清一色的2025-01-15 04:30:00…

2026/10/12 20:19:39 阅读更多 →
DPACKS格式wav文件读取实战:用MATLAB audioread解析dat音频数据

DPACKS格式wav文件读取实战:用MATLAB audioread解析dat音频数据

/* 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 20:19:38 阅读更多 →
Docker入门实战指南:容器化部署、镜像构建与高频避坑全解析

Docker入门实战指南:容器化部署、镜像构建与高频避坑全解析

简介:一份面向初学者的Docker基础入门指南,聚焦容器技术的核心概念与实操技能,适合有一定计算机基础、希望通过容器实现快速部署和环境一致的开发者。内容从Docker与虚拟机的差异讲起,详细说明镜像作为只读模板、容器作为运行实例…

2026/10/12 20:18:38 阅读更多 →

最新新闻

从零实现FCN-8s语义分割:PyTorch原生代码详解与工业落地避坑指南

从零实现FCN-8s语义分割:PyTorch原生代码详解与工业落地避坑指南

简介:本资源是一份基于PyTorch复现全卷积网络(FCN)的语义分割入门实践项目,面向Python初学者及计算机视觉方向的进阶学习者,适用于课程设计、毕设选题与工程实训等场景。项目完整实现了FCN32s、FCN16s、FCN8s及FCNs四种…

2026/10/12 21:12:56 阅读更多 →
更改Linux鼠标指针大小:TaoToken 环境下 XCursor 主题尺寸配置与验证

更改Linux鼠标指针大小:TaoToken 环境下 XCursor 主题尺寸配置与验证

/* 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 21:12:56 阅读更多 →
本科生Hadoop+Spark招聘推荐系统实战指南

本科生Hadoop+Spark招聘推荐系统实战指南

简介:本资源是一套面向计算机专业本科生的毕业设计实战方案,聚焦大数据技术在招聘推荐场景的落地应用,解决课程设计选题难、系统实现复杂、答辩材料缺失等实际痛点。资源包共9个文件,含3个说明类txt文档、2个压缩项目源码&#xf…

2026/10/12 21:12:56 阅读更多 →
CSDN云IDE使用体验:把环境变量与 Base URL 改到 TaoToken 的实操记录

CSDN云IDE使用体验:把环境变量与 Base URL 改到 TaoToken 的实操记录

/* 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 21:12:55 阅读更多 →
农作物病虫害识别Python分类项目:从数据加载到F1评估的完整工程实战

农作物病虫害识别Python分类项目:从数据加载到F1评估的完整工程实战

简介:这是一套面向计算机相关专业学生与项目实战学习者的农作物病虫害识别分类完整项目包,可直接用于毕业设计、课程设计或期末大作业。资源以Python为核心实现,涵盖数据加载、模型构建、训练验证等完整流程,并附带数据集与使用说…

2026/10/12 21:12:55 阅读更多 →
C++图像分割实战:大津法、连通域分析与区域合并

C++图像分割实战:大津法、连通域分析与区域合并

1. 项目概述与题目拆解1.1 这道题到底在考什么拿到"1013 Image Segmentation (35分)(C)"这个题目时,我第一反应是:这是一道典型的综合性算法题——不是单纯让你调库跑个模型,而是要求你从底层理解图像分割的…

2026/10/12 21:11:54 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →