踩坑记录 | Windows11 硬件服务异常、移动文件卡死、无法打开硬件管理器、系统信息不显示 cpu 和存储和 RAM 频率等问题

<h2 id="问题描述">问题描述</h2> <p>该问题有几个主要现象:</p> <ol> <li>无法打开硬件管理器</li> <li>在桌面或资源管理器移动或剪贴文件夹会卡死</li> <li>资源管理器卡死时只会该窗口卡死,而不是系统卡死</li> <li>部分软件无法启动</li> <li>打开设置&gt;系统&gt;系统信息会卡顿,且不显示 cpu、存储、机带 RAM 的速度</li> <li>Win+R 打开 eventvwr.msc 后查看 Windows 日志&gt;系统里面有一堆10010错误(而且似乎发生在开机时)</li> <li>以上问题重启电脑无法解决</li> </ol> <h2 id="解决方法">解决方法</h2> <p>Win+X 打开终端管理员。</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmd" data-lang="cmd"><span style="display:flex;"><span>net stop <span style="color:#98c379">&#34;Plug and Play&#34;</span> </span></span><span style="display:flex;"><span>net start <span style="color:#98c379">&#34;Plug and Play&#34;</span> </span></span><span style="display:flex;"><span>net stop winmgmt </span></span><span style="display:flex;"><span>net start winmgmt </span></span></code></pre></div><p>我似乎是运行完前三行就当场几乎恢复正常(硬件管理器和打不开的软件直接打开了,再查看系统信息里面看得到cpu了,移动文件正常了),重启电脑后,系统信息里的存储和内存也恢复正常,日志里也没有错误了。</p> <h2 id="问题原因">问题原因</h2> <p>我发现我三次遇到这个问题之前,都有一个共通点:<strong>正在高负载任务,甚至可能存在爆内存或爆显存的情况</strong>。</p> <p>我前两次是在跑大模型,第三次是在玩大型3D游戏,风扇库库转的时候直接卡死了。</p> <p>推测可能是这种极限负载情况下,硬件服务出现异常,从而出现以上问题。</p> <p>至于之前卸载windows更新能解决问题,可能是因为卸载完之后的那次重启会起到重启服务的作用。</p> <h2 id="原内容">原内容</h2> <p>注意,原内容很可能是错误观点。至少,他没有真正解决问题。</p> <blockquote> <p>今天我刚从 zlib 美美下了几本书,结果想移动文件到我的电子书文件夹的时候,资源管理器卡死了。</p> <p>具体情况为:可以创建文件夹,但<strong>直接移动文件和剪贴文件都会导致卡死</strong>。卡死时只会<strong>该窗口卡死</strong>,但系统本身并没有过多卡顿,甚至还可以打开新的资源管理器窗口,新窗口一切正常。重启资源管理器和重启电脑均无效,一拖动还是会窗口卡死,并且出现<strong>部分软件无法启动</strong>的情况。</p> <p>因为出现问题时我要处理的文件和软件都在 D 盘,刚好 D 盘是单独一条 SSD,联想到我这几天在 D 盘大量部署新东西,我还以为是硬盘被我写坏了,结果测试发现打开另一条 SSD 的盘里移动文件也会出现一样问题。我开始慌了,不会两条 SSD 都炸了吧,现在存储这价格可不兴坏啊。</p> <p>我想启动 CrystalDiskInfo 查看硬盘情况,结果因为这个问题,启动不了 CrystalDiskInfo。好在重启之后再试一遍,卡了一分钟后启动成功了,结果发现硬盘很健康,一个警告都没有。</p> <p>这时候我突然想到,哎我前几天是不是更新了一下 Windows 来着??遂查看。</p> <p>KB5079473,三天前更新的。</p> <p>(粗口)</p> <p>没错,<strong>卸载掉 KB5079473</strong> 这个更新,问题就没有出现了。</p> <p>更新带来的原因多种多样,甚至可能这个问题不是由于更新的内容本身带来的,这里面的原因我也搞不明白。</p> <p>按理说更新了好几天了,不应该今天才发现这个问题,总之很奇怪,但修好了我也懒得折腾了。</p> <p>不管怎么样,先把他更新给暂停了再说。</p> </blockquote> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <p>无,我没在网络上搜到相关信息。我似乎是第一个遇到这个问题写文章的?</p> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2026/3/15
阅读更多

随笔杂谈 | 媒体因何无损

<h2 id="前言">前言</h2> <p>还是这样的文章写起来最顺手,比写科普专栏有意思多了。</p> <p>本文谈谈媒体的有损与无损的本质,并尝试从信息论的角度定义无损,以及记录一些了我的思考。</p> <h2 id="从媒体格式说起">从媒体格式说起</h2> <p>其实文件的本质就是一坨数据,而媒体文件的数据主要分成两个部分:一部分是媒体的元数据,一部分是媒体本身的数据,除此之外就是一些结构编码和文件本身的元数据了。</p> <p>众所周知,无论是什么数据,在计算机系统中都是01编码的,媒体元数据和媒体本身数据自然是如此,所以媒体怎样变成文件,基本完全取决于“信息的编码方式”,这就是所谓的<strong>媒体格式</strong>。</p> <p>通常,媒体格式会通过文件扩展名的形式显式告诉用户文件的类型,让用户明白不同格式的文件是有区别的,要区别对待,比如应该使用不同的软件打开。</p> <p>题外话:实际上,文件拓展名不能完全代表媒体的格式,涉及媒体时其中通常还有编码器、色彩位数、采样率之类的多种细分。详细格式会存储在文件元数据区,严谨的软件是通过这一部分数据来判断文件格式的,这就是为什么改扩展名并不影响很多东西。例如你可以把 png 改成 jpg 后正常打开,因为看图软件会正常认出这是 png 格式然后正常读取;例如你可以把一个 txt 改成 exe 后强制用记事本打开,里面的文字仍然显示正常,因为记事本会认出这是 txt 文件,然后正常读取;例如很多系统或配置文件甚至没有扩展名,因为这类文件不面向用户,也就没有告诉用户文件格式的必要。</p> <p>总之,媒体文件是有很多种规则去编码他的数据的,因此有不同格式。<strong>不同格式之间其实都是存储的同一个媒体,只是不同编码方式</strong>,所以只要把媒体读取解码出来再以另一种方式编码存储,就完成了所谓“转格式”。比如 ffmpeg 可以随意转换,从 mkv 到 mov,从 mp4 到 flv,从 jpg 到 bmp,只要是同一类媒体基本就可以互相转换,内部存储的信息没有改变。甚至你把一段文字从记事本复制到 word,何尝不是完成了一次格式转换?</p> <p>那为什么格式有无损和有损之别呢?比如熟知的 mp4 和 jpg 是压缩格式,png 和 mov 是无损格式。其实区别在于,有损格式是<strong>特意为了高效存储“损失了信息的数据”而设计的格式</strong>,而无损格式是<strong>为高效存储“较少或无损的数据”而设计的格式</strong>。</p> <p>因为我们不常需要完全无损,选择性地丢弃一些信息可以在对影响不大的情况下让文件小很多。这里需要一个知识前提:颜色在计算机系统中通常以三个0-255的数字来表示,也就是 rgb 格式,原因与显示器的显示原理有关。</p> <p>例如 png 格式如何存储图像,比较粗略的解释就是这样的:他首先存储了图像的调色盘,也就是找到了图像中的所有颜色,这样后面就可以用颜色在调色盘中的序号来代替 rgb,这样每个像素块从用三个数字表示变成一个数字表示。而此时我们说的压缩图像,通常就是合并相似颜色来缩小这个调色盘,这样编号范围就小了,就可以用更少的二进制位表示颜色。</p> <p>比如一个 100*100 的图像,原始图像有 1000 种不同的颜色,但假如把相近的颜色合并之后只剩下 256 种颜色,那么原先需要用 10 个 bit 才能表示这个颜色编号,现在只要 8 个 bit,这样每个像素块省了 2 bit,在存图像的部分一共省了 2w bit=2.5 kb,同时存颜色盘的部分少了 744 种颜色,即少了 744*24 个 bit,我就不算了。</p> <p>以上就是常见的图像压缩过程。这就是变成有损的过程中,我们损失了什么:一些你肉眼分辨不出区别,甚至显示器都显示不出区别的颜色。实际上,文件压缩包也是相似的原理。</p> <p>这里就可以直接理解:为什么存在无损格式?因为我只要设计一个格式,找个比较优的方法老老实实去存所有数据就好了。为什么 png 也可以无损?因为我可以硬要让 png 存下所有颜色,不做压缩,相似颜色我全部留着不动。其他媒体文件也是同理。</p> <h2 id="真无损吗">真无损吗?</h2> <p>看到这里也许你会疑惑:为什么假定原始图像就是无损的?这个原始图像,真的够“原始”吗?他们本身就“无损”吗?</p> <p>确实。前面的“无损”,我们只定义为与<strong>原始图像完全一致</strong>,但我们的需求并不只有传输和转移数据。单论“保存”这件事,如果我们要保存的信息本身就不完整,那什么格式都无济于事。</p> <p>假如是电子数据,<strong>产生时本来就是以数据的形式</strong>,例如电子制图、编曲,亦或是 3D 渲染的内容、AIGC 内容。那么毫无疑问,我们追求这些数据的无损是合理的。事实上,你看到的这个 html 网页,你记事本写的 txt,何尝不是“无损”的?我在这里打的每一个字都完整、毫无更改、未被丢弃的保留下来,如果十年后再来看,错字仍然是错字。</p> <p>假如是现实数据要保存成数据,那么将其采样编码时,<strong>信息损失往往是不可避免的</strong>。举个最简单的例子:照片的颜色、音频的频率,他们的实际数值可能小数点后有成千上万位,采样时可能只能保留小数点后三位,再往后可能既没有能力处理这么高精度也没有能力存下这么大数据。这时候便不存在真正的无损,只能用一个相对高的门槛来设定一个近似的无损。</p> <p>这也是摄影偏爱 RAW 格式的原因之一:RAW 格式保存了最原始的传感器数据和详细的拍摄参数,尚未经过那些不够智能的自动处理和压缩,他保留了更多的信息,有更少的损失率,因而有更大的后期调整空间。</p> <h2 id="广义的无损">广义的无损</h2> <p>不过,我们还是可以严格定义广义上的无损。从信息论的角度来讲:</p> <ol> <li> <p>当一个信息可以<strong>被有限的参数完全确定</strong>时,就是就是<strong>理论上可无损</strong>的。这里的“有限”有双重含义,一是参数个数有限,二是参数本身是有限数字。</p> </li> <li> <p>当这样的信息<strong>可以被完整采集</strong>时,就是<strong>实际上可无损</strong>的。</p> </li> </ol> <p>大量的信息因第一点而丢失,例如我们留不下来的小数精度。也有大量的信息因第二点而丢失,例如手机拍不到原子核的结构。但是并非所有乱七八糟的信息都是我们想要的信息。只要我们要保存的所有信息都是可无损的,那这次保存就是可无损的。</p> <p>例如截图时,我们需要保存的信息就是当前显示的一切,而电子屏幕本身就是按 rgb 值来显示像素块颜色的,因此只要存下所有像素的 rgb 就是无损的,无论是什么编码格式;相对比,拍照时我们想要保存的信息应该是“摄像头这个视角能看到的一切”,摄像头当然做不到,因此无论什么编码格式都是有损的。</p> <p>当然,如果你要说你拍照是为了存下“摄像头没坏”这个信息,那也可以说照片是无损的;同理如果你截屏是为了记录摔碎的屏幕,显然屏幕没有这个采集能力,这是一次全损的尝试。</p> <p>有损无损的定义很灵活,这正体现了保存意义的多样。无论出何原因而存下那份数据,在有损的领域追求无损,也许都是为了保存更多的意义。</p> <p>有关这些事情的思考本身也有意义,保存于此。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <p>无</p> <h3 id="免责声明">免责声明</h3> <p>本文并非专业科普,仅作分享,作者不负责由此产生的任何后果。欢迎交流,如有错误也欢迎指正。</p> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2026/1/10
阅读更多

踩坑记录 | ArtTS 异步回调中 Toast 和 Dialog 不显示的解决方法与可能原因

<h2 id="现象描述">现象描述</h2> <p>在某些异步回调中,Toast 和 Dialog 不能显示,如:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#e06c75">commentManager</span>.<span style="color:#e06c75">showCommentDialog</span>(<span style="color:#c678dd">this</span>.<span style="color:#e06c75">getUIContext</span>().<span style="color:#e06c75">getHostContext</span>() <span style="color:#c678dd">as</span> <span style="color:#e06c75">common</span>.<span style="color:#e06c75">UIAbilityContext</span>).<span style="color:#e06c75">then</span>(()<span style="color:#56b6c2">=&gt;</span>{ </span></span><span style="display:flex;"><span> <span style="color:#7f848e">// something </span></span></span><span style="display:flex;"><span><span style="color:#7f848e"></span>}).<span style="color:#c678dd">catch</span>((<span style="color:#e06c75">err</span>: <span style="color:#e5c07b">BusinessError</span>&lt;<span style="color:#e06c75">Object</span>&gt;) <span style="color:#56b6c2">=&gt;</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">myShowToast</span>(); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">myShowDialog</span>(); </span></span><span style="display:flex;"><span>}); </span></span></code></pre></div><p>myShowToast 和 myShowDialog 是一些非常正常的显示逻辑,但是却发现无法正常显示。</p> <h2 id="解决方法">解决方法</h2> <ol> <li>对于 Toast,可以设置 showMode 为 TOP_MOST</li> </ol> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#c678dd">this</span>.<span style="color:#e06c75">getUIContext</span>().<span style="color:#e06c75">getPromptAction</span>().<span style="color:#e06c75">showToast</span>({ </span></span><span style="display:flex;"><span> <span style="color:#e06c75">message</span><span style="color:#56b6c2">:</span> <span style="color:#98c379">&#34;message&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">duration</span>: <span style="color:#e5c07b">1500</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">showMode</span>: <span style="color:#e5c07b">promptAction.ToastShowMode.TOP_MOST</span> </span></span><span style="display:flex;"><span>}); </span></span></code></pre></div><ol start="2"> <li>对于 Toast 和 Dialog,可以延时调用</li> </ol> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#e06c75">commentManager</span>.<span style="color:#e06c75">showCommentDialog</span>(<span style="color:#c678dd">this</span>.<span style="color:#e06c75">getUIContext</span>().<span style="color:#e06c75">getHostContext</span>() <span style="color:#c678dd">as</span> <span style="color:#e06c75">common</span>.<span style="color:#e06c75">UIAbilityContext</span>).<span style="color:#e06c75">then</span>(()<span style="color:#56b6c2">=&gt;</span>{ </span></span><span style="display:flex;"><span> <span style="color:#7f848e">// something </span></span></span><span style="display:flex;"><span><span style="color:#7f848e"></span>}).<span style="color:#c678dd">catch</span>((<span style="color:#e06c75">err</span>: <span style="color:#e5c07b">BusinessError</span>&lt;<span style="color:#e06c75">Object</span>&gt;) <span style="color:#56b6c2">=&gt;</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">setTimeout</span>(() <span style="color:#56b6c2">=&gt;</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">myShowToast</span>(); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">myShowDialog</span>(); </span></span><span style="display:flex;"><span> }, <span style="color:#d19a66">500</span>); </span></span><span style="display:flex;"><span>}); </span></span></code></pre></div><p>个人测试发现,当延时调用后就可以正常显示 Toast 和 Dialog 了。</p> <p>当 delay 为 100 时,本地模拟器测试可以正常显示;当 delay 为 10 时,本地模拟器测试有概率显示;当 delay 为 1 时,本地模拟器测试无法正常显示。</p> <p>为尽量在无感前提下保证成功率,建议考虑将 delay 设置为 300 以上。</p> <h2 id="异常原因">异常原因</h2> <p>由以上解决方法,可以猜测这与线程或是上下文无关,而是对于特定的操作(实测 startAbility 的回调也会出现这种情况,个人猜测是打开页面或者调用界面一类的),在执行时能会阻止 Toast 和 Dialog 的显示(可能是将应用判成处于后台从而不能显示),因此延时调用,等待操作完全结束后就可以正常显示了。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a> 与华为开发者联盟,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2025/12/2
阅读更多

踩坑记录 | nvm 安装 node 显示 error installing 24.11.1: Node.js v24.11.1 is not yet released or is not available for download yet.

<h2 id="问题描述">问题描述</h2> <p>今天使用 nvm 安装 Node.js 时报错:error installing 24.11.1: Node.js v24.11.1 is not yet released or is not available for download yet.</p> <p>然而 <code>nvm ls available</code> 里并不空,镜像配置似乎没问题。</p> <h2 id="解决方法">解决方法</h2> <p>删掉 settings.txt 中的镜像配置就可以正常安装了。</p> <h2 id="原因">原因</h2> <p>推测可能是新版本 nvm 安装行为逻辑有变动,导致镜像不适配了。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2025/12/2
阅读更多

学习笔记 | Cloudflare 邮箱实现自动回复

<h2 id="前言">前言</h2> <p>也许你需要为你的 Cloudflare 实现一个自动回复?</p> <p>不管如何,我是需要的,但网络上资料又确实不多(至少我并未在中文互联网中找到),因而在此记录。</p> <h2 id="原理">原理</h2> <p>理所当然也别无选择地,我们使用 Cloudflare Workers 下的 Email Workers 实现。</p> <p>根据<a class="link" href="https://developers.cloudflare.com/email-routing/email-workers/reply-email-workers/" target="_blank" rel="noopener" >官方文档给出的回复方法</a>,我们需要使用 mimetext 中的 createMimeMessage,因此没办法像<a class="link" href="https://blog.reincarnatey.net/2024/0803-cloudflare-email-worker-forward-more/" target="_blank" rel="noopener" >上次</a>一样直接在线编辑了,必须要在本地开发好后部署。</p> <h2 id="安装-wrangler">安装 Wrangler</h2> <p>Wrangler 是 Cloudflare 官方推出的命令行界面(CLI)工具,专门用于管理和开发 Cloudflare Workers 以及其他相关的开发者平台产品。</p> <p>我们要在本地进行 Worker 的开发,所以先来安装 Wrangler,需要用到 npm。</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmd" data-lang="cmd"><span style="display:flex;"><span>npm install -g wrangler </span></span></code></pre></div><p>安装完成后使用以下命令登录,在弹出的窗口中登录 Cloudflare 并授权。</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmd" data-lang="cmd"><span style="display:flex;"><span>wrangler login </span></span></code></pre></div><h2 id="创建项目">创建项目</h2> <p>接着在你希望创建项目的目录下打开 cmd(用 powershell 可能因为禁止运行脚本而出错),新建项目。</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmd" data-lang="cmd"><span style="display:flex;"><span>wrangler init auto-reply </span></span></code></pre></div><p>过程中需要回答一些问题,按照自己的情况选即可,使用方向键选择。</p> <p>这里我们只是想实现一个邮箱自动回复,模版选择 <code>Hello World example</code> 的 <code>Worker only</code> 即可,语言推荐 JavaScript(文档使用的语言),其他的随意。</p> <p>等待一段时间,项目目录就创建好了。</p> <p>因为要使用 Node.js 库,因此需要修改项目配置,启用 Node.js 兼容。</p> <p>项目目录下的 <code>wrangler.jsonc</code> 是项目配置文件,我们在里面添加:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#98c379">&#34;compatibility_flags&#34;</span>: [ </span></span><span style="display:flex;"><span> <span style="color:#98c379">&#34;nodejs_compat&#34;</span> </span></span><span style="display:flex;"><span>] </span></span></code></pre></div><p>就完成项目配置了。</p> <h2 id="安装-mimetext-并编写代码">安装 mimetext 并编写代码</h2> <p>在项目目录下运行 cmd,输入命令安装。</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmd" data-lang="cmd"><span style="display:flex;"><span>npm install mimetext </span></span></code></pre></div><p>然后修改 <code>src/index.js</code> 为下方代码,并修改注释标记的三处配置,就完成了。</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#c678dd">import</span> { <span style="color:#e06c75">EmailMessage</span> } <span style="color:#e06c75">from</span> <span style="color:#98c379">&#34;cloudflare:email&#34;</span>; </span></span><span style="display:flex;"><span><span style="color:#c678dd">import</span> { <span style="color:#e06c75">createMimeMessage</span> } <span style="color:#e06c75">from</span> <span style="color:#98c379">&#34;mimetext&#34;</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">export</span> <span style="color:#c678dd">default</span> { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">async</span> <span style="color:#e06c75">email</span>(<span style="color:#e06c75">message</span>, <span style="color:#e06c75">env</span>, <span style="color:#e06c75">ctx</span>) { </span></span><span style="display:flex;"><span> <span style="color:#7f848e">// 配置发件人信息 </span></span></span><span style="display:flex;"><span><span style="color:#7f848e"></span> <span style="color:#c678dd">const</span> <span style="color:#e06c75">senderName</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;no-reply&#34;</span>; </span></span><span style="display:flex;"><span> <span style="color:#c678dd">const</span> <span style="color:#e06c75">senderAddress</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;&lt;SENDER&gt;@example.com&#34;</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">const</span> <span style="color:#e06c75">msg</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">createMimeMessage</span>(); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">msg</span>.<span style="color:#e06c75">setHeader</span>(<span style="color:#98c379">&#34;In-Reply-To&#34;</span>, <span style="color:#e06c75">message</span>.<span style="color:#e06c75">headers</span>.<span style="color:#e06c75">get</span>(<span style="color:#98c379">&#34;Message-ID&#34;</span>)); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">msg</span>.<span style="color:#e06c75">setSender</span>({ <span style="color:#e06c75">name</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">senderName</span>, <span style="color:#e06c75">addr</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">senderAddress</span> }); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">msg</span>.<span style="color:#e06c75">setRecipient</span>(<span style="color:#e06c75">message</span>.<span style="color:#e06c75">from</span>); </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#7f848e">// 配置邮件标题和内容 </span></span></span><span style="display:flex;"><span><span style="color:#7f848e"></span> <span style="color:#e06c75">msg</span>.<span style="color:#e06c75">setSubject</span>(<span style="color:#98c379">&#34;自动回复&#34;</span>); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">msg</span>.<span style="color:#e06c75">addMessage</span>({ </span></span><span style="display:flex;"><span> <span style="color:#e06c75">contentType</span><span style="color:#56b6c2">:</span> <span style="color:#98c379">&#39;text/plain&#39;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">data</span><span style="color:#56b6c2">:</span> <span style="color:#98c379">`我们已收到您的邮件`</span> </span></span><span style="display:flex;"><span> }); </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">const</span> <span style="color:#e06c75">replyMessage</span> <span style="color:#56b6c2">=</span> <span style="color:#c678dd">new</span> <span style="color:#e06c75">EmailMessage</span>( </span></span><span style="display:flex;"><span> <span style="color:#e06c75">senderAddress</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">message</span>.<span style="color:#e06c75">from</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">msg</span>.<span style="color:#e06c75">asRaw</span>() </span></span><span style="display:flex;"><span> ); </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">await</span> <span style="color:#e06c75">message</span>.<span style="color:#e06c75">reply</span>(<span style="color:#e06c75">replyMessage</span>); </span></span><span style="display:flex;"><span> <span style="color:#7f848e">// 当然,不能光回复,还要将邮件转发到你接受邮件的邮箱,这个邮箱需要在 Cloudflare Email Routing 里验证过 </span></span></span><span style="display:flex;"><span><span style="color:#7f848e"></span> <span style="color:#c678dd">await</span> <span style="color:#e06c75">message</span>.<span style="color:#e06c75">forward</span>(<span style="color:#98c379">&#34;example@example.com&#34;</span>); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><h2 id="部署">部署</h2> <p>最后,在项目目录下运行以下命令部署到云端。</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmd" data-lang="cmd"><span style="display:flex;"><span>wrangler deploy </span></span></code></pre></div><p>接着,打开你的 Cloudflare Dashboard,来到对应域名的 Email Routing 电子邮件路由页面,查看电子邮件 Workers,可以发现刚刚部署的已经出现在上面了,将其分配给对应的邮箱地址即可。</p> <p>注意需要等待约半分钟才会生效。</p> <p>可以给自己发一条邮件测试,发现成功收到了自动回复。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li><a class="link" href="https://developers.cloudflare.com/email-routing/email-workers/reply-email-workers/" target="_blank" rel="noopener" >官方文档</a></li> <li><a class="link" href="https://github.com/mgmgprndev/CloudflareEmailWorker_AutoReply?tab=readme-ov-file" target="_blank" rel="noopener" >mgmgprndev/CloudflareEmailWorker_AutoReply 项目</a></li> <li><a class="link" href="https://blog.csdn.net/Tisfy/article/details/144488128" target="_blank" rel="noopener" >Cloudflare workers - 如何使用(起步篇)</a></li> <li><a class="link" href="https://fe.azhubaby.com/BackEnd/cloudflare/worker.html#%E9%80%9A%E8%BF%87-wrangler-%E9%83%A8%E7%BD%B2" target="_blank" rel="noopener" >Cloudflare菩萨系列:workers 使用指南</a></li> <li><a class="link" href="https://yangzh.cn/posts/cloudflare/wrangler" target="_blank" rel="noopener" >Wrangler 入门:开启你的 Cloudflare Serverless 之旅</a></li> <li><a class="link" href="https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag" target="_blank" rel="noopener" >官方文档 - Compatibility flags</a></li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2025/12/2
阅读更多

逻辑解构 | 秩序的帽子戏法

<h2 id="题目">题目</h2> <p>数学大臣尤泽尔被国王亚路开德召到皇宫,却发现前面已经排了 9 个非常奇怪的人——这些人全都戴上了帽子,或黑或白。</p> <p>早在一旁等候的侍卫克里亚,向一脸疑惑的尤泽尔走了过来。</p> <p>“陛下正在让他们完成一个挑战”,克里亚解释说,“每次我都会给他们每个人随机戴上黑或白色的帽子,所有人要轮流猜自己头顶上的帽子的颜色,最多只可以猜错一次。”</p> <p>“原来这些帽子不是他们自己戴上的?但陛下恐怕要失望了,很显然人类无法看到自己的头顶”,尤泽尔略加思索,“除非里面有数学大臣。”</p> <p>“数学大臣就可以看到头顶吗?”</p> <hr> <p>尤泽尔加入了挑战。</p> <p>他们有一次商讨策略的机会,可以按任意顺序排队和报答案,但在排好队之后尤泽尔才会开始给他们戴帽子,排好队后只有嘴可以动。请问他们有完成挑战的方法吗?</p> <blockquote> <p><strong>答案(不唯一)</strong></p> <p>由于每个人都看得到前面的人的帽子,也听得到其他人报答案,所以以下策略是可行的:</p> <ul> <li>每个人都面朝前方排成一条直线,并从后往前报答案</li> <li>对于最后一个人,若前面有奇数顶黑帽子,就报黑色,若有偶数顶黑帽子,就报白色</li> <li>对于其他人,根据后面的人报的颜色可以推算出自己的颜色</li> </ul> <p><strong>证明</strong></p> <p>由于每个人都看得到前面的人的帽子,也听得到其他人报答案,所以除了最后一个人以外,其他人:</p> <ul> <li>可以看到前面有多少顶黑帽子</li> <li>可以听到除了最后一个人,后面有多少顶黑帽子</li> <li>最后一个人报出了前 9 个人共有奇数还是偶数顶黑帽子</li> </ul> <p>不妨设总共有偶数顶黑帽子,否则就将颜色取反,此时有:</p> <ul> <li>若前面有偶数顶,后面有偶数顶,那么自己一定是白帽子</li> <li>若前面有偶数顶,后面有奇数顶,那么自己一定是黑帽子</li> <li>若前面有奇数顶,后面有偶数顶,那么自己一定是黑帽子</li> <li>若前面有奇数顶,后面有奇数顶,那么自己一定是白帽子</li> </ul> <p>上述推理很好证明,以第一点为例:若前面有偶数顶,后面有偶数顶,如果自己还是黑帽子的话就总共有“前面偶数顶+后面偶数顶+自己1顶=奇数顶”了,与总共有偶数顶黑帽子矛盾,所以自己一定是白帽子。</p> <p>其他三点证明也同理。因此该策略可以保证除了最后一个人以外必定猜对,可以完成挑战。</p> </blockquote> <h2 id="原理">原理</h2> <p>原理在证明中已经体现得淋漓尽致了,就是<strong>奇偶性</strong>,然后舍弃最后一个人来传递信息,再充分利用游戏规则中隐含的已知信息来推算答案。</p> <p>更深层的原理其实类似于<a class="link" href="https://blog.reincarnatey.net/2025/0630-2coin-problem/" target="_blank" rel="noopener" >本系列第一题</a>的结论:在所有数值域均为模数的情况下,模意义下减法具有唯一解。</p> <p>我们可以这么表述这个答案:设 $c_i$ 为从前往后第 $i$ 个人帽子的颜色,$0$ 为白色,$1$ 为黑色,这样从后往前报颜色,那么第 $i$ 个人:</p> <ul> <li>可以看到前面的颜色和:$s_1=c_1+c_2+\cdots+c_{i-1}$</li> <li>可以听到后面的颜色和:$s_2=c_{i+1}+c_{i+2}+\cdots+c_9$</li> <li>最后一个人报了总颜色和:$s=c_1+c_2+\cdots+c_9$</li> </ul> <p>因此自然可以算出自己的颜色 $c_i=s-s_1-s_2$。上式全部是模 $2$ 意义下的,根据上述结论,$c_i$ 有唯一解。</p> <p>当然,我们其实也可以将上述操作看作异或,这是另一种解释。</p> <h2 id="思考">思考</h2> <h3 id="可以更多人吗">可以更多人吗?</h3> <p>显然可以,因为上述策略总是可以保证除了最后一个人以外全对,因此无论有多少个人都可以完成挑战。</p> <h3 id="可以更多颜色吗">可以更多颜色吗?</h3> <p>也可以,因为该结论在任意模数下都是成立的,因此这个策略可以推广到任意颜色数</p> <h3 id="其他的答案">其他的答案?</h3> <p>在理解以上答案之后,我们不禁思考能否选择观察别的数据或别的性质。</p> <p>毫无疑问,由于最后一个人已知信息最多,但唯独绝对不可能知道自己的信息,因此最后一个人一定是负责报一个全局信息来让其他人能够猜出自己颜色的。</p> <p>什么样的信息符合要求呢?思考上述过程,我们发现要求是:对于任意已有的颜色序列,再加入不同的颜色会对信息造成不同的变化(不变也算作一种变化),且这一运算是存在逆运算的,且这一信息的可能状态数不超过颜色个数。</p> <p>先只考虑两个颜色的情况,此时我们需要用一个 bit 来表示出包含所有人颜色在内的信息。</p> <p>我绞尽脑汁想出来一个“长度为奇数的黑色段的奇偶性”,这是可行的,但其本质上其实与之前一个答案是一样的,因为黑帽子总数的奇偶性就取决于长度为奇数的黑色段的奇偶性,因此实际上是换一个表述。</p> <p>还有其他的表述形式,例如“黑色与白色个数的差值(若 $n$ 为奇数还需要减去 $1$)是否是 $4$ 的倍数”、“黑色连续段的长度的异或和的奇偶性”,这些其实都等价于黑帽子个数的奇偶性。</p> <p>实际上,对于每个人都是已知除了自己与最后一个人以外的所有颜色信息,所以我们只需要满足,在其他信息相同的情况下,自己是黑是白会导致全局信息不同即可。</p> <p>用数学来描述的话,这样的全局信息必须对应一个函数 $f: {B,W}^{n-1} \to {0,1}$,使得对于每个位置 $i$ 和每个其他帽子的配置,改变第 $i$ 顶帽子的颜色都会改变函数值 $f$。</p> <p>满足这种条件的函数,只能是前 $n-1$ 顶帽子黑帽子个数的奇偶性函数或其否定(即奇偶性的补)。</p> <p>因此,唯一可行的策略就是基于奇偶性的策略,不存在其他策略能保证最多错一次。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <p>由于这个问题非常经典,解法也非常经典,所以就写到这了。</p> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2025/9/5
阅读更多

学习笔记 | 初学打字标准指法常见问题的自我 Q&A

<h2 id="前言">前言</h2> <p>博主于 2024 年八月末开始学习打字标准指法,于 2024 年九月初提出本文中的问题,于 2025年七月中回答本文中的问题。</p> <p>博主使用的是机械键盘,使用拼音输入。</p> <p>我原本的输入指法十分不标准,左手四指初始位置为 Shift、A、S、D,大致是由于我是在游戏过程中熟悉电脑键盘的,而游戏的常见键位导致很容易出现这样的初始位置。并在此错误基础上,由于我后来接触了编程与算法竞赛,需要大量的输入,由此形成了一套自己的“按法”(例如输入 scanf 时我的手的动作跨度很大、幅度很不合理,但由于习惯所以打得很快,或是 G 我会下意识用右手去按)。</p> <p>习惯的事情,难改是正常的,但越早改越好。要改过来也很容易,实际上打个小半天就能有点习惯了。</p> <p>我是使用打字练习网站进行练习,其他的打字软件应该也是一样的效果。</p> <p>个人推荐先从拼音练起,拼音是很固定的搭配组合,很容易就练得很顺手,相比之下英文与编程时可能远没有那么顺手。</p> <p>据说练习时最好不看键盘 ,我持保留意见。的确你不能想着一直看着键盘打字,练熟以后肯定是不看的,但是初学时知道自己的手指在做什么也很重要,我认为偶尔以检查的目光去看一下还是有必要的。</p> <p>打这篇文章时实际上还是没有完全习惯,打错很多,不过这是我没有练习的原因。</p> <p>总而言之,希望能解答一些初学者的疑惑。</p> <h2 id="练习标准指法的好处">练习标准指法的好处</h2> <ul> <li> <p>每个手指的安排相对更合理,可以减少手部移动,减轻手指负担</p> </li> <li> <p>减少需要的位移也会间接降低输错的概率,按三格开外的键比按旁边的键要容易按错的多</p> </li> <li> <p>熟悉以后可以加快平均输入速度</p> </li> <li> <p>可以更充分地利用所有手指</p> </li> <li> <p>个人认为打字时候的动作也更美观,至少你盯着自己打字的手看还蛮解压的</p> </li> </ul> <h2 id="q--a">Q &amp; A</h2> <p><strong>Q:哪只手按空格</strong></p> <p>A:左右手大拇指都停留在空格上,想用哪只用哪只。有一只说法是,按空格的上一个键是左手的话就用右手按空格,上一个键是右手的话就用左手按空格,这样会更省力,但实际上我认为怎么按都比较舒适,不会有太大的差别。</p> <p><strong>Q:无名指和小拇指感觉没力气,按键盘很虚,而且容易触发多次按下</strong></p> <p>A:这就说明以前的指法是错误的,没怎么用到这两个手指,打多了自然就有力气了,现在我的无名指和小拇指打字打起来感觉很正常,不会很虚,也不会触发多次按下。</p> <p><strong>Q:一换指法就跟不记得键盘一样</strong></p> <p>A:打少了导致的。</p> <p><strong>Q:凭什么右手小拇指在分号上</strong></p> <p>A:我是怎么问出这种问题的。右手小拇指在分号上,确实分号很少按,但它还负责了一大片区域呀,比方说回车、退格等等,实际上是最累的手指。</p> <p><strong>Q:左右手不协调</strong></p> <p>A:打少了导致的。</p> <p><strong>Q:左手中指无名指和右手中指移动的时候会连带着其他手指一起移动</strong></p> <p>A:打少了导致的。现在我不会出现这种情况,实际上出现的话也没关系,轻微动一动也可以的。</p> <p><strong>Q:总感觉某些指按某些键更合理</strong></p> <p>A:我已经记不起来那时候我是怎么想的了,应该也是打少了导致的。</p> <p><strong>Q:左手到 B、右手到 Y 太远了</strong></p> <p>A:确实,左手的 B 确实比较不舒服,右手的 Y 还好,但是习惯就好。</p> <p><strong>Q:Shift、Ctrl、Alt 哪只手按</strong></p> <p>A:如果要配合的键是右手按,那么就用左手按 Shift、Ctrl、Alt;如果要配合的键是左手按 ,那么就用右手按 Shift、Ctrl、Alt。</p> <p><strong>Q:打中文选词按数字键好远</strong></p> <p>A:这对于所有指法都是一样的吧。只能说善用空格键,然后习惯就好。</p> <p><strong>Q:老是打错</strong></p> <p>A:这不是指法的问题。</p> <p><strong>Q:打 R 的时候感觉手指有点挤</strong></p> <p>A:好像是有点,但现在已经蛮习惯的,没有觉得不舒服。所以习惯就好。</p> <p><strong>Q:手指何时该归位</strong></p> <p>A:最初的我认为打完之后手指可以在那个键上停留一会,这样如果连续打的话可以加快速度,过大概几秒手指才会归位,但是现在的我发现,打习惯之后,会感觉手指就像弹簧,你不用力的话会自动归位到初始位置,于是也就打完之后自动归位了。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2025/7/20
阅读更多

逻辑解构 | 真实与谎言的守门人

<h2 id="题目">题目</h2> <p>数学大臣尤泽尔受到国王亚路开德的召见,被侍卫克里亚带到了一个房间。在他的面前只有两扇门,门前各有一个守门人。</p> <p>“两个守门人中有一个人只说真话,另一个人只说假话”,克里亚说,“陛下在其中一扇门后面等着您,但陛下要求您只可以问其中一个人一个问题。”</p> <p>“另一扇门后面有什么?”尤泽尔问。</p> <p>这位王国最聪明的头脑,以往都是二话不说地轻松解决所有谜题,今天居然问起了失败的后果。克里亚有些诧异,但还是耐心地解释道“只有一把空椅子。”</p> <p>“不错”,尤泽尔呢喃了一句,令克里亚心里顿感不妙。</p> <p>尤泽尔随便走向其中一个守门人,问了一个有些复杂的问题,守门人随即陷入沉思,那痛苦的表情让克里亚有些担心。但好在,几分钟过后,守门人还是回答了上来,是正确的门。然而,克里亚尚未来得及松一口气,只见尤泽尔直接打开了另外一扇门——然后坐到了那把空椅子上。在守门人赶上之前,尤泽尔砰地一声关上了门。</p> <p>“让陛下来找我”,尤泽尔的声音透过门传来,“他只能问其中一个人一个问题”。</p> <hr> <p>陛下应该问什么问题,才能找到正确的门?</p> <blockquote> <p><strong>答案(不唯一)</strong></p> <p>问其中一个守门人:“如果我问另一个守门人‘哪扇是正确的门&rsquo;,他会回答哪扇门?”,回答的门就是错误的门,选择另一扇门即可。</p> <p><strong>证明</strong></p> <p>分类讨论:</p> <ul> <li>若问到了说真话的守门人,则另一个守门人说的是假话,会回答错误的门,那么这个说真话的守门人就会告诉你错误的门</li> <li>若问到了说假话的守门人,则另一个守门人说的是真话,会回答正确的门,那么这个说假话的守门人就会告诉你错误的门</li> </ul> <p>所以无论问的是哪个守门人,回答都是错误的门,因此选择另一扇门即可。</p> </blockquote> <h2 id="原理">原理</h2> <p>这里我们先完善一下“说假话”的定义:若答案是二元的(如是或不是、左或右等),则会说相反的答案;若答案非二元,则有可能说出任何不正确的答案。</p> <p>我们先来弄清楚这个游戏存在的二元:</p> <ul> <li>两个守门人,其中一个只说真话,一个只说假话</li> <li>两扇门,只有一扇门是正确的</li> </ul> <p>前者是游戏背景规则,也是做选择时的限制,但<strong>并非我们需要的答案</strong>;后者是游戏背景规则,也是我们需要的答案。</p> <p>只问一个问题很难同时得到两个问题的答案,因此我们应该考虑放弃弄清楚谁说真话谁说假话,只要确定哪扇门是正确或错误的就可以了。换言之,我们需要问一个“<strong>不管问到说真话还是说假话的人,都会回答相同的答案</strong>”的问题。</p> <p>答案的问题正是如此。从证明里我们能发现,不管问到说真话还是说假话的守门人,最后回答的都是错误的门。</p> <p>为何能达成这样的效果呢?我们研究一下,问题的回答在被我们得知之前都经过了什么。以左边的门为正确的门且问到了说真话的守门人为例:</p> <ul> <li>最内层的问题:哪扇是正确的门,此时回答为左</li> <li>内层问题:问另一个守门人“哪扇是正确的门”的回答,由于另一个守门人说的是假话,最内层回答为左,所以此时回答为右</li> <li>外层问题:问这个守门人“如果问另一个守门人…”的回答,由于这个守门人说的是真话,内存回答为右,所以此时回答为右</li> </ul> <p>再以左边的门为正确的门,且问到了说假话的守门人为例;</p> <ul> <li>最内层的问题:哪扇是正确的门,此时回答为左</li> <li>内层问题:问另一个守门人“哪扇是正确的门”的回答,由于另一个守门人说的是真话,最内层回答为左,所以此时回答为左</li> <li>外层问题:问这个守门人“如果问另一个守门人…”的回答,由于这个守门人说的是假话,内存回答为左,所以此时回答为右</li> </ul> <p>可以发现,两次回答的过程的共同点是,回答都是被反转了一次的。</p> <p>答案的巧妙之处在于,这个问题虽然只对一个守门人问了,但其实<strong>回答的过程把两个人都经过了</strong>,因此得到的回答一定是最内层问题的相反回答。</p> <p>因此,原理同上的问题都可以完成本题的任务。如“另一个守门人会告诉我正确的门是左边的门吗?”,如果回答不会,那么正确的门就是左边的门。</p> <h2 id="思考">思考</h2> <h3 id="这里面有没有什么数学原理呢">这里面有没有什么数学原理呢?</h3> <p>当然是有的。</p> <p>说句题外话:逻辑解构系列里的谜题当然都是有更深的原理的,这样才值得我们研究,而且几乎必然离不开数学,这也是为什么尤泽尔是数学大臣而不是物理大臣或者解谜大臣。</p> <p>理解上面的原理后,我们发现,因为原问题必定把两个守门人都经过一次,所以回答一定会被反转一次,此时两个守门人有点类似“串联”的关系。</p> <p>用逻辑表示说真话与说假话使答案发生的变化的话,说真话就是保持不变,说假话就是取反。</p> <p>答案问题中的“串联”,实际上就等价于最内层问题的一个取反,只不过是利用游戏规则表达成了语言而已。</p> <pre class="mermaid" style="text-align: center;"> flowchart LR A[原问题回答] B[说真话的守门人] C[说假话的守门人] D[给尤泽尔的回答] A--&gt;B B--保持不变--&gt;C C--取反--&gt;D </pre> <p>同理,我们很容易想到另一种“串联”方法:将自己与自己串联。说真话串上说真话,答案不变;说假话串上说假话,就是取反再取反,也是不变。转换成语言就是“如果我问你,哪扇是正确的门,你会回答哪扇门?”</p> <pre class="mermaid" style="text-align: center;"> flowchart LR subgraph 问到说假话的守门人 direction LR E[原问题回答] F[说假话的守门人] G[说假话的守门人] H[给尤泽尔的回答] E--&gt;F F--取反--&gt;G G--再取反--&gt;H end subgraph 问到说真话的守门人 direction LR A[原问题回答] B[说真话的守门人] C[说真话的守门人] D[给尤泽尔的回答] A--&gt;B B--保持不变--&gt;C C--保持不变--&gt;D end </pre> <p>这样我们就得出了本题的另一个可能的答案,并且这个答案会直接得到正确的门,不用再反转一次,并且哪怕只有一个守门人也可以问到正确的门。</p> <h3 id="和位运算有关系吗">和位运算有关系吗?</h3> <p>过程之中的反转,很容易让我们联想到布尔值。提到布尔值,很容易联想到的就是逻辑运算或者位运算,这里我们研究位运算,那不得不做的就是看一下真值表。</p> <p>以二元问题为例,我们把其中一个答案看作 $1$,相反答案看作 $0$。这里我们将一个问题在经过守门人之前的答案看作输入,守门人会给出的答案看作输出。</p> <p>那么,只说真话的守门人的真值表:</p> <table> <thead> <tr> <th style="text-align: center">输入</th> <th style="text-align: center">输出</th> </tr> </thead> <tbody> <tr> <td style="text-align: center">0</td> <td style="text-align: center">0</td> </tr> <tr> <td style="text-align: center">1</td> <td style="text-align: center">1</td> </tr> </tbody> </table> <p>原封不动,我们可以看作和 $1$ 做与运算,也就是 <code>x &amp; 1</code>。</p> <p>我们再来看看只说假话的守门人的真值表:</p> <table> <thead> <tr> <th style="text-align: center">输入</th> <th style="text-align: center">输出</th> </tr> </thead> <tbody> <tr> <td style="text-align: center">0</td> <td style="text-align: center">1</td> </tr> <tr> <td style="text-align: center">1</td> <td style="text-align: center">0</td> </tr> </tbody> </table> <p>取反,我们可以看作取反,也就是 <code>~x</code>,也可以看作 <code>x ^ 1</code>。</p> <p>由此,我们可以发现上面的第一种答案对应的运算其实就是 <code>~(x &amp; 1)</code> 或者 <code>~(x ^ 1)</code>,第二种对应的是 <code>~(~x)</code> 或者 <code>x ^ 1 ^ 1</code>。</p> <p>由此我们也可以玩的更复杂一些,叠更多的运算。当然,由于我们并不知道哪个人说真话哪个人说假话,所以两种运算符的数量要一致。</p> <p>例如,我们尝试构造 <code>~(~~x&amp;1&amp;1)&amp;1</code> 对应的问题:</p> <pre class="mermaid" style="text-align: center;"> graph LR A[原问题回答]--&gt;B[真话]--不变--&gt;C[真话]--不变--&gt;D[假话]--取反--&gt;E[假话]--取反--&gt;F[真话]--不变--&gt;G[假话]--取反--&gt;H[尤泽尔] </pre> <blockquote> <p>问说假话的人:如果我问另一个人【如果我问另一个人〖如果我问你“如果我问另一个人‘如果我问你,哪扇是正确的门,你会回答哪扇门’,他会回答哪扇门”,你会回答哪扇门〗他会回答哪扇门】,他会回答哪扇门?</p> </blockquote> <h3 id="更多的守门人">更多的守门人?</h3> <p>位运算总共有六种,运算对象有两种,由此可以构造出一共八种守门人,请选择你的角色:</p> <table> <thead> <tr> <th>位运算</th> <th>对应的守门人</th> </tr> </thead> <tbody> <tr> <td><code>x &amp; 1</code></td> <td>只说真话</td> </tr> <tr> <td><code>x &amp; 0</code></td> <td>只说否</td> </tr> <tr> <td><code>x | 1</code></td> <td>只说是</td> </tr> <tr> <td><code>x | 0</code></td> <td>只说真话</td> </tr> <tr> <td><code>x ^ 1</code></td> <td>只说假话</td> </tr> <tr> <td><code>x ^ 0</code></td> <td>只说真话</td> </tr> <tr> <td><code>~x</code></td> <td>只说假话</td> </tr> <tr> <td>左右移</td> <td>胡言乱语</td> </tr> </tbody> </table> <h3 id="真的没办法同时问出谁说真话谁说假话吗">真的没办法同时问出谁说真话谁说假话吗?</h3> <p>倘若限定只问二元问题,那确实是没办法的,因为规则限定只能问到一个二元信息,只能表示一个回答。</p> <p>若不限定只问二元问题,那就需要对“说假话”有更进一步的定义了,在特定定义下像这样的问题可能会有效:</p> <blockquote> <p>若你说真话且这是正确的门,请说 1;若你说真话且这是错误的门,请说 2;若你说假话且这是正确的门,请<strong>不要</strong>说 3;若你说假话且这是错误的门,请<strong>不要</strong>说 4。</p> </blockquote> <p>但我更倾向于,“说假话”的定义应该可以解释为:先假设你说真话,得到的答案我们定义为正确答案,则你可以回答除了正确答案以外的任意答案。</p> <p>在这个定义下,上述问题也无效。</p> <p>若答案只有两种,则只能表示一个回答;若答案有两种以上,那么说假话的人,除了正确答案可能会回答任意一种,无法和情况一一对应。也就是说,以最严格的规则是无法同时问出的。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <p>问题经过故事化(这会成为逻辑解构的传统),原问题与原答案来自于某本小说。</p> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2025/7/4
阅读更多

逻辑解构 | 默契硬币

<h2 id="题目">题目</h2> <p>国王亚路开德试图量化一对搭档有多默契,于是他设计了一个默契测试:两位受试者分别在两个相隔遥远的封闭房间内各抛一枚硬币,然后猜测<strong>其搭档的抛硬币的结果</strong>,如果两个人中<strong>至少一个人</strong>猜对搭档的结果,则计一分。进行一百次这样的测试后的总分,就是这对搭档的默契值。</p> <p>然而,数学大臣尤泽尔立刻发现了这个测试的漏洞,他想到了一个可以百分百得分的策略。</p> <p>请问尤泽尔的策略是什么?</p> <blockquote> <p><strong>答案</strong></p> <p>一人始终把自己硬币的结果猜上去,一人始终把自己硬币的相反结果猜上去。</p> <p>在这样的策略下,总会有一人猜对,一人猜错。</p> <p><strong>证明</strong></p> <p>设 A 是那个始终把自己硬币的结果猜上去的人,B 是始终把自己硬币的相反结果猜上去的人。</p> <p>不妨设 A 抛到了正面。根据 A 的策略,A 会猜 B 抛到了正面,此时有两种可能:</p> <ul> <li> <p>如果 A 猜对了,即 B 抛到的是正面,根据 B 的策略,B 会猜 A 抛到了反面,B 猜错。</p> </li> <li> <p>如果 A 猜错了,即 B 抛到的是反面,根据 B 的策略,B 会猜 A 抛到了正面,B 猜对。</p> </li> </ul> <p>所以总会有一人猜对,一人猜对。根据游戏规则,在这样的策略下可以百分百得分。</p> </blockquote> <h2 id="原理">原理</h2> <p>先来看最容易想到的一种错误方式:每个人猜对的概率是 $\frac12$,那么两个人中至少一个人猜对的概率应该是 $\frac34$。如果这是对的,那么不应该存在百分百得分的策略才对。</p> <p>答案的正确性毫无疑问,但其并非来自于灵光一现,下面我们来理解答案的原理。</p> <p>答案是怎么起作用的?上面的想法错在哪儿了?为什么猜对方的硬币,策略却只与自己的结果有关?</p> <p>硬币只有两种结果,所以一共只有 16 种情况,我们可以使用枚举法。这里我列出了所有无法得分的情况,即两个人都猜错的情况:</p> <table> <thead> <tr> <th style="text-align: center">序号</th> <th style="text-align: center">A 结果</th> <th style="text-align: center">A 猜测 B 结果</th> <th style="text-align: center">B 结果</th> <th style="text-align: center">B 猜测 A 结果</th> </tr> </thead> <tbody> <tr> <td style="text-align: center">1</td> <td style="text-align: center">正</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> <td style="text-align: center">反</td> </tr> <tr> <td style="text-align: center">2</td> <td style="text-align: center">反</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> <td style="text-align: center">正</td> </tr> <tr> <td style="text-align: center">3</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> </tr> <tr> <td style="text-align: center">4</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> </tr> </tbody> </table> <p>如果你观察力足够强,可以发现四种情况存在一些共性:</p> <ul> <li>1 和 2 是两个人都把自己硬币的结果猜上去了</li> <li>3 和 4 是两个人都把自己硬币的相反结果猜上去了</li> </ul> <p>现在可以回答第一个问题了:答案的策略完全避开了两个人都猜错的情况,所以就必然得分了。</p> <p>的确,枚举之后就可以瞪出答案。直接枚举情况然后针对特点来设计策略是一种好方法,但可能会有点令人失望。有没有更深一点的原理呢?</p> <p>还可以这样理解答案:”把自己硬币的结果猜上去“有些奇怪,其实意思就是“<strong>猜两个硬币的结果相同</strong>“。同理,另一方实际上在做的是”<strong>猜两个硬币的结果不相同</strong>”。显然,两个硬币的结果要么相同,要么不相同,所以必然一人对一人错,因而必然得分。这么一讲就非常好理解了。</p> <p>要回答第二个问题,我们需要用新的理解去重新看待这些事件。</p> <p>抛两次硬币,结果相同的概率的确是 $\frac12$,也就是这种策略下 A 和 B 猜对的概率确实都是 $\frac12$,但不同的是,这时 A 猜对和 B 猜对是一对<strong>互斥事件</strong>,而非我们先前认为的相互独立。</p> <p>独立与互斥,显然是策略不同导致的。随机猜时确实相互独立,但为什么这个游戏里可以按某种策略然后变成互斥?</p> <p>关键在于,正如上表,这个双人游戏实际上并非简单的猜两次抛硬币,而是<strong>包含了两个信息与两个决策,总共四个事件</strong>的。</p> <p>如果受试者随机等概率地猜测,信息与决策无关,四个事件之间相互独立,所以十六种情况概率相等,于是得分的概率就是 $\frac{12}{16}$。思维盲点在于,四个事件在游戏中并不完全独立,游戏中存在一个很容易忽视、极其隐秘的联系:<strong>猜之前是已知自己硬币的结果的。</strong></p> <p>画成流程图则如下:</p> <pre class="mermaid" style="text-align: center;"> graph TB A[受试者 A]--已知--&gt;B[A 的结果] A-.猜测.-&gt;D C[受试者 B]--已知--&gt;D[B 的结果] C-.猜测.-&gt;B </pre> <p>有已知信息,那么决策时就可以考虑<strong>利用已知信息,从 16 种情况中划去一些情况</strong>。</p> <p>例如,若 A 的策略是只把自己硬币的结果猜上去,那么 16 种情况就会划去 8 种,剩下 8 种情况。而上表 4 种不能得分的情况也被划掉了两种,只剩下上表中序号 1 和 2 的情况。</p> <p>不难想到,假如 B 也有一个利用已知信息的策略能够把 1 和 2 给划掉,同时可以得分的情况没有被划完,那么就可以实现百分百得分了。</p> <p>观察序号 1 和 2 中 B 的结果与已知的关系发现,B 都是把自己硬币的结果猜上去了,不难想到 B 的策略:只把自己硬币的相反结果猜上去。不能得分的情况全被划掉,同时还剩下 4 种可以得分的情况,这样就必然得分了,也就是我们的答案。</p> <p>第三个问题也迎刃而解:因为在猜之前,自己的结果是一个已知信息,既然要有策略,那么当然就只能是由已知信息来决定猜什么。</p> <p>好像划掉情况很容易呀,那还有没有别的答案呢?</p> <p>先来个简单的:A 只猜正,也就是把自己身份也当做一个已知信息来决策。我们把前面不能得分的四种情况再列一下:</p> <table> <thead> <tr> <th style="text-align: center">序号</th> <th style="text-align: center">A 结果</th> <th style="text-align: center">A 猜测 B 结果</th> <th style="text-align: center">B 结果</th> <th style="text-align: center">B 猜测 A 结果</th> </tr> </thead> <tbody> <tr> <td style="text-align: center">1</td> <td style="text-align: center">正</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> <td style="text-align: center">反</td> </tr> <tr> <td style="text-align: center">2</td> <td style="text-align: center">反</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> <td style="text-align: center">正</td> </tr> <tr> <td style="text-align: center">3</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> </tr> <tr> <td style="text-align: center">4</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> </tr> </tbody> </table> <p>A 只猜正时,总情况数少一半,上表中四种情况只剩下 1 和 4。那么此时 B 能否找到对应的策略呢?答案是不能,因为情况 1 和 4 中,B 自己硬币结果都是反,但 B 猜测结果有正有反,两者找不到关联,没办法形成<strong>从已知推决策</strong>的有效策略,他只能知道自己抛到正面就稳了,但是抛到反面的话,B 没办法依据已知信息来决策自己猜正还是猜反,不能划掉这两种情况,因此不能必胜。A 只猜反也同理。</p> <p>从这里我们可以发现,上表四种情况我们要么剩 1 和 2,要么剩 3 和 4,否则会出现 B 自己的结果与 B 的猜测结果找不到关联,那么 B 决策时就利用不了已知信息,那就变回了独立的情况了。</p> <p>因此,没有别的答案,只能是答案的策略或其等价形式。</p> <h2 id="思考">思考</h2> <h3 id="这还是抛硬币吗">这还是抛硬币吗?</h3> <p>答案的策略,实际上将问题转化为了猜两个人抛硬币结果相不相同,为什么可以这样转化呢?两者之间有什么隐含的联系?</p> <p>我们来看看这两个游戏分别都发生了什么。</p> <p>采取答案的策略后,只剩下四种可能情况,我们一一列出:</p> <table> <thead> <tr> <th style="text-align: center">序号</th> <th style="text-align: center">A 结果</th> <th style="text-align: center">A 猜测 B 结果</th> <th style="text-align: center">B 结果</th> <th style="text-align: center">B 猜测 A 结果</th> <th style="text-align: center">结果</th> </tr> </thead> <tbody> <tr> <td style="text-align: center">1</td> <td style="text-align: center">正</td> <td style="text-align: center">正</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> <td style="text-align: center">A 猜对</td> </tr> <tr> <td style="text-align: center">2</td> <td style="text-align: center">正</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> <td style="text-align: center">B 猜对</td> </tr> <tr> <td style="text-align: center">3</td> <td style="text-align: center">反</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> <td style="text-align: center">B 猜对</td> </tr> <tr> <td style="text-align: center">4</td> <td style="text-align: center">反</td> <td style="text-align: center">反</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> <td style="text-align: center">A 猜对</td> </tr> </tbody> </table> <p>因为 A 与 B 的猜测来自于 A 与 B 的结果,是确定的,因此我们不妨略去这两列,即:</p> <table> <thead> <tr> <th style="text-align: center">序号</th> <th style="text-align: center">A 结果</th> <th style="text-align: center">B 结果</th> <th style="text-align: center">结果</th> </tr> </thead> <tbody> <tr> <td style="text-align: center">1</td> <td style="text-align: center">正</td> <td style="text-align: center">正</td> <td style="text-align: center">A 猜对</td> </tr> <tr> <td style="text-align: center">2</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> <td style="text-align: center">B 猜对</td> </tr> <tr> <td style="text-align: center">3</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> <td style="text-align: center">B 猜对</td> </tr> <tr> <td style="text-align: center">4</td> <td style="text-align: center">反</td> <td style="text-align: center">反</td> <td style="text-align: center">A 猜对</td> </tr> </tbody> </table> <p>抛两次硬币也是四种可能情况,我们列出:</p> <table> <thead> <tr> <th style="text-align: center">序号</th> <th style="text-align: center">第一个硬币</th> <th style="text-align: center">第二个硬币</th> <th style="text-align: center">结果</th> </tr> </thead> <tbody> <tr> <td style="text-align: center">1</td> <td style="text-align: center">正</td> <td style="text-align: center">正</td> <td style="text-align: center">相同</td> </tr> <tr> <td style="text-align: center">2</td> <td style="text-align: center">正</td> <td style="text-align: center">反</td> <td style="text-align: center">不同</td> </tr> <tr> <td style="text-align: center">3</td> <td style="text-align: center">反</td> <td style="text-align: center">正</td> <td style="text-align: center">不同</td> </tr> <tr> <td style="text-align: center">4</td> <td style="text-align: center">反</td> <td style="text-align: center">反</td> <td style="text-align: center">相同</td> </tr> </tbody> </table> <p>很容易发现,之所以能这么转化,是因为答案的策略划掉了十二种情况,剩下的四种情况与抛两次硬币的情况相同,因而我们可以这么转化地去解释,但两个问题并没有本质上的关联。</p> <h3 id="那么我们能否设计出类似的游戏呢">那么,我们能否设计出类似的游戏呢?</h3> <p>实际上只要是类似于这个流程图的,都是一样的策略。</p> <pre class="mermaid" style="text-align: center;"> graph TB A[A]--已知--&gt;B[二元信息 1] A-.猜测.-&gt;D C[B]--已知--&gt;D[二元信息 2] C-.猜测.-&gt;B </pre> <p>例如,两个相隔遥远的人,互猜对方的性别。</p> <h3 id="有没有什么数学原理呢">有没有什么数学原理呢?</h3> <p>我们不妨转换为布尔值(硬币正面为 0,反面为 1),列出 A 与 B 策略的真值表。</p> <p>A 的策略:</p> <table> <thead> <tr> <th style="text-align: center">已知</th> <th style="text-align: center">猜测</th> </tr> </thead> <tbody> <tr> <td style="text-align: center">0</td> <td style="text-align: center">0</td> </tr> <tr> <td style="text-align: center">1</td> <td style="text-align: center">1</td> </tr> </tbody> </table> <p>B 的策略:</p> <table> <thead> <tr> <th style="text-align: center">已知</th> <th style="text-align: center">猜测</th> </tr> </thead> <tbody> <tr> <td style="text-align: center">0</td> <td style="text-align: center">1</td> </tr> <tr> <td style="text-align: center">1</td> <td style="text-align: center">0</td> </tr> </tbody> </table> <p>直接说 A 是相同 B 是相反好像并没有发现什么新的原理,因为策略本来就是这么描述的。所以我们往其他方向考虑:奇偶性?</p> <p>A 的已知与猜测的和是 0 或 2,都是偶数;而 B 的已知与猜测的和是 1,是奇数。因此我们可以这么描述策略:A 始终猜两个硬币的和是偶数,B 始终猜两个硬币的和是奇数。由于两个硬币的和要么是偶数要么是奇数,自然总有一个人对。</p> <p>上面的思路看起来很有空间,因为奇偶性其实就是进行模二,而<strong>模 $n$ 运算具有天然的 $n$ 分性。</strong></p> <h3 id="改成模三是不是可以拓展到三个呢">改成模三是不是可以拓展到三个呢?</h3> <p>当然是可以的。</p> <pre class="mermaid" style="text-align: center;"> flowchart TB subgraph 决策者 direction LR A[A] B[B] C[C] end subgraph 信息 direction LR D[三元信息 1] E[三元信息 2] F[三元信息 3] end A-.猜测.-&gt;D A--已知--&gt;E A--已知--&gt;F B-.猜测.-&gt;E B--已知--&gt;D B--已知--&gt;F C-.猜测.-&gt;F C--已知--&gt;D C--已知--&gt;E </pre> <p>流程图表示出来较为复杂,设计起来也是。这里有 $3^6$ 种情况,采用决策过后有 $81$ 种情况,只需让每个人占有其中 $27$ 种情况作为胜利即可。</p> <p>我们依据这个图设计一个最简单的、没有玩什么文字游戏、没有隐藏信息、没有加太多限制的游戏:</p> <blockquote> <p>三个人各戴一顶红色或黄色或蓝色的帽子(颜色可以重复),每个人只看得到别人帽子的颜色,看不到自己的,不能进行任何形式的交流,若三个人中有人猜对自己帽子的颜色即胜利。</p> </blockquote> <p>类比上述数学原理,我们设红为 0,黄为 1,蓝为 2,设三个帽子的和为 $s$。</p> <p>由于 $s\bmod 3$ 的必然是 0 或 1 或 2,我们容易想到以下策略:</p> <ul> <li>A 始终猜 $s\bmod 3\equiv 0$</li> <li>B 始终猜 $s \bmod 3 \equiv 1$</li> <li>C 始终猜 $s \bmod 3 \equiv 2$</li> </ul> <p>这样就必然有一个人是猜对的。同理,可以推广至更多人的情况。</p> <p>当然这个游戏是我现场设计的,不要纠结里面的漏洞。</p> <h3 id="套娃数学原理的数学原理">套娃:数学原理的数学原理</h3> <p>如果你深究上面的策略究竟是不是对的,一定会注意到策略成立的一个前提:ABC 这样的“猜”等价于某一种符合游戏规则的策略,即,<strong>这样的策略在游戏中能对应唯一确定的颜色。</strong></p> <p>我们可以直接列。由于另外两个帽子的和模三一定是 $0,1,2$ 之中一个,且自己的帽子也只能是 $0,1,2$ 之中一个,所以:</p> <ul> <li>若 $s \bmod 3\equiv 0$,则 $s$ 来自于 $0+0$ 或 $1+2$ 或 $2+1$</li> <li>若 $s \bmod 3\equiv 1$,则 $s$ 来自于 $0+1$ 或 $1+0$ 或 $2+2$</li> <li>若 $s \bmod 3\equiv 2$,则 $s$ 来自于 $0+2$ 或 $1+1$ 或 $2+0$</li> </ul> <p>例如,看到的是一个红帽子和一个黄帽子(总和为 $1$),并且我猜 $s\bmod3\equiv0$,那么可以算出自己是 $2$,即蓝帽子。</p> <p>实际上,这就是一个模运算的填空题:$1+\square\equiv0\pmod 3$。</p> <p>在 $\square\in{0,1,2}$ 的情况下,类似这样的填空题中 $\square$ 是有唯一解的。也就是说,在已知 $s$ 并已知另外两个帽子的和时,确实可以唯一确定自己的颜色出来,该策略有效。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <p>本题经过了我的美化(当然,这会成为逻辑解构系列的传统),第一道思考题来自 <a class="link" href="https://www.bilibili.com/video/BV17p4y1r7Qs" target="_blank" rel="noopener" >GM 的视频</a>。</p> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2025/6/30
阅读更多

拾光纪行 | USTC 强基计划游记

<h2 id="前言">前言</h2> <p>距离高考大约一个多月的时候,开始报名强基计划。</p> <p>综合各学校、各专业以及我的倾向,最终选定了 USTC。我对 USTC 最初的好感来自于很帅的名字,在我眼里仅次于 THU;其次是在人工智能我喜欢的方向上 USTC 有先进的研究;最后是听说 USTC 的学生浓度都…比较有生活(</p> <p>虽然对我来说难度很高,能接受的专业也仅有一个,但抱着“文化考得上的大学不值得我读十年”以及出于中大的保底情况,所以还是选择了这一所我好感度很高、可以说比较向往的学校,尽管是冲全广东只招一个人的专业且不接受调剂。</p> <p>尽管合肥目前给我的印象其实不是很好,相比深圳确实没有那么舒适和现代化,但还是选择相信 USTC。</p> <p>实际上现在并不知道未来会如何,落榜的可能性远大于录取,并且在一个相对没那么喜欢的城市待十年对我来说也很难直接接受。USTC 的强基会锁档,出分前完成所有流程,然而我只要在笔试、面试、高考之中任意一个爆炸就会直接离场。</p> <p>我尽力去尝试,不管结果如何,我相信以我自己的能力,大学过后都能走到我应该的结局。</p> <h2 id="笔试">笔试</h2> <p>2025 年的笔试是在 6 月 15 日,我 14 日上午从深圳宝安机场出发飞抵合肥。</p> <p>下午去东校区踩点,不知道东校区是不是本校区,但是这个大门感觉还是比较…低调。但附近配套感觉除了“科气”十足以外并不称得上舒适,可能比起出校门,选择点外卖会更科学一点。</p> <p>踩完点之后去附近的南七天地逛了一下,以为是很大的超商,可能是我去的时间不对,去的时候人很少而且很多很多店铺都没开,感觉还是有些失望。什么吃的也没买到就出去了,在旁边另一个商场逛了一下发现不卖吃的,只好打道回府。顺带一提,那里的地下通道是真的有些…不合格。</p> <p>最后发现反而走到酒店附近才找到吸引我进去吃的店,后来我才注意到原来我酒店那条道叫做美食街。</p> <p>晚上继续复习,复习的主要是物理,因为以为会考竞赛知识点,所以这三天临时补了很多。然后看了一下物理数学近两年的真题找找感觉,就结束复习了。可能是因为我已经考过一次 SUSTech 的机试(其实被我当 USTC 笔试的模拟赛打了)</p> <p>第二天一早就起来,然后提前了一些出发,也提前了一些到考场。什么,人怎么这么多。</p> <p>进考场之后,监考老师提醒我们 17 号查成绩,21 号面试,注意消息。等了半个小时后就开考了,大致流程应该在尽量模仿高考,但不时露馅。</p> <p>先考 100 分钟数学,提前七八分钟拿到卷子开始浏览题目,有十道填空和两道大题。我从第一道题开始看,看了一会有思路了就往下过,一直过到了第七八题才打开考铃,也就是说还没打铃我就已经会做七八道题了,芜湖起飞。</p> <p>考前想的思路大部分都是对的,最终都得到了答案,虽然有些题我不记得我的答案是多少,后续对答案也对不了,但我觉得应该是没问题。不会的题有两道,一道是球盒的期望问题,可以写出式子但化简时无从下手,最后蒙了答案但是错了;另一道是奇怪数列吧好像,有 sigma 和一个一九九几年的数字的题,一开始的思路错了,后面发现不会做于是等到考试快结束的时候感觉回来列了前三个找规律,但因为时间不够,第三个没算出来,又忘了第一个,所以找了个很错的规律,打铃之后才发现存在规律,当时很遗憾,后面发现那个规律也只有前三个是对的,释怀了,不会就是不会。</p> <p>值得一提的是我很喜欢斐波那契数列那道题,虽然被学 OI 出身的我读完题后就感觉到了很浓的递推味然后秒了,但那道题的设题方式确实很新颖很有创意。</p> <p>大题的话,第一道大题是复数大题,最后应该会变成数论,我数论都是从 OI 学的,做不来,跳。第二道大题发现讨论就可以了,咔咔写,但是脑子有点点转不动所以写的比较慢,挺久才写完,在这里应该浪费了不少时间。</p> <p>10 分钟休息过后又是 100 分钟的物理,我不想评价,没考太多竞赛但一点也不简单,而且还有多选题,可恶。我感觉我最有把握的题是多选最后一道,虽然那道题题面有误,能量标错了,但是不影响答案。其他的题基本连蒙带猜,大题也根本写不动,把能理解的写了就结束了。</p> <p>后来听说物理好像不答不加也不扣分,但答错还要倒扣一分,我当时没注意看。我只一直盯着多选题里面那个“可能有多个答案“的”可能“然后在想他会不会在多选题里出单选题。</p> <p>考完之后有一种解放感,去找女朋友玩了好久。17 号从早上开始时不时刷新,最终是晚上在女朋友家刚吃完饭已刷新看到我入围面试,蛮开心的。</p> <p>数学 70 分,满分 100 的这个难度能考到这个分数我还是相当满意了,也就是说我写的大部分都是对的。物理 34,意料之中。笔试总分 104,入围线 70,那我岂不是不用考物理也能入围。</p> <p>毕竟我学过 MO 和 IO 但没学过 PhiO,所以我觉得这样的分数还是意料之中的。不知道这个入围线能不能参考,但按照往年的分数线,其实感觉 104 分竞争力不是很大,也同样是淘汰边缘了感觉。</p> <p>不过我听说我的竞赛奖在面试环节可以加分,希望如此。好好准备面试,等到出分后就知道结局啦!</p> <p>话说看知乎,之前强基有礼物,今年怎么没有?是要等到面试才有嘛,期待一波。</p> <h2 id="面试">面试</h2> <p>没有礼物,可恶,为什么今年没有,为什么为什么为什么为什么。</p> <p>21 号上午面试,我 20 号上午面试完 SUSTech 直接前往机场飞到合肥。</p> <p>上午应该都是第一类考生,大概有两个考场,每个考场约 108 个人,分为三轮,每轮三组,每组 12 个人。</p> <p>大概面试流程:12 个人分为甲乙两组,先五分钟阅读和讨论材料 A 及问题,然后 1-6 号轮流发言,接着 12-7 号轮流对前面同学的发言提出问题,发言都是两分钟,然后再给五分钟阅读和讨论材料 B 及问题,然后 7-12 号轮流发言,6-1 号轮流提出问题,也都是两分钟。</p> <p>材料 A 与 B 印在同一张纸的正反两面,另给了两张白纸可供记录,需自己带笔。不能提及自己身份和高中,所以当然不需要自我介绍。</p> <p>今年我的面试真题两则都是科大今年的创新成果,材料 A 是可用于抓握复杂或微小物体的对数螺旋软体触手,材料 B 是利用谐波衰减速度不同以及拍频效应来减少纳米波雷达的心跳谐波监测受到呼吸谐波的干扰,问题大致都是些有关专业、进行联想、推理原因、探索应用之类的。</p> <p>个人认为提前押材料并没有意义,因为考场给出的材料基本都有具体的点,还是要依据具体问题和材料原文去回答,像我这次材料 A 早在很久之前在 b 站发布视频的时候我就有看过,但个人感觉除了稳定信心以外没有实际帮助。</p> <p>值得注意的是,如果你是提出问题的一方,有必要认真先前发言的同学的内容(包括对方和己方),这样你才有针对性的回答,或者在合适的时候提到哪个同学发言的哪个内容,告诉老师你认真听别人发言了;如果你是回答问题的一方,一定要在讨论阶段和大家分配好每个人的角色,比如四个问题六个人怎么分配,可能一个问题里有两个小问要不要拆开来回答,或者需不需要一个人负责补充或总结,最好是在准备阶段简单记录下自己的发言要点,然后也可以记录一下前面同学的发言,听发言的时候也可以思考一下别人的问题,在自己发言的时候可以提出补充。</p> <p>个人觉得,发言的时候客套话(老师好同学好谢谢之类的)适量就好,但是重要的是发言流畅,有逻辑性。可以说慢一点,但是嘴巴最好不要停,脑子暂时跟不上的时候就随便塞一些有逻辑的空话进去(考虑到技术可行性、考虑到实际情况、根据前面同学发言、根据材料之类的),另外说话应该谦虚友善一些,毕竟这不是辩论只是小组讨论,应该使用“我认同对方同学的观点,但我觉得还可以考虑”、”对方同学的角度很好,我可以补充“、”我认为/我觉得/我的看法是“、”对方同学的考虑有一定合理性,但我有别的看法“、”对方同学的看法可能…,我们不妨…“之类的语言,要避免使用强烈的否定甚至攻击对方。<strong>一定要知道不是在辩论,只是小组讨论</strong>,让你提出问题不是和对方辩论,对方发言没问题或者很好的部分应该要认同对方观点甚至可以帮对方补充。</p> <p>理论上非金银牌的竞赛获奖的加分会在面试分数中有所体现,但这只是理论。面试不会提前出分,会在高考出分后直接与综合成绩一并公布,并在高考出分几天后公布各省各专业录取线以及可以录取情况查询方法。有一说一,强基招那么少人,应该基本每个省都是每个专业只招一个人,这个公布录取线基本就是公布录取的人的综合分了吧。</p> <p>这个综合分其实很不好估,往年录取分数线参考意义也不大,因为综合分的三个部分中面试成绩你不知道,而笔试和高考每年难度都不同,尽管相对排名可能不变,但在综合分中的参与得分情况就会不同,所以具体分数线无法比较。</p> <p>个人感觉发挥的还不错。笔试+面试的两分就顶高考一分了,希望我的省一可以给面试多加点分,这样可以补一补笔试和高考的漏分,别白学三年竞赛。</p> <p>虽然其实录取概率极小,但不管结果怎么样我都已经尽力了,rp++ 吧。</p> <h2 id="出分">出分</h2> <p>高考分实在有些炸裂,无缘 USTC 了。</p> <h2 id="结果">结果</h2> <p>未录取,但是也不算太遗憾吧。除了这次没有礼物。</p> <p>面试分 55.5。</p> <p>综合分相差的有点多,约等于三十分高考,或是六十分校测。</p> <p>就算我发挥好了也考不到这么高,这么看来爆炸的其实不只有高考,基本是全炸,所以也就不遗憾了。虽然是三年以来很喜欢的学校,但倒也称不上梦想吧,无非就是好感比 SYSU 高一点所以挣扎了一下罢了。</p> <p>过段时间还会写 SYSU 综评游记。</p> <p>顺带一提,填这个志愿是真的烦。</p> <h2 id="附录">附录</h2> <p>依稀记得报考 USTC 羟基前看了一篇往年学长的知乎文章,与我的结局十分类似。</p> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2025/6/18
阅读更多

随笔杂谈 | 2025 新春快乐

<p>依旧是不知不觉间,博客已经过去了千天的四分之三。在去年里博客总共更新了十七篇文章,收获了许许多多的互动,还是相当满意的。</p> <p>2025 年,予我而言是人生迄今为止最为重要的一年,我也正在努力着,期待今年六月的结果如何。</p> <p>每个人的一年里真的会发生很多很多事情,时间滋润着成长,不管是我还是这篇博客,亦或者我在元旦之日种下的六株向日葵。</p> <p>但无论如何,祝各位新的一年巳巳如意、生活美满;多喜乐,长安宁。</p> <blockquote class="quote"> <p>健康、幸福、进步。</p></blockquote>

2025/1/28
阅读更多

拾光纪行 | NOIP 2024 游记

<h2 id="游记">游记</h2> <p>作为一个高三半 AFO 选手,这次 NOIP 意味着彻底的 AFO 和全面的 whk。</p> <h3 id="赛前">赛前</h3> <h4 id="day--3">day -3</h4> <p>中午开始停课。</p> <p>教练安排我和另外一位同学在明后两天组两场模拟赛,我的题倒是提前组好了,不过感觉 A 题放了绿题会难过头,所以临时换了一题黄题。(然而 NOIP A 题蓝,哈哈)</p> <p>下午帮同学证明代码正确性,然后在讨论区看到野题拉同学想了两小时,结果那个帖子被删掉了。后面在讨论区看到了你画我猜,第一次见到信息竞赛词库,挺好奇的,进去看了一下,结果一不小心玩了快两个小时,哈哈,太好玩了。</p> <p>顺便也靠这个重新回忆起了许多算法,原来我三年来已经学了或者了解了这么多,十分感慨。</p> <p>晚上在机房组了一场模板手速赛,基本十分顺利的写出来了。除了树状数组不小心把 $m$ 打成 $n$ 以及第一次写 $O(n)$ 建树,还有字典树的映射写挂,其他模板都一次过了。不过手速不够所以一些图论高级算法的模板没打到。</p> <h4 id="day--2">day -2</h4> <p>上午验了模拟赛的 C,中午开始考。然后经典赛时开始造大样例,以及赛时才验 D(本来几乎一次过的,结果因为不小心把 min 写成 max 调了好久)。</p> <p>晚上给机房讲题,讲到喉咙冒烟。组比赛真的比打比赛累多了。</p> <h4 id="day--1">day -1</h4> <p>上午有点摸鱼,下午打同学组的模拟赛。A 因为差点没开 long long 差点见祖宗,B 是我推荐给他的题所以原了,C 是缩点板子所以二次原,D 是万恶的小木棍。</p> <p>通过洛谷帖子保存站,发现前天那个帖子删之前有人回答了,并且做法大致和我想得一样。然后找到了那个你画我猜的帖子,但房间已经没了所以自己创建了一个。</p> <p>和机房同学试了几个词库之后回到了 OI 词库,然后人一下子变多了起来,好玩。后面有点累了,想着等再轮到我画一次就退。结果中间有个人画了矩阵快速幂,所有人都答出来了只剩我,然后我猜了各种“矩阵优化dp”“矩阵加速递推”直到有人提示我“矩阵什么也没优化”才压着时间线答出了正确答案,他们甚至发起投票踢我,然而我是房主,乐。<strong>(伏笔)</strong></p> <p>晚上对着题解把小木棍调出来了,并且成功快速理解了那两个优化。后续接着打图论模板,除了欧拉路径有点模糊以外都很顺利的打出来了。</p> <p>放学前半小时在机房里扬言要挑战 15min 敲树剖板子,结果因为太久没打所以手疏导致实际用了一倍的时常。打完之后成功过了样例,提交发现最后一个点没过。可恶,差点一次性过掉了树剖。</p> <p>后面看讨论区才知道最后一个点是 hack 没对输入取模的,长记性了。</p> <h4 id="day-0">day 0</h4> <p>顺利起床,吃完早餐后坐校车前往考点。</p> <p>在考场门外面基了某位神犇数秒,大致流程是:突然被手机贴脸-&gt;递东西-&gt;消失。</p> <p>进考场,发现挡板不太行,能够清楚看到斜对角两位选手的屏幕,但看得出来主办方已经尽力遮挡了。</p> <p>我们学校和另一强校交替着坐,感觉压力很大。而且复赛时候的咬笔哥这次来到了我的左侧,并且我依旧不知道他是否有第二支笔。</p> <p>签字,话说今年居然不用填“我选择在 Windows 系统”,以及经典提前三分钟发密码提前三分钟收卷,到底有什么必要嘞,还导致我们开始前三分钟不能建文件夹。</p> <p>看到这解压密码,感觉有点东西。莫名有点应景(指 AFO 前最后一战)</p> <h3 id="赛时">赛时</h3> <p>开 A,感觉有点难办。来来回回只能往贪心上想,去年的这个时候已经写完 A 了。</p> <p>记得最开始写了一版贪心,但是后面发现假了,但是改一改就真了。一个半小时才敲完了,是现在主流的统计区间个数然后双指针的做法,我以为很对,结果大样例第 7 个和最后一个过不去,莫名其妙多了一点。</p> <p>对着代码找不出错,想过写对拍但发现我不会暴力做法,于是只好硬着头皮去看大样例,但好在 7 是特殊性质,比较简单。</p> <p>分析了 7 后手动模拟发现手算出来的就是那个错误的答案,很绝望,这可能说明我贪心假了,此时已经两个小时了,去年这时候我马上就要调出来 B 了。</p> <p>想着至少能过好多个点,先去看 B 了。读完 B 感觉,完了,B 也不会,甚至不知道读没读懂。一开始以为是推出式子快速幂算就好,仔细读题之后分析了一下发现不是简单的相乘,要分情况递推。</p> <p>因为感觉最后肯定是想办法压缩不确定的一段的方案计算,于是就先对 $n$ 写了个 dp。</p> <p>可能是由于昨天你画我猜里矩阵快速幂给我印象很深刻,写完之后定睛一看,发现这状态转移式子长得很递推啊我啪的一下就改成了矩阵。卧槽那这不快速幂秒了吗,喜滋滋写完之后稍微一挑就过掉了大样例,芜湖。</p> <p>还剩一个半小时,打算把后面两题暴力和特殊性质拼了再回去调 A。</p> <p>C 题想了一会写出来一个式子,感觉是换根 dp 但我不太会,所以想着写个树形 dp 的暴力,结果写了四十分钟发现假了,只写了个链,然后菊花图的式子也假了,绷。</p> <p>D 题很优雅,但是不会,看特殊性质火速打了链,但是是 $O(qn^2)$ 的暴力取 min 和 max,哈哈,炸裂。而且大样例不知道为什么会卡死,运行不了。</p> <p>最后二十分钟调 A,然后这时候我终于想出来了错在没有考虑相交区间长度的限制,但是我以为这里要做决策来决定怎么分配 0 和 1 的比例,所以以为贪心假了。但是十来分钟也改不了什么了,所以写了个数学里的分层抽样一样按比例的神秘决策,结果过掉了大样例。</p> <p>最后十分钟检查代码的,找到了 D 应该是导致卡死的原因,改了编译了但没测样例。最后三分钟想把 A 的 memset 改成 for 来初始化,最后勉强卡着时间改完了,能过编但是也没测。</p> <p>出考场之后才回想起来 $T\le 10$,memset 根本不慢,后悔改了。然后这时候悟到了提前收卷的意义,因为这样哪怕结束后才保存的话也不会判时间无效。</p> <h3 id="赛后">赛后</h3> <h4 id="出考场后">出考场后</h4> <p>考完后听到很多人说很高分,压力。在考场外地板上发现了野生零食,乐。车上吃了好多零食,得知机房其他人考的也很炸裂,悲。</p> <p>晚上过掉了 B 题的自测,舒服了。</p> <h4 id="次日">次日</h4> <p>后面证出来了 A 的那个决策不重要,不影响答案,怎么选都行,于是乎就连最神秘的我的按比例选也能过掉,哈哈。</p> <p>中午过掉了 A 的自测,舒服了。C 的链原来只有 4 分,好吧,合理。D 的暴力仔细一想好像其实估计没有分,悲。</p> <h4 id="退役很久之后">退役很久之后</h4> <p>最终成绩 $100+100+4+0=204\text{pts}$。</p> <p>实际上在上面的内容是去年十二月初写在洛谷的,但时隔很久(次年年初)才发布。不过并不是因为不接受成绩或者 AFO 伤感之类的,只是单纯没有机会转移到博客上来。</p> <p>“没有机会”指的不是没有时间,尽管我确实更多忙于文化课。也不是懒,好吧感觉确实更贴近懒。</p> <p>学习 OI 三年,不长不短,按理说 AFO 之后应该需要写一篇 OI 生涯总结,也许不久就会开始写吧,虽然已经 AFO 有点久了。甚至现在的时间点,比起 AFO,可能离我高考后回归 OI 的日子还要更近。</p> <p>虽然 AFO 且时隔久远,还是多多少少复盘一下。首先这次最大的失利就是我在第一题上花的时间不太合理,并且我认为其中一个细节其实是很难想清楚的,导致我考场上不能肯定我的贪心是错的。我总觉得不太签到,但毕竟竞赛不是小孩玩的游戏,有选拔的目的在的。然后第二题能够写出来,真的很有运气的因素(详见 <a class="link" href="#day--1" >day -1</a> 的伏笔),否则我真的没有信心能够在考场上想出正确的做法。第三第四题一如既往地不善于写部分分导致错失一等,不过大学比赛就不需要写部分分了,哈哈。</p> <p>虽然认为没有以一等作收尾有些遗憾,但是也完全能接受。三年 OI 最终至少给我带来了相当于保送中大计算机系的机会,所以也算是减少了一些心理上的压力。尽管如此,我也还想尝试一下靠文化课能否达到更高的高度,所以仍然在加油的路上。</p> <p>尚有星光伴前路,心向远方更前行,以此作结。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/12/1
阅读更多

拾光纪行 | CSP-S 2024 复赛游记

<h2 id="day--1">day -1</h2> <p>退役前的最后一场 CSP-S。听说 kkk 今年可能不会提前公开选手代码,坏。</p> <p>白天忙着组了一场模拟赛,晚上自己复习知识点。</p> <p>重新理解了一下三种联通分量,然后熬夜到了两点。</p> <h2 id="day-0">day 0</h2> <h3 id="上午">上午</h3> <p>组模板手速赛但是只有我打,大部分都打的很顺利。</p> <p>LCA 写挂了,发现是忘记初始化 $dep_s=1$。之前也在这里挂过,当时没理解原因,这次好好想了一下发现是因为 $dep_0=0$,如果 $s$ 深度不设成 $1$ 的话就和 $0$ 点区分不开,跳的时候会跳错到 $0$。</p> <p>线段树 2 写挂了,以为是懒标记的问题,调了半天,最后发现是把 <code>add()</code> 复制出来改成 <code>mult()</code> 时忘记修改函数内递归的函数名了,我说怎么退化成区间加了。其他部分都没问题,很好。</p> <p>KMP 几乎不记得了,坏。重新看代码理解了一下,发现很好理解。</p> <h3 id="中午">中午</h3> <p>出发去考场。路上重新复习了一下三种联通分量和对应的 Tarjan 算法,看了一眼 Nim 游戏结论。最后就在复习数位 dp,然而直到进入考场都没完全理解。</p> <p>由于准考证和行李之类的问题在考点大门和考场来回跑。</p> <p>今年还是不让带零食。还有五分钟时进了考场。</p> <p>位置还是在角落,斜对角就是一位特别强的学弟。</p> <h3 id="赛时">赛时</h3> <p>提前三分钟发卷,压缩包和 pdf 密码都一次性打对了,rp++。</p> <p>敲了代码缺省,开 T1。前五分钟想复杂了,思路有些乱,想上数据结构。重新读题发现就这,很快就会了排序贪心的做法,决定先过掉 A 再开后面题。开写的时候注意到值域可以直接桶排,14:41 的时候过了所有样例。</p> <p>开 T2,怎么是物理?发现超速区间很好推,显然转化成区间覆盖问题。第一问可以离线下来用前缀和来求区间内是否有摄像头,线性。第二问读完就想到了种树,给我一种按端点排序贪心很对的感觉,但是不太敢写。</p> <p>想了二十分钟,第二问还是不知道怎么做,遂跳至 T3。读完题很有思路,马上就想到了 $dp_i$ 可以分成 $dp_{lst_i}$ 和 $[lst_i+1,i-1]$,后者用前缀和很可以维护,但唯独 $lst_i+1$ 这个点的贡献到处飞,很难搞。</p> <p>想了一会不会搞这个点,但是觉得只要搞出来就可以 dp,开 T4 一眼不会,遂回到 T2。</p> <p>虽然一开始贪心思路记反了,按左端点排序但是取了最右,过掉了小样例,遂开写,发现过不了第二个样例。</p> <p>对样例调了一下发现贪心错了,想了一会想不到别的贪心,但发现如果按右端点排序两个样例都能过,遂改。改完之后过掉了所有样例,但发现 T 了。</p> <p>于是优化掉了 vector,然后其他地方卡常了一下,同时确认了清空和边界之类的细节,大样例 1.7s 过了。此时 16:43,也就是花了两个小时。</p> <p>回到 T3,然后思考了很久 $lst_i+1$ 这个点要怎么搞,甚至想过退化成记搜或者上个数据结构,想了大概半小时后灵光一闪发现了 $dp_i$ 和 $dp_{lst_i+1}$ 的结构是一样的,给状态加个不影响的条件就可以直接从 $dp_{lst_i+1}$ 转移了。</p> <p>这样的话就很好写了,码码码,发现第二个样例没过,对着调了一会发现对 $dp_{i-1}$ 取 max 就能过,虽然当时不理解要如何解释,不过写上之后过掉了所有样例,此时 17:40,剩余不到 1h。</p> <p>后面就很坐牢了,完全把题读懂花了一些时间,甚至不会朴素暴力,只会一个 $O(n^2logn)$ 的树上大模拟,加点时从叶子向上 vector 合并。感觉应该有很多分,遂开写。由于建树的时候写了类似动态开点线段树的写法,所以被如何计算是第几场比赛难住了好久。</p> <p>最后五分钟的时候放弃了,无奈 <code>puts(&quot;1&quot;)</code>。最后两分钟检查了所有文操、注释、函数返回值(高一省选的时候就因为把 <code>void</code> 打成了 <code>int</code> 又没有返回值导致那题 CE 100-&gt;0)。</p> <p>最后开了 checker 确认了一遍文件名和准考证号,铃响结束。</p> <h3 id="赛后">赛后</h3> <p>本次比赛遇到三位莫名其妙像熟人一样跟我搭话的,以及对面的大哥全程嘴里叼着笔,也许他不止一只,以及右边的大哥总感觉经常瞅我进度,也许被我压力到了(</p> <p>过程中经常观察学弟的表情,看起来很轻松,以为被单调队列了。</p> <p>出去后吃了点零食重启一下脑子,回去路上晕车但是刷洛谷讨论区。貌似机房其他人没有会 C 的,希望他们也能 1= 吧,rp++。</p> <p>发现全机房只有我一个人考完就返校,其他人都回去睡觉了,愤愤。</p> <p>先回机房吃了点面包和酸奶垫垫肚子,发现自测快开了,然后开始默写代码和写题解。默写的过程中发现我 T2 虽然已经很小心但还是有地方没处理好边界,最后效果相当于默认 $0$ 处有摄像头,一旦有车在 $0$ 位置超速而 $0$ 位置没有摄像头,答案就错了,寄。</p> <p>当晚自测结果前三题都没挂,甚至第四题出民间数据之前在洛谷上还排到了 rk3,激动。</p> <h2 id="后日谈">后日谈</h2> <p>后续在多个平台自测均为 $300\text{pts}$,第二题也过掉除了我以外的所有 hack 数据。也许是因为卡掉我的情况算非常 corner case,所以非刻意构造的话很难出现,希望 CCF 也不要卡我。</p> <p>中午敲了 $400$ 下电子木鱼,结果 CCF 连续诈骗两次,然后下午一赶到机房就听到了同学的祝贺。</p> <p>$100+100+100+0=300\text{pts}$,达成三年 CSP-S 都没有挂分的成就。</p> <p>虽然省排有些烂,但分数还不错,第四题还是黑题,还是很满意了。</p> <p>希望 NOIP 可以继续发挥好,rp++。</p> <p>update:今年 GD 高中生分数线居然要 $100\text{pts}$,并且刚好 $100\text{pts}$ 的还要求初赛 $\ge 61\text{pts}$,放以前想都不敢想。虽然我的初复赛都超线很多,但是机房同学好多卡线然后因为初赛被筛掉退役了的,好可惜。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/11/9
阅读更多

建站技术 | 我的链接页面是如何实现的?

<h2 id="前言">前言</h2> <p>近期读了一位大佬的文章:<a class="link" href="https://hin.cool/posts/aboutfriendlink.html" target="_blank" rel="noopener" >谈谈独立博客友链 | 瓦解的生活记事</a>,颇有感悟。</p> <p>随即大改友链页面,以实现收藏和推荐的作用,你也可以访问 <a class="link" href="https://blog.reincarnatey.net/links/" target="_blank" rel="noopener" >失迹的博客 - 链接</a></p> <p><figure class="gallery-image" style=" flex-grow: 355; flex-basis: 852px" > <a href="https://blog.reincarnatey.net/2024/0803-my-links-page/before.png" data-size="2025x570"> <img src="https://blog.reincarnatey.net/2024/0803-my-links-page/before.png" width="2025" height="570" srcset="https://blog.reincarnatey.net/2024/0803-my-links-page/before_hu_a49336aa69363a66.png 480w, https://blog.reincarnatey.net/2024/0803-my-links-page/before_hu_428a094b025f98d2.png 1024w" loading="lazy" alt="默认样式"> </a> <figcaption>默认样式</figcaption> </figure></p> <p><figure class="gallery-image" style=" flex-grow: 215; flex-basis: 517px" > <a href="https://blog.reincarnatey.net/2024/0803-my-links-page/before2.png" data-size="2006x930"> <img src="https://blog.reincarnatey.net/2024/0803-my-links-page/before2.png" width="2006" height="930" srcset="https://blog.reincarnatey.net/2024/0803-my-links-page/before2_hu_b7092a1063e61eb2.png 480w, https://blog.reincarnatey.net/2024/0803-my-links-page/before2_hu_7df2abdce4f301c3.png 1024w" loading="lazy" alt="初次修改后"> </a> <figcaption>初次修改后</figcaption> </figure> <figure class="gallery-image" style=" flex-grow: 185; flex-basis: 444px" > <a href="https://blog.reincarnatey.net/2024/0803-my-links-page/after.png" data-size="2016x1088"> <img src="https://blog.reincarnatey.net/2024/0803-my-links-page/after.png" width="2016" height="1088" srcset="https://blog.reincarnatey.net/2024/0803-my-links-page/after_hu_216e090e0e575cad.png 480w, https://blog.reincarnatey.net/2024/0803-my-links-page/after_hu_c0da656a966805a2.png 1024w" loading="lazy" alt="最终样式"> </a> <figcaption>最终样式</figcaption> </figure></p> <p>主要的改动点在于:</p> <ul> <li>两栏修改为更紧凑的三栏</li> <li>区分板块</li> <li>允许在链接右下角加入一个小图标(我称之为 &ldquo;badge&rdquo;)以增加一个链接</li> <li>适配了暗黑模式和移动端</li> </ul> <p>下面我会给出我的代码,并简单讲解作用。</p> <p>由于我只针对我的博客,所以不保证能在其他地方使用,但思路必然可以参考。</p> <h2 id="实现代码">实现代码</h2> <h3 id="scss">SCSS</h3> <p><code>assets\scss\custom.scss</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scss" data-lang="scss"><span style="display:flex;"><span><span style="color:#7f848e">//链接页面 </span></span></span><span style="display:flex;"><span><span style="color:#7f848e"></span><span style="color:#c678dd">@media</span> (<span style="color:#e06c75">min-width</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">1024</span><span style="color:#e5c07b">px</span>) { </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">.article-list--compact.links</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">display</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">grid</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">grid-template-columns</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">1</span><span style="color:#e5c07b">fr</span> <span style="color:#d19a66">1</span><span style="color:#e5c07b">fr</span> <span style="color:#d19a66">1</span><span style="color:#e5c07b">fr</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">background</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">none</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">box-shadow</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">none</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">article</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">background</span><span style="color:#56b6c2">:</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card-background</span>); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">border</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">none</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">box-shadow</span><span style="color:#56b6c2">:</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">shadow-l2</span>); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">margin-bottom</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">8</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">border-radius</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">10</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">&amp;</span><span style="color:#61afef">:not</span><span style="color:#56b6c2">(</span><span style="color:#61afef">:nth-child</span><span style="color:#56b6c2">(</span><span style="color:#e06c75">3n</span><span style="color:#56b6c2">))</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">margin-right</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">8</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#e5c07b">.article-list--compact.links</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">margin-top</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">10</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">margin-bottom</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">40</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">article</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">position</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">relative</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">.badge</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">position</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">absolute</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">width</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">40</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">height</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">40</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">line-height</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">40</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">border-radius</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">100</span><span style="color:#e5c07b">%</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">bottom</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">10</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">right</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">10</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">padding</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">0</span> <span style="color:#c678dd">!important</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">display</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">block</span> <span style="color:#c678dd">!important</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">text-align</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">center</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">font-size</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">20</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">background-color</span><span style="color:#56b6c2">:</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card-background</span>); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">box-shadow</span><span style="color:#56b6c2">:</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">shadow-l2</span>); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#e5c07b">.article-list--title</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">color</span><span style="color:#56b6c2">:</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">accent-color</span>); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">font-weight</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">700</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">font-size</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">1</span><span style="color:#d19a66">.6</span><span style="color:#e5c07b">rem</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">margin-left</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">10</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>具体而言,针对电脑端我们开启了三栏显示,同时增加了我们新增元素的相关样式。</p> <h3 id="json">json</h3> <p>当然,我们需要补充更多的数据且格式有所变化。</p> <p><code>data\links.json</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>[ </span></span><span style="display:flex;"><span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;title&#34;</span>: <span style="color:#98c379">&#34;失迹的网站&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;links&#34;</span>: [ </span></span><span style="display:flex;"><span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;title&#34;</span>: <span style="color:#98c379">&#34;失迹的博客&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;website&#34;</span>: <span style="color:#98c379">&#34;https://blog.reincarnatey.net/&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;image&#34;</span>: <span style="color:#98c379">&#34;korita.png&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;description&#34;</span>: <span style="color:#98c379">&#34;直至雨凝结之时:一个记录技术与思考的博客。&#34;</span> </span></span><span style="display:flex;"><span> }, </span></span><span style="display:flex;"><span> ... </span></span><span style="display:flex;"><span> ] </span></span><span style="display:flex;"><span> }, { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;title&#34;</span>: <span style="color:#98c379">&#34;博客聚合网站&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;links&#34;</span>: [ </span></span><span style="display:flex;"><span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;title&#34;</span>: <span style="color:#98c379">&#34;开往 Travellings&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;website&#34;</span>: <span style="color:#98c379">&#34;https://www.travellings.cn/&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;image&#34;</span>: <span style="color:#98c379">&#34;travelling-light.png&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;description&#34;</span>: <span style="color:#98c379">&#34;「开往 Travellings」是一个友链接力项目,旨在通过网络跳转的方式将流量引入那些鲜为人知的独立站点,从而推动网络的开放性和多元性。&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;badge&#34;</span>: { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;icon&#34;</span>: <span style="color:#98c379">&#34;🚇&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;link&#34;</span>: <span style="color:#98c379">&#34;https://www.travellings.cn/go.html&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;description&#34;</span>: <span style="color:#98c379">&#34;跳转到随机博客&#34;</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> },{ </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;title&#34;</span>: <span style="color:#98c379">&#34;中文独立博客列表&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;website&#34;</span>: <span style="color:#98c379">&#34;https://github.com/timqian/chinese-independent-blogs&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;image&#34;</span>: <span style="color:#98c379">&#34;&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&#34;description&#34;</span>: <span style="color:#98c379">&#34;&#34;</span> </span></span><span style="display:flex;"><span> }, </span></span><span style="display:flex;"><span> ... </span></span><span style="display:flex;"><span> ] </span></span><span style="display:flex;"><span> }, </span></span><span style="display:flex;"><span> ... </span></span><span style="display:flex;"><span>] </span></span></code></pre></div><h3 id="html">html</h3> <p><code>layouts\page\links.html</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>{{ range $i, $category := $.Site.Data.links }} </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">span</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;article-list--title&#34;</span>&gt;{{ $category.title }}&lt;/<span style="color:#e06c75">span</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;article-list--compact links&#34;</span>&gt; </span></span><span style="display:flex;"><span> {{ $siteResources := resources }} </span></span><span style="display:flex;"><span> {{ range $i, $link := $category.links }} </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">article</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">a</span> <span style="color:#e06c75">href</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{ $link.website }}&#34;</span> <span style="color:#e06c75">target</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;_blank&#34;</span> <span style="color:#e06c75">rel</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;noopener&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;article-details&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">h2</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;article-title&#34;</span>&gt; </span></span><span style="display:flex;"><span> {{- $link.title -}} </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">h2</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">footer</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;article-time&#34;</span>&gt; </span></span><span style="display:flex;"><span> {{ with $link.description }} </span></span><span style="display:flex;"><span> {{ . }} </span></span><span style="display:flex;"><span> {{ else }} </span></span><span style="display:flex;"><span> {{ $link.website }} </span></span><span style="display:flex;"><span> {{ end }} </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">footer</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> {{ if $link.image }} </span></span><span style="display:flex;"><span> {{ $image := $siteResources.Get (delimit (slice &#34;link-img/&#34; $link.image) &#34;&#34;) | resources.Fingerprint &#34;md5&#34; }} </span></span><span style="display:flex;"><span> {{ $imageResized := $image.Resize &#34;120x120&#34; }} </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;article-image&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">img</span> <span style="color:#e06c75">src</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{ $imageResized.RelPermalink }}&#34;</span> <span style="color:#e06c75">width</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{ $imageResized.Width }}&#34;</span> <span style="color:#e06c75">height</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{ $imageResized.Height }}&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">loading</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;lazy&#34;</span> <span style="color:#e06c75">data-key</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;links-{{ $link.website }}&#34;</span> <span style="color:#e06c75">data-hash</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{ $image.Data.Integrity }}&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> {{ end }} </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">a</span>&gt; </span></span><span style="display:flex;"><span> {{ if $link.badge }} </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">a</span> <span style="color:#e06c75">href</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{ $link.badge.link }}&#34;</span> <span style="color:#e06c75">target</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;_blank&#34;</span> <span style="color:#e06c75">title</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{ $link.badge.description}}&#34;</span> <span style="color:#e06c75">rel</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;noopener&#34;</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;badge&#34;</span>&gt;{{ $link.badge.icon }}&lt;/<span style="color:#e06c75">a</span>&gt; </span></span><span style="display:flex;"><span> {{ end }} </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">article</span>&gt; </span></span><span style="display:flex;"><span> {{ end }} </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span>{{ end }} </span></span></code></pre></div><p>由于这一部分改动略大,所以直接一整段放上来了。</p> <p>大致上就是改成了对应的布局。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li> <p>本人 css 不好,多次请教了 ChatGLM</p> </li> <li> <p>一些有关 css 的教程或文档</p> </li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/8/3
阅读更多

学习笔记 | Cloudflare 邮箱收到邮件时转发至多个邮箱

<h2 id="起因">起因</h2> <p>尽管官方提供的功能已经很足,但总有人会产生新的需求。</p> <p>像我,这里需要<strong>使 Cloudflare 邮箱收到邮件时同时转发到数个邮箱</strong>。</p> <p>官方并不提供这个功能,因此我们需要使用电子邮箱 Worker 实现。</p> <h2 id="实现">实现</h2> <p>在”电子邮件路由“页面创建一个电子邮件 Worker,写入以下代码:</p> <pre tabindex="0"><code>export default { async email(message, env, ctx) { await message.forward(&#34;email@example.com&#34;); await message.forward(&#34;email2@example2.com&#34;); } } </code></pre><p>当然你还可以继续添加更多个,但理论上来说转发到的邮箱都需要在“目标地址”中经过验证。</p> <p>然后在“路由规则”中添加到 Catch-all 或者某个自定义地址。</p> <p>之后可以给自己发送一封邮件进行测试,可以看到邮件成功被转发到了多个邮箱。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/8/3
阅读更多

建站技术 | 如何让 Bing 必应收录你的网站图标

<h2 id="问题描述">问题描述</h2> <p>网页成功被 Bing 搜索引擎收录,但图标却没有被收录。在 Bing 中搜索自己的网页,在网站的左侧无法显示图标,而是显示一个灰色小地球,别人的网站左侧却可以正常显示图标。</p> <p><figure class="gallery-image" style=" flex-grow: 61; flex-basis: 148px" > <a href="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/1.png" data-size="530x857"> <img src="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/1.png" width="530" height="857" srcset="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/1_hu_23aaf2411412192d.png 480w, https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/1_hu_617b70a82691ed56.png 1024w" loading="lazy" alt="由于我之前的截图找不到了,所以对比一下其他的网站"> </a> <figcaption>由于我之前的截图找不到了,所以对比一下其他的网站</figcaption> </figure></p> <p>问题的原因在于,Bing 没有收录你的网站的图标。</p> <h2 id="准备">准备</h2> <p>想要 Bing 收录你的图标,首先你的网站应该有一个图标,并具备特定程度的资质。</p> <h3 id="事先声明">事先声明</h3> <p>本文章分享了我所使用并成功的方法,但我无法保证本方法是正确的收录图标的方法,也不保证本方法在未来依旧有效。</p> <p>但目前除此方法之外,据说只能等待资质达到标准后 Bing 主动抓取收录图标。</p> <h3 id="关于资质">关于资质</h3> <p>这一点具体标准并不明确,可以确定的是显然你的网站需要被 Bing 收录,但若你的<strong>网站被收录的时间较长,内容较多且质量较高</strong>,相对来说我认为收录的概率会大一些。</p> <p>这里放上我申请时的资质:</p> <ul> <li> <p>收录时间超过一年</p> </li> <li> <p>超过二十篇原创文章</p> </li> <li> <p>Bing 近半年点击次数 1.6k</p> </li> </ul> <p>由于具体标准未知,所以大家都可以尝试,<strong>欢迎在评论区分享你的资质与结果,为他人提供参考!</strong></p> <h3 id="正确的添加你的图标">正确的添加你的图标</h3> <p>正确的标准依旧未知,但显然你至少需要用某种方式添加你的图标:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#e06c75">link</span> <span style="color:#e06c75">rel</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;shortcut icon&#34;</span> <span style="color:#e06c75">type</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;image/x-icon&#34;</span> <span style="color:#e06c75">href</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;/favicon.ico&#34;</span>&gt; </span></span></code></pre></div><p>推荐将图标文件放置在网站根目录。</p> <h2 id="提交-support-request">提交 support request</h2> <p>在 Bing Webmaster Tools 中点击右上角的问号,点击&quot;必应网站管理员支持&quot;。</p> <p><figure class="gallery-image" style=" flex-grow: 72; flex-basis: 174px" > <a href="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/2.png" data-size="477x657"> <img src="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/2.png" width="477" height="657" srcset="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/2_hu_a53f0d8a15d09894.png 480w, https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/2_hu_494ba1cfed1b2e4e.png 1024w" loading="lazy" alt="进入支持页面"> </a> <figcaption>进入支持页面</figcaption> </figure></p> <p>进入支持页面后,点击页面下方蓝色按钮 &ldquo;Raise support request&rdquo;。</p> <p><figure class="gallery-image" style=" flex-grow: 383; flex-basis: 921px" > <a href="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/3.png" data-size="1167x304"> <img src="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/3.png" width="1167" height="304" srcset="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/3_hu_dbbe91a21a5c0ec1.png 480w, https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/3_hu_a1d10aa494fe9ec3.png 1024w" loading="lazy" alt="发起 support request"> </a> <figcaption>发起 support request</figcaption> </figure></p> <p>填写表格,网站选择你申请收录的网站,问题类型选择 &ldquo;Crawling issue&rdquo;,再选择 &ldquo;Other&rdquo;,并描述你的问题(可以按照我申请时写的 &ldquo;Bing didn&rsquo;t crawl my site&rsquo;s icon&rdquo;),最后填写验证码,点击蓝色按钮 &ldquo;Get Help&rdquo;。</p> <p><figure class="gallery-image" style=" flex-grow: 97; flex-basis: 234px" > <a href="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/4.png" data-size="1472x1507"> <img src="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/4.png" width="1472" height="1507" srcset="https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/4_hu_17a42617a98bbbd6.png 480w, https://blog.reincarnatey.net/2024/0802-bing-crawl-website-icon/4_hu_1c3695fd450b5b24.png 1024w" loading="lazy" alt="填写信息"> </a> <figcaption>填写信息</figcaption> </figure></p> <p>完成提交后,你会立即收到两封邮件,分别是你提交的信息以及官方收到你的提交。</p> <h2 id="后续">后续</h2> <p>关注邮箱等待回复即可。</p> <p>这里我在申请后的第三天便收到了回复:</p> <blockquote> <p>I would like to inform you that the favicon has been updated from our end and it will take 7-8 days to reflect on the Bing search results.</p> </blockquote> <p>表示已经成功更新我的网站图标,大约在一周后便可以在搜索引擎中看到。</p> <p>然而实际上当天就已经可以在搜索引擎中看到图标了,详见 <a class="link" href="https://blog.reincarnatey.net/2024/0722-sth-on-blog/#%E6%88%91%E7%9A%84%E5%8D%9A%E5%AE%A2" target="_blank" rel="noopener" >随笔杂谈 | 博客二三事</a>。</p> <p>实际需要的时间也并不确定,例如:我申请的那一天是周五,收到的那天是周一,我等待三天可能与跨了一个周末有关。目前根据评论的反馈,最快的貌似一天就收录了。</p> <p>你需要的时间可能更短也可能更长,欢迎尝试后在评论区分享你收到回复的用时以供参考。</p> <h2 id="写在最后">写在最后</h2> <p>没想到困扰我这么久的方法这么轻松就解决了,以前怎么就没找到这么个入口呢,网上也搜索不到相关内容。</p> <p>另外,如果你跟着我的文章进行了尝试,无论结果如何,欢迎在评论区分享你的相关情况(例如资质、用时、结果)以供更多人参考!</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/8/2
阅读更多

踩坑记录 | Windows 删除文件/文件夹提示“找不到该项目”

<h2 id="问题描述">问题描述</h2> <p>删除文件夹时,发现其中存在一个顽固文件,不管怎么删除都会提示”<strong>找不到该项目</strong>“,无法删除。</p> <p>根据网络所见,貌似文件夹也存在这种情况。</p> <h2 id="问题猜想">问题猜想</h2> <p>我这里存在的问题推测是因为文件名末尾多带了一个 <code>.</code>,即类似于 <code>example.file.txt.</code>,正常操作是无法创建这样的文件的,貌似我这里是由于旧版本 adb 的 bug 所产生。</p> <p>该文件并非无拓展名,具体肯定是属于异常情况,但这里不深究。</p> <p>据说文件夹也存在这样的异常。</p> <h2 id="解决方法">解决方法</h2> <h3 id="方法一">方法一</h3> <p>这里提供我试验过后的成功方法。</p> <p>创建一个 <code>del.bat</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span><span style="color:#c678dd">del</span> /f /a /q \\?\<span style="color:#e06c75">%1</span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">rd</span> /s /q \\?\<span style="color:#e06c75">%1</span> </span></span></code></pre></div><p>然后<strong>将要删除的东西拖到这个批处理文件</strong>上即可。</p> <p>我这里直接拖入顽固文件不起作用,但是<strong>将顽固文件所在的文件夹直接拖入</strong>就删除成功了。</p> <p>如果连正常文件都无法起作用,重新创建批处理文件并将编码修改为 ANSI。</p> <h3 id="方法二">方法二</h3> <p>右键,选择 Winrar 的添加到压缩文件,并勾选删除源文件(或者叫”压缩后删除原来的文件“)。</p> <p>我这里直接对顽固文件进行操作未能成功,没有对所在文件夹尝试过,一些网友说有用,故也记录下来。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li><a class="link" href="https://blog.csdn.net/qq_41042595/article/details/112576438" target="_blank" rel="noopener" >Windows桌面文件夹删除被提示“找不到该项目”——顽固目录、文件的删除_电脑桌面文件夹删除提示找不到该项目-CSDN博客</a></li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/7/29
阅读更多

随笔杂谈 | 博客二三事

<h2 id="前言">前言</h2> <p>今天是我的博客建站 555 天。近来不止博客,我的网站均发生了许多的变化以及事件,以这篇文章作简述。</p> <h2 id="我的博客">我的博客</h2> <p>这段时间里,我的博客是变化与事件最多的,其中最大的事件莫过于解决了此前博客在大陆访问一直不通畅的问题,我<strong>从 Netlify 迁移到了 Vercel</strong>。</p> <p><figure class="gallery-image" style=" flex-grow: 114; flex-basis: 274px" > <a href="https://blog.reincarnatey.net/2024/0722-sth-on-blog/blog-netlify.png" data-size="912x798"> <img src="https://blog.reincarnatey.net/2024/0722-sth-on-blog/blog-netlify.png" width="912" height="798" srcset="https://blog.reincarnatey.net/2024/0722-sth-on-blog/blog-netlify_hu_610bff2943b91238.png 480w, https://blog.reincarnatey.net/2024/0722-sth-on-blog/blog-netlify_hu_1760ddcd4cbc3074.png 1024w" loading="lazy" alt="迁移前"> </a> <figcaption>迁移前</figcaption> </figure> <figure class="gallery-image" style=" flex-grow: 114; flex-basis: 274px" > <a href="https://blog.reincarnatey.net/2024/0722-sth-on-blog/blog-vercel.png" data-size="906x792"> <img src="https://blog.reincarnatey.net/2024/0722-sth-on-blog/blog-vercel.png" width="906" height="792" srcset="https://blog.reincarnatey.net/2024/0722-sth-on-blog/blog-vercel_hu_c6a17e627130c7c0.png 480w, https://blog.reincarnatey.net/2024/0722-sth-on-blog/blog-vercel_hu_a99f09c7b12deed7.png 1024w" loading="lazy" alt="迁移后"> </a> <figcaption>迁移后</figcaption> </figure></p> <p>令人感慨。</p> <p>剩余的变化与事件,简而言之:</p> <ul> <li>更好的接入了 Waline,同时记录了过程发表了一篇文章</li> <li>加入了十年之约、开往、个站商店、博友圈、中文独立博客列表等项目</li> <li>将上述项目加入了友链,都是非常好的项目,也算是助了一份力</li> <li>除了原本就有的 Bing 与 Google 外,新增百度、360、Yandex 搜索引擎收录</li> <li>更新了关于、友链、计划页面,开始记录博客大事记</li> <li>侧栏加入了开往的接力链接,欢迎随意探索,发现更多的博客</li> <li>更换了我的 Github 个人主页链接,因为改了名</li> <li>新增了 Robots.txt</li> <li>解决了 Bing 搜索页面不显示网站图标的问题</li> <li>尽力优化了 SEO,真的</li> </ul> <h2 id="我的其他网站">我的其他网站</h2> <p>除此之外,失迹云也历经数次更新,虽然看上去变化不大,但是修复了一些较为严重的 bug。</p> <p>同时,尽管我的个人主页原本在大陆的访问较为通常,不过我依旧选择从 Netlify 迁移到了 Vercel。</p> <p><figure class="gallery-image" style=" flex-grow: 111; flex-basis: 268px" > <a href="https://blog.reincarnatey.net/2024/0722-sth-on-blog/home-netlify.png" data-size="903x807"> <img src="https://blog.reincarnatey.net/2024/0722-sth-on-blog/home-netlify.png" width="903" height="807" srcset="https://blog.reincarnatey.net/2024/0722-sth-on-blog/home-netlify_hu_91dea8878f40996b.png 480w, https://blog.reincarnatey.net/2024/0722-sth-on-blog/home-netlify_hu_d37593a1c6c59dcd.png 1024w" loading="lazy" alt="迁移前"> </a> <figcaption>迁移前</figcaption> </figure> <figure class="gallery-image" style=" flex-grow: 113; flex-basis: 272px" > <a href="https://blog.reincarnatey.net/2024/0722-sth-on-blog/home-vercel.png" data-size="892x786"> <img src="https://blog.reincarnatey.net/2024/0722-sth-on-blog/home-vercel.png" width="892" height="786" srcset="https://blog.reincarnatey.net/2024/0722-sth-on-blog/home-vercel_hu_a3c5181dafcb8341.png 480w, https://blog.reincarnatey.net/2024/0722-sth-on-blog/home-vercel_hu_a729e52aa5bdd9a9.png 1024w" loading="lazy" alt="迁移后"> </a> <figcaption>迁移后</figcaption> </figure></p> <p>同时也类似于我的博客,尽力优化了 SEO,更换了 Github 链接,新增了 Robots.txt,也顺带解决了网站图标的事。</p> <h2 id="未来数月内的计划">未来数月内的计划</h2> <p>接下来一段时间内不会有新文章更新,也不会有较大的改动,大约半个月后会进行一次较集中的更新。</p> <p>此后内容更新会变得较为缓慢,字数可能不长,内容可能简单,但质量不会下降(我认为的)。</p> <h2 id="遇到的一些有趣的事情">遇到的一些有趣的事情</h2> <p>按时间顺序排序。</p> <ol> <li> <p>关于第三方 Cookie 弃用(aka. 3PCD)</p> <p>谷歌,你先看看自家 Analytics。</p> <p><figure class="gallery-image" style=" flex-grow: 329; flex-basis: 791px" > <a href="https://blog.reincarnatey.net/2024/0722-sth-on-blog/1.png" data-size="1708x518"> <img src="https://blog.reincarnatey.net/2024/0722-sth-on-blog/1.png" width="1708" height="518" srcset="https://blog.reincarnatey.net/2024/0722-sth-on-blog/1_hu_7347aff5ca6a6218.png 480w, https://blog.reincarnatey.net/2024/0722-sth-on-blog/1_hu_8f7419b872e8c526.png 1024w" loading="lazy" alt="谷歌只需要在前方 3PCD 就可以了,而使用 GA 的博主要考虑的就多了"> </a> <figcaption>谷歌只需要在前方 3PCD 就可以了,而使用 GA 的博主要考虑的就多了</figcaption> </figure></p> <p>你们要不吵一架?根据官方仓库下的 issue,目前该问题谷歌内部正在商讨解决方案。</p> </li> <li> <p>在 Cloudflare 配置里看到一个有意思的选项。</p> <p><figure class="gallery-image" style=" flex-grow: 102; flex-basis: 245px" > <a href="https://blog.reincarnatey.net/2024/0722-sth-on-blog/2.png" data-size="535x523"> <img src="https://blog.reincarnatey.net/2024/0722-sth-on-blog/2.png" width="535" height="523" srcset="https://blog.reincarnatey.net/2024/0722-sth-on-blog/2_hu_7dac170d4c9a5963.png 480w, https://blog.reincarnatey.net/2024/0722-sth-on-blog/2_hu_e2a67384f7c7cdfe.png 1024w" loading="lazy" alt="笑死我了"> </a> <figcaption>笑死我了</figcaption> </figure></p> <p>我承认我的笑点很奇怪,但是本质上为关和 I&rsquo;m under attack 真的很好笑。</p> </li> <li> <p>等博客更新以后我要写在那篇文章最开头。</p> <p><figure class="gallery-image" style=" flex-grow: 185; flex-basis: 444px" > <a href="https://blog.reincarnatey.net/2024/0722-sth-on-blog/waline.png" data-size="461x249"> <img src="https://blog.reincarnatey.net/2024/0722-sth-on-blog/waline.png" width="461" height="249" srcset="https://blog.reincarnatey.net/2024/0722-sth-on-blog/waline_hu_2e43cf02450092f2.png 480w, https://blog.reincarnatey.net/2024/0722-sth-on-blog/waline_hu_c4c7d5ed63f3ddfe.png 1024w" loading="lazy" alt="被 Waline 开发者巡回了"> </a> <figcaption>被 Waline 开发者巡回了</figcaption> </figure></p> </li> <li> <p>困扰了我许久的网站图标不收录问题,提了个工单四天就解决了。</p> <p><figure class="gallery-image" style=" flex-grow: 435; flex-basis: 1045px" > <a href="https://blog.reincarnatey.net/2024/0722-sth-on-blog/4.png" data-size="954x219"> <img src="https://blog.reincarnatey.net/2024/0722-sth-on-blog/4.png" width="954" height="219" srcset="https://blog.reincarnatey.net/2024/0722-sth-on-blog/4_hu_e4da39561a6f33b9.png 480w, https://blog.reincarnatey.net/2024/0722-sth-on-blog/4_hu_4dd44052a9388255.png 1024w" loading="lazy" alt="个人主页"> </a> <figcaption>个人主页</figcaption> </figure> <figure class="gallery-image" style=" flex-grow: 252; flex-basis: 605px" > <a href="https://blog.reincarnatey.net/2024/0722-sth-on-blog/5.png" data-size="939x372"> <img src="https://blog.reincarnatey.net/2024/0722-sth-on-blog/5.png" width="939" height="372" srcset="https://blog.reincarnatey.net/2024/0722-sth-on-blog/5_hu_21ae1b2d7acb1827.png 480w, https://blog.reincarnatey.net/2024/0722-sth-on-blog/5_hu_ef498863bbbbb787.png 1024w" loading="lazy" alt="博客"> </a> <figcaption>博客</figcaption> </figure></p> <p>考虑到之前在网络上搜索不到可以参考的解决方案,之后或许会单独发一篇博客来分享我的解决方法。</p> </li> <li> <p>写完这篇文章之后已经到第二天了,所以本文的第一句话就存在问题。</p> </li> </ol> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol start="6"> <li>随笔还有参考文献?</li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/7/22
阅读更多

建站技术 | 使博客更好地接入 Waline

<h2 id="前言">前言</h2> <p>近期为我的网站进行了一次整体的升级,修复了个人主页和云盘的诸多 bug,新增了更多功能。其中包括优化 Waline 的使用,在这里记录下来以供他人参考。</p> <p>这里我使用的是 Hugo 以及 stack 主题,使用其他方式的可以更多的参考 waline 的官方文档。</p> <h2 id="迁移到最新版本-waline">迁移到最新版本 Waline</h2> <h3 id="客户端">客户端</h3> <p>由于 stack 模板中所接入的 waline 版本为 V2,或者像我一样从更远古的版本就开始魔改的话引入的是 V1,因此这里我们先来迁移到 V3 版本。</p> <p>直接按照<a class="link" href="https://waline.js.org/guide/get-started/#html-%E5%BC%95%E5%85%A5-%E5%AE%A2%E6%88%B7%E7%AB%AF" target="_blank" rel="noopener" >官方文档</a>中的引入方式修改代码即可,这里贴上我将 stack 模板修改后的代码。</p> <p><code>layouts\partials\comments\provider\waline.html</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#e06c75">link</span> <span style="color:#e06c75">rel</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;stylesheet&#34;</span> <span style="color:#e06c75">href</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;https://unpkg.com/@waline/client@v3/dist/waline.css&#34;</span>/&gt; </span></span><span style="display:flex;"><span>&lt;<span style="color:#e06c75">div</span> <span style="color:#e06c75">id</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;waline&#34;</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;waline-container&#34;</span>&gt;&lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span>&lt;<span style="color:#e06c75">style</span>&gt; </span></span><span style="display:flex;"><span> .<span style="color:#e5c07b">waline-container</span> { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">background-color</span>: <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card</span><span style="color:#56b6c2">-</span><span style="color:#e06c75">background</span>); </span></span><span style="display:flex;"><span> <span style="color:#c678dd">border-radius</span>: <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card</span><span style="color:#56b6c2">-</span><span style="color:#e06c75">border</span><span style="color:#56b6c2">-</span><span style="color:#e06c75">radius</span>); </span></span><span style="display:flex;"><span> <span style="color:#c678dd">box-shadow</span>: <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">shadow</span><span style="color:#56b6c2">-</span><span style="color:#e06c75">l1</span>); </span></span><span style="display:flex;"><span> <span style="color:#c678dd">padding</span>: <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card</span><span style="color:#56b6c2">-</span><span style="color:#e06c75">padding</span>); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">--waline-font-size</span>: <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">article</span><span style="color:#56b6c2">-</span><span style="color:#e06c75">font</span><span style="color:#56b6c2">-</span><span style="color:#e06c75">size</span>); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> .<span style="color:#e5c07b">waline-container</span> .<span style="color:#e5c07b">wl-count</span> { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">color</span>: <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card</span><span style="color:#56b6c2">-</span><span style="color:#e5c07b">text</span><span style="color:#56b6c2">-</span><span style="color:#e5c07b">color</span><span style="color:#56b6c2">-</span><span style="color:#e06c75">main</span>); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>&lt;/<span style="color:#e06c75">style</span>&gt; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>{{- $showReaction := (default true .Params.reaction) -}} </span></span><span style="display:flex;"><span>{{- with .Site.Params.comments.waline -}} </span></span><span style="display:flex;"><span>{{- $config := dict &#34;el&#34; &#34;#waline&#34; &#34;dark&#34; `html[data-scheme=&#34;dark&#34;]` -}} </span></span><span style="display:flex;"><span>{{- $replaceKeys := dict &#34;serverurl&#34; &#34;serverURL&#34; &#34;requiredmeta&#34; &#34;requiredMeta&#34; &#34;wordlimit&#34; &#34;wordLimit&#34; &#34;pagesize&#34; &#34;pageSize&#34; &#34;imageuploader&#34; &#34;imageUploader&#34; &#34;texrenderer&#34; &#34;texRenderer&#34; &#34;commentsorting&#34; &#34;commentSorting&#34; &#34;recaptchav3key&#34; &#34;recaptchaV3Key&#34; &#34;turnstilekey&#34; &#34;turnstileKey&#34; -}} </span></span><span style="display:flex;"><span>{{- $replaceLocaleKeys := dict &#34;reactiontitle&#34; &#34;reactionTitle&#34; &#34;gifsearchplaceholder&#34; &#34;gifSearchPlaceholder&#34; &#34;nickerror&#34; &#34;nickError&#34; &#34;mailerror&#34; &#34;mailError&#34; &#34;wordhint&#34; &#34;wordHint&#34; &#34;cancellike&#34; &#34;cancelLike&#34; &#34;cancelreply&#34; &#34;cancelReply&#34; &#34;uploadimage&#34; &#34;uploadImage&#34; -}} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>{{- range $key, $val := . -}} </span></span><span style="display:flex;"><span> {{- if ne $val nil -}} </span></span><span style="display:flex;"><span> {{- $replaceKey := index $replaceKeys $key -}} </span></span><span style="display:flex;"><span> {{- $k := default $key $replaceKey -}} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> {{- if eq $k &#34;locale&#34; -}} </span></span><span style="display:flex;"><span> {{- $locale := dict -}} </span></span><span style="display:flex;"><span> {{- range $lkey, $lval := $val -}} </span></span><span style="display:flex;"><span> {{- if ne $lval nil -}} </span></span><span style="display:flex;"><span> {{- $replaceLKey := index $replaceLocaleKeys $lkey -}} </span></span><span style="display:flex;"><span> {{- $lk := default $lkey $replaceLKey -}} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> {{- $locale = merge $locale (dict $lk $lval) -}} </span></span><span style="display:flex;"><span> {{- end -}} </span></span><span style="display:flex;"><span> {{- end -}} </span></span><span style="display:flex;"><span> {{- $config = merge $config (dict $k $locale) -}} </span></span><span style="display:flex;"><span> {{- else if eq $k &#34;reaction&#34; -}} </span></span><span style="display:flex;"><span> {{- $config = merge $config (dict $k (cond $showReaction $val false)) -}} </span></span><span style="display:flex;"><span> {{- else -}} </span></span><span style="display:flex;"><span> {{- $config = merge $config (dict $k $val) -}} </span></span><span style="display:flex;"><span> {{- end -}} </span></span><span style="display:flex;"><span> {{- end -}} </span></span><span style="display:flex;"><span>{{- end -}} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>&lt;<span style="color:#e06c75">script</span> <span style="color:#e06c75">type</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;module&#34;</span>&gt; </span></span><span style="display:flex;"><span> <span style="color:#c678dd">import</span> { <span style="color:#e06c75">init</span> } <span style="color:#e06c75">from</span> <span style="color:#98c379">&#39;https://unpkg.com/@waline/client@v3/dist/waline.js&#39;</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">init</span>({{ <span style="color:#e06c75">$config</span> <span style="color:#56b6c2">|</span> <span style="color:#e06c75">jsonify</span> <span style="color:#56b6c2">|</span> <span style="color:#e06c75">safeJS</span> }}); </span></span><span style="display:flex;"><span>&lt;/<span style="color:#e06c75">script</span>&gt; </span></span><span style="display:flex;"><span>{{- end -}} </span></span></code></pre></div><p>这里我使用的 VSCode 虽然安装了 <a class="link" href="https://marketplace.visualstudio.com/items?itemName=budparr.language-hugo-vscode" target="_blank" rel="noopener" >Hugo Language and Syntax Support</a> 插件,但依旧无法正确判断 Hugo 语言语法,倒数第三行会报错,只要能正常运行即可。</p> <p>另外,除了引入 V3 以外,这里也包括了 <code>locale</code> 大小写的修复,<a class="link" href="#%e6%9b%b4%e5%a4%9a%e7%9a%84-waline-%e5%ae%a2%e6%88%b7%e7%ab%af%e9%85%8d%e7%bd%ae" >后文</a>会提到,以及在 FrontMatter 中新增了一个自定义属性 <code>reaction</code>,用以开关文章反应,默认为 <code>true</code>。</p> <p>刷新网页,如果发现评论系统右下角变成了 Powered by <a class="link" href="https://github.com/walinejs/waline" target="_blank" rel="noopener" >Waline</a> v3.3.0 就说明升级成功了。</p> <p>如果你曾经已经接入 Waline 的更多功能例如统计,那么你可能需要注意按照<a class="link" href="https://waline.js.org/migration/v2.html" target="_blank" rel="noopener" >官方的迁移指南</a>修改你的代码。</p> <h3 id="服务端">服务端</h3> <p>由于数据库与服务端分开部署,所以升级服务端理所当然无需担心会丢失数据。</p> <p>在管理端 UI 界面最上方可以看到当前最新版本为 <code>@waline/vercel@1.32.0</code>。</p> <p>官方给的方式是将自己的 waline github 库中的 <code>package.json</code> 中的 <code>@waline/vercel</code> 版本修改为最新的即可。当然理论上来说,我们这里用的是 <code>latest</code>,直接在 Vercel 控制台中重部署即可。</p> <p>我这里版本差的有点多,所以选择将自己的库 pull 下来,然后替换成官方的 <a class="link" href="https://github.com/walinejs/waline/tree/main/example" target="_blank" rel="noopener" >waline/example at main · walinejs/waline · GitHub</a> 再 push 回去。</p> <p>接着回到 Vercel 控制台,看到已经重新部署了,打开管理端也可以看到上方不再显示版本更新提示。</p> <h2 id="关于-vercel-墙内-dns-污染">关于 Vercel 墙内 Dns 污染</h2> <p>这里顺带一提,污染会导致墙内无法访问,进而墙内无法加载 Waline。</p> <p>解决方法:自定义域名,并且 CNAME 换成 <code>cname-china.vercel-dns.com</code>。</p> <p>本身访问速度已经较快了,建议关掉自定义域名 Cloudflare 代理,不然会出现 Cloudflare 的代理 ip 地址。</p> <h2 id="配置邮件提醒">配置邮件提醒</h2> <p>本步骤需要修改较多的环境变量,你可以在这里查看相关的服务端环境变量配置的详细信息:<a class="link" href="https://waline.js.org/reference/server/env.html#markdown" target="_blank" rel="noopener" >服务端环境变量 | Waline</a>。</p> <h3 id="配置-gmail-邮箱">配置 Gmail 邮箱</h3> <p>首先需要对谷歌邮箱账号进行一些设置。</p> <p>在邮箱设置的<strong>转发和 POP/IMAP</strong> 中启用 IMAP。</p> <p><figure class="gallery-image" style=" flex-grow: 112; flex-basis: 269px" > <a href="https://blog.reincarnatey.net/2024/0719-better-waline/1.png" data-size="714x635"> <img src="https://blog.reincarnatey.net/2024/0719-better-waline/1.png" width="714" height="635" srcset="https://blog.reincarnatey.net/2024/0719-better-waline/1_hu_737cc89507a54088.png 480w, https://blog.reincarnatey.net/2024/0719-better-waline/1_hu_bdaa0ac07ade0670.png 1024w" loading="lazy" alt="启用 IMAP"> </a> <figcaption>启用 IMAP</figcaption> </figure></p> <p>然后在<a class="link" href="https://security.google.com/settings/security/apppasswords" target="_blank" rel="noopener" >这里</a>生成谷歌账号的应用专用密码,复制下来一会用。</p> <p>随后按照官方指南 <a class="link" href="https://waline.js.org/guide/features/notification.html#%E9%82%AE%E4%BB%B6%E9%80%9A%E7%9F%A5" target="_blank" rel="noopener" >评论通知 | Waline</a> 配置 Vercel 上的环境变量即可:</p> <ul> <li> <p><code>SMTP_SERVICE</code>:填 <code>Gmail</code></p> </li> <li> <p><code>SMTP_USER</code>:填谷歌邮箱</p> </li> <li> <p><code>SMTP_PASS</code>:填刚刚获取的应用专用密码,有 16 位,记得删除空格</p> </li> <li> <p><code>SITE_NAME</code>:填博客的名字</p> </li> <li> <p><code>SITE_URL</code>:要带 <code>https://</code>,不带最后一个斜杠,例如 <code>https://blog.reincarnatey.net</code></p> </li> <li> <p><code>AUTHOR_EMAIL</code>:填自己常用的邮箱,用来接受评论通知</p> </li> </ul> <h3 id="套上自己的-cloudflare-邮箱">套上自己的 Cloudflare 邮箱</h3> <p>当然直接用谷歌邮箱发还是过于粗糙,这里希望再套上自己的 Cloudflare 邮箱实现更好的效果。这里的操作就类似于创建 Cloudflare 邮箱然后绑定到谷歌邮箱的别名上,详细可以在网络上搜索相关博客,这里只简单记录一下步骤。</p> <ol> <li> <p>在 Cloudflare 电子邮件路由设置中创建邮箱地址,需要这一步是因为待会要收一个验证邮件(当然可以像我一样设置 Catch-All 就不用这一步)</p> </li> <li> <p>谷歌账号需要开启一些设置(例如开启双重验证)</p> </li> <li> <p>像刚刚一样再创建一个应用专用密码</p> </li> <li> <p>在谷歌邮箱账号设置中在<strong>账号与导入</strong>页面选择<strong>添加其他电子邮件地址</strong></p> <ol> <li> <p>第一步名称和电子邮件地址填写你要导入的名称和电子邮件地址,例如 <code>失迹的博客</code> 和 <code>noreply@reincarnatey.net</code>,勾选作为别名</p> </li> <li> <p><code>SMTP服务器</code>:填 <code>smtp.gmail.com</code></p> </li> <li> <p><code>用户名</code>:填你的谷歌邮箱</p> </li> <li> <p><code>密码</code>:填刚刚获取的应用专用密码,记得删空格</p> </li> </ol> </li> <li> <p>别名电子邮件会收到一封邮件,完成验证</p> </li> <li> <p>添加 Vercel 的环境变量为新的邮箱信息,与在谷歌那边填的一致即可</p> <ol> <li> <p><code>SENDER_NAME</code>:自定义发件人名字,如 <code>失迹的博客</code></p> </li> <li> <p><code>SENDER_EMAIL</code>:自定义发件地址,如 <code>noreply@reincarnatey.net</code></p> </li> </ol> </li> </ol> <p>这样就完成别名的设置了,如果要精致一点还可以为这个邮箱设置一个 gravatar。</p> <p>我们发送一个回复,可以看到收到的邮件的发件人信息已经设置好了。</p> <p><figure class="gallery-image" style=" flex-grow: 245; flex-basis: 589px" > <a href="https://blog.reincarnatey.net/2024/0719-better-waline/2.png" data-size="511x208"> <img src="https://blog.reincarnatey.net/2024/0719-better-waline/2.png" width="511" height="208" srcset="https://blog.reincarnatey.net/2024/0719-better-waline/2_hu_1c551a5a8fabf24d.png 480w, https://blog.reincarnatey.net/2024/0719-better-waline/2_hu_d6d56c3ecac6d509.png 1024w" loading="lazy" alt="评论提醒邮件"> </a> <figcaption>评论提醒邮件</figcaption> </figure></p> <h3 id="使用模板样式">使用模板样式</h3> <p>这里我使用的模板修改自 <a class="link" href="https://www.sarakale.top/blog/posts/537344b2" target="_blank" rel="noopener" >SaraKale&rsquo;s blog</a>,非常感谢。</p> <p>效果展示:</p> <p><figure class="gallery-image" style=" flex-grow: 62; flex-basis: 149px" > <a href="https://blog.reincarnatey.net/2024/0719-better-waline/3.png" data-size="910x1462"> <img src="https://blog.reincarnatey.net/2024/0719-better-waline/3.png" width="910" height="1462" srcset="https://blog.reincarnatey.net/2024/0719-better-waline/3_hu_a2f95159e3bf636.png 480w, https://blog.reincarnatey.net/2024/0719-better-waline/3_hu_58db888ce7dd7b19.png 1024w" loading="lazy" alt="访客评论被回复收到的"> </a> <figcaption>访客评论被回复收到的</figcaption> </figure> <figure class="gallery-image" style=" flex-grow: 66; flex-basis: 159px" > <a href="https://blog.reincarnatey.net/2024/0719-better-waline/4.png" data-size="909x1366"> <img src="https://blog.reincarnatey.net/2024/0719-better-waline/4.png" width="909" height="1366" srcset="https://blog.reincarnatey.net/2024/0719-better-waline/4_hu_ce0e8c455384a2b2.png 480w, https://blog.reincarnatey.net/2024/0719-better-waline/4_hu_9974aa66cf36d48f.png 1024w" loading="lazy" alt="站长收到的"> </a> <figcaption>站长收到的</figcaption> </figure></p> <ul> <li> <p><code>MAIL_SUBJECT</code>:<code>{{parent.nick | safe}},您在 『{{site.name | safe}}』 上的评论收到了回复</code></p> </li> <li> <p><code>MAIL_TEMPLATE</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#e06c75">div</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;background: url(https://tva3.sinaimg.cn/large/c56b8822ly1h62npb7s1ej201y01y0lh.jpg);padding:40px 0px 20px;margin:0px;background-color:#FFCDCE;width:100%;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">style</span> <span style="color:#e06c75">type</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;text/css&#34;</span>&gt; </span></span><span style="display:flex;"><span> @<span style="color:#c678dd">media</span> <span style="color:#e06c75">screen</span> <span style="color:#e06c75">and</span> <span style="color:#56b6c2">(</span><span style="color:#e06c75">max-width</span>:<span style="color:#61afef">600px</span><span style="color:#56b6c2">)</span> { </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> .<span style="color:#e5c07b">afterimg</span><span style="color:#56b6c2">,</span> </span></span><span style="display:flex;"><span> .<span style="color:#e5c07b">beforeimg</span> { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">display</span>: <span style="color:#e5c07b">none</span> <span style="color:#7f848e">!important</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">style</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;border-radius: 10px 10px 10px 10px;font-size:14px;color: #555555;width: 530px;font-family:&#39;Century Gothic&#39;,&#39;Trebuchet MS&#39;,&#39;Hiragino Sans GB&#39;,微软雅黑,&#39;Microsoft Yahei&#39;,Tahoma,Helvetica,Arial,&#39;SimSun&#39;,sans-serif;margin:50px auto;max-width:100%;background: ##ffffff;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">img</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;beforeimg&#34;</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;width:530px;height:317px;z-index:-100;pointer-events:none&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">src</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;https://npm.elemecdn.com/hexo-butterfly-envelope/lib/before.png&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">img</span> <span style="color:#e06c75">src</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;https://npm.elemecdn.com/hexo-butterfly-envelope/lib/violet.jpg&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;width:100%;overflow:hidden;pointer-events:none;margin-top: -120px;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;width:100%;background:#f8d1ce;color:#9d2850;background-image: -moz-linear-gradient(0deg, rgb(67, 198, 184), rgb(255, 209, 244));height: 66px;background: url(https://tva2.sinaimg.cn/large/c56b8822ly1h61tb7tagcj20ii01u3yc.jpg) left top no-repeat;display: flex;justify-content: center;flex-direction: column;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">p</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;font-size:16px;font-weight: bold;text-align:center;word-break:break-all;margin:0;&#34;</span>&gt; </span></span><span style="display:flex;"><span> 您在&lt;<span style="color:#e06c75">a</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;text-decoration:none;color: #9d2850;&#34;</span> <span style="color:#e06c75">href</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{site.url}}&#34;</span>&gt;『{{site.name | </span></span><span style="display:flex;"><span> safe}}』&lt;/<span style="color:#e06c75">a</span>&gt;上的留言收到一条回复&lt;/<span style="color:#e06c75">p</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;formmain&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;background:#fff;width:100%;max-width:800px;margin:auto auto;overflow:hidden;margin-bottom: -155px;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;margin:40px auto;width:90%;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">p</span>&gt;😊Hi,{{parent.nick}},您曾发表的评论:&lt;/<span style="color:#e06c75">p</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;background: #fafafa repeating-linear-gradient(-45deg,#fff,#fff 1.125rem,transparent 1.125rem,transparent 2.25rem);box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;&#34;</span>&gt; </span></span><span style="display:flex;"><span> {{parent.comment | safe}}&lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">p</span>&gt;&lt;<span style="color:#e06c75">strong</span>&gt;{{self.nick}}&lt;/<span style="color:#e06c75">strong</span>&gt; 回复您:&lt;/<span style="color:#e06c75">p</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;background: #fafafa repeating-linear-gradient(-45deg,#fff,#fff 1.125rem,transparent 1.125rem,transparent 2.25rem);box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;&#34;</span>&gt; </span></span><span style="display:flex;"><span> {{self.comment | safe}}&lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">p</span>&gt;&lt;<span style="color:#e06c75">a</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;text-decoration:none; color:#cf5c83&#34;</span> <span style="color:#e06c75">href</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{site.postUrl}}&#34;</span> <span style="color:#e06c75">target</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;_blank&#34;</span>&gt; 查看回复的完整內容 </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">a</span>&gt;,欢迎再次访问&lt;<span style="color:#e06c75">a</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;text-decoration:none; color:#cf5c83&#34;</span> <span style="color:#e06c75">href</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{site.url}}&#34;</span> <span style="color:#e06c75">target</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;_blank&#34;</span>&gt; </span></span><span style="display:flex;"><span> {{site.name}} &lt;/<span style="color:#e06c75">a</span>&gt;。 </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">hr</span> /&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">p</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;font-size:14px;color:#b7adad;text-align:center;position: relative;z-index: 99;&#34;</span>&gt; </span></span><span style="display:flex;"><span> 本邮件为系统自动发送,请勿直接回复邮件哦,可到博文内容回复。&lt;<span style="color:#e06c75">br</span> /&gt;{{site.url}}&lt;/<span style="color:#e06c75">p</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">p</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">img</span> <span style="color:#e06c75">src</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;https://npm.elemecdn.com/hexo-butterfly-envelope/lib/line.png&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;width:100%;margin:25px auto 5px auto;display:block;pointer-events:none&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">p</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;bottomhr&#34;</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;font-size:12px;text-align:center;color:#999&#34;</span>&gt;© 失迹的博客&lt;/<span style="color:#e06c75">p</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">img</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;afterimg&#34;</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;width:535px;height:317px;z-index:100;margin-left: -3px;&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">src</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;https://npm.elemecdn.com/hexo-butterfly-envelope/lib/after.png&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span>&lt;/<span style="color:#e06c75">div</span>&gt; </span></span></code></pre></div></li> <li> <p><code>MAIL_SUBJECT_ADMIN</code>:<code>您有一条新的评论</code></p> </li> <li> <p><code>MAIL_TEMPLATE_ADMIN</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#e06c75">div</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;background: url(https://tva3.sinaimg.cn/large/c56b8822ly1h62npb7s1ej201y01y0lh.jpg);padding:40px 0px 20px;margin:0px;background-color:#FFCDCE; width:100%;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">style</span> <span style="color:#e06c75">type</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;text/css&#34;</span>&gt; </span></span><span style="display:flex;"><span> @<span style="color:#c678dd">media</span> <span style="color:#e06c75">screen</span> <span style="color:#e06c75">and</span> <span style="color:#56b6c2">(</span><span style="color:#e06c75">max-width</span>:<span style="color:#61afef">600px</span><span style="color:#56b6c2">)</span> { </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> .<span style="color:#e5c07b">afterimg</span><span style="color:#56b6c2">,</span> </span></span><span style="display:flex;"><span> .<span style="color:#e5c07b">beforeimg</span> { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">display</span>: <span style="color:#e5c07b">none</span> <span style="color:#7f848e">!important</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">style</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;border-radius: 10px 10px 10px 10px;font-size:14px;color:#555555;width: 530px;font-family:&#39;Century Gothic&#39;,&#39;Trebuchet MS&#39;,&#39;Hiragino Sans GB&#39;,微软雅黑,&#39;Microsoft Yahei&#39;,Tahoma,Helvetica,Arial,&#39;SimSun&#39;,sans-serif;margin:50px auto;max-width:100%;background: ##ffffff;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">img</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;beforeimg&#34;</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;width:530px;height:317px;pointer-events:none&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">src</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;https://npm.elemecdn.com/hexo-butterfly-envelope/lib/before.png&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">img</span> <span style="color:#e06c75">src</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;https://npm.elemecdn.com/hexo-butterfly-envelope/lib/violet.jpg&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;width:100%;overflow:hidden;pointer-events:none;margin-top: -120px;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;width:100%;background:#f8d1ce;color:#9d2850;background-image: -moz-linear-gradient(0deg, rgb(67, 198, 184), rgb(255, 209, 244));height: 66px;background: url(https://tva2.sinaimg.cn/large/c56b8822ly1h61tb7tagcj20ii01u3yc.jpg) left top no-repeat;display: flex;justify-content: center;flex-direction: column;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">p</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;font-size:16px;font-weight: bold;text-align:center;word-break:break-all;margin:0;&#34;</span>&gt; </span></span><span style="display:flex;"><span> 您在 &lt;<span style="color:#e06c75">a</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;text-decoration:none;color:#9d2850;&#34;</span> <span style="color:#e06c75">href</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{site.url}}&#34;</span> <span style="color:#e06c75">target</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;_blank&#34;</span>&gt;{{site.name}}&lt;/<span style="color:#e06c75">a</span>&gt; </span></span><span style="display:flex;"><span> 上的文章有了新的评论&lt;/<span style="color:#e06c75">p</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;formmain&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;background:#fff;width:100%;max-width:800px;margin:auto auto;overflow:hidden;margin-bottom:-155px;z-index:100;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;margin:40px auto;width:90%;&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">p</span>&gt;&lt;<span style="color:#e06c75">strong</span>&gt;{{self.nick}}&lt;/<span style="color:#e06c75">strong</span>&gt; 发表了评论:&lt;/<span style="color:#e06c75">p</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;background: #fafafa repeating-linear-gradient(-45deg,#fff,#fff 1.125rem,transparent 1.125rem,transparent 2.25rem);box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);margin:20px 0px;padding:15px;border-radius:5px;font-size:15px;color:#555555;&#34;</span>&gt; </span></span><span style="display:flex;"><span> {{self.comment | safe}}&lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">p</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;text-align:center;position: relative;z-index: 99;&#34;</span>&gt;&lt;<span style="color:#e06c75">a</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;text-decoration:none;color:#cf5c83&#34;</span> <span style="color:#e06c75">href</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{site.postUrl}}&#34;</span> <span style="color:#e06c75">target</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;_blank&#34;</span>&gt;查看回复的完整內容&lt;/<span style="color:#e06c75">a</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">p</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">img</span> <span style="color:#e06c75">src</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;https://npm.elemecdn.com/hexo-butterfly-envelope/lib/line.png&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;width:100%;margin:25px auto 5px auto;display:block;pointer-events:none&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">p</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;bottomhr&#34;</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;font-size:12px;text-align:center;color:#999&#34;</span>&gt;© 失迹的博客&lt;/<span style="color:#e06c75">p</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">img</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;afterimg&#34;</span> <span style="color:#e06c75">style</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;width:535px;height:317px;margin-left:-3px;&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">src</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;https://npm.elemecdn.com/hexo-butterfly-envelope/lib/after.png&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span>&lt;/<span style="color:#e06c75">div</span>&gt; </span></span></code></pre></div></li> </ul> <p>直接在 Vercel 设置环境变量即可,大胆复制进去。注意如果选择压行的话会丢掉一部分空格,需要手动补上否则不美观。</p> <h2 id="更多的-waline-客户端配置">更多的 Waline 客户端配置</h2> <p>可以在官方文档 <a class="link" href="https://waline.js.org/reference/client/props.html" target="_blank" rel="noopener" >组件属性 | Waline</a> 中查看。</p> <p>放上这里我的配置:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#e06c75">waline</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">serverURL</span>: https://waline.reincarnatey.net/ </span></span><span style="display:flex;"><span> <span style="color:#e06c75">lang</span>: zh-CN </span></span><span style="display:flex;"><span> <span style="color:#e06c75">search</span>: <span style="color:#e5c07b">false</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">pageview</span>: <span style="color:#e5c07b">true</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">comment</span>: <span style="color:#e5c07b">true</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">emoji</span>: </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/bmoji </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/bilibili </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/weibo </span></span><span style="display:flex;"><span> <span style="color:#e06c75">reaction</span>: </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/bilibili/bb_heart_eyes.png </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/bilibili/bb_thumbsup.png </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/bilibili/bb_zhoumei.png </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/bilibili/bb_grievance.png </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/bilibili/bb_dizzy_face.png </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/bilibili/bb_slap.png </span></span><span style="display:flex;"><span> <span style="color:#e06c75">requiredMeta</span>: </span></span><span style="display:flex;"><span> - nick </span></span><span style="display:flex;"><span> <span style="color:#e06c75">locale</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">admin</span>: 站长 </span></span><span style="display:flex;"><span> <span style="color:#e06c75">sofa</span>: 还没有人评论哦!快来抢沙发吧~ </span></span><span style="display:flex;"><span> <span style="color:#e06c75">placeholder</span>: 欢迎留下宝贵的评论!请留下正确的邮箱以便有回复时进行邮箱提醒,请勿发布任何与本文章无关的内容。 </span></span><span style="display:flex;"><span> <span style="color:#e06c75">reactionTitle</span>: 这篇文章对你有帮助吗? </span></span><span style="display:flex;"><span> <span style="color:#e06c75">reaction0</span>: 非常有用 </span></span><span style="display:flex;"><span> <span style="color:#e06c75">reaction1</span>: 有帮助 </span></span><span style="display:flex;"><span> <span style="color:#e06c75">reaction2</span>: 一般 </span></span><span style="display:flex;"><span> <span style="color:#e06c75">reaction3</span>: 无帮助 </span></span><span style="display:flex;"><span> <span style="color:#e06c75">reaction4</span>: 看不懂 </span></span><span style="display:flex;"><span> <span style="color:#e06c75">reaction5</span>: 有错误 </span></span></code></pre></div><p>需要注意的是由于 <code>yaml</code> 读取为统一小写,而 Waline 的配置又大小写敏感,所以需要专门的步骤将大小写转换。</p> <p>我在前文提供的 <code>layouts\partials\comments\provider\waline.html</code> 已包含了基本配置以及 <code>locale</code> 配置的大小写修复,其他配置同理。同时也包含新增了一个文章 FrontMatter 属性用以控制是否显示文章反应:<code>reaction: true</code>。</p> <h2 id="接入浏览量与评论数统计">接入浏览量与评论数统计</h2> <h3 id="文章页">文章页</h3> <p>在 <code>config.yaml</code> 的 <code>waline</code> 配置中将 <code>pageview</code> 与 <code>comment</code> 都设置为 <code>true</code>。</p> <p>并且修改 <code>layouts\partials\article\components\details.html</code>,将末尾的 <code>&lt;footer&gt;</code> 中的内容修改为:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#e06c75">footer</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;article-time&#34;</span>&gt; </span></span><span style="display:flex;"><span> {{ if not .Date.IsZero }} </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> {{ partial &#34;helper/icon&#34; &#34;date&#34; }} </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">time</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;article-time--published&#34;</span>&gt; </span></span><span style="display:flex;"><span> {{- .Date.Format (or .Site.Params.dateFormat.published &#34;Jan 02, 2006&#34;) -}} </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">time</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> {{ end }} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> {{ if .Site.Params.article.readingTime }} </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> {{ partial &#34;helper/icon&#34; &#34;clock&#34; }} </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">time</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;article-words&#34;</span>&gt; </span></span><span style="display:flex;"><span> {{ $fixedWordCount := add .WordCount 224}} </span></span><span style="display:flex;"><span> 约{{ div $fixedWordCount 225 }}分钟,共{{ .WordCount }}字 </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">time</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> {{ end }} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> {{ partial &#34;helper/icon&#34; &#34;view&#34; }} </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">time</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;article-pageview&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">span</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;waline-pageview-count&#34;</span> <span style="color:#e06c75">data-path</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{.RelPermalink}}&#34;</span>&gt;0&lt;/<span style="color:#e06c75">span</span>&gt; 次浏览 </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">time</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> {{ partial &#34;helper/icon&#34; &#34;comment&#34; }} </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">time</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;article-comment&#34;</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">span</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;waline-comment-count&#34;</span> <span style="color:#e06c75">data-path</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;{{.RelPermalink}}&#34;</span>&gt;0&lt;/<span style="color:#e06c75">span</span>&gt; 条评论 </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">time</span>&gt; </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span>&lt;/<span style="color:#e06c75">footer</span>&gt; </span></span></code></pre></div><p>另外虽然有提供 <code>.ReadingTime</code> 变量以供估算阅读时长,但官方设置的速度或许是基于英文的,个人感觉误差较大。所以这里根据网络上的数据,成人每分钟阅读约 200-250 字进行向上取整估算。</p> <p>至于图标,按照<a class="link" href="https://blog.reincarnatey.net/2023/build-hugo-blog-with-stack-mod/#%E6%B7%BB%E5%8A%A0%E8%87%AA%E5%B7%B1%E7%9A%84%E7%A4%BE%E4%BA%A4%E5%AA%92%E4%BD%93%E8%81%94%E7%B3%BB%E6%96%B9%E5%BC%8Frss" target="_blank" rel="noopener" >这里</a>所使用的方式下载对应的图标 <code>view.svg</code> 和 <code>comment.svg</code>,并修改成适配暗黑模式后,扔到 <code>\assets\icons</code> 下即可。</p> <p>由于文章页会直接引入完整的 Waline,所以不需要额外的引入了。可以看到在文章页面已经可以正常显示修改的内容了。</p> <p><figure class="gallery-image" style=" flex-grow: 398; flex-basis: 956px" > <a href="https://blog.reincarnatey.net/2024/0719-better-waline/5.png" data-size="1020x256"> <img src="https://blog.reincarnatey.net/2024/0719-better-waline/5.png" width="1020" height="256" srcset="https://blog.reincarnatey.net/2024/0719-better-waline/5_hu_34a61b0445310d9b.png 480w, https://blog.reincarnatey.net/2024/0719-better-waline/5_hu_530cc52a09c4e750.png 1024w" loading="lazy" alt="显示浏览量与评论数"> </a> <figcaption>显示浏览量与评论数</figcaption> </figure></p> <h3 id="页脚">页脚</h3> <p>这里希望在所有页面的页脚展示主页的浏览量。</p> <p>由于除了文章以外的页面不会引入浏览量统计,所以这里需要额外引入,在 <code>layouts\partials\footer\footer.html</code> 的 <code>&lt;footer&gt;</code> 内添加:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#56b6c2">&lt;</span><span style="color:#e06c75">script</span> <span style="color:#e06c75">type</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;module&#34;</span><span style="color:#56b6c2">&gt;</span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">import</span> { <span style="color:#e06c75">pageviewCount</span> } <span style="color:#e06c75">from</span> <span style="color:#98c379">&#39;https://unpkg.com/@waline/client@v3/dist/pageview.js&#39;</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">pageviewCount</span>({ </span></span><span style="display:flex;"><span> <span style="color:#e06c75">serverURL</span><span style="color:#56b6c2">:</span> <span style="color:#98c379">&#34;{{.Site.Params.comments.waline.serverURL}}&#34;</span> </span></span><span style="display:flex;"><span> }); </span></span><span style="display:flex;"><span><span style="color:#56b6c2">&lt;</span>/script&gt; </span></span></code></pre></div><p>然后在页脚相关代码中,你需要展示的地方添加上显示的 <code>span</code> 即可,例如在某个地方添加:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>共 &lt;<span style="color:#e06c75">span</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;waline-pageview-count&#34;</span> <span style="color:#e06c75">data-path</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;/&#34;</span>&gt;0&lt;/<span style="color:#e06c75">span</span>&gt; 次浏览 </span></span></code></pre></div><p>可以看到各页面都可以成功显示首页浏览量,同时这里悄悄解决了首页中浏览量显示的问题。</p> <p><figure class="gallery-image" style=" flex-grow: 304; flex-basis: 730px" > <a href="https://blog.reincarnatey.net/2024/0719-better-waline/6.png" data-size="289x95"> <img src="https://blog.reincarnatey.net/2024/0719-better-waline/6.png" width="289" height="95" srcset="https://blog.reincarnatey.net/2024/0719-better-waline/6_hu_3817a0b2eb7e5c3b.png 480w, https://blog.reincarnatey.net/2024/0719-better-waline/6_hu_b0c071a17e31293d.png 1024w" loading="lazy" alt="首页浏览量"> </a> <figcaption>首页浏览量</figcaption> </figure></p> <blockquote> <p>这里也可以测试,首页浏览量:<span class="waline-pageview-count" data-path="/">0</span></p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>首页浏览量:&lt;<span style="color:#e06c75">span</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;waline-pageview-count&#34;</span> <span style="color:#e06c75">data-path</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;/&#34;</span>&gt;0&lt;/<span style="color:#e06c75">span</span>&gt; </span></span></code></pre></div></blockquote> <h3 id="首页">首页</h3> <p>由于首页不开启评论功能,而页脚中只引入了浏览量统计,所以首页文章列表中显示不了评论数。我们希望能够显示出文章列表中各文章的评论数,需要额外的方式进行引入。</p> <p>在 <code>layouts\index.html</code> 中 <code>&lt;/section&gt;</code> 后面加上:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#e06c75">script</span> <span style="color:#e06c75">type</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;module&#34;</span>&gt; </span></span><span style="display:flex;"><span> <span style="color:#c678dd">import</span> { <span style="color:#e06c75">commentCount</span> } <span style="color:#e06c75">from</span> <span style="color:#98c379">&#39;https://unpkg.com/@waline/client@v3/dist/comment.js&#39;</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">commentCount</span>({ </span></span><span style="display:flex;"><span> <span style="color:#e06c75">serverURL</span><span style="color:#56b6c2">:</span> <span style="color:#98c379">&#34;{{.Site.Params.comments.waline.serverURL}}&#34;</span> </span></span><span style="display:flex;"><span> }); </span></span><span style="display:flex;"><span>&lt;/<span style="color:#e06c75">script</span>&gt; </span></span></code></pre></div><p>可以看到主页文章列表中,浏览量与回复数统计数据已经正常填充。</p> <h2 id="接入最新评论挂件">接入最新评论挂件</h2> <p><figure class="gallery-image" style=" flex-grow: 84; flex-basis: 202px" > <a href="https://blog.reincarnatey.net/2024/0719-better-waline/7.png" data-size="624x738"> <img src="https://blog.reincarnatey.net/2024/0719-better-waline/7.png" width="624" height="738" srcset="https://blog.reincarnatey.net/2024/0719-better-waline/7_hu_9fe46b7de86c06ac.png 480w, https://blog.reincarnatey.net/2024/0719-better-waline/7_hu_5cf1676e8a70e897.png 1024w" loading="lazy" alt="组件效果(自定义样式)"> </a> <figcaption>组件效果(自定义样式)</figcaption> </figure></p> <p>可以在 <a class="link" href="https://waline.js.org/reference/client/api.html#recentcomments" target="_blank" rel="noopener" >客户端 API | Waline</a> 页面查看这个挂件的 API。</p> <p>这里我们以侧边栏组件的形式引入这个挂件,并设计成只有首页会出现。</p> <p>在 <code>assets\scss\partials\widgets.scss</code> 的最下方添加:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scss" data-lang="scss"><span style="display:flex;"><span><span style="color:#7f848e">/* Recent Comment widget */</span> </span></span><span style="display:flex;"><span><span style="color:#e5c07b">.widget.recentComment</span> { </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">.recent-comment-list</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">border-radius</span><span style="color:#56b6c2">:</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card-border-radius</span>); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">box-shadow</span><span style="color:#56b6c2">:</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">shadow-l1</span>); </span></span><span style="display:flex;"><span> <span style="color:#e06c75">background-color</span><span style="color:#56b6c2">:</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card-background</span>); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">.recent-comment-list-item</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">display</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">flex</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">padding</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">0</span> <span style="color:#d19a66">1</span><span style="color:#d19a66">.25</span><span style="color:#e5c07b">em</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">align-items</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">center</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">&amp;</span><span style="color:#61afef">:not</span><span style="color:#56b6c2">(</span><span style="color:#61afef">:last-of-type</span><span style="color:#56b6c2">)</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">border-bottom</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">1</span><span style="color:#d19a66">.4</span><span style="color:#e5c07b">px</span> <span style="color:#e06c75">solid</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card-separator-color</span>); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">.recent-comment-card</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">flex</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">1</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">width</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">0</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">padding</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">20</span><span style="color:#e5c07b">px</span> <span style="color:#d19a66">10</span><span style="color:#e5c07b">px</span> <span style="color:#c678dd">!important</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">p</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">font-size</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">.8</span><span style="color:#e5c07b">em</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">margin-top</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">3</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">margin-bottom</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">0</span><span style="color:#e5c07b">px</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">white-space</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">nowrap</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">overflow</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">hidden</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">text-overflow</span><span style="color:#56b6c2">:</span> <span style="color:#e06c75">ellipsis</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">color</span><span style="color:#56b6c2">:</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card-text-color-secondary</span>); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">.recent-comment-avatar</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">width</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">3</span><span style="color:#e5c07b">rem</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">border-radius</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">50</span><span style="color:#e5c07b">%</span>; </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">.recent-comment-nick</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">font-size</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">.875</span><span style="color:#e5c07b">em</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">color</span><span style="color:#56b6c2">:</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card-text-color-main</span>); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">.recent-comment-time</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">font-size</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">.75</span><span style="color:#e5c07b">em</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">color</span><span style="color:#56b6c2">:</span> <span style="color:#61afef;font-weight:bold">var</span>(<span style="color:#56b6c2">--</span><span style="color:#e06c75">card-text-color-tertiary</span>); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">.vemoji</span><span style="color:#56b6c2">,</span> </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">.wl-emoji</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">height</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">1</span><span style="color:#d19a66">.25</span><span style="color:#e5c07b">em</span>; </span></span><span style="display:flex;"><span> <span style="color:#e06c75">margin</span><span style="color:#56b6c2">:</span> <span style="color:#56b6c2">-</span><span style="color:#d19a66">0</span><span style="color:#d19a66">.2</span><span style="color:#e5c07b">em</span> <span style="color:#d19a66">.1</span><span style="color:#e5c07b">em</span>; </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>创建文件 <code>layouts\partials\widget\recent-comment.html</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span>{{ if .IsHome }} </span></span><span style="display:flex;"><span>&lt;<span style="color:#e06c75">section</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;widget recentComment&#34;</span> <span style="color:#e06c75">hidden</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;widget-icon&#34;</span>&gt; </span></span><span style="display:flex;"><span> {{ partial &#34;helper/icon&#34; &#34;comment&#34; }} </span></span><span style="display:flex;"><span> &lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">h2</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;widget-title section-title&#34;</span>&gt;最新评论&lt;/<span style="color:#e06c75">h2</span>&gt; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> &lt;<span style="color:#e06c75">div</span> <span style="color:#e06c75">class</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;recent-comment-list&#34;</span>&gt;&lt;/<span style="color:#e06c75">div</span>&gt; </span></span><span style="display:flex;"><span>&lt;/<span style="color:#e06c75">section</span>&gt; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>&lt;<span style="color:#e06c75">script</span> <span style="color:#e06c75">type</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;module&#34;</span>&gt; </span></span><span style="display:flex;"><span> <span style="color:#c678dd">import</span> { <span style="color:#e06c75">RecentComments</span> } <span style="color:#e06c75">from</span> <span style="color:#98c379">&#39;https://unpkg.com/@waline/client@v3/dist/waline.js&#39;</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">RecentComments</span>({ </span></span><span style="display:flex;"><span> <span style="color:#e06c75">serverURL</span><span style="color:#56b6c2">:</span> <span style="color:#98c379">&#34;{{.Site.Params.comments.waline.serverURL}}&#34;</span>, </span></span><span style="display:flex;"><span> <span style="color:#e06c75">count</span><span style="color:#56b6c2">:</span> <span style="color:#d19a66">5</span>, </span></span><span style="display:flex;"><span> }).<span style="color:#e06c75">then</span>((<span style="color:#e06c75">comments</span>) =&gt; { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">if</span> (<span style="color:#e06c75">comments</span>.<span style="color:#e06c75">comments</span>.<span style="color:#e06c75">data</span>.<span style="color:#e06c75">length</span>) { </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">document</span>.<span style="color:#e06c75">getElementsByClassName</span>(<span style="color:#98c379">&#39;recentComment&#39;</span>)[<span style="color:#d19a66">0</span>].<span style="color:#e06c75">removeAttribute</span>(<span style="color:#98c379">&#34;hidden&#34;</span>); </span></span><span style="display:flex;"><span> <span style="color:#e5c07b">document</span>.<span style="color:#e06c75">getElementsByClassName</span>(<span style="color:#98c379">&#39;recent-comment-list&#39;</span>)[<span style="color:#d19a66">0</span>].<span style="color:#e06c75">innerHTML</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">comments</span>.<span style="color:#e06c75">comments</span>.<span style="color:#e06c75">data</span>.<span style="color:#e06c75">map</span>((<span style="color:#e06c75">comment</span>) =&gt; </span></span><span style="display:flex;"><span> <span style="color:#98c379">`&lt;a class=&#34;recent-comment-list-item&#34; href=&#34;</span><span style="color:#98c379">${</span><span style="color:#e06c75">comment</span>.<span style="color:#e06c75">url</span><span style="color:#98c379">}</span><span style="color:#98c379">&#34; target=&#34;_blank&#34;&gt; </span></span></span><span style="display:flex;"><span><span style="color:#98c379"> &lt;img class=&#34;recent-comment-avatar&#34; src=&#34;</span><span style="color:#98c379">${</span><span style="color:#e06c75">comment</span>.<span style="color:#e06c75">avatar</span><span style="color:#98c379">}</span><span style="color:#98c379">&#34;/&gt; </span></span></span><span style="display:flex;"><span><span style="color:#98c379"> &lt;div class=&#34;recent-comment-card&#34;&gt; </span></span></span><span style="display:flex;"><span><span style="color:#98c379"> &lt;div&gt; </span></span></span><span style="display:flex;"><span><span style="color:#98c379"> &lt;span class=&#34;recent-comment-nick&#34;&gt;</span><span style="color:#98c379">${</span><span style="color:#e06c75">comment</span>.<span style="color:#e06c75">nick</span><span style="color:#98c379">}</span><span style="color:#98c379">&lt;/span&gt; </span></span></span><span style="display:flex;"><span><span style="color:#98c379"> &lt;span class=&#34;recent-comment-time&#34;&gt;</span><span style="color:#98c379">${</span><span style="color:#c678dd">new</span> <span style="color:#e5c07b">Date</span>(<span style="color:#e06c75">comment</span>.<span style="color:#e06c75">time</span>).<span style="color:#e06c75">toISOString</span>().<span style="color:#e06c75">split</span>(<span style="color:#98c379">&#39;T&#39;</span>)[<span style="color:#d19a66">0</span>]<span style="color:#98c379">}</span><span style="color:#98c379">&lt;/span&gt; </span></span></span><span style="display:flex;"><span><span style="color:#98c379"> &lt;/div&gt; </span></span></span><span style="display:flex;"><span><span style="color:#98c379"> &lt;p&gt;</span><span style="color:#98c379">${</span><span style="color:#e06c75">comment</span>.<span style="color:#e06c75">comment</span>.<span style="color:#e06c75">replaceAll</span>(<span style="color:#98c379">&#34;&lt;br&gt;&#34;</span>, <span style="color:#98c379">&#34; &#34;</span>).<span style="color:#e06c75">replaceAll</span>(<span style="color:#98c379">&#34;\n&#34;</span>, <span style="color:#98c379">&#34; &#34;</span>).<span style="color:#e06c75">replace</span>(<span style="color:#98c379">/&lt;\/?a.*?&gt;/g</span>, <span style="color:#98c379">&#34;&#34;</span>)<span style="color:#98c379">}</span><span style="color:#98c379">&lt;/p&gt; </span></span></span><span style="display:flex;"><span><span style="color:#98c379"> &lt;/div&gt; </span></span></span><span style="display:flex;"><span><span style="color:#98c379"> &lt;/a&gt;`</span> </span></span><span style="display:flex;"><span> ).<span style="color:#e06c75">join</span>(<span style="color:#98c379">&#39;&#39;</span>); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> }); </span></span><span style="display:flex;"><span>&lt;/<span style="color:#e06c75">script</span>&gt; </span></span><span style="display:flex;"><span>{{ end }} </span></span></code></pre></div><p>当然我们还需要在 <code>config.yaml</code> 中开启这个组件:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#e06c75">params</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">widgets</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">enabled</span>: </span></span><span style="display:flex;"><span> - recent-comment <span style="color:#7f848e"># 加上这一条</span> </span></span></code></pre></div><p>最外面那一个 <code>if</code> 就是用来限制只在首页出现的。这里设定成获取到数据后再显示,以及将评论内容中的链接全部去掉了,设定成展示最近的 5 条评论。</p> <p>由于官方样式令人堪忧,所以这里是获取数据后自定义。关于 <code>comment</code> 的属性,可以在 <a class="link" href="https://waline.js.org/reference/api/#api" target="_blank" rel="noopener" >API | Waline</a> 中查看关于 <code>BaseWalineResponseComment</code> 的接口,这里简单介绍几个比较有用的:</p> <table> <thead> <tr> <th style="text-align: left">属性</th> <th style="text-align: left">类型</th> <th style="text-align: left">含义</th> <th style="text-align: left">说明</th> </tr> </thead> <tbody> <tr> <td style="text-align: left">avatar</td> <td style="text-align: left">string</td> <td style="text-align: left">头像链接</td> <td style="text-align: left"></td> </tr> <tr> <td style="text-align: left">comment</td> <td style="text-align: left">string</td> <td style="text-align: left">评论内容</td> <td style="text-align: left">渲染成 html 后的</td> </tr> <tr> <td style="text-align: left">like</td> <td style="text-align: left">number</td> <td style="text-align: left">评论喜欢数</td> <td style="text-align: left"></td> </tr> <tr> <td style="text-align: left">link</td> <td style="text-align: left">string</td> <td style="text-align: left">用户地址</td> <td style="text-align: left">用户自己填写的,不建议用,避免出现乱七八糟的 backlink 和影响 SEO</td> </tr> <tr> <td style="text-align: left">nick</td> <td style="text-align: left">string</td> <td style="text-align: left">用户昵称</td> <td style="text-align: left">用户自己填写的</td> </tr> <tr> <td style="text-align: left">orig</td> <td style="text-align: left">string</td> <td style="text-align: left">原始评论</td> <td style="text-align: left"></td> </tr> <tr> <td style="text-align: left">time</td> <td style="text-align: left">number</td> <td style="text-align: left">评论时间</td> <td style="text-align: left">时间戳,单位毫秒</td> </tr> <tr> <td style="text-align: left">url</td> <td style="text-align: left">string</td> <td style="text-align: left">原文章地址</td> <td style="text-align: left"></td> </tr> </tbody> </table> <p>当然,这里没有考虑到国际化的需求以及更多的可扩展性,欢迎有想法的朋友实现更高拓展性和更美观的侧边栏组件。</p> <p>后续计划再更新一次,添加显示评论所在的原文章,会在之后关于侧边栏的文章中再次提及。</p> <p>除此之外 Waline 还有一个 <a class="link" href="https://waline.js.org/reference/client/api.html#userlist" target="_blank" rel="noopener" >UserList</a> 挂件,我这里并不需要,要接入的话也是类似的。</p> <h2 id="结语">结语</h2> <p>至此,我们已经捣鼓完了 Waline 大部分功能。你可以在官方手册中了解到更多的配置项以及我未提及的插件系统。</p> <p>很遗憾此前我曾对模板进行过一次较大的改动,实现了例如独立出来的 <a class="link" href="https://blog.reincarnatey.net/solutions" target="_blank" rel="noopener" >Solutions 题解区</a>以及 Algorithms 算法标签等,由于过于久远已经不记得调整内容无法写成文章,同时也因为放弃了拓展性的修改,无法合适地将我修改过后的主题开源。</p> <p>当然,对博客主题的改造还远不止于此。后续的改造我也会保持记录,并发布文章,欢迎在评论区插眼或是订阅 RSS 以便关注后续更新。</p> <p>有不足之处请见谅,有错误欢迎指正,也欢迎大家交换友链或是在评论区玩耍~</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li><a class="link" href="https://waline.js.org/" target="_blank" rel="noopener" >Walin 官网</a></li> <li><a class="link" href="https://www.eula.club/blogs/%E4%BD%BF%E7%94%A8Waline%E7%BB%99Hexo%E9%9D%99%E6%80%81%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F.html#_4-2-gmail%E9%80%9A%E7%9F%A5" target="_blank" rel="noopener" >使用Waline给Hexo静态博客添加评论系统 | Quantum Bit</a></li> <li><a class="link" href="https://oragekk.me/blog/waline-mail.html#_1-4-waline%E9%82%AE%E4%BB%B6%E9%80%9A%E7%9F%A5%E6%A8%A1%E7%89%88index-js%E6%9C%8D%E5%8A%A1%E7%AB%AF%E9%85%8D%E7%BD%AE%E5%8F%82%E6%95%B0" target="_blank" rel="noopener" >评论插件 Waline 之邮件通知配置 | Oragekk&rsquo;s Blog</a></li> <li><a class="link" href="https://blog.csdn.net/lyb8010/article/details/133748277" target="_blank" rel="noopener" >Gmail发送邮件的配置方法_gmail smtp-CSDN博客</a></li> <li><a class="link" href="https://www.sarakale.top/blog/posts/537344b2" target="_blank" rel="noopener" >waline邮件通知模板样式一览 | SaraKale&rsquo;s blog</a></li> <li><a class="link" href="https://hugo.opendocs.io/functions/" target="_blank" rel="noopener" >函数 | Hugo官方文档</a></li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/7/19
阅读更多

学习笔记 | OneIndex 伪静态配置以及迁移到 PHP8.2

<h2 id="伪静态配置">伪静态配置</h2> <p>不知道你是否存在该问题:开启伪静态后,访问文件时若目录或文件名带有空格则无法浏览和下载。</p> <p>如果我们手动加上 <code>/?/</code> 则可以正确访问,那么显然这是伪静态的锅。然而以我的正则水平完全看不出我的伪静态为何有问题,遂求助网络,发现只有<a class="link" href="https://moluuser.com/archives/16/" target="_blank" rel="noopener" >这篇博文</a>中提供的伪静态文件可以解决以上问题:</p> <pre tabindex="0"><code>AddDefaultCharset utf-8 Options +FollowSymLinks IndexIgnore */* RewriteEngine on # if a directory or a file exists, use it directly RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # otherwise forward it to index.php RewriteRule . index.php </code></pre><p>很神奇,以我的正则水平实在看不懂为何这是有效的,但正所谓能跑就行。</p> <h2 id="迁移到-php82">迁移到 PHP8.2</h2> <p>最近我的盘又出问题了,发现输出了一堆错误,所以这里总结了一下自从升级到 PHP8.2 后遇到的错误时能参考的博客:</p> <ol> <li> <p><a class="link" href="https://blog.reincarnatey.net/2023/1209-oneindex-fix/" target="_blank" rel="noopener" >踩坑记录 | OneIndex 首页出现 HTTP ERROR 500</a></p> </li> <li> <p><a class="link" href="https://blog.csdn.net/weixin_43944691/article/details/120530134" target="_blank" rel="noopener" >(转载)PHP 8.0 部分错误的解决办法 - 无主界 (Deprecated: Required parameter $xxx follows optional parameter $yyy )</a></p> </li> <li> <p><a class="link" href="https://blog.csdn.net/qq_58991802/article/details/124934404" target="_blank" rel="noopener" >PHP8中使用$_POST或者$_GET出现Undefined array key “xx“</a></p> </li> </ol> <p>总而言之,不需要改动过多代码,只需要在报错的地方删除或者加上一些 <code>isset</code> 即可。但尤其需要注意部分 <code>if</code> 中 <code>isset</code> 的逻辑,避免产生漏洞。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li><a class="link" href="https://moluuser.com/archives/16/" target="_blank" rel="noopener" >搭建oneindex作为外链/图床 - moluuser&rsquo;s Blog</a></li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/7/18
阅读更多

踩坑记录 | Windows 下 PHP 开启 curl 扩展

<h2 id="前言">前言</h2> <p>相信各位一定搜到过许多方法,什么移动到 Win32 、修改 apache 加载、手动安装 curl 什么的。</p> <p>由于我并非专业后端开发,只是想跑一跑代码,因此<strong>并没有安装 apache</strong>,这时候如何为 PHP 开启 curl 拓展呢。</p> <h2 id="开启方法">开启方法</h2> <p>首先修改 <code>php.ini</code>,将 <code>extension=curl</code> 前面的分号去掉。</p> <p>但是在 cmd 中输入 <code>php -i</code> 还是看不到开启了 curl 扩展,运行代码还会有以下报错信息:</p> <pre tabindex="0"><code>Warning: PHP Startup: Unable to load dynamic library &#39;curl&#39; (tried: C:\php\ext\curl (找不到指定的模块。), C:\php\ext\php_curl.dll (找不到指定的模块。)) in Unknown on line 0 </code></pre><p>注意到报错信息里的拓展位置不对,我这里 PHP 根目录明明是在 <code>D:\PHP8.2</code>,因而找到解决办法。</p> <p>修改 <code>php.ini</code> 的这一条配置:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ini" data-lang="ini"><span style="display:flex;"><span><span style="color:#e06c75">extension_dir</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;D:\PHP8.2\ext&#34;</span> </span></span></code></pre></div><p>再次查看 phpinfo,可以看到成功开启 curl 拓展,正常运行。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <p>网上搜了一堆,甚至问过 Copilot,没一个有用的。</p> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/7/18
阅读更多

学习笔记 | QQNT 聊天记录导出

<h2 id="更新">更新</h2> <p>已将研究成果整合至文档: <a class="link" href="https://qq.sbcnm.top/" target="_blank" rel="noopener" >QQDecrypt:QQ 聊天数据库解密</a>。</p> <h2 id="前言">前言</h2> <p>不管是出于希望将聊天记录保存到本地,还是需要导出和将聊天记录用于语言模型训练等用途,都需要导出 QQ 聊天记录这一步。</p> <p>近期发现 QQNT 也存在导出聊天记录的方法,遂进行尝试和记录,感谢各位前辈的探索和作出的贡献。</p> <h2 id="环境及准备工作">环境及准备工作</h2> <p>我的环境是 Windows10,QQNT 9.9.11-24568,Python 3.10.6。</p> <p>需要任意一款反编译软件,以及用于查看数据库的 DB Browser for SQLite(安装包来自网络,可从参考文献第一条中获取链接)。</p> <p>操作的所有文件建议在操作前进行备份。</p> <h2 id="逆向获取数据库密码">逆向获取数据库密码</h2> <p>由于本人不会逆向,是按照参考文献第一条中的记录进行操作,故在此不重复记录。</p> <p>需要注意的是附加到的进程是从上往下的第一个,与参考博客中的描述并不一致,也许不同设备会有不同。</p> <p>如果进程选择正确,在附加后需要一定时间加载,且过程中有弹出窗口,并在 F9 继续运行之前无法使用 QQ,并在登录后触发断点。如果进程选择错误则以上会有不同,关掉 QQ 重新附加即可。</p> <h2 id="从数据库中导出数据">从数据库中导出数据</h2> <p>依旧按照参考文献第一条中的记录进行操作,不重复记录。</p> <p>其中 <code>nt_msg.header.txt</code> 使用文本编辑器打开后可以在文件开头看到 <code>SQLite header 3</code> 以及 <code>QQ_NT DB</code>、<code>HMAC_SHA1</code> 等字样。亲测使用笔记本打开会显示乱码,而使用 VS Code 打开没有问题。</p> <p>在 DB Browser for SQLite 中,打开菜单-&gt;工具-&gt;设置加密,不输入密码直接点击 OK 以解除数据库加密。</p> <p>以私聊数据表 <code>c2c_msg_table</code> 为例,这里介绍一部分关键数据。</p> <table> <thead> <tr> <th>列名</th> <th>类型</th> <th>含义</th> <th>说明</th> </tr> </thead> <tbody> <tr> <td>40050</td> <td>int</td> <td>发送时间</td> <td>单位为秒的时间戳</td> </tr> <tr> <td>40058</td> <td>int</td> <td>发送时间</td> <td>单位为秒的时间戳,精确到天</td> </tr> <tr> <td>40030</td> <td>int</td> <td>私聊对象</td> <td>对方 QQ 号(无论是对方还是自己发送的消息)</td> </tr> <tr> <td>40033</td> <td>int</td> <td>消息发送者</td> <td>发送者的 QQ 号</td> </tr> <tr> <td>40093</td> <td>str</td> <td>消息发送者</td> <td>QQ 昵称或是备注名</td> </tr> <tr> <td>40800</td> <td>protobuf</td> <td>消息内容</td> <td>格式较为复杂,可使用 <a class="link" href="https://gchq.github.io/CyberChef/#recipe=Protobuf_Decode%28%27%27,false,false%29" target="_blank" rel="noopener" >CyberChef</a> 工具进行解析</td> </tr> </tbody> </table> <p>关于 40800 的解析,由于较复杂所以不展开。</p> <p>至此,已可以使用 sql 命令或编写程序导出数据。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li> <p><a class="link" href="https://github.com/mobyw/GroupChatAnnualReport?tab=readme-ov-file" target="_blank" rel="noopener" >GitHub - mobyw/GroupChatAnnualReport: 使用 QQNT Windows 聊天记录制作群聊年度报告!</a></p> </li> <li> <p><a class="link" href="https://github.com/Mythologyli/qq-nt-db" target="_blank" rel="noopener" >GitHub - Mythologyli/qq-nt-db: QQ NT Windows 数据库解密+图片/文件清理</a></p> </li> <li> <p><a class="link" href="https://github.com/QQBackup/qq-win-db-key" target="_blank" rel="noopener" >GitHub - QQBackup/qq-win-db-key: 全平台 QQ 聊天数据库解密</a></p> </li> <li> <p><a class="link" href="https://lengyue.me/2023/09/19/ntqq-db/" target="_blank" rel="noopener" >解析 NTQQ 数据库 | 冷月的博客</a></p> </li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/7/7
阅读更多

拾光纪行 | CEIC2024 游记

<h2 id="初赛">初赛</h2> <p>题目太面向对象了,打了一坨。</p> <h2 id="复赛">复赛</h2> <p>复赛题面就是一坨,题也是一坨,打了一坨。</p> <p>你说得对,A 题是小学奥数题,但是我已经是高中生了,所以卡了半个多小时才想出来样例二。没想出来的时候写了个 dfs 验证一下,想出来之后感觉不会写索性就先交 dfs 后面再回来推逻辑了。但是没给数据范围,什么情况。</p> <p>开 B,一眼 next_permutation 暴力,仔细算了算复杂度发现暴力变成正解了,随便写写就过掉了。</p> <p>开 C,读题目挺简单的,一看样例发现看不懂,这和题目说的是一道题吗?遂不可以总司令跳过。</p> <p>开 D,前面一堆废话,其实最后一句话就把题目说完了。刚准备开敲快速幂,一琢磨数据规模发现原来是高精度,遂转 py 打表。写了个 py 计算答案,再写了个 cpp 用快读的方式读入答案并把长度与前一百位打成表,于是本题 $O(1)$。然而悲催的是由于开题比较晚所以打表比较晚,数据范围是三万但是我 py 跑到一万九就差不多速度很慢了,然后我抽了一不小心关掉了输出没保存,于是重跑打表,只来得及跑到三千,遂只打了三千的表,亏爆。</p> <p>赛后发现 B 题原来给的那个数字就是 $n$,服了,没说清楚,而且经同学暴力检验发现只要 $n\gt3$ 那么答案就只有 $4$ 和 $9$,$n\le3$ 就输出 $0$,不可以总司令在本题输给了输出样例。</p> <p>唯一值得夸奖的比赛体验就是居家比赛,自家电脑上的 IDE 随便用。</p> <p>结果:66 分(换算成 400 分制就是 264 分),喜提一等。</p> <h2 id="决赛">决赛</h2> <h3 id="前言">前言</h3> <p>8 月 2 号的时候想看证书发现晋级了,啊?思考了一会要不要去,毕竟又远又耽误课还不报销。</p> <p>没想到就我这么烂的成绩也能进决赛,感慨。虽然深圳到乌镇有点远,但毕竟是全国的,难得有机会,所以还是报名了,也顺便去杭州和乌镇玩一玩。</p> <p>关于旅游的部分,在<a class="link" href="" >这篇文章</a>可以看到一些记录以及照片,本文章只记录有关比赛的内容。</p> <h3 id="游记">游记</h3> <h4 id="day--2">day -2</h4> <p>中午从宝安机场起飞,下午到萧山机场。然后坐了两个小时车到乌镇,等到了民宿之后已经是晚上了,所以在附近吃了个饭,然后在附近逛了逛,今天就结束了。</p> <p>其实原定是一早起飞,然后下午到了还能去签到的,但临时有事所以改到中午了。</p> <h4 id="day--1">day -1</h4> <p>签到。</p> <p>但由于原定昨天签到改成了今天,忘了签到地点有不同,因此一开始走错了地,看到那边大场馆已经在比赛了,还以为我们这项也是像 NOI 一样一大群人在大场馆比。</p> <p>签到时使用的是二维码,然后发了一个包,包里有手册、比赛衣服以及参赛证之类的,除了手册以外都有纪念意义,还是很不错的。听负责签到的人说参赛时要穿着衣服然后带参赛证。</p> <p>顺带一提,由于来这边比赛的人特别特别多,而且由于大部分赛项都是面向小学生初中生的因此还会拖家带口,所以基本到哪都能看见来参加比赛的人,甚至周边的出租车司机和店家之类的全都知道比赛的事情,很神奇。</p> <h4 id="day-0">day 0</h4> <p>当天下午稍晚一点到了考场,距离检录结束还有十多分钟。</p> <p>跟随现场志愿者前往考场,中途差点进到初中组考场,后续爬了四楼到了高中组考场。进考场发现居然只有二十个人左右,并且我是最后一个进考场的,大概是跑远路参加决赛的不多。</p> <h5 id="赛前">赛前</h5> <p>随便找了一个座位坐下,发现其他选手已经开始敲键盘。显示屏极其古老,是很旧的方形的那种。印象中系统貌似装的是 win10,键盘还行。</p> <p>需要打开官网登录账号,遂登录,输了很多遍密码都登录失败,诧异,遂打开记事本输入好后粘贴入框,发现这个机子的键盘绑键是错位的,我按 W 会打出来 Q,按 T 会打出来 R,但数字键盘是正常的,因此输入手机号时没有发现,但密码输入框是隐藏的,因此输入密码里的字母时就错了。</p> <p>诧异,遂换机位,发现这一台的键盘莫得 F12,诧异,遂换机位,成功登录。</p> <p>由于不是 NOI 系列比赛,不知道文件夹规则,因此没办法提前建好文件。编译器貌似只有比较经典的 dev cpp 和 vim 那些的,因此启动 dev cpp,还是英文版的,调了一些设置,然后敲了文件缺省,等开赛。</p> <h5 id="赛时">赛时</h5> <p>提前五分钟告知了比赛密码,在官网进入比赛并下载试卷。读题。</p> <p>A 题读完之后第一个联想到的是 <a class="link" href="https://www.luogu.com.cn/problem/UVA11997" target="_blank" rel="noopener" >K Smallest Sums</a>,虽然实际上关联不大,但也因此马上想到了一个重要性质,给出的最小的数字一定是由最小的两个加来的,因此这里可以作为一个枚举点,确定好前两个数之后就可以根据给出的第二小的数直接确定第三个数,然后一直递推下去,有多种可能只需要搜索一下。</p> <p>此时认为会做 A,于是接着读 B,读完就会了。建一个表达式树然后 dp 一下即可,极其简单。成功切题,美滋滋。遂读 C,发现见过原题,但那道原题我不会做,悲。</p> <p>此时比赛正式开始,两个小时敲两个会的题和两道暴力应该问题不大吧?</p> <p>问题大得很。开始写之后才发现 A 的搜索难写的离谱。想不出来如何实现,然后浪费了一个小时。没错,比赛一半的时间完完全全浪费掉了。但是 A 题还是要写的,后面又花了半小时写了降一档的 next_permutation 暴力。</p> <p>还剩半个小时,本来以为 B 已经会了还是能打的出来的,但很快发现了新问题:嘶,表达式树怎么建来着?哈哈,完蛋。想了整整二十分钟,只记得和笛卡尔树差不多,要用到栈什么的,半天写不出来。</p> <p>还剩不到十分钟的时候放弃,输出样例了。然后 C 写了个 map 暴力,结果样例过不去,试着调暴力也没调出来,不理解,最后改成了 set 但是没测样例。D 输出样例。</p> <p>最后一分钟极速删无关文件打包改文件名然后回官网提交,巨惊险,出现提交成功的页面的时候发现时钟已经 40 分,刷新一下就显示比赛结束了。</p> <h5 id="赛后">赛后</h5> <p>出考场之后,天很快就黑了。</p> <p><figure class="gallery-image" style=" flex-grow: 221; flex-basis: 530px" > <a href="https://blog.reincarnatey.net/2024/0707-ceic2024/5.jpg" data-size="4032x1824"> <img src="https://blog.reincarnatey.net/2024/0707-ceic2024/5.jpg" width="4032" height="1824" srcset="https://blog.reincarnatey.net/2024/0707-ceic2024/5_hu_dfb6b8de78391dff.jpg 480w, https://blog.reincarnatey.net/2024/0707-ceic2024/5_hu_2ae2867446c7f01f.jpg 1024w" loading="lazy" alt="比赛当晚的天空"> </a> <figcaption>比赛当晚的天空</figcaption> </figure></p> <p>进来时来不及拍照留念,出来的时候拍了许多。然后有一位老师一直在关心“孩子都出来没有”“找到家长了没有”哈哈哈。</p> <p>对自己的表现不太满意,完全没有任何发挥,甚至都不确定代码有没有提交上去。</p> <p>虽然是不太重要的比赛,但毕竟全国级的赛事只打出了这样的水平,还是很丢人的。</p> <h3 id="后日谈">后日谈</h3> <p>当天晚上在酒店通过在官网上抓包,从一些请求的返回数据里面看到了自己提交的文件信息,这才确认代码成功卡着时间提交上去了,惊险,看来至少没有爆零。</p> <p>以及查到了 C 的原题是可见的点,原来正解是欧拉筛求欧拉函数,幽默数论,不会。</p> <p>毕竟第二天就是闭幕式和颁奖,所以理所当然第二天就出分了,具体分数很难看就不说了,总之打了一个 3=,突然有种打铜不如打铁的意味了,但丢人归丢人,但比起比赛其实这次体验更多的是出远门玩了好久,比赛带给我的遗憾情绪连那天晚上都没撑到就无影无踪了。</p> <p>不过,接下来的时间会越来越快,越来越不够用,自然是不允许我再这样玩了,这篇游记也是我半周里每天的空隙敲出来的。不过我并没有感到遗憾,毕竟我一直对高考之后的事情有所期待。</p> <p>我相信时机到了的时候,自然而然就该来到该有的状态,就像这篇文章就该于此停笔。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/7/7
阅读更多

拾光纪行 | YIIC2024 游记

<h2 id="前言">前言</h2> <p>实际上已经半 AFO 三个月了,再来参加这场比赛也没什么好说的,尽力而为吧,也无需过于在意。</p> <p>这段时间洛谷上做题寥寥无几,只有偶尔会出一些题,比方说给学校高一集训出题和组题,或是给区里的竞赛(貌似是给初中生考的?)供题。</p> <p>更多的还是回到 whk 上了。</p> <h2 id="初赛">初赛</h2> <p>初赛还是比较简单的,就不细讲了,补全代码都很好懂,不过阅读题有一篇只看出来树上背包,不知道合并的什么玩意儿,另外单选貌似也有选错的。</p> <p>最后出分虽然是机房第一,但还是比我预想的八九十分要低一点点。另外这次高一学弟也晋级了三个,也有几个差了一点点,相比往年我们学校的情况感觉好多,但相比这届竞赛班人数又感觉有点少。</p> <p>喜提一个市1=,虽然感觉作用不大。</p> <h2 id="复赛">复赛</h2> <p>总的来说,抛开题目不看,这次复赛是所有比赛中最舒服的一次。不管是比赛场地、设备、比赛系统、IDE、赛时安排之类的,都是体验最好的一次。</p> <h3 id="day--3">day -3</h3> <p>整理了一下官网给出的考试范围和对应的模版题。居然有网络流?而且复杂树和复杂图论里面也不知道有什么…数据结构也没说清楚。</p> <p>这一天光整理题单去了,好像没做题。</p> <h3 id="day--2">day -2</h3> <p>敲了一些图论板子,只剩下欧拉回路和联通性相关的那些没敲了。</p> <h3 id="day--1">day -1</h3> <p>花了好多时间把三种联通分量和对应缩点、割点割边都重新复习和理解了一遍。也许我第一次学的时候没有理解清楚,一直有点模糊,这次终于区分清楚了。然后欧拉回路也重新理解了一遍。</p> <p>发现这次重写模板题,又多了好几个优化代码减少码长的地方,学 OI 两年了技术也就这样,倒是压行和偷懒越来越会了。</p> <p>网络流应该不考?不看了。数据结构还挺熟练的,不看了。字符串大纲说只考基础,不看了。</p> <h3 id="day-0">day 0</h3> <h4 id="赛前">赛前</h4> <p>因为这天本来学校需要补高考假,所以是从学校出发。考点在广州的广技师,从深圳过去需要大概两个小时。</p> <p>六点起床,大概 6:20 左右一边吃着面包一边赶到了门口。高一有两个学弟没在线请假,校门口刷脸没有开门权限,走人工通道要打电话跟教练确认,搞了有一会。高二另一位同学(之前一起打 THUPC 和省选的)不知道是起晚了还是怎么,来的有点晚,打电话催了一会。六点半多一点出发了。</p> <p>洛谷运势大凶。在车上看一些骗分和防挂分的博客。</p> <p>到了考场之后以为不用着急进去,上个厕所回来发现人都进去了,有人提醒我们这场比赛需要签到和登录?于是赶紧进考场了。</p> <p>进来发现比赛场地环境好舒服,签完到之后找到自己座位坐下,发现比赛设备居然是笔记本电脑,而且提供的 IDE 除了常见的 devC 和 VSCode 以外居然还有小熊猫!震惊。听说要登录,不知所措,遂举手询问工作人员,原来要打开桌面某个程序登录。发现居然是 java 写的,会定时发卷和自动同步文件,我去这也太牛了。</p> <p>发现周围的同学可以动电脑,遂启动小熊猫开始配置,调了一下编译选项和缺省模版之类的。在桌面上发现了样例文件夹,打开来看了一眼,发现输入都好少,而且看起来没有图论和数据结构题,啊?</p> <p>看大屏幕系统终端发现有同学已经同步了四题的 cpp,于是我也先把样例复制到考试文件夹,然后把四题的源代码文件和文操先弄好了。</p> <h4 id="赛时">赛时</h4> <p>开考前一分钟发现已经发卷了,遂打开看题面。</p> <p>啊?七选五…我开的是英语卷子?</p> <p>看了一会发现这不就是错排列 plus,应该可以一样的去推,遂推柿子。桌面空间有点小,草稿纸写起来不舒服,以及只给了一个小样例,离谱。</p> <p>按错排列的思路推了半天发现行不通,因为递推到 $n+1$ 的时候不知道对应正确答案是否被前面选过,好像加一个状态也不行,加限制也不太行。</p> <p>没什么思路,跑去看一眼其他题…发现都好难的样子,然后火速回来。推了一个 $O(n^2)$ 的式子,感觉没问题,把里面的组合数展开然后消了一下阶乘,发现如果换一个主元的话就可以把分母提出来,这样只需要算一次逆元,遂推推推。</p> <p>1h 过去,推到后面想出了一个基于错排列的 $O(n)$ 式子,刚写一部分就发现不对,然后再回去看原来的式子发现也不对。遂难绷,1.5h 打完暴力,测了几个错排列的样例没问题就过了。</p> <p>开 B,一开始感觉要拆位后面发现不太对。想了一会之后认定操作二是没用的,因为或运算只会把 $0$ 变成 $1$,而与运算需要的是 $0$,异或运算需要的是相同。首先显然可以异或自己,也可以把数字的任意 $0$ 取出变成 $1$ 其他变成 $0$,这样一做与运算也可以一次性变成零。然后想错了,误以为先异或任意数字再与上同一个数字一定会清零,所以发现只需要找到每个函数的最小值或零点去尝试即可。所以答案只可能是 $\min{x,f_i(x),k,f_i(k),2min,2f_i(min)}$,读入每个函数的时候检验一下就好了。</p> <p>后来发现不对,先异或再与也不一定会变成零。但是发现答案不超过 $2$,因为要么直接与 $1$ 可以变成 $0$,要么先异或再与也可以变成 $0$,因此答案最大为 $2$,所以只需要考虑一下什么情况可能会取到更小的答案就好了。</p> <p>如果函数 $\Delta\ge0$,那么最小值才有可能可以取到 $0$,否则由于 $a,b,c$ 都是整数,所以该函数的每个整数坐标点都是经过格点的,那么就最低会取到 $1$。</p> <p>所以这里继续猜测,答案只会从取最小值和取 $1$ 中选?于是火速开写,过掉了所有样例,但是没有任何证明,也没有对拍。但是只有十多分钟了,后面的暴力有些紧张。</p> <p>火速看完 C 然后开码暴力,谢谢你 <code>next permutation</code>。</p> <p>最后五分钟开 D,读题好像是 KMP,坏了,暴力都写不出来。虽然可以暴力真的拼好之后用 <code>find</code> 计数,不过来不及了,遂输出样例跑路。</p> <p>最后一分钟检查了一下四份代码,遗憾退场。</p> <h4 id="赛后">赛后</h4> <p>发现大家都考的挺炸裂,不过高一成绩都比我想的要好吗,感觉他们其实剩下打满暴力的话分应该比我高。</p> <p>赛后在校门口找了一家沙县小吃吃了午饭,然后想了一下发现 D 是不是跑两次 KMP 就可以了。然而给的考试范围里面没提会有字符串高级算法,所以没复习 KMP,虽然原理很理解,但细节不太熟悉,感觉就算时间充裕也没办法裸写出来,就是有点可惜暴力分,无所谓了。</p> <p>ABC 出场后也没有思路,C 的话具体应该要看给的那个函数来贪心或者 dp?</p> <p>洛谷的大凶说的确实没错,可恶。</p> <h2 id="后日谈">后日谈</h2> <ul> <li>2024/06/16:哈哈。</li> <li>2024/07/07:120 分,喜提 2=。</li> </ul> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/6/16
阅读更多

学习笔记 | bilibili 远古弹幕查看

<h2 id="起因">起因</h2> <p>最近想要在某个视频查找一些 2020 年的弹幕,发现在 2024 的当今通过官方弹幕列表查询最早只能到 2021 年的了。</p> <p>然而测试了一下拉取弹幕数据的 api,2021 年以前的弹幕都还有保存,也可以拉取到数据,因此只需要前端绕开时间限制即可。</p> <h2 id="方法">方法</h2> <p>在 b 站对应视频页面点击”展开弹幕列表“,点击下方的”查看历史弹幕“,在日历中翻到一个亮着的可以点的日期。</p> <p>接着 F12 打开开发者工具,找到那个日期对应的按钮的 html 代码,其 <code>data-timestamp</code> 这个属性里的值就是对应的时间戳(单位为秒),换成自己要查询的时间戳,然后点击修改后的日期即可。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/6/7
阅读更多

学习笔记 | 旧版本 QQ 聊天记录导出

<h2 id="起因与结果">起因与结果</h2> <p>闲来无事,注意到了七八年前使用过的旧手机,希望查看上面曾经的 QQ 聊天记录。</p> <p>然而手机十分老,QQ 版本也很老。手机上的 QQ 因版本过久无法打开,但更新到最新版也因为手机版本过低无法安装。总而言之,这个手机不可能打得开 QQ 了。</p> <p>网上聊天记录导出方法有许多种,然而我这旧手机不能 ROOT,自带的备份软件备份不了 QQ,会提示权限错误。捣鼓了半天没有可行方法,可谓是山重水复疑无路。</p> <p>所幸找到了<a class="link" href="https://www.cnblogs.com/roadwide/p/11220211.html" target="_blank" rel="noopener" >这篇文章</a>,其中提到的第四种方法,尝试过后成功导出记录。</p> <p>但有必要事先说明:不清楚是我的设备的问题还是方法本身的问题,总之虽然成功导出但只有一部分,并没有达成符合预期的效果,但至少看到了以前的部分聊天记录还是颇为欣慰,毕竟也只剩下这一个办法了。</p> <p>当然其实后面又想到了别的方法,可能会有更好的效果,后文会提到。</p> <h2 id="我的情况">我的情况</h2> <p>手机 Android 版本为 4.3,内存空间寥寥无几,系统过旧无法安装新版 QQ,未解锁 OEM,无法 ROOT,可以开启开发者模式使用 adb 调试。手机上安装有远古版本 QQ,也无法使用。手机自带备份软件,但无法备份 QQ,会显示权限错误。</p> <h2 id="导出方法">导出方法</h2> <h3 id="工具">工具</h3> <p>由于资源比较难找,这里我将导出过程中我所使用的工具打包分享,均来自于网络,不一定适合所有情况。</p> <p><a class="link" href="https://pan.reincarnatey.net/BlogResource/QQ_recovery.zip" target="_blank" rel="noopener" >&raquo; QQ_recovery.zip &laquo;</a></p> <h3 id="原理">原理</h3> <p>旧版(非 QQNT 版本)QQ 的聊天记录相关文件主要存储于</p> <pre tabindex="0"><code>/data/data/com.tencent.mobileqq/ </code></pre><p>下的 <code>databases/&lt;QQ号&gt;.db</code>,<code>databases/slowtable_&lt;QQ号&gt;.db</code> 以及 <code>files/kc</code>,只需要获取到这三个文件我们就可以导出基本的聊天消息文字记录,更多的图片、语音等也在该目录下可导出。</p> <p>获取该文件一般需要 ROOT,因此这里我们只能使用别的方式,我们可以利用 adb 的备份功能将该文件提出出来。</p> <h3 id="使用-adb-备份">使用 adb 备份</h3> <p>首先手机开启开发者模式,允许 usb 调试,然后连接 adb。</p> <p>在 <code>AndroidManifest.xml</code> 中有一个参数为 <code>allowBackup</code>,表示该软件是否允许使用 adb 备份。尽管我手机上已经是七八年前的版本,但该属性已设置为 <code>false</code>,但好在发现 QQ 更远古的版本中该属性还是 <code>true</code>,因此这里我们需要保留数据卸载并安装允许备份的版本。</p> <p>保留数据卸载:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>adb uninstall -k com.tencent.mobileqq </span></span></code></pre></div><p>安装允许备份的旧版后,进行备份:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>adb backup com.tencent.mobileqq </span></span></code></pre></div><p>备份时需要在手机上点击同意,备份可能需要一些时间,会在当前目录下生成一个扩展名为 <code>.ab</code> 的备份文件。</p> <p>如果备份文件只有 1kb 那就是失败了,可能是软件版本不对或 adb 与设备 Android 版本不对应。旧版 Android 不兼容高版本 adb,我这里 4.3 使用 1.0.32 版本的 adb 是可以的。</p> <h3 id="提取数据">提取数据</h3> <p>提取出数据后,我们这里使用 <a class="link" href="https://github.com/nelenkov/android-backup-extractor" target="_blank" rel="noopener" >android-backup-extractor</a> 从<code>.ab</code> 中提取文件:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>java -jar abe.jar unpack backup.ab backup.tar </span></span></code></pre></div><p>之后直接解压即可。</p> <h3 id="导出聊天记录">导出聊天记录</h3> <p>这里使用的是 <a class="link" href="https://github.com/QQBackup/QQ-History-Backup" target="_blank" rel="noopener" >QQ-History-Backup</a> 这个库。</p> <p>在导出的文件夹里找出我们需要的文件之后,放入对应的文件夹内,接着按照教程使用该工具导出即可。</p> <h2 id="另一种或可行方法">另一种或可行方法</h2> <p>在备份完之后,<code>.ab</code> 文件其实也可以使用 adb 将其恢复到其他设备上(例如模拟器)。</p> <p>我们可以选择在新设备上将 QQ 更新到最新版尝试打开,也可以从可以 root 的新设备上更方便的直接拿到我们需要的文件。</p> <p>这两种方法我都没有试过,或许效果比我更好。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li> <p><a class="link" href="https://www.cnblogs.com/roadwide/p/11220211.html" target="_blank" rel="noopener" >安卓QQ聊天记录导出、备份完全攻略 - roadwide - 博客园</a></p> </li> <li> <p><a class="link" href="https://github.com/QQBackup/QQ-History-Backup" target="_blank" rel="noopener" >GitHub - QQBackup/QQ-History-Backup: 【停更】QQ/TIM 聊天记录导出为 HTML,支持图片、语音,可 GUI 与 非 GUI 操作 (Python)</a></p> </li> <li> <p><a class="link" href="https://github.com/Yiyiyimu/QQ-History-Backup" target="_blank" rel="noopener" >GitHub - Yiyiyimu/QQ-History-Backup: QQ聊天记录备份导出,支持无密钥导出,图片导出。无需编译有GUI界面。Backup Chating History of Instant Messaging QQ.</a></p> </li> <li> <p><a class="link" href="https://github.com/roadwide/qqmessageoutput" target="_blank" rel="noopener" >GitHub - roadwide/qqmessageoutput: 安卓QQ聊天记录导出</a></p> </li> <li> <p><a class="link" href="https://github.com/117503445/qq_get_message" target="_blank" rel="noopener" >GitHub - 117503445/qq_get_message: 2020年从安卓QQ数据库提取聊天记录</a></p> </li> <li> <p><a class="link" href="https://www.cnblogs.com/inkqx/p/15025473.html" target="_blank" rel="noopener" >利用adb备份app的数据 - 小七闲 - 博客园</a></p> </li> <li> <p><a class="link" href="https://blog.csdn.net/qq_17798399/article/details/124950288" target="_blank" rel="noopener" >ADB读取和备份安卓应用数据(无Root)_adb backup-CSDN博客</a></p> </li> <li> <p><a class="link" href="https://github.com/nelenkov/android-backup-extractor" target="_blank" rel="noopener" >GitHub - nelenkov/android-backup-extractor: Android backup extractor</a></p> </li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/6/7
阅读更多

拾光纪行 | 省选联考 2024 游记

<h2 id="前言">前言</h2> <p>第二次省选了,这次的心态又与去年有所不同。</p> <p>一来是因为,NOIP 成绩并不优秀,不抱希望,也就没有压力。二来是因为,很久没有碰 OI 了,自 THUPC 后还是会来机房,但是来机房基本只是找机会跟女朋友聊天和看着和她的聊天记录发呆,顺便补补文化课。</p> <p>毕竟实力就在这个地方了。如果我 NOIP 后继续停课到现在,进队的概率或许还能看得见,但我最好最好也就是打个 Ag 潦草收场,然后去跟本来就能裸分的大佬抢清北羟基。什么啊。</p> <p>去年参加省选时也没多大希望,高一嘛来试试水,本来想着还是高二冲队,谁知道 NOIP 后半场一败涂地直接退役。</p> <p>但不管怎么说,这次比赛也宣告了我本赛季的结束,姑且是写了游记。下一次重新面对 OI,应该是年底了,届时是真的退役赛了。</p> <p>另外,其实去年参加时也写了游记,但并未公开,本次一并附在附录中。</p> <h2 id="游记">游记</h2> <h3 id="day--4-1">day -4~-1</h3> <p>考前一周才想起来三月初要打省选。</p> <p>谁爱打谁打。</p> <p>什么,还要占我一个周末?去你的吧。</p> <p>什么,可以提前一天回家?我马上来。</p> <p>本来打算 day0 早上回家,但是 day-1 是我可爱的女朋友的生日,所以特意提前到 day-1 晚上就回去了,码了好久的字然后定时发到空间了嘿嘿。</p> <p>当然这一周也不是什么事都没干。day-4 vp 了 YDOI R1,打了 220 跑路了。day-3 vp 了 HBOI day1,赛时 T1 死活不知道哪里错了,只得了 50 pts,而且对的还是后半部分的点,诧异,day-1 的时候发现是最后扫一遍统计答案的时候忘记判 inf 了,改了之后一发过掉。应该算签到成功了吧。</p> <h3 id="day-0">day 0</h3> <p>上午没学 OI,但是和她断断续续地聊了一些。</p> <p>中午吃完饭没多久就出发去考点学校了,去年在广州,有些远;今年在东莞,近了许多。先去了酒店放了行李,楼层很高,拍了很多照片。然后去了考点学校报道,领了牌子和赠品。今年怎么有赠品?去年怎么没有?去的时候考点学校的学生还在上课,哈哈。</p> <p>试机的时候打了个不带懒标记的线段树,测了小数据一次过了,非常好。</p> <p>之后回酒店点了晚餐,之后开始打模版手速赛。十三道模版题,前十道一路打过去基本都是一次过,最后三道是三种联通分量,忘干净了,边双点双强联通的代码全记混了,打崩溃了,遂跑路。</p> <p>最后成功比室友多打了一倍的模板,拿下手速赛第一名。赛后看了以前的边双代码,把边双改出来了。点双和强联通一点都不想打,跑路。</p> <p>本来想着姑且尊重一下比赛要早点睡的,没忍住,和她聊到了一点多。</p> <h3 id="day-1">day 1</h3> <p>早上起来还算精神。</p> <p>吃了早餐,提前大概半小时赶到了考点学校。打不到车,最后加了钱才打到的。</p> <p>开考提前三分钟给了解压密码,先看了数据点只看得出来有一题图论。后面发现 pdf 没密码可以直接打开,火速看题。</p> <p>T1 读题:我题目背景呢?怎么全是公式?好不容易读明白题,想了一会感觉好复杂,后面套回题目背景上之后好理解多了,有了一些头绪。</p> <p>毫无疑问,先翻转坐标系把目标位置整到第一象限,然后接下来大力讨论,然后写着写着发现讨论不明白。</p> <p>注意到风吹和移动的顺序不重要,以及具体行动路线也不重要,在某些情况下只关心吹完风之后的位置到目标位置的曼哈顿距离,但存在特殊情况。</p> <p>自然而然想到,先计算一整轮风吹下来后的情况,枚举最后一次吹风,再讨论出前面轮数,然后总天数取最小值即是答案。</p> <p>对于一整轮风的情况,贪心的尽可能往目标点走,所以将所有向负方向吹的风尽可能抵消,得到了一轮下来的位移以及可以自由安排的移动距离,接下来只需要枚举后讨论。</p> <p>写的时候发现讨论不出来,但可以用来 check,遂退一步,枚举后二分出总轮数。</p> <p>过掉小样例,然后在第二个样例发现寄了;修修改改过掉了第二个样例,然后在大样例发现假了。遂开摆。好想她。</p> <p>T2 有一些思路,感觉这个博弈是假的,二进制拆位去贪心就好了。但发现这个定向加强很不好做,遂开摆,只打了最低一档暴力和做 $m=0$ 的特殊性质分,特殊性质写了半天 01 Trie 过不去,最后全都上了暴力。好想她。</p> <p>T3 不可以,总司令。检查了所有代码。好想她。</p> <p>结束了,四个半小时纯坐牢。出校门的时候风很大,衣服没帽子,耳朵冻没知觉了,打到了车但是要十分钟才到,冷死我了。</p> <p>缓过来之后突然发现她不对劲,还以为在学校出事了,吓死我了。终于回到酒店,本来想出去吃的,被风冻怕了不敢出门,还好酒店里全天开着暖气,这才活过来。</p> <p>下午摊着了,迷迷糊糊的和她聊天,等消息的时候好像睡着了一会。中间点了个肠粉当晚餐,之后和她聊了五个多小时,这个周末最美好的一段时间。</p> <h3 id="day-2">day 2</h3> <p>早上六点多起来了一次,发现室友通宵了。又躺回去,好像没睡着,一直到了七点才起床。</p> <p>和她聊天的中途赶到了考场,提前十五分钟进了考场等开考。</p> <p>我一点都不想比赛,为什么要比这么久,好想继续和她说话。</p> <p>开题。</p> <p>T1 这个博弈有点意思,很显然有一个最基本的贪心。最开始的思路是,从确保 $1$ 不被选开始,尽可能让 $2,3,\cdots$ 也选不上,通过这个找到要唤醒的结点,最后搜一遍出答案。</p> <p>小样例过掉,第二个样例就寄了。</p> <p>好想她,我为什么在这里做题。</p> <p>突然注意到题目一个要求是必须走完所有子树才能回到根节点,所以发现前面思路单纯按数字大小来贪是不对的,应该按当前能走到的最小值来贪。</p> <p>想到思路了,大改特改。有点分治的想法在里面,维护了结点子树中能够到达的最小结点,然后 solve 会在当前子树中进行贪心,然后会根据决策,进入到不同的子树中去 solve。</p> <p>写完之后我很满意,debug 了流程很符合我的想法,简直神来之代码。结果还是一样,只过掉了第一个样例。</p> <p>好想她,我不想做题了。她今天放假,我想找她说话。</p> <p>手模了第三个样例中的一个数据就发现,问题出在选取要唤醒的结点的地方。试着换了别的贪心方法也过不去,估计这个地方不是贪心而是某种规划。。寄!</p> <p>我在这里比赛不如拿这个时间和她聊天多好。</p> <p>最后改成了自己认为正确率比较高的决策方法后就润去写剩下两题暴力了。</p> <p>T2 发现是我最不擅长的概率与期望之类的题目,虽然我觉得这个概率好像是假的。不管怎么样,我发现当 $n+k\le 10$ 的时候还是可以用 next_permutation 水过去的,于是火速开写。</p> <p>这暴力都不简单。check 是按照约束对整块碎片建 DAG,然后跑拓扑排序来 check。然后写了个 gcd 和 qpow 完成了有理数取模。中间还记错逆元的次数了,一直写成了 $mod-1$ 次方,我说怎么 qpow 跑出来一直是 $1$。</p> <p>最后是过掉了范围内的样例,剩下的部分就不可以总司令了,我相信总是会出现一个的(</p> <p>好想她,为什么不能打完暴力提前交卷。</p> <p>T3 逆天题面,给我看傻了。后半部分题目不是很懂,索性 <code>puts(&quot;1&quot;)</code> 了。</p> <p>还剩下十几分钟,好无聊,好想她。</p> <p>最后一次完全检查了代码没有出现去年的错误,于是开始玩起了电脑。启动了虚拟机看看,发现 vs code 不会配置,于是在终端用 gcc 编译,结果编译出来的程序无法运行。退出来研究了一下别的 ide。</p> <p>终于结束了,什么都结束了。耶,终于可以和她说话啦。</p> <p>比完赛马上就回去吃午饭,吃完之后收拾了东西,酒店退了房,然后就离开东莞回家了。路上因为给她发消息看手机,晕车很严重,差点没撑住。到家之后继续聊天,真好。</p> <h2 id="后日谈">后日谈</h2> <p>Day1 蓝黑黑,Day2 紫黑黑,真有你的省选联考。</p> <p>没想到今年也签到失败了,太菜了,day0 打的模板一个没考到,不过无所谓。感觉最重要的是和她聊了好多,但我觉得还不够,要是假期就好了。</p> <p>不过接下来还是专心 whk,恋爱脑也得收收。老婆和学校哪个重要我还是掂的清的,不能因为学习耽搁了恋爱(?)。</p> <h2 id="附录">附录</h2> <h3 id="省选联考-2023-游寄">省选联考 2023 游寄</h3> <p>注:以下是去年写的,格式不好,也没用 Latex。而且当时也不敢提到女朋友的事,就单纯写了游记。现在看着蛮难受的,但总之就是一起放出来了。</p> <h4 id="day--1">Day -1</h4> <p>虽然初三暑假才开始学的算法,但我对自己还是挺有信心的,基本上算法我是不会写错的(但是题目会不会做就是另一码事了),所以一上午在学校机房里就只是简单的看了下各种数据结构的代码后就继续学新东西。看了b站上关于计算几何的一些视频,向量、凸包、旋转卡壳、最近点对、随机增量之类的,除了随机增量以外基本都很简单很好理解。下午就坐了两个半钟的车到省会,先去酒店放好行李然后就去考点了。</p> <p>结果走错门了,门口一大群家长,然而当时我还没发现,因为看到了好几个学生,估计他们也是打车打错了。好在当时周五下午,等了一会正好考点学校放学,学校大门开了,我就随着那些家长一起进去了。虽然因为走错门所以完全看不懂地图,不过看路标还是找到了报到的地方。因为弱校校队只有我有省选资格,所以教练也没跟着来,于是报到处的老师直接把整个文件袋都给我了,里面除了我的胸牌还有教练的胸牌和文件之类的,打算回学校后拿给他。听到对其他学生说有饭票,考完可以在考点学校的食堂吃,发现自己没有,或许是需要学校提前订吧,感到有些悲哀。</p> <p>然后就回酒店了,复习主要是看了一下比较容易混淆的一群Tarjan算法还有突然想不起来和spfa有啥差别的dijkstra。然后就学了一下新的东西,比如爬山和模拟退火,还有一些乱搞(骗分但不完全骗分)。</p> <h4 id="day-1-1">Day 1</h4> <p>昨晚没睡好,悲。但还好比较精神,可能我本来在学校就天天凌晨睡,所以没太在意。早上吃完早餐就直接去考点了,结果走到昨天那个门这时候才发现走错了,不过时间很充裕,后面又坐车坐到了正确的门。在考场门口又看了下昨晚的模拟退火,总感觉能用上。进去后就看注意事项然后等开考,旁边的哥们在趴下休息,我由于第一次参加省选,格外的精神。想着D1能不能切一题暴俩题拿个200。输完神奇的压缩包密码和pdf密码后,一看目录感觉T3名字眼熟,多半是图论。</p> <p>T1看完之后有点不是很明白,就看了看T2,一眼DCC,但是对于是边双还是点双有点不确定,但是能确定最后缩完应该是棵树。一看对大数取模就知道这题凉了,因为我对于这种树上dp+大方案+容斥很不会,虽然前不久才做过一道,于是就赶紧回T1了。第一眼觉得跟差分有关,但是试了试没推出来结论,所以最后选了线段树维护并查集。写+调一直到了十一点,不过由于已经放弃T2了,所以压力不是很大。最后大样例跑的飞快,写出来的那一刻还是挺高兴的。之后T2再看一眼发现还是不会于是赶紧跳T3,想着剩下差不多两个小时打个暴力四十分,D1就算圆满收官。一看题目,发现挺像之前做过的一道模拟费用流,但是又感觉很不一样。题目看到一半的时候想到刚学的模拟退火,感觉很能做,每次随机让某位员工往下走,结果往后一看发现居然是动态的,心里偷偷竖中指。于是只好敲暴力,结果发现数组开不了那么大,无奈只能放弃66666的点,最后小样例跑的飞快,大样例直接爆数组RE,D1到此结束。</p> <p>中午吃过饭后就一直待在酒店里看电脑,翻了翻wiki感觉没啥想学的,会的懒得看,不会的看不懂,于是就一直在洛谷讨论区里翻来翻去看大家“关于省选”。然后发现了好多和自己做法一样的,发现T1正解居然只是道贪心,估计我最近数据结构写多了,后来发现确实有差分的做法但很容易挂。然后下午就出代码了,发现我竟然手误给几个没有返回值的方法写了返回类型,寄,T1爆零了估计。没有拿noilinux虚拟机编译一次,不然估计不会有这个问题,确实可惜,但我确实怎么也想不到居然我真的会写出这种低级错误,还是在考场上,幸好我本来就是来试试水,没打算冲队,压力没那么大。</p> <p>晚上突然在b站刷到了用DQN训练只狼AI的视频,点进去就出不来了。估计因为我学AI的时间是我学OI的时间的好几倍吧,我感觉炼丹才更像是我的主业,OI只是感兴趣?不过前几年就在书的尾页中看到过DQN的介绍,对这一种直接输入屏幕图像的训练方式特别感兴趣,当时还跟朋友说打算用DQN训练某个我很喜欢玩的小众游戏的AI,结果后面找了发现DQN的资料实在太少,而且网上实现的DQN玩的基本都是叫A什么的某种很古早的只有几百个像素的电子游戏,所以最后放弃了,结果这时候突然发现有大佬用DQN实现玩现代游戏,还开源了出了教程,真的很震惊,于是就乐呼呼的看到了晚上都没碰OI。</p> <p>D1理想分,100+0+48,实际估分0+0+40左右(T1其实洛谷能编译而且打了90,但CCF测评机应该是会CE的),目前民间0+0+30左右。虽然我会说没爆零就是胜利,但实际上我估计会等官方数据出来以后在洛谷交一次T1,再把T1分加回去当做我的非官方成绩,毕竟不是很在意官方排名,只是想看看自己能打成啥样。</p> <h4 id="day-2-1">Day 2</h4> <p>昨晚睡得还不错,而且这次没走错门。来到考场门口听见有同学说D2肯定有计算几何,我心里偷偷一笑然后赶紧复习计算几何,顺便看了眼模拟退火,然而结果没有考到。进了考场以后隔壁的哥们又在趴下休息,我还是格外的精神。开考念密码,跟昨天的反过来,哈哈。</p> <p>一看目录,看到game就感觉要来博弈论了,但是我不急,先看T1,发现好像又是大模拟博弈论,寄。当时感觉有点像启发式搜索,但又想不出来怎么写启发函数,考虑他一个网格地图感觉会跟曼哈顿距离有点联系,一看样例又看不懂了,我手推都不知道样例答案怎么来的,想不明白为什么另一颗被困住的红棋也会移动(还没看题解,不过此时写这篇游记的时候我突然有点想法,感觉黑方的启发函数应该是棋子到第一行的距离+两颗红方棋子到黑方的曼哈顿距离, 红方可能是棋子到黑方的曼哈顿距离的相反数?这么一来确实能解决我那个疑惑。显然不是IDA*,那么肯定是A*,并且可以用记忆化搜索标记一下状态来判平局,欸卧槽好像感觉突然会了)。看到输入里面有数据编号就感觉有好事,火速看数据范围,嘶……显然,像我这种考场上没想出的正解的蒟蒻,肯定是针对每一种数据去写暴力啦,看到特殊性质A送分,我就直接跳到T2去了,打算最后一小时再回来写T1。</p> <p>看到T2,果然是博弈论(至少当时我是真的以为是博弈论)。一上来先输入的时候判断一下能不能满足B互不相同,然后提前计算一部分已经确定的答案然后剔除出去。那么就剩下最后一部分了,都是B有俩选择的,这时候就感觉有点像动态规划,但是想了半天,不会表示状态。好吧,老实暴力。然后,直接敲了个2^n的dfs+回溯和剪枝出答案,测第一个样例过了,测第二个发现直接就寄了。然后死活想不明白为什么暴力都写错了,一直调一直改,差不多到十点的时候我就怀疑是我关于A的部分有问题,于是就转而投靠刚学几天的模拟退火,在A的有的选的部分中随机一个换,然后再对B暴力求答案,结果调半天还是错(现在看来,感觉是前面那一段剔除确定部分的地方出了问题。。),直到快十二点,赶紧看眼T3看看能不能骗分,发现样例好多1而且只对第一问也有分,就写了个输出1和一个随机数,然后回到T1写暴力。发现暴力不会写,只有性质A比较稳,后面的BCD好像都有乱写的成分在的,然后D2到此结束,我的第一次省选也结束了。</p> <p>看洛谷有大佬评价说D2确实很难,而且T2好像不是博弈论,性质A是匈牙利、C是费用流,但我考场上确实没看出来,而且暴力也写挂了,只能说D2不爆零就是胜利。</p> <p>D2理想分30+5+0,实际估分20+0+0,目前民间10+0+1(T3没想到骗到了一分hhh)</p> <p>总的来说,感觉确实很难,但又好像比我想象中的简单(或许是因为我压根不去考虑正解吧),这次官方分数估计就是40左右,至少对我来说已经很不错了,毕竟我才学了不到一年。不过D1T1果然还是略有点遗憾,不过当时忘记随机化了,所以不能指望官方数据全随机,可能确实会T几个点吧;D1T2这类题感觉很难但我可以掌握,再学一年的话应该能做出来,毕竟感觉这一类题都思路很明显,缩成树后dp+容斥嘛;D1T3,明年的我写的暴力估计会更优吧;D2T1出考场才想到思路,D2T2和T3相信明年的我能写出暴力。但值得庆幸的是,虽然我这次犯了很多问题,但我都找到了可以改进和避免的方法,至少下次应该不会错在同一个地方了,而我没有下下次,所以,这次确实攒下了很好的经验,值了!</p> <p>最终官方成绩没到40,太难看就不说了,不过挺可惜的是后面把D1T1考场代码里手误写的的几个没有return的函数的返回值改成void之后官方数据AC了,然而实际上虽然没有CE和RE但是也没有分。</p> <p>最后的最后,鼓励一下自己,顺便立个志向:<em>数据删除</em>(注:搬过来的时候这一段我还是删了,实际考的远达不到这个,就不放出来丢人了)</p> <h3 id="参考文献">参考文献</h3> <p>我去年写的别的游记。</p> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/3/3
阅读更多

踩坑记录 | Win11 输入法切换语言快捷键失效

<h2 id="问题描述">问题描述</h2> <p>因为经常需要输入英文或者关闭输入法,所以一直习惯在 Windows 里添加两种语言,然后用 Alt+Shift 切换语言,就可以很方便的打开和关闭输入法了。</p> <p>当然,这里的关闭输入法实质上是切换到英文输入法。</p> <p>不知道我电脑发生了什么(或许是一次自动更新),某一次开机之后我就发现我无法使用 Alt+Shift 快捷键切换语言了,而且右下角托盘的输入法设置里也无法切换成英文输入法、</p> <p>我的电脑系统是 Windows 11 家庭中文版,其他的版本貌似没有出现过这个问题。</p> <h2 id="解决方案">解决方案</h2> <p>最初我发现,<strong>打开系统语言设置后将两种语言的顺序调换一下</strong>就可以恢复正常。但是开机后会被重置,需要重新进行一遍操作,而且有概率失败,需要重启电脑再试一次。</p> <p>最开始我也是不厌其烦的这样做了,毕竟不能切换语言更加难受,但最近发现这样的方法失效了,按网上说着试了也不行,最后自己捣鼓出来了一个解决方法:</p> <p><strong>在系统语言设置中删除英语,然后重新安装。</strong></p> <p>成功的标志是,在系统托盘的输入法设置中,可以选择 “英语(美国) - 美式键盘” 这一项。</p> <p>我使用了这个解决方法,目前没有出现问题。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <p>网上没一个管用的,无法参考。</p> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2024/2/16
阅读更多

随笔杂谈 | 2024 新春快乐!

<p>不知不觉间这个博客过了一年多了,迎来了第二个新年,年份归档中也终于能见到第二项。</p> <p>忙于学业和 OI,这一年里我写和研究的项目越来越少,所学习的事物也越来越少。博客也更新的不多,绝大部分字数还是我的游记,中间网站甚至瘫痪了不知多久。</p> <p>在这一年里,我也变化了许多,About 中记载的许多内容不再切实甚至不再有必要写下。另外我也从今年开始将按照中文符号使用规范编写文章,博客中还有很多地方使用错误或不准确(如中英文之间未加空格)的地方,会在看到的时候将其修补。</p> <p>许多想法放弃的同时,也有许多新的想法出现。我会将其记录于 Plans 中,等待未来有机会将其实现。</p> <p>不知道博客究竟有多少人来看过,但每次收到评论,或是出现新的 backlinks 的时候都会十分欣喜。</p> <p>值得一提的是,一月初的时候给本博客抢了一个还算不错的萌备号。另外其实年初一的时候因为设备原因写不了文章,但其实文章的时间是可以修改的,是不是当天也无所谓,所以本文章其实是在初七补写的。</p> <p>这个博客仍然很新,但已经有所成长。期待在新的一年里,变得更新欣欣向“龙”。</p>

2024/2/10
阅读更多

学习笔记 | 为 MuMu 模拟器 12 安装 HttpCanary 证书

<h2 id="update">update</h2> <p>目前 MuMu 模拟器官方已在 <a class="link" href="https://mumu.163.com/help/" target="_blank" rel="noopener" >MuMu 模拟器常见问题</a>页面中更新了关于 MuMu12 抓包的官方教程,<strong>本文不再适用</strong>。</p> <ul> <li> <p><a class="link" href="https://mumu.163.com/help/20240814/40912_1174291.html" target="_blank" rel="noopener" >MuMu 模拟器 Charles 抓包教程(官方)</a></p> </li> <li> <p><a class="link" href="https://mumu.163.com/help/20240809/40912_1173300.html" target="_blank" rel="noopener" >MuMu 模拟器 HttpCanary 抓包教程(官方)</a></p> </li> </ul> <h2 id="原因">原因</h2> <p>最近需要对软件进行抓包,但是 HttpCanary 的证书无法正常安装。按照教程点击安装,但是却弹出”未安装该证书“。这是官方的说法:</p> <blockquote> <p>部分用户在使用MuMu模拟器X版本的过程中需要进行抓包一类的操作,但发现无法安装导入证书,无法正常抓包,这是因Android7.0之后新版本系统的安全限制导致(Android7.0 之后默认不信任用户添加到系统的CA证书)</p> </blockquote> <p>官方给出了使用 adb 安装的方法,网络上也有一些相关博文,然而都是使用其他抓包工具,没有使用 HttpCanary 的,所以自己记录一下。</p> <p>我的 MuMu 模拟器 12 的版本是 V3.6.11(2438)。</p> <p>由于我过程中没有截图,所以这里只简单用文字描述一下过程,如果有疑惑的话可以查看<a class="link" href=".#%e5%8f%82%e8%80%83%e6%96%87%e7%8c%ae" >参考文献</a>中的相同步骤的截图。</p> <h2 id="安装流程">安装流程</h2> <h3 id="原理">原理</h3> <p>用 adb 直接把证书移到系统证书位置 <code>/etc/security/cacerts/</code> 里。</p> <h3 id="模拟器设置">模拟器设置</h3> <p>打开 MuMu 模拟器设置界面。</p> <ul> <li> <p>在“磁盘”中将“磁盘共享”改为“可写系统盘”。</p> </li> <li> <p>在“其他”中将“开启手机Root权限”选中。</p> </li> </ul> <p>当然,在完成安装流程后这些都是可以改回去的。</p> <h3 id="导出证书">导出证书</h3> <p>在 HttpCanary 设置界面中,进入&quot;SSL 证书设置&quot;,点击“导出HttpCanary根证书”,选择“System Trusted(.0)”格式,导出证书默认就在 <code>/HttpCanary/cert/</code> 下,我的文件名是 <code>87bc3517.0</code>。</p> <p>参考文献里其他的抓包工具只能导出 .pem 格式,需要用 OpenSSL 重新签发为 .0 格式,而 HttpCanary 直接为我们提供了 .0 格式的导出,很方便,减少了很多步骤。</p> <h3 id="adb-安装证书">adb 安装证书</h3> <p>如果不了解或者没有 adb,可以先在其他博客中自行学习一下。</p> <p>首先连接到 MuMu 模拟器并申请权限:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>adb connect 127.0.0.1:7555 </span></span><span style="display:flex;"><span>adb root </span></span><span style="display:flex;"><span>adb shell </span></span></code></pre></div><p>其中第二步运行后,模拟器会有一个弹窗,显示“Shell 正在请求超级用户访问权限“,选择“仅此次”,点击允许。</p> <p>第三步运行后,提示符会发生变化,这时候就进入 Android 的 shell 了。</p> <p>将证书文件放入系统位置:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mv /sdcard/HttpCanary/cert/87bc3517.0 /etc/security/cacerts/ </span></span></code></pre></div><p>可以检查一下是否成功移动文件:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#e5c07b">cd</span> /etc/security/cacerts/ </span></span><span style="display:flex;"><span>ls 87bc3517.0 </span></span></code></pre></div><p>确认后证书就安装完毕了,可以输入 <code>exit</code> 按下回车退出 shell。</p> <h3 id="安装完成">安装完成</h3> <p>这时候启动 HttpCanary 开始抓包,软件可能仍然会显示提示条,不要紧,打开软件试试,如果能抓到 Https 请求就代表证书安装成功了,可以正常抓包了。</p> <p>当然,有些软件会有 root 检查,启动会闪退,你可以把 MuMu 模拟器设置修改回去,没有影响。</p> <p>然而有些软件同时还有证书验证,可能开始抓包后会无法联网(没错,说的就是你,森空岛。),难绷。</p> <h3 id="后续">后续</h3> <p>写完上面一大堆之后,不知道在写博客的时候是哪里捣鼓坏了,我的抓包又变成之前那样没法抓了,但是当时刚搞完确实是可以抓的。</p> <p>后面又按照 <a class="link" href="https://7e2hj.com/burp%e6%8a%93%e5%8c%85mumu%e6%a8%a1%e6%8b%9f%e5%99%a812%e6%95%99%e7%a8%8b/" target="_blank" rel="noopener" >这篇博客</a> 整了一下,效果也只持续了一下,重启模拟器之后就也不能抓了,不知道是什么鬼。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li> <p><a class="link" href="https://www.cnblogs.com/wutou/p/17873632.html" target="_blank" rel="noopener" >网易mumu模拟器安装下载charles的CA证书 - 悟透 - 博客园</a></p> </li> <li> <p><a class="link" href="https://mumu.163.com/help/20221018/35047_1047210.html" target="_blank" rel="noopener" >MuMu模拟器X版本如何安装证书?_MuMu模拟器_安卓模拟器</a></p> </li> <li> <p><a class="link" href="https://blog.csdn.net/tainqiuer123/article/details/128547580" target="_blank" rel="noopener" >burp抓包mumu模拟器_mumu模拟器如何安装信任凭证-CSDN博客</a></p> </li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于<a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循CC BY-NC-SA 4.0协议,复制请保留原文出处。</p>

2023/12/31
阅读更多

学习笔记 | ripgrep(rg) 如何排除文件夹

<h2 id="前言">前言</h2> <p>最近又开始捣鼓博客模板,要搜索关键词的时候总是被 <code>public</code> 目录下的文件干扰,但直接删除这个文件夹的话又会导致 git 需要重建,所以就学习了下搜索时排除文件夹。</p> <h2 id="命令">命令</h2> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#e06c75">rg</span> <span style="color:#98c379">&#34;content&#34;</span> <span style="color:#e06c75">-g</span> !<span style="color:#e06c75">public</span> </span></span></code></pre></div><p>这样就可以在搜索 &ldquo;content&rdquo; 的时候排除 <code>public</code> 文件夹下的文件了。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li><a class="link" href="https://www.cnblogs.com/Tokubara/p/15754418.html" target="_blank" rel="noopener" >ripgrep(rg)忽略某些文件(exclude, ignore) - Tokubara - 博客园</a></li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于<a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循CC BY-NC-SA 4.0协议,复制请保留原文出处。</p>

2023/12/10
阅读更多

踩坑记录 | OneIndex 首页出现 HTTP ERROR 500

<h2 id="踩坑">踩坑</h2> <h3 id="背景">背景</h3> <p>最近突然发现自己的网盘首页上不去了,然而其他页面却能打开,唯独首页不行,怪异。</p> <p>出现问题前后没动过源码,onedrive 账号当然也没有问题,查了 api 手册貌似也没有变动,重启服务器也是一样的情况,怎么回事呢。</p> <p>发现主机服务商帮我自动升级了 PHP 版本,猜想应该是由于版本升级带来的兼容性问题。</p> <h3 id="问题描述">问题描述</h3> <p>无法打开 OneIndex 的首页(根目录),但其他页面可以正常访问,尝试过重启甚至重装都是一样的情况。</p> <p>这里我的 OneIndex 是自己以及别的大佬魔改后的 OneIndexN,但从代码上分析,应该所有的未经修复的 OneIndex 版本都会出现该问题。</p> <h3 id="解决方法">解决方法</h3> <p>修改 <code>/lib/onedrive.php</code> 的 <code>urlencode</code> 方法:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#c678dd">static</span> <span style="color:#c678dd">function</span> <span style="color:#61afef;font-weight:bold">urlencode</span>(<span style="color:#e06c75">$path</span>){ </span></span><span style="display:flex;"><span> <span style="color:#c678dd">if</span>(<span style="color:#e06c75">$path</span> <span style="color:#56b6c2">==</span> <span style="color:#98c379">&#39;/&#39;</span>) <span style="color:#c678dd">return</span> <span style="color:#98c379">&#34;&#34;</span>; <span style="color:#7f848e">// 在最开始加入这一行 </span></span></span><span style="display:flex;"><span><span style="color:#7f848e"></span> <span style="color:#c678dd">foreach</span>(<span style="color:#e06c75">explode</span>(<span style="color:#98c379">&#39;/&#39;</span>, <span style="color:#e06c75">$path</span>) <span style="color:#c678dd">as</span> <span style="color:#e06c75">$k</span><span style="color:#56b6c2">=&gt;</span><span style="color:#e06c75">$v</span>){ </span></span><span style="display:flex;"><span> <span style="color:#c678dd">if</span>(<span style="color:#c678dd">empty</span>(<span style="color:#56b6c2">!</span><span style="color:#e06c75">$v</span>)){ </span></span><span style="display:flex;"><span> <span style="color:#e06c75">$paths</span>[] <span style="color:#56b6c2">=</span> <span style="color:#e06c75">rawurlencode</span>(<span style="color:#e06c75">$v</span>); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#c678dd">return</span> <span style="color:#56b6c2">@</span><span style="color:#e06c75">join</span>(<span style="color:#98c379">&#39;/&#39;</span>,<span style="color:#e06c75">$paths</span>); </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>以 <a class="link" href="https://github.com/xieqifei/OneindexN/tree/master" target="_blank" rel="noopener" >GitHub - xieqifei/OneindexN</a> 为例,这一行代码应被加在<a class="link" href="https://github.com/xieqifei/OneindexN/blob/master/lib/onedrive.php#L259" target="_blank" rel="noopener" >此处</a>。</p> <p>问题应当被成功解决,现在应该可以正常访问首页(根目录)了。</p> <h3 id="分析原因">分析原因</h3> <p>经过排查,应该是由于 PHP 版本升级(我这里是被升级到了 8.2),<code>/lib/onedrive.php</code> 中的 <code>urlencode</code> 方法出现了问题。</p> <p>这个方法的作用应该是将 <code>$path</code> 分割后重新拼接,但对于根目录的情况,由于上下文传入的 <code>$path</code> 仅为一个斜杠 <code>'/'</code>,处理的过程中出于某种原因而没有起到作用,最终使得 <code>$path</code> 不合法。导致在后续发起请求时传入错误的参数,从而出现异常。因而解决方法就是在开头进行特判。</p> <p>没有仔细分析,推测是其中的某一行代码所调用的字符串处理方法在新版本中有不同的实现。</p> <p>当然,其实也有可能是 onedrive 的 api 原本支持上述错误,而后 api 更新时不再支持,这样也会出现此问题,但我认为更可能为前一种情况。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li> <p><a class="link" href="https://learn.microsoft.com/zh-cn/graph/api/driveitem-list-children?view=graph-rest-1.0&amp;tabs=http#http-request" target="_blank" rel="noopener" >列出文件夹的内容 - Microsoft Graph v1.0 | Microsoft Learn</a></p> </li> <li> <p><a class="link" href="https://github.com/xieqifei/OneindexN/tree/master" target="_blank" rel="noopener" >GitHub - xieqifei/OneindexN</a></p> </li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于<a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循CC BY-NC-SA 4.0协议,复制请保留原文出处。</p>

2023/12/9
阅读更多

拾光纪行 | NOIP 备赛停课日记

<h2 id="前日谈">前日谈</h2> <p>这次 NOIP 太重要了,如果还是打的像 CSP-S 一样烂就真 AFO 了。</p> <p>确实需要时间去好好复习备赛,遂停课。</p> <h3 id="10-月-28-日">10 月 28 日</h3> <p>其实今天白天还在学校,不过经过这一周的多方谈判协调,已经确实准备好停课了。</p> <p>早上出宿舍时带走了所有衣服,多余的东西全部收起来或者放到柜子里了,到教室后也把桌面和抽屉都清空了。</p> <p>中午在机房制定了一下接下来的停课计划,大致就是每天早上起来敲几个板子,然后就补之前的网课录播并做做题,有比赛的话尽可能打打。</p> <p>由于我自己并不是很自律,所以打算量化每日任务,如下:</p> <table> <thead> <tr> <th style="text-align: center">题目</th> <th style="text-align: center">红</th> <th style="text-align: center">橙</th> <th style="text-align: center">黄</th> <th style="text-align: center">绿</th> <th style="text-align: center">蓝</th> <th style="text-align: center">紫</th> <th style="text-align: center">黑</th> </tr> </thead> <tbody> <tr> <td style="text-align: center"><strong>分数</strong></td> <td style="text-align: center">$10$</td> <td style="text-align: center">$20$</td> <td style="text-align: center">$30$</td> <td style="text-align: center">$50$</td> <td style="text-align: center">$80$</td> <td style="text-align: center">$100$</td> <td style="text-align: center">$150$</td> </tr> </tbody> </table> <table> <thead> <tr> <th style="text-align: center">活动</th> <th style="text-align: center">参加比赛</th> <th style="text-align: center">发布题解</th> <th style="text-align: center">完成课程</th> <th style="text-align: center">记录 trick</th> </tr> </thead> <tbody> <tr> <td style="text-align: center"><strong>分数</strong></td> <td style="text-align: center">$50$</td> <td style="text-align: center">$20$</td> <td style="text-align: center">$50$</td> <td style="text-align: center">$5$</td> </tr> </tbody> </table> <p>每日要求:</p> <ul> <li> <p>模板题 $100\text{pts}$</p> </li> <li> <p>刷题 $200\text{pts}$</p> </li> <li> <p>活动 $100\text{pts}$</p> </li> </ul> <p>其实原本没打算将分数调到那么高,但是想了想,把总分定为了 $400\text{pts}$,<del>暗示我 AK NOIP</del>,这样溢出来的分就会变成 rp 加到 NOIP 里面去(</p> <p>每周六晚上比赛都蛮多的,错过了 AcWing 的周赛,不过 AcWing 的周赛一直很水,每次参加基本上算是练练签到题的手感。</p> <p>发现<a class="link" href="https://atcoder.jp/contests/abc326" target="_blank" rel="noopener" >有场 ABC</a>,但是也来晚了,比赛只剩四五十分钟,已经无法报名 rated 了,所以就简单写了下前三题。前两题没什么好说的,C 题一眼切,但我看数据范围总感觉直接暴力好像能过,所以花了几分钟写了暴力试试,发现 T 的很快后还是老老实实离散化双指针了。最后 D 题看完题感觉挺有意思,但剩下五分钟肯定写不完,遂开摆。</p> <p>然后晚上又打了场 <a class="link" href="https://codeforces.com/contest/1890" target="_blank" rel="noopener" >CF Div2</a>,迅速地切了 AB,然后花了一个小时被 C 给切了,看了下 D 也不会,猜个贪心但是已经不想写了。</p> <p>开始写了一点 CSP-S 的题解。</p> <h3 id="10-月-29-日">10 月 29 日</h3> <p>睡到了十点才起床。</p> <p>打开 CF 发现 rating 变成 1200了,升入绿名!</p> <p>继续写 CSP-S 题解,写着写着我们省出分了:$80+25+100+0=205\text{pts}$。</p> <p>感觉我能拿到的分应该在 $100+50+100+20=270\text{pts}$ 左右的,总的来说还是考差了,但貌似也没有那么差。1= 估计没什么问题,听说甚至还过了七级钩线,就是不知道省排多少,唉。</p> <p>下午打了一场模拟赛,出题人说不难,我信了。</p> <p>到点开题,发现 A 是一道数数题。仔细分析了一下发现答案是分组后进行错排列,套公式就可以了。</p> <p>错排列很好预处理,但组合数 $O(n^2)$ 预处理会爆,所以自己糊了个等价的分组公式出来,这样就可以只用阶乘表示答案了,然后顺便预处理一下阶乘的逆元即可。</p> <p>写完后过掉了样例一,但样例二输出比答案多了一点,手推了一遍发现我的程序的输出没有错,应该是自己思路本身有问题,仔细想了想发现分组后不是错排列而是圆排列!</p> <p>这下更简单了,随便改改就过掉了第二个样例。然后第三个样例又错了一堆,遂检查,发现我糊的分组公式漏除了一个东西,补回去之后式子就被化简到了更简单的形式,然后把这题过掉了。</p> <p>开 B 题,询问的拆分方式一眼出,但是不知道怎么快速统计二维前缀和,遂打表。打表出来发现里面存在一些循环节,但找不到什么可用的规律。</p> <p>打了好几种表,也往二进制上分析了,找不到什么规律,遂写了 $30\text{pts}$ 的部分分的暴力。</p> <p>开 C 题,不会,但是发现对于水位相同的连续块,其性质是一样的,所以应该会需要合并这些块,遂猜测用珂朵莉树来做。</p> <p>开 D 题,第一眼以为是莫队。想了很久,口糊了一种字符串哈希+树状数组的离线做法,感觉很正确,但复杂度是 $O(n^2+m\log n)$ 的,貌似能过一半的点,但实现起来超级复杂。</p> <p>A 题不会写暴力,不知道怎么拍,遂开摆。</p> <p>模拟赛就这么结束了,ACD 都不会写暴力,B 题只会写暴力,最后以 $100+30+0+0=130\text{pts}$ 收尾。</p> <p>结束后发现 BCD 都全场没有通过的,甚至 C 题没有人有分,以至于出题人还问我们是不是都没认真打。</p> <p>A 题我是正解,没有挂分,而且似乎是由于我糊的那个式子,跑的也比其他人快很多,不错。B 题正解是二进制拆位后打表找规律,个人觉得这个思路很好,拆完位后性质也很好,值得记录。C 题确实如我所说需要合并,但不是 ODT 而是并查集。D 题正解很复杂,得用 SA 做 LIS 和 LCS,这个处理方式也很好,值得记录。</p> <p>总的来说题目很好,但是太难了,考前出题人还说简单,被骗了!</p> <p>其实下午本来前几天报名了 GenshinOI Round 3 的,这比赛我参加过 R1,R2 也报名了但未参加,结果没想到 R3 正好撞上模拟赛所以也报名了未参加。</p> <p>晚上熬夜写完了 <a class="link" href="https://blog.reincarnatey.net/solutions/csps2023/" target="_blank" rel="noopener" >CSP-S 题解</a>,写的很全,四题一共八种做法全写上去了。</p> <h2 id="第一周">第一周</h2> <h3 id="10-月-30-日">10 月 30 日</h3> <p>睡到了快十点才起床。今天开始算是正式停课了。</p> <p>起床后先按计划打了点板子。其实我一开始决定每天早上打随机几道板子,是因为 exlg 有题单内随机跳题的功能,但是今天早上才发现原来那个随机跳题只能跳到我没做过的题目,所以不能用于随板子题了,于是就变成了闭眼随便抽。</p> <p>最后是随到了 LCA、割点、笛卡尔树的板子,挺快敲完了,模板 $+160\text{pts}$。然后就开始补这几天的停课日记。</p> <p>没错,因为前天回来之后先写了题解,一直没什么空闲,所以今天才开始写停课日记,前面的几天都是刚刚补的。</p> <p>十一点半的时候听说教练忘记报名 NOIP 了,距离报名截止还剩半小时,急急急急急!最后几分钟把报名信息交上去了,好险,停课第一天就差点被迫 AFO 了。</p> <p>下午我的新键盘到了,是我今年的生日礼物,双十一打折入的牧马人 K200!之前用的基本都是附赠的几十块钱不到的键盘或者笔记本自带薄膜键盘,第一次用这么好的,打起字来感觉都不一样了,敲代码舒服到飞起,状态极佳!</p> <p>晚上在学分治,<a class="link" href="https://www.luogu.com.cn/problem/P7482" target="_blank" rel="noopener" >P7482</a> 敲到一半去打了场 <a class="link" href="https://codeforces.com/contest/1891" target="_blank" rel="noopener" >CF Div2</a>。</p> <p>A 题很简单,由于 $n\le20$,直接特判一下需要修改的位置是否是 $1,2,4,8,16$ 即可,读完题就切掉了。B 题思考了十几分钟后也会了,直接 $O(30(q+n))$ 跳就好,于是 B 题也过掉了。C 题好像有一点点眼熟,摸了下样例感觉很能贪,于是直接写了个贪心,交上去结果直接过掉了,尽管我一点都不会证。D 题不难,但是我二分打了个表以为 $g$ 是单调的,结果并非如此,没能过掉这道题。听说 F 很板,但估计我还是太菜了,只看得出来题面长得就很板,但不知道到底是哪种 ds 的板子。</p> <p>应该算是上分场吧。其实 Div2 能过三题对我来说还算不差,D 题有点可惜,离正解不远了都。</p> <p>比完之后继续敲 <a class="link" href="https://www.luogu.com.cn/problem/P7482" target="_blank" rel="noopener" >P7482</a>,敲完后发现样例都过不去,检查一遍后发现是忘记做前缀和了,改完之后一发过了这道题。</p> <p>今天只做了一道紫题,没想到我竟然如此不自律,停课第一天开始就欠分了,考虑之后要不要增加个补回来的机制,不然真的 rp 堪忧啊。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板($160/100$)</p> </li> <li> <p><input disabled="" type="checkbox"> 刷题($100/200$)</p> </li> <li> <p><input disabled="" type="checkbox"> 活动($50/100$)</p> </li> <li> <p>今日总分 $310\text{pts}$,溢出 $0\text{pts}$,挂分 $0\text{pts}$,总计增加 $0\text{rp}$。</p> </li> </ul> <h3 id="10-月-31-日">10 月 31 日</h3> <p>睡到九点多起床。</p> <p>昨晚 CF 上分场 Rating 加了 126,感觉过几天的 Edu 有望直接升 Specialist 啊!</p> <p>今天是我的生日,一早起来领了好多红包,开心!停课后才想起来这件事的,发现这次停课还顺便避免了在学校过生日。</p> <p>发现洛谷英文字体变了,感觉不是很好看啊。</p> <p>早上起来打模板,这次直接复制所有题号,然后随便找了个在线工具去抽,抽到了字典树、Nim 游戏、逆元 2,难度黄绿绿。</p> <p>发现确实不是很熟,字典树虽然打的很熟但看漏了 $|\Sigma|$ 的值域,Nim 游戏直接把必胜必败条件记反了,逆元 2 的 exgcd 还写挂了。</p> <p>看来多些模板还是有很大好处的!</p> <p>上午发现昨晚写的题目还能投稿题解,于是写了一篇投上去,不知道能不能过。</p> <p>中午开始想 <a class="link" href="https://www.luogu.com.cn/problem/P6406" target="_blank" rel="noopener" >P6406</a> 这道题,想了一会只会一点外层做法就去看题解了,看了一会突然好奇写题解的大佬是怎样的人,于是点开他博客看了好久游记和回忆录,突然发现我好像很久之前就看过他的博客,而且至今都不知道他 NOI 打银之后到底签了哪个大学……</p> <p>两点的时候发布的题解就过审了,这么快的嘛!</p> <p>听 kkksc03 说据说大部分省的省选要提前一个月,千万别啊!</p> <p>一个下午半颓不颓的对着题解码码码改改改,好不容易写完结果答案差了好多,对着题解调了好久最后发现是少预处理一个前缀和,总算是过了这道题。</p> <p>突然发现我对自己的要求貌似只有一天两道紫题,但是我写的挺痛苦的,一时间不知道自己到底颓还是没颓。</p> <p>可能是题目问题吧,分治的合并写的太痛苦了。那道题其实还是有很多很好的 trick 的,但我也完全没有写题解的欲望了。</p> <p>写完那题之后刷到了 LibreOJ 的<a class="link" href="https://loj.ac/d/4240" target="_blank" rel="noopener" >一个帖子</a>,于是上洛谷看了看他的比赛,发现正好还剩几小时结束,想着放松一下就打一打,结果 T1 一点都不普及-,感觉都快能评普及+了,想了十几分钟才会,也是挺好的一道题目!后面两题就比较简单了,T2 推推式子就行,T3 就是 SCC 板子,愉快 AK 了。</p> <p>小比赛也是比赛!活动得分 $+50\text{pts}$。滑稽。</p> <p>请教了大佬后把昨晚的 F 题过掉了,确实板,不过逆序处理操作我上次见还是在 LCT 里见到的,把删边转为加边。</p> <p>然后去做了昨晚 D 题,死活过不去,一直 TLE,后面删了一个取模就过了,看来 <code>__int128</code> 的取模真的是慢的离谱。</p> <p>最后做了道绿色的单调栈练习题,由于双倍经验,总算是凑够了 $200$ 分。我给自己的要求到底算高还是算低?</p> <p>家长联系到了一直很想去的集训的学校!算是个好消息吧,尤其是今天还是我的生日!高兴了很多,不过明天一早就得去,感觉家长和教练比我还急……不想早起。</p> <p>去那边的话只能带笔记本去了,所以今晚还得提前给笔记本准备一下各种软件和文件之类的,打算到那边继续写日记,就是可惜昨天才到的键盘马上就用不到了。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(130/100)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 刷题(200/200)</p> </li> <li> <p><input disabled="" type="checkbox"> 活动(70/100)</p> </li> <li> <p>今日总分 $330\text{pts}$,溢出 $0\text{pts}$,挂分 $0\text{pts}$,总计增加 $0\text{rp}$。</p> </li> </ul> <h3 id="11-月-1-日">11 月 1 日</h3> <p>被迫七点起床。</p> <p>一大早出发了,坐了两个多小时车到了集训的学校,上次来还是省选前。</p> <p>一来老师先让我做了套他们的 CSP 模拟题,这应该也算比赛吧?</p> <p>A 题求树上每个点的经过这个点的链的最大点权和,看完题就会了,写了几分钟过掉了样例,结果被自己造的小样例 Hack 了,发现这个链也可以是在某个祖先处往下折,然后马上又会了,感觉有点像之前做过的题,直接维护以子节点为起点的前两个最大点权的链,然后再跑第二遍 dfs 统计答案即可,然后一发过掉了这道题。</p> <p>B 题推公式推了半天,写出来的代码却不对,诧异。遂看机房其他大佬的代码,发现别人的公式跟我的公式有一个符号是相反的,但是我自己推了感觉我是对的,然后再按自己的公式改完发现还是有问题,诧异。</p> <p>C 题要求统计以每个点为右端点的满足条件的区间个数,第一眼就想到了分治,但是只会做最大值在右边的情况,如果最大值在左边的话不知道如何为右端点统计答案。遂看机房其他大佬的代码,发现对方统计答案使用了差分,啊?想了一会,注意到题目条件具有单调性,所以可以枚举 $l$ 之后二分出一个满足条件的最大的右端点 $r$,于是 $l$ 就对所有 $r&rsquo;\in[mid+1,r]$ 都有 $1$ 的贡献,需要区间加,于是上个差分,最后做一遍前缀和就把答案统计好了,妙!</p> <p>D 题一看就不会,过。</p> <p>剩下的时间把今天的模板随了,虽然人在外面但还是得继续刷模板,然后今天随到的是最小生成树、最长公共子序列、欧拉路径。其实我的规定不是每天三道而是每天打满 $100\text{pts}$,但不知为何总是刚刚好要三题才能打满。</p> <p>然后做了几道 NOIP 计划的作业,一道红一道橙,橙题是前年 J 组 T2,我第一眼甚至以为要上平衡树,后来发现值域 $600$,直接桶排即可。</p> <p>晚上继续补洛谷课程,学单调队列的时候突然学到了最大子段和原来可以维护最小前缀和来做,我去,还能这样?之前做还是用的 $dp$ 讨论每个点选还是不选,离谱。想到了求最长不降序列这个问题,刚学的时候还是敲的暴力,前一段时间突然想到了树状数组的做法,很感慨以前的简单题居然还有更简单的做法!</p> <p>顺便还问了一下今天上午的 B 题的公式以及枚举范围,Alex_Wei 老师说我推的公式没问题,应该是我写挂了,我也这么觉得,然后还帮我简单证明了枚举范围,非常厉害!</p> <p>晚上机房同学发来一道题,做了一晚上发现我也不会,问了认识的大佬说肯定是区间 dp 但还是不可做,后面查到全网唯一题解结果只有四行话……</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(90/100)</p> </li> <li> <p><input disabled="" type="checkbox"> 刷题(50/200)</p> </li> <li> <p><input disabled="" type="checkbox"> 活动(50/100)</p> </li> <li> <p>今日总分 $190\text{pts}$,溢出 $0\text{pts}$,挂分 $0\text{pts}$,总计增加 $0\text{rp}$。</p> </li> </ul> <h3 id="11-月-2-日">11 月 2 日</h3> <p>停课,但是还是得七点起床上学。</p> <p>上午有一场模拟赛,听说难度巨高,遂开摆。</p> <p>A 题给了一棵树,每个点有一个分数,可能为负。初始时得分为 $0$,第一次经过一个结点时可以获得该点的分数,可以多次经过同一个节点,问是否存在移动方案可以从 $1$ 走到给定的目标结点 $t$ 使得过程中保持得分非负。</p> <p>一开始打了个搜索,找到 $1$ 到 $t$ 的那条链,对于不被链经过的所有子树都做一个压缩,得到进入这个子树后最后最多能得到的得分,然后链上跑一遍,有分就收集,看过程中得分是不是始终为正就行了。</p> <p>敲起来很快,样例测起来很错。对着好几个 $n=15$ 的样例调了半天过掉了,然后这时候只差最后一个小样例还没过了,把树画出来之后发现卧槽我做法假了,因为我压缩子树的时候没有考虑到过程中得分会不会小于零,这样的话进入这个子树时的得分不同,能得到的分数也不同。</p> <p>看看 B 题,好家伙,数论,对着一个看不懂的柿子求 lcm。看看 C 题,好家伙,暴力都写不出来一点!</p> <p>遂开摆,11 月份第一场模拟赛喜提爆零。</p> <p>赛后讲了题,第一题确实是这么个理,所以对每个节点都维护一个可重二元组集合 $(i,j)$ 表示得分大于 $i$ 时进去能额外得到 $j$ 的得分,将子节点的全部二元组加入父节点后贪心地合并,然后最后再在 $1$ 到 $t$ 的链上跑一遍贪心地得分即可。</p> <p>而 B 题可以用等比数列求和公式转换一下,然后用 min-max 容斥转成求一坨奇怪的 gcd,当然,我不会。C 题更是神秘网络流,最大流转最小割的做法很神奇。</p> <p>今天洛谷运势为大凶,我试过了,是真的,今天随模板题,随出来LCA、Dijistra、SPFA判负环,三道黄题总计 $90$ 分,还差十分所以不得不再多随一题,结果随出来一道 CRT!再看了几眼大纲确认 NOIP 真的会考 CRT 之后不情不愿的打了,于是今天就打了四道板子,三黄一蓝总计 $170$ 分。</p> <p>把昨天说写挂了的 B 题重新写了一遍,还是一样的连样例的过不去。调了半天发现原来是区间左边界应该对枚举值取 max,否则多统计了一些答案。于是顺着这个 max 去推公式,终于发现了错误原因,并不是我写挂了,而是当枚举到一定大小之后会导致左端点变成负数,所以要作处理,顺便按同样的做法推了右端点的关系式,成功推到了昨天问 Alex_Wei 老师的那个枚举范围的那个公式,感觉一切都合理起来了,我真强!</p> <p>下午写了一道 <a class="link" href="https://www.luogu.com.cn/problem/P8945" target="_blank" rel="noopener" >P8945</a>,刚开始写的迷迷糊糊的,后来受不了了于是索性重写,直接不想这么多先 $O(n)$ 一遍把那些鬼前缀全部处理好,然后再重新跑一遍来求答案,总算是理清楚了,一次性把题过掉了。</p> <p>但是跑的奇慢,于是就开始压缩,把两次遍历合并,这一次就清晰多了,顺便上了个快读,直接排到了最优解第五,随开始卡常抢最优解。</p> <p>换成关闭同步流的 cin 试了下发现时间慢了一倍,诧异。然后用从大佬那里贺来的 fread 成功抢到了最优解。</p> <p>下午放学前和机房大佬 vp 了一场 Edu,切了 A 题之后成功被 B 题切了,偷偷看了眼洛谷题解,然后去做 C 题,半天做不出来,后面发现做法假了。最后才知道原来这场 Edu 难得离谱,C 题都 1800 了,这真的是 Edu??</p> <p>晚上继续补洛谷网校,终于看完第一节课了!突然发现讲倍增的时候讲到一题博弈论+倍增,我好久之前还随到过这一题!当时盯了好一会连怎么博弈都不知道,现在上课的时候讲到这道题目读完题就看出来 Nim 了,有些感慨。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(170/100)</p> </li> <li> <p><input disabled="" type="checkbox"> 刷题(80/200)</p> </li> <li> <p><input disabled="" type="checkbox"> 活动(50/100)</p> </li> <li> <p>今日总分 $300\text{pts}$,溢出 $0\text{pts}$,挂分 $0\text{pts}$,总计增加 $0\text{rp}$。</p> </li> </ul> <h3 id="11-月-3-日">11 月 3 日</h3> <p>洛谷运势:凶。是真的。今天模板题写的 ST 表和树状数组 1,为什么只有两道呢?因为接下来我随到了 exgcd 和平衡树,然后我说服了自己不写这两道。</p> <p>上午 vp 了一场 edu,我 C 题罚了三次时,最后只过了三题,一起的大佬已经切到 F 了,其实感觉 D 题会了,但是最后没 rush 出来,看来实力还是太菜了。</p> <p>vp 完之后继续看昨天的 min-max 容斥,今天看懂了!然后要了一份代码读明白了!</p> <p>中午和下午就继续上洛谷网校,进度快了很多,估计是因为第二节课简单很多吧!而且还做了一道灰题,具体难度其实我觉得有蓝到紫,打算有空可以写个题解!</p> <p>放学后突然自己想出来一个 min-max 的简单证明,感觉理解的更深刻了!</p> <p>晚上打了场 cf,A 题切了,B 题勉强切了,C 题想了一会就会了,但是一直调不出来,好不容易过了所有样例,一交发现 WA on 1,怎么本地测的和 CF 上跑的不一样呢,怎么会是呢?后来发现数组开小了,循环的边界处理也有问题,改了四五十分钟才过掉。后面就一直在看 D 了,感觉有规律,打表半天一点规律都没瞪出来比赛就结束了。</p> <p>考后阅读到了一篇代码,发现 D 题应该从拆位上面去考虑!其实之前有场模拟赛的某一题就考到过拆位,对每一位进行考察,下次看到异或应该主动往这方面想,深刻。</p> <p>本来今天模板只有 $80$ 分,但睡前看到总分 $390$,能完成任务的机会不多,为了攒 rp 决定拼一把,又随了两道 Floyd 和单调栈,成功拿到 $440$ 的总分。第一次满分,希望溢出的 $40$ 分真的能加到 rp 里面去吧。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(110/100)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 刷题(230/200)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 活动(100/100)</p> </li> <li> <p>今日总分 $440\text{pts}$,溢出 $40\text{pts}$,挂分 $0\text{pts}$,总计增加 $40\text{rp}$。</p> </li> </ul> <h3 id="11-月-4-日">11 月 4 日</h3> <p>洛谷运势:大凶。希望洛谷是在帮我攒 rp。</p> <p>今天周六诶,周六!为什么这边的学校周六都不放假的啊!!</p> <p>上午打了一场模拟赛,比前几天简单了许多,但还是爆难。A 题构造,不会,遂打表。找到一个规律,感觉贪的很对,统计答案也很好做,写了写把全部样例过掉了就交了!B 题推了一下之后写了个分治,感觉很对,但是样例太少了,遂写拍子,一拍就挂!改了之后再拍,继续挂!再改,再拍,哦豁,我做法假了。C 题怎么看怎么不会,D 题怎么是概率啊再见!</p> <p>打完之后发现 A 题挂到了 $60\text{pts}$,不知道是贪心假了还是哪里写的不对,题解写的很抽象。B 题做法确实是假的,正解是经过一些抽象操作后转化成二维偏序。</p> <p>下午一边继续补网课一边写题,中途还帮学校一位高一的后辈调了道题!写了一道 <a class="link" href="https://www.luogu.com.cn/problem/P5522" target="_blank" rel="noopener" >P5522</a>,状态压缩后用线段树维护,很不错的一道题!另外还打了两道线段树求区间最大子段和的板题,当初第一次看到这个做法还是在某个初赛阅读题里面。然后突然扶苏姐姐就开始讲矩阵转移了,也就是名为动态dp(DDP)的神奇做法!现在已经会四种求最大子段和的方法了(虽然矩阵转移本质就是 dp)。</p> <p>发现昨天那场 CF rating 结算了,这次没有通知我。加了九十分,上 specialist 了!!</p> <p>网课又上完一节,感觉现在进度快多了,不知道是因为之前比较难还是之前我太颓了。学到了很多新的结论,比如说如果 $a\gt b$,那么 $a\text{ mod } b\lt\frac a2$,这个结论可以启示我们如果每轮取模,其实总共是 $\log n$ 轮的,所以一些看起来暴力的做法的复杂度其实是正确的。以及关于 lxl 的&quot;线段树其实就是特殊的 01Trie&quot;这一说法也很值得思考。</p> <p>晚上打了场 ABC。A 题切了,B 题切了,C 题也切了。D 题稍微想了想,发现好像给的条件其实相当于连边后判二分图,直接染色就好了,第一次交上去一半 WA 了,后面发现是 dfs 时传错节点编号了,这都能过一半的点?第二次交上去还是 WA 了,然后突然想到图不一定联通,应该对所有联通块都判二分图,马上改好交上去了,过了,吃了两发罚时。</p> <p>这次打的好快啊,二十多分钟就过掉 D 了,抓住机会上大分!遂开 E,给出了一个计算 rating 的神秘式子。观察式子,发现选取的比赛越多越好,其次越往后的比赛的得分越大越好,猜了下其实在求最长不降序列,写完后交了下发现假了。继续观察式子,发现如果倒序处理,那么指数就跟总数无关而只跟已选个数无关了,那岂不是直接 $O(n^2)$ 倒序 dp 就可以了?迅速开写,写完之后样例全过,自己也感觉很合理,结果交上去 WA 了一半,不知道是怎么假的。</p> <p>发现很多人写了 F 题,遂开 F,读完题目发现这不就是 <a class="link" href="https://www.luogu.com.cn/problem/P1502" target="_blank" rel="noopener" >P1502</a> 吗?我居然能在 ABC 做到原题?于是直接贺了自己的代码过来,改了改交上去,结果发现有一个点始终过不去,啊?</p> <p>感觉代码一点问题都没有,样例全都过了,完全无从下手,不知道怎么调,于是 ABC 就这么结束了。结束后突然想到可以贺别人题解过来交,于是随便贺了一篇改了一下,一发就过了。</p> <p>啊?合着原来是我自己的扫描线板子代码本来就有问题,最丢人的一集。赛时没想到题解,不然多过一题应该能多上好多分!</p> <p>结束之后马上就更新 rating 了,加了一百五十多分,上棕名了!</p> <p>晚上仔细想了想,虽然很不想写 splay 和 exgcd,但毕竟 NOIP 还是有可能考的,所以今天没遂模拟题,而是把这两道昨天随到的补了。写 splay 的时候有点手足无措,感觉不会写,但是记住了扶苏说的“splay 操作其实就是模拟“,莫名其妙地写出来了,基本没什么大问题。</p> <p>今天分也挺高的,都是为了攒 rp!</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(130/100)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 刷题(240/200)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 活动(150/100)</p> </li> <li> <p>今日总分 $520\text{pts}$,溢出 $120\text{pts}$,挂分 $40\text{pts}$,总计增加 $200\text{rp}$。</p> </li> </ul> <h3 id="11-月-5-日">11 月 5 日</h3> <p>周日了,九点多醒的,睡够了。</p> <p>今天不光颓,还唐。洛谷运势:中平,总算不是凶和大凶了。</p> <p>醒来之后没有写题,在 OI Wiki 上学了下矩阵,发现这个东西其实不难,然后颓到了中午。</p> <p>中午考了场 AMC。没错,是 AMC(美国数学评测),不是 ACM。其实上面的数学题大部分不是很难,由于我没草稿纸,所以都是在手机备忘录里打的草稿,偶尔用下计算器,有一个数列问题还查了 oeis,最后比出来一百多分,貌似按去年分数线都没奖。</p> <p>下午打了场模拟赛,是 lxl 出的题。十分钟看了下四题,没看出什么所以然,还以为会是四场数据结构。A 题看起来很贪心,发现貌似直接排序后双指针即可,开写,中间差点把自己绕晕了,最后写完了,过掉了两个样例,结果最后一个大样例错了,啊?</p> <p>检查了一下代码感觉没问题,遂写拍。结果还没拍上呢,测试数据生成器的时候发现我的暴力写的不对,然后 debug 了一会暴力突然发现这玩意儿有后效性,我写的贪心直接就假了。想了一会想不出正解怎么做,想着既然过掉了好几个样例那应该部分分还是有的,遂开 B。</p> <p>B 题发现求树上存在绝对众数的联通块个数,但我没学过有关绝对众数的东西啊,顶多做过 Violet 和蒲公英以及 lxl 大爷题。</p> <p>于是现场开学,学完了摩尔投票法之后发现这事不对啊,树上不能这么做,遂试着搜相关题,结果发现了这道题的序列版 <a class="link" href="https://www.luogu.com.cn/problem/P4062" target="_blank" rel="noopener" >P4026</a> 和区间查询版 <a class="link" href="https://www.luogu.com.cn/problem/P7882" target="_blank" rel="noopener" >P7882</a>,现场看题解学了下做法,有一点头绪但不多。最后还是不知道怎么上树,遂写部分分,链的部分就等于问题回到序列上,由于 $n$ 很小所以直接上个 BIT 即可。然后小数据打算写个暴力一点的 dp,结果发现写不出来一点。</p> <p>还有一分半结束的时候,听 lxl 说为了模拟真实情况,D 题给我们保留了不可以总司令,遂半分钟内迅速敲了个骗分,直接全部输出 NO。</p> <p>最后拿了 $70+20+0+19=109\text{pts}$。由于第一题本来就假了,所以也不算挂分,倒是最后一题不可以总司令居然有 $19$ 分,诧异。</p> <p>考完之后 lxl 马上就开始讲题了,A 题题解看一半就会了,感觉赛时真降智了,这么简单的贪心都看不出来,还写双指针,重新写了下只用十几行就过掉了。B 题正解是优化后的树上背包,我的思路没什么大问题,但由于我不会树上背包所以这题后面一点暴力都写不出来。我确实背包问题做少了,不光树上的,序列上的背包都不是很会做,确实要练练!C 题是个比较复杂的 dp,用线段树进行了很神奇的优化。D 题是从联考搬的题,貌似实际难度不比 C 高。</p> <p>貌似求满足条件联通块是树上背包的典题,打算明天学学。晚上稍微看了一点下节的课的开头,然后就继续颓了,毕竟是周日嘛。</p> <p>睡前随了道模板题,又是像之前那种情况,先随了三道黄题:逆元、三分、负环,因为分数不够然后再随了一道,结果是绿题:割点。不过其实还好,除了三分以外都敲得很熟练,看来刷模板战术起效果了!</p> <p>今天真的是又颓又唐,题没练,比赛也没打好,之前说要写的题解也没写,希望明天开始又能回到前天昨天的状态吧。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(140/100)</p> </li> <li> <p><input disabled="" type="checkbox"> 刷题(0/200)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 活动(100/100)</p> </li> <li> <p>今日总分 $240\text{pts}$,溢出 $0\text{pts}$,挂分 $0\text{pts}$,总计增加 $200\text{rp}$。</p> </li> </ul> <h2 id="第二周">第二周</h2> <h3 id="11-月-6-日">11 月 6 日</h3> <p>周一了,上学,要早起。</p> <p>上午有场模拟赛,这次没有先看一遍全部题,反正也没机会做后面的题。</p> <p>开 A,第一眼以为我小学奥数题,后面发现好像不太一样,想不出公式,遂模拟,样例全过,时间貌似还行。写了个拍,不拍答案只算时间,发现会有些点 T 了,尝试优化。想了半天,推出来一个式子,感觉是类似于分块一样先把大块的快速做了,剩下的小块再暴力,结果后面拍起来发现比优化前更慢。但是后面发现虽然拍子上提示我 T 了,但实际单独运行程序的时候又没 T,我也不知道什么情况,所以就挂后台拍着了。</p> <p>开 B,不会,复制粘贴,诶找到原题了,启动!发现做法很简单,有一部分我已经想到了,看完后花了几分钟写了二三十行就搞定了。其实感觉这题我应该能想出来的,毕竟做法似乎有点显然,不过这次就算积累到经验了!交完后看了一眼 A 题的拍子,已经拍到几千组了,几乎没有超时的,貌似挺稳,但是随机生成的数据感觉很水,有点慌?</p> <p>开 C,使劲想,发现曼哈顿距离有个绝对值,联想到周日下午打的一场比赛里面拆了绝对值,遂尝试拆,但发现曼哈顿距离绝对值情况有点多,好像不能按那个来。火速打开 OI Wiki 看二分图最大权匹配相关东西,发现里面有费用流做法,想了想发现还是得建 $n^2$ 条边,做不了。最后还是不会,复制粘贴,我去还有原题,启动!原题是 <a class="link" href="https://www.luogu.com.cn/problem/AT_agc034_d" target="_blank" rel="noopener" >AGC034D</a>,看一眼题解马上就会了。发现确实跟我想的一样要拆绝对值和跑费用流,绝对值拆出来四种情况之后建立四个中心点对应四种不同情况,然后黑点连到源点和中心四个点,白点连到汇点和中心四个点,这样边数就是 $O(n)$,跑费用流即可。火速从我的费用流板子那里贺了段 EK 过来,改一下建图方式就过掉了所有样例,芜湖,我起飞了?</p> <p>开 D,什么玩意儿?开摆。发现有 $20%$ 的 $n\le 8$ 的部分分,一分钟手动打个 $[1,8]$ 的表,交上去完事。看了一眼 A 的拍子,已经上万了,感觉没问题于是停拍。感觉这次分挺高的,这几天比了那么多场终于舒爽了一回,尽管我 BC 都翻了原题。</p> <p>考完后出分:$40+100+55+20=215\text{pts}$,啊?</p> <p>A 题看完题解才发现可以推式子做到 $O(1)$,是我蠢了。而且我随机的数据强度太弱,竟然一万组 $1e5$ 的数据都没卡到超时。C 题我的 EK 忘记开十倍边数了,而且数据范围比原题大,费用流只有 $70$ 分,正解要用模拟费用流。</p> <p>好吧,虽然这样,但这次还是打的不错了。打完比赛后发现洛谷咕值更新了,由于上周交了篇题解所以社区贡献到了 $8$,其实还是蛮好赚的,另外练习情况也增加了几分,不懂这个是怎么增加的,总估值 $311$,排名 $465$。等我下周发到七级钩,然后再刷一周题,再发两篇题解,应该有望提升更多!</p> <p>中午继续看了一点课,然后就去做上一节课作业单了。先切了一题比较水的黄题,然后由于作业单里有字典树的板子题,联想到我前几次刷板子每次刷到字典树都会写挂,所以打算顺便写一遍。果不其然,又挂了,对着以前的代码调了好几次才过,感觉有必要加强练习啊!</p> <p>写完之后看了眼大纲,发现卡特兰数我还不会,就去 OI Wiki 上看组合排列相关的东西。</p> <p>晚上继续补了一点课,然后开始写 <a class="link" href="https://www.luogu.com.cn/problem/P6587" target="_blank" rel="noopener" >P6587</a>,调了一个多小时才过,最后发现是因为几个地方漏开 <code>long long</code> 了,无语,但是这题确实不错,让我体会到了之前说的“线段树是特殊的 01Trie”这一观点确实很有道理。</p> <p>睡前随模板题,结果又随到普通平衡树了。这次没有犹豫,直接开写,写的非常流畅,最后简单调一下就过掉了,感觉随模板题的做法很正确啊,很有效果。忘了自己下午做了道字典树,又继续随,随了一道 ST 表,几分钟写完然后一发过掉了。</p> <p>感觉今天状态恢复了许多。接下来计划是 OI Wik 上补一补组合排列、矩阵、各类背包,另外课也继续上,作业单也及时跟进,感觉很有前途啊。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(140/100)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 刷题(290/200)</p> </li> <li> <p><input disabled="" type="checkbox"> 活动(50/100)</p> </li> <li> <p>今日总分 $480\text{pts}$,溢出 $80\text{pts}$,挂分 $75\text{pts}$,总计增加 $355\text{rp}$。</p> </li> </ul> <h3 id="11-月-7-日">11 月 7 日</h3> <p>起床,再睡一会,起床,再睡一会,起床。</p> <p>早上打了场模拟赛,但是一上午脑子有点晕晕的。</p> <p>A 题读完题没什么头绪,只会写 $O(2^n)$ 大力搜索,一看数据范围 $n\le 20$,哦,原来是签到题,直接开搜。里面有一点细节,所以写+调了大概半小时不到过掉了。发现这题没什么能拍的,但这次写的很稳,应该不至于像前两天那样 A 题挂分,遂开下一题。</p> <p>B 题,基环树上 dp??查了半天跟联通块有关的知识点,发现全都用不上。开摆了,抱着死马当活马医的心态,试着搜了下题目标题,结果这一搜直接搜出来了,果然还是原题!读完题解就会了,原来联通块只是恍的,实际上找个环之后讨论一下可以直接 $O(1)$ 修改和查询。写了半个多小时,几乎没怎么调就一次性过掉了,感觉非常慌,但是我不会写暴力,也没办法对拍。</p> <p>C 题会写暴力,猜测也有原题,一搜出来后发现正解是比较复杂的分治,后半部分就看不懂了,于是开始自己想做法。跟机房大佬交流了一下,把我的贪心给毙掉了,然后会了他的 $O(n^2)$ 归并排序优化暴力的 $60$ 分的做法。</p> <p>写完后还剩五分钟,看到这次 D 没法骗分,直接开摆。</p> <p>出分之后发现自己 $100+0+40+0=140\text{pts}$,怎么会是呢。C 题发现 subtask 绑错了,后面重测之后 $40\to60\text{pst}$,但总分还是只有 $160$ 分。A 题没挂,甚至还跑到了最优解,很好。C 题也没挂分,很好。B 题怎么回事?</p> <p>后面才知道 B 题中途更新了样例,但我没刷新所以没看到公告。对着新样例调了一会发现我忘记把对反向边也做设置了,加上之后拿到了 $60\text{pts}$,应该是剩下还有哪里写挂了,离谱。</p> <p>中午继续补课,然后老师讲了 <a class="link" href="https://www.luogu.com.cn/problem/AT_agc010_e" target="_blank" rel="noopener" >AGC010E</a>,会了,想着刚听完不如写一写,毕竟是道黑题,于是写了二十分钟把这题过掉了,黑题喜加一。</p> <p>下午补完了第二课的作业的最后一题,总算是 AK 一次作业了!开始写第三课的作业,第一题是 <a class="link" href="https://www.luogu.com.cn/problem/CF1253F" target="_blank" rel="noopener" >CF1253F</a>,好家伙,一上来就一题紫。听课的时候感觉还行,没想到写起来这么麻烦。类似于货车运输那道题,先建图跑一次 Dijistra,然后重新赋边权后做一次 Kruskal 之后,询问就变成了求最小生成树上两点间简单路径上的边权最大值,倍增求 LCA 的同时维护即可。写了一小时,调了半小时,最后等评测结果特别难熬,一百二十多个点。</p> <p>晚上回来先打了打板子,第一题裴蜀定理,两分钟打完过了。第二题乘法逆元 2,五分钟写完之后 T 了,稍微改改就过了。第三题非常巧,随到了乘法逆元 1,啊这,一分钟打完过了。怎么今天的板子都是数论?</p> <p>晚上打了一场 CF Div2。</p> <p>A 题题面好长,而且出现了我不认识的单词,看了好久才读懂,模拟判定一下就好。</p> <p>B 题有点难读懂,一开始没理解什么什么叫做 exactly two 和 exist,后面把样例仔细看了看才略微明白一点,貌似也简单模拟一下即可。</p> <p>总感觉今晚状态不佳,写模拟写的很不顺,感觉没有平时那种码力了,或者说感觉写出来的模拟都好蠢,总之将近一个小时才开 C 题。</p> <p>C 题想了十分钟,突然发现往回走是唯一的,所以一直往前找看能不能循环即可,五分钟写完交上去结果 WA on 2 了,瞪了一会发现就算不能循环,如果往前推的次数能够达到 $k$ 次也是满足题意了,一分钟改完交上去就过掉了。</p> <p>最后半个多小时一直在想 D,发现貌似一定存在某种使得 LIS 增加最多不超过 $1$ 的插入方法,感觉将 $b$ 从大到小排个序,如果能直接插在末尾的不升序列中那么一定不会使得 LIS 长度增加,除此之外如果直接插在相等的数字后面也不会增加,以及 $b$ 中所有大于 $\max_{i=1}^n a_i$ 的数倒序插在最前面也不会增加。剩余的既没有在 $a$ 中出现过也无法插在末尾的数就比较难办,可能会使得 LIS 增加,但是同样倒着插在最前面的话可以保证 LIS 增加不超过 $1$。</p> <p>然后剩下十几分钟马上就开写了,写了个离散化维护数字出现次数,然后用栈来倒着记录答案,一个个插入即可,最后倒序输出答案。写完了,发现样例有一条不对,拉出来单独测试发现又对了,以为是多测没情况,但是检查了好久甚至最后上了 memset 都没救过来,然后比赛就结束了。</p> <p>比赛结束后 system testing 期间发现我有一处使用了 $a_{i+1}$,也就是默认了 $a_{n+1}=0$,这一部分没清空,所以出现了问题。但是经过交流之后发现我的做法不是最优的,最优的做法也差不多,将 $b$ 从大到小枚举,将 $b_i$ 插入第一个大于等于 $b_i$ 的 $a_i$ 后面即可,仔细想了想发现这样就能保证不会使答案增加了,确实犯蠢了。</p> <p>感觉这次比赛打的不是很顺,而且题面都比较长比较难读,确实打的不算好。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(110/100)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 刷题(300/200)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 活动(100/100)</p> </li> <li> <p>今日总分 $510\text{pts}$,溢出 $110\text{pts}$,挂分 $100\text{pts}$,总计增加 $565\text{rp}$。</p> </li> </ul> <h3 id="11-月-8-日">11 月 8 日</h3> <p>早上起晚了,但也不是很晚。</p> <p>打了模拟赛。</p> <p>A 题怎么是期望啊,他建议我先做第三题?所以就去看了第三题。第三题貌似不难,推了将近一个半小时之后 $k=0/1$ 的部分已经没问题了,但是 $k=2$ 的部分讨论起来特别复杂,已经完全不想写了,于是打完 $k=0/1$ 的 $20$ 分就跑路了。</p> <p>回来看 A 题,发现求期望部分貌似不难,似乎第一个区间只需要区间乘 $2$ 的逆元后区间加,第二个区间只需要区间乘 $2$ 的逆元即可,这不是线段树 2 嘛?火速贺来了线段树 2 的代码,过掉了样例一,结果样例二挂了。稍微推了一会,发现假了,这是全概率事件,不能这么拆,岂可修。</p> <p>现在只剩下半个多小时了,而我只打出来 $20$ 分,坏了。赶紧看 B,给了一个序列,操作一是整个序列对给的 $x$ 做平均数,操作二求区间第 $k$ 大。题意读懂之后发现不会做,遂写暴力。</p> <p>但是,反转来了。写暴力刚写完离散化的时候,突然发现操作一不会影响操作二,操作二要求的区间第 $k$ 大就是离散化后的区间第 $k$ 大,变成静态的了,而操作一是对全体 $[1,n]$ 做平均数,那我岂不是在离散化数组上建一棵线段树进行区间加和区间除,然后在离散化后的数组上建主席树求静态区间第 $k$ 大然后回离散化数组的线段树上单点查询就好了?!</p> <p>火速开写,此时还剩二十分钟,而我直接删掉了写一半的暴力,决定开始 rush 正解。当然,不是现场敲。直接贺来了我的主席树板子代码,调了几分钟就可以用了,然后发现我没写过区间除线段树,遂从网上贺了博客的代码,方便起见直接放到 namespace 里,直接按原代码的用法就可以用!简单改了几处问题之后就可以用了,测了下前几个样例都过了,此时还剩两分钟,赶紧交上去了。最后两分钟前 rush 出了 B 题,心里十分激动。</p> <p>过了一会就出分了,这次是 $0+100+0+0=100\text{pts}$,啊?我 C 的部分分怎么挂了?检查了一下发现数组开小了,重新交上去就拿到了 $20$ 分。我怎么每次模拟赛总得挂点分?</p> <p>后面看了题解,发现 A 题期望确实一个一个点看就好了,但是要用排列组合推一个比较复杂的式子,其实我感觉我能推,但我赛时上没有往这方面想。。。至于 B 题,大致上和我的做法差不多,只不过他们只用建一棵主席树求 kth,而操作一则是直接暴力做前面的 $O(\log V)$ 次后整个序列会只剩两种值,剩余的操作一就可以 $O(1)$ 判断了,而实际上区间除线段树里面也差不多是这样的。然后发现全场没有做出来 C 题的,是很复杂的讨论,被出题人骗了,还在 A 题推荐我先做 C,太可恶了。至于 D 题貌似要用 FWT 优化 dp,不会。</p> <p>中午继续补课,把第三节课也看完了!然后就开始做第三节课作业的第二题,结果写出来之后怎么调都调不出来,调了一个小时后才发现是记错样例答案了,无语。</p> <p>晚上回来直接开了第四节课,上了状圧 dp 部分内容。然后机房同学发来一道边双缩点的板题让我帮忙调,读了一会之后就调完了,顺便还拿他代码偷偷过掉了这道题。</p> <p>晚上随了三道模板,第一个随出来的刚好是边双联通分量,好巧!后面两道分别是拓扑排序和单调队列。</p> <p>感觉今天状态不太对,题目做的有点少,明天没有模拟赛了,希望能恢复过来。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(100/100)</p> </li> <li> <p><input disabled="" type="checkbox"> 刷题(180/200)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 活动(100/100)</p> </li> <li> <p>今日总分 $380\text{pts}$,溢出 $0\text{pts}$,挂分 $20\text{pts}$,总计增加 $585\text{rp}$。</p> </li> </ul> <h3 id="11-月-9-日">11 月 9 日</h3> <p>洛谷运势:大吉!!</p> <p>由于今天上午没有模拟赛,所以早上随了三道模板,希望晚上能早点睡。今天随到的是 LCA、裴蜀定理、exgcd,都是我不久前写过的板子,很快就写完并一次过了。</p> <p>接下来继续完成第三节课的作业,然后发现第一题居然是<a class="link" href="https://www.luogu.com.cn/problem/P7113" target="_blank" rel="noopener" >排水系统</a>。这一道题我去年的 11 月 18 日,也就是去年 NOIP 前一周还做过这道题,当时以为这题只是简单的 toposort 加个 gcd 通分约分就好了,结果只有 $20$ 分。现在重新做了一下,写了个分数类,总计十分钟写完,一次性过掉全部样例,交上去发现只有 $90$ 分,把 long long 全部改成 __in128 之后就过掉了,没想到我先除后乘也会爆 long long,放在当年不能用 __int128 的时候来看这题确实很坑,可能需要用两个 long long 拼个 __int128 才能过。</p> <p>后面又做了一道 <a class="link" href="https://www.luogu.com.cn/problem/P8867" target="_blank" rel="noopener" >[NOIP2022] 建造军营</a>,去年的 T3,当时我考场上看到这道题直接放弃了,考后对着题解才补了这道题。不知道我补题的时候有没有看懂题解,应该是看懂了,反正之前是过了。现在又重新写一遍,重新看了下题面,很显然的思路是边双缩点后跑树形 dp,设 $dp_{u,0/1}$ 表示以 $u$ 为根节点的子树中有/无军营时的方案数,但后面发现到根节点的边很难讨论,转移方程好像有问题,遂开摆,看题解,发现只需要令 $dp_{u,1}$ 里面的方案强制所有军营到根节点的边都要选,再在统计答案时强制 $u$ 到 $fa_u$ 这条边不选即可,这样统计答案也不会重复或者漏,因为 $u$ 到 $fa_u$ 这条边不选时的贡献会在 $u$ 处被统计,选时的贡献会被合并入 $fa_u$ 后统计。写完之后一次性过掉了前三个样例,第四个死循环了,后来发现是栈开小了,开大之后就过掉了,交上去喜得 AC。</p> <p>然后就去写了一下 <a class="link" href="https://www.luogu.com.cn/problem/P9565" target="_blank" rel="noopener" >P9565</a> 的题解,很有意思的一道题目,二进制分析后用 $62$ 个并查集维护 $62$ 个子图的连通。</p> <p>中午补了一下课,看了一点树形 dp 的内容,但我发现我基础不太好,背包问题都不熟练,需要抽时间恶补一下!</p> <p>下午打了场模拟赛,开题不会 A,感觉很树状数组,很容斥,结果想了半小时都想不出来,遂跳。开 B,啊?怎么是我做过的原题,甚至这题我还写过题解?直接贺了我之前写的代码,一测样例全过直接交。开 C,怎么感觉有点眼熟,等等,我好像也见过?一搜发现,又是原,只不过这道题当时没写出来,后面也没补,又贺了一份题解来好好研究,看了半小时学明白了就交了。回去看 A,又想了十几分钟,突然一拍脑袋拍出来了,直接上了三棵树状数组然后容斥出答案,测了所有样例全过!!天啊,起飞了这场,一个半小时过了 ABC。开 D,不是原,但是感觉很简单,貌似跑两遍 dfs 就可以了?赶紧开写,写完之后过掉了链的样例,但是第一个样例错了一个数,仔细一琢磨发现我做法假了一半,第二遍 dfs 没问题,但是第一遍 dfs 里面转移答案是错的,不能简单的乘起来,貌似得做一个背包。坏了,我刚好背包一点都不熟啊。</p> <p>考试开始两小时时有事所以提前退场了,等回来的时候比赛只剩十三分钟了,火速回来试着改,但是怎么改都改不好,最后三分钟的时候放弃了,直接回退到第一次写完的版本,寄希望于过掉链的部分分,然后开始摆烂。</p> <p>过了一会就出分了,$90+100+100+0=290\text{pts}$,啊?我 A 怎么又挂分了?看到最后一个点 T 了,检查代码发现,我写了 <code>#define ll __int128</code>,然后改成 <code>long long</code> 就过了,离谱。</p> <p>总排名 rk5,第一次这么高,也是我 NOIP 前打了十几场模拟赛来分数第二高的一次。感觉这场比赛有点赚又有点亏,赚是因为两道原题,亏是因为少了两个多小时赛时,不然我有信心现场学完背包然后写出 D 题。</p> <p>晚上听了讲评,貌似我三题做法都和正解略有不同但不多,第四题也确实非常简单。我感觉难度是绿绿蓝蓝,但貌似出题人评的蓝蓝蓝紫,第一次评难度比出题人低,赢!</p> <p>睡前从机房其他大佬的做题记录那找到一道水紫,做个边双之后直接暴力找一次 LCA 就好了,没怎么调就一次性过掉了。</p> <p>可能是今天上午没有模拟赛,轻松一点吧。今天状态还是不错的,不过背包确实赶紧安排时间学一下了。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(110/100)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 刷题(300/200)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 活动(120/100)</p> </li> <li> <p>今日总分 $530\text{pts}$,溢出 $100\text{pts}$,挂分 $10\text{pts}$,总计增加 $715\text{rp}$。</p> </li> </ul> <h3 id="11-月-10-日">11 月 10 日</h3> <p>洛谷运势:大凶。难道昨天那场二次原模拟赛把我运势耗光了?</p> <p>今天上午也没有模拟赛,所以先打模板。随到了欧拉路径、ST 表、线性筛。然后就开始写图论作业了。</p> <p>作业第一道题是一个老朋友:<a class="link" href="https://www.luogu.com.cn/problem/CF786B" target="_blank" rel="noopener" >CF786B</a>。我六月初的时候刚学线段树优化建图时写了这道板子题,而且时不时就 UKE,当天调了一个小时都没过。第二天又回来接着调,还是没过,于是就放弃了。过了一个月后,七月初我又回来调了两下这题,还是没过。再过了一个月,八月初我又调了一次,结果还是没过。今天上午又遇到这道题,打算从头开始写,都准备好调一上午了,结果竟然,一次都没调,一口气写完就直接过掉了样例,然后直接交就过掉了!!</p> <p>估计是得益于,我现在写的线段树规范了很多,并且刷题数量和模板题刷题次数大了很多,可以说是已经形成一种正确且较为优秀的码风了(毕竟我每次重新打模板,都和之前打出来的长得几乎一模一样),所以这题的线段树和 Dijistra 写的特别的稳,因而就一次性过掉了。</p> <p>后面继续写了几道题,感觉都还行,就是有点困。下午见到了 THU 计算机系的主任,开了一次交流会,大概内容就是讲的 THU 专业和强基之类的事。我很感兴趣,奈何我没这个实力,就不在这白日做梦了。</p> <p>做了一道 <a class="link" href="https://www.luogu.com.cn/problem/P5344" target="_blank" rel="noopener" >P5344</a>,倍增优化建图,算是挺难的,但是意外的没怎么写挂,但是写完 Dijistra 忘记在 main 里调用了,虚空调试了属于是。后面交上去 WA 了一个点,把数组开大点就变成 RE 了,调了很久,提交了一整页都没调出来,最后直接对着题解的数组大小改才过掉了。</p> <p>晚上有入门赛,不过我没打。群友问了一道构造题,然后被我切了,我真棒!今天战绩四紫一蓝,感觉还是不错的。明天上下午都有模拟赛,晚上还有一场 ABC,估计会很累,所以今天就早一点睡了!</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(100/100)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 刷题(480/200)</p> </li> <li> <p><input disabled="" type="checkbox"> 活动(0/100)</p> </li> <li> <p>今日总分 $580\text{pts}$,溢出 $180\text{pts}$,挂分 $0\text{pts}$,总计增加 $895\text{rp}$。</p> </li> </ul> <h3 id="11-月-11-日">11 月 11 日</h3> <p>洛谷运势:大吉。今天双十一,并且今天开始是真正的倒计时一周了。</p> <p>上午是模拟赛,听说是雅礼供的题,很难。</p> <p>开 A,想了很久,最后只想出来个 $O(n^4)$ 的区间 dp 做法,对比本体数据范围估计这只能叫做暴力了。写了好一会,测出来过了前两个样例,结果第三个样例没过,思考了一会发现这个东西是可以中转的,所以就用类似于 Floyd 的方法预处理了转移代价,然后就过掉了三个样例。虽然最后一个跑了七十秒,但是答案对了。</p> <p>开 B,不会,遂想暴力,发现可以直接树剖后用树状数组维护,我之前出题出过这个,但是那题造数据的时候才发现暴力更快,所以我想了想就直接上暴力了。</p> <p>开 C,不会,但是时间很宽松,貌似直接 $O(nmq)$ 模拟都有很高的分。</p> <p>开 D,交互题,不会。写了个随机数交上去了。</p> <p>最终得分:$60+20+60+0=140\text{pts}$,一题正解都不会,但是暴力分还是蛮足的。发现全场没有人过了 A,然后 D 也只有一个人过了。</p> <p>C 题机房大佬讲了一个很有意思的 trick,用 bitset 去判断高维空间的 $n$ 个坐标点有哪几个在一个给定的超立方体内。比如说先看一维的情况,离散化坐标后,我们预处理出 $n$ 个长度为 $n$ 的 bitset,其中第 $i$ 个 bitset 为 $b_i$,所有坐标小于 $i$ 的点在 bitset 内对应的位置设为 $1$。那么一条线段 $[l,r]$ 在一维空间覆盖到的点其实就是 $b_r\oplus b_{l-1}$。对于高维空间,用一样的方法对每个维度进行相同的处理,把每个维度得到的 bitset 全部做与运算,最终得到的 bitset 内为 $1$ 的点满足所有维度坐标都在范围内,也就是在超立方体内了。</p> <p>至于 D 题,我后面自测数据的时候发现跑的飞快,比答案给定的次数限制基本能快一倍,但是不知道是我写挂了还是评测机动了手脚,本地测都能过,交上去永远过不了,悲催。如果这是正式比赛,我可能已经靠着随机数把这题水过去了。</p> <p>下午洛谷有场模拟赛。考前听线下的朋友说很难,害怕。</p> <p>开 A,思考了一会发现质因数分解一下好像就做完了,后面再稍微想了一下发现直接贪就好,往质因数幂次最小的上面加就完事了,但是写起来发现有点麻烦,特别是贪心的操作有点难写,花了半小时才过掉这道题。</p> <p>开 B,构造题,感觉跟去年喵了个喵有的一拼。对于构造题我一般都不考虑去证明,一般都是先观察样例或者打表找规律,找到规律后再去检验,最后试着证明。结果这题硬是瞪了一个小时都没找到什么有用的规律,遂先跳过。</p> <p>开 C,优美度之和很好算,但是第 $k$ 小不太会处理。考虑到交换相同数字会带来很大的相等总和的情况,合理怀疑到后面很有可能问到的就是最小的。思考了半个小时,最后拼了份暴力,$n\le10$ 时暴力做出每一次然后找第 $k$ 小处理,否则直接当做 $k=1$ 去 $O(n)$ 算一遍最小的。</p> <p>开 D,有点像莫队。思考了一会发现给定 $l$ 是奇数保证了 A 拿的牌都是奇数上的牌,B 拿的都是偶数上的牌,但是博弈本身就不会,所以一点头绪都没有,连暴力都打不出来。</p> <p>回去看 B,继续思考,直到比赛结束都没看出什么来,最后面向答案直接输出了 $n\le6$ 的答案。</p> <p>最终得分:$85+5+32+0=122\text{pts}$,啊?A 怎么挂了 $15$ 分?</p> <p>比赛刚结束时四题难度评的是绿蓝蓝紫,可能是结果太低于预期了,最后四题难度变成了绿紫紫黑,离谱。</p> <p>由于跟评讲时间冲突,所以没打 AcWing 的周赛,不知道以后还有没有机会打了……</p> <p>讲题,A 和我思路一致,看了下别人的实现才发现原来我想复杂了。B 题则证明可以从图论方面去证,但大部分还是考找规律,而找构造题规律就得看和出题人电波能不能对得上了,这次我没有。至于 C 和 D,太难了就不没听,明天补一下题解吧。</p> <p>晚上打了一场 ABC。</p> <p>A 题由于没读明白题所以花了点时间,看了样例才搞懂,一分五十才写完,开局已经落后了。B 题由于没想到 10 和 12 这种情况,还花了点时间调试,八分四十五才过掉。C 题一眼莫队板子,啊?后面想了想,发现查询的话前缀和就可以,特判一下端点的情况,树状数组就可以做了。写一半突然发现压根就没有修改操作,直接求前缀和就可以了,浪费了很多时间,十四分才过掉 C。D 题发现很像这周考过的一场模拟赛里面的题目,直接一样的办法用链表维护一下就可以了,二十二分的时候过掉了 D。</p> <p>E 题发现是模 $k$ 最小生成树,啊?模 $k$ 最短路我知道,但模 $k$ MST 是啥啊,有这东西?后面看到数据范围发现不超过 $28$ 条边,直接状压然后拿并查集判断一下即可,中途用 <code>__builtin_popcount()</code> 剪枝,时间复杂度实际上是组合数级别的,应该没有什么问题,但是我状压不是很熟,稍微写了有一会,四十六分的时候过掉了 E。</p> <p>开 F,我还是第一次在 ABC 从 A 打到 F,虽然我本来就没打过多少场 ABC。F 题想了很久,总感觉是某种拓扑排序 dp,可能是因为最近做过题面长得比较像的题目。发现加边时会出现需要一整块往上平移的情况,所以想法是用并查集维护相对于集合根节点的大小关系,但是感觉很难维护,所以把路径压缩换成了按秩合并,最后成功维护出来了,跑样例貌似蛮快的,但是交上去之后 TLE 了 6 个点,还 WA 了 8 个点。后面再仔细想想,发现路径压缩是可以做的,只是压缩的时候需要更新一下相对大小关系,改完之后一口气过掉了所有样例,交上去过了,于是我在八十五分半的时候过掉了 F。</p> <p>剩下 G 题看了一下,只能看出来操作一只有一次以及操作之间顺序无所谓,想法只有枚举第一次操作后的序列然后 $O(n)$ 算总代价,发现不太好算,遂放弃。</p> <p>这场打完之后 rating 加了 256,算是一点小惊喜吧,白天打比赛打的都挺烂的,晚上总算打好了一会。其实这也是我第一次把 A 到 E 全部做出来,看了一下我的比赛记录,一共九场,rated 的只有八场,之前有一场迟到了所以打的 unrated。我第一场只写了 A,不知道剩下的是不会还是不想打了,performance 甚至不到 200,后面几场基本都是只能做 2-3 题的样子。上周那场 ABC 做出来前四题,E F 都写了但是都挂了,我的 performance 第一次上千。这一场则是直接一路切到了 E,然后 F 一次罚时之后过掉了,performance 到了 1599,排名也是第一次三位数。</p> <p>感觉进步确实很大,但是可惜不知道以后有没有机会再打了。</p> <p>睡前随了模板题:裴蜀定理、Nim 游戏、线段树 1。今天没有做题,一整天都在打比赛,也蛮累的。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(110/100)</p> </li> <li> <p><input disabled="" type="checkbox"> 刷题(0/200)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 活动(150/100)</p> </li> <li> <p>今日总分 $260\text{pts}$,溢出 $0\text{pts}$,挂分 $15\text{pts}$,总计增加 $910\text{rp}$。</p> </li> </ul> <h3 id="11-月-12-日">11 月 12 日</h3> <p>洛谷运势:小吉。今天是周日,好耶。</p> <p>早上起来先看了下昨天下午的比赛,A 题昨天其实改了但忘记写了。</p> <p>改了下 B 题,感觉 zig-zag pattern 这个东西很神秘,网上也查不到,不过大致的构造方法我是会了。我发现 B 题我其实可以打 $40$ 分的,但是昨天后面忘了打 $k=1$ 的分了,然后 $n=6$ 输出答案的时候不小心输反了,所以最后只过了第一个点。按照题解的构造方法,很轻易就过掉了。感觉这题其实构造实现很简单,但是构造方法太难了,不过其实貌似也不到紫的难度。</p> <p>看了下 C 题,昨晚讲完课后发现我写的部分分应该不止 $32$ 分,我猜是我写挂了,重新看了下发现果不其然,有些地方漏开 <code>long long</code>,答案也忘取模了,改完之后就有了 $52$ 分。</p> <p>这场满打满算我应该能打 $100+40+52+0=192\text{pts}$,已经超过线下联测强校的 $10%$ 分数线了,洛谷上甚至能排到 rk33,如果 B 题电波对上做出来的话就能排在 rk6。不得不说这场比赛虽然难了点,但是质量确实很高!</p> <p>中午继续补课。由于背包没学好,dp 有点听不下去,打算先补补背包再来听,于是先去听数学部分了。</p> <p>下午打了场模拟赛。</p> <p>A 题读完就会贪,但是读完同时也忘了题目保证给的是排列,导致我的做法复杂了,然后不知道什么地方写挂了之后就换了个更复杂的做法。B 题第一眼数学题,想了一会没什么头绪,跳了。看到 C,虽然我看这种多次询问区间的题都感觉像莫队,但是这次我学会了先不看区间问题,考虑对于整个序列做时要怎么做,然后序列上发现就是个 dp,然后联想到了扶苏姐姐讲过的 DDP(虽然这题并不带修),发先 dp 的过程用矩阵可以很容易得表示,所以就把每个位置的矩阵求一下之后用查询区间乘积即可。想到可以前缀和优化,但是不会求逆矩阵,所以只好用线段树来查询区间积。复杂度有 $O(m^2q\log n)$,发现过不去,于是就加了很多优化。最后发现 MLE 了,放弃后面的 $40$ 分然后又优化了一下空间之后才过编,然后非常神奇的一次性过掉了!过掉之后再优化了一点点空间,想着多过一个点也好。然后回去做 B,观察大样例答案发现要么是自己是质数、要么是和相邻的相加为质数、要么把一段前缀加上负数的一半消去后由另外两个数相加的得到质数,推测连续三个数以上的和不可能为素数,那这样的话做法就简单了,筛完质数然后记录一下两种情况,再倒过来推一下每个数到这两种情况的数的间隔,最后应该答案是可以 $O(1)$ 出的,过掉小样例后就交了,发现大样例第一个就错了,但只剩三分钟所以摆烂了。</p> <p>最后总分:$80+0+0+0=80\text{pts}$。A 题的烂做法挂了 $20$,B 也没过,C 全部 MLE 了,再缩小空间之后有 $30$。这段时间打的最烂的一场。</p> <p>A 题压根就没那么复杂。B 由于时间剩的不多,然后脑子又特别乱,没有想清楚,所以最后还是写挂了,靠着特判过掉了一个点,但由于 subtask 所以没分。</p> <p>听了评价,发现我 B 做法是对的,就是没有讨论明白。C 也确实是处理矩阵前缀积和处理逆矩阵前缀积,但我不会。其实这是我第一次写矩阵,我个人觉得已经不错了,就是可惜没拿到分。</p> <p>晚上随了模板题:裴蜀定理、逆元、字符串哈希、exgcd。全都一次性过掉了,感觉随到的经常重复啊,是不是要考虑 ban 掉一些已经熟到闭着眼就能打出来的模板呢?</p> <p>今天的做题量很低,比赛也打的很烂,感觉状态不是很好,需要调整,希望明天能好起来。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(140/100)</p> </li> <li> <p><input disabled="" type="checkbox"> 刷题(0/200)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 活动(100/100)</p> </li> <li> <p>今日总分 $240\text{pts}$,溢出 $0\text{pts}$,挂分 $30\text{pts}$,总计增加 $940\text{rp}$。</p> </li> </ul> <h2 id="第三周">第三周</h2> <h3 id="11-月-13-日">11 月 13 日</h3> <p>洛谷运势是大吉,等级分没有更新,咕值貌似也没有更新,无所谓了。</p> <p>上午有模拟赛。做了两个小时一题都不会,连暴力都写不出来,实在不行了去求助各路 gpt,连清华的 glm 都做不出来第一题,这种题不拿去 CTST,拿来做 NOIp 模拟赛??</p> <p>还剩半个多小时的时候放弃了,真的是浪费几个小时的时间。后面看完题解发现自己 A 题想法是正确的,已经转化完了,就差最后的贪心了,但是真的很难想。</p> <p>感觉自己状态很差,加上现在没人监督很容易颓,必须调整状态,遂战术洗手间。冷静思考了一下接下来 NOIP 考前还有什么要做的:</p> <ol> <li> <p>把洛谷的网课补完,并完成能完成的作业</p> </li> <li> <p>把 NOIP 前的所有模拟赛题,在我能力范围内的都改一下</p> </li> <li> <p>整理一下近期完成的题目用到的 trick</p> </li> <li> <p>补充知识点,尤其是背包问题</p> </li> <li> <p>把这几年的 NOIP 真题大致看一下</p> </li> </ol> <p>我嘞个豆,这么多?</p> <p>但是不管怎么样,每天打板子是必须且正确的。随了板子:ST 表、逆元 2、单调栈。然后继续完成图论的倒数第三道作业,写完交上去发现洛谷的 rmj 又寄了,上 at 看是 AC 的。</p> <p>中午看了下背包 dp,其实很简单,很快就全部理解并记住了。接着又看了树上背包以及区间 dp,还看了几眼 DAG 图上 dp。其实都可以理解,只不过我自己并没有形成观念和套路,打算有空把上面的例题都刷一下。看完之后顺便还看了一下格雷码、约瑟夫问题、悬线法,基本都可以理解。</p> <p>然后学了下 2-SAT,也不难理解,过掉了模板题。晚上一不小心又刷了会 b 站,注意过来后赶紧回来了。然后听说 at 的 rmj 又暂时好了,赶紧回去交了一发,UKE,再交一发,过掉啦!</p> <p>晚上继续写题,然后终于完成图论的作业了!感觉最后几题二分图匹配的题真的很妙,但目前还是没那么容易看出来二分图匹配,对于这类二分图或者网络流的建模能力确实不是很好。</p> <p>看了一下洛谷日报的二维线段树,分为四分树和树套树两种写法,都没写过,最沾边的只写过线段树套平衡树,而且 NOIP 也不考,所以就没仔细学了。</p> <p>然后写了状压的板子题?其实感觉连我都能做出来的状压基本都很板了(),基本没调就过掉了,感觉最近好稳,码力提高了很多!虽然该挂分的还是会挂。</p> <p>今天从决定放弃模拟赛开始状态就有回升了,感觉不错!dp 基本上是把提高级范围内的又重新复习了一下,现在感觉就差上手了,打算明天把几道例题过一过。rp 破千了,嘿嘿。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(110/100)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 刷题(380/200)</p> </li> <li> <p><input disabled="" type="checkbox"> 活动(50/100)</p> </li> <li> <p>今日总分 $490\text{pts}$,溢出 $90\text{pts}$,挂分 $0\text{pts}$,总计增加 $1030\text{rp}$。</p> </li> </ul> <h3 id="11-月-14-日">11 月 14 日</h3> <p>洛谷运势:小吉。</p> <p>早上看了下教练发来的一套 NOIP 模拟赛题,A 题想了很久,一开始以为是容斥,后来发现是拆贡献,再后来发现不能拆,再后来发现是个完全背包。前前后后两个多小时,结果好不容易写完了,第二个样例都过不去,但第二个样例又很大,手算也算不出来,检查了无数遍代码都没明白错哪了,甚至换了种写法重新写过出来的答案还是跟之前的一样,感觉像是我读错题了。不管怎么说,感觉这个平台的模拟赛一直都很缺德。</p> <p>由于完全不知道错哪了,感觉状态又变成昨天上午那样了,遂像昨天一样放弃了这场,赶紧调整状态。</p> <p>然后就开始刷背包的例题,然后很轻松的把 OI Wiki 上给出的各种背包的例题都过掉了。感觉普通的背包 dp 很好想也很好写,基本都可以一次过,应该是掌握的比较好了。</p> <p>中午上网课,由于现在掌握背包 dp 之后感觉对 dp 理解加深了,遂退回去从第二题开始再看一遍 dp 那一堂课。看完第二题之后就开始自己写,然后一遍过掉了,好稳啊我。</p> <p>然后就开始打板子,今天随到了拓扑排序、字典树、缩点。这次字典树终于一遍过了,另外缩点写一半突然就晕了,我 SCC 写的好好的,突然把 SCC 当成了 eDCC 以为我自己写错了,然后改成了 eDCC,结果刚准备提交的时候看到记录发现我又记错了。最后又改回来了,还是有惊无险一次性过掉了。</p> <p>其实讲道理 SCC、eDCC、vDCC 还是很好区分的:SCC 是有向图、eDCC 需要传入反向边,vDCC 需要记录 <code>child</code>。不过有时候就是会突然一下搞混。</p> <p>闲的没事数了一下,发现绝大部分板子我都敲过一遍了,但是貌似还是有五六个没有敲过,明天开始直接选没敲过的板子吧,就不随机抽取了,毕竟要确保考前全部至少打一遍。</p> <p>接下来就在一直写作业了,感觉 dp 的作业都很有启发。然后一下午和一晚上,边听课边写题完成了状压 dp 和树上 dp 的部分。而且我发现我写的意外的很稳,几乎都不需要对着主程序调。</p> <p>感觉今天的状态其实还是很不错的!背包问题练习了,作业也完成了很多,课程进度也追了很多,就是上午浪费了点时间,希望这几天都能像今天下午和晚上这样高效!</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(100/100)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 刷题(570/200)</p> </li> <li> <p><input disabled="" type="checkbox"> 活动(50/100)</p> </li> <li> <p>今日总分 $720\text{pts}$,溢出 $320\text{pts}$,挂分 $0\text{pts}$,总计增加 $1350\text{rp}$。</p> </li> </ul> <h3 id="11-月-15-日">11 月 15 日</h3> <p>洛谷运势:大吉。</p> <p>上午有场模拟赛。A 题想了一会想出了 $O(n^2)$ 的暴力做法后就去搜原题了,发现今天这场四题都是原题。</p> <p>倒不是不认真打,但是 NOIP 前也不要求我赛时做出什么题了,现在更主要的是拓宽思路、练习部分分和思考,尤其是不能连暴力都打不出来。</p> <p>然后 A、B、C 三题都是在想出暴力以后去看题解,并且在看懂的情况下完全由自己写代码,然后过掉了这三题。其实 C 题我已经接近想到正解了,但是我以为区间 dp 没有优化空间了,但事实上是有效状态是 $O(n^2)$ 级别的,所以可以把区间 dp 优化为记搜就可以过掉了。</p> <p>总分 $100+100+100+0=300\text{pts}$,打得比较高,而且三题我都是最短代码。发现自己其实有优化代码的习惯,而且优化能力很强。</p> <p>下午继续听课,中间随了板子:欧拉路径、边双。然后看了一下换根 dp 板子题,并且发现了我半年前写的代码中的一处错误,并且以更优秀的实现方式重写了代码。</p> <p>放学时思考了一下仍未完成的任务:</p> <ol> <li> <p>完成课程及相关作业</p> </li> <li> <p>看一看这几年的 NOIP 真题</p> </li> <li> <p>补一补这段时间的模拟赛题</p> </li> <li> <p>整理 trick</p> </li> <li> <p>上 OI Wiki 补充知识点</p> </li> </ol> <p>任务很重,时间很紧张。</p> <p>晚上继续听课,讲完一个知识点就做一下对应部分作业。讲基环树 dp 的时候想着找环比较麻烦,想去题解里学一个比较好的视线,翻来翻去发现没一个写的比我好的。</p> <p>最后做了一下比较有特殊技巧的题目,比如悬线法、优先队列优化 dp 等等,结束了 dp 部分的课程。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(100/100)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 刷题(340/200)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 活动(100/100)</p> </li> <li> <p>今日总分 $540\text{pts}$,溢出 $140\text{pts}$,挂分 $0\text{pts}$,总计增加 $1490\text{rp}$。</p> </li> </ul> <h3 id="11-月-16-日">11 月 16 日</h3> <p>集训今天结束,上午起的很晚,起来后收拾了一下吃了个饭就出发了。</p> <p>下午有场模拟赛,但是到家的时候模拟赛只剩不到两个小时了。</p> <p>开题,5 min 会了 A,然后 10 min 切掉了。开 B,给了一堆二元关系,考虑了并查集和图论,最后感觉像是转成图论后用拓扑之类的,但是由于 $n,m$ 很小,很慌。写完之后过了小样例,大样例第一个测试就挂了,但是先交一发再说!开 C,呃,随机增量法?哦好像不对,求的不是同一个东西……不会。开 D,不会。回来写 B,再重新推了一下之后发现之前写的并不完全,改一下之后过了小样例,大样例没测但是不管了直接交。</p> <p>然后就开摆去吃饭了。最后成绩:$100+16+0+0=116\text{pts}$,全场只有两个做出来 B 的,没有做出来 CD 的,哈哈。</p> <p>晚上听了讲评,A 题我感觉我的做法比正解更好想。B 题正解是一个很神奇的 trick:当我们只关心数值之间大小关系的时候,我们可以考虑枚举或者二分一个数字,然后把大于等于这个数字的数看做 1,小于这个数字的数看做 0,然后再在 01 串的角度去按照题目 check 或计算。在 B 题中的体现则是:我们需要 check 所有排列($n\le18$),$O(n!)$ 难以接受,但我们可以按上述 trick 之后就可以转变为 $O(2^n)$ 枚举 01 串后对 01 串去 check。问了 mrsrz 老师说这个套路很常见,希望 NOIP 会考。C 题则是扫描线,其中有线段树的高级使用技巧。D 是防 AK 题,能写出来 $O(n^2)$ 的 dp 就不错了。</p> <p>希望 NOIP 我也能 10min 切掉 A。(考后upd:我去,我 NOIP 真的 10min 切了 A)</p> <p>晚上刷了板子,随了一道 CRT,然后自己挑了没写过的 KMP 和点双。今天没写题,也没补课,不应该,但是有点忙,实在没办法。</p> <p>现在任务更紧张了,打算放弃第五节课的数学部分了,相信 NOIP 不会考!明天任务计划就是把去年 T4 写了,然后补一补之前的比赛题,然后整理一下午的 trick。</p> <ul> <li> <p><input checked="" disabled="" type="checkbox"> 模板(160/100)</p> </li> <li> <p><input disabled="" type="checkbox"> 刷题(0/200)</p> </li> <li> <p><input checked="" disabled="" type="checkbox"> 活动(100/100)</p> </li> <li> <p>今日总分 $260\text{pts}$,溢出 $0\text{pts}$,挂分 $0\text{pts}$,总计增加 $1490\text{rp}$。</p> </li> </ul> <h3 id="11-月-17-日">11 月 17 日</h3> <p>洛谷运势:中吉。最后一天了。</p> <p>早上起来之后看了下去年的 T4,想了一会只会 $O(n^2q)$ 的做法,感觉能优化到 $O(nq)$。</p> <p>毕竟难度还是有的,所以就不考虑死磕了,直接开题解,发现是我没学过的东西,区间历史和是什么?在大佬的博客学了一下吉老师的神仙做法后再回来看就明白多了。</p> <p>做法大致会了,确实就是查询区间历史和,不过具体式子还没有详细推,放掉这题了。</p> <p>然后就开始整理 trick 了,把集训期间所有比赛以及洛谷 NOIP 冲刺计划所有比赛和我这三周做的题都看了一下,从上午整理到晚上才算是基本整理完,希望考到的全是上面的。</p> <p>还看了一些大佬收集的 trick、易错点、骗分等内容。</p> <p>停课日记正文的最后一篇了,这三周应该是我学 OI 以来进步最大、最认真、水平最高的时间。不祈求超常发挥,不要给自己太大压力,希望明天能不留遗憾就好!</p> <p>NOIP 2023,rp++!</p> <h2 id="后日谈">后日谈</h2> <p>感觉一下子,不仅仅是 NOIP,很多事情都结束了。要说的话反而意外地没有那么多,一时间不知道究竟需不需要单独开一篇游记,索性就接着记录于此了。</p> <h3 id="11-月-18-日">11 月 18 日</h3> <p>考完 NOIP 回来之后,其实已经陆陆续续在各大估分平台上得知了自己的成绩。所有平台除了 T3 有小浮动以外都一样,大致估分应当如此:$100+100+[10,25]+0=[210,225]\text{pts}$。</p> <p>考场上还特意用注释标记了时间,所以这里就简单写一段游记吧:</p> <p>8 点 52 分,开考 20min 时读了一遍题之后切掉了 T1。</p> <p>T2 从 2-sat 想到并查集想到有向图想到搜索,换了很多做法调了很久,10 点 47 才过掉所有样例,其中最大样例刚测的时候炸了,怀疑爆栈了,然后确实调大了就过了。</p> <p>此时考试时间刚过去一半,前两题还没拍但是已经过了所有样例。心里想着貌似有机会写出 T3 或者至少剩下两题打个比较优的暴力,于是战术洗手间,让自己冷静一下。</p> <p>回来继续开 T3,在刚开考读题的时候就已经有过一些思考,所以读完 T3 之后马上就会了判断合法情况的方法,那接下来只要考虑如何进行构造即可。大概是由于这题数据比较难造,我手造的小样例完全没有任何强度,让我误以为可以直接贪心,写了半个多小时之后只过掉了第一个小样例,而第二个样例在手玩两三次之后发现依旧可以直接贪心,所以依旧继续错下去。最后不到一小时的时候赶忙去看 T4。</p> <p>然而 T4 没有什么头绪,感觉可以 dp,但是 dp 式子貌似很难写。最开始大概糊了个 $O(mk)$ 的做法,发现根本写不出来,一步步退档,最后写了个不知道什么东西,总之只过掉了第一个样例。</p> <p>回到 T3,这时候决定不要再想正解了,直接注释掉代码开始暴搜,发现连搜都搜不对,很不理解。但此时 T4 又有一个 $O(nk)$ 思路,决定在最后半小时冲 T4 暴力。</p> <p>到最后依旧没冲出来,依旧只过了第一个样例。最后十分钟回去调 T3 暴力,也没调出来,也只过了第一个样例。收卷最后十秒发现暴力的一处错误,改掉了但没有任何测试。</p> <p>出考场很平淡也很无奈,AB 过了但没拍,CD 暴力很可能写挂,这就是我最后的结局。</p> <p>回到家后拿到了代码,测了 C 的小样例对了,松了一口气。另外这三周备赛挺稳的,代码一次性过的概率很高,所以我相信我应该是不会挂分的。那么最终的成绩应该就是看后面两题的暴力究竟能不能打出一点东西吧。</p> <p>最好的结局就是 CCF 的数据过水,T3 意外拿了比较多的分,然后 T4 也能有一个点的强度跟样例相同导致我的神秘暴力可以骗到一点分。但是这不太可能,而且用处也不大。</p> <p>有一个学的比我晚几个月的同届也做出了前两题,但是他后面两题挂的很彻底。有两位高三的学长为了 NOIP 冲 1= 也停了一段时间课,但最后的成绩有点遗憾。</p> <p>其实这次我打的并不差,可能确实到水平上限了。该做出来的题确实做出来,剩下两道也确实不在我的能力范围内,只是两个多小时连暴力都没打好,有些可惜。</p> <p>这段时间完全荒废了 whk,之后要补的内容实在太多太多。最终考的分数比 CSPS 高,但又没高到哪去。不过还算安慰的是,至少这个赛季始终都是 1= 的。</p> <h3 id="11-月-19-日">11 月 19 日</h3> <p>今天上午作为深圳科创之星代表被邀请参加了创客节的活动,看到了很多项目,但其实感觉所谓的 AI 展和我所喜欢和期待的人工智能略有一点区别。仔细想想,其实我想研究的应该叫机器学习或者神经网络,相比于现在所谓的广泛的人工智能,应该要更加理论化,也更贴近与编程。</p> <p>下午五点,CCF 终于公布了 CSP 获奖名单,而 cz 也马上就公布了今年的钩子线。没想到在超大基数的加持下,今年的七级钩真的只需要 175。</p> <p>喜提七级钩,蛮高兴的。</p> <h3 id="11-月-20-日">11 月 20 日</h3> <p>一觉睡到了将近十二点。</p> <p>咕值更新了,$322$,排名 $344$。下午获奖记录更新了,也顺利拿到了七级钩。</p> <p>今晚开始就要回学校回归 whk 了。</p> <h3 id="11-月-26-日">11 月 26 日</h3> <p>这一周基本在专心 whk,简单复盘了一下 NOIP,并且至今不会 T3T4,确实是实力问题无法避免,不过暴力写挂了比较可惜。查到了得分:$100+100+10+0=210\text{pts}$,取到了估分区间的下界。</p> <p>其实打的并不差,在省选中占分比去年高了几分,但还是希望不大,所以决定直接退役了。WC 没必要参加了,APIO 也不打算参加了,NOID 太费钱还一点用没有,说实话这两万给我拿去买好的电脑或者游戏主机岂不是更爽?</p> <p>打算打一场 THUPC,然后明年争取 NOIP 再考个 1= 就彻底结束了,大学也有点懒得打 AMC 了。</p> <p>其实以我的实力,THUPC 和网上认识的比较强的选手组队的话有可能可以打进决赛的,但是还是决定跟同学一起组队打,不过说实话进决赛希望不大,但也不重要了。</p> <p>其实我放得下的,这半年来学算法已经学傻了。打算退役后回归到之前,自己玩玩编程,做做项目,有空打打游戏写写小说,在学校就认真 whk,我一样会过得很好。</p> <p>虽然这么说,但其实我 whk 不太想跟学校。不知道有没有那种速成班,半年卷完文化课。</p> <p>停课这段时间天天写日记,其实有点养成习惯了,感觉写的时候回顾一天的历程很放松,最近没有写反而感觉有点奇怪。哈哈,说不定之后我要开始写周记了。</p> <h3 id="12-月-9-日">12 月 9 日</h3> <p>去你的 OI,我发现原来我的生活中没有 OI 过的一样很好,我只是想玩电脑罢了。</p> <p>重新读了一遍日记,发现我之前因为模拟赛里 10min 切了 A,说过希望我 NOIP 也能 10min 切 A,没想到最后成真了,哈哈。</p> <p>下午回来打了场洛谷月赛,以后可能就这样隔一段时间打打比赛仅仅保持手感就好了。</p> <p>现在有点尴尬的是,我的博客里专门为题解开了一栏 Solutions,甚至还专门造了个 algorithms tag,然而我才发了四篇题解就 AFO 了,那我这不白搞了?</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <p>日记应该不会有参考文献吧……?</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span><span style="color:#c678dd">- [ ]</span> 模板(0/100) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">- [ ]</span> 刷题(0/200) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">- [ ]</span> 活动(0/100) </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">-</span> 今日总分 $0\text{pts}$,溢出 $0\text{pts}$,挂分 $0\text{pts}$,总计增加 $1490\text{rp}$。 </span></span></code></pre></div><h3 id="版权信息">版权信息</h3> <p>本文原载于<a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循CC BY-NC-SA 4.0协议,复制请保留原文出处。</p>

2023/10/30
阅读更多

拾光纪行 | CSP-S 2023 复赛游记

<h2 id="前传">前传</h2> <p><a class="link" href="https://blog.reincarnatey.net/2023/0916-csps2023/" target="_blank" rel="noopener" >随笔杂谈 | CSP-S 2023 初赛游记</a></p> <h2 id="游记">游记</h2> <h3 id="day--22">day ~-22</h3> <p>初赛回来后,进行了深刻的反思。</p> <p>我怀疑我的基础并不是很牢,于是开始尝试去打新手村。</p> <p>直到国庆节前的这段时间其实大部分时间真的去打了,貌似打了近百道,打到 bfs 了,也不清楚具体打到了多少。(upd:后面才知道原来新手村有题单,一直是对着剪贴板做的)</p> <h3 id="day--22-9">day -22~-9</h3> <p>国庆后,新手村就先停下了。</p> <p>倒不是因为我觉得写简单题太无聊,而是因为我洛谷网校在国庆有课,这段时间基本是在跟着秋令营的课和题单慢慢做,慢慢学。</p> <p>这让我想起来之前多校联训的课也是要补的,基本上我只跟着学了个莫队后基本一直处在挂机状态了。</p> <p>(upd:这很不对。我不应该这样,花了钱报了课最后又没听进去,最后没有好结果的还是我自己。)</p> <p>顺带一提,这段时间还拿了一道黑题的最优解,还写了我的第一道题解 <a class="link" href="http://blog.reincarnatey.net/solutions/p4480/" target="_blank" rel="noopener" >P4480 [BJWC2018] 餐巾计划问题</a>,算是一个里程碑了!</p> <p>其实这段时间没有专心搞 OI,因为这段时间的最后两天要考月考。我一直认为竞赛和 whk 之前不得不有一个平衡,否则两个都搞不好,而我确实没办法承担丢下 whk 后可能带来的后果(upd:停课真爽,不想上学了)。</p> <p>不得不分心去复习一下,至少 whk 不能丢下太多,想保持在重点班第 5, 6 名左右的位置已经让我感到吃力了,但最后考试结果还是在接受范围内。</p> <h3 id="day--8-1">day -8~-1</h3> <p>考完试之后,这段时间基本就在停课了。</p> <p>最初我还是很认真的一直打模板、补充知识点,到后来又出现老毛病了——越到关头越松懈,反而一天天开始摸鱼越来越频繁了。</p> <p>最开始只是打打音游(说起来,由于换了个网,打不开之前的 4K 音游了,于是换了个 <a class="link" href="https://www.beatstage.com/" target="_blank" rel="noopener" >5K 的音游</a>,之前一直不习惯空格,现在已经能打的比较顺了,fc 了 YOASOBI 的两首曲子的中等难度),后来大家在机房里翻到了不知道什么团体之前使用机房留下来的 cs1.6(这个机房没有冰点还原),于是信竞就真的变成电竞了。</p> <p>当然我还是有保证一定量的做题的。</p> <p>貌似我初赛之后也是这个样子的?下次绝对不会再这样了。</p> <p>这段时间我同时还自己出了四道 ds 模板题,拿来出校测,还是蛮有意思的一次出题体验。当然,为了不让题目太板,我每个都在板的基础上加了点需要思考的内容,同时也给自己拓宽了一点想法(比如线段树新增一种特殊的懒标记、树剖搭配树状数组之类的)</p> <p>后来因为办的不错,教练要求再加一场,于是从洛谷选了四题我自己做过的感觉比较有学习价值(增加了我的 trick 的那种)的题目。</p> <p>另外这段期间,做了超多模板题,包括 Nim 游戏、各种基础数据结构和图论,以及补了很多提高级范围内的数论!(但最后如我所料,一点没用上)</p> <p>到最后一天为止,终于完成了我计划内的所有模板题,包括最长的树剖以及最不熟练的 splay,顺带修了下很久之前写的 splay 的错误然后过了数据加强版。(所以现在我觉得我的 splay 熟练很多了,但做题还是不一定看得出来)</p> <h3 id="day-0">day 0</h3> <p>上午又打了一遍 splay,看了一下对拍(直到这个时候我还是觉得我考场上有机会写对拍的)以及各种考前注意事项和骗分导论。</p> <p>考场还是去年那个,很小,很烂,而且还不能带零食进考场。</p> <p>开考时解压包密码还输错了一次,属于是开题不利了。</p> <p>T1 看了几分钟,硬是没看懂题意,不过好在我见过一道类似的题目(给出 $n$ 个约束条件,求出原数的可能种数),还是差不多读明白了(upd:其实并没有)。</p> <p>后来我注意到,其实只需要 $O(10^5n)$ 枚举然后 check 一下貌似就可以了,换句话说就是时间完全够用,所以立刻就开始动手写了。</p> <p>然而悲惨的事,我题意有一部分理解错了,我以为他要求 $n$ 个状态都是同时滚两个滚出来的或者都是同时只滚一个滚出来的,这就导致我不仅花费了更多时间,写出来代码连第二个样例都没过,到最后都没想明白为什么第二个样例答案是 $10$。</p> <p>我一直怀疑那个 $10$ 里面算上了两个状态本身,然而题目说状态本身不正确,所以我一度考虑特判 $n\gt1$ 时算上状态本身,但最后还是没这么写。(upd:谢天谢地我没改)</p> <p>大约卡了一个半小时后,我决定先停下 T1,先把后面至少写点暴力先,等后面再回来调(upd:根本回不来)。</p> <p>T2 看到串串题,再见!</p> <p>T3 一看到题面就猜到大模拟了,虽然很烦但技术含量倒不高,有可能可以做,看了眼 T4 估计只写得出暴力,就打算先写一个小时 T3 了。读完题之后发现这个东西很复杂,但是我意外地没有什么阅读上的问题,都能读得懂,心里也大致对结构有一点想法了,于是就火速开始写。</p> <p>其实我之所以敢冲大模拟而不是像大部分人一样见到大模拟就跳,是因为我一直以为我写过一道紫色大模拟题 <a class="link" href="https://www.luogu.com.cn/problem/P2262" target="_blank" rel="noopener" >[HNOI2004] FTP服务器 - 洛谷</a>,我觉得这道题貌似没有那道难,最难的部分应该就是考察 STL 的应用以及操作三四的字符串处理了,刚好我都有在别的比赛中写过,感觉是能做的。(upd:后面回到家之后才发现其实那道题我还没写,一直躺在题单里面)</p> <p>刚开始写,我的想法是维护类型的大小和对其要求以及他的成员,用 map 维护名字到类型 id 的对应,而初始时手动添加四个基本类型即可,很快就写完了操作一,而且操作一是一次性过的。</p> <p>开始写操作二,一样的做法,维护变量的名字、类型和起始地址,用 map 维护名字到变量 id 的对应,很快写完了操作二,而且很神奇的也一次性过了。</p> <p>由于莫名其妙的居然一点问题都没有,这时候就整个人是非常有信心的状态。</p> <p>开始写操作三,这个涉及到一个字符串分割,幸运的是我应该是在最近一个月的某场 ABC 或者 CF 上写过这个,简单调试后很顺利的写出来了,剩下的操作其实就是顺着字符串进行一个递推搜索,累加答案,写完了简单调了会之后过掉了第一个样例,并且把第一个样例的每一个成员都做了下操作三,跟我纸上算出来的一模一样。这时候这一题已经写了超过 1h 了。</p> <p>开始写操作四,这个也是跟上一题一样的思路,顺着地址进行一个递推,然后路上拼接字符串出答案即可。代码写得很乱,但是逻辑上觉得没问题,就这么写下去,最后调完过掉样例时已经过了 1.5h 了。我还对每一个地址都算了一下操作四,也跟我纸上算的一模一样。</p> <p>这时候我已经过掉了第一个样例,非常的高兴,结果一测第二个样例,fc 出来好几行不同,当时心态都要崩了,写代码写的很乱,很不好调了已经。</p> <p>幸运的是,仔细看了下第二个样例,发现还是比较简单的,在纸上模拟出来之后对程序进行了调试,发现是在下标计算啊大小对齐之类的问题,以及操作四判空那里漏判了一种情况。调了 20min,过掉了第二个样例。</p> <p>这时候我高兴的去测大样例,fc 出来满屏的不同,命令行每滚动一行我的心里就像是被捅一刀,但是仔细看后发现我输出了负数!于是返回代码,把所有大小有关的变量开了llu,顺带还发现操作三漏掉了一个跟地址有关的变量没开llu。迅速的修改完之后,三个样例都过掉了!!!</p> <p>虽然我很高兴,但高兴的同时时间只剩下 30min 了,而我除了 T3 之外只写了一道 T1,而且还没写出来。</p> <p>这时候决定全力去冲剩下的暴力,T1 就丢掉了,相信能够拿到部分分。开始写 T2,第一个猜想是跟回文有关,似乎处理出每个点的长度为偶数的回文串的长度,然后对连续的回文串做个前缀就可以了,继而想到我一点也想不起来的 Manacher。</p> <p>考场上几乎认定这是正解了,于是很难过,前几天想学的时候翻 OI Wiki 的时候想补 Manacher 但看到概念又觉得不会考最后还是放掉了,感觉很后悔。</p> <p>于是我想着没办法了,打个暴力吧。但是可能是因为悲喜交加、时间紧迫、没有零食,导致我考场严重降智,竟然完全没想到枚举左端点然后用栈可以做到 $O(n^2)$ 的暴力,而是蠢蠢的写了一个 $O(n^2)$ 处理出所有偶数回文串后又 $O(n^3)$ 合并的鬼东西,而且还不知道哪里写错了,连第二个大样例都没过。(upd:考后发现是结论假了,合并做法是错误的)</p> <p>看完 T4,我感觉有点贪心,因此脑子里也闪过二分的想法(因为刚做过一道三分答案后贪心的黑题,现在对于二分三分+贪心这种简单算法可以出难题是深信不疑的),但是很遗憾时间只剩几分钟了,于是我飞速的打了一个骗分,特判了一下 $n=1$ 的情况,其余就输出样例。</p> <p>最后比赛结束的时候,一度感觉 AFO 了。毕竟本来是为了弥补去年的遗憾,才希望今年复赛拿个 1= 的,结果这次感觉考的比上次还差。</p> <p>在校车上明白了 T1 我理解错的点了,发现如果我理解正确的话我写代码的时间应该还能短一半,感觉很亏。虽然确实是我的问题,但我还是强烈谴责 CCF 不给大样例,并且第二个样例没有任何解释,我觉得他哪怕给我一个 $10=8+2$ 我都能想出来。顺便我才发现 $n=1$ 的时候答案必定为 $81$,那我相信我能保下这个部分分,不爆零就意味着 NOIp 还有机会。(upd:今年高中生不是有分就行了,好在我最后考的还不算差)</p> <p>另外 T2 我也觉得我能拿点部分分,不过看到有大佬的线性做法,感觉很震惊,但是看不懂代码!</p> <p>发现很多人都跳了 T3,并且很多人说自己 T3 挂了,很害怕自己 T3 也挂分。</p> <p>发现 T4 确实是排序后贪心,我宣布我精神上做出了 T4。</p> <p>回家路上搞懂了 T2 的线性做法,类似于一个接受回文串的状态机,同时维护了连续回文串的数量(这里还是在坚持我的那个想法),到家后已经十点多了,今天可是周六耶?</p> <p>考后估分:$30+20+100+0=150\text {pts}$,和去年一样的分数,估计没 1= 了。</p> <h2 id="后日谈">后日谈</h2> <ul> <li> <p>10.21:回家后拿到代码自测了,T1 莫名奇妙在各大 OJ 都跑到了远超我预期的分数,T2 也很稳定都都反向挂了五分,T3 也运行的很稳没有挂,T4 也稳定的一分都没骗到!目前云斗估分:$90+25+100+0=215\text {pts}$,核桃估分:$80+25+100+0=205\text{pts}$,洛谷和小图灵还没出完整数据。</p> </li> <li> <p>10.22:洛谷估分:$70+25+100+0=195\text{pts}$,小图灵估分:$80+20+100+0=200\text{pts}$。没想到 T3 意外地很稳,一点都没挂,T4 依旧一分都没骗到。T2 有一点小浮动但和我预期的差不多,而 T1 就是一直在 $[70,90]$ 漂浮不定,基本是决定我这次的分数了。</p> </li> <li> <p>10.28:这段时间看了很多游记,同时 AK 了洛谷上的自测并写了些题解。看游记的时候学到了很多做法,同时也发现很多人都和我一样 T2 不会栈做法或者推出错误的结论,松了口气。另外看游记的时候又找到俩估分平台,计蒜客估分:$70+15+100+0=185\text{pts}$,信友队估分:$80+25+100+0=205\text{pts}$。然而 CCF 就是不出分,不知道要拖到什么时候。</p> </li> <li> <p>10.29:昨晚刚说完,今天一早就出分了:$80+25+100+0=205\text{pts}$,跟核桃和信友队上的估分一模一样。至少是上 $200$ 了,松了一口气。虽然我本就发挥的不好,但现在看来还不算很差。</p> </li> <li> <p>10.30:把<a class="link" href="https://blog.reincarnatey.net/solutions/csps2023/" target="_blank" rel="noopener" >这次比赛四道题的题解</a>写完了。听说今年七级钩线很低,那我岂不是还有希望喜提七级钩!</p> </li> </ul> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li> <p><a class="link" href="https://blog.reincarnatey.net/solutions/csps2023/" target="_blank" rel="noopener" >CSP-S 2023 Solutions</a></p> </li> <li> <p>还有许多其他人的游记</p> </li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于<a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循CC BY-NC-SA 4.0协议,复制请保留原文出处。</p>

2023/10/22
阅读更多

拾光纪行 | CSP-S 2023 初赛游记

<h2 id="关于博客">关于博客</h2> <p>在这篇游记之前,我想先提一下关于我消失的这大半年。</p> <p>最后一次发布博客,是在今年一月了。那时候博客刚建立不足一月,又正值寒假,自然更新的很勤快,我相信绝大部分博客都是这样的。</p> <p>虽然我并没有更新,但我说过,我不会放弃这个博客,这句话到目前为止依旧有效。</p> <p>实际上,在今年四月份的时候,我还有在编写博客,内容是为Stack模板添加更多界面,实现像本博客一样的题解等功能。但是由于改动部分太多,间隔时间太久导致对GO语言越来越不熟悉,最后便一点也写不出来了。当然,等有空了我会尽量填坑的。</p> <p>由于我在那之后是专心学习OI,几乎没有碰什么项目,我的模型也停止了训练,最后确实很长一段时间都没有什么能写的内容了。这段时间唯一有在确实的进步的,恐怕只剩下设计了——绞尽脑汁为学校社团设计了张海报,用了许多半调和几何之类的,PS技术也得到了非常大的进步,个人还是非常满意的。(或许我以后有机会可以在博客里发一些设计相关内容?)</p> <p>另外,很不幸的是,由于服务器ip地址重新分配,导致我的域名解析失效,因此整个网站貌似有几个月的时间只有云盘能上了。当然,目前已经解决了该问题。</p> <p>在接下来很长一段时间,我都有可能会与之前一样处于没有内容可以更新的状态。不过,我会试着写一些于我而言很有启发意义的题的题解,算是另外一方面的更新吧。</p> <p>接下来是游记正文。</p> <h2 id="游记">游记</h2> <p>想了很久,还是决定把游记发在博客而不是题解区里。话说我为什么连CSP-S初赛都想发游记呢?可能是因为我只是单纯的想发一篇游记,顺便提一提我最近的状况。</p> <h3 id="前言">前言</h3> <p>其实这不是我第一次写游记。去年省选联考的时候我也写了一篇超长的游记,然而D1T1因为返回类型写错导致100pts -&gt; 0pts,省选总分烂到没脸发出来,在游记结尾定的今年的目标看起来也过于笑话,所以并没有发出来。</p> <p>去年的初赛,我作为一个刚学OI不足三个月的初学者,莫名其妙考了六十来分,还抱回了自己的第一个一等奖。</p> <p>在这之后,一路学一路比,最后止步于省选,结束了我第一个赛季。</p> <p>经过剩下半个学期和暑假的努力,我应该是进步了很多的,希望这次能走的更远。</p> <p>哎呀,怎么前言全在写以前的事呢。</p> <h3 id="day--7---2">day -7 ~ -2</h3> <p>这段时间一直在认真复习初赛知识点,甚至完全没有摸鱼。</p> <p>基本上每天一套模拟题或者往年真题,然后其余时间就在b站上看真题解析,或者是给机房其他同学讲题。</p> <p>在这里感谢一下谷友们总结的<a class="link" href="https://www.luogu.com.cn/blog/334586/csp-pre-knowledge#" target="_blank" rel="noopener" >初赛知识点提纲</a>,虽然最后究竟考到了多少我也不清楚。</p> <p>总体来说,刷的真题和模拟题都在七十分以上,自己认为还是很稳的,但是距离目标还是差一点。</p> <p>基本上是寄希望于考场超常发挥,做的好一点。遗憾的是,这个希望破灭了。</p> <h3 id="day--1">day -1</h3> <p>距离考试只剩下一天,最后做的一套模拟题,虽然主观上很难,但依旧拿了78分。</p> <p>哟西,很稳。</p> <p>于是反而更加放松了。由于没有同学需要我解答问题,于是我开始了摸鱼,这时还是简单打打<a class="link" href="https://www.sparebeat.com" target="_blank" rel="noopener" >4k的网页音游</a>。</p> <h3 id="day-0">day 0</h3> <p>教练说上午就不刷题了,好好放松一下,于是我名正言顺的坐在机房里摸鱼。打打音游,然后在剪贴板里找到了个<a class="link" href="https://wyxkk.gitee.io/the-luogu-tree-zh/" target="_blank" rel="noopener" >洛谷树</a>的游戏,于是一边挂机一边打音游,偶尔给同学讲题,上午大概就这样子过去了。</p> <p>中午和教练以及同学们一起吃了个午饭,收拾了一下东西,就出发了。</p> <p>校车好颠簸。</p> <p>下午以一种神奇但不能说的方式进入了考点。找了一小会考场,发现居然是在电脑室。或许是件好事吧,刷题的时候一直是在电脑上刷的,或许在电脑室的话比较能找到感觉。</p> <p>他们的电脑室好漂亮,桌子好舒服,就是显示器有点小。</p> <p>隔壁怎么是个小学生。感觉比我强。感觉被压力了。</p> <p>开考前,先涂了个什么鬼填涂知情协议书,对我这个填了这玩意儿几年的学生来说自然不是问题。</p> <p>发试卷,先秒掉第一题,然后看到第二题就感觉不对劲了,越往后面越觉得前几题难度不太对。</p> <p>糟糕,前15题做太久了。</p> <p>糟糕,阅读题第一道做太久了。</p> <p>空调好冷,手好冻。</p> <p>糟糕,只剩下二十分钟了,我还没写填空。哦豁,完蛋。</p> <p>后面就疯狂赶进度,十五分钟把填空题做完然后涂卡,至少是在比赛结束前完成了所有内容。</p> <p>好在最后填空题并不是很难,我也并没有失去很多分。但是,没有以往做模拟题的那种有70分的感觉。</p> <p>总的来说,还是相信自己不至于初赛都过不了的,但是总感觉没有去年高。出了考场心情算是不太好,感觉发挥的很不好,没有这段时间做模拟题和真题的状态。</p> <p>不知道是摸鱼过度了,还是不习惯纸质题,但总之我知道肯定到不了我应该考到的分数。</p> <p>回去的路上,在校车上一直在想:会不会有人比我更惨,准备了一年冲省队,结果初赛翻车连第二把的机会都没有。竞赛这个东西,那么多次比赛,只要失误一次就止步于此了,那样真的会很不甘心。</p> <p>回家后简单测了一下分数,一开始在计蒜客上测得七十多分,然后在小图灵上是不到七十分,只比去年高了一点点,感觉这次就这样了。虽然发挥失常,但是一等奖还是有的,复赛也不至于进不了。</p> <p>接下来还是准备好好备赛复赛,在NOIP前展示一下自己的实力,目标是两题半不挂分就行。打算把题库上的提高组的题好好刷一下,以及开始攻略洛谷新手村。</p> <p>希望自己复赛能发挥的更好吧。</p> <h3 id="后日谈">后日谈</h3> <ul> <li> <p>09.29</p> <p>出成绩了,感觉的很准,确实没上70,只有68.5。出成绩前一直担心会不会有涂错卡之类的问题,看到分数跟预估的一模一样才放下心。虽然连一等奖都很稳,但还是觉得没有发挥好。按照平时刷题的状态,这种题应该属于75以上,可以试试冲80的。前面时间花费太多了,可能是我给自己压力太大了,毕竟关系到的太多太重了。不过不管怎么说,稳过就好。</p> </li> </ul> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <p>无</p> <h3 id="版权信息">版权信息</h3> <p>本文原载于<a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循CC BY-NC-SA 4.0协议,复制请保留原文出处。</p>

2023/9/16
阅读更多

学习笔记 | MVVM架构中事件(Event)的正确处理方式

<h2 id="前言">前言</h2> <p>最近读到<a class="link" href="https://blog.csdn.net/vitaviva/article/details/122163479" target="_blank" rel="noopener" >一篇博文</a>,一语惊醒梦中人。虽然早就注意到了这个问题的存在,却对解决方法没有任何的思考,看来我对MVVM的理解依旧不够深。十分感谢博主,学习到了很有用的知识,特记录此笔记。</p> <h2 id="mvvm中的事件">MVVM中的“事件”</h2> <p>在MVVM架构中,ViewModel通常承担着处理数据的责任,在处理完数据后,将新的UI状态通过LiveData传递给View进行UI更新。其中,LiveData起到了在VM与V中的数据通信作用。</p> <p>这个逻辑没有任何问题,同时也是目前MVVM的主流和推荐的做法。</p> <p>然而,相信很多人在使用MVVM架构的过程中都遇到了这个问题:<strong>有些时候ViewModel处理完数据后,并不需要更新UI,只不过是想要进行一些需要Context的操作</strong>。也就是本文中的“事件(Event)”</p> <p>例如:弹出Toast、导航、页面跳转。这些或许算UI“操作”,但在我的理解上和UI的“更新”八竿子打不着边。</p> <p>这时候很容易想到用LiveData来传递……事件?</p> <p>错错错,大错特错!</p> <h2 id="事件event和状态state的区别">事件(Event)和状态(State)的区别</h2> <p>那篇博文关于这里讲的很好,这里简单摘抄一下:</p> <p>虽然“状态”和“事件”都可以通过响应式的方式通知到 UI 侧,但是它们的消费场景不同:</p> <ul> <li> <p><strong>状态(State)</strong>:是需要 UI 长久呈现的内容,在新的状态到来之前呈现的内容保持不变。比如显示一个Loading框或是显示一组请求的数据集。</p> </li> <li> <p><strong>事件(Event)</strong>:是需要 UI 即时执行的动作,是一个短期行为。比如显示一个 Toast 、 SnackBar,或者完成一次页面导航等。</p> </li> </ul> <p>我们从覆盖性、时效性、幂等性等三个维度列举状态和事件的具体区别</p> <table> <thead> <tr> <th style="text-align: center"></th> <th style="text-align: center">状态</th> <th style="text-align: center">事件</th> </tr> </thead> <tbody> <tr> <td style="text-align: center"><strong>覆盖性</strong></td> <td style="text-align: center">新状态会覆盖旧状态,如果短时间内发生多次状态更新,可以抛弃中间态<strong>只保留最新状态</strong>即可。这也是为什么 LiveData 连续 postValue 时会出现数据丢失。</td> <td style="text-align: center">新事件不应该覆盖旧事件,订阅者<strong>按照发送顺序接收到所有事件</strong>,中间的事件不能遗漏。</td> </tr> <tr> <td style="text-align: center"><strong>时效性</strong></td> <td style="text-align: center">最新状态是需要<strong>长久保持</strong>的,可以被时刻访问到,因此状态一般是“粘性的”,在新的订阅出现时为其发送最新状态。</td> <td style="text-align: center"><strong>事件只能被消费一次</strong>,消费后应该丢弃。因此事件一般不是“粘性”的,避免多次消费。</td> </tr> <tr> <td style="text-align: center"><strong>幂等性</strong></td> <td style="text-align: center">状态是幂等的,唯一状态决定唯一UI,<strong>同样的状态无需响应多次</strong>。因此 StateFlow 在 setValue 时会对新旧数据进行比较,避免重复发送。</td> <td style="text-align: center">订阅者需要<strong>对发送的每个事件进行消费</strong>,即使是同一类事件发送多次。</td> </tr> </tbody> </table> <p>看到这里,你是不是也和我一样突然恍然大悟?</p> <h2 id="传递事件的正确姿势">传递事件的正确姿势</h2> <p>EventBus、Flow都可以用于处理事件,然而历史悠久的EventBus虽然业界推崇但太难还有点小题大做、未来主流的Flow虽然谷歌推荐但太新不会用还有点看不懂。</p> <p><strong>就想用LiveData,怎么办?</strong></p> <p>这里贴一个相对完善的解决方案:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#c678dd">open</span> <span style="color:#c678dd">class</span> <span style="color:#e5c07b">LiveEvent</span>&lt;<span style="color:#e06c75">T</span>&gt; : <span style="color:#e06c75">MediatorLiveData</span>&lt;<span style="color:#e06c75">T</span>&gt;() { </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">private</span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">observers</span> = <span style="color:#e06c75">ArraySet</span>&lt;<span style="color:#e06c75">ObserverWrapper</span>&lt;<span style="color:#c678dd">in</span> <span style="color:#e06c75">T</span>&gt;&gt;() </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#61afef">@MainThread</span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">observe</span>(<span style="color:#e06c75">owner</span>: <span style="color:#e06c75">LifecycleOwner</span>, <span style="color:#e06c75">observer</span>: <span style="color:#e06c75">Observer</span>&lt;<span style="color:#c678dd">in</span> <span style="color:#e06c75">T</span>&gt;) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">find</span> { <span style="color:#c678dd">it</span>.<span style="color:#e06c75">observer</span> <span style="color:#56b6c2">===</span> <span style="color:#e06c75">observer</span> }<span style="color:#56b6c2">?.</span><span style="color:#e06c75">let</span> { <span style="color:#e06c75">_</span> <span style="color:#56b6c2">-&gt;</span> <span style="color:#7f848e">// existing </span></span></span><span style="display:flex;"><span><span style="color:#7f848e"></span> <span style="color:#c678dd">return</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">wrapper</span> = <span style="color:#e06c75">ObserverWrapper</span>(<span style="color:#e06c75">observer</span>) </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">add</span>(<span style="color:#e06c75">wrapper</span>) </span></span><span style="display:flex;"><span> <span style="color:#c678dd">super</span>.<span style="color:#e06c75">observe</span>(<span style="color:#e06c75">owner</span>, <span style="color:#e06c75">wrapper</span>) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#61afef">@MainThread</span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">observeForever</span>(<span style="color:#e06c75">observer</span>: <span style="color:#e06c75">Observer</span>&lt;<span style="color:#c678dd">in</span> <span style="color:#e06c75">T</span>&gt;) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">find</span> { <span style="color:#c678dd">it</span>.<span style="color:#e06c75">observer</span> <span style="color:#56b6c2">===</span> <span style="color:#e06c75">observer</span> }<span style="color:#56b6c2">?.</span><span style="color:#e06c75">let</span> { <span style="color:#e06c75">_</span> <span style="color:#56b6c2">-&gt;</span> <span style="color:#7f848e">// existing </span></span></span><span style="display:flex;"><span><span style="color:#7f848e"></span> <span style="color:#c678dd">return</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">wrapper</span> = <span style="color:#e06c75">ObserverWrapper</span>(<span style="color:#e06c75">observer</span>) </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">add</span>(<span style="color:#e06c75">wrapper</span>) </span></span><span style="display:flex;"><span> <span style="color:#c678dd">super</span>.<span style="color:#e06c75">observeForever</span>(<span style="color:#e06c75">wrapper</span>) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#61afef">@MainThread</span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">removeObserver</span>(<span style="color:#e06c75">observer</span>: <span style="color:#e06c75">Observer</span>&lt;<span style="color:#c678dd">in</span> <span style="color:#e06c75">T</span>&gt;) { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">if</span> (<span style="color:#e06c75">observer</span> <span style="color:#c678dd">is</span> <span style="color:#e06c75">ObserverWrapper</span> <span style="color:#56b6c2">&amp;&amp;</span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">remove</span>(<span style="color:#e06c75">observer</span>)) { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">super</span>.<span style="color:#e06c75">removeObserver</span>(<span style="color:#e06c75">observer</span>) </span></span><span style="display:flex;"><span> <span style="color:#c678dd">return</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">iterator</span> = <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">iterator</span>() </span></span><span style="display:flex;"><span> <span style="color:#c678dd">while</span> (<span style="color:#e06c75">iterator</span>.<span style="color:#e06c75">hasNext</span>()) { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">wrapper</span> = <span style="color:#e06c75">iterator</span>.<span style="color:#e06c75">next</span>() </span></span><span style="display:flex;"><span> <span style="color:#c678dd">if</span> (<span style="color:#e06c75">wrapper</span>.<span style="color:#e06c75">observer</span> <span style="color:#56b6c2">==</span> <span style="color:#e06c75">observer</span>) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">iterator</span>.<span style="color:#e06c75">remove</span>() </span></span><span style="display:flex;"><span> <span style="color:#c678dd">super</span>.<span style="color:#e06c75">removeObserver</span>(<span style="color:#e06c75">wrapper</span>) </span></span><span style="display:flex;"><span> <span style="color:#c678dd">break</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#61afef">@MainThread</span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">setValue</span>(<span style="color:#e06c75">t</span>: <span style="color:#e06c75">T</span>?) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">forEach</span> { <span style="color:#c678dd">it</span>.<span style="color:#e06c75">newValue</span>() } </span></span><span style="display:flex;"><span> <span style="color:#c678dd">super</span>.<span style="color:#e06c75">setValue</span>(<span style="color:#e06c75">t</span>) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">private</span> <span style="color:#c678dd">class</span> <span style="color:#e5c07b">ObserverWrapper</span>&lt;<span style="color:#e06c75">T</span>&gt;(<span style="color:#c678dd">val</span> <span style="color:#e06c75">observer</span>: <span style="color:#e06c75">Observer</span>&lt;<span style="color:#e06c75">T</span>&gt;) : <span style="color:#e06c75">Observer</span>&lt;<span style="color:#e06c75">T</span>&gt; { </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">private</span> <span style="color:#c678dd">var</span> <span style="color:#e06c75">pending</span> = <span style="color:#c678dd">false</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">onChanged</span>(<span style="color:#e06c75">t</span>: <span style="color:#e06c75">T</span>?) { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">if</span> (<span style="color:#e06c75">pending</span>) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">pending</span> = <span style="color:#c678dd">false</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observer</span>.<span style="color:#e06c75">onChanged</span>(<span style="color:#e06c75">t</span>) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">newValue</span>() { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">pending</span> = <span style="color:#c678dd">true</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>该解决方案最早由 <strong>Jose Alcérreca</strong> 给出了思路和基本框架,经过了许多大佬修改和完善,使用方法也大致和LiveData相同。</p> <p>有一点比较重要的是,LiveData数据是粘性的,无论何时订阅都会立即触发一次 <code>onChanged()</code>。对于状态来说,订阅时立即获得最新的状态,这没有问题;然而对于事件来说,并不需要获得订阅前的上一个事件。</p> <p>变量 <code>pending</code> 在这里正是用于解决这一问题的。在初始化时 <code>pending = false</code>,使得在刚订阅时不会立即触发 <code>onChanged()</code>。</p> <p>相似解决方式有很多,例如美团和<a class="link" href="https://juejin.cn/post/6844903949267386376#heading-1" target="_blank" rel="noopener" >这篇文章</a>的方案都是设置一个版本标识符,初始化时等于LiveData的version,再通过判断新接收的数据的version是否大于存储的version来分辨是不是上一次的数据。</p> <p>另外,该文章中也提供了使用LiveData实现事件总线的思路和例子,可以参考。</p> <p>不过我并不希望在 <code>ObserverWrapper</code> 中持有LiveData对象,因此推荐使用 <code>pending</code> 做法。</p> <p>因此,实际上,这个方案与LiveData的区别只是非粘性而已,对 <code>postValue</code> 丢失数据的问题并没有进行处理。方法当然也是有的,我参考<a class="link" href="https://www.jianshu.com/p/b2646bf112ce" target="_blank" rel="noopener" >这篇文章</a>对该方案进行了修改,增加了一部分内容以避免丢数据:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#c678dd">open</span> <span style="color:#c678dd">class</span> <span style="color:#e5c07b">LiveEvent</span>&lt;<span style="color:#e06c75">T</span>&gt; : <span style="color:#e06c75">MediatorLiveData</span>&lt;<span style="color:#e06c75">T</span>&gt;() { </span></span><span style="display:flex;"><span> <span style="color:#56b6c2">..</span>. </span></span><span style="display:flex;"><span> <span style="color:#c678dd">private</span> <span style="color:#c678dd">var</span> <span style="color:#e06c75">mainHandler</span>: <span style="color:#e06c75">Handler</span>? = <span style="color:#c678dd">null</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">postValue</span>(<span style="color:#c678dd">value</span>: <span style="color:#e06c75">T</span>) { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">if</span> (<span style="color:#e06c75">mainHandler</span> <span style="color:#56b6c2">==</span> <span style="color:#c678dd">null</span>) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">mainHandler</span> = <span style="color:#e06c75">Handler</span>(<span style="color:#e5c07b">Looper</span>.<span style="color:#e06c75">getMainLooper</span>()) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#e06c75">mainHandler</span><span style="color:#56b6c2">!!</span>.<span style="color:#e06c75">post</span>(<span style="color:#e06c75">Runnable</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">setValue</span>(<span style="color:#c678dd">value</span>) </span></span><span style="display:flex;"><span> }) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#56b6c2">..</span>. </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>那么,这一段代码为什么会生效呢?这个就要涉及到LiveData的源码了,可以看<a class="link" href="https://blog.csdn.net/catzifeng/article/details/103931517" target="_blank" rel="noopener" >这篇文章</a>,里面对这两个方法的源码进行的分析和解释。</p> <p>简单来说,<code>postValue</code> 其实就是使用了一个线程锁(这也是 <code>postValue</code> 高并发时丢失数据的原因),使用Handler切换到主进程后调用<code>setValue</code> 来设置数据。</p> <p>因此,我们只需要重写 <code>postValue</code>,收到新数据一律直接抛到主线程 <code>setValue</code> 即可。</p> <p>现在,我们已经有了一个简单好用、不会丢数据、非粘性、可感知生命周期、可观察的事件对象了。</p> <p>最后,贴上总代码:</p> <p><code>LiveEvent.kt</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#c678dd">import</span> <span style="color:#e06c75">android.os.Handler</span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">import</span> <span style="color:#e06c75">android.os.Looper</span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">import</span> <span style="color:#e06c75">androidx.annotation.MainThread</span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">import</span> <span style="color:#e06c75">androidx.collection.ArraySet</span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">import</span> <span style="color:#e06c75">androidx.lifecycle.LifecycleOwner</span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">import</span> <span style="color:#e06c75">androidx.lifecycle.MediatorLiveData</span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">import</span> <span style="color:#e06c75">androidx.lifecycle.Observer</span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">import</span> <span style="color:#e06c75">kotlinx.coroutines.Runnable</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">open</span> <span style="color:#c678dd">class</span> <span style="color:#e5c07b">LiveEvent</span>&lt;<span style="color:#e06c75">T</span>&gt; : <span style="color:#e06c75">MediatorLiveData</span>&lt;<span style="color:#e06c75">T</span>&gt;() { </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">private</span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">observers</span> = <span style="color:#e06c75">ArraySet</span>&lt;<span style="color:#e06c75">ObserverWrapper</span>&lt;<span style="color:#c678dd">in</span> <span style="color:#e06c75">T</span>&gt;&gt;() </span></span><span style="display:flex;"><span> <span style="color:#c678dd">private</span> <span style="color:#c678dd">var</span> <span style="color:#e06c75">mainHandler</span>: <span style="color:#e06c75">Handler</span>? = <span style="color:#c678dd">null</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#61afef">@MainThread</span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">observe</span>(<span style="color:#e06c75">owner</span>: <span style="color:#e06c75">LifecycleOwner</span>, <span style="color:#e06c75">observer</span>: <span style="color:#e06c75">Observer</span>&lt;<span style="color:#c678dd">in</span> <span style="color:#e06c75">T</span>&gt;) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">find</span> { <span style="color:#c678dd">it</span>.<span style="color:#e06c75">observer</span> <span style="color:#56b6c2">===</span> <span style="color:#e06c75">observer</span> }<span style="color:#56b6c2">?.</span><span style="color:#e06c75">let</span> { <span style="color:#e06c75">_</span> <span style="color:#56b6c2">-&gt;</span> <span style="color:#7f848e">// existing </span></span></span><span style="display:flex;"><span><span style="color:#7f848e"></span> <span style="color:#c678dd">return</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">wrapper</span> = <span style="color:#e06c75">ObserverWrapper</span>(<span style="color:#e06c75">observer</span>) </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">add</span>(<span style="color:#e06c75">wrapper</span>) </span></span><span style="display:flex;"><span> <span style="color:#c678dd">super</span>.<span style="color:#e06c75">observe</span>(<span style="color:#e06c75">owner</span>, <span style="color:#e06c75">wrapper</span>) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#61afef">@MainThread</span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">observeForever</span>(<span style="color:#e06c75">observer</span>: <span style="color:#e06c75">Observer</span>&lt;<span style="color:#c678dd">in</span> <span style="color:#e06c75">T</span>&gt;) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">find</span> { <span style="color:#c678dd">it</span>.<span style="color:#e06c75">observer</span> <span style="color:#56b6c2">===</span> <span style="color:#e06c75">observer</span> }<span style="color:#56b6c2">?.</span><span style="color:#e06c75">let</span> { <span style="color:#e06c75">_</span> <span style="color:#56b6c2">-&gt;</span> <span style="color:#7f848e">// existing </span></span></span><span style="display:flex;"><span><span style="color:#7f848e"></span> <span style="color:#c678dd">return</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">wrapper</span> = <span style="color:#e06c75">ObserverWrapper</span>(<span style="color:#e06c75">observer</span>) </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">add</span>(<span style="color:#e06c75">wrapper</span>) </span></span><span style="display:flex;"><span> <span style="color:#c678dd">super</span>.<span style="color:#e06c75">observeForever</span>(<span style="color:#e06c75">wrapper</span>) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#61afef">@MainThread</span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">removeObserver</span>(<span style="color:#e06c75">observer</span>: <span style="color:#e06c75">Observer</span>&lt;<span style="color:#c678dd">in</span> <span style="color:#e06c75">T</span>&gt;) { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">if</span> (<span style="color:#e06c75">observer</span> <span style="color:#c678dd">is</span> <span style="color:#e06c75">ObserverWrapper</span> <span style="color:#56b6c2">&amp;&amp;</span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">remove</span>(<span style="color:#e06c75">observer</span>)) { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">super</span>.<span style="color:#e06c75">removeObserver</span>(<span style="color:#e06c75">observer</span>) </span></span><span style="display:flex;"><span> <span style="color:#c678dd">return</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">iterator</span> = <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">iterator</span>() </span></span><span style="display:flex;"><span> <span style="color:#c678dd">while</span> (<span style="color:#e06c75">iterator</span>.<span style="color:#e06c75">hasNext</span>()) { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">wrapper</span> = <span style="color:#e06c75">iterator</span>.<span style="color:#e06c75">next</span>() </span></span><span style="display:flex;"><span> <span style="color:#c678dd">if</span> (<span style="color:#e06c75">wrapper</span>.<span style="color:#e06c75">observer</span> <span style="color:#56b6c2">==</span> <span style="color:#e06c75">observer</span>) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">iterator</span>.<span style="color:#e06c75">remove</span>() </span></span><span style="display:flex;"><span> <span style="color:#c678dd">super</span>.<span style="color:#e06c75">removeObserver</span>(<span style="color:#e06c75">wrapper</span>) </span></span><span style="display:flex;"><span> <span style="color:#c678dd">break</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#61afef">@MainThread</span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">setValue</span>(<span style="color:#c678dd">value</span>: <span style="color:#e06c75">T</span>) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observers</span>.<span style="color:#e06c75">forEach</span> { <span style="color:#c678dd">it</span>.<span style="color:#e06c75">newValue</span>() } </span></span><span style="display:flex;"><span> <span style="color:#c678dd">super</span>.<span style="color:#e06c75">setValue</span>(<span style="color:#c678dd">value</span>) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">postValue</span>(<span style="color:#c678dd">value</span>: <span style="color:#e06c75">T</span>) { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">if</span> (<span style="color:#e06c75">mainHandler</span> <span style="color:#56b6c2">==</span> <span style="color:#c678dd">null</span>) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">mainHandler</span> = <span style="color:#e06c75">Handler</span>(<span style="color:#e5c07b">Looper</span>.<span style="color:#e06c75">getMainLooper</span>()) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#e06c75">mainHandler</span><span style="color:#56b6c2">!!</span>.<span style="color:#e06c75">post</span>(<span style="color:#e06c75">Runnable</span> { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">setValue</span>(<span style="color:#c678dd">value</span>) </span></span><span style="display:flex;"><span> }) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">private</span> <span style="color:#c678dd">class</span> <span style="color:#e5c07b">ObserverWrapper</span>&lt;<span style="color:#e06c75">T</span>&gt;(<span style="color:#c678dd">val</span> <span style="color:#e06c75">observer</span>: <span style="color:#e06c75">Observer</span>&lt;<span style="color:#e06c75">T</span>&gt;) : <span style="color:#e06c75">Observer</span>&lt;<span style="color:#e06c75">T</span>&gt; { </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">private</span> <span style="color:#c678dd">var</span> <span style="color:#e06c75">pending</span> = <span style="color:#c678dd">false</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">override</span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">onChanged</span>(<span style="color:#e06c75">t</span>: <span style="color:#e06c75">T</span>?) { </span></span><span style="display:flex;"><span> <span style="color:#c678dd">if</span> (<span style="color:#e06c75">pending</span>) { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">pending</span> = <span style="color:#c678dd">false</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">observer</span>.<span style="color:#e06c75">onChanged</span>(<span style="color:#e06c75">t</span>) </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">newValue</span>() { </span></span><span style="display:flex;"><span> <span style="color:#e06c75">pending</span> = <span style="color:#c678dd">true</span> </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>当然,建议还是至少要了解一下被谷歌十分看重并当做未来主流来提供全方位支持的Flow,相比LiveData更贴合Kotlin的协程,非常强大。</p> <h2 id="关于读写权限">关于读写权限</h2> <p>在MVVC架构中,LiveData应该只能由ViewModel持有和写入,而View层则应该只读,所以我们经常会有以下写法:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#c678dd">private</span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">_name</span> = <span style="color:#e06c75">MutableLiveData</span>&lt;<span style="color:#e06c75">String</span>&gt;() </span></span><span style="display:flex;"><span><span style="color:#c678dd">val</span> <span style="color:#e06c75">name</span>: <span style="color:#e06c75">LiveData</span>&lt;<span style="color:#e06c75">String</span>&gt; = <span style="color:#e06c75">_name</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">somefunction</span>(){ </span></span><span style="display:flex;"><span> <span style="color:#56b6c2">..</span>. </span></span><span style="display:flex;"><span> <span style="color:#e06c75">_name</span>.<span style="color:#e06c75">postValue</span>(<span style="color:#98c379">&#34;失迹&#34;</span>) </span></span><span style="display:flex;"><span> <span style="color:#56b6c2">..</span>. </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>这个想法和写法并没有问题。将具有读写权限的MutableLiveData设为私有成员,只对View暴露只读的LiveData。</p> <p>此写法更为规范,但仅仅是为了防止你在View和Observe中误修改,就需要增加代码的数量和降低阅读性,我个人是极不情愿的。</p> <p>就如同约束性的问题一样,我们真的有必要为了防止可以避免的失误而增加多余且无实际作用的内容吗?</p> <p>或许我们需要照顾不懂操作的用户,但我们有必要考虑其他不懂规范的程序员到这种程度吗?</p> <p>话题扯远了。因为LiveEvent本质上就是个没有粘性、不会丢数据的LiveData,因此自然是支持以上写法的:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#c678dd">private</span> <span style="color:#c678dd">val</span> <span style="color:#e06c75">_showToast</span> = <span style="color:#e06c75">LiveEvent</span>&lt;<span style="color:#e06c75">String</span>&gt;() </span></span><span style="display:flex;"><span><span style="color:#c678dd">val</span> <span style="color:#e06c75">showToast</span>: <span style="color:#e06c75">LiveData</span>&lt;<span style="color:#e06c75">String</span>&gt; = <span style="color:#e06c75">_showToast</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#c678dd">fun</span> <span style="color:#61afef;font-weight:bold">somefunction</span>(){ </span></span><span style="display:flex;"><span> <span style="color:#56b6c2">..</span>. </span></span><span style="display:flex;"><span> <span style="color:#e06c75">_showToast</span>.<span style="color:#e06c75">postValue</span>(<span style="color:#98c379">&#34;如你所见,这是一条吐司&#34;</span>) </span></span><span style="display:flex;"><span> <span style="color:#56b6c2">..</span>. </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>这样写的话最后在类型上又会转回LiveData,不过没有影响,当做事件用即可。</p> <p>对于跳转页面、弹出Snacebar之类的事件我们无可奈何,但除此之外,我们应当避免其他的事件。<strong>将数据处理为UI状态,才是MVVM的正确实践!</strong></p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li> <p><a class="link" href="https://blog.csdn.net/vitaviva/article/details/122163479" target="_blank" rel="noopener" >Jetpack MVVM 七宗罪之四: 使用 LiveData/StateFlow 发送 Events</a></p> </li> <li> <p>Jose Alcérreca , <a class="link" href="https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150" target="_blank" rel="noopener" >LiveData with SnackBar, Navigation and other events</a></p> </li> <li> <p><a class="link" href="https://blog.csdn.net/catzifeng/article/details/103931517" target="_blank" rel="noopener" >LiveData setValue和postValue的区别及详解</a></p> </li> <li> <p><a class="link" href="https://www.jianshu.com/p/b2646bf112ce" target="_blank" rel="noopener" >把 LiveData 用于事件传递那些坑</a></p> </li> <li> <p><a class="link" href="https://juejin.cn/post/6844903949267386376#heading-1" target="_blank" rel="noopener" >基于LiveData实现事件总线思路和方案</a></p> </li> <li> <p><a class="link" href="https://juejin.cn/post/6955724479506939935#heading-7" target="_blank" rel="noopener" >使用 Architecture Component 实现 MVVM 的正确姿势</a></p> </li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于<a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循CC BY-NC-SA 4.0协议,复制请保留原文出处。</p>

2023/1/27
阅读更多

踩坑记录 | Android中LayoutInflater读取attr颜色属性报错:Failed to resolve attribute at index 77: TypedValue{t=0x2/d=0x7f030107 a=-1}

<h2 id="踩坑">踩坑</h2> <h3 id="背景">背景</h3> <p>项目要适配夜间模式,因此要添加深色主题(Dark Theme),也就是Android开发中很常见的开发要求——<strong>多主题</strong>。</p> <p>在查找资料的时候发现多主题似乎在select和drawable上存在很大的坑,不过这里估计是因为使用了Androidx、AppCompat、Material3,所以并没有跳进去他们的坑,反倒是自己找了一处偏僻的小坑钻进去了。</p> <p>发生这个问题的时候,我正在尝试使用Databinding获取自定义的Toast布局:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-kotlin" data-lang="kotlin"><span style="display:flex;"><span><span style="color:#c678dd">val</span> <span style="color:#e06c75">binding</span> = <span style="color:#e5c07b">ToastBinding</span>.<span style="color:#e06c75">inflate</span>(<span style="color:#e5c07b">LayoutInflater</span>.<span style="color:#e06c75">from</span>(<span style="color:#e06c75">context</span>)) </span></span></code></pre></div><p>在 <code>attrs.xml</code> 添加自定义属性、在 <code>theme</code> 中赋值、在布局和 <code>Drawable</code> 中引用。</p> <p><code>values/attrs.xml</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#e06c75">&lt;resources&gt;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&lt;attr</span> <span style="color:#e06c75">name=</span><span style="color:#98c379">&#34;toastColor&#34;</span> <span style="color:#e06c75">format=</span><span style="color:#98c379">&#34;reference|color&#34;</span> <span style="color:#e06c75">/&gt;</span> </span></span><span style="display:flex;"><span> ... </span></span><span style="display:flex;"><span><span style="color:#e06c75">&lt;/resources&gt;</span> </span></span></code></pre></div><p><code>values/themes.xml</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#e06c75">&lt;resources&gt;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&lt;style</span> <span style="color:#e06c75">name=</span><span style="color:#98c379">&#34;AppTheme&#34;</span> <span style="color:#e06c75">parent=</span><span style="color:#98c379">&#34;Theme.Material3.Light.NoActionBar&#34;</span><span style="color:#e06c75">&gt;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&lt;item</span> <span style="color:#e06c75">name=</span><span style="color:#98c379">&#34;toastColor&#34;</span><span style="color:#e06c75">&gt;</span>@color/toast_light<span style="color:#e06c75">&lt;/item&gt;</span> </span></span><span style="display:flex;"><span> ... </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&lt;/style&gt;</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&lt;style</span> <span style="color:#e06c75">name=</span><span style="color:#98c379">&#34;Theme.MyApp&#34;</span> <span style="color:#e06c75">parent=</span><span style="color:#98c379">&#34;AppTheme&#34;</span> <span style="color:#e06c75">/&gt;</span> </span></span><span style="display:flex;"><span><span style="color:#e06c75">&lt;/resources&gt;</span> </span></span></code></pre></div><p><code>drawable/check_circle.xml</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#e06c75">&lt;vector</span> <span style="color:#e06c75">android:autoMirrored=</span><span style="color:#98c379">&#34;false&#34;</span> <span style="color:#e06c75">android:height=</span><span style="color:#98c379">&#34;24dp&#34;</span> <span style="color:#e06c75">android:viewportHeight=</span><span style="color:#98c379">&#34;24&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">android:viewportWidth=</span><span style="color:#98c379">&#34;24&#34;</span> <span style="color:#e06c75">android:width=</span><span style="color:#98c379">&#34;24dp&#34;</span> <span style="color:#e06c75">xmlns:android=</span><span style="color:#98c379">&#34;http://schemas.android.com/apk/res/android&#34;</span><span style="color:#e06c75">&gt;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&lt;path</span> <span style="color:#e06c75">android:fillColor=</span><span style="color:#98c379">&#34;?attr/toastIconColor&#34;</span> <span style="color:#e06c75">android:pathData=</span><span style="color:#98c379">&#34;M10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z&#34;</span><span style="color:#e06c75">/&gt;</span> </span></span><span style="display:flex;"><span> <span style="color:#7f848e">&lt;!-- 这是一个svg格式的矢量图,重要的是↑这个fillColor里引用了attr,后面的pathData是图片绘制路径 --&gt;</span> </span></span><span style="display:flex;"><span><span style="color:#e06c75">&lt;/vector&gt;</span> </span></span></code></pre></div><p><code>layout/my_toast.xml</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#7f848e">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span> </span></span><span style="display:flex;"><span><span style="color:#e06c75">&lt;layout</span> <span style="color:#e06c75">xmlns:android=</span><span style="color:#98c379">&#34;http://schemas.android.com/apk/res/android&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">xmlns:app=</span><span style="color:#98c379">&#34;http://schemas.android.com/apk/res-auto&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">xmlns:tools=</span><span style="color:#98c379">&#34;http://schemas.android.com/tools&#34;</span><span style="color:#e06c75">&gt;</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&lt;androidx.constraintlayout.widget.ConstraintLayout</span> </span></span><span style="display:flex;"><span> ... </span></span><span style="display:flex;"><span> <span style="color:#e06c75">android:background=</span><span style="color:#98c379">&#34;?attr/toastColor&#34;</span> <span style="color:#e06c75">&gt;</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&lt;ImageView</span> </span></span><span style="display:flex;"><span> ... </span></span><span style="display:flex;"><span> <span style="color:#e06c75">android:src=</span><span style="color:#98c379">&#34;@drawable/check_circle&#34;</span> <span style="color:#e06c75">/&gt;</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&lt;TextView</span> </span></span><span style="display:flex;"><span> ... </span></span><span style="display:flex;"><span> <span style="color:#e06c75">android:textColor=</span><span style="color:#98c379">&#34;?attr/toastTextColor&#34;</span> <span style="color:#e06c75">/&gt;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</span> </span></span><span style="display:flex;"><span><span style="color:#e06c75">&lt;/layout&gt;</span> </span></span></code></pre></div><p>编译时一切顺利,然后一运行,问题就嘎嘎找上门了……</p> <h3 id="问题描述">问题描述</h3> <p>正如题目所说,运行时报了以下错误:</p> <pre tabindex="0"><code>Failed to resolve attribute at index 77: TypedValue{t=0x2/d=0x7f030107 a=-1} </code></pre><p>一开始我以为这里的 <code>attribute</code> 是属性的意思而不是特指 <code>?attr/</code> ,看了半天没看懂,于是上网一顿猛搜,发现跟Theme的自定义属性有关。</p> <p>然而查来查去,都没有解决办法,勉强知道问题是出在<strong>控件没有正确地获取到 <code>?attr/</code> 的值</strong>。</p> <p>然而我适配多主题,靠的就是 <code>attr</code> 呀,那不完蛋了?</p> <h3 id="解决方法">解决方法</h3> <p>在查找资料的过程中,我发现其他发生相似问题的,大多是 <code>Application</code> 没有设置好Theme、<code>Activity</code> 没有设置好Theme、<code>Fragment</code> 没有设置好Theme。一句话总结相同点,就是<strong>用于 <code>LayoutInflater.from(context)</code> 的 <code>context</code> 没有正确地配置Theme</strong>。</p> <p>在尝试过各种办法都无效过后,走投无路的我大胆的猜测:难道是因为这个 <strong><code>View</code> 没有配置好Theme</strong>吗?</p> <p>于是我在 <code>layout/my_toast.xml</code> 的根布局的属性中加上了一行:</p> <p><code>android:theme=&quot;@style/Theme.MyApp&quot;</code></p> <p>也就是变成下面的这个样子:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>... </span></span><span style="display:flex;"><span><span style="color:#e06c75">&lt;androidx.constraintlayout.widget.ConstraintLayout</span> </span></span><span style="display:flex;"><span> ... </span></span><span style="display:flex;"><span> <span style="color:#e06c75">android:theme=</span><span style="color:#98c379">&#34;@style/Theme.MyApp&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">android:background=</span><span style="color:#98c379">&#34;?attr/toastColor&#34;</span> <span style="color:#e06c75">&gt;</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> ... </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</span> </span></span><span style="display:flex;"><span><span style="color:#e06c75">&lt;/layout&gt;</span> </span></span></code></pre></div><p>然后问题就神奇的解决了。</p> <p>既没有在 <code>inflate</code> 的时候报错,<code>Drawable</code> 也正常显示出来了。</p> <h3 id="分析原因">分析原因</h3> <p>似乎由于传入 <code>LayoutInflater.from(context)</code> 的 <code>context</code> 是一个我初始化时传入的 <code>Application</code> 对象,因此布局的View并没有自动获取到 <code>AndroidManifest.xml</code> 中的Theme配置。</p> <p>而我们知道,<strong><code>attr</code> 是与当前的 <code>theme</code> 完全关联的。</strong></p> <p>你当前是什么Theme,就获取到对应的 <code>attr</code> 。而如果当前的Theme使用的是默认的Theme或其他Theme,而这个Theme中并没有为对应的 <code>attr</code> 赋值,自然而然就要报错或者出现显示问题了。</p> <h3 id="总结">总结</h3> <p><strong><code>attr</code> 是与当前的 <code>theme</code> 完全关联的</strong>。出现此问题,大概率是因为<strong>用于 <code>LayoutInflater.from(context)</code> 的 <code>context</code> 没有正确地配置Theme</strong>。</p> <p>在对应的xml文件的根布局加入:</p> <p><code>android:theme=&quot;@style/配置了对应attr的Theme&quot;</code></p> <p>应该能解决大部分同类情况。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <p>说实话,看了很多博客之后才使我受到启发找到解决方法,但是还是感谢他们。虽然他们现在没有帮助我解决问题,但也使我在未来不会遇到同样问题或是遇到了能够解决。</p> <p>看过的实在太多,但都没有实质性帮助,这里就不列参考文献了。</p> <h3 id="版权信息">版权信息</h3> <p>本文原载于<a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循CC BY-NC-SA 4.0协议,复制请保留原文出处。</p>

2023/1/22
阅读更多

随笔杂谈 | 2023 新春快乐!

<p>今天是这个小博客成立的<strong>第七天</strong>,也是这个小博客迎来的第一个春节!</p> <p>祝大家新的一年里万事如意,目标全达成,2023新春快乐!</p>

2023/1/21
阅读更多

踩坑记录 | Gradle7.0+仓库配置踩坑:Build was configured to prefer settings repositories over project repositories but repository 'maven' was added by build file 'build.gradle'

<h2 id="踩坑">踩坑</h2> <h3 id="问题描述">问题描述</h3> <p>新版Gradle从7.0版本开始,新增了 <code>dependencyResolutionManagement</code> 作为推荐的配置仓库方法。</p> <p>Android Studio也不知道从哪个版本开始,创建新Project时开始使用这种方法作为默认配置仓库的方法,然而这个代码块是放在 <code>settings.gradle</code> 里面的。</p> <p>这就导致了我想要配置镜像时翻遍了两个 <code>build.gradle</code> 找不到配置仓库的地方,自己加又一直报错:</p> <pre tabindex="0"><code>Build was configured to prefer settings repositories over project repositories but repository &#39;maven&#39; was added by build file &#39;build.gradle&#39; </code></pre><p>意思是已经加入过这个仓库了,然而提示的重复位置却不是默认的配置而是我们自己加入配置的位置,完全不知道重复在哪了,令人摸不着头脑。</p> <h3 id="解决方法">解决方法</h3> <p>删除掉其他地方的 <code>repositories</code>,在 <code>settings.gradle</code> 下配置仓库。</p> <p>顺便附上我自己修改后的镜像代码:</p> <p><code>settings.gradle</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-gradle" data-lang="gradle"><span style="display:flex;"><span><span style="color:#e06c75">pluginManagement</span> <span style="color:#56b6c2">{</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">repositories</span> <span style="color:#56b6c2">{</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">gradlePluginPortal</span><span style="color:#56b6c2">()</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">maven</span> <span style="color:#56b6c2">{</span> <span style="color:#e06c75">url</span> <span style="color:#98c379">&#34;https://maven.aliyun.com/repository/google&#34;</span> <span style="color:#56b6c2">}</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">maven</span> <span style="color:#56b6c2">{</span> <span style="color:#e06c75">url</span> <span style="color:#98c379">&#34;https://maven.aliyun.com/repository/public&#34;</span> <span style="color:#56b6c2">}</span> </span></span><span style="display:flex;"><span> <span style="color:#56b6c2">}</span> </span></span><span style="display:flex;"><span><span style="color:#56b6c2">}</span> </span></span><span style="display:flex;"><span><span style="color:#e06c75">dependencyResolutionManagement</span> <span style="color:#56b6c2">{</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">repositoriesMode</span><span style="color:#56b6c2">.</span><span style="color:#e06c75">set</span><span style="color:#56b6c2">(</span><span style="color:#e06c75">RepositoriesMode</span><span style="color:#56b6c2">.</span><span style="color:#e06c75">FAIL_ON_PROJECT_REPOS</span><span style="color:#56b6c2">)</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">repositories</span> <span style="color:#56b6c2">{</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">maven</span> <span style="color:#56b6c2">{</span> <span style="color:#e06c75">url</span> <span style="color:#98c379">&#34;https://maven.aliyun.com/repository/google&#34;</span> <span style="color:#56b6c2">}</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">maven</span> <span style="color:#56b6c2">{</span> <span style="color:#e06c75">url</span> <span style="color:#98c379">&#34;https://maven.aliyun.com/repository/public&#34;</span> <span style="color:#56b6c2">}</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">maven</span> <span style="color:#56b6c2">{</span> <span style="color:#e06c75">url</span> <span style="color:#98c379">&#39;https://www.jitpack.io&#39;</span> <span style="color:#56b6c2">}</span> </span></span><span style="display:flex;"><span> <span style="color:#56b6c2">}</span> </span></span><span style="display:flex;"><span><span style="color:#56b6c2">}</span> </span></span><span style="display:flex;"><span><span style="color:#e06c75">rootProject</span><span style="color:#56b6c2">.</span><span style="color:#e06c75">name</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;Sample&#34;</span> </span></span><span style="display:flex;"><span><span style="color:#e06c75">include</span> <span style="color:#98c379">&#39;:app&#39;</span> </span></span></code></pre></div><h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <h3 id="版权信息">版权信息</h3> <p>本文原载于<a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循CC BY-NC-SA 4.0协议,复制请保留原文出处。</p>

2023/1/17
阅读更多

建站技术 | 为自己的网站使用Google Analytics统计

<img src="https://blog.reincarnatey.net/2023/how-to-ues-google-analytics/cover.jpg" alt="Featured image of post 建站技术 | 为自己的网站使用Google Analytics统计" /><h2 id="须知">须知</h2> <p>你应当知晓:在中国大陆境内的服务器使用Google Analytics是<strong>不合法行为</strong>。</p> <blockquote> <p><a class="link" href="http://www.npc.gov.cn/npc/c30834/202108/a8c4e3672c74491a80b53a172bb753fe.shtml" target="_blank" rel="noopener" >《中华人民共和国个人信息保护法》</a></p> <p>第三十六条:国家机关处理的个人信息应当在中华人民共和国境内存储;确需向境外提供的,应当进行安全评估。安全评估可以要求有关部门提供支持与协助。</p> <p>第三十九条:个人信息处理者向中华人民共和国境外提供个人信息的,应当向个人告知境外接收方的名称或者姓名、联系方式、处理目的、处理方式、个人信息的种类以及个人向境外接收方行使本法规定权利的方式和程序等事项,并取得个人的单独同意。</p> </blockquote> <p>本教程仅适用于满足使用条件的网站,如果你在不满足使用条件的情况下参考本教程使用Google Analytics,本教程<strong>不承担任何责任</strong>。</p> <h2 id="是什么为什么怎么做">是什么?为什么?怎么做?</h2> <p>关于Google Analytics的详细介绍可以参考这位博主的文章:<a class="link" href="https://zhuanlan.zhihu.com/p/136378374" target="_blank" rel="noopener" >谷歌流量分析工具Google Analytics使用方法指南教程</a></p> <p>本教程<strong>只记录步骤</strong>而省略一部分解释。</p> <h2 id="如何使用google-analytics">如何使用Google Analytics</h2> <ol> <li> <p>注册一个Google账号(需要科学上网)</p> </li> <li> <p>打开 <a class="link" href="https://analytics.google.com/analytics/web/provision/#/provision" target="_blank" rel="noopener" >Google Analytics官网</a>(需要科学上网),点击<strong>开始衡量</strong></p> </li> <li> <p>开设账号</p> <ul> <li> <p>填自己的账号名<figure > <a href="https://s2.loli.net/2023/01/14/5enUyk8oWSREXYL.png" > <img src="https://s2.loli.net/2023/01/14/5enUyk8oWSREXYL.png" loading="lazy" alt="填写账号名"> </a> <figcaption>填写账号名</figcaption> </figure></p> </li> <li> <p>名称取自己好认的即可,时区和人民币按默认的。<figure > <a href="https://s2.loli.net/2023/01/14/4Fg6Jqpkxa98BRO.png" > <img src="https://s2.loli.net/2023/01/14/4Fg6Jqpkxa98BRO.png" loading="lazy" alt="取名"> </a> <figcaption>取名</figcaption> </figure></p> </li> <li> <p>如实填写即可<figure > <a href="https://s2.loli.net/2023/01/14/BXMFp5wRSYfrvOW.png" > <img src="https://s2.loli.net/2023/01/14/BXMFp5wRSYfrvOW.png" loading="lazy" alt="行业与企业的填写"> </a> <figcaption>行业与企业的填写</figcaption> </figure></p> </li> <li> <p>完成后点击<strong>创建</strong>,会弹出一个弹窗问你是否同意协议,勾选同意后选择<strong>我接受</strong>,完成账号创建<figure > <a href="https://s2.loli.net/2023/01/14/oljK1p4LnwtqSMh.png" > <img src="https://s2.loli.net/2023/01/14/oljK1p4LnwtqSMh.png" loading="lazy" alt="同意协议"> </a> <figcaption>同意协议</figcaption> </figure></p> </li> </ul> </li> <li> <p>完成后会自动跳转到刚刚新建的媒体源,选择平台为<strong>网站</strong><figure > <a href="https://s2.loli.net/2023/01/14/VNjoJrDKHsGi8t2.png" > <img src="https://s2.loli.net/2023/01/14/VNjoJrDKHsGi8t2.png" loading="lazy" alt="选择平台"> </a> <figcaption>选择平台</figcaption> </figure></p> </li> <li> <p>设置网站和数据流的名称,然后点击<strong>创建数据流</strong>,注意http和https不要选错,网站网址也不要写错<figure > <a href="https://s2.loli.net/2023/01/14/3sKVWMhne9C1vY5.png" > <img src="https://s2.loli.net/2023/01/14/3sKVWMhne9C1vY5.png" loading="lazy" alt="设置名称"> </a> <figcaption>设置名称</figcaption> </figure></p> </li> <li> <p>接下来设置统计代码</p> <ul> <li> <p>在数据流详情页面点击查看代码说明<figure > <a href="https://s2.loli.net/2023/01/14/jyeSAmBL3KrECh4.png" > <img src="https://s2.loli.net/2023/01/14/jyeSAmBL3KrECh4.png" loading="lazy" alt="查看代码说明"> </a> <figcaption>查看代码说明</figcaption> </figure></p> </li> <li> <p>如果是使用CMS来构建网站的话使用第一项可以快捷设置,这里我是静态网页,因此点击<strong>手动添加</strong>,然后点击代码块右上角<strong>复制代码</strong><figure > <a href="https://s2.loli.net/2023/01/14/jtIKCmMVN1EDROg.png" > <img src="https://s2.loli.net/2023/01/14/jtIKCmMVN1EDROg.png" loading="lazy" alt="复制代码"> </a> <figcaption>复制代码</figcaption> </figure></p> </li> <li> <p>将代码复制到你的网站源代码的 <code>body</code> 标签中<figure > <a href="https://s2.loli.net/2023/01/14/uft6Wx7EDInKAN4.png" > <img src="https://s2.loli.net/2023/01/14/uft6Wx7EDInKAN4.png" loading="lazy" alt="添加代码"> </a> <figcaption>添加代码</figcaption> </figure></p> </li> <li> <p>重新部署至服务器,统计就会生效了</p> </li> </ul> </li> <li> <p>查看你的网站数据,具体面板详细介绍可以看<a class="link" href="https://zhuanlan.zhihu.com/p/136378374" target="_blank" rel="noopener" >谷歌流量分析工具Google Analytics使用方法指南教程</a>,上面讲的很详细</p> </li> </ol> <p>至此,你已成功为你的网站使用了Google Analytics。</p> <h2 id="注意">注意</h2> <p>根据官网:数据可能最多需要 24 小时才会显示在您的 Google Analytics(分析)帐号中。</p> <p>我这边将近十分钟就有数据,如果你等太久也没有看到统计,建议F12检查一下统计代码是否生效。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li> <p><a class="link" href="http://www.npc.gov.cn/npc/c30834/202108/a8c4e3672c74491a80b53a172bb753fe.shtm" target="_blank" rel="noopener" >中华人民共和国个人信息保护法</a></p> </li> <li> <p><a class="link" href="https://zhuanlan.zhihu.com/p/136378374" target="_blank" rel="noopener" >谷歌流量分析工具Google Analytics使用方法指南教程</a></p> </li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于<a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循CC BY-NC-SA 4.0协议,复制请保留原文出处。</p>

2023/1/16
阅读更多

建站技术 | 使用 Hugo + Stack 简单搭建一个博客

<img src="https://blog.reincarnatey.net/2023/build-hugo-blog-with-stack-mod/cover.png" alt="Featured image of post 建站技术 | 使用 Hugo + Stack 简单搭建一个博客" /><h2 id="前言">前言</h2> <p>相信很多人都跟我一样,搭建博客后的第一篇博文便是教如何搭建博客。就像是在第一次坐飞机打电话教你如何购买飞机票,甚至路上还一边走一边记录了去往飞机场的地图。</p> <p>明明自己也是刚刚才搭建好甚至第一次搭建,或许对于博客而言,第一篇博文记录这个博客的诞生过程是相当有意义的事吧。</p> <p><strong>2024/07/07 更新</strong>:本博客受到许多人的参考,当初帮助其他人的目的已经实现,由衷感到高兴。在本文完成后,又对本博客所使用的 stack 进行过一次大改,但未能及时记录下来,时隔久远已无法记得当初修改的内容以及对 golang 语言也变的陌生,所以很遗憾本文应该不再更新。已修复文章中的排版错误(例如空格与标点的使用等),若博客中存在错误,欢迎联系指正。</p> <h2 id="使用-hugo--stack-搭建博客">使用 Hugo + Stack 搭建博客</h2> <h3 id="什么是-hugo">什么是 Hugo?</h3> <blockquote> <p><a class="link" href="https://www.gohugo.org/" target="_blank" rel="noopener" >Hugo</a> 是由 Go 语言实现的静态网站生成器。简单、易用、高效、易扩展、快速部署。</p> </blockquote> <h3 id="在本地安装-hugo-拓展版">在本地安装 Hugo 拓展版</h3> <p>可以使用<a class="link" href="https://gohugo.io/installation/" target="_blank" rel="noopener" >官方文档</a>中的安装方式,需要按照<strong>拓展版</strong>,即名字后带 <code>-extended</code> 的版本。</p> <p>这里我是 Win10 系统,所以使用官方推荐的 <a class="link" href="https://chocolatey.org/" target="_blank" rel="noopener" >Chocolatey</a> 安装方式:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>choco install hugo-extended </span></span></code></pre></div><p>Chocolatey 是一个 windows 系统的软件包管理器,类似于 linux 的 apt-get(不过 choco 不是自带的,也远没有 apt-get 那么常用),算是 windows 码农的老朋友了。如果没有安装 choco 的可以使用其他安装方式或者自行搜索学习使用 choco。</p> <p>安装完成后,你可以输入以下命令检查 hugo 的版本:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>hugo version </span></span></code></pre></div><p><figure > <a href="https://s2.loli.net/2023/01/14/UoBzItuaRf6lZJH.png" > <img src="https://s2.loli.net/2023/01/14/UoBzItuaRf6lZJH.png" loading="lazy" alt="Hugo 拓展版的安装"> </a> <figcaption>Hugo 拓展版的安装</figcaption> </figure></p> <p>如果版本后面有 <code>+extended</code> 就说明安装对了(我的版本可能不一样,用最新的版本即可)。</p> <h3 id="用-hugo-创建博客">用 Hugo 创建博客</h3> <p>hugo 是一个静态网页生成器,简单来讲,hugo 负责的是把内容填到模板里,而具体生成什么网站则要看你是什么模板。</p> <p>因此,要用它来生成博客需要用到对应的博客模板,你可以在<a class="link" href="https://www.gohugo.org/theme/" target="_blank" rel="noopener" >皮肤列表</a>或者 github 上查找模板,这里除了博客以外还有许多个人主页、导航站、文档页面等的模板。</p> <p>这里我原本想使用的是 <a class="link" href="https://stack.jimmycai.com/" target="_blank" rel="noopener" >Stack</a> 模板,比较符合我的审美需求,然而功能略有不足,因此我这里使用的是大佬美化后的版本 <a class="link" href="https://github.com/Mantyke/Hugo-stack-theme-mod" target="_blank" rel="noopener" >Mantyke/Hugo-stack-theme-mod</a>。</p> <blockquote> <p><strong>2025.7.21 Update:</strong></p> <p>该库目前已被删除,我已将备份上传至网盘,点击<a class="link" href="https://pan.reincarnatey.net/BlogResource/Hugo-stack-theme-mod-master.zip" target="_blank" rel="noopener" >这里</a>可下载。我更加推荐直接使用原版 Stack 模板(目前已更新地更为完善,功能更丰富)。</p> </blockquote> <p>使用这个版本必须安装 hugo 的拓展版,否则会报错:</p> <pre tabindex="0"><code>Error: Error building site: TOCSS: failed to transform &#34;scss/style.scss&#34; (text/x-scss): this feature is not available in your current Hugo version </code></pre><p>这个 mod 版增加了一些实用的功能和设计,虽然我个人更喜欢原版的左侧栏样式,但除此之外这个 mod 版都比原版要好。</p> <p>由于 mod 版直接提供了修改好的版本,这里可以直接 clone 或者下载到本地解压即可作为网站启动。正常创建方法可以参考<a class="link" href="https://www.gohugo.org/" target="_blank" rel="noopener" >hugo官方中文指南</a>和其他的教程。</p> <p>按照 <a class="link" href="https://github.com/Mantyke/Hugo-stack-theme-mod#%E9%83%A8%E5%88%86%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E" target="_blank" rel="noopener" >README</a> 的使用说明修改配置和内容。</p> <blockquote> <p>注意 <code>footer.html</code> 中的那个 <code>2006</code> <strong>不要修改</strong>!这个 2006 是 golang 的基准时间,如果改了的话时间显示就会出问题。在 <code>config.yaml</code> 中配置好建站年份即可,当前年份是会自动获取的。</p> </blockquote> <p>修改好后,在项目根目录运行以下命令:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>hugo server </span></span></code></pre></div><p>如果没有报错的话,就可以看到 hugo 服务器在本地运行了。</p> <p><figure > <a href="https://s2.loli.net/2023/01/14/mFSQYrEj1b6LBxi.png" > <img src="https://s2.loli.net/2023/01/14/mFSQYrEj1b6LBxi.png" loading="lazy" alt="本地运行"> </a> <figcaption>本地运行</figcaption> </figure></p> <p>可以打开返回的网址(默认是 localhost:1313),就可以看到自己的 demo 站了。</p> <p>hugo 生成速度极快,修改完成后几乎可以同时更新,调试和测试很方便(这也是大多数人选择 Hugo 而不是 Hexo 的原因)。</p> <p><figure > <a href="https://s2.loli.net/2023/01/14/9LF26PlG5Uwnos1.png" > <img src="https://s2.loli.net/2023/01/14/9LF26PlG5Uwnos1.png" loading="lazy" alt="意外发现全屏模式下超级好看!"> </a> <figcaption>意外发现全屏模式下超级好看!</figcaption> </figure></p> <p>你可以在根目录创建一个批处理脚本,用来快速启动本地服务器并使用 chrome 打开网页。</p> <p><code>本地运行.bat</code></p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>@<span style="color:#c678dd">echo</span> off </span></span><span style="display:flex;"><span><span style="color:#c678dd">echo</span> 【本地运行】 </span></span><span style="display:flex;"><span><span style="color:#c678dd">start</span> chrome http://localhost:1313/ </span></span><span style="display:flex;"><span>hugo server </span></span><span style="display:flex;"><span><span style="color:#c678dd">pause</span> </span></span></code></pre></div><h3 id="修改模板">修改模板</h3> <p>我希望点击头像的时候跳转到我的个人主页。</p> <p><code>\layouts\partials\sidebar\left.html</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-html" data-lang="html"><span style="display:flex;"><span><span style="color:#7f848e">&lt;!-- 第 12 行修改为自己的网址 --&gt;</span> </span></span><span style="display:flex;"><span>&lt;<span style="color:#e06c75">a</span> <span style="color:#e06c75">href</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;https://reincarnatey.net/&#34;</span>&gt; </span></span></code></pre></div><p>居然没有配置 favicon?</p> <p><code>\config.yaml</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#7f848e"># 第 30 行修改为:</span> </span></span><span style="display:flex;"><span><span style="color:#e06c75">favicon</span>: \favicon.ico </span></span></code></pre></div><p>随后将你的 <code>favicon.ico</code> 放在 <code>\static\</code> 下,重启 Hugo 服务器即可看到图标已经应用了(貌似不重启就看不到)。</p> <p>友链页面还是略有问题的:</p> <ul> <li> <p>左侧导航栏图标错误</p> </li> <li> <p>使用小写开头,格式不统一</p> </li> <li> <p>友链页面不显示回复</p> </li> </ul> <p><code>\content\page\friends\index.md</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#7f848e"># comments: false</span> </span></span><span style="display:flex;"><span><span style="color:#7f848e"># 这个 comments 无论是 false 还是 true 都会导致没有回复功能,注释或删除这个属性即可</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#7f848e"># name: friends</span> </span></span><span style="display:flex;"><span><span style="color:#7f848e"># icon: home </span> </span></span><span style="display:flex;"><span><span style="color:#7f848e"># 修改为</span> </span></span><span style="display:flex;"><span><span style="color:#e06c75">name</span>: Friends </span></span><span style="display:flex;"><span><span style="color:#e06c75">icon</span>: link </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#7f848e"># 除此之外,你的友链页面内容也在此文件编写,建议提供友链格式和己方友链信息等。</span> </span></span></code></pre></div><p>除此之外,左侧导航栏的间距过大导致出现滑动条,不喜欢的可以调小一点:</p> <p><figure > <a href="https://s2.loli.net/2023/01/14/l2weVaWq7JI483O.png" > <img src="https://s2.loli.net/2023/01/14/l2weVaWq7JI483O.png" loading="lazy" alt="间距"> </a> <figcaption>间距</figcaption> </figure></p> <p><code>\assets\scss\partials\menu.scss</code> :</p> <p>第 157 行:间距①,即导航栏与上方社交媒体之间的间距</p> <p>第 166 行:小屏模式下的间距②,即导航项之间的间距</p> <p>第 169 行:大屏模式(即正常模式)下的间距②,建议设成与 166 行一样的间距</p> <p>我使用的分别是 <code>0px 5px 5px</code>,可以根据自己的审美和导航项数量来设置。</p> <h3 id="添加自己的社交媒体联系方式rss">添加自己的社交媒体/联系方式/RSS</h3> <p>stack-mod 默认只有 github 和 twitter,显然不太够。而且尽管没人会用 RSS,但是既然stack 有这个功能那又有什么理由不加上呢。因此,这里需要来添加自己的链接。</p> <p>首先需要下载自己需要的图标,这里推荐阿里的 <a class="link" href="https://www.iconfont.cn/" target="_blank" rel="noopener" >iconfont</a>。</p> <p>搜索图标时建议选择线性,尽量选择粗细一致的,保持图标风格相同,比较美观。</p> <p>选择自己需要的即可,不用太多,我这里选择了 bilibili 和邮箱,下载时选择 svg 格式,颜色设置为 <code>#2c3e50</code>,大小设置为 <code>24</code>,然后下载即可。(RSS 的图标自带有)</p> <p><figure > <a href="https://s2.loli.net/2023/01/15/f4IeEyFjmTc9rUA.png" > <img src="https://s2.loli.net/2023/01/15/f4IeEyFjmTc9rUA.png" loading="lazy" alt="选择需要的图标"> </a> <figcaption>选择需要的图标</figcaption> </figure></p> <p>将下载下来的图标放到 <code>\assets\icons\</code> 文件夹下,然后修改社交媒体的配置。</p> <p><code>\config.yaml</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span> <span style="color:#e06c75">social</span>: </span></span><span style="display:flex;"><span> - <span style="color:#e06c75">identifier</span>: github </span></span><span style="display:flex;"><span> <span style="color:#e06c75">name</span>: GitHub </span></span><span style="display:flex;"><span> <span style="color:#e06c75">url</span>: https://github.com/Mitr-yuzr </span></span><span style="display:flex;"><span> <span style="color:#e06c75">weight</span>: <span style="color:#d19a66">1</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">params</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">icon</span>: brand-github </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> - <span style="color:#e06c75">identifier</span>: bilibili </span></span><span style="display:flex;"><span> <span style="color:#e06c75">name</span>: Bilibili </span></span><span style="display:flex;"><span> <span style="color:#e06c75">url</span>: https://space.bilibili.com/310707183 </span></span><span style="display:flex;"><span> <span style="color:#e06c75">weight</span>: <span style="color:#d19a66">2</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">params</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">icon</span>: bilibili-line </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> - <span style="color:#e06c75">identifier</span>: twitter </span></span><span style="display:flex;"><span> <span style="color:#e06c75">name</span>: Twitter </span></span><span style="display:flex;"><span> <span style="color:#e06c75">url</span>: https://twitter.com/kkkkkorita </span></span><span style="display:flex;"><span> <span style="color:#e06c75">weight</span>: <span style="color:#d19a66">3</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">params</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">icon</span>: brand-twitter </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> - <span style="color:#e06c75">identifier</span>: email </span></span><span style="display:flex;"><span> <span style="color:#e06c75">name</span>: Email </span></span><span style="display:flex;"><span> <span style="color:#e06c75">url</span>: mailto:korita@reincarnatey.net </span></span><span style="display:flex;"><span> <span style="color:#e06c75">weight</span>: <span style="color:#d19a66">4</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">params</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">icon</span>: email </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> - <span style="color:#e06c75">identifier</span>: rss </span></span><span style="display:flex;"><span> <span style="color:#e06c75">name</span>: RSS </span></span><span style="display:flex;"><span> <span style="color:#e06c75">url</span>: /index.xml </span></span><span style="display:flex;"><span> <span style="color:#e06c75">weight</span>: <span style="color:#d19a66">5</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">params</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">icon</span>: rss </span></span></code></pre></div><p><code>weight</code>:权重,决定排序的顺序,越小越靠前。</p> <p><code>icon</code>:图标,即你放入 <code>\assets\icons\</code> 的文件的文件名(不带扩展名)</p> <p><figure > <a href="https://s2.loli.net/2023/01/15/3wWiqCDyZo2vbQL.png" > <img src="https://s2.loli.net/2023/01/15/3wWiqCDyZo2vbQL.png" loading="lazy" alt="正常显示的亮色模式图标"> </a> <figcaption>正常显示的亮色模式图标</figcaption> </figure></p> <p>不过这样还是会有问题:</p> <p><figure > <a href="https://s2.loli.net/2023/01/16/fEXRKkyD7dNoA2u.png" > <img src="https://s2.loli.net/2023/01/16/fEXRKkyD7dNoA2u.png" loading="lazy" alt="错误显示的暗色模式图标"> </a> <figcaption>错误显示的暗色模式图标</figcaption> </figure></p> <p>可以看到在暗色模式下并没有正常显示。这是因为我们下载时指定了颜色,而图标内部指定的颜色是要比样式表优先的,因此导致夜间模式时切换颜色的样式没有生效。</p> <p>我们右键编辑这两个图标,然后将所有的 <code>#2c3e50</code> 都替换成 <code>currentColor</code>,也就是使用样式设置的颜色。</p> <p>现在图标在暗色模式下就可以正常显示了。</p> <p><figure > <a href="https://s2.loli.net/2023/01/16/mp4Fvfj6JHtZ87s.png" > <img src="https://s2.loli.net/2023/01/16/mp4Fvfj6JHtZ87s.png" loading="lazy" alt="正常显示的暗色模式图标"> </a> <figcaption>正常显示的暗色模式图标</figcaption> </figure></p> <h3 id="添加代码高亮">添加代码高亮</h3> <p>默认是没有开启代码高亮的,这种东西自然是要开启的。</p> <p><code>config.yaml</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span> <span style="color:#e06c75">highlight</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">style</span>: onedark </span></span></code></pre></div><p>添加一个 <code>style</code> 属性,其他全部删掉,就可以显示代码高亮了。你可以在 <a class="link" href="https://swapoff.org/chroma/playground/" target="_blank" rel="noopener" >Chroma Playground</a> 找你自己喜欢的高亮主题,但真没几个好看的,挑了个 <code>onedark</code> 勉强凑合着用吧。</p> <h3 id="博客评论系统的使用">博客评论系统的使用</h3> <p>博客中的评论系统是很重要的。这里我使用的是 Stack 模板支持的 <strong>Waline</strong>,搭建很简单,可以参考 <a class="link" href="https://waline.js.org/guide/get-started/" target="_blank" rel="noopener" >Waline官方指南</a>。</p> <p>配置 Waline 则可以参考这篇博客 <a class="link" href="https://site.zhelper.net/Hugo/hugo-comment/" target="_blank" rel="noopener" >hugo:添加评论功能(Waline) - 建站指南</a>,我的Waline配置:</p> <p><code>\config.yaml</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#e06c75">comments</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">enabled</span>: <span style="color:#e5c07b">true</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">provider</span>: waline </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">waline</span>: </span></span><span style="display:flex;"><span> <span style="color:#7f848e"># 这里填你的vercel服务器地址。</span> </span></span><span style="display:flex;"><span> <span style="color:#7f848e"># vercel自定义域名会和cloudflare会冲突导致无限301,所以干脆直接用送的域名了</span> </span></span><span style="display:flex;"><span> <span style="color:#7f848e"># 注意要部署总域名,而不是最新部署的版本域名(中间有一段随机英文字符的),否则会报 401 Unauthorized</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">serverURL</span>: https://waline-你的用户名.vercel.app/ </span></span><span style="display:flex;"><span> <span style="color:#e06c75">lang</span>: zh-CN </span></span><span style="display:flex;"><span> <span style="color:#7f848e"># 文章浏览量统计,在新版waline中已更名为pageview属性,貌似用不了了</span> </span></span><span style="display:flex;"><span> <span style="color:#7f848e"># 填入false代表不启用,填入字符串的时候会作为css选择器</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">visitor</span>: <span style="color:#e5c07b">false</span> </span></span><span style="display:flex;"><span> <span style="color:#7f848e"># 头像来源,在V2中已移除该属性</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">avatar</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">emoji</span>: </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/bilibili </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/bmoji </span></span><span style="display:flex;"><span> - https://npm.elemecdn.com/@waline/emojis@1.1.0/weibo </span></span><span style="display:flex;"><span> <span style="color:#7f848e"># 回复时必填的内容</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">requiredMeta</span>: </span></span><span style="display:flex;"><span> - name </span></span><span style="display:flex;"><span> - email </span></span><span style="display:flex;"><span> - url </span></span><span style="display:flex;"><span> <span style="color:#7f848e"># 评论框的默认的文字</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">placeholder</span>: 欢迎留下宝贵的评论! </span></span><span style="display:flex;"><span> <span style="color:#7f848e"># 自定义语言设置,参考https://waline.js.org/cookbook/customize/locale.html#locale-%E9%80%89%E9%A1%B9</span> </span></span><span style="display:flex;"><span> <span style="color:#e06c75">locale</span>: </span></span><span style="display:flex;"><span> <span style="color:#e06c75">admin</span>: 站长 </span></span><span style="display:flex;"><span> <span style="color:#e06c75">sofa</span>: 还没有人评论哦!快来抢沙发吧~ </span></span></code></pre></div><p><figure > <a href="https://s2.loli.net/2023/01/15/OKxm9Lyr3Dj5vg1.png" > <img src="https://s2.loli.net/2023/01/15/OKxm9Lyr3Dj5vg1.png" loading="lazy" alt="评论系统"> </a> <figcaption>评论系统</figcaption> </figure></p> <p>另外 <code>emoji</code> 官方提供了许多预设,可以查看<a class="link" href="https://waline.js.org/guide/features/emoji.html#%E9%A2%84%E8%AE%BE" target="_blank" rel="noopener" >预设列表</a>自行选择。</p> <p>但是官方提供的预设使用的 <code>unpkg</code> 经常会被墙而导致无法正常访问,这里推荐替换为饿了么提供的国内镜像(将 <code>unpkg.com</code> 替换为 <code>npm.elemecdn.com</code>)</p> <p><figure > <a href="https://s2.loli.net/2023/01/15/P8s2tmiIvQbwJhX.png" > <img src="https://s2.loli.net/2023/01/15/P8s2tmiIvQbwJhX.png" loading="lazy" alt="自定义表情"> </a> <figcaption>自定义表情</figcaption> </figure></p> <h2 id="如何编写博客">如何编写博客</h2> <p>正如同其他的博客,使用 <strong>Markdown 语言</strong>来编写博客。Markdown流行且极易上手,因此这里就不多介绍语法,如果不会的可以自己搜索了解。</p> <h3 id="使用-hugo-创建文章">使用 Hugo 创建文章</h3> <p>在博客根目录下运行:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>hugo new post/untitled.md </span></span></code></pre></div><p>为什么要用 hugo 来新建而不是创建一个 <code>.md</code> 文件呢?这是因为使用 hugo 创建会自动使用已填入 <code>Front Matter</code>的模板。</p> <p><code>Front Matter</code> 用于标识文章的标题、时间等信息,hugo 就是据此来生成静态页面。关于属性的含义和用法可以参考 <a class="link" href="https://www.gohugo.org/doc/content/front-matter/" target="_blank" rel="noopener" >Hugo中文文档</a>。</p> <p>模板可以在 <code>\archetypes\default.md</code> 下找到:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>--- </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>title: &#34;{{ replace .Name &#34;-&#34; &#34; &#34; | title }}&#34; # 标题,创建时自动填充 </span></span><span style="display:flex;"><span>description: # 文章简介 </span></span><span style="display:flex;"><span>date: {{ .Date }} # 日期,创建时自动填充,格式同 2023-01-15T12:00:00+08:00 </span></span><span style="display:flex;"><span>image: # 文章的封面,留空就是没有,填文章所在位置的相对地址,通常放在同目录下, </span></span><span style="display:flex;"><span>math: # 是否启用 KaTex,填 true 启用 </span></span><span style="display:flex;"><span>license: # 文章尾部显示的协议,false 为隐藏,其他作为内容,留空就是使用 config.yaml 里默认的 </span></span><span style="display:flex;"><span>hidden: false # 是否隐藏,一般用不到 </span></span><span style="display:flex;"><span>comments: true # 因为 bug 所以这个属性只要存在,不管是 true 还是 false 都会导致回复无法显示,需要删掉 </span></span><span style="display:flex;"><span>draft: true # 是否为草稿,建议改为 false 或者删掉这个属性以防止忘记修改,毕竟我们一般都是写好了才部署到服务器上 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>--- </span></span></code></pre></div><p>为了方便,我参考网络以及stack-mod的功能对模板进行了一些改造:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>--- </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>title: &#34;{{ replace .Name &#34;-&#34; &#34; &#34; | title }}&#34; </span></span><span style="display:flex;"><span>slug: &#34;{{ replace .Name &#34;-&#34; &#34; &#34; | title }}&#34; </span></span><span style="display:flex;"><span>description: </span></span><span style="display:flex;"><span>date: &#34;{{ .Date }}&#34; </span></span><span style="display:flex;"><span>lastmod: &#34;{{ .Date }}&#34; </span></span><span style="display:flex;"><span>image: cover.png </span></span><span style="display:flex;"><span>math: </span></span><span style="display:flex;"><span>license: </span></span><span style="display:flex;"><span>hidden: false </span></span><span style="display:flex;"><span>draft: false </span></span><span style="display:flex;"><span>categories: [&#34;&#34;] </span></span><span style="display:flex;"><span>tags: [&#34;&#34;] </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>--- </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>## 附录 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>### 参考文献 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>### 版权信息 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>本文原载于 [<span style="color:#e06c75">reincarnatey.net</span>](<span style="color:#e06c75">https://blog.reincarnatey.net</span>),遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。 </span></span></code></pre></div><p>因此我们可以编写一个批处理程序来快速帮我们生成文章:</p> <p><code>创建文章.bat</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>@<span style="color:#c678dd">echo</span> off </span></span><span style="display:flex;"><span><span style="color:#c678dd">echo</span> 【创建文章】 </span></span><span style="display:flex;"><span><span style="color:#c678dd">set</span> <span style="color:#c678dd">/p</span> <span style="color:#e06c75">input</span>= 请输入Slug: </span></span><span style="display:flex;"><span>hugo new post/<span style="color:#e06c75">%Date:~0,4%</span>/<span style="color:#e06c75">%Date:~5,2%%Date:~8,2%</span>-<span style="color:#e06c75">%input%</span>/index.md </span></span><span style="display:flex;"><span><span style="color:#c678dd">pause</span> </span></span></code></pre></div><p>用此批处理程序生成的文章会创建在 <code>\content\post\2023\0115-test\index.md</code>,便于我们整理文章资料,同时后续在同目录下存放文章的封面也不会导致内容很乱。</p> <h3 id="使用-hugo-创建类别标签">使用 Hugo 创建类别、标签</h3> <p>创建 Categories 和 Tag 也同理:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>hugo new categories/testcat.md </span></span><span style="display:flex;"><span>hugo new tags/testtag.md </span></span></code></pre></div><p>hugo 会自动应用 <code>\archetypes\categories.md</code> 和 <code>\archetypes\tags.md</code> 的模板,但是这两个模板都不太好,建议都改为:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>--- </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>title: &#34;{{ replace .Name &#34;-&#34; &#34; &#34; | title }}&#34; </span></span><span style="display:flex;"><span>slug: &#34;{{ replace .Name &#34;-&#34; &#34; &#34; | title }}&#34; </span></span><span style="display:flex;"><span>description: </span></span><span style="display:flex;"><span>image: cover.png </span></span><span style="display:flex;"><span>style: </span></span><span style="display:flex;"><span> background: &#34;<span style="color:#e06c75">#2a9d8f</span>&#34; </span></span><span style="display:flex;"><span> color: &#34;<span style="color:#e06c75">#fff</span>&#34; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>--- </span></span></code></pre></div><p><code>创建类别.bat</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>@<span style="color:#c678dd">echo</span> off </span></span><span style="display:flex;"><span><span style="color:#c678dd">echo</span> 【创建类别】 </span></span><span style="display:flex;"><span><span style="color:#c678dd">set</span> <span style="color:#c678dd">/p</span> <span style="color:#e06c75">input</span>= 请输入类别名: </span></span><span style="display:flex;"><span>hugo new categories/<span style="color:#e06c75">%input%</span>/_index.md </span></span><span style="display:flex;"><span><span style="color:#c678dd">pause</span> </span></span></code></pre></div><p><code>创建标签.bat</code>:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>@<span style="color:#c678dd">echo</span> off </span></span><span style="display:flex;"><span><span style="color:#c678dd">echo</span> 【创建标签】 </span></span><span style="display:flex;"><span><span style="color:#c678dd">set</span> <span style="color:#c678dd">/p</span> <span style="color:#e06c75">input</span>= 请输入标签名: </span></span><span style="display:flex;"><span>hugo new tags/<span style="color:#e06c75">%input%</span>/_index.md </span></span><span style="display:flex;"><span><span style="color:#c678dd">pause</span> </span></span></code></pre></div><p><strong>注意</strong>:如果创建多级文件夹时文章文件名不是 <code>index.md</code> 或者类别、标签文件名不是 <code>_index.md</code> 的话,设置封面图片会出现问题。</p> <h3 id="markdown-编写工具的安装">Markdown 编写工具的安装</h3> <p>生成之后,我们就要编辑文章了。</p> <p>实际上我也是第一次在本地写博客,此前并没有安装专门的 Markdown 编辑器,基本是手边是啥就用啥(比如 VS、IDEA、我不记得名字的某个云笔记、Notepad++、甚至 windows自带的记事本)。</p> <p>毕竟之前只是为项目编写 README,够用就行了,但对于博客来说这样肯定是不满足我们日常编写的需求的,因此推荐安装一个 Markdown 编辑器。</p> <p>曾经听过 Typora 的大名,然而不幸的是该编辑器已经转为收费且优化不足,因此这里推荐免费开源且好用的 <a class="link" href="https://github.com/marktext/marktext" target="_blank" rel="noopener" >MarkText</a>。</p> <p>安装方法很简单,<a class="link" href="https://github.com/marktext/marktext/releases" target="_blank" rel="noopener" >release</a> 里面下载对应的版本安装即可。不过在我写这篇博客的时候 MarkText 还没有做好国际化的准备,因此尽管已经(看起来)有中文语言文件却无法切换语言。</p> <p>这里推荐一个大佬的汉化版本 <a class="link" href="https://github.com/chinayangxiaowei/marktext-chinese-language-pack" target="_blank" rel="noopener" >marktext-chinese-language-pack</a>,可以直接在该仓库的 <a class="link" href="https://github.com/chinayangxiaowei/marktext-chinese-language-pack/releases" target="_blank" rel="noopener" >release</a> 仓库里下载对应版本的压缩包,替换到原版安装目录就好了(或者不安装原版直接使用汉化的程序文件,应该是直接可以用的,不过我比较喜欢通过官方安装器安装一遍以免注册表或者启动栏之类的问题)。</p> <p>打开之后应该就是中文了,简单设置后直接输入便可以开始进行内容的编写了。</p> <h3 id="marktext-中图床相关设置">MarkText 中图床相关设置</h3> <p>在文章中图片是少不了的,那么图片该放哪呢?虽然你可以放在自己服务器上,但这样会占用服务器资源、增加请求数,还增加了你的部署成本和时间。</p> <p>因此,在编写博客的时候,我们通常会需要一个图床来存放那些不是特别重要的图片,例如过程的截图等。</p> <p>曾经最常用的微博图床现在已经开了防盗链,其他图床大多要钱或是国内无法访问,难以找到稳定的图床。因此有能力建议自己搭一个,并不是很难。</p> <p>这里我使用的图床上传工具是 <a class="link" href="https://picgo.github.io/PicGo-Doc/" target="_blank" rel="noopener" >PicGo</a>,虽然 MarkText 支持调用 PicGo 进行上传,不过由于对接的不是很好,目前使用上还是会有问题,不过直接在 PicGo 软件内进行上传已经足够方便了。</p> <p>使用方法很简单,安装完成后<a class="link" href="https://picgo.github.io/PicGo-Doc/zh/guide/config.html#smms" target="_blank" rel="noopener" >按照这里获取 Token</a> 在<strong>图床设置</strong>-&gt;<strong>SM.MS</strong> 中进行配置即可。之后就可以在上传区上传图片文件或是截图后点击<strong>剪贴板图片</strong>快速上传了。默认会在上传完成后自动复制 Markdown 格式的链接,直接复制到编辑器就好了。</p> <p>由于 sm.ms 最近经常被墙,建议在备用上传域名一栏填上 smms.app,这是官方的镜像(?)地址。</p> <p><figure > <a href="https://s2.loli.net/2023/01/08/cFjzaJXryRWEiT1.png" > <img src="https://s2.loli.net/2023/01/08/cFjzaJXryRWEiT1.png" loading="lazy" alt="镜像设置"> </a> <figcaption>镜像设置</figcaption> </figure></p> <h3 id="marktext-中编辑图片的标题">MarkText 中编辑图片的标题</h3> <p>在我们的博客中,有时会希望在图片下方显示一个标题。在 MarkText 中因为会直接渲染文件,这时需要在修改页面切换至完整模式。(不是很方便,这算是缺点吧)</p> <p><figure > <a href="https://s2.loli.net/2023/01/08/8Xm5kS3DUprbPVO.png" > <img src="https://s2.loli.net/2023/01/08/8Xm5kS3DUprbPVO.png" loading="lazy" alt="选择编辑"> </a> <figcaption>选择编辑</figcaption> </figure></p> <p><figure > <a href="https://s2.loli.net/2023/01/08/rcoM7jg2qPTNJHX.png" > <img src="https://s2.loli.net/2023/01/08/rcoM7jg2qPTNJHX.png" loading="lazy" alt="嵌入链接"> </a> <figcaption>嵌入链接</figcaption> </figure></p> <p><figure > <a href="https://s2.loli.net/2023/01/08/K6xs497COnBYRSG.png" > <img src="https://s2.loli.net/2023/01/08/K6xs497COnBYRSG.png" loading="lazy" alt="使用完整模式"> </a> <figcaption>使用完整模式</figcaption> </figure></p> <p>简单模式下只可以编辑链接,如需要设置图片标题或替代文本的话需要打开<strong>编辑图片</strong>页面点击下方的蓝字切换到完整模式,输入替代文本和标题后点击<strong>嵌入图像</strong>即可。</p> <p>另外,点击图片后显示的悬浮窗内也可以修改图片的对齐方式(比如居中对齐)</p> <p>除了图片以外,选中文字后同样也会出现悬浮框可以修改文字样式。</p> <h2 id="生成静态网页">生成静态网页</h2> <p>在完成编写之后,就可以生成网页了。</p> <p>运行以下命令:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>hugo </span></span></code></pre></div><p>或携带一些参数:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span>hugo --theme=stack --baseUrl=<span style="color:#98c379">&#34;https://blog.reincarnatey.net/&#34;</span> </span></span></code></pre></div><p>随后可以看到根目录下出现了 <code>public</code> 文件夹,该文件夹下就是生成的静态网页。你可以将该文件夹内的文件上传到服务器,或是 push 到你的 github 仓库部署到你使用的托管服务,访问你的域名就可以看到了。</p> <p><strong>2025/12/02 Update:新版本 Hugo 中已经修改这个参数的名字,需改用以下命令:</strong></p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cmd" data-lang="cmd"><span style="display:flex;"><span>hugo build --theme=stack --baseURL=<span style="color:#98c379">&#34;https://blog.reincarnatey.net/&#34;</span> </span></span></code></pre></div><h3 id="使用-git-一键推送">使用 git 一键推送</h3> <p>如果你需要 push 到 github 仓库,那么在配置完成 <code>git</code> 后你可以使用这段代码进行初始化:</p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span><span style="color:#c678dd">cd</span> public </span></span><span style="display:flex;"><span>git init </span></span><span style="display:flex;"><span>git remote add origin 你的仓库地址.git </span></span><span style="display:flex;"><span>git add -A </span></span><span style="display:flex;"><span>git commit -m <span style="color:#98c379">&#34;create&#34;</span> </span></span><span style="display:flex;"><span>git push -u origin master </span></span></code></pre></div><p>初始化后,以后你可以使用此脚本进行一键 push:</p> <p><code>push.bat</code></p> <div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-batch" data-lang="batch"><span style="display:flex;"><span><span style="color:#c678dd">cd</span> public </span></span><span style="display:flex;"><span>git add -A </span></span><span style="display:flex;"><span>git commit -m <span style="color:#98c379">&#34;update&#34;</span> </span></span><span style="display:flex;"><span>git push -u origin master </span></span></code></pre></div><p>当然,你也可以将生产静态网页的命令加入到一键 push 的脚本的开头,可以实现一键生成并推送,这里就不作过多演示了。</p> <p>至此,一个崭新的博客已然搭建完成。但搭建只是第一步,后续还需要更新和维护,按照以上流程编写、生成、部署即可。</p> <h2 id="附录">附录</h2> <h3 id="参考文献">参考文献</h3> <ol> <li> <p><a class="link" href="https://www.gohugo.org/" target="_blank" rel="noopener" >Hugo中文文档</a></p> </li> <li> <p><a class="link" href="https://site.zhelper.net/Hugo/hugo-stack/" target="_blank" rel="noopener" >Hugo:Stack主题 - 建站指南</a></p> </li> <li> <p><a class="link" href="https://stack.jimmycai.com/" target="_blank" rel="noopener" >Stack官方文档</a></p> </li> <li> <p><a class="link" href="https://github.com/Mantyke/Hugo-stack-theme-mod" target="_blank" rel="noopener" >GitHub - Mantyke/Hugo-stack-theme-mod</a></p> </li> <li> <p><a class="link" href="https://github.com/chinayangxiaowei/marktext-chinese-language-pack" target="_blank" rel="noopener" >GitHub - chinayangxiaowei/marktext-chinese-language-pack: Simplified Chinese language pack for marktext</a></p> </li> <li> <p><a class="link" href="https://waline.js.org/guide/get-started/" target="_blank" rel="noopener" >Waline官方文档</a></p> </li> <li> <p><a class="link" href="https://picgo.github.io/PicGo-Doc/" target="_blank" rel="noopener" >PicGo官方文档</a></p> </li> <li> <p><a class="link" href="https://maintao.com/2021/code-syntax-highlight/" target="_blank" rel="noopener" >Hugo 代码高亮</a></p> </li> </ol> <h3 id="版权信息">版权信息</h3> <p>本文原载于 <a class="link" href="https://blog.reincarnatey.net" target="_blank" rel="noopener" >reincarnatey.net</a>,遵循 CC BY-NC-SA 4.0 协议,复制请保留原文出处。</p>

2023/1/15
阅读更多