<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://azusaaa.com/</id>
  <title type="text">azusaaa</title>
  <subtitle type="text">一些乱七八糟的笔记</subtitle>
  <updated>2026-03-28T14:18:59.000Z</updated>
  <author><name>dsyfb_437</name></author>
  <link rel="alternate" href="https://azusaaa.com/"/>
  <link rel="self" href="https://azusaaa.com/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.8</generator>
    <entry>
      <id>https://azusaaa.com/posts/uestc-acm-icpc-24th-preliminary/</id>
      <title type="text">第二十四届电子科技大学 ACM-ICPC 程序设计竞赛 初赛游记</title>
      <published>2026-03-28T14:18:59.000Z</published>
      <updated>2026-03-28T14:18:59.000Z</updated>
      <author><name>dsyfb_437</name></author>
      <link rel="alternate" href="https://azusaaa.com/posts/uestc-acm-icpc-24th-preliminary/"/>
      <summary type="text">第一次校赛qwq</summary>
      <content type="html"><![CDATA[<p></p><figure><img alt="最终结果" loading="lazy" width="2531" height="185" src="/_astro/24th-pre-final_results.DKzObb3A_Z2aU47v.webp" /><figcaption>最终结果</figcaption></figure><p></p>
<p>rank 50 ，solo 还可以喵，太久没打了，也不会评价难度，就按开题顺序水水</p>
<hr />
<p>几乎打满 awa</p>
<hr />
<p><code>09:00</code> 开始比赛</p>
<p><code>09:50</code> 起床</p>
<section><h2>L<a href="#l"><span>#</span></a></h2><p><code>约10:00</code> 看到大家都在早起做题，B、F、L 过的最多，于是决定先开L</p><p><code>30min</code> 发现是个递推，然后还有逆元，找了个快速幂板子写了</p><p><code>10:31</code> 交了一发 L，WA</p><p><code>5min</code> 发现 0 没有逆元，补了个特判</p><p><code>10:36</code> AC，第一题就吃 20min 罚时（</p></section>
<section><h2>B<a href="#b"><span>#</span></a></h2><p><code>30min</code> 开 B，发现是个背包，去翻背包九讲了，调半天发现自己忘记输入ABC了（</p><p><code>11:12</code> 过 B，感觉自己做题好慢</p></section>
<section><h2>F<a href="#f"><span>#</span></a></h2><p><code>20min</code> 看 F，发现自己不会博弈，然后判了一下异或和为 <span><span>00</span><span><span><span></span><span>0</span></span></span></span> 以及非零堆数为 <span><span>22</span><span><span><span></span><span>2</span></span></span></span> （因为样例有个 <code>0 2 3</code> ），乱写凑正解说是</p><p><code>11:32</code> 交了一发，果然 WA</p><p><code>40min</code> 后来又补了异或和 <span><span>xx</span><span><span><span></span><span>x</span></span></span></span> 和 <span><span>a[i]a[i]</span><span><span><span></span><span>a</span><span>[</span><span>i</span><span>]</span></span></span></span> 相同，然后甚至补了非零堆数为偶数的判断（因为自己造了个 <code>1 2 3 4 5 6</code>）的数据</p><p><code>11:39</code> <code>11:50</code> 又是两发 WA</p></section>
<section><h2>Q<a href="#q"><span>#</span></a></h2><p><code>40min</code> 又看到很多人做 Q ，去做 Q。感谢室友帮忙拿外卖！吃饭。Q 也是一道递推，做了半天样例一直有个点没过，怀疑是自己等比数列求和要求逆元然后模数不是质数有 bug 。改成倍增（应叫这个吧？）求等比数列和。然后过样例。</p><p><code>12:31</code> 喜提 WA</p><p><code>3min</code> 多加了个模（怀疑爆 long long 了）</p><p><code>12:34</code> 意外地过了，然后场上人也渐渐多起来了，solo 确实不如组队，唉</p></section>
<section><h2>F<a href="#f-1"><span>#</span></a></h2><p><code>20min</code> 继续做 F，依然无思路，开始看 oi-wiki 的博弈论，收到一个 <a href="https://oi-wiki.org/math/game-theory/impartial-game/#moores-nim-k-%E6%B8%B8%E6%88%8F" target="_blank">Moore’s Nim-k 游戏</a> 的启发，决定考虑异或和 <span><span>xx</span><span><span><span></span><span>x</span></span></span></span> 是否每一位都是 <span><span>11</span><span><span><span></span><span>1</span></span></span></span> 。写了个暴力右移同时判奇数。</p><p><code>12:56</code> WA</p><p><code>1min</code> 迅速发现万一只有一个数就先手直接赢了（此时依然没有意识到自己题读错了），补了个 <code>n==1</code> 的特判</p><p><code>12:57</code> 依然 WA</p><p><code>20min</code> 终于发现 <code>每次拿完后，如果剩下的所有石子堆的按位异或（BitwiseXOR）和是0，那么这回合拿石子的人就输了。</code> 一直被我读成 <code>每次拿完后，如果剩下的所有石子堆的按位异或（BitwiseXOR）和是0，那么这回合要拿石子的人就输了。</code></p><p>就是理解成下家了，emmm，获得新题目思路，然后就顺理成章了，只有偶数堆或者一开始就是 <span><span>00</span><span><span><span></span><span>0</span></span></span></span> 能赢了，进度大大落后</p><p><code>13:20</code> 终于A了，又获得 100min 罚时</p></section>
<section><h2>P<a href="#p"><span>#</span></a></h2><p><code>20min</code> 然后看了看场上，开始做 P ，估了一下 <span><span>xx</span><span><span><span></span><span>x</span></span></span></span> 的范围就开始暴力枚举，但还是没有每次都重算一遍时长</p><p><code>13:42</code> 直接交，WA</p><p><code>8min</code> 感觉估计范围不准还是什么然后直接改成 <span><span>00</span><span><span><span></span><span>0</span></span></span></span> 到 <span><span>1e91e9</span><span><span><span></span><span>1</span><span>e</span><span>9</span></span></span></span>，然后天数每次 <span><span>11</span><span><span><span></span><span>1</span></span></span></span> 到 <span><span>nn</span><span><span><span></span><span>n</span></span></span></span> 算一遍</p><p><code>13:50</code> WA</p><p><code>3min</code> 意识到 <span><span>xx</span><span><span><span></span><span>x</span></span></span></span> 可能不止 <span><span>1e91e9</span><span><span><span></span><span>1</span><span>e</span><span>9</span></span></span></span> 换了 <span><span>2e182e18</span><span><span><span></span><span>2</span><span>e</span><span>18</span></span></span></span></p><p><code>13:53</code> WA</p><p><code>3min</code> 发现是正整数 <span><span>xx</span><span><span><span></span><span>x</span></span></span></span> ，速改成 <span><span>11</span><span><span><span></span><span>1</span></span></span></span> 到 <span><span>1e91e9</span><span><span><span></span><span>1</span><span>e</span><span>9</span></span></span></span></p><p><code>13:56</code> AC，呃，可能就是因为初赛不计罚时所以随便交，然后最后 8 题里垫底了（话说这个 P 我赛后才知道正解是二分来着，竟然没有 T）</p><p><code>30min</code> 找下一题做，发现自己每次交完题都从 70 多名到 50 多名，可能大家都是这个速度？找了 H 题，读了半天发现题干一半以上在定义 “不降子段” ，呃，然后就用链表模拟删除了，同时考虑删除后连接导致不降子段数减少，两端的数据也兼顾到了。。</p><p><code>14:24</code> WA</p><p><code>30min</code> 依次又补充了一开始算不降子段数时漏的等号，链表两端端点外的指向，改了输出（甚至怀疑行末空格，但其实应该没问题），每改一次交一次，在 <code>14:24</code> <code>14:29</code> <code>14:41</code> <code>14:45</code> 分别喜提四个 WA。最后才发现自己少考虑了单独成列的情况，严格单调递减就每个都是了。</p><p><code>14:53</code> AC，比赛过半</p></section>
<section><h2>J<a href="#j"><span>#</span></a></h2><p><code>1h</code> 开始考虑接下来做哪题，统计了一下剩下题当前的通过人数，考虑E，M，J。都看了看，E 看起来是道贪心，但感觉没那么简单，问了发现材料也可合成而且目标物品也可能会是自己材料的材料，感觉涉及什么拓扑排序（题解是优先队列）。M 是道交互题，昨天热身赛 B 就是交互。。。
J 是个最短路重叠问题。然后 A 也看了一眼。然后文件建好了半天没进展，于是开 J 。找了个 dijkstra 板子，记录路径，然后模拟。</p><p><code>16:42</code> 交一发，不出所料 TLE（但是没有WA，虽说纯模拟也不太会有错）</p><p><code>1.2h</code> 然后就是乱调 J 题，找各种优化。把 dijkstra 范围改小，改成最短路两点之间（想来确实不行，可能会绕一下松弛），然后在 <code>16:44</code> WA ；换了一个堆优化的板子，又交一遍 <code>17:06</code> TLE ；不信邪，又改一遍遍历范围，竟然是 TLE (<code>17:14</code>)。反正虽说算复杂度已经 <span><span>1e111e11</span><span><span><span></span><span>1</span><span>e</span><span>11</span></span></span></span> 就是爆了，但还是试图 dijkstra 卡过去。。。</p><p>最终还是翻到树上最短路可以用求 LCA ，然后也是没有了解细节就直接拿了 LCA 板子，结果是样例都没过（要是过了又是一发不明所以的 WA），后来改名白了又发现样例 <span><span>107ms107ms</span><span><span><span></span><span>107</span><span>m</span><span>s</span></span></span></span> ，把 memset 去掉了，交题，<code>18:02</code> AC，至此算是已经过了 7 题，rank 53 。赛后知道这题其实纯暴力，然后很多人用树链剖分做？</p><p>然而 acm 群里的管理有预测 8 题线的（去年 4 题），决定还是继续比赛，室友已经准备吃饭去了。</p></section>
<section><h2>E<a href="#e"><span>#</span></a></h2><p><code>1h</code> 开始看 E ，先是写了个贪心（问过了还是试一试），交了一发 RE(<code>18:43</code>)，竟然是读入前 <code>continue</code> 吗，改了当然还是 WA (<code>18:52</code>)，最终 E 也是没过</p></section>
<section><h2>O<a href="#o"><span>#</span></a></h2><p><code>19:10</code> 吃饭。</p><p><code>1h</code> 发现 O 是个打表题（呃，反正就是 <span><span>11</span><span><span><span></span><span>1</span></span></span></span> 到 <span><span>1e91e9</span><span><span><span></span><span>1</span><span>e</span><span>9</span></span></span></span> 质数的子集，然后花大概 30min 打了个表，然后又花大概 30min 写了剩下的输出。</p><p><code>20:12</code> WA</p><p><code>20min</code> 调 20min ，疯狂交题，又吃 3 发 WA（<code>20:18</code> <code>20:21</code> <code>20:26</code>）。然后总结下来就是要注意左端点尽量小，然后比较的时候还有左右端点，标准也要统一。</p><p><code>20:32</code> 终于 AC 了，然后发现已经封榜了，也只剩不到 30min 了。看了 M 其实有思路可以二分那个环，但赛时确实没有时间了。。</p><hr /><p>12h 超长初赛。还是有很多不足吧，感觉考了很多图论的知识都不会（只知道概念不会实现），然后快速幂也只会搬板子。赛时也没有很好的发挥，一直是跟着榜在做题，有些题都没仔细思考。</p><p>应该是能进决赛吧。希望一周后线下能有更好的表现吧。</p></section>]]></content>
    </entry>
    <entry>
      <id>https://azusaaa.com/posts/problems-and-errors/</id>
      <title type="text">Problems &amp; Errors</title>
      <published>2026-03-11T10:06:18.000Z</published>
      <updated>2026-03-11T10:06:18.000Z</updated>
      <author><name>dsyfb_437</name></author>
      <link rel="alternate" href="https://azusaaa.com/posts/problems-and-errors/"/>
      <summary type="text">错题集（大概）</summary>
      <content type="html"><![CDATA[<p>做题记录（nt错误）合集</p>
<p><code>2026-4-18</code></p>
<ul>
<li><a href="https://www.luogu.com.cn/problem/P1955" target="_blank">P1955 [NOI2015] 程序自动分析</a>
<ul>
<li>初试离散化 &amp; <code>vector</code></li>
</ul>
</li>
</ul>
<p><code>2026-4-16</code></p>
<ul>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1525" target="_blank">P1525 [NOIP 2010 提高组] 关押罪犯</a></p>
<ul>
<li>贪心是容易的，但考虑维护罪犯（点）之间的相对关系而不是维护监狱（树），算是扩展并查集吧</li>
</ul>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1621" target="_blank">P1621 集合</a></p>
<ul>
<li>过拟合， <span><span>pp</span><span><span><span></span><span>p</span></span></span></span> 不一定是质数，还有记得注释调试代码；另外，可以边筛边合并而不用 <span><span>O(n)O(\sqrt{n})</span><span><span><span></span><span>O</span><span>(</span><span><span><span><span><span><span></span><span><span>n</span></span></span><span><span></span><span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span>)</span></span></span></span> 判素数</li>
<li>了解到一些统计并查集数目的方法，比如统计father为自己的节点个数，边合并边算这种</li>
</ul>
</li>
</ul>
<p><code>2026-3-20</code></p>
<ul>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1802" target="_blank">P1802 5 倍经验日</a></p>
<ul>
<li>这道题我是直接用插值转化为 01 背包做的，基础值应该要另外拿出来或者最后加。总之就是不要把初值设为基础值之和。</li>
</ul>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1064" target="_blank">P1064 [NOIP 2006 提高组] 金明的预算方案</a></p>
<ul>
<li>一道有依赖的背包问题</li>
<li>理论上可以用树形dp做，但是我注意到 附件没有附件 ，于是就分别对附件主件做一遍01背包</li>
<li>因为附件对主件的依赖性处理不当导致 0ptsWA</li>
<li>最后汇总主件时内外循环错误（感觉背包问题就是循环首尾或者内外换一下完全就是另一道题） 20pts</li>
<li>上面的都修了然后 TLE 60pts</li>
<li>最后注意到价格都是 10 的倍数，整体 /10 最后乘回来 100pts</li>
<li>ps：没注意到附件只有 0/1/2 个，可以单独枚举情况</li>
</ul>
</li>
</ul>
<p><code>2026-3-19</code></p>
<ul>
<li>
<p><a href="https://www.luogu.com.cn/problem/P2196" target="_blank">P2196 [NOIP 1996 提高组] 挖地雷</a></p>
<ul>
<li>注意 dp 数组初值！</li>
</ul>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1434" target="_blank">P1434 [SHOI2002] 滑雪</a> &amp; <a href="https://www.luogu.com.cn/problem/P4017" target="_blank">P4017 最大食物链计数</a></p>
<ul>
<li>这两题反映出我做之前还处于线性 dp 思维，对遍历顺序没有拓扑概念，这时可以考虑转化为记忆化搜索去做，尤其是后面一道 DAG 问题</li>
</ul>
</li>
</ul>
<p><code>2026-3-18</code></p>
<ul>
<li><a href="https://www.luogu.com.cn/problem/P1827" target="_blank">P1827 [USACO3.4] 美国血统 American Heritage</a>
<ul>
<li>似乎用下标递归更好（不然似乎会 MLE ？）</li>
</ul>
</li>
</ul>
<p><code>2026-3-15</code></p>
<ul>
<li>
<p><a href="https://www.luogu.com.cn/problem/P15795" target="_blank">P15795 【MX-J25-T2】「Cfz Round 8」Update Problem</a></p>
<ul>
<li>极端数据考虑（很大，很小，接近化归的情况，比如这题我把 <span><span>nn</span><span><span><span></span><span>n</span></span></span></span> 先 <span><span>×−1\times-1</span><span><span><span></span><span>×</span><span></span><span>−</span><span></span></span><span><span></span><span>1</span></span></span></span> 后多次 <span><span>+1+1</span><span><span><span></span><span>+</span><span>1</span></span></span></span> 化成 <span><span>00</span><span><span><span></span><span>0</span></span></span></span> 处理，但实际上 <span><span>∣n∣|n|</span><span><span><span></span><span>∣</span><span>n</span><span>∣</span></span></span></span> 较小时先 <span><span>+1+1</span><span><span><span></span><span>+</span><span>1</span></span></span></span> 后 <span><span>×−1\times-1</span><span><span><span></span><span>×</span><span></span><span>−</span><span></span></span><span><span></span><span>1</span></span></span></span> 更优）</li>
</ul>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P15796" target="_blank">P15796 【MX-J25-T3】「Cfz Round 8」Juice Problem</a></p>
<ul>
<li>开始是一个暴力模拟，就是按题意一个一个倒，溢出用取 <span><span>min⁡\min</span><span><span><span></span><span>min</span></span></span></span> 处理，复杂度 <span><span>O(nq)O(nq)</span><span><span><span></span><span>O</span><span>(</span><span>n</span><span>q</span><span>)</span></span></span></span> , 70pts</li>
<li>然后优化：合并 <span><span>min⁡\min</span><span><span><span></span><span>min</span></span></span></span> 里面的公共部分，不用每次现取 <span><span>min⁡\min</span><span><span><span></span><span>min</span></span></span></span> ，而是先前缀和（ <span><span>s[i]=∑i=1nb[i]s[i]=\sum\limits_{i=1}^n b[i]</span><span><span><span></span><span>s</span><span>[</span><span>i</span><span>]</span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>i</span><span>=</span><span>1</span></span></span></span><span><span></span><span><span>∑</span></span></span><span><span></span><span><span>n</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span>b</span><span>[</span><span>i</span><span>]</span></span></span></span> ）出所有可能值，然后取 <span><span>min⁡\min</span><span><span><span></span><span>min</span></span></span></span>，即求 <span><span>min⁡{b[0]+s[p],a[i]+s[p]−s[i]}\min\{b[0]+s[p],a[i]+s[p]-s[i]\}</span><span><span><span></span><span>min</span><span>{</span><span>b</span><span>[</span><span>0</span><span>]</span><span></span><span>+</span><span></span></span><span><span></span><span>s</span><span>[</span><span>p</span><span>]</span><span>,</span><span></span><span>a</span><span>[</span><span>i</span><span>]</span><span></span><span>+</span><span></span></span><span><span></span><span>s</span><span>[</span><span>p</span><span>]</span><span></span><span>−</span><span></span></span><span><span></span><span>s</span><span>[</span><span>i</span><span>]}</span></span></span></span> ，但复杂度依然是 <span><span>O(nq)O(nq)</span><span><span><span></span><span>O</span><span>(</span><span>n</span><span>q</span><span>)</span></span></span></span></li>
<li>后来发现提取一个 <span><span>s[p]s[p]</span><span><span><span></span><span>s</span><span>[</span><span>p</span><span>]</span></span></span></span> 就可以将题目化为求 <span><span>s[p]+min⁡{b[0],a[i]−s[i]}s[p]+\min\{b[0],a[i]-s[i]\}</span><span><span><span></span><span>s</span><span>[</span><span>p</span><span>]</span><span></span><span>+</span><span></span></span><span><span></span><span>min</span><span>{</span><span>b</span><span>[</span><span>0</span><span>]</span><span>,</span><span></span><span>a</span><span>[</span><span>i</span><span>]</span><span></span><span>−</span><span></span></span><span><span></span><span>s</span><span>[</span><span>i</span><span>]}</span></span></span></span> ，后者用前缀最小预处理，复杂度优化至 <span><span>O(n+q)O(n+q)</span><span><span><span></span><span>O</span><span>(</span><span>n</span><span></span><span>+</span><span></span></span><span><span></span><span>q</span><span>)</span></span></span></span></li>
</ul>
</li>
</ul>
<p><code>2026-3-14</code></p>
<ul>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1241" target="_blank">P1241 括号序列</a></p>
<ul>
<li>如果栈是空的，取栈顶会 RE</li>
<li>然后似乎不需要 <code>replace</code>，直接输出就行了</li>
</ul>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P2234" target="_blank">P2234 [HNOI2002] 营业额统计</a></p>
<ul>
<li>学习 set（STL 大法好）</li>
</ul>
</li>
</ul>
<p><code>2026-3-12</code></p>
<ul>
<li><a href="https://www.luogu.com.cn/problem/P2058" target="_blank">P2058 [NOIP 2016 普及组] 海港</a>
<ul>
<li>过于路径依赖，去重直接 map，然后并没有处理好</li>
<li>实际上是维护一个动态的窗口，于是用队列</li>
</ul>
</li>
</ul>
<p><code>2026-03-11</code></p>
<ul>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1433" target="_blank">P1433 吃奶酪</a></p>
<ul>
<li>第一道状压 DP（也许）！</li>
<li>似乎是经典问题（Traveling Salesman Problem, TSP）简化版（</li>
<li>搜索用普通剪枝过不了（这里 <span><span>NN</span><span><span><span></span><span>N</span></span></span></span> 比较小，然后似乎感觉 DP 的时间复杂度确定一些？因为递归还要考虑栈）</li>
<li>用 <code>memset</code> 的时候注意 <code>memset</code> 是按字节赋值的， <code>double</code> 赋值有bug</li>
</ul>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1596" target="_blank">P1596 [USACO10OCT] Lake Counting S</a></p>
<ul>
<li>这是什么<code>Flood Fill</code> 问题来着，求连通分量个数</li>
<li>然后我用 BFS 写入队没标记重复入队爆队列 RE 了（也没用循环队列）</li>
<li>似乎 DFS 更直观？</li>
<li>BFS 结尾神秘 <code>\n</code> 导致 10pts WA</li>
</ul>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1032" target="_blank">P1032 [NOIP 2002 提高组] 字串变换（疑似错题）</a></p>
<ul>
<li>第一次（？）用 STL</li>
<li>学习 <code>queue</code>，<code>map</code>，包括 <code>string</code> 标准库</li>
<li>用 <code>map</code> 是为了去重，原来 MLE 了甚至</li>
<li><code>map</code> 的键似乎应具有可比性（结构体：… ）</li>
</ul>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1825" target="_blank">P1825 [USACO11OPEN] Corn Maze S</a></p>
<ul>
<li>BFS 也要记得去重，不然反复入队也可能 TLE</li>
<li>感觉去重的同时可以 DP ？</li>
<li>预处理传送门可以三维数组（</li>
</ul>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P3613" target="_blank">P3613 【深基15.例2】寄包柜</a></p>
<ul>
<li>稀疏数组，还得是 STL</li>
</ul>
</li>
</ul>
<p><code>2025-8</code> - <code>2026-2</code></p>
<p>之前记在洛谷上</p>
<blockquote><p>任何一个伟大的思想，都有一个微不足道的开始。</p></blockquote>
<ul>
<li>
<p><a href="https://www.luogu.com.cn/problem/P5707" target="_blank">P5707 【深基2.例12】上学迟到</a>
这是自己不hack的问题，只用样例，测评数据倒是挺到位的</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P5709" target="_blank">P5709 【深基2.习6】Apples Prologue / 苹果和虫子</a>
又是没hack（确实太懒了），总结一些hack点：边界条件（越界和卡界），除法取整问题。 。</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1980" target="_blank">P1980 [NOIP 2013 普及组] 计数问题</a>
<code>float</code>精度不够，不开<code>double</code>见祖宗。 。</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1914" target="_blank">P1914 小书童——凯撒密码</a>
ASCII码只有128个。 。</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1321" target="_blank">P1321 单词覆盖还原</a>
题解区做法好哇，覆盖并没有改变什么，只是变得更好输入了</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1518" target="_blank">P1518 [USACO2.4] 两只塔姆沃斯牛 The Tamworth Two</a>
做完看了看题解，发现有佬用农夫与牛的状态处理判断重复，很多用6维数组，最妙的一个是用农夫与牛的位置加权得到专属值，%%%</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1098" target="_blank">P1098 [NOIP 2007 提高组] 字符串的展开</a>
模拟题就该模拟，不要乱改写题意（比如反面的条件），一定要考虑边界情况！</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1786" target="_blank">P1786 帮贡排序</a>
当冒泡排序练习，两次排序多关键字，注意储存初始相对顺序</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P14972" target="_blank">P14972 『GTOI - 2C』Fliping</a>
比赛题，错在<code>n-2</code>与<code>n-1</code>需要交换时没有特殊考虑，40pts</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1249" target="_blank">P1249 最大乘积</a>
对上界估计方面仍需严谨，当n较大误差也会很大</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1088" target="_blank">P1088 [NOIP 2004 普及组] 火星人</a>
似乎考虑 <span><span>M≤100M\le100</span><span><span><span></span><span>M</span><span></span><span>≤</span><span></span></span><span><span></span><span>100</span></span></span></span> 就以为只要后六位全排列，还是能有数据hack掉的。考虑剪枝全排列dfs或者直接<code>next_permutation</code>唔。然后似乎有康托尔展开的背景（原来是火星人）</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1928" target="_blank">P1928 外星密码</a>
递归但是只能处理最先出现的一些括号套，还得遍历。考虑一个一个字符读入会比字符串方便一些（？），找数字至少不用分类讨论。</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1164" target="_blank">P1164 小A点菜</a>
似乎是01背包，实测dfs剪枝+记忆化才能AC</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P3612" target="_blank">P3612 [USACO17JAN] Secret Cow Code S</a>
一个分治，呃，卡了我两天（awa，后面又是各种细节出错，且没开<code>long long</code>，基础不扎实……感觉这道题可能是从正面考虑，按照题中延长 <span><span>ss</span><span><span><span></span><span>s</span></span></span></span> 的顺序来要方便一些，我从 <span><span>NN</span><span><span><span></span><span>N</span></span></span></span> 入手，结果就……了。看了题解发现就是这个位置好像本来就是字符串反转再平移了一下而已（</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1498" target="_blank">P1498 南蛮图腾</a>
要用数组！！！（卡了好久是因为想直接输出）看到有一个异或递推的（杨辉三角模2发现）的题解，感觉有点接近本质？</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1803" target="_blank">P1803 凌乱的yyy / 线段覆盖</a>
贪心。但是我是考虑优先选和其他比赛重叠最少的（可能不完全正确而且还TLE），而题解给出的做法普遍是优先选结束时间最早的（可以留更多时间去参加后面的比赛）。</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1106" target="_blank">P1106 删数问题</a>
一开始考虑删去比后一个数大的数这个贪心，然后发现这个删数有多轮，可能第一轮就把答案里的数删了。后来改进每轮只删一个数（我还不怎么用STL），这就等价于 <span><span>k=1k=1</span><span><span><span></span><span>k</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span></span></span></span> 的情形，又忘记前导 0 。最后改了前导 0 ，发现还有一个答案就是 0 的点。呃……
然后看题解，有可以选 <span><span>n−kn-k</span><span><span><span></span><span>n</span><span></span><span>−</span><span></span></span><span><span></span><span>k</span></span></span></span> 个数的，每次选第一位，设还要删 <span><span>mm</span><span><span><span></span><span>m</span></span></span></span> 个数，那这个第一位必然在前 <span><span>m+1m+1</span><span><span><span></span><span>m</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span></span></span></span> 个数中产生，这样甚至可以边找边输出。orz 。然后，这似乎又是一个 <a href="https://oi-wiki.org/topic/rmq/" target="_blank">RMQ</a> 问题，有时间复杂度更优的算法，涉及一些数据结构之类的，之后学习一下，挖个坑（</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P5019" target="_blank">P5019 [NOIP 2018 提高组] 铺设道路</a>
感觉很多贪心题都值得好好想一想，比如这个我感觉自己虽然一遍过了但是是一个递归的模拟，看了题解就有 <span><span>O(n)O(n)</span><span><span><span></span><span>O</span><span>(</span><span>n</span><span>)</span></span></span></span> 的做法，每次两个坑填下去， <span><span>ans+=a[i]−a[i−1](a[i]&gt;a[i−1])ans+=a[i]-a[i-1](a[i]&gt;a[i-1])</span><span><span><span></span><span>an</span><span>s</span><span>+</span><span></span><span>=</span><span></span></span><span><span></span><span>a</span><span>[</span><span>i</span><span>]</span><span></span><span>−</span><span></span></span><span><span></span><span>a</span><span>[</span><span>i</span><span></span><span>−</span><span></span></span><span><span></span><span>1</span><span>]</span><span>(</span><span>a</span><span>[</span><span>i</span><span>]</span><span></span><span>&gt;</span><span></span></span><span><span></span><span>a</span><span>[</span><span>i</span><span></span><span>−</span><span></span></span><span><span></span><span>1</span><span>])</span></span></span></span>。因为题目只关心次数（结果），而不是其中的过程，那么并没有必要模拟，就是你一个一个坑填过去，然后如果后面的坑更浅或一样就顺便填了，如果更深还要花费次数再去填。</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P4447" target="_blank">P4447 [AHOI2018初中组] 分组</a>
看到队列标签才想到用队列的（虽然只有用到及时出队降低时间复杂度），我是维护一个分组人数及实力最强者，然后更强的依次入队。题解区还有俄罗斯方块图形化（但确实很有道理）……有一个维护单调性并且使用二分查找的可能比我这个效率高（好像确实欸，一般人看到什么最少的最大就直接二分了）</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1080" target="_blank">P1080 [NOIP 2012 提高组] 国王游戏</a>
邻项交换法似乎不是 <span><span>ii</span><span><span><span></span><span>i</span></span></span></span> 和 <span><span>i+1i+1</span><span><span><span></span><span>i</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span></span></span></span> 项交换的意思</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P3853" target="_blank">P3853 [TJOI2007] 路标设置</a>
二等分不一定最优（</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P2895" target="_blank">P2895 [USACO08FEB] Meteor Shower S</a>
标记有无的桶就直接用bool吧，，，不然会 MLE</p>
</li>
<li>
<p><a href="https://www.luogu.com.cn/problem/P1019" target="_blank">P1019 [NOIP 2000 提高组] 单词接龙（疑似错题）</a>
似乎<code>s.length()</code>返回无符号数，然后如果和有符号的<code>-1</code>比较，<code>-1</code>会被转化为有符号数</p>
</li>
</ul>
<hr />
<p>咕咕咕</p>]]></content>
    </entry>
    <entry>
      <id>https://azusaaa.com/posts/hexo-blog-tutorial/</id>
      <title type="text">Hexo 博客搭建教程</title>
      <published>2026-02-18T14:51:27.000Z</published>
      <updated>2026-02-18T14:51:27.000Z</updated>
      <author><name>dsyfb_437</name></author>
      <link rel="alternate" href="https://azusaaa.com/posts/hexo-blog-tutorial/"/>
      <summary type="text">从零开始的个人博客搭建！</summary>
      <content type="html"><![CDATA[<section><h2>前言<a href="#前言"><span>#</span></a></h2><p>这个博客起源于2020年暑假发现的 <a href="https://open.gridea.dev/" target="_blank">Gridea</a> ，然后这个其实当时也是比较新的软件（RT</p><p></p><figure><img src="https://open.gridea.dev/media/images/gridea.jpg" alt="Gridea官网的图" /><figcaption>Gridea官网的图</figcaption></figure><p></p><p>然后部署了一个基于 Gridea + Github Page 的博客（也是现在这个网址）</p><p>好处就是有客户端，但是主题太少自由度不高，后来就放弃 Gridea 转向 <a href="https://hexo.io/zh-cn/" target="_blank">Hexo</a> 了。（其实也是因为自己很久没维护了）</p><p>虽然同样是静态博客生成器，也是 Github Page 部署，Hexo的社区生态可能就要丰富一些qwq</p><p></p><figure><img alt="Hexo官网截图" loading="lazy" width="2528" height="3593" src="/_astro/hexo-blog-image.DKwvGLtr_f6P3i.webp" /><figcaption>Hexo官网截图</figcaption></figure><p></p></section>
<section><h2>搭建过程<a href="#搭建过程"><span>#</span></a></h2><p>其实 <a href="https://hexo.io/zh-cn/docs/" target="_blank">Hexo文档</a> 里就有</p><section><h3>1. 环境准备<a href="#1-环境准备"><span>#</span></a></h3><ul>
<li>安装 <a href="https://nodejs.org/" target="_blank">Node.js</a></li>
<li>安装 <a href="https://git-scm.com/" target="_blank">Git</a></li>
</ul></section><section><h3>2. 安装 Hexo<a href="#2-安装-hexo"><span>#</span></a></h3><p>终端输入</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>-g</span><span> </span><span>hexo-cli</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>3. 初始化<a href="#3-初始化"><span>#</span></a></h3><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hexo</span><span> </span><span>init</span><span> </span><span>my-blog</span></div></div><div><div><div>2</div></div><div><span>cd</span><span> </span><span>my-blog</span></div></div><div><div><div>3</div></div><div><span>npm</span><span> </span><span>install</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其中 <code>my-blog</code> 为你的博客文件夹</p><p>这一步做完后你的博客目录大概长这样</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>.</span></div></div><div><div><div>2</div></div><div><span>├── _config.yml   # 网站配置文件</span></div></div><div><div><div>3</div></div><div><span>├── package.json  # 应用程序信息</span></div></div><div><div><div>4</div></div><div><span>├── scaffolds     # 模板文件夹</span></div></div><div><div><div>5</div></div><div><span>├── source        # 资源文件夹</span></div></div><div><div><div>6</div></div><div><span>|   ├── _drafts</span></div></div><div><div><div>7</div></div><div><span>|   └── _posts</span></div></div><div><div><div>8</div></div><div><span>└── themes        # 主题文件夹</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>此时你在博客目录下打开终端</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hexo</span><span> </span><span>g</span></div></div><div><div><div>2</div></div><div><span>hexo</span><span> </span><span>s</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后访问 <a href="http://localhost:4000/" target="_blank">http://localhost:4000/</a> 就可以看到你的博客啦</p></section><section><h3>4. 安装主题<a href="#4-安装主题"><span>#</span></a></h3><p>默认主题是 <a href="https://github.com/hexojs/hexo-theme-landscape" target="_blank">landscape</a>，你可以在 <a href="https://hexo.io/themes/" target="_blank">这里</a> 寻找自己喜爱的主题，我目前使用的是 <a href="https://github.com/jerryc127/hexo-theme-butterfly" target="_blank">Butterfly</a>。</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>clone</span><span> </span><span>https://github.com/jerryc127/butterfly-theme.git</span><span> </span><span>themes/butterfly</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>当然也可以直接下载解压后放入<code>theme</code>文件夹，安装完后去 <code>_config.yml</code> 里设置主题。</p></section><section><h3>5. 基础配置<a href="#5-基础配置"><span>#</span></a></h3><p>在网站配置文件和主题文件中进行</p><ul>
<li>_config.yml</li>
<li>_config.butterfly.yml（这个文件在对应主题文件夹里，建议放到博客根目录，便于主题更新）</li>
</ul></section></section>
<section><h2>部署<a href="#部署"><span>#</span></a></h2><p>我使用的是 GitHub Pages，当然也可以部署到别的平台或者自己的服务器上。</p><p>具体步骤如下</p><ul>
<li>
<p>注册 <a href="https://github.com/" target="_blank">Github</a> 账号</p>
</li>
<li>
<p>建立一个名为 <code>你的用户名.github.io</code> 的仓库</p>
</li>
<li>
<p>安装 <a href="https://github.com/hexojs/hexo-deployer-git" target="_blank">hexo-deployer-git</a>：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>hexo-deployer-git</span><span> </span><span>--save</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>如果你不想提供用户名和密码，可以配置 SSH 进行部署</p>
<div><div><div></div><div>Warning</div></div><div></div></div>
</li>
</ul><p>注意你的 Windows 用户名应不含中文
:::</p><ul>
<li>
<p>修改 <code>_config.yml</code></p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>deploy</span><span>:</span></div></div><div><div><div>2</div></div><div><span>type</span><span>: </span><span>git</span></div></div><div><div><div>3</div></div><div><span>repo</span><span>: </span><span>git@github.com:你的用户名/你的用户名.github.io.git</span></div></div><div><div><div>4</div></div><div><span>branch</span><span>: </span><span>main</span></div></div><div><div><div>5</div></div><div><span>url</span><span>: </span><span>https://你的用户名.github.io/</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>在博客文件夹下右键，Git Bash Here</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>git</span><span> </span><span>config</span><span> </span><span>--global</span><span> </span><span>user.name</span><span> </span><span>"你的GitHub用户名"</span></div></div><div><div><div>2</div></div><div><span>git</span><span> </span><span>config</span><span> </span><span>--global</span><span> </span><span>user.email</span><span> </span><span>"你的GitHub邮箱"</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>然后生成 SSH 密钥</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh-keygen</span><span> </span><span>-t</span><span> </span><span>rsa</span><span> </span><span>-C</span><span> </span><span>"你的GitHub邮箱"</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>（可选）添加 SSH 配置文件（似乎是 Github 默认的22端口不能用了要换成443端口？）</p>
<p>运行以下命令创建配置：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>touch</span><span> </span><span>~/.ssh/config</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>然后用记事本打开编辑：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>notepad ~/.ssh/config</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>在打开的文件里粘贴以下内容：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Host github.com</span></div></div><div><div><div>2</div></div><div><span>Hostname ssh.github.com</span></div></div><div><div><div>3</div></div><div><span>User git</span></div></div><div><div><div>4</div></div><div><span>Port 443</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>保存并关闭。</p>
</li>
<li>
<p>把公钥添加到 GitHub</p>
<p>查看公钥内容：</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>cat</span><span> </span><span>~/.ssh/id_rsa.pub</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>复制输出的内容（以 ssh-rsa 开头的那一长串）。</p>
<p>在 GitHub 上添加：</p>
<ol>
<li>打开 <a href="https://github.com/settings/keys" target="_blank">https://github.com/settings/keys</a></li>
<li>点击 “New SSH key”</li>
<li>Title 随便填</li>
<li>Key 粘贴刚才复制的公钥</li>
<li>点击 “Add SSH key”</li>
</ol>
</li>
<li>
<p>测试连接</p>
<div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>ssh</span><span> </span><span>-T</span><span> </span><span>git@github.com</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>成功的话会显示：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>Hi 你的用户名! You've successfully authenticated, but GitHub does not provide shell access.</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>然后执行</p>
</li>
</ul><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>hexo</span><span> </span><span>clean</span><span>  </span><span># 清除缓存文件和生成的静态文件</span></div></div><div><div><div>2</div></div><div><span>hexo</span><span> </span><span>g</span><span>      </span><span># 生成静态文件</span></div></div><div><div><div>3</div></div><div><span>hexo</span><span> </span><span>d</span><span>      </span><span># 部署</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>过一会即可在 <code>https://你的用户名.github.io/</code> 上看到你的博客啦~</p></section>
<section><h2>主题配置<a href="#主题配置"><span>#</span></a></h2><p>具体主题配置参考 <a href="https://butterfly.js.org/posts/21cfbf15/" target="_blank">Butterfly主题文档</a></p><p>以下是我个人使用的一些配置</p><section><h3>1. 固定链接（abbrlink）<a href="#1-固定链接abbrlink"><span>#</span></a></h3><p>需下载 <a href="https://github.com/ohroy/hexo-abbrlink" target="_blank">hexo-abbrlink</a> 插件（或者其他类似插件）：</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>npm</span><span> </span><span>install</span><span> </span><span>hexo-abbrlink</span><span> </span><span>--save</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>然后修改配置文件 <code>_config.yml</code></p><p>主要是文章链接不固定会影响下面的评论记录功能</p><p>每次生成博客时会为文章生成一个固定链接（参考本文链接）</p></section><section><h3>2. 评论系统<a href="#2-评论系统"><span>#</span></a></h3><p>Butterfly主题支持多种评论系统，并支持双评论系统，我采用的是 <a href="https://twikoo.js.org/" target="_blank">Twikoo</a>，免费，有后台，并支持邮箱/微信提醒。</p><p>同样可以参考 <a href="https://twikoo.js.org/quick-start.html" target="_blank">相应文档</a> 以及 <a href="https://www.bilibili.com/video/BV1Fh411e7ZH" target="_blank">视频教程</a> 进行部署</p><ul>
<li>
<p>参考 <a href="https://twikoo.js.org/mongodb-atlas.html" target="_blank">这页</a> 申请 <a href="https://www.mongodb.com/cloud/atlas/register" target="_blank">MongoDB</a> 账号并获得 连接字符串</p>
</li>
<li>
<p>由于了解到 <a href="https://vercel.com/" target="_blank">Vercel</a> 在国内访问较慢（但如果绑自己域名会快一些），并且腾讯云额度有限，于是接下来采用 <a href="https://www.netlify.com/" target="_blank">Netlify</a> 部署后端</p>
<div><div><div></div><div>Warning</div></div><div></div></div>
</li>
</ul><p>注意 Netlify 在国内一些省份的某些运营商网络环境下访问速度较慢，需自行更换网络环境
:::</p><ul>
<li>
<p>首先申请并登录 Netlify</p>
</li>
<li>
<p>把 <a href="https://github.com/twikoojs/twikoo-netlify" target="_blank">这个仓库</a> fork 到自己的仓库下</p>
</li>
<li>
<p>在 Netlify 中依次选择 <code>Add new project</code>、<code>Import an existing project</code>，选择 Github 中自己刚 fork 的仓库</p>
</li>
<li>
<p>找到环境变量 <code>environment variables</code>，<code>Key</code> 填 <code>MONGODB_URI</code>，<code>Value</code> 填刚刚获得的 MongoDB 连接字符串，点击 <code>Deploy twikoo-netlify</code> 进行部署</p>
</li>
<li>
<p>可以绑自己的域名，如果不绑，那么一个形如 <code>https://xxx.netlify.app/.netlify/functions/twikoo</code> 的字符串即是你的环境id，其中 <code>xxx</code> 替换为你的 project 名</p>
</li>
<li>
<p>修改 <code>_config.butterfly.yml</code>：</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>comments</span><span>:</span></div></div><div><div><div>2</div></div><div><span>use</span><span>: </span><span>twikoo</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>以及</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>twikoo</span><span>:</span></div></div><div><div><div>2</div></div><div><span>envId</span><span>: </span><span>https://xxx.netlify.app/.netlify/functions/twikoo</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
</li>
<li>
<p>此时，再执行一次 <code>hexo g -d</code> 等待一会后刷新页面，即可看到你的评论系统啦~</p>
</li>
</ul></section></section>]]></content>
    </entry>
    <entry>
      <id>https://azusaaa.com/posts/pian-fen-dao-lun/</id>
      <title type="text">[转]骗分导论</title>
      <published>2020-08-22T12:10:56.000Z</published>
      <updated>2020-08-22T12:10:56.000Z</updated>
      <author><name>dsyfb_437</name></author>
      <link rel="alternate" href="https://azusaaa.com/posts/pian-fen-dao-lun/"/>
      <summary type="text">新 版 骗 分 导 论</summary>
      <content type="html"><![CDATA[<p>新 版 骗 分 导 论
THE NEW GUIDE OF CHEATING IN INFORMATICS OLYMPIAD
蒟 蒻 的 宝 书</p>
<section><h1>第 1 章 绪论<a href="#第-1-章-绪论"><span>#</span></a></h1><p>在 Oier 中，有一句话广为流传：</p><blockquote><p>任何蒟蒻必须经过大量的刷题练习才能成为大牛乃至于神牛。</p></blockquote><p>这就是著名的 lzn 定理。然而，我们这些蒟蒻们，没有经过那么多历练，却要和大牛们同场竞技，我们该怎么以弱胜强呢？答案就是：</p><p>骗分 那么，骗分是什么呢？骗分就是用简单的程序（比标准算法简单很多，保证蒟蒻能轻松搞定的程序），尽可能多得骗取分数。</p><p>让我们走进这本《新版骗分导论》，来学习骗分的技巧，来挑战神牛吧！</p></section>
<section><h1>第 2 章 从无解出发<a href="#第-2-章-从无解出发"><span>#</span></a></h1><section><h2>2.1 无解情况<a href="#21-无解情况"><span>#</span></a></h2><p>在很多题目中都有这句话：“若无解，请输出-1.”</p><p>看到这句话时，骗分的蒟蒻们就欣喜若狂，因为——数据中必定会有无解的</p><p>情况！那么，只要打出下面这个程序：</p><p><code>printf(“-1”);</code></p><p>就能得到 10 分，甚至 20 分，30 分！</p><p>举个例子：</p><section><h3>NOIP2012 第 4 题，文化之旅<a href="#noip2012-第-4-题文化之旅"><span>#</span></a></h3><section><h4>题目描述 Description<a href="#题目描述-description"><span>#</span></a></h4><p>有一位使者要游历各国，他每到一个国家，都能学到一种文化，但他不愿意学习任何一种文化超过一次（即如果他学习了某种文化，则他就不能到达其他有这种文化的国家）。不同的国家可能有相同的文化。不同文化的国家对其他文化的看法不同，有些文化会排斥外来文化（即如果他学习了某种文化，则他不能到达排斥这种文化的其他国家）。</p><p>现给定各个国家间的地理关系，各个国家的文化，每种文化对其他文化的看法，以及这位使者游历的起点和终点（在起点和终点也会学习当地的文化），国家间的道路距离，试求从起点到终点最少需走多少路。</p></section><section><h4>输入描述 Input Description<a href="#输入描述-input-description"><span>#</span></a></h4><p>第一行为五个整数<span><span>NN</span><span><span><span></span><span>N</span></span></span></span>，<span><span>KK</span><span><span><span></span><span>K</span></span></span></span>，<span><span>MM</span><span><span><span></span><span>M</span></span></span></span>，<span><span>SS</span><span><span><span></span><span>S</span></span></span></span>，<span><span>TT</span><span><span><span></span><span>T</span></span></span></span>，每两个整数之间用一个空格隔开，依次代表国家个数（国家编号为<span><span>11</span><span><span><span></span><span>1</span></span></span></span>到<span><span>NN</span><span><span><span></span><span>N</span></span></span></span>），文化种数（文化编号为<span><span>11</span><span><span><span></span><span>1</span></span></span></span>到<span><span>KK</span><span><span><span></span><span>K</span></span></span></span>），道路的条数，以及起点和终点的编号（保证<span><span>SS</span><span><span><span></span><span>S</span></span></span></span>不等于<span><span>TT</span><span><span><span></span><span>T</span></span></span></span>）；</p><p>第二行为<span><span>NN</span><span><span><span></span><span>N</span></span></span></span>个整数，每两个整数之间用一个空格隔开，其中第<span><span>ii</span><span><span><span></span><span>i</span></span></span></span>个数<span><span>CiC_i</span><span><span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，表示国家 i 的文化为<span><span>CiC_i</span><span><span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>。</p><p>接下来的<span><span>KK</span><span><span><span></span><span>K</span></span></span></span>行，每行<span><span>KK</span><span><span><span></span><span>K</span></span></span></span>个整数，每两个整数之间用一个空格隔开，记第 i 行的第 j 个数为<span><span>ai,ja_i,j</span><span><span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>j</span></span></span></span>，<span><span>ai,j=1a_i,j = 1</span><span><span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>j</span><span></span><span>=</span><span></span></span><span><span></span><span>1</span></span></span></span>表示文化<span><span>ii</span><span><span><span></span><span>i</span></span></span></span>排斥外来文化<span><span>jj</span><span><span><span></span><span>j</span></span></span></span>（<span><span>ii</span><span><span><span></span><span>i</span></span></span></span>等于<span><span>jj</span><span><span><span></span><span>j</span></span></span></span>时表示排斥相同文化的外来人），<span><span>ai,j=0a_i,j = 0</span><span><span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span>,</span><span></span><span>j</span><span></span><span>=</span><span></span></span><span><span></span><span>0</span></span></span></span>表示不排斥（注意<span><span>ii</span><span><span><span></span><span>i</span></span></span></span>排斥<span><span>jj</span><span><span><span></span><span>j</span></span></span></span>并不保证<span><span>jj</span><span><span><span></span><span>j</span></span></span></span>一定也排斥 i）。</p><p>接下来的<span><span>MM</span><span><span><span></span><span>M</span></span></span></span>行，每行三个整数<span><span>uu</span><span><span><span></span><span>u</span></span></span></span>，<span><span>vv</span><span><span><span></span><span>v</span></span></span></span>，<span><span>dd</span><span><span><span></span><span>d</span></span></span></span>，每两个整数之间用一个空格隔开，表示国家 u 与国家 v 有一条距离为<span><span>dd</span><span><span><span></span><span>d</span></span></span></span>的可双向通行的道路（保证<span><span>uu</span><span><span><span></span><span>u</span></span></span></span>不等于<span><span>vv</span><span><span><span></span><span>v</span></span></span></span>，两个国家之间可能有多条道路）。</p></section><section><h4>输出描述 Output Description<a href="#输出描述-output-description"><span>#</span></a></h4><p>输出只有一行，一个整数，表示使者从起点国家到达终点国家最少需要走的距离数（如果无解则输出<span><span>−1-1</span><span><span><span></span><span>−</span><span>1</span></span></span></span>）。</p></section><section><h4>样例输入 Sample Input<a href="#样例输入-sample-input"><span>#</span></a></h4><section><h5>输入样例 1<a href="#输入样例-1"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>2 2 1 1 2</span></div></div><div><div><div>2</div></div><div><span>1 2 0 1 1 0 1 2 10</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>输入样例 2<a href="#输入样例-2"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>2 2 1 1 2</span></div></div><div><div><div>2</div></div><div><span>1 2 0 1 0 0 1 2 10</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h4>样例输出 Sample Output<a href="#样例输出-sample-output"><span>#</span></a></h4><section><h5>输出样例 1<a href="#输出样例-1"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>-1</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>输出样例 2<a href="#输出样例-2"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>10</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section><section><h4>数据范围及提示 Data Size &amp; Hint<a href="#数据范围及提示-data-size--hint"><span>#</span></a></h4><section><h5>【输入输出样例 1 说明】<a href="#输入输出样例-1-说明"><span>#</span></a></h5><p>由于到国家 2 必须要经过国家 1，而国家 2 的文明却排斥国家 1 的文明，所以不可能到达国家 2。</p></section><section><h5>【输入输出样例 2 说明】<a href="#输入输出样例-2-说明"><span>#</span></a></h5><p>路线为<span><span>1−&gt;21 -&gt; 2</span><span><span><span></span><span>1</span><span>−</span><span></span><span>&gt;</span><span></span></span><span><span></span><span>2</span></span></span></span>。</p></section><section><h5>【数据范围】<a href="#数据范围"><span>#</span></a></h5><p>对于<span><span>20%20\%</span><span><span><span></span><span>20%</span></span></span></span>的数据，有<span><span>2≤N≤82\le N\le 8</span><span><span><span></span><span>2</span><span></span><span>≤</span><span></span></span><span><span></span><span>N</span><span></span><span>≤</span><span></span></span><span><span></span><span>8</span></span></span></span>，<span><span>K≤5K\le 5</span><span><span><span></span><span>K</span><span></span><span>≤</span><span></span></span><span><span></span><span>5</span></span></span></span>；</p><p>对于<span><span>30%30\%</span><span><span><span></span><span>30%</span></span></span></span>的数据，有<span><span>2≤N≤102\le N\le 10</span><span><span><span></span><span>2</span><span></span><span>≤</span><span></span></span><span><span></span><span>N</span><span></span><span>≤</span><span></span></span><span><span></span><span>10</span></span></span></span>，<span><span>K≤5K\le 5</span><span><span><span></span><span>K</span><span></span><span>≤</span><span></span></span><span><span></span><span>5</span></span></span></span>；</p><p>对于<span><span>50%50\%</span><span><span><span></span><span>50%</span></span></span></span>的数据，有<span><span>2≤N≤202\le N\le 20</span><span><span><span></span><span>2</span><span></span><span>≤</span><span></span></span><span><span></span><span>N</span><span></span><span>≤</span><span></span></span><span><span></span><span>20</span></span></span></span>，<span><span>K≤8K\le 8</span><span><span><span></span><span>K</span><span></span><span>≤</span><span></span></span><span><span></span><span>8</span></span></span></span>；</p><p>对于<span><span>70%70\%</span><span><span><span></span><span>70%</span></span></span></span>的数据，有<span><span>2≤N≤1002\le N\le 100</span><span><span><span></span><span>2</span><span></span><span>≤</span><span></span></span><span><span></span><span>N</span><span></span><span>≤</span><span></span></span><span><span></span><span>100</span></span></span></span>，<span><span>K≤10K\le 10</span><span><span><span></span><span>K</span><span></span><span>≤</span><span></span></span><span><span></span><span>10</span></span></span></span>；</p><p>对于<span><span>100%100\%</span><span><span><span></span><span>100%</span></span></span></span>的数据，有<span><span>2≤N≤1002\le N\le 100</span><span><span><span></span><span>2</span><span></span><span>≤</span><span></span></span><span><span></span><span>N</span><span></span><span>≤</span><span></span></span><span><span></span><span>100</span></span></span></span>，<span><span>1≤K≤1001\le K\le 100</span><span><span><span></span><span>1</span><span></span><span>≤</span><span></span></span><span><span></span><span>K</span><span></span><span>≤</span><span></span></span><span><span></span><span>100</span></span></span></span>，<span><span>1≤M≤N21\le M\le N2</span><span><span><span></span><span>1</span><span></span><span>≤</span><span></span></span><span><span></span><span>M</span><span></span><span>≤</span><span></span></span><span><span></span><span>N</span><span>2</span></span></span></span>，<span><span>1≤ki≤K1\le k_i\le K</span><span><span><span></span><span>1</span><span></span><span>≤</span><span></span></span><span><span></span><span><span>k</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>≤</span><span></span></span><span><span></span><span>K</span></span></span></span>，<span><span>1≤u,v≤N1\le u,v\le N</span><span><span><span></span><span>1</span><span></span><span>≤</span><span></span></span><span><span></span><span>u</span><span>,</span><span></span><span>v</span><span></span><span>≤</span><span></span></span><span><span></span><span>N</span></span></span></span>，<span><span>1≤d≤10001\le d\le 1000</span><span><span><span></span><span>1</span><span></span><span>≤</span><span></span></span><span><span></span><span>d</span><span></span><span>≤</span><span></span></span><span><span></span><span>1000</span></span></span></span>，<span><span>S≠TS\ne T</span><span><span><span></span><span>S</span><span></span><span><span><span><span><span><span></span><span><span><span></span></span></span><span></span></span></span></span></span><span></span><span>=</span></span><span></span></span><span><span></span><span>T</span></span></span></span>，<span><span>1≤S,T≤N1\le S, T\le N</span><span><span><span></span><span>1</span><span></span><span>≤</span><span></span></span><span><span></span><span>S</span><span>,</span><span></span><span>T</span><span></span><span>≤</span><span></span></span><span><span></span><span>N</span></span></span></span>。</p><p>这道题看起来很复杂，但其中有振奋人心的一句话“输出-1”，我考试时就高兴坏了（当时我才初一，水平太烂），随手打了个<code>printf(“-1”);</code> ,得 10 分。</p></section></section></section></section><section><h2>2.2 样例——白送的分数<a href="#22-样例白送的分数"><span>#</span></a></h2><p>每道题目的后面，都有一组“样例输入”和“样例输出”。它们的价值极大，不仅能初步帮你检验程序的对错（特别坑的样例除外），而且，如果你不会做这道题（这种情况蒟蒻们已经司空见惯了），你就可以直接输出样例！</p><p>例如美国的 USACO，它的题目有一个规则，就是第一组数据必须是样例。那么，只要你输出所有的样例，你就能得到 100 分（满分 1000）！这是相当可观的分数了。</p><p>现在，你已经掌握了最基础的骗分技巧。只要你会基本的输入输出语句，你就能实现这些骗分方法。那么，如果你有一定的基础，请看下一章——我将教你怎样用简单方法骗取部分分数。</p></section></section>
<section><h1>第 3 章 “艰苦朴素永不忘”<a href="#第-3-章-艰苦朴素永不忘"><span>#</span></a></h1><p>本章的标题来源于《学习雷锋好榜样》的一句歌词，但我不是想教导你们学习雷锋精神，而是学习骗分！</p><p>看到“朴素”两个字了吗？它们代表了一类算法，主要有模拟和 DFS。下面我就来介绍它们在骗分中的应用。</p><section><h2>3.1 模拟<a href="#31-模拟"><span>#</span></a></h2><p>所谓模拟，就是用计算机程序来模拟实际的事件。例如 NOIP2012 的“寻宝”，就是写一个程序来模拟小明上藏宝塔的动作。</p><p>较繁的模拟就不叫骗分了，我这里也不讨论这个问题。</p><p>模拟主要可以应用在骗高级数据结构题上的分，例如线段树。下面举一个例子来说明一下。</p><section><h3>排 队(USACO 2007 January Silver)<a href="#排-队usaco-2007-january-silver"><span>#</span></a></h3><section><h4>【问题描述】<a href="#问题描述"><span>#</span></a></h4><p>每天，农夫约翰的<span><span>NN</span><span><span><span></span><span>N</span></span></span></span>（<span><span>1≤N≤500001\le N\le 50000</span><span><span><span></span><span>1</span><span></span><span>≤</span><span></span></span><span><span></span><span>N</span><span></span><span>≤</span><span></span></span><span><span></span><span>50000</span></span></span></span>）头奶牛总是按同一顺序排好队，有一天，约翰决定让一些牛玩一场飞盘游戏（Ultimate Frisbee），他决定在队列里选择一群位置连续的奶牛进行比赛，为了避免比赛结果过于悬殊，要求挑出的奶牛身高不要相差太大。</p><p>约翰准备了<span><span>QQ</span><span><span><span></span><span>Q</span></span></span></span>（<span><span>1≤Q≤2000001\le Q\le 200000</span><span><span><span></span><span>1</span><span></span><span>≤</span><span></span></span><span><span></span><span>Q</span><span></span><span>≤</span><span></span></span><span><span></span><span>200000</span></span></span></span>）组奶牛选择，并告诉你所有奶牛的身高<span><span>HiH_i</span><span><span><span></span><span><span>H</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>（<span><span>1≤Hi≤1061\le H_i\le 10^6</span><span><span><span></span><span>1</span><span></span><span>≤</span><span></span></span><span><span></span><span><span>H</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>≤</span><span></span></span><span><span></span><span>1</span><span><span>0</span><span><span><span><span><span><span></span><span><span>6</span></span></span></span></span></span></span></span></span></span></span>）。他想知道每组里最高的奶牛和最矮的奶牛身高差是多少。</p><p>注意：在最大的数据上，输入输出将占据大部分时间。</p></section><section><h4>【输入】<a href="#输入"><span>#</span></a></h4><p>第一行，两个用空格隔开的整数<span><span>NN</span><span><span><span></span><span>N</span></span></span></span>和<span><span>QQ</span><span><span><span></span><span>Q</span></span></span></span>。</p><p>第<span><span>22</span><span><span><span></span><span>2</span></span></span></span>到第<span><span>N+1N+1</span><span><span><span></span><span>N</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span></span></span></span>行，每行一个整数，第<span><span>i+1i+1</span><span><span><span></span><span>i</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span></span></span></span>行表示第<span><span>ii</span><span><span><span></span><span>i</span></span></span></span>头奶牛的身高<span><span>HiH_i</span><span><span><span></span><span><span>H</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span></p><p>第<span><span>N+2N+2</span><span><span><span></span><span>N</span><span></span><span>+</span><span></span></span><span><span></span><span>2</span></span></span></span>到第<span><span>N+Q+1N+Q+1</span><span><span><span></span><span>N</span><span></span><span>+</span><span></span></span><span><span></span><span>Q</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span></span></span></span>行，每行两个用空格隔开的整数<span><span>AA</span><span><span><span></span><span>A</span></span></span></span>和<span><span>BB</span><span><span><span></span><span>B</span></span></span></span>，表示选择从<span><span>AA</span><span><span><span></span><span>A</span></span></span></span>到<span><span>BB</span><span><span><span></span><span>B</span></span></span></span>的所有牛（<span><span>1≤A≤B≤N1 \le A \le B \le N</span><span><span><span></span><span>1</span><span></span><span>≤</span><span></span></span><span><span></span><span>A</span><span></span><span>≤</span><span></span></span><span><span></span><span>B</span><span></span><span>≤</span><span></span></span><span><span></span><span>N</span></span></span></span>）</p></section><section><h4>【输出】<a href="#输出"><span>#</span></a></h4><p>共<span><span>QQ</span><span><span><span></span><span>Q</span></span></span></span>行，每行一个整数，代表每个询问的答案。</p><section><h5>输入样例<a href="#输入样例"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>6 3</span></div></div><div><div><div>2</div></div><div><span>1</span></div></div><div><div><div>3</div></div><div><span>7</span></div></div><div><div><div>4</div></div><div><span>3</span></div></div><div><div><div>5</div></div><div><span>4</span></div></div><div><div><div>6</div></div><div><span>2</span></div></div><div><div><div>7</div></div><div><span>5</span></div></div><div><div><div>8</div></div><div><span>1 5</span></div></div><div><div><div>9</div></div><div><span>4 6</span></div></div><div><div><div>10</div></div><div><span>2 2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>输出样例<a href="#输出样例"><span>#</span></a></h5><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>6</span></div></div><div><div><div>2</div></div><div><span>3</span></div></div><div><div><div>3</div></div><div><span>0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>对于这个例子，大牛们可以写个线段树，而我们蒟蒻，就模拟吧。</p><p>附模拟程序：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for</span><span> (</span><span>int</span><span> </span><span>i</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>i</span><span> </span><span>&lt;=</span><span> </span><span>q</span><span>; </span><span>i</span><span>++</span><span>)</span></div></div><div><div><div>2</div></div><div><span>{</span></div></div><div><div><div>3</div></div><div><span>  </span><span>scanf</span><span>(</span><span>"</span><span>%d%d</span><span>"</span><span>, </span><span>&amp;</span><span>a</span><span>, </span><span>&amp;</span><span>b</span><span>);</span></div></div><div><div><div>4</div></div><div><span>  </span><span>int</span><span> </span><span>min</span><span> </span><span>=</span><span> </span><span>INT_MAX</span><span>, </span><span>max</span><span> </span><span>=</span><span> </span><span>INT_MIN</span><span>;</span></div></div><div><div><div>5</div></div><div><span>  </span><span>for</span><span> (</span><span>int</span><span> </span><span>i</span><span> </span><span>=</span><span> </span><span>a</span><span>; </span><span>i</span><span> </span><span>&lt;=</span><span> </span><span>b</span><span>; </span><span>i</span><span>++</span><span>)</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>{</span></div></div><div><div><div>7</div></div><div><span>    </span><span>if</span><span> (</span><span>h</span><span>[</span><span>i</span><span>] </span><span>&lt;</span><span> </span><span>min</span><span>)</span></div></div><div><div><div>8</div></div><div><span>      </span><span>min</span><span> </span><span>=</span><span> </span><span>h</span><span>[</span><span>i</span><span>];</span></div></div><div><div><div>9</div></div><div><span>    </span><span>if</span><span> (</span><span>h</span><span>[</span><span>i</span><span>] </span><span>&gt;</span><span> </span><span>max</span><span>)</span></div></div><div><div><div>10</div></div><div><span>      </span><span>max</span><span> </span><span>=</span><span> </span><span>h</span><span>[</span><span>i</span><span>];</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span>  </span><span>printf</span><span>(</span><span>"</span><span>%d</span><span>\n</span><span>"</span><span>, </span><span>max</span><span> </span><span>-</span><span> </span><span>min</span><span>);</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>程序简洁明了，并且能高效骗分。本程序得 50 分。</p></section></section></section></section><section><h2>3.2 万能钥匙——DFS<a href="#32-万能钥匙dfs"><span>#</span></a></h2><p>DFS 是图论中的重要算法，但我们看来，图论神马的都是浮云，关键就是如何骗分。下面引出本书的第 2 条定理：</p><p>DFS 是万能的。</p><p>这对于你的骗分是至关重要的。比如说，一些动态规划题，可以 DFS；数学题，可以 DFS；剪枝的题，更能 DFS。下面以一道省选题为例，解释一下 DFS 骗分。</p><section><h3>例题：NOIP2003，采药<a href="#例题noip2003采药"><span>#</span></a></h3><section><h4>题目描述 Description<a href="#题目描述-description-1"><span>#</span></a></h4><p>辰辰是个天资聪颖的孩子，他的梦想是成为世界上最伟大的医师。为此，他想拜附近最有威望的医师为师。医师为了判断他的资质，给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说：“孩子，这个山洞里有一些不同的草药，采每一株都需要一些时间，每一株也有它自身的价值。我会给你一段时间，在这段时间里，你可以采到一些草药。如果你是一个聪明的孩子，你应该可以让采到的草药的总价值最大。”</p><p>如果你是辰辰，你能完成这个任务吗？</p></section><section><h4>输入描述 Input Description<a href="#输入描述-input-description-1"><span>#</span></a></h4><p>输入第一行有两个整数 T（1&lt;=T&lt;=1000）和 M（1&lt;=M&lt;=100），用一个空格隔开，T 代表总共能够用来采药的时间，M 代表山洞里的草药的数目。接下来的 M 行每行包括两个在 1 到 100 之间（包括 1 和 100）的整数，分别表示采摘某株草药的时间和这株草药的价值。</p></section><section><h4>输出描述 Output Description<a href="#输出描述-output-description-1"><span>#</span></a></h4><p>输出包括一行，这一行只包含一个整数，表示在规定的时间内，可以采到的草药的最大总价值。</p></section><section><h4>样例输入 Sample Input<a href="#样例输入-sample-input-1"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>70 3</span></div></div><div><div><div>2</div></div><div><span>71 100</span></div></div><div><div><div>3</div></div><div><span>69 1</span></div></div><div><div><div>4</div></div><div><span>1 2</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>样例输出 Sample Output<a href="#样例输出-sample-output-1"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>3</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h4>数据范围及提示 Data Size &amp; Hint<a href="#数据范围及提示-data-size--hint-1"><span>#</span></a></h4><p>对于<span><span>30%30\%</span><span><span><span></span><span>30%</span></span></span></span>的数据，<span><span>M≤10M\le 10</span><span><span><span></span><span>M</span><span></span><span>≤</span><span></span></span><span><span></span><span>10</span></span></span></span>；</p><p>对于全部的数据，<span><span>M≤100M\le 100</span><span><span><span></span><span>M</span><span></span><span>≤</span><span></span></span><span><span></span><span>100</span></span></span></span>。</p><p>这题的方法很简单。我们瞄准<span><span>30%30\%</span><span><span><span></span><span>30%</span></span></span></span>的数据来做，可以用 DFS 枚举方案，然后模拟计算出最优解。附一个大致的代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>void</span><span> </span><span>DFS</span><span>(</span><span>int</span><span><span> </span><span>d</span><span>, </span></span><span>int</span><span><span> </span><span>c</span><span>)</span></span></div></div><div><div><div>2</div></div><div><span>{</span></div></div><div><div><div>3</div></div><div><span>  </span><span>if</span><span> (</span><span>d</span><span> </span><span>==</span><span> </span><span>n</span><span>)</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span>{</span></div></div><div><div><div>5</div></div><div><span>    </span><span>if</span><span> (</span><span>c</span><span> </span><span>&gt;</span><span> </span><span>ans</span><span>)</span></div></div><div><div><div>6</div></div><div><span>      </span><span>ans</span><span> </span><span>=</span><span> </span><span>c</span><span>;</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span>;</span></div></div><div><div><div>8</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>9</div></div><div><span>  </span><span>DFS</span><span>(</span><span>d</span><span> </span><span>+</span><span> </span><span>1</span><span>, </span><span>c</span><span> </span><span>+</span><span> </span><span>w</span><span>[</span><span>i</span><span>]);</span></div></div><div><div><div>10</div></div><div><span>  </span><span>DFS</span><span>(</span><span>d</span><span> </span><span>+</span><span> </span><span>1</span><span>, </span><span>c</span><span>);</span></div></div><div><div><div>11</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section></section></section></section>
<section><h1>第 4 章 骗分的关键——猜想<a href="#第-4-章-骗分的关键猜想"><span>#</span></a></h1><section><h2>4.1 听天由命<a href="#41-听天由命"><span>#</span></a></h2><p>如果你觉得你的人品很好，可以试试这一招——输出随机数。</p><p>先看一下代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;stdlib.h&gt;</span></div></div><div><div><div>2</div></div><div><span>#include</span><span> </span><span>&lt;time.h&gt;</span></div></div><div><div><div>3</div></div><div><span>//以上两个头文件必须加</span></div></div><div><div><div>4</div></div><div><span>srand</span><span>(</span><span>time</span><span>(</span><span>NULL</span><span>));</span></div></div><div><div><div>5</div></div><div><span>//输出随机数前执行此语句</span></div></div><div><div><div>6</div></div><div><span>printf</span><span>(</span><span>"</span><span>%d</span><span>"</span><span>, </span><span>rand</span><span>() </span><span>%</span><span> </span><span>X</span><span>);</span></div></div><div><div><div>7</div></div><div><span>//输出一个0~X-1的随机整数。</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这种方法适用于输出一个整数（或判断是否）的题目中，答案的范围越小越好。让老天决定你的得分吧。 据说，在 NOIP2013 中，有人最后一题不会，愤然打了个随机数，结果得了 70 分啊!!</p></section><section><h2>4.2 猜测答案<a href="#42-猜测答案"><span>#</span></a></h2><p>有些时候，问题的答案可能很有特点：对于大多数情况，答案是一样的。这时，骗分就该出手了。你需要做的，就是发掘出这个答案，然后直接输出。</p><p>有时，你需要运用第 3 章中学到的知识，先写出朴素算法，然后造一些数据，可能就会发现规律。</p><p>例如，本班月赛中有一道题：</p><section><h3>炸毁计划<a href="#炸毁计划"><span>#</span></a></h3><section><h4>【问题描述】<a href="#问题描述-1"><span>#</span></a></h4><p>皇军侵占了通往招远的黄金要道。为了保护渤海通道的安全，使得黄金能够顺利地运送到敌后战略总指挥地延安，从而购买战需武器，所以我们要通过你的程序确定这条战略走廊是否安全。</p><p>已知我们有<span><span>NN</span><span><span><span></span><span>N</span></span></span></span>座小岛，只有使得每一个小岛都能与其他任意一个小岛联通才能保证走廊的安全。每个小岛之间只能通过若干双向联通的桥保持联系，已知有<span><span>MM</span><span><span><span></span><span>M</span></span></span></span>座桥(<span><span>AiA_i</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>,<span><span>BiB_i</span><span><span><span></span><span><span>B</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>)表示第 i 座桥连接了<span><span>AiA_i</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>与<span><span>BiB_i</span><span><span><span></span><span><span>B</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>这两座城市。</p><p>现在，敌人的炸药只能炸毁其中一座桥，请问在仅仅炸毁这一座桥的情况下，能否保证所有岛屿安全，都能联通起来。</p><p>现在给出<span><span>QQ</span><span><span><span></span><span>Q</span></span></span></span>个询问<span><span>CiC_i</span><span><span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，其中<span><span>CiC_i</span><span><span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>表示桥梁编号，桥梁的编号按照输入顺序编号。每个询问表示在仅仅炸毁第<span><span>CiC_i</span><span><span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>座桥的情况下能否保证所有岛屿安全。如果可以，在输出文件当中，对应输入顺序输出<code>yes</code>，否则输出<code>no</code>（输出为半角英文单词，区分大小写，默认为小写，不含任何小写符号，每行输出一个空格，忽略文末空格）。</p></section><section><h4>【输入格式】<a href="#输入格式"><span>#</span></a></h4><p>第一行 三个整数<span><span>NN</span><span><span><span></span><span>N</span></span></span></span>，<span><span>MM</span><span><span><span></span><span>M</span></span></span></span>，<span><span>QQ</span><span><span><span></span><span>Q</span></span></span></span>，分别表示岛屿的个数，桥梁的个数和询问的个数。</p><p>第二行到第<span><span>M+1M+1</span><span><span><span></span><span>M</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span></span></span></span>行 每行两个整数。第<span><span>i+1i+1</span><span><span><span></span><span>i</span><span></span><span>+</span><span></span></span><span><span></span><span>1</span></span></span></span>行有两个整数<span><span>AiA_i</span><span><span><span></span><span><span>A</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>，<span><span>BiB_i</span><span><span><span></span><span><span>B</span><span><span><span><span><span><span></span><span><span>i</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>表示这个桥梁的属性。</p><p>第<span><span>M+2M+2</span><span><span><span></span><span>M</span><span></span><span>+</span><span></span></span><span><span></span><span>2</span></span></span></span>行 有<span><span>QQ</span><span><span><span></span><span>Q</span></span></span></span>个整数<span><span>CiCi</span><span><span><span></span><span>C</span><span>i</span></span></span></span>表示查询。</p></section><section><h4>【输出格式】<a href="#输出格式"><span>#</span></a></h4><p><span><span>QQ</span><span><span><span></span><span>Q</span></span></span></span>行，表示查询结果。</p><section><h5>【样例】<a href="#样例"><span>#</span></a></h5><p><code>destroy.in</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>2 1 1</span></div></div><div><div><div>2</div></div><div><span>1</span></div></div><div><div><div>3</div></div><div><span>2</span></div></div><div><div><div>4</div></div><div><span>1</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><code>destroy.out</code></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>no</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h5>【样例范围】<a href="#样例范围"><span>#</span></a></h5><p>对于<span><span>80%80\%</span><span><span><span></span><span>80%</span></span></span></span>的数据，<span><span>N≤100N\le 100</span><span><span><span></span><span>N</span><span></span><span>≤</span><span></span></span><span><span></span><span>100</span></span></span></span>。</p><p>对于<span><span>100%100\%</span><span><span><span></span><span>100%</span></span></span></span>的数据，<span><span>N≤1000N\le 1000</span><span><span><span></span><span>N</span><span></span><span>≤</span><span></span></span><span><span></span><span>1000</span></span></span></span>，<span><span>N,Q≤M≤2000N,Q\le M\le 2000</span><span><span><span></span><span>N</span><span>,</span><span></span><span>Q</span><span></span><span>≤</span><span></span></span><span><span></span><span>M</span><span></span><span>≤</span><span></span></span><span><span></span><span>2000</span></span></span></span>。</p><p>你发现问题了吗？那么多座桥，炸一座就破坏岛屿的联系，可能性微乎其微（除非特别设计数据）。那么，我们的骗分策略就出来了：对于所有询问，输出<code>yes</code>.果然，此算法效果不错，得 80 分。</p><p>现在知道猜测答案的厉害了吧？</p></section></section></section></section><section><h2>4.3 寻找规律<a href="#43-寻找规律"><span>#</span></a></h2><p>首先声明：本节讲的规律不是正当的算法规律，而是数据的特点。</p><p>某些题目会给你很多样例，你就可以观察他们的特点了。有时，数据中的某一个（或几个）数，能通过简单的关系直接算出答案。</p><p>只要你找到了规律，在很多情况下你都能得到可观的分数。</p><p>这样的题目大多出现在 NOI 或更高等级的比赛中，本人蒟蒻一个，就不举例了。传说某人去省选时专门琢磨数据的规律，结果有一题得了 30 分。</p></section><section><h2>4.4 小数据杀手——打表<a href="#44-小数据杀手打表"><span>#</span></a></h2><p>我认识一个人，他在某老师家上 C 语言家教，老师每讲一题，他都喊一句：“打表行吗？”</p><p>他真的是打表的忠实粉丝。表虽然不能乱打，但还是很有用的。</p><section><h3>先看一个例子：NOIP2003 栈<a href="#先看一个例子noip2003-栈"><span>#</span></a></h3><section><h4>题目描述 Description<a href="#题目描述-description-2"><span>#</span></a></h4><p>栈是计算机中经典的数据结构，简单的说，栈就是限制在一端进行插入删除操作的线性表。</p><p>栈有两种最重要的操作，即 pop（从栈顶弹出一个元素）和 push（将一个元素进栈）。</p><p>栈的重要性不言自明，任何一门数据结构的课程都会介绍栈。宁宁同学在复习栈的基本概念时，想到了一个书上没有讲过的问题，而他自己无法给出答案，所以需要你的帮忙</p><p>宁宁考虑的是这样一个问题：一个操作数序列，从 1，2，一直到 n（图示为 1 到 3 的情况），栈 A 的深度大于 n。</p><p>现在可以进行两种操作，</p><p>1.将一个数，从操作数序列的头端移到栈的头端（对应数据结构栈的 push 操作）</p><p>将一个数，从栈的头端移到输出序列的尾端（对应数据结构栈的 pop 操作）
使用这两种操作，由一个操作数序列就可以得到一系列的输出序列，下图所示为由 1 2 3 生成序列 2 3 1 的过程。（原始状态如上图所示） 。</p><p>你的程序将对给定的 n，计算并输出由操作数序列 1，2，…，n 经过操作可能得到的输出序列的总数。</p></section><section><h4>输入描述 Input Description<a href="#输入描述-input-description-2"><span>#</span></a></h4><p>输入文件只含一个整数 n（1≤n≤18）</p></section><section><h4>输出描述 Output Description<a href="#输出描述-output-description-2"><span>#</span></a></h4><p>输出文件只有一行，即可能输出序列的总数目</p></section><section><h4>样例输入 Sample Input<a href="#样例输入-sample-input-2"><span>#</span></a></h4></section><section><h4>3 样例输出 Sample Output<a href="#3-样例输出-sample-output"><span>#</span></a></h4><p>5</p><p>这题看似复杂，但数据范围太小，N&lt;=18。所以，骗分程序就好写了：</p><p>int a[18]={1,2,5,14,42,132,429,1430,4862,16796,58786,208012,742900,2674440,9694845,35357670,129644790,477638700};</p><p>scanf(“%d”,&amp;n):</p><p>printf(“%d”,ans[n-1]);</p><p>测试结果不言而喻，AC 了。</p><p>学完这一章，你已基本掌握了骗分技巧。下面的内容涉及一点算法知识，难度有所增加。蒟蒻中的蒟蒻可以止步于此了。</p></section></section></section></section>
<section><h1>第 5 章 做贪心的人<a href="#第-5-章-做贪心的人"><span>#</span></a></h1><section><h2>5.1 贪心的算法<a href="#51-贪心的算法"><span>#</span></a></h2><p>给你一堆纸币，让你挑一张，相信你一定会挑面值最大的。其实，这就是贪心算法。</p><p>贪心算法是个复杂的问题，但你不用管那么多。我们只关心骗分。给你一个问题，让你从一些东西中选出一些，你就可以使用贪心的方法，尽量挑好的。</p><p>举个例子：这是我们的市队选拔的一道题。</p><section><h3>有趣的问题<a href="#有趣的问题"><span>#</span></a></h3><section><h4>【问题描述】<a href="#问题描述-2"><span>#</span></a></h4><p>2013 年的 NOIP 结束后， Smart 发现自己又被题目碾压了，心里非常地不爽，于是</p><p>暗下决心疯狂地刷数学题目，做到天昏地暗、废寝忘食，准备在今年的中考中大展身手。</p><p>有一天，他在做题时发现了一个有趣的问题：</p><p>给定 n 个二元组(ai, bi) i)，记函数：</p><p>y=100*sigma(ai)/sigma(bi);</p><p>将函数 y 的值四舍五入取整。</p><p>现将 n 个二元组去掉其中的 k 个计算一个新的 y 值（也四舍五入取整），均能满足：y &lt;= z ，求出最小的 z 值。Smart 想让你帮他一起找出最小的 z 值。</p></section><section><h4>【输入格式】<a href="#输入格式-1"><span>#</span></a></h4><p>输入包含多组测试数据。每组测试数据第一行两个整数：n 和 k；第二行为 n 个数：</p><p>a1 a2 …… an；第三行为；n 个数： b1 b2 …… bn。</p><p>输入数据当 n、k 均为 0 时结束。</p></section><section><h4>【输出格式】<a href="#输出格式-1"><span>#</span></a></h4><p>对于每组测试数据输出一行，即找出的最小的冘值。</p><p>注意：为避免精度四舍五入出现误差，测试点保证每个函数值与最终结果的差值至</p><p>少为 0.001 。</p></section><section><h4>【样例】<a href="#样例-1"><span>#</span></a></h4><p>math.in</p><p>3 1 5 0 1 5 1 6 4 2 1 2 7 9</p><p>5 6 7 9</p><p>0 0 math. out</p></section><section><h4>83 100 【数据范围】<a href="#83-100-数据范围"><span>#</span></a></h4><p>对于 40% 的数据： n≤20；</p><p>对于 70% 的数据： n≤1000；</p><p>对于 100% 的数据： n≤10000，ai,bi 都在 int 范围内。</p><p>这题让人望而生畏，但我们有贪心的手段。每个二元组的 a 值是乘到答案中的，所以 a 越大越好，那么只要选择出最小的 k 个去掉即可。代码就不写了，因为这个设计到下一章的内容：排序。</p><p>此代码得<span><span>2020</span><span><span><span></span><span>20</span></span></span></span>分。</p></section></section></section><section><h2>5.2 贪心地得分<a href="#52-贪心地得分"><span>#</span></a></h2><p>我们已经学了很多骗分方法，但他们中的大多效率并不高，一般能骗 10~20 分。这不能满足我们的贪心。</p><p>然而，我们可以合成骗分的程序。举个最简单的例子，有些含有无解情况的题目，它们同样有样例。我们可以写这个程序</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>if</span><span> (是样例)</span></div></div><div><div><div>2</div></div><div><span>  </span><span>printf</span><span>(样例);</span></div></div><div><div><div>3</div></div><div><span>else</span></div></div><div><div><div>4</div></div><div><span>  </span><span>printf</span><span>(</span><span>"-1"</span><span>);</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这样也许能变<span><span>1010</span><span><span><span></span><span>10</span></span></span></span>分为<span><span>2020</span><span><span><span></span><span>20</span></span></span></span>分，甚至更多。</p><p>当然，合并骗分方法时要注意，不要重复骗同一种情况，或漏考虑一些情况。</p><p>大量能骗分的问题都能用此法，大家可以试试用新方法骗<code>2.1</code>中的例子“文化之旅”。</p></section></section>
<section><h1>第 6 章 C++的福利<a href="#第-6-章-c的福利"><span>#</span></a></h1><p>（请 P 党们跳过本章，这不是你们的福利）</p><p>在 C++中，有一个好东西，名唤<code>STL</code>，被万千 Oier 们所崇拜，所喜爱。下面让我们走进<code>STL</code>。</p><section><h2>6.1 快速排序<a href="#61-快速排序"><span>#</span></a></h2><p>快速排序是一个经典算法，也是 C++党的经典福利。他们有这样的代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;algorithm&gt;</span><span> </span><span>//这是必须的</span></div></div><div><div><div>2</div></div><div><span>sort</span><span>(</span><span>A</span><span>, </span><span>A</span><span> </span><span>+</span><span> </span><span>n</span><span>);</span><span>     //对一个下标从0开始存储，长度为n的数组升序排序</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>就这么简单，完成了 P 党一大堆代码干的事情。</p></section><section><h2>6.2 “如意金箍棒”<a href="#62-如意金箍棒"><span>#</span></a></h2><p>C++里有一种东西，叫<code>vector</code>容器。它好比如意金箍棒，可以随着元素的数量而改变大小。它其实就是数组，却比数组强得多。</p><p>下面看看它的几种操作：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>vector</span><span>&lt;int&gt;</span><span> </span><span>V</span><span>;</span><span>  //定义</span></div></div><div><div><div>2</div></div><div><span>V</span><span>.</span><span>push_back</span><span>(</span><span>x</span><span>);</span><span> //末尾增加一个元素x</span></div></div><div><div><div>3</div></div><div><span>V</span><span>.</span><span>pop_back</span><span>();</span><span>  //末尾删除一个元素</span></div></div><div><div><div>4</div></div><div><span>V</span><span>.</span><span>size</span><span>();</span><span>    //返回容器中的元素个数</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>它同样可以使用下标访问。（从<span><span>00</span><span><span><span></span><span>0</span></span></span></span>开始）</p></section></section>
<section><h1>第 7 章 “宁为玉碎，不为瓦全”<a href="#第-7-章-宁为玉碎不为瓦全"><span>#</span></a></h1><p>至此，我已介绍完了我所知的骗分方法。如果上面的方法都不奏效，我也无能为力。但是，我还有最后一招——</p><p>有句古话说：“宁为玉碎，不为瓦全”。我们蒟蒻也应有这样的精神。骗不到分，就报复一下，卡评测以泄愤吧！</p><p>卡评测主要有两种方法：一是死循环，故意超时；二是进入终端，卡住编译器。</p><p>先介绍下第一种。代码很简单，请看：</p><p><code>while(1);</code></p><p>就是这短短一句话，就能卡住评测机长达 10s，20s，甚至更多！对于测试点多、时限长的题目，这是个不错的方法。</p><p>第二种方法也很简单，但危害性较大，建议不要在重要比赛中使用，否则可能让你追悔莫及。它就是：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>include&lt;con&gt; （windows系统中使用）</span></div></div><div><div><div>2</div></div><div><span>或 #include&lt;/dev/console&gt; （Linux系统中使用）</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>它非常强大，可以卡住评测系统，使其永远停止不了编译你的程序。唯一的解除方法是，工作人员强行关机，重启，重测。当然，我不保证他们不会气愤地把你的成绩变成 0 分。请慎用此方法。</p></section>
<section><h1>第 8 章 实战演练<a href="#第-8-章-实战演练"><span>#</span></a></h1><p>下面我们来做一些习题，练习骗分技巧。</p><p>我们来一起分析一下 NOIP2013 普及组的试题吧。</p><section><h3>记数问题（NOIP 普及组 2013 第一题）<a href="#记数问题noip-普及组-2013-第一题"><span>#</span></a></h3><p>(count.cpp/c/pas)</p><p>描述 试计算在区间 1 到 n 的所有整数中,数字 x(0 ≤ x ≤ 9)共出现了多少次?例如,在 1 到 11 中,即在 1、2、3、4、5、6、7、8、9、10、11 中,数字 1 出现了 4 次。</p><section><h4>【输入】<a href="#输入-1"><span>#</span></a></h4><p>输入文件名为 count.in。</p><p>输入共 1 行，包含 2 个整数 n、x，之间用一个空格隔开</p></section><section><h4>【输出】<a href="#输出-1"><span>#</span></a></h4><p>输出文件名为 count.out。</p><p>输出共 1 行，包含一个整数，表示 x 出现的次数。</p></section><section><h4>【输入输出样例】<a href="#输入输出样例"><span>#</span></a></h4><p>count.in count.out</p><p>11 1 4 限制 每个测试点 1s。</p></section><section><h4>【数据说明】<a href="#数据说明"><span>#</span></a></h4><p>对于 100%的数据，1≤ n ≤ 1,000,000，0 ≤ x ≤ 9。</p></section></section><section><h3>表达式求值（noip2013 普及组第二题）<a href="#表达式求值noip2013-普及组第二题"><span>#</span></a></h3><p>(expr.cpp/c/pas)</p><p>描述 给定一个只包含加法和乘法的算术表达式,请你编程计算表达式的值。</p><section><h4>【输入】<a href="#输入-2"><span>#</span></a></h4><p>输入文件为 expr.in。</p><p>输入仅有一行，为需要你计算的表达式，表达式中只包含数字、加法运算符“+”和乘 ，且没有括号，所有参与运算的数字均为 0 到 231-1 之间的整数。输入数据保 法运算符“*”</p><p>证这一行只有 0~ 9、+、*这 12 种字符。</p></section><section><h4>【输出】<a href="#输出-2"><span>#</span></a></h4><p>输出文件名为 expr.out。</p><p>输出只有一行，包含一个整数，表示这个表达式的值。注意：当答案长度多于 4 位时，</p><p>请只输出最后 4 位，前导 0 不输出。</p></section><section><h4>【输入输出样例 1】<a href="#输入输出样例-1"><span>#</span></a></h4><p>expr.in expr.out</p><p>1+1*3+4 8</p></section><section><h4>【输入输出样例 2】<a href="#输入输出样例-2"><span>#</span></a></h4><p>expr.in expr.out</p><p>1+1234567890*1 7891</p></section><section><h4>【输入输出样例 3】<a href="#输入输出样例-3"><span>#</span></a></h4><p>expr.in expr.out</p><p>1+1000000003*1 4</p></section><section><h4>【输入输出样例说明】<a href="#输入输出样例说明"><span>#</span></a></h4><p>样例 1 计算的结果为 8，直接输出 8。</p><p>样例 2 计算的结果为 1234567891，输出后 4 位，即 7891。</p><p>样例 3 计算的结果为 1000000004，输出后 4 位，即 4。</p></section><section><h4>【数据范围】<a href="#数据范围-1"><span>#</span></a></h4><p>对于 30%的数据，0≤ 表达式中加法运算符和乘法运算符的总数 ≤100；</p><p>对于 80%的数据，0≤ 表达式中加法运算符和乘法运算符的总数 ≤1000；</p><p>对于 100%的数据，0≤ 表达式中加法运算符和乘法运算符的总数 ≤100000。</p></section></section><section><h3>小朋友的数字（noip2013 普及组第三题）(number.cpp/c/pas)<a href="#小朋友的数字noip2013-普及组第三题numbercppcpas"><span>#</span></a></h3><p>描述 有 n 个小朋友排成一列。每个小朋友手上都有一个数字,这个数字可正可负。规定每个小朋友的特征值等于排在他前面(包括他本人)的小朋友中连续若干个(最少有一个)小朋友手上的数字之和的最大值。 作为这些小朋友的老师,你需要给每个小朋友一个分数,分数是这样规定的&lt;第一个小朋友的分数是他的特征值&gt;,其它小朋友的分数为排在他前面的所有小朋友中(不包括他本人),小朋友分数加上其特征值的最大值。</p><p>请计算所有小朋友分数的最大值,输出时保持最大值的符号,将其绝对值对 p 取模后输出。</p><section><h4>格式 【输入】<a href="#格式-输入"><span>#</span></a></h4><p>输入文件为 number.in。</p><p>第一行包含两个正整数 n、p，之间用一个空格隔开。</p><p>第二行包含 n 个数，每两个整数之间用一个空格隔开，表示每个小朋友手上的数字。</p></section><section><h4>【输出】<a href="#输出-3"><span>#</span></a></h4><p>输出文件名为 number.out。</p><p>输出只有一行，包含一个整数，表示最大分数对 p 取模的结果。</p></section><section><h4>【输入输出样例 1】<a href="#输入输出样例-1-1"><span>#</span></a></h4><p>number.in number.out</p><p>5 997 21</p><p>1 2 3 4 5</p></section><section><h4>【输入输出样例说明】<a href="#输入输出样例说明-1"><span>#</span></a></h4><p>小朋友的特征值分别为 1、3、6、10、15，分数分别为 1、2、5、11、21，最大值 21</p><p>对 997 的模是 21。</p></section><section><h4>【输入输出样例 2】<a href="#输入输出样例-2-1"><span>#</span></a></h4><p>第 2/4 页</p><p>number.in number.out</p><p>5 7 -1 -1 -1 -1 -1 -1</p></section><section><h4>【输入输出样例说明】<a href="#输入输出样例说明-2"><span>#</span></a></h4><p>小朋友的特征值分别为-1、-1、-1、-1、-1，分数分别为-1、-2、-2、-2、-2，最大值 -1 对 7 的模为-1，输出-1。</p></section><section><h4>【数据范围】<a href="#数据范围-2"><span>#</span></a></h4><p>对于 50%的数据，1 ≤ n ≤ 1,000，1 ≤ p ≤ 1,000 所有数字的绝对值不超过 1000；</p><p>99 对于 100%的数据，1 ≤ n ≤ 1,000,000， 1≤ p ≤ 10， 其他数字的绝对值均不超过 10。</p></section></section><section><h3>车站分级（NOIP 普及组 2013 第四题）(level.cpp/c/pas)<a href="#车站分级noip-普及组-2013-第四题levelcppcpas"><span>#</span></a></h3><p>描述 一条单向的铁路线上,依次有编号为 1, 2, …, n 的 n 个火车站。每个火车站都有一个级别,最低为 1 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求&lt;如果这趟车次停靠了火车站&gt; x,则始发站、终点站之间所有级别大于等于火车站 x 的都必须停靠。</p><p>(注意&lt;起始站和终点站自然也算作事先已知需要停靠的站点&gt;)</p><p>例如,下表是 5 趟车次的运行情况。其中,前 4 趟车次均满足要求,而第 5 趟车次由于停靠了 3 号火车站(2 级)却未停靠途经的 6 号火车站(亦为 2 级)而不满足要求。</p><p>现有 m 趟车次的运行情况（全部满足要求） ，试推算这 n 个火车站至少分为几个不同的 级别。</p><section><h4>【输入】<a href="#输入-3"><span>#</span></a></h4><p>输入文件为 level.in。</p><p>第一行包含 2 个正整数 n, m，用一个空格隔开。</p><p>第 i + 1 行（1 ≤ i ≤ m）中，首先是一个正整数 si（2 ≤ si ≤ n），表示第 i 趟车次有 si 个停</p><p>靠站；接下来有 si 个正整数，表示所有停靠站的编号，从小到大排列。每两个数之间用一个 空格隔开。输入保证所有的车次都满足要求。</p></section><section><h4>【输出】<a href="#输出-4"><span>#</span></a></h4><p>输出文件为 level.out。</p><p>输出只有一行，包含一个正整数，即 n 个火车站最少划分的级别数。</p><p>第 3/4 页</p></section><section><h4>【输入输出样例】<a href="#输入输出样例-4"><span>#</span></a></h4></section><section><h4>【数据范围】<a href="#数据范围-3"><span>#</span></a></h4><p>对于 20%的数据，1 ≤ n, m ≤ 10； 对于 50%的数据，1 ≤ n, m ≤ 100； 对于 100%的数据，1 ≤ n, m ≤ 1000。</p><p>第 1 题，太弱了，不用骗，得 100 分。</p><p>第 2 题，数据很大，但是可以直接输入一个数，输出它 mod 10000 的值。得 10 分。</p><p>第 3 题，是一道非常基础的 DP，但对于不知 DP 为何物的蒟蒻来说，就使用暴力算法（即 DFS）。得 20 分。</p><p>第 4 题，我们可以寻找一下数据的规律，你会发现，在所有样例中，M 值即为答案。所以直接输出 M，得 10 分。</p><p>这样下来，一共得 140 分，比一等分数线还高 20 分（弱省）！你的信心一定会得到鼓舞的。这就是骗分的神奇。</p></section></section></section>
<section><h1>第 9 章 结语<a href="#第-9-章-结语"><span>#</span></a></h1><p>骗分是蒟蒻的有力武器，可以在比赛中骗得大量分数。相信大家在这本书中收获了很多，希望本书能帮助你多得一些分。</p><p>但是，最后我还是要说一句：</p><blockquote><p>不骗分，是骗分的最高境界。</p></blockquote></section>]]></content>
    </entry>
    <entry>
      <id>https://azusaaa.com/posts/matrix-quick-power/</id>
      <title type="text">矩阵快速幂</title>
      <published>2020-08-15T06:22:11.000Z</published>
      <updated>2020-08-15T06:22:11.000Z</updated>
      <author><name>dsyfb_437</name></author>
      <link rel="alternate" href="https://azusaaa.com/posts/matrix-quick-power/"/>
      <summary type="text">矩阵快速幂笔记</summary>
      <content type="html"><![CDATA[<section><h3>快速幂<a href="#快速幂"><span>#</span></a></h3><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span><span> </span><span>qp</span><span>(</span><span>int</span><span><span> </span><span>a</span><span>, </span></span><span>int</span><span><span> </span><span>b</span><span>) {</span></span><span>          //quick_power</span></div></div><div><div><div>2</div></div><div><span>  </span><span>int</span><span> </span><span>ans</span><span> </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>for</span><span> (; </span><span>b</span><span>; </span><span>b</span><span> </span><span>&gt;&gt;=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>4</div></div><div><span>    </span><span>if</span><span> (</span><span>b</span><span> </span><span>&amp;</span><span> </span><span>1</span><span>) </span><span>ans</span><span> </span><span>=</span><span> (</span><span>long</span><span> </span><span>long</span><span>)</span><span>ans</span><span> </span><span>*</span><span> </span><span>a</span><span>;</span></div></div><div><div><div>5</div></div><div><span>    </span><span>a</span><span> </span><span>=</span><span> (</span><span>long</span><span> </span><span>long</span><span>)</span><span>a</span><span> </span><span>*</span><span> </span><span>a</span><span>;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>7</div></div><div><span>  </span><span>return</span><span> </span><span>ans</span><span>;</span></div></div><div><div><div>8</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h3>矩阵乘法<a href="#矩阵乘法"><span>#</span></a></h3><p>设<strong>A</strong>为<span><span>m×pm \times p</span><span><span><span></span><span>m</span><span></span><span>×</span><span></span></span><span><span></span><span>p</span></span></span></span>的矩阵，<strong>B</strong>为<span><span>p×np \times n</span><span><span><span></span><span>p</span><span></span><span>×</span><span></span></span><span><span></span><span>n</span></span></span></span>的矩阵，那么称<span><span>m×nm \times n</span><span><span><span></span><span>m</span><span></span><span>×</span><span></span></span><span><span></span><span>n</span></span></span></span>的矩阵<strong>C</strong>为矩阵<strong>A</strong>与<strong>B</strong>的乘积，记作<span><span>C=ABC = AB</span><span><span><span></span><span>C</span><span></span><span>=</span><span></span></span><span><span></span><span>A</span><span>B</span></span></span></span>，其中矩阵<strong>C</strong>中的第<span><span>ii</span><span><span><span></span><span>i</span></span></span></span>行第<span><span>jj</span><span><span><span></span><span>j</span></span></span></span>列元素可以表示为：
<span><span>Ci,j=∑k=1pai,k×bj,kC_{i,j}=\sum\limits_{k=1}^p a_{i,k} \times b_{j,k}</span><span><span><span></span><span><span>C</span><span><span><span><span><span><span></span><span><span><span>i</span><span>,</span><span>j</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span><span><span><span><span></span><span><span><span>k</span><span>=</span><span>1</span></span></span></span><span><span></span><span><span>∑</span></span></span><span><span></span><span><span>p</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span><span>a</span><span><span><span><span><span><span></span><span><span><span>i</span><span>,</span><span>k</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span></span><span>×</span><span></span></span><span><span></span><span><span>b</span><span><span><span><span><span><span></span><span><span><span>j</span><span>,</span><span>k</span></span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span></span></span>
如下所示：
<span><span>[abcdef]×[ghijkl]=[ag+ch+ei,aj+ck+elbg+dh+fi,bj+dk+fl]\begin{bmatrix}a&amp;b\\c&amp;d\\e&amp;f\end{bmatrix} \times \begin{bmatrix}g&amp;h&amp;i\\j&amp;k&amp;l\end{bmatrix} = \begin{bmatrix}ag+ch+ei,&amp;aj+ck+el\\bg+dh+fi,&amp;bj+dk+fl\end{bmatrix}</span><span><span><span></span><span><span><span><span><span><span><span><span></span><span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span><span><span><span><span><span><span><span><span></span><span><span>a</span></span></span><span><span></span><span><span>c</span></span></span><span><span></span><span><span>e</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span></span><span><span><span><span><span><span></span><span><span>b</span></span></span><span><span></span><span><span>d</span></span></span><span><span></span><span><span>f</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span><span><span><span><span><span><span></span><span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span></span><span>×</span><span></span></span><span><span></span><span><span><span>[</span></span><span><span><span><span><span><span><span><span></span><span><span>g</span></span></span><span><span></span><span><span>j</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span></span><span><span><span><span><span><span></span><span><span>h</span></span></span><span><span></span><span><span>k</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span></span><span><span><span><span><span><span></span><span><span>i</span></span></span><span><span></span><span><span>l</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span><span>]</span></span></span><span></span><span>=</span><span></span></span><span><span></span><span><span><span>[</span></span><span><span><span><span><span><span><span><span></span><span><span>a</span><span>g</span><span></span><span>+</span><span></span><span>c</span><span>h</span><span></span><span>+</span><span></span><span>e</span><span>i</span><span>,</span></span></span><span><span></span><span><span>b</span><span>g</span><span></span><span>+</span><span></span><span>d</span><span>h</span><span></span><span>+</span><span></span><span>f</span><span>i</span><span>,</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span><span></span><span></span><span><span><span><span><span><span></span><span><span>aj</span><span></span><span>+</span><span></span><span>c</span><span>k</span><span></span><span>+</span><span></span><span>e</span><span>l</span></span></span><span><span></span><span><span>bj</span><span></span><span>+</span><span></span><span>d</span><span>k</span><span></span><span>+</span><span></span><span>f</span><span>l</span></span></span></span><span>​</span></span><span><span><span></span></span></span></span></span></span></span><span><span>]</span></span></span></span></span></span></p><section><h4>代码<a href="#代码"><span>#</span></a></h4><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>jz</span><span> </span><span>operator</span><span>*(</span><span>const</span><span> </span><span>jz</span><span>&amp;</span><span><span> </span><span>x</span><span>, </span></span><span>const</span><span> </span><span>jz</span><span>&amp;</span><span><span> </span><span>y</span><span>)</span></span><span>   //为了方便快速幂采用运算符重载</span></div></div><div><div><div>2</div></div><div><span>{</span></div></div><div><div><div>3</div></div><div><span>    </span><span>jz</span><span> </span><span>z</span><span>;</span></div></div><div><div><div>4</div></div><div><span>    </span><span>z</span><span>.</span><span>n</span><span> </span><span>=</span><span> </span><span>x</span><span>.</span><span>n</span><span>;</span></div></div><div><div><div>5</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>i</span><span> </span><span>&lt;=</span><span> </span><span>z</span><span>.</span><span>n</span><span>; </span><span>++</span><span>i</span><span>)</span></div></div><div><div><div>6</div></div><div><span>        </span><span>for</span><span> (</span><span>int</span><span> </span><span>j</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>j</span><span> </span><span>&lt;=</span><span> </span><span>z</span><span>.</span><span>n</span><span>; </span><span>++</span><span>j</span><span>)</span></div></div><div><div><div>7</div></div><div><span>            </span><span>z</span><span>.</span><span>e</span><span>[</span><span>i</span><span>][</span><span>j</span><span>] </span><span>=</span><span> </span><span>INF</span><span>;</span></div></div><div><div><div>8</div></div><div><span>    </span><span>for</span><span> (</span><span>int</span><span> </span><span>k</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>k</span><span> </span><span>&lt;=</span><span> </span><span>z</span><span>.</span><span>n</span><span>; </span><span>++</span><span>k</span><span>)</span></div></div><div><div><div>9</div></div><div><span>        </span><span>for</span><span> (</span><span>int</span><span> </span><span>i</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>i</span><span> </span><span>&lt;=</span><span> </span><span>z</span><span>.</span><span>n</span><span>; </span><span>++</span><span>i</span><span>)</span></div></div><div><div><div>10</div></div><div><span>            </span><span>for</span><span> (</span><span>int</span><span> </span><span>j</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>j</span><span> </span><span>&lt;=</span><span> </span><span>z</span><span>.</span><span>n</span><span>; </span><span>++</span><span>j</span><span>)</span></div></div><div><div><div>11</div></div><div><span>                    </span><span>z</span><span>.</span><span>e</span><span>[</span><span>i</span><span>][</span><span>j</span><span>] </span><span>+=</span><span> </span><span>x</span><span>.</span><span>e</span><span>[</span><span>i</span><span>][</span><span>k</span><span>] </span><span>+</span><span> </span><span>y</span><span>.</span><span>e</span><span>[</span><span>k</span><span>][</span><span>j</span><span>];</span></div></div><div><div><div>12</div></div><div><span>    </span><span>return</span><span> </span><span>z</span><span>;</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>所以，矩阵快速幂即为作若干次矩阵乘法，代码如下：</p></section></section>
<section><h3>完整代码<a href="#完整代码"><span>#</span></a></h3><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#define</span><span> </span><span>N</span><span> </span><span>1010</span></div></div><div><div><div>2</div></div><div><span>struct</span><span> </span><span>jz</span><span>          </span><span>//矩阵</span></div></div><div><div><div>3</div></div><div><span>{</span></div></div><div><div><div>4</div></div><div><span>    </span><span>short</span><span> </span><span>n</span><span>;</span></div></div><div><div><div>5</div></div><div><span>    </span><span>long</span><span> </span><span>long</span><span> </span><span>e</span><span>[</span><span>N</span><span>][</span><span>N</span><span>];</span></div></div><div><div><div>6</div></div><div><span>} </span><span>a</span><span>;</span></div></div><div><div><div>7</div></div><div><span>jz</span><span> </span><span>operator</span><span>*(</span><span>const</span><span> </span><span>jz</span><span>&amp;</span><span><span> </span><span>x</span><span>, </span></span><span>const</span><span> </span><span>jz</span><span>&amp;</span><span><span> </span><span>y</span><span>)</span></span></div></div><div><div><div>8</div></div><div><span>{</span></div></div><div><div><div>9</div></div><div><span>    </span><span>short</span><span> </span><span>i</span><span>, </span><span>j</span><span>, </span><span>k</span><span>;</span></div></div><div><div><div>10</div></div><div><span>    </span><span>jz</span><span> </span><span>z</span><span>;</span></div></div><div><div><div>11</div></div><div><span>    </span><span>z</span><span>.</span><span>n</span><span> </span><span>=</span><span> </span><span>x</span><span>.</span><span>n</span><span>;</span></div></div><div><div><div>12</div></div><div><span>    </span><span>for</span><span> (</span><span>i</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>i</span><span> </span><span>&lt;=</span><span> </span><span>z</span><span>.</span><span>n</span><span>; </span><span>++</span><span>i</span><span>)</span></div></div><div><div><div>13</div></div><div><span>        </span><span>for</span><span> (</span><span>j</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>j</span><span> </span><span>&lt;=</span><span> </span><span>z</span><span>.</span><span>n</span><span>; </span><span>++</span><span>j</span><span>)</span></div></div><div><div><div>14</div></div><div><span>            </span><span>z</span><span>.</span><span>e</span><span>[</span><span>i</span><span>][</span><span>j</span><span>] </span><span>=</span><span> </span><span>INF</span><span>;</span></div></div><div><div><div>15</div></div><div><span>    </span><span>for</span><span> (</span><span>k</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>k</span><span> </span><span>&lt;=</span><span> </span><span>z</span><span>.</span><span>n</span><span>; </span><span>++</span><span>k</span><span>)</span></div></div><div><div><div>16</div></div><div><span>        </span><span>for</span><span> (</span><span>i</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>i</span><span> </span><span>&lt;=</span><span> </span><span>z</span><span>.</span><span>n</span><span>; </span><span>++</span><span>i</span><span>)</span></div></div><div><div><div>17</div></div><div><span>            </span><span>for</span><span> (</span><span>j</span><span> </span><span>=</span><span> </span><span>1</span><span>; </span><span>j</span><span> </span><span>&lt;=</span><span> </span><span>z</span><span>.</span><span>n</span><span>; </span><span>++</span><span>j</span><span>)</span></div></div><div><div><div>18</div></div><div><span>                </span><span>if</span><span> (</span><span>x</span><span>.</span><span>e</span><span>[</span><span>i</span><span>][</span><span>k</span><span>] </span><span>+</span><span> </span><span>y</span><span>.</span><span>e</span><span>[</span><span>k</span><span>][</span><span>j</span><span>] </span><span>&lt;</span><span> </span><span>z</span><span>.</span><span>e</span><span>[</span><span>i</span><span>][</span><span>j</span><span>])</span></div></div><div><div><div>19</div></div><div><span>                    </span><span>z</span><span>.</span><span>e</span><span>[</span><span>i</span><span>][</span><span>j</span><span>] </span><span>=</span><span> </span><span>x</span><span>.</span><span>e</span><span>[</span><span>i</span><span>][</span><span>k</span><span>] </span><span>+</span><span> </span><span>y</span><span>.</span><span>e</span><span>[</span><span>k</span><span>][</span><span>j</span><span>];</span></div></div><div><div><div>20</div></div><div><span>    </span><span>return</span><span> </span><span>z</span><span>;</span></div></div><div><div><div>21</div></div><div><span>}</span></div></div><div><div><div>22</div></div><div><span>jz</span><span> </span><span>qp</span><span>(</span><span>jz</span><span><span> </span><span>a</span><span>, </span></span><span>int</span><span><span> </span><span>b</span><span>) {</span></span><span>          //quick_power</span></div></div><div><div><div>23</div></div><div><span>  </span><span>jz</span><span> </span><span>ans</span><span> </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>24</div></div><div><span>  </span><span>for</span><span> (; </span><span>b</span><span>; </span><span>b</span><span> </span><span>&gt;&gt;=</span><span> </span><span>1</span><span>) {</span></div></div><div><div><div>25</div></div><div><span>    </span><span>if</span><span> (</span><span>b</span><span> </span><span>&amp;</span><span> </span><span>1</span><span>) </span><span>ans</span><span> </span><span>=</span><span> (</span><span>long</span><span> </span><span>long</span><span>)</span><span>ans</span><span> </span><span>*</span><span> </span><span>a</span><span>;</span></div></div><div><div><div>26</div></div><div><span>    </span><span>a</span><span> </span><span>=</span><span> (</span><span>long</span><span> </span><span>long</span><span>)</span><span>a</span><span> </span><span>*</span><span> </span><span>a</span><span>;</span></div></div><div><div><div>27</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>28</div></div><div><span>  </span><span>return</span><span> </span><span>ans</span><span>;</span></div></div><div><div><div>29</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>]]></content>
    </entry>
    <entry>
      <id>https://azusaaa.com/posts/cpp-operator-overload/</id>
      <title type="text">C++ 运算符重载</title>
      <published>2020-08-13T14:17:36.000Z</published>
      <updated>2020-08-13T14:17:36.000Z</updated>
      <author><name>dsyfb_437</name></author>
      <link rel="alternate" href="https://azusaaa.com/posts/cpp-operator-overload/"/>
      <summary type="text">运算符重载笔记</summary>
      <content type="html"><![CDATA[<section><h1>简单的方式—使用 operator<a href="#简单的方式使用-operator"><span>#</span></a></h1><p><strong>格式:</strong></p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>返回类型 operator运算符(参数)</span></div></div><div><div><div>2</div></div><div><span>{</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>//do something</span></div></div><div><div><div>4</div></div><div><span>    </span><span>return</span><span> 返回值;</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>//do something</span></div></div><div><div><div>6</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p><strong>举个栗子:</strong> 重载”+“计算时间</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>#include</span><span> </span><span>&lt;iostream&gt;</span></div></div><div><div><div>2</div></div><div><span>using</span><span> </span><span>namespace</span><span><span> </span><span>std</span><span>;</span></span></div></div><div><div><div>3</div></div><div>
</div></div><div><div><div>4</div></div><div><span>struct</span><span> </span><span>Time</span></div></div><div><div><div>5</div></div><div><span>{</span></div></div><div><div><div>6</div></div><div><span>  </span><span>int</span><span> </span><span>minute</span><span>, </span><span>hour</span><span>;</span></div></div><div><div><div>7</div></div><div><span>} </span><span>x</span><span>, </span><span>y</span><span>;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>Time</span><span> </span><span>operator</span><span>+(</span><span>Time</span><span><span> </span><span>a</span><span>, </span></span><span>Time</span><span><span> </span><span>b</span><span>)</span></span></div></div><div><div><div>10</div></div><div><span>{</span></div></div><div><div><div>11</div></div><div><span>  </span><span>Time</span><span> </span><span>c</span><span>;</span></div></div><div><div><div>12</div></div><div><span>  </span><span>c</span><span>.</span><span>minute</span><span> </span><span>=</span><span> </span><span>a</span><span>.</span><span>minute</span><span> </span><span>+</span><span> </span><span>b</span><span>.</span><span>minute</span><span>;</span></div></div><div><div><div>13</div></div><div><span>  </span><span>c</span><span>.</span><span>hour</span><span> </span><span>=</span><span> </span><span>a</span><span>.</span><span>hour</span><span> </span><span>+</span><span> </span><span>b</span><span>.</span><span>hour</span><span>;</span></div></div><div><div><div>14</div></div><div><span>  </span><span>while</span><span> (</span><span>c</span><span>.</span><span>minute</span><span> </span><span>&gt;=</span><span> </span><span>60</span><span>)</span></div></div><div><div><div>15</div></div><div><span><span>  </span></span><span>{</span></div></div><div><div><div>16</div></div><div><span>    </span><span>c</span><span>.</span><span>hour</span><span> </span><span>+=</span><span> </span><span>c</span><span>.</span><span>minute</span><span> </span><span>/</span><span> </span><span>60</span><span>;</span></div></div><div><div><div>17</div></div><div><span>    </span><span>c</span><span>.</span><span>minute</span><span> </span><span>%=</span><span> </span><span>60</span><span>;</span></div></div><div><div><div>18</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>19</div></div><div><span>  </span><span>return</span><span> </span><span>c</span><span>;</span></div></div><div><div><div>20</div></div><div><span>}</span></div></div><div><div><div>21</div></div><div>
</div></div><div><div><div>22</div></div><div><span>int</span><span> </span><span>main</span><span>()</span></div></div><div><div><div>23</div></div><div><span>{</span></div></div><div><div><div>24</div></div><div><span>  </span><span>cin</span><span> </span><span>&gt;&gt;</span><span> </span><span>x</span><span>.</span><span>hour</span><span> </span><span>&gt;&gt;</span><span> </span><span>x</span><span>.</span><span>minute</span><span>;</span></div></div><div><div><div>25</div></div><div><span>  </span><span>cin</span><span> </span><span>&gt;&gt;</span><span> </span><span>y</span><span>.</span><span>hour</span><span> </span><span>&gt;&gt;</span><span> </span><span>y</span><span>.</span><span>minute</span><span>;</span></div></div><div><div><div>26</div></div><div><span>  </span><span>Time</span><span> </span><span>z</span><span>;</span></div></div><div><div><div>27</div></div><div><span>  </span><span>z</span><span> </span><span>=</span><span> </span><span>x</span><span> </span><span>+</span><span> </span><span>y</span><span>;</span></div></div><div><div><div>28</div></div><div><span>  </span><span>cout</span><span> </span><span>&lt;&lt;</span><span> </span><span>z</span><span>.</span><span>hour</span><span> </span><span>&lt;&lt;</span><span> </span><span>" "</span><span> </span><span>&lt;&lt;</span><span> </span><span>z</span><span>.</span><span>minute</span><span>;</span></div></div><div><div><div>29</div></div><div><span>  </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>30</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>展开</span><span>收起</span></div></div></section>]]></content>
    </entry>
    <entry>
      <id>https://azusaaa.com/posts/backpack-nine-lectures/</id>
      <title type="text">[转]背包九讲</title>
      <published>2020-08-05T03:17:23.000Z</published>
      <updated>2020-08-05T03:17:23.000Z</updated>
      <author><name>dsyfb_437</name></author>
      <link rel="alternate" href="https://azusaaa.com/posts/backpack-nine-lectures/"/>
      <summary type="text">背包问题九讲</summary>
      <content type="html"><![CDATA[<blockquote><p>原文出处：<a href="http://love-oriented.com/pack" target="_blank">http://love-oriented.com/pack</a></p></blockquote>
<section><h2>前言<a href="#前言"><span>#</span></a></h2><p>本篇文章是我(dd_engi)正在进行中的一个雄心勃勃的写作计划的一部分，这个计划的内容是写作一份较为完善的 NOIP 难度的动态规划总结，名为《解动态规划题的基本思考方式》。现在你看到的是这个写作计划最先发布的一部分。</p><p>背包问题是一个经典的动态规划模型。它既简单形象容易理解，又在某种程度上能够揭示动态规划的本质，故不少教材都把它作为动态规划部分的第一道例题，我也将它放在我的写作计划的第一部分。</p><p>读本文最重要的是思考。因为我的语言和写作方式向来不以易于理解为长，思路也偶有跳跃的地方，后面更有需要大量思考才能理解的比较抽象的内容。更重要的是：不大量思考，绝对不可能学好动态规划这一信息学奥赛中最精致的部分。</p><p>你现在看到的是本文的 v1.1 版，发布于 2007 年 11 月 15 日。我会长期维护这份文本，把大家的意见和建议融入其中，也会不断加入我在 OI 学习以及将来可能的 ACM-ICPC 的征程中得到的新的心得。但目前本文还没有一个固定的发布页面，想了解本文是否有更新版本发布，可以在<a href="http://oibh.org/bbs/" target="_blank">OIBH</a>论坛中以“背包问题九讲”为关键字搜索贴子，每次比较重大的版本更新都会在这个论坛里发贴公布。也可以用“背包问题九讲”为关键字在搜索引擎中搜索以得到最新版本。</p><hr /></section>
<section><h2>第一讲 01 背包问题<a href="#第一讲-01-背包问题"><span>#</span></a></h2><section><h3>题目<a href="#题目"><span>#</span></a></h3><p>有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 c[i]，价值是 w[i] 。求解将哪些物品装入背包可使价值总和最大。</p></section><section><h3>基本思路<a href="#基本思路"><span>#</span></a></h3><p>这是最基础的背包问题，特点是：每种物品仅有一件，可以选择放或不放。</p><p>用子问题定义状态：即 f[i][v] 表示前 i 件物品恰放入一个容量为 V 的背包可以获得的最大价值。则其状态转移方程便是：</p><p><code>f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}</code></p><p>这个方程非常重要，基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下：“将前 i 件物品放入容量为 V 的背包中”这个子问题，若只考虑第 i 件物品的策略（放或不放），那么就可以转化为一个只牵扯前 i-1 件物品的问题。如果不放第 i 件物品，那么问题就转化为“前 i-1 件物品放入容量为 V 的背包中”，价值为 f[i-1][v]；如果放第 i 件物品，那么问题就转化为“前 i-1 件物品放入剩下的容量为 v-c[i]的背包中”，此时能获得的最大价值就是 f[i-1][v-c[i]]再加上通过放入第 i 件物品获得的价值 w[i]。</p></section><section><h3>优化空间复杂度<a href="#优化空间复杂度"><span>#</span></a></h3><p>以上方法的时间和空间复杂度均为 O(VN)，其中时间复杂度应该已经不能再优化了，但空间复杂度却可以优化到 O(N) 。</p><p>先考虑上面讲的基本思路如何实现，肯定是有一个主循环 i=1..N，每次算出来二维数组 f[i][0..V] 的所有值。那么，如果只用一个数组 f[0..V] ，能不能保证第 i 次循环结束后 f[v]中表示的就是我们定义的状态 f[i][v] 呢？f[i][v]是由 f[i-1][v] 和 f[i-1]v-c[i]] 两个子问题递推而来，能否保证在推 f[i][v] 时（也即在第 i 次主循环中推 f[v] 时）能够得到 f[i-1][v] 和 f[i-1][v-c[i]] 的值呢？事实上，这要求在每次主循环中我们以 v=V..0 的顺序推 f[v]，这样才能保证推 f[v] 时 f[v-c[i]]保存的是状态 f[i-1]v-c[i]] 的值。伪代码如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for i=1..N</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>for v=V..0</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>f[v]=max{f[v],f[v-c[i]]+w[i]};</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其中的 <code>f[v]=max{f[v],f[v-c[i]]}</code> 一句恰就相当于我们的转移方程 <code>f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]}</code> ，因为现在的 f[v-c[i]] 就相当于原来的 f[i-1][v-c[i]] 。如果将 v 的循环顺序从上面的逆序改成顺序的话，那么则成了 f[i][v] 由 f[i][v-c[i]] 推知，与本题意不符，但它却是另一个重要的背包问题 P02 最简捷的解决方案，故学习只用一维数组解 01 背包问题是十分必要的。</p><p>事实上，使用一维数组解 01 背包的程序在后面会被多次用到，所以这里抽象出一个处理一件 01 背包中的物品过程，以后的代码中直接调用不加说明。</p><p>过程 ZeroOnePack，表示处理一件 01 背包中的物品，两个参数 cost、weight 分别表明这件物品的费用和价值。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>procedure ZeroOnePack(cost,weight)</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>for v=V..cost</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>f[v]=max{f[v],f[v-cost]+weight}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意这个过程里的处理与前面给出的伪代码有所不同。前面的示例程序写成 v=V..0 是为了在程序中体现每个状态都按照方程求解了，避免不必要的思维复杂度。而这里既然已经抽象成看作黑箱的过程了，就可以加入优化。费用为 cost 的物品不会影响状态 f[0..cost-1] ，这是显然的。</p><p>有了这个过程以后，01 背包问题的伪代码就可以这样写：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for i=1..N</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>ZeroOnePack(c[i],w[i]);</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>初始化的细节问题<a href="#初始化的细节问题"><span>#</span></a></h3><p>我们看到的求最优解的背包问题题目中，事实上有两种不太相同的问法。有的题目要求“恰好装满背包”时的最优解，有的题目则并没有要求必须把背包装满。一种区别这两种问法的实现方法是在初始化的时候有所不同。</p><p>如果是第一种问法，要求恰好装满背包，那么在初始化时除了 f[0] 为 0 其它 f[1..V] 均设为 <span><span>−∞-\infty</span><span><span><span></span><span>−</span><span>∞</span></span></span></span> ，这样就可以保证最终得到的 f[N] 是一种恰好装满背包的最优解。</p><p>如果并没有要求必须把背包装满，而是只希望价格尽量大，初始化时应该将 f[0..V] 全部设为 0 。</p><p>为什么呢？可以这样理解：初始化的 f 数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满，那么此时只有容量为 0 的背包可能被价值为 0 的 nothing “恰好装满”，其它容量的背包均没有合法的解，属于未定义的状态，它们的值就都应该是 <span><span>−∞-\infty</span><span><span><span></span><span>−</span><span>∞</span></span></span></span> 了。如果背包并非必须被装满，那么任何容量的背包都有一个合法解“什么都不装”，这个解的价值为 0，所以初始时状态的值也就全部为 0 了。</p><p>这个小技巧完全可以推广到其它类型的背包问题，后面也就不再对进行状态转移之前的初始化进行讲解。</p></section><section><h3>一个常数优化<a href="#一个常数优化"><span>#</span></a></h3><p>前面的伪代码中有 for v=V..1，可以将这个循环的下限进行改进。</p><p>由于只需要最后 f[v] 的值，倒推前一个物品，其实只要知道 f[v-w[n]] 即可。以此类推，对以第 j 个背包，其实只需要知道到 f[v-sum{w[j..n]}] 即可，即代码中的</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for i=1..N</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>for v=V..0</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>可以改成</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for i=1..n</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>bound=max{V-sum{w[i..n]},c[i]}</span></div></div><div><div><div>3</div></div><div><span><span>    </span></span><span>for v=V..bound</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>这对于 V 比较大时是有用的。</p></section><section><h3>小结<a href="#小结"><span>#</span></a></h3><p>01 背包问题是最基本的背包问题，它包含了背包问题中设计状态、方程的最基本思想，另外，别的类型的背包问题往往也可以转换成 01 背包问题求解。故一定要仔细体会上面基本思路的得出方法，状态转移方程的意义，以及最后怎样优化的空间复杂度。</p><hr /></section></section>
<section><h2>第二讲 完全背包问题<a href="#第二讲-完全背包问题"><span>#</span></a></h2><section><h3>题目<a href="#题目-1"><span>#</span></a></h3><p>有 N 种物品和一个容量为 V 的背包，每种物品都有无限件可用。第 i 种物品的费用是 c[i]，价值是 w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量，且价值总和最大。</p></section><section><h3>基本思路<a href="#基本思路-1"><span>#</span></a></h3><p>这个问题非常类似于 01 背包问题，所不同的是每种物品有无限件。也就是从每种物品的角度考虑，与它相关的策略已并非取或不取两种，而是有取 0 件、取 1 件、取 2 件……等很多种。如果仍然按照解 01 背包时的思路，令 f[i][v] 表示前 i 种物品恰放入一个容量为 v 的背包的最大权值。仍然可以按照每种物品不同的策略写出状态转移方程，像这样：</p><p><code>f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0&lt;=k*c[i]&lt;=v}</code></p><p>这跟 01 背包问题一样有 O(VN)个状态需要求解，但求解每个状态的时间已经不是常数了，求解状态 f[i][v] 的时间是 O(v/c[i]) ，总的复杂度可以认为是 O(V*Σ(V/c[i])) ，是比较大的。</p><p>将 01 背包问题的基本思路加以改进，得到了这样一个清晰的方法。这说明 01 背包问题的方程的确是很重要，可以推及其它类型的背包问题。但我们还是试图改进这个复杂度。</p></section><section><h3>一个简单有效的优化<a href="#一个简单有效的优化"><span>#</span></a></h3><p>完全背包问题有一个很简单有效的优化，是这样的：若两件物品 i、j 满足 c[i]=w[j] ，则将物品 j 去掉，不用考虑。这个优化的正确性显然：任何情况下都可将价值小费用高得 j 换成物美价廉的 i，得到至少不会更差的方案。对于随机生成的数据，这个方法往往会大大减少物品的件数，从而加快速度。然而这个并不能改善最坏情况的复杂度，因为有可能特别设计的数据可以一件物品也去不掉。</p><p>这个优化可以简单的 O(N^2) 地实现，一般都可以承受。另外，针对背包问题而言，比较不错的一种方法是：首先将费用大于 V 的物品去掉，然后使用类似计数排序的做法，计算出费用相同的物品中价值最高的是哪个，可以 O(V+N) 地完成这个优化。这个不太重要的过程就不给出伪代码了，希望你能独立思考写出伪代码或程序。</p></section><section><h3>转化为 01 背包问题求解<a href="#转化为-01-背包问题求解"><span>#</span></a></h3><p>既然 01 背包问题是最基本的背包问题，那么我们可以考虑把完全背包问题转化为 01 背包问题来解。最简单的想法是，考虑到第 i 种物品最多选 V/c[i] 件，于是可以把第 i 种物品转化为 V/c[i] 件费用及价值均不变的物品，然后求解这个 01 背包问题。这样完全没有改进基本思路的时间复杂度，但这毕竟给了我们将完全背包问题转化为 01 背包问题的思路：将一种物品拆成多件物品。</p><p>更高效的转化方法是：把第 i 种物品拆成费用为 c[i]*2^k 、价值为 w[i]*2^k 的若干件物品，其中 k 满足 <code>c[i]*2^k&lt;=V</code> 。这是二进制的思想，因为不管最优策略选几件第 i 种物品，总可以表示成若干个 2^k 件物品的和。这样把每种物品拆成 O(log V/c[i]) 件物品，是一个很大的改进。</p><p>但我们有更优的 O(VN) 的算法。</p></section><section><h3>O(VN)的算法<a href="#ovn的算法"><span>#</span></a></h3><p>这个算法使用一维数组，先看伪代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for i=1..N</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>for v=0..V</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>f[v]=max{f[v],f[v-cost]+weight}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>你会发现，这个伪代码与 P01 的伪代码只有 v 的循环次序不同而已。为什么这样一改就可行呢？首先想想为什么 P01 中要按照 v=V..0 的逆序来循环。这是因为要保证第 i 次循环中的状态 f[i][v] 是由状态 f[i-1][v-c[i]] 递推而来。换句话说，这正是为了保证每件物品只选一次，保证在考虑“选入第 i 件物品”这件策略时，依据的是一个绝无已经选入第 i 件物品的子结果 f[i-1][v-c[i]] 。而现在完全背包的特点恰是每种物品可选无限件，所以在考虑“加选一件第 i 种物品”这种策略时，却正需要一个可能已选入第 i 种物品的子结果 f[i][v-c[i]] ，所以就可以并且必须采用 v=0..V 的顺序循环。这就是这个简单的程序为何成立的道理。</p><p>值得一提的是，上面的伪代码中两层 for 循环的次序可以颠倒。这个结论有可能会带来算法时间常数上的优化。</p><p>这个算法也可以以另外的思路得出。例如，将基本思路中求解 f[i][v-c[i]] 的状态转移方程显式地写出来，代入原方程中，会发现该方程可以等价地变形成这种形式：</p><p><code>f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]}</code></p><p>将这个方程用一维数组实现，便得到了上面的伪代码。</p><p>最后抽象出处理一件完全背包类物品的过程伪代码：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>procedure CompletePack(cost,weight)</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>for v=cost..V</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>f[v]=max{f[v],f[v-c[i]]+w[i]}</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>总结<a href="#总结"><span>#</span></a></h3><p>完全背包问题也是一个相当基础的背包问题，它有两个状态转移方程，分别在“基本思路”以及“ O(VN) 的算法“的小节中给出。希望你能够对这两个状态转移方程都仔细地体会，不仅记住，也要弄明白它们是怎么得出来的，最好能够自己想一种得到这些方程的方法。事实上，对每一道动态规划题目都思考其方程的意义以及如何得来，是加深对动态规划的理解、提高动态规划功力的好方法。</p><hr /></section></section>
<section><h2>第三讲 多重背包问题<a href="#第三讲-多重背包问题"><span>#</span></a></h2><section><h3>题目<a href="#题目-2"><span>#</span></a></h3><p>有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 n[i] 件可用，每件费用是 c[i] ，价值是 w[i] 。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量，且价值总和最大。</p></section><section><h3>基本算法<a href="#基本算法"><span>#</span></a></h3><p>这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可，因为对于第 i 种物品有 n[i]+1 种策略：取 0 件，取 1 件……取 n[i] 件。令 f[i][v] 表示前 i 种物品恰放入一个容量为 v 的背包的最大权值，则有状态转移方程：</p><p><code>f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0&lt;=k&lt;=n[i]}</code></p><p>复杂度是 O(V*Σn[i]) 。</p></section><section><h3>转化为 01 背包问题<a href="#转化为-01-背包问题"><span>#</span></a></h3><p>另一种好想好写的基本方法是转化为 01 背包求解：把第 i 种物品换成 n[i] 件 01 背包中的物品，则得到了物品数为 Σn[i] 的 01 背包问题，直接求解，复杂度仍然是 O(V*Σn[i]) 。</p><p>但是我们期望将它转化为 01 背包问题之后能够像完全背包一样降低复杂度。仍然考虑二进制的思想，我们考虑把第 i 种物品换成若干件物品，使得原问题中第 i 种物品可取的每种策略——取 0..n[i] 件——均能等价于取若干件代换以后的物品。另外，取超过 n[i] 件的策略必不能出现。</p><p>方法是：将第 i 种物品分成若干件物品，其中每件物品有一个系数，这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为 1,2,4,…,2^(k-1),n[i]-2^k+1 ，且 k 是满足 n[i]-2^k+1&gt;0 的最大整数。例如，如果 n[i] 为 13，就将这种物品分成系数分别为 1,2,4,6 的四件物品。</p><p>分成的这几件物品的系数和为 n[i] ，表明不可能取多于 n[i]件的第 i 种物品。另外这种方法也能保证对于 0..n[i] 间的每一个整数，均可以用若干个系数的和表示，这个证明可以分 0..2^k-1 和 2^k..n[i] 两段来分别讨论得出，并不难，希望你自己思考尝试一下。</p><p>这样就将第 i 种物品分成了 O(log n[i]) 种物品，将原问题转化为了复杂度为 O(V*Σlog n[i]) 的 01 背包问题，是很大的改进。</p><p>下面给出 O(log amount) 时间处理一件多重背包中物品的过程，其中 amount 表示物品的数量：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>procedure MultiplePack(cost,weight,amount)</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>if cost*amount&gt;=V</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>CompletePack(cost,weight)</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>return</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>integer k=1</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>while k&lt;amount</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>ZeroOnePack(k*cost,k*weight)</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>amount=amount-k</span></div></div><div><div><div>9</div></div><div><span><span>        </span></span><span>k=k*2</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>ZeroOnePack(amount*cost,amount*weight)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>希望你仔细体会这个伪代码，如果不太理解的话，不妨翻译成程序代码以后，单步执行几次，或者头脑加纸笔模拟一下，也许就会慢慢理解了。</p></section><section><h3>O(VN)的算法<a href="#ovn的算法-1"><span>#</span></a></h3><p>多重背包问题同样有 O(VN) 的算法。这个算法基于基本算法的状态转移方程，但应用单调队列的方法使每个状态的值可以以均摊 O(1) 的时间求解。由于用单调队列优化的 DP 已超出了 NOIP 的范围，故本文不再展开讲解。我最初了解到这个方法是在楼天成的“男人八题”幻灯片上。</p></section><section><h3>小结<a href="#小结-1"><span>#</span></a></h3><p>这里我们看到了将一个算法的复杂度由 O(VΣn[i]) 改进到 O(VΣlog n[i]) 的过程，还知道了存在应用超出 NOIP 范围的知识的 O(VN) 算法。希望你特别注意“拆分物品”的思想和方法，自己证明一下它的正确性，并将完整的程序代码写出来。</p><hr /></section></section>
<section><h2>第四讲 混合三种背包问题<a href="#第四讲-混合三种背包问题"><span>#</span></a></h2><section><h3>问题<a href="#问题"><span>#</span></a></h3><p>如果将 P01、P02、P03 混合起来。也就是说，有的物品只可以取一次（01 背包），有的物品可以取无限次（完全背包），有的物品可以取的次数有一个上限（多重背包）。应该怎么求解呢？</p></section><section><h3>01 背包与完全背包的混合<a href="#01-背包与完全背包的混合"><span>#</span></a></h3><p>考虑到在 P01 和 P02 中给出的伪代码只有一处不同，故如果只有两类物品：一类物品只能取一次，另一类物品可以取无限次，那么只需在对每个物品应用转移方程时，根据物品的类别选用顺序或逆序的循环即可，复杂度是 O(VN) 。伪代码如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for i=1..N</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>if 第i件物品属于01背包</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>for v=V..0</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>f[v]=max{f[v],f[v-c[i]]+w[i]};</span></div></div><div><div><div>5</div></div><div><span><span>    </span></span><span>else if 第i件物品属于完全背包</span></div></div><div><div><div>6</div></div><div><span><span>        </span></span><span>for v=0..V</span></div></div><div><div><div>7</div></div><div><span><span>            </span></span><span>f[v]=max{f[v],f[v-c[i]]+w[i]};</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section><section><h3>再加上多重背包<a href="#再加上多重背包"><span>#</span></a></h3><p>如果再加上有的物品最多可以取有限次，那么原则上也可以给出 O(VN) 的解法：遇到多重背包类型的物品用单调队列解即可。但如果不考虑超过 NOIP 范围的算法的话，用 P03 中将每个这类物品分成 O(log n[i]) 个 01 背包的物品的方法也已经很优了。</p><p>当然，更清晰的写法是调用我们前面给出的三个相关过程。</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for i=1..N</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>if 第i件物品属于01背包</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>ZeroOnePack(c[i],w[i])</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>else if 第i件物品属于完全背包</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>CompletePack(c[i],w[i])</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>else if 第i件物品属于多重背包</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>MultiplePack(c[i],w[i],n[i])</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>在最初写出这三个过程的时候，可能完全没有想到它们会在这里混合应用。我想这体现了编程中抽象的威力。如果你一直就是以这种“抽象出过程”的方式写每一类背包问题的，也非常清楚它们的实现中细微的不同，那么在遇到混合三种背包问题的题目时，一定能很快想到上面简洁的解法，对吗？</p></section><section><h3>小结<a href="#小结-2"><span>#</span></a></h3><p>有人说，困难的题目都是由简单的题目叠加而来的。这句话是否公理暂且存之不论，但它在本讲中已经得到了充分的体现。本来 01 背包、完全背包、多重背包都不是什么难题，但将它们简单地组合起来以后就得到了这样一道一定能吓倒不少人的题目。但只要基础扎实，领会三种基本背包问题的思想，就可以做到把困难的题目拆分成简单的题目来解决。</p><hr /></section></section>
<section><h2>第五讲 二维费用的背包问题<a href="#第五讲-二维费用的背包问题"><span>#</span></a></h2><section><h3>问题<a href="#问题-1"><span>#</span></a></h3><p>二维费用的背包问题是指：对于每件物品，具有两种不同的费用；选择这件物品必须同时付出这两种代价；对于每种代价都有一个可付出的最大值（背包容量）。问怎样选择物品可以得到最大的价值。设这两种代价分别为代价 1 和代价 2，第 i 件物品所需的两种代价分别为 a[i]和 b[i] 。两种代价可付出的最大值（两种背包容量）分别为 V 和 U。物品的价值为 w[i] 。</p></section><section><h3>算法<a href="#算法"><span>#</span></a></h3><p>费用加了一维，只需状态也加一维即可。设 f[i][v][u] 表示前 i 件物品付出两种代价分别为 v 和 u 时可获得的最大价值。状态转移方程就是：</p><p><code>f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}</code></p><p>如前述方法，可以只使用二维的数组：当每件物品只可以取一次时变量 v 和 u 采用逆序的循环，当物品有如完全背包问题时采用顺序的循环。当物品有如多重背包问题时拆分物品。这里就不再给出伪代码了，相信有了前面的基础，你能够自己实现出这个问题的程序。</p></section><section><h3>物品总个数的限制<a href="#物品总个数的限制"><span>#</span></a></h3><p>有时，“二维费用”的条件是以这样一种隐含的方式给出的：最多只能取 M 件物品。这事实上相当于每件物品多了一种“件数”的费用，每个物品的件数费用均为 1，可以付出的最大件数费用为 M。换句话说，设 f[v][m] 表示付出费用 v、最多选 m 件时可得到的最大价值，则根据物品的类型（01、完全、多重）用不同的方法循环更新，最后在 f[0..V][0..M] 范围内寻找答案。</p></section><section><h3>复数域上的背包问题<a href="#复数域上的背包问题"><span>#</span></a></h3><p>另一种看待二维背包问题的思路是：将它看待成复数域上的背包问题。也就是说，背包的容量以及每件物品的费用都是一个复数。而常见的一维背包问题则是实数域上的背包问题。（注意：上面的话其实不严谨，因为事实上我们处理的都只是整数而已。）所以说，一维背包的种种思想方法，往往可以应用于二位背包问题的求解中，因为只是数域扩大了而已。</p><p>作为这种思想的练习，你可以尝试将 P11 中提到的“子集和问题”扩展到复数域（即二维），并试图用同样的复杂度解决。</p></section><section><h3>小结<a href="#小结-3"><span>#</span></a></h3><p>当发现由熟悉的动态规划题目变形得来的题目时，在原来的状态中加一纬以满足新的限制是一种比较通用的方法。希望你能从本讲中初步体会到这种方法。</p><hr /></section></section>
<section><h2>第六讲 分组的背包问题<a href="#第六讲-分组的背包问题"><span>#</span></a></h2><section><h3>问题<a href="#问题-2"><span>#</span></a></h3><p>有 N 件物品和一个容量为 V 的背包。第 i 件物品的费用是 c[i] ，价值是 w[i] 。这些物品被划分为若干组，每组中的物品互相冲突，最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量，且价值总和最大。</p></section><section><h3>算法<a href="#算法-1"><span>#</span></a></h3><p>这个问题变成了每组物品有若干种策略：是选择本组的某一件，还是一件都不选。也就是说设 f[k][v] 表示前 k 组物品花费费用 v 能取得的最大权值，则有：</p><p><code>f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于组k}</code></p><p>使用一维数组的伪代码如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for 所有的组k</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>for v=V..0</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>for 所有的i属于组k</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>f[v]=max{f[v],f[v-c[i]]+w[i]}</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>注意这里的三层循环的顺序，甚至在本文的第一个 beta 版中我自己都写错了。“for v=V..0”这一层循环必须在“for 所有的 i 属于组 k”之外。这样才能保证每一组内的物品最多只有一个会被添加到背包中。</p><p>另外，显然可以对每组内的物品应用 P02 中“一个简单有效的优化”。</p></section><section><h3>小结<a href="#小结-4"><span>#</span></a></h3><p>分组的背包问题将彼此互斥的若干物品称为一个组，这建立了一个很好的模型。不少背包问题的变形都可以转化为分组的背包问题（例如 P07），由分组的背包问题进一步可定义“泛化物品”的概念，十分有利于解题。</p><hr /></section></section>
<section><h2>第七讲 有依赖的背包问题<a href="#第七讲-有依赖的背包问题"><span>#</span></a></h2><section><h3>简化的问题<a href="#简化的问题"><span>#</span></a></h3><p>这种背包问题的物品间存在某种“依赖”的关系。也就是说，i 依赖于 j，表示若选物品 i，则必须选物品 j。为了简化起见，我们先设没有某个物品既依赖于别的物品，又被别的物品所依赖；另外，没有某件物品同时依赖多件物品。</p></section><section><h3>算法<a href="#算法-2"><span>#</span></a></h3><p>这个问题由 NOIP2006 金明的预算方案一题扩展而来。遵从该题的提法，将不依赖于别的物品的物品称为“主件”，依赖于某主件的物品称为“附件”。由这个问题的简化条件可知所有的物品由若干主件和依赖于每个主件的一个附件集合组成。</p><p>按照背包问题的一般思路，仅考虑一个主件和它的附件集合。可是，可用的策略非常多，包括：一个也不选，仅选择主件，选择主件后再选择一个附件，选择主件后再选择两个附件……无法用状态转移方程来表示如此多的策略。（事实上，设有 n 个附件，则策略有 2^n+1 个，为指数级。）</p><p>考虑到所有这些策略都是互斥的（也就是说，你只能选择一种策略），所以一个主件和它的附件集合实际上对应于 P06 中的一个物品组，每个选择了主件又选择了若干个附件的策略对应于这个物品组中的一个物品，其费用和价值都是这个策略中的物品的值的和。但仅仅是这一步转化并不能给出一个好的算法，因为物品组中的物品还是像原问题的策略一样多。</p><p>再考虑 P06 中的一句话： 可以对每组中的物品应用 P02 中“一个简单有效的优化”。 这提示我们，对于一个物品组中的物品，所有费用相同的物品只留一个价值最大的，不影响结果。所以，我们可以对主件 i 的“附件集合”先进行一次 01 背包，得到费用依次为 0..V-c[i]所有这些值时相应的最大价值 f’[0..V-c[i]]。那么这个主件及它的附件集合相当于 V-c[i]+1 个物品的物品组，其中费用为 c[i]+k 的物品的价值为 f’[k]+w[i]。也就是说原来指数级的策略中有很多策略都是冗余的，通过一次 01 背包后，将主件 i 转化为 V-c[i]+1 个物品的物品组，就可以直接应用 P06 的算法解决问题了。</p></section><section><h3>较一般的问题<a href="#较一般的问题"><span>#</span></a></h3><p>更一般的问题是：依赖关系以图论中“森林”的形式给出（森林即多叉树的集合），也就是说，主件的附件仍然可以具有自己的附件集合，限制只是每个物品最多只依赖于一个物品（只有一个主件）且不出现循环依赖。</p><p>解决这个问题仍然可以用将每个主件及其附件集合转化为物品组的方式。唯一不同的是，由于附件可能还有附件，就不能将每个附件都看作一个一般的 01 背包中的物品了。若这个附件也有附件集合，则它必定要被先转化为物品组，然后用分组的背包问题解出主件及其附件集合所对应的附件组中各个费用的附件所对应的价值。</p><p>事实上，这是一种树形 DP，其特点是每个父节点都需要对它的各个儿子的属性进行一次 DP 以求得自己的相关属性。这已经触及到了“泛化物品”的思想。看完 P08 后，你会发现这个“依赖关系树”每一个子树都等价于一件泛化物品，求某节点为根的子树对应的泛化物品相当于求其所有儿子的对应的泛化物品之和。</p></section><section><h3>小结<a href="#小结-5"><span>#</span></a></h3><p>NOIP2006 的那道背包问题我做得很失败，写了上百行的代码，却一分未得。后来我通过思考发现通过引入“物品组”和“依赖”的概念可以加深对这题的理解，还可以解决它的推广问题。用物品组的思想考虑那题中极其特殊的依赖关系：物品不能既作主件又作附件，每个主件最多有两个附件，可以发现一个主件和它的两个附件等价于一个由四个物品组成的物品组，这便揭示了问题的某种本质。</p><p>我想说：失败不是什么丢人的事情，从失败中全无收获才是。</p><hr /></section></section>
<section><h2>第八讲 泛化物品<a href="#第八讲-泛化物品"><span>#</span></a></h2><section><h3>定义<a href="#定义"><span>#</span></a></h3><p>考虑这样一种物品，它并没有固定的费用和价值，而是它的价值随着你分配给它的费用而变化。这就是泛化物品的概念。</p><p>更严格的定义之。在背包容量为 V 的背包问题中，泛化物品是一个定义域为 0..V 中的整数的函数 h，当分配给它的费用为 v 时，能得到的价值就是 h(v) 。</p><p>这个定义有一点点抽象，另一种理解是一个泛化物品就是一个数组 h[0..V]，给它费用 v，可得到价值 h[V] 。</p><p>一个费用为 c 价值为 w 的物品，如果它是 01 背包中的物品，那么把它看成泛化物品，它就是除了 h(c)=w 其它函数值都为 0 的一个函数。如果它是完全背包中的物品，那么它可以看成这样一个函数，仅当 v 被 c 整除时有 h(v)=v/cw，其它函数值均为 0。如果它是多重背包中重复次数最多为 n 的物品，那么它对应的泛化物品的函数有 h(v)=v/cw 仅当 v 被 c 整除且 v/c&lt;=n，其它情况函数值均为 0 。</p><p>一个物品组可以看作一个泛化物品 h。对于一个 0..V 中的 v，若物品组中不存在费用为 v 的的物品，则 h(v)=0，否则 h(v)为所有费用为 v 的物品的最大价值。P07 中每个主件及其附件集合等价于一个物品组，自然也可看作一个泛化物品。</p></section><section><h3>泛化物品的和<a href="#泛化物品的和"><span>#</span></a></h3><p>如果面对两个泛化物品 h 和 l，要用给定的费用从这两个泛化物品中得到最大的价值，怎么求呢？事实上，对于一个给定的费用 v，只需枚举将这个费用如何分配给两个泛化物品就可以了。同样的，对于 0..V 的每一个整数 v，可以求得费用 v 分配到 h 和 l 中的最大价值 f(v)。也即</p><p><code>f(v)=max{h(k)+l(v-k)|0&lt;=k&lt;=v}</code></p><p>可以看到，f 也是一个由泛化物品 h 和 l 决定的定义域为 0..V 的函数，也就是说，f 是一个由泛化物品 h 和 l 决定的泛化物品。</p><p>由此可以定义泛化物品的和：h、l 都是泛化物品，若泛化物品 f 满足以上关系式，则称 f 是 h 与 l 的和。这个运算的时间复杂度取决于背包的容量，是 O(V^2) 。</p><p>泛化物品的定义表明：在一个背包问题中，若将两个泛化物品代以它们的和，不影响问题的答案。事实上，对于其中的物品都是泛化物品的背包问题，求它的答案的过程也就是求所有这些泛化物品之和的过程。设此和为 s，则答案就是 s[0..V] 中的最大值。</p></section><section><h3>背包问题的泛化物品<a href="#背包问题的泛化物品"><span>#</span></a></h3><p>一个背包问题中，可能会给出很多条件，包括每种物品的费用、价值等属性，物品之间的分组、依赖等关系等。但肯定能将问题对应于某个泛化物品。也就是说，给定了所有条件以后，就可以对每个非负整数 v 求得：若背包容量为 v，将物品装入背包可得到的最大价值是多少，这可以认为是定义在非负整数集上的一件泛化物品。这个泛化物品——或者说问题所对应的一个定义域为非负整数的函数——包含了关于问题本身的高度浓缩的信息。一般而言，求得这个泛化物品的一个子域（例如 0..V）的值之后，就可以根据这个函数的取值得到背包问题的最终答案。</p><p>综上所述，一般而言，求解背包问题，即求解这个问题所对应的一个函数，即该问题的泛化物品。而求解某个泛化物品的一种方法就是将它表示为若干泛化物品的和然后求之。</p></section><section><h3>小结<a href="#小结-6"><span>#</span></a></h3><p>本讲可以说都是我自己的原创思想。具体来说，是我在学习函数式编程的 Scheme 语言时，用函数编程的眼光审视各类背包问题得出的理论。这一讲真的很抽象，也许在“模型的抽象程度”这一方面已经超出了 NOIP 的要求，所以暂且看不懂也没关系。相信随着你的 OI 之路逐渐延伸，有一天你会理解的。</p><p>我想说：“思考”是一个 OIer 最重要的品质。简单的问题，深入思考以后，也能发现更多。</p><hr /></section></section>
<section><h2>第九讲 背包问题问法的变化<a href="#第九讲-背包问题问法的变化"><span>#</span></a></h2><p>以上涉及的各种背包问题都是要求在背包容量（费用）的限制下求可以取到的最大价值，但背包问题还有很多种灵活的问法，在这里值得提一下。但是我认为，只要深入理解了求背包问题最大价值的方法，即使问法变化了，也是不难想出算法的。</p><p>例如，求解最多可以放多少件物品或者最多可以装满多少背包的空间。这都可以根据具体问题利用前面的方程求出所有状态的值（f 数组）之后得到。</p><p>还有，如果要求的是“总价值最小”“总件数最小”，只需简单的将上面的状态转移方程中的 max 改成 min 即可。</p><p>下面说一些变化更大的问法。</p><section><h3>输出方案<a href="#输出方案"><span>#</span></a></h3><p>一般而言，背包问题是要求一个最优值，如果要求输出这个最优值的方案，可以参照一般动态规划问题输出方案的方法：记录下每个状态的最优值是由状态转移方程的哪一项推出来的，换句话说，记录下它是由哪一个策略推出来的。便可根据这条策略找到上一个状态，从上一个状态接着向前推即可。</p><p>还是以 01 背包为例，方程为 <code>f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}</code> 。再用一个数组 g[i][v]，设 g[i][v]=0 表示推出 f[i][v] 的值时是采用了方程的前一项（也即 f[i][v]=f[i-1][v]），g[i][v] 表示采用了方程的后一项。注意这两项分别表示了两种策略：未选第 i 个物品及选了第 i 个物品。那么输出方案的伪代码可以这样写（设最终状态为 f[N][V]）：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>i=N</span></div></div><div><div><div>2</div></div><div><span>v=V</span></div></div><div><div><div>3</div></div><div><span>while(i&gt;0)</span></div></div><div><div><div>4</div></div><div><span><span>    </span></span><span>if(g[i][v]==0)</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>print "未选第i项物品"</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>else if(g[i][v]==1)</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>print "选了第i项物品"</span></div></div><div><div><div>8</div></div><div><span><span>        </span></span><span>v=v-c[i]</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>另外，采用方程的前一项或后一项也可以在输出方案的过程中根据 f[i][v] 的值实时地求出来，也即不须纪录 g 数组，将上述代码中的 g[i][v]==0 改成 f[i][v]==f[i-1][v]，g[i][v]==1 改成 f[i][v]==f[i-1]v-c[i]]+w[i] 也可。</p></section><section><h3>输出字典序最小的最优方案<a href="#输出字典序最小的最优方案"><span>#</span></a></h3><p>这里“字典序最小”的意思是 1..N 号物品的选择方案排列出来以后字典序最小。以输出 01 背包最小字典序的方案为例。</p><p>一般而言，求一个字典序最小的最优方案，只需要在转移时注意策略。首先，子问题的定义要略改一些。我们注意到，如果存在一个选了物品 1 的最优方案，那么答案一定包含物品 1，原问题转化为一个背包容量为 v-c[1] ，物品为 2..N 的子问题。反之，如果答案不包含物品 1，则转化成背包容量仍为 V，物品为 2..N 的子问题。不管答案怎样，子问题的物品都是以 i..N 而非前所述的 1..i 的形式来定义的，所以状态的定义和转移方程都需要改一下。但也许更简易的方法是先把物品逆序排列一下，以下按物品已被逆序排列来叙述。</p><p>在这种情况下，可以按照前面经典的状态转移方程来求值，只是输出方案的时候要注意：从 N 到 1 输入时，如果 f[i][v]==f[i-1][i-v] 及 f[i][v]==f[i-1]f-c[i]]+w[i] 同时成立，应该按照后者（即选择了物品 i）来输出方案。</p></section><section><h3>求方案总数<a href="#求方案总数"><span>#</span></a></h3><p>对于一个给定了背包容量、物品费用、物品间相互关系（分组、依赖等）的背包问题，除了再给定每个物品的价值后求可得到的最大价值外，还可以得到装满背包或将背包装至某一指定容量的方案总数。</p><p>对于这类改变问法的问题，一般只需将状态转移方程中的 max 改成 sum 即可。例如若每件物品均是完全背包中的物品，转移方程即为</p><p><code>f[i][v]=sum{f[i-1][v],f[i][v-c[i]]}</code></p><p>初始条件 f[0][0]=1。</p><p>事实上，这样做可行的原因在于状态转移方程已经考察了所有可能的背包组成方案。</p><p>最优方案的总数
这里的最优方案是指物品总价值最大的方案。以 01 背包为例。</p><p>结合求最大总价值和方案总数两个问题的思路，最优方案的总数可以这样求：f[i][v]意义同前述，g[i][v]表示这个子问题的最优方案的总数，则在求 f[i][v]的同时求 g[i][v]的伪代码如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>for i=1..N</span></div></div><div><div><div>2</div></div><div><span><span>   </span></span><span>for v=0..V</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}</span></div></div><div><div><div>4</div></div><div><span><span>        </span></span><span>g[i][v]=0</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>if(f[i][v]==f[i-1][v])</span></div></div><div><div><div>6</div></div><div><span><span>            </span></span><span>inc(g[i][v],g[i-1][v])</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>if(f[i][v]==f[i-1][v-c[i]]+w[i])</span></div></div><div><div><div>8</div></div><div><span><span>            </span></span><span>inc(g[i][v],g[i-1][v-c[i]])</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>如果你是第一次看到这样的问题，请仔细体会上面的伪代码。</p></section><section><h3>求次优解、第 K 优解<a href="#求次优解第-k-优解"><span>#</span></a></h3><p>对于求次优解、第 K 优解类的问题，如果相应的最优解问题能写出状态转移方程、用动态规划解决，那么求次优解往往可以相同的复杂度解决，第 K 优解则比求最优解的复杂度上多一个系数 K。</p><p>其基本思想是将每个状态都表示成有序队列，将状态转移方程中的 max/min 转化成有序队列的合并。这里仍然以 01 背包为例讲解一下。</p><p>首先看 01 背包求最优解的状态转移方程：f[i][v]=max{f[i-1][v],f[i-1]v-c[i]]+w[i]}。如果要求第 K 优解，那么状态 f[i][v]就应该是一个大小为 K 的数组 f[i][v][1..K]。其中 f[i][v][k]表示前 i 个物品、背包大小为 v 时，第 k 优解的值。“f[i][v]是一个大小为 K 的数组”这一句，熟悉 C 语言的同学可能比较好理解，或者也可以简单地理解为在原来的方程中加了一维。显然 f[i][v][1..K]这 K 个数是由大到小排列的，所以我们把它认为是一个有序队列。</p><p>然后原方程就可以解释为：f[i][v]这个有序队列是由 f[i-1][v]和 f[i-1]v-c[i]]+w[i]这两个有序队列合并得到的。有序队列 f[i-1][v]即 f[i-1][v][1..K]，f[i-1]v-c[i]]+w[i]则理解为在 f[i-1]v-c[i]][1..K]的每个数上加上 w[i]后得到的有序队列。合并这两个有序队列并将结果的前 K 项储存到 f[i][v][1..K]中的复杂度是 O(K)。最后的答案是 f[N][V][K] 。总的复杂度是 O(VNK) 。</p><p>为什么这个方法正确呢？实际上，一个正确的状态转移方程的求解过程遍历了所有可用的策略，也就覆盖了问题的所有方案。只不过由于是求最优解，所以其它在任何一个策略上达不到最优的方案都被忽略了。如果把每个状态表示成一个大小为 K 的数组，并在这个数组中有序的保存该状态可取到的前 K 个最优值。那么，对于任两个状态的 max 运算等价于两个由大到小的有序队列的合并。</p><p>另外还要注意题目对于“第 K 优解”的定义，将策略不同但权值相同的两个方案是看作同一个解还是不同的解。如果是前者，则维护有序队列时要保证队列里的数没有重复的。</p></section><section><h3>小结<a href="#小结-7"><span>#</span></a></h3><p>显然，这里不可能穷尽背包类动态规划问题所有的问法。甚至还存在一类将背包类动态规划问题与其它领域（例如数论、图论）结合起来的问题，在这篇论背包问题的专文中也不会论及。但只要深刻领会前述所有类别的背包问题的思路和状态转移方程，遇到其它的变形问法，只要题目难度还属于 NOIP，应该也不难想出算法。</p><p>触类旁通、举一反三，应该也是一个 OIer 应有的品质吧。</p><hr /></section></section>
<section><h2>附录一：USACO 中的背包问题<a href="#附录一usaco-中的背包问题"><span>#</span></a></h2><p>USACO 是 USA Computing Olympiad 的简称，它组织了很多面向全球的计算机竞赛活动。</p><p>USACO Trainng 是一个很适合初学者的题库，我认为它的特色是题目质量高，循序渐进，还配有不错的课文和题目分析。其中关于背包问题的那篇课文 (TEXT Knapsack Problems) 也值得一看。</p><p>另外，USACO Contest 是 USACO 常年组织的面向全球的竞赛系列，在此也推荐 NOIP 选手参加。</p><p>我整理了 USACO Training 中涉及背包问题的题目，应该可以作为不错的习题。其中标加号的是我比较推荐的，标叹号的是我认为对 NOIP 选手比较有挑战性的。</p><section><h3>题目列表<a href="#题目列表"><span>#</span></a></h3><p>Inflate (+) （基本 01 背包）
Stamps (+)(!) （对初学者有一定挑战性）
Money
Nuggets
Subsets
Rockers (+) （另一类有趣的“二维”背包问题）
Milk4 (!) （很怪的背包问题问法，较难用纯 DP 求解）</p></section><section><h3>题目简解<a href="#题目简解"><span>#</span></a></h3><p>以下文字来自我所撰的《USACO 心得》一文，该文的完整版本，包括我的程序，可在 DD 的 USACO 征程中找到。</p><blockquote><p>Inflate 是加权 01 背包问题，也就是说：每种物品只有一件，只可以选择放或者 不放；而且每种物品有对应的权值，目标是使总权值最大或最小。它最朴素的状态 转移方程是：f[k][i] = max{f[k-1][i] , f[k-1]i-v[k]]+w[k]}。f[k][i]表示前 k 件物品花费代 价 i 可以得到的最大权值。v[k]和 w[k]分别是第 k 件物品的花费和权值。可以看到， f[k]的求解过程就是使用第 k 件物品对 f[k-1]进行更新的过程。那么事实上就不用使用 二维数组，只需要定义 f[i]，然后对于每件物品 k，顺序地检查 f[i]与 f[i-v[k]]+w[k]的大 小，如果后者更大，就对前者进行更新。这是背包问题中典型的优化方法。</p></blockquote><blockquote><p>题目 stamps 中，每种物品的使用量没有直接限制，但使用物品的总量有限制。 求第一个不能用这有限个物品组成的背包的大小。（可以这样等价地认为）设 f[k][i] 表示前 k 件物品组成大小为 i 的背包， 最少需要物品的数量。则 f[k][i]= min{f[k-1][i],f[k-1]i-j*s[k]]+j}，其中 j 是选择使用第 k 件物品的数目，这个方程运用时 可以用和上面一样的方法处理成一维的。求解时先设置一个粗糙的循环上限，即最 大的物品乘最多物品数。</p></blockquote><blockquote><p>Money 是多重背包问题。也就是每个物品可以使用无限多次。要求解的是构成 一种背包的不同方案总数。基本上就是把一般的多重背包的方程中的 min 改成 sum 就行了。</p></blockquote><blockquote><p>Nuggets 的模型也是多重背包。要求求解所给的物品不能恰好放入的背包大小 的最大值（可能不存在）。只需要根据“若 i、j 互质，则关于 x、y 的不定方程 ix+yj=n 必有正整数解，其中 n&gt;ij”这一定理得出一个循环的上限。 Subsets 子集和问题相当于物品大小是前 N 个自然数时求大小为 N(N+1)/4 的 01 背包的方案数。</p></blockquote><blockquote><p>Rockers 可以利用求解背包问题的思想设计解法。我的状态转移方程如下： f[i][j][t]=max{f[i][j][t-1] , f[i-1][j][t] , f[i-1][j][t-time[i]]+1 , f[i-1][j-1][T]+(t&gt;=time[i])} 。其中 f[i][j][t]表示前 i 首歌用 j 张完整的盘和一张录了 t 分钟的盘可以放入的最多歌数，T 是 一张光盘的最大容量，t&gt;=time[i]是一个 bool 值转换成 int 取值为 0 或 1。但我后来发 现我当时设计的状态和方程效率有点低，如果换成这样：f[i][j]=(a,b)表示前 i 首歌中 选了 j 首需要用到 a 张完整的光盘以及一张录了 b 分钟的光盘，会将时空复杂度都大 大降低。这种将状态的值设为二维的方法值得注意。</p></blockquote><blockquote><p>Milk4 是这些类背包问题中难度最大的一道了。很多人无法做到将它用纯 DP 方 法求解，而是用迭代加深搜索枚举使用的桶，将其转换成多重背包问题再 DP。由于 USACO 的数据弱，迭代加深的深度很小，这样也可以 AC，但我们还是可以用纯 DP 方法将它完美解决的。设 f[k]为称量出 k 单位牛奶需要的最少的桶数。那么可以用类 似多重背包的方法对 f 数组反复更新以求得最小值。然而困难在于如何输出字典序最 小的方案。我们可以对每个 i 记录 pre_f[i]和 pre_v[i]。表示得到 i 单位牛奶的过程是 用 pre_f[i]单位牛奶加上若干个编号为 pre_v[i]的桶的牛奶。这样就可以一步步求得得 到 i 单位牛奶的完整方案。为了使方案的字典序最小，我们在每次找到一个耗费桶数 相同的方案时对已储存的方案和新方案进行比较再决定是否更新方案。为了使这种 比较快捷，在使用各种大小的桶对 f 数组进行更新时先大后小地进行。USACO 的官 方题解正是这一思路。如果认为以上文字比较难理解可以阅读官方程序或我的程序。</p></blockquote><hr /></section></section>
<section><h2>附录二：背包问题的搜索解法<a href="#附录二背包问题的搜索解法"><span>#</span></a></h2><p>《背包问题九讲》的本意是将背包问题作为动态规划问题中的一类进行讲解。但鉴于的确有一些背包问题只能用搜索来解，所以这里也对用搜索解背包问题做简单介绍。大部分以 01 背包为例，其它的应该可以触类旁通。</p><section><h3>简单的深搜<a href="#简单的深搜"><span>#</span></a></h3><p>对于 01 背包问题，简单的深搜的复杂度是 O(2^N)。就是枚举出所有 2^N 种将物品放入背包的方案，然后找最优解。基本框架如下：</p><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>procedure SearchPack(i,cur_v,cur_w)</span></div></div><div><div><div>2</div></div><div><span><span>    </span></span><span>if(i&gt;N)</span></div></div><div><div><div>3</div></div><div><span><span>        </span></span><span>if(cur_w&gt;best)</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span>best=cur_w</span></div></div><div><div><div>5</div></div><div><span><span>        </span></span><span>return</span></div></div><div><div><div>6</div></div><div><span><span>    </span></span><span>if(cur_v+v[i]&lt;=V)</span></div></div><div><div><div>7</div></div><div><span><span>        </span></span><span>SearchPack(i+1,cur_v+v[i],cur_w+w[i])</span></div></div><div><div><div>8</div></div><div><span><span>    </span></span><span>SearchPack(i+1,cur_v,cur_w)</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>其中 cur_v 和 cur_w 表示当前解的费用和权值。主程序中调用 SearchPack(1,0,0)即可。</p></section><section><h3>搜索的剪枝<a href="#搜索的剪枝"><span>#</span></a></h3><p>基本的剪枝方法不外乎可行性剪枝或最优性剪枝。</p><p>可行性剪枝即判断按照当前的搜索路径搜下去能否找到一个可行解，例如：若将剩下所有物品都放入背包仍然无法将背包充满（设题目要求必须将背包充满），则剪枝。</p><p>最优性剪枝即判断按照当前的搜索路径搜下去能否找到一个最优解，例如：若加上剩下所有物品的权值也无法得到比当前得到的最优解更优的解，则剪枝。</p></section><section><h3>搜索的顺序<a href="#搜索的顺序"><span>#</span></a></h3><p>在搜索中，可以认为顺序靠前的物品会被优先考虑。所以利用贪心的思想，将更有可能出现在结果中的物品的顺序提前，可以较快地得出贪心地较优解，更有利于最优性剪枝。所以，可以考虑将按照“性价比”（权值/费用）来排列搜索顺序。</p><p>另一方面，若将费用较大的物品排列在前面，可以较快地填满背包，有利于可行性剪枝。</p><p>最后一种可以考虑的方案是：在开始搜索前将输入文件中给定的物品的顺序随机打乱。这样可以避免命题人故意设置的陷阱。</p><p>以上三种决定搜索顺序的方法很难说哪种更好，事实上每种方法都有适用的题目和数据，也有可能将它们在某种程度上混合使用。</p></section><section><h3>子集和问题<a href="#子集和问题"><span>#</span></a></h3><p>子集和问题是一个 NP-Complete 问题，与前述的（加权的）01 背包问题并不相同。给定一个整数的集合 S 和一个整数 X，问是否存在 S 的一个子集满足其中所有元素的和为 X。</p><p>这个问题有一个时间复杂度为 O(2^(N/2))的较高效的搜索算法，其中 N 是集合 S 的大小。</p><p>第一步思想是二分。将集合 S 划分成两个子集 S1 和 S2，它们的大小都是 N/2。对于 S1 和 S2，分别枚举出它们所有的 2^(N/2)个子集和，保存到某种支持查找的数据结构中，例如 hash set。</p><p>然后就要将两部分结果合并，寻找是否有和为 X 的 S 的子集。事实上，对于 S1 的某个和为 X1 的子集，只需寻找 S2 是否有和为 X-X1 的子集。</p><p>假设采用的 hash set 是理想的，每次查找和插入都仅花费 O(1)的时间。两步的时间复杂度显然都是 O(2^(N/2))。</p><p>实践中，往往可以先将第一步得到的两组子集和分别排序，然后再用两个指针扫描的方法查找是否有满足要求的子集和。这样的实现，在可接受的时间内可以解决的最大规模约为 N=42。</p></section><section><h3>搜索还是 DP?<a href="#搜索还是-dp"><span>#</span></a></h3><p>在看到一道背包问题时，应该用搜索还是动态规划呢？</p><p>首先，可以从数据范围中得到命题人意图的线索。如果一个背包问题可以用 DP 解，V 一定不能很大，否则 O(VN)的算法无法承受，而一般的搜索解法都是仅与 N 有关，与 V 无关的。所以，V 很大时（例如上百万），命题人的意图就应该是考察搜索。另一方面，N 较大时（例如上百），命题人的意图就很有可能是考察动态规划了。</p><p>另外，当想不出合适的动态规划算法时，就只能用搜索了。例如看到一个从未见过的背包中物品的限制条件，无法想出 DP 的方程，只好写搜索以谋求一定的分数了。</p><hr /></section></section>
<section><h2>联系方式<a href="#联系方式"><span>#</span></a></h2><p>如果有任何意见和建议，特别是文章的错误和不足，或者希望为文章添加新的材料，可以通过<a href="http://kontactr.com/user/tianyi/" target="_blank">http://kontactr.com/user/tianyi/</a>这个网页联系我。</p><p>值得说明的是，如果有 OI 方面的问题，例如不明白自己的程序为什么错了或者索要某种算法的源代码，使用这个联系方式可能得不到及时解答。请在<a href="http://oibh.org/bbs/" target="_blank">OIBH 论坛</a>发问。</p><hr /></section>
<section><h2>致谢<a href="#致谢"><span>#</span></a></h2><p>感谢以下名单：</p><ul>
<li>阿坦</li>
<li>jason911</li>
<li>donglixp</li>
<li>LeafDuo</li>
</ul><p>他们每人都最先指出了本文曾经存在的某个并非无关紧要的错误。谢谢你们如此仔细地阅读拙作并弥补我的疏漏。</p><p>感谢 XiaQ，它针对本文的第一个 beta 版发表了用词严厉的六条建议，虽然我只认同并采纳了其中的两条。在所有读者几乎一边倒的赞扬将我包围的当时，你的贴子是我的一剂清醒剂，让我能清醒起来并用更严厉的眼光审视自己的作品。</p><p>sfita 提供了 P01 中的“一个常数优化”。</p><p>当然，还有用各种方式对我表示鼓励和支持的几乎无法计数的同学。不管是当面赞扬，或是在论坛上回复我的贴子，不管是发来热情洋溢的邮件，或是在即时聊天的窗口里竖起大拇指，你们的鼓励和支持是支撑我的写作计划的强大动力，也鞭策着我不断提高自身水平，谢谢你们！</p><p>最后，感谢 <a href="http://www.gnu.org/software/emacs/" target="_blank">Emacs</a> 这一世界最强大的编辑器的所有贡献者，感谢它的插件 <a href="http://mwolson.org/projects/EmacsMuse.html" target="_blank">EmacsMuse</a> 的开发者们，本文的所有编辑工作都借助这两个卓越的自由软件完成。谢谢你们——自由软件社群——为社会提供了如此有生产力的工具。我深深钦佩你们身上体现出的自由软件的精神，没有你们的感召，我不能完成本文。在你们的影响下，采用自由文档的方式发布本文档，也是我对自由社会事业的微薄努力。</p></section>]]></content>
    </entry>
</feed>
