<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="/atom.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>ff66ccff</title>
    <link>https://ff66ccff.github.io/</link>
    <description>
      <![CDATA[ff66ccff 的技术与生活手记，四川大学，计算机，中文VOCALOID，科幻小说 | Tech & life notes by ff66ccff; Sichuan University, CS, Chinese VOCALOID, sci-fi.]]>
    </description>
    <language>zh-CN</language>
    <copyright>All rights reserved 2026, ff66ccff</copyright>
    <lastBuildDate>Sat, 12 Sep 2026 00:45:00 GMT</lastBuildDate>
    <generator>Hexo</generator>
    <image>
      <url>https://ff66ccff.github.io/avatar.jpg</url>
      <title>ff66ccff</title>
      <link>https://ff66ccff.github.io/</link>
    </image>
    <atom:link href="https://ff66ccff.github.io/rss2.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>CCF-CSP 算法板子</title>
      <link>https://ff66ccff.github.io/2026/09/12/CCF-CSP%E7%AE%97%E6%B3%95%E6%9D%BF%E5%AD%90/</link>
      <description>
        <![CDATA[<h2 id="第-00-章-速查索引-·-复杂度预算与数据类型"><a href="#第-00-章-速查索引-·-复杂度预算与数据类型" class="headerlink" title="第 00 章 速查索引 · 复杂度预算与数据类型"></a>第 00 章 速查索引 ·]]>
      </description>
      <author>ff66ccff</author>
      <category domain="https://ff66ccff.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      <category domain="https://ff66ccff.github.io/tags/CCF-CSP/">CCF-CSP</category>
      <category domain="https://ff66ccff.github.io/tags/%E7%AE%97%E6%B3%95/">算法</category>
      <category domain="https://ff66ccff.github.io/tags/C/">C++</category>
      <pubDate>Sat, 12 Sep 2026 00:45:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="第-00-章-速查索引-·-复杂度预算与数据类型"><a href="#第-00-章-速查索引-·-复杂度预算与数据类型" class="headerlink" title="第 00 章 速查索引 · 复杂度预算与数据类型"></a>第 00 章 速查索引 · 复杂度预算与数据类型</h2><blockquote><p>本章是”翻手册前的第一站”：拿到题目先看数据范围，用 0.2 的表反推算法；写代码时用 0.3 的表选类型；不确定 STL 会不会超时时查 0.4。</p></blockquote><h3 id="0-1-本手册使用方式"><a href="#0-1-本手册使用方式" class="headerlink" title="0.1 本手册使用方式"></a>0.1 本手册使用方式</h3><ul><li><code>CCF-CSP算法板子.md</code>：可搜索、可复制的源文件，平时在电脑上 <code>Ctrl+F</code> 查。</li><li><code>CCF-CSP算法板子.pdf</code>：三栏高密度排版，A4 共 <strong>23 页</strong>（双面打印 12 张纸），页脚有页码，目录带全书页码，适合考前纸质速览。</li><li>所有代码为 <strong>C++17</strong>，编译命令统一 <code>g++ -O2 -std=c++17 -o a a.cpp</code>。</li><li>代码块均为”板子风格”片段：全局量（<code>n</code>、<code>a[]</code>、<code>g[]</code> 等）默认已在外部声明，直接抄用即可。</li><li><code>⚠</code> 标注的是<strong>真实踩过的坑</strong>，考前重点看这些。</li><li>排版密度可调：<code>build/</code> 目录保留了完整生成链，<code>CSP_COLS=2 CSP_BODY=7.6 CSP_CODE=6.9 python3 build_html.py …</code> 可重新生成更宽松（更好读、页数更多）的版本，详见 <code>build/README.md</code>。</li></ul><h3 id="0-2-数据范围-→-算法复杂度反查表"><a href="#0-2-数据范围-→-算法复杂度反查表" class="headerlink" title="0.2 数据范围 → 算法复杂度反查表"></a>0.2 数据范围 → 算法复杂度反查表</h3><p>拿到题先看 n 的上限，倒推可以写多复杂的算法。按 <strong>1 秒 ≈ 1e8 次简单运算</strong>（C++ <code>-O2</code>，保守估计）计算。</p><table><thead><tr><th>n 的范围</th><th>可承受复杂度</th><th>典型算法</th></tr></thead><tbody><tr><td>n ≤ 10</td><td>O(n!)</td><td>全排列暴力、next_permutation</td></tr><tr><td>n ≤ 20</td><td>O(2^n)、O(2^n · n)</td><td>状压 DP、子集枚举、DFS 暴搜</td></tr><tr><td>n ≤ 40</td><td>O(2^(n&#x2F;2))、O(3^(n&#x2F;2))</td><td>折半枚举（meet in the middle）</td></tr><tr><td>n ≤ 100</td><td>O(n^4) 勉强，O(n^3) 稳</td><td>Floyd、区间 DP、高斯消元、矩阵乘</td></tr><tr><td>n ≤ 500</td><td>O(n^3)</td><td>同上（注意常数，可用 bitset 压位）</td></tr><tr><td>n ≤ 5000</td><td>O(n^2)</td><td>朴素 DP、朴素 Dijkstra、二维前缀和</td></tr><tr><td>n ≤ 1e5</td><td>O(n log n)、O(n sqrt n)</td><td>排序、线段树、树状数组、堆优化 Dijkstra、莫队</td></tr><tr><td>n ≤ 5e5</td><td>O(n log n)（常数要小）</td><td>同上，避免 map&#x2F;set，改用手写或离散化</td></tr><tr><td>n ≤ 1e6</td><td>O(n)、O(n log n) 常数小</td><td>前缀和、双指针、线性筛、KMP、快读</td></tr><tr><td>n ≤ 1e7</td><td>O(n) 常数极小</td><td>只用数组扫描 + fread 快读，慎用 STL</td></tr><tr><td>n ≤ 1e9</td><td>O(log n)、O(sqrt n)</td><td>快速幂、数位 DP、整除分块、矩阵快速幂</td></tr><tr><td>n ≤ 1e18</td><td>O(log n)、O(log^2 n)</td><td>快速幂、exgcd、Miller-Rabin、BSGS</td></tr></tbody></table><blockquote><p>⚠️ 这是”能跑完”的<strong>下界而非目标</strong>：n ≤ 1e5 时写 O(n^2) 必 TLE。反过来 n ≤ 5000 时 O(n^2) 是送分，不要为了炫技写 O(n log n)。</p></blockquote><h3 id="0-3-基本数据类型与范围"><a href="#0-3-基本数据类型与范围" class="headerlink" title="0.3 基本数据类型与范围"></a>0.3 基本数据类型与范围</h3><table><thead><tr><th>类型</th><th>字节</th><th>范围 &#x2F; 精度</th></tr></thead><tbody><tr><td><code>int</code></td><td>4</td><td>−2 147 483 648 ~ 2 147 483 647（约 ±2.1e9）</td></tr><tr><td><code>unsigned int</code></td><td>4</td><td>0 ~ 4 294 967 295（约 4.3e9）</td></tr><tr><td><code>long long</code></td><td>8</td><td>−9.22e18 ~ 9.22e18（约 ±9.2e18，即 2^63−1）</td></tr><tr><td><code>unsigned long long</code></td><td>8</td><td>0 ~ 1.8e19（2^64−1）</td></tr><tr><td><code>__int128</code></td><td>16</td><td>约 ±1.7e38（GCC 支持，无 cin&#x2F;cout 重载）</td></tr><tr><td><code>float</code></td><td>4</td><td>有效 6~7 位十进制</td></tr><tr><td><code>double</code></td><td>8</td><td>有效 15~16 位十进制，最大约 1.8e308</td></tr><tr><td><code>long double</code></td><td>16</td><td>有效 18~19 位十进制</td></tr></tbody></table><p><strong>溢出速判</strong>：<code>a * b</code> 用 long long 时，只要 <code>a, b ≤ 3e9</code> 就安全（3e9 × 3e9 &#x3D; 9e18 &lt; 9.22e18）。常见规模下 <code>1e5 × 1e5 × 1e5 = 1e15</code> 安全；<code>1e9 × 1e9 = 1e18</code> 安全；<code>1e9 × 1e9 × 1e9</code> <strong>溢出</strong>，必须先取模。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 加法溢出判定 (a, b &gt;= 0)</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">addOverflow</span><span class="params">(ll a, ll b)</span> </span>&#123; <span class="keyword">return</span> a &gt; LLONG_MAX - b; &#125;</span><br><span class="line"><span class="comment">// 乘法安全写法: 先转 long double 估商, 或直接用 __int128</span></span><br><span class="line"><span class="function">ll <span class="title">mul</span><span class="params">(ll a, ll b, ll m)</span> </span>&#123; <span class="keyword">return</span> (ll)((__int128)a * b % m); &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="0-4-STL-复杂度与性能陷阱"><a href="#0-4-STL-复杂度与性能陷阱" class="headerlink" title="0.4 STL 复杂度与性能陷阱"></a>0.4 STL 复杂度与性能陷阱</h3><table><thead><tr><th>容器 &#x2F; 操作</th><th>复杂度</th><th>备注</th></tr></thead><tbody><tr><td><code>vector::push_back</code></td><td>均摊 O(1)</td><td>预留 <code>reserve</code> 可避免扩容拷贝</td></tr><tr><td><code>vector</code> 中间 <code>insert/erase</code></td><td>O(n)</td><td>频繁中间插入改用 <code>list</code>&#x2F;<code>deque</code> 或换思路</td></tr><tr><td><code>deque</code> 两端插入删除</td><td>O(1)</td><td>随机访问 O(1) 但常数大于 vector</td></tr><tr><td><code>priority_queue</code> push&#x2F;pop</td><td>O(log n)</td><td>默认大根堆，小根堆用 <code>greater&lt;&gt;</code></td></tr><tr><td><code>set/map</code> 插入查找删除</td><td>O(log n)</td><td>常数约为手写平衡树的 3~5 倍</td></tr><tr><td><code>unordered_map/set</code></td><td>平均 O(1)，<strong>最坏 O(n)</strong></td><td>可被构造数据卡成 O(n)，CSP 中慎用</td></tr><tr><td><code>sort</code></td><td>O(n log n)</td><td>内省排序，最坏也 O(n log n)</td></tr><tr><td><code>nth_element</code></td><td>平均 O(n)</td><td>求第 k 小且不需全序时用它</td></tr><tr><td><code>lower_bound</code>（数组&#x2F;vector）</td><td>O(log n)</td><td>必须已排序；对 <code>set/map</code> 要用成员函数版</td></tr><tr><td><code>bitset</code> 位运算</td><td>O(n &#x2F; 64)</td><td>加速可达 64 倍，见 06 章</td></tr></tbody></table><blockquote><p>⚠️ <code>unordered_map</code> 在 CSP 中曾被卡（如 2021 年某题用 <code>unordered_map</code> 的选手大面积 TLE）。需要哈希表时优先手写哈希或改用 <code>map</code>。</p></blockquote><blockquote><p>⚠️ <code>endl</code> 会强制刷新缓冲区，<code>cout &lt;&lt; endl</code> 在循环里输出 1e5 次可慢 10 倍以上。统一用 <code>&#39;\n&#39;</code>。</p></blockquote><h3 id="0-5-模运算与常量约定"><a href="#0-5-模运算与常量约定" class="headerlink" title="0.5 模运算与常量约定"></a>0.5 模运算与常量约定</h3><table><thead><tr><th>常量</th><th>值</th><th>用途</th></tr></thead><tbody><tr><td>模数 1</td><td>1 000 000 007 &#x3D; 1e9+7</td><td>最常用，是质数</td></tr><tr><td>模数 2</td><td>998 244 353 &#x3D; 119·2^23+1</td><td>NTT 友好，是质数</td></tr><tr><td><code>INF</code></td><td>0x3f3f3f3f &#x3D; 1 061 109 567</td><td>约 1.06e9，<strong>两个相加不溢出 int</strong></td></tr><tr><td><code>LINF</code></td><td>0x3f3f3f3f3f3f3f3f</td><td>约 4.55e18，可安全相加</td></tr><tr><td><code>EPS</code></td><td>1e-8</td><td>浮点比较阈值</td></tr></tbody></table><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 负数取模: C++ 中 (-7) % 3 == -1, 不是 2</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">mod</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> m)</span> </span>&#123; x %= m; <span class="keyword">return</span> x &lt; <span class="number">0</span> ? x + m : x; &#125;</span><br><span class="line"><span class="comment">// 浮点比较</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sgn</span><span class="params">(<span class="type">double</span> x)</span> </span>&#123; <span class="keyword">return</span> x &lt; -EPS ? <span class="number">-1</span> : (x &gt; EPS ? <span class="number">1</span> : <span class="number">0</span>); &#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">eq</span><span class="params">(<span class="type">double</span> a, <span class="type">double</span> b)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">fabs</span>(a - b) &lt; EPS; &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>INF</code> 用 <code>0x3f3f3f3f</code> 而不是 <code>1e9</code>：<code>INF + INF = 2.12e9</code> 已经超过 int 上限会溢出成负数；<code>0x3f3f3f3f + 0x3f3f3f3f = 0x7e7e7e7e</code> 仍在 int 范围内，是竞赛圈的标准选择。</p></blockquote><h2 id="第-01-章-CSP-基础与常用技巧"><a href="#第-01-章-CSP-基础与常用技巧" class="headerlink" title="第 01 章 CSP 基础与常用技巧"></a>第 01 章 CSP 基础与常用技巧</h2><p>本章是 CCF-CSP 认证（上机 4 小时 5 题）的通用底座。代码为 C++17，统一编译：<code>g++ -O2 -std=c++17 -o a a.cpp</code>。<br>文中代码块均已在 g++ 11.4 &#x2F; C++17 实测编译通过；二分、前缀和差分、单调队列、离散化、贪心、高精度均已与暴力或 <code>__int128</code> 随机对拍验证。</p><h3 id="1-考场模板骨架"><a href="#1-考场模板骨架" class="headerlink" title="1. 考场模板骨架"></a>1. 考场模板骨架</h3><h4 id="1-1-万能头、宏与常量"><a href="#1-1-万能头、宏与常量" class="headerlink" title="1.1 万能头、宏与常量"></a>1.1 万能头、宏与常量</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span>          <span class="comment">// GCC 万能头; 非 GCC 需逐个 include</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">using</span> ll = <span class="type">long</span> <span class="type">long</span>;</span><br><span class="line"><span class="keyword">using</span> pii = pair&lt;<span class="type">int</span>, <span class="type">int</span>&gt;;</span><br><span class="line"><span class="meta">#<span class="keyword">define</span> all(x) (x).begin(), (x).end()</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> sz(x)  (int)(x).size()</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> pb     push_back</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> fi     first</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> se     second</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> rep(i, a, b) for (int i = (a); i &lt; (b); ++i)</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> INF = <span class="number">0x3f3f3f3f</span>;              <span class="comment">// 10^9 级, 两倍不溢出 int</span></span><br><span class="line"><span class="type">const</span> ll  LINF = <span class="number">0x3f3f3f3f3f3f3f3fLL</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> MOD = <span class="number">1000000007</span>;              <span class="comment">// 1e9+7</span></span><br><span class="line"><span class="type">const</span> <span class="type">double</span> EPS = <span class="number">1e-8</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> dx4[] = &#123;<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">-1</span>&#125;, dy4[] = &#123;<span class="number">1</span>, <span class="number">-1</span>, <span class="number">0</span>, <span class="number">0</span>&#125;;   <span class="comment">// 四方向; 八方向补对角</span></span><br><span class="line"><span class="function">ll <span class="title">qpow</span><span class="params">(ll a, ll b, ll m = MOD)</span> </span>&#123;        <span class="comment">// 快速幂 O(log n)</span></span><br><span class="line">    ll r = <span class="number">1</span> % m; a %= m; <span class="keyword">if</span> (a &lt; <span class="number">0</span>) a += m;</span><br><span class="line">    <span class="keyword">for</span> (; b; b &gt;&gt;= <span class="number">1</span>, a = a * a % m) <span class="keyword">if</span> (b &amp; <span class="number">1</span>) r = r * a % m;</span><br><span class="line">    <span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">gcd</span><span class="params">(ll a, ll b)</span> </span>&#123; <span class="keyword">return</span> b ? <span class="built_in">gcd</span>(b, a % b) : a; &#125;   <span class="comment">// O(log n)</span></span><br><span class="line"><span class="function">ll <span class="title">lcm</span><span class="params">(ll a, ll b)</span> </span>&#123; <span class="keyword">return</span> a / <span class="built_in">gcd</span>(a, b) * b; &#125;        <span class="comment">// 先除后乘防溢出</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>多测骨架（写在 <code>main</code> 里）：<code>int T; cin &gt;&gt; T; while (T--) solve();</code></p><blockquote><p>⚠️ 不要写 <code>#define int long long</code>：破坏 <code>main</code> 返回类型、令 <code>printf(&quot;%d&quot;)</code> 全部 UB、常数翻倍。需要 64 位就显式写 <code>ll</code>。</p></blockquote><h4 id="1-2-快读快写（fread-整块读入）"><a href="#1-2-快读快写（fread-整块读入）" class="headerlink" title="1.2 快读快写（fread 整块读入）"></a>1.2 快读快写（fread 整块读入）</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 快读: 比 cin/scanf 快 3~5 倍, 适合 10^6 级输入. 依赖 &lt;cstdio&gt;</span></span><br><span class="line"><span class="keyword">namespace</span> io &#123;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> BUF = <span class="number">1</span> &lt;&lt; <span class="number">20</span>;</span><br><span class="line"><span class="type">char</span> buf[BUF]; <span class="type">int</span> len = <span class="number">0</span>, pos = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">char</span> <span class="title">gc</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (pos == len) &#123; len = (<span class="type">int</span>)<span class="built_in">fread</span>(buf, <span class="number">1</span>, BUF, stdin); pos = <span class="number">0</span>; <span class="keyword">if</span> (len &lt;= <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>; &#125; <span class="keyword">return</span> buf[pos++]; &#125;</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">bool</span> <span class="title">readInt</span><span class="params">(<span class="type">int</span> &amp;x)</span> </span>&#123;            <span class="comment">// 支持负号; 读到 EOF 返回 false</span></span><br><span class="line">    <span class="type">char</span> c = <span class="built_in">gc</span>();</span><br><span class="line">    <span class="keyword">while</span> (c &amp;&amp; (c &lt; <span class="string">&#x27;0&#x27;</span> || c &gt; <span class="string">&#x27;9&#x27;</span>) &amp;&amp; c != <span class="string">&#x27;-&#x27;</span>) c = <span class="built_in">gc</span>();</span><br><span class="line">    <span class="keyword">if</span> (!c) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="type">bool</span> neg = <span class="literal">false</span>; <span class="keyword">if</span> (c == <span class="string">&#x27;-&#x27;</span>) &#123; neg = <span class="literal">true</span>; c = <span class="built_in">gc</span>(); &#125;</span><br><span class="line">    <span class="keyword">for</span> (x = <span class="number">0</span>; c &gt;= <span class="string">&#x27;0&#x27;</span> &amp;&amp; c &lt;= <span class="string">&#x27;9&#x27;</span>; c = <span class="built_in">gc</span>()) x = x * <span class="number">10</span> + (c - <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line">    <span class="keyword">if</span> (neg) x = -x;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// readLL 与 readInt 完全同构, 仅把 int 换成 ll (长整数必须走这个)</span></span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">void</span> <span class="title">writeLL</span><span class="params">(ll x)</span> </span>&#123;              <span class="comment">// 快写: 比 printf 快约 2 倍</span></span><br><span class="line">    <span class="keyword">if</span> (x &lt; <span class="number">0</span>) &#123; <span class="built_in">putchar</span>(<span class="string">&#x27;-&#x27;</span>); x = -x; &#125;</span><br><span class="line">    <span class="type">char</span> stk[<span class="number">24</span>]; <span class="type">int</span> top = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">do</span> &#123; stk[top++] = <span class="built_in">char</span>(<span class="string">&#x27;0&#x27;</span> + x % <span class="number">10</span>); x /= <span class="number">10</span>; &#125; <span class="keyword">while</span> (x);     <span class="keyword">while</span> (top) <span class="built_in">putchar</span>(stk[--top]); &#125;</span><br><span class="line">&#125;  <span class="comment">// namespace io</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 快读快写<strong>不能与 <code>cin</code>&#x2F;<code>scanf</code> 混用同一个流</strong>，否则读入错位。若要用 <code>cin</code>，加 <code>ios::sync_with_stdio(false); cin.tie(nullptr);</code>，此时<strong>不能再混用</strong> <code>scanf</code>&#x2F;<code>printf</code>。</p></blockquote><h4 id="1-3-int128-读写（GCC-扩展）"><a href="#1-3-int128-读写（GCC-扩展）" class="headerlink" title="1.3 __int128 读写（GCC 扩展）"></a>1.3 __int128 读写（GCC 扩展）</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// __int128: 约 ±1.7e38. GCC/Clang 支持, MSVC 不支持; 无 IO 重载必须手写</span></span><br><span class="line"><span class="function">__int128 <span class="title">readI128</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    __int128 x = <span class="number">0</span>; <span class="type">int</span> sign = <span class="number">1</span>; <span class="type">char</span> c = <span class="built_in">getchar</span>();</span><br><span class="line">    <span class="keyword">while</span> (c &lt; <span class="string">&#x27;0&#x27;</span> || c &gt; <span class="string">&#x27;9&#x27;</span>) &#123; <span class="keyword">if</span> (c == <span class="string">&#x27;-&#x27;</span>) sign = <span class="number">-1</span>; c = <span class="built_in">getchar</span>(); &#125;</span><br><span class="line">    <span class="keyword">for</span> (; c &gt;= <span class="string">&#x27;0&#x27;</span> &amp;&amp; c &lt;= <span class="string">&#x27;9&#x27;</span>; c = <span class="built_in">getchar</span>()) x = x * <span class="number">10</span> + (c - <span class="string">&#x27;0&#x27;</span>);</span><br><span class="line">    <span class="keyword">return</span> x * sign;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">writeI128</span><span class="params">(__int128 x)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (x == <span class="number">0</span>) &#123; <span class="built_in">putchar</span>(<span class="string">&#x27;0&#x27;</span>); <span class="keyword">return</span>; &#125;</span><br><span class="line">    <span class="keyword">if</span> (x &lt; <span class="number">0</span>) &#123; <span class="built_in">putchar</span>(<span class="string">&#x27;-&#x27;</span>); x = -x; &#125;</span><br><span class="line">    <span class="type">char</span> stk[<span class="number">45</span>]; <span class="type">int</span> top = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (x &gt; <span class="number">0</span>) &#123; stk[top++] = <span class="built_in">char</span>(<span class="string">&#x27;0&#x27;</span> + (<span class="type">int</span>)(x % <span class="number">10</span>)); x /= <span class="number">10</span>; &#125;     <span class="keyword">while</span> (top) <span class="built_in">putchar</span>(stk[--top]); &#125;</span><br><span class="line"><span class="function">string <span class="title">i128str</span><span class="params">(__int128 v)</span> </span>&#123;             <span class="comment">// std::to_string 不支持 __int128</span></span><br><span class="line">    <span class="keyword">if</span> (v == <span class="number">0</span>) <span class="keyword">return</span> <span class="string">&quot;0&quot;</span>;</span><br><span class="line">    <span class="type">bool</span> ng = v &lt; <span class="number">0</span>; <span class="keyword">if</span> (ng) v = -v;</span><br><span class="line">    string s; <span class="keyword">while</span> (v &gt; <span class="number">0</span>) &#123; s += <span class="built_in">char</span>(<span class="string">&#x27;0&#x27;</span> + (<span class="type">int</span>)(v % <span class="number">10</span>)); v /= <span class="number">10</span>; &#125;</span><br><span class="line">    <span class="keyword">if</span> (ng) s += <span class="string">&#x27;-&#x27;</span>;</span><br><span class="line">    <span class="built_in">reverse</span>(s.<span class="built_in">begin</span>(), s.<span class="built_in">end</span>()); <span class="keyword">return</span> s;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 乘法防溢出: __int128 c = (__int128)a * b % MOD;  再转回 ll</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>std::to_string</code> 与 <code>cout &lt;&lt;</code> 都<strong>不支持</strong> <code>__int128</code>（前者报 ambiguous，后者编译失败），必须用手写函数。</p></blockquote><h4 id="1-4-编译与调试命令"><a href="#1-4-编译与调试命令" class="headerlink" title="1.4 编译与调试命令"></a>1.4 编译与调试命令</h4><ul><li>提交：<code>g++ -O2 -std=c++17 -o a a.cpp</code>；查错加 <code>-Wall -Wextra</code>；查越界溢出加 <code>-g -fsanitize=address,undefined</code>（慢 5~10 倍，仅调试）。</li><li>放开递归栈：<code>ulimit -s unlimited</code>（Linux 默认 8 MB）；测时限：<code>time ./a &lt; big.txt</code>。</li><li>对拍：<code>while true; do ./gen &gt; in; ./a &lt; in &gt; o1; ./b &lt; in &gt; o2; diff o1 o2 || break; done</code></li></ul><h3 id="2-二分"><a href="#2-二分" class="headerlink" title="2. 二分"></a>2. 二分</h3><h4 id="2-1-整数二分三种写法"><a href="#2-1-整数二分三种写法" class="headerlink" title="2.1 整数二分三种写法"></a>2.1 整数二分三种写法</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">check</span><span class="params">(<span class="type">int</span> x)</span></span>;   <span class="comment">// 要求: 在候选区间上单调 (false...false true...true)</span></span><br><span class="line"><span class="comment">// 【写法一】闭区间 [lo,hi], 最小可行值; mid 下取整</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">lowerBound</span><span class="params">(<span class="type">int</span> lo, <span class="type">int</span> hi)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (lo &lt; hi) &#123;</span><br><span class="line">        <span class="type">int</span> mid = lo + (hi - lo) / <span class="number">2</span>;      <span class="comment">// 防 (lo+hi) 溢出</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">check</span>(mid)) hi = mid; <span class="keyword">else</span> lo = mid + <span class="number">1</span>;</span><br><span class="line">    &#125; <span class="keyword">return</span> lo; &#125;</span><br><span class="line"><span class="comment">// 【写法二】闭区间 [lo,hi], 最大可行值; mid 上取整</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">upperBound</span><span class="params">(<span class="type">int</span> lo, <span class="type">int</span> hi)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (lo &lt; hi) &#123;</span><br><span class="line">        <span class="type">int</span> mid = lo + (hi - lo + <span class="number">1</span>) / <span class="number">2</span>;  <span class="comment">// 上取整, 否则死循环</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">check</span>(mid)) lo = mid; <span class="keyword">else</span> hi = mid - <span class="number">1</span>;</span><br><span class="line">    &#125; <span class="keyword">return</span> lo; &#125;</span><br><span class="line"><span class="comment">// 【写法三】左闭右开, 维护 check(l)=false 且 check(r)=true, 返回 r (最小可行值)</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">solveOpen</span><span class="params">(<span class="type">int</span> lo, <span class="type">int</span> hi)</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> l = lo - <span class="number">1</span>, r = hi + <span class="number">1</span>;            <span class="comment">// 两端必须各外扩一格!</span></span><br><span class="line">    <span class="keyword">while</span> (l + <span class="number">1</span> &lt; r) &#123; <span class="type">int</span> mid = l + (r - l) / <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">check</span>(mid)) r = mid; <span class="keyword">else</span> l = mid; &#125; <span class="keyword">return</span> r; &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 写法三的前提是<strong>初始两端已满足不变式</strong>。若直接写 <code>l = lo</code>，当答案恰好是 <code>lo</code> 时会返回 <code>lo + 1</code>（已实测复现），所以必须取 <code>l = lo - 1, r = hi + 1</code>。求最大可行值则镜像：维护 <code>check(l)=true, check(r)=false</code>，返回 <code>l</code>。<br>⚠️ 死循环的唯二原因：<strong>mid 取整方向与收缩方向不匹配</strong>。口诀：<code>hi = mid</code> 配下取整，<code>lo = mid</code> 配上取整。</p></blockquote><h4 id="2-2-STL-二分与浮点二分"><a href="#2-2-STL-二分与浮点二分" class="headerlink" title="2.2 STL 二分与浮点二分"></a>2.2 STL 二分与浮点二分</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// lower_bound: 首个 &gt;= x; upper_bound: 首个 &gt; x. 均 O(log n), 要求已排序</span></span><br><span class="line"><span class="type">int</span> p1 = (<span class="type">int</span>)(<span class="built_in">lower_bound</span>(a, a + n, x) - a);           <span class="comment">// [a, a+n)</span></span><br><span class="line"><span class="type">int</span> p3 = (<span class="type">int</span>)(<span class="built_in">lower_bound</span>(a, a + n, x, <span class="built_in">greater</span>&lt;<span class="type">int</span>&gt;()) - a);   <span class="comment">// 下降序列</span></span><br><span class="line"><span class="comment">// vector 同理; x 出现次数 = upper_bound - lower_bound</span></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">f</span><span class="params">(<span class="type">double</span> x)</span></span>;                                     <span class="comment">// 单调函数</span></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">solveDouble</span><span class="params">(<span class="type">double</span> lo, <span class="type">double</span> hi)</span> </span>&#123;              <span class="comment">// 浮点二分</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> it = <span class="number">0</span>; it &lt; <span class="number">100</span>; ++it) &#123;                  <span class="comment">// 100 轮精度约 1e-30</span></span><br><span class="line">        <span class="type">double</span> mid = (lo + hi) / <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">f</span>(mid) &gt;= <span class="number">0</span>) hi = mid; <span class="keyword">else</span> lo = mid;</span><br><span class="line">    &#125; <span class="keyword">return</span> lo; &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 浮点二分**不要写 <code>while (r - l &gt; 1e-8)</code>**：lo&#x2F;hi 量级到 1e9 时 double 已无 1e-8 分辨率，循环永不退出。</p></blockquote><h4 id="2-3-二分答案（最大值最小-最小值最大）"><a href="#2-3-二分答案（最大值最小-最小值最大）" class="headerlink" title="2.3 二分答案（最大值最小 &#x2F; 最小值最大）"></a>2.3 二分答案（最大值最小 &#x2F; 最小值最大）</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 二分答案 = 二分枚举答案 + 贪心/模拟判定. 前提: 答案单调 + check 可 O(n) 实现</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">check</span><span class="params">(ll x)</span></span>;</span><br><span class="line"><span class="function">ll <span class="title">solveMaxMin</span><span class="params">(ll lo, ll hi)</span> </span>&#123;           <span class="comment">// 最大值最小: check 随 x 增大 false -&gt; true</span></span><br><span class="line">    <span class="keyword">while</span> (lo &lt; hi) &#123; ll mid = lo + (hi - lo) / <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">check</span>(mid)) hi = mid; <span class="keyword">else</span> lo = mid + <span class="number">1</span>; &#125; <span class="keyword">return</span> lo; &#125;</span><br><span class="line"><span class="function">ll <span class="title">solveMinMax</span><span class="params">(ll lo, ll hi)</span> </span>&#123;           <span class="comment">// 最小值最大: check 随 x 增大 true -&gt; false</span></span><br><span class="line">    <span class="keyword">while</span> (lo &lt; hi) &#123; ll mid = lo + (hi - lo + <span class="number">1</span>) / <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">check</span>(mid)) lo = mid; <span class="keyword">else</span> hi = mid - <span class="number">1</span>; &#125; <span class="keyword">return</span> lo; &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 完整可编译示例: 洛谷 P1873 砍树 (求最大的 H 使 sum(a[i]-H | a[i]&gt;H) &gt;= M)</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">typedef</span> <span class="type">long</span> <span class="type">long</span> ll;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">1000005</span>;</span><br><span class="line"><span class="type">int</span> n, a[N]; ll m;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">check</span><span class="params">(ll k)</span> </span>&#123;                       <span class="comment">// 锯片高 k 时能否拿到 &gt;= m 木材</span></span><br><span class="line">    ll sum = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) <span class="keyword">if</span> (a[i] &gt; k &amp;&amp; (sum += a[i] - k) &gt;= m) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">return</span> sum &gt;= m;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%d%lld&quot;</span>, &amp;n, &amp;m);</span><br><span class="line">    ll lo = <span class="number">0</span>, hi = <span class="number">0</span>;                   <span class="comment">// 答案区间 [0, max a]</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) &#123; <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>, &amp;a[i]); hi = <span class="built_in">max</span>(hi, (ll)a[i]); &#125;</span><br><span class="line">    <span class="keyword">while</span> (lo &lt; hi) &#123; ll mid = lo + (hi - lo + <span class="number">1</span>) / <span class="number">2</span>;   <span class="comment">// 最大可行值 -&gt; 上取整</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">check</span>(mid)) lo = mid; <span class="keyword">else</span> hi = mid - <span class="number">1</span>; &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%lld\n&quot;</span>, lo); <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 二分答案三要素：① 上下界要够宽（宁可 <code>lo=0, hi=1e18</code>）；② <code>check</code> 内累加量必须 <code>ll</code>（1e5 个 1e9 相加爆 int）；③ 先想清楚求”最大可行”还是”最小可行”，选错 mid 取整方向就是死循环。</p></blockquote><h3 id="3-前缀和与差分"><a href="#3-前缀和与差分" class="headerlink" title="3. 前缀和与差分"></a>3. 前缀和与差分</h3><h4 id="3-1-一维前缀和与差分"><a href="#3-1-一维前缀和与差分" class="headerlink" title="3.1 一维前缀和与差分"></a>3.1 一维前缀和与差分</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">ll s[N], d[N];                          <span class="comment">// 全局数组默认清零; d 需开到 n+2</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">build1D</span><span class="params">()</span> </span>&#123; <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) s[i] = s[i - <span class="number">1</span>] + a[i]; &#125;   <span class="comment">// O(n) 预处理</span></span><br><span class="line"><span class="function">ll <span class="title">rangeSum</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> r)</span> </span>&#123; <span class="keyword">return</span> s[r] - s[l - <span class="number">1</span>]; &#125;                     <span class="comment">// O(1) 查询</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">addRange</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> r, ll v)</span> </span>&#123; d[l] += v; d[r + <span class="number">1</span>] -= v; &#125;           <span class="comment">// O(1) 区间加</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">rebuild1D</span><span class="params">()</span> </span>&#123; <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) a[i] = a[i - <span class="number">1</span>] + d[i]; &#125; <span class="comment">// O(n) 还原</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 差分数组必须开到 <code>n + 2</code>（<code>d[r+1]</code> 在 <code>r == n</code> 时会写到 <code>n+1</code>）。差分<strong>不支持边改边查</strong>，所有区间加完成后才能 <code>rebuild</code>。</p></blockquote><h4 id="3-2-二维前缀和与二维差分"><a href="#3-2-二维前缀和与二维差分" class="headerlink" title="3.2 二维前缀和与二维差分"></a>3.2 二维前缀和与二维差分</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">ll s[N][N], d[N][N];                    <span class="comment">// d 需开到 n+2 行、m+2 列</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">build2D</span><span class="params">()</span> </span>&#123;                        <span class="comment">// O(nm) 预处理, O(1) 矩形查询</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= m; ++j)</span><br><span class="line">        s[i][j] = s[i<span class="number">-1</span>][j] + s[i][j<span class="number">-1</span>] - s[i<span class="number">-1</span>][j<span class="number">-1</span>] + a[i][j];</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">rectSum</span><span class="params">(<span class="type">int</span> x1, <span class="type">int</span> y1, <span class="type">int</span> x2, <span class="type">int</span> y2)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> s[x2][y2] - s[x1<span class="number">-1</span>][y2] - s[x2][y1<span class="number">-1</span>] + s[x1<span class="number">-1</span>][y1<span class="number">-1</span>];</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">addRect</span><span class="params">(<span class="type">int</span> x1, <span class="type">int</span> y1, <span class="type">int</span> x2, <span class="type">int</span> y2, ll v)</span> </span>&#123;   <span class="comment">// O(1) 矩形加: 四角打标记</span></span><br><span class="line">    d[x1][y1] += v;      d[x2 + <span class="number">1</span>][y1] -= v;</span><br><span class="line">    d[x1][y2 + <span class="number">1</span>] -= v;  d[x2 + <span class="number">1</span>][y2 + <span class="number">1</span>] += v;       <span class="comment">// 容斥: 多减的加回来</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">seed2D</span><span class="params">()</span> </span>&#123;                         <span class="comment">// 用原数组 a 初始化差分数组 d</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= m; ++j)</span><br><span class="line">        d[i][j] = a[i][j] - a[i<span class="number">-1</span>][j] - a[i][j<span class="number">-1</span>] + a[i<span class="number">-1</span>][j<span class="number">-1</span>];</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">rebuild2D</span><span class="params">()</span> </span>&#123;                      <span class="comment">// 还原: 一遍二维前缀和</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= m; ++j)</span><br><span class="line">        d[i][j] += d[i<span class="number">-1</span>][j] + d[i][j<span class="number">-1</span>] - d[i<span class="number">-1</span>][j<span class="number">-1</span>];</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ **差分数组初值是 0，只表示”增量”**。直接从 0 开始打标记再 <code>rebuild2D()</code>，得到的是增量矩阵，必须再加原数组 <code>a</code>；否则用 <code>seed2D()</code> 先把 <code>a</code> 转成差分。这是二维差分最常见的错（已实测复现）。</p></blockquote><h3 id="4-双指针-滑动窗口-尺取法"><a href="#4-双指针-滑动窗口-尺取法" class="headerlink" title="4. 双指针 &#x2F; 滑动窗口 &#x2F; 尺取法"></a>4. 双指针 &#x2F; 滑动窗口 &#x2F; 尺取法</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> i)</span></span>; <span class="function"><span class="type">void</span> <span class="title">del</span><span class="params">(<span class="type">int</span> i)</span></span>; <span class="function"><span class="type">bool</span> <span class="title">ok</span><span class="params">()</span></span>;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">shortestWindow</span><span class="params">()</span> </span>&#123;                  <span class="comment">// 最短的满足条件的连续区间. O(n)</span></span><br><span class="line">    <span class="type">int</span> l = <span class="number">1</span>, ans = INF;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> r = <span class="number">1</span>; r &lt;= n; ++r) &#123;</span><br><span class="line">        <span class="built_in">add</span>(r);</span><br><span class="line">        <span class="keyword">while</span> (l &lt;= r &amp;&amp; <span class="built_in">ok</span>()) &#123; ans = <span class="built_in">min</span>(ans, r - l + <span class="number">1</span>); <span class="built_in">del</span>(l++); &#125;  <span class="comment">// 可行则收缩</span></span><br><span class="line">    &#125; <span class="keyword">return</span> ans; &#125;</span><br><span class="line"><span class="comment">// 求最长满足条件的区间: while 条件改成 !ok(), 更新 ans = max(ans, r - l + 1)</span></span><br><span class="line"><span class="comment">// 判定型双指针 (两有序数组求两数之和, O(n+m)): i 从 0 递增, j 从 m-1 递减,</span></span><br><span class="line"><span class="comment">// 若 a[i]+b[j] &lt; target 则 ++i, 否则 --j</span></span><br><span class="line"><span class="type">int</span> q[N];                               <span class="comment">// 单调队列求滑动窗口最值: 窗口长 k, O(n)</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">windowMax</span><span class="params">()</span> </span>&#123;                      <span class="comment">// 存下标而非值! 求最小值改比较符为 &gt;=</span></span><br><span class="line">    <span class="type">int</span> head = <span class="number">1</span>, tail = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) &#123;</span><br><span class="line">        <span class="keyword">while</span> (head &lt;= tail &amp;&amp; a[q[tail]] &lt;= a[i]) --tail;  <span class="comment">// 队尾更小 -&gt; 永无出头之日</span></span><br><span class="line">        q[++tail] = i;</span><br><span class="line">        <span class="keyword">if</span> (q[head] &lt;= i - k) ++head;                       <span class="comment">// 队头滑出 (先判越界)</span></span><br><span class="line">        <span class="keyword">if</span> (i &gt;= k) <span class="built_in">printf</span>(<span class="string">&quot;%d &quot;</span>, a[q[head]]);              <span class="comment">// 队头即窗口最大值</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 单调队列存<strong>下标</strong>而非值，否则无法判越界；越界判断必须在<strong>输出之前</strong>做；<code>q</code> 数组开 <code>n + 1</code>。</p></blockquote><h3 id="5-离散化"><a href="#5-离散化" class="headerlink" title="5. 离散化"></a>5. 离散化</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">vector&lt;<span class="type">int</span>&gt; xs;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">compress</span><span class="params">()</span> </span>&#123;                       <span class="comment">// O(n log n)</span></span><br><span class="line">    xs.<span class="built_in">assign</span>(a + <span class="number">1</span>, a + n + <span class="number">1</span>);                       <span class="comment">// 1. 拷贝</span></span><br><span class="line">    <span class="built_in">sort</span>(xs.<span class="built_in">begin</span>(), xs.<span class="built_in">end</span>());                        <span class="comment">// 2. 排序</span></span><br><span class="line">    xs.<span class="built_in">erase</span>(<span class="built_in">unique</span>(xs.<span class="built_in">begin</span>(), xs.<span class="built_in">end</span>()), xs.<span class="built_in">end</span>());  <span class="comment">// 3. 去重</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i)                       <span class="comment">// 4. 映射为 1-based 排名</span></span><br><span class="line">        a[i] = (<span class="type">int</span>)(<span class="built_in">lower_bound</span>(xs.<span class="built_in">begin</span>(), xs.<span class="built_in">end</span>(), a[i]) - xs.<span class="built_in">begin</span>()) + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 反查第 r 名的原值: xs[r - 1];  原值 x 的排名: lower_bound(...) - begin() + 1</span></span><br><span class="line"><span class="comment">// 值域大小: int len = (int)xs.size();</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>unique</code> <strong>只把重复元素移到末尾并返回新尾迭代器</strong>，不删除元素，必须配合 <code>erase</code>。映射用 <code>lower_bound</code>（首个 &gt;&#x3D; x）；若查询值不在集合内，<code>find</code> 会失败而 <code>lower_bound</code> 仍给出插入位置。</p></blockquote><h3 id="6-贪心"><a href="#6-贪心" class="headerlink" title="6. 贪心"></a>6. 贪心</h3><h4 id="6-1-区间调度-区间选点"><a href="#6-1-区间调度-区间选点" class="headerlink" title="6.1 区间调度 &#x2F; 区间选点"></a>6.1 区间调度 &#x2F; 区间选点</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Seg</span> &#123; <span class="type">int</span> l, r; &#125; seg[N];</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">cmpR</span><span class="params">(<span class="type">const</span> Seg &amp;x, <span class="type">const</span> Seg &amp;y)</span> </span>&#123; <span class="keyword">return</span> x.r &lt; y.r; &#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">maxDisjoint</span><span class="params">()</span> </span>&#123;   <span class="comment">// 【区间调度】最多两两不交的区间 (相接算不重叠). O(n log n)</span></span><br><span class="line">    <span class="built_in">sort</span>(seg + <span class="number">1</span>, seg + n + <span class="number">1</span>, cmpR);           <span class="comment">// 按右端点升序</span></span><br><span class="line">    <span class="type">int</span> cnt = <span class="number">0</span>, last = -INF;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) <span class="keyword">if</span> (seg[i].l &gt;= last) &#123; ++cnt; last = seg[i].r; &#125; <span class="keyword">return</span> cnt; &#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">minPoints</span><span class="params">()</span> </span>&#123;     <span class="comment">// 【区间选点】最少点命中所有区间. O(n log n)</span></span><br><span class="line">    <span class="built_in">sort</span>(seg + <span class="number">1</span>, seg + n + <span class="number">1</span>, cmpR);           <span class="comment">// 在右端点放点最划算</span></span><br><span class="line">    <span class="type">int</span> pts = <span class="number">0</span>, lastP = -INF;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) <span class="keyword">if</span> (seg[i].l &gt; lastP) &#123; ++pts; lastP = seg[i].r; &#125; <span class="keyword">return</span> pts; &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 两者只差一个等号：区间调度用 <code>l &gt;= last</code>（端点相接不算重叠），区间选点用 <code>l &gt; last</code>（点落在端点仍算命中）。先确认题目端点开闭。<br>⚠️ 区间调度要求 <code>l &lt; r</code>。若允许 <code>l == r</code> 的空区间，贪心可能少选（实测 <code>[9,9] [4,9] [1,10]</code> 贪心得 1，最优 2）。</p></blockquote><h4 id="6-2-Huffman-合并果子-反悔贪心-邻项交换"><a href="#6-2-Huffman-合并果子-反悔贪心-邻项交换" class="headerlink" title="6.2 Huffman &#x2F; 合并果子 &#x2F; 反悔贪心 &#x2F; 邻项交换"></a>6.2 Huffman &#x2F; 合并果子 &#x2F; 反悔贪心 &#x2F; 邻项交换</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function">ll <span class="title">huffman</span><span class="params">()</span> </span>&#123;                          <span class="comment">// 每次合并最小的两个. O(n log n)</span></span><br><span class="line">    priority_queue&lt;ll, vector&lt;ll&gt;, greater&lt;ll&gt;&gt; pq;   <span class="comment">// 第三个参数才是小根堆</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) pq.<span class="built_in">push</span>(a[i]);</span><br><span class="line">    ll cost = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (pq.<span class="built_in">size</span>() &gt; <span class="number">1</span>) &#123;</span><br><span class="line">        ll x = pq.<span class="built_in">top</span>(); pq.<span class="built_in">pop</span>(); ll y = pq.<span class="built_in">top</span>(); pq.<span class="built_in">pop</span>();</span><br><span class="line">        cost += x + y; pq.<span class="built_in">push</span>(x + y);                <span class="comment">// 合并代价</span></span><br><span class="line">    &#125; <span class="keyword">return</span> cost; &#125;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Job</span> &#123; ll d, p; &#125; job[N];</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">cmpD</span><span class="params">(<span class="type">const</span> Job &amp;x, <span class="type">const</span> Job &amp;y)</span> </span>&#123; <span class="keyword">return</span> x.d &lt; y.d; &#125;</span><br><span class="line"><span class="function">ll <span class="title">workScheduling</span><span class="params">()</span> </span>&#123;   <span class="comment">// 反悔贪心 (USACO Work Scheduling). O(n log n)</span></span><br><span class="line">    <span class="built_in">sort</span>(job + <span class="number">1</span>, job + n + <span class="number">1</span>, cmpD);                 <span class="comment">// 按截止时间升序</span></span><br><span class="line">    priority_queue&lt;ll, vector&lt;ll&gt;, greater&lt;ll&gt;&gt; pq;   <span class="comment">// 小根堆: 已选中最差的</span></span><br><span class="line">    ll ans = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) &#123;</span><br><span class="line">        pq.<span class="built_in">push</span>(job[i].p); ans += job[i].p;           <span class="comment">// 先接受</span></span><br><span class="line">        <span class="keyword">if</span> ((ll)pq.<span class="built_in">size</span>() &gt; job[i].d) &#123; ans -= pq.<span class="built_in">top</span>(); pq.<span class="built_in">pop</span>(); &#125;   <span class="comment">// 超期反悔</span></span><br><span class="line">    &#125; <span class="keyword">return</span> ans; &#125;</span><br><span class="line"><span class="comment">// 邻项交换法 (排序贪心): 设交换相邻两项不影响其他项, 比较两种顺序推出 cmp</span></span><br><span class="line"><span class="comment">// 例 (NOIP 2012 国王游戏 / 耍杂技的牛): 按 a*b 升序</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Cow</span> &#123; ll a, b; &#125; cow[N];</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">cmpCow</span><span class="params">(<span class="type">const</span> Cow &amp;x, <span class="type">const</span> Cow &amp;y)</span> </span>&#123; <span class="keyword">return</span> x.a * x.b &lt; y.a * y.b; &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 反悔贪心必须能证明”局部反悔后仍全局最优”。本题依据是「前 i 项工作最多只能做 <code>d_i</code> 项」。没有这类性质就不能反悔，要转 DP。</p></blockquote><h3 id="7-排序与去重"><a href="#7-排序与去重" class="headerlink" title="7. 排序与去重"></a>7. 排序与去重</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sortDemo</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">sort</span>(a + <span class="number">1</span>, a + n + <span class="number">1</span>);                              <span class="comment">// 升序 O(n log n)</span></span><br><span class="line">    <span class="built_in">sort</span>(a + <span class="number">1</span>, a + n + <span class="number">1</span>, <span class="built_in">greater</span>&lt;<span class="type">int</span>&gt;());              <span class="comment">// 降序</span></span><br><span class="line">    <span class="type">int</span> len = (<span class="type">int</span>)(<span class="built_in">unique</span>(a + <span class="number">1</span>, a + n + <span class="number">1</span>) - (a + <span class="number">1</span>)); <span class="comment">// 去重(先 sort), 有效元素 a[1..len]</span></span><br><span class="line">    <span class="built_in">nth_element</span>(a + <span class="number">1</span>, a + k, a + n + <span class="number">1</span>);                <span class="comment">// O(n): 第 k 小就位, 左 &lt;= 它 &lt;= 右</span></span><br><span class="line">    <span class="comment">// 第 k 大 = nth_element(a+1, a+n-k+1, a+n+1) 后的 a[n-k+1]</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span> &#123; <span class="type">int</span> x, y, z; &#125;;                            <span class="comment">// 多关键字: tie 字典序比较</span></span><br><span class="line"><span class="type">bool</span> <span class="keyword">operator</span>&lt;(<span class="type">const</span> Node &amp;A, <span class="type">const</span> Node &amp;B) &#123; <span class="keyword">return</span> <span class="built_in">tie</span>(A.x, A.y, A.z) &lt; <span class="built_in">tie</span>(B.x, B.y, B.z); &#125;</span><br><span class="line"><span class="comment">// 混合升降序: return tie(A.x, B.y) &lt; tie(B.x, A.y);   // x 升序, y 降序</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>sort</code> 的比较函数必须满足<strong>严格弱序</strong>：相等时必须返回 <code>false</code>，写成 <code>&lt;=</code> 会在 GCC 下段错误。这是最隐蔽的 RE 来源。</p></blockquote><h3 id="8-模拟题技巧"><a href="#8-模拟题技巧" class="headerlink" title="8. 模拟题技巧"></a>8. 模拟题技巧</h3><h4 id="8-1-日期推算"><a href="#8-1-日期推算" class="headerlink" title="8.1 日期推算"></a>8.1 日期推算</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isLeap</span><span class="params">(<span class="type">int</span> y)</span> </span>&#123; <span class="keyword">return</span> (y % <span class="number">4</span> == <span class="number">0</span> &amp;&amp; y % <span class="number">100</span> != <span class="number">0</span>) || y % <span class="number">400</span> == <span class="number">0</span>; &#125;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> MD[] = &#123;<span class="number">0</span>, <span class="number">31</span>, <span class="number">28</span>, <span class="number">31</span>, <span class="number">30</span>, <span class="number">31</span>, <span class="number">30</span>, <span class="number">31</span>, <span class="number">31</span>, <span class="number">30</span>, <span class="number">31</span>, <span class="number">30</span>, <span class="number">31</span>&#125;;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">daysInMonth</span><span class="params">(<span class="type">int</span> y, <span class="type">int</span> m)</span> </span>&#123; <span class="keyword">return</span> (m == <span class="number">2</span> &amp;&amp; <span class="built_in">isLeap</span>(y)) ? <span class="number">29</span> : MD[m]; &#125;</span><br><span class="line"><span class="function">ll <span class="title">daysFromCivil</span><span class="params">(ll y, <span class="type">int</span> m, <span class="type">int</span> d)</span> </span>&#123;   <span class="comment">// 日期 -&gt; 绝对天数, 1970-01-01 = 0, 相减即天数差</span></span><br><span class="line">    y -= (m &lt;= <span class="number">2</span>);</span><br><span class="line">    ll era = (y &gt;= <span class="number">0</span> ? y : y - <span class="number">399</span>) / <span class="number">400</span>;</span><br><span class="line">    ll yoe = y - era * <span class="number">400</span>;</span><br><span class="line">    ll doy = (<span class="number">153</span> * (m + (m &gt; <span class="number">2</span> ? <span class="number">-3</span> : <span class="number">9</span>)) + <span class="number">2</span>) / <span class="number">5</span> + d - <span class="number">1</span>;</span><br><span class="line">    ll doe = yoe * <span class="number">365</span> + yoe / <span class="number">4</span> - yoe / <span class="number">100</span> + doy;</span><br><span class="line">    <span class="keyword">return</span> era * <span class="number">146097</span> + doe - <span class="number">719468</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">weekday</span><span class="params">(ll y, <span class="type">int</span> m, <span class="type">int</span> d)</span> </span>&#123; <span class="keyword">return</span> (<span class="type">int</span>)((<span class="built_in">daysFromCivil</span>(y, m, d) % <span class="number">7</span> + <span class="number">10</span>) % <span class="number">7</span>); &#125; <span class="comment">// 0=周一</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>已实测：<code>1970-01-01 -&gt; 0</code>；<code>2000-01-01 -&gt; 10957</code>；<code>1900-02-28</code> 到 <code>1900-03-01</code> 差 1 天（1900 非闰年）。</p><blockquote><p>⚠️ 星期偏移量易错：1970-01-01 是<strong>周四</strong>，故取 <code>+10</code>（<code>(0+10)%7 = 3 = 周四</code>）。写成 <code>+11</code> 会整体错一天。</p></blockquote><h4 id="8-2-进制转换与大数取模"><a href="#8-2-进制转换与大数取模" class="headerlink" title="8.2 进制转换与大数取模"></a>8.2 进制转换与大数取模</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function">string <span class="title">toBase</span><span class="params">(ll x, <span class="type">int</span> B)</span> </span>&#123;             <span class="comment">// 十进制 -&gt; B 进制 (2&lt;=B&lt;=36). O(log_B x)</span></span><br><span class="line">    <span class="keyword">if</span> (x == <span class="number">0</span>) <span class="keyword">return</span> <span class="string">&quot;0&quot;</span>;</span><br><span class="line">    <span class="type">bool</span> neg = x &lt; <span class="number">0</span>; <span class="keyword">if</span> (neg) x = -x;</span><br><span class="line">    string s;</span><br><span class="line">    <span class="keyword">while</span> (x &gt; <span class="number">0</span>) &#123; <span class="type">int</span> d = (<span class="type">int</span>)(x % B); s += <span class="built_in">char</span>(d &lt; <span class="number">10</span> ? <span class="string">&#x27;0&#x27;</span> + d : <span class="string">&#x27;A&#x27;</span> + d - <span class="number">10</span>); x /= B; &#125;</span><br><span class="line">    <span class="keyword">if</span> (neg) s += <span class="string">&#x27;-&#x27;</span>;</span><br><span class="line">    <span class="built_in">reverse</span>(s.<span class="built_in">begin</span>(), s.<span class="built_in">end</span>()); <span class="keyword">return</span> s;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">fromBase</span><span class="params">(<span class="type">const</span> string &amp;s, <span class="type">int</span> B)</span> </span>&#123;    <span class="comment">// B 进制 -&gt; 十进制. O(len)</span></span><br><span class="line">    ll x = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">char</span> c : s) &#123; <span class="keyword">if</span> (c == <span class="string">&#x27;-&#x27;</span>) <span class="keyword">continue</span>;</span><br><span class="line">        x = x * B + ((c &lt;= <span class="string">&#x27;9&#x27;</span>) ? c - <span class="string">&#x27;0&#x27;</span> : (c &gt;= <span class="string">&#x27;a&#x27;</span> ? c - <span class="string">&#x27;a&#x27;</span> + <span class="number">10</span> : c - <span class="string">&#x27;A&#x27;</span> + <span class="number">10</span>)); &#125; <span class="keyword">return</span> x; &#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">modString</span><span class="params">(<span class="type">const</span> string &amp;s, <span class="type">int</span> m)</span> </span>&#123;  <span class="comment">// 大数取模: 逐位取模. O(len)</span></span><br><span class="line">    ll r = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">char</span> c : s) r = (r * <span class="number">10</span> + (c - <span class="string">&#x27;0&#x27;</span>)) % m;</span><br><span class="line">    <span class="keyword">return</span> (<span class="type">int</span>)r;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="8-3-高精度"><a href="#8-3-高精度" class="headerlink" title="8.3 高精度"></a>8.3 高精度</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 高精度整数 (十进制, 低位在前). 加/减 O(n), 乘 O(n*m), 除 O(n*m*log10)</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Big</span> &#123;</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; d; <span class="type">bool</span> neg = <span class="literal">false</span>;                <span class="comment">// d[0] 是个位</span></span><br><span class="line">    <span class="built_in">Big</span>(<span class="type">long</span> <span class="type">long</span> x = <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (x &lt; <span class="number">0</span>) &#123; neg = <span class="literal">true</span>; x = -x; &#125;</span><br><span class="line">        <span class="keyword">if</span> (x == <span class="number">0</span>) d.<span class="built_in">push_back</span>(<span class="number">0</span>);</span><br><span class="line">        <span class="keyword">while</span> (x) &#123; d.<span class="built_in">push_back</span>((<span class="type">int</span>)(x % <span class="number">10</span>)); x /= <span class="number">10</span>; &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">trim</span><span class="params">()</span> </span>&#123;                                   <span class="comment">// 去前导零并修正 -0</span></span><br><span class="line">        <span class="keyword">while</span> (d.<span class="built_in">size</span>() &gt; <span class="number">1</span> &amp;&amp; d.<span class="built_in">back</span>() == <span class="number">0</span>) d.<span class="built_in">pop_back</span>();</span><br><span class="line">        <span class="keyword">if</span> (d.<span class="built_in">size</span>() == <span class="number">1</span> &amp;&amp; d[<span class="number">0</span>] == <span class="number">0</span>) neg = <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">cmpAbs</span><span class="params">(<span class="type">const</span> Big &amp;a, <span class="type">const</span> Big &amp;b)</span> </span>&#123;            <span class="comment">// 无符号比较: -1 / 0 / 1</span></span><br><span class="line">    <span class="keyword">if</span> (a.d.<span class="built_in">size</span>() != b.d.<span class="built_in">size</span>()) <span class="keyword">return</span> a.d.<span class="built_in">size</span>() &lt; b.d.<span class="built_in">size</span>() ? <span class="number">-1</span> : <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = (<span class="type">int</span>)a.d.<span class="built_in">size</span>() - <span class="number">1</span>; i &gt;= <span class="number">0</span>; --i)</span><br><span class="line">        <span class="keyword">if</span> (a.d[i] != b.d[i]) <span class="keyword">return</span> a.d[i] &lt; b.d[i] ? <span class="number">-1</span> : <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">Big <span class="title">add</span><span class="params">(<span class="type">const</span> Big &amp;a, <span class="type">const</span> Big &amp;b)</span> </span>&#123;               <span class="comment">// 无符号加法</span></span><br><span class="line">    Big c; c.d.<span class="built_in">assign</span>(<span class="built_in">max</span>(a.d.<span class="built_in">size</span>(), b.d.<span class="built_in">size</span>()) + <span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; a.d.<span class="built_in">size</span>(); ++i) c.d[i] += a.d[i];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; b.d.<span class="built_in">size</span>(); ++i) c.d[i] += b.d[i];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i + <span class="number">1</span> &lt; c.d.<span class="built_in">size</span>(); ++i) &#123; c.d[i<span class="number">+1</span>] += c.d[i]/<span class="number">10</span>; c.d[i] %= <span class="number">10</span>; &#125;     c.<span class="built_in">trim</span>(); <span class="keyword">return</span> c; &#125;</span><br><span class="line"><span class="function">Big <span class="title">sub</span><span class="params">(<span class="type">const</span> Big &amp;a, <span class="type">const</span> Big &amp;b)</span> </span>&#123;               <span class="comment">// 无符号减法 (要求 a &gt;= b)</span></span><br><span class="line">    Big c = a;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; b.d.<span class="built_in">size</span>(); ++i) c.d[i] -= b.d[i];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i + <span class="number">1</span> &lt; c.d.<span class="built_in">size</span>(); ++i) <span class="keyword">if</span> (c.d[i] &lt; <span class="number">0</span>) &#123; c.d[i] += <span class="number">10</span>; c.d[i<span class="number">+1</span>]--; &#125;     c.<span class="built_in">trim</span>(); <span class="keyword">return</span> c; &#125;</span><br><span class="line"><span class="function">Big <span class="title">mul</span><span class="params">(<span class="type">const</span> Big &amp;a, <span class="type">const</span> Big &amp;b)</span> </span>&#123;               <span class="comment">// 无符号乘法 (先累加后统一进位)</span></span><br><span class="line">    Big c; c.d.<span class="built_in">assign</span>(a.d.<span class="built_in">size</span>() + b.d.<span class="built_in">size</span>(), <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; a.d.<span class="built_in">size</span>(); ++i) <span class="keyword">for</span> (<span class="type">size_t</span> j = <span class="number">0</span>; j &lt; b.d.<span class="built_in">size</span>(); ++j)</span><br><span class="line">        c.d[i+j] += a.d[i] * b.d[j];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i + <span class="number">1</span> &lt; c.d.<span class="built_in">size</span>(); ++i) &#123; c.d[i<span class="number">+1</span>] += c.d[i]/<span class="number">10</span>; c.d[i] %= <span class="number">10</span>; &#125;     c.<span class="built_in">trim</span>(); <span class="keyword">return</span> c; &#125;</span><br><span class="line"><span class="comment">// 高精度 * 单精度: 直接 mul(a, Big(k)) 即可 (下面 divBig 就这么用)</span></span><br><span class="line"><span class="function">Big <span class="title">divSmall</span><span class="params">(<span class="type">const</span> Big &amp;a, <span class="type">int</span> b, <span class="type">int</span> &amp;rem)</span> </span>&#123;       <span class="comment">// 高精度 / 单精度, rem 回传余数</span></span><br><span class="line">    Big c; c.d.<span class="built_in">assign</span>(a.d.<span class="built_in">size</span>(), <span class="number">0</span>); rem = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = (<span class="type">int</span>)a.d.<span class="built_in">size</span>() - <span class="number">1</span>; i &gt;= <span class="number">0</span>; --i) &#123;</span><br><span class="line">        <span class="type">int</span> cur = rem * <span class="number">10</span> + a.d[i]; c.d[i] = cur / b; rem = cur % b;</span><br><span class="line">    &#125;     c.<span class="built_in">trim</span>(); <span class="keyword">return</span> c; &#125;</span><br><span class="line"><span class="function">Big <span class="title">divBig</span><span class="params">(<span class="type">const</span> Big &amp;a, <span class="type">const</span> Big &amp;b, Big &amp;r)</span> </span>&#123;    <span class="comment">// 竖式长除法 + 二分试商</span></span><br><span class="line">    Big q; q.d.<span class="built_in">assign</span>(a.d.<span class="built_in">size</span>(), <span class="number">0</span>); r = <span class="built_in">Big</span>(<span class="number">0</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = (<span class="type">int</span>)a.d.<span class="built_in">size</span>() - <span class="number">1</span>; i &gt;= <span class="number">0</span>; --i) &#123;</span><br><span class="line">        r.d.<span class="built_in">insert</span>(r.d.<span class="built_in">begin</span>(), a.d[i]); r.<span class="built_in">trim</span>();  <span class="comment">// r = r * 10 + a.d[i]</span></span><br><span class="line">        <span class="type">int</span> lo = <span class="number">0</span>, hi = <span class="number">9</span>, t = <span class="number">0</span>;                  <span class="comment">// 试商 0..9</span></span><br><span class="line">        <span class="keyword">while</span> (lo &lt;= hi) &#123; <span class="type">int</span> mid = (lo + hi) / <span class="number">2</span>;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">cmpAbs</span>(<span class="built_in">mul</span>(b, <span class="built_in">Big</span>(mid)), r) &lt;= <span class="number">0</span>) &#123; t = mid; lo = mid + <span class="number">1</span>; &#125; <span class="keyword">else</span> hi = mid - <span class="number">1</span>; &#125;</span><br><span class="line">        q.d[i] = t; r = <span class="built_in">sub</span>(r, <span class="built_in">mul</span>(b, <span class="built_in">Big</span>(t)));</span><br><span class="line">    &#125;</span><br><span class="line">    q.<span class="built_in">trim</span>(); <span class="keyword">return</span> q;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">printBig</span><span class="params">(<span class="type">const</span> Big &amp;a)</span> </span>&#123;                       <span class="comment">// 输出</span></span><br><span class="line">    <span class="keyword">if</span> (a.neg) <span class="built_in">putchar</span>(<span class="string">&#x27;-&#x27;</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = (<span class="type">int</span>)a.d.<span class="built_in">size</span>() - <span class="number">1</span>; i &gt;= <span class="number">0</span>; --i) <span class="built_in">putchar</span>(<span class="built_in">char</span>(<span class="string">&#x27;0&#x27;</span> + a.d[i]));</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>以上函数已用 20000 组随机数据与 <code>__int128</code>（加&#x2F;减&#x2F;乘&#x2F;乘单精度&#x2F;除单精度&#x2F;除高精度）对拍全部通过。</p><blockquote><p>⚠️ 高精度乘法是 O(n*m)，两个上千位的数会超时（需 FFT&#x2F;NTT）。CSP 中一般不超过 10^3 位，竖式足够。压位（每 9 位存一个 <code>int</code>）可把常数降到约 1&#x2F;9。</p></blockquote><h4 id="8-4-字符串分割与去空格"><a href="#8-4-字符串分割与去空格" class="headerlink" title="8.4 字符串分割与去空格"></a>8.4 字符串分割与去空格</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function">vector&lt;string&gt; <span class="title">split</span><span class="params">(<span class="type">const</span> string &amp;s, <span class="type">char</span> delim)</span> </span>&#123;   <span class="comment">// 按分隔符分割 (保留空段)</span></span><br><span class="line">    vector&lt;string&gt; res; string cur;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">char</span> c : s) &#123; <span class="keyword">if</span> (c == delim) &#123; res.<span class="built_in">push_back</span>(cur); cur.<span class="built_in">clear</span>(); &#125; <span class="keyword">else</span> cur += c; &#125;</span><br><span class="line">    res.<span class="built_in">push_back</span>(cur); <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">vector&lt;string&gt; <span class="title">splitWs</span><span class="params">(<span class="type">const</span> string &amp;s)</span> </span>&#123;             <span class="comment">// 按空白分割 (跳过连续空白)</span></span><br><span class="line">    vector&lt;string&gt; res; string tok; <span class="function">stringstream <span class="title">ss</span><span class="params">(s)</span></span>;</span><br><span class="line">    <span class="keyword">while</span> (ss &gt;&gt; tok) res.<span class="built_in">push_back</span>(tok);</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">string <span class="title">trim</span><span class="params">(<span class="type">const</span> string &amp;s)</span> </span>&#123;                        <span class="comment">// 去首尾空白</span></span><br><span class="line">    <span class="type">size_t</span> l = s.<span class="built_in">find_first_not_of</span>(<span class="string">&quot; \t\r\n&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (l == string::npos) <span class="keyword">return</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="type">size_t</span> r = s.<span class="built_in">find_last_not_of</span>(<span class="string">&quot; \t\r\n&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> s.<span class="built_in">substr</span>(l, r - l + <span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 读整行 (cin &gt;&gt; 后残留换行, 用 &gt;&gt; ws 吃掉): string line; getline(cin &gt;&gt; ws, line);</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>s.substr(pos, len)</code> 第二个参数是<strong>长度不是终点下标</strong>。<code>find</code> 失败返回 <code>string::npos</code>（即 <code>(size_t)-1</code>），不能用 <code>&gt;= 0</code> 判断。</p></blockquote><h3 id="9-常用-STL-速查"><a href="#9-常用-STL-速查" class="headerlink" title="9. 常用 STL 速查"></a>9. 常用 STL 速查</h3><table><thead><tr><th>容器</th><th>关键复杂度</th><th>竞赛要点</th></tr></thead><tbody><tr><td><code>vector</code></td><td><code>push_back</code> 均摊 O(1)，随机访问 O(1)</td><td><code>reserve(n)</code> 防多次扩容；<code>clear()</code> 不释放内存</td></tr><tr><td><code>string</code></td><td><code>substr</code> O(len)；<code>+=</code> 均摊 O(1)</td><td><code>substr(pos,len)</code> 参数是长度</td></tr><tr><td><code>map</code></td><td>增删查 O(log n)，红黑树，<strong>有序</strong></td><td><code>mp[k]</code> 会<strong>插入</strong>默认值，只查用 <code>find</code>&#x2F;<code>count</code></td></tr><tr><td><code>unordered_map</code></td><td>平均 O(1)，最坏 O(n)</td><td>会被卡哈希，需自定义 <code>custom_hash</code></td></tr><tr><td><code>set</code></td><td>O(log n)，有序去重</td><td>无随机访问；<code>*s.begin()</code> 取最小</td></tr><tr><td><code>priority_queue</code></td><td>堆顶 O(1)，增删 O(log n)</td><td>默认<strong>大根堆</strong>；无 <code>clear()</code>，重新声明即可</td></tr><tr><td><code>bitset</code></td><td>位运算 O(n&#x2F;64)</td><td><code>bitset&lt;N&gt;</code> 的 N 必须是编译期常量</td></tr></tbody></table><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">stlDemo</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    priority_queue&lt;<span class="type">int</span>&gt; pqMax;                            <span class="comment">// 大根堆 (默认)</span></span><br><span class="line">    priority_queue&lt;<span class="type">int</span>, vector&lt;<span class="type">int</span>&gt;, greater&lt;<span class="type">int</span>&gt;&gt; pqMin; <span class="comment">// 小根堆</span></span><br><span class="line">    <span class="built_in">sort</span>(a, a + n);                                       <span class="comment">// 必须先升序</span></span><br><span class="line">    <span class="keyword">do</span> &#123; <span class="comment">/* 使用 a[0..n-1] */</span> &#125; <span class="keyword">while</span> (<span class="built_in">next_permutation</span>(a, a + n));  <span class="comment">// false=已最大</span></span><br><span class="line">    bitset&lt;1005&gt; bs; bs.<span class="built_in">set</span>(); bs.<span class="built_in">reset</span>(); bs.<span class="built_in">flip</span>(); bs[<span class="number">3</span>] = <span class="number">1</span>;</span><br><span class="line">    <span class="type">int</span> c = (<span class="type">int</span>)bs.<span class="built_in">count</span>();                              <span class="comment">// 数 1 的个数</span></span><br><span class="line">    bs |= (bs &lt;&lt; <span class="number">2</span>);                                      <span class="comment">// 位运算即集合运算</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 优先队列自定义比较: 与 sort 的 cmp 相反! 返回 true 表示 a 优先级&quot;低于&quot; b</span></span><br><span class="line"><span class="comment">// struct Cmp &#123; bool operator()(const pii &amp;x, const pii &amp;y) const &#123; return x.first &gt; y.first; &#125; &#125;;</span></span><br><span class="line"><span class="comment">// priority_queue&lt;pii, vector&lt;pii&gt;, Cmp&gt; pq;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// unordered_map 防卡哈希: splitmix64 雪崩函数 + 随机种子</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">custom_hash</span> &#123;</span><br><span class="line">    <span class="function"><span class="type">static</span> <span class="type">uint64_t</span> <span class="title">splitmix64</span><span class="params">(<span class="type">uint64_t</span> x)</span> </span>&#123;</span><br><span class="line">        x += <span class="number">0x9e3779b97f4a7c15ULL</span>;</span><br><span class="line">        x = (x ^ (x &gt;&gt; <span class="number">30</span>)) * <span class="number">0xbf58476d1ce4e5b9ULL</span>;</span><br><span class="line">        x = (x ^ (x &gt;&gt; <span class="number">27</span>)) * <span class="number">0x94d049bb133111ebULL</span>;</span><br><span class="line">        <span class="keyword">return</span> x ^ (x &gt;&gt; <span class="number">31</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">size_t</span> <span class="title">operator</span><span class="params">()</span><span class="params">(<span class="type">uint64_t</span> x)</span> <span class="type">const</span> </span>&#123;</span><br><span class="line">        <span class="type">static</span> <span class="type">const</span> <span class="type">uint64_t</span> FIXED_RANDOM =</span><br><span class="line">            chrono::steady_clock::<span class="built_in">now</span>().<span class="built_in">time_since_epoch</span>().<span class="built_in">count</span>();</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">splitmix64</span>(x + FIXED_RANDOM);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// unordered_map&lt;ll, int, custom_hash&gt; mp;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 值域是 <code>1..n</code> 时优先用 <code>vector</code> 代替 <code>unordered_map</code>：常数小 5~10 倍且无卡哈希风险。<code>map</code>&#x2F;<code>set</code> 遍历是<strong>有序</strong>的，别依赖插入顺序。</p></blockquote><h3 id="10-常见坑"><a href="#10-常见坑" class="headerlink" title="10. 常见坑"></a>10. 常见坑</h3><p><strong>整数溢出</strong></p><ul><li><code>int</code> 上限约 2.1e9。<code>a * b</code> 若两边都是 <code>int</code> 会<strong>先溢出再赋值</strong>，写 <code>(ll)a * b</code>。前缀和、方案数、答案累加一律 <code>ll</code>（1e5 个 1e9 相加已是 1e14）。</li><li><code>INF</code> 用 <code>0x3f3f3f3f</code>（两倍 &#x3D; 0x7e7e7e7e 仍不溢出）；用 <code>0x7fffffff</code> 相加就变负。</li><li><code>1 &lt;&lt; 31</code> 是有符号溢出（UB），写 <code>1LL &lt;&lt; 31</code>；位移量 <code>&gt;= 31</code> 一律用 <code>1LL</code>。</li><li>取模减法写 <code>(a - b % MOD + MOD) % MOD</code>，别漏 <code>+ MOD</code>。</li></ul><p><strong>浮点误差</strong></p><ul><li>判等用 <code>fabs(a - b) &lt; EPS</code>（1e-8），不要用 <code>==</code>。</li><li><strong>能整数二分就不要浮点二分</strong>：把不等式两边乘开、或对答案乘 1000 转成整数。</li><li><code>double</code> 有效位约 15~16 位十进制；1e9 级数据相减会丢精度，能转 <code>ll</code> 就转。</li><li>输出用 <code>printf(&quot;%.10f\n&quot;, ans)</code> 多打几位，避免被 SPJ 卡边界。</li></ul><p><strong>数组越界与初始化</strong></p><ul><li>全局数组默认清零，<strong>局部数组不清零</strong>；局部大数组还会爆栈（8 MB），大小 &gt; 1e5 的数组一律放全局。（实测踩坑：<code>ll b[1005][1005]</code> 作局部变量就是 8 MB，直接爆栈。）</li><li>差分、双指针、单调队列的下标上界要 <code>+1</code> 或 <code>+2</code>，见 3.1 &#x2F; 3.2 &#x2F; 4 的 ⚠️。</li><li>多测必须清空所有全局量：<code>vector::clear()</code>、链式前向星的 <code>head/tot</code>、<code>cnt</code>、<code>ans</code>。最稳是封成 <code>init()</code> 并在每组数据开头调用。</li><li><code>memset</code> 按字节赋值：<code>memset(a, 0x3f, sizeof a)</code> 得到 0x3f3f3f3f（可当 INF），但 <code>memset(a, 1, ...)</code> 得到 0x01010101 而不是 1。</li></ul><p><strong>endl 与 ‘\n’</strong></p><ul><li><code>endl</code> &#x3D; 换行 <strong>+ flush</strong>。输出 1e6 行时比 <code>&#39;\n&#39;</code> 慢数倍，**一律用 <code>&#39;\n&#39;</code>**。</li><li>交互题相反：必须 <code>cout &lt;&lt; ... &lt;&lt; endl;</code> 或 <code>fflush(stdout);</code>，否则对方读不到。</li><li><code>ios::sync_with_stdio(false); cin.tie(nullptr);</code> 放 <code>main</code> 开头，之后不能再混用 <code>scanf</code>&#x2F;<code>printf</code>。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 标准 main 骨架, 直接抄 (依赖 1.1 的万能头与 using namespace std)</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ios::<span class="built_in">sync_with_stdio</span>(<span class="literal">false</span>);</span><br><span class="line">    cin.<span class="built_in">tie</span>(<span class="literal">nullptr</span>);</span><br><span class="line">    <span class="type">int</span> T = <span class="number">1</span>;</span><br><span class="line">    <span class="comment">// cin &gt;&gt; T;                    // 多测</span></span><br><span class="line">    <span class="keyword">while</span> (T--) <span class="built_in">solve</span>();</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="第-02-章-搜索与图论"><a href="#第-02-章-搜索与图论" class="headerlink" title="第 02 章 搜索与图论"></a>第 02 章 搜索与图论</h2><blockquote><p>代码默认以 <code>#include &lt;bits/stdc++.h&gt;</code> + <code>using namespace std;</code> 开头（C++17），不再重复；下标无说明均从 1 开始。全部模板已用 g++ 11.4 <code>-std=c++17</code> 编译通过（含随机对拍）。同一代码块内含多个编号模板（如 11.1&#x2F;11.2&#x2F;11.3）时它们互相独立、可能重名，复制时只取所需那一个。</p></blockquote><h3 id="A-搜索"><a href="#A-搜索" class="headerlink" title="A. 搜索"></a>A. 搜索</h3><h4 id="1-DFS-框架-剪枝"><a href="#1-DFS-框架-剪枝" class="headerlink" title="1. DFS 框架 + 剪枝"></a>1. DFS 框架 + 剪枝</h4><p>四类剪枝：<strong>可行性</strong>（当前状态已不可能合法）、<strong>最优性</strong>（当前代价 + 乐观下界 &gt;&#x3D; 已知最优）、<strong>上下界</strong>（剩余部分的最小&#x2F;最大贡献）、<strong>顺序</strong>（先搜分支少&#x2F;更可能出解的）。DFS 复杂度 &#x3D; 状态数 x 转移数，剪枝改变可达状态集与常数。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 0/1 背包搜索: 可行性 + 最优性(剩余价值上界) + 顺序剪枝, 最坏 O(2^n)</span></span><br><span class="line"><span class="type">int</span> n,C,w[N],v[N],suf[N],best;                 <span class="comment">// suf[i]=v[i..n] 之和</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">cmp</span><span class="params">(<span class="type">int</span> a,<span class="type">int</span> b)</span></span>&#123;<span class="keyword">return</span> <span class="number">1LL</span>*v[a]*w[b]&gt;<span class="number">1LL</span>*v[b]*w[a];&#125;   <span class="comment">// 单位价值降序</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> i,<span class="type">int</span> cw,<span class="type">int</span> cv)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(cw&gt;C)<span class="keyword">return</span>;                                          <span class="comment">// 可行性剪枝</span></span><br><span class="line">  <span class="keyword">if</span>(cv+suf[i]&lt;=best)<span class="keyword">return</span>;                               <span class="comment">// 最优性剪枝(上界)</span></span><br><span class="line">  <span class="keyword">if</span>(i&gt;n)&#123;best=<span class="built_in">max</span>(best,cv);<span class="keyword">return</span>;&#125;</span><br><span class="line">  <span class="keyword">if</span>(cw+w[i]&lt;=C)<span class="built_in">dfs</span>(i<span class="number">+1</span>,cw+w[i],cv+v[i]);                  <span class="comment">// 顺序剪枝: 先搜&quot;选&quot;</span></span><br><span class="line">  <span class="built_in">dfs</span>(i<span class="number">+1</span>,cw,cv);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 最优性剪枝的上界必须<strong>可采纳</strong>（绝不高估），否则剪掉最优解。常用紧上界：「剩余全取」「剩余按单位价值贪心」。<br>⚠️ 递归深度可达 1e5 级（长链图），深链 + 大局部数组会 RE。</p></blockquote><h4 id="2-迭代加深-IDDFS-IDA"><a href="#2-迭代加深-IDDFS-IDA" class="headerlink" title="2. 迭代加深 IDDFS &#x2F; IDA*"></a>2. 迭代加深 IDDFS &#x2F; IDA*</h4><p>限制深度 dep 反复 DFS，用「重搜浅层」换 O(dep) 空间；分支多时浅层重搜可忽略。加**估价函数 h(u)*<em>（u 到目标的乐观下界）即 IDA</em>。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// IDA*: 适用&quot;求最少步数 + 状态空间大 + 判重难&quot;, 时间 O(b^limit), 空间 O(limit)</span></span><br><span class="line"><span class="type">int</span> limit;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">h</span><span class="params">(<span class="type">int</span> u)</span></span>;                                  <span class="comment">// 题目给出, 必须 h(u) &lt;= 真实剩余步数</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">iddfs</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> d)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">h</span>(u)==<span class="number">0</span>)<span class="keyword">return</span> <span class="literal">true</span>;                                  <span class="comment">// 到达目标</span></span><br><span class="line">  <span class="keyword">if</span>(d+<span class="built_in">h</span>(u)&gt;limit)<span class="keyword">return</span> <span class="literal">false</span>;                            <span class="comment">// IDA* 剪枝</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:<span class="built_in">nxt</span>(u))&#123;<span class="keyword">if</span>(vis[v])<span class="keyword">continue</span>;vis[v]=<span class="number">1</span>;<span class="keyword">if</span>(<span class="built_in">iddfs</span>(v,d<span class="number">+1</span>))<span class="keyword">return</span> <span class="literal">true</span>;vis[v]=<span class="number">0</span>;&#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">solve</span><span class="params">(<span class="type">int</span> s)</span></span>&#123;<span class="keyword">for</span>(limit=<span class="number">0</span>;limit&lt;=MAXD;++limit)<span class="keyword">if</span>(<span class="built_in">iddfs</span>(s,<span class="number">0</span>))<span class="keyword">return</span> limit;<span class="keyword">return</span> <span class="number">-1</span>;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ IDA* 每层从根重搜，h 必须 O(1) 或 O(小常数) 维护，否则常数爆炸。</p></blockquote><h4 id="3-双向-BFS"><a href="#3-双向-BFS" class="headerlink" title="3. 双向 BFS"></a>3. 双向 BFS</h4><p>起点终点同时 BFS，<strong>每次扩展队列较小的一侧</strong>，两端 frontier 相遇即最短路。状态数从 b^d 降到约 2*b^(d&#x2F;2)。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 双向 BFS: 以&quot;翻转某一位&quot;的玩具状态空间为例; 时间约 O(b^(d/2)), 空间同</span></span><br><span class="line">unordered_map&lt;<span class="type">int</span>,<span class="type">int</span>&gt; d1,d2;                  <span class="comment">// 两侧到该状态的距离</span></span><br><span class="line"><span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">nxt</span><span class="params">(<span class="type">int</span> u)</span></span>;                        <span class="comment">// 题目给出的状态转移</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">bfs2</span><span class="params">(<span class="type">int</span> s,<span class="type">int</span> t,<span class="type">int</span> k)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(s==t)<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  queue&lt;<span class="type">int</span>&gt; q1,q2; q<span class="number">1.</span><span class="built_in">push</span>(s); q<span class="number">2.</span><span class="built_in">push</span>(t); d1[s]=<span class="number">0</span>; d2[t]=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">while</span>(!q<span class="number">1.</span><span class="built_in">empty</span>()&amp;&amp;!q<span class="number">2.</span><span class="built_in">empty</span>())&#123;</span><br><span class="line">    <span class="keyword">if</span>(q<span class="number">1.</span><span class="built_in">size</span>()&gt;q<span class="number">2.</span><span class="built_in">size</span>())&#123;<span class="built_in">swap</span>(q1,q2);<span class="built_in">swap</span>(d1,d2);&#125;      <span class="comment">// 扩展小的一侧</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> sz=q<span class="number">1.</span><span class="built_in">size</span>();sz--;)&#123;                           <span class="comment">// 按层扩展</span></span><br><span class="line">      <span class="type">int</span> u=q<span class="number">1.f</span>ront(); q<span class="number">1.</span><span class="built_in">pop</span>();</span><br><span class="line">      <span class="keyword">for</span>(<span class="type">int</span> v:<span class="built_in">nxt</span>(u))&#123;<span class="keyword">if</span>(d<span class="number">1.</span><span class="built_in">count</span>(v))<span class="keyword">continue</span>;</span><br><span class="line">        <span class="keyword">if</span>(d<span class="number">2.</span><span class="built_in">count</span>(v))<span class="keyword">return</span> d1[u]<span class="number">+1</span>+d2[v];               <span class="comment">// 相遇</span></span><br><span class="line">        d1[v]=d1[u]<span class="number">+1</span>; q<span class="number">1.</span><span class="built_in">push</span>(v);&#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 相遇判断必须在<strong>入队时</strong>做（出队时做会漏最优层）。双向 BFS 要求<strong>转移可逆</strong>且目标是单个状态；目标是一整类状态时用多源 BFS。</p></blockquote><h4 id="4-BFS-框架：网格-多源-0-1-状态压缩"><a href="#4-BFS-框架：网格-多源-0-1-状态压缩" class="headerlink" title="4. BFS 框架：网格 &#x2F; 多源 &#x2F; 0-1 &#x2F; 状态压缩"></a>4. BFS 框架：网格 &#x2F; 多源 &#x2F; 0-1 &#x2F; 状态压缩</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 4.1 网格 BFS 最短路(边权全 1): O(n*m)</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> dx[<span class="number">4</span>]=&#123;<span class="number">-1</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">0</span>&#125;,dy[<span class="number">4</span>]=&#123;<span class="number">0</span>,<span class="number">1</span>,<span class="number">0</span>,<span class="number">-1</span>&#125;;   <span class="comment">// 四方向</span></span><br><span class="line"><span class="comment">// 八方向: dx[8]=&#123;-1,-1,-1,0,0,1,1,1&#125;, dy[8]=&#123;-1,0,1,-1,1,-1,0,1&#125;</span></span><br><span class="line"><span class="type">int</span> n,m,dis[N][N];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">bfs</span><span class="params">(<span class="type">int</span> sx,<span class="type">int</span> sy,<span class="type">int</span> tx,<span class="type">int</span> ty)</span></span>&#123;</span><br><span class="line">  <span class="built_in">memset</span>(dis,<span class="number">-1</span>,<span class="keyword">sizeof</span> dis); queue&lt;pair&lt;<span class="type">int</span>,<span class="type">int</span>&gt;&gt; q; q.<span class="built_in">push</span>(&#123;sx,sy&#125;); dis[sx][sy]=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;</span><br><span class="line">    <span class="keyword">auto</span> [x,y]=q.<span class="built_in">front</span>(); q.<span class="built_in">pop</span>();</span><br><span class="line">    <span class="keyword">if</span>(x==tx&amp;&amp;y==ty)<span class="keyword">return</span> dis[x][y];</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> d=<span class="number">0</span>;d&lt;<span class="number">4</span>;++d)&#123;<span class="type">int</span> nx=x+dx[d],ny=y+dy[d];</span><br><span class="line">      <span class="keyword">if</span>(nx&lt;<span class="number">1</span>||nx&gt;n||ny&lt;<span class="number">1</span>||ny&gt;m||dis[nx][ny]!=<span class="number">-1</span>||g[nx][ny]==<span class="string">&#x27;#&#x27;</span>)<span class="keyword">continue</span>;</span><br><span class="line">      dis[nx][ny]=dis[x][y]<span class="number">+1</span>; q.<span class="built_in">push</span>(&#123;nx,ny&#125;);&#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 4.2 多源 BFS: 所有源点 dis=0 一起入队, 求每点到最近源点距离; O(n*m)</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">bfs_multi</span><span class="params">(vector&lt;pair&lt;<span class="type">int</span>,<span class="type">int</span>&gt;&gt;&amp;src)</span></span>&#123;</span><br><span class="line">  <span class="built_in">memset</span>(dis,<span class="number">-1</span>,<span class="keyword">sizeof</span> dis); queue&lt;pair&lt;<span class="type">int</span>,<span class="type">int</span>&gt;&gt; q;</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">auto</span>&amp;p:src)&#123;dis[p.first][p.second]=<span class="number">0</span>;q.<span class="built_in">push</span>(p);&#125;</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;<span class="comment">/* 同上扩展 */</span>&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 4.3 0-1 BFS: 边权只有 0/1, deque 代替优先队列; 每点最多入队 2 次, O(n+m)</span></span><br><span class="line"><span class="type">int</span> dis[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">bfs01</span><span class="params">(<span class="type">int</span> s)</span></span>&#123;</span><br><span class="line">  <span class="built_in">memset</span>(dis,<span class="number">0x3f</span>,<span class="keyword">sizeof</span> dis); deque&lt;<span class="type">int</span>&gt; q; dis[s]=<span class="number">0</span>; q.<span class="built_in">push_front</span>(s);</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;<span class="type">int</span> u=q.<span class="built_in">front</span>();q.<span class="built_in">pop_front</span>();</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">auto</span> [v,w]:e[u])                                          <span class="comment">// w 必须是 0 或 1</span></span><br><span class="line">      <span class="keyword">if</span>(dis[u]+w&lt;dis[v])&#123;dis[v]=dis[u]+w;(w==<span class="number">0</span>?q.<span class="built_in">push_front</span>(v):q.<span class="built_in">push_back</span>(v));&#125;&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 4.4 状态压缩 BFS: 状态压进 int/uint64, 数组或哈希判重; O(状态数 * 转移数)</span></span><br><span class="line"><span class="type">int</span> dis[<span class="number">1</span>&lt;&lt;K];                                 <span class="comment">// K &lt;= 20 左右</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">bfs_mask</span><span class="params">(<span class="type">int</span> s,<span class="type">int</span> t)</span></span>&#123;</span><br><span class="line">  <span class="built_in">memset</span>(dis,<span class="number">-1</span>,<span class="keyword">sizeof</span> dis); queue&lt;<span class="type">int</span>&gt; q; q.<span class="built_in">push</span>(s); dis[s]=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;<span class="type">int</span> u=q.<span class="built_in">front</span>();q.<span class="built_in">pop</span>(); <span class="keyword">if</span>(u==t)<span class="keyword">return</span> dis[u];</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;K;++i)&#123;<span class="type">int</span> v=u^(<span class="number">1</span>&lt;&lt;i);<span class="keyword">if</span>(dis[v]==<span class="number">-1</span>)&#123;dis[v]=dis[u]<span class="number">+1</span>;q.<span class="built_in">push</span>(v);&#125;&#125;&#125;  <span class="comment">// 翻转第 i 位</span></span><br><span class="line">  <span class="keyword">return</span> <span class="number">-1</span>;&#125;                                   <span class="comment">// 更小的 K 用数组快, 大的 K 换哈希表</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 0-1 BFS 不能用「出队即定型」的 <code>vis</code> 剪枝：一个点可能先以较大 dis 出队、之后被更小 dis 更新，只能靠 <code>dis[u]+w&lt;dis[v]</code> 判断，这是最常见的写挂点。<br>⚠️ 2^K 开不下数组就换 <code>unordered_map&lt;int,int&gt;</code>；哈希常数大，能开数组就别用哈希。</p></blockquote><h4 id="5-记忆化搜索"><a href="#5-记忆化搜索" class="headerlink" title="5. 记忆化搜索"></a>5. 记忆化搜索</h4><p>自顶向下 DFS + 缓存，只访问可达状态，适合转移的拓扑序不明显的 DP。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 滑雪(网格最长下降路径): 时间 O(n*m), 空间 O(n*m)</span></span><br><span class="line"><span class="type">int</span> n,m,h[N][N],f[N][N];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(f[x][y]!=<span class="number">-1</span>)<span class="keyword">return</span> f[x][y];                           <span class="comment">// 命中缓存</span></span><br><span class="line">  f[x][y]=<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> d=<span class="number">0</span>;d&lt;<span class="number">4</span>;++d)&#123;<span class="type">int</span> nx=x+dx[d],ny=y+dy[d];</span><br><span class="line">    <span class="keyword">if</span>(nx&lt;<span class="number">1</span>||nx&gt;n||ny&lt;<span class="number">1</span>||ny&gt;m||h[nx][ny]&gt;=h[x][y])<span class="keyword">continue</span>;</span><br><span class="line">    f[x][y]=<span class="built_in">max</span>(f[x][y],<span class="built_in">dfs</span>(nx,ny)<span class="number">+1</span>);&#125;</span><br><span class="line">  <span class="keyword">return</span> f[x][y];&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 缓存初值必须用<strong>取不到的值</strong>（如 -1）区分「未算」与「答案就是 0」；多测要整体清空。<br>⚠️ 递归层数 &#x3D; 最长路径长度，网格题可达 n*m，深链会爆栈，必要时改按拓扑序递推。</p></blockquote><h4 id="6-A-启发式搜索"><a href="#6-A-启发式搜索" class="headerlink" title="6. A* 启发式搜索"></a>6. A* 启发式搜索</h4><p>按 <code>f = g + h</code> 出堆，h(u) 是 u 到目标的估计距离。<strong>可采纳</strong>（h &lt;&#x3D; h*）保证最优；<strong>一致</strong>（h(u) &lt;&#x3D; w(u,v) + h(v)）保证出堆即定型。h ≡ 0 退化为 Dijkstra，边权全 1 时即 BFS。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// A* 网格最短路: h=曼哈顿距离(四方向, 可采纳且一致); 最坏 O(n*m*log), 通常远快</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span>&#123;<span class="type">int</span> f,x,y; <span class="type">bool</span> <span class="keyword">operator</span>&lt;(<span class="type">const</span> Node&amp;o)<span class="type">const</span>&#123;<span class="keyword">return</span> f&gt;o.f;&#125;&#125;;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">astar</span><span class="params">(<span class="type">int</span> sx,<span class="type">int</span> sy,<span class="type">int</span> tx,<span class="type">int</span> ty)</span></span>&#123;</span><br><span class="line">  <span class="built_in">memset</span>(dis,<span class="number">0x3f</span>,<span class="keyword">sizeof</span> dis); priority_queue&lt;Node&gt; q;</span><br><span class="line">  dis[sx][sy]=<span class="number">0</span>; q.<span class="built_in">push</span>(&#123;<span class="built_in">abs</span>(sx-tx)+<span class="built_in">abs</span>(sy-ty),sx,sy&#125;);</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;</span><br><span class="line">    Node c=q.<span class="built_in">top</span>(); q.<span class="built_in">pop</span>();</span><br><span class="line">    <span class="keyword">if</span>(c.x==tx&amp;&amp;c.y==ty)<span class="keyword">return</span> dis[tx][ty];</span><br><span class="line">    <span class="keyword">if</span>(c.f&gt;dis[c.x][c.y]+<span class="built_in">abs</span>(c.x-tx)+<span class="built_in">abs</span>(c.y-ty))<span class="keyword">continue</span>;  <span class="comment">// 过期节点</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> d=<span class="number">0</span>;d&lt;<span class="number">4</span>;++d)&#123;<span class="type">int</span> nx=c.x+dx[d],ny=c.y+dy[d];</span><br><span class="line">      <span class="keyword">if</span>(nx&lt;<span class="number">1</span>||nx&gt;n||ny&lt;<span class="number">1</span>||ny&gt;m||g[nx][ny]==<span class="string">&#x27;#&#x27;</span>)<span class="keyword">continue</span>;</span><br><span class="line">      <span class="keyword">if</span>(dis[c.x][c.y]<span class="number">+1</span>&lt;dis[nx][ny])&#123;dis[nx][ny]=dis[c.x][c.y]<span class="number">+1</span>;</span><br><span class="line">        q.<span class="built_in">push</span>(&#123;dis[nx][ny]+<span class="built_in">abs</span>(nx-tx)+<span class="built_in">abs</span>(ny-ty),nx,ny&#125;);&#125;&#125;&#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 八数码的 h 常用「不在位数字个数」（可采纳且一致），比「曼哈顿距离和」弱但更快；k 短路取 h &#x3D; 到终点的最短路（反图预处理），第 k 次弹出终点即答案。</p></blockquote><h4 id="7-Dancing-Links-DLX-简述"><a href="#7-Dancing-Links-DLX-简述" class="headerlink" title="7. Dancing Links (DLX) 简述"></a>7. Dancing Links (DLX) 简述</h4><p>求解<strong>精确覆盖</strong>（选若干行使每列恰好一个 1）。X 算法：选含 1 最少的列 c，枚举行 r 覆盖 c，删除 r 覆盖的所有列及其行，递归；失败则恢复。DLX 用<strong>双向十字链表</strong>把删除&#x2F;恢复做到 O(1)。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// DLX 精确覆盖模板: 节点数 = 1 的个数 + 列数</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> MAXN=<span class="number">500010</span>;</span><br><span class="line"><span class="type">int</span> n,m,tot,ansn,L[MAXN],R[MAXN],U[MAXN],D[MAXN],row[MAXN],col[MAXN],siz[MAXN],head[MAXN],ans[MAXN];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">(<span class="type">int</span> r,<span class="type">int</span> c)</span></span>&#123;n=r; m=c;               <span class="comment">// r 行 c 列</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;=c;++i)&#123;L[i]=i<span class="number">-1</span>;R[i]=i<span class="number">+1</span>;U[i]=D[i]=i;siz[i]=<span class="number">0</span>;&#125;</span><br><span class="line">  L[<span class="number">0</span>]=c; R[c]=<span class="number">0</span>; tot=c<span class="number">+1</span>; <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=r;++i)head[i]=<span class="number">-1</span>;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> r,<span class="type">int</span> c)</span></span>&#123;                         <span class="comment">// 第 r 行插入一个 1(列 c)</span></span><br><span class="line">  <span class="type">int</span> x=tot++; row[x]=r; col[x]=c; ++siz[c];</span><br><span class="line">  D[x]=D[c]; U[x]=c; U[D[c]]=x; D[c]=x;</span><br><span class="line">  <span class="keyword">if</span>(head[r]==<span class="number">-1</span>)head[r]=L[x]=R[x]=x;</span><br><span class="line">  <span class="keyword">else</span>&#123;R[x]=R[head[r]];L[x]=head[r];L[R[head[r]]]=x;R[head[r]]=x;&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">remove</span><span class="params">(<span class="type">int</span> c)</span></span>&#123;R[L[c]]=R[c];L[R[c]]=L[c];</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=D[c];i!=c;i=D[i])<span class="keyword">for</span>(<span class="type">int</span> j=R[i];j!=i;j=R[j])&#123;U[D[j]]=U[j];D[U[j]]=D[j];--siz[col[j]];&#125;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">resume</span><span class="params">(<span class="type">int</span> c)</span></span>&#123;<span class="keyword">for</span>(<span class="type">int</span> i=U[c];i!=c;i=U[i])<span class="keyword">for</span>(<span class="type">int</span> j=L[i];j!=i;j=L[j])&#123;U[D[j]]=j;D[U[j]]=j;++siz[col[j]];&#125;</span><br><span class="line">  R[L[c]]=c; L[R[c]]=c;&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">dance</span><span class="params">(<span class="type">int</span> d)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(R[<span class="number">0</span>]==<span class="number">0</span>)&#123;ansn=d;<span class="keyword">return</span> <span class="literal">true</span>;&#125;                         <span class="comment">// 所有列已覆盖</span></span><br><span class="line">  <span class="type">int</span> c=R[<span class="number">0</span>]; <span class="keyword">for</span>(<span class="type">int</span> i=R[<span class="number">0</span>];i!=<span class="number">0</span>;i=R[i])<span class="keyword">if</span>(siz[i]&lt;siz[c])c=i;   <span class="comment">// 选 1 最少的列</span></span><br><span class="line">  <span class="built_in">remove</span>(c);</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=D[c];i!=c;i=D[i])&#123;</span><br><span class="line">    ans[d]=row[i];</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> j=R[i];j!=i;j=R[j])<span class="built_in">remove</span>(col[j]);</span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">dance</span>(d<span class="number">+1</span>))<span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> j=L[i];j!=i;j=L[j])<span class="built_in">resume</span>(col[j]);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">resume</span>(c); <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 恢复顺序必须与删除顺序<strong>严格相反</strong>：删按 R 走，恢复就按 L 走，写反会静默出错。</p></blockquote><h4 id="8-连通块-Floodfill"><a href="#8-连通块-Floodfill" class="headerlink" title="8. 连通块 Floodfill"></a>8. 连通块 Floodfill</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// BFS 版求连通块个数/大小: O(n*m)</span></span><br><span class="line"><span class="type">int</span> cnt;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">flood</span><span class="params">(<span class="type">int</span> sx,<span class="type">int</span> sy)</span></span>&#123;</span><br><span class="line">  queue&lt;pair&lt;<span class="type">int</span>,<span class="type">int</span>&gt;&gt; q; q.<span class="built_in">push</span>(&#123;sx,sy&#125;); vis[sx][sy]=<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;</span><br><span class="line">    <span class="keyword">auto</span> [x,y]=q.<span class="built_in">front</span>(); q.<span class="built_in">pop</span>();</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> d=<span class="number">0</span>;d&lt;<span class="number">4</span>;++d)&#123;<span class="type">int</span> nx=x+dx[d],ny=y+dy[d];</span><br><span class="line">      <span class="keyword">if</span>(nx&lt;<span class="number">1</span>||nx&gt;n||ny&lt;<span class="number">1</span>||ny&gt;m||vis[nx][ny])<span class="keyword">continue</span>;</span><br><span class="line">      <span class="keyword">if</span>(g[nx][ny]!=g[sx][sy])<span class="keyword">continue</span>;                    <span class="comment">// 同色/同类型才扩展</span></span><br><span class="line">      vis[nx][ny]=<span class="number">1</span>; q.<span class="built_in">push</span>(&#123;nx,ny&#125;);&#125;&#125;&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 主函数: for i,j if(!vis[i][j]) flood(i,j), ++cnt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 8 连通时对角线可穿过「两个障碍的夹角」，题目要求不可穿越时需额外判断。</p></blockquote><h3 id="B-图论"><a href="#B-图论" class="headerlink" title="B. 图论"></a>B. 图论</h3><h4 id="9-存图三件套与适用场景"><a href="#9-存图三件套与适用场景" class="headerlink" title="9. 存图三件套与适用场景"></a>9. 存图三件套与适用场景</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 9.1 链式前向星: 最省内存最快, 支持边编号(i^1 取反向边)</span></span><br><span class="line"><span class="type">int</span> hd[N],to[M],wt[M],nxt[M],cnt=<span class="number">1</span>;            <span class="comment">// cnt 从 1 开始, 反向边 = i^1</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> v,<span class="type">int</span> w)</span></span>&#123;to[++cnt]=v;wt[cnt]=w;nxt[cnt]=hd[u];hd[u]=cnt;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add2</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> v,<span class="type">int</span> w)</span></span>&#123;<span class="built_in">add</span>(u,v,w);<span class="built_in">add</span>(v,u,w);&#125;</span><br><span class="line"><span class="comment">// 遍历出边: for(int i=hd[u];i;i=nxt[i])&#123;int v=to[i],w=wt[i];&#125;</span></span><br><span class="line"><span class="comment">// 9.2 vector 邻接表: 写法最短, 遍历方便, 不支持边编号; 默认首选</span></span><br><span class="line">vector&lt;pair&lt;<span class="type">int</span>,<span class="type">int</span>&gt;&gt; e[N];                    <span class="comment">// 加边: e[u].push_back(&#123;v,w&#125;);</span></span><br><span class="line"><span class="comment">// 遍历: for(auto [v,w]:e[u])&#123; &#125;</span></span><br><span class="line"><span class="comment">// 9.3 邻接矩阵: O(1) 查边, 适合 n &lt;= 2000 的稠密图 / Floyd / 传递闭包</span></span><br><span class="line"><span class="type">int</span> g[N][N];                                   <span class="comment">// 加边: g[u][v]=min(g[u][v],w);</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><table><thead><tr><th>存图方式</th><th>空间</th><th>查边</th><th>遍历出边</th><th>适用</th></tr></thead><tbody><tr><td>链式前向星</td><td>O(n+m)</td><td>O(deg)</td><td>O(deg) 最快</td><td>大图、需边编号（网络流&#x2F;割边）</td></tr><tr><td>vector 邻接表</td><td>O(n+m)</td><td>O(deg)</td><td>O(deg)</td><td>默认首选</td></tr><tr><td>邻接矩阵</td><td>O(n^2)</td><td>O(1)</td><td>O(n)</td><td>n&lt;&#x3D;2000 稠密图、Floyd、bitset 闭包</td></tr></tbody></table><blockquote><p>⚠️ 无向图边数开 2m，有向图开 m；链式前向星 <code>cnt</code> 必须从 1 开始才能用 <code>i^1</code>。</p></blockquote><h4 id="10-拓扑排序与-DAG-上-DP"><a href="#10-拓扑排序与-DAG-上-DP" class="headerlink" title="10. 拓扑排序与 DAG 上 DP"></a>10. 拓扑排序与 DAG 上 DP</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 10.1 Kahn: O(n+m)。最终出队点数 &lt; n 则有环</span></span><br><span class="line"><span class="type">int</span> deg[N],ord[N],tot;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">topo</span><span class="params">(<span class="type">int</span> n)</span></span>&#123;</span><br><span class="line">  queue&lt;<span class="type">int</span>&gt; q; <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;++i)<span class="keyword">if</span>(!deg[i])q.<span class="built_in">push</span>(i);</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;<span class="type">int</span> u=q.<span class="built_in">front</span>();q.<span class="built_in">pop</span>();ord[++tot]=u;<span class="keyword">for</span>(<span class="type">int</span> v:e[u])<span class="keyword">if</span>(--deg[v]==<span class="number">0</span>)q.<span class="built_in">push</span>(v);&#125;</span><br><span class="line">  <span class="keyword">return</span> tot==n;                               <span class="comment">// false 表示有环; 要字典序最小就换 priority_queue</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 10.2 DFS 三色判环: 0 未访问, 1 在栈中, 2 已完成</span></span><br><span class="line"><span class="type">int</span> col[N]; <span class="type">bool</span> cyc;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u)</span></span>&#123;</span><br><span class="line">  col[u]=<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:e[u])&#123;<span class="keyword">if</span>(col[v]==<span class="number">1</span>)cyc=<span class="literal">true</span>;<span class="keyword">else</span> <span class="keyword">if</span>(col[v]==<span class="number">0</span>)<span class="built_in">dfs</span>(v);&#125;   <span class="comment">// 指向栈中节点 =&gt; 有环</span></span><br><span class="line">  col[u]=<span class="number">2</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 10.3 DAG 上 DP: 按拓扑序转移(最长路/计数/方案数); O(n+m)</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dagdp</span><span class="params">(<span class="type">int</span> n)</span></span>&#123;<span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;++i)&#123;<span class="type">int</span> u=ord[i];<span class="keyword">for</span>(<span class="keyword">auto</span> [v,w]:e[u])f[v]=<span class="built_in">max</span>(f[v],f[u]+w);&#125;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 有环图不能直接 DP；先 Tarjan 缩点成 DAG 再 DP 是通用套路。</p></blockquote><h4 id="11-最短路全家桶"><a href="#11-最短路全家桶" class="headerlink" title="11. 最短路全家桶"></a>11. 最短路全家桶</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 11.1 Dijkstra 堆优化: 不能有负权边, O(m log n)</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> INF=<span class="number">0x3f3f3f3f</span>;</span><br><span class="line"><span class="type">int</span> dis[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dijkstra</span><span class="params">(<span class="type">int</span> s)</span></span>&#123;</span><br><span class="line">  <span class="built_in">memset</span>(dis,<span class="number">0x3f</span>,<span class="keyword">sizeof</span> dis);</span><br><span class="line">  priority_queue&lt;pair&lt;<span class="type">int</span>,<span class="type">int</span>&gt;,vector&lt;pair&lt;<span class="type">int</span>,<span class="type">int</span>&gt;&gt;,greater&lt;&gt;&gt; q;</span><br><span class="line">  dis[s]=<span class="number">0</span>; q.<span class="built_in">push</span>(&#123;<span class="number">0</span>,s&#125;);</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;</span><br><span class="line">    <span class="keyword">auto</span> [d,u]=q.<span class="built_in">top</span>(); q.<span class="built_in">pop</span>();</span><br><span class="line">    <span class="keyword">if</span>(d&gt;dis[u])<span class="keyword">continue</span>;                                  <span class="comment">// 过期节点</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">auto</span> [v,w]:e[u])<span class="keyword">if</span>(dis[u]+w&lt;dis[v])&#123;dis[v]=dis[u]+w;q.<span class="built_in">push</span>(&#123;dis[v],v&#125;);&#125;&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 11.2 SPFA + SLF/LLL + 判负环: 最坏 O(nm), 可被构造数据卡死</span></span><br><span class="line"><span class="type">int</span> dis[N],cnt[N]; <span class="type">bool</span> inq[N];</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">spfa</span><span class="params">(<span class="type">int</span> s)</span></span>&#123;                              <span class="comment">// 返回 false &lt;=&gt; s 可达负环</span></span><br><span class="line">  <span class="built_in">memset</span>(dis,<span class="number">0x3f</span>,<span class="keyword">sizeof</span> dis); <span class="built_in">memset</span>(cnt,<span class="number">0</span>,<span class="keyword">sizeof</span> cnt); <span class="built_in">memset</span>(inq,<span class="number">0</span>,<span class="keyword">sizeof</span> inq);  <span class="comment">// 多测必须清空</span></span><br><span class="line">  deque&lt;<span class="type">int</span>&gt; q; dis[s]=<span class="number">0</span>; q.<span class="built_in">push_back</span>(s); inq[s]=<span class="number">1</span>;</span><br><span class="line">  <span class="type">long</span> <span class="type">long</span> sum=<span class="number">0</span>;                                         <span class="comment">// 队内 dis 之和, 供 LLL 用</span></span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;</span><br><span class="line">    <span class="type">int</span> u=q.<span class="built_in">front</span>(); q.<span class="built_in">pop_front</span>(); inq[u]=<span class="number">0</span>; sum-=dis[u];</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">auto</span> [v,w]:e[u])&#123;</span><br><span class="line">      <span class="keyword">if</span>(dis[u]+w&lt;dis[v])&#123;</span><br><span class="line">        dis[v]=dis[u]+w; cnt[v]=cnt[u]<span class="number">+1</span>;</span><br><span class="line">        <span class="keyword">if</span>(cnt[v]&gt;=n)<span class="keyword">return</span> <span class="literal">false</span>;                         <span class="comment">// 最短路经过 &gt;= n 条边 =&gt; 负环</span></span><br><span class="line">        <span class="keyword">if</span>(!inq[v])&#123;</span><br><span class="line">          inq[v]=<span class="number">1</span>; sum+=dis[v];</span><br><span class="line">          <span class="keyword">if</span>(!q.<span class="built_in">empty</span>()&amp;&amp;<span class="number">1LL</span>*dis[v]*q.<span class="built_in">size</span>()&lt;sum)q.<span class="built_in">push_front</span>(v);      <span class="comment">// LLL</span></span><br><span class="line">          <span class="keyword">else</span> <span class="keyword">if</span>(!q.<span class="built_in">empty</span>()&amp;&amp;dis[v]&lt;dis[q.<span class="built_in">front</span>()])q.<span class="built_in">push_front</span>(v);   <span class="comment">// SLF</span></span><br><span class="line">          <span class="keyword">else</span> q.<span class="built_in">push_back</span>(v);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <strong>以 s 为源点判不出负环，只说明 s 到不了负环</strong>，不代表图上没有负环。判整图负环要建超级源点 0 向所有点连权 0 边，再从 0 跑。<br>⚠️ 无负权边<strong>一律用 Dijkstra</strong>；SPFA 最坏 O(nm)，网格类数据可被轻易卡死。</p></blockquote><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 11.3 Floyd: 任意两点最短路, O(n^3), n &lt;= 500 量级</span></span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> k=<span class="number">1</span>;k&lt;=n;++k)                          <span class="comment">// k 必须在最外层!</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;++i)<span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">1</span>;j&lt;=n;++j)</span><br><span class="line">    <span class="keyword">if</span>(dis[i][k]+dis[k][j]&lt;dis[i][j])dis[i][j]=dis[i][k]+dis[k][j];</span><br><span class="line"><span class="comment">// 传递闭包: bitset&lt;N&gt; b[N]; for(k)for(i)if(b[i][k])b[i]|=b[k];  // O(n^3/64)</span></span><br><span class="line"><span class="comment">// 判负环: 存在 i 使 dis[i][i] &lt; 0</span></span><br><span class="line"><span class="comment">// 11.4 Johnson 全源最短路(简述): 有负权边但无负环, O(n*m*log m), 稀疏图优于 Floyd</span></span><br><span class="line"><span class="comment">// 超级源点 0 -&gt; 所有点(权 0), SPFA 求势能 h[]; 有负环则无解</span></span><br><span class="line"><span class="comment">// 重赋权 w&#x27;(u,v)=w(u,v)+h[u]-h[v] &gt;= 0, 对每个源点跑 Dijkstra; 还原 dis(u,v)=d&#x27;(u,v)-h[u]+h[v]</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ Floyd 的 INF 用 0x3f3f3f3f（两倍不溢出 int）；用 0x7fffffff 时 <code>dis[i][k]+dis[k][j]</code> 会溢出成负数。<br>⚠️ 11.4 为简述，代码未展开（待验证）；CSP 中全源最短路通常直接用 Floyd。</p></blockquote><h4 id="12-最小生成树"><a href="#12-最小生成树" class="headerlink" title="12. 最小生成树"></a>12. 最小生成树</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 12.1 Kruskal + 并查集: O(m log m), 稀疏图首选</span></span><br><span class="line"><span class="type">int</span> fa[N];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">find</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;<span class="keyword">return</span> fa[x]==x?x:fa[x]=<span class="built_in">find</span>(fa[x]);&#125;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Edge</span>&#123;<span class="type">int</span> u,v,w;&#125; eg[M];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">kruskal</span><span class="params">(<span class="type">int</span> n,<span class="type">int</span> m)</span></span>&#123;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;++i)fa[i]=i;</span><br><span class="line">  <span class="built_in">sort</span>(eg<span class="number">+1</span>,eg+m<span class="number">+1</span>,[](<span class="type">const</span> Edge&amp;a,<span class="type">const</span> Edge&amp;b)&#123;<span class="keyword">return</span> a.w&lt;b.w;&#125;);</span><br><span class="line">  <span class="type">int</span> sum=<span class="number">0</span>,used=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=m;++i)&#123;</span><br><span class="line">    <span class="type">int</span> x=<span class="built_in">find</span>(eg[i].u),y=<span class="built_in">find</span>(eg[i].v);</span><br><span class="line">    <span class="keyword">if</span>(x==y)<span class="keyword">continue</span>;</span><br><span class="line">    fa[x]=y; sum+=eg[i].w; <span class="keyword">if</span>(++used==n<span class="number">-1</span>)<span class="keyword">break</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> used==n<span class="number">-1</span>?sum:<span class="number">-1</span>;                     <span class="comment">// -1 表示不连通</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 12.2 Prim 朴素: O(n^2), 稠密图/完全图优于 Kruskal; g[][] 为邻接矩阵</span></span><br><span class="line"><span class="type">int</span> dis[N]; <span class="type">bool</span> vis[N];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">prim</span><span class="params">(<span class="type">int</span> n)</span></span>&#123;</span><br><span class="line">  <span class="built_in">memset</span>(dis,<span class="number">0x3f</span>,<span class="keyword">sizeof</span> dis); <span class="built_in">memset</span>(vis,<span class="number">0</span>,<span class="keyword">sizeof</span> vis); dis[<span class="number">1</span>]=<span class="number">0</span>; <span class="type">int</span> sum=<span class="number">0</span>;   <span class="comment">// 多测要清 vis</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;++i)&#123;</span><br><span class="line">    <span class="type">int</span> u=<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> v=<span class="number">1</span>;v&lt;=n;++v)<span class="keyword">if</span>(!vis[v]&amp;&amp;(!u||dis[v]&lt;dis[u]))u=v;</span><br><span class="line">    <span class="keyword">if</span>(dis[u]==INF)<span class="keyword">return</span> <span class="number">-1</span>;                              <span class="comment">// 不连通</span></span><br><span class="line">    vis[u]=<span class="number">1</span>; sum+=dis[u];</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> v=<span class="number">1</span>;v&lt;=n;++v)<span class="keyword">if</span>(!vis[v]&amp;&amp;g[u][v]&lt;dis[v])dis[v]=g[u][v];&#125;</span><br><span class="line">  <span class="keyword">return</span> sum;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 12.3 非严格次小生成树(简述): 先求 MST, 枚举每条非树边 (u,v,w),</span></span><br><span class="line"><span class="comment">//      用倍增求 MST 上 u-v 路径的最大边权 mx, ans=min(ans, sum-mx+w); O(m log m)</span></span><br><span class="line"><span class="comment">// 严格次小生成树: 再维护&quot;严格次大边权&quot;, 当 w == mx 时用严格次大值替换</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 堆优化 Prim 无法 decrease-key，复杂度不优于 Kruskal 且常数更大；稠密图直接用 O(n^2) 版本。<br>⚠️ 12.3 为简述，代码未展开（待验证）；了解「枚举非树边 + 路径最大值」即可。</p></blockquote><h4 id="13-Tarjan-全家桶"><a href="#13-Tarjan-全家桶" class="headerlink" title="13. Tarjan 全家桶"></a>13. Tarjan 全家桶</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 13.1 强连通分量 SCC + 缩点: O(n+m)。scc 编号是反拓扑序(编号小 =&gt; 靠近汇点)</span></span><br><span class="line">vector&lt;<span class="type">int</span>&gt; e[N];</span><br><span class="line"><span class="type">int</span> dfn[N],low[N],stk[N],top,tim,scc[N],scnt,sz[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">tarjan</span><span class="params">(<span class="type">int</span> u)</span></span>&#123;</span><br><span class="line">  dfn[u]=low[u]=++tim; stk[++top]=u;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:e[u])&#123;</span><br><span class="line">    <span class="keyword">if</span>(!dfn[v])&#123;<span class="built_in">tarjan</span>(v);low[u]=<span class="built_in">min</span>(low[u],low[v]);&#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(!scc[v])low[u]=<span class="built_in">min</span>(low[u],dfn[v]);             <span class="comment">// v 还在栈中</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span>(low[u]==dfn[u])&#123;++scnt;<span class="type">int</span> v;<span class="keyword">do</span>&#123;v=stk[top--];scc[v]=scnt;++sz[scnt];&#125;<span class="keyword">while</span>(v!=u);&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 调用: for(int i=1;i&lt;=n;++i)if(!dfn[i])tarjan(i);</span></span><br><span class="line"><span class="comment">// 缩点: for(u)for(v:e[u])if(scc[u]!=scc[v])add(scc[u],scc[v]);  // 先排序去重</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>else if(!scc[v])</code> 不能写成 <code>else</code>：指向已定型 SCC 的边用 dfn[v] 更新会使 low 变小而出错。</p></blockquote><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 13.2 割点 &amp; 桥(无向图, 支持重边): 链式前向星 cnt 从 1 开始, O(n+m)</span></span><br><span class="line"><span class="type">int</span> hd[N],to[M],nxt[M],cnt=<span class="number">1</span>;</span><br><span class="line"><span class="type">int</span> dfn[N],low[N],tim,root;</span><br><span class="line"><span class="type">bool</span> cut[N],isbridge[M];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> v)</span></span>&#123;to[++cnt]=v;nxt[cnt]=hd[u];hd[u]=cnt;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">tarjan</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> inedge)</span></span>&#123;</span><br><span class="line">  dfn[u]=low[u]=++tim; <span class="type">int</span> child=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=hd[u];i;i=nxt[i])&#123;<span class="type">int</span> v=to[i];</span><br><span class="line">    <span class="keyword">if</span>(i==(inedge^<span class="number">1</span>))<span class="keyword">continue</span>;                             <span class="comment">// 只跳过&quot;来时那条边&quot;, 重边不受影响</span></span><br><span class="line">    <span class="keyword">if</span>(!dfn[v])&#123;</span><br><span class="line">      ++child; <span class="built_in">tarjan</span>(v,i); low[u]=<span class="built_in">min</span>(low[u],low[v]);</span><br><span class="line">      <span class="keyword">if</span>(low[v]&gt;dfn[u])isbridge[i]=isbridge[i^<span class="number">1</span>]=<span class="literal">true</span>;      <span class="comment">// 桥</span></span><br><span class="line">      <span class="keyword">if</span>(low[v]&gt;=dfn[u]&amp;&amp;u!=root)cut[u]=<span class="literal">true</span>;               <span class="comment">// 割点(非根)</span></span><br><span class="line">    &#125;<span class="keyword">else</span> low[u]=<span class="built_in">min</span>(low[u],dfn[v]);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span>(u==root&amp;&amp;child&gt;<span class="number">1</span>)cut[u]=<span class="literal">true</span>;                         <span class="comment">// 根: 至少两个孩子才是割点</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 13.3 边双 eDCC(简述): 删掉所有桥后的连通块; 求完桥对非桥边 DFS 染色即可, O(n+m)</span></span><br><span class="line"><span class="comment">// 13.4 点双 vDCC(简述): 栈存&quot;点&quot;, low[v]&gt;=dfn[u] 时弹栈直到 v, 再把 u 也压入该分量;</span></span><br><span class="line"><span class="comment">//      每个割点属于多个点双, 点双缩点后得到&quot;圆方树&quot;(约 2n 个点)</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 割点判定 <code>low[v]&gt;=dfn[u]</code>，桥判定 <code>low[v]&gt;dfn[u]</code>，差一个等号，写反全错。<br>⚠️ 用 <code>v!=fa</code> 判父亲在有重边时会漏判桥（第二条重边被当成回边）；必须用<strong>边编号</strong>判断。<br>⚠️ 13.3&#x2F;13.4 为简述，代码未展开（待验证）；CSP 中掌握割点&#x2F;桥即可，点双极少考。</p></blockquote><h4 id="14-2-SAT"><a href="#14-2-SAT" class="headerlink" title="14. 2-SAT"></a>14. 2-SAT</h4><p>每个变量 i 拆两点：i 表示「真」，i+n 表示「假」。约束 (a 取 av) 或 (b 取 bv) 连两条<strong>逆否</strong>边。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 2-SAT: O(n+m)。点 i = 真, i+n = 假</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add_clause</span><span class="params">(<span class="type">int</span> a,<span class="type">bool</span> av,<span class="type">int</span> b,<span class="type">bool</span> bv)</span></span>&#123;  <span class="comment">// (x_a == av) 或 (x_b == bv)</span></span><br><span class="line">  <span class="type">int</span> na=av?a+n:a, nb=bv?b+n:b;                <span class="comment">// na = ¬(a==av), nb = ¬(b==bv)</span></span><br><span class="line">  e[na].<span class="built_in">push_back</span>(bv?b:b+n);                   <span class="comment">// ¬(a==av) -&gt; (b==bv)</span></span><br><span class="line">  e[nb].<span class="built_in">push_back</span>(av?a:a+n);                   <span class="comment">// ¬(b==bv) -&gt; (a==av)</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">solve</span><span class="params">()</span></span>&#123;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=<span class="number">2</span>*n;++i)<span class="keyword">if</span>(!dfn[i])<span class="built_in">tarjan</span>(i);</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;++i)<span class="keyword">if</span>(scc[i]==scc[i+n])<span class="keyword">return</span> <span class="literal">false</span>;   <span class="comment">// 无解</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;++i)val[i]=scc[i]&lt;scc[i+n];             <span class="comment">// Tarjan 编号是反拓扑序</span></span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 取值方向：Tarjan 的 SCC 编号是<strong>反拓扑序</strong>，故 <code>scc[i] &lt; scc[i+n]</code> 时 x_i 取真；换成 Kosaraju 或按拓扑序编号要反向。<br>⚠️ 常见改写：<code>x_a 或 x_b</code> → add_clause(a,1,b,1)；<code>x_a 蕴含 x_b</code> → add_clause(a,0,b,1)；<code>x_a 与 x_b 不同</code> → add_clause(a,1,b,0) + add_clause(a,0,b,1)。</p></blockquote><h4 id="15-最近公共祖先-LCA"><a href="#15-最近公共祖先-LCA" class="headerlink" title="15. 最近公共祖先 LCA"></a>15. 最近公共祖先 LCA</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 15.1 倍增法: 预处理 O(n log n), 单次查询 O(log n)</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> LG=<span class="number">20</span>;                               <span class="comment">// 2^20 &gt; 1e6; n 到 1e5 用 17</span></span><br><span class="line"><span class="type">int</span> dep[N],fa[N][LG];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> p)</span></span>&#123;</span><br><span class="line">  dep[u]=dep[p]<span class="number">+1</span>; fa[u][<span class="number">0</span>]=p;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">1</span>;j&lt;LG;++j)fa[u][j]=fa[fa[u][j<span class="number">-1</span>]][j<span class="number">-1</span>];</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:e[u])<span class="keyword">if</span>(v!=p)<span class="built_in">dfs</span>(v,u);&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">lca</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> v)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(dep[u]&lt;dep[v])<span class="built_in">swap</span>(u,v);</span><br><span class="line">  <span class="type">int</span> d=dep[u]-dep[v];</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>;d;++j,d&gt;&gt;=<span class="number">1</span>)<span class="keyword">if</span>(d&amp;<span class="number">1</span>)u=fa[u][j];               <span class="comment">// 先拉到同深度</span></span><br><span class="line">  <span class="keyword">if</span>(u==v)<span class="keyword">return</span> u;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> j=LG<span class="number">-1</span>;j&gt;=<span class="number">0</span>;--j)<span class="keyword">if</span>(fa[u][j]!=fa[v][j])u=fa[u][j],v=fa[v][j];</span><br><span class="line">  <span class="keyword">return</span> fa[u][<span class="number">0</span>];</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 树上两点距离: dep[u]+dep[v]-2*dep[lca(u,v)]</span></span><br><span class="line"><span class="comment">// 15.2 Tarjan 离线 LCA: O(n + m*alpha(n)), 适合大量询问</span></span><br><span class="line"><span class="type">int</span> fa[N],ans[Q]; <span class="type">bool</span> vis[N];</span><br><span class="line">vector&lt;pair&lt;<span class="type">int</span>,<span class="type">int</span>&gt;&gt; qry[N];                  <span class="comment">// qry[u] = &#123;(v, 询问编号)&#125;</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">find</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;<span class="keyword">return</span> fa[x]==x?x:fa[x]=<span class="built_in">find</span>(fa[x]);&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">tarjan</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> p)</span></span>&#123;</span><br><span class="line">  vis[u]=<span class="number">1</span>; fa[u]=u;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:e[u])<span class="keyword">if</span>(v!=p)&#123;<span class="built_in">tarjan</span>(v,u);fa[v]=u;&#125;            <span class="comment">// 回溯时把子树并到 u</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">auto</span> [v,id]:qry[u])<span class="keyword">if</span>(vis[v])ans[id]=<span class="built_in">find</span>(v);        <span class="comment">// v 已访问 =&gt; LCA = find(v)</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 根的父亲初始化为 0 且 <code>fa[0][j]=0</code>，否则倍增跳出树外会读到脏值。LG 至少取 log2(n)+1。<br>⚠️ 离线算法必须先读完所有询问；<code>fa[v]=u</code> 必须在子节点 DFS <strong>返回之后</strong>执行。</p></blockquote><h4 id="16-树上问题"><a href="#16-树上问题" class="headerlink" title="16. 树上问题"></a>16. 树上问题</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 16.1 树的直径: 两次 DFS/BFS, O(n); 不能有负权边</span></span><br><span class="line"><span class="type">int</span> far; <span class="type">long</span> <span class="type">long</span> best;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> p,<span class="type">long</span> <span class="type">long</span> d)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(d&gt;best)best=d,far=u;</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">auto</span> [v,w]:e[u])<span class="keyword">if</span>(v!=p)<span class="built_in">dfs</span>(v,u,d+w);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 调用: best=-1,dfs(1,0,0); best=-1,dfs(far,0,0); 此时 best 即直径长度</span></span><br><span class="line"><span class="comment">// 16.2 树的直径(树形 DP): O(n), 可处理负权边</span></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> dp[N],D;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs2</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> p)</span></span>&#123;</span><br><span class="line">  dp[u]=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">auto</span> [v,w]:e[u])<span class="keyword">if</span>(v!=p)&#123;<span class="built_in">dfs2</span>(v,u);</span><br><span class="line">    D=<span class="built_in">max</span>(D,dp[u]+dp[v]+w);                                <span class="comment">// 先用旧 dp[u] 再更新, 避免同子树走两次</span></span><br><span class="line">    dp[u]=<span class="built_in">max</span>(dp[u],dp[v]+w);&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 16.3 树的重心: O(n)。删去后最大连通块 &lt;= n/2; 重心至多两个且相邻</span></span><br><span class="line"><span class="type">int</span> sz[N],mx[N],ct;                           <span class="comment">// 依赖全局 n</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs3</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> p)</span></span>&#123;</span><br><span class="line">  sz[u]=<span class="number">1</span>; mx[u]=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:e[u])<span class="keyword">if</span>(v!=p)&#123;<span class="built_in">dfs3</span>(v,u);sz[u]+=sz[v];mx[u]=<span class="built_in">max</span>(mx[u],sz[v]);&#125;</span><br><span class="line">  mx[u]=<span class="built_in">max</span>(mx[u],n-sz[u]);                                <span class="comment">// &quot;向上&quot;的那棵子树</span></span><br><span class="line">  <span class="keyword">if</span>(!ct||mx[u]&lt;mx[ct])ct=u;                               <span class="comment">// 重量最小的点即重心(注意 ct 初值)</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 16.4 树上差分: 先做修改, 最后一遍 DFS 求子树和还原; O(n + k log n)</span></span><br><span class="line"><span class="type">int</span> d[N];                                      <span class="comment">// 差分数组; 依赖 15.1 的 lca / fa[][]</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add_point</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y,<span class="type">int</span> v)</span></span>&#123;<span class="type">int</span> l=<span class="built_in">lca</span>(x,y); d[x]+=v; d[y]+=v; d[l]-=v; d[fa[l][<span class="number">0</span>]]-=v;&#125;  <span class="comment">// 点差分</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add_edge</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y,<span class="type">int</span> v)</span></span>&#123;<span class="type">int</span> l=<span class="built_in">lca</span>(x,y); d[x]+=v; d[y]+=v; d[l]-=<span class="number">2</span>*v;&#125;                <span class="comment">// 边差分</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs4</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> p)</span></span>&#123;<span class="keyword">for</span>(<span class="type">int</span> v:e[u])<span class="keyword">if</span>(v!=p)&#123;<span class="built_in">dfs4</span>(v,u);d[u]+=d[v];&#125;&#125;  <span class="comment">// 自底向上求和还原</span></span><br><span class="line"><span class="comment">// 16.5 欧拉序: DFS 时每到一个点(含回溯)都记录, 长度 2n-1</span></span><br><span class="line"><span class="comment">// pos[u] = u 首次出现的位置; LCA(u,v) = E[pos[u]..pos[v]] 中深度最小的点</span></span><br><span class="line"><span class="type">int</span> E[<span class="number">2</span>*N],dd[<span class="number">2</span>*N],pos[N],tot;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs5</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> p,<span class="type">int</span> d)</span></span>&#123;</span><br><span class="line">  E[++tot]=u; dd[tot]=d; pos[u]=tot;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:e[u])<span class="keyword">if</span>(v!=p)&#123;<span class="built_in">dfs5</span>(v,u,d<span class="number">+1</span>);E[++tot]=u;dd[tot]=d;&#125;&#125;</span><br><span class="line"><span class="comment">// 配 ST 表: 预处理 O(n log n), 查询 O(1)</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 点差分要减到 <code>fa[lca]</code>，边差分只减到 lca（减 2v）；根的 <code>fa[root][0]=0</code>，减在下标 0 上不影响答案。<br>⚠️ 另一种「欧拉序」是括号序（入栈出栈各记一次，长度 2n），用于把子树变成连续区间 <code>[in[u],out[u]]</code>，两者别混。</p></blockquote><h4 id="17-二分图与网络流"><a href="#17-二分图与网络流" class="headerlink" title="17. 二分图与网络流"></a>17. 二分图与网络流</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 17.1 染色判定二分图: O(n+m)。存在奇环 &lt;=&gt; 不是二分图</span></span><br><span class="line"><span class="type">int</span> col[N];                                    <span class="comment">// 0 未染色, 1 / -1 两色</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> c)</span></span>&#123;col[u]=c;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:e[u])&#123;<span class="keyword">if</span>(col[v]==c)<span class="keyword">return</span> <span class="literal">false</span>;<span class="keyword">if</span>(!col[v]&amp;&amp;!<span class="built_in">dfs</span>(v,-c))<span class="keyword">return</span> <span class="literal">false</span>;&#125;  <span class="comment">// 同色 =&gt; 奇环</span></span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;&#125;</span><br><span class="line"><span class="comment">// 主函数: for i if(!col[i] &amp;&amp; !dfs(i,1)) &#123; 不是二分图 &#125;  —— 图可能不连通, 每个未染色点都要跑</span></span><br><span class="line"><span class="comment">// 17.2 匈牙利(Kuhn) 二分图最大匹配: O(V*E)</span></span><br><span class="line"><span class="type">int</span> match[N]; <span class="type">bool</span> vis[N];</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">dfs2</span><span class="params">(<span class="type">int</span> u)</span></span>&#123;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:e[u])&#123;                             <span class="comment">// e 只存 左部 -&gt; 右部 的边</span></span><br><span class="line">    <span class="keyword">if</span>(vis[v])<span class="keyword">continue</span>;</span><br><span class="line">    vis[v]=<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span>(!match[v]||<span class="built_in">dfs2</span>(match[v]))&#123;match[v]=u;<span class="keyword">return</span> <span class="literal">true</span>;&#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">hungary</span><span class="params">(<span class="type">int</span> n)</span></span>&#123;</span><br><span class="line">  <span class="type">int</span> res=<span class="number">0</span>; <span class="built_in">memset</span>(match,<span class="number">0</span>,<span class="keyword">sizeof</span> match);                        <span class="comment">// 多测要清 match</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;++i)&#123;<span class="built_in">memset</span>(vis,<span class="number">0</span>,<span class="keyword">sizeof</span> vis);<span class="keyword">if</span>(<span class="built_in">dfs2</span>(i))++res;&#125;   <span class="comment">// vis 只标记右部, 每轮清空</span></span><br><span class="line">  <span class="keyword">return</span> res;&#125;                                 <span class="comment">// match 只记&quot;右部匹配到谁&quot;, 左部不需要 match</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><table><thead><tr><th>König 定理与常用转化（二分图）</th><th>公式</th></tr></thead><tbody><tr><td>最大匹配</td><td>&#x3D; 最小点覆盖</td></tr><tr><td>最大独立集</td><td>&#x3D; 总点数 - 最大匹配</td></tr><tr><td>最小边覆盖</td><td>&#x3D; 总点数 - 最大匹配</td></tr><tr><td>DAG 最小路径覆盖</td><td>&#x3D; n - 拆点二分图最大匹配</td></tr></tbody></table><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 17.3 Dinic 最大流: 一般图 O(n^2 m), 单位容量二分图 O(m*sqrt(n))</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Edge</span>&#123;<span class="type">int</span> to,nxt,cap;&#125; e[M];</span><br><span class="line"><span class="type">int</span> hd[N],cnt=<span class="number">1</span>,dep[N],cur[N],S,T;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> v,<span class="type">int</span> c)</span></span>&#123;e[++cnt]=&#123;v,hd[u],c&#125;;hd[u]=cnt;e[++cnt]=&#123;u,hd[v],<span class="number">0</span>&#125;;hd[v]=cnt;&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">bfs</span><span class="params">()</span></span>&#123;                                    <span class="comment">// 在残量网络上分层</span></span><br><span class="line">  <span class="built_in">memset</span>(dep,<span class="number">0</span>,<span class="keyword">sizeof</span> dep); queue&lt;<span class="type">int</span>&gt; q; dep[S]=<span class="number">1</span>; q.<span class="built_in">push</span>(S);</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;<span class="type">int</span> u=q.<span class="built_in">front</span>();q.<span class="built_in">pop</span>();</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=hd[u];i;i=e[i].nxt)&#123;<span class="type">int</span> v=e[i].to;<span class="keyword">if</span>(e[i].cap&amp;&amp;!dep[v])&#123;dep[v]=dep[u]<span class="number">+1</span>;q.<span class="built_in">push</span>(v);&#125;&#125;&#125;</span><br><span class="line">  <span class="keyword">return</span> dep[T];&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> f)</span></span>&#123;                          <span class="comment">// 当前弧优化</span></span><br><span class="line">  <span class="keyword">if</span>(u==T)<span class="keyword">return</span> f;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> &amp;i=cur[u];i;i=e[i].nxt)&#123;<span class="type">int</span> v=e[i].to;</span><br><span class="line">    <span class="keyword">if</span>(e[i].cap&amp;&amp;dep[v]==dep[u]<span class="number">+1</span>)&#123;<span class="type">int</span> d=<span class="built_in">dfs</span>(v,<span class="built_in">min</span>(f,e[i].cap));<span class="keyword">if</span>(d)&#123;e[i].cap-=d;e[i^<span class="number">1</span>].cap+=d;<span class="keyword">return</span> d;&#125;&#125;&#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">dinic</span><span class="params">()</span></span>&#123;<span class="type">int</span> flow=<span class="number">0</span>,f; <span class="keyword">while</span>(<span class="built_in">bfs</span>())&#123;<span class="built_in">memcpy</span>(cur,hd,<span class="keyword">sizeof</span> hd);<span class="keyword">while</span>((f=<span class="built_in">dfs</span>(S,<span class="number">0x3f3f3f3f</span>)))flow+=f;&#125; <span class="keyword">return</span> flow;&#125;</span><br><span class="line"><span class="comment">// 二分图匹配建模: S-&gt;左部容量 1, 右部-&gt;T 容量 1, 中间边容量 1, 答案即最大流</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 无向边要建<strong>两条有向边</strong>各带反向边，不能只 add(u,v,c) 一次。</p></blockquote><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 17.4 费用流(SSP/EK, SPFA 找单位费用最小增广路): 单次 O(nm), 总 O(n*m*f)</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Edge</span>&#123;<span class="type">int</span> to,nxt,cap,w;&#125; e[M];</span><br><span class="line"><span class="type">int</span> hd[N],cnt=<span class="number">1</span>,dis[N],inq[N],pre[N],pe[N],S,T;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> v,<span class="type">int</span> c,<span class="type">int</span> w)</span></span>&#123;e[++cnt]=&#123;v,hd[u],c,w&#125;;hd[u]=cnt;e[++cnt]=&#123;u,hd[v],<span class="number">0</span>,-w&#125;;hd[v]=cnt;&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">spfa</span><span class="params">()</span></span>&#123;</span><br><span class="line">  <span class="built_in">memset</span>(dis,<span class="number">0x3f</span>,<span class="keyword">sizeof</span> dis); <span class="built_in">memset</span>(inq,<span class="number">0</span>,<span class="keyword">sizeof</span> inq);</span><br><span class="line">  queue&lt;<span class="type">int</span>&gt; q; dis[S]=<span class="number">0</span>; q.<span class="built_in">push</span>(S); inq[S]=<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;<span class="type">int</span> u=q.<span class="built_in">front</span>();q.<span class="built_in">pop</span>();inq[u]=<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=hd[u];i;i=e[i].nxt)&#123;<span class="type">int</span> v=e[i].to;</span><br><span class="line">      <span class="keyword">if</span>(e[i].cap&amp;&amp;dis[u]+e[i].w&lt;dis[v])&#123;dis[v]=dis[u]+e[i].w;pre[v]=u;pe[v]=i;<span class="keyword">if</span>(!inq[v])inq[v]=<span class="number">1</span>,q.<span class="built_in">push</span>(v);&#125;&#125;&#125;</span><br><span class="line">  <span class="keyword">return</span> dis[T]&lt;<span class="number">0x3f3f3f3f</span>;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">mcmf</span><span class="params">(<span class="type">int</span> &amp;flow,<span class="type">int</span> &amp;cost)</span></span>&#123;</span><br><span class="line">  flow=cost=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">while</span>(<span class="built_in">spfa</span>())&#123;<span class="type">int</span> f=<span class="number">0x3f3f3f3f</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> v=T;v!=S;v=pre[v])f=<span class="built_in">min</span>(f,e[pe[v]].cap);</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> v=T;v!=S;v=pre[v])&#123;e[pe[v]].cap-=f;e[pe[v]^<span class="number">1</span>].cap+=f;cost+=f*e[pe[v]].w;&#125;</span><br><span class="line">    flow+=f;&#125;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 有负费用边时 SPFA 版仍正确；换 Dijkstra 版必须先用 SPFA 求初始势能 h[]，每轮增广后 <code>h[i]+=dis[i]</code>（Primal-Dual）。</p></blockquote><h4 id="18-差分约束-欧拉路径-基环树"><a href="#18-差分约束-欧拉路径-基环树" class="headerlink" title="18. 差分约束 &#x2F; 欧拉路径 &#x2F; 基环树"></a>18. 差分约束 &#x2F; 欧拉路径 &#x2F; 基环树</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 18.1 差分约束(简述):</span></span><br><span class="line"><span class="comment">// x_i - x_j &lt;= c  =&gt;  加边 j -&gt; i, 权 c, 跑最短路</span></span><br><span class="line"><span class="comment">// x_i - x_j &gt;= c  =&gt;  加边 j -&gt; i, 权 c, 跑最长路(或改写成 x_j - x_i &lt;= -c 跑最短路)</span></span><br><span class="line"><span class="comment">// x_i - x_j == c  =&gt;  上面两条都加</span></span><br><span class="line"><span class="comment">// 超级源点 0 向所有点连权 0 的边, dis[0]=0; 有负环 =&gt; 无解, 否则 x_i = dis[i]</span></span><br><span class="line"><span class="comment">// 求最大解跑最短路, 求最小解跑最长路; 用 SPFA 判负环, 最坏 O(nm)</span></span><br><span class="line"><span class="comment">// 18.2 欧拉路径/回路 Hierholzer: O(n+m), 用栈存&quot;回溯点&quot;, 答案逆序</span></span><br><span class="line"><span class="type">int</span> hd[N],to[M],nxt[M],cnt=<span class="number">1</span>;</span><br><span class="line"><span class="type">bool</span> vis[M];</span><br><span class="line"><span class="type">int</span> stk[M],top;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u)</span></span>&#123;</span><br><span class="line">  <span class="keyword">while</span>(hd[u])&#123;</span><br><span class="line">    <span class="type">int</span> i=hd[u]; hd[u]=nxt[i];                 <span class="comment">// 当前弧: 边用完就删</span></span><br><span class="line">    <span class="keyword">if</span>(vis[i])<span class="keyword">continue</span>;</span><br><span class="line">    vis[i]=<span class="number">1</span>; vis[i^<span class="number">1</span>]=<span class="number">1</span>;                      <span class="comment">// 无向图: 反向边一起标记</span></span><br><span class="line">    <span class="built_in">dfs</span>(to[i]);&#125;</span><br><span class="line">  stk[++top]=u;                                <span class="comment">// 回溯时入栈 =&gt; 逆序输出</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 输出: while(top) printf(&quot;%d &quot;, stk[top--]);</span></span><br><span class="line"><span class="comment">// 18.3 基环树: n 点 n 边的连通无向图, 恰好一个环, 环上每点挂一棵树</span></span><br><span class="line"><span class="comment">// 找环(拓扑剥叶): 不断删度为 1 的点, 剩下的就是环上的点</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">find_circle</span><span class="params">(<span class="type">int</span> n)</span></span>&#123;queue&lt;<span class="type">int</span>&gt; q; <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;++i)<span class="keyword">if</span>(deg[i]==<span class="number">1</span>)q.<span class="built_in">push</span>(i);</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;<span class="type">int</span> u=q.<span class="built_in">front</span>();q.<span class="built_in">pop</span>();oncir[u]=<span class="literal">false</span>;<span class="keyword">for</span>(<span class="type">int</span> v:e[u])<span class="keyword">if</span>(--deg[v]==<span class="number">1</span>)q.<span class="built_in">push</span>(v);&#125;&#125;</span><br><span class="line"><span class="comment">// 基环内向树(每点出度 1): 常配合&quot;跳父亲 + 访问标记&quot;找环, 或直接 Tarjan</span></span><br><span class="line"><span class="comment">// 处理套路: 断环成链 —— 枚举环上一条边 (u,v), 分别&quot;删边&quot;和&quot;强制选边&quot;做两次树形 DP 取最优</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 超级源点不可省：只从某点跑会漏掉不可达部分的负环。建图方向口诀：<code>x_i &lt;= x_j + c</code> → 边 <code>j → i</code> 权 c。<br>⚠️ 欧拉路径判定：<strong>非零度点连通</strong> 且（回路：所有点度为偶 &#x2F; 有向图入度&#x3D;出度）或（路径：恰好两个奇度点，从奇度点出发；有向图起点出-入&#x3D;1，终点入-出&#x3D;1）。<br>⚠️ 有向图不能盲目标记反向边 <code>i^1</code>——反向边可能是真实存在的边；有向图只标记当前边。<br>⚠️ 基环树断环时<strong>两种情况都要算</strong>（删边 &#x2F; 强制选边）；环上 DP 注意不能同时选相邻环点。</p></blockquote><h3 id="附：CSP-图论易错清单"><a href="#附：CSP-图论易错清单" class="headerlink" title="附：CSP 图论易错清单"></a>附：CSP 图论易错清单</h3><ul><li>数组大小：无向图边数组开 2m，Dinic 边数组开 2*(m+n)，SCC 缩点后重边先去重。</li><li>1-based 与 0-based 混用是最高频 RE&#x2F;WA 来源；本页统一 1-based，输入 0-based 时先 +1。</li><li>多测务必清空 dfn&#x2F;low&#x2F;scc&#x2F;tim&#x2F;scnt&#x2F;hd&#x2F;cnt&#x2F;tot 等全局量。</li><li><code>0x3f3f3f3f</code> 做 INF 可安全相加两次；不要用 <code>0x7fffffff</code>。</li><li>递归深度：链式图 DFS 可达 1e5 层，必要时改迭代或改用 BFS。</li><li>无负权边优先 Dijkstra；SPFA 在 CSP 中容易被卡到 O(nm)。</li><li>树题先想「是否要换根 &#x2F; 是否用 LCA + 差分」再动手写暴力。</li></ul><h2 id="第-03-章-动态规划"><a href="#第-03-章-动态规划" class="headerlink" title="第 03 章 动态规划"></a>第 03 章 动态规划</h2><p>CSP 中 DP 是第 3~5 题的主力，常见形态是「线性&#x2F;树形&#x2F;状压 + 一个优化」。先定状态，再定转移顺序，最后估复杂度。</p><table><thead><tr><th>模型</th><th>状态</th><th>复杂度</th><th>常见优化</th></tr></thead><tbody><tr><td>线性</td><td>f[i] &#x2F; f[i][j]</td><td>O(n) ~ O(n^2)</td><td>前缀和、滚动数组</td></tr><tr><td>背包</td><td>f[j]</td><td>O(nW)</td><td>二进制拆分、单调队列、bitset</td></tr><tr><td>区间</td><td>f[i][j]</td><td>O(n^3)</td><td>四边形不等式 → O(n^2)</td></tr><tr><td>树形</td><td>f[u][…]</td><td>O(nm)</td><td>换根、上下界剪枝</td></tr><tr><td>状压</td><td>f[S] &#x2F; f[S][i]</td><td>O(2^n * n)</td><td>枚举子集、轮廓线</td></tr><tr><td>数位</td><td>f[pos][state]</td><td>O(位数 * 状态数)</td><td>记忆化</td></tr><tr><td>期望</td><td>E[u]</td><td>同图 DP</td><td>DAG 逆推</td></tr></tbody></table><h3 id="1-线性-DP-与状态设计"><a href="#1-线性-DP-与状态设计" class="headerlink" title="1. 线性 DP 与状态设计"></a>1. 线性 DP 与状态设计</h3><p>状态设计三步：<strong>① 阶段</strong>（已处理到哪）→ <strong>② 附加信息</strong>（还需知道什么才能转移）→ <strong>③ 值</strong>（最值 &#x2F; 计数 &#x2F; 可行性）。能压维就压维：f[i][<em>] 只由 f[i-1][</em>] 转移就用滚动数组；转移是连续区间就用前缀和。</p><h4 id="1-1-数字三角形（滚动数组）"><a href="#1-1-数字三角形（滚动数组）" class="headerlink" title="1.1 数字三角形（滚动数组）"></a>1.1 数字三角形（滚动数组）</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 数字三角形: 自底向上, 一维滚动. O(n^2) 时间, O(n) 空间</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= n; j++) f[j] = a[n][j];          <span class="comment">// 最底层</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = n - <span class="number">1</span>; i &gt;= <span class="number">1</span>; i--)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= i; j++)</span><br><span class="line">    f[j] = <span class="built_in">max</span>(f[j], f[j + <span class="number">1</span>]) + a[i][j];             <span class="comment">// f[j]/f[j+1] 都是下一层</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="1-2-最大子段和"><a href="#1-2-最大子段和" class="headerlink" title="1.2 最大子段和"></a>1.2 最大子段和</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 最大子段和: cur = 以当前位置结尾的最大和. O(n)</span></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> best = <span class="number">-1e18</span>, cur = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)</span><br><span class="line">  cur = <span class="built_in">max</span>(cur + a[i], (<span class="type">long</span> <span class="type">long</span>)a[i]), best = <span class="built_in">max</span>(best, cur);</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="1-3-多维-DP（方格取数，四维压三维）"><a href="#1-3-多维-DP（方格取数，四维压三维）" class="headerlink" title="1.3 多维 DP（方格取数，四维压三维）"></a>1.3 多维 DP（方格取数，四维压三维）</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 方格取数: 两人同时从 (1,1) 走到 (n,n), 同格只算一次</span></span><br><span class="line"><span class="comment">// f[k][i][j]: 走了 k 步, 两人分别在 (i,k-i) 和 (j,k-j). O(n^3)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">2</span>; k &lt;= <span class="number">2</span> * n; k++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= n; j++) &#123;</span><br><span class="line">      <span class="type">int</span> y1 = k - i, y2 = k - j;                       <span class="comment">// 行号 -&gt; 列号</span></span><br><span class="line">      <span class="keyword">if</span> (y1 &lt; <span class="number">1</span> || y1 &gt; n || y2 &lt; <span class="number">1</span> || y2 &gt; n) <span class="keyword">continue</span>;</span><br><span class="line">      <span class="type">long</span> <span class="type">long</span> add = (i == j) ? a[i][y1] : a[i][y1] + a[j][y2];</span><br><span class="line">      f[k &amp; <span class="number">1</span>][i][j] = <span class="built_in">max</span>(<span class="built_in">max</span>(f[~k &amp; <span class="number">1</span>][i][j], f[~k &amp; <span class="number">1</span>][i<span class="number">-1</span>][j]),</span><br><span class="line">                           <span class="built_in">max</span>(f[~k &amp; <span class="number">1</span>][i][j<span class="number">-1</span>], f[~k &amp; <span class="number">1</span>][i<span class="number">-1</span>][j<span class="number">-1</span>])) + add;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 滚动数组务必确认「本轮用到的旧值不会被本轮覆盖」；多维滚动先想清楚滚掉哪一维、读写是否同层。</p></blockquote><h3 id="2-背包九讲精炼"><a href="#2-背包九讲精炼" class="headerlink" title="2. 背包九讲精炼"></a>2. 背包九讲精炼</h3><h4 id="2-1-01-背包"><a href="#2-1-01-背包" class="headerlink" title="2.1 01 背包"></a>2.1 01 背包</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 01 背包: f[j] = 容量 j 的最大价值; 体积 w[i], 价值 v[i]. O(nW)</span></span><br><span class="line"><span class="comment">// 至多 W: 全部初始化为 0; 恰好装满: f[0]=0, 其余 -INF</span></span><br><span class="line"><span class="type">const</span> <span class="type">long</span> <span class="type">long</span> NEG = <span class="number">-0x3f3f3f3f3f3f3f3fLL</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt;= W; j++) f[j] = (j == <span class="number">0</span> ? <span class="number">0</span> : NEG);   <span class="comment">// 恰好装满</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = W; j &gt;= w[i]; j--)                          <span class="comment">// 必须逆序!</span></span><br><span class="line">    f[j] = <span class="built_in">max</span>(f[j], f[j - w[i]] + v[i]);</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 逆序是为了让 f[j-w[i]] 仍是「第 i 件未考虑」的旧值（每件只用一次）。写成正序就退化成完全背包。</p></blockquote><h4 id="2-2-完全背包"><a href="#2-2-完全背包" class="headerlink" title="2.2 完全背包"></a>2.2 完全背包</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 完全背包: 每件无限个, 容量正序. O(nW)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = w[i]; j &lt;= W; j++) f[j] = <span class="built_in">max</span>(f[j], f[j - w[i]] + v[i]);</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="2-3-多重背包（二进制拆分）"><a href="#2-3-多重背包（二进制拆分）" class="headerlink" title="2.3 多重背包（二进制拆分）"></a>2.3 多重背包（二进制拆分）</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 多重背包: 第 i 件有 c0[i] 个, 拆成 1,2,4,...,余数 后做 01 背包</span></span><br><span class="line"><span class="comment">// O(W * sum log c[i])</span></span><br><span class="line"><span class="type">int</span> idx = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) &#123;</span><br><span class="line">  <span class="type">int</span> c = c0[i];</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">1</span>; k &lt;= c; k &lt;&lt;= <span class="number">1</span>) &#123; c -= k; ww[++idx] = k * w[i]; vv[idx] = k * v[i]; &#125;</span><br><span class="line">  <span class="keyword">if</span> (c) &#123; ww[++idx] = c * w[i]; vv[idx] = c * v[i]; &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= idx; i++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = W; j &gt;= ww[i]; j--) f[j] = <span class="built_in">max</span>(f[j], f[j - ww[i]] + vv[i]);</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="2-4-多重背包（单调队列优化）"><a href="#2-4-多重背包（单调队列优化）" class="headerlink" title="2.4 多重背包（单调队列优化）"></a>2.4 多重背包（单调队列优化）</h4><p>转移 f[j] &#x3D; max_{0&lt;&#x3D;k&lt;&#x3D;c} (g[j-k<em>w] + k</em>v)。按余数 r 分组，同组内 j &#x3D; r + k<em>w，令 g[k] &#x3D; f[r+k</em>w] - k<em>v，则 f_new[r+k</em>w] &#x3D; max_{k-c&lt;&#x3D;k’&lt;&#x3D;k} g[k’] + k*v，即滑动窗口最大值。<strong>可原地更新</strong>（顺序枚举 k 时读到的 f[j] 仍是旧值）。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 多重背包 · 单调队列优化. O(nW); 队列存 (值, 下标 k)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) &#123;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> r = <span class="number">0</span>; r &lt; w[i] &amp;&amp; r &lt;= W; r++) &#123;              <span class="comment">// 按余数分组</span></span><br><span class="line">    <span class="type">int</span> L = <span class="number">0</span>, R = <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>; r + k * w[i] &lt;= W; k++) &#123;</span><br><span class="line">      <span class="type">int</span> j = r + k * w[i];</span><br><span class="line">      <span class="type">long</span> <span class="type">long</span> cur = f[j] - <span class="number">1LL</span> * k * v[i];</span><br><span class="line">      <span class="keyword">while</span> (L &lt;= R &amp;&amp; q[R].first &lt;= cur) R--;            <span class="comment">// 单调队列</span></span><br><span class="line">      q[++R] = &#123;cur, k&#125;;</span><br><span class="line">      <span class="keyword">while</span> (q[L].second &lt; k - c0[i]) L++;                <span class="comment">// 超出个数限制</span></span><br><span class="line">      f[j] = q[L].first + <span class="number">1LL</span> * k * v[i];</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 内层上界是 r + k*w[i] &lt;&#x3D; W；队列要开在分组外、每组复位。w[i] &gt; W 的整件物品无贡献，需特判跳过。</p></blockquote><h4 id="2-5-分组背包"><a href="#2-5-分组背包" class="headerlink" title="2.5 分组背包"></a>2.5 分组背包</h4><p>每组至多选一件。<strong>容量循环必须在组内物品循环的外层</strong>，否则同组会被选多次。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 分组背包: 第 k 组有 cnt[k] 件物品 (Wk[t], Vk[t]). O(W * sum cnt)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">1</span>; k &lt;= ts; k++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = W; j &gt;= <span class="number">0</span>; j--)                            <span class="comment">// 容量在外</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> t = <span class="number">0</span>; t &lt; cnt[k]; t++)                      <span class="comment">// 组内物品在内</span></span><br><span class="line">      <span class="keyword">if</span> (j &gt;= Wk[k][t]) f[j] = <span class="built_in">max</span>(f[j], f[j - Wk[k][t]] + Vk[k][t]);</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="2-6-依赖背包"><a href="#2-6-依赖背包" class="headerlink" title="2.6 依赖背包"></a>2.6 依赖背包</h4><ul><li>附件少（金明的预算方案：每主件 ≤ 2 个附件）：把「主件 + 附件子集」枚举成 2^t 个方案，转<strong>分组背包</strong>。</li><li>附件是任意多叉树：直接做<strong>树上背包</strong>（见 5.3），复杂度 O(nm)。</li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 金明式依赖 -&gt; 分组背包: plan[k] 预存组内所有 (费用, 价值) 方案. O(W * sum |plan[k]|)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">1</span>; k &lt;= ts; k++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = W; j &gt;= <span class="number">0</span>; j--)</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;pr : plan[k])</span><br><span class="line">      <span class="keyword">if</span> (j &gt;= pr.first) f[j] = <span class="built_in">max</span>(f[j], f[j - pr.first] + pr.second);</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="2-7-方案数背包-恰好装满-vs-至多"><a href="#2-7-方案数背包-恰好装满-vs-至多" class="headerlink" title="2.7 方案数背包 &#x2F; 恰好装满 vs 至多"></a>2.7 方案数背包 &#x2F; 恰好装满 vs 至多</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 方案数: 把 max 换成加法, 初值 dp[0] = 1. O(nW)</span></span><br><span class="line">dp[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)                   <span class="comment">// 01: 逆序, 每件用一次</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = W; j &gt;= w[i]; j--) dp[j] += dp[j - w[i]];</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)                   <span class="comment">// 完全: 正序, 每件无限</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = w[i]; j &lt;= W; j++) dp[j] += dp[j - w[i]];</span><br><span class="line"><span class="comment">// 答案: 恰好装满 -&gt; dp[W]; 至多 -&gt; sum_&#123;j&lt;=W&#125; dp[j]</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 初始化决定语义：<strong>恰好</strong>装满必须 f[0]&#x3D;0、其余 -INF（求 max）&#x2F;INF（求 min）；<strong>至多</strong>不超过容量则全 0。恰好装满时最优解不一定是 f[W]（可能装不满），求最优方案数要扫一遍所有取到最优值的 j。</p></blockquote><h4 id="2-8-求具体方案"><a href="#2-8-求具体方案" class="headerlink" title="2.8 求具体方案"></a>2.8 求具体方案</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// g[i][j]: 前 i 件物品容量 j 时是否选了第 i 件. O(nW)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = W; j &gt;= w[i]; j--)</span><br><span class="line">    <span class="keyword">if</span> (f[i<span class="number">-1</span>][j-w[i]] + v[i] &gt; f[i<span class="number">-1</span>][j]) &#123; f[i][j] = f[i<span class="number">-1</span>][j-w[i]] + v[i]; g[i][j] = <span class="number">1</span>; &#125;</span><br><span class="line">    <span class="keyword">else</span> f[i][j] = f[i<span class="number">-1</span>][j];</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = n, j = W; i &gt;= <span class="number">1</span>; i--)            <span class="comment">// 从最后一件倒着回溯</span></span><br><span class="line">  <span class="keyword">if</span> (g[i][j]) &#123; cout &lt;&lt; i &lt;&lt; <span class="string">&#x27; &#x27;</span>; j -= w[i]; &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="3-LIS-LCS-最短编辑距离"><a href="#3-LIS-LCS-最短编辑距离" class="headerlink" title="3. LIS &#x2F; LCS &#x2F; 最短编辑距离"></a>3. LIS &#x2F; LCS &#x2F; 最短编辑距离</h3><h4 id="3-1-LIS-O-n-2-与方案还原"><a href="#3-1-LIS-O-n-2-与方案还原" class="headerlink" title="3.1 LIS O(n^2) 与方案还原"></a>3.1 LIS O(n^2) 与方案还原</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// LIS O(n^2) + 方案还原; p[i] 记录前驱</span></span><br><span class="line"><span class="type">int</span> ans = <span class="number">0</span>, ed = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) &#123;</span><br><span class="line">  f[i] = <span class="number">1</span>; p[i] = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt; i; j++)</span><br><span class="line">    <span class="keyword">if</span> (a[j] &lt; a[i] &amp;&amp; f[j] + <span class="number">1</span> &gt; f[i]) &#123; f[i] = f[j] + <span class="number">1</span>; p[i] = j; &#125;  <span class="comment">// 非严格改 &lt;=</span></span><br><span class="line">  <span class="keyword">if</span> (f[i] &gt; ans) ans = f[i], ed = i;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> x = ed; x; x = p[x]) stk.<span class="built_in">push_back</span>(a[x]);       <span class="comment">// 逆序输出即一个 LIS</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="3-2-LIS-O-n-log-n-（贪心-二分）"><a href="#3-2-LIS-O-n-log-n-（贪心-二分）" class="headerlink" title="3.2 LIS O(n log n)（贪心 + 二分）"></a>3.2 LIS O(n log n)（贪心 + 二分）</h4><p>d[l] &#x3D; 所有长度为 l 的上升子序列中末尾元素的最小值。<strong>严格上升用 lower_bound（第一个 &gt;&#x3D; a[i]），非严格（不下降）用 upper_bound（第一个 &gt; a[i]）</strong>。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// LIS O(n log n) + 方案还原</span></span><br><span class="line"><span class="type">int</span> len = <span class="number">0</span>, pos[N] = &#123;<span class="number">0</span>&#125;, pre[N] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) &#123;</span><br><span class="line">  <span class="type">int</span> p = <span class="built_in">lower_bound</span>(d + <span class="number">1</span>, d + len + <span class="number">1</span>, a[i]) - d;   <span class="comment">// 非严格: 换成 upper_bound</span></span><br><span class="line">  d[p] = a[i]; pos[p] = i;</span><br><span class="line">  pre[i] = (p &gt; <span class="number">1</span> ? pos[p - <span class="number">1</span>] : <span class="number">0</span>);</span><br><span class="line">  <span class="keyword">if</span> (p &gt; len) len = p;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> x = pos[len]; x; x = pre[x]) stk.<span class="built_in">push_back</span>(a[x]);   <span class="comment">// 还原一个 LIS(逆序)</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ d[] 本身<strong>不是</strong>合法 LIS，只能求长度；还原必须额外记 pos[]（每个长度最后被谁更新）与 pre[]。最长下降子序列 &#x3D; 取负求 LIS，或倒序 + 严格性互换。</p></blockquote><h4 id="3-3-LCS-与方案还原"><a href="#3-3-LCS-与方案还原" class="headerlink" title="3.3 LCS 与方案还原"></a>3.3 LCS 与方案还原</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// LCS. O(nm); 滚动数组只能求长度, 还原必须存满表</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= m; j++)</span><br><span class="line">    <span class="keyword">if</span> (a[i] == b[j]) f[i][j] = f[i<span class="number">-1</span>][j<span class="number">-1</span>] + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">else</span> f[i][j] = <span class="built_in">max</span>(f[i<span class="number">-1</span>][j], f[i][j<span class="number">-1</span>]);</span><br><span class="line"><span class="type">int</span> i = n, j = m; string s;</span><br><span class="line"><span class="keyword">while</span> (i &amp;&amp; j) &#123;</span><br><span class="line">  <span class="keyword">if</span> (a[i] == b[j]) s += a[i], i--, j--;</span><br><span class="line">  <span class="keyword">else</span> <span class="keyword">if</span> (f[i<span class="number">-1</span>][j] &gt;= f[i][j<span class="number">-1</span>]) i--; <span class="keyword">else</span> j--;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">reverse</span>(s.<span class="built_in">begin</span>(), s.<span class="built_in">end</span>());</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="3-4-最短编辑距离"><a href="#3-4-最短编辑距离" class="headerlink" title="3.4 最短编辑距离"></a>3.4 最短编辑距离</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 编辑距离 (插入/删除/替换, 各代价 1). O(nm)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt;= n; i++) f[i][<span class="number">0</span>] = i;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt;= m; j++) f[<span class="number">0</span>][j] = j;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= m; j++)</span><br><span class="line">    f[i][j] = <span class="built_in">min</span>(<span class="built_in">min</span>(f[i<span class="number">-1</span>][j] + <span class="number">1</span>, f[i][j<span class="number">-1</span>] + <span class="number">1</span>), f[i<span class="number">-1</span>][j<span class="number">-1</span>] + (a[i] != b[j]));</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="4-区间-DP"><a href="#4-区间-DP" class="headerlink" title="4. 区间 DP"></a>4. 区间 DP</h3><p>套路：f[i][j] 表示区间 [i, j] 的最优值，按<strong>区间长度</strong>从小到大枚举；转移一般是「枚举分割点 k」或「由 f[i+1][j-1] 收缩」。</p><h4 id="4-1-石子合并（环形-→-破环成链）"><a href="#4-1-石子合并（环形-→-破环成链）" class="headerlink" title="4.1 石子合并（环形 → 破环成链）"></a>4.1 石子合并（环形 → 破环成链）</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 环形石子合并: 破环成链, 在 2n 的链上做区间 DP, 取长度 n 的窗口最优. O(n^3)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">2</span> * n; i++) s[i] = s[i<span class="number">-1</span>] + a[(i - <span class="number">1</span>) % n + <span class="number">1</span>];</span><br><span class="line"><span class="built_in">memset</span>(f, <span class="number">0x3f</span>, <span class="keyword">sizeof</span> f);</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">2</span> * n; i++) f[i][i] = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> len = <span class="number">2</span>; len &lt;= n; len++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>, j = len; j &lt;= <span class="number">2</span> * n; i++, j++)</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> k = i; k &lt; j; k++)</span><br><span class="line">      f[i][j] = <span class="built_in">min</span>(f[i][j], f[i][k] + f[k<span class="number">+1</span>][j] + s[j] - s[i<span class="number">-1</span>]);</span><br><span class="line"><span class="type">int</span> ans = INF;                                 <span class="comment">// ans 与 f 同类型, 避免 min 类型不匹配</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) ans = <span class="built_in">min</span>(ans, f[i][i + n - <span class="number">1</span>]);</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="4-2-括号匹配"><a href="#4-2-括号匹配" class="headerlink" title="4.2 括号匹配"></a>4.2 括号匹配</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 最长合法括号子序列长度 (s[1..n]). O(n^3)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> len = <span class="number">2</span>; len &lt;= n; len++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>, j = len; j &lt;= n; i++, j++) &#123;</span><br><span class="line">    <span class="keyword">if</span> ((s[i] == <span class="string">&#x27;(&#x27;</span> &amp;&amp; s[j] == <span class="string">&#x27;)&#x27;</span>) || (s[i] == <span class="string">&#x27;[&#x27;</span> &amp;&amp; s[j] == <span class="string">&#x27;]&#x27;</span>))</span><br><span class="line">      f[i][j] = <span class="built_in">max</span>(f[i][j], f[i<span class="number">+1</span>][j<span class="number">-1</span>] + <span class="number">2</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> k = i; k &lt; j; k++) f[i][j] = <span class="built_in">max</span>(f[i][j], f[i][k] + f[k<span class="number">+1</span>][j]);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="4-3-回文区间-DP"><a href="#4-3-回文区间-DP" class="headerlink" title="4.3 回文区间 DP"></a>4.3 回文区间 DP</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 最长回文子序列长度; 最少插入次数构成回文 = n - 该值. O(n^2)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) f[i][i] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> len = <span class="number">2</span>; len &lt;= n; len++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>, j = len; j &lt;= n; i++, j++)</span><br><span class="line">    <span class="keyword">if</span> (a[i] == a[j]) f[i][j] = f[i<span class="number">+1</span>][j<span class="number">-1</span>] + <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">else</span> f[i][j] = <span class="built_in">max</span>(f[i<span class="number">+1</span>][j], f[i][j<span class="number">-1</span>]);</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 区间 DP 边界：len&#x3D;1（或 2）必须手动初始化，f[i+1][j-1] 在 len&#x3D;2 时是空区间，按题意给 0。环形题最后要在长度为 n 的窗口里取最优。</p></blockquote><h4 id="4-4-四边形不等式加速"><a href="#4-4-四边形不等式加速" class="headerlink" title="4.4 四边形不等式加速"></a>4.4 四边形不等式加速</h4><p>w 满足四边形不等式（交叉小于包含：a&lt;&#x3D;b&lt;&#x3D;c&lt;&#x3D;d 时 w(a,c)+w(b,d) &lt;&#x3D; w(a,d)+w(b,c)）时，f[i][j] &#x3D; min_k (f[i][k] + f[k+1][j] + w(i,j)) 的决策点满足 opt[i][j-1] &lt;&#x3D; opt[i][j] &lt;&#x3D; opt[i+1][j]，均摊 O(n^2)。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 四边形不等式优化区间 DP. O(n^2)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) &#123; f[i][i] = <span class="number">0</span>; opt[i][i] = i; &#125;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> len = <span class="number">2</span>; len &lt;= n; len++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>, j = len; j &lt;= n; i++, j++) &#123;</span><br><span class="line">    f[i][j] = INF;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> k = opt[i][j<span class="number">-1</span>]; k &lt;= opt[i<span class="number">+1</span>][j]; k++)     <span class="comment">// 决策区间被夹住</span></span><br><span class="line">      <span class="keyword">if</span> (f[i][j] &gt; f[i][k] + f[k<span class="number">+1</span>][j] + <span class="built_in">w</span>(i, j)) &#123;</span><br><span class="line">        f[i][j] = f[i][k] + f[k<span class="number">+1</span>][j] + <span class="built_in">w</span>(i, j); opt[i][j] = k;</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="5-树形-DP"><a href="#5-树形-DP" class="headerlink" title="5. 树形 DP"></a>5. 树形 DP</h3><h4 id="5-1-链式前向星-子树大小-深度"><a href="#5-1-链式前向星-子树大小-深度" class="headerlink" title="5.1 链式前向星 + 子树大小&#x2F;深度"></a>5.1 链式前向星 + 子树大小&#x2F;深度</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="type">int</span> head[N], nxt[N &lt;&lt; <span class="number">1</span>], to[N &lt;&lt; <span class="number">1</span>], ecnt;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> u, <span class="type">int</span> v)</span> </span>&#123; to[++ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt; &#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs1</span><span class="params">(<span class="type">int</span> u, <span class="type">int</span> fa)</span> </span>&#123;                    <span class="comment">// 预处理 sz / dep. O(n)</span></span><br><span class="line">  sz[u] = <span class="number">1</span>; dep[u] = dep[fa] + <span class="number">1</span>;      <span class="comment">// 根深度为 1; 需根深度为 0 时先置 dep[0] = -1</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = head[u]; i; i = nxt[i]) &#123;</span><br><span class="line">    <span class="type">int</span> v = to[i]; <span class="keyword">if</span> (v == fa) <span class="keyword">continue</span>;</span><br><span class="line">    <span class="built_in">dfs1</span>(v, u); sz[u] += sz[v];</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="5-2-最大独立集-最小点覆盖"><a href="#5-2-最大独立集-最小点覆盖" class="headerlink" title="5.2 最大独立集 &#x2F; 最小点覆盖"></a>5.2 最大独立集 &#x2F; 最小点覆盖</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 没有上司的舞会: f[u][1] 选 u, f[u][0] 不选 u. O(n)</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u, <span class="type">int</span> fa)</span> </span>&#123;</span><br><span class="line">  f[u][<span class="number">1</span>] = val[u]; f[u][<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = head[u]; i; i = nxt[i]) &#123;</span><br><span class="line">    <span class="type">int</span> v = to[i]; <span class="keyword">if</span> (v == fa) <span class="keyword">continue</span>;</span><br><span class="line">    <span class="built_in">dfs</span>(v, u);</span><br><span class="line">    f[u][<span class="number">1</span>] += f[v][<span class="number">0</span>];                       <span class="comment">// 选了 u 则儿子都不能选</span></span><br><span class="line">    f[u][<span class="number">0</span>] += <span class="built_in">max</span>(f[v][<span class="number">0</span>], f[v][<span class="number">1</span>]);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 最小点覆盖 = 总点数 - 最大独立集</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="5-3-树上背包"><a href="#5-3-树上背包" class="headerlink" title="5.3 树上背包"></a>5.3 树上背包</h4><p>合并子树时按<strong>已合并大小</strong>限制循环上界，总复杂度 O(nm)（而不是 O(n m^2)）。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 选课: f[u][j] = u 的子树中选 j 个点的最大价值, u 必选. O(nm)</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u, <span class="type">int</span> fa)</span> </span>&#123;</span><br><span class="line">  <span class="type">int</span> p = <span class="number">1</span>; f[u][<span class="number">1</span>] = val[u];</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = head[u]; i; i = nxt[i]) &#123;</span><br><span class="line">    <span class="type">int</span> v = to[i]; <span class="keyword">if</span> (v == fa) <span class="keyword">continue</span>;</span><br><span class="line">    <span class="type">int</span> siz = <span class="built_in">dfs</span>(v, u);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> a = <span class="built_in">min</span>(p, m); a &gt;= <span class="number">1</span>; a--)      <span class="comment">// 倒序, 只用已合并的部分</span></span><br><span class="line">      <span class="keyword">for</span> (<span class="type">int</span> b = <span class="number">1</span>; b &lt;= siz &amp;&amp; a + b &lt;= m; b++)</span><br><span class="line">        f[u][a + b] = <span class="built_in">max</span>(f[u][a + b], f[u][a] + f[v][b]);</span><br><span class="line">    p += siz;                                 <span class="comment">// 上界剪枝的关键</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 树上背包的内存是 n*m，f[u] 必须开成 f[N][M]。合并时 a 倒序 + p 剪枝是 O(nm) 的关键，漏了会退化成 O(n m^2)。</p></blockquote><h4 id="5-4-换根-DP（二次扫描）"><a href="#5-4-换根-DP（二次扫描）" class="headerlink" title="5.4 换根 DP（二次扫描）"></a>5.4 换根 DP（二次扫描）</h4><p>先任取根自底向上求一次，再用 f[v] 与 f[u] 的递推关系自上而下推第二次。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// f[u] = 所有点到 u 的距离之和. O(n)</span></span><br><span class="line">dep[<span class="number">0</span>] = <span class="number">-1</span>;                               <span class="comment">// 使根深度为 0, 否则 f[1] 整体偏大 n</span></span><br><span class="line">f[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"><span class="built_in">dfs1</span>(<span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) f[<span class="number">1</span>] += dep[i];  <span class="comment">// 先算根的值</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs2</span><span class="params">(<span class="type">int</span> u, <span class="type">int</span> fa)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = head[u]; i; i = nxt[i]) &#123;</span><br><span class="line">    <span class="type">int</span> v = to[i]; <span class="keyword">if</span> (v == fa) <span class="keyword">continue</span>;</span><br><span class="line">    f[v] = f[u] - <span class="number">2</span> * sz[v] + n;              <span class="comment">// 子树内 -1, 其余 +1</span></span><br><span class="line">    <span class="built_in">dfs2</span>(v, u);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="5-5-树的直径（DP-版，可带负边权）"><a href="#5-5-树的直径（DP-版，可带负边权）" class="headerlink" title="5.5 树的直径（DP 版，可带负边权）"></a>5.5 树的直径（DP 版，可带负边权）</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// d1/d2 = 向下的最长/次长链, 边权 wt. O(n)</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u, <span class="type">int</span> fa)</span> </span>&#123;</span><br><span class="line">  d1[u] = d2[u] = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = head[u]; i; i = nxt[i]) &#123;</span><br><span class="line">    <span class="type">int</span> v = to[i]; <span class="keyword">if</span> (v == fa) <span class="keyword">continue</span>;</span><br><span class="line">    <span class="built_in">dfs</span>(v, u);</span><br><span class="line">    <span class="type">int</span> d = d1[v] + wt[i];</span><br><span class="line">    <span class="keyword">if</span> (d &gt; d1[u]) &#123; d2[u] = d1[u]; d1[u] = d; &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (d &gt; d2[u]) d2[u] = d;</span><br><span class="line">  &#125;</span><br><span class="line">  ans = <span class="built_in">max</span>(ans, d1[u] + d2[u]);              <span class="comment">// 经过 u 的最长路</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="6-状压-DP"><a href="#6-状压-DP" class="headerlink" title="6. 状压 DP"></a>6. 状压 DP</h3><h4 id="6-1-枚举子集-与-子集和（SOS）"><a href="#6-1-枚举子集-与-子集和（SOS）" class="headerlink" title="6.1 枚举子集 与 子集和（SOS）"></a>6.1 枚举子集 与 子集和（SOS）</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 枚举 s 的所有非空子集, 总复杂度 O(3^n)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> s = <span class="number">1</span>; s &lt; (<span class="number">1</span> &lt;&lt; n); s++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> t = s; t; t = (t - <span class="number">1</span>) &amp; s) &#123; <span class="comment">/* 处理子集 t */</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 子集和 DP (SOS): f[s] = sum_&#123;t 是 s 的子集&#125; a[t]. O(n 2^n)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> s = <span class="number">0</span>; s &lt; (<span class="number">1</span> &lt;&lt; n); s++)</span><br><span class="line">    <span class="keyword">if</span> (s &gt;&gt; i &amp; <span class="number">1</span>) f[s] += f[s ^ (<span class="number">1</span> &lt;&lt; i)];</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="6-2-TSP（哈密顿回路）"><a href="#6-2-TSP（哈密顿回路）" class="headerlink" title="6.2 TSP（哈密顿回路）"></a>6.2 TSP（哈密顿回路）</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// f[S][i] = 走过集合 S 且当前在 i 的最小代价. O(2^n * n^2), n &lt;= 18</span></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> d[N][N], f[<span class="number">1</span> &lt;&lt; <span class="number">18</span>][<span class="number">18</span>];       <span class="comment">// 距离矩阵与 DP 表</span></span><br><span class="line"><span class="built_in">memset</span>(f, <span class="number">0x3f</span>, <span class="keyword">sizeof</span> f); f[<span class="number">1</span>][<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> S = <span class="number">1</span>; S &lt; (<span class="number">1</span> &lt;&lt; n); S++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">    <span class="keyword">if</span> (f[S][i] &gt;= INF) <span class="keyword">continue</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; n; j++) &#123;</span><br><span class="line">      <span class="keyword">if</span> (S &gt;&gt; j &amp; <span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">      f[S | <span class="number">1</span> &lt;&lt; j][j] = <span class="built_in">min</span>(f[S | <span class="number">1</span> &lt;&lt; j][j], f[S][i] + d[i][j]);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="type">long</span> <span class="type">long</span> ans = INF;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) ans = <span class="built_in">min</span>(ans, f[(<span class="number">1</span> &lt;&lt; n) - <span class="number">1</span>][i] + d[i][<span class="number">0</span>]);</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="6-3-棋盘覆盖（逐行状压）"><a href="#6-3-棋盘覆盖（逐行状压）" class="headerlink" title="6.3 棋盘覆盖（逐行状压）"></a>6.3 棋盘覆盖（逐行状压）</h4><p>先 dfs 预处理<strong>行内合法状态</strong>（互不侵犯要求行内 1 不相邻），再逐行用位运算判两行兼容。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 互不侵犯: n*n 棋盘放 k 个国王 (八连通不互邻). O(n * cnt^2 * k)</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">gen</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> num, <span class="type">int</span> cur)</span> </span>&#123;           <span class="comment">// 预处理一行内的合法状态</span></span><br><span class="line">  <span class="keyword">if</span> (cur &gt;= n) &#123; sit[++cnt] = x; sta[cnt] = num; <span class="keyword">return</span>; &#125;</span><br><span class="line">  <span class="built_in">gen</span>(x, num, cur + <span class="number">1</span>);                       <span class="comment">// 不放</span></span><br><span class="line">  <span class="built_in">gen</span>(x + (<span class="number">1</span> &lt;&lt; cur), num + <span class="number">1</span>, cur + <span class="number">2</span>);      <span class="comment">// 放, 隔一格</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">ok</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> </span>&#123;                       <span class="comment">// 相邻两行是否兼容</span></span><br><span class="line">  <span class="keyword">return</span> !(sit[a] &amp; sit[b]) &amp;&amp; !((sit[a] &lt;&lt; <span class="number">1</span>) &amp; sit[b]) &amp;&amp; !(sit[a] &amp; (sit[b] &lt;&lt; <span class="number">1</span>));</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= cnt; j++) f[<span class="number">1</span>][j][sta[j]] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i &lt;= n; i++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> a = <span class="number">1</span>; a &lt;= cnt; a++)</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> b = <span class="number">1</span>; b &lt;= cnt; b++) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!<span class="built_in">ok</span>(a, b)) <span class="keyword">continue</span>;</span><br><span class="line">      <span class="keyword">for</span> (<span class="type">int</span> l = sta[a]; l &lt;= k; l++) f[i][a][l] += f[i<span class="number">-1</span>][b][l - sta[a]];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="6-4-轮廓线-DP（插头-DP-入门）"><a href="#6-4-轮廓线-DP（插头-DP-入门）" class="headerlink" title="6.4 轮廓线 DP（插头 DP 入门）"></a>6.4 轮廓线 DP（插头 DP 入门）</h4><p>逐格转移，轮廓线宽度 m：s 的第 j 位 &#x3D; 1 表示该位置已被覆盖（上方竖放下来或左侧横放过来）。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 轮廓线 DP: n*m 棋盘用 1*2 骨牌铺满的方案数. O(n*m*2^m)</span></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> f[<span class="number">2</span>][<span class="number">1</span> &lt;&lt; <span class="number">12</span>], *f0 = f[<span class="number">0</span>], *f1 = f[<span class="number">1</span>];</span><br><span class="line"><span class="built_in">fill</span>(f1, f1 + (<span class="number">1</span> &lt;&lt; m), <span class="number">0</span>); f1[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; m; j++) &#123;</span><br><span class="line">    <span class="built_in">swap</span>(f0, f1); <span class="built_in">fill</span>(f1, f1 + (<span class="number">1</span> &lt;&lt; m), <span class="number">0</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> s = <span class="number">0</span>; s &lt; (<span class="number">1</span> &lt;&lt; m); s++) &#123;</span><br><span class="line">      <span class="type">long</span> <span class="type">long</span> u = f0[s]; <span class="keyword">if</span> (!u) <span class="keyword">continue</span>;</span><br><span class="line">      <span class="keyword">if</span> (s &gt;&gt; j &amp; <span class="number">1</span>) f1[s ^ <span class="number">1</span> &lt;&lt; j] += u;                          <span class="comment">// 已被覆盖, 不放</span></span><br><span class="line">      <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (j != m - <span class="number">1</span> &amp;&amp; !(s &gt;&gt; j &amp; <span class="number">3</span>)) f1[s ^ <span class="number">1</span> &lt;&lt; (j + <span class="number">1</span>)] += u; <span class="comment">// 横放</span></span><br><span class="line">        f1[s ^ <span class="number">1</span> &lt;&lt; j] += u;                                        <span class="comment">// 竖放</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"><span class="comment">// 答案: f1[0]</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 真正的插头 DP（回路计数、哈密顿路径）还要在轮廓线上编码<strong>连通性</strong>（最小表示法 &#x2F; 括号表示），状态数远大于 2^m，通常配哈希表转移；CSP 里出现概率低，先掌握上面这个 2^m 轮廓线模型。</p></blockquote><h3 id="7-数位-DP"><a href="#7-数位-DP" class="headerlink" title="7. 数位 DP"></a>7. 数位 DP</h3><p>记忆化递归框架 dfs(pos, state, lim, lead)。只有 !lim &amp;&amp; !lead 的状态才能记忆化，否则会把「贴着上界」的答案污染到通用状态里。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 数位 DP: 统计 [0, x] 中相邻两位数字之差 &gt;= 2 的数的个数 (windy 数). O(位数 * 状态数 * 10)</span></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> f[<span class="number">12</span>][<span class="number">10</span>];                              <span class="comment">// f[pos][pre], 仅用于 !lim &amp;&amp; !lead</span></span><br><span class="line"><span class="type">int</span> a[<span class="number">12</span>];</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> pos, <span class="type">int</span> pre, <span class="type">bool</span> lim, <span class="type">bool</span> lead)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (pos == <span class="number">0</span>) <span class="keyword">return</span> lead ? <span class="number">0</span> : <span class="number">1</span>;              <span class="comment">// 排除数字 0 本身</span></span><br><span class="line">  <span class="keyword">if</span> (!lim &amp;&amp; !lead &amp;&amp; f[pos][pre] != <span class="number">-1</span>) <span class="keyword">return</span> f[pos][pre];</span><br><span class="line">  <span class="type">int</span> up = lim ? a[pos] : <span class="number">9</span>;</span><br><span class="line">  <span class="type">long</span> <span class="type">long</span> res = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> d = <span class="number">0</span>; d &lt;= up; d++) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!lead &amp;&amp; <span class="built_in">abs</span>(d - pre) &lt; <span class="number">2</span>) <span class="keyword">continue</span>;      <span class="comment">// 题目约束</span></span><br><span class="line">    <span class="keyword">if</span> (lead &amp;&amp; d == <span class="number">0</span>) res += <span class="built_in">dfs</span>(pos - <span class="number">1</span>, <span class="number">0</span>, lim &amp;&amp; d == up, <span class="literal">true</span>);   <span class="comment">// 仍在前导零</span></span><br><span class="line">    <span class="keyword">else</span> res += <span class="built_in">dfs</span>(pos - <span class="number">1</span>, d, lim &amp;&amp; d == up, <span class="literal">false</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (!lim &amp;&amp; !lead) f[pos][pre] = res;</span><br><span class="line">  <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">calc</span><span class="params">(<span class="type">long</span> <span class="type">long</span> x)</span> </span>&#123;                     <span class="comment">// 统计 [0, x]; 答案 = calc(r) - calc(l - 1)</span></span><br><span class="line">  <span class="keyword">if</span> (x &lt; <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  <span class="type">int</span> len = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">while</span> (x) a[++len] = x % <span class="number">10</span>, x /= <span class="number">10</span>;           <span class="comment">// 低位在 a[1]</span></span><br><span class="line">  <span class="built_in">memset</span>(f, <span class="number">-1</span>, <span class="keyword">sizeof</span> f);</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">dfs</span>(len, <span class="number">0</span>, <span class="literal">true</span>, <span class="literal">true</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 三个易错点：① lim 为真时不能记忆化；② 前导零必须单独用 lead 传递，否则 000123 会被当成 3 位数；③ 记忆化数组必须整体清成 -1，多组询问每组重清。</p></blockquote><h3 id="8-计数-DP-与期望-DP"><a href="#8-计数-DP-与期望-DP" class="headerlink" title="8. 计数 DP 与期望 DP"></a>8. 计数 DP 与期望 DP</h3><h4 id="8-1-计数-DP"><a href="#8-1-计数-DP" class="headerlink" title="8.1 计数 DP"></a>8.1 计数 DP</h4><p>核心是<strong>想清楚每个方案被数的次数</strong>，避免重复计数（按最后一个元素 &#x2F; 第一次出现的位置分类）。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 整数划分: 把 n 拆成若干正整数之和(无序) 的方案数 = 完全背包计数. O(n^2)</span></span><br><span class="line">dp[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)                  <span class="comment">// 枚举可用的数 i</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = i; j &lt;= n; j++) dp[j] += dp[j - i];   <span class="comment">// 正序 = 完全背包</span></span><br><span class="line"><span class="comment">// 卡特兰数: C[i] = sum_&#123;j=1..i&#125; C[j-1] * C[i-j], C[0] = 1</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="8-2-概率正推-期望逆推"><a href="#8-2-概率正推-期望逆推" class="headerlink" title="8.2 概率正推 &#x2F; 期望逆推"></a>8.2 概率正推 &#x2F; 期望逆推</h4><p><strong>概率正推</strong>（从起点推概率），<strong>期望逆推</strong>（从终点推期望）。带环的期望要列方程高斯消元，DAG 上直接逆序递推。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 概率 DP 正推: p[i] = 到达 i 的概率. O(n + m)</span></span><br><span class="line">p[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++)</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;pr : g[i]) p[pr.to] += p[i] * pr.prob;    <span class="comment">// g 须按 DP 序使用</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 期望 DP 逆推: E[u] = sum_&#123;u-&gt;v&#125; (w(u,v) + E[v]) / outdeg(u)</span></span><br><span class="line"><span class="type">double</span> E[N]; <span class="type">bool</span> vis[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (vis[u]) <span class="keyword">return</span>; vis[u] = <span class="literal">true</span>;</span><br><span class="line">  <span class="keyword">if</span> (g[u].<span class="built_in">empty</span>()) &#123; E[u] = <span class="number">0</span>; <span class="keyword">return</span>; &#125;</span><br><span class="line">  E[u] = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;e : g[u]) &#123; <span class="built_in">dfs</span>(e.to); E[u] += e.w + E[e.to]; &#125;</span><br><span class="line">  E[u] /= g[u].<span class="built_in">size</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 期望线性性只管<strong>和</strong>，不管积；E[XY] &#x3D; E[X]E[Y] 只在独立时成立。求「期望的平方」「期望的倒数」等非线性量时，必须把相应信息加进状态。</p></blockquote><h3 id="9-DP-优化"><a href="#9-DP-优化" class="headerlink" title="9. DP 优化"></a>9. DP 优化</h3><h4 id="9-1-前缀和优化"><a href="#9-1-前缀和优化" class="headerlink" title="9.1 前缀和优化"></a>9.1 前缀和优化</h4><p>转移是连续区间求和&#x2F;最值时，用前缀和把一维 O(n) 降到 O(1)。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// f[i] = sum_&#123;j=i-k&#125;^&#123;i-1&#125; f[j]. O(n)</span></span><br><span class="line">s[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) &#123;</span><br><span class="line">  f[i] = s[i<span class="number">-1</span>] - (i - k - <span class="number">1</span> &gt;= <span class="number">0</span> ? s[i-k<span class="number">-1</span>] : <span class="number">0</span>);</span><br><span class="line">  s[i] = s[i<span class="number">-1</span>] + f[i];</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 二维前缀和: sum(x1..x2,y1..y2) = s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="9-2-单调队列优化"><a href="#9-2-单调队列优化" class="headerlink" title="9.2 单调队列优化"></a>9.2 单调队列优化</h4><p>转移形如 f[i] &#x3D; min_{j in [i-k, i-1]} (g[j]) + w[i] 时，用单调队列维护窗口最值。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 滑动窗口最小值 + 单调队列转移. O(n)</span></span><br><span class="line"><span class="type">int</span> L = <span class="number">0</span>, R = <span class="number">-1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) &#123;</span><br><span class="line">  <span class="keyword">while</span> (L &lt;= R &amp;&amp; q[L] &lt; i - k) L++;                 <span class="comment">// 出窗口</span></span><br><span class="line">  <span class="keyword">while</span> (L &lt;= R &amp;&amp; val[q[R]] &gt;= val[i]) R--;          <span class="comment">// 维护单调</span></span><br><span class="line">  q[++R] = i;</span><br><span class="line">  f[i] = val[q[L]] + w[i];</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="9-3-斜率优化"><a href="#9-3-斜率优化" class="headerlink" title="9.3 斜率优化"></a>9.3 斜率优化</h4><p>把 f[i] &#x3D; min_j (a[i]*x[j] + y[j]) + b[i] 看成用斜率 a[i] 的直线去切点集 (x[j], y[j]) 的凸包。<strong>斜率与横坐标都单调</strong>用单调队列；<strong>只有斜率单调</strong>时在凸包上二分；都不单调用 CDQ 分治或李超线段树。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 斜率优化 · 单调队列版: 维护下凸壳; dp[i] = min_&#123;j&lt;i&#125; &#123; dp[j] + (s[i]-s[j])^2 &#125;, s 单调递增. O(n)</span></span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">long</span> <span class="type">long</span> <span class="title">X</span><span class="params">(<span class="type">int</span> j)</span> </span>&#123; <span class="keyword">return</span> s[j]; &#125;</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">long</span> <span class="type">long</span> <span class="title">Y</span><span class="params">(<span class="type">int</span> j)</span> </span>&#123; <span class="keyword">return</span> dp[j] + s[j] * s[j]; &#125;</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">bool</span> <span class="title">bad</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b, <span class="type">int</span> c)</span> </span>&#123;        <span class="comment">// b 不在下凸壳上</span></span><br><span class="line">  <span class="keyword">return</span> (__int128)(<span class="built_in">Y</span>(b) - <span class="built_in">Y</span>(a)) * (<span class="built_in">X</span>(c) - <span class="built_in">X</span>(b))</span><br><span class="line">       &gt;= (__int128)(<span class="built_in">Y</span>(c) - <span class="built_in">Y</span>(b)) * (<span class="built_in">X</span>(b) - <span class="built_in">X</span>(a));</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> q[N], l = <span class="number">1</span>, r = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">solve</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  q[++r] = <span class="number">0</span>;                                 <span class="comment">// 决策点 0: dp[0] = s[0] = 0</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) &#123;</span><br><span class="line">    <span class="type">long</span> <span class="type">long</span> k = <span class="number">2</span> * s[i];</span><br><span class="line">    <span class="keyword">while</span> (l &lt; r &amp;&amp; <span class="built_in">Y</span>(q[l<span class="number">+1</span>]) - <span class="built_in">Y</span>(q[l]) &lt;= k * (<span class="built_in">X</span>(q[l<span class="number">+1</span>]) - <span class="built_in">X</span>(q[l]))) l++;</span><br><span class="line">    dp[i] = <span class="built_in">Y</span>(q[l]) - k * <span class="built_in">X</span>(q[l]) + s[i] * s[i];</span><br><span class="line">    <span class="keyword">while</span> (l &lt; r &amp;&amp; <span class="built_in">bad</span>(q[r<span class="number">-1</span>], q[r], i)) r--;        <span class="comment">// 弹掉被盖住的决策</span></span><br><span class="line">    q[++r] = i;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 斜率不单调时改用凸壳二分: 找到第一个使 Y(q[mid+1])-k*X(q[mid+1]) 不劣于 q[mid] 的位置</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">query</span><span class="params">(<span class="type">long</span> <span class="type">long</span> k)</span> </span>&#123;</span><br><span class="line">  <span class="type">int</span> lo = l, hi = r;</span><br><span class="line">  <span class="keyword">while</span> (lo &lt; hi) &#123;</span><br><span class="line">    <span class="type">int</span> mid = (lo + hi) &gt;&gt; <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">Y</span>(q[mid<span class="number">+1</span>]) - <span class="built_in">Y</span>(q[mid]) &lt;= k * (<span class="built_in">X</span>(q[mid<span class="number">+1</span>]) - <span class="built_in">X</span>(q[mid]))) lo = mid + <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">else</span> hi = mid;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> q[lo];</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 一律用 __int128 或交叉相乘判斜率，用 double 比较叉积在坐标大时会 WA。下凸壳求 min、上凸壳求 max；弹队首的等号取法决定取最左还是最右最优解，多解时按题意选。</p></blockquote><h4 id="9-4-决策单调性（分治-四边形不等式）"><a href="#9-4-决策单调性（分治-四边形不等式）" class="headerlink" title="9.4 决策单调性（分治 &#x2F; 四边形不等式）"></a>9.4 决策单调性（分治 &#x2F; 四边形不等式）</h4><p>若 opt(i) 单调不减，可用分治在 O(n log n) 内求出所有 dp 值（w 需能 O(1) 或均摊 O(1) 计算）。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 分治优化: dp[i] = min_&#123;j&lt;=i&#125; w(j, i) 且最优决策 opt(i) 单调不减. O(n log n)</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">solve</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> r, <span class="type">int</span> ol, <span class="type">int</span> orr)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (l &gt; r) <span class="keyword">return</span>;</span><br><span class="line">  <span class="type">int</span> mid = (l + r) &gt;&gt; <span class="number">1</span>, pos = ol;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> j = ol; j &lt;= <span class="built_in">min</span>(mid, orr); j++)</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">w</span>(j, mid) &lt; dp[mid]) &#123; dp[mid] = <span class="built_in">w</span>(j, mid); pos = j; &#125;</span><br><span class="line">  <span class="built_in">solve</span>(l, mid - <span class="number">1</span>, ol, pos);</span><br><span class="line">  <span class="built_in">solve</span>(mid + <span class="number">1</span>, r, pos, orr);                <span class="comment">// 调用: solve(1, n, 1, n)</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="9-5-bitset-优化"><a href="#9-5-bitset-优化" class="headerlink" title="9.5 bitset 优化"></a>9.5 bitset 优化</h4><p>把布尔&#x2F;计数 DP 打包成位运算，常数除 64。适合可达性、方案存在性、多重背包可行性。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 01 背包可行性: f 第 j 位 = 容量 j 可达. O(n * W / 64)</span></span><br><span class="line">bitset&lt;MAXW&gt; f; f[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) f |= f &lt;&lt; w[i];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 多重背包可行性 (每件 c0[i] 个): 二进制拆分 + bitset (独立示例)</span></span><br><span class="line">bitset&lt;MAXW&gt; g; g[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) &#123;</span><br><span class="line">  <span class="type">int</span> c = c0[i];</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">1</span>; k &lt;= c; k &lt;&lt;= <span class="number">1</span>) &#123; c -= k; g |= g &lt;&lt; (k * w[i]); &#125;</span><br><span class="line">  <span class="keyword">if</span> (c) g |= g &lt;&lt; (c * w[i]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ bitset 的位数必须是编译期常量，且移位溢出的高位直接丢弃（不报错），容量上界要开够：MAXW &gt;&#x3D; W + max(w)。计数问题不能用 bitset 直接加，除非把每一位拆成独立的多位域。</p></blockquote><h3 id="10-常见坑-1"><a href="#10-常见坑-1" class="headerlink" title="10. 常见坑"></a>10. 常见坑</h3><table><thead><tr><th>坑</th><th>症状</th><th>修法</th></tr></thead><tbody><tr><td>初始化错</td><td>恰好装满当成至多，答案偏大</td><td>max 用 -INF、min 用 INF，只把 f[0] 置 0</td></tr><tr><td>转移顺序错</td><td>01 背包写成正序，物品被重复选</td><td>01 逆序、完全正序、分组容量在外</td></tr><tr><td>循环边界</td><td>区间 DP 越界访问 f[i+1][j-1]</td><td>先枚举 len，len&#x3D;1&#x2F;2 手动初始化</td></tr><tr><td>记忆化污染</td><td>数位 DP 把贴上限的答案记进 f</td><td>只在 !lim &amp;&amp; !lead 时写 f</td></tr><tr><td>溢出</td><td>方案数&#x2F;距离&#x2F;乘法爆 int</td><td>计数、答案、中间量统一 long long，叉积用 __int128</td></tr><tr><td>复杂度误判</td><td>O(n^3)&#x2F;O(2^n n) 直接 TLE</td><td>n&lt;&#x3D;500 容 O(n^3)，n&lt;&#x3D;20 容 O(2^n n)，n&lt;&#x3D;1e6 只容 O(n)</td></tr><tr><td>滚动数组</td><td>覆盖了本轮还要用的旧值</td><td>明确「本层&#x2F;上层」，必要时开 2 个数组 swap</td></tr><tr><td>多组数据</td><td>数组没清空，第二组答案错</td><td>memset 或重新初始化，注意 f 的 -1 标记</td></tr></tbody></table><blockquote><p>⚠️ CSP 上机 4 小时 5 题，DP 题的性价比取决于复杂度是否压得住：n&#x3D;1e5 配 O(n sqrt n) 会挂，n&#x3D;2000 配 O(n^2) 才稳，先写暴力对拍再上优化。空间上 int f[5000][5000] 已经 100MB、逼近 256MB 限制，二维 DP 优先滚动数组，树上背包与区间 DP 按 n 的实际范围开数组。</p></blockquote><h2 id="第-04-章-数据结构与字符串"><a href="#第-04-章-数据结构与字符串" class="headerlink" title="第 04 章 数据结构与字符串"></a>第 04 章 数据结构与字符串</h2><blockquote><p>多组数据时记得清空 ls&#x2F;rs&#x2F;rev&#x2F;ch 等数组。代码默认以 <code>#include &lt;bits/stdc++.h&gt;</code> + <code>using namespace std;</code> 开头（CSP 允许），不再重复；下标无说明从 1 开始。同一小节内代码块共用数组名，实际使用时按需保留一种。</p></blockquote><h3 id="A-数据结构"><a href="#A-数据结构" class="headerlink" title="A. 数据结构"></a>A. 数据结构</h3><h4 id="1-并查集"><a href="#1-并查集" class="headerlink" title="1. 并查集"></a>1. 并查集</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 1.1 路径压缩 + 按大小合并, 总 O(n alpha(n))</span></span><br><span class="line"><span class="type">int</span> fa[N],sz[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">(<span class="type">int</span> n)</span></span>&#123;<span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)fa[i]=i,sz[i]=<span class="number">1</span>;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">find</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;<span class="keyword">return</span> fa[x]==x?x:fa[x]=<span class="built_in">find</span>(fa[x]);&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">unite</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y)</span></span>&#123;x=<span class="built_in">find</span>(x),y=<span class="built_in">find</span>(y);<span class="keyword">if</span>(x==y)<span class="keyword">return</span> <span class="number">0</span>;<span class="keyword">if</span>(sz[x]&lt;sz[y])<span class="built_in">swap</span>(x,y);fa[y]=x,sz[x]+=sz[y];<span class="keyword">return</span> <span class="number">1</span>;&#125;</span><br><span class="line"><span class="comment">// 1.2 带权并查集: dis[x]=val[x]-val[fa[x]] (模 M), O(alpha(n)); qfind 替换上面的 find</span></span><br><span class="line"><span class="type">int</span> dis[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">init2</span><span class="params">(<span class="type">int</span> n)</span></span>&#123;<span class="built_in">init</span>(n);<span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)dis[i]=<span class="number">0</span>;&#125;   <span class="comment">// 带权版本必须额外清空 dis</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">qfind</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;<span class="keyword">if</span>(fa[x]==x)<span class="keyword">return</span> x; <span class="type">int</span> y=<span class="built_in">qfind</span>(fa[x]);   <span class="comment">// fa[x] 此刻仍是旧父亲, 其 dis 已指向根</span></span><br><span class="line">  <span class="keyword">return</span> dis[x]=(dis[x]+dis[fa[x]])%M,fa[x]=y;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">query</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y)</span></span>&#123;<span class="built_in">qfind</span>(x),<span class="built_in">qfind</span>(y);<span class="keyword">return</span> fa[x]!=fa[y]?<span class="number">-1</span>:(dis[y]-dis[x]+M)%M;&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">qunite</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y,<span class="type">int</span> d)</span></span>&#123;                 <span class="comment">// 断言 val[y]-val[x]=d, 返回是否相容</span></span><br><span class="line">  <span class="built_in">qfind</span>(x),<span class="built_in">qfind</span>(y); d=(d+M-dis[y])%M,d=(d+dis[x])%M; x=fa[x],y=fa[y];</span><br><span class="line">  <span class="keyword">if</span>(x==y)<span class="keyword">return</span> d==<span class="number">0</span>; <span class="keyword">if</span>(sz[x]&lt;sz[y])<span class="built_in">swap</span>(x,y),d=(M-d)%M;</span><br><span class="line">  <span class="keyword">return</span> fa[y]=x,sz[x]+=sz[y],dis[y]=d,<span class="number">1</span>;&#125;</span><br><span class="line"><span class="comment">// 1.3 种类并查集(拆点): x 自身 | x+n 猎物 | x+2n 天敌; k 个种类开 k 倍点</span></span><br><span class="line"><span class="type">int</span> cf[<span class="number">3</span>*N];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">cfind</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;<span class="keyword">return</span> cf[x]==x?x:cf[x]=<span class="built_in">cfind</span>(cf[x]);&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">cmerge</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y)</span></span>&#123;x=<span class="built_in">cfind</span>(x),y=<span class="built_in">cfind</span>(y);<span class="keyword">if</span>(x!=y)cf[x]=y;&#125;</span><br><span class="line"><span class="comment">// 同类 cmerge(x,y),cmerge(x+n,y+n),cmerge(x+2n,y+2n);  x 吃 y cmerge(x,y+2n),cmerge(x+n,y),cmerge(x+2n,y+n)</span></span><br><span class="line"><span class="comment">// 冲突: 同类 cfind(x)==cfind(y+n)||cfind(x+n)==cfind(y);  x 吃 y cfind(x)==cfind(y)||cfind(x)==cfind(y+n)</span></span><br><span class="line"><span class="comment">// 与&quot;权值模 3 的带权并查集&quot;等价</span></span><br><span class="line"><span class="comment">// 1.4 可撤销并查集: 只按大小合并 + 栈记录, 单次 O(log n)</span></span><br><span class="line"><span class="type">int</span> rfa[N],rsz[N],top; <span class="keyword">struct</span> <span class="title class_">Op</span>&#123;<span class="type">int</span> x,y,sy;&#125; st[N*<span class="number">20</span>];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">rfind</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;<span class="keyword">while</span>(rfa[x]!=x)x=rfa[x];<span class="keyword">return</span> x;&#125;   <span class="comment">// 绝不能路径压缩!</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">runite</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y)</span></span>&#123;</span><br><span class="line">  x=<span class="built_in">rfind</span>(x),y=<span class="built_in">rfind</span>(y);</span><br><span class="line">  <span class="keyword">if</span>(x==y)&#123;st[++top]=&#123;<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>&#125;;<span class="keyword">return</span> <span class="number">0</span>;&#125;           <span class="comment">// 占位, 使回滚步数对齐</span></span><br><span class="line">  <span class="keyword">if</span>(rsz[x]&lt;rsz[y])<span class="built_in">swap</span>(x,y); st[++top]=&#123;x,y,rsz[y]&#125;;</span><br><span class="line">  rfa[y]=x,rsz[x]+=rsz[y]; <span class="keyword">return</span> <span class="number">1</span>;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">undo</span><span class="params">()</span></span>&#123;Op o=st[top--];<span class="keyword">if</span>(o.x)rfa[o.y]=o.y,rsz[o.x]-=o.sy;&#125;</span><br><span class="line"><span class="comment">// 用法: int save=top; ...操作...; while(top&gt;save)undo();</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 递归 find 深链可能爆栈，可改迭代；可撤销并查集<strong>绝不能路径压缩</strong>（否则无法还原），退化到 O(log n) 但仍正确，常用于线段树分治 &#x2F; 回滚莫队。</p></blockquote><h4 id="2-树状数组"><a href="#2-树状数组" class="headerlink" title="2. 树状数组"></a>2. 树状数组</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 2.1 单点改+区间查 / 2.2 区间改(差分)+单点查, 均 O(log n)</span></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> t[N]; <span class="type">int</span> n;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> i,<span class="type">long</span> <span class="type">long</span> v)</span></span>&#123;<span class="keyword">for</span>(;i&lt;=n;i+=i&amp;-i)t[i]+=v;&#125;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">pre</span><span class="params">(<span class="type">int</span> i)</span></span>&#123;<span class="type">long</span> <span class="type">long</span> s=<span class="number">0</span>;<span class="keyword">for</span>(;i&gt;<span class="number">0</span>;i-=i&amp;-i)s+=t[i];<span class="keyword">return</span> s;&#125;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">qry</span><span class="params">(<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;<span class="keyword">return</span> <span class="built_in">pre</span>(r)-<span class="built_in">pre</span>(l<span class="number">-1</span>);&#125;          <span class="comment">// 2.1</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">upd</span><span class="params">(<span class="type">int</span> l,<span class="type">int</span> r,<span class="type">long</span> <span class="type">long</span> v)</span></span>&#123;<span class="built_in">add</span>(l,v),<span class="built_in">add</span>(r<span class="number">+1</span>,-v);&#125;     <span class="comment">// 2.2</span></span><br><span class="line"><span class="comment">// 2.3 区间改 + 区间查(两个 BIT 维护 d[i] 与 d[i]*i), O(log n)</span></span><br><span class="line"><span class="comment">// 公式 sum_&#123;i&lt;=r&#125; a[i] = (r+1)*sum d[i] - sum d[i]*i</span></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> t1[N],t2[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">tadd</span><span class="params">(<span class="type">int</span> k,<span class="type">long</span> <span class="type">long</span> v)</span></span>&#123;<span class="type">long</span> <span class="type">long</span> v1=<span class="number">1LL</span>*k*v;   <span class="comment">// 必须先用原下标算; 循环里 k 已变成树上下标</span></span><br><span class="line">  <span class="keyword">for</span>(;k&lt;=n;k+=k&amp;-k)t1[k]+=v,t2[k]+=v1;&#125;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">tpre</span><span class="params">(<span class="type">long</span> <span class="type">long</span>*t,<span class="type">int</span> k)</span></span>&#123;<span class="type">long</span> <span class="type">long</span> s=<span class="number">0</span>;<span class="keyword">for</span>(;k&gt;<span class="number">0</span>;k-=k&amp;-k)s+=t[k];<span class="keyword">return</span> s;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">tupd</span><span class="params">(<span class="type">int</span> l,<span class="type">int</span> r,<span class="type">long</span> <span class="type">long</span> v)</span></span>&#123;<span class="built_in">tadd</span>(l,v),<span class="built_in">tadd</span>(r<span class="number">+1</span>,-v);&#125;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">preSum</span><span class="params">(<span class="type">int</span> k)</span></span>&#123;<span class="keyword">return</span> <span class="number">1LL</span>*(k<span class="number">+1</span>)*<span class="built_in">tpre</span>(t1,k)-<span class="built_in">tpre</span>(t2,k);&#125;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">qry2</span><span class="params">(<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;<span class="keyword">return</span> <span class="built_in">preSum</span>(r)-<span class="built_in">preSum</span>(l<span class="number">-1</span>);&#125;</span><br><span class="line"><span class="comment">// 2.4 求逆序对(权值 BIT) + 2.5 树状数组上二分求全局第 k 小</span></span><br><span class="line"><span class="type">int</span> a[N],b[N];</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">inv</span><span class="params">(<span class="type">int</span> n)</span></span>&#123;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)b[i]=a[i]; <span class="built_in">sort</span>(b<span class="number">+1</span>,b+n<span class="number">+1</span>);</span><br><span class="line">  <span class="type">int</span> m=<span class="built_in">unique</span>(b<span class="number">+1</span>,b+n<span class="number">+1</span>)-b<span class="number">-1</span>; <span class="type">long</span> <span class="type">long</span> ans=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;<span class="type">int</span> p=<span class="built_in">lower_bound</span>(b<span class="number">+1</span>,b+m<span class="number">+1</span>,a[i])-b; ans+=(i<span class="number">-1</span>)-<span class="built_in">pre</span>(p); <span class="built_in">add</span>(p,<span class="number">1</span>);&#125;</span><br><span class="line">  <span class="keyword">return</span> ans;&#125;                                       <span class="comment">// 前面严格大于 a[i] 的个数</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">kth</span><span class="params">(<span class="type">int</span> k)</span></span>&#123;                                      <span class="comment">// 权值 BIT 上二分; 越界返回 n+1</span></span><br><span class="line">  <span class="type">int</span> x=<span class="number">0</span>,i=<span class="number">1</span>&lt;&lt;(<span class="number">31</span>-__builtin_clz(n)); <span class="type">long</span> <span class="type">long</span> s=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(;i;i&gt;&gt;=<span class="number">1</span>) <span class="keyword">if</span>(x+i&lt;=n&amp;&amp;s+t[x+i]&lt;k)x+=i,s+=t[x];</span><br><span class="line">  <span class="keyword">return</span> x<span class="number">+1</span>;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>__builtin_clz(0)</code> 未定义，n 是 2 的幂时循环上界直接取 n；求第 k 大先转成第 (总数-k+1) 小。区间改区间查的 <code>t2</code> 必须用<strong>原下标</strong>乘 v。</p></blockquote><h4 id="3-线段树"><a href="#3-线段树" class="headerlink" title="3. 线段树"></a>3. 线段树</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 3.1 区间加 + 区间和(懒标记), O(log n) 每次, 空间 4n; build: 叶子置 a[l] 后 pushup</span></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> d[<span class="number">4</span>*N],b[<span class="number">4</span>*N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">applyNode</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> len,<span class="type">long</span> <span class="type">long</span> v)</span></span>&#123;d[p]+=v*len,b[p]+=v;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">pushdown</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(!b[p]||l==r)<span class="keyword">return</span>; <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  <span class="built_in">applyNode</span>(p&lt;&lt;<span class="number">1</span>,m-l<span class="number">+1</span>,b[p]),<span class="built_in">applyNode</span>(p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,r-m,b[p]),b[p]=<span class="number">0</span>;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">upd</span><span class="params">(<span class="type">int</span> L,<span class="type">int</span> R,<span class="type">long</span> <span class="type">long</span> v,<span class="type">int</span> p=<span class="number">1</span>,<span class="type">int</span> l=<span class="number">1</span>,<span class="type">int</span> r=<span class="number">0</span>)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(!r)r=n; <span class="keyword">if</span>(L&lt;=l&amp;&amp;r&lt;=R)&#123;<span class="built_in">applyNode</span>(p,r-l<span class="number">+1</span>,v);<span class="keyword">return</span>;&#125;</span><br><span class="line">  <span class="built_in">pushdown</span>(p,l,r); <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">if</span>(L&lt;=m)<span class="built_in">upd</span>(L,R,v,p&lt;&lt;<span class="number">1</span>,l,m); <span class="keyword">if</span>(R&gt;m)<span class="built_in">upd</span>(L,R,v,p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,m<span class="number">+1</span>,r);</span><br><span class="line">  d[p]=d[p&lt;&lt;<span class="number">1</span>]+d[p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>];&#125;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">qry</span><span class="params">(<span class="type">int</span> L,<span class="type">int</span> R,<span class="type">int</span> p=<span class="number">1</span>,<span class="type">int</span> l=<span class="number">1</span>,<span class="type">int</span> r=<span class="number">0</span>)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(!r)r=n; <span class="keyword">if</span>(L&lt;=l&amp;&amp;r&lt;=R)<span class="keyword">return</span> d[p];</span><br><span class="line">  <span class="built_in">pushdown</span>(p,l,r); <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>; <span class="type">long</span> <span class="type">long</span> s=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">if</span>(L&lt;=m)s+=<span class="built_in">qry</span>(L,R,p&lt;&lt;<span class="number">1</span>,l,m); <span class="keyword">if</span>(R&gt;m)s+=<span class="built_in">qry</span>(L,R,p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,m<span class="number">+1</span>,r);</span><br><span class="line">  <span class="keyword">return</span> s;&#125;</span><br><span class="line"><span class="comment">// 3.2 区间赋值 + 区间加 + 区间和 + 区间最值(双标记), O(log n); 查询用 qry3(...,isMax)</span></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> sum[<span class="number">4</span>*N],mx[<span class="number">4</span>*N],add[<span class="number">4</span>*N],setv[<span class="number">4</span>*N]; <span class="type">bool</span> has[<span class="number">4</span>*N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">applySet</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> len,<span class="type">long</span> <span class="type">long</span> v)</span></span>&#123;sum[p]=v*len,mx[p]=v,setv[p]=v,has[p]=<span class="number">1</span>,add[p]=<span class="number">0</span>;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">applyAdd</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> len,<span class="type">long</span> <span class="type">long</span> v)</span></span>&#123;sum[p]+=v*len,mx[p]+=v; <span class="keyword">if</span>(has[p])setv[p]+=v; <span class="keyword">else</span> add[p]+=v;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">pushdown2</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;                 <span class="comment">// 先赋值后加法</span></span><br><span class="line">  <span class="keyword">if</span>(l==r)<span class="keyword">return</span>; <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">if</span>(has[p])<span class="built_in">applySet</span>(p&lt;&lt;<span class="number">1</span>,m-l<span class="number">+1</span>,setv[p]),<span class="built_in">applySet</span>(p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,r-m,setv[p]),has[p]=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">if</span>(add[p])<span class="built_in">applyAdd</span>(p&lt;&lt;<span class="number">1</span>,m-l<span class="number">+1</span>,add[p]),<span class="built_in">applyAdd</span>(p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,r-m,add[p]),add[p]=<span class="number">0</span>;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">upd2</span><span class="params">(<span class="type">int</span> L,<span class="type">int</span> R,<span class="type">long</span> <span class="type">long</span> v,<span class="type">int</span> op,<span class="type">int</span> p=<span class="number">1</span>,<span class="type">int</span> l=<span class="number">1</span>,<span class="type">int</span> r=<span class="number">0</span>)</span></span>&#123;  <span class="comment">// op=1 赋值, op=0 加</span></span><br><span class="line">  <span class="keyword">if</span>(!r)r=n; <span class="keyword">if</span>(L&lt;=l&amp;&amp;r&lt;=R)&#123;op?<span class="built_in">applySet</span>(p,r-l<span class="number">+1</span>,v):<span class="built_in">applyAdd</span>(p,r-l<span class="number">+1</span>,v);<span class="keyword">return</span>;&#125;</span><br><span class="line">  <span class="built_in">pushdown2</span>(p,l,r); <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">if</span>(L&lt;=m)<span class="built_in">upd2</span>(L,R,v,op,p&lt;&lt;<span class="number">1</span>,l,m); <span class="keyword">if</span>(R&gt;m)<span class="built_in">upd2</span>(L,R,v,op,p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,m<span class="number">+1</span>,r);</span><br><span class="line">  sum[p]=sum[p&lt;&lt;<span class="number">1</span>]+sum[p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>],mx[p]=<span class="built_in">max</span>(mx[p&lt;&lt;<span class="number">1</span>],mx[p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>]);&#125;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">qry3</span><span class="params">(<span class="type">int</span> L,<span class="type">int</span> R,<span class="type">int</span> p=<span class="number">1</span>,<span class="type">int</span> l=<span class="number">1</span>,<span class="type">int</span> r=<span class="number">0</span>,<span class="type">bool</span> isMax=<span class="number">0</span>)</span></span>&#123;   <span class="comment">// 区间和; isMax=1 求最值</span></span><br><span class="line">  <span class="keyword">if</span>(!r)r=n; <span class="keyword">if</span>(L&lt;=l&amp;&amp;r&lt;=R)<span class="keyword">return</span> isMax?mx[p]:sum[p];</span><br><span class="line">  <span class="built_in">pushdown2</span>(p,l,r); <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">if</span>(R&lt;=m)<span class="keyword">return</span> <span class="built_in">qry3</span>(L,R,p&lt;&lt;<span class="number">1</span>,l,m,isMax); <span class="keyword">if</span>(L&gt;m)<span class="keyword">return</span> <span class="built_in">qry3</span>(L,R,p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,m<span class="number">+1</span>,r,isMax);</span><br><span class="line">  <span class="keyword">return</span> isMax?<span class="built_in">max</span>(<span class="built_in">qry3</span>(L,R,p&lt;&lt;<span class="number">1</span>,l,m,<span class="number">1</span>),<span class="built_in">qry3</span>(L,R,p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,m<span class="number">+1</span>,r,<span class="number">1</span>))</span><br><span class="line">              :<span class="built_in">qry3</span>(L,R,p&lt;&lt;<span class="number">1</span>,l,m)+<span class="built_in">qry3</span>(L,R,p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,m<span class="number">+1</span>,r);&#125;</span><br><span class="line"><span class="comment">// 3.3 动态开点 + 权值线段树(第 k 小 / 排名), 单点改 O(log V)</span></span><br><span class="line"><span class="type">int</span> ls[N*(LOG<span class="number">+2</span>)],rs[N*(LOG<span class="number">+2</span>)],cnt,rt; <span class="type">long</span> <span class="type">long</span> sval[N*(LOG<span class="number">+2</span>)];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dupd</span><span class="params">(<span class="type">int</span>&amp;p,<span class="type">int</span> l,<span class="type">int</span> r,<span class="type">int</span> x,<span class="type">long</span> <span class="type">long</span> v)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(!p)p=++cnt,ls[p]=rs[p]=<span class="number">0</span>,sval[p]=<span class="number">0</span>; <span class="keyword">if</span>(l==r)&#123;sval[p]+=v;<span class="keyword">return</span>;&#125; <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  x&lt;=m?<span class="built_in">dupd</span>(ls[p],l,m,x,v):<span class="built_in">dupd</span>(rs[p],m<span class="number">+1</span>,r,x,v);</span><br><span class="line">  sval[p]=sval[ls[p]]+sval[rs[p]];&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">kth</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> l,<span class="type">int</span> r,<span class="type">int</span> k)</span></span>&#123;                   <span class="comment">// 区间内第 k 小, 返回下标</span></span><br><span class="line">  <span class="keyword">if</span>(l==r)<span class="keyword">return</span> l; <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">return</span> sval[ls[p]]&gt;=k?<span class="built_in">kth</span>(ls[p],l,m,k):<span class="built_in">kth</span>(rs[p],m<span class="number">+1</span>,r,k-sval[ls[p]]);&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">rnk</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> l,<span class="type">int</span> r,<span class="type">int</span> x)</span></span>&#123;                   <span class="comment">// &lt;= x 的个数</span></span><br><span class="line">  <span class="keyword">if</span>(!p||r&lt;=x)<span class="keyword">return</span> sval[p]; <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">return</span> x&lt;=m?<span class="built_in">rnk</span>(ls[p],l,m,x):sval[ls[p]]+<span class="built_in">rnk</span>(rs[p],m<span class="number">+1</span>,r,x);&#125;</span><br><span class="line"><span class="comment">// 3.4 线段树合并: 均摊 O(总点数), 整棵树合并 O(n log n)</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">merge</span><span class="params">(<span class="type">int</span> a,<span class="type">int</span> b,<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(!a||!b)<span class="keyword">return</span> a|b; <span class="keyword">if</span>(l==r)&#123;sval[a]+=sval[b];<span class="keyword">return</span> a;&#125; <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  ls[a]=<span class="built_in">merge</span>(ls[a],ls[b],l,m),rs[a]=<span class="built_in">merge</span>(rs[a],rs[b],m<span class="number">+1</span>,r);</span><br><span class="line">  <span class="keyword">return</span> sval[a]=sval[ls[a]]+sval[rs[a]],a;&#125;</span><br><span class="line"><span class="comment">// 3.5 扫描线求矩形面积并: O(n log n)</span></span><br><span class="line"><span class="type">int</span> n2,xs[<span class="number">2</span>*N],tot; <span class="type">long</span> <span class="type">long</span> cov[<span class="number">8</span>*N],len[<span class="number">8</span>*N];   <span class="comment">// 覆盖次数 / 被覆盖长度, 标记永不下传</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Edge</span>&#123;<span class="type">int</span> x1,x2,y,o;&#125; e[<span class="number">2</span>*N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">pushupSeg</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(cov[p])len[p]=xs[r]-xs[l]; <span class="keyword">else</span> len[p]=(l<span class="number">+1</span>==r)?<span class="number">0</span>:len[p&lt;&lt;<span class="number">1</span>]+len[p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>];&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">supd</span><span class="params">(<span class="type">int</span> L,<span class="type">int</span> R,<span class="type">int</span> o,<span class="type">int</span> p=<span class="number">1</span>,<span class="type">int</span> l=<span class="number">1</span>,<span class="type">int</span> r=<span class="number">0</span>)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(!r)r=tot; <span class="keyword">if</span>(L&lt;=l&amp;&amp;r&lt;=R)&#123;cov[p]+=o,<span class="built_in">pushupSeg</span>(p,l,r);<span class="keyword">return</span>;&#125;</span><br><span class="line">  <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">if</span>(L&lt;m)<span class="built_in">supd</span>(L,R,o,p&lt;&lt;<span class="number">1</span>,l,m); <span class="keyword">if</span>(R&gt;m)<span class="built_in">supd</span>(L,R,o,p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,m,r);</span><br><span class="line">  <span class="built_in">pushupSeg</span>(p,l,r);&#125;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">solve</span><span class="params">()</span></span>&#123;                                 <span class="comment">// 读入 n2 个矩形 x1,y1,x2,y2 后调用</span></span><br><span class="line">  <span class="built_in">sort</span>(xs<span class="number">+1</span>,xs<span class="number">+2</span>*n2<span class="number">+1</span>); tot=<span class="built_in">unique</span>(xs<span class="number">+1</span>,xs<span class="number">+2</span>*n2<span class="number">+1</span>)-xs<span class="number">-1</span>;</span><br><span class="line">  <span class="built_in">sort</span>(e<span class="number">+1</span>,e<span class="number">+2</span>*n2<span class="number">+1</span>,[](<span class="type">const</span> Edge&amp;a,<span class="type">const</span> Edge&amp;b)&#123;<span class="keyword">return</span> a.y&lt;b.y;&#125;);</span><br><span class="line">  <span class="type">long</span> <span class="type">long</span> ans=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;<span class="number">2</span>*n2;i++)&#123;<span class="built_in">supd</span>(e[i].x1,e[i].x2,e[i].o); ans+=<span class="number">1LL</span>*(e[i<span class="number">+1</span>].y-e[i].y)*len[<span class="number">1</span>];&#125;</span><br><span class="line">  <span class="keyword">return</span> ans;&#125;</span><br><span class="line"><span class="comment">// 3.6 李超线段树(直线版, 简述): 每点存中点最优直线, 插入时与节点直线比较, 输的那条只往一侧递归</span></span><br><span class="line"><span class="comment">// (两直线最多一个交点), 故插入直线 O(log V)、查询 O(log V); 插入线段需拆成 O(log V) 个整区间</span></span><br><span class="line"><span class="type">double</span> K[N],B[N]; <span class="type">int</span> id[N];</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">f</span><span class="params">(<span class="type">int</span> i,<span class="type">double</span> x)</span></span>&#123;<span class="keyword">return</span> K[i]*x+B[i];&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">ins</span><span class="params">(<span class="type">int</span> i,<span class="type">int</span> p,<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(!id[p])&#123;id[p]=i;<span class="keyword">return</span>;&#125; <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">f</span>(i,m)&gt;<span class="built_in">f</span>(id[p],m))<span class="built_in">swap</span>(i,id[p]);</span><br><span class="line">  <span class="keyword">if</span>(l==r)<span class="keyword">return</span>;</span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">f</span>(i,l)&gt;<span class="built_in">f</span>(id[p],l))<span class="built_in">ins</span>(i,p&lt;&lt;<span class="number">1</span>,l,m); <span class="keyword">else</span> <span class="keyword">if</span>(<span class="built_in">f</span>(i,r)&gt;<span class="built_in">f</span>(id[p],r))<span class="built_in">ins</span>(i,p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,m<span class="number">+1</span>,r);&#125;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">lqry</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> p,<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">  <span class="type">double</span> res=id[p]?<span class="built_in">f</span>(id[p],x):<span class="number">-1e18</span>; <span class="keyword">if</span>(l==r)<span class="keyword">return</span> res; <span class="type">int</span> m=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">return</span> x&lt;=m?<span class="built_in">max</span>(res,<span class="built_in">lqry</span>(x,p&lt;&lt;<span class="number">1</span>,l,m)):<span class="built_in">max</span>(res,<span class="built_in">lqry</span>(x,p&lt;&lt;<span class="number">1</span>|<span class="number">1</span>,m<span class="number">+1</span>,r));&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 双标记下传顺序<strong>先赋值后加法</strong>，<code>applyAdd</code> 遇到已有赋值标记要并进 setv 而不能新开 add；扫描线线段树不是满二叉树，<strong>空间开 8n</strong>。</p></blockquote><h4 id="4-可持久化线段树（主席树）——静态区间第-k-小"><a href="#4-可持久化线段树（主席树）——静态区间第-k-小" class="headerlink" title="4. 可持久化线段树（主席树）——静态区间第 k 小"></a>4. 可持久化线段树（主席树）——静态区间第 k 小</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// O(n log n) 建树, O(log n) 询问, 空间约 n*log2(值域)（n=1e5 开 N&lt;&lt;5）</span></span><br><span class="line"><span class="type">int</span> n,m,a[N],ind[N],len_;   <span class="comment">// ind 为离散化数组</span></span><br><span class="line"><span class="type">int</span> tot,sum[N&lt;&lt;<span class="number">5</span>],ls[N&lt;&lt;<span class="number">5</span>],rs[N&lt;&lt;<span class="number">5</span>],root[N];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">getid</span><span class="params">(<span class="type">int</span> v)</span></span>&#123;<span class="keyword">return</span> <span class="built_in">lower_bound</span>(ind<span class="number">+1</span>,ind+len_<span class="number">+1</span>,v)-ind;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">build</span><span class="params">(<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;<span class="type">int</span> p=++tot; <span class="keyword">if</span>(l==r)<span class="keyword">return</span> p; <span class="type">int</span> mid=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">return</span> ls[p]=<span class="built_in">build</span>(l,mid),rs[p]=<span class="built_in">build</span>(mid<span class="number">+1</span>,r),p;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">update</span><span class="params">(<span class="type">int</span> pre,<span class="type">int</span> l,<span class="type">int</span> r,<span class="type">int</span> k)</span></span>&#123;</span><br><span class="line">  <span class="type">int</span> p=++tot; ls[p]=ls[pre],rs[p]=rs[pre],sum[p]=sum[pre]<span class="number">+1</span>;</span><br><span class="line">  <span class="keyword">if</span>(l==r)<span class="keyword">return</span> p; <span class="type">int</span> mid=(l+r)&gt;&gt;<span class="number">1</span>;</span><br><span class="line">  k&lt;=mid?ls[p]=<span class="built_in">update</span>(ls[pre],l,mid,k):rs[p]=<span class="built_in">update</span>(rs[pre],mid<span class="number">+1</span>,r,k);</span><br><span class="line">  <span class="keyword">return</span> p;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">query</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> v,<span class="type">int</span> l,<span class="type">int</span> r,<span class="type">int</span> k)</span></span>&#123;          <span class="comment">// (u,v] 中第 k 小, 返回离散化下标</span></span><br><span class="line">  <span class="keyword">if</span>(l==r)<span class="keyword">return</span> l; <span class="type">int</span> mid=(l+r)&gt;&gt;<span class="number">1</span>,x=sum[ls[v]]-sum[ls[u]];</span><br><span class="line">  <span class="keyword">return</span> k&lt;=x?<span class="built_in">query</span>(ls[u],ls[v],l,mid,k):<span class="built_in">query</span>(rs[u],rs[v],mid<span class="number">+1</span>,r,k-x);&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">init</span><span class="params">()</span></span>&#123;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)ind[i]=a[i];</span><br><span class="line">  <span class="built_in">sort</span>(ind<span class="number">+1</span>,ind+n<span class="number">+1</span>),len_=<span class="built_in">unique</span>(ind<span class="number">+1</span>,ind+n<span class="number">+1</span>)-ind<span class="number">-1</span>;</span><br><span class="line">  root[<span class="number">0</span>]=<span class="built_in">build</span>(<span class="number">1</span>,len_);</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)root[i]=<span class="built_in">update</span>(root[i<span class="number">-1</span>],<span class="number">1</span>,len_,<span class="built_in">getid</span>(a[i]));&#125;</span><br><span class="line"><span class="comment">// 答案 = ind[query(root[l-1],root[r],1,len_,k)]</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 主席树节点数与”修改次数 * log 值域”同阶；带修改（可持久化 BIT 套权值树）要再放大。</p></blockquote><h4 id="5-ST-表与稀疏表-LCA"><a href="#5-ST-表与稀疏表-LCA" class="headerlink" title="5. ST 表与稀疏表 LCA"></a>5. ST 表与稀疏表 LCA</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 5.1 一维 ST 表: O(n log n) 预处理, O(1) 查询, 不支持修改</span></span><br><span class="line"><span class="type">int</span> a[N],lg[N],f[LOG<span class="number">+1</span>][N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">build</span><span class="params">(<span class="type">int</span> n)</span></span>&#123;</span><br><span class="line">  lg[<span class="number">1</span>]=<span class="number">0</span>; <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">2</span>;i&lt;=n;i++)lg[i]=lg[i&gt;&gt;<span class="number">1</span>]<span class="number">+1</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)f[<span class="number">0</span>][i]=a[i];</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">1</span>;j&lt;=LOG;j++)<span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i+(<span class="number">1</span>&lt;&lt;j)<span class="number">-1</span>&lt;=n;i++)</span><br><span class="line">    f[j][i]=<span class="built_in">max</span>(f[j<span class="number">-1</span>][i],f[j<span class="number">-1</span>][i+(<span class="number">1</span>&lt;&lt;(j<span class="number">-1</span>))]);&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">qry</span><span class="params">(<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;<span class="type">int</span> s=lg[r-l<span class="number">+1</span>];<span class="keyword">return</span> <span class="built_in">max</span>(f[s][l],f[s][r-(<span class="number">1</span>&lt;&lt;s)<span class="number">+1</span>]);&#125;</span><br><span class="line"><span class="comment">// 5.2 二维 ST 表: O(nm log n log m) 预处理, O(1) 查询子矩阵最值(空间同阶, n,m 大时慎用)</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> NS=<span class="number">505</span>; <span class="type">int</span> nn,mm,g2[NS][NS],f2[LOG][LOG][NS][NS];   <span class="comment">// NS 为行列上界, 空间 O(NS^2 log^2)</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">build2</span><span class="params">()</span></span>&#123;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=nn;i++)<span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">1</span>;j&lt;=mm;j++)f2[<span class="number">0</span>][<span class="number">0</span>][i][j]=g2[i][j];</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> k=<span class="number">0</span>;k&lt;LOG;k++)<span class="keyword">for</span>(<span class="type">int</span> l=<span class="number">0</span>;l&lt;LOG;l++)&#123; <span class="keyword">if</span>(!k&amp;&amp;!l)<span class="keyword">continue</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i+(<span class="number">1</span>&lt;&lt;k)<span class="number">-1</span>&lt;=nn;i++)<span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">1</span>;j+(<span class="number">1</span>&lt;&lt;l)<span class="number">-1</span>&lt;=mm;j++)&#123;</span><br><span class="line">      <span class="type">int</span> v=k?<span class="built_in">max</span>(f2[k<span class="number">-1</span>][l][i][j],f2[k<span class="number">-1</span>][l][i+(<span class="number">1</span>&lt;&lt;(k<span class="number">-1</span>))][j])   <span class="comment">// 只能从已算好的邻居取值</span></span><br><span class="line">               :<span class="built_in">max</span>(f2[k][l<span class="number">-1</span>][i][j],f2[k][l<span class="number">-1</span>][i][j+(<span class="number">1</span>&lt;&lt;(l<span class="number">-1</span>))]);</span><br><span class="line">      <span class="keyword">if</span>(k&amp;&amp;l)v=<span class="built_in">max</span>(v,<span class="built_in">max</span>(f2[k][l<span class="number">-1</span>][i][j],f2[k][l<span class="number">-1</span>][i][j+(<span class="number">1</span>&lt;&lt;(l<span class="number">-1</span>))]));</span><br><span class="line">      f2[k][l][i][j]=v;&#125;&#125;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">qry2</span><span class="params">(<span class="type">int</span> x1,<span class="type">int</span> y1,<span class="type">int</span> x2,<span class="type">int</span> y2)</span></span>&#123;</span><br><span class="line">  <span class="type">int</span> kx=lg[x2-x1<span class="number">+1</span>],ky=lg[y2-y1<span class="number">+1</span>],dx=x2-(<span class="number">1</span>&lt;&lt;kx)<span class="number">+1</span>,dy=y2-(<span class="number">1</span>&lt;&lt;ky)<span class="number">+1</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">max</span>(<span class="built_in">max</span>(f2[kx][ky][x1][y1],f2[kx][ky][dx][y1]),<span class="built_in">max</span>(f2[kx][ky][x1][dy],f2[kx][ky][dx][dy]));&#125;</span><br><span class="line"><span class="comment">// 5.3 稀疏表求 LCA: 欧拉序 + ST, O(n log n) 预处理, O(1) 查询</span></span><br><span class="line">vector&lt;<span class="type">int</span>&gt; g[N];</span><br><span class="line"><span class="type">int</span> dep[N],fir[N],euler[<span class="number">2</span>*N],tot,lg2[<span class="number">2</span>*N],st[LOG][<span class="number">2</span>*N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> f)</span></span>&#123;</span><br><span class="line">  dep[u]=dep[f]<span class="number">+1</span>,fir[u]=tot,euler[tot++]=u;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:g[u])<span class="keyword">if</span>(v!=f)<span class="built_in">dfs</span>(v,u),euler[tot++]=u;&#125;    <span class="comment">// 欧拉序长度 2n-1</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">build3</span><span class="params">(<span class="type">int</span> root)</span></span>&#123;</span><br><span class="line">  tot=<span class="number">0</span>,<span class="built_in">dfs</span>(root,<span class="number">0</span>);</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;tot;i++)st[<span class="number">0</span>][i]=euler[i];</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">2</span>;i&lt;=tot;i++)lg2[i]=lg2[i&gt;&gt;<span class="number">1</span>]<span class="number">+1</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">1</span>;(<span class="number">1</span>&lt;&lt;j)&lt;=tot;j++)<span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i+(<span class="number">1</span>&lt;&lt;j)&lt;=tot;i++)&#123;</span><br><span class="line">    <span class="type">int</span> x=st[j<span class="number">-1</span>][i],y=st[j<span class="number">-1</span>][i+(<span class="number">1</span>&lt;&lt;(j<span class="number">-1</span>))];</span><br><span class="line">    st[j][i]=dep[x]&lt;dep[y]?x:y;&#125;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">lca</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> v)</span></span>&#123;</span><br><span class="line">  <span class="type">int</span> l=fir[u],r=fir[v]; <span class="keyword">if</span>(l&gt;r)<span class="built_in">swap</span>(l,r);</span><br><span class="line">  <span class="type">int</span> k=lg2[r-l<span class="number">+1</span>],x=st[k][l],y=st[k][r-(<span class="number">1</span>&lt;&lt;k)<span class="number">+1</span>];</span><br><span class="line">  <span class="keyword">return</span> dep[x]&lt;dep[y]?x:y;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="6-单调栈与单调队列"><a href="#6-单调栈与单调队列" class="headerlink" title="6. 单调栈与单调队列"></a>6. 单调栈与单调队列</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 6.1 下一个更大元素 nxt[i]: 右侧第一个 &gt; a[i] 的下标, O(n)</span></span><br><span class="line"><span class="type">int</span> stk[N],top=<span class="number">0</span>,nxt[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">nextGreater</span><span class="params">()</span></span>&#123;<span class="keyword">for</span>(<span class="type">int</span> i=n;i&gt;=<span class="number">1</span>;i--)&#123;<span class="keyword">while</span>(top&amp;&amp;a[stk[top]]&lt;=a[i])top--; nxt[i]=top?stk[top]:n<span class="number">+1</span>; stk[++top]=i;&#125;&#125;</span><br><span class="line"><span class="comment">// 6.2 柱状图最大子矩形, O(n)</span></span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">maxRect</span><span class="params">(<span class="type">int</span> n,<span class="type">int</span> h[])</span></span>&#123;</span><br><span class="line">  <span class="type">static</span> <span class="type">int</span> s2[N]; <span class="type">int</span> tp=<span class="number">0</span>; <span class="type">long</span> <span class="type">long</span> ans=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n<span class="number">+1</span>;i++)&#123;</span><br><span class="line">    <span class="type">int</span> cur=(i&lt;=n?h[i]:<span class="number">0</span>);</span><br><span class="line">    <span class="keyword">while</span>(tp&amp;&amp;h[s2[tp]]&gt;=cur)&#123;<span class="type">int</span> H=h[s2[tp]]; tp--; ans=<span class="built_in">max</span>(ans,<span class="number">1LL</span>*H*(i-(tp?s2[tp]<span class="number">+1</span>:<span class="number">1</span>)));&#125;</span><br><span class="line">    s2[++tp]=i;&#125;</span><br><span class="line">  <span class="keyword">return</span> ans;&#125;</span><br><span class="line"><span class="comment">// 6.3 单调队列: 滑动窗口最大值, O(n); 求最小值把 &lt;= 改成 &gt;=</span></span><br><span class="line"><span class="type">int</span> q[N],head=<span class="number">1</span>,tail=<span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">slideWin</span><span class="params">(<span class="type">int</span> k)</span></span>&#123;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">    <span class="keyword">while</span>(head&lt;=tail&amp;&amp;a[q[tail]]&lt;=a[i])tail--;</span><br><span class="line">    q[++tail]=i;</span><br><span class="line">    <span class="keyword">while</span>(q[head]&lt;=i-k)head++;                 <span class="comment">// 弹出过期</span></span><br><span class="line">    <span class="keyword">if</span>(i&gt;=k)<span class="built_in">printf</span>(<span class="string">&quot;%d &quot;</span>,a[q[head]]);&#125;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="7-堆、可并堆与对顶堆"><a href="#7-堆、可并堆与对顶堆" class="headerlink" title="7. 堆、可并堆与对顶堆"></a>7. 堆、可并堆与对顶堆</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 7.1 priority_queue: 每次操作 O(log n)</span></span><br><span class="line">priority_queue&lt;<span class="type">int</span>&gt; q1;                                              <span class="comment">// 大根堆</span></span><br><span class="line">priority_queue&lt;<span class="type">int</span>,vector&lt;<span class="type">int</span>&gt;,greater&lt;<span class="type">int</span>&gt;&gt; q2;                     <span class="comment">// 小根堆</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span>&#123;<span class="type">int</span> d,u; <span class="type">bool</span> <span class="keyword">operator</span>&lt;(<span class="type">const</span> Node&amp;o)<span class="type">const</span>&#123;<span class="keyword">return</span> d&gt;o.d;&#125;&#125;;  <span class="comment">// Dijkstra 用</span></span><br><span class="line"><span class="comment">// 7.2 左偏树(可并堆), 合并 O(log n); d[0] 必须初始化为 -1</span></span><br><span class="line"><span class="type">int</span> ls[N],rs[N],val[N],d[N],tot;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">merge</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(!x||!y)<span class="keyword">return</span> x|y; <span class="keyword">if</span>(val[x]&gt;val[y])<span class="built_in">swap</span>(x,y);   <span class="comment">// 小根堆</span></span><br><span class="line">  rs[x]=<span class="built_in">merge</span>(rs[x],y);</span><br><span class="line">  <span class="keyword">if</span>(d[ls[x]]&lt;d[rs[x]])<span class="built_in">swap</span>(ls[x],rs[x]);             <span class="comment">// 保持左偏</span></span><br><span class="line">  <span class="keyword">return</span> d[x]=d[rs[x]]<span class="number">+1</span>,x;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">push</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> v)</span></span>&#123;val[++tot]=v,d[tot]=<span class="number">0</span>;<span class="keyword">return</span> <span class="built_in">merge</span>(x,tot);&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">pop</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;<span class="keyword">return</span> <span class="built_in">merge</span>(ls[x],rs[x]);&#125;</span><br><span class="line"><span class="comment">// 7.3 对顶堆求中位数: 插入 O(log n), 取中位数 O(1)</span></span><br><span class="line">priority_queue&lt;<span class="type">int</span>&gt; L;                                   <span class="comment">// 较小一半(大根堆)</span></span><br><span class="line">priority_queue&lt;<span class="type">int</span>,vector&lt;<span class="type">int</span>&gt;,greater&lt;<span class="type">int</span>&gt;&gt; R;          <span class="comment">// 较大一半(小根堆)</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">addm</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(L.<span class="built_in">empty</span>()||x&lt;=L.<span class="built_in">top</span>())L.<span class="built_in">push</span>(x); <span class="keyword">else</span> R.<span class="built_in">push</span>(x);</span><br><span class="line">  <span class="keyword">if</span>(L.<span class="built_in">size</span>()&gt;R.<span class="built_in">size</span>()<span class="number">+1</span>)R.<span class="built_in">push</span>(L.<span class="built_in">top</span>()),L.<span class="built_in">pop</span>();</span><br><span class="line">  <span class="keyword">if</span>(R.<span class="built_in">size</span>()&gt;L.<span class="built_in">size</span>())L.<span class="built_in">push</span>(R.<span class="built_in">top</span>()),R.<span class="built_in">pop</span>();&#125;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">median</span><span class="params">()</span></span>&#123;<span class="keyword">return</span> L.<span class="built_in">size</span>()==R.<span class="built_in">size</span>()?(L.<span class="built_in">top</span>()+R.<span class="built_in">top</span>())/<span class="number">2.0</span>:L.<span class="built_in">top</span>();&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 叶子 dist 应为 0，故空节点 <code>d[0]</code> 必须是 <strong>-1</strong>，否则左偏性质与 log 复杂度全崩。</p></blockquote><h4 id="8-分块与莫队"><a href="#8-分块与莫队" class="headerlink" title="8. 分块与莫队"></a>8. 分块与莫队</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 8.1 分块: 区间加 + 区间和, 每次 O(sqrt n)</span></span><br><span class="line"><span class="type">int</span> n,B,nb,bl[N],L[N],R[N]; <span class="type">long</span> <span class="type">long</span> a[N],sum[N],tag[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">build</span><span class="params">()</span></span>&#123;</span><br><span class="line">  B=<span class="built_in">sqrt</span>(n)<span class="number">+1</span>,nb=(n+B<span class="number">-1</span>)/B;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)bl[i]=(i<span class="number">-1</span>)/B<span class="number">+1</span>,sum[bl[i]]+=a[i];</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> b=<span class="number">1</span>;b&lt;=nb;b++)L[b]=(b<span class="number">-1</span>)*B<span class="number">+1</span>,R[b]=<span class="built_in">min</span>(n,b*B);&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">upd</span><span class="params">(<span class="type">int</span> l,<span class="type">int</span> r,<span class="type">long</span> <span class="type">long</span> v)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(bl[l]==bl[r])&#123;<span class="keyword">for</span>(<span class="type">int</span> i=l;i&lt;=r;i++)a[i]+=v,sum[bl[i]]+=v;<span class="keyword">return</span>;&#125;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=l;i&lt;=R[bl[l]];i++)a[i]+=v,sum[bl[i]]+=v;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=L[bl[r]];i&lt;=r;i++)a[i]+=v,sum[bl[i]]+=v;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> b=bl[l]<span class="number">+1</span>;b&lt;bl[r];b++)tag[b]+=v,sum[b]+=v*(R[b]-L[b]<span class="number">+1</span>);&#125;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">qry</span><span class="params">(<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">  <span class="type">long</span> <span class="type">long</span> s=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">if</span>(bl[l]==bl[r])&#123;<span class="keyword">for</span>(<span class="type">int</span> i=l;i&lt;=r;i++)s+=a[i]+tag[bl[i]];<span class="keyword">return</span> s;&#125;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=l;i&lt;=R[bl[l]];i++)s+=a[i]+tag[bl[i]];</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=L[bl[r]];i&lt;=r;i++)s+=a[i]+tag[bl[i]];</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> b=bl[l]<span class="number">+1</span>;b&lt;bl[r];b++)s+=sum[b];</span><br><span class="line">  <span class="keyword">return</span> s;&#125;</span><br><span class="line"><span class="comment">// 8.2 普通莫队(含奇偶化排序), 块长 n/sqrt(m); 总 O(n sqrt m)</span></span><br><span class="line"><span class="type">int</span> unit,cur,ans[M],cnt[N];</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Q</span>&#123;<span class="type">int</span> l,r,id;&#125; q[M];</span><br><span class="line"><span class="type">bool</span> <span class="keyword">operator</span>&lt;(<span class="type">const</span> Q&amp;x,<span class="type">const</span> Q&amp;y)&#123;</span><br><span class="line">  <span class="type">int</span> bx=x.l/unit,by=y.l/unit; <span class="keyword">if</span>(bx!=by)<span class="keyword">return</span> bx&lt;by;</span><br><span class="line">  <span class="keyword">return</span> (bx&amp;<span class="number">1</span>)?x.r&lt;y.r:x.r&gt;y.r;&#125;   <span class="comment">// 奇偶化: 奇数块 r 升序, 偶数块 r 降序, 约快 30%</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">solve</span><span class="params">(<span class="type">int</span> m,<span class="type">int</span> n)</span></span>&#123;</span><br><span class="line">  unit=<span class="built_in">max</span>(<span class="number">1</span>,(<span class="type">int</span>)(n/<span class="built_in">sqrt</span>(m))); <span class="built_in">sort</span>(q<span class="number">+1</span>,q+m<span class="number">+1</span>);</span><br><span class="line">  <span class="type">int</span> l=<span class="number">1</span>,r=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=m;i++)&#123;</span><br><span class="line">    <span class="keyword">while</span>(r&lt;q[i].r)<span class="built_in">add</span>(++r); <span class="keyword">while</span>(r&gt;q[i].r)<span class="built_in">del</span>(r--);</span><br><span class="line">    <span class="keyword">while</span>(l&gt;q[i].l)<span class="built_in">add</span>(--l); <span class="keyword">while</span>(l&lt;q[i].l)<span class="built_in">del</span>(l++);</span><br><span class="line">    ans[q[i].id]=cur;&#125;&#125;</span><br><span class="line"><span class="comment">// 8.3 带修莫队, 块长 n^&#123;2/3&#125;, 总 O(n^&#123;5/3&#125;)</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">QQ</span>&#123;<span class="type">int</span> l,r,t,id;&#125; qq[M];</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">C</span>&#123;<span class="type">int</span> p; <span class="type">long</span> <span class="type">long</span> x;&#125; c[M];                        <span class="comment">// 第 i 次修改: 位置 p 改成 x</span></span><br><span class="line"><span class="type">bool</span> <span class="keyword">operator</span>&lt;(<span class="type">const</span> QQ&amp;x,<span class="type">const</span> QQ&amp;y)&#123;</span><br><span class="line">  <span class="type">int</span> bx=x.l/unit,by=y.l/unit; <span class="keyword">if</span>(bx!=by)<span class="keyword">return</span> bx&lt;by;</span><br><span class="line">  <span class="type">int</span> rx=x.r/unit,ry=y.r/unit; <span class="keyword">if</span>(rx!=ry)<span class="keyword">return</span> (bx&amp;<span class="number">1</span>)?rx&lt;ry:rx&gt;ry;</span><br><span class="line">  <span class="keyword">return</span> (rx&amp;<span class="number">1</span>)?x.t&gt;y.t:x.t&lt;y.t;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">solve2</span><span class="params">(<span class="type">int</span> qcnt)</span></span>&#123;</span><br><span class="line">  unit=<span class="built_in">pow</span>(qcnt,<span class="number">2.0</span>/<span class="number">3.0</span>),<span class="built_in">sort</span>(qq<span class="number">+1</span>,qq+qcnt<span class="number">+1</span>);</span><br><span class="line">  <span class="type">int</span> l=<span class="number">1</span>,r=<span class="number">0</span>,t=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=qcnt;i++)&#123;</span><br><span class="line">    <span class="keyword">while</span>(r&lt;qq[i].r)<span class="built_in">add</span>(a[++r]); <span class="keyword">while</span>(r&gt;qq[i].r)<span class="built_in">del</span>(a[r--]);</span><br><span class="line">    <span class="keyword">while</span>(l&gt;qq[i].l)<span class="built_in">add</span>(a[--l]); <span class="keyword">while</span>(l&lt;qq[i].l)<span class="built_in">del</span>(a[l++]);</span><br><span class="line">    <span class="keyword">while</span>(t&lt;qq[i].t)&#123;t++; <span class="type">int</span> p=c[t].p; <span class="keyword">if</span>(l&lt;=p&amp;&amp;p&lt;=r)<span class="built_in">del</span>(a[p]),<span class="built_in">add</span>(c[t].x); <span class="built_in">swap</span>(a[p],c[t].x);&#125;</span><br><span class="line">    <span class="keyword">while</span>(t&gt;qq[i].t)&#123;<span class="type">int</span> p=c[t].p; <span class="keyword">if</span>(l&lt;=p&amp;&amp;p&lt;=r)<span class="built_in">del</span>(a[p]),<span class="built_in">add</span>(c[t].x); <span class="built_in">swap</span>(a[p],c[t].x); t--;&#125;</span><br><span class="line">    ans[qq[i].id]=cur;&#125;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 块长：普通莫队 <code>n/sqrt(m)</code>（m 为询问数），带修莫队 <code>n^&#123;2/3&#125;</code>；移动时间指针必须用 <code>swap</code> 才能双向撤销。</p></blockquote><h4 id="9-平衡树"><a href="#9-平衡树" class="headerlink" title="9. 平衡树"></a>9. 平衡树</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 9.1 FHQ Treap: 各操作期望 O(log n)</span></span><br><span class="line"><span class="type">int</span> ls[N],rs[N],sz[N],val[N],pri[N],rev[N],tot;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">newNode</span><span class="params">(<span class="type">int</span> v)</span></span>&#123;val[++tot]=v,sz[tot]=<span class="number">1</span>,pri[tot]=<span class="built_in">rand</span>(),ls[tot]=rs[tot]=rev[tot]=<span class="number">0</span>;<span class="keyword">return</span> tot;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">pushup</span><span class="params">(<span class="type">int</span> p)</span></span>&#123;sz[p]=sz[ls[p]]+sz[rs[p]]<span class="number">+1</span>;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">pushdown</span><span class="params">(<span class="type">int</span> p)</span></span>&#123;<span class="keyword">if</span>(!rev[p])<span class="keyword">return</span>; <span class="built_in">swap</span>(ls[p],rs[p]),rev[ls[p]]^=<span class="number">1</span>,rev[rs[p]]^=<span class="number">1</span>,rev[p]=<span class="number">0</span>;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">split</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> k,<span class="type">int</span>&amp;x,<span class="type">int</span>&amp;y)</span></span>&#123;              <span class="comment">// 按大小: x 取前 k 个</span></span><br><span class="line">  <span class="keyword">if</span>(!p)&#123;x=y=<span class="number">0</span>;<span class="keyword">return</span>;&#125; <span class="built_in">pushdown</span>(p);</span><br><span class="line">  <span class="keyword">if</span>(sz[ls[p]]&lt;k)x=p,<span class="built_in">split</span>(rs[p],k-sz[ls[p]]<span class="number">-1</span>,rs[p],y);</span><br><span class="line">  <span class="keyword">else</span> y=p,<span class="built_in">split</span>(ls[p],k,x,ls[p]);</span><br><span class="line">  <span class="built_in">pushup</span>(p);&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">splitv</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> v,<span class="type">int</span>&amp;x,<span class="type">int</span>&amp;y)</span></span>&#123;             <span class="comment">// 按值: x 中 val&lt;=v</span></span><br><span class="line">  <span class="keyword">if</span>(!p)&#123;x=y=<span class="number">0</span>;<span class="keyword">return</span>;&#125;</span><br><span class="line">  <span class="keyword">if</span>(val[p]&lt;=v)x=p,<span class="built_in">splitv</span>(rs[p],v,rs[p],y); <span class="keyword">else</span> y=p,<span class="built_in">splitv</span>(ls[p],v,x,ls[p]);</span><br><span class="line">  <span class="built_in">pushup</span>(p);&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">merge</span><span class="params">(<span class="type">int</span> x,<span class="type">int</span> y)</span></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(!x||!y)<span class="keyword">return</span> x|y;</span><br><span class="line">  <span class="keyword">if</span>(pri[x]&lt;pri[y])&#123;<span class="built_in">pushdown</span>(x),rs[x]=<span class="built_in">merge</span>(rs[x],y),<span class="built_in">pushup</span>(x);<span class="keyword">return</span> x;&#125;</span><br><span class="line">  <span class="built_in">pushdown</span>(y),ls[y]=<span class="built_in">merge</span>(x,ls[y]),<span class="built_in">pushup</span>(y);<span class="keyword">return</span> y;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">kth</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> k)</span></span>&#123;                             <span class="comment">// 第 k 小</span></span><br><span class="line">  <span class="keyword">while</span>(p)&#123;<span class="built_in">pushdown</span>(p);</span><br><span class="line">    <span class="keyword">if</span>(sz[ls[p]]&gt;=k)p=ls[p];</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(sz[ls[p]]<span class="number">+1</span>==k)<span class="keyword">return</span> val[p];</span><br><span class="line">    <span class="keyword">else</span> k-=sz[ls[p]]<span class="number">+1</span>,p=rs[p];&#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">-1</span>;&#125;</span><br><span class="line"><span class="comment">// 插入 v:  splitv(root,v,x,y); root=merge(merge(x,newNode(v)),y);</span></span><br><span class="line"><span class="comment">// 前驱:     splitv(root,v-1,x,y); ans=kth(x,sz[x]); root=merge(x,y);</span></span><br><span class="line"><span class="comment">// 后继:     splitv(root,v,x,y);   ans=kth(y,1);     root=merge(x,y);</span></span><br><span class="line"><span class="comment">// 区间翻转: split(root,r,x,z); split(x,l-1,x,y); rev[y]^=1; root=merge(merge(x,y),z);</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 带区间翻转时 <strong>split 与 kth 路上必须 pushdown</strong>；纯权值平衡树（splitv）不需要翻转标记。<br>Splay（简述）：核心是 rotate + 双旋 <code>splay(x,goal)</code>，把访问节点提到根，均摊 O(log n)，适合 LCT 或”反复把某点提到根”的场景。<code>rotate(x)</code> 用 <code>get(x)=ch[fa[x]][1]==x</code> 分左右旋（x 转到父亲位置，x 的 k^1 儿子过继给 y）；<code>splay</code> 中先按 <code>get(x)==get(y)</code> 判断同侧（先旋 y）还是异侧（先旋 x），双旋后再 <code>rotate(x)</code>，最后 <code>if(!goal)root=x;</code>。CSP 做区间翻转用 FHQ Treap 更省事。</p></blockquote><h4 id="10-树链剖分（重链剖分-线段树）"><a href="#10-树链剖分（重链剖分-线段树）" class="headerlink" title="10. 树链剖分（重链剖分 + 线段树）"></a>10. 树链剖分（重链剖分 + 线段树）</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 预处理 O(n), 单次路径操作 O(log^2 n), 子树操作 O(log n)</span></span><br><span class="line">vector&lt;<span class="type">int</span>&gt; g[N];</span><br><span class="line"><span class="type">int</span> fa[N],dep[N],siz[N],son[N],top[N],dfn[N],rnk[N],idx;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs1</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> f)</span></span>&#123;</span><br><span class="line">  fa[u]=f,dep[u]=dep[f]<span class="number">+1</span>,siz[u]=<span class="number">1</span>,son[u]=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:g[u])&#123; <span class="keyword">if</span>(v==f)<span class="keyword">continue</span>; <span class="built_in">dfs1</span>(v,u),siz[u]+=siz[v]; <span class="keyword">if</span>(siz[v]&gt;siz[son[u]])son[u]=v; &#125;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dfs2</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> ftop)</span></span>&#123;                        <span class="comment">// 必须先走重儿子, 保证子树是连续区间</span></span><br><span class="line">  top[u]=ftop,dfn[u]=++idx,rnk[idx]=u;</span><br><span class="line">  <span class="keyword">if</span>(son[u])<span class="built_in">dfs2</span>(son[u],ftop);</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> v:g[u])<span class="keyword">if</span>(v!=son[u]&amp;&amp;v!=fa[u])<span class="built_in">dfs2</span>(v,v);&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">pathUpd</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> v,<span class="type">long</span> <span class="type">long</span> x)</span></span>&#123;            <span class="comment">// 路径修改, 查询同理换成 qry</span></span><br><span class="line">  <span class="keyword">while</span>(top[u]!=top[v])&#123;</span><br><span class="line">    <span class="keyword">if</span>(dep[top[u]]&lt;dep[top[v]])<span class="built_in">swap</span>(u,v);</span><br><span class="line">    <span class="built_in">upd</span>(dfn[top[u]],dfn[u],x);  u=fa[top[u]];&#125;    <span class="comment">// upd 为 3.1 的线段树</span></span><br><span class="line">  <span class="keyword">if</span>(dep[u]&gt;dep[v])<span class="built_in">swap</span>(u,v); <span class="built_in">upd</span>(dfn[u],dfn[v],x);&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">lca</span><span class="params">(<span class="type">int</span> u,<span class="type">int</span> v)</span></span>&#123;</span><br><span class="line">  <span class="keyword">while</span>(top[u]!=top[v])dep[top[u]]&gt;dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];</span><br><span class="line">  <span class="keyword">return</span> dep[u]&lt;dep[v]?u:v;&#125;</span><br><span class="line"><span class="comment">// 子树 u 在 dfn 上恰为 [dfn[u], dfn[u]+siz[u]-1]</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 剖分必须<strong>先走重儿子</strong>；跳链时比较的是 <code>dep[top[u]]</code> 而不是 <code>dep[u]</code>。</p></blockquote><h3 id="B-字符串"><a href="#B-字符串" class="headerlink" title="B. 字符串"></a>B. 字符串</h3><h4 id="11-字符串哈希"><a href="#11-字符串哈希" class="headerlink" title="11. 字符串哈希"></a>11. 字符串哈希</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 单模数自然溢出(mod 2^64): 预处理 O(n), 子串哈希 O(1); 回文判定需再维护反串哈希</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span> ull;</span><br><span class="line"><span class="type">const</span> ull B=<span class="number">131</span>; ull h[N],pw[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">build</span><span class="params">(<span class="type">const</span> <span class="type">char</span>*s,<span class="type">int</span> n)</span></span>&#123;pw[<span class="number">0</span>]=<span class="number">1</span>;<span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)pw[i]=pw[i<span class="number">-1</span>]*B,h[i]=h[i<span class="number">-1</span>]*B+s[i];&#125;</span><br><span class="line"><span class="function">ull <span class="title">sub</span><span class="params">(<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;<span class="keyword">return</span> h[r]-h[l<span class="number">-1</span>]*pw[r-l<span class="number">+1</span>];&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isPal</span><span class="params">(<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;<span class="keyword">return</span> <span class="built_in">sub</span>(l,r)==<span class="built_in">rsub</span>(n-r<span class="number">+1</span>,n-l<span class="number">+1</span>);&#125;</span><br><span class="line"><span class="comment">// 双模数(更稳), 打包成一个 long long 比较</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> M1=<span class="number">1e9</span><span class="number">+7</span>,M2=<span class="number">1e9</span><span class="number">+9</span>,B1=<span class="number">131</span>,B2=<span class="number">13331</span>;</span><br><span class="line"><span class="type">int</span> h1[N],h2[N],p1[N],p2[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">build2</span><span class="params">(<span class="type">const</span> <span class="type">char</span>*s,<span class="type">int</span> n)</span></span>&#123;</span><br><span class="line">  p1[<span class="number">0</span>]=p2[<span class="number">0</span>]=<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">    p1[i]=<span class="number">1LL</span>*p1[i<span class="number">-1</span>]*B1%M1,h1[i]=(<span class="number">1LL</span>*h1[i<span class="number">-1</span>]*B1+s[i])%M1;</span><br><span class="line">    p2[i]=<span class="number">1LL</span>*p2[i<span class="number">-1</span>]*B2%M2,h2[i]=(<span class="number">1LL</span>*h2[i<span class="number">-1</span>]*B2+s[i])%M2;&#125;&#125;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">sub2</span><span class="params">(<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">  <span class="type">int</span> a=(h1[r]<span class="number">-1LL</span>*h1[l<span class="number">-1</span>]*p1[r-l<span class="number">+1</span>])%M1; <span class="keyword">if</span>(a&lt;<span class="number">0</span>)a+=M1;</span><br><span class="line">  <span class="type">int</span> b=(h2[r]<span class="number">-1LL</span>*h2[l<span class="number">-1</span>]*p2[r-l<span class="number">+1</span>])%M2; <span class="keyword">if</span>(b&lt;<span class="number">0</span>)b+=M2;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">1LL</span>*a*M2+b;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 自然溢出可被 Thue-Morse 串构造卡掉，防卡用双模数；字符不要映射成 0，否则 a 与 aa 等会撞。</p></blockquote><h4 id="12-KMP-与-Z-函数"><a href="#12-KMP-与-Z-函数" class="headerlink" title="12. KMP 与 Z 函数"></a>12. KMP 与 Z 函数</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 12.1 前缀函数(fail 数组) + 匹配 + 最小循环节, O(n)</span></span><br><span class="line"><span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">prefix_function</span><span class="params">(<span class="type">const</span> string&amp;s)</span></span>&#123;       <span class="comment">// pi[i]: s[0..i] 的最长真前后缀</span></span><br><span class="line">  <span class="type">int</span> n=s.<span class="built_in">size</span>(); <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">pi</span><span class="params">(n)</span></span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;n;i++)&#123;</span><br><span class="line">    <span class="type">int</span> j=pi[i<span class="number">-1</span>];</span><br><span class="line">    <span class="keyword">while</span>(j&gt;<span class="number">0</span>&amp;&amp;s[i]!=s[j])j=pi[j<span class="number">-1</span>];</span><br><span class="line">    <span class="keyword">if</span>(s[i]==s[j])j++;</span><br><span class="line">    pi[i]=j;&#125;</span><br><span class="line">  <span class="keyword">return</span> pi;&#125;</span><br><span class="line"><span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">kmp</span><span class="params">(<span class="type">const</span> string&amp;s,<span class="type">const</span> string&amp;t)</span></span>&#123;    <span class="comment">// 返回 t 在 s 中所有出现位置</span></span><br><span class="line">  string u=t+(<span class="type">char</span>)<span class="number">1</span>+s;                            <span class="comment">// 用不出现在串中的分隔符</span></span><br><span class="line">  vector&lt;<span class="type">int</span>&gt; pi=<span class="built_in">prefix_function</span>(u),res;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=t.<span class="built_in">size</span>()<span class="number">+1</span>;i&lt;(<span class="type">int</span>)u.<span class="built_in">size</span>();i++)</span><br><span class="line">    <span class="keyword">if</span>(pi[i]==(<span class="type">int</span>)t.<span class="built_in">size</span>())res.<span class="built_in">push_back</span>(i<span class="number">-2</span>*t.<span class="built_in">size</span>());</span><br><span class="line">  <span class="keyword">return</span> res;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">period</span><span class="params">(<span class="type">const</span> string&amp;s)</span></span>&#123;                        <span class="comment">// 最小循环节: 整除则 n-pi[n-1], 否则 n</span></span><br><span class="line">  <span class="type">int</span> n=s.<span class="built_in">size</span>(),p=n-<span class="built_in">prefix_function</span>(s)[n<span class="number">-1</span>];</span><br><span class="line">  <span class="keyword">return</span> n%p==<span class="number">0</span>?p:n;&#125;</span><br><span class="line"><span class="comment">// 12.2 扩展 KMP(Z 函数): z[i] = LCP(s, s[i:]), O(n)</span></span><br><span class="line"><span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">z_function</span><span class="params">(<span class="type">const</span> string&amp;s)</span></span>&#123;</span><br><span class="line">  <span class="type">int</span> n=s.<span class="built_in">size</span>(); <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">z</span><span class="params">(n)</span></span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>,l=<span class="number">0</span>,r=<span class="number">0</span>;i&lt;n;i++)&#123;</span><br><span class="line">    <span class="keyword">if</span>(i&lt;=r&amp;&amp;z[i-l]&lt;r-i<span class="number">+1</span>)z[i]=z[i-l];</span><br><span class="line">    <span class="keyword">else</span>&#123; z[i]=<span class="built_in">max</span>(<span class="number">0</span>,r-i<span class="number">+1</span>); <span class="keyword">while</span>(i+z[i]&lt;n&amp;&amp;s[z[i]]==s[i+z[i]])z[i]++; &#125;</span><br><span class="line">    <span class="keyword">if</span>(i+z[i]<span class="number">-1</span>&gt;r)l=i,r=i+z[i]<span class="number">-1</span>;&#125;</span><br><span class="line">  <span class="keyword">return</span> z;&#125;</span><br><span class="line"><span class="comment">// s 的每个后缀与 t 的 LCP: 求 z_function(t+(char)1+s) 的后半段</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="13-Trie-树与-01-Trie"><a href="#13-Trie-树与-01-Trie" class="headerlink" title="13. Trie 树与 01-Trie"></a>13. Trie 树与 01-Trie</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 13.1 Trie: 单次 O(|s|), 空间 O(总字符数 * 26)</span></span><br><span class="line"><span class="type">int</span> ch[N][<span class="number">26</span>],cnt[N],tot=<span class="number">1</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">insert</span><span class="params">(<span class="type">const</span> <span class="type">char</span>*s)</span></span>&#123;</span><br><span class="line">  <span class="type">int</span> p=<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;s[i];i++)&#123; <span class="type">int</span> c=s[i]-<span class="string">&#x27;a&#x27;</span>; <span class="keyword">if</span>(!ch[p][c])ch[p][c]=++tot; p=ch[p][c]; &#125;</span><br><span class="line">  cnt[p]++;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">query</span><span class="params">(<span class="type">const</span> <span class="type">char</span>*s)</span></span>&#123;                           <span class="comment">// 该串出现次数</span></span><br><span class="line">  <span class="type">int</span> p=<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;s[i];i++)&#123; <span class="type">int</span> c=s[i]-<span class="string">&#x27;a&#x27;</span>; <span class="keyword">if</span>(!ch[p][c])<span class="keyword">return</span> <span class="number">0</span>; p=ch[p][c]; &#125;</span><br><span class="line">  <span class="keyword">return</span> cnt[p];&#125;</span><br><span class="line"><span class="comment">// 13.2 01-Trie 求最大异或对(值域 [0,2^30)), 单次 O(位数)</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> BITS=<span class="number">30</span>;</span><br><span class="line"><span class="type">int</span> tr[N*(BITS<span class="number">+1</span>)][<span class="number">2</span>],tn=<span class="number">1</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">i01</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;<span class="type">int</span> p=<span class="number">1</span>;<span class="keyword">for</span>(<span class="type">int</span> i=BITS;i&gt;=<span class="number">0</span>;i--)&#123;<span class="type">int</span> c=(x&gt;&gt;i)&amp;<span class="number">1</span>;<span class="keyword">if</span>(!tr[p][c])tr[p][c]=++tn;p=tr[p][c];&#125;&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">q01</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;                                    <span class="comment">// 返回与 x 异或的最大值</span></span><br><span class="line">  <span class="type">int</span> p=<span class="number">1</span>,res=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=BITS;i&gt;=<span class="number">0</span>;i--)&#123;<span class="type">int</span> c=(x&gt;&gt;i)&amp;<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span>(tr[p][c^<span class="number">1</span>])res|=<span class="number">1</span>&lt;&lt;i,p=tr[p][c^<span class="number">1</span>]; <span class="keyword">else</span> p=tr[p][c];&#125;</span><br><span class="line">  <span class="keyword">return</span> res;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 01-Trie 位数要覆盖值域最高位（a[i] &lt;&#x3D; 1e9 用 30 位）；有负数先整体加偏移量转非负。</p></blockquote><h4 id="14-AC-自动机"><a href="#14-AC-自动机" class="headerlink" title="14. AC 自动机"></a>14. AC 自动机</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 建树 O(总长 * 26), 匹配 O(|s| + 节点数); 根节点编号为 1</span></span><br><span class="line"><span class="type">int</span> ch[N][<span class="number">26</span>],fail[N],cnt[N],vis[N],deg[N],tot=<span class="number">1</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">ins</span><span class="params">(<span class="type">const</span> <span class="type">char</span>*s)</span></span>&#123;</span><br><span class="line">  <span class="type">int</span> p=<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;s[i];i++)&#123; <span class="type">int</span> c=s[i]-<span class="string">&#x27;a&#x27;</span>; <span class="keyword">if</span>(!ch[p][c])ch[p][c]=++tot; p=ch[p][c]; &#125;</span><br><span class="line">  cnt[p]++;&#125;                                       <span class="comment">// 该模式串结尾计数</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">build</span><span class="params">()</span></span>&#123;</span><br><span class="line">  queue&lt;<span class="type">int</span>&gt; q;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;<span class="number">26</span>;i++)</span><br><span class="line">    <span class="keyword">if</span>(ch[<span class="number">1</span>][i])fail[ch[<span class="number">1</span>][i]]=<span class="number">1</span>,q.<span class="built_in">push</span>(ch[<span class="number">1</span>][i]); <span class="keyword">else</span> ch[<span class="number">1</span>][i]=<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;<span class="type">int</span> u=q.<span class="built_in">front</span>(); q.<span class="built_in">pop</span>();</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;<span class="number">26</span>;i++)</span><br><span class="line">      <span class="keyword">if</span>(ch[u][i])fail[ch[u][i]]=ch[fail[u]][i],q.<span class="built_in">push</span>(ch[u][i]);</span><br><span class="line">      <span class="keyword">else</span> ch[u][i]=ch[fail[u]][i];&#125;&#125;              <span class="comment">// 建成 Trie 图, 失配 O(1) 跳</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">query</span><span class="params">(<span class="type">const</span> <span class="type">char</span>*s)</span></span>&#123;                          <span class="comment">// 统计各模式串出现次数(拓扑优化); 多次匹配前清空 vis/deg</span></span><br><span class="line">  <span class="type">int</span> p=<span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;s[i];i++)p=ch[p][s[i]-<span class="string">&#x27;a&#x27;</span>],vis[p]++;  <span class="comment">// 只在文本路径上打标记, 与 ins 的 cnt 分开</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=tot;i++)deg[fail[i]]++;</span><br><span class="line">  queue&lt;<span class="type">int</span>&gt; q;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=tot;i++)<span class="keyword">if</span>(!deg[i])q.<span class="built_in">push</span>(i);</span><br><span class="line">  <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;<span class="type">int</span> u=q.<span class="built_in">front</span>(); q.<span class="built_in">pop</span>(); <span class="type">int</span> f=fail[u];   <span class="comment">// fail 树拓扑序自底向上</span></span><br><span class="line">    <span class="keyword">if</span>(f)&#123;vis[f]+=vis[u]; <span class="keyword">if</span>(--deg[f]==<span class="number">0</span>)q.<span class="built_in">push</span>(f);&#125;&#125;&#125;</span><br><span class="line">  <span class="comment">// 模式串(结尾节点 u)的出现次数 = vis[u], cnt[u] 是&quot;有多少个模式串以 u 结尾&quot;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 根编号必须是 1 且 <code>ch[1][i]</code> 失配指向自己，把 1 当空指针会死循环；多模式串统计必须<strong>拓扑优化</strong>（或 fail 树上 DFS），暴力跳 fail 会被卡成 O(|s| * 深度)。</p></blockquote><h4 id="15-Manacher-最长回文子串"><a href="#15-Manacher-最长回文子串" class="headerlink" title="15. Manacher 最长回文子串"></a>15. Manacher 最长回文子串</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// O(n)</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">manacher</span><span class="params">(<span class="type">const</span> string&amp;s)</span></span>&#123;</span><br><span class="line">  string a=<span class="string">&quot;^#&quot;</span>; <span class="keyword">for</span>(<span class="type">char</span> c:s)a+=c,a+=<span class="string">&#x27;#&#x27;</span>; a+=<span class="string">&#x27;$&#x27;</span>;   <span class="comment">// 两端哨兵, 省边界判断</span></span><br><span class="line">  <span class="type">int</span> n=a.<span class="built_in">size</span>(),mx=<span class="number">0</span>,id=<span class="number">0</span>,ans=<span class="number">0</span>; <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">p</span><span class="params">(n)</span></span>;</span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;n<span class="number">-1</span>;i++)&#123;</span><br><span class="line">    p[i]=(mx&gt;i)?<span class="built_in">min</span>(p[<span class="number">2</span>*id-i],mx-i):<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span>(a[i+p[i]]==a[i-p[i]])p[i]++;</span><br><span class="line">    <span class="keyword">if</span>(i+p[i]&gt;mx)mx=i+p[i],id=i;</span><br><span class="line">    ans=<span class="built_in">max</span>(ans,p[i]<span class="number">-1</span>);&#125;                           <span class="comment">// p[i]-1 即原串中的回文长度</span></span><br><span class="line">  <span class="keyword">return</span> ans;&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 哨兵字符不能出现在原串中；插入的 ‘#’ 也是哨兵，原串含 ‘#’ 时换一个不冲突的字符。</p></blockquote><h4 id="16-后缀数组-SA-与最小表示法"><a href="#16-后缀数组-SA-与最小表示法" class="headerlink" title="16. 后缀数组 SA 与最小表示法"></a>16. 后缀数组 SA 与最小表示法</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 16.1 后缀数组(倍增 + 计数排序) 与 height: O(n log n); rk/oldrk 开 2n 防 sa[i]+w 越界</span></span><br><span class="line"><span class="type">int</span> n,sa[N],rk[N&lt;&lt;<span class="number">1</span>],oldrk[N&lt;&lt;<span class="number">1</span>],id[N],cnt[N],height[N]; <span class="type">char</span> s[N];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">buildSA</span><span class="params">()</span></span>&#123;                                    <span class="comment">// s[1..n]</span></span><br><span class="line">  <span class="type">int</span> m=<span class="number">127</span>,p=<span class="number">0</span>,i,w;</span><br><span class="line">  <span class="keyword">for</span>(i=<span class="number">1</span>;i&lt;=n;i++)cnt[rk[i]=(<span class="type">unsigned</span> <span class="type">char</span>)s[i]]++;</span><br><span class="line">  <span class="keyword">for</span>(i=<span class="number">1</span>;i&lt;=m;i++)cnt[i]+=cnt[i<span class="number">-1</span>];</span><br><span class="line">  <span class="keyword">for</span>(i=n;i&gt;=<span class="number">1</span>;i--)sa[cnt[rk[i]]--]=i;</span><br><span class="line">  <span class="built_in">memcpy</span>(oldrk<span class="number">+1</span>,rk<span class="number">+1</span>,n*<span class="built_in">sizeof</span>(<span class="type">int</span>));</span><br><span class="line">  <span class="keyword">for</span>(p=<span class="number">0</span>,i=<span class="number">1</span>;i&lt;=n;i++)rk[sa[i]]=(oldrk[sa[i]]==oldrk[sa[i<span class="number">-1</span>]])?p:++p;</span><br><span class="line">  <span class="keyword">for</span>(w=<span class="number">1</span>;w&lt;n;w&lt;&lt;=<span class="number">1</span>,m=n)&#123;</span><br><span class="line">    <span class="built_in">memset</span>(cnt,<span class="number">0</span>,<span class="built_in">sizeof</span>(cnt)); <span class="built_in">memcpy</span>(id<span class="number">+1</span>,sa<span class="number">+1</span>,n*<span class="built_in">sizeof</span>(<span class="type">int</span>));</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">1</span>;i&lt;=n;i++)cnt[rk[id[i]+w]]++;</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">1</span>;i&lt;=m;i++)cnt[i]+=cnt[i<span class="number">-1</span>];</span><br><span class="line">    <span class="keyword">for</span>(i=n;i&gt;=<span class="number">1</span>;i--)sa[cnt[rk[id[i]+w]]--]=id[i];</span><br><span class="line">    <span class="built_in">memset</span>(cnt,<span class="number">0</span>,<span class="built_in">sizeof</span>(cnt)); <span class="built_in">memcpy</span>(id<span class="number">+1</span>,sa<span class="number">+1</span>,n*<span class="built_in">sizeof</span>(<span class="type">int</span>));</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">1</span>;i&lt;=n;i++)cnt[rk[id[i]]]++;</span><br><span class="line">    <span class="keyword">for</span>(i=<span class="number">1</span>;i&lt;=m;i++)cnt[i]+=cnt[i<span class="number">-1</span>];</span><br><span class="line">    <span class="keyword">for</span>(i=n;i&gt;=<span class="number">1</span>;i--)sa[cnt[rk[id[i]]]--]=id[i];</span><br><span class="line">    <span class="built_in">memcpy</span>(oldrk<span class="number">+1</span>,rk<span class="number">+1</span>,n*<span class="built_in">sizeof</span>(<span class="type">int</span>));</span><br><span class="line">    <span class="keyword">for</span>(p=<span class="number">0</span>,i=<span class="number">1</span>;i&lt;=n;i++)</span><br><span class="line">      rk[sa[i]]=(oldrk[sa[i]]==oldrk[sa[i<span class="number">-1</span>]]&amp;&amp;oldrk[sa[i]+w]==oldrk[sa[i<span class="number">-1</span>]+w])?p:++p;&#125;&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">buildHeight</span><span class="params">()</span></span>&#123;                                <span class="comment">// height[i] = LCP(sa[i], sa[i-1])</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>,k=<span class="number">0</span>;i&lt;=n;i++)&#123;</span><br><span class="line">    <span class="keyword">if</span>(rk[i]==<span class="number">1</span>)&#123;k=<span class="number">0</span>;<span class="keyword">continue</span>;&#125; <span class="keyword">if</span>(k)k--;</span><br><span class="line">    <span class="type">int</span> j=sa[rk[i]<span class="number">-1</span>];</span><br><span class="line">    <span class="keyword">while</span>(i+k&lt;=n&amp;&amp;j+k&lt;=n&amp;&amp;s[i+k]==s[j+k])k++;</span><br><span class="line">    height[rk[i]]=k;&#125;&#125;</span><br><span class="line"><span class="comment">// 16.2 最小表示法: O(n); 求最大表示把 a&gt;b 与 a&lt;b 的分支对调</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">minRep</span><span class="params">(<span class="type">const</span> string&amp;s)</span></span>&#123;</span><br><span class="line">  <span class="type">int</span> n=s.<span class="built_in">size</span>(),i=<span class="number">0</span>,j=<span class="number">1</span>,k=<span class="number">0</span>;</span><br><span class="line">  <span class="keyword">while</span>(k&lt;n&amp;&amp;i&lt;n&amp;&amp;j&lt;n)&#123;</span><br><span class="line">    <span class="type">char</span> a=s[(i+k)%n],b=s[(j+k)%n];</span><br><span class="line">    <span class="keyword">if</span>(a==b)k++;</span><br><span class="line">    <span class="keyword">else</span>&#123; <span class="keyword">if</span>(a&gt;b)i=i+k<span class="number">+1</span>; <span class="keyword">else</span> j=j+k<span class="number">+1</span>; <span class="keyword">if</span>(i==j)i++; k=<span class="number">0</span>; &#125;&#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">min</span>(i,j);&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>应用速查：两后缀 LCP &#x3D; height 在 rk 区间上的 RMQ 最小值（套 5.1 的 ST 表做到 O(1)）；本质不同子串数 &#x3D; n(n+1)&#x2F;2 - sum(height[2..n])；可重叠最长重复子串 &#x3D; max(height)，不可重叠需二分答案 + 按 height 分组；比较两子串大小先比 LCP 与长度、再比 rk。</p><h2 id="第-05-章-数学"><a href="#第-05-章-数学" class="headerlink" title="第 05 章 数学"></a>第 05 章 数学</h2><blockquote><p>除注明「独立」外，本章代码依赖下面的公共头（C++17）。<code>MOD</code> 按题目替换；<code>1e9+7</code> 与 <code>998244353</code> 的区别见 5.14。</p></blockquote><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">using</span> ll = <span class="type">long</span> <span class="type">long</span>; <span class="keyword">using</span> ull = <span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>; <span class="keyword">using</span> i128 = <span class="type">__int128_t</span>;</span><br><span class="line"><span class="type">const</span> ll MOD = <span class="number">1000000007</span>;   <span class="comment">// 1e9+7, 素数, 原根 5</span></span><br><span class="line"><span class="type">const</span> ll MOD2 = <span class="number">998244353</span>;   <span class="comment">// 119*2^23+1, 素数, 原根 3, 可 NTT</span></span><br><span class="line"><span class="type">const</span> <span class="type">double</span> EPS = <span class="number">1e-9</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="5-1-快速幂-快速乘-矩阵快速幂"><a href="#5-1-快速幂-快速乘-矩阵快速幂" class="headerlink" title="5.1 快速幂 &#x2F; 快速乘 &#x2F; 矩阵快速幂"></a>5.1 快速幂 &#x2F; 快速乘 &#x2F; 矩阵快速幂</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function">ll <span class="title">qpow</span><span class="params">(ll a, ll b, ll p = MOD)</span> </span>&#123;          <span class="comment">// O(log b), 要求 p*p &lt; 9.2e18 (即 p &lt; 3e9)</span></span><br><span class="line">  ll r = <span class="number">1</span> % p; a %= p;</span><br><span class="line">  <span class="keyword">for</span> (; b; b &gt;&gt;= <span class="number">1</span>) &#123; <span class="keyword">if</span> (b &amp; <span class="number">1</span>) r = r * a % p; a = a * a % p; &#125;</span><br><span class="line">  <span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">mul</span><span class="params">(ll a, ll b, ll m)</span> </span>&#123; <span class="keyword">return</span> (ll)((i128)a * b % m); &#125;   <span class="comment">// O(1) 快速乘, GCC/Clang 首选</span></span><br><span class="line"><span class="function">ll <span class="title">mul_ld</span><span class="params">(ll a, ll b, ll m)</span> </span>&#123;              <span class="comment">// 无 __int128 时的 O(1) 快速乘(实测精度见 5.14)</span></span><br><span class="line">  ull c = (ull)a * (ull)b - (ull)((<span class="type">long</span> <span class="type">double</span>)a / m * b + <span class="number">0.5L</span>) * (ull)m;</span><br><span class="line">  <span class="keyword">if</span> (c &lt; (ull)m) <span class="keyword">return</span> (ll)c;</span><br><span class="line">  <span class="keyword">if</span> (c &lt; (ull)m * <span class="number">2</span>) <span class="keyword">return</span> (ll)(c - m);</span><br><span class="line">  <span class="keyword">return</span> (ll)(c + m);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">qpow_big</span><span class="params">(ll a, ll b, ll p)</span> </span>&#123;            <span class="comment">// O(log b), 模数可到 1e18, 内部用快速乘</span></span><br><span class="line">  ll r = <span class="number">1</span> % p; a %= p;</span><br><span class="line">  <span class="keyword">for</span> (; b; b &gt;&gt;= <span class="number">1</span>) &#123; <span class="keyword">if</span> (b &amp; <span class="number">1</span>) r = <span class="built_in">mul</span>(r, a, p); a = <span class="built_in">mul</span>(a, a, p); &#125;</span><br><span class="line">  <span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">qmul</span><span class="params">(ll a, ll b, ll p)</span> </span>&#123;                <span class="comment">// 龟速乘 O(log b), 常数大但绝不出错</span></span><br><span class="line">  ll r = <span class="number">0</span>; a %= p; <span class="keyword">if</span> (a &lt; <span class="number">0</span>) a += p;</span><br><span class="line">  <span class="keyword">for</span> (; b; b &gt;&gt;= <span class="number">1</span>, a = (a + a) % p) <span class="keyword">if</span> (b &amp; <span class="number">1</span>) r = (r + a) % p;</span><br><span class="line">  <span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> SZ = <span class="number">2</span>;                          <span class="comment">// 矩阵阶数, 按需改</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Mat</span> &#123;</span><br><span class="line">  ll a[SZ][SZ];</span><br><span class="line">  <span class="built_in">Mat</span>() &#123; <span class="built_in">memset</span>(a, <span class="number">0</span>, <span class="keyword">sizeof</span> a); &#125;</span><br><span class="line">  <span class="function"><span class="type">static</span> Mat <span class="title">I</span><span class="params">()</span> </span>&#123; Mat r; <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; SZ; i++) r.a[i][i] = <span class="number">1</span>; <span class="keyword">return</span> r; &#125;</span><br><span class="line">  Mat <span class="keyword">operator</span>*(<span class="type">const</span> Mat &amp;b) <span class="type">const</span> &#123;      <span class="comment">// O(SZ^3), 先枚举 k 更快</span></span><br><span class="line">    Mat r;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; SZ; i++) <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>; k &lt; SZ; k++) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!a[i][k]) <span class="keyword">continue</span>; ll t = a[i][k];</span><br><span class="line">      <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; SZ; j++) r.a[i][j] = (r.a[i][j] + t * b.a[k][j]) % MOD;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> r;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function">Mat <span class="title">mpow</span><span class="params">(Mat A, ll k)</span> </span>&#123;                    <span class="comment">// O(SZ^3 log k)</span></span><br><span class="line">  Mat r = Mat::<span class="built_in">I</span>();</span><br><span class="line">  <span class="keyword">for</span> (; k; k &gt;&gt;= <span class="number">1</span>, A = A * A) <span class="keyword">if</span> (k &amp; <span class="number">1</span>) r = r * A;</span><br><span class="line">  <span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">fib</span><span class="params">(ll n)</span> </span>&#123;                             <span class="comment">// O(log n): f(0)=0, f(1)=1</span></span><br><span class="line">  <span class="keyword">if</span> (n == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  Mat T; T.a[<span class="number">0</span>][<span class="number">0</span>] = T.a[<span class="number">0</span>][<span class="number">1</span>] = T.a[<span class="number">1</span>][<span class="number">0</span>] = <span class="number">1</span>;   <span class="comment">// [[1,1],[1,0]]</span></span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">mpow</span>(T, n - <span class="number">1</span>).a[<span class="number">0</span>][<span class="number">0</span>];                  <span class="comment">// [f(n),f(n-1)] = T^(n-1) * [1,0]</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>构造套路（把线性递推写成 <code>[f(n+1), f(n)] = [f(n), f(n-1)] * T</code>）：</p><table><thead><tr><th>场景</th><th>转移矩阵 T</th><th>说明</th></tr></thead><tbody><tr><td>斐波那契 f(n)&#x3D;f(n-1)+f(n-2)</td><td>[[1,1],[1,0]]</td><td>乘 T^(n-1) 作用在 [f(1),f(0)]&#x3D;[1,0]</td></tr><tr><td>带常数项 f(n)&#x3D;a*f(n-1)+b</td><td>[[a,b],[0,1]]</td><td>状态向量 [f(n), 1]</td></tr><tr><td>同时含 f(n-1) 与 n</td><td>[[a,1,0],[0,1,1],[0,0,1]]</td><td>状态向量 [f(n), n, 1]</td></tr><tr><td>图中长度恰为 k 的路径数</td><td>邻接矩阵 A</td><td>(A^k)[i][j] &#x3D; i 到 j 恰 k 步的方案数</td></tr><tr><td>路径数不超过 k 步</td><td>A + I</td><td>加单位阵表示「原地停留一步」</td></tr></tbody></table><h3 id="5-2-gcd-exgcd-裴蜀-同余方程-逆元"><a href="#5-2-gcd-exgcd-裴蜀-同余方程-逆元" class="headerlink" title="5.2 gcd &#x2F; exgcd &#x2F; 裴蜀 &#x2F; 同余方程 &#x2F; 逆元"></a>5.2 gcd &#x2F; exgcd &#x2F; 裴蜀 &#x2F; 同余方程 &#x2F; 逆元</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function">ll <span class="title">gcd</span><span class="params">(ll a, ll b)</span> </span>&#123; <span class="keyword">return</span> b ? <span class="built_in">gcd</span>(b, a % b) : a; &#125;     <span class="comment">// O(log min(a,b))</span></span><br><span class="line"><span class="function">ll <span class="title">lcm</span><span class="params">(ll a, ll b)</span> </span>&#123; <span class="keyword">return</span> a / <span class="built_in">gcd</span>(a, b) * b; &#125;         <span class="comment">// 先除后乘防溢出</span></span><br><span class="line"><span class="comment">// 返回 d = gcd(a,b), 并解出 a*x + b*y = d（裴蜀定理: 这样的整数解总存在）</span></span><br><span class="line"><span class="function">ll <span class="title">exgcd</span><span class="params">(ll a, ll b, ll &amp;x, ll &amp;y)</span> </span>&#123;                     <span class="comment">// O(log min(a,b))</span></span><br><span class="line">  <span class="keyword">if</span> (!b) &#123; x = <span class="number">1</span>; y = <span class="number">0</span>; <span class="keyword">return</span> a; &#125;</span><br><span class="line">  ll d = <span class="built_in">exgcd</span>(b, a % b, y, x); y -= a / b * x; <span class="keyword">return</span> d;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 同余方程 a*x ≡ b (mod m): 有解 iff gcd(a,m) | b; 通解 x0 + k*(m/d)</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">cong</span><span class="params">(ll a, ll b, ll m, ll &amp;x)</span> </span>&#123;                     <span class="comment">// O(log m)</span></span><br><span class="line">  ll y, d = <span class="built_in">exgcd</span>(a, m, x, y);</span><br><span class="line">  <span class="keyword">if</span> (b % d) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">  ll t = m / d;                                          <span class="comment">// 先取模再乘, 防溢出</span></span><br><span class="line">  x = (ll)((i128)(x % t + t) % t * ((b / d) % t) % t);</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">inv_exgcd</span><span class="params">(ll a, ll m)</span> </span>&#123; ll x, y; <span class="built_in">exgcd</span>(a, m, x, y); <span class="keyword">return</span> (x % m + m) % m; &#125;  <span class="comment">// O(log m), 任意 m</span></span><br><span class="line"><span class="function">ll <span class="title">inv_fermat</span><span class="params">(ll a, ll p)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">qpow</span>(a, p - <span class="number">2</span>, p); &#125;                          <span class="comment">// O(log p), 仅 p 为素数</span></span><br><span class="line"><span class="function">vector&lt;ll&gt; <span class="title">invs</span><span class="params">(<span class="type">int</span> n, ll p)</span> </span>&#123;                           <span class="comment">// O(n) 递推 1..n 的逆元, 仅 p 素数, n &lt; p</span></span><br><span class="line">  <span class="function">vector&lt;ll&gt; <span class="title">inv</span><span class="params">(n + <span class="number">1</span>)</span></span>; inv[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i &lt;= n; i++) inv[i] = (p - p / i) % p * inv[p % i] % p;</span><br><span class="line">  <span class="keyword">return</span> inv;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 阶乘逆元: ifac[n] = inv(fac[n]), 再倒推 ifac[i-1] = ifac[i] * i</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>m</code> 为合数时不能用费马（<code>a^(m-2)</code> 是错的），只能 <code>exgcd</code>（需 <code>gcd(a,m)=1</code>）或欧拉定理 <code>a^(φ(m)-1)</code>。</p></blockquote><h3 id="5-3-素数：筛法-Miller-Rabin-Pollard-Rho"><a href="#5-3-素数：筛法-Miller-Rabin-Pollard-Rho" class="headerlink" title="5.3 素数：筛法 &#x2F; Miller-Rabin &#x2F; Pollard-Rho"></a>5.3 素数：筛法 &#x2F; Miller-Rabin &#x2F; Pollard-Rho</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">vector&lt;<span class="type">int</span>&gt; pri;                           <span class="comment">// 质数表</span></span><br><span class="line">vector&lt;<span class="type">bool</span>&gt; isp;                          <span class="comment">// 埃氏筛标记</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sieve_erat</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;                   <span class="comment">// 埃氏筛 O(n log log n)</span></span><br><span class="line">  isp.<span class="built_in">assign</span>(n + <span class="number">1</span>, <span class="literal">true</span>); isp[<span class="number">0</span>] = isp[<span class="number">1</span>] = <span class="literal">false</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; (ll)i * i &lt;= n; i++) <span class="keyword">if</span> (isp[i])</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = i * i; j &lt;= n; j += i) isp[j] = <span class="literal">false</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i &lt;= n; i++) <span class="keyword">if</span> (isp[i]) pri.<span class="built_in">push_back</span>(i);</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> MAXN = <span class="number">1e6</span> + <span class="number">5</span>;                  <span class="comment">// 线性筛 O(n): 最小质因子 / 欧拉函数 / 莫比乌斯 / 约数个数</span></span><br><span class="line"><span class="type">int</span> lp[MAXN], phi[MAXN], mu[MAXN], d[MAXN], cnt[MAXN];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">sieve</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">  phi[<span class="number">1</span>] = mu[<span class="number">1</span>] = d[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i &lt;= n; i++) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!lp[i]) &#123; lp[i] = i; pri.<span class="built_in">push_back</span>(i); phi[i] = i - <span class="number">1</span>; mu[i] = <span class="number">-1</span>; d[i] = <span class="number">2</span>; cnt[i] = <span class="number">1</span>; &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; (<span class="type">int</span>)pri.<span class="built_in">size</span>() &amp;&amp; (ll)i * pri[j] &lt;= n; j++) &#123;</span><br><span class="line">      <span class="type">int</span> p = pri[j], x = i * p; lp[x] = p;              <span class="comment">// p 就是 x 的最小质因子</span></span><br><span class="line">      <span class="keyword">if</span> (i % p == <span class="number">0</span>) &#123;                                  <span class="comment">// p | i: 分解式中 p 的指数 +1</span></span><br><span class="line">        phi[x] = phi[i] * p; mu[x] = <span class="number">0</span>; cnt[x] = cnt[i] + <span class="number">1</span>;</span><br><span class="line">        d[x] = d[i] / (cnt[i] + <span class="number">1</span>) * (cnt[i] + <span class="number">2</span>);</span><br><span class="line">        <span class="keyword">break</span>;                                           <span class="comment">// 每个合数只被最小质因子筛一次</span></span><br><span class="line">      &#125;</span><br><span class="line">      phi[x] = phi[i] * (p - <span class="number">1</span>); mu[x] = -mu[i]; cnt[x] = <span class="number">1</span>; d[x] = d[i] * <span class="number">2</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">phi_one</span><span class="params">(ll n)</span> </span>&#123;                         <span class="comment">// 单点欧拉函数 O(sqrt n)</span></span><br><span class="line">  ll ans = n;</span><br><span class="line">  <span class="keyword">for</span> (ll i = <span class="number">2</span>; i * i &lt;= n; i++) <span class="keyword">if</span> (n % i == <span class="number">0</span>) &#123;</span><br><span class="line">    ans = ans / i * (i - <span class="number">1</span>); <span class="keyword">while</span> (n % i == <span class="number">0</span>) n /= i;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> n &gt; <span class="number">1</span> ? ans / n * (n - <span class="number">1</span>) : ans;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> ll MR_BASE[<span class="number">7</span>] = &#123;<span class="number">2</span>, <span class="number">325</span>, <span class="number">9375</span>, <span class="number">28178</span>, <span class="number">450775</span>, <span class="number">9780504</span>, <span class="number">1795265022</span>&#125;;  <span class="comment">// 对 &lt; 2^64 确定正确</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isPrime</span><span class="params">(ll n)</span> </span>&#123;                       <span class="comment">// Miller-Rabin, O(log^3 n)</span></span><br><span class="line">  <span class="keyword">if</span> (n &lt; <span class="number">2</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">  <span class="keyword">for</span> (ll p : &#123;<span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">11</span>, <span class="number">13</span>, <span class="number">17</span>, <span class="number">19</span>, <span class="number">23</span>, <span class="number">29</span>, <span class="number">31</span>, <span class="number">37</span>&#125;) <span class="keyword">if</span> (n % p == <span class="number">0</span>) <span class="keyword">return</span> n == p;</span><br><span class="line">  ll d = n - <span class="number">1</span>; <span class="type">int</span> r = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">while</span> (!(d &amp; <span class="number">1</span>)) d &gt;&gt;= <span class="number">1</span>, ++r;</span><br><span class="line">  <span class="keyword">for</span> (ll a : MR_BASE) &#123;</span><br><span class="line">    <span class="keyword">if</span> (a % n == <span class="number">0</span>) <span class="keyword">continue</span>;              <span class="comment">// 基底是 n 的倍数时必须跳过, 否则 n = 73, 193 会误判</span></span><br><span class="line">    ll x = <span class="built_in">qpow_big</span>(a % n, d, n);</span><br><span class="line">    <span class="keyword">if</span> (x == <span class="number">1</span> || x == n - <span class="number">1</span>) <span class="keyword">continue</span>;</span><br><span class="line">    <span class="type">bool</span> ok = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt; r; i++) &#123; x = <span class="built_in">mul</span>(x, x, n); <span class="keyword">if</span> (x == n - <span class="number">1</span>) &#123; ok = <span class="literal">true</span>; <span class="keyword">break</span>; &#125; &#125;</span><br><span class="line">    <span class="keyword">if</span> (!ok) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">pollard_once</span><span class="params">(ll n, ll c)</span> </span>&#123;              <span class="comment">// 失败返回 0</span></span><br><span class="line">  ll s = <span class="number">0</span>, t = <span class="number">0</span>, val = <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span> (ll goal = <span class="number">1</span>; goal &lt;= (<span class="number">1LL</span> &lt;&lt; <span class="number">20</span>); goal &lt;&lt;= <span class="number">1</span>, s = t, val = <span class="number">1</span>) &#123;</span><br><span class="line">    <span class="keyword">for</span> (ll step = <span class="number">1</span>; step &lt;= goal; step++) &#123;</span><br><span class="line">      t = (<span class="built_in">mul</span>(t, t, n) + c) % n;</span><br><span class="line">      val = <span class="built_in">mul</span>(val, <span class="built_in">abs</span>(t - s), n);</span><br><span class="line">      <span class="keyword">if</span> (val == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;              <span class="comment">// 退化, 换 c 重试</span></span><br><span class="line">      <span class="keyword">if</span> (step % <span class="number">127</span> == <span class="number">0</span>) &#123; ll g = <span class="built_in">gcd</span>(val, n); <span class="keyword">if</span> (g &gt; <span class="number">1</span>) <span class="keyword">return</span> g == n ? <span class="number">0</span> : g; &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">pollard</span><span class="params">(ll n)</span> </span>&#123;                         <span class="comment">// 期望 O(n^&#123;1/4&#125;)</span></span><br><span class="line">  <span class="keyword">if</span> (n % <span class="number">2</span> == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line">  <span class="keyword">for</span> (ll c = <span class="number">1</span>;; c++) &#123; ll dd = <span class="built_in">pollard_once</span>(n, c); <span class="keyword">if</span> (dd) <span class="keyword">return</span> dd; &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">factor</span><span class="params">(ll n, vector&lt;ll&gt; &amp;v)</span> </span>&#123;         <span class="comment">// 质因数分解(不去重)</span></span><br><span class="line">  <span class="keyword">if</span> (n == <span class="number">1</span>) <span class="keyword">return</span>;</span><br><span class="line">  <span class="keyword">if</span> (<span class="built_in">isPrime</span>(n)) &#123; v.<span class="built_in">push_back</span>(n); <span class="keyword">return</span>; &#125;</span><br><span class="line">  ll dd = <span class="built_in">pollard</span>(n); <span class="built_in">factor</span>(dd, v); <span class="built_in">factor</span>(n / dd, v);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>c</code> 从 1 递增重试即可，不要用 <code>rand()</code> 取 <code>c</code>（撞上坏值会卡死）。分解结果记得排序去重。</p></blockquote><h3 id="5-4-约数与积性函数-莫比乌斯-整除分块"><a href="#5-4-约数与积性函数-莫比乌斯-整除分块" class="headerlink" title="5.4 约数与积性函数 &#x2F; 莫比乌斯 &#x2F; 整除分块"></a>5.4 约数与积性函数 &#x2F; 莫比乌斯 &#x2F; 整除分块</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function">pair&lt;ll, ll&gt; <span class="title">divs</span><span class="params">(ll n)</span> </span>&#123;                  <span class="comment">// 试除法: (约数个数, 约数和), O(sqrt n)</span></span><br><span class="line">  ll cnt = <span class="number">1</span>, sum = <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span> (ll i = <span class="number">2</span>; i * i &lt;= n; i++) <span class="keyword">if</span> (n % i == <span class="number">0</span>) &#123;</span><br><span class="line">    ll pw = <span class="number">1</span>, s = <span class="number">1</span>, k = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (n % i == <span class="number">0</span>) &#123; n /= i; pw *= i; s += pw; k++; &#125;</span><br><span class="line">    cnt *= k + <span class="number">1</span>; sum *= s;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (n &gt; <span class="number">1</span>) &#123; cnt *= <span class="number">2</span>; sum *= n + <span class="number">1</span>; &#125;</span><br><span class="line">  <span class="keyword">return</span> &#123;cnt, sum&#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 线性筛约数和: 记 sp[i]=p^a(最小质因子的最高幂), sg[i]=σ(p^a), rest[i]=σ(其余部分), σ(i)=sg*rest; i%p==0 时 sp[x]=sp[i]*p, sg[x]=sg[i]+sp[x], rest[x]=rest[i]; 否则 sp[x]=p, sg[x]=p+1, rest[x]=sig[i]</span></span><br><span class="line"><span class="comment">// 莫比乌斯反演:</span></span><br><span class="line"><span class="comment">//   1) [n==1] = Σ_&#123;d|n&#125; μ(d)</span></span><br><span class="line"><span class="comment">//   2) F(n)=Σ_&#123;d|n&#125; f(d)  &lt;=&gt;  f(n)=Σ_&#123;d|n&#125; μ(d) F(n/d)</span></span><br><span class="line"><span class="comment">//   3) Σ_&#123;i&lt;=n&#125;Σ_&#123;j&lt;=m&#125; [gcd(i,j)==1] = Σ_&#123;d&lt;=min(n,m)&#125; μ(d) * (n/d) * (m/d)</span></span><br><span class="line"><span class="comment">//   狄利克雷卷积 (f*g)(n)=Σ_&#123;d|n&#125; f(d)g(n/d); ε=μ*1, id=φ*1, d=1*1, σ=id*1</span></span><br><span class="line"><span class="function">ll <span class="title">divblock</span><span class="params">(ll n, ll m)</span> </span>&#123;                  <span class="comment">// 整除分块 Σ_&#123;i=1&#125;^&#123;min(n,m)&#125; (n/i)*(m/i), O(sqrt n)</span></span><br><span class="line">  ll ans = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> (ll l = <span class="number">1</span>, r; l &lt;= <span class="built_in">min</span>(n, m); l = r + <span class="number">1</span>) &#123;</span><br><span class="line">    r = <span class="built_in">min</span>(n / (n / l), m / (m / l));     <span class="comment">// 单变量时 r = n / (n / l)</span></span><br><span class="line">    ans += (r - l + <span class="number">1</span>) * (n / l) * (m / l);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>d[x] = d[i]/(cnt[i]+1)*(cnt[i]+2)</code> 依赖整除，必须按此顺序写。<br>⚠️ 整除分块的 <code>r = n/(n/l)</code> 在 <code>n/l == 0</code> 时除零；上界取 <code>min(n,m)</code> 可保证 <code>n/l &gt;= 1</code>。</p></blockquote><h3 id="5-5-组合数学"><a href="#5-5-组合数学" class="headerlink" title="5.5 组合数学"></a>5.5 组合数学</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Comb</span> &#123;                              <span class="comment">// O(n) 预处理, O(1) 查询; 要求 p 为素数且 n &lt; p</span></span><br><span class="line">  <span class="type">int</span> n; ll p; vector&lt;ll&gt; fac, ifac;</span><br><span class="line">  <span class="built_in">Comb</span>(<span class="type">int</span> n, ll p = MOD) : <span class="built_in">n</span>(n), <span class="built_in">p</span>(p), <span class="built_in">fac</span>(n + <span class="number">1</span>), ifac(n + <span class="number">1</span>) &#123;</span><br><span class="line">    fac[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) fac[i] = fac[i - <span class="number">1</span>] * i % p;</span><br><span class="line">    ifac[n] = <span class="built_in">qpow</span>(fac[n], p - <span class="number">2</span>, p);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = n; i &gt;= <span class="number">1</span>; i--) ifac[i - <span class="number">1</span>] = ifac[i] * i % p;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function">ll <span class="title">C</span><span class="params">(ll a, ll b)</span> <span class="type">const</span> </span>&#123; <span class="keyword">return</span> (b &lt; <span class="number">0</span> || b &gt; a || a &lt; <span class="number">0</span>) ? <span class="number">0</span> : fac[a] * ifac[b] % p * ifac[a - b] % p; &#125;</span><br><span class="line">  <span class="function">ll <span class="title">A</span><span class="params">(ll a, ll b)</span> <span class="type">const</span> </span>&#123; <span class="keyword">return</span> (b &lt; <span class="number">0</span> || b &gt; a) ? <span class="number">0</span> : fac[a] * ifac[a - b] % p; &#125;</span><br><span class="line">  <span class="function">ll <span class="title">Cat</span><span class="params">(ll k)</span> <span class="type">const</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">C</span>(<span class="number">2</span> * k, k) * <span class="built_in">qpow</span>(k + <span class="number">1</span>, p - <span class="number">2</span>, p) % p; &#125;   <span class="comment">// 卡特兰数</span></span><br><span class="line">  <span class="function">ll <span class="title">D</span><span class="params">(ll k)</span> <span class="type">const</span> </span>&#123;                                                       <span class="comment">// 错排, D(0)=1, D(1)=0</span></span><br><span class="line">    <span class="keyword">if</span> (k &lt;= <span class="number">1</span>) <span class="keyword">return</span> k == <span class="number">0</span> ? <span class="number">1</span> % p : <span class="number">0</span>;   <span class="comment">// D(1) = 0</span></span><br><span class="line">    ll a = <span class="number">0</span>, b = <span class="number">1</span>; <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">3</span>; i &lt;= k; i++) &#123; ll c = (i - <span class="number">1</span>) * ((a + b) % p) % p; a = b; b = c; &#125;</span><br><span class="line">    <span class="keyword">return</span> b;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function">ll <span class="title">C_small</span><span class="params">(ll n, ll m, ll p)</span> </span>&#123;             <span class="comment">// 0 &lt;= n, m &lt; p</span></span><br><span class="line">  <span class="keyword">if</span> (m &lt; <span class="number">0</span> || m &gt; n) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  ll a = <span class="number">1</span>, b = <span class="number">1</span>; <span class="keyword">for</span> (ll i = <span class="number">1</span>; i &lt;= m; i++) &#123; a = a * ((n - m + i) % p) % p; b = b * i % p; &#125;</span><br><span class="line">  <span class="keyword">return</span> a * <span class="built_in">qpow</span>(b, p - <span class="number">2</span>, p) % p;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">lucas</span><span class="params">(ll n, ll m, ll p)</span> </span>&#123;               <span class="comment">// Lucas: C(n,m) mod p, p 素数, n,m 可到 1e18, O(p + log_p n)</span></span><br><span class="line">  <span class="keyword">if</span> (m &lt; <span class="number">0</span> || m &gt; n) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">if</span> (m == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">1</span> % p;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">C_small</span>(n % p, m % p, p) * <span class="built_in">lucas</span>(n / p, m / p, p) % p;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 模数为合数 M: 分解 M=Πp_i^&#123;a_i&#125;, 对每个素数幂求 C(n,m) mod p^a（阶乘剥掉 p 的因子后用 exgcd 求逆）,</span></span><br><span class="line"><span class="comment">// 再用 CRT 合并 —— 即扩展 Lucas, 代码较长, 用到时按此思路现写。</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><table><thead><tr><th>计数对象</th><th>公式</th><th>备注</th></tr></thead><tbody><tr><td>组合数</td><td>C(n,m) &#x3D; n!&#x2F;(m!(n-m)!)</td><td>小 n 直接杨辉三角 O(n^2)</td></tr><tr><td>卡特兰数</td><td>Cat(n) &#x3D; C(2n,n)&#x2F;(n+1) &#x3D; C(2n,n)-C(2n,n-1)</td><td>括号序列、出栈序列、二叉树形态数</td></tr><tr><td>卡特兰递推</td><td>Cat(n) &#x3D; Σ Cat(i)Cat(n-1-i) &#x3D; Cat(n-1)*(4n-2)&#x2F;(n+1)</td><td>除法用逆元</td></tr><tr><td>错排</td><td>D(n) &#x3D; (n-1)(D(n-1)+D(n-2))</td><td>也等于 n! * Σ_{k&#x3D;0}^{n} (-1)^k &#x2F; k!</td></tr><tr><td>第二类斯特林数</td><td>S(n,k) &#x3D; S(n-1,k-1) + k*S(n-1,k)</td><td>n 个不同球放入 k 个相同非空盒</td></tr><tr><td>第一类斯特林数</td><td>c(n,k) &#x3D; c(n-1,k-1) + (n-1)*c(n-1,k)</td><td>n 个元素排成 k 个轮换</td></tr><tr><td>贝尔数</td><td>B(n) &#x3D; Σ_k S(n,k), B(n+1) &#x3D; Σ_i C(n,i)B(i)</td><td>集合划分数</td></tr><tr><td>隔板法</td><td>xi&gt;&#x3D;1: C(n-1,k-1); xi&gt;&#x3D;0: C(n+k-1,k-1); xi&gt;&#x3D;li: C(n-Σli+k-1,k-1)</td><td>先减下界</td></tr><tr><td>不相邻选取</td><td>n 个中选 k 个互不相邻: C(n-k+1,k)</td><td>插空法</td></tr></tbody></table><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 斯特林数 / 贝尔数打表 O(n^2)</span></span><br><span class="line">ll S[<span class="number">1005</span>][<span class="number">1005</span>], C1[<span class="number">1005</span>][<span class="number">1005</span>], B[<span class="number">1005</span>];</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">init_stirling</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">  S[<span class="number">0</span>][<span class="number">0</span>] = C1[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= i; j++) &#123;</span><br><span class="line">    S[i][j] = (S[i - <span class="number">1</span>][j - <span class="number">1</span>] + j * S[i - <span class="number">1</span>][j]) % MOD;            <span class="comment">// 第二类</span></span><br><span class="line">    C1[i][j] = (C1[i - <span class="number">1</span>][j - <span class="number">1</span>] + (i - <span class="number">1</span>) * C1[i - <span class="number">1</span>][j]) % MOD;   <span class="comment">// 第一类</span></span><br><span class="line">  &#125;</span><br><span class="line">  B[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= i; j++) B[i] = (B[i] + S[i][j]) % MOD;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 第二类通项: S(n,k) = (1/k!) * Σ_&#123;i=0&#125;^&#123;k&#125; (-1)^i * C(k,i) * (k-i)^n</span></span><br><span class="line"><span class="function">ll <span class="title">inclusion</span><span class="params">(vector&lt;ll&gt; a, ll n)</span> </span>&#123;         <span class="comment">// 容斥, 二进制枚举 O(2^k): 1..n 中被任一 a_i 整除的个数</span></span><br><span class="line">  ll res = <span class="number">0</span>;                              <span class="comment">// |∪A_i| = Σ_&#123;S≠∅&#125; (-1)^&#123;|S|+1&#125; |∩_&#123;i∈S&#125; A_i|</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> msk = <span class="number">1</span>; msk &lt; (<span class="number">1</span> &lt;&lt; a.<span class="built_in">size</span>()); msk++) &#123;</span><br><span class="line">    ll L = <span class="number">1</span>; <span class="type">int</span> bits = <span class="number">0</span>; <span class="type">bool</span> ok = <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; (<span class="type">int</span>)a.<span class="built_in">size</span>(); i++) <span class="keyword">if</span> (msk &gt;&gt; i &amp; <span class="number">1</span>) &#123;</span><br><span class="line">      bits++; L = L / <span class="built_in">gcd</span>(L, a[i]) * a[i];</span><br><span class="line">      <span class="keyword">if</span> (L &gt; n) &#123; ok = <span class="literal">false</span>; <span class="keyword">break</span>; &#125;    <span class="comment">// 剪枝: lcm &gt; n 时贡献为 0</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (ok) res += (bits &amp; <span class="number">1</span> ? <span class="number">1</span> : <span class="number">-1</span>) * (n / L);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 1..n 中与 m 互素的个数 = Σ_&#123;d|m&#125; μ(d) * (n/d)</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="5-6-高斯消元-矩阵求逆-行列式"><a href="#5-6-高斯消元-矩阵求逆-行列式" class="headerlink" title="5.6 高斯消元 &#x2F; 矩阵求逆 &#x2F; 行列式"></a>5.6 高斯消元 &#x2F; 矩阵求逆 &#x2F; 行列式</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">gauss</span><span class="params">(vector&lt;vector&lt;<span class="type">double</span>&gt;&gt; a, vector&lt;<span class="type">double</span>&gt; &amp;x)</span> </span>&#123;   <span class="comment">// a: n x (n+1) 增广矩阵; O(n^3)</span></span><br><span class="line">  <span class="type">int</span> n = a.<span class="built_in">size</span>(), m = n; <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">where</span><span class="params">(m, <span class="number">-1</span>)</span></span>; <span class="type">int</span> row = <span class="number">0</span>;                                 <span class="comment">// 返回 0 无解 / 1 唯一解 / 2 无穷多解</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> col = <span class="number">0</span>; col &lt; m &amp;&amp; row &lt; n; col++) &#123;</span><br><span class="line">    <span class="type">int</span> sel = row;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = row; i &lt; n; i++) <span class="keyword">if</span> (<span class="built_in">fabs</span>(a[i][col]) &gt; <span class="built_in">fabs</span>(a[sel][col])) sel = i;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">fabs</span>(a[sel][col]) &lt; EPS) <span class="keyword">continue</span>;                 <span class="comment">// 该列全 0, 出现自由元</span></span><br><span class="line">    <span class="built_in">swap</span>(a[sel], a[row]);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) <span class="keyword">if</span> (i != row) &#123;</span><br><span class="line">      <span class="type">double</span> t = a[i][col] / a[row][col];</span><br><span class="line">      <span class="keyword">if</span> (<span class="built_in">fabs</span>(t) &lt; EPS) <span class="keyword">continue</span>;</span><br><span class="line">      <span class="keyword">for</span> (<span class="type">int</span> j = col; j &lt;= m; j++) a[i][j] -= t * a[row][j];</span><br><span class="line">    &#125;</span><br><span class="line">    where[col] = row++;</span><br><span class="line">  &#125;</span><br><span class="line">  x.<span class="built_in">assign</span>(m, <span class="number">0</span>);</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; m; i++) <span class="keyword">if</span> (where[i] != <span class="number">-1</span>) x[i] = a[where[i]][m] / a[where[i]][i];</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;                            <span class="comment">// 回代校验, 抓 0 = 非零</span></span><br><span class="line">    <span class="type">double</span> s = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; m; j++) s += a[i][j] * x[j];</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">fabs</span>(s - a[i][m]) &gt; EPS) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; m; i++) <span class="keyword">if</span> (where[i] == <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line">bitset&lt;1005&gt; mat[<span class="number">2005</span>];                    <span class="comment">// 异或消元: mat[1..m] 为增广矩阵, 第 0 位存常数项</span></span><br><span class="line"><span class="function">vector&lt;<span class="type">bool</span>&gt; <span class="title">gauss_xor</span><span class="params">(<span class="type">int</span> n, <span class="type">int</span> m)</span> </span>&#123;     <span class="comment">// n 个未知数 m 个方程; 无解/多解返回空; O(n*m/64)</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) &#123;</span><br><span class="line">    <span class="type">int</span> cur = i;</span><br><span class="line">    <span class="keyword">while</span> (cur &lt;= m &amp;&amp; !mat[cur].<span class="built_in">test</span>(i)) cur++;</span><br><span class="line">    <span class="keyword">if</span> (cur &gt; m) <span class="keyword">return</span> <span class="built_in">vector</span>&lt;<span class="type">bool</span>&gt;(<span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span> (cur != i) <span class="built_in">swap</span>(mat[cur], mat[i]);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= m; j++) <span class="keyword">if</span> (i != j &amp;&amp; mat[j].<span class="built_in">test</span>(i)) mat[j] ^= mat[i];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function">vector&lt;<span class="type">bool</span>&gt; <span class="title">ans</span><span class="params">(n + <span class="number">1</span>)</span></span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) ans[i] = mat[i].<span class="built_in">test</span>(<span class="number">0</span>);</span><br><span class="line">  <span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">bool</span> <span class="title">matInv</span><span class="params">(vector&lt;vector&lt;ll&gt;&gt; a, vector&lt;vector&lt;ll&gt;&gt; &amp;inv, ll p)</span> </span>&#123;   <span class="comment">// 模素数 p 求逆, O(n^3 log p)</span></span><br><span class="line">  <span class="type">int</span> n = a.<span class="built_in">size</span>();</span><br><span class="line">  inv.<span class="built_in">assign</span>(n, <span class="built_in">vector</span>&lt;ll&gt;(n, <span class="number">0</span>));</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) inv[i][i] = <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">    <span class="type">int</span> piv = <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = i; j &lt; n; j++) <span class="keyword">if</span> (a[j][i]) &#123; piv = j; <span class="keyword">break</span>; &#125;</span><br><span class="line">    <span class="keyword">if</span> (piv == <span class="number">-1</span>) <span class="keyword">return</span> <span class="literal">false</span>;                             <span class="comment">// 奇异矩阵</span></span><br><span class="line">    <span class="built_in">swap</span>(a[i], a[piv]); <span class="built_in">swap</span>(inv[i], inv[piv]);</span><br><span class="line">    ll t = <span class="built_in">qpow</span>(a[i][i], p - <span class="number">2</span>, p);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; n; j++) &#123; a[i][j] = a[i][j] * t % p; inv[i][j] = inv[i][j] * t % p; &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; n; j++) <span class="keyword">if</span> (j != i &amp;&amp; a[j][i]) &#123;</span><br><span class="line">      ll f = a[j][i];</span><br><span class="line">      <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>; k &lt; n; k++) &#123;</span><br><span class="line">        a[j][k] = (a[j][k] - f * a[i][k]) % p;</span><br><span class="line">        inv[j][k] = (inv[j][k] - f * inv[i][k]) % p;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;r : inv) <span class="keyword">for</span> (<span class="keyword">auto</span> &amp;v : r) v = (v % p + p) % p;</span><br><span class="line">  <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">det</span><span class="params">(vector&lt;vector&lt;ll&gt;&gt; a, ll p)</span> </span>&#123;       <span class="comment">// 行列式 mod 素数 p, O(n^3 log p)</span></span><br><span class="line">  <span class="type">int</span> n = a.<span class="built_in">size</span>(); ll res = <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">    <span class="type">int</span> piv = <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = i; j &lt; n; j++) <span class="keyword">if</span> (a[j][i] % p) &#123; piv = j; <span class="keyword">break</span>; &#125;</span><br><span class="line">    <span class="keyword">if</span> (piv == <span class="number">-1</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> (piv != i) &#123; <span class="built_in">swap</span>(a[piv], a[i]); res = (p - res) % p; &#125;</span><br><span class="line">    res = res * ((a[i][i] % p + p) % p) % p;</span><br><span class="line">    ll t = <span class="built_in">qpow</span>(a[i][i], p - <span class="number">2</span>, p);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = i + <span class="number">1</span>; j &lt; n; j++) &#123;</span><br><span class="line">      ll f = a[j][i] * t % p;</span><br><span class="line">      <span class="keyword">if</span> (!f) <span class="keyword">continue</span>;</span><br><span class="line">      <span class="keyword">for</span> (<span class="type">int</span> k = i; k &lt; n; k++) a[j][k] = (a[j][k] - f * a[i][k]) % p;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> (res % p + p) % p;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 实数消元必须选主元（绝对值最大的行）；判零用 <code>fabs(x) &lt; EPS</code>，不要写 <code>== 0</code>。<br>⚠️ 模意义下行列式不能直接除，先判 <code>a[i][i] % p == 0</code> 再换行；换一次行 <code>res</code> 变一次号。</p></blockquote><h3 id="5-7-中国剩余定理-CRT-扩展-CRT"><a href="#5-7-中国剩余定理-CRT-扩展-CRT" class="headerlink" title="5.7 中国剩余定理 CRT &#x2F; 扩展 CRT"></a>5.7 中国剩余定理 CRT &#x2F; 扩展 CRT</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function">ll <span class="title">CRT</span><span class="params">(<span class="type">int</span> k, ll *a, ll *r)</span> </span>&#123;              <span class="comment">// x ≡ a[i] (mod r[i]), r[i] 两两互素; O(n log)</span></span><br><span class="line">  ll n = <span class="number">1</span>, ans = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= k; i++) n *= r[i];</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= k; i++) &#123;</span><br><span class="line">    ll m = n / r[i], b, y;</span><br><span class="line">    <span class="built_in">exgcd</span>(m, r[i], b, y);                  <span class="comment">// b * m ≡ 1 (mod r[i])</span></span><br><span class="line">    ans = (ans + (i128)a[i] * m % n * b) % n;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> (ans % n + n) % n;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">excrt</span><span class="params">(<span class="type">int</span> n, ll *a, ll *m)</span> </span>&#123;            <span class="comment">// 扩展 CRT: 模数不必互素; 无解返回 -1; O(n log)</span></span><br><span class="line">  ll x = a[<span class="number">1</span>], M = m[<span class="number">1</span>];                   <span class="comment">// 维护 x ≡ a[i] (mod m[i]) 的通解 x + k*M</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i &lt;= n; i++) &#123;</span><br><span class="line">    ll A = M, B = m[i], C = ((a[i] - x) % B + B) % B, p, q;</span><br><span class="line">    ll g = <span class="built_in">exgcd</span>(A, B, p, q);              <span class="comment">// A*p + B*q = g</span></span><br><span class="line">    <span class="keyword">if</span> (C % g) <span class="keyword">return</span> <span class="number">-1</span>;                  <span class="comment">// 无解</span></span><br><span class="line">    ll t = B / g;</span><br><span class="line">    ll k = (ll)((i128)(p % t + t) % t * ((C / g) % t) % t);</span><br><span class="line">    x = (ll)(((i128)x + (i128)k * M) % ((i128)M / g * B));</span><br><span class="line">    M = M / g * B;                         <span class="comment">// lcm(M, m[i])</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> (x % M + M) % M;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>M = M / g * B</code> 必须先除后乘，直接 <code>M * B</code> 溢出；<code>k</code> 要归一到 <code>[0, B/g)</code>，否则 <code>k*M</code> 为负导致取模出错。</p></blockquote><h3 id="5-8-BSGS-离散对数-原根"><a href="#5-8-BSGS-离散对数-原根" class="headerlink" title="5.8 BSGS 离散对数 &#x2F; 原根"></a>5.8 BSGS 离散对数 &#x2F; 原根</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function">ll <span class="title">bsgs</span><span class="params">(ll a, ll b, ll p)</span> </span>&#123;                <span class="comment">// 最小非负 x 使 a^x ≡ b (mod p), 要求 gcd(a,p)=1; O(sqrt p)</span></span><br><span class="line">  a %= p; b %= p;</span><br><span class="line">  <span class="keyword">if</span> (b == <span class="number">1</span> % p) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  ll m = (ll)<span class="built_in">ceil</span>(<span class="built_in">sqrt</span>((<span class="type">long</span> <span class="type">double</span>)p));</span><br><span class="line">  unordered_map&lt;ll, ll&gt; mp; ll e = <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">for</span> (ll j = <span class="number">0</span>; j &lt; m; j++) &#123; <span class="keyword">if</span> (!mp.<span class="built_in">count</span>(e)) mp[e] = j; e = e * a % p; &#125;  <span class="comment">// 只存最小 j</span></span><br><span class="line">  ll am = <span class="built_in">qpow</span>(a, m, p), iam, tmp;</span><br><span class="line">  <span class="built_in">exgcd</span>(am, p, iam, tmp); iam = (iam % p + p) % p;       <span class="comment">// a^(-m)</span></span><br><span class="line">  ll cur = b;</span><br><span class="line">  <span class="keyword">for</span> (ll i = <span class="number">0</span>; i &lt; m; i++) &#123;</span><br><span class="line">    <span class="keyword">auto</span> it = mp.<span class="built_in">find</span>(cur);</span><br><span class="line">    <span class="keyword">if</span> (it != mp.<span class="built_in">end</span>()) <span class="keyword">return</span> i * m + it-&gt;second;</span><br><span class="line">    cur = (i128)cur * iam % p;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">exbsgs</span><span class="params">(ll a, ll b, ll p)</span> </span>&#123;              <span class="comment">// 扩展 BSGS: 不要求互素, 无解返回 -1; O(sqrt p)</span></span><br><span class="line">  a %= p; b %= p;</span><br><span class="line">  <span class="keyword">if</span> (b == <span class="number">1</span> % p) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">if</span> (p == <span class="number">1</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  ll k = <span class="number">1</span>, add = <span class="number">0</span>, g;</span><br><span class="line">  <span class="keyword">while</span> ((g = <span class="built_in">gcd</span>(a, p)) &gt; <span class="number">1</span>) &#123;            <span class="comment">// 约简到 gcd(a,p)=1: 等价于 k * a^(x-add) ≡ b</span></span><br><span class="line">    <span class="keyword">if</span> (b % g) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    b /= g; p /= g; ++add; k = (i128)k * (a / g) % p;</span><br><span class="line">    <span class="keyword">if</span> (k == b) <span class="keyword">return</span> add;                <span class="comment">// x = add 就是一个解</span></span><br><span class="line">  &#125;</span><br><span class="line">  ll ik, tmp; <span class="built_in">exgcd</span>(k, p, ik, tmp); ik = (ik % p + p) % p;</span><br><span class="line">  ll y = <span class="built_in">bsgs</span>(a, (i128)b * ik % p, p);     <span class="comment">// 此时 gcd(a,p)=1, 复用 BSGS</span></span><br><span class="line">  <span class="keyword">return</span> y &lt; <span class="number">0</span> ? <span class="number">-1</span> : y + add;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">primitive_root</span><span class="params">(ll p)</span> </span>&#123;                  <span class="comment">// 最小原根(素数模): 对所有 q|(p-1) 有 g^((p-1)/q) != 1</span></span><br><span class="line">  <span class="keyword">if</span> (p == <span class="number">2</span>) <span class="keyword">return</span> <span class="number">1</span>;                    <span class="comment">// O(sqrt p + log^2 p)</span></span><br><span class="line">  ll phi = p - <span class="number">1</span>, x = phi; vector&lt;ll&gt; fac;</span><br><span class="line">  <span class="keyword">for</span> (ll i = <span class="number">2</span>; i * i &lt;= x; i++) <span class="keyword">if</span> (x % i == <span class="number">0</span>) &#123; fac.<span class="built_in">push_back</span>(i); <span class="keyword">while</span> (x % i == <span class="number">0</span>) x /= i; &#125;</span><br><span class="line">  <span class="keyword">if</span> (x &gt; <span class="number">1</span>) fac.<span class="built_in">push_back</span>(x);</span><br><span class="line">  <span class="keyword">for</span> (ll g = <span class="number">2</span>; g &lt; p; g++) &#123;</span><br><span class="line">    <span class="type">bool</span> ok = <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">for</span> (ll q : fac) <span class="keyword">if</span> (<span class="built_in">qpow</span>(g, phi / q, p) == <span class="number">1</span>) &#123; ok = <span class="literal">false</span>; <span class="keyword">break</span>; &#125;</span><br><span class="line">    <span class="keyword">if</span> (ok) <span class="keyword">return</span> g;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="5-9-博弈论"><a href="#5-9-博弈论" class="headerlink" title="5.9 博弈论"></a>5.9 博弈论</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">nim</span><span class="params">(vector&lt;ll&gt; &amp;a)</span> </span>&#123; ll s = <span class="number">0</span>; <span class="keyword">for</span> (ll x : a) s ^= x; <span class="keyword">return</span> s != <span class="number">0</span>; &#125;  <span class="comment">// Nim: 异或和非 0 先手必胜</span></span><br><span class="line"><span class="type">int</span> sg[<span class="number">1005</span>];                              <span class="comment">// SG 定理: sg(u)=mex&#123;sg(v): u-&gt;v&#125;, sg(初始)!=0 先手必胜</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">get_sg</span><span class="params">(<span class="type">int</span> x)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (sg[x] != <span class="number">-1</span>) <span class="keyword">return</span> sg[x];</span><br><span class="line">  <span class="type">bool</span> vis[<span class="number">64</span>] = &#123;<span class="literal">false</span>&#125;;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> y : <span class="built_in">moves</span>(x)) vis[<span class="built_in">get_sg</span>(y)] = <span class="literal">true</span>;   <span class="comment">// moves(x): x 的所有后继状态</span></span><br><span class="line">  <span class="type">int</span> g = <span class="number">0</span>; <span class="keyword">while</span> (vis[g]) g++;</span><br><span class="line">  <span class="keyword">return</span> sg[x] = g;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// SG 打表技巧: 先记忆化暴力算小范围 sg 并打印, 观察周期(通常很小), 再按周期 O(1) 求</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">anti_nim</span><span class="params">(vector&lt;ll&gt; &amp;a)</span> </span>&#123;             <span class="comment">// anti-Nim(取走最后一颗者输): 先手必胜 iff</span></span><br><span class="line">  ll s = <span class="number">0</span>; <span class="type">bool</span> big = <span class="literal">false</span>; <span class="type">int</span> one = <span class="number">0</span>;<span class="comment">//   (全是 1 且 1 的堆数为偶数) 或 (有堆 &gt; 1 且异或和 != 0)</span></span><br><span class="line">  <span class="keyword">for</span> (ll x : a) &#123; s ^= x; <span class="keyword">if</span> (x &gt; <span class="number">1</span>) big = <span class="literal">true</span>; <span class="keyword">else</span> <span class="keyword">if</span> (x == <span class="number">1</span>) one++; &#125;</span><br><span class="line">  <span class="keyword">return</span> big ? s != <span class="number">0</span> : one % <span class="number">2</span> == <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">stair_nim</span><span class="params">(vector&lt;ll&gt; &amp;a)</span> </span>&#123;            <span class="comment">// 阶梯 Nim: 第 i 级石子可移到第 i-1 级(第 1 级出局)</span></span><br><span class="line">  ll s = <span class="number">0</span>;                                <span class="comment">// 先手必胜 iff 奇数级台阶石子数异或和 != 0</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt; (<span class="type">int</span>)a.<span class="built_in">size</span>(); i += <span class="number">2</span>) s ^= a[i];</span><br><span class="line">  <span class="keyword">return</span> s != <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ SG 值只能异或，不要做加减；anti-Nim 的两种情况必须写全（全 1 看奇偶，否则看异或和）。</p></blockquote><h3 id="5-10-线性基"><a href="#5-10-线性基" class="headerlink" title="5.10 线性基"></a>5.10 线性基</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">LinearBasis</span> &#123;                       <span class="comment">// 异或空间, 值域 &lt; 2^62</span></span><br><span class="line">  <span class="type">static</span> <span class="type">const</span> <span class="type">int</span> B = <span class="number">62</span>;</span><br><span class="line">  ll d[B + <span class="number">1</span>], p[B + <span class="number">1</span>]; <span class="type">int</span> cnt; <span class="type">bool</span> zero;</span><br><span class="line">  <span class="built_in">LinearBasis</span>() &#123; <span class="built_in">memset</span>(d, <span class="number">0</span>, <span class="keyword">sizeof</span> d); cnt = <span class="number">0</span>; zero = <span class="literal">false</span>; &#125;</span><br><span class="line">  <span class="function"><span class="type">bool</span> <span class="title">insert</span><span class="params">(ll x)</span> </span>&#123;                      <span class="comment">// O(B), 返回是否使秩增加</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = B; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!(x &gt;&gt; i &amp; <span class="number">1</span>)) <span class="keyword">continue</span>;</span><br><span class="line">      <span class="keyword">if</span> (!d[i]) &#123; d[i] = x; <span class="keyword">return</span> <span class="literal">true</span>; &#125;</span><br><span class="line">      x ^= d[i];</span><br><span class="line">    &#125;</span><br><span class="line">    zero = <span class="literal">true</span>; <span class="keyword">return</span> <span class="literal">false</span>;             <span class="comment">// 线性相关, 说明能异或出 0</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="type">bool</span> <span class="title">query</span><span class="params">(ll x)</span> </span>&#123;                       <span class="comment">// 能否被线性表出, O(B)</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = B; i &gt;= <span class="number">0</span>; i--) <span class="keyword">if</span> (x &gt;&gt; i &amp; <span class="number">1</span>) &#123; <span class="keyword">if</span> (!d[i]) <span class="keyword">return</span> <span class="literal">false</span>; x ^= d[i]; &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function">ll <span class="title">qmax</span><span class="params">()</span> </span>&#123; ll r = <span class="number">0</span>; <span class="keyword">for</span> (<span class="type">int</span> i = B; i &gt;= <span class="number">0</span>; i--) r = <span class="built_in">max</span>(r, r ^ d[i]); <span class="keyword">return</span> r; &#125;   <span class="comment">// O(B)</span></span><br><span class="line">  <span class="function">ll <span class="title">qmin</span><span class="params">()</span> </span>&#123; <span class="keyword">if</span> (zero) <span class="keyword">return</span> <span class="number">0</span>; <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt;= B; i++) <span class="keyword">if</span> (d[i]) <span class="keyword">return</span> d[i]; <span class="keyword">return</span> <span class="number">0</span>; &#125;  <span class="comment">// O(B)</span></span><br><span class="line">  <span class="function"><span class="type">void</span> <span class="title">rebuild</span><span class="params">()</span> </span>&#123;                         <span class="comment">// 化为简化阶梯形, O(B^2)</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = B; i &gt;= <span class="number">0</span>; i--) <span class="keyword">for</span> (<span class="type">int</span> j = i - <span class="number">1</span>; j &gt;= <span class="number">0</span>; j--)</span><br><span class="line">      <span class="keyword">if</span> (d[i] &gt;&gt; j &amp; <span class="number">1</span>) d[i] ^= d[j];</span><br><span class="line">    cnt = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt;= B; i++) <span class="keyword">if</span> (d[i]) p[cnt++] = d[i];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function">ll <span class="title">kth</span><span class="params">(ll k)</span> </span>&#123;                           <span class="comment">// 第 k 小(从 1 开始), 必须先 rebuild; 越界返回 -1</span></span><br><span class="line">    <span class="keyword">if</span> (zero) &#123; <span class="keyword">if</span> (k == <span class="number">1</span>) <span class="keyword">return</span> <span class="number">0</span>; k--; &#125;</span><br><span class="line">    <span class="keyword">if</span> (k &lt; <span class="number">1</span> || k &gt;= (<span class="number">1LL</span> &lt;&lt; cnt)) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    ll r = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; cnt; i++) <span class="keyword">if</span> (k &gt;&gt; i &amp; <span class="number">1</span>) r ^= p[i];</span><br><span class="line">    <span class="keyword">return</span> r;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>kth</code> 必须在 <code>rebuild</code> 之后调用；<code>B</code> 改 63（<code>unsigned long long</code> 值域）时 <code>1LL &lt;&lt; cnt</code> 要同步改 <code>1ULL</code>。</p></blockquote><h3 id="5-11-概率与期望"><a href="#5-11-概率与期望" class="headerlink" title="5.11 概率与期望"></a>5.11 概率与期望</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 1) 期望线性性: E[X+Y] = E[X] + E[Y], 不要求独立 —— 把总次数的期望拆成每个元素的贡献之和</span></span><br><span class="line"><span class="comment">// 2) 全概率 P(A)=Σ_i P(B_i)*P(A|B_i);  3) 条件期望 E[X]=Σ_y P(Y=y)*E[X|Y=y]</span></span><br><span class="line"><span class="comment">// 套路: 设 E[i] = 从状态 i 到终点的期望步数, 倒推 E[i] = 1 + Σ_j P(i-&gt;j) * E[j], E[终点] = 0</span></span><br><span class="line"><span class="comment">// 有自环时不满足后效性, 必须移项: E[i] = p*E[i] + (1-p)*E[j] + 1  =&gt;  E[i] = E[j] + 1/(1-p)</span></span><br><span class="line"><span class="comment">// 多个状态互相依赖(成环)时列方程组, 用高斯消元解</span></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">coupon</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;                     <span class="comment">// 集齐 n 种券的期望次数 = n * H(n), O(n)</span></span><br><span class="line">  <span class="type">double</span> e = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) e += <span class="number">1.0</span> * n / i;</span><br><span class="line">  <span class="keyword">return</span> e;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 转移式右边出现 <code>E[i]</code> 时必须先移项除以 <code>(1-p)</code>；<code>p == 1</code> 表示永远走不出去。注意 <code>n = 0</code> 等边界。</p></blockquote><h3 id="5-12-三分法-牛顿迭代"><a href="#5-12-三分法-牛顿迭代" class="headerlink" title="5.12 三分法 &#x2F; 牛顿迭代"></a>5.12 三分法 &#x2F; 牛顿迭代</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">trisearch</span><span class="params">(<span class="type">double</span> l, <span class="type">double</span> r)</span> </span>&#123;     <span class="comment">// 实数三分求单峰极值, 迭代 100 次足够</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> it = <span class="number">0</span>; it &lt; <span class="number">100</span>; it++) &#123;</span><br><span class="line">    <span class="type">double</span> m1 = l + (r - l) / <span class="number">3</span>, m2 = r - (r - l) / <span class="number">3</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">f</span>(m1) &lt; <span class="built_in">f</span>(m2)) r = m2; <span class="keyword">else</span> l = m1;   <span class="comment">// 求极大值时把判断反过来</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> l;                                <span class="comment">// 返回极值点; 需要极值就 return f(l)</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">tri_int</span><span class="params">(ll l, ll r)</span> </span>&#123;                   <span class="comment">// 整数三分求凸函数极小, O(log n)</span></span><br><span class="line">  <span class="keyword">while</span> (r - l &gt; <span class="number">2</span>) &#123;</span><br><span class="line">    ll m1 = l + (r - l) / <span class="number">3</span>, m2 = r - (r - l) / <span class="number">3</span>;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">f</span>(m1) &lt; <span class="built_in">f</span>(m2)) r = m2; <span class="keyword">else</span> l = m1;</span><br><span class="line">  &#125;</span><br><span class="line">  ll best = LLONG_MAX;</span><br><span class="line">  <span class="keyword">for</span> (ll i = l; i &lt;= r; i++) best = <span class="built_in">min</span>(best, <span class="built_in">f</span>(i));   <span class="comment">// 小区间暴力收尾</span></span><br><span class="line">  <span class="keyword">return</span> best;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">newton_sqrt</span><span class="params">(<span class="type">double</span> c)</span> </span>&#123;             <span class="comment">// 牛顿迭代 x=(x+c/x)/2, 二阶收敛</span></span><br><span class="line">  <span class="type">double</span> x = c; <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) x = (x + c / x) / <span class="number">2</span>; <span class="keyword">return</span> x;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 解一般方程 f(x)=0: x &lt;- x - f(x)/f&#x27;(x); 如 f=x^3-a, f&#x27;=3x^2</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 整数三分只对<strong>严格</strong>单峰函数成立，出现相等平台会卡住（改用二分差分符号）；实数三分写 100~200 次迭代最省心。</p></blockquote><h3 id="5-13-高精度分数-有理数比较"><a href="#5-13-高精度分数-有理数比较" class="headerlink" title="5.13 高精度分数 &#x2F; 有理数比较"></a>5.13 高精度分数 &#x2F; 有理数比较</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">cmp_frac</span><span class="params">(ll a, ll b, ll c, ll d)</span> </span>&#123;     <span class="comment">// 交叉相乘比较 a/b 与 c/d (b, d &gt; 0); O(1)</span></span><br><span class="line">  i128 x = (i128)a * d, y = (i128)c * b;</span><br><span class="line">  <span class="keyword">return</span> x &lt; y ? <span class="number">-1</span> : (x &gt; y ? <span class="number">1</span> : <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 相乘前先 <code>gcd</code> 约分可显著降低溢出概率；分母为负时先把符号提到分子。<code>__int128</code> 上限约 1.7e38，两个 1e18 相乘刚好够，再大必须手写高精度。</p></blockquote><h3 id="5-14-常见坑"><a href="#5-14-常见坑" class="headerlink" title="5.14 常见坑"></a>5.14 常见坑</h3><blockquote><p>⚠️ <strong>负数取模</strong>：C++ 的 <code>%</code> 向零取整，<code>-7 % 3 == -1</code>；要数学结果一律写 <code>(a % p + p) % p</code>。<br>⚠️ <strong>整除向下取整</strong>：<code>-7 / 2 == -3</code> 而不是 <code>-4</code>；需要 floor 写 <code>(a - ((a % b) + b) % b) / b</code>。<br>⚠️ <strong>模数为合数</strong>：费马小定理失效，逆元用 <code>exgcd</code>（需互素）或欧拉定理 <code>a^(φ(m)-1)</code>；组合数走扩展 Lucas + CRT。<br>⚠️ <strong>1e9+7 与 998244353</strong>：都是素数，逆元都能用费马。<code>998244353 = 119*2^23+1</code>，原根 3，支持长度 <code>2^23</code> 的 NTT；<code>1e9+7</code> 原根 5，但 <code>p-1 = 2*500000003</code> 只含一个因子 2，<strong>不能 NTT</strong>。<br>⚠️ <strong>pow 与快速幂混用</strong>：<code>std::pow</code> 返回 <code>double</code>（<code>pow(2,60)</code> 丢精度），也没有三参数版本；取模幂一律自己写 <code>qpow</code>。<br>⚠️ <strong>快速幂初值</strong>：写 <code>ll r = 1 % p</code> 而非 <code>1</code>（<code>p == 1</code> 时才对）；<code>lucas</code> 里同理 <code>return 1 % p</code>。<br>⚠️ <strong>乘法溢出</strong>：<code>a * b % p</code> 中两数都可能到 1e18 时必须转 <code>__int128</code> 或用快速乘；<code>int</code> 混用时写 <code>1LL * a * b</code>。<br>⚠️ <strong>long double 快速乘不靠谱</strong>：<code>mul_ld</code> 这类写法实测（x86-64 g++ -O2，20 万次随机）在 <code>m &lt;= 1e12</code> 时 0 错（20 万次随机断言通过），<code>m ~ 1e15</code> 时 0.001% 错，<code>m ~ 1e18</code> 时约 1.7%（两次实测 1.71%&#x2F;1.75%），<code>m ~ 4e18</code> 时约 6.6%，<code>m ~ 2^62</code> 时约 7.9%；模数超过 1e12 就别用它，改用 <code>__int128</code> 或龟速乘。<br>⚠️ <strong>欧拉降幂</strong>：<code>b &gt;= φ(m)</code> 时 <code>a^b ≡ a^(b mod φ(m) + φ(m)) (mod m)</code>，不要求互素；互素时直接 <code>a^(b mod φ(m))</code>。<br>⚠️ <strong>筛法边界</strong>：<code>μ(1)=1, φ(1)=1, d(1)=1</code>，线性筛主循环从 <code>i = 2</code> 起，别忘了单独初始化 1。<br>⚠️ <strong>组合数上界</strong>：预处理阶乘要求 <code>n &lt; p</code>；<code>n &gt;= p</code> 时 <code>fac[n] ≡ 0</code> 且逆元不存在，必须改 Lucas。</p><p>⚠️ <strong>内存</strong>：<code>1e7</code> 个 <code>int</code> 就是 40MB，多开几个数组就会超（CSP 常见 256MB）。</p></blockquote><h2 id="第-06-章-计算几何与杂项技巧"><a href="#第-06-章-计算几何与杂项技巧" class="headerlink" title="第 06 章 计算几何与杂项技巧"></a>第 06 章 计算几何与杂项技巧</h2><blockquote><p>CCF-CSP 上机 4 小时 5 题。几何题优先「整数坐标 + 叉积」避开浮点。</p></blockquote><h3 id="A-计算几何"><a href="#A-计算几何" class="headerlink" title="A. 计算几何"></a>A. 计算几何</h3><h4 id="1-点-向量-直线：基础结构"><a href="#1-点-向量-直线：基础结构" class="headerlink" title="1. 点 &#x2F; 向量 &#x2F; 直线：基础结构"></a>1. 点 &#x2F; 向量 &#x2F; 直线：基础结构</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 依赖: 无. 所有运算 O(1)</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="type">const</span> <span class="type">double</span> eps = <span class="number">1e-9</span>, PI = <span class="built_in">acos</span>(<span class="number">-1.0</span>);</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">sgn</span><span class="params">(<span class="type">double</span> x)</span> </span>&#123; <span class="keyword">return</span> x &gt; eps ? <span class="number">1</span> : (x &lt; -eps ? <span class="number">-1</span> : <span class="number">0</span>); &#125;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">P</span> &#123;                                   <span class="comment">// 点 / 向量</span></span><br><span class="line">    <span class="type">double</span> x, y;</span><br><span class="line">    <span class="built_in">P</span>(<span class="type">double</span> x = <span class="number">0</span>, <span class="type">double</span> y = <span class="number">0</span>) : <span class="built_in">x</span>(x), <span class="built_in">y</span>(y) &#123;&#125;</span><br><span class="line">    P <span class="keyword">operator</span>+(<span class="type">const</span> P&amp; b) <span class="type">const</span> &#123; <span class="keyword">return</span> <span class="built_in">P</span>(x + b.x, y + b.y); &#125;</span><br><span class="line">    P <span class="keyword">operator</span>-(<span class="type">const</span> P&amp; b) <span class="type">const</span> &#123; <span class="keyword">return</span> <span class="built_in">P</span>(x - b.x, y - b.y); &#125;</span><br><span class="line">    P <span class="keyword">operator</span>*(<span class="type">double</span> k) <span class="type">const</span> &#123; <span class="keyword">return</span> <span class="built_in">P</span>(x * k, y * k); &#125;    P <span class="keyword">operator</span>/(<span class="type">double</span> k) <span class="type">const</span> &#123; <span class="keyword">return</span> <span class="built_in">P</span>(x / k, y / k); &#125;</span><br><span class="line">    P <span class="keyword">operator</span>-() <span class="type">const</span> &#123; <span class="keyword">return</span> <span class="built_in">P</span>(-x, -y); &#125;</span><br><span class="line">    <span class="type">bool</span> <span class="keyword">operator</span>&lt;(<span class="type">const</span> P&amp; b) <span class="type">const</span> &#123; <span class="keyword">return</span> <span class="built_in">sgn</span>(x - b.x) ? x &lt; b.x : y &lt; b.y; &#125;</span><br><span class="line">    <span class="type">bool</span> <span class="keyword">operator</span>==(<span class="type">const</span> P&amp; b) <span class="type">const</span> &#123; <span class="keyword">return</span> !<span class="built_in">sgn</span>(x - b.x) &amp;&amp; !<span class="built_in">sgn</span>(y - b.y); &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">dot</span><span class="params">(P a, P b)</span> </span>&#123; <span class="keyword">return</span> a.x * b.x + a.y * b.y; &#125;       <span class="comment">// 点积, &gt;0 锐角</span></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">cross</span><span class="params">(P a, P b)</span> </span>&#123; <span class="keyword">return</span> a.x * b.y - a.y * b.x; &#125;     <span class="comment">// 叉积, &gt;0 表示 b 在 a 逆时针侧</span></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">cross</span><span class="params">(P o, P a, P b)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">cross</span>(a - o, b - o); &#125;  <span class="comment">// oa x ob</span></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">len2</span><span class="params">(P a)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">dot</span>(a, a); &#125;                       <span class="comment">// 模长平方(比距离优先用)</span></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">len</span><span class="params">(P a)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">sqrt</span>(<span class="built_in">len2</span>(a)); &#125;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">dist</span><span class="params">(P a, P b)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">len</span>(a - b); &#125;</span><br><span class="line"><span class="function">P <span class="title">unit</span><span class="params">(P a)</span> </span>&#123; <span class="keyword">return</span> a / <span class="built_in">len</span>(a); &#125;                           <span class="comment">// 单位化</span></span><br><span class="line"><span class="function">P <span class="title">rot</span><span class="params">(P a, <span class="type">double</span> t)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">P</span>(a.x * <span class="built_in">cos</span>(t) - a.y * <span class="built_in">sin</span>(t), a.x * <span class="built_in">sin</span>(t) + a.y * <span class="built_in">cos</span>(t)); &#125;</span><br><span class="line"><span class="function">P <span class="title">rot90</span><span class="params">(P a)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">P</span>(-a.y, a.x); &#125;                        <span class="comment">// 逆时针 90 度</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>cross(a,b) &gt; 0</code> 指 b 在 a 逆时针方向；判「c 在有向直线 ab 哪侧」写 <code>cross(b-a, c-a)</code>，顺序反了结论相反。比较距离一律比 <code>len2</code>（整数坐标可全程 long long）。</p></blockquote><h4 id="2-浮点比较与精度控制"><a href="#2-浮点比较与精度控制" class="headerlink" title="2. 浮点比较与精度控制"></a>2. 浮点比较与精度控制</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 依赖: 上文 eps / sgn / P / len2</span></span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">bool</span> <span class="title">eq</span><span class="params">(<span class="type">double</span> a, <span class="type">double</span> b)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">fabs</span>(a - b) &lt; eps; &#125;     <span class="comment">// lt: a &lt; b - eps 为容差版严格小于</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">cmpAng</span><span class="params">(P a, P b)</span> </span>&#123;                        <span class="comment">// 极角排序: atan2 值域 (-pi, pi]</span></span><br><span class="line">    <span class="type">double</span> t1 = <span class="built_in">atan2</span>(a.y, a.x), t2 = <span class="built_in">atan2</span>(b.y, b.x);</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">fabs</span>(t1 - t2) &gt; eps) <span class="keyword">return</span> t1 &lt; t2;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">len2</span>(a) &lt; <span class="built_in">len2</span>(b);                  <span class="comment">// 同角度近的在前(Graham 用)</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 半平面交要跨 pi 的连续极角: ang = atan2(y, x); if (ang &lt; 0) ang += 2 * PI;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ eps 经验值：坐标 1e9 → 1e-9；多次乘除开方累积 → 1e-8；二分几何 → 1e-7。eps 过大会把「相切 &#x2F; 共线」误判成相交。能用整数叉积判定的（相交、共线、凸包、面积）绝不用 double。</p></blockquote><h4 id="3-线段相交-点在多边形内-距离与垂足-多边形面积与-Pick-定理"><a href="#3-线段相交-点在多边形内-距离与垂足-多边形面积与-Pick-定理" class="headerlink" title="3. 线段相交 &#x2F; 点在多边形内 &#x2F; 距离与垂足 &#x2F; 多边形面积与 Pick 定理"></a>3. 线段相交 &#x2F; 点在多边形内 &#x2F; 距离与垂足 &#x2F; 多边形面积与 Pick 定理</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 依赖: 上文 P / sgn / dot / cross / len2 / len / dist</span></span><br><span class="line"><span class="function">P <span class="title">proj</span><span class="params">(P p, P a, P b)</span> </span>&#123; <span class="keyword">return</span> a + (b - a) * (<span class="built_in">dot</span>(p - a, b - a) / <span class="built_in">len2</span>(b - a)); &#125;   <span class="comment">// 垂足</span></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">distLine</span><span class="params">(P p, P a, P b)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">fabs</span>(<span class="built_in">cross</span>(b - a, p - a)) / <span class="built_in">len</span>(b - a); &#125;   <span class="comment">// 点到直线</span></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">distSeg</span><span class="params">(P p, P a, P b)</span> </span>&#123;                       <span class="comment">// 点到线段</span></span><br><span class="line">    <span class="keyword">if</span> (a == b) <span class="keyword">return</span> <span class="built_in">dist</span>(p, a);</span><br><span class="line">    P v = b - a; <span class="type">double</span> t = <span class="built_in">dot</span>(p - a, v) / <span class="built_in">len2</span>(v);</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">sgn</span>(t) &lt;= <span class="number">0</span>) <span class="keyword">return</span> <span class="built_in">dist</span>(p, a);</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">sgn</span>(t - <span class="number">1</span>) &gt;= <span class="number">0</span>) <span class="keyword">return</span> <span class="built_in">dist</span>(p, b);</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">fabs</span>(<span class="built_in">cross</span>(v, p - a)) / <span class="built_in">len</span>(v);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">onSeg</span><span class="params">(P p, P a, P b)</span> </span>&#123; <span class="keyword">return</span> !<span class="built_in">sgn</span>(<span class="built_in">cross</span>(a - p, b - p)) &amp;&amp; <span class="built_in">sgn</span>(<span class="built_in">dot</span>(a - p, b - p)) &lt;= <span class="number">0</span>; &#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">segInt</span><span class="params">(P a, P b, P c, P d)</span> </span>&#123;                     <span class="comment">// 线段 ab 与 cd 是否相交(含端点接触)</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">onSeg</span>(a, c, d) || <span class="built_in">onSeg</span>(b, c, d) || <span class="built_in">onSeg</span>(c, a, b) || <span class="built_in">onSeg</span>(d, a, b)) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">sgn</span>(<span class="built_in">cross</span>(b - a, c - a)) * <span class="built_in">sgn</span>(<span class="built_in">cross</span>(b - a, d - a)) &lt; <span class="number">0</span> &amp;&amp;</span><br><span class="line">           <span class="built_in">sgn</span>(<span class="built_in">cross</span>(d - c, a - c)) * <span class="built_in">sgn</span>(<span class="built_in">cross</span>(d - c, b - c)) &lt; <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">P <span class="title">lineIts</span><span class="params">(P a, P b, P c, P d)</span> </span>&#123;                       <span class="comment">// 直线交点(需不平行)</span></span><br><span class="line">    <span class="type">double</span> s1 = <span class="built_in">cross</span>(b - a, c - a), s2 = <span class="built_in">cross</span>(b - a, d - a); <span class="keyword">return</span> c + (d - c) * (s1 / (s1 - s2));</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">inPoly</span><span class="params">(<span class="type">const</span> vector&lt;P&gt;&amp; p, P q)</span> </span>&#123;                 <span class="comment">// 射线法: 1=内 0=外 -1=边界. O(n)</span></span><br><span class="line">    <span class="type">int</span> n = p.<span class="built_in">size</span>(), c = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        P a = p[i], b = p[(i + <span class="number">1</span>) % n];</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">onSeg</span>(q, a, b)) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">if</span> ((a.y &gt; q.y) != (b.y &gt; q.y)) &#123;             <span class="comment">// 水平射线跨越该边(裸比较, 不加 eps)</span></span><br><span class="line">            <span class="type">double</span> x = a.x + (q.y - a.y) / (b.y - a.y) * (b.x - a.x);</span><br><span class="line">            <span class="keyword">if</span> (x &gt; q.x) c ^= <span class="number">1</span>;                      <span class="comment">// 交点严格在右侧才翻转</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> c;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">winding</span><span class="params">(<span class="type">const</span> vector&lt;P&gt;&amp; p, P q)</span> </span>&#123;                <span class="comment">// 转角法: 0=外 非0=内 -1=边界. O(n)</span></span><br><span class="line">    <span class="type">int</span> n = p.<span class="built_in">size</span>(), w = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        P a = p[i], b = p[(i + <span class="number">1</span>) % n];</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">onSeg</span>(q, a, b)) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">if</span> (a.y &lt;= q.y) &#123; <span class="keyword">if</span> (b.y &gt; q.y &amp;&amp; <span class="built_in">cross</span>(b - a, q - a) &gt; <span class="number">0</span>) w++; &#125;</span><br><span class="line">        <span class="keyword">else</span>            &#123; <span class="keyword">if</span> (b.y &lt;= q.y &amp;&amp; <span class="built_in">cross</span>(b - a, q - a) &lt; <span class="number">0</span>) w--; &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> w;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// ===== 多边形面积 / 三角形 / Pick 定理 =====</span></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">area</span><span class="params">(<span class="type">const</span> vector&lt;P&gt;&amp; p)</span> </span>&#123;                     <span class="comment">// 有向面积, 逆时针为正. O(n)</span></span><br><span class="line">    <span class="type">double</span> s = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>, n = p.<span class="built_in">size</span>(); i &lt; n; i++) s += <span class="built_in">cross</span>(p[i], p[(i + <span class="number">1</span>) % n]);</span><br><span class="line">    <span class="keyword">return</span> s / <span class="number">2</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">triArea</span><span class="params">(P a, P b, P c)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">fabs</span>(<span class="built_in">cross</span>(b - a, c - a)) / <span class="number">2</span>; &#125;   <span class="comment">// 三角形面积</span></span><br><span class="line"><span class="function">P <span class="title">triCentroid</span><span class="params">(P a, P b, P c)</span> </span>&#123; <span class="keyword">return</span> (a + b + c) / <span class="number">3</span>; &#125;                  <span class="comment">// 重心</span></span><br><span class="line"><span class="function">P <span class="title">polyCentroid</span><span class="params">(<span class="type">const</span> vector&lt;P&gt;&amp; p)</span> </span>&#123;                  <span class="comment">// 多边形重心(面积加权). O(n)</span></span><br><span class="line">    <span class="type">double</span> s = <span class="number">0</span>; <span class="function">P <span class="title">c</span><span class="params">(<span class="number">0</span>, <span class="number">0</span>)</span></span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>, n = p.<span class="built_in">size</span>(); i &lt; n; i++) &#123;</span><br><span class="line">        <span class="type">double</span> t = <span class="built_in">cross</span>(p[i], p[(i + <span class="number">1</span>) % n]);</span><br><span class="line">        s += t; c = c + (p[i] + p[(i + <span class="number">1</span>) % n]) * t;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> c / (<span class="number">3</span> * s);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Pick 定理(顶点全为整点的简单多边形): S = I + B/2 - 1</span></span><br><span class="line"><span class="comment">//   B = Σ gcd(|dx|, |dy|) 逐边求和;  I = (2S - B + 2) / 2</span></span><br><span class="line"><span class="comment">//   2S 直接用叉积和(不除 2), 全程整数不丢精度</span></span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">pickI</span><span class="params">(<span class="type">long</span> <span class="type">long</span> s2, <span class="type">long</span> <span class="type">long</span> B)</span> </span>&#123; <span class="keyword">return</span> (s2 - B + <span class="number">2</span>) / <span class="number">2</span>; &#125;   <span class="comment">// s2 = |叉积和|</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 射线法用 <code>(a.y &gt; q.y) != (b.y &gt; q.y)</code> 裸比较而非 <code>sgn</code>，正是为避开水平边退化与 eps 抖动，别好心加 eps。Pick 只对顶点全为整点的简单多边形成立，<code>s2 - B + 2</code> 必为偶数，中途不要转 double。</p></blockquote><h4 id="4-凸包：Andrew-单调链-Graham-扫描"><a href="#4-凸包：Andrew-单调链-Graham-扫描" class="headerlink" title="4. 凸包：Andrew 单调链 &#x2F; Graham 扫描"></a>4. 凸包：Andrew 单调链 &#x2F; Graham 扫描</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// Andrew 单调链. 依赖: 上文 P / sgn / cross。返回逆时针凸包, 已去共线点与重复点. O(n log n)</span></span><br><span class="line"><span class="function">vector&lt;P&gt; <span class="title">convexHull</span><span class="params">(vector&lt;P&gt; p)</span> </span>&#123;</span><br><span class="line">    <span class="built_in">sort</span>(p.<span class="built_in">begin</span>(), p.<span class="built_in">end</span>()); p.<span class="built_in">erase</span>(<span class="built_in">unique</span>(p.<span class="built_in">begin</span>(), p.<span class="built_in">end</span>()), p.<span class="built_in">end</span>());</span><br><span class="line">    <span class="type">int</span> n = p.<span class="built_in">size</span>();</span><br><span class="line">    <span class="keyword">if</span> (n &lt;= <span class="number">2</span>) <span class="keyword">return</span> p;</span><br><span class="line">    <span class="function">vector&lt;P&gt; <span class="title">h</span><span class="params">(n + <span class="number">1</span>)</span></span>; <span class="type">int</span> k = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;                     <span class="comment">// 下凸壳</span></span><br><span class="line">        <span class="keyword">while</span> (k &gt;= <span class="number">2</span> &amp;&amp; <span class="built_in">sgn</span>(<span class="built_in">cross</span>(h[k - <span class="number">1</span>] - h[k - <span class="number">2</span>], p[i] - h[k - <span class="number">1</span>])) &lt;= <span class="number">0</span>) k--;</span><br><span class="line">        h[k++] = p[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = n - <span class="number">2</span>, t = k + <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;     <span class="comment">// 上凸壳</span></span><br><span class="line">        <span class="keyword">while</span> (k &gt;= t &amp;&amp; <span class="built_in">sgn</span>(<span class="built_in">cross</span>(h[k - <span class="number">1</span>] - h[k - <span class="number">2</span>], p[i] - h[k - <span class="number">1</span>])) &lt;= <span class="number">0</span>) k--;</span><br><span class="line">        h[k++] = p[i];</span><br><span class="line">    &#125;</span><br><span class="line">    h.<span class="built_in">resize</span>(k - <span class="number">1</span>); <span class="keyword">return</span> h;                        <span class="comment">// 去掉重复起点</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function">vector&lt;P&gt; <span class="title">graham</span><span class="params">(vector&lt;P&gt; p)</span> </span>&#123;                       <span class="comment">// Graham 扫描: 极角排序版. O(n log n)</span></span><br><span class="line">    <span class="type">int</span> n = p.<span class="built_in">size</span>();</span><br><span class="line">    <span class="keyword">if</span> (n &lt;= <span class="number">2</span>) <span class="keyword">return</span> p;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt; n; i++)                       <span class="comment">// 取最低最左点为基准</span></span><br><span class="line">        <span class="keyword">if</span> (p[i].y &lt; p[<span class="number">0</span>].y || (p[i].y == p[<span class="number">0</span>].y &amp;&amp; p[i].x &lt; p[<span class="number">0</span>].x)) <span class="built_in">swap</span>(p[<span class="number">0</span>], p[i]);</span><br><span class="line">    P o = p[<span class="number">0</span>];</span><br><span class="line">    <span class="built_in">sort</span>(p.<span class="built_in">begin</span>() + <span class="number">1</span>, p.<span class="built_in">end</span>(), [&amp;](P a, P b) &#123;</span><br><span class="line">        <span class="type">double</span> t1 = <span class="built_in">atan2</span>(a.y - o.y, a.x - o.x), t2 = <span class="built_in">atan2</span>(b.y - o.y, b.x - o.x);</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">fabs</span>(t1 - t2) &gt; eps) <span class="keyword">return</span> t1 &lt; t2;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">len2</span>(a - o) &lt; <span class="built_in">len2</span>(b - o);             <span class="comment">// 同角度保留最远的</span></span><br><span class="line">    &#125;);</span><br><span class="line">    vector&lt;P&gt; h;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        <span class="keyword">while</span> (h.<span class="built_in">size</span>() &gt;= <span class="number">2</span> &amp;&amp; <span class="built_in">sgn</span>(<span class="built_in">cross</span>(h.<span class="built_in">back</span>() - h[h.<span class="built_in">size</span>() - <span class="number">2</span>], p[i] - h.<span class="built_in">back</span>())) &lt;= <span class="number">0</span>) h.<span class="built_in">pop_back</span>();</span><br><span class="line">        h.<span class="built_in">push_back</span>(p[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> h;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">hullPeri</span><span class="params">(<span class="type">const</span> vector&lt;P&gt;&amp; h)</span> </span>&#123;                 <span class="comment">// 凸包周长. O(n)</span></span><br><span class="line">    <span class="type">double</span> s = <span class="number">0</span>; <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>, n = h.<span class="built_in">size</span>(); i &lt; n; i++) s += <span class="built_in">dist</span>(h[i], h[(i + <span class="number">1</span>) % n]);</span><br><span class="line">    <span class="keyword">return</span> s;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">hullArea</span><span class="params">(<span class="type">const</span> vector&lt;P&gt;&amp; h)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">fabs</span>(<span class="built_in">area</span>(h)); &#125;             <span class="comment">// 凸包面积</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ Andrew 里 <code>&lt;= 0</code> 弹出共线点（严格凸包、边数最少）；要保留边上的点改 <code>&lt; 0</code>。所有点共线时返回 2 个点，后续旋转卡壳必须先特判 <code>h.size() &lt;= 2</code>。</p></blockquote><h4 id="5-旋转卡壳：凸包直径-最小外接矩形"><a href="#5-旋转卡壳：凸包直径-最小外接矩形" class="headerlink" title="5. 旋转卡壳：凸包直径 &#x2F; 最小外接矩形"></a>5. 旋转卡壳：凸包直径 &#x2F; 最小外接矩形</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 凸包直径(最远点对). 依赖: P / cross / dist. 要求 h 逆时针无重复点. O(n)</span></span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">diameter</span><span class="params">(<span class="type">const</span> vector&lt;P&gt;&amp; h)</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> n = h.<span class="built_in">size</span>();</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">1</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">2</span>) <span class="keyword">return</span> <span class="built_in">dist</span>(h[<span class="number">0</span>], h[<span class="number">1</span>]);</span><br><span class="line">    <span class="type">double</span> ans = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>, j = <span class="number">2</span>; i &lt; n; i++) &#123;</span><br><span class="line">        P a = h[i], b = h[(i + <span class="number">1</span>) % n];</span><br><span class="line">        <span class="keyword">while</span> (<span class="built_in">fabs</span>(<span class="built_in">cross</span>(b - a, h[(j + <span class="number">1</span>) % n] - a)) &gt; <span class="built_in">fabs</span>(<span class="built_in">cross</span>(b - a, h[j] - a))) j = (j + <span class="number">1</span>) % n;</span><br><span class="line">        ans = <span class="built_in">max</span>(ans, <span class="built_in">max</span>(<span class="built_in">dist</span>(a, h[j]), <span class="built_in">dist</span>(b, h[j])));   <span class="comment">// j 为面积最大的对踵点</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 最小面积 / 周长外接矩形: 必有一条边与凸包某条边共线, 只枚举边. O(n)</span></span><br><span class="line"><span class="function">pair&lt;<span class="type">double</span>, <span class="type">double</span>&gt; <span class="title">minRect</span><span class="params">(<span class="type">const</span> vector&lt;P&gt;&amp; h)</span> </span>&#123;    <span class="comment">// 返回 &#123;最小面积, 最小周长&#125;</span></span><br><span class="line">    <span class="type">int</span> n = h.<span class="built_in">size</span>();</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">1</span>) <span class="keyword">return</span> &#123;<span class="number">0</span>, <span class="number">0</span>&#125;;</span><br><span class="line">    <span class="keyword">if</span> (n == <span class="number">2</span>) <span class="keyword">return</span> &#123;<span class="number">0</span>, <span class="number">2</span> * <span class="built_in">len</span>(h[<span class="number">0</span>] - h[<span class="number">1</span>])&#125;;</span><br><span class="line">    <span class="type">double</span> ba = <span class="number">1e100</span>, bp = <span class="number">1e100</span>;</span><br><span class="line">    <span class="type">int</span> j = <span class="number">1</span>, l = <span class="number">1</span>, r = <span class="number">1</span>;                          <span class="comment">// j: 最高点, r/l: 最右/最左点</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        P e = h[(i + <span class="number">1</span>) % n] - h[i];</span><br><span class="line">        <span class="keyword">while</span> (<span class="built_in">cross</span>(e, h[(j + <span class="number">1</span>) % n] - h[i]) &gt; <span class="built_in">cross</span>(e, h[j] - h[i])) j = (j + <span class="number">1</span>) % n;</span><br><span class="line">        <span class="keyword">while</span> (<span class="built_in">dot</span>(h[(r + <span class="number">1</span>) % n] - h[i], e) &gt; <span class="built_in">dot</span>(h[r] - h[i], e)) r = (r + <span class="number">1</span>) % n;</span><br><span class="line">        <span class="keyword">if</span> (i == <span class="number">0</span>) l = r;</span><br><span class="line">        <span class="keyword">while</span> (<span class="built_in">dot</span>(h[(l + <span class="number">1</span>) % n] - h[i], e) &lt;= <span class="built_in">dot</span>(h[l] - h[i], e)) l = (l + <span class="number">1</span>) % n;   <span class="comment">// 必须 &lt;= , 否则并列时会停在 r 上使宽度为 0</span></span><br><span class="line">        <span class="type">double</span> w = <span class="built_in">dot</span>(h[r] - h[i], e) - <span class="built_in">dot</span>(h[l] - h[i], e);   <span class="comment">// 宽度 * |e|</span></span><br><span class="line">        <span class="type">double</span> hh = <span class="built_in">cross</span>(e, h[j] - h[i]), d2 = <span class="built_in">len2</span>(e);        <span class="comment">// 高度 * |e|</span></span><br><span class="line">        ba = <span class="built_in">min</span>(ba, w * hh / d2);</span><br><span class="line">        bp = <span class="built_in">min</span>(bp, <span class="number">2</span> * (w + hh) / <span class="built_in">sqrt</span>(d2));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> &#123;ba, bp&#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 旋转卡壳两个坑：① <code>while</code> 内的下标必须 <code>% n</code> 回绕且 <code>j / l / r</code> 只能单调前进；② 求最左点的循环必须用 <code>&lt;=</code> 比较(不能用 <code>&lt;</code>)，否则投影并列时 <code>l</code> 会停在 <code>r</code> 上使宽度算成 0。凸包含重复点或共线点时会退化，先 <code>convexHull</code> 去干净。<code>while</code> 内必须 <code>% n</code> 回绕，且 <code>j / l / r</code> 只能单调前进；凸包含重复点或共线点时会退化，先 <code>convexHull</code> 去干净。</p></blockquote><h4 id="6-扫描线：矩形面积并-周长并-面积交"><a href="#6-扫描线：矩形面积并-周长并-面积交" class="headerlink" title="6. 扫描线：矩形面积并 &#x2F; 周长并 &#x2F; 面积交"></a>6. 扫描线：矩形面积并 &#x2F; 周长并 &#x2F; 面积交</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 矩形面积并: 离散化 x + 按 y 扫描 + 线段树. 节点区间 [l, r), 叶子是基本区间 [l, l+1)</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">2e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Line</span> &#123; <span class="type">double</span> x1, x2, y; <span class="type">int</span> o; &#125; L[N];</span><br><span class="line"><span class="type">double</span> xs[N], len[N &lt;&lt; <span class="number">3</span>];             <span class="comment">// len[p]: 节点内被覆盖 &gt;= 1 次的长度</span></span><br><span class="line"><span class="type">int</span> cnt[N &lt;&lt; <span class="number">3</span>];                       <span class="comment">// cnt[p]: 节点整段被&quot;完整覆盖&quot;的次数</span></span><br><span class="line"><span class="type">int</span> m, tot;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">pushup</span><span class="params">(<span class="type">int</span> p, <span class="type">int</span> l, <span class="type">int</span> r)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (cnt[p]) len[p] = xs[r] - xs[l];</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (r - l == <span class="number">1</span>) len[p] = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">else</span> len[p] = len[p &lt;&lt; <span class="number">1</span>] + len[p &lt;&lt; <span class="number">1</span> | <span class="number">1</span>];</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">upd</span><span class="params">(<span class="type">int</span> p, <span class="type">int</span> l, <span class="type">int</span> r, <span class="type">int</span> ql, <span class="type">int</span> qr, <span class="type">int</span> v)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (qr &lt;= l || r &lt;= ql) <span class="keyword">return</span>;</span><br><span class="line">    <span class="keyword">if</span> (ql &lt;= l &amp;&amp; r &lt;= qr) &#123; cnt[p] += v; <span class="built_in">pushup</span>(p, l, r); <span class="keyword">return</span>; &#125;</span><br><span class="line">    <span class="type">int</span> mid = (l + r) &gt;&gt; <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">upd</span>(p &lt;&lt; <span class="number">1</span>, l, mid, ql, qr, v), <span class="built_in">upd</span>(p &lt;&lt; <span class="number">1</span> | <span class="number">1</span>, mid, r, ql, qr, v);</span><br><span class="line">    <span class="built_in">pushup</span>(p, l, r);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;                                        <span class="comment">// O(n log n)</span></span><br><span class="line">    <span class="type">int</span> n; <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>, &amp;n);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">        <span class="type">double</span> x1, y1, x2, y2; <span class="built_in">scanf</span>(<span class="string">&quot;%lf%lf%lf%lf&quot;</span>, &amp;x1, &amp;y1, &amp;x2, &amp;y2);</span><br><span class="line">        L[i] = &#123;x1, x2, y1, <span class="number">1</span>&#125;, L[i + n] = &#123;x1, x2, y2, <span class="number">-1</span>&#125;;</span><br><span class="line">        xs[i] = x1, xs[i + n] = x2;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">sort</span>(xs, xs + <span class="number">2</span> * n), m = <span class="built_in">unique</span>(xs, xs + <span class="number">2</span> * n) - xs, tot = m - <span class="number">1</span>;   <span class="comment">// tot 为基本区间数</span></span><br><span class="line">    <span class="built_in">sort</span>(L, L + <span class="number">2</span> * n, [](<span class="type">const</span> Line&amp; a, <span class="type">const</span> Line&amp; b) &#123; <span class="keyword">return</span> a.y &lt; b.y; &#125;);</span><br><span class="line">    <span class="type">double</span> ans = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">2</span> * n; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (i) ans += (L[i].y - L[i - <span class="number">1</span>].y) * len[<span class="number">1</span>];</span><br><span class="line">        <span class="built_in">upd</span>(<span class="number">1</span>, <span class="number">0</span>, tot, <span class="built_in">lower_bound</span>(xs, xs + m, L[i].x1) - xs, <span class="built_in">lower_bound</span>(xs, xs + m, L[i].x2) - xs, L[i].o);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%.2f\n&quot;</span>, ans);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 矩形周长并: 线段树再加 int num[N&lt;&lt;3]; bool lc[N&lt;&lt;3], rc[N&lt;&lt;3];  pushup:</span></span><br><span class="line"><span class="comment">//   cnt[p] ? (len=xs[r]-xs[l], num=1, lc=rc=true)</span></span><br><span class="line"><span class="comment">//          : (r-l==1 ? (len=num=0, lc=rc=false)</span></span><br><span class="line"><span class="comment">//                    : (len=len[a]+len[b], num=num[a]+num[b]-(rc[a]&amp;&amp;lc[b]), lc=lc[a], rc=rc[b]));</span></span><br><span class="line"><span class="comment">//   主循环: ans += fabs(len[1]-pre), pre=len[1];  再 ans += 2*num[1]*(L[i+1].y-L[i].y);</span></span><br><span class="line"><span class="comment">// 矩形面积交: n 个轴对齐矩形的公共部分仍是&quot;一个&quot;矩形, 直接 O(n):</span></span><br><span class="line"><span class="comment">//   x1=max(x1,a), y1=max(y1,b), x2=min(x2,c), y2=min(y2,d); s=max(0,x2-x1)*max(0,y2-y1);</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 线段树区间写 <code>[l, r)</code>、根节点传 <code>[0, tot]</code>（<code>tot = m-1</code>）；写成 <code>[0, m-1]</code> 会丢掉最后一段。<code>cnt</code> 不 pushdown 也不清空——<code>upd</code> 里 <code>cnt[p] += v</code> 后立刻 <code>pushup</code>，回溯时父节点再 <code>pushup</code>，这是板子成立的关键。</p></blockquote><h4 id="7-最小圆覆盖-半平面交-平面最近点对"><a href="#7-最小圆覆盖-半平面交-平面最近点对" class="headerlink" title="7. 最小圆覆盖 &#x2F; 半平面交 &#x2F; 平面最近点对"></a>7. 最小圆覆盖 &#x2F; 半平面交 &#x2F; 平面最近点对</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 最小圆覆盖: 随机增量. 依赖: P / sgn / dist / lineIts / rot90. 期望 O(n), 最坏 O(n^3)</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">C</span> &#123; P o; <span class="type">double</span> r; &#125;;</span><br><span class="line"><span class="function">C <span class="title">circle2</span><span class="params">(P a, P b)</span> </span>&#123; <span class="keyword">return</span> &#123;(a + b) / <span class="number">2</span>, <span class="built_in">dist</span>(a, b) / <span class="number">2</span>&#125;; &#125;</span><br><span class="line"><span class="function">C <span class="title">circle3</span><span class="params">(P a, P b, P c)</span> </span>&#123;                     <span class="comment">// 三点外接圆(需不共线)</span></span><br><span class="line">    P m1 = (a + b) / <span class="number">2</span>, m2 = (a + c) / <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="built_in">lineIts</span>(m1, m1 + <span class="built_in">rot90</span>(b - a), m2, m2 + <span class="built_in">rot90</span>(c - a)), <span class="number">0</span>&#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">inC</span><span class="params">(<span class="type">const</span> C&amp; c, P p)</span> </span>&#123; <span class="keyword">return</span> <span class="built_in">sgn</span>(<span class="built_in">dist</span>(c.o, p) - c.r) &lt;= <span class="number">0</span>; &#125;</span><br><span class="line"><span class="function">C <span class="title">minCircle</span><span class="params">(vector&lt;P&gt; p)</span> </span>&#123;</span><br><span class="line">    <span class="function">mt19937 <span class="title">rng</span><span class="params">(chrono::steady_clock::now().time_since_epoch().count())</span></span>;</span><br><span class="line">    <span class="built_in">shuffle</span>(p.<span class="built_in">begin</span>(), p.<span class="built_in">end</span>(), rng);          <span class="comment">// 必须打乱, 否则可被卡到 O(n^3)</span></span><br><span class="line">    C c&#123;p[<span class="number">0</span>], <span class="number">0</span>&#125;;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; (<span class="type">int</span>)p.<span class="built_in">size</span>(); i++) <span class="keyword">if</span> (!<span class="built_in">inC</span>(c, p[i])) &#123;</span><br><span class="line">        c = &#123;p[i], <span class="number">0</span>&#125;;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; i; j++) <span class="keyword">if</span> (!<span class="built_in">inC</span>(c, p[j])) &#123;</span><br><span class="line">            c = <span class="built_in">circle2</span>(p[i], p[j]);</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>; k &lt; j; k++) <span class="keyword">if</span> (!<span class="built_in">inC</span>(c, p[k]))</span><br><span class="line">                c = <span class="built_in">circle3</span>(p[i], p[j], p[k]), c.r = <span class="built_in">dist</span>(c.o, p[i]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> c;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>半平面交（简述）：每个半平面用有向直线 <code>a -&gt; b</code> 表示「左侧」区域。极角排序后用<strong>双端队列</strong>维护当前交多边形：加入新半平面时，若队尾 &#x2F; 队首的交点落在它右侧就弹出；最后用队首半平面回删队尾，相邻半平面两两求交得到顶点。总复杂度 <code>O(n log n)</code>（排序主导）。CSP 极少直接考，遇到优先转化为凸包 &#x2F; 二分。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 平面最近点对: 分治 + 归并. O(n log n)</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std; <span class="keyword">typedef</span> <span class="type">long</span> <span class="type">long</span> ll; <span class="type">const</span> <span class="type">int</span> N = <span class="number">500005</span>;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">PT</span> &#123; ll x, y, id; &#125; a[N]; <span class="type">int</span> n, A, B; ll mind2 = LLONG_MAX;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">upd</span><span class="params">(<span class="type">const</span> PT&amp; u, <span class="type">const</span> PT&amp; v)</span> </span>&#123;</span><br><span class="line">    ll d = (u.x - v.x) * (u.x - v.x) + (u.y - v.y) * (u.y - v.y);</span><br><span class="line">    <span class="keyword">if</span> (d &lt; mind2) mind2 = d, A = u.id, B = v.id;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dc</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> r)</span> </span>&#123;                        <span class="comment">// 区间 [l, r)</span></span><br><span class="line">    <span class="keyword">if</span> (l + <span class="number">1</span> &gt;= r) <span class="keyword">return</span>;</span><br><span class="line">    <span class="type">int</span> m = (l + r) &gt;&gt; <span class="number">1</span>; ll midx = a[m].x;</span><br><span class="line">    <span class="built_in">dc</span>(l, m), <span class="built_in">dc</span>(m, r);</span><br><span class="line">    <span class="built_in">inplace_merge</span>(a + l, a + m, a + r, [](<span class="type">const</span> PT&amp; u, <span class="type">const</span> PT&amp; v) &#123; <span class="keyword">return</span> u.y &lt; v.y; &#125;);</span><br><span class="line">    vector&lt;PT&gt; t;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = l; i &lt; r; i++) <span class="keyword">if</span> ((a[i].x - midx) * (a[i].x - midx) &lt; mind2) t.<span class="built_in">push_back</span>(a[i]);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; t.<span class="built_in">size</span>(); i++)       <span class="comment">// 归并后按 y 有序, 内层可 break</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">size_t</span> j = i + <span class="number">1</span>; j &lt; t.<span class="built_in">size</span>() &amp;&amp; (t[j].y - t[i].y) * (t[j].y - t[i].y) &lt; mind2; j++)</span><br><span class="line">            <span class="built_in">upd</span>(t[i], t[j]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>, &amp;n);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) <span class="built_in">scanf</span>(<span class="string">&quot;%lld%lld&quot;</span>, &amp;a[i].x, &amp;a[i].y), a[i].id = i;</span><br><span class="line">    <span class="built_in">sort</span>(a, a + n, [](<span class="type">const</span> PT&amp; u, <span class="type">const</span> PT&amp; v) &#123; <span class="keyword">return</span> u.x &lt; v.x; &#125;);</span><br><span class="line">    <span class="built_in">dc</span>(<span class="number">0</span>, n), <span class="built_in">printf</span>(<span class="string">&quot;%d %d %lld\n&quot;</span>, A, B, mind2);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 最近点对用平方距离（long long）比较且<strong>不取等号</strong>，否则退化。C++17 已删除 <code>random_shuffle</code>，一律用 <code>shuffle</code> + <code>mt19937</code>。</p></blockquote><h3 id="B-杂项与实战技巧"><a href="#B-杂项与实战技巧" class="headerlink" title="B. 杂项与实战技巧"></a>B. 杂项与实战技巧</h3><h4 id="8-随机化"><a href="#8-随机化" class="headerlink" title="8. 随机化"></a>8. 随机化</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 随机数基础设施 + 洗牌 + 随机化快排 + 爬山法</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="function">mt19937_64 <span class="title">rng</span><span class="params">(chrono::steady_clock::now().time_since_epoch().count())</span></span>;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">rnd</span><span class="params">(<span class="type">long</span> <span class="type">long</span> l, <span class="type">long</span> <span class="type">long</span> r)</span> </span>&#123; <span class="keyword">return</span> l + (<span class="type">long</span> <span class="type">long</span>)(<span class="built_in">rng</span>() % (r - l + <span class="number">1</span>)); &#125;</span><br><span class="line"><span class="function"><span class="type">double</span> <span class="title">rndd</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> (<span class="type">double</span>)(<span class="built_in">rng</span>() &gt;&gt; <span class="number">11</span>) / (<span class="type">double</span>)(<span class="number">1ULL</span> &lt;&lt; <span class="number">53</span>); &#125;   <span class="comment">// [0,1)</span></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">T</span>&gt; <span class="function"><span class="type">void</span> <span class="title">shuf</span><span class="params">(vector&lt;T&gt;&amp; v)</span> </span>&#123;          <span class="comment">// 洗牌: Fisher-Yates. O(n)</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = v.<span class="built_in">size</span>() - <span class="number">1</span>; i &gt; <span class="number">0</span>; i--) <span class="built_in">swap</span>(v[i], v[<span class="built_in">rnd</span>(<span class="number">0</span>, i)]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">qsort3</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; a, <span class="type">int</span> l, <span class="type">int</span> r)</span> </span>&#123;           <span class="comment">// 随机基准 + 三路划分, 防有序退化</span></span><br><span class="line">    <span class="keyword">if</span> (l &gt;= r) <span class="keyword">return</span>;                               <span class="comment">// 期望 O(n log n)</span></span><br><span class="line">    <span class="built_in">swap</span>(a[l], a[<span class="built_in">rnd</span>(l, r)]);</span><br><span class="line">    <span class="type">int</span> p = a[l], i = l, lt = l, gt = r;</span><br><span class="line">    <span class="keyword">while</span> (i &lt;= gt) &#123;</span><br><span class="line">        <span class="keyword">if</span> (a[i] &lt; p) <span class="built_in">swap</span>(a[lt++], a[i++]);</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (a[i] &gt; p) <span class="built_in">swap</span>(a[i], a[gt--]);</span><br><span class="line">        <span class="keyword">else</span> i++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">qsort3</span>(a, l, lt - <span class="number">1</span>), <span class="built_in">qsort3</span>(a, gt + <span class="number">1</span>, r);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">hillclimb</span><span class="params">()</span> </span>&#123;                                    <span class="comment">// 爬山法(带权费马点): 沿合力方向走</span></span><br><span class="line">    <span class="type">double</span> t = <span class="number">1000</span>;</span><br><span class="line">    <span class="keyword">while</span> (t &gt; <span class="number">1e-8</span>) &#123;</span><br><span class="line">        <span class="type">double</span> nx = <span class="number">0</span>, ny = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">            <span class="type">double</span> dx = x[i] - ansx, dy = y[i] - ansy, d = <span class="built_in">sqrt</span>(dx * dx + dy * dy);</span><br><span class="line">            <span class="keyword">if</span> (d &gt; <span class="number">1e-12</span>) nx += dx * w[i] / d, ny += dy * w[i] / d;   <span class="comment">// 合力方向 = 梯度</span></span><br><span class="line">        &#125;</span><br><span class="line">        ansx += nx * t, ansy += ny * t, t *= (t &gt; <span class="number">0.5</span>) ? <span class="number">0.5</span> : <span class="number">0.97</span>;   <span class="comment">// 先快后慢</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 模拟退火伪代码: T0 初温, Tend 终温, k 降温系数(0.95~0.998)</span></span><br><span class="line"><span class="comment">// srand(chrono::steady_clock::now().time_since_epoch().count());</span></span><br><span class="line"><span class="comment">// double t = T0, nx = ansx, ny = ansy;</span></span><br><span class="line"><span class="comment">// while (t &gt; Tend) &#123;</span></span><br><span class="line"><span class="comment">//   double cx = nx + t * (Rand() * 2 - 1), cy = ny + t * (Rand() * 2 - 1);  // 邻域正比温度</span></span><br><span class="line"><span class="comment">//   double d = calc(cx, cy) - calc(nx, ny);            // calc 顺带更新全局最优</span></span><br><span class="line"><span class="comment">//   if (d &lt; 0 || exp(-d / t) &gt; Rand()) nx = cx, ny = cy;                    // Metropolis 准则</span></span><br><span class="line"><span class="comment">//   t *= k;   &#125;   结束后在最优点附近再做 1000 次小步长随机尝试, 防止停在次优</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>参数调法（实战经验）：初温 <code>T0</code> 取「随便走一步的 |Δf| 量级」的 10 倍，或坐标范围的 1&#x2F;10；降温系数 <code>0.95 ~ 0.998</code>，越接近 1 越准越慢，CSP 有 4 小时可用 <code>0.99</code>；终温 <code>Tend</code> 比题目精度低 2<del>3 个数量级（要求 1e-3 就取 1e-5）；<strong>多次退火取最优</strong>——单次 SA 方差大，用不同种子跑 5</del>20 遍取 min 比调参数更有效；也可按时间退火 <code>while ((double)clock() / CLOCKS_PER_SEC &lt; 1.8)</code>；邻域要匹配问题（连续型用高斯 &#x2F; 均匀扰动，离散型用 swap &#x2F; 翻转）。</p><blockquote><p>⚠️ SA &#x2F; 爬山不保证正确性，只适合「精度要求不高 + 正解写不出」的场景；CSP 优先写正解，SA 作 T3&#x2F;T4 部分分保底。</p></blockquote><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 随机化哈希防卡: 每个 key 赋随机权值, 出题人无法预先构造冲突. 64 位自然溢出</span></span><br><span class="line"><span class="function"><span class="type">static</span> <span class="type">uint64_t</span> <span class="title">splitmix64</span><span class="params">(<span class="type">uint64_t</span> x)</span> </span>&#123;</span><br><span class="line">    x += <span class="number">0x9e3779b97f4a7c15ULL</span>;</span><br><span class="line">    x = (x ^ (x &gt;&gt; <span class="number">30</span>)) * <span class="number">0xbf58476d1ce4e5b9ULL</span>;</span><br><span class="line">    x = (x ^ (x &gt;&gt; <span class="number">27</span>)) * <span class="number">0x94d049bb133111ebULL</span>;</span><br><span class="line">    <span class="keyword">return</span> x ^ (x &gt;&gt; <span class="number">31</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">ULLHash</span> &#123;                                    <span class="comment">// 防 unordered_map 被卡</span></span><br><span class="line">    <span class="function"><span class="type">size_t</span> <span class="title">operator</span><span class="params">()</span><span class="params">(<span class="type">uint64_t</span> x)</span> <span class="type">const</span> </span>&#123;</span><br><span class="line">        <span class="type">static</span> <span class="type">const</span> <span class="type">uint64_t</span> FIXED = chrono::steady_clock::<span class="built_in">now</span>().<span class="built_in">time_since_epoch</span>().<span class="built_in">count</span>(); <span class="keyword">return</span> <span class="built_in">splitmix64</span>(x + FIXED);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// unordered_map&lt;long long, int, ULLHash&gt; mp;   字符串哈希同理: h = h * B + hsh(s[i]);</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="9-分治：CDQ-整体二分-归并排序求逆序对"><a href="#9-分治：CDQ-整体二分-归并排序求逆序对" class="headerlink" title="9. 分治：CDQ &#x2F; 整体二分 &#x2F; 归并排序求逆序对"></a>9. 分治：CDQ &#x2F; 整体二分 &#x2F; 归并排序求逆序对</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// CDQ 分治求三维偏序: 统计每个点被多少个点三维都 &lt;= 它. O(n log^2 n)</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">1e5</span> + <span class="number">5</span>, K = <span class="number">2e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="type">int</span> n, k, m, res[N], tr[K];</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">E</span> &#123; <span class="type">int</span> a, b, c, cnt, ans; &#125; e[N], u[N];</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">cmpA</span><span class="params">(<span class="type">const</span> E&amp; x, <span class="type">const</span> E&amp; y)</span> </span>&#123; <span class="keyword">return</span> x.a != y.a ? x.a &lt; y.a : (x.b != y.b ? x.b &lt; y.b : x.c &lt; y.c); &#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">cmpB</span><span class="params">(<span class="type">const</span> E&amp; x, <span class="type">const</span> E&amp; y)</span> </span>&#123; <span class="keyword">return</span> x.b != y.b ? x.b &lt; y.b : x.c &lt; y.c; &#125;</span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">same</span><span class="params">(<span class="type">const</span> E&amp; x, <span class="type">const</span> E&amp; y)</span> </span>&#123; <span class="keyword">return</span> x.a == y.a &amp;&amp; x.b == y.b &amp;&amp; x.c == y.c; &#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">add</span><span class="params">(<span class="type">int</span> p, <span class="type">int</span> v)</span> </span>&#123; <span class="keyword">for</span> (; p &lt;= k; p += p &amp; -p) tr[p] += v; &#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">ask</span><span class="params">(<span class="type">int</span> p)</span> </span>&#123; <span class="type">int</span> s = <span class="number">0</span>; <span class="keyword">for</span> (; p; p -= p &amp; -p) s += tr[p]; <span class="keyword">return</span> s; &#125;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">cdq</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> r)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (l == r) <span class="keyword">return</span>;</span><br><span class="line">    <span class="type">int</span> mid = (l + r) &gt;&gt; <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">cdq</span>(l, mid), <span class="built_in">cdq</span>(mid + <span class="number">1</span>, r);                                       <span class="comment">// 1) 递归两半</span></span><br><span class="line">    <span class="built_in">sort</span>(u + l, u + mid + <span class="number">1</span>, cmpB), <span class="built_in">sort</span>(u + mid + <span class="number">1</span>, u + r + <span class="number">1</span>, cmpB);  <span class="comment">// 2) 按 b 排序</span></span><br><span class="line">    <span class="type">int</span> i = l;                                                           <span class="comment">// 3) 双指针跨区间</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = mid + <span class="number">1</span>; j &lt;= r; j++) &#123;</span><br><span class="line">        <span class="keyword">while</span> (i &lt;= mid &amp;&amp; u[i].b &lt;= u[j].b) <span class="built_in">add</span>(u[i].c, u[i].cnt), i++;</span><br><span class="line">        u[j].ans += <span class="built_in">ask</span>(u[j].c);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> t = l; t &lt; i; t++) <span class="built_in">add</span>(u[t].c, -u[t].cnt);                  <span class="comment">// 4) 回滚 BIT</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%d%d&quot;</span>, &amp;n, &amp;k);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) <span class="built_in">scanf</span>(<span class="string">&quot;%d%d%d&quot;</span>, &amp;e[i].a, &amp;e[i].b, &amp;e[i].c);</span><br><span class="line">    <span class="built_in">sort</span>(e + <span class="number">1</span>, e + n + <span class="number">1</span>, cmpA);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>, t = <span class="number">0</span>; i &lt;= n; i++) &#123;              <span class="comment">// 去重: 相同三元组合并计数</span></span><br><span class="line">        t++;</span><br><span class="line">        <span class="keyword">if</span> (i == n || !<span class="built_in">same</span>(e[i], e[i + <span class="number">1</span>])) &#123; u[++m] = e[i], u[m].cnt = t, u[m].ans = <span class="number">0</span>, t = <span class="number">0</span>; &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">cdq</span>(<span class="number">1</span>, m);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= m; i++) res[u[i].ans + u[i].cnt - <span class="number">1</span>] += u[i].cnt;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) <span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, res[i]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>整体二分（简述）：把「多次询问第 k 小」放一起二分。递归 <code>solve(l, r, Q)</code> 取 <code>mid</code>，把值域 <code>&lt;= mid</code> 的数插入 BIT；对每个询问查区间计数 <code>t</code>，<code>k &lt;= t</code> 分到左半，否则 <code>k -= t</code> 分到右半，回溯时撤销 BIT。总复杂度 <code>O((n + q) log n log V)</code>。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 整体二分核心(区间第 k 小). 事件: type=1 在 id 位置插入值 l; type=2 询问 [l,r] 第 k 小</span></span><br><span class="line"><span class="comment">// struct Q &#123; int l, r, k, id, type; &#125; q[N*2], q1[N*2], q2[N*2];</span></span><br><span class="line"><span class="comment">// solve(l, r, ql, qr):  l==r 时 ans[q[i].id] = l 并返回;  否则 mid = (l+r)&gt;&gt;1, c1 = c2 = 0;</span></span><br><span class="line"><span class="comment">//   for i in [ql, qr]:</span></span><br><span class="line"><span class="comment">//     type==1: q[i].l &lt;= mid ? (add(q[i].id, 1), q1[++c1] = q[i]) : q2[++c2] = q[i];</span></span><br><span class="line"><span class="comment">//     否则:     x = sum(q[i].r) - sum(q[i].l - 1); q[i].k &lt;= x ? q1[++c1] = q[i] : (q[i].k -= x, q2[++c2] = q[i]);</span></span><br><span class="line"><span class="comment">//   撤销: for i in [1, c1] 若 q1[i].type == 1 则 add(q1[i].id, -1);</span></span><br><span class="line"><span class="comment">//   归位: q[ql..] = q1[1..c1], q[ql+c1..] = q2[1..c2];  递归 solve(l, mid, ql, ql+c1-1), solve(mid+1, r, ql+c1, qr);</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 归并排序求逆序对. O(n log n)</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">5e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="type">int</span> n, a[N], t[N]; <span class="type">long</span> <span class="type">long</span> cnt = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">msort</span><span class="params">(<span class="type">int</span> l, <span class="type">int</span> r)</span> </span>&#123;                 <span class="comment">// 区间 [l, r)</span></span><br><span class="line">    <span class="keyword">if</span> (r - l &lt;= <span class="number">1</span>) <span class="keyword">return</span>;</span><br><span class="line">    <span class="type">int</span> m = (l + r) &gt;&gt; <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">msort</span>(l, m), <span class="built_in">msort</span>(m, r);</span><br><span class="line">    <span class="type">int</span> i = l, j = m, k = l;</span><br><span class="line">    <span class="keyword">while</span> (i &lt; m &amp;&amp; j &lt; r) &#123;</span><br><span class="line">        <span class="keyword">if</span> (a[i] &lt;= a[j]) t[k++] = a[i++];          <span class="comment">// 取等号放左边 =&gt; 统计严格逆序对</span></span><br><span class="line">        <span class="keyword">else</span> t[k++] = a[j++], cnt += m - i;         <span class="comment">// 左边剩余 m-i 个都比 a[j] 大</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (i &lt; m) t[k++] = a[i++];</span><br><span class="line">    <span class="keyword">while</span> (j &lt; r) t[k++] = a[j++];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> x = l; x &lt; r; x++) a[x] = t[x];</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>, &amp;n);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>, &amp;a[i]);</span><br><span class="line">    <span class="built_in">msort</span>(<span class="number">0</span>, n), <span class="built_in">printf</span>(<span class="string">&quot;%lld\n&quot;</span>, cnt);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ CDQ 里 BIT 回滚必须 <code>for (int t = l; t &lt; i; t++)</code>，只撤销真正插入过的；漏回滚会让下一层统计翻倍。逆序对用 <code>long long</code>（n&#x3D;5e5 时约 1.25e11），累加写 <code>cnt += m - i</code> 而非 <code>cnt++</code>。</p></blockquote><h4 id="10-位运算技巧"><a href="#10-位运算技巧" class="headerlink" title="10. 位运算技巧"></a>10. 位运算技巧</h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 位运算速查. 全部 O(1)</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">lowbit</span><span class="params">(<span class="type">int</span> x)</span> </span>&#123; <span class="keyword">return</span> x &amp; -x; &#125;                 <span class="comment">// 最低位 1 及其后的 0</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isPow2</span><span class="params">(<span class="type">int</span> x)</span> </span>&#123; <span class="keyword">return</span> x &gt; <span class="number">0</span> &amp;&amp; !(x &amp; (x - <span class="number">1</span>)); &#125;</span><br><span class="line"><span class="comment">// 枚举 mask 的所有非空子集(总 O(3^n)): for (int s = mask; s; s = (s - 1) &amp; mask) &#123; ... &#125;</span></span><br><span class="line"><span class="comment">// 含空集: do &#123; ... &#125; while (s = (s - 1) &amp; mask);</span></span><br><span class="line">__builtin_popcount(x) / __builtin_popcountll(x);   <span class="comment">// 1 的个数(unsigned int / unsigned long long)</span></span><br><span class="line">__builtin_clz(x) / __builtin_ctz(x);               <span class="comment">// 前导 0 / 末尾 0 个数, 均要求 x != 0</span></span><br><span class="line">__builtin_parity(x) / __builtin_ffs(x);            <span class="comment">// popcount 奇偶 / 最低位 1 的下标 + 1</span></span><br><span class="line"><span class="comment">// 32 位下 x 的最高位下标 = 31 - __builtin_clz(x)</span></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">popcnt</span><span class="params">(<span class="type">unsigned</span> x)</span> </span>&#123;   <span class="comment">// 手写 popcount(编译器不认 __builtin 时)</span></span><br><span class="line">    x = x - ((x &gt;&gt; <span class="number">1</span>) &amp; <span class="number">0x55555555u</span>), x = (x &amp; <span class="number">0x33333333u</span>) + ((x &gt;&gt; <span class="number">2</span>) &amp; <span class="number">0x33333333u</span>);</span><br><span class="line">    <span class="keyword">return</span> (<span class="type">int</span>)(((x + (x &gt;&gt; <span class="number">4</span>)) &amp; <span class="number">0x0f0f0f0fu</span>) * <span class="number">0x01010101u</span> &gt;&gt; <span class="number">24</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// bitset 压位: 01 背包可达性 f |= f &lt;&lt; w, 每次 O(N/64), 比 bool DP 快 64 倍</span></span><br><span class="line"><span class="comment">//   bitset&lt;N&gt; f; f[0] = 1; f.count() 为 O(N/64);</span></span><br><span class="line"><span class="comment">//   f._Find_first() / f._Find_next(p) 为 libstdc++ 扩展(g++ 可用)</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ <code>x == 0</code> 时 <code>__builtin_clz / ctz / ffs</code> 行为未定义，必须先特判。<code>bitset&lt;N&gt;</code> 的 N 必须是编译期常量，过大（&gt; 1e8 位）会 MLE；<code>&lt;&lt;</code> &#x2F; <code>&gt;&gt;</code> 是逻辑移位，做背包要先截断上限。</p></blockquote><h4 id="11-卡常与优化"><a href="#11-卡常与优化" class="headerlink" title="11. 卡常与优化"></a>11. 卡常与优化</h4><table><thead><tr><th>手段</th><th>说明</th><th>收益</th></tr></thead><tbody><tr><td><code>-O2</code></td><td>考场默认开启；本地务必用 <code>g++ -O2 -std=c++17</code> 复现</td><td>巨大，先确认这个</td></tr><tr><td><code>inline</code></td><td>小函数加 <code>inline</code>（类内成员函数默认内联）；大函数加了没用</td><td>小</td></tr><tr><td><code>register</code></td><td>C++17 已弃用，仅作提示，不要指望</td><td>无</td></tr><tr><td>快读快写</td><td><code>fread</code> &#x2F; <code>getchar_unlocked</code> 整块读写，比 <code>cin</code> 快 3~10 倍</td><td>大（IO 密集）</td></tr><tr><td>减少取模</td><td>能只在最后取模就只在最后取模；<code>%</code> 比加法慢 10~20 倍</td><td>中~大</td></tr><tr><td>数组维度顺序</td><td>多维数组把「最内层循环对应的维度」放最后一维，保证连续访问</td><td>大</td></tr><tr><td>cache 友好</td><td>循环嵌套顺序与内存布局一致；大矩阵用分块（blocking）</td><td>大</td></tr><tr><td>循环展开</td><td>手动展开 2~4 次或 <code>#pragma GCC unroll</code>；现代编译器常自动做</td><td>小~中</td></tr><tr><td><code>memset</code> &#x2F; <code>vector</code></td><td><code>memset</code> 按字节填充(1e7 个 int 约 10ms), 别在循环里反复清；<code>v.reserve(n)</code> 避免扩容拷贝</td><td>视情况</td></tr></tbody></table><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 快读 / 快写: 比 cin(+sync_with_stdio) 快 3~10 倍; 数据极大时改用 fread 整块读</span></span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="type">int</span> <span class="title">rd</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> x = <span class="number">0</span>, f = <span class="number">1</span>, c = <span class="built_in">getchar_unlocked</span>();</span><br><span class="line">    <span class="keyword">while</span> (c &lt; <span class="string">&#x27;0&#x27;</span> || c &gt; <span class="string">&#x27;9&#x27;</span>) &#123; <span class="keyword">if</span> (c == <span class="string">&#x27;-&#x27;</span>) f = <span class="number">-1</span>; c = <span class="built_in">getchar_unlocked</span>(); &#125;</span><br><span class="line">    <span class="keyword">while</span> (c &gt;= <span class="string">&#x27;0&#x27;</span> &amp;&amp; c &lt;= <span class="string">&#x27;9&#x27;</span>) x = x * <span class="number">10</span> + (c - <span class="string">&#x27;0&#x27;</span>), c = <span class="built_in">getchar_unlocked</span>();</span><br><span class="line">    <span class="keyword">return</span> x * f;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 快写: 转十进制入栈后 putchar_unlocked; 模板化把 int 换成 template &lt;class T&gt;</span></span><br><span class="line"><span class="comment">// getchar_unlocked / putchar_unlocked 是 POSIX 扩展, Linux g++ 可直接用; 超大输入改 fread 整块读</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ cache 友好要点：最内层循环访问的应是「最后一维下标连续变化」的数组。反例 <code>c[i][j] += a[i][k]*b[k][j]</code> 中 <code>b[k][j]</code> 步长为 N，每次 cache miss；交换 j &#x2F; k 循环并把 <code>a[i][k]</code> 提到外层即可。<br>⚠️ <code>-O2</code> 下不要依赖未定义行为（有符号溢出、越界、未初始化变量），本地跑对、交上去挂多半是 UB 被优化掉了。CCF 评测<strong>把所有声明的全局数组都算进内存占用</strong>（不像多数 OJ 只算实际使用的部分），别随手开 <code>int a[100000000]</code>。<code>memset(a, 0x3f, sizeof a)</code> 得 <code>0x3f3f3f3f ≈ 1.06e9</code>，两两相加不溢出 int；<code>memset(a, 0x7f, ...)</code> 得 <code>0x7f7f7f7f ≈ 2.14e9</code>，相加必溢出。</p></blockquote><h4 id="12-对拍-调试-数据生成器"><a href="#12-对拍-调试-数据生成器" class="headerlink" title="12. 对拍 &#x2F; 调试 &#x2F; 数据生成器"></a>12. 对拍 &#x2F; 调试 &#x2F; 数据生成器</h4><p>Linux 版（存为 <code>duipai.sh</code>，同目录放 <code>gen.cpp / std.cpp / my.cpp</code>，执行 <code>bash duipai.sh</code>）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">#!/bin/bash</span></span><br><span class="line">g++ -O2 -std=c++17 -o gen gen.cpp &amp;&amp; g++ -O2 -std=c++17 -o std std.cpp &amp;&amp; g++ -O2 -std=c++17 -o my my.cpp || <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">for</span> ((i = <span class="number">1</span>; ; i++)); <span class="keyword">do</span></span><br><span class="line">  ./gen &gt; in.txt; ./std &lt; in.txt &gt; out1.txt; ./my &lt; in.txt &gt; out2.txt</span><br><span class="line">  <span class="keyword">if</span> ! diff -q out1.txt out2.txt &gt; /dev/null; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;WA on test <span class="variable">$i</span>&quot;</span>; <span class="built_in">echo</span> <span class="string">&quot;-- in --&quot;</span>; <span class="built_in">cat</span> in.txt</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;-- std --&quot;</span>; <span class="built_in">cat</span> out1.txt; <span class="built_in">echo</span> <span class="string">&quot;-- my --&quot;</span>; <span class="built_in">cat</span> out2.txt</span><br><span class="line">    <span class="built_in">break</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;AC <span class="variable">$i</span>&quot;</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>Windows 版（存为 <code>duipai.bat</code>，同目录放 <code>gen.exe / std.exe / my.exe</code>，双击运行）：</p><figure class="highlight bat"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">@<span class="built_in">echo</span> off</span><br><span class="line"><span class="keyword">for</span> /l <span class="variable">%%i</span> <span class="keyword">in</span> (<span class="number">1</span>,<span class="number">1</span>,<span class="number">100000</span>) <span class="keyword">do</span> (</span><br><span class="line">    gen.exe &gt; <span class="keyword">in</span>.txt</span><br><span class="line">    std.exe &lt; <span class="keyword">in</span>.txt &gt; out1.txt</span><br><span class="line">    my.exe  &lt; <span class="keyword">in</span>.txt &gt; out2.txt</span><br><span class="line">    fc out1.txt out2.txt &gt; <span class="built_in">nul</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">errorlevel</span> <span class="number">1</span> (</span><br><span class="line">        <span class="built_in">echo</span> WA on test <span class="variable">%%i</span></span><br><span class="line">        <span class="built_in">type</span> <span class="keyword">in</span>.txt &amp; <span class="built_in">echo</span> -- std -- &amp; <span class="built_in">type</span> out1.txt &amp; <span class="built_in">echo</span> -- my -- &amp; <span class="built_in">type</span> out2.txt</span><br><span class="line">        <span class="built_in">pause</span> &amp; <span class="keyword">exit</span> /b</span><br><span class="line">    )</span><br><span class="line">    <span class="built_in">echo</span> AC <span class="variable">%%i</span></span><br><span class="line">)</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// gen.cpp: 随机数据生成器(时间种子保证每次不同, 也支持命令行固定种子复现)</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="function">mt19937_64 <span class="title">rng</span><span class="params">(chrono::steady_clock::now().time_since_epoch().count())</span></span>;</span><br><span class="line"><span class="function"><span class="type">long</span> <span class="type">long</span> <span class="title">rnd</span><span class="params">(<span class="type">long</span> <span class="type">long</span> l, <span class="type">long</span> <span class="type">long</span> r)</span> </span>&#123; <span class="keyword">return</span> l + (<span class="type">long</span> <span class="type">long</span>)(<span class="built_in">rng</span>() % (r - l + <span class="number">1</span>)); &#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span>** argv)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (argc &gt; <span class="number">1</span>) rng.<span class="built_in">seed</span>((<span class="type">unsigned</span> <span class="type">long</span> <span class="type">long</span>)<span class="built_in">atoll</span>(argv[<span class="number">1</span>]));</span><br><span class="line">    <span class="type">int</span> n = (<span class="type">int</span>)<span class="built_in">rnd</span>(<span class="number">1</span>, <span class="number">10</span>); <span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>, n);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) <span class="built_in">printf</span>(<span class="string">&quot;%lld %lld\n&quot;</span>, <span class="built_in">rnd</span>(<span class="number">-10</span>, <span class="number">10</span>), <span class="built_in">rnd</span>(<span class="number">-10</span>, <span class="number">10</span>));</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 数据设计四件套: 1 极小值(n=1,全 0,全负数)  2 极大值(卡上限)  3 纯随机  4 特殊构造(全相等/有序/全共线/链)</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>调试技巧：本地编译 <code>g++ -std=c++17 -O2 -Wall -Wextra -Wshadow -g a.cpp -o a</code>；查越界 &#x2F; 溢出加 <code>-fsanitize=address,undefined</code>（慢 2~5 倍，时限紧时别用）；查 STL 误用加 <code>-D_GLIBCXX_DEBUG</code>；Windows 递归爆栈加 <code>-Wl,--stack=268435456</code>（256MB），Linux 用 <code>ulimit -s unlimited</code>；调试信息一律输出到 <code>cerr</code>（不会被 OJ 当作答案）；<code>assert</code> 在 <code>-DNDEBUG</code> 下被完全移除，且不要写 <code>assert(f(i++))</code> 这类带副作用的断言。</p><blockquote><p>⚠️ 对拍里的 <code>std</code> 必须是<strong>你自己写的暴力</strong>，不能抄题解（否则一起错）；暴力正确性先用「极小数据手算」验证。<code>diff</code> 对行尾空格 &#x2F; 换行敏感，Windows 造的数据在 Linux 上跑可能因 <code>\r\n</code> 全部 WA，造数据用 <code>printf</code> 而非 <code>echo</code>。</p></blockquote><h4 id="13-WA-TLE-RE-MLE-原因速查表"><a href="#13-WA-TLE-RE-MLE-原因速查表" class="headerlink" title="13. WA &#x2F; TLE &#x2F; RE &#x2F; MLE 原因速查表"></a>13. WA &#x2F; TLE &#x2F; RE &#x2F; MLE 原因速查表</h4><table><thead><tr><th>症状</th><th>常见原因</th><th>快速排查</th></tr></thead><tbody><tr><td>WA</td><td>没开 <code>long long</code>（和 ≥ 2^31、乘积、累加）</td><td>参与运算的量统一 <code>long long</code></td></tr><tr><td>WA</td><td>多组数据没清空数组 &#x2F; 没重置全局变量</td><td>每组开头 <code>memset</code> 或重建 <code>vector</code></td></tr><tr><td>WA</td><td><code>sync_with_stdio(false)</code> 后又混用 <code>scanf/printf</code> 与 <code>cin/cout</code></td><td>二选一，绝不混用</td></tr><tr><td>WA</td><td>读入优化没处理负数；<code>%d</code> 读了 <code>long long</code></td><td>手测负数样例</td></tr><tr><td>WA</td><td>边界漏判：n&#x3D;1、全相等、空集、除零</td><td>专门造这四类数据</td></tr><tr><td>WA</td><td>比较函数不满足严格弱序（<code>return a &lt;= b</code>、<code>(x&amp;1)^(a&lt;b)</code>）</td><td>检查 <code>a &lt; a</code> 必须为 false</td></tr><tr><td>WA</td><td>浮点用 <code>==</code> 比较或 eps 不当；取模后忘记处理负数</td><td>改用 <code>sgn</code> &#x2F; 尽量整数化；<code>(x % mod + mod) % mod</code></td></tr><tr><td>WA</td><td>DP &#x2F; 递归顺序反了，循环边界 <code>&lt;=</code> 与 <code>&lt;</code> 混用</td><td>手推小样例</td></tr><tr><td>WA</td><td>宏没加括号：<code>#define sq(x) x*x</code></td><td>宏参数全加括号，优先 <code>inline</code> 函数</td></tr><tr><td>TLE</td><td>死循环 &#x2F; 分治没判边界导致无限递归</td><td>加深度计数打印</td></tr><tr><td>TLE</td><td>宏写 <code>#define max(x,y) ((x)&gt;(y)?(x):(y))</code>，函数被求值 2~3 次</td><td>改用 <code>std::max</code></td></tr><tr><td>TLE</td><td>线段树 &#x2F; DFS 查询没剪枝，退化成 O(n)</td><td>检查递归出口与剪枝条件</td></tr><tr><td>TLE</td><td><code>std::string</code> 用 <code>a = a + b</code> 而非 <code>a += b</code>；循环里反复调用 <code>strlen</code> 等 O(n) 函数</td><td>用 <code>+=</code>；提到循环外</td></tr><tr><td>TLE</td><td>常数太大：<code>cin</code> 未加速、用 <code>map</code> 代替 <code>unordered_map</code>、频繁取模</td><td>按第 11 节逐条替换</td></tr><tr><td>RE</td><td>数组开小 &#x2F; 下标越界（线段树 4n、邻接表 2m）</td><td>本地开 <code>-fsanitize=address</code></td></tr><tr><td>RE</td><td>递归太深爆栈；除以 0、对 0 取模；<code>vector</code> &#x2F; <code>stack</code> 下标越界、迭代器失效</td><td>Windows <code>-Wl,--stack=</code>、Linux <code>ulimit -s</code> 或改迭代；特判；加 <code>-D_GLIBCXX_DEBUG</code></td></tr><tr><td>RE</td><td>提交时没删文件操作（CCF 环境无写权限）</td><td>注释掉 <code>freopen</code></td></tr><tr><td>MLE</td><td>数组过大</td><td>算一遍 <code>sizeof</code>：1e7 个 int &#x3D; 40MB</td></tr><tr><td>MLE</td><td>全局静态数组全量计入（CCF 评测特点）</td><td>只申请真正需要的，或改 <code>vector</code> 按需分配</td></tr><tr><td>MLE</td><td>递归 &#x2F; <code>vector</code> 反复扩容且不释放</td><td><code>reserve</code> + 复用容器</td></tr><tr><td>格式错</td><td>多输出空格 &#x2F; 换行、大小写、<code>%.2f</code> 与 <code>%.2lf</code></td><td>严格对照题面输出格式</td></tr></tbody></table><blockquote><p>⚠️ CSP 按测试点给分：T3&#x2F;T4 写不出正解时优先交暴力 &#x2F; 骗分版本（<code>n &lt;= 20</code> 枚举、<code>n &lt;= 1000</code> 的 O(n^2)），别交空文件。<br>⚠️ 最后 20 分钟固定动作：① 关掉所有调试输出 ② 删掉 <code>freopen</code> ③ 把大样例再跑一遍 ④ 确认 5 道题都提交过一次。</p></blockquote><h2 id="第-07-章-CCF-CSP-考情分析与应考策略"><a href="#第-07-章-CCF-CSP-考情分析与应考策略" class="headerlink" title="第 07 章 CCF-CSP 考情分析与应考策略"></a>第 07 章 CCF-CSP 考情分析与应考策略</h2><blockquote><p>本章所有事实性结论均给出可访问来源。凡”社区整理”（非 CCF 官方）的数据均已注明；确实查不到的写”未检索到可靠来源”，未能证实的写「（待验证）」。</p><p>⚠️ <strong>先纠正一个流传很广的错误</strong>：CSP 专业级认证的考试时长是 <strong>4 小时（240 分钟）</strong>，不是 5 小时。CCF 官方报名通知写的是”考试时间：13:30 - 17:30”，《CCF 软件能力认证标准》写的是”考试时间为 240 分钟”。</p></blockquote><h3 id="7-1-考试结构（CSP-专业级认证）"><a href="#7-1-考试结构（CSP-专业级认证）" class="headerlink" title="7.1 考试结构（CSP 专业级认证）"></a>7.1 考试结构（CSP 专业级认证）</h3><h4 id="7-1-1-官方硬事实"><a href="#7-1-1-官方硬事实" class="headerlink" title="7.1.1 官方硬事实"></a>7.1.1 官方硬事实</h4><table><thead><tr><th>项目</th><th>官方规定</th></tr></thead><tbody><tr><td>全称</td><td>CCF 计算机软件能力认证（Certified Software Professional，CSP），即俗称的”专业级”</td></tr><tr><td>题量与分值</td><td>共 5 道题，每题 100 分，总分 500 分</td></tr><tr><td>时长</td><td>240 分钟（4 小时）；近年场次为考试日 13:30 - 17:30</td></tr><tr><td>形式</td><td>全部上机编程；黑盒测试，程序在限定时间空间内通过给定数据即得分</td></tr><tr><td>支持语言</td><td>C&#x2F;C++、Java、Python（报名可选 ALL，不同题目可用不同语言）</td></tr><tr><td>命题评测</td><td>由 CCF 统一命题、统一评测</td></tr><tr><td>举办频率</td><td>每年 3 次左右（3 月 &#x2F; 5-6 月 &#x2F; 9 月；2025 年另有 12 月场）</td></tr><tr><td>费用</td><td>个人报名：CCF 会员 400 元 &#x2F; 非会员 600 元；校内团报最低 200 元</td></tr><tr><td>模拟练习</td><td>报名成功后可获 1 个兑换码，兑换 1 套往期真题在线模拟评测</td></tr></tbody></table><p>来源：<a href="https://www.cspro.org/cms/show.action?code=jumpnewstemplate&siteid=100000&channelid=0000000103&newsid=1cc1a98caa94481a8a6473ca339230a4">第43次CCF CSP认证（2026年9月13日）报名通知</a> ｜ <a href="https://www.cspro.org/cms/show.action?code=jumpnewstemplate&siteid=100000&channelid=0000000107&newsid=62ebd5ce75b54c56a04332c97705f421">CCF软件能力认证标准</a></p><h4 id="7-1-2-官方场次与题目前缀对照表（2022-2026）"><a href="#7-1-2-官方场次与题目前缀对照表（2022-2026）" class="headerlink" title="7.1.2 官方场次与题目前缀对照表（2022 - 2026）"></a>7.1.2 官方场次与题目前缀对照表（2022 - 2026）</h4><p>题目前缀 &#x3D; 认证年月，例如 202409-1 表示 2024 年 9 月场第 1 题。日期一列全部来自 CCF 官方报名通知标题；第 41 - 43 次的题目前缀按同一规则推算（该三次认证的题目在撰写时尚未公开）。</p><table><thead><tr><th>次数</th><th>日期</th><th>前缀</th><th>次数</th><th>日期</th><th>前缀</th></tr></thead><tbody><tr><td>第 25 次</td><td>2022-03-20</td><td>202203</td><td>第 34 次</td><td>2024-06-02</td><td>202406</td></tr><tr><td>第 26 次</td><td>2022-06-12</td><td>202206</td><td>第 35 次</td><td>2024-09-22</td><td>202409</td></tr><tr><td>第 27 次</td><td>2022-09-18</td><td>202209</td><td>第 36 次</td><td>2024-12-08</td><td>202412</td></tr><tr><td>第 28 次</td><td>2022-12-18</td><td>202212</td><td>第 37 次</td><td>2025-03-30</td><td>202503</td></tr><tr><td>第 29 次</td><td>2023-03-19</td><td>202303</td><td>第 38 次</td><td>2025-06-08</td><td>202506</td></tr><tr><td>第 30 次</td><td>2023-05-28</td><td>202305</td><td>第 39 次</td><td>2025-09-21</td><td>202509</td></tr><tr><td>第 31 次</td><td>2023-09-17</td><td>202309</td><td>第 40 次</td><td>2025-12-07</td><td>202512</td></tr><tr><td>第 32 次</td><td>2023-12-10</td><td>202312</td><td>第 41 次</td><td>2026-03-29</td><td>202603</td></tr><tr><td>第 33 次</td><td>2024-03-31</td><td>202403</td><td>第 42 次</td><td>2026-05-31</td><td>202605</td></tr><tr><td>第 17 次</td><td>2019-09-15</td><td>201909</td><td>第 43 次</td><td>2026-09-13</td><td>202609</td></tr><tr><td>第 18 次</td><td>2019-12-15</td><td>201912</td><td></td><td></td><td></td></tr></tbody></table><p>来源：<a href="https://www.cspro.org/cms/show.action?code=jumpchanneltemplate&siteid=100000&channelid=0000000103">CCF CSP 认证通知公告（列表页，翻页参数 pageNo&#x3D;2~7）</a></p><h4 id="7-1-3-分数到底值多少（200-300-400）"><a href="#7-1-3-分数到底值多少（200-300-400）" class="headerlink" title="7.1.3 分数到底值多少（200 &#x2F; 300 &#x2F; 400）"></a>7.1.3 分数到底值多少（200 &#x2F; 300 &#x2F; 400）</h4><ul><li><strong>200 分</strong>：CCF 官方口径 —— “每年 CSP 高分考生（200 分及以上）均可报名参加 CCSP 竞赛”。</li><li><strong>300 分</strong>：社区与高校普遍认可的”有用线”：多所高校将其用于保研加分、研究生复试机试折算（社区经验称北航、人大等可用 CSP 成绩抵夏令营机试，通常要求 300 分以上）。</li><li><strong>400 分以上 &#x2F; 满分</strong>：官方新闻通报的全国前列水平（第 30 次认证全国第四 450 分，全场仅 1 人满分）。</li><li>⚠️ <strong>专业级 CSP 没有官方”一等 &#x2F; 二等 &#x2F; 三等”合格线</strong>。等级名称规范只针对 CSP-J&#x2F;S（非专业级），且 CCF 2026 年公告明确 CSP-J&#x2F;S”不设置一等奖、二等奖、三等奖”。别把 CSP-J&#x2F;S 的等级和 CSP 专业级的分数混为一谈。</li></ul><p>来源：<a href="https://www.ccf.org.cn/CCF_BC/activities/csp/">CSP - 中国计算机学会</a> ｜ <a href="https://blog.csdn.net/Morishima04/article/details/143726288">CSP CCF认证介绍 &amp; 备考建议</a> ｜ <a href="https://www.noi.cn/xw/2026-09-09/930554.shtml">关于CCF CSP-J&#x2F;S认证等级名称规范的公告</a></p><h4 id="7-1-4-评测与提交细节"><a href="#7-1-4-评测与提交细节" class="headerlink" title="7.1.4 评测与提交细节"></a>7.1.4 评测与提交细节</h4><ul><li>时间 &#x2F; 空间限制：历年题面常见 1.0 s &#x2F; 256 MB（例：201312-1）。</li><li>评测机 C++ 标准：社区题解作者标注为 C++14（”评测机标准”）。本地用 C++17 写没问题，但<strong>避免使用 C++17 独有特性</strong>（结构化绑定、if constexpr、std::optional 等）——（待验证，请以考场实际编译参数为准）。</li><li>赛制：OI 制，无实时榜单、无罚时；提交后按通过的测试点给分（部分分）。</li><li>关于”多次提交取最高分”：社区广泛流传（”多次刷分取最高”），但 CCF 公开页面未检索到明确条文 ——（待验证）。考前请在官方模拟系统上实测该规则，规则确认前按 7.5.4 的保守策略提交。</li></ul><p>来源：<a href="https://blog.csdn.net/Morishima04/article/details/143726288">CCF-CSP认证介绍 &amp; 备考建议</a> ｜ <a href="https://blog.csdn.net/qq_45123552/article/details/135997410">CCF-CSP认证考试真题（含题解和c++代码）</a> ｜ <a href="https://blog.csdn.net/spadgerz/article/details/52673804">CCF-CSP认证知识要求</a></p><h3 id="7-2-CSP-J-S（非专业级）：第一轮与第二轮"><a href="#7-2-CSP-J-S（非专业级）：第一轮与第二轮" class="headerlink" title="7.2 CSP-J&#x2F;S（非专业级）：第一轮与第二轮"></a>7.2 CSP-J&#x2F;S（非专业级）：第一轮与第二轮</h3><table><thead><tr><th>项目</th><th>官方规定</th></tr></thead><tbody><tr><td>定位</td><td>面向社会非专业人士（以青少年为主）的<strong>非专业级</strong>认证，分 CSP-J（入门组）与 CSP-S（提高组）</td></tr><tr><td>阶段</td><td>分第一轮和第二轮；报名第一轮成绩优异者方可进入第二轮；两轮各设 J &#x2F; S 两组</td></tr><tr><td>形式</td><td>第一轮为集中笔试（经 CCF 批准可以机试方式认证）；第二轮为现场集中上机</td></tr><tr><td>第二轮题量</td><td>每次认证有 <strong>4 个题目</strong></td></tr><tr><td>报名资格</td><td>2026 年 9 月 1 日（不含）前须满 12 周岁，且须网上注册报名</td></tr><tr><td>认证点</td><td>第一轮认证点每点不少于 20 人；第二轮认证点每省一个</td></tr><tr><td>等级名称</td><td>不设一等奖、二等奖、三等奖（CCF 2026 年公告规范）</td></tr><tr><td>第一轮题型</td><td>单项选择 + 阅读程序 + 完善程序（来自社区对历年第一轮真题的解析，官方通知未列明分值 → 待验证）</td></tr></tbody></table><p>与专业级 CSP 的区别（别搞混）：</p><ul><li><strong>CSP 专业级</strong>：5 题 &#x2F; 500 分 &#x2F; 4 小时，面向大学生与社会人士，是保研、考研复试、企业内推的依据。</li><li><strong>CSP-J&#x2F;S</strong>：2 轮制 &#x2F; 第二轮 4 题，面向青少年，与 NOIP、NOI 体系衔接。</li></ul><p>来源：<a href="https://www.noi.cn/xw/2026-07-06/908155.shtml">CCF关于举办CSP-J&#x2F;S 2026的通知</a> ｜ <a href="https://www.noi.cn/xw/2026-09-09/930554.shtml">关于CCF CSP-J&#x2F;S认证等级名称规范的公告</a></p><h3 id="7-3-历年真题考点分布表"><a href="#7-3-历年真题考点分布表" class="headerlink" title="7.3 历年真题考点分布表"></a>7.3 历年真题考点分布表</h3><p>难度标记为按社区题解与题目定位给出的经验判断（易 &#x2F; 中 &#x2F; 难 &#x2F; 极难）。</p><h4 id="7-3-1-第-25-28-次（2022-年）"><a href="#7-3-1-第-25-28-次（2022-年）" class="headerlink" title="7.3.1 第 25 - 28 次（2022 年）"></a>7.3.1 第 25 - 28 次（2022 年）</h4><table><thead><tr><th>题号</th><th>题目名</th><th>核心考点</th><th>难度</th></tr></thead><tbody><tr><td>202203-1</td><td>未初始化警告</td><td>模拟 &#x2F; 哈希计数</td><td>易</td></tr><tr><td>202203-2</td><td>出行计划</td><td>差分 &#x2F; 前缀和</td><td>易</td></tr><tr><td>202203-3</td><td>计算资源调度器</td><td>大模拟 &#x2F; STL</td><td>中</td></tr><tr><td>202203-4</td><td>通信系统管理</td><td>数据结构综合</td><td>难</td></tr><tr><td>202203-5</td><td>博弈论与石子合并</td><td>博弈 &#x2F; 贪心 &#x2F; DP</td><td>极难</td></tr><tr><td>202206-1</td><td>归一化处理</td><td>模拟 &#x2F; 数学</td><td>易</td></tr><tr><td>202206-2</td><td>寻宝！大冒险！</td><td>哈希 &#x2F; 枚举</td><td>易</td></tr><tr><td>202206-3</td><td>角色授权</td><td>大模拟 &#x2F; 哈希 &#x2F; 集合</td><td>中</td></tr><tr><td>202206-4</td><td>光线追踪</td><td>计算几何 &#x2F; 数据结构</td><td>难</td></tr><tr><td>202206-5</td><td>PS无限版</td><td>数据结构综合</td><td>极难</td></tr><tr><td>202209-*</td><td>未检索到可靠来源</td><td>-</td><td>-</td></tr><tr><td>202212-1</td><td>现值计算</td><td>循环 &#x2F; 浮点</td><td>易</td></tr><tr><td>202212-2</td><td>训练计划</td><td>拓扑序 &#x2F; DP</td><td>易</td></tr><tr><td>202212-3</td><td>JPEG 解码</td><td>大模拟 &#x2F; 矩阵</td><td>中</td></tr><tr><td>202212-4</td><td>聚集方差</td><td>启发式合并 &#x2F; set</td><td>难</td></tr><tr><td>202212-5</td><td>星际网络</td><td>线段树建图 &#x2F; 高精度</td><td>极难</td></tr></tbody></table><p>来源：<a href="https://blog.csdn.net/qq_41823101/article/details/126153808">CCF-CSP历年真题大全附题解（CPP11）</a> ｜ <a href="https://blog.csdn.net/qq_45123552/article/details/135997410">CCF-CSP认证考试真题（含题解和c++代码）</a></p><h4 id="7-3-2-第-29-34-次（2023-年-2024-上半年）"><a href="#7-3-2-第-29-34-次（2023-年-2024-上半年）" class="headerlink" title="7.3.2 第 29 - 34 次（2023 年 - 2024 上半年）"></a>7.3.2 第 29 - 34 次（2023 年 - 2024 上半年）</h4><table><thead><tr><th>题号</th><th>题目名</th><th>核心考点</th><th>难度</th></tr></thead><tbody><tr><td>202303-1</td><td>田地丈量</td><td>循环 &#x2F; 矩形面积交</td><td>易</td></tr><tr><td>202303-2</td><td>垦田计划</td><td>二分答案</td><td>易</td></tr><tr><td>202303-3</td><td>LDAP</td><td>递归 &#x2F; bitset</td><td>中</td></tr><tr><td>202303-4</td><td>星际网络II</td><td>线段树 &#x2F; 离散化</td><td>难</td></tr><tr><td>202303-5</td><td>施肥</td><td>分治 &#x2F; 线段树 &#x2F; 树状数组</td><td>极难</td></tr><tr><td>202305-1</td><td>重复局面</td><td>STL（string、map）</td><td>易</td></tr><tr><td>202305-2</td><td>矩阵运算</td><td>矩阵乘法（运算顺序优化）</td><td>易</td></tr><tr><td>202305-3</td><td>解压缩</td><td>字符串处理 &#x2F; 位运算</td><td>中</td></tr><tr><td>202305-4</td><td>电力网络</td><td>图论建模 &#x2F; 暴力枚举</td><td>难</td></tr><tr><td>202305-5</td><td>闪耀巡航</td><td>最短路 &#x2F; 状压 DP</td><td>极难</td></tr><tr><td>202309-1</td><td>坐标变换（其一）</td><td>循环 &#x2F; 累加</td><td>易</td></tr><tr><td>202309-2</td><td>坐标变换（其二）</td><td>前缀积 &#x2F; 前缀和</td><td>易</td></tr><tr><td>202309-3</td><td>梯度求解</td><td>后缀表达式 &#x2F; 表达式求值</td><td>中</td></tr><tr><td>202309-4</td><td>阴阳龙</td><td>数据结构（set）</td><td>难</td></tr><tr><td>202309-5</td><td>阻击</td><td>动态 DP &#x2F; 树剖 &#x2F; 线段树</td><td>极难</td></tr><tr><td>202312-1</td><td>仓库规划</td><td>循环枚举</td><td>易</td></tr><tr><td>202312-2</td><td>因子化简</td><td>质因数分解</td><td>易</td></tr><tr><td>202312-3</td><td>树上搜索</td><td>模拟 &#x2F; DFS</td><td>中</td></tr><tr><td>202312-4</td><td>宝藏</td><td>分块 &#x2F; 前缀（矩阵乘法）</td><td>难</td></tr><tr><td>202312-5</td><td>彩色路径</td><td>状压 DP + 折半</td><td>极难</td></tr><tr><td>202403-1</td><td>词频统计</td><td>循环计数</td><td>易</td></tr><tr><td>202403-2</td><td>相似度计算</td><td>集合交并</td><td>易</td></tr><tr><td>202403-3</td><td>化学方程式配平</td><td>高斯消元</td><td>中</td></tr><tr><td>202403-4</td><td>十滴水</td><td>set&#x2F;map + 优先队列（模拟）</td><td>难</td></tr><tr><td>202403-5</td><td>文件夹合并</td><td>链表 &#x2F; DFS 序 &#x2F; 线段树</td><td>难</td></tr><tr><td>202406-1</td><td>矩阵重塑（其一）</td><td>循环 &#x2F; 数组</td><td>易</td></tr><tr><td>202406-2</td><td>矩阵重塑（其二）</td><td>矩阵 &#x2F; 循环</td><td>易</td></tr><tr><td>202406-3</td><td>文本分词</td><td>大模拟 &#x2F; STL &#x2F; 优先队列</td><td>中</td></tr><tr><td>202406-4</td><td>货物调度</td><td>贪心 &#x2F; 背包</td><td>难</td></tr><tr><td>202406-5</td><td>哥德尔机</td><td>未检索到明确考点标注</td><td>极难</td></tr></tbody></table><p>来源：<a href="https://blog.csdn.net/qq_45123552/article/details/135997410">CCF-CSP认证考试真题（含题解和c++代码）</a> ｜ <a href="https://blog.csdn.net/weixin_54867159/article/details/159417816">CCF CSP认证 第29次至第40次第一题A题代码</a></p><h4 id="7-3-3-第-35-40-次（2024-下半年-2025-年，社区整理，完整性有限）"><a href="#7-3-3-第-35-40-次（2024-下半年-2025-年，社区整理，完整性有限）" class="headerlink" title="7.3.3 第 35 - 40 次（2024 下半年 - 2025 年，社区整理，完整性有限）"></a>7.3.3 第 35 - 40 次（2024 下半年 - 2025 年，社区整理，完整性有限）</h4><table><thead><tr><th>题号</th><th>题目名</th><th>核心考点</th><th>难度</th></tr></thead><tbody><tr><td>202409-1</td><td>密码</td><td>字符串模拟 &#x2F; 分类讨论</td><td>易</td></tr><tr><td>202409-2</td><td>字符串变换</td><td>字符串 &#x2F; 哈希 &#x2F; 变换周期预处理</td><td>易-中</td></tr><tr><td>202409-3</td><td>补丁应用</td><td>字符串解析 &#x2F; 模拟</td><td>中</td></tr><tr><td>202409-4 &#x2F; 5</td><td>未检索到可靠来源</td><td>-</td><td>-</td></tr><tr><td>202412-1</td><td>移动</td><td>模拟（网格移动 &#x2F; 边界判断）</td><td>易</td></tr><tr><td>202412-2</td><td>梦境巡查</td><td>前缀和 + 后缀最值 &#x2F; 贪心</td><td>中</td></tr><tr><td>202412-3 &#x2F; 4 &#x2F; 5</td><td>未检索到可靠来源</td><td>-</td><td>-</td></tr><tr><td>202503-1</td><td>数值积分</td><td>循环求和</td><td>易</td></tr><tr><td>202503-2</td><td>机器人饲养指南</td><td>完全背包</td><td>易-中</td></tr><tr><td>202503-3</td><td>模板展开</td><td>字符串 &#x2F; 递归 + 记忆化（取模防溢出）</td><td>中-难</td></tr><tr><td>202503-4</td><td>集体锻炼</td><td>数学（gcd）</td><td>难</td></tr><tr><td>202503-5</td><td>未检索到可靠来源</td><td>-</td><td>-</td></tr><tr><td>202506-1</td><td>正态分布</td><td>数学 &#x2F; 浮点转整数查表</td><td>易</td></tr><tr><td>202506-2</td><td>机器人复健指南</td><td>BFS &#x2F; DFS 网格计数</td><td>易-中</td></tr><tr><td>202506-3</td><td>消息解码</td><td>大模拟 &#x2F; 位运算 &#x2F; 哈希</td><td>中</td></tr><tr><td>202506-4 &#x2F; 5</td><td>未检索到可靠来源</td><td>-</td><td>-</td></tr><tr><td>202509-1</td><td>蒙特卡洛</td><td>模拟 &#x2F; 浮点</td><td>易</td></tr><tr><td>202509-2</td><td>水印检查</td><td>二维差分 &#x2F; 阈值区间</td><td>中</td></tr><tr><td>202509-3</td><td>HTTP 头信息</td><td>大模拟 &#x2F; Huffman 树 &#x2F; 进制转换</td><td>中</td></tr><tr><td>202509-4 &#x2F; 5</td><td>未检索到可靠来源</td><td>-</td><td>-</td></tr><tr><td>202512-1</td><td>集合</td><td>模拟 &#x2F; 哈希</td><td>易</td></tr><tr><td>202512-2</td><td>数字变换</td><td>逆向查表优化</td><td>易-中</td></tr><tr><td>202512-3</td><td>图片解码</td><td>坐标映射 &#x2F; 懒旋转</td><td>中</td></tr><tr><td>202512-4</td><td>C形阵</td><td>暴力 + set 判重</td><td>难</td></tr><tr><td>202512-5</td><td>数据抢修</td><td>DFS 回溯 + 剪枝</td><td>难</td></tr></tbody></table><p>来源：<a href="https://blog.csdn.net/m0_74045028/article/details/161457771">第37次CCF计算机软件能力认证(CSP)＜题解＞</a> ｜ <a href="https://blog.csdn.net/weixin_54867159/article/details/159417816">CCF CSP认证 第29次至第40次第一题A题代码</a> ｜ <a href="https://blog.csdn.net/2501_92983269/article/details/155617780">CCF-CSP第38次认证第一题——正态分布</a></p><h4 id="7-3-4-更早场次（2019-2021，仅核实到部分题目）"><a href="#7-3-4-更早场次（2019-2021，仅核实到部分题目）" class="headerlink" title="7.3.4 更早场次（2019 - 2021，仅核实到部分题目）"></a>7.3.4 更早场次（2019 - 2021，仅核实到部分题目）</h4><table><thead><tr><th>场次</th><th>已核实的题目名</th><th>备注</th></tr></thead><tbody><tr><td>201909（第 17 次）</td><td>小明种苹果</td><td>序列处理</td></tr><tr><td>201912（第 18 次）</td><td>报数、回收站选址</td><td>模拟 &#x2F; 序列处理</td></tr><tr><td>202009</td><td>称检测点查询、风险人群筛查</td><td>来自检索摘要，未逐一打开原文（待验证）</td></tr><tr><td>202012</td><td>期末预测之安全指数、期末预测之最佳阈值、带配额的文件系统、食材运输、星际旅行</td><td>来自检索摘要（待验证）</td></tr><tr><td>202104（第 22 次）</td><td>灰度直方图、邻域均值</td><td>计数 &#x2F; 二维前缀和</td></tr></tbody></table><p>来源：<a href="https://blog.csdn.net/wu_xin1/article/details/100181379">CCF-CSP认证历年真题解（100分）</a> ｜ <a href="https://blog.csdn.net/qq_45123552/article/details/135997410">CCF-CSP认证考试真题（含题解和c++代码）</a></p><h3 id="7-4-题号与难度规律（基于-7-3-的真题统计）"><a href="#7-4-题号与难度规律（基于-7-3-的真题统计）" class="headerlink" title="7.4 题号与难度规律（基于 7.3 的真题统计）"></a>7.4 题号与难度规律（基于 7.3 的真题统计）</h3><p>统计口径：7.3 中已核实题名的第 25 ~ 40 次共 16 场。</p><table><thead><tr><th>题号</th><th>已出现的主要考点（场次数）</th><th>结论</th></tr></thead><tbody><tr><td>T1</td><td>循环 &#x2F; 枚举 &#x2F; 数组 8；字符串与模拟 3；简单数学与浮点 2</td><td>20 - 50 行，O(n) 或 O(n log n)，读题仔细就能满分</td></tr><tr><td>T2</td><td>前缀和 &#x2F; 差分 4；哈希与集合 2；二分答案 1；背包等基础 DP 1；数论 1；BFS &#x2F; 网格 1；拓扑序 1</td><td>一道”标准模板题”，套板子即可满分</td></tr><tr><td>T3</td><td>大模拟 &#x2F; 长题干字符串处理 11 &#x2F; 11 场</td><td>唯一稳定规律：<strong>T3 &#x3D; 大模拟</strong>，细节远重于算法</td></tr><tr><td>T4</td><td>线段树 &#x2F; 分块 &#x2F; set &#x2F; 优先队列等数据结构 5；图论 2；贪心 + 背包 1；数学 1</td><td>需要真算法，但暴力往往能拿 30 - 60 分</td></tr><tr><td>T5</td><td>状压 DP、动态 DP、树链剖分、线段树建图、搜索剪枝</td><td>全场最难，目标是 20 - 50 分部分分</td></tr></tbody></table><p>由此得到的五条可操作结论：</p><ol><li><strong>T1 + T2 &#x3D; 200 分是保底线</strong>。这两题几乎不涉及高级算法，丢分只可能丢在边界与格式上。</li><li><strong>T3 决定 300 分线</strong>。16 场里有 11 场 T3 是大模拟 &#x2F; 解析类，没有一场考高级算法。它考的是”把题读懂 + 细节不出 bug”。</li><li><strong>T4 是 400 分线的分水岭</strong>。常见组合是”线段树 &#x2F; 树状数组 &#x2F; 平衡树 + 离散化”或”图上 DP &#x2F; 最短路”。</li><li><strong>T5 的正解通常超纲</strong>（动态 DP、树链剖分、折半状压），把目标定为”暴力 + 特殊性质子任务”更划算。</li><li>**T3 的核心形态是”字符串 + 层次结构”**：表达式求值（202309-3 梯度求解、202503-3 模板展开）、编解码（202305-3 解压缩、202212-3 JPEG 解码、202506-3 消息解码、202509-3 HTTP 头）、文件系统与权限（202012-3 带配额的文件系统、202206-3 角色授权）。</li></ol><h3 id="7-5-分数策略与时间分配"><a href="#7-5-分数策略与时间分配" class="headerlink" title="7.5 分数策略与时间分配"></a>7.5 分数策略与时间分配</h3><h4 id="7-5-1-240-分钟时间预算模板"><a href="#7-5-1-240-分钟时间预算模板" class="headerlink" title="7.5.1 240 分钟时间预算模板"></a>7.5.1 240 分钟时间预算模板</h4><table><thead><tr><th>时段</th><th>分钟</th><th>任务</th><th>目标分</th></tr></thead><tbody><tr><td>0:00 - 0:10</td><td>10</td><td>通读 5 题，圈出数据范围与”评测用例规模与约定”，判断 T3 题干长度、T4&#x2F;T5 可做性</td><td>-</td></tr><tr><td>0:10 - 0:35</td><td>25</td><td>T1 正解 + 自造边界（0、1、极值、重复）</td><td>100</td></tr><tr><td>0:35 - 1:15</td><td>40</td><td>T2 正解</td><td>100</td></tr><tr><td>1:15 - 2:20</td><td>65</td><td>T3 大模拟：先搭”数据结构 + 输入解析”，跑通样例再逐条补规则</td><td>60 - 100</td></tr><tr><td>2:20 - 3:20</td><td>60</td><td>T4：先写暴力 &#x2F; 子任务档保证有分，再想正解</td><td>30 - 100</td></tr><tr><td>3:20 - 3:45</td><td>25</td><td>T5：暴力 + 特殊性质子任务</td><td>20 - 40</td></tr><tr><td>3:45 - 4:00</td><td>15</td><td>回查：重读题面限制、检查越界 &#x2F; 多测清空、确认语言与题目对应</td><td>-</td></tr></tbody></table><blockquote><p>⚠️ 这张表的核心是 <strong>T3 不允许超过 65 分钟</strong>。最常见的崩盘方式是 T3 卡 2 小时 → T4 &#x2F; T5 全丢，总分停在 200。T3 写不完就先交一个”能过样例 + 部分子任务”的版本走人。</p></blockquote><h4 id="7-5-2-分段程序（一题多档，按子任务拿分）"><a href="#7-5-2-分段程序（一题多档，按子任务拿分）" class="headerlink" title="7.5.2 分段程序（一题多档，按子任务拿分）"></a>7.5.2 分段程序（一题多档，按子任务拿分）</h4><p>CSP 每题都给出”评测用例规模与约定”，按数据范围分档写多个解法，是最稳的得分方式：同一份代码内部按 n 的大小选择算法。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">typedef</span> <span class="type">long</span> <span class="type">long</span> ll;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> N = <span class="number">100005</span>;</span><br><span class="line"><span class="type">int</span> n, m;</span><br><span class="line">ll a[N], pre[N];</span><br><span class="line"><span class="comment">// 档 1：n &lt;= 2000 暴力 O(n^2) —— 通常能吃到 30~60 分</span></span><br><span class="line"><span class="function">ll <span class="title">solveBrute</span><span class="params">()</span></span>&#123;</span><br><span class="line">    ll r = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> j = i; j &lt;= n; j++)&#123;</span><br><span class="line">            ll s = pre[j] - pre[i<span class="number">-1</span>];</span><br><span class="line">            <span class="keyword">if</span> (s % m == <span class="number">0</span>) r++;</span><br><span class="line">        &#125;</span><br><span class="line">    <span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 档 2：正解 O(n log n)</span></span><br><span class="line"><span class="function">ll <span class="title">solveFull</span><span class="params">()</span></span>&#123;</span><br><span class="line">    map&lt;ll,<span class="type">int</span>&gt; c; c[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line">    ll r = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)&#123;</span><br><span class="line">        ll k = ((pre[i] % m) + m) % m;</span><br><span class="line">        r += c[k]; c[k]++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%d%d&quot;</span>, &amp;n, &amp;m);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++)&#123; <span class="built_in">scanf</span>(<span class="string">&quot;%lld&quot;</span>, &amp;a[i]); pre[i] = pre[i<span class="number">-1</span>] + a[i]; &#125;</span><br><span class="line">    <span class="keyword">if</span> (n &lt;= <span class="number">2000</span>) <span class="built_in">printf</span>(<span class="string">&quot;%lld\n&quot;</span>, <span class="built_in">solveBrute</span>());</span><br><span class="line">    <span class="keyword">else</span>           <span class="built_in">printf</span>(<span class="string">&quot;%lld\n&quot;</span>, <span class="built_in">solveFull</span>());</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>⚠️ 提交前把分档阈值调到与题面子任务表一致；不要凭感觉写 n &lt;&#x3D; 2000。</p></blockquote><h4 id="7-5-3-对拍（写完”正解”后用暴力验证）"><a href="#7-5-3-对拍（写完”正解”后用暴力验证）" class="headerlink" title="7.5.3 对拍（写完”正解”后用暴力验证）"></a>7.5.3 对拍（写完”正解”后用暴力验证）</h4><p>三个文件：数据生成器、你的程序、暴力程序，然后循环比对。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// gen.cpp —— 数据生成器：g++ -O2 -o gen gen.cpp</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span>** argv)</span></span>&#123;</span><br><span class="line">    <span class="built_in">srand</span>(<span class="built_in">atoi</span>(argv[<span class="number">1</span>]));</span><br><span class="line">    <span class="type">int</span> n = <span class="built_in">rand</span>() % <span class="number">10</span> + <span class="number">1</span>, m = <span class="built_in">rand</span>() % <span class="number">5</span> + <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%d %d\n&quot;</span>, n, m);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; i++) <span class="built_in">printf</span>(<span class="string">&quot;%d &quot;</span>, <span class="built_in">rand</span>() % <span class="number">20</span> - <span class="number">10</span>);</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// check.cpp —— 对拍驱动：g++ -O2 -o check check.cpp（Linux 下把 .exe 去掉）</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> t = <span class="number">1</span>; t &lt;= <span class="number">1000</span>; t++)&#123;</span><br><span class="line">        <span class="built_in">system</span>(<span class="string">&quot;python3 gen.py &gt; in.txt&quot;</span>);   <span class="comment">// 或 ./gen</span></span><br><span class="line">        <span class="built_in">system</span>(<span class="string">&quot;./a &lt; in.txt &gt; out1.txt&quot;</span>);   <span class="comment">// 你的程序</span></span><br><span class="line">        <span class="built_in">system</span>(<span class="string">&quot;./b &lt; in.txt &gt; out2.txt&quot;</span>);   <span class="comment">// 暴力</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">system</span>(<span class="string">&quot;diff out1.txt out2.txt &gt; /dev/null&quot;</span>))&#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;WA on test %d\n&quot;</span>, t);</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;all ok&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>考场提示：CSP 考场多为 NOI Linux 环境，python3 与 diff 一般可用；若没有 python3，就用 gen 可执行文件生成数据。</p><h4 id="7-5-4-提交策略"><a href="#7-5-4-提交策略" class="headerlink" title="7.5.4 提交策略"></a>7.5.4 提交策略</h4><blockquote><p>⚠️ “多次提交取最高分”是社区说法，CCF 公开页面未检索到条文（待验证）。<strong>进考场先在模拟系统上用一道题实测</strong>：先交一版明显错解，再交一版正解，看成绩页最终显示哪个分数。在规则确认之前，按下面保守执行。</p></blockquote><ol><li><strong>只交有把握的版本</strong>：若规则是”以最后一次提交为准”，交一版更差的代码等于直接掉分。</li><li><strong>每题先保证有分</strong>：T3 &#x2F; T4 &#x2F; T5 先写”能过样例 + 小数据”的暴力，本地验证无误后再交。</li><li><strong>利用部分分</strong>：CSP 按测试点给分，一个只处理小数据的暴力常能拿 30 - 60 分。</li><li><strong>不做无编译把握的提交</strong>：本地 g++ -O2 -std&#x3D;c++17 编一遍再交。</li><li><strong>确认题目与提交框的对应关系</strong>：交错了题就是 0 分。</li></ol><h4 id="7-5-5-什么时候放弃正解"><a href="#7-5-5-什么时候放弃正解" class="headerlink" title="7.5.5 什么时候放弃正解"></a>7.5.5 什么时候放弃正解</h4><ul><li>T5 读完 10 分钟还没有明确算法 → 放弃正解，转暴力 + 子任务。</li><li>T4 卡 40 分钟仍无思路 → 交暴力走人，回头复查 T1 - T3。</li><li>T3 到 65 分钟 → 交当前版本，转 T4。</li><li>任何一题写完样例却过不了超过 15 分钟 → 换暴力重写（大模拟常见的”越改越乱”）。</li></ul><h3 id="7-6-高频必备算法清单（按真题实际出现频次分档）"><a href="#7-6-高频必备算法清单（按真题实际出现频次分档）" class="headerlink" title="7.6 高频必备算法清单（按真题实际出现频次分档）"></a>7.6 高频必备算法清单（按真题实际出现频次分档）</h3><h4 id="7-6-1-必须掌握（不掌握就拿不到-300-分）"><a href="#7-6-1-必须掌握（不掌握就拿不到-300-分）" class="headerlink" title="7.6.1 必须掌握（不掌握就拿不到 300 分）"></a>7.6.1 必须掌握（不掌握就拿不到 300 分）</h4><table><thead><tr><th>算法 &#x2F; 技巧</th><th>对应真题</th></tr></thead><tbody><tr><td>模拟 + 边界处理</td><td>全部 T1；202409-1 密码、202412-1 移动</td></tr><tr><td>字符串解析（stringstream、getline、split、进制与位运算）</td><td>202305-3 解压缩、202506-3 消息解码、202509-3 HTTP 头信息、202409-3 补丁应用</td></tr><tr><td>STL：vector &#x2F; map &#x2F; set &#x2F; unordered_map &#x2F; priority_queue</td><td>202305-1 重复局面、202312-3 树上搜索、202403-2 相似度计算</td></tr><tr><td>排序、二分（lower_bound &#x2F; 二分答案）</td><td>202303-2 垦田计划</td></tr><tr><td>前缀和与差分（一维 &amp; 二维）</td><td>202309-2 坐标变换（其二）、202412-2 梦境巡查、202509-2 水印检查</td></tr><tr><td>递归 &#x2F; DFS &#x2F; BFS（含网格搜索）</td><td>202506-2 机器人复健指南、202312-3 树上搜索</td></tr><tr><td>基础 DP（线性、背包）</td><td>202503-2 机器人饲养指南（完全背包）</td></tr><tr><td>简单数论（质因数分解、gcd、快速幂、取模）</td><td>202312-2 因子化简、202503-4 集体锻炼</td></tr><tr><td>栈 &#x2F; 队列 &#x2F; 表达式求值（后缀表达式）</td><td>202309-3 梯度求解</td></tr><tr><td>矩阵操作</td><td>202305-2 矩阵运算、202406-1&#x2F;2 矩阵重塑、202212-3 JPEG 解码</td></tr></tbody></table><h4 id="7-6-2-应当掌握（冲-400-分）"><a href="#7-6-2-应当掌握（冲-400-分）" class="headerlink" title="7.6.2 应当掌握（冲 400 分）"></a>7.6.2 应当掌握（冲 400 分）</h4><table><thead><tr><th>算法 &#x2F; 技巧</th><th>对应真题</th></tr></thead><tbody><tr><td>树状数组 &#x2F; 线段树（区间和、区间最值、懒标记）</td><td>202303-4 星际网络II、202403-5 文件夹合并</td></tr><tr><td>离散化</td><td>202303-4 星际网络II</td></tr><tr><td>并查集</td><td>历年 T4 高频（社区统计）</td></tr><tr><td>图论：最短路（Dijkstra）、拓扑排序、建图技巧</td><td>202212-2 训练计划（拓扑序）、202305-5 闪耀巡航</td></tr><tr><td>高斯消元</td><td>202403-3 化学方程式配平</td></tr><tr><td>贪心 + 排序</td><td>202406-4 货物调度</td></tr><tr><td>分块 &#x2F; 前缀技巧</td><td>202312-4 宝藏</td></tr><tr><td>启发式合并 &#x2F; set 维护有序序列</td><td>202212-4 聚集方差</td></tr><tr><td>位运算 + bitset</td><td>202303-3 LDAP</td></tr><tr><td>记忆化搜索（防指数级重复展开）</td><td>202503-3 模板展开</td></tr><tr><td>树形 DP</td><td>202309-5 阻击（48 分档）</td></tr></tbody></table><h4 id="7-6-3-加分项（基本只在-T5-满分档出现）"><a href="#7-6-3-加分项（基本只在-T5-满分档出现）" class="headerlink" title="7.6.3 加分项（基本只在 T5 满分档出现）"></a>7.6.3 加分项（基本只在 T5 满分档出现）</h4><table><thead><tr><th>算法 &#x2F; 技巧</th><th>对应真题</th></tr></thead><tbody><tr><td>状压 DP + 折半枚举（meet in the middle）</td><td>202312-5 彩色路径（100 分档）</td></tr><tr><td>动态 DP + 树链剖分 + 广义矩阵乘法</td><td>202309-5 阻击（100 分档）</td></tr><tr><td>线段树优化建图</td><td>202212-5 星际网络</td></tr><tr><td>分治 + 线段树 &#x2F; 树状数组</td><td>202303-5 施肥</td></tr><tr><td>计算几何（射线追踪、坐标变换）</td><td>202206-4 光线追踪</td></tr><tr><td>搜索 + 剪枝（DFS 回溯）</td><td>202512-5 数据抢修</td></tr><tr><td>链表 + DFS 序 + 线段树</td><td>202403-5 文件夹合并</td></tr></tbody></table><p>来源：<a href="https://blog.csdn.net/qq_45123552/article/details/135997410">CCF-CSP认证考试真题（含题解和c++代码）</a> ｜ <a href="https://blog.csdn.net/m0_74045028/article/details/161457771">第37次CCF计算机软件能力认证(CSP)＜题解＞</a> ｜ <a href="https://blog.csdn.net/weixin_54867159/article/details/159417816">CCF CSP认证 第29次至第40次第一题A题代码</a></p><h3 id="7-7-真题原题与题单链接（均已实测可访问）"><a href="#7-7-真题原题与题单链接（均已实测可访问）" class="headerlink" title="7.7 真题原题与题单链接（均已实测可访问）"></a>7.7 真题原题与题单链接（均已实测可访问）</h3><table><thead><tr><th>资源</th><th>链接</th><th>说明</th></tr></thead><tbody><tr><td>CCF CSP 官网</td><td><a href="https://www.cspro.org/">https://www.cspro.org/</a></td><td>认证报名、成绩查询、认证指南、常见问题</td></tr><tr><td>官方模拟考试系统（往期真题）</td><td><a href="https://sim.csp.thusaac.com/">https://sim.csp.thusaac.com/</a></td><td>官网”往期真题 &#x2F; 模拟考试”入口；报名后凭兑换码兑换 1 套真题</td></tr><tr><td>CCF 数字图书馆 · CSP 真题解析</td><td><a href="https://dl.ccf.org.cn/albumList/albumSecondary.html?xl=CSP">https://dl.ccf.org.cn/albumList/albumSecondary.html?xl=CSP</a></td><td>官方真题解析资料专辑</td></tr><tr><td>CSP-J&#x2F;S 官方通知（NOI 官网）</td><td><a href="https://www.noi.cn/xw/2026-07-06/908155.shtml">https://www.noi.cn/xw/2026-07-06/908155.shtml</a></td><td>第一轮 &#x2F; 第二轮规则原文</td></tr><tr><td>CSP-J&#x2F;S 等级名称规范公告</td><td><a href="https://www.noi.cn/xw/2026-09-09/930554.shtml">https://www.noi.cn/xw/2026-09-09/930554.shtml</a></td><td>明确不设一二三等奖</td></tr><tr><td>OI-Wiki（模板总站）</td><td><a href="https://oi-wiki.org/">https://oi-wiki.org/</a></td><td>各类算法模板与复杂度说明</td></tr><tr><td>洛谷 · CSP-J 真题（示例）</td><td><a href="https://www.luogu.com.cn/problem/P11227">https://www.luogu.com.cn/problem/P11227</a></td><td>P11227 [CSP-J 2024] 扑克牌；P14357 拼数、P14358 座位、P14360 多边形为 CSP-J 2025</td></tr><tr><td>洛谷 · CSP-J 2024 真题（示例）</td><td><a href="https://www.luogu.com.cn/problem/P11228">https://www.luogu.com.cn/problem/P11228</a></td><td>P11228 [CSP-J 2024] 地图探险</td></tr></tbody></table><blockquote><p>⚠️ 洛谷的题单页（&#x2F;training&#x2F;…）需要登录 Cookie，直接访问会 401；建议用站内搜索”CCF”或”CSP”找题单。<strong>专业级 CSP 真题主要沉淀在 CCF 官方模拟系统与 CCF 数字图书馆</strong>，洛谷上以 CSP-J&#x2F;S 真题为主，别把两者当成同一批题。</p></blockquote><p>来源：<a href="https://www.ccf.org.cn/CCF_BC/activities/csp/">CSP - 中国计算机学会</a> ｜ <a href="https://www.noi.cn/xw/2026-07-06/908155.shtml">CCF关于举办CSP-J&#x2F;S 2026的通知</a> ｜ <a href="https://www.luogu.com.cn/problem/P11227">洛谷 P11227 [CSP-J 2024] 扑克牌</a></p><h3 id="7-8-考场易错点清单"><a href="#7-8-考场易错点清单" class="headerlink" title="7.8 考场易错点清单"></a>7.8 考场易错点清单</h3><blockquote><p>⚠️ 多测不清空：T2 &#x2F; T3 常有多组数据，全局数组与容器每次都要清空。<br>⚠️ cin 后接 getline：cin &gt;&gt; n 之后必须 ignore 或再 getline 一次吃掉换行，否则会读到空行（202503-3 模板展开的真实坑）。<br>⚠️ 先看”评测用例规模与约定”：子任务表就是官方给的送分指南，照着它写分档。<br>⚠️ 递归深度：CSP 数据里常出现长度 1e5 的链，DFS 递归会爆栈，改非递归或手写栈。<br>⚠️ 大模拟不要过早优化：先把逻辑写对，再考虑常数与复杂度。<br>⚠️ 输出格式：行末空格、多余换行、浮点保留位数都可能判错。<br>⚠️ 时间 1 s、内存 256 MB 是常态：vector 反复扩容、map 大量插入都可能被卡。<br>⚠️ 取模题：长度或计数要全程取模（202503-3 的 100 分做法就是只存长度并对 1e9+7 取模）。</p></blockquote>]]>
      </content:encoded>
    </item>
    <item>
      <title>Vibe Coding 入门指南</title>
      <link>https://ff66ccff.github.io/2026/09/08/%E9%80%9F%E6%88%90%E8%B7%AF%E7%BA%BF/</link>
      <description>
        <![CDATA[<h1 id="Vibe-Coding-入门指南"><a href="#Vibe-Coding-入门指南" class="headerlink" title="Vibe Coding 入门指南"></a>Vibe Coding 入门指南</h1><hr>
<h2]]>
      </description>
      <author>ff66ccff</author>
      <category domain="https://ff66ccff.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      <category domain="https://ff66ccff.github.io/tags/Vibe-Coding/">Vibe Coding</category>
      <category domain="https://ff66ccff.github.io/tags/AI/">AI</category>
      <pubDate>Tue, 08 Sep 2026 06:35:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="Vibe-Coding-入门指南"><a href="#Vibe-Coding-入门指南" class="headerlink" title="Vibe Coding 入门指南"></a>Vibe Coding 入门指南</h1><hr><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ol><li><a href="#%E4%B8%80%E4%BB%80%E4%B9%88%E6%98%AF-vibe-coding">什么是 Vibe Coding？</a></li><li><a href="#%E4%BA%8C%E5%B7%A5%E5%85%B7%E9%80%89%E5%9E%8Bvs-code-%E4%B8%8E-harness">工具选型：VS Code 与 Harness</a></li><li><a href="#%E4%B8%897-%E5%A4%A9%E6%9E%81%E7%AE%80%E8%B7%AF%E7%BA%BF">7 天极简路线</a><ul><li><a href="#day-1%E5%B7%A5%E5%85%B7%E5%9F%BA%E5%BA%A7%E4%B8%8E%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86">Day 1：工具基座与版本管理</a></li><li><a href="#day-2cs-%E6%9C%AC%E7%A7%91%E5%A4%9A%E8%AF%AD%E8%A8%80%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E4%B8%8E-git-%E7%BA%AA%E5%BE%8B">Day 2：CS 本科多语言开发环境与 Git 纪律</a></li><li><a href="#day-3agent-harness-%E5%B0%B1%E4%BD%8D%E4%B8%8E%E8%A7%84%E5%88%92%E5%B7%A5%E4%BD%9C%E6%B5%81">Day 3：Agent Harness 就位与规划工作流</a></li><li><a href="#day-4%E4%BB%A3%E7%A0%81%E8%B0%83%E8%AF%95%E4%B8%8E%E5%8F%8C-harness-%E5%8D%8F%E4%BD%9C">Day 4：代码调试与双 Harness 协作</a></li><li><a href="#day-5%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98%E4%B8%80%E5%85%A8%E6%A0%88-ddl-%E7%AE%A1%E7%90%86%E5%99%A8fastapi--sqlite--%E5%89%8D%E7%AB%AF">Day 5：项目实战一：全栈 DDL 管理器（FastAPI + SQLite + 前端）</a></li><li><a href="#day-6%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98%E4%BA%8Cnodejs-%E5%B7%A5%E7%A8%8B%E5%8C%96-canvas-%E6%B8%B8%E6%88%8F%E4%B8%8E%E5%85%AC%E7%BD%91%E9%83%A8%E7%BD%B2">Day 6：项目实战二：Node.js 工程化 Canvas 游戏与公网部署</a></li><li><a href="#day-7%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93%E4%B8%8E%E8%AE%A1%E7%AE%97%E6%9C%BA%E4%B8%93%E4%B8%9A%E8%BF%9B%E9%98%B6">Day 7：复盘总结与计算机专业进阶</a></li></ul></li><li><a href="#%E5%9B%9B%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E4%B8%8E%E5%AE%9E%E7%94%A8%E6%8A%80%E5%B7%A7">常见问题与实用技巧</a></li><li><a href="#%E9%99%84%E5%BD%95-a%E5%8F%AF%E9%80%89%E9%AB%98%E7%BA%A7%E9%85%8D%E7%BD%AE%E4%B8%8E%E7%AC%AC%E4%B8%89%E6%96%B9%E6%A8%A1%E5%9E%8B%E6%8E%A5%E5%85%A5cc-switch-%E4%B8%8E-api-%E6%96%B9%E8%A8%80">附录 A：可选高级配置与第三方模型接入（CC Switch 与 API 方言）</a></li><li><a href="#%E9%99%84%E5%BD%95-b%E7%BA%AF%E5%91%BD%E4%BB%A4%E8%A1%8C-git-%E5%85%A5%E9%97%A8">附录 B：纯命令行 Git 入门</a></li></ol><hr><h2 id="一、什么是-Vibe-Coding？"><a href="#一、什么是-Vibe-Coding？" class="headerlink" title="一、什么是 Vibe Coding？"></a>一、什么是 Vibe Coding？</h2><p>2025 年初，OpenAI 联合创始人 Andrej Karpathy 提出了 <strong>Vibe Coding</strong> 的概念：</p><blockquote><p><em>“我只是看着屏幕、说说需求、运行代码、复制粘贴，它大部分时候就能工作……我已经完全沉浸在 Vibe Coding 模式中了。”</em></p></blockquote><h3 id="核心转变：从手写语法到主导设计"><a href="#核心转变：从手写语法到主导设计" class="headerlink" title="核心转变：从手写语法到主导设计"></a>核心转变：从手写语法到主导设计</h3><ul><li><strong>传统编程</strong>：开发者需要熟记语言语法、处理类型与内存细节，花费大量时间手动敲入每一行代码。</li><li><strong>Vibe Coding</strong>：AI 负责具体的代码实现与语法细节；开发者聚焦于<strong>需求拆解、架构设计、流程把控与功能测试</strong>。</li><li><strong>特别说明</strong>：Vibe Coding 绝非“不管代码、随缘生成、盲目接受”。<strong>本指南教的是有工程纪律的版本</strong>——你掌控需求拆解、架构设计、审查 Diff（改动对比）、分步验证与版本管理，AI 在你的严密把控下高效交付。</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">graph LR</span><br><span class="line">    A[&quot;明确需求 (PROJECT.md)&quot;] --&gt; B[&quot;Harness 规划并修改代码&quot;]</span><br><span class="line">    B --&gt; C[&quot;运行测试效果&quot;]</span><br><span class="line">    C --&gt;|发现问题| D[&quot;提供报错信息，调整提示词&quot;]</span><br><span class="line">    D --&gt; B</span><br><span class="line">    C --&gt;|验证通过| E[&quot;Git 提交版本，进入下一步&quot;]</span><br></pre></td></tr></table></figure><h3 id="边界：AI-不能替你做什么"><a href="#边界：AI-不能替你做什么" class="headerlink" title="边界：AI 不能替你做什么"></a>边界：AI 不能替你做什么</h3><p>AI 能替你完成基础编码，但无法替代计算机基础思维。模块设计、数据流向、边界条件以及系统底层逻辑，依然由你掌控。对数据结构、操作系统和网络原理理解越清晰，你给出的提示词就越精准，诊断复杂代码问题的效率也就越高。</p><hr><h2 id="二、工具选型：VS-Code-与-Harness"><a href="#二、工具选型：VS-Code-与-Harness" class="headerlink" title="二、工具选型：VS Code 与 Harness"></a>二、工具选型：VS Code 与 Harness</h2><p>在工具架构上，推荐采用 <strong>“通用代码编辑器（VS Code）+ 专用编程助手（Harness）”</strong> 的协作模式。</p><h3 id="1-为什么是-VS-Code-Harness？"><a href="#1-为什么是-VS-Code-Harness？" class="headerlink" title="1. 为什么是 VS Code + Harness？"></a>1. 为什么是 VS Code + Harness？</h3><ul><li><strong>VS Code（代码与项目基座）</strong>：业界最通用、生态最大的现代化代码编辑器，也是大学期间学习 Python、Java、C&#x2F;C++、前端及各种系统开发的主流工具，拥有成熟庞大的扩展生态（语法高亮、实时预览、代码格式化、终端集成等）。</li><li><strong>Harness（AI 编程助手）</strong>：指<strong>能自己读写工程文件、调用终端跑命令的专用工具</strong>（Agent Harness 是业内叫法）。它具备整个项目的全局视野，能自主进行任务规划、跨文件检索与批量重构。</li><li><strong>协同方式</strong>：两者共同打开<strong>同一个本地项目文件夹</strong>。你在 Harness 中表达需求并指挥任务，在 VS Code 中实时查看代码改动、组织文件结构并进行测试与微调。</li></ul><h3 id="2-主流-Harness-对比与推荐"><a href="#2-主流-Harness-对比与推荐" class="headerlink" title="2. 主流 Harness 对比与推荐"></a>2. 主流 Harness 对比与推荐</h3><p>每个 Harness 在使用自家的官方模型时体验最好（Codex 配 GPT，Claude Code 配 Claude，DeepSeek Harness 配 DeepSeek，ZCode 配 GLM，Kimi Code 配 Kimi）。</p><p>以下主流工具<strong>按推荐顺序排列</strong>：</p><table><thead><tr><th align="center">推荐顺序</th><th>Harness</th><th>对应主力模型</th><th>特点与优势</th></tr></thead><tbody><tr><td align="center">1</td><td><strong>Codex App</strong></td><td>GPT 系列 (OpenAI)</td><td>OpenAI 官方桌面端编码工具，深度集成 GPT 编程能力；需国际网络与订阅</td></tr><tr><td align="center">2</td><td><strong>Claude Code Desktop</strong></td><td>Claude 系列 (Anthropic)</td><td>官方桌面端多会话多任务应用，代码架构理解与生成能力极强；需国际网络与订阅</td></tr><tr><td align="center">3</td><td><strong>DeepSeek Harness</strong></td><td>DeepSeek 系列</td><td>DeepSeek 官方开源的插件化框架，架构灵活，调用成本极低；依赖 Node.js 环境运行</td></tr><tr><td align="center">4</td><td><strong>ZCode</strong></td><td>GLM 系列 (Z.ai &#x2F; 智谱)</td><td>官方 ADE 独立环境，国内直连免代理，支持微信&#x2F;支付宝，集成文件管理与终端</td></tr><tr><td align="center">5</td><td><strong>Kimi Code</strong></td><td>Kimi 系列 (Moonshot)</td><td>官方编码助手，长上下文理解能力出色，国内直连稳定</td></tr></tbody></table><blockquote><p><strong>选型策略</strong>：</p><ul><li><strong>遵循“先环境、后 Harness”的顺序</strong>：切勿在未安装语言环境时提前装 Harness。Day 1 准备好网络、VS Code 与 Git 基座；Day 2 安装好包括 Node.js 在内的多语言开发环境；Day 3 再正式配置 Harness（例如 DeepSeek Harness 官方明确要求 Node.js 运行时，提前配好环境才能避免运行依赖报错）。</li><li><strong>先选 1 款作为主力</strong>：若具备国际网络与订阅条件，优先选择 <strong>Codex App</strong> 或 <strong>Claude Code Desktop</strong>；若无外币卡或处于国内网络环境，建议选择免代理直连的 <strong>DeepSeek Harness</strong>、<strong>ZCode</strong> 或 <strong>Kimi Code</strong>（<em>若想用 Claude Code &#x2F; Codex 的工作流但没有官方订阅条件，可参阅<a href="#%E9%99%84%E5%BD%95-a%E5%8F%AF%E9%80%89%E9%AB%98%E7%BA%A7%E9%85%8D%E7%BD%AE%E4%B8%8E%E7%AC%AC%E4%B8%89%E6%96%B9%E6%A8%A1%E5%9E%8B%E6%8E%A5%E5%85%A5cc-switch-%E4%B8%8E-api-%E6%96%B9%E8%A8%80">附录 A</a>接入国产模型 API</em>）。</li><li><strong>Day 4 调试日再配置第 2 款作为备用</strong>：避免初期配置过载，同时在后续遇到疑难 Bug 时拥有备用模型支持。</li></ul></blockquote><h3 id="3-版本管理辅助工具"><a href="#3-版本管理辅助工具" class="headerlink" title="3. 版本管理辅助工具"></a>3. 版本管理辅助工具</h3><ol><li><strong>GitHub 账号</strong>：代码托管与展示平台。后续可申请 GitHub Student Developer Pack 权益。</li><li><strong>GitHub Desktop</strong>：GitHub 官方图形化客户端。通过可视化界面完成仓库同步与版本提交，无需初期记忆复杂的 Git 命令行。</li></ol><hr><h2 id="三、7-天极简路线"><a href="#三、7-天极简路线" class="headerlink" title="三、7 天极简路线"></a>三、7 天极简路线</h2><h3 id="Day-1：工具基座与版本管理"><a href="#Day-1：工具基座与版本管理" class="headerlink" title="Day 1：工具基座与版本管理"></a>Day 1：工具基座与版本管理</h3><p><strong>目标</strong>：完成网络准备，安装并配置专业代码编辑器 VS Code 与 GitHub Desktop，跑通第一个本地网页预览。</p><h4 id="1-网络准备（关键前提）"><a href="#1-网络准备（关键前提）" class="headerlink" title="1. 网络准备（关键前提）"></a>1. 网络准备（关键前提）</h4><p>Codex App、Claude Code Desktop 以及 VS Code 插件市场等工具需要顺畅的网络环境。</p><ul><li>前往 GitHub Releases 下载图形化代理客户端（如 <a href="https://github.com/mihomo-party-org/clash-party/releases">Clash Party</a>、<a href="https://github.com/clash-verge-rev/clash-verge-rev/releases/">Clash Verge Rev</a> 或 <a href="https://github.com/chen08209/FlClash/releases/">FlClash</a> 任选一款）。若下载缓慢，用手机热点或请同学发安装包。</li><li>导入你的服务订阅，开启“系统代理”。</li><li>在浏览器中测试验证，确保能正常打开 <code>github.com</code> 与 <code>marketplace.visualstudio.com</code>（GitHub Desktop 和 VS Code 插件市场是今天真正要用的；也可访问 <code>youtube.com</code> 作为附加验证）。</li><li><em>提示</em>：若使用国产模型（如 DeepSeek、智谱、Kimi），其官方 API 域名建议在代理软件中设置为“直连”，不走代理连接反而更稳定流畅。</li></ul><h4 id="2-安装-VS-Code"><a href="#2-安装-VS-Code" class="headerlink" title="2. 安装 VS Code"></a>2. 安装 VS Code</h4><p>VS Code 是本周乃至整个大学专业课的“操作台”：查看代码、管理项目文件、运行终端命令都在这里完成。整个过程约 10~15 分钟，请按顺序操作。</p><p><strong>(1) 下载安装包</strong></p><ul><li>打开 <a href="https://code.visualstudio.com/">code.visualstudio.com</a>，点击首页中央的蓝色大按钮 <strong>Download for Windows</strong>（网站会通过浏览器识别系统，Mac 用户会看到 Download for Mac）。</li><li>Windows 下载得到的是 <code>VSCodeUserSetup-x64-x.xx.x.exe</code>。</li><li>Mac 用户下载后得到一个 <code>.zip</code>，解压后把 <code>Visual Studio Code.app</code> 拖进“应用程序（Applications）”文件夹即可。</li><li><em>提示</em>：若官网下载卡在 0% 或速度极慢，说明第 1 步的代理尚未生效，先回去检查“系统代理”是否开启。</li></ul><p><strong>(2) 运行安装程序（Windows）</strong></p><ol><li>双击安装包，勾选“我同意此协议”，点击“下一步”。</li><li>安装位置保持默认即可，连续点“下一步”。</li><li>到达 <strong>“选择附加任务”</strong> 页面时，这是最关键的一步，请勾选以下选项：<ul><li>☑ <strong>将“通过 Code 打开”操作添加到 Windows 资源管理器文件上下文菜单</strong></li><li>☑ <strong>将“通过 Code 打开”操作添加到 Windows 资源管理器目录上下文菜单</strong></li><li>☑ <strong>添加到 PATH（重启后生效）</strong></li><li>☑ 创建桌面快捷方式（可选，方便找到）</li><li><strong>为什么要勾这些</strong>：前两项让你以后在任意文件夹上右键就能“通过 Code 打开”，省去每次在软件里找路径；“添加到 PATH”让终端和工具能通过 <code>code</code> 命令直接呼出 VS Code。</li><li><em>注意</em>：另有一项“将 Code 注册为受支持的文件类型的编辑器”不建议勾选，否则以后双击 <code>.txt</code>、<code>.md</code> 等文件都会默认用 VS Code 打开。</li></ul></li><li>点击“安装”，等待进度条走完，勾选“运行 Visual Studio Code”后点击“完成”。</li></ol><ul><li><em>Mac 用户补充</em>：打开 VS Code，按 <code>Cmd+Shift+P</code> 打开命令面板，输入 <code>Shell Command: Install &#39;code&#39; command in PATH</code> 并回车，即可在终端使用 <code>code</code> 命令。</li></ul><p><strong>(3) 首次启动与中文化</strong></p><ul><li>首次打开是英文界面，右下角可能弹出欢迎页，直接关掉即可。</li><li>按快捷键 <code>Ctrl+Shift+X</code>（Mac 为 <code>Cmd+Shift+X</code>）打开左侧 <strong>扩展市场（Extensions）</strong>。</li><li>在顶部搜索框输入 <code>Chinese</code>，找到 <strong>Chinese (Simplified) (简体中文) Language Pack for Visual Studio Code</strong>（发布者为 Microsoft，带蓝色对勾），点击 <strong>Install</strong>。</li><li>安装完成后右下角会弹出提示 <strong>“Change Language and Restart”</strong>，点击它，VS Code 会自动重启并变为中文界面。</li><li><em>若没有弹出提示</em>：按 <code>Ctrl+Shift+P</code> 打开命令面板，输入 <code>Configure Display Language</code>，回车后选择 <code>中文(简体)</code>，再点击“重启”。</li></ul><p><strong>(4) 安装 Live Server（网页实时预览）</strong></p><ul><li>再次打开扩展市场，搜索 <code>Live Server</code>，选择发布者为 <strong>Ritwick Dey</strong>、下载量最高的那一个，点击“安装”。</li><li><strong>它的作用</strong>：本地双击 <code>.html</code> 文件也能在浏览器打开，但 Live Server 会启动一个本地小服务器（地址形如 <code>http://127.0.0.1:5500</code>），并且<strong>你每次保存文件，浏览器都会自动刷新</strong>。</li><li>安装完成后，VS Code 窗口最底部的状态栏右侧会出现 <strong>Go Live</strong> 按钮，这就是启动入口。</li></ul><p><strong>(5) 用 2 分钟认识界面</strong></p><ul><li><strong>左侧活动栏（竖排图标）</strong>：最上面的“资源管理器”显示当前打开文件夹的文件树；“扩展”就是刚才用的应用商店。</li><li><strong>中央编辑区</strong>：点击文件树中的文件即可在此查看和修改代码。</li><li><strong>底部面板 &#x2F; 终端</strong>：按 <code>Ctrl+`</code>（数字 1 左边的反引号键）可以呼出内置终端。Day 2 起会在这里运行环境自检与各种命令。</li><li><strong>底部状态栏</strong>：显示当前文件类型、行号以及 Live Server 的 <strong>Go Live</strong> 按钮。</li><li><em>打开文件夹的方式</em>：菜单 <code>文件</code> → <code>打开文件夹</code>，或直接在资源管理器中对某个文件夹右键 → “通过 Code 打开”。弹出“是否信任此文件夹的作者”时点击 <strong>“是，我信任此作者”</strong> 即可。</li></ul><p><strong>(6) 开启自动保存</strong></p><ul><li>菜单 <code>文件</code> → 勾选 <code>自动保存</code>。初学者最常见的困惑是“改了代码为什么页面没变化”，原因往往是没按 <code>Ctrl+S</code> 保存；开启后即可避免，Live Server 也能随之自动刷新。</li></ul><h4 id="3-安装-GitHub-Desktop-并建立首个仓库"><a href="#3-安装-GitHub-Desktop-并建立首个仓库" class="headerlink" title="3. 安装 GitHub Desktop 并建立首个仓库"></a>3. 安装 GitHub Desktop 并建立首个仓库</h4><ul><li>前往 <a href="https://desktop.github.com/">desktop.github.com</a> 下载并安装，登录你的 GitHub 账号。</li><li>点击 <code>File</code> → <code>New Repository</code>，名称填写 <code>vibe-coding-101</code>，本地路径选一个易记的英文目录（如 <code>D:\projects</code>）。</li><li>勾选 <code>Initialize this repository with a README</code>，点击 <code>Create Repository</code>。</li><li>点击右上角 <strong>Publish repository</strong>，将仓库发布同步到 GitHub 云端。</li><li><strong>说明</strong>：本周前几天的所有练习代码均存放在该仓库目录下。</li></ul><h4 id="4-初始体验：在-VS-Code-中跑通第一个网页"><a href="#4-初始体验：在-VS-Code-中跑通第一个网页" class="headerlink" title="4. 初始体验：在 VS Code 中跑通第一个网页"></a>4. 初始体验：在 VS Code 中跑通第一个网页</h4><p>此时还没有安装 Agent Harness。为了先体验最原始的 Vibe Coding 工作方式，可以直接打开任意网页版 AI 聊天工具，例如 ChatGPT、DeepSeek 或 Kimi，把需求发给它，让它生成代码，再复制到 VS Code 中运行。</p><p>这一步主要体验“自然语言提出需求 → AI 生成代码 → 复制到编辑器 → 实际运行”的最基础闭环。Day 3 安装 Harness 后，再体验 AI 直接读取和修改整个项目的区别。</p><ol><li>在 <strong>VS Code</strong> 中点击 <code>文件</code> → <code>打开文件夹</code>，选择刚才建立的 <code>vibe-coding-101</code> 目录。</li><li>新建文件 <code>index.html</code>。</li><li>打开已有的网页版 AI（无需注册新工具，ChatGPT &#x2F; DeepSeek &#x2F; Kimi 均可），发送以下需求 Prompt：<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">请生成一个单文件 index.html 倒计时网页。</span><br><span class="line"></span><br><span class="line">要求：</span><br><span class="line">1. HTML、CSS、JavaScript 全部写在一个文件中；</span><br><span class="line">2. 页面居中显示“我的倒计时”；</span><br><span class="line">3. 提供日期选择器；</span><br><span class="line">4. 页面首次打开时，默认目标日期自动设置为当前日期之后 30 天；</span><br><span class="line">5. 实时显示剩余天、小时、分钟、秒；</span><br><span class="line">6. 不使用任何外部框架或第三方库；</span><br><span class="line">7. 代码直接完整输出，我会复制到 VS Code 中运行。</span><br></pre></td></tr></table></figure></li><li>将 AI 完整输出的代码复制并粘贴到 VS Code 的 <code>index.html</code> 中保存。</li><li>在编辑区右键选择 <code>Open with Live Server</code>（或点击右下角 <strong>Go Live</strong>），浏览器会自动打开网页并跳动显示倒计时。</li><li>尝试在 VS Code 中修改一行标题文本，由于开启了自动保存，切到浏览器时页面已自动刷新完成更新！</li></ol><h4 id="今日验收"><a href="#今日验收" class="headerlink" title="今日验收"></a>今日验收</h4><ul><li><input disabled="" type="checkbox"> 代理配置可用，能顺畅打开 GitHub 与 VS Code 插件市场</li><li><input disabled="" type="checkbox"> VS Code 与 GitHub Desktop 安装完成，已开启自动保存，<code>vibe-coding-101</code> 仓库已发布到 GitHub</li><li><input disabled="" type="checkbox"> 使用网页版 AI 成功生成并复制 <code>index.html</code></li><li><input disabled="" type="checkbox"> Live Server 能正常打开页面</li><li><input disabled="" type="checkbox"> 默认目标日期为当前日期之后 30 天</li><li><input disabled="" type="checkbox"> 能修改日期，并实时更新剩余天、时、分、秒</li></ul><hr><h3 id="Day-2：CS-本科多语言开发环境与-Git-纪律"><a href="#Day-2：CS-本科多语言开发环境与-Git-纪律" class="headerlink" title="Day 2：CS 本科多语言开发环境与 Git 纪律"></a>Day 2：CS 本科多语言开发环境与 Git 纪律</h3><p><strong>目标</strong>：搭建计算机专业本科核心技术栈基础环境（Python、Node.js、JDK、C&#x2F;C++），配置国内镜像源，掌握 Git 安全与版本后悔药（Discard &#x2F; Revert）。</p><blockquote><p><strong>环境规划：本周刚性依赖与 CS 本科开发基座</strong><br>计算机专业培养体系涵盖系统底层、核心算法、企业级工程与现代化工具链。为了避免后续课程与实战反复折腾配置，Day 2 将开发环境分为两类进行安装：</p><ul><li><strong>本周项目刚性依赖</strong>：<ul><li><strong>Python</strong>：Day 5 全栈后端（FastAPI + SQLite）与自动化脚本。</li><li><strong>Node.js</strong>：Day 3 启动 <strong>DeepSeek Harness</strong>（基于 npx 运行）及 Day 6 使用 Vite 构建小游戏的刚性前置依赖。</li><li><strong>Git</strong>：版本管理与代码同步基石。</li></ul></li><li><strong>CS 本科开发基座</strong>：<ul><li>**JDK (Java)**：面向对象程序设计、数据结构经典教学与企业级后端核心。</li><li>**C&#x2F;C++ 编译器 (GCC&#x2F;G++)**：程序设计基础、操作系统、计算机组成原理必须掌握的底层基石。</li></ul></li><li><strong>安装节奏建议</strong>：理想情况下 Day 2 一次配齐；如果当天时间不足，应优先保证 Python、Node.js、Git 可用，JDK 与 GCC&#x2F;G++ 随后补齐，但在完成 Day 2 最终环境建设前仍建议全部安装完成。</li></ul></blockquote><h4 id="1-安装-Python-3-12"><a href="#1-安装-Python-3-12" class="headerlink" title="1. 安装 Python 3.12+"></a>1. 安装 Python 3.12+</h4><ul><li>前往 <a href="https://python.org/">python.org</a> 下载 3.12 或 3.13 稳定版。</li><li><strong>关键操作</strong>：在安装首界面务必勾选 <strong>☑ Add python.exe to PATH</strong>！</li><li>Windows 自检使用 <code>python --version</code>；macOS 从 python.org 安装 Python 后，常见命令为 <code>python3</code>，因此 Mac 用户优先验证：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python3 --version</span><br></pre></td></tr></table></figure>避免因 <code>python</code> 命令不存在而误以为安装失败。</li><li>配置国内镜像加速（重启 VS Code 后在内置终端执行）：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip config <span class="built_in">set</span> global.index-url https://pypi.tuna.tsinghua.edu.cn/simple</span><br></pre></td></tr></table></figure></li></ul><h4 id="2-安装-Node-js-LTS（含-npm-npx）"><a href="#2-安装-Node-js-LTS（含-npm-npx）" class="headerlink" title="2. 安装 Node.js LTS（含 npm &#x2F; npx）"></a>2. 安装 Node.js LTS（含 npm &#x2F; npx）</h4><ul><li>前往 <a href="https://nodejs.org/">nodejs.org</a> 下载标注 <strong>LTS（长期支持版，如 Node.js 24 LTS）</strong> 的安装包，一路默认“下一步”安装即可。</li><li>安装完成后会自动附带 <code>npm</code> 与 <code>npx</code> 工具。</li><li>配置国内 npm 镜像加速（在内置终端执行）：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm config <span class="built_in">set</span> registry https://registry.npmmirror.com</span><br></pre></td></tr></table></figure></li><li><strong>重要说明</strong>：这一步彻底解决了“先装 Harness 却因缺少 Node.js 导致启动失败”的依赖断裂问题。Node.js &#x2F; npm &#x2F; npx 是 Day 3 某些 Harness（如 DeepSeek Harness）的运行前提，也是 Day 6 使用 Vite 构建小游戏的刚性依赖。</li></ul><h4 id="3-安装-JDK-LTS-Java-17-或-21"><a href="#3-安装-JDK-LTS-Java-17-或-21" class="headerlink" title="3. 安装 JDK LTS (Java 17 或 21)"></a>3. 安装 JDK LTS (Java 17 或 21)</h4><ul><li>前往主流开源发行版 <a href="https://adoptium.net/">adoptium.net</a> 下载 Eclipse Temurin 的 LTS 版本（Java 17 或 21）。</li><li>Windows 安装包在自定义设置页面中，确保勾选了 <strong>Set JAVA_HOME variable</strong> 与 <strong>Add to PATH</strong>。</li><li>计算机专业的面向对象程序设计与经典数据结构课程大多以此为基石。</li></ul><h4 id="4-安装-C-C-编译环境-MinGW-w64-GCC"><a href="#4-安装-C-C-编译环境-MinGW-w64-GCC" class="headerlink" title="4. 安装 C&#x2F;C++ 编译环境 (MinGW-w64 &#x2F; GCC)"></a>4. 安装 C&#x2F;C++ 编译环境 (MinGW-w64 &#x2F; GCC)</h4><ul><li><strong>Windows 极简推荐</strong>：下载免安装纯净版 MinGW-w64（如 <a href="https://github.com/skeeto/w64devkit/releases">w64devkit</a> 或 <a href="https://winlibs.com/">winlibs</a>），解压至无中文路径（例如 <code>C:\mingw64</code>），将其 <code>bin</code> 目录添加到系统的 <code>Path</code> 环境变量中。<ul><li><strong>Windows GUI 配置 Path 步骤</strong>：<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Win 键搜索“编辑系统环境变量”</span><br><span class="line">→ 打开“系统属性”</span><br><span class="line">→ 环境变量</span><br><span class="line">→ 在 Path 中点击“编辑”</span><br><span class="line">→ 新建</span><br><span class="line">→ 填入 MinGW 的 bin 目录，例如 C:\mingw64\bin</span><br><span class="line">→ 一路确定</span><br><span class="line">→ 彻底重启 VS Code</span><br></pre></td></tr></table></figure><em>注：若实际解压路径不同，填入对应的实际 <code>bin</code> 目录。</em></li></ul></li><li><strong>Mac 用户</strong>：直接在终端执行 <code>xcode-select --install</code> 安装 Command Line Tools。</li><li><strong>Ubuntu &#x2F; Debian 用户</strong>：终端执行：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install -y build-essential</span><br></pre></td></tr></table></figure>其他 Linux 发行版请使用对应的包管理器安装 GCC&#x2F;G++。</li><li>这是大一学习 C 语言程序设计、后续深入操作系统与计算机组成原理必不可少的工具链。</li></ul><h4 id="5-安装-Git"><a href="#5-安装-Git" class="headerlink" title="5. 安装 Git"></a>5. 安装 Git</h4><ul><li><strong>Windows 用户</strong>：<ul><li>前往官网下载安装包：<a href="https://git-scm.com/">git-scm.com</a></li><li>下载 Git for Windows，运行安装程序，使用默认推荐设置安装；</li><li>安装完成后彻底重启 VS Code；</li><li>在终端执行：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git --version</span><br></pre></td></tr></table></figure>确认正确输出版本号。</li></ul></li><li><strong>macOS &#x2F; Linux 用户</strong>：在终端执行 <code>git --version</code> 确认安装。若未安装，macOS 会提示安装 Command Line Tools，Linux 用户按各自包管理器安装即可。</li><li><strong>说明</strong>：系统 Git 是开发基座之一，后续工具链也可能依赖它。本节仅负责安装与环境验证，具体的 Git 命令行操作（如 <code>add</code>、<code>commit</code>、<code>pull</code>、<code>push</code> 等）全部保留在<a href="#%E9%99%84%E5%BD%95-b%E7%BA%AF%E5%91%BD%E4%BB%A4%E8%A1%8C-git-%E5%85%A5%E9%97%A8">附录 B：纯命令行 Git 入门</a>中深入学习。</li></ul><h4 id="6-环境变量避坑与-Windows-综合自检"><a href="#6-环境变量避坑与-Windows-综合自检" class="headerlink" title="6. 环境变量避坑与 Windows 综合自检"></a>6. 环境变量避坑与 Windows 综合自检</h4><ul><li><strong>避坑铁律</strong>：凡是新安装了会写入 PATH 的程序，都<strong>必须彻底关闭并重启 VS Code</strong>，其内置终端才能读到新的环境变量。</li><li><strong>综合环境自检</strong>（重启 VS Code 后在内置终端中执行）：<ul><li><strong>Windows 命令</strong>：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">python --version</span><br><span class="line">node -v</span><br><span class="line">npm -v</span><br><span class="line">java -version</span><br><span class="line">javac -version</span><br><span class="line">gcc --version</span><br><span class="line">g++ --version</span><br><span class="line">git --version</span><br></pre></td></tr></table></figure></li><li><strong>macOS &#x2F; Linux 说明</strong>：macOS &#x2F; Linux 用户由于不同发行版、Shell 和包管理器环境存在差异，可以把自己的系统版本、发行版和当前安装情况告诉 AI，让 AI 为当前设备生成对应的安装与自检命令。</li></ul></li><li><strong>命令验证重点</strong>：<ul><li><code>python</code> 验证 Python 解释器；</li><li><code>node</code> 与 <code>npm</code> 验证 Node.js 运行时与包管理器；</li><li><code>java</code> 验证 Java 运行时（JRE）；</li><li><code>javac</code> 验证 Java 编译器（JDK）；</li><li><code>gcc</code> 验证 C 语言编译器；</li><li><code>g++</code> 验证 C++ 语言编译器（Windows MinGW 附带）；</li><li><code>git</code> 验证系统 Git CLI 工具。<br>如果全部正确输出版本号（未提示“找不到命令”或弹出 Windows 应用商店），说明多语言开发基座已全部就位！</li></ul></li></ul><h4 id="7-安全规范与-Git-纪律"><a href="#7-安全规范与-Git-纪律" class="headerlink" title="7. 安全规范与 Git 纪律"></a>7. 安全规范与 Git 纪律</h4><p>公开仓库中若泄露敏感配置会导致安全风险。</p><ol><li>**生成全能 <code>.gitignore</code>**：在 <code>vibe-coding-101</code> 项目根目录下创建 <code>.gitignore</code>，写入针对 Python、Node、Java、C&#x2F;C++ 与 SQLite 的通用忽略项：<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"># 敏感密钥与本地配置（绝对禁止提交）</span><br><span class="line">.env</span><br><span class="line">*.local</span><br><span class="line"></span><br><span class="line"># Python</span><br><span class="line">__pycache__/</span><br><span class="line">*.pyc</span><br><span class="line">.venv/</span><br><span class="line"></span><br><span class="line"># SQLite 本地数据</span><br><span class="line">*.db</span><br><span class="line">*.sqlite</span><br><span class="line">*.sqlite3</span><br><span class="line"></span><br><span class="line"># Node</span><br><span class="line">node_modules/</span><br><span class="line">dist/</span><br><span class="line"></span><br><span class="line"># Java</span><br><span class="line">*.class</span><br><span class="line">target/</span><br><span class="line"></span><br><span class="line"># C/C++</span><br><span class="line">*.o</span><br><span class="line">*.exe</span><br><span class="line">*.out</span><br></pre></td></tr></table></figure><strong>安全铁律</strong>：API Key 绝不进代码、绝不进仓库（完整规范见第四节）。</li><li><strong>掌握 Git 的两套“撤销与后悔”操作</strong>：<ul><li>打开 Day 1 使用过的网页版 ChatGPT &#x2F; DeepSeek &#x2F; Kimi，要求它生成一个简单的 Python 猜数字程序 <code>game.py</code>，发送以下 Prompt：<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">请写一个适合 Python 初学者的猜数字小游戏。</span><br><span class="line"></span><br><span class="line">要求：</span><br><span class="line">1. 使用 random.randint 随机生成 1~100 的整数；</span><br><span class="line">2. 使用 while 循环让用户重复输入；</span><br><span class="line">3. 用 input() 获取用户输入；</span><br><span class="line">4. 猜大了提示“太大了”，猜小了提示“太小了”；</span><br><span class="line">5. 猜中后输出尝试次数并结束；</span><br><span class="line">6. 只使用 Python 标准库；</span><br><span class="line">7. 代码控制在 30 行以内；</span><br><span class="line">8. 直接输出完整 game.py 代码。</span><br></pre></td></tr></table></figure></li><li>将 AI 输出的代码复制到 VS Code 中新建的 <code>game.py</code>，保存并在终端运行 <code>python game.py</code>（Mac 用户 <code>python3 game.py</code>）简单测试。</li><li>打开 GitHub Desktop，在左下方填写摘要（如“添加猜数字小游戏”），点击 <strong>Commit to main</strong>，再点击 <strong>Push origin</strong> 推送到云端。</li><li><strong>练习操作 A（丢弃未提交的修改）</strong>：在 VS Code 中随意改坏几行代码并保存。回到 GitHub Desktop，在更改的文件上右键点击 <strong>Discard Changes</strong>，本地文件会瞬间还原。</li><li><strong>练习操作 B（回退已提交的历史版本）</strong>：先修改一处代码（例如修改欢迎语），在 GitHub Desktop 中单独 commit 一次（摘要如“修改欢迎语”）。在左侧切换到 <strong>History</strong> 标签页，右键选中该 Commit，点击 <strong>Revert changes in commit</strong>。观察欢迎语恢复原样而 <code>game.py</code> 依然存在。Git 会自动生成一次反向修改的新提交，安全撤销历史更改。</li></ul></li></ol><blockquote><p>[!TIP]<br><strong>想进一步掌握 Git 命令行？</strong><br>GitHub Desktop 足以完成本周的基础版本管理，但作为计算机专业学生，建议在熟悉图形化操作后进一步理解 Git CLI。<br>可以继续阅读<a href="#%E9%99%84%E5%BD%95-b%E7%BA%AF%E5%91%BD%E4%BB%A4%E8%A1%8C-git-%E5%85%A5%E9%97%A8">附录 B：纯命令行 Git 入门</a>，学习 <code>status → diff → add → commit → push</code> 的终端工作流。</p></blockquote><h4 id="今日验收-1"><a href="#今日验收-1" class="headerlink" title="今日验收"></a>今日验收</h4><ul><li><input disabled="" type="checkbox"> Python、Node.js、JDK、C&#x2F;C++ 与 Git 环境全部就位，综合自检命令全部通过</li><li><input disabled="" type="checkbox"> 根目录下已创建包含密钥保护与 SQLite 本地数据忽略规则的 <code>.gitignore</code></li><li><input disabled="" type="checkbox"> 使用网页版 AI 生成 <code>game.py</code>，并在 GitHub Desktop 中实际跑通了一次 <code>Discard Changes</code> 与一次 <code>Revert changes in commit</code></li></ul><hr><h3 id="Day-3：Agent-Harness-就位与规划工作流"><a href="#Day-3：Agent-Harness-就位与规划工作流" class="headerlink" title="Day 3：Agent Harness 就位与规划工作流"></a>Day 3：Agent Harness 就位与规划工作流</h3><p><strong>目标</strong>：安装配置主力 Harness，掌握结构化提示词规范与项目规则文件（优化工程约束），跑通第一个 Harness 驱动的规划与开发闭环。</p><h4 id="1-安装与启动主力-Harness（1-款）"><a href="#1-安装与启动主力-Harness（1-款）" class="headerlink" title="1. 安装与启动主力 Harness（1 款）"></a>1. 安装与启动主力 Harness（1 款）</h4><p>依据第二节的推荐选择 1 款工具：</p><ul><li>若使用 <strong>Codex App</strong> 或 <strong>Claude Code Desktop</strong>：前往官网下载客户端完成安装与登录。</li><li>若使用 <strong>DeepSeek Harness</strong>：得益于 Day 2 已经安装好 Node.js，直接在终端执行官方命令即可无缝拉起 Web 交互界面：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx @deepseek-ai/dsh web</span><br></pre></td></tr></table></figure></li><li>若使用 <strong>ZCode</strong> 或 <strong>Kimi Code</strong>：下载对应官方客户端并登录。</li><li><strong>安全权限提醒</strong>：初次使用请务必保持默认的“每次执行前手动确认&#x2F;批准”模式，<strong>第一周严禁开启“全自动 &#x2F; 自动批准（Auto-approve &#x2F; YOLO）”模式</strong>。AI 执行终端命令前必须人工过目，防止高危指令。</li><li><em>提示</em>：若需要将 Claude Code &#x2F; Codex 等成熟工作流接入国产大模型，可参阅<a href="#%E9%99%84%E5%BD%95-a%E5%8F%AF%E9%80%89%E9%AB%98%E7%BA%A7%E9%85%8D%E7%BD%AE%E4%B8%8E%E7%AC%AC%E4%B8%89%E6%96%B9%E6%A8%A1%E5%9E%8B%E6%8E%A5%E5%85%A5cc-switch-%E4%B8%8E-api-%E6%96%B9%E8%A8%80">附录 A</a>。</li></ul><h4 id="2-结构化提示词四要素"><a href="#2-结构化提示词四要素" class="headerlink" title="2. 结构化提示词四要素"></a>2. 结构化提示词四要素</h4><p>对具备自主执行能力的 Harness，单纯设定“角色”意义有限，真正决定质量的是以下四个要素：<strong>背景说明 + 核心功能 + 明确约束 + 验收方式</strong>。</p><table><thead><tr><th>模糊表述（容易发散偏航）</th><th>结构化表述（质量与边界可控）</th></tr></thead><tbody><tr><td>帮我写个倒计时工具</td><td><strong>背景</strong>：用于记录学期重要考试与 DDL 的网页工具。<br><strong>功能</strong>：纯前端实现，支持添加自定义事项、手动调整目标日期（默认设为下一次元旦）；实时显示剩余天、时、分、秒。<br><strong>约束</strong>：数据存入 LocalStorage，刷新页面不丢失；页面极简深色风格，单个 HTML 文件包含完整 CSS&#x2F;JS。<br><strong>验收方式</strong>：在浏览器打开后能正常倒计时；刷新页面已有事项保留；添加过去的时间会给出友好拦截提示。</td></tr></tbody></table><h4 id="3-需求文档-PROJECT-md-模板"><a href="#3-需求文档-PROJECT-md-模板" class="headerlink" title="3. 需求文档 PROJECT.md 模板"></a>3. 需求文档 <code>PROJECT.md</code> 模板</h4><p>在开展多步骤任务前，先在根目录创建 <code>PROJECT.md</code>，为 Agent 划定开发范围与验收标准：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="section"># 项目名称：学期倒计时看板</span></span><br><span class="line"></span><br><span class="line"><span class="section">## 我要做什么</span></span><br><span class="line">一个现代极简风格的纯前端 DDL 与学期倒计时看板。</span><br><span class="line"></span><br><span class="line"><span class="section">## 核心功能</span></span><br><span class="line"><span class="bullet">-</span> [ ] 顶部醒目展示距离下一次元旦的实时倒计时（天、时、分、秒动态跳动）</span><br><span class="line"><span class="bullet">-</span> [ ] 支持点击日期选择器自定义目标时间，自动计算相对差距</span><br><span class="line"><span class="bullet">-</span> [ ] 支持添加多个备忘 DDL 任务列表，数据保存到浏览器 LocalStorage</span><br><span class="line"></span><br><span class="line"><span class="section">## 约束条件</span></span><br><span class="line"><span class="bullet">-</span> 纯 HTML5/CSS3/JavaScript 单文件实现，不引入重型外部框架</span><br><span class="line"><span class="bullet">-</span> 默认目标日期必须动态计算为“下一次元旦”，切忌硬编码过去失效的年份常量</span><br><span class="line"><span class="bullet">-</span> 布局自适应屏幕宽度，支持深色夜间模式</span><br><span class="line"></span><br><span class="line"><span class="section">## 验收方式</span></span><br><span class="line">在 VS Code 中用 Live Server 打开，页面秒级动态刷新，刷新浏览器后自定义事项不丢失。</span><br></pre></td></tr></table></figure><h4 id="4-配置项目规则文件（性价比最高的一步）"><a href="#4-配置项目规则文件（性价比最高的一步）" class="headerlink" title="4. 配置项目规则文件（性价比最高的一步）"></a>4. 配置项目规则文件（性价比最高的一步）</h4><p>主流 Harness 会在启动时自动读取项目规则文件：</p><ul><li><strong>Codex App</strong> 使用：<code>AGENTS.md</code></li><li><strong>Claude Code Desktop</strong> 使用：<code>CLAUDE.md</code></li><li><strong>其他 Harness</strong>：不同 Harness 的项目规则文件名称、加载目录与优先级不同。如果官方文档没有明确声明支持 <code>AGENTS.md</code>，不要默认该工具会自动读取它。优先查阅当前 Harness 官方文档。</li></ul><p><strong>规则文件标准内容模板</strong>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">-</span> 全程使用中文进行对话和代码注释。</span><br><span class="line"><span class="bullet">-</span> 每次修改代码后，自行在终端运行测试，或详细指导我在浏览器中验证效果。</span><br><span class="line"><span class="bullet">-</span> 【核心工程纪律】：每轮只完成一个可独立验收的功能，不修改无关模块；如果预计涉及多个模块，先说明修改范围与影响，切忌随意重构无关代码。</span><br><span class="line"><span class="bullet">-</span> 每完成一个独立功能点并通过验证后，提醒我进行一次 Git Commit。</span><br><span class="line"><span class="bullet">-</span> 我是大一计算机专业学生，遇到关键算法、数据结构或系统调用时，请用一两句话解释底层原理。</span><br></pre></td></tr></table></figure><blockquote><p>[!IMPORTANT]<br><strong>警惕不良工程约束</strong>：切勿设置诸如“单次修改不要超过 3 个文件”等机械的数字限制。这种死板规则会诱导 Agent 为了满足数字而把代码硬塞进少数文件，严重破坏软件的模块化与高内聚设计。正确的工程约束是<strong>限定每轮的任务内聚性与修改范围</strong>，不要限制文件个数。</p></blockquote><h4 id="5-今日实战练习（动手任务）"><a href="#5-今日实战练习（动手任务）" class="headerlink" title="5. 今日实战练习（动手任务）"></a>5. 今日实战练习（动手任务）</h4><blockquote><p><strong>教学对比</strong>：Day 1 的网页版 AI 通常无法直接访问和修改你本地工程文件，需要用户人工复制代码、报错和上下文；Day 3 的 Agent Harness 可以直接读取工程文件、跨文件检索、修改代码并运行终端命令，这就是两种工作流最直观的差别。</p></blockquote><ul><li>在 <code>vibe-coding-101</code> 仓库中新建 <code>PROJECT.md</code> 与对应的规则文件（<code>AGENTS.md</code> 或 <code>CLAUDE.md</code>）。</li><li>切换 Harness 至 <strong>Plan（规划）模式</strong>（若使用的工具没有独立开关，在对话中输入：<em>“请先完整阅读 PROJECT.md 与规则文件，给出分步实现计划，经我确认后再动手写代码”</em>）。</li><li>指挥 Harness 分步对 Day 1 的简易网页进行工程级升级重构，明确要求增加以下能力：<ol><li><strong>动态计算下一次元旦</strong>（次年 1 月 1 日）作为默认主倒计时；</li><li>支持添加与管理<strong>多个 DDL 任务列表</strong>；</li><li>使用 <strong>LocalStorage</strong> 进行本地数据持久化；</li><li>保证<strong>刷新页面数据不丢失</strong>；</li><li>添加过去时间时进行友好的<strong>输入校验与拦截提示</strong>；</li><li>必要时对 Day 1 的简易代码进行<strong>结构重构</strong>。</li></ol></li><li>审查 Diff 并确认修改，在 GitHub Desktop 中至少产生 3 次清晰独立的功能 Commit。</li></ul><h4 id="今日验收-2"><a href="#今日验收-2" class="headerlink" title="今日验收"></a>今日验收</h4><ul><li><input disabled="" type="checkbox"> 主力 Harness 成功安装并能正常调用模型</li><li><input disabled="" type="checkbox"> 根目录包含规范的 <code>PROJECT.md</code> 与规则文件（采用健康的工程约束）</li><li><input disabled="" type="checkbox"> 体验了 Plan（规划）模式，指挥 Harness 完成了 Day 1 网页的多任务与 LocalStorage 升级</li><li><input disabled="" type="checkbox"> 审查改动 Diff，在 GitHub 产生至少 3 次清晰 Commit</li></ul><hr><h3 id="Day-4：代码调试与双-Harness-协作"><a href="#Day-4：代码调试与双-Harness-协作" class="headerlink" title="Day 4：代码调试与双 Harness 协作"></a>Day 4：代码调试与双 Harness 协作</h3><p><strong>目标</strong>：配置备用 Harness，掌握向 AI 准确反馈 Bug 的规范方法，攻克常见卡点。</p><h4 id="1-安装与配置备用-Harness"><a href="#1-安装与配置备用-Harness" class="headerlink" title="1. 安装与配置备用 Harness"></a>1. 安装与配置备用 Harness</h4><ul><li>在推荐列表中选择第 2 款工具作为备选（例如主力为 Codex App &#x2F; Claude Code，备选可配置 DeepSeek Harness、ZCode 或 Kimi Code）。</li><li>确保备用工具能正常连接运行。在后续遇到棘手问题时，双模型交叉诊断能极大降低死锁概率。</li><li><em>说明</em>：若需要灵活切换不同模型供应商，可参阅<a href="#%E9%99%84%E5%BD%95-a%E5%8F%AF%E9%80%89%E9%AB%98%E7%BA%A7%E9%85%8D%E7%BD%AE%E4%B8%8E%E7%AC%AC%E4%B8%89%E6%96%B9%E6%A8%A1%E5%9E%8B%E6%8E%A5%E5%85%A5cc-switch-%E4%B8%8E-api-%E6%96%B9%E8%A8%80">附录 A</a>。</li></ul><h4 id="2-结构化报错反馈方法"><a href="#2-结构化报错反馈方法" class="headerlink" title="2. 结构化报错反馈方法"></a>2. 结构化报错反馈方法</h4><p>当程序运行异常时，不要只发“报错了”或“为什么不行”，向 Harness 提供以下三项完整信息：</p><ol><li><strong>完整报错内容</strong>：终端中的红字 Traceback，或浏览器控制台（F12 Console）中的报错文本全部复制。</li><li><strong>触发操作路径</strong>：进行了什么输入、点击了哪个按钮后出现。</li><li><strong>预期与实际差异</strong>：明确指出“预期应该输出 A，但实际出现了 B”。</li></ol><p>配合约束指令限制其动作：</p><blockquote><p><em>“请先分析报错的具体根因，说明需要修改哪几个模块的哪些逻辑，等我确认后再动手。不要改动不相关的功能代码。”</em></p></blockquote><h4 id="3-应对三大高频卡点"><a href="#3-应对三大高频卡点" class="headerlink" title="3. 应对三大高频卡点"></a>3. 应对三大高频卡点</h4><ul><li><strong>卡点 A：AI 声称“已修复”但实际上没修好</strong>：要求它在终端中实际运行测试命令，并粘贴完整的执行输出作为通过证据。</li><li><strong>卡点 B：会话上下文过长导致胡说八道</strong>：立即开启新会话（New Session），指令其：“重新阅读根目录下的 <code>PROJECT.md</code> 与规则文件，接管当前开发进度”。</li><li><strong>卡点 C：同一个 Bug 反复陷入死循环</strong>：停下当前工具，打开备用 Harness，将相同代码、需求与报错喂给备用模型重新诊断。</li></ul><h4 id="4-今日实战练习（构造与排查-Bug）"><a href="#4-今日实战练习（构造与排查-Bug）" class="headerlink" title="4. 今日实战练习（构造与排查 Bug）"></a>4. 今日实战练习（构造与排查 Bug）</h4><ul><li><strong>练习 A（语法报错排查）</strong>：手动改坏一个变量名或拼错函数触发报错，将终端&#x2F;控制台报错复制给 AI，体会标准报错信息的秒级定位。</li><li><strong>练习 B（隐蔽逻辑 Bug 排查）</strong>：在备用 Harness 中打开项目，让它故意植入一个无报错的真实逻辑缺陷（例如将 LocalStorage 写入的键名 <code>ddlTasks</code> 偷偷改为读取 <code>ddl_tasks</code>，导致“新任务当前能添加但刷新后全部消失”；或将日期边界判断 <code>&lt;=</code> 偷偷改为 <code>&lt;</code> 导致“恰好今天截止的任务行为异常”），要求其保持保密不解释改动。在 GitHub Desktop 中做一次中性提交（如“微调数据处理逻辑”）。随后切回主力 Harness，仅通过描述“预期表现与实际表现的差异”，引导 AI 精准找出并修复逻辑漏洞。</li></ul><h4 id="今日验收-3"><a href="#今日验收-3" class="headerlink" title="今日验收"></a>今日验收</h4><ul><li><input disabled="" type="checkbox"> 备用 Harness 安装就位，完成连通性测试</li><li><input disabled="" type="checkbox"> 熟练掌握“完整报错 + 操作路径 + 预期差异”的结构化反馈三要素</li><li><input disabled="" type="checkbox"> 成功排查并修复了人为构造的语法报错与逻辑 Bug，并在 Git 中完成提交</li></ul><hr><h3 id="Day-5：项目实战一：全栈-DDL-管理器（FastAPI-SQLite-前端）"><a href="#Day-5：项目实战一：全栈-DDL-管理器（FastAPI-SQLite-前端）" class="headerlink" title="Day 5：项目实战一：全栈 DDL 管理器（FastAPI + SQLite + 前端）"></a>Day 5：项目实战一：全栈 DDL 管理器（FastAPI + SQLite + 前端）</h3><p><strong>目标</strong>：脱离玩具脚本，建立独立项目仓库，构建计算机专业级别的前后端分层全栈应用：Python FastAPI 提供 RESTful API，SQLite 进行轻量数据持久化，前端 HTML&#x2F;CSS&#x2F;JS 进行动态交互，打通多语言协作闭环。</p><h4 id="1-建立独立项目仓库并补齐-gitignore"><a href="#1-建立独立项目仓库并补齐-gitignore" class="headerlink" title="1. 建立独立项目仓库并补齐 .gitignore"></a>1. 建立独立项目仓库并补齐 .gitignore</h4><ul><li>打开 GitHub Desktop，点击 <code>File</code> → <code>New Repository</code>，新建仓库 <code>ddl-manager</code>，Git Ignore 下拉选择 <strong>Python</strong>，点击 <code>Create Repository</code>。</li><li>创建本地仓库后，点击 GitHub Desktop 顶部的 <strong>Publish repository</strong>。在发布窗口中根据是否希望公开展示选择 Public &#x2F; Private，然后完成发布。</li><li>将新仓库分别在 VS Code 与主力 Harness 中打开。</li><li>**检查并补齐当前仓库的 <code>.gitignore</code>**：Day 2 的 <code>.gitignore</code> 属于 <code>vibe-coding-101</code> 仓库，不会自动作用于新建的 <code>ddl-manager</code>。打开 <code>ddl-manager</code> 根目录下的 <code>.gitignore</code>，检查 GitHub Desktop 自动生成的忽略规则并补齐缺失项，至少确保包含：<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">.venv/</span><br><span class="line">.env</span><br><span class="line">__pycache__/</span><br><span class="line">*.pyc</span><br><span class="line"></span><br><span class="line">*.db</span><br><span class="line">*.sqlite</span><br><span class="line">*.sqlite3</span><br></pre></td></tr></table></figure></li></ul><h4 id="2-全栈架构设计（编写-PROJECT-md）"><a href="#2-全栈架构设计（编写-PROJECT-md）" class="headerlink" title="2. 全栈架构设计（编写 PROJECT.md）"></a>2. 全栈架构设计（编写 <code>PROJECT.md</code>）</h4><p>引导 Harness 设计并实现一个轻量实用的全栈任务看板系统：</p><ul><li><strong>后端架构（Python FastAPI + SQLite）</strong>：<ul><li>使用 Python 现代高性能框架 FastAPI 开发后端 RESTful 接口。</li><li>使用 Python 内置的 <code>sqlite3</code> 数据库持久化存储数据（表字段设计：<code>id</code>, <code>title</code>, <code>deadline</code>, <code>urgency</code>, <code>is_done</code>）。</li><li><strong>数据库提交规范</strong>：数据库文件（如 <code>tasks.db</code>）属于运行时本地数据，<strong>不提交到 GitHub</strong>（需在当前仓库的 <code>.gitignore</code> 中配置忽略规则）。项目首次启动运行时由程序自动检查并建表。</li><li><strong>SQLite 数据库工程约束</strong>：<ul><li>不长期共享一个全局 <code>sqlite3.Connection</code>，避免多请求并发时的连接争用与状态混乱；</li><li>数据库操作应按请求或操作粒度获取连接，并及时关闭，或使用统一的 context manager &#x2F; FastAPI 依赖注入封装；</li><li>所有 SQL 查询必须使用参数化查询（如 <code>cursor.execute(&quot;SELECT ... WHERE id = ?&quot;, (task_id,))</code>）；</li><li>严禁直接把用户输入通过字符串拼接进 SQL 语句，防范 SQL 注入。</li></ul></li><li><strong>时间格式与时区约定</strong>：<ul><li><code>deadline</code> 在 API 与 SQLite 中统一使用 ISO 8601 格式字符串。本教学项目按用户浏览器所在本地时区解释和显示时间，前后端必须采用一致约定，避免一部分逻辑使用 UTC、一部分逻辑使用本地时间导致倒计时与预警计算偏差。</li></ul></li><li>提供标准 RESTful API：<ul><li><code>GET /api/tasks</code>（获取任务列表）</li><li><code>POST /api/tasks</code>（新增任务）</li><li><code>PATCH /api/tasks/&#123;id&#125;</code>（更新任务状态，请求体形如 <code>&#123;&quot;is_done&quot;: true&#125;</code>；由客户端明确告诉后端希望任务最终处于什么状态，比单纯执行 toggle 更清晰健壮）</li><li><code>DELETE /api/tasks/&#123;id&#125;</code>（删除任务）</li></ul></li></ul></li><li><strong>前端架构（原生 HTML + CSS + JS）</strong>：<ul><li>单页面仪表盘，通过原生 <code>fetch()</code> 异步调用后端 API（使用相对路径如 <code>fetch(&#39;/api/tasks&#39;)</code>，无需手工写死绝对地址）。</li><li>动态计算倒计时剩余天数与小时，临近 24 小时的 DDL 自动标红预警。</li><li>表单提交后无需刷新整页，前端无感异步刷新列表。</li></ul></li></ul><h4 id="3-分步落地流程"><a href="#3-分步落地流程" class="headerlink" title="3. 分步落地流程"></a>3. 分步落地流程</h4><ol><li><strong>创建 Python 虚拟环境并安装依赖</strong>：<br>在 VS Code 内置终端中执行以下命令创建独立的虚拟环境：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python -m venv .venv</span><br></pre></td></tr></table></figure><ul><li>Windows PowerShell 激活：<figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.venv\Scripts\Activate.ps1</span><br></pre></td></tr></table></figure><em>若 PowerShell 提示脚本执行策略受限阻止激活，可切换到 Command Prompt 终端执行 <code>.venv\Scripts\activate.bat</code>，或者在 VS Code 中按 <code>Ctrl+Shift+P</code> → 输入并选择 <code>Python: Select Interpreter</code> → 选择当前项目下的 <code>.venv</code>。</em></li><li>macOS &#x2F; Linux 激活：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> .venv/bin/activate</span><br></pre></td></tr></table></figure></li><li>在激活的虚拟环境中安装依赖：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python -m pip install fastapi uvicorn</span><br></pre></td></tr></table></figure></li><li>导出依赖清单：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python -m pip freeze &gt; requirements.txt</span><br></pre></td></tr></table></figure></li><li><strong>工程原理解释</strong>：<code>pip freeze</code> 会同时记录 FastAPI、Uvicorn 以及它们安装的间接依赖，因此即使你只手动安装了两个包，<code>requirements.txt</code> 中出现十几项也是正常现象。后续学习 <code>uv</code>、Poetry 等依赖管理工具时，会接触更加现代的直接依赖管理方式。<code>.venv</code> 隔离每个项目的 Python 依赖库，避免污染全局环境；<code>.venv/</code> 已经在当前仓库的 <code>.gitignore</code> 中被忽略；<code>requirements.txt</code> 记录项目依赖，方便重新安装或团队协作。</li></ul></li><li><strong>后端接口与自动文档体验</strong>：<ul><li>让 Harness 编写 <code>main.py</code> 与 SQLite 初始化建表逻辑。</li><li>终端运行后端：<code>uvicorn main:app --reload</code>。</li><li>打开浏览器访问 <code>http://127.0.0.1:8000/docs</code>。这是计算机本科生必学的一课：体验 FastAPI 自动生成的交互式 Swagger API 文档，并在网页上手动测试接口的收发包与 JSON 数据结构。</li></ul></li><li><strong>前端页面与前后端联调</strong>：<ul><li><strong>重要禁令：Day 5 不要再通过 Live Server 打开 <code>static/index.html</code>！</strong><br>Day 1 的 Live Server 只适合纯前端静态页面。Day 5 已经有 FastAPI 后端，如果前端跑在 <code>127.0.0.1:5500</code>，而 API 跑在 <code>127.0.0.1:8000</code>，就会形成不同源请求，容易出现 404 或 CORS（跨域）问题。</li><li><strong>同源托管方案</strong>：要求 Harness 将前端通过 FastAPI 直接提供，可采用方案 A（把页面挂在根路由 <code>http://127.0.0.1:8000/</code>）或方案 B（通过挂载静态目录访问 <code>http://127.0.0.1:8000/static/index.html</code>）。前端 API 统一使用相对路径 <code>fetch(&#39;/api/tasks&#39;)</code>，不硬编码端口。</li><li>浏览器打开页面，录入数条示例数据（如“高等数学作业”、“程序设计实验”、“英语报告”），测试增删改查及 PATCH 状态更新。</li><li>重启终端中的 Python 后端，刷新浏览器，验证 SQLite 本地数据库持久化有效，数据未丢失。</li><li>在终端执行 <code>git status</code>，确认 <code>tasks.db</code>、<code>.venv</code> 等运行时文件没有出现在待提交文件列表中（确保当前仓库的 <code>.gitignore</code> 生效）。</li></ul></li><li><strong>生成规范文档与提交代码</strong>：让 Harness 编写包含架构说明、接口规范与本地运行指南的 <code>README.md</code>，在 GitHub Desktop 中完成提交并推送到远端仓库（<em>如果已经完成附录 B，可以尝试不用 GitHub Desktop，而是使用 <code>git status → git diff → git add → git commit → git push</code> 完成本次项目提交</em>）。</li></ol><h4 id="今日验收-4"><a href="#今日验收-4" class="headerlink" title="今日验收"></a>今日验收</h4><ul><li><input disabled="" type="checkbox"> 成功创建独立的 <code>ddl-manager</code> 仓库，补齐了 <code>.gitignore</code>，配置并激活了 <code>.venv</code> 虚拟环境，生成了 <code>requirements.txt</code></li><li><input disabled="" type="checkbox"> Python FastAPI 后端成功运行，能在 <code>/docs</code> 中查看并调试接口（更新接口采用 PATCH）</li><li><input disabled="" type="checkbox"> 前端由 FastAPI 提供同源访问（未通过 Live Server 打开），相对路径 <code>fetch(&#39;/api/tasks&#39;)</code> 联调顺畅</li><li><input disabled="" type="checkbox"> SQLite 本地数据库持久化有效，且 <code>tasks.db</code>、<code>.venv</code> 等运行时文件已被 <code>.gitignore</code> 忽略未提交至 Git</li><li><input disabled="" type="checkbox"> 提交全部代码并推送到 GitHub 远端</li></ul><hr><h3 id="Day-6：项目实战二：Node-js-工程化-Canvas-游戏与公网部署"><a href="#Day-6：项目实战二：Node-js-工程化-Canvas-游戏与公网部署" class="headerlink" title="Day 6：项目实战二：Node.js 工程化 Canvas 游戏与公网部署"></a>Day 6：项目实战二：Node.js 工程化 Canvas 游戏与公网部署</h3><p><strong>目标</strong>：使用现代前端 Node.js 工具链（Vite 脚手架）构建模块化 Canvas 互动小游戏，加入原创机制与移动端触控，并通过 GitHub Pages 部署上线供全球访问。</p><h4 id="1-使用-Node-js-工具链初始化项目"><a href="#1-使用-Node-js-工具链初始化项目" class="headerlink" title="1. 使用 Node.js 工具链初始化项目"></a>1. 使用 Node.js 工具链初始化项目</h4><p>在 Day 2 安装的 Node.js 与 npm 环境，今天正式展现现代软件工程的威力。</p><ol><li>在 GitHub Desktop 中点击 <code>File</code> → <code>New Repository</code>，新建仓库 <code>canvas-retro-game</code>，Git Ignore 下拉选择 <strong>Node</strong>，点击 <code>Create Repository</code>。<ul><li>创建本地仓库后，点击顶部的 <strong>Publish repository</strong>。在发布窗口中确保仓库最终为 <strong>Public（公开）</strong>，以便后续使用 GitHub Pages（GitHub Free 免费账户的 Pages 仅对公开仓库开放）。确认远程仓库已经建立后再进入后续开发。</li></ul></li><li>在 VS Code 终端中使用 Vite 现代前端脚手架初始化工程：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm create vite@latest . -- --template vanilla --no-immediate</span><br></pre></td></tr></table></figure><ul><li><strong>处理非空目录提示</strong>：由于当前目录已经包含 GitHub Desktop 创建的仓库文件，终端可能会提示 <code>Current directory is not empty</code>。此时选择类似 <strong>Ignore files and continue</strong>（继续并忽略已有文件）的选项即可。</li></ul></li><li>安装依赖并启动本地开发服务：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm install</span><br><span class="line">npm run dev</span><br></pre></td></tr></table></figure>终端会输出本地开发地址（如 <code>http://localhost:5173</code>），支持极速热模块替换（HMR）。</li><li><strong>配置 Vite 的 <code>base</code> 路径</strong>：<br>在项目根目录下新建或修改 <code>vite.config.js</code>：<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; defineConfig &#125; <span class="keyword">from</span> <span class="string">&#x27;vite&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title function_">defineConfig</span>(&#123;</span><br><span class="line">  <span class="attr">base</span>: <span class="string">&#x27;/canvas-retro-game/&#x27;</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><ul><li><strong>为什么要配置 base</strong>：GitHub Pages 部署的项目地址通常形如 <code>https://用户名.github.io/canvas-retro-game/</code>。如果不配置子路径，网页打包后引入的 JS&#x2F;CSS 会默认从根域名根路径查找，导致 404 资源加载失败。如果你的仓库名不同，需将 <code>base</code> 同步修改为对应的仓库名。</li></ul></li><li>感受模块化开发优势：告别千行面条代码单文件，让 Harness 将游戏逻辑清晰拆分为 <code>main.js</code>（入口与循环）、<code>game.js</code>（核心实体逻辑）与 <code>style.css</code>。</li></ol><h4 id="2-迭代开发基础版本与移动端适配"><a href="#2-迭代开发基础版本与移动端适配" class="headerlink" title="2. 迭代开发基础版本与移动端适配"></a>2. 迭代开发基础版本与移动端适配</h4><p>在 Harness 中循序渐进推进：</p><ol><li><strong>基础游戏循环</strong>：<ul><li>使用 <code>requestAnimationFrame</code> 建立与浏览器刷新周期同步的动画循环。</li><li><strong>工程认知点</strong>：不要将动画循环机械假设为“固定 60FPS”。现代不同屏幕可能是 60Hz、90Hz、120Hz 甚至 144Hz 刷新率，因此游戏逻辑不要假设每一帧时间固定为 1&#x2F;60 秒。指挥 Harness 尽量使用前后两帧的时间戳差值（<code>deltaTime</code>）来计算角色位移与动画速度，保证在不同刷新率设备上移动速度一致。</li></ul></li><li><strong>移动端适配（核心教学点）</strong>：<ul><li>电脑端使用键盘方向键控制。</li><li><strong>手机端适配</strong>：在画布下方增加虚拟方向按键，确保手机端可以正常操作。</li></ul></li><li><strong>本地最高分记录</strong>：使用 LocalStorage 记录历史最高分，刷新不丢失。</li></ol><h4 id="3-核心挑战：必须实现-1-项原创玩法机制"><a href="#3-核心挑战：必须实现-1-项原创玩法机制" class="headerlink" title="3. 核心挑战：必须实现 1 项原创玩法机制"></a>3. 核心挑战：必须实现 1 项原创玩法机制</h4><p>简单的基础游戏代码网上现成范例过多，无法检验你的控制力。<strong>今天必须设计并让 AI 实现至少 1 个原创机制</strong>：</p><ul><li>例如贪吃蛇：吃到“金色沙漏”道具触发 5 秒子弹时间（减速）；或地图上动态生成激光障碍物；或增加“护盾果实”可免疫一次撞墙。</li><li>例如打砖块：不同颜色砖块击碎后掉落不同技能符文（多重分身球、加宽弹板）。</li></ul><h4 id="4-本地构建验证与-GitHub-Actions-自动部署-Pages"><a href="#4-本地构建验证与-GitHub-Actions-自动部署-Pages" class="headerlink" title="4. 本地构建验证与 GitHub Actions 自动部署 Pages"></a>4. 本地构建验证与 GitHub Actions 自动部署 Pages</h4><p><em>提示：已经掌握 Git CLI 的同学，可以尝试在本项目迭代中至少使用 3 次命令行 Commit，建立“完成一个独立功能 → 验证 → Commit”的工程节奏；未阅读附录 B 的同学继续使用 GitHub Desktop 即可。</em></p><ol><li><strong>本地生产打包与预览验证</strong>：<br>在项目根目录运行打包命令：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm run build</span><br></pre></td></tr></table></figure><ul><li><strong>工程原理解释</strong>：是 <strong>Vite</strong> 工具链将你的 ES 模块化源码、CSS 资源进行编译、合并与压缩打包，最终输出到 <code>dist</code> 目录中（而不是由“Node.js 自动打包”）。</li><li>运行本地生产预览命令：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm run preview</span><br></pre></td></tr></table></figure>在终端给出的地址中提前验证生产环境运行表现。</li><li><strong>重要说明</strong>：生产产物 <code>dist/</code> 属于编译输出，已被 <code>.gitignore</code> 自动忽略，<strong>切勿将 dist 手工 Commit 提交到仓库</strong>。</li></ul></li><li><strong>开启 GitHub Pages</strong>：<ul><li>打开 GitHub 仓库网页端 → <strong>Settings</strong> → <strong>Pages</strong>。</li><li>在 <code>Build and deployment</code> 的 <strong>Source</strong> 下拉菜单中选择 <strong>GitHub Actions</strong>。</li><li><strong>重要提醒</strong>：在首次推送 Pages deployment workflow 之前，先将当前仓库的 Pages Source 设置为 <strong>GitHub Actions</strong>。</li></ul></li><li><strong>配置 GitHub Actions 自动构建与部署</strong>：<br>未构建的 Vite 源码无法直接通过静态分支托管运行，必须由 CI&#x2F;CD 构建出 <code>dist</code> 成品再发布。本教程统一使用官方推荐的 GitHub Actions 自动流：<ul><li>在项目根目录下创建 <code>.github/workflows/deploy.yml</code>，让 Harness 按照 Vite 官方规范编写自动部署工作流：<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">name:</span> <span class="string">Deploy</span> <span class="string">static</span> <span class="string">content</span> <span class="string">to</span> <span class="string">Pages</span></span><br><span class="line"></span><br><span class="line"><span class="attr">on:</span></span><br><span class="line">  <span class="attr">push:</span></span><br><span class="line">    <span class="attr">branches:</span> [<span class="string">&#x27;main&#x27;</span>]</span><br><span class="line">  <span class="attr">workflow_dispatch:</span></span><br><span class="line"></span><br><span class="line"><span class="attr">permissions:</span></span><br><span class="line">  <span class="attr">contents:</span> <span class="string">read</span></span><br><span class="line">  <span class="attr">pages:</span> <span class="string">write</span></span><br><span class="line">  <span class="attr">id-token:</span> <span class="string">write</span></span><br><span class="line"></span><br><span class="line"><span class="attr">concurrency:</span></span><br><span class="line">  <span class="attr">group:</span> <span class="string">&#x27;pages&#x27;</span></span><br><span class="line">  <span class="attr">cancel-in-progress:</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"><span class="attr">jobs:</span></span><br><span class="line">  <span class="attr">deploy:</span></span><br><span class="line">    <span class="attr">environment:</span></span><br><span class="line">      <span class="attr">name:</span> <span class="string">github-pages</span></span><br><span class="line">      <span class="attr">url:</span> <span class="string">$&#123;&#123;</span> <span class="string">steps.deployment.outputs.page_url</span> <span class="string">&#125;&#125;</span></span><br><span class="line">    <span class="attr">runs-on:</span> <span class="string">ubuntu-latest</span></span><br><span class="line">    <span class="attr">steps:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Checkout</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/checkout@v7</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Set</span> <span class="string">up</span> <span class="string">Node</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/setup-node@v7</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">node-version:</span> <span class="number">24</span></span><br><span class="line">          <span class="attr">cache:</span> <span class="string">&#x27;npm&#x27;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Install</span> <span class="string">dependencies</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">npm</span> <span class="string">ci</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Build</span></span><br><span class="line">        <span class="attr">run:</span> <span class="string">npm</span> <span class="string">run</span> <span class="string">build</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Setup</span> <span class="string">Pages</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/configure-pages@v6</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Upload</span> <span class="string">artifact</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/upload-pages-artifact@v5</span></span><br><span class="line">        <span class="attr">with:</span></span><br><span class="line">          <span class="attr">path:</span> <span class="string">&#x27;./dist&#x27;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="attr">name:</span> <span class="string">Deploy</span> <span class="string">to</span> <span class="string">GitHub</span> <span class="string">Pages</span></span><br><span class="line">        <span class="attr">id:</span> <span class="string">deployment</span></span><br><span class="line">        <span class="attr">uses:</span> <span class="string">actions/deploy-pages@v5</span></span><br></pre></td></tr></table></figure></li><li>将 <code>vite.config.js</code>、<code>.github/workflows/deploy.yml</code> 以及项目源码 Commit 并 Push 到 GitHub 的 <code>main</code> 分支。</li></ul></li><li><strong>查看 Actions 部署</strong>：<ul><li>Push 后切到 GitHub 仓库网页端的 <strong>Actions</strong> 标签页，点击 <code>Deploy static content to Pages</code> 工作流。</li><li>观察自动触发的流水线执行：依次完成 checkout → setup Node → <code>npm ci</code> → <code>npm run build</code> 打包 → upload artifact（上传产物）→ deploy Pages（部署上线）。</li><li>等待 workflow 成功后在 Actions 页面或 Settings → Pages 获取专属公网链接（形如 <code>https://你的用户名.github.io/canvas-retro-game/</code>）。</li></ul></li><li><strong>移动端实测</strong>：<ul><li>用手机浏览器直接输入链接或扫码测试，体验虚拟按键，分享给同学试玩。</li></ul></li></ol><h4 id="今日验收-5"><a href="#今日验收-5" class="headerlink" title="今日验收"></a>今日验收</h4><h5 id="1-MVP-核心验收（基础闭环）"><a href="#1-MVP-核心验收（基础闭环）" class="headerlink" title="1. MVP 核心验收（基础闭环）"></a>1. MVP 核心验收（基础闭环）</h5><ul><li><input disabled="" type="checkbox"> 熟练掌握 Node.js &#x2F; Vite 工具链的运行、调试与本地打包（<code>npm run dev</code> &#x2F; <code>npm run build</code> &#x2F; <code>npm run preview</code>）</li><li><input disabled="" type="checkbox"> 配置了正确的 <code>base: &#39;/canvas-retro-game/&#39;</code> 路径，处理了非空目录初始化情况</li><li><input disabled="" type="checkbox"> 游戏基于 Canvas 能够流畅运行，键盘方向键控制正常</li><li><input disabled="" type="checkbox"> 动画循环采用 <code>requestAnimationFrame</code> 配合 <code>deltaTime</code> 驱动平滑位移</li><li><input disabled="" type="checkbox"> 使用 LocalStorage 记录历史最高分，刷新页面不丢失</li><li><input disabled="" type="checkbox"> 产物 <code>dist/</code> 未手工提交至 Git，通过 GitHub Actions 成功自动化部署至 GitHub Pages</li><li><input disabled="" type="checkbox"> 手机浏览器能成功打开公网在线页面</li></ul><h5 id="2-进阶目标（交互与原创）"><a href="#2-进阶目标（交互与原创）" class="headerlink" title="2. 进阶目标（交互与原创）"></a>2. 进阶目标（交互与原创）</h5><ul><li><input disabled="" type="checkbox"> 画布下方实现手机端虚拟方向按键，手机上可操作游玩</li><li><input disabled="" type="checkbox"> 成功设计并让 Harness 实现了至少 1 项原创玩法机制（如子弹时间、技能掉落、动态激光等）</li></ul><h5 id="3-挑战项（可选探索）"><a href="#3-挑战项（可选探索）" class="headerlink" title="3. 挑战项（可选探索）"></a>3. 挑战项（可选探索）</h5><ul><li><input disabled="" type="checkbox"> 实现更复杂的原创机制组合或得分特效</li><li><input disabled="" type="checkbox"> 处理移动端 DPR &#x2F; Retina 屏幕的高清 Canvas 缩放适配</li></ul><hr><h3 id="Day-7：复盘总结与计算机专业进阶"><a href="#Day-7：复盘总结与计算机专业进阶" class="headerlink" title="Day 7：复盘总结与计算机专业进阶"></a>Day 7：复盘总结与计算机专业进阶</h3><p><strong>目标</strong>：完成学习复盘，梳理技术盲区，规划大学后续的计算机核心进阶路径。</p><h4 id="1-产出复盘文档"><a href="#1-产出复盘文档" class="headerlink" title="1. 产出复盘文档"></a>1. 产出复盘文档</h4><p>在 <code>vibe-coding-101</code> 仓库中新建 <code>REVIEW.md</code>（字数约 500~800 字），记录：</p><ul><li>哪些提示词交互一次成功、哪些反复出现偏差，分析根本原因。</li><li>主力与备用两款 Harness 在代码生成、架构重构与查错时的体验差异。</li><li>列出本周遇到并记录下来的“底层知识盲区清单”（如浏览器事件循环、FastAPI 异步协程、RESTful 设计、Canvas 渲染机制等）。</li></ul><h4 id="2-利用-Harness-进行深度概念答疑"><a href="#2-利用-Harness-进行深度概念答疑" class="headerlink" title="2. 利用 Harness 进行深度概念答疑"></a>2. 利用 Harness 进行深度概念答疑</h4><p>在 Harness 对话框中，针对清单上的盲区逐条提问：</p><blockquote><p><em>“请结合 Day 5 的 FastAPI 项目与 Day 6 的 Canvas 游戏解释：</em></p><ol><li><em>当我们使用同步的标准库 sqlite3 时，为什么路由可以写成普通 def？</em></li><li><em>FastAPI 中什么时候应该使用 async def？</em></li><li><em>如果未来把数据库换成真正支持 await 的异步数据库驱动，代码结构可能发生什么变化？</em></li><li><em>Day 6 为什么使用 requestAnimationFrame 而不是简单的 setInterval？”</em></li></ol></blockquote><h4 id="3-衔接大学计算机专业核心课程"><a href="#3-衔接大学计算机专业核心课程" class="headerlink" title="3. 衔接大学计算机专业核心课程"></a>3. 衔接大学计算机专业核心课程</h4><p>本周你搭建的开发环境与工程思维，是大学核心专业课的最佳实践土壤：</p><ol><li><strong>C&#x2F;C++ 与底层系统课程（《程序设计基础》《操作系统》《计算机组成原理》）</strong>：<ul><li>Day 2 安装的 GCC&#x2F;G++ 环境将伴随你的大学前两年。尝试用 C&#x2F;C++ 编写一个命令行版链表或模拟简易操作系统的进程调度器，让 AI 深入解释指针、内存分布与段错误（Segmentation Fault）。</li></ul></li><li><strong>Java 与企业级工程（《面向对象程序设计》《软件工程》）</strong>：<ul><li>Day 2 安装的 JDK 帮助你衔接后续的数据结构大作业与 Spring Boot 企业级实战。尝试体会强类型面向对象架构与设计模式的魅力。</li></ul></li><li><strong>算法与数据结构实战</strong>：<ul><li>在 LeetCode 刷题时，尝试先自己写出核心思路，让 Harness 为你构造极端边界反例（如空指针、超大数值溢出），培养严密的逻辑思维。</li></ul></li><li><strong>GitHub Student Developer Pack</strong>：<ul><li>有兴趣的学生可自行搜索 GitHub Education &#x2F; Student Developer Pack，按照 GitHub 当前官方页面了解申请资格、材料和权益。</li></ul></li></ol><h4 id="今日验收-6"><a href="#今日验收-6" class="headerlink" title="今日验收"></a>今日验收</h4><ul><li><input disabled="" type="checkbox"> 完成 <code>REVIEW.md</code> 复盘文档并提交入库</li><li><input disabled="" type="checkbox"> 搞懂了盲区清单中的至少 2 个底层计算机核心原理</li><li><input disabled="" type="checkbox"> 明确了后续专业课与多语言实践项目的衔接规划</li></ul><hr><h2 id="四、常见问题与实用技巧"><a href="#四、常见问题与实用技巧" class="headerlink" title="四、常见问题与实用技巧"></a>四、常见问题与实用技巧</h2><h3 id="1-修改陷入循环时的应对三板斧"><a href="#1-修改陷入循环时的应对三板斧" class="headerlink" title="1. 修改陷入循环时的应对三板斧"></a>1. 修改陷入循环时的应对三板斧</h3><ol><li><strong>第一步（版本回退）</strong>：立即在 Harness 中停止任务，打开 GitHub Desktop。<ul><li>若只是当前未提交的改动乱了，直接在 Changes 列表中的文件上右键点击 <strong>Discard Changes</strong> 瞬间还原；</li><li>若已经连续提交了多次错误代码，切换到 <strong>History</strong> 标签页，<strong>从最新的 Commit 开始由新到旧逐个点击【Revert changes in commit】</strong>，一路安全回退到上一个正常运行的节点。</li></ul></li><li><strong>第二步</strong>：将需求进一步拆分细化，每次只提出一个单一、具体的修改指令。</li><li><strong>第三步</strong>：如果主力 Harness 依然无法解决，切换到备用 Harness 重新描述问题。</li></ol><h3 id="2-安全与权限防线"><a href="#2-安全与权限防线" class="headerlink" title="2. 安全与权限防线"></a>2. 安全与权限防线</h3><h4 id="1-Agent-终端权限"><a href="#1-Agent-终端权限" class="headerlink" title="(1) Agent 终端权限"></a>(1) Agent 终端权限</h4><ul><li><strong>第一周坚决不开全自动模式（Auto-approve &#x2F; YOLO）</strong>：AI 每次执行终端命令前必须人工过目确认。</li><li><strong>警惕高危文件操作</strong>：对涉及 <code>rm</code>、<code>del</code>、<code>git reset --hard</code>、批量移动或覆盖文件的指令保持警惕，防止误删或弄乱本地真实文件。</li></ul><h4 id="2-API-Key-规范管理与泄露应急"><a href="#2-API-Key-规范管理与泄露应急" class="headerlink" title="(2) API Key 规范管理与泄露应急"></a>(2) API Key 规范管理与泄露应急</h4><ul><li><strong>正确做法（.env + 环境变量 + .gitignore）</strong>：<ol><li>敏感 Key 存入本地根目录的 <code>.env</code> 文件。</li><li>确保 <code>.gitignore</code> 中已写入 <code>.env</code>，彻底阻断 Git 追踪。</li><li>代码通过环境变量读取（如 Python 的 <code>os.getenv</code>）。公开仓库可附带 <code>.env.example</code> 占位模板。</li></ol></li><li><strong>Key 泄露应急处理</strong>：Key 一旦被 Commit 并推送到远端，即使后来从当前代码中删除，也仍可能存在于既有 Git 历史、远程仓库副本或其他已获取的副本中。因此一旦发生泄露，应立即在厂商后台作废旧 Key 并重新生成。后续清理 Git 历史只能作为补救措施，不能替代凭据轮换。</li></ul><h3 id="3-工具与流程速查表"><a href="#3-工具与流程速查表" class="headerlink" title="3. 工具与流程速查表"></a>3. 工具与流程速查表</h3><ul><li><strong>代码与项目基座</strong>：VS Code（负责代码浏览、扩展插件与终端运行）。</li><li><strong>开发环境</strong>：Python (3.12&#x2F;3.13) &#x2F; Node.js 24 LTS &#x2F; JDK 17&#x2F;21 &#x2F; GCC-G++ &#x2F; Git。安装完成后需重启 VS Code，并通过 Day 2 的环境自检命令确认 PATH 配置正常。</li><li><strong>编程助手（Harness）推荐排序</strong>：<ol><li>Codex App（OpenAI &#x2F; GPT）</li><li>Claude Code Desktop（Anthropic &#x2F; Claude）</li><li>DeepSeek Harness（DeepSeek）</li><li>ZCode（Z.ai &#x2F; GLM）</li><li>Kimi Code（Moonshot &#x2F; Kimi）</li></ol></li><li><strong>版本控制与安全网</strong>：GitHub Desktop（新手图形化操作，负责可视化提交、安全丢弃 Discard 与历史版本 Revert）+ Git CLI（推荐进阶，详见<a href="#%E9%99%84%E5%BD%95-b%E7%BA%AF%E5%91%BD%E4%BB%A4%E8%A1%8C-git-%E5%85%A5%E9%97%A8">附录 B</a>）。</li><li><strong>标准开发闭环</strong>：明确需求文档（<code>PROJECT.md</code>）→ Harness 规则约束 → 分步规划修改 → 运行验证 → Git 存档。</li><li><strong>第三方模型切换</strong>：若没有官方外币订阅条件，想要使用 Claude Code &#x2F; Codex 工作流接入国内模型 API，请详细查阅<a href="#%E9%99%84%E5%BD%95-a%E5%8F%AF%E9%80%89%E9%AB%98%E7%BA%A7%E9%85%8D%E7%BD%AE%E4%B8%8E%E7%AC%AC%E4%B8%89%E6%96%B9%E6%A8%A1%E5%9E%8B%E6%8E%A5%E5%85%A5cc-switch-%E4%B8%8E-api-%E6%96%B9%E8%A8%80">附录 A：可选高级配置与第三方模型接入</a>。</li></ul><hr><h2 id="附录-A：可选高级配置与第三方模型接入（CC-Switch-与-API-方言）"><a href="#附录-A：可选高级配置与第三方模型接入（CC-Switch-与-API-方言）" class="headerlink" title="附录 A：可选高级配置与第三方模型接入（CC Switch 与 API 方言）"></a>附录 A：可选高级配置与第三方模型接入（CC Switch 与 API 方言）</h2><blockquote><p>[!NOTE]<br><strong>适用场景</strong>：如果你没有外币信用卡无法订阅 Claude Pro 或 ChatGPT Plus，但希望体验 Claude Code 或 Codex 的强大工程代理工作流，可以让这些工具“换上国产模型的大脑”。若你使用的是官方直连的国产 Harness（如 DeepSeek Harness、ZCode、Kimi Code），可直接跳过本附录。</p></blockquote><h3 id="1-核心原理：Claude-Code-与-Codex-常见的两类第三方模型兼容协议"><a href="#1-核心原理：Claude-Code-与-Codex-常见的两类第三方模型兼容协议" class="headerlink" title="1. 核心原理：Claude Code 与 Codex 常见的两类第三方模型兼容协议"></a>1. 核心原理：Claude Code 与 Codex 常见的两类第三方模型兼容协议</h3><p>Harness（工程驱动工具）和模型（底层大脑）在架构上是解耦的。不同 Harness 有各自的通信协议与扩展机制，例如 Claude Code 与 Codex 常见以下两类第三方模型兼容方式：</p><ul><li><strong>Claude Code 常见的 Anthropic-compatible 协议</strong>：Claude Code 通常通过厂商提供的兼容 Base URL 与对应鉴权配置接入第三方模型。不同厂商使用的鉴权环境变量、模型映射与附加配置可能不同，请以目标厂商当前针对 Claude Code 的官方接入文档为准。</li><li><strong>Codex 常见的 OpenAI Responses API 协议</strong>：读取配置文件中的 <code>model_providers</code> 设置，通过兼容 OpenAI Responses API 规范的端点进行调用。</li><li><strong>其他 Harness</strong>：如 DeepSeek Harness、ZCode、Kimi Code 等原生客户端拥有自己的 Provider 抽象、插件系统或专用接口规范。</li></ul><h3 id="2-国内主流模型-Coding-接口规范（更新基准）"><a href="#2-国内主流模型-Coding-接口规范（更新基准）" class="headerlink" title="2. 国内主流模型 Coding 接口规范（更新基准）"></a>2. 国内主流模型 Coding 接口规范（更新基准）</h3><table><thead><tr><th align="center">厂商</th><th>给 Claude Code 用（Anthropic 方言兼容）</th><th>给 Codex 用（OpenAI 方言兼容）</th><th>Coding 模型示例</th><th>说明</th></tr></thead><tbody><tr><td align="center"><strong>DeepSeek</strong></td><td><code>https://api.deepseek.com/anthropic</code></td><td><code>https://api.deepseek.com</code></td><td><code>deepseek-v4-flash</code><br><code>deepseek-v4-pro</code></td><td>官方已演进至 V4 系列；Claude Code 可通过环境变量或别名映射模型档位</td></tr><tr><td align="center"><strong>Kimi Code</strong></td><td><code>https://api.kimi.com/coding/</code></td><td><code>https://api.kimi.com/coding/v1</code></td><td>请查阅 Kimi Code 当前官方模型列表</td><td><strong>请特别注意</strong>：Kimi Code 官方 Coding API 采用独立的 <code>api.kimi.com/coding</code> 端点，与 Moonshot 开放平台的通用 API 互为独立服务</td></tr><tr><td align="center"><strong>智谱 GLM</strong></td><td><code>https://open.bigmodel.cn/api/anthropic</code></td><td>请查阅智谱当前官方 Codex &#x2F; Coding Plan 接入文档</td><td><code>glm-5.3</code> &#x2F; 最新代码模型</td><td>Coding PaaS 专线为大代码工程高并发设计</td></tr></tbody></table><blockquote><p>[!TIP]<br><strong>重要认知</strong>：各大 AI 厂商的模型迭代速度极快，端点与模型 ID 会随着版本持续演进，表中仅为 Coding 模型示例。<strong>具体有效的模型代号与完整接入规范请务必以各厂商开发者控制台当前官方“Coding API &#x2F; 编程助手接入”文档为准</strong>。</p></blockquote><h3 id="3-图形化管理神器：CC-Switch"><a href="#3-图形化管理神器：CC-Switch" class="headerlink" title="3. 图形化管理神器：CC Switch"></a>3. 图形化管理神器：CC Switch</h3><p>手动编辑各个工具的底层 JSON&#x2F;TOML 配置文件非常繁琐，极易因格式拼写错误导致启动失败。推荐使用开源的图形化切换工具 <strong>CC Switch</strong>。</p><ul><li><strong>仓库地址</strong>：<a href="https://github.com/farion1231/cc-switch">github.com&#x2F;farion1231&#x2F;cc-switch</a></li><li><strong>核心功能</strong>：一键保存多家模型供应商的 Base URL、API Key 与模型参数；通过图形界面无缝切换 Claude Code 与 Codex 的当前大脑，切换时自动备份旧配置。</li><li><strong>使用步骤</strong>：<ol><li>在厂商开发者控制台完成注册，创建专属 API Key（务必复制保存至安全位置）。</li><li>下载并安装 CC Switch，选择需要配置的工具标签页（Claude Code 或 Codex）。</li><li>点击“添加供应商”，选择预设厂商（如 DeepSeek、智谱、Kimi），粘贴 API Key 并保存。</li><li>点击“启用”，CC Switch 会自动改写底层的配置文件。</li><li><strong>关键一步：彻底重启 Harness</strong>！Harness 仅在启动初始化时读取一次配置，重启后输入 <code>/status</code> 查看端点与模型是否生效，并在厂商控制台观察用量消耗。</li></ol></li></ul><h3 id="4-不用-CC-Switch-的底层手动配置参考"><a href="#4-不用-CC-Switch-的底层手动配置参考" class="headerlink" title="4. 不用 CC Switch 的底层手动配置参考"></a>4. 不用 CC Switch 的底层手动配置参考</h3><p>如果你希望了解底层机制，或在无 GUI 环境下部署，可直接手动修改配置文件（与 CC Switch 二选一即可）：</p><h4 id="1-Claude-Code-手动配置"><a href="#1-Claude-Code-手动配置" class="headerlink" title="(1) Claude Code 手动配置"></a>(1) Claude Code 手动配置</h4><p>Claude Code 会在启动时从配置文件（如 <code>~/.claude/settings.json</code>，Windows 路径为 <code>C:\Users\你的用户名\.claude\settings.json</code>）或系统环境变量中读取 Base URL、API Key 与模型映射规则：</p><ul><li>Kimi Code 的 Anthropic-compatible Base URL 为 <code>https://api.kimi.com/coding/</code>（请注意与 Moonshot 通用开放平台区隔）；</li><li>认证环境变量应使用 <code>ANTHROPIC_API_KEY</code>；</li><li>Kimi Code 针对 Claude Code 的配置项（包含模型 tier、上下文窗口、subagent 支持、模型别名等字段）更新速度很快，配置时请直接以 <strong>Kimi Code 当前官方“Claude Code 接入”文档提供的完整配置模板</strong>为准，复制写入 <code>settings.json</code> 即可。请勿仅手工修改单个模型字段或使用残缺模板，以免导致高级特性或子代理调用异常。</li></ul><h4 id="2-Codex-手动配置"><a href="#2-Codex-手动配置" class="headerlink" title="(2) Codex 手动配置"></a>(2) Codex 手动配置</h4><ul><li><strong>官方推荐方案（一键配置脚本）</strong>：<br>DeepSeek 官方提供了自动配置脚本，会自动写入所需的 <code>models.json</code> 与 <code>config.toml</code>：<ul><li>**Windows (PowerShell)**：<figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">irm</span> https://cdn.deepseek.com/api<span class="literal">-docs</span>/codex<span class="literal">-deepseek-setup-en</span>.ps1 | <span class="built_in">iex</span></span><br></pre></td></tr></table></figure></li><li><strong>macOS &#x2F; Linux</strong>：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bash &lt;(curl -fsSL https://cdn.deepseek.com/api-docs/codex-deepseek-setup-en.sh)</span><br></pre></td></tr></table></figure></li></ul></li><li><strong>手动配置说明（备用方案）</strong>：<br>如果希望手工理解 Provider、model catalog、认证和 Responses API 配置，请直接参考 DeepSeek 当前官方 Codex 集成文档。相关配置项（如 <code>models.json</code> 中的 model catalog 与 <code>config.toml</code> 中的 provider 映射）会随 Codex 与 DeepSeek 版本迭代，本指南不长期复制易过期的完整配置快照。</li></ul><h4 id="3-桌面-App-不读取-Provider-配置时的兜底方式"><a href="#3-桌面-App-不读取-Provider-配置时的兜底方式" class="headerlink" title="(3) 桌面 App 不读取 Provider 配置时的兜底方式"></a>(3) 桌面 App 不读取 Provider 配置时的兜底方式</h4><p>如果桌面 App 没有读取第三方 Provider 配置，先确认该版本桌面 App 是否支持这套配置文件。如果不支持，请打开 Claude Code &#x2F; Codex 当前官方文档，按照官方当前推荐方式安装并使用 CLI 版本验证 Provider 配置。</p><p>CLI 安装方式和包名可能随版本变化，因此以官方文档为准，不在本指南中长期硬编码安装命令。</p><h3 id="5-注意事项与安全防线"><a href="#5-注意事项与安全防线" class="headerlink" title="5. 注意事项与安全防线"></a>5. 注意事项与安全防线</h3><ul><li><strong>API Key 敏感安全铁律</strong>：<ul><li>API Key 属于极其敏感的高权限凭据。CC Switch、<code>~/.claude/settings.json</code>、<code>~/.codex/config.toml</code> 以及系统环境变量中均可能以明文或可读取形式保存 Key。</li><li><strong>严守安全边界</strong>：绝对不要截图包含 Key 的界面发到交流群；不要将相关用户配置文件复制到项目仓库中；不要把 Key 写入 <code>README.md</code>；绝对禁止将 Key Commit 提交到 Git。</li><li><strong>泄露应急处置</strong>：一旦怀疑或确认 Key 发生泄露，必须<strong>立即前往对应厂商控制台作废（Revoke）旧 Key 并重新生成</strong>，绝不能仅仅删除本地文件中的明文或覆盖 Git 提交。</li><li><strong>防盗刷与消费监控</strong>：<strong>如平台支持</strong>，建议开启余额或用量提醒、消费限额等费用控制功能，并定期检查 Coding Plan &#x2F; API 的使用量和费用，避免自动化任务异常造成意外消耗。</li></ul></li><li><strong>第三方兼容性折损</strong>：第三方模型走 Claude Code &#x2F; Codex 协议时，部分专属工具调用特性或深度思考反馈可能存在微小兼容性差异，遇到代码无关的报错，可先用小型任务测试连通性。模型代号、API 端点与 Provider 字段迭代迅速，请务必以各厂商开发者平台当前最新官方文档为准。</li></ul><hr><h2 id="附录-B：纯命令行-Git-入门"><a href="#附录-B：纯命令行-Git-入门" class="headerlink" title="附录 B：纯命令行 Git 入门"></a>附录 B：纯命令行 Git 入门</h2><blockquote><p>[!NOTE]<br>正文为了降低第一周的学习门槛，主要使用 GitHub Desktop 完成版本管理。但作为计算机专业学生，仍然建议掌握最基本的 Git 命令行操作。</p><p>本附录不会涉及复杂的 Branch、Rebase、Cherry-pick 等高级内容，只学习日常开发最常用的一条主线：</p><p><strong><code>status → diff → add → commit → push</code></strong></p></blockquote><h3 id="1-Git-的四层结构与数据流向"><a href="#1-Git-的四层结构与数据流向" class="headerlink" title="1. Git 的四层结构与数据流向"></a>1. Git 的四层结构与数据流向</h3><p>理解 Git 命令行的关键，在于掌握代码改动在计算机中流转的<strong>四层结构</strong>：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">工作区 (Working Directory)</span><br><span class="line">  │</span><br><span class="line">  │ git add</span><br><span class="line">  ▼</span><br><span class="line">暂存区 (Staging Area / Index)</span><br><span class="line">  │</span><br><span class="line">  │ git commit</span><br><span class="line">  ▼</span><br><span class="line">本地 Git 仓库 (Local Repository)</span><br><span class="line">  │</span><br><span class="line">  │ git push</span><br><span class="line">  ▼</span><br><span class="line">GitHub 远程仓库 (Remote Repository)</span><br></pre></td></tr></table></figure><p>远程同步的数据流向则相反：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">GitHub 远程仓库 (Remote Repository)</span><br><span class="line">  │</span><br><span class="line">  │ git pull</span><br><span class="line">  ▼</span><br><span class="line">本地仓库与工作区</span><br></pre></td></tr></table></figure><h4 id="四层结构的含义"><a href="#四层结构的含义" class="headerlink" title="四层结构的含义"></a>四层结构的含义</h4><ul><li><strong>工作区（Working Directory）</strong>：你在 VS Code 中正在查看、编辑的代码目录。你随时打字、修改、保存的文件都在这一层。</li><li><strong>暂存区（Staging Area &#x2F; Index）</strong>：准备放入下一个版本的“打包工作台”。你可以挑选部分改动放进来，决定哪些改动将进入下一次提交。</li><li><strong>本地 Git 仓库（Local Repository）</strong>：本地电脑由 Git 维护的版本数据库（保存在项目根目录隐藏的 <code>.git</code> 文件夹中）。每当你执行一次提交，就会在这里生成一个正式的版本快照。</li><li><strong>GitHub 远程仓库（Remote Repository）</strong>：托管在云端服务器的代码仓库，用于代码备份、跨设备同步以及向他人展示你的作品。</li></ul><h4 id="核心命令的数据流动"><a href="#核心命令的数据流动" class="headerlink" title="核心命令的数据流动"></a>核心命令的数据流动</h4><ul><li><code>git add</code>：将工作区中挑选的文件改动放入<strong>暂存区</strong>（选择哪些修改进入下一次版本）；</li><li><code>git commit</code>：把暂存区中的所有修改打包保存为一个新版本，正式记入<strong>本地 Git 仓库</strong>；</li><li><code>git push</code>：把本地 Git 仓库中新生成的 Commit 上传至 <strong>GitHub 远程仓库</strong>；</li><li><code>git pull</code>：把 GitHub 远程仓库上的新版本同步拉取到<strong>本地仓库与工作区</strong>。</li></ul><blockquote><p>[!IMPORTANT]<br><strong>必须重点牢记：<code>git commit</code> 绝不等于上传 GitHub！</strong></p><ul><li><code>git commit</code> 完全是在你本地硬盘上记录快照，整个过程<strong>不需要连接网络</strong>。哪怕你的电脑断开 Wi-Fi、在没有网络的自习室，你依然可以随时执行 <code>git commit</code> 记录每一步进展。</li><li>只有当你执行 <code>git push</code>（上传）或 <code>git pull</code>（下载）时，Git 才会真正发起网络请求与 GitHub 远程服务器通信。</li></ul></blockquote><hr><h3 id="2-安装-Git-CLI"><a href="#2-安装-Git-CLI" class="headerlink" title="2. 安装 Git CLI"></a>2. 安装 Git CLI</h3><blockquote><p>[!CAUTION]<br><strong>常见误解</strong>：安装了 GitHub Desktop 并不意味着 VS Code 或 PowerShell 终端中一定可以直接执行 <code>git</code> 命令。GitHub Desktop 内部自带的 Git 默认不会自动配置进系统的全局环境变量，因此需要单独确认并安装系统级 Git CLI。</p></blockquote><h4 id="1-Windows-用户安装"><a href="#1-Windows-用户安装" class="headerlink" title="(1) Windows 用户安装"></a>(1) Windows 用户安装</h4><ul><li>前往官方网站下载安装包：<a href="https://git-scm.com/">git-scm.com</a></li><li>下载 64-bit Git for Windows 安装程序，运行安装包，一路保持默认推荐配置连续点击“Next”即可。</li><li><strong>关键操作</strong>：安装完成后，<strong>必须彻底关闭并重新启动 VS Code</strong>，VS Code 内置终端才能识别到新的环境变量。</li><li>验证安装（在 VS Code 内置终端中输入并回车）：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git --version</span><br></pre></td></tr></table></figure>如果正确输出了类似 <code>git version 2.4x.x.windows.x</code> 的版本号，说明安装成功。</li></ul><h4 id="2-macOS-Linux-用户"><a href="#2-macOS-Linux-用户" class="headerlink" title="(2) macOS &#x2F; Linux 用户"></a>(2) macOS &#x2F; Linux 用户</h4><ul><li>打开终端，首先执行自检命令：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git --version</span><br></pre></td></tr></table></figure></li><li>如果系统已经正确输出了版本号，说明开发环境已自带 Git，无需重复安装。</li><li>若提示找不到命令，macOS 会自动弹出提示安装 Command Line Tools，Linux 用户按各自发行版的标准方式安装即可（本附录不展开包管理器细节）。</li></ul><hr><h3 id="3-第一次配置-Git-身份信息"><a href="#3-第一次配置-Git-身份信息" class="headerlink" title="3. 第一次配置 Git 身份信息"></a>3. 第一次配置 Git 身份信息</h3><p>首次使用 Git 命令行前，需要告诉 Git 你的身份，以便在每一个 Commit 中记录作者信息：</p><p>在 VS Code 内置终端中依次执行以下两行命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global user.name <span class="string">&quot;你的名字或希望显示的提交作者名&quot;</span></span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global user.email <span class="string">&quot;你的 GitHub 邮箱&quot;</span></span><br></pre></td></tr></table></figure><p>检查配置是否设置成功：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git config --global user.name</span><br><span class="line">git config --global user.email</span><br></pre></td></tr></table></figure><blockquote><p>[!NOTE]<br>这里的 <code>user.name</code> 和 <code>user.email</code> 是记录在 Commit 提交历史中的“作者签名”，让其他人知道每一行代码是谁提交的；<code>user.name</code> 是作者显示名，<strong>不要求等于 GitHub 用户名</strong>。关于 <code>user.email</code>，如果希望 GitHub 正确将提交关联到你的个人贡献图，推荐使用 GitHub 账号已验证的邮箱；如果不希望公开真实邮箱，也可以使用 GitHub 提供的 noreply 邮箱。它<strong>不是你的 GitHub 登录账号或密码</strong>。</p></blockquote><hr><h3 id="4-git-status：你的首要雷达"><a href="#4-git-status：你的首要雷达" class="headerlink" title="4. git status：你的首要雷达"></a>4. <code>git status</code>：你的首要雷达</h3><p>这是你在日常终端开发中使用频率最高、也是最安全的命令。在项目终端中执行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git status</span><br></pre></td></tr></table></figure><p>它可以让你一眼看清当前项目的完整状态：</p><ul><li><strong>当前所在分支</strong>：例如 <code>On branch main</code>。</li><li><strong>哪些文件被修改了但未暂存</strong>：以红色列出（<code>Changes not staged for commit</code>）。</li><li><strong>哪些是新创建但尚未被 Git 追踪的文件</strong>：以红色列出（<code>Untracked files</code>）。</li><li><strong>哪些修改已经进入了暂存区</strong>：以绿色列出（<code>Changes to be committed</code>）。</li><li><strong>本地与远程的同步状态</strong>：例如提示本地分支领先远程（<code>Your branch is ahead of &#39;origin/main&#39; by 1 commit</code>）或已保持最新。<ul><li><em>重要认知</em>：<code>git status</code> 展示的是当前<strong>本地保存的远程跟踪状态</strong>（例如 <code>origin/main</code> 是本地记录的远程跟踪分支快照），<strong>并不会每次执行都主动联网询问 GitHub</strong>。在多人协作或远程可能发生变化的场景下，需要先执行 Fetch 或 Pull，才能刷新并得到真正的最新远程状态。</li></ul></li></ul><blockquote><p>[!TIP]<br><strong>黄金法则</strong>：遇到任何 Git 疑问、报错或不确定代码处于什么状态时，第一反应通常应该先执行 <code>git status</code>。</p><p><code>git status</code> 是一个纯粹的“只读”命令，执行一千次也不会增删改你的任何一行代码，初学者遇到任何情况都可以大胆运行它来了解现状。</p></blockquote><hr><h3 id="5-git-diff：提交前审查每一行改动"><a href="#5-git-diff：提交前审查每一行改动" class="headerlink" title="5. git diff：提交前审查每一行改动"></a>5. <code>git diff</code>：提交前审查每一行改动</h3><p>在进行任何提交之前，我们需要看清楚自己到底改了哪些代码行。</p><p>打开 Day 1 创建的 <code>vibe-coding-101</code> 仓库，在 VS Code 中给 <code>index.html</code> 的某个标题文字稍微增加几个字并保存，然后在内置终端中运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git diff</span><br></pre></td></tr></table></figure><p>终端会输出精细的代码对比：</p><ul><li>绿色带有 <code>+</code> 前缀的行：表示本次新增或修改后的代码行；</li><li>红色带有 <code>-</code> 前缀的行：表示本次被替换或删除的旧代码行。</li></ul><p>这完全等价于你在 GitHub Desktop 界面左侧点击变更文件后，右侧展示的代码对比面板。</p><blockquote><p>[!IMPORTANT]<br><strong>核心工程纪律：Commit 前先看 Diff。</strong><br>在 Vibe Coding 模式下，AI 编程助手（Harness）有时可能会在不知不觉中误改与当前任务无关的模块、遗留临时调试日志（如 <code>console.log</code> 或临时测试函数），甚至误修改了关键配置。养成 Commit 前先执行 <code>git diff</code> 审查每一处增删的习惯，能有效防止脏代码混入版本库。</p></blockquote><hr><h3 id="6-git-add：挑选改动进入暂存区"><a href="#6-git-add：挑选改动进入暂存区" class="headerlink" title="6. git add：挑选改动进入暂存区"></a>6. <code>git add</code>：挑选改动进入暂存区</h3><p>审查完改动无误后，我们需要将改动送入“暂存区”（打包台）。</p><p>根据实际需求，常用的两种添加方式如下：</p><ul><li><strong>精确暂存指定文件（推荐）</strong>：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add index.html</span><br></pre></td></tr></table></figure></li><li><strong>暂存当前目录下的所有改动</strong>：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add .</span><br></pre></td></tr></table></figure></li></ul><h4 id="深刻理解暂存区"><a href="#深刻理解暂存区" class="headerlink" title="深刻理解暂存区"></a>深刻理解暂存区</h4><p>暂存区是提交前的一个缓冲地带。假设你本次同时修改了 5 个文件，其中 2 个属于“修复倒计时计算 Bug”，另外 3 个属于“美化按钮样式”。为了保持版本记录的独立与纯粹，你可以先 <code>git add</code> 那 2 个文件提交一个 Bug 修复版本，再 <code>git add</code> 剩下的文件提交美化版本。</p><blockquote><p>[!WARNING]<br><strong>绝对不要误以为 <code>git add .</code> 等于保存了版本！</strong><br><code>git add</code> 只是确定“下一次 Commit 准备包含哪些修改”，在真正执行 <code>commit</code> 之前，版本库中没有任何新快照生成。</p></blockquote><p>执行 <code>git add index.html</code> 后，再次执行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git status</span><br></pre></td></tr></table></figure><p>观察输出变化：原本标红的文件现在变成了绿色的 <code>Changes to be committed</code>，说明该文件已成功进入暂存区，随时等待打包提交。</p><hr><h3 id="7-git-commit：为暂存区修改生成正式版本"><a href="#7-git-commit：为暂存区修改生成正式版本" class="headerlink" title="7. git commit：为暂存区修改生成正式版本"></a>7. <code>git commit</code>：为暂存区修改生成正式版本</h3><p>将暂存区的内容正式封箱，并在本地生成一个带有版本号的正式提交快照：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git commit -m <span class="string">&quot;完善倒计时页面&quot;</span></span><br></pre></td></tr></table></figure><ul><li><strong>参数解释</strong>：<code>-m</code> 代表 message（提交说明），双引号内填写针对本次修改的简要说明。</li></ul><h4 id="编写清晰的-Commit-Message"><a href="#编写清晰的-Commit-Message" class="headerlink" title="编写清晰的 Commit Message"></a>编写清晰的 Commit Message</h4><p>Commit Message 是给未来的自己和团队队友看的，必须清晰回答：<strong>“这个版本到底完成了什么？”</strong></p><ul><li><strong>推荐的良好示例</strong>：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git commit -m <span class="string">&quot;添加任务列表&quot;</span></span><br><span class="line">git commit -m <span class="string">&quot;增加本地数据存储&quot;</span></span><br><span class="line">git commit -m <span class="string">&quot;修复倒计时计算错误&quot;</span></span><br></pre></td></tr></table></figure></li><li><strong>不推荐的糟糕示例（缺乏有效信息）</strong>：<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">update</span><br><span class="line">test</span><br><span class="line">修改</span><br><span class="line">111</span><br><span class="line">aaa</span><br></pre></td></tr></table></figure></li></ul><blockquote><p>[!NOTE]<br>再次强调：执行完 <code>git commit</code> 之后，新版本<strong>依然仅仅保存在你本地电脑的 Git 仓库中</strong>。你的本地仓库有了新快照，但 GitHub 网页端此时还没有任何变化。</p></blockquote><hr><h3 id="8-git-log：查看版本提交历史（可选）"><a href="#8-git-log：查看版本提交历史（可选）" class="headerlink" title="8. git log：查看版本提交历史（可选）"></a>8. <code>git log</code>：查看版本提交历史（可选）</h3><p>如果需要查看本地提交历史，可以使用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">log</span> --oneline</span><br></pre></td></tr></table></figure><p>终端会以精简的单行格式输出提交历史，例如：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">a83f921 完善倒计时页面</span><br><span class="line">51e8d02 添加任务列表</span><br><span class="line">03c26a1 Initial commit</span><br></pre></td></tr></table></figure><ul><li>每行开头显示的是完整 Commit ID 的缩写形式（abbreviated hash），Git 会使用足够区分当前仓库中提交的缩写长度。这个短 ID 可以方便地定位对应的 Commit。</li><li>这一单行列表与你在 GitHub Desktop 左侧的 <strong>History</strong> 历史面板相对应。日常提交不需要每次都执行此命令，需要查看或核对历史时随时调用即可。</li></ul><hr><h3 id="9-git-push：把本地提交上传到远程"><a href="#9-git-push：把本地提交上传到远程" class="headerlink" title="9. git push：把本地提交上传到远程"></a>9. <code>git push</code>：把本地提交上传到远程</h3><p>当你确认本地的一个或多个 Commit 已经完成并验证无误，就可以将它们上传至 GitHub：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push</span><br></pre></td></tr></table></figure><h4 id="明确区分-Commit-与-Push"><a href="#明确区分-Commit-与-Push" class="headerlink" title="明确区分 Commit 与 Push"></a>明确区分 Commit 与 Push</h4><ul><li><code>git commit</code>：将暂存区内容保存为新的本地提交，写入本地 Git 仓库，离线即可完成；</li><li><code>git push</code>：将当前要推送分支中、本地已有而对应远程分支尚未拥有的新提交推送到远程仓库。</li></ul><h4 id="首次-Push-的安全凭据提醒"><a href="#首次-Push-的安全凭据提醒" class="headerlink" title="首次 Push 的安全凭据提醒"></a>首次 Push 的安全凭据提醒</h4><ul><li>第一次在终端执行 <code>git push</code> 时，Windows 系统通常会弹出一个小窗口，或自动打开默认浏览器跳转到 GitHub 授权页面，提示授权 Git Credential Manager。</li><li>点击绿色的 <strong>Authorize</strong> 按钮确认授权即可，系统凭据管理器会自动保存登录凭据，后续无需重复操作。</li><li><strong>安全红线</strong>：千万不要在任何未经验证的终端弹窗或第三方脚本中随意输入你的 GitHub 账号密码或个人访问令牌（Token）。</li></ul><hr><h3 id="10-git-pull：安全同步远程的新版本"><a href="#10-git-pull：安全同步远程的新版本" class="headerlink" title="10. git pull：安全同步远程的新版本"></a>10. <code>git pull</code>：安全同步远程的新版本</h3><p>当远程仓库存在本地尚未拥有的更新时，需要从云端拉取最新代码：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git pull</span><br></pre></td></tr></table></figure><p>但对于刚接触命令行的初学者，<strong>重点推荐使用以下安全命令</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git pull --ff-only</span><br></pre></td></tr></table></figure><h4 id="为什么新手推荐-ff-only？"><a href="#为什么新手推荐-ff-only？" class="headerlink" title="为什么新手推荐 --ff-only？"></a>为什么新手推荐 <code>--ff-only</code>？</h4><ul><li><code>--ff-only</code> 代表 <strong>Fast-forward only（仅快进）</strong>。</li><li>它的作用机制是：<strong>只有当本地改动能够直接沿着远程提交历史平滑向前推进时，才执行同步</strong>。</li><li>当本地与远程历史分叉时，普通 <code>git pull</code> 的具体行为会受到 Git 配置影响，可能涉及 merge、rebase 或直接拒绝。为了避免新手在不理解历史关系时自动修改提交历史，本指南统一推荐 <code>git pull --ff-only</code>；当无法直接快进时，它会立刻拒绝并停下来给出明确提示，保护你的工作区与提交历史。</li></ul><blockquote><p>[!TIP]<br><strong>遇到 <code>--ff-only</code> 失败时怎么办？</strong><br>如果 <code>--ff-only</code> 报错终止，千万不要慌乱去网上乱抄乱试高级命令。先停下键盘，运行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git status</span><br></pre></td></tr></table></figure><p>确认本地和远程具体分歧在哪里，然后向助教、学长或将状态反馈给 AI 编程助手获取诊断建议。第一周切忌盲目强行合并。</p></blockquote><hr><h3 id="11-常见误区：Push-前不是永远必须-Pull"><a href="#11-常见误区：Push-前不是永远必须-Pull" class="headerlink" title="11. 常见误区：Push 前不是永远必须 Pull"></a>11. 常见误区：Push 前不是永远必须 Pull</h3><p>很多初学者容易被机械的教程带偏，死记一套所谓的“黄金流水线”：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">git add .</span><br><span class="line">git commit -m &quot;...&quot;</span><br><span class="line">git pull</span><br><span class="line">git push</span><br></pre></td></tr></table></figure><p><strong>必须澄清这一误区</strong>：</p><ul><li>如果当前项目只有你自己一个人在一台电脑上开发；</li><li>你没有在 GitHub 网页端手动修改过任何文件；</li><li>当前本地已经与云端保持同步；</li><li>那么在 <code>git commit</code> 之后，<strong>直接执行 <code>git push</code> 即可</strong>，根本不需要每次多此一举去 <code>git pull</code>。</li></ul><p><code>git pull</code> 真正的使用场景是：<strong>“远程存在本地尚未拥有的新版本时”</strong>（例如你在另一台电脑上写过代码并推送到了 GitHub，或者团队协作中队友向仓库提交了新改动）。理解数据流向，不要养成无脑机械 Pull 的坏习惯。</p><hr><h3 id="12-完整实操：在-vibe-coding-101-中走完一次全流程"><a href="#12-完整实操：在-vibe-coding-101-中走完一次全流程" class="headerlink" title="12. 完整实操：在 vibe-coding-101 中走完一次全流程"></a>12. 完整实操：在 <code>vibe-coding-101</code> 中走完一次全流程</h3><p>现在我们以 Day 1 已经建好的 <code>vibe-coding-101</code> 仓库为例，暂时不打开 GitHub Desktop，完全在 VS Code 内置终端中独立跑通一次最标准的 Git 终端闭环：</p><h4 id="1-单人日常开发标准闭环（status-→-diff-→-add-→-commit-→-push）"><a href="#1-单人日常开发标准闭环（status-→-diff-→-add-→-commit-→-push）" class="headerlink" title="(1) 单人日常开发标准闭环（status → diff → add → commit → push）"></a>(1) 单人日常开发标准闭环（status → diff → add → commit → push）</h4><ol><li><strong>修改代码</strong>：在 VS Code 中打开 <code>index.html</code>，稍微修改页面主标题文字（例如改为“我的倒计时看板”），保存文件（如果 Day 1 已经开启自动保存，则无需额外操作）。</li><li><strong>查看改动状态</strong>：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git status</span><br></pre></td></tr></table></figure>终端中会清晰地将 <code>index.html</code> 标红，提示该文件已被修改但尚未暂存。</li><li><strong>审查工作区修改细节</strong>：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git diff</span><br></pre></td></tr></table></figure>检查终端显示的绿色 <code>+</code> 新增行与红色 <code>-</code> 旧代码行，确认改动完全符合预期且没有无关脏代码。</li><li><strong>将改动放入暂存区</strong>：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add index.html</span><br></pre></td></tr></table></figure></li><li><strong>创建本地版本提交</strong>：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git commit -m <span class="string">&quot;修改倒计时页面标题&quot;</span></span><br></pre></td></tr></table></figure>终端会输出提交摘要与本次生成的短 Commit ID。</li><li><strong>推送到 GitHub 远程仓库</strong>：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push</span><br></pre></td></tr></table></figure>终端显示上传进度，最终提示更新了远程 <code>main</code> 分支。</li><li><strong>验证效果</strong>：打开浏览器，访问你的 GitHub 仓库网页端并刷新页面，你会发现刚刚在终端中提交的最新信息已经成功展示在云端仓库中！</li></ol><h4 id="2-体验真正的-Pull-场景：远程发生变化时的同步实验"><a href="#2-体验真正的-Pull-场景：远程发生变化时的同步实验" class="headerlink" title="(2) 体验真正的 Pull 场景：远程发生变化时的同步实验"></a>(2) 体验真正的 Pull 场景：远程发生变化时的同步实验</h4><p>在理解了“单人开发无需机械 Pull”之后，我们通过一个真实场景来体会 <code>git pull --ff-only</code> 的必要性：</p><ol><li><strong>在 GitHub 网页端制造一次新提交</strong>：打开浏览器访问你的 GitHub 仓库页面，点击 <code>README.md</code>，点击右上角编辑按钮（铅笔图标），在末尾增加一行文字（例如 <code>## 练习记录</code>），点击绿色的 <strong>Commit changes…</strong> 按钮完成提交。</li><li><strong>回到本地终端</strong>：此时云端拥有了一个本地尚不存在的新提交。</li><li><strong>安全拉取远程更新</strong>：在 VS Code 内置终端中执行：<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git pull --ff-only</span><br></pre></td></tr></table></figure>终端会显示从 GitHub 拉取了更新，并快进合并了 <code>README.md</code>。</li><li><strong>验证同步结果</strong>：在 VS Code 中查看 <code>README.md</code>，你会发现网页端添加的文字已经同步到了本地。通过这个实验，你在真正需要同步的真实场景中掌握了 <code>git pull</code> 的时机。</li></ol><hr><h3 id="13-初学阶段警惕的高危操作"><a href="#13-初学阶段警惕的高危操作" class="headerlink" title="13. 初学阶段警惕的高危操作"></a>13. 初学阶段警惕的高危操作</h3><p>命令行赋予了开发者极高的控制力，但同时也去除了图形界面的二次防呆机制。作为大一新生，在第一周学习期间，<strong>绝对不要主动在终端执行以下高危命令</strong>：</p><ul><li><strong><code>git reset --hard</code>（高危）</strong>：<br>会直接丢弃受 Git 跟踪文件中的工作区和暂存区修改（普通未跟踪文件通常不会仅因为这一条命令被删除）。一旦对已追踪文件误操作，刚刚写完的代码将无法找回。第一周不推荐使用。正文通过 GitHub Desktop 的 <code>Discard Changes</code> 提供了更安全、可视化、可单文件选择的放弃修改方式。</li><li><strong><code>git push --force</code> 或 <code>git push -f</code>（高危）</strong>：<br><code>git push --force</code> 会绕过正常的非快进保护，强制改写被推送目标远程分支的历史，可能导致远程已有 Commit 不再从该分支可达。多人协作中风险极高。第一周绝对不要主动使用。</li></ul><blockquote><p>[!CAUTION]<br>记住：第一周遇到任何改坏代码、需要后悔回退的场景，优先使用正文在 GitHub Desktop 中传授的 <strong>Discard Changes</strong>（放弃未提交修改）与 <strong>Revert changes in commit</strong>（安全反向撤销历史提交），既直观又绝不会造成代码灾难。</p></blockquote><hr><h3 id="14-Git-CLI-核心命令速查"><a href="#14-Git-CLI-核心命令速查" class="headerlink" title="14. Git CLI 核心命令速查"></a>14. Git CLI 核心命令速查</h3><table><thead><tr><th>命令</th><th>作用</th></tr></thead><tbody><tr><td><code>git status</code></td><td>查看当前 Git 状态</td></tr><tr><td><code>git diff</code></td><td>查看尚未暂存的代码改动（工作区 vs 暂存区）</td></tr><tr><td><code>git add &lt;file&gt;</code></td><td>将指定文件修改加入暂存区（<code>&lt;file&gt;</code> 为占位符，例如 <code>git add index.html</code>）</td></tr><tr><td><code>git commit -m &quot;...&quot;</code></td><td>为暂存区修改生成正式的本地版本快照</td></tr><tr><td><code>git log --oneline</code></td><td>精简查看本地 Commit 历史（可选）</td></tr><tr><td><code>git pull --ff-only</code></td><td>安全同步远程的新版本（仅在远程有更新时使用）</td></tr><tr><td><code>git push</code></td><td>将本地新 Commit 推送到 GitHub 远程仓库</td></tr></tbody></table><hr><h3 id="15-附录验收-checklist"><a href="#15-附录验收-checklist" class="headerlink" title="15. 附录验收 checklist"></a>15. 附录验收 checklist</h3><h4 id="Git-CLI-自检"><a href="#Git-CLI-自检" class="headerlink" title="Git CLI 自检"></a>Git CLI 自检</h4><ul><li><input disabled="" type="checkbox"> <code>git --version</code> 能正常输出版本</li><li><input disabled="" type="checkbox"> 能解释工作区、暂存区、本地仓库、远程仓库的区别</li><li><input disabled="" type="checkbox"> 能解释 <code>git add</code>、<code>git commit</code>、<code>git push</code> 分别操作了哪一层</li><li><input disabled="" type="checkbox"> 能解释 <code>git diff</code> 的作用（审查工作区未暂存的修改）</li><li><input disabled="" type="checkbox"> 能解释 <code>git pull</code> 与 <code>git push</code> 的数据流方向</li><li><input disabled="" type="checkbox"> 能解释为什么 <code>git commit</code> 不等于上传 GitHub，且 <code>git status</code> 是本地快照查询</li><li><input disabled="" type="checkbox"> 能独立使用 CLI 完成一次 <code>status → diff → add → commit → push</code></li><li><input disabled="" type="checkbox"> 能通过实验体会 <code>git pull --ff-only</code> 在远程存在新提交时的真实应用场景</li><li><input disabled="" type="checkbox"> 掌握在需要时使用 <code>git log --oneline</code> 查看历史提交</li></ul>]]>
      </content:encoded>
    </item>
    <item>
      <title>C++ 竞赛（CSP）常用 STL 全注释版</title>
      <link>https://ff66ccff.github.io/2026/01/21/CPP%E5%B8%B8%E7%94%A8STL/</link>
      <description>
        <![CDATA[<h1 id="C-竞赛（CSP）常用-STL-全注释版"><a href="#C-竞赛（CSP）常用-STL-全注释版" class="headerlink" title="C++ 竞赛（CSP）常用 STL 全注释版"></a>C++ 竞赛（CSP）常用 STL]]>
      </description>
      <author>ff66ccff</author>
      <category domain="https://ff66ccff.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      <pubDate>Wed, 21 Jan 2026 08:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="C-竞赛（CSP）常用-STL-全注释版"><a href="#C-竞赛（CSP）常用-STL-全注释版" class="headerlink" title="C++ 竞赛（CSP）常用 STL 全注释版"></a>C++ 竞赛（CSP）常用 STL 全注释版</h1><h2 id="1-基础容器：array-vector-string"><a href="#1-基础容器：array-vector-string" class="headerlink" title="1. 基础容器：array &#x2F; vector &#x2F; string"></a>1. 基础容器：<code>array</code> &#x2F; <code>vector</code> &#x2F; <code>string</code></h2><h3 id="1-1-array（定长数组）"><a href="#1-1-array（定长数组）" class="headerlink" title="1.1 array（定长数组）"></a>1.1 <code>array</code>（定长数组）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;array&gt;</span>              <span class="comment">// 1. 引入 array 头文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 定义一个长度固定、元素类型为 int 的 array</span></span><br><span class="line">std::array&lt;<span class="type">int</span>, 100005&gt; a;    <span class="comment">// 模板参数第二个是长度，必须是编译期常量</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 用 fill 统一赋值</span></span><br><span class="line">a.<span class="built_in">fill</span>(<span class="number">0</span>);                    <span class="comment">// 把所有元素赋值为 0，复杂度 O(n)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 按下标访问</span></span><br><span class="line">a[<span class="number">0</span>] = <span class="number">1</span>;                     <span class="comment">// 访问/修改下标 0 的元素，O(1)</span></span><br><span class="line"><span class="type">int</span> x = a[<span class="number">0</span>];                 <span class="comment">// 读取下标 0 的元素</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. 查询大小</span></span><br><span class="line">std::<span class="type">size_t</span> n = a.<span class="built_in">size</span>();     <span class="comment">// 返回 array 的长度，这里是 100005</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 6. 判断是否为空（常用在通用代码里）</span></span><br><span class="line"><span class="type">bool</span> emp = a.<span class="built_in">empty</span>();         <span class="comment">// 如果 size 为 0 则为 true，这里恒为 false</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 7. 遍历</span></span><br><span class="line"><span class="keyword">for</span> (std::<span class="type">size_t</span> i = <span class="number">0</span>; i &lt; a.<span class="built_in">size</span>(); ++i) &#123; <span class="comment">// 经典 for</span></span><br><span class="line">    <span class="type">int</span> v = a[i];             <span class="comment">// 依次访问每个元素</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="1-2-vector（变长数组，最常用）"><a href="#1-2-vector（变长数组，最常用）" class="headerlink" title="1.2 vector（变长数组，最常用）"></a>1.2 <code>vector</code>（变长数组，最常用）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span>             <span class="comment">// 1. 引入 vector 头文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 定义一个存 int 的动态数组</span></span><br><span class="line">std::vector&lt;<span class="type">int</span>&gt; v;           <span class="comment">// 初始 size = 0, capacity = 0</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 尾部插入元素</span></span><br><span class="line">v.<span class="built_in">push_back</span>(<span class="number">3</span>);               <span class="comment">// 在末尾插入一个 3，均摊 O(1)</span></span><br><span class="line">v.<span class="built_in">push_back</span>(<span class="number">5</span>);               <span class="comment">// 再插入一个 5，此时 v = &#123;3, 5&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 访问元素</span></span><br><span class="line"><span class="type">int</span> a0 = v[<span class="number">0</span>];                <span class="comment">// 直接用下标访问（不检查越界），O(1)</span></span><br><span class="line"><span class="type">int</span> a1 = v.<span class="built_in">at</span>(<span class="number">1</span>);             <span class="comment">// 使用 at，会做越界检查（略慢）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. 访问并删除末尾元素</span></span><br><span class="line"><span class="type">int</span> last = v.<span class="built_in">back</span>();          <span class="comment">// 访问最后一个元素，这里是 5</span></span><br><span class="line">v.<span class="built_in">pop_back</span>();                 <span class="comment">// 删除最后一个元素，O(1)，现在 v = &#123;3&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 6. 查询当前元素个数</span></span><br><span class="line">std::<span class="type">size_t</span> sz = v.<span class="built_in">size</span>();    <span class="comment">// 这里 sz = 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 7. 预分配容量（重要的性能优化）</span></span><br><span class="line">v.<span class="built_in">reserve</span>(<span class="number">100000</span>);            <span class="comment">// 提前分配容量为 100000，避免多次扩容</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 8. 一次性构造某个大小的 vector</span></span><br><span class="line"><span class="function">std::vector&lt;<span class="type">int</span>&gt; <span class="title">v2</span><span class="params">(<span class="number">10</span>, <span class="number">0</span>)</span></span>;   <span class="comment">// 长度为 10，所有元素初值为 0</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 9. 使用迭代器遍历</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span> it = v<span class="number">2.</span><span class="built_in">begin</span>(); it != v<span class="number">2.</span><span class="built_in">end</span>(); ++it) &#123;</span><br><span class="line">    <span class="type">int</span> val = *it;            <span class="comment">// 通过迭代器解引用访问元素</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 10. 范围 for 遍历</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> val : v2) &#123;          <span class="comment">// C++11，语法更简洁</span></span><br><span class="line">    <span class="comment">// 使用 val</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 11. 清空元素但保留容量</span></span><br><span class="line">v.<span class="built_in">clear</span>();                    <span class="comment">// size 变 0，capacity 不变</span></span><br></pre></td></tr></table></figure><h3 id="1-3-string（字符串）"><a href="#1-3-string（字符串）" class="headerlink" title="1.3 string（字符串）"></a>1.3 <code>string</code>（字符串）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span>             <span class="comment">// 1. 引入 string 头文件</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span>           <span class="comment">// 2. 用于输入输出</span></span></span><br><span class="line"></span><br><span class="line">std::string s;                <span class="comment">// 3. 定义一个空字符串</span></span><br><span class="line"></span><br><span class="line">std::cin &gt;&gt; s;                <span class="comment">// 4. 读入到空白字符为止（不包含空格）</span></span><br><span class="line"></span><br><span class="line">std::<span class="built_in">getline</span>(std::cin, s);    <span class="comment">// 5. 一次读入整行（包含空格），到换行结束</span></span><br><span class="line"></span><br><span class="line">std::<span class="type">size_t</span> len = s.<span class="built_in">size</span>();   <span class="comment">// 6. 获取字符串长度（O(1)）</span></span><br><span class="line"></span><br><span class="line"><span class="type">char</span> c0 = s[<span class="number">0</span>];               <span class="comment">// 7. 访问第 0 个字符（不检查越界）</span></span><br><span class="line"><span class="type">char</span> c1 = s.<span class="built_in">at</span>(<span class="number">1</span>);            <span class="comment">// 8. 访问第 1 个字符（会做越界检查）</span></span><br><span class="line"></span><br><span class="line">s.<span class="built_in">push_back</span>(<span class="string">&#x27;a&#x27;</span>);             <span class="comment">// 9. 在末尾添加一个字符 &#x27;a&#x27;</span></span><br><span class="line">s.<span class="built_in">pop_back</span>();                 <span class="comment">// 10. 删除末尾 1 个字符</span></span><br><span class="line"></span><br><span class="line">std::string t = <span class="string">&quot;xyz&quot;</span>;</span><br><span class="line">std::string u = s + t;        <span class="comment">// 11. 字符串拼接（整体 O(|s|+|t|)）</span></span><br><span class="line"></span><br><span class="line">std::string sub = s.<span class="built_in">substr</span>(<span class="number">2</span>, <span class="number">3</span>); <span class="comment">// 12. 从下标 2 开始，取长度为 3 的子串</span></span><br><span class="line"></span><br><span class="line">std::<span class="type">size_t</span> pos = s.<span class="built_in">find</span>(<span class="string">&quot;abc&quot;</span>);  <span class="comment">// 13. 查找子串 &quot;abc&quot; 的起始位置</span></span><br><span class="line"><span class="keyword">if</span> (pos == std::string::npos) &#123;   <span class="comment">// 若未找到，返回 npos</span></span><br><span class="line">    <span class="comment">// 未找到</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="2-有序关联容器：set-multiset-map-multimap"><a href="#2-有序关联容器：set-multiset-map-multimap" class="headerlink" title="2. 有序关联容器：set &#x2F; multiset &#x2F; map &#x2F; multimap"></a>2. 有序关联容器：<code>set</code> &#x2F; <code>multiset</code> &#x2F; <code>map</code> &#x2F; <code>multimap</code></h2><h3 id="2-1-set（不重复、有序集合）"><a href="#2-1-set（不重复、有序集合）" class="headerlink" title="2.1 set（不重复、有序集合）"></a>2.1 <code>set</code>（不重复、有序集合）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;set&gt;</span>                <span class="comment">// 1. 引入 set 头文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 定义一个存 int 的 set，自动按升序排列，且不允许重复</span></span><br><span class="line">std::set&lt;<span class="type">int</span>&gt; s;</span><br><span class="line"></span><br><span class="line">s.<span class="built_in">insert</span>(<span class="number">3</span>);                  <span class="comment">// 3. 插入 3，O(log n)</span></span><br><span class="line">s.<span class="built_in">insert</span>(<span class="number">1</span>);                  <span class="comment">// 4. 插入 1，集合内容为 &#123;1, 3&#125;</span></span><br><span class="line">s.<span class="built_in">insert</span>(<span class="number">3</span>);                  <span class="comment">// 5. 再插 3 不会生效（set 元素唯一）</span></span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> has1 = s.<span class="built_in">count</span>(<span class="number">1</span>);       <span class="comment">// 6. 判断是否有 1，有则为 1，没有为 0，O(log n)</span></span><br><span class="line"><span class="type">bool</span> has2 = s.<span class="built_in">count</span>(<span class="number">2</span>);       <span class="comment">// 7. 判断是否有 2，这里为 0</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> mn = *s.<span class="built_in">begin</span>();          <span class="comment">// 8. 取最小值（第一个元素），这里是 1，O(1)</span></span><br><span class="line"><span class="type">int</span> mx = *s.<span class="built_in">rbegin</span>();         <span class="comment">// 9. 取最大值（最后一个元素），这里是 3，O(1)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> it = s.<span class="built_in">lower_bound</span>(<span class="number">2</span>);   <span class="comment">// 10. 找到第一个 &gt;= 2 的迭代器，这里指向 3</span></span><br><span class="line"><span class="comment">// 若 it == s.end()，说明不存在 &gt;= 2 的元素</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> it2 = s.<span class="built_in">upper_bound</span>(<span class="number">1</span>);  <span class="comment">// 11. 找到第一个 &gt; 1 的迭代器，这里同样指向 3</span></span><br><span class="line"></span><br><span class="line">s.<span class="built_in">erase</span>(<span class="number">3</span>);                   <span class="comment">// 12. 按值删除 3，O(log n)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> x : s) &#123;             <span class="comment">// 13. 中序遍历，得到升序序列</span></span><br><span class="line">    <span class="comment">// 依次访问集合中的每个元素</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-2-multiset（可重复、有序集合）"><a href="#2-2-multiset（可重复、有序集合）" class="headerlink" title="2.2 multiset（可重复、有序集合）"></a>2.2 <code>multiset</code>（可重复、有序集合）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;set&gt;</span>                <span class="comment">// multiset 也在 &lt;set&gt; 头文件中</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 定义一个 multiset，可以存重复元素，自动升序</span></span><br><span class="line">std::multiset&lt;<span class="type">int</span>&gt; ms;</span><br><span class="line"></span><br><span class="line">ms.<span class="built_in">insert</span>(<span class="number">3</span>);                 <span class="comment">// 2. 插入一个 3</span></span><br><span class="line">ms.<span class="built_in">insert</span>(<span class="number">3</span>);                 <span class="comment">// 3. 再插入一个 3，现在有两个 3</span></span><br><span class="line">ms.<span class="built_in">insert</span>(<span class="number">5</span>);                 <span class="comment">// 4. 插入一个 5，内容为 &#123;3, 3, 5&#125;</span></span><br><span class="line"></span><br><span class="line">std::<span class="type">size_t</span> c = ms.<span class="built_in">count</span>(<span class="number">3</span>);  <span class="comment">// 5. 统计 3 的个数，这里是 2，O(k + log n)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> it = ms.<span class="built_in">find</span>(<span class="number">3</span>);         <span class="comment">// 6. 找到任意一个等于 3 的迭代器，O(log n)</span></span><br><span class="line"><span class="keyword">if</span> (it != ms.<span class="built_in">end</span>()) &#123;</span><br><span class="line">    ms.<span class="built_in">erase</span>(it);             <span class="comment">// 7. 只删除这一个 3，剩下 &#123;3, 5&#125;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">ms.<span class="built_in">erase</span>(<span class="number">3</span>);                  <span class="comment">// 8. 再按值删除所有 3，剩下 &#123;5&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> x : ms) &#123;            <span class="comment">// 9. 以升序遍历所有元素（包含重复）</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-3-map（key-唯一、有序字典）"><a href="#2-3-map（key-唯一、有序字典）" class="headerlink" title="2.3 map（key 唯一、有序字典）"></a>2.3 <code>map</code>（key 唯一、有序字典）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;map&gt;</span>                <span class="comment">// 1. 引入 map 头文件</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span>             <span class="comment">// 2. key 用 string 时需引入</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 定义一个 map，key 为 string，value 为 int</span></span><br><span class="line">std::map&lt;std::string, <span class="type">int</span>&gt; mp;</span><br><span class="line"></span><br><span class="line">mp[<span class="string">&quot;alice&quot;</span>] = <span class="number">3</span>;              <span class="comment">// 4. 通过下标插入/修改，log n</span></span><br><span class="line">mp[<span class="string">&quot;bob&quot;</span>] = <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> x = mp[<span class="string">&quot;alice&quot;</span>];          <span class="comment">// 5. 访问 key 为 &quot;alice&quot; 的 value，若不存在会插入 0</span></span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> hasBob = mp.<span class="built_in">count</span>(<span class="string">&quot;bob&quot;</span>);<span class="comment">// 6. 判断是否存在 key &quot;bob&quot;，O(log n)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> it = mp.<span class="built_in">find</span>(<span class="string">&quot;alice&quot;</span>);   <span class="comment">// 7. 使用 find 查找（不插入），不存在返回 end()</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (it != mp.<span class="built_in">end</span>()) &#123;         <span class="comment">// 8. 确认存在后再访问</span></span><br><span class="line">    std::string k = it-&gt;first;<span class="comment">// 9. 迭代器指向 pair&lt;key, value&gt;</span></span><br><span class="line">    <span class="type">int</span> v = it-&gt;second;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">mp.<span class="built_in">erase</span>(<span class="string">&quot;bob&quot;</span>);              <span class="comment">// 10. 按 key 删除这个键值对，O(log n)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span> &amp;kv : mp) &#123;         <span class="comment">// 11. 遍历时按 key 升序</span></span><br><span class="line">    std::string name = kv.first;</span><br><span class="line">    <span class="type">int</span> score = kv.second;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-4-multimap（key-可重复、有序字典）"><a href="#2-4-multimap（key-可重复、有序字典）" class="headerlink" title="2.4 multimap（key 可重复、有序字典）"></a>2.4 <code>multimap</code>（key 可重复、有序字典）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;map&gt;</span>                <span class="comment">// multimap 也在 &lt;map&gt; 中</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 定义一个 multimap，允许同一个 key 对应多个 value</span></span><br><span class="line">std::multimap&lt;std::string, <span class="type">int</span>&gt; mm;</span><br><span class="line"></span><br><span class="line">mm.<span class="built_in">insert</span>(&#123;<span class="string">&quot;a&quot;</span>, <span class="number">1</span>&#125;);          <span class="comment">// 2. 插入 key=&quot;a&quot;, value=1</span></span><br><span class="line">mm.<span class="built_in">insert</span>(&#123;<span class="string">&quot;a&quot;</span>, <span class="number">2</span>&#125;);          <span class="comment">// 3. 再插入 key=&quot;a&quot;, value=2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. equal_range 返回一段区间，包含所有 key==&quot;a&quot; 的元素</span></span><br><span class="line"><span class="keyword">auto</span> range = mm.<span class="built_in">equal_range</span>(<span class="string">&quot;a&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 5. 遍历所有 key==&quot;a&quot; 的 value</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span> it = range.first; it != range.second; ++it) &#123;</span><br><span class="line">    std::string k = it-&gt;first;<span class="comment">// 恒为 &quot;a&quot;</span></span><br><span class="line">    <span class="type">int</span> v = it-&gt;second;       <span class="comment">// 依次为 1、2</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="3-无序关联容器：unordered-set-unordered-map"><a href="#3-无序关联容器：unordered-set-unordered-map" class="headerlink" title="3. 无序关联容器：unordered_set &#x2F; unordered_map"></a>3. 无序关联容器：<code>unordered_set</code> &#x2F; <code>unordered_map</code></h2><h3 id="3-1-unordered-set（哈希集合）"><a href="#3-1-unordered-set（哈希集合）" class="headerlink" title="3.1 unordered_set（哈希集合）"></a>3.1 <code>unordered_set</code>（哈希集合）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unordered_set&gt;</span>      <span class="comment">// 1. 引入 unordered_set 头文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 定义一个无序、元素唯一的哈希集合</span></span><br><span class="line">std::unordered_set&lt;<span class="type">int</span>&gt; us;</span><br><span class="line"></span><br><span class="line">us.<span class="built_in">insert</span>(<span class="number">3</span>);                 <span class="comment">// 3. 插入 3，均摊 O(1)</span></span><br><span class="line">us.<span class="built_in">insert</span>(<span class="number">5</span>);                 <span class="comment">// 4. 插入 5</span></span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> has3 = us.<span class="built_in">count</span>(<span class="number">3</span>);      <span class="comment">// 5. 判断是否有 3，O(1) 均摊</span></span><br><span class="line"><span class="type">bool</span> has7 = us.<span class="built_in">count</span>(<span class="number">7</span>);      <span class="comment">// 6. 判断是否有 7，这里为 0</span></span><br><span class="line"></span><br><span class="line">us.<span class="built_in">erase</span>(<span class="number">3</span>);                  <span class="comment">// 7. 删除 3，O(1) 均摊</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> x : us) &#123;            <span class="comment">// 8. 遍历所有元素（顺序不保证）</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-2-unordered-map（哈希字典）"><a href="#3-2-unordered-map（哈希字典）" class="headerlink" title="3.2 unordered_map（哈希字典）"></a>3.2 <code>unordered_map</code>（哈希字典）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unordered_map&gt;</span>      <span class="comment">// 1. 引入 unordered_map</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 定义一个无序字典：string -&gt; int</span></span><br><span class="line">std::unordered_map&lt;std::string, <span class="type">int</span>&gt; um;</span><br><span class="line"></span><br><span class="line">um[<span class="string">&quot;apple&quot;</span>] = <span class="number">2</span>;              <span class="comment">// 3. 插入/修改 key=&quot;apple&quot; 对应的值为 2，均摊 O(1)</span></span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> has = um.<span class="built_in">count</span>(<span class="string">&quot;apple&quot;</span>); <span class="comment">// 4. 是否存在这个 key</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> v = um[<span class="string">&quot;apple&quot;</span>];          <span class="comment">// 5. 访问 value；若 key 不存在会插入默认值 0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> it = um.<span class="built_in">find</span>(<span class="string">&quot;banana&quot;</span>);  <span class="comment">// 6. 查找但不插入，不存在则为 end()</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (it != um.<span class="built_in">end</span>()) &#123;</span><br><span class="line">    std::string k = it-&gt;first;</span><br><span class="line">    <span class="type">int</span> val = it-&gt;second;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">um.<span class="built_in">erase</span>(<span class="string">&quot;apple&quot;</span>);            <span class="comment">// 7. 删除 key 为 &quot;apple&quot; 的键值对</span></span><br></pre></td></tr></table></figure><hr><h2 id="4-线性容器：queue-stack-deque-priority-queue"><a href="#4-线性容器：queue-stack-deque-priority-queue" class="headerlink" title="4. 线性容器：queue &#x2F; stack &#x2F; deque &#x2F; priority_queue"></a>4. 线性容器：<code>queue</code> &#x2F; <code>stack</code> &#x2F; <code>deque</code> &#x2F; <code>priority_queue</code></h2><h3 id="4-1-queue（队列，FIFO）"><a href="#4-1-queue（队列，FIFO）" class="headerlink" title="4.1 queue（队列，FIFO）"></a>4.1 <code>queue</code>（队列，FIFO）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;queue&gt;</span>              <span class="comment">// 1. 引入 queue 头文件</span></span></span><br><span class="line"></span><br><span class="line">std::queue&lt;<span class="type">int</span>&gt; q;            <span class="comment">// 2. 定义一个 int 队列</span></span><br><span class="line"></span><br><span class="line">q.<span class="built_in">push</span>(<span class="number">1</span>);                    <span class="comment">// 3. 把 1 放到队尾</span></span><br><span class="line">q.<span class="built_in">push</span>(<span class="number">2</span>);                    <span class="comment">// 4. 把 2 放到队尾，此时队列为 [1, 2]</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> f = q.<span class="built_in">front</span>();            <span class="comment">// 5. 查看队头元素（不删除），这里是 1</span></span><br><span class="line"></span><br><span class="line">q.<span class="built_in">pop</span>();                      <span class="comment">// 6. 弹出队头元素，这里删除 1，剩 [2]</span></span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> emp = q.<span class="built_in">empty</span>();         <span class="comment">// 7. 判断队列是否为空</span></span><br><span class="line">std::<span class="type">size_t</span> cnt = q.<span class="built_in">size</span>();   <span class="comment">// 8. 获取当前队列长度</span></span><br></pre></td></tr></table></figure><h3 id="4-2-stack（栈，FILO）"><a href="#4-2-stack（栈，FILO）" class="headerlink" title="4.2 stack（栈，FILO）"></a>4.2 <code>stack</code>（栈，FILO）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stack&gt;</span>              <span class="comment">// 1. 引入 stack 头文件</span></span></span><br><span class="line"></span><br><span class="line">std::stack&lt;<span class="type">int</span>&gt; st;           <span class="comment">// 2. 定义一个 int 栈</span></span><br><span class="line"></span><br><span class="line">st.<span class="built_in">push</span>(<span class="number">1</span>);                   <span class="comment">// 3. 把 1 压入栈顶</span></span><br><span class="line">st.<span class="built_in">push</span>(<span class="number">2</span>);                   <span class="comment">// 4. 再压入 2，此时栈顶是 2</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> t = st.<span class="built_in">top</span>();             <span class="comment">// 5. 查看栈顶元素（不弹出），这里是 2</span></span><br><span class="line"></span><br><span class="line">st.<span class="built_in">pop</span>();                     <span class="comment">// 6. 弹出栈顶元素，删除 2，栈顶变成 1</span></span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> empS = st.<span class="built_in">empty</span>();       <span class="comment">// 7. 栈是否为空</span></span><br><span class="line">std::<span class="type">size_t</span> ssz = st.<span class="built_in">size</span>();  <span class="comment">// 8. 当前栈内元素个数</span></span><br></pre></td></tr></table></figure><h3 id="4-3-deque（双端队列）"><a href="#4-3-deque（双端队列）" class="headerlink" title="4.3 deque（双端队列）"></a>4.3 <code>deque</code>（双端队列）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;deque&gt;</span>              <span class="comment">// 1. 引入 deque 头文件</span></span></span><br><span class="line"></span><br><span class="line">std::deque&lt;<span class="type">int</span>&gt; dq;           <span class="comment">// 2. 定义一个双端队列</span></span><br><span class="line"></span><br><span class="line">dq.<span class="built_in">push_back</span>(<span class="number">1</span>);              <span class="comment">// 3. 从尾部插入 1，队列为 [1]</span></span><br><span class="line">dq.<span class="built_in">push_front</span>(<span class="number">2</span>);             <span class="comment">// 4. 从头部插入 2，队列为 [2, 1]</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> f = dq.<span class="built_in">front</span>();           <span class="comment">// 5. 查看头部元素，这里是 2</span></span><br><span class="line"><span class="type">int</span> b = dq.<span class="built_in">back</span>();            <span class="comment">// 6. 查看尾部元素，这里是 1</span></span><br><span class="line"></span><br><span class="line">dq.<span class="built_in">pop_front</span>();               <span class="comment">// 7. 从头部弹出，删除 2，队列为 [1]</span></span><br><span class="line">dq.<span class="built_in">pop_back</span>();                <span class="comment">// 8. 从尾部弹出，删除 1，队列为空</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> x = dq[<span class="number">0</span>];                <span class="comment">// 9. 随机访问，类似 vector，O(1)</span></span><br></pre></td></tr></table></figure><h3 id="4-4-priority-queue（优先队列，堆）"><a href="#4-4-priority-queue（优先队列，堆）" class="headerlink" title="4.4 priority_queue（优先队列，堆）"></a>4.4 <code>priority_queue</code>（优先队列，堆）</h3><p><strong>大根堆（默认）</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;queue&gt;</span>              <span class="comment">// priority_queue 在 &lt;queue&gt; 中</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 默认是大根堆：top() 是最大值</span></span><br><span class="line">std::priority_queue&lt;<span class="type">int</span>&gt; pq;</span><br><span class="line"></span><br><span class="line">pq.<span class="built_in">push</span>(<span class="number">3</span>);                   <span class="comment">// 2. 插入 3</span></span><br><span class="line">pq.<span class="built_in">push</span>(<span class="number">1</span>);                   <span class="comment">// 3. 插入 1</span></span><br><span class="line">pq.<span class="built_in">push</span>(<span class="number">5</span>);                   <span class="comment">// 4. 插入 5，此时内部堆会维护最大堆性质</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> top1 = pq.<span class="built_in">top</span>();          <span class="comment">// 5. 取当前最大值，这里是 5（不删除）</span></span><br><span class="line"></span><br><span class="line">pq.<span class="built_in">pop</span>();                     <span class="comment">// 6. 弹出最大值 5，堆里剩下 &#123;3,1&#125;，新的 top 为 3</span></span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> empP = pq.<span class="built_in">empty</span>();       <span class="comment">// 7. 判断是否为空</span></span><br><span class="line">std::<span class="type">size_t</span> psz = pq.<span class="built_in">size</span>();  <span class="comment">// 8. 当前元素个数</span></span><br></pre></td></tr></table></figure><p><strong>小根堆</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;queue&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;functional&gt;</span>         <span class="comment">// greater 在 &lt;functional&gt; 里</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 使用 greater&lt;int&gt; 把 priority_queue 变成小根堆</span></span><br><span class="line">std::priority_queue&lt;<span class="type">int</span>, std::vector&lt;<span class="type">int</span>&gt;, std::greater&lt;<span class="type">int</span>&gt;&gt; pqMin;</span><br><span class="line"></span><br><span class="line">pqMin.<span class="built_in">push</span>(<span class="number">3</span>);</span><br><span class="line">pqMin.<span class="built_in">push</span>(<span class="number">1</span>);</span><br><span class="line">pqMin.<span class="built_in">push</span>(<span class="number">5</span>);</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> minv = pqMin.<span class="built_in">top</span>();       <span class="comment">// 2. 现在 top() 是最小值，这里是 1</span></span><br></pre></td></tr></table></figure><p><strong>自定义结构 + 自定义比较</strong></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;queue&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 自定义结点类型</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">    <span class="type">int</span> w;                    <span class="comment">// 权值</span></span><br><span class="line">    <span class="type">int</span> id;                   <span class="comment">// 编号</span></span><br><span class="line">    <span class="comment">// 2. 重载 &lt; 运算符（注意：priority_queue 把“最大”的看作 top）</span></span><br><span class="line">    <span class="type">bool</span> <span class="keyword">operator</span>&lt;(<span class="type">const</span> Node&amp; other) <span class="type">const</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> w &gt; other.w;   <span class="comment">// 让 w 小的“看起来更大”，从而实现小根堆</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">std::priority_queue&lt;Node&gt; q;  <span class="comment">// 3. 现在 q.top() 是 w 最小的结点</span></span><br><span class="line"></span><br><span class="line">q.<span class="built_in">push</span>(&#123;<span class="number">3</span>, <span class="number">1</span>&#125;);</span><br><span class="line">q.<span class="built_in">push</span>(&#123;<span class="number">1</span>, <span class="number">2</span>&#125;);</span><br><span class="line">q.<span class="built_in">push</span>(&#123;<span class="number">2</span>, <span class="number">3</span>&#125;);</span><br><span class="line"></span><br><span class="line">Node t = q.<span class="built_in">top</span>();             <span class="comment">// 4. 得到 w 最小的结点，这里 w=1, id=2</span></span><br></pre></td></tr></table></figure><hr><h2 id="5-常用算法："><a href="#5-常用算法：" class="headerlink" title="5. 常用算法：&lt;algorithm&gt; &#x2F; &lt;numeric&gt;"></a>5. 常用算法：<code>&lt;algorithm&gt;</code> &#x2F; <code>&lt;numeric&gt;</code></h2><h3 id="5-1-sort-排序"><a href="#5-1-sort-排序" class="headerlink" title="5.1 sort 排序"></a>5.1 <code>sort</code> 排序</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span>          <span class="comment">// sort 在 &lt;algorithm&gt; 中</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> a[] = &#123;<span class="number">3</span>, <span class="number">1</span>, <span class="number">4</span>, <span class="number">2</span>&#125;;       <span class="comment">// 1. 普通数组</span></span><br><span class="line"><span class="type">int</span> n = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 对数组进行升序排序</span></span><br><span class="line">std::<span class="built_in">sort</span>(a, a + n);          <span class="comment">// 排完后 a = &#123;1, 2, 3, 4&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 对 vector 排序</span></span><br><span class="line">std::vector&lt;<span class="type">int</span>&gt; v = &#123;<span class="number">3</span>, <span class="number">1</span>, <span class="number">4</span>, <span class="number">2</span>&#125;;</span><br><span class="line">std::<span class="built_in">sort</span>(v.<span class="built_in">begin</span>(), v.<span class="built_in">end</span>());<span class="comment">// 升序，v = &#123;1, 2, 3, 4&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 使用 greater 降序排序</span></span><br><span class="line">std::<span class="built_in">sort</span>(v.<span class="built_in">begin</span>(), v.<span class="built_in">end</span>(), std::<span class="built_in">greater</span>&lt;<span class="type">int</span>&gt;()); <span class="comment">// v = &#123;4, 3, 2, 1&#125;</span></span><br></pre></td></tr></table></figure><p>带自定义比较的 <code>sort</code>：</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 自定义结构体</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">    <span class="type">int</span> x, y;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 自定义比较：x 小的排前面；若 x 相同，y 大的排前面</span></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">cmp</span><span class="params">(<span class="type">const</span> Node&amp; a, <span class="type">const</span> Node&amp; b)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (a.x != b.x) <span class="keyword">return</span> a.x &lt; b.x;</span><br><span class="line">    <span class="keyword">return</span> a.y &gt; b.y;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">std::vector&lt;Node&gt; v;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 使用自定义比较排序</span></span><br><span class="line">std::<span class="built_in">sort</span>(v.<span class="built_in">begin</span>(), v.<span class="built_in">end</span>(), cmp);</span><br></pre></td></tr></table></figure><h3 id="5-2-二分相关：lower-bound-upper-bound-binary-search"><a href="#5-2-二分相关：lower-bound-upper-bound-binary-search" class="headerlink" title="5.2 二分相关：lower_bound &#x2F; upper_bound &#x2F; binary_search"></a>5.2 二分相关：<code>lower_bound</code> &#x2F; <code>upper_bound</code> &#x2F; <code>binary_search</code></h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line">std::vector&lt;<span class="type">int</span>&gt; v = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>&#125;;</span><br><span class="line"><span class="type">int</span> n = v.<span class="built_in">size</span>();</span><br><span class="line"><span class="type">int</span> x = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. lower_bound：第一个 &gt;= x 的位置</span></span><br><span class="line"><span class="keyword">auto</span> it1 = std::<span class="built_in">lower_bound</span>(v.<span class="built_in">begin</span>(), v.<span class="built_in">end</span>(), x);</span><br><span class="line"><span class="type">int</span> pos1 = it1 - v.<span class="built_in">begin</span>();   <span class="comment">// 这里 pos1 = 1（v[1] == 2）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. upper_bound：第一个 &gt; x 的位置</span></span><br><span class="line"><span class="keyword">auto</span> it2 = std::<span class="built_in">upper_bound</span>(v.<span class="built_in">begin</span>(), v.<span class="built_in">end</span>(), x);</span><br><span class="line"><span class="type">int</span> pos2 = it2 - v.<span class="built_in">begin</span>();   <span class="comment">// 这里 pos2 = 3（v[3] == 3）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 使用 binary_search 判断是否存在 x</span></span><br><span class="line"><span class="type">bool</span> exists = std::<span class="built_in">binary_search</span>(v.<span class="built_in">begin</span>(), v.<span class="built_in">end</span>(), x); <span class="comment">// 这里为 true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 出现次数 = upper_bound - lower_bound</span></span><br><span class="line"><span class="type">int</span> cnt = pos2 - pos1;        <span class="comment">// 这里 cnt = 2（两个 2）</span></span><br></pre></td></tr></table></figure><h3 id="5-3-min-max-min-element-max-element"><a href="#5-3-min-max-min-element-max-element" class="headerlink" title="5.3 min &#x2F; max &#x2F; min_element &#x2F; max_element"></a>5.3 <code>min</code> &#x2F; <code>max</code> &#x2F; <code>min_element</code> &#x2F; <code>max_element</code></h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> a = <span class="number">3</span>, b = <span class="number">5</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> mn = std::<span class="built_in">min</span>(a, b);      <span class="comment">// 1. 返回较小值，这里是 3</span></span><br><span class="line"><span class="type">int</span> mx = std::<span class="built_in">max</span>(a, b);      <span class="comment">// 2. 返回较大值，这里是 5</span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> arr[] = &#123;<span class="number">3</span>, <span class="number">1</span>, <span class="number">4</span>, <span class="number">2</span>&#125;;</span><br><span class="line"><span class="type">int</span> n = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 最小元素迭代器</span></span><br><span class="line"><span class="type">int</span> mnVal = *std::<span class="built_in">min_element</span>(arr, arr + n); <span class="comment">// 结果为 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. 最大元素迭代器</span></span><br><span class="line"><span class="type">int</span> mxVal = *std::<span class="built_in">max_element</span>(arr, arr + n); <span class="comment">// 结果为 4</span></span><br></pre></td></tr></table></figure><h3 id="5-4-reverse-unique"><a href="#5-4-reverse-unique" class="headerlink" title="5.4 reverse &#x2F; unique"></a>5.4 <code>reverse</code> &#x2F; <code>unique</code></h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line">std::vector&lt;<span class="type">int</span>&gt; v = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 反转整个容器</span></span><br><span class="line">std::<span class="built_in">reverse</span>(v.<span class="built_in">begin</span>(), v.<span class="built_in">end</span>()); <span class="comment">// v = &#123;4, 3, 2, 1&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 去重常规套路：先排序，再 unique 再 resize</span></span><br><span class="line">std::vector&lt;<span class="type">int</span>&gt; u = &#123;<span class="number">3</span>, <span class="number">1</span>, <span class="number">3</span>, <span class="number">2</span>, <span class="number">1</span>&#125;;</span><br><span class="line">std::<span class="built_in">sort</span>(u.<span class="built_in">begin</span>(), u.<span class="built_in">end</span>());    <span class="comment">// u = &#123;1, 1, 2, 3, 3&#125;</span></span><br><span class="line"><span class="keyword">auto</span> it = std::<span class="built_in">unique</span>(u.<span class="built_in">begin</span>(), u.<span class="built_in">end</span>());</span><br><span class="line"><span class="comment">// 3. unique 把相邻重复挤到后面，返回“新逻辑末尾”迭代器</span></span><br><span class="line">u.<span class="built_in">erase</span>(it, u.<span class="built_in">end</span>());             <span class="comment">// 4. 真正把后面的脏数据删掉，u = &#123;1, 2, 3&#125;</span></span><br></pre></td></tr></table></figure><h3 id="5-5-accumulate（在-中）"><a href="#5-5-accumulate（在-中）" class="headerlink" title="5.5 accumulate（在 &lt;numeric&gt; 中）"></a>5.5 <code>accumulate</code>（在 <code>&lt;numeric&gt;</code> 中）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;numeric&gt;</span>            <span class="comment">// accumulate 在 &lt;numeric&gt; 中</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> arr[] = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;;</span><br><span class="line"><span class="type">int</span> n = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 求和，初始值为 0</span></span><br><span class="line"><span class="type">int</span> sum = std::<span class="built_in">accumulate</span>(arr, arr + n, <span class="number">0</span>);      <span class="comment">// 结果为 10</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 用 long long 防溢出</span></span><br><span class="line"><span class="type">long</span> <span class="type">long</span> sum2 = std::<span class="built_in">accumulate</span>(arr, arr + n, <span class="number">0LL</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 自定义运算（比如求积）</span></span><br><span class="line"><span class="type">int</span> prod = std::<span class="built_in">accumulate</span>(arr, arr + n, <span class="number">1</span>, std::<span class="built_in">multiplies</span>&lt;<span class="type">int</span>&gt;()); <span class="comment">// 1*2*3*4=24</span></span><br></pre></td></tr></table></figure><hr><h2 id="6-其它常见-STL：pair-tuple-bitset"><a href="#6-其它常见-STL：pair-tuple-bitset" class="headerlink" title="6. 其它常见 STL：pair &#x2F; tuple &#x2F; bitset"></a>6. 其它常见 STL：<code>pair</code> &#x2F; <code>tuple</code> &#x2F; <code>bitset</code></h2><h3 id="6-1-pair-tuple"><a href="#6-1-pair-tuple" class="headerlink" title="6.1 pair &#x2F; tuple"></a>6.1 <code>pair</code> &#x2F; <code>tuple</code></h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;utility&gt;</span>            <span class="comment">// pair 在 &lt;utility&gt; 中</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;tuple&gt;</span>              <span class="comment">// tuple 在 &lt;tuple&gt; 中</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 定义并初始化一个 pair</span></span><br><span class="line">std::pair&lt;<span class="type">int</span>, <span class="type">int</span>&gt; p = &#123;<span class="number">1</span>, <span class="number">2</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> first = p.first;          <span class="comment">// 2. 访问 first，结果为 1</span></span><br><span class="line"><span class="type">int</span> second = p.second;        <span class="comment">// 3. 访问 second，结果为 2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 4. pair 自带字典序比较：先比 first，再比 second</span></span><br><span class="line">std::pair&lt;<span class="type">int</span>, <span class="type">int</span>&gt; a = &#123;<span class="number">1</span>, <span class="number">3</span>&#125;;</span><br><span class="line">std::pair&lt;<span class="type">int</span>, <span class="type">int</span>&gt; b = &#123;<span class="number">2</span>, <span class="number">0</span>&#125;;</span><br><span class="line"><span class="type">bool</span> res = (a &lt; b);           <span class="comment">// 5. true，因为 1 &lt; 2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 6. 定义一个三元组 tuple</span></span><br><span class="line">std::tuple&lt;<span class="type">int</span>, <span class="type">int</span>, <span class="type">int</span>&gt; t = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> x = std::<span class="built_in">get</span>&lt;<span class="number">0</span>&gt;(t);       <span class="comment">// 7. 获取第 0 个元素，1</span></span><br><span class="line"><span class="type">int</span> y = std::<span class="built_in">get</span>&lt;<span class="number">1</span>&gt;(t);       <span class="comment">// 8. 获取第 1 个元素，2</span></span><br><span class="line"><span class="type">int</span> z = std::<span class="built_in">get</span>&lt;<span class="number">2</span>&gt;(t);       <span class="comment">// 9. 获取第 2 个元素，3</span></span><br></pre></td></tr></table></figure><h3 id="6-2-bitset（定长位集，用于状态压缩）"><a href="#6-2-bitset（定长位集，用于状态压缩）" class="headerlink" title="6.2 bitset（定长位集，用于状态压缩）"></a>6.2 <code>bitset</code>（定长位集，用于状态压缩）</h3><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bitset&gt;</span>             <span class="comment">// 1. 引入 bitset 头文件</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 定义一个有 1000 位的 bitset，索引范围 [0, 999]</span></span><br><span class="line">std::bitset&lt;1000&gt; bs;</span><br><span class="line"></span><br><span class="line">bs.<span class="built_in">set</span>(<span class="number">3</span>);                    <span class="comment">// 3. 把第 3 位设为 1</span></span><br><span class="line">bs.<span class="built_in">reset</span>(<span class="number">3</span>);                  <span class="comment">// 4. 把第 3 位设为 0</span></span><br><span class="line">bs.<span class="built_in">flip</span>(<span class="number">3</span>);                   <span class="comment">// 5. 把第 3 位取反（0-&gt;1 或 1-&gt;0）</span></span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> b3 = bs[<span class="number">3</span>];              <span class="comment">// 6. 访问第 3 位的值（bool）</span></span><br><span class="line"></span><br><span class="line">std::<span class="type">size_t</span> ones = bs.<span class="built_in">count</span>();<span class="comment">// 7. 统计有多少位为 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 8. 位运算：&amp;（与）、|（或）、^（异或）</span></span><br><span class="line"><span class="function">std::bitset&lt;8&gt; <span class="title">a8</span><span class="params">(std::string(<span class="string">&quot;11001100&quot;</span>))</span></span>;</span><br><span class="line"><span class="function">std::bitset&lt;8&gt; <span class="title">b8</span><span class="params">(std::string(<span class="string">&quot;10101010&quot;</span>))</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">auto</span> c8 = a8 &amp; b8;            <span class="comment">// 位与</span></span><br><span class="line"><span class="keyword">auto</span> d8 = a8 | b8;            <span class="comment">// 位或</span></span><br><span class="line"><span class="keyword">auto</span> e8 = a8 ^ b8;            <span class="comment">// 位异或</span></span><br></pre></td></tr></table></figure><hr>]]>
      </content:encoded>
    </item>
    <item>
      <title>辞旧迎新之时</title>
      <link>https://ff66ccff.github.io/2026/01/01/%E8%BE%9E%E6%97%A7%E8%BF%8E%E6%96%B0%E4%B9%8B%E6%97%B6-bgm/</link>
      <description>
        <![CDATA[<h1 id="辞旧迎新之时-BGM"><a href="#辞旧迎新之时-BGM" class="headerlink" title="辞旧迎新之时[BGM]"></a>辞旧迎新之时[BGM]</h1><div class="music-player">
<iframe]]>
      </description>
      <author>ff66ccff</author>
      <category domain="https://ff66ccff.github.io/categories/%E7%94%9F%E6%B4%BB/">生活</category>
      <pubDate>Thu, 01 Jan 2026 08:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="辞旧迎新之时-BGM"><a href="#辞旧迎新之时-BGM" class="headerlink" title="辞旧迎新之时[BGM]"></a>辞旧迎新之时[BGM]</h1><div class="music-player"><iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width="330" height="86" src="https://music.163.com/outchain/player?type=2&id=2616680887&auto=0&height=66"></iframe></div><p>How time flies！<br>2025年就这么过去了。可惜大学生活并没有我高中畅想中那么<strong>轻松</strong>。<br>感觉总是被莫名其妙的琐事缠身，一身力气全打棉花上了属于是。<br>不过还是很庆幸自己来到了SCU，累了真的可以休息一会。去隔壁UESTC的朋友已经叫苦连天了（苦笑）。<br>总结一下2025吧，大概是从一片黑暗森林逃到了另一片黑暗森林，感觉花费了好多好多精力但是啥也没干好。故事没有太多，太多没有结果。<br>幻想一下2026吧，这个游戏本真让我背破防了，看看能不能靠自己圈点钱整个轻薄本；现在这形势不太妙，只能走一步看一步了，万物凋敝之时啊……</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>记得向尘世之外瞥一眼</title>
      <link>https://ff66ccff.github.io/2025/11/15/%E8%AE%B0%E5%BE%97%E5%90%91%E5%B0%98%E4%B8%96%E4%B9%8B%E5%A4%96%E7%9E%A5%E4%B8%80%E7%9C%BC/</link>
      <description>
        <![CDATA[<h1 id="记得向尘世之外瞥一眼"><a href="#记得向尘世之外瞥一眼" class="headerlink"]]>
      </description>
      <author>ff66ccff</author>
      <category domain="https://ff66ccff.github.io/categories/%E7%94%9F%E6%B4%BB/">生活</category>
      <pubDate>Sat, 15 Nov 2025 09:22:44 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="记得向尘世之外瞥一眼"><a href="#记得向尘世之外瞥一眼" class="headerlink" title="记得向尘世之外瞥一眼"></a>记得向尘世之外瞥一眼</h1><hr><p>宇宙很大，生活更大。<br>低头做题的同时，要记得向尘世之外瞥一眼。</p><blockquote><p>云天明发现何博士似乎对自己的话并没没感到吃惊，只是默默地点了一支烟，也许，他已经察觉到了什么。沉默许久后，他说：“真那样的话，你仍然很幸运，大多数人，到死都没向尘世之外瞥一眼。” 何博士吐出的烟雾飘过云天明而前，使那颗黯淡的星星闪动起来。云天明想，当程心看到这颗星时，自己已不在人世了。其实，他和程心看到的这颗星星，是它在二百八十六年前的样子，这束微弱的光线在太空中行走了近三个世纪才接触到他们的视网膜，而它现在发出的光线，要二百八十六年后才能到达地球，那时程心也不在人世了。 她将度过怎样的一生呢？但愿她能记得，茫茫星海中，有一颗星星是属于她的。</p></blockquote><p>（拍摄于四川大学江安校区西园七舍七单元）<br><img src="/photos/%E8%AE%B0%E5%BE%97%E5%90%91%E5%B0%98%E4%B8%96%E4%B9%8B%E5%A4%96%E7%9E%A5%E4%B8%80%E7%9C%BC.jpg" class="lazyload" data-srcset="/photos/%E8%AE%B0%E5%BE%97%E5%90%91%E5%B0%98%E4%B8%96%E4%B9%8B%E5%A4%96%E7%9E%A5%E4%B8%80%E7%9C%BC.jpg" srcset="data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>心累</title>
      <link>https://ff66ccff.github.io/2025/11/11/%E5%BF%83%E7%B4%AF/</link>
      <description>
        <![CDATA[<h1 id="心累"><a href="#心累" class="headerlink"]]>
      </description>
      <author>ff66ccff</author>
      <category domain="https://ff66ccff.github.io/categories/%E7%94%9F%E6%B4%BB/">生活</category>
      <pubDate>Tue, 11 Nov 2025 07:06:39 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="心累"><a href="#心累" class="headerlink" title="心累"></a>心累</h1><p>大学的累和高中的累是不太一致的。高中单纯是长期机械性工作，进而导致思想迟滞和筋疲力尽的累。<br>大学的累是心累，大量的神学课挤占课表，各种课程繁重的课业压力，学业与生活的不平衡，凡此种种，都指向了大学的累。<br>翻看早些年的大学真实生活，我不禁怀疑我上的是不是大号高中。<br>同样的四川大学，二十年前的大学生显然比我更轻松。<strong>（比如<a href="https://mingjietang.wordpress.com/">唐明洁老师</a>）</strong><br>他们那时候没有现在这么多神学课，没有现在这么多学业压力，敢爱敢恨。保研失败大不了考研。身边人没有勾心斗角，非常纯粹的关系。<br>究竟从什么时候大学变成了现在这番模样？<br>我认为<strong>三年疫情</strong>算得上是一个转折点，在此之前的大学虽然学业压力大，但还没有那么多神秘的规矩。<br>但是，在此之后，大学生的自由愈发收缩，人与人的交往愈发困难，再叠加上经济低谷期，一切的一切导致了现在这糟糕的时代。<br>我不喜欢现在的时代。。。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>游戏本的痛</title>
      <link>https://ff66ccff.github.io/2025/11/09/%E6%B8%B8%E6%88%8F%E6%9C%AC%E7%9A%84%E7%97%9B/</link>
      <description>
        <![CDATA[<h1 id="游戏本的痛"><a href="#游戏本的痛" class="headerlink"]]>
      </description>
      <author>ff66ccff</author>
      <category domain="https://ff66ccff.github.io/categories/%E7%94%9F%E6%B4%BB/">生活</category>
      <pubDate>Sun, 09 Nov 2025 20:01:14 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="游戏本的痛"><a href="#游戏本的痛" class="headerlink" title="游戏本的痛"></a>游戏本的痛</h1><p>从宿舍到教学楼大约<strong>2km</strong>，每次背着十多斤的电脑包，不敢骑车只能腿过去。<br>上大学让我被迫成为了习武之人（哭）。<br>玩游戏时瞧不上轻薄本，上课时又痛恨游戏本。。。<br><strong>唉，说到底还是太穷了！</strong></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>欢迎来到新的博客</title>
      <link>https://ff66ccff.github.io/2025/11/09/welcome-to-the-blog/</link>
      <description>
        <![CDATA[<h1 id="欢迎来到新的博客"><a href="#欢迎来到新的博客" class="headerlink"]]>
      </description>
      <author>ff66ccff</author>
      <category domain="https://ff66ccff.github.io/categories/%E7%BC%96%E7%A8%8B/">编程</category>
      <pubDate>Sun, 09 Nov 2025 07:25:04 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h1 id="欢迎来到新的博客"><a href="#欢迎来到新的博客" class="headerlink" title="欢迎来到新的博客"></a>欢迎来到新的博客</h1><blockquote><p>「写给未来读者的第一封信」</p></blockquote><p>最近给主页换上了全新的界面，我决定把这片空间正式升级成博客。<br>这个网站的代码主要依靠Claude和ChatGPT编写，现在是AI时代。</p>]]>
      </content:encoded>
    </item>
  </channel>
</rss>
