[{"content":" \u0026#x26a0;\u0026#xfe0f; 本文大量引用自鱼丸粗面的这篇文章\n我的网卡是I217-LM，用的是e1000e的驱动，故障日志为：\nApr 26 16:03:44 pve kernel: e1000e 0000:00:19.0 nic0: NIC Link is Down Apr 26 16:03:44 pve kernel: vmbr0: port 1(nic0) entered disabled state Apr 26 16:03:47 pve kernel: e1000e 0000:00:19.0 nic0: NIC Link is Up 1000 Mbps Full Duplex, Flow Control: Rx/Tx Apr 26 16:03:47 pve kernel: vmbr0: port 1(nic0) entered blocking state Apr 26 16:03:47 pve kernel: vmbr0: port 1(nic0) entered forwarding state Apr 26 16:04:07 pve kernel: e1000e 0000:00:19.0 nic0: Detected Hardware Unit Hang: TDH \u0026lt;0\u0026gt; TDT \u0026lt;3\u0026gt; next_to_use \u0026lt;3\u0026gt; next_to_clean \u0026lt;0\u0026gt; buffer_info[next_to_clean]: time_stamp \u0026lt;105cdabd7\u0026gt; next_to_watch \u0026lt;0\u0026gt; jiffies \u0026lt;105cdb6c0\u0026gt; next_to_watch.status \u0026lt;0\u0026gt; MAC Status \u0026lt;80083\u0026gt; PHY Status \u0026lt;796d\u0026gt; PHY 1000BASE-T Status \u0026lt;3800\u0026gt; PHY Extended Status \u0026lt;3000\u0026gt; PCI Status \u0026lt;10\u0026gt; Apr 26 16:04:08 pve kernel: e1000e 0000:00:19.0 nic0: NIC Link is Down 故障原因 # 这是 Linux 内核中 e1000e 驱动的一个经典 Bug，在较新的内核版本（尤其是 5.15+ 到 6.x）配合特定批次的 Intel I219 系列网卡时极易触发。\n网卡的 TCP 分段卸载 (TSO, TCP Segmentation Offload) 和通用分段卸载 (GSO) 功能在处理高并发或特定数据包时，可能导致网卡的环形缓冲区 (Ring Buffer) 指针计算错误或死锁，导致网卡硬件挂起并尝试重置。\n解决方案 # 以我的网卡名nic0为例\n临时 # sudo ethtool -K nic0 tso off gso off gro off 持久化 # 编辑/etc/network/interfaces：\nauto lo iface lo inet loopback iface nic0 inet manual post-up /usr/sbin/ethtool -K nic0 tso off gso off gro off #在你的网卡模块下添加此行，注意tab。 iface nic1 inet manual auto vmbr0 iface vmbr0 inet static address 192.168.10.200/24 gateway 192.168.10.1 bridge-ports nic0 bridge-stp off bridge-fd 0 source /etc/network/interfaces.d/* 补充 # 如果关闭 Offload 后仍偶发断网，可能是电源管理 (ASPM) 问题。可修改 GRUB 配置：\n编辑 /etc/default/grub，在 GRUB_CMDLINE_LINUX_DEFAULT 中追加：pcie_aspm=off 更新 GRUB：update-grub。 ","date":"26 April 2026","externalUrl":null,"permalink":"/posts/2026/debian-13-pve%E5%86%85%E6%A0%B8-%E4%B8%8B-intel-e1000e-%E7%BD%91%E5%8D%A1%E9%97%B4%E6%AD%87%E6%80%A7-hardware-unit-hang-%E6%96%AD%E7%BD%91%E9%97%AE%E9%A2%98%E5%8E%9F%E5%9B%A0%E4%B8%8E%E8%A7%A3%E5%86%B3/","section":"帖子总览","summary":"","title":"Debian 13 (PVE内核) 下 Intel e1000e 网卡间歇性 “Hardware Unit Hang” 断网问题原因与解决","type":"posts"},{"content":"","date":"26 April 2026","externalUrl":null,"permalink":"/tags/linux/","section":"标签总览","summary":"","title":"Linux","type":"tags"},{"content":"","date":"26 April 2026","externalUrl":null,"permalink":"/tags/pve/","section":"标签总览","summary":"","title":"Pve","type":"tags"},{"content":"通过标签进行索引：\n","date":"26 April 2026","externalUrl":null,"permalink":"/tags/","section":"标签总览","summary":"","title":"标签总览","type":"tags"},{"content":"","date":"26 April 2026","externalUrl":null,"permalink":"/","section":"光mon's 静态博客小站","summary":"","title":"光mon's 静态博客小站","type":"page"},{"content":"","date":"26 April 2026","externalUrl":null,"permalink":"/categories/%E6%8A%80%E6%9C%AF%E6%8A%98%E8%85%BE/","section":"类别总览","summary":"","title":"技术折腾","type":"categories"},{"content":"通过类别进行索引：\n","date":"26 April 2026","externalUrl":null,"permalink":"/categories/","section":"类别总览","summary":"","title":"类别总览","type":"categories"},{"content":"","date":"26 April 2026","externalUrl":null,"permalink":"/tags/%E9%A9%B1%E5%8A%A8/","section":"标签总览","summary":"","title":"驱动","type":"tags"},{"content":"这里列出我写的所有帖子。\n","date":"26 April 2026","externalUrl":null,"permalink":"/posts/","section":"帖子总览","summary":"","title":"帖子总览","type":"posts"},{"content":"","date":"26 April 2026","externalUrl":null,"permalink":"/tags/%E8%99%9A%E6%8B%9F%E6%9C%BA/","section":"标签总览","summary":"","title":"虚拟机","type":"tags"},{"content":"","date":"25 April 2026","externalUrl":null,"permalink":"/tags/openwrt/","section":"标签总览","summary":"","title":"Openwrt","type":"tags"},{"content":" \u0026#x26a0;\u0026#xfe0f; 本文思路来源于这篇文章，问题原因见此帖。\n在宿舍部署的基于PVE虚拟机方案的openwrt软路由一直以来都工作得十分正常。然而，这个学期开始的时候，我尝试开机，主板上的蜂鸣器发出长声嘀（没有间隔）。虽然根据联想知识库的说明来看应该是显卡和显示器未插好导致的，但是经过反复尝试无果后我选择换了张主板（B85-\u0026gt;H81）。\n在H81台式机这个阶段，会出现openwrt时不时死机的bug，而且会顺带着宿主机一起死机，需要强制重启来解决。 后来，扔了台式机，换了HP 800g1 dm这个小主机。\n在小主机阶段，会出现lan口频繁断网的bug，并不会导致openwrt和宿主机死机，只需重新拔插小主机和ap之间的网线即可解决。 这两个阶段的问题，共同的表现是lan口的服务暂停，上网设备无法获取dhcp服务下发的ipv4地址：\n一般来说，遇到问题后应该去查看系统日志，然而我翻看了pve和openwrt的日志，均找不到相关日志，于是，就有了这篇博客的内容：用打补丁的方式临时解决一下这个问题。\n网络环境 # 因为我是基于PVE的虚拟机方案，所以有必要先介绍一下我的网络环境，如下图所示：\n经过实验，当ETH0接口（pve的lan口）掉ip（断网）的时候，需要拔插ETH0和AP（ap的lan口）之间的网线，才能解决问题；但是由于这个现象十分频繁，一直手动拔插十分不现实，因此决定写脚本使得当pve的lan口断网时，自动重启lan口。\nPVE操作 # 脚本 # #!/bin/bash #/usr/local/bin/net_watchdog.sh TARGET=\u0026#34;192.168.10.119\u0026#34; # 你的AP ip while true; do # 检测网络 if ! ping -c 3 -W 2 $TARGET \u0026gt; /dev/null; then echo \u0026#34;$(date) - Network down! Restarting...\u0026#34; \u0026gt;\u0026gt; /var/log/net_watchdog.log ifdown nic0 \u0026amp;\u0026amp; ifup nic0 # 或者你的重启网络命令 sleep 20 # 重启后等待20秒，防止频繁抖动 else sleep 5 # 每5秒检测一次，降低CPU占用 fi done 添加执行权限 # chmod +x /usr/local/bin/net_watchdog.sh 创建 Systemd 服务文件 # #/etc/systemd/system/net-watchdog.service [Unit] Description=Network Watchdog Service After=network.target [Service] Type=simple ExecStart=/usr/local/bin/net_watchdog.sh Restart=always RestartSec=5 [Install] WantedBy=multi-user.target 启动并启用服务 # systemctl daemon-reload systemctl start net-watchdog systemctl enable net-watchdog 搞定之后，就可以在/var/log/net_watchdog.log文件中查看lan口的重启信息了：\n可以看到我这里重启得非常频繁……😭\n附：openwrt脚本（如果你是openwrt实体机的话） # #!/bin/bash TARGET=\u0026#34;192.168.10.119\u0026#34; # 替换成你的ap lan口ip if /bin/ping -c 3 -W 2 $TARGET \u0026gt; /dev/null; then exit 0 else logger \u0026#34;Network unreachable! Restarting LAN interface...\u0026#34; /sbin/ifup lan sleep 10 fi 注意添加可执行权限。\n可以通过openwrt自带的计划任务来启用脚本，比如：\ncrontab -e 添加以下内容：\n* * * * * /bin/bash /root/check_net.sh \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 重启openwrt，脚本激活时系统日志就会有类似如下的输出：\nSat Apr 25 18:11:00 2026 cron.err crond[10729]: USER root pid 16297 cmd /bin/bash /root/check_net.sh \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 Sat Apr 25 18:11:04 2026 user.notice root: Network unreachable! Restarting LAN interface... Sat Apr 25 18:11:04 2026 daemon.notice netifd: Interface \u0026#39;lan\u0026#39; is now down Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Registering new address record for fe80::be24:11ff:fe91:8b90 on br-lan.*. Sat Apr 25 18:11:04 2026 daemon.notice ttyd[12083]: [2026/04/25 18:11:04:2047] N: rops_handle_POLLIN_netlink: DELADDR Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Withdrawing address record for 192.168.10.1 on br-lan. Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Leaving mDNS multicast group on interface br-lan.IPv4 with address 192.168.10.1. Sat Apr 25 18:11:04 2026 daemon.err odhcpd[3926]: Failed to send to ff02::1%lan@br-lan (Network unreachable) Sat Apr 25 18:11:04 2026 daemon.notice ttyd[12083]: [2026/04/25 18:11:04:2064] N: rops_handle_POLLIN_netlink: DELADDR Sat Apr 25 18:11:04 2026 daemon.notice ttyd[12083]: [2026/04/25 18:11:04:2065] N: rops_handle_POLLIN_netlink: DELADDR Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Interface br-lan.IPv4 no longer relevant for mDNS. Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Interface br-lan.IPv6 no longer relevant for mDNS. Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Leaving mDNS multicast group on interface br-lan.IPv6 with address fc00::1. Sat Apr 25 18:11:04 2026 kern.info kernel: [ 84.202900] br-lan: port 1(eth0) entered disabled state Sat Apr 25 18:11:04 2026 kern.info kernel: [ 84.205774] device eth0 left promiscuous mode Sat Apr 25 18:11:04 2026 kern.info kernel: [ 84.206924] br-lan: port 1(eth0) entered disabled state Sat Apr 25 18:11:04 2026 user.warn mwan3-hotplug[16327]: hotplug called on lan before mwan3 has been set up Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Withdrawing address record for fe80::be24:11ff:fe91:8b90 on br-lan. Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Withdrawing address record for fc00::1 on br-lan. Sat Apr 25 18:11:04 2026 daemon.notice netifd: Interface \u0026#39;lan\u0026#39; is disabled Sat Apr 25 18:11:04 2026 daemon.notice netifd: bridge \u0026#39;br-lan\u0026#39; link is down Sat Apr 25 18:11:04 2026 daemon.notice netifd: Interface \u0026#39;lan\u0026#39; has link connectivity loss Sat Apr 25 18:11:04 2026 daemon.notice netifd: Network device \u0026#39;eth0\u0026#39; link is down Sat Apr 25 18:11:04 2026 kern.info kernel: [ 84.435162] 8021q: adding VLAN 0 to HW filter on device eth0 Sat Apr 25 18:11:04 2026 kern.info kernel: [ 84.436686] br-lan: port 1(eth0) entered blocking state Sat Apr 25 18:11:04 2026 kern.info kernel: [ 84.437839] br-lan: port 1(eth0) entered disabled state Sat Apr 25 18:11:04 2026 kern.info kernel: [ 84.439124] device eth0 entered promiscuous mode Sat Apr 25 18:11:04 2026 kern.info kernel: [ 84.441441] br-lan: port 1(eth0) entered blocking state Sat Apr 25 18:11:04 2026 kern.info kernel: [ 84.442787] br-lan: port 1(eth0) entered forwarding state Sat Apr 25 18:11:04 2026 daemon.notice netifd: Interface \u0026#39;lan\u0026#39; is enabled Sat Apr 25 18:11:04 2026 daemon.notice netifd: Interface \u0026#39;lan\u0026#39; is setting up now Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Joining mDNS multicast group on interface br-lan.IPv4 with address 192.168.10.1. Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: New relevant interface br-lan.IPv4 for mDNS. Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Registering new address record for 192.168.10.1 on br-lan.IPv4. Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Joining mDNS multicast group on interface br-lan.IPv6 with address fc00::1. Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: New relevant interface br-lan.IPv6 for mDNS. Sat Apr 25 18:11:04 2026 daemon.info avahi-daemon[4398]: Registering new address record for fc00::1 on br-lan.*. Sat Apr 25 18:11:04 2026 daemon.notice netifd: Interface \u0026#39;lan\u0026#39; is now up Sat Apr 25 18:11:04 2026 daemon.notice netifd: Network device \u0026#39;eth0\u0026#39; link is up Sat Apr 25 18:11:04 2026 daemon.notice netifd: bridge \u0026#39;br-lan\u0026#39; link is up Sat Apr 25 18:11:04 2026 daemon.notice netifd: Interface \u0026#39;lan\u0026#39; has link connectivity Sat Apr 25 18:11:04 2026 user.warn mwan3-hotplug[16463]: hotplug called on lan before mwan3 has been set up Sat Apr 25 18:11:04 2026 user.notice firewall: Reloading firewall due to ifup of lan (br-lan) Sat Apr 25 18:11:06 2026 daemon.warn odhcpd[3926]: A default route is present but there is no public prefix on lan thus we don\u0026#39;t announce a default route by overriding ra_lifetime! Sat Apr 25 18:11:10 2026 user.notice nlbwmon: Reloading nlbwmon due to ifup of lan (br-lan) Sat Apr 25 18:11:19 2026 daemon.warn odhcpd[3926]: A default route is present but there is no public prefix on lan thus we don\u0026#39;t announce a default route by overriding ra_lifetime! Sat Apr 25 18:11:22 2026 daemon.warn odhcpd[3926]: A default route is present but there is no public prefix on lan thus we don\u0026#39;t announce a default route by overriding ra_lifetime! Sat Apr 25 18:11:23 2026 daemon.warn odhcpd[3926]: A default route is present but there is no public prefix on lan thus we don\u0026#39;t announce a default route by overriding ra_lifetime! Sat Apr 25 18:11:27 2026 daemon.warn odhcpd[3926]: A default route is present but there is no public prefix on lan thus we don\u0026#39;t announce a default route by overriding ra_lifetime! Sat Apr 25 18:11:30 2026 daemon.err uhttpd[4067]: luci: accepted login on /admin for root from 192.168.10.81 整理时发现一个有趣的事：PVE下的ifup命令和openwrt下的ifup命令是不同的实现，前者只有当lan口未正常运行时才会启动lan口，后者几乎是强制重启lan口；因此PVE的重启lan口命令需要在前面加一个ifdown。\n","date":"25 April 2026","externalUrl":null,"permalink":"/posts/2026/openwrt%E8%B7%AF%E7%94%B1%E5%99%A8lan%E5%8F%A3%E8%8E%AB%E5%90%8D%E5%85%B6%E5%A6%99%E6%96%AD%E7%BD%91%E7%9A%84%E8%A1%A5%E4%B8%81%E5%BC%8F%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/","section":"帖子总览","summary":"","title":"openwrt路由器lan口莫名其妙断网的补丁式解决方案","type":"posts"},{"content":"","date":"25 April 2026","externalUrl":null,"permalink":"/tags/%E8%BD%AF%E8%B7%AF%E7%94%B1/","section":"标签总览","summary":"","title":"软路由","type":"tags"},{"content":"","date":"16 April 2026","externalUrl":null,"permalink":"/tags/archisteamfarm/","section":"标签总览","summary":"","title":"Archisteamfarm","type":"tags"},{"content":"","date":"16 April 2026","externalUrl":null,"permalink":"/tags/ast/","section":"标签总览","summary":"","title":"Ast","type":"tags"},{"content":" ❌ 首先，在Docker中运行ArchiSteamFarm是不被官方建议的行为（被视为进阶安装方式），因此着这种使用方式相比普通桌面系统安装使用会有更多bug和意想不到的行为，请提前做好心理准备，正如官方文档所说的，\n在Docker容器中运行ASF通常会带来一些新问题，您必须自己面对并解决。\n叠甲完成，回到正题。\n既然是在Docker环境中部署，那首先先了解一下Docker的网络结构：\n以飞牛OS为例，其Docker默认使用bridge模式，容器的虚拟接口看到的是172.17.0.0/16这个子网，而假设宿主机飞牛OS属于192.168.18.0/24这个子网；局域网内设备想要访问容器内开启的服务，就需要设置一层端口映射，把宿主机的端口映射成bridge子网范围内的一组ip+端口号，用宿主机的ip+端口访问容器内的服务。\n接着下载容器的镜像，配置相关参数，用镜像创建ASF(ArchiSteamFarm)容器，就像每个docker用户都会做的那样：\n端口映射和卷映射都很自然地配置好，接着启动ASF容器，你会发现： 竟然打不开网页！\n坑一 打不开服务页面 # 别慌，让我们不要太过于相信自己的经验，而是回到ArchiSteamFarm的官方文档，来到 Docker - IPC 这一栏，可以看到里面来了句：\n您必须额外做两件事，才能使 IPC（基于Kestrel HTTP服务端的“ASF Web 接口”，作为最终用户使用的前端或者第三方集成工具使用的后端与ASF进程进行交互） 在 Docker 中正常工作，\n即：\n修改掉默认的监听地址localhost # 文章开头回顾了docker的网络架构，ASF的默认监听地址是localhost（包括docker版），而Docker无法将外部流量路由到环回接口，所以我们需要把默认的监听地址localhost修改掉，那该怎么修改呢？文档中也给到了这个自定义ipc配置：\n## /path/to/your/config/IPC.config { \u0026#34;Kestrel\u0026#34;: { \u0026#34;Endpoints\u0026#34;: { \u0026#34;HTTP\u0026#34;: { \u0026#34;Url\u0026#34;: \u0026#34;http://*:1242\u0026#34; } } } } （这里的*不可以换成192.168.18.102，因为容器虚拟接口不含这个宿主ip） 修改完把这个IPC.config复制到装载路径为/app/config/的本地路径下后重启容器即可打开web界面。\n可是这时打开web界面会发现下方提示：\n设置IPCPassword或者在自定义IPC.config中修改默认的KnownNetworks # 因为IPCPassword需要在另一个文件里设置，所以为了省事我选择在IPC.config中加上了KnownNetworks的配置：\n{ \u0026#34;Kestrel\u0026#34;: { \u0026#34;Endpoints\u0026#34;: { \u0026#34;HTTP\u0026#34;: { \u0026#34;Url\u0026#34;: \u0026#34;http://*:1242\u0026#34; } }, \u0026#34;KnownNetworks\u0026#34;: [ \u0026#34;172.17.0.0/16\u0026#34;, \u0026#34;192.168.18.0/24\u0026#34; ] } } 重启容器即可正常打开web页面：\n坑二 ASF无法登陆steam # 新建bot，输入Name，SteamLogin，SteamPassword; Enabled设为√后，手机steam客户端批准登陆后，ASF_ui报错HandleLoginResult() Unable to login to Steam: TryAnotherCM/Invalid，如下日志输出所示：\n其实这里我们可以注意到在这个报错前还有个warning，让我们确认已在steam手机客户端上确认登陆或直接使用steam令牌验证码。 我们尝试在这里输入Y/N，可以发现没有任何反应，因为这里是日志界面，不是终端。\n这里我们就可以发现问题所在了，其实ASF的终端在等待我们的输入来确认下一步干什么，但是我们压根无法输入，所以ASF也就无法确认如何登陆steam了。\nPS：如果这里是在普通桌面客户端环境下的话，不会有任何问题，因为桌面环境会打开一个终端给我们输入。\n那么问题来了，在ASF_ui的web页面下，我们该如何输入呢？\n这个问题其实在官方文档的 Docker - 高级技巧 一栏中写出了：\n通常，您应该在全局配置文件中设置Docker容器内的ASF运行于Headless: true模式。 这会明确告诉ASF，您无法直接提供它所需的数据，它不应该在命令行中直接询问这些。\n于是我们来到ASF配置页，将Headless启用：\n保存配置，等待ASF自动重启。\n然后我们可以发现日志中的输出已经出现了变化：\n这时我们启动机器人，就已经出现了让我们输入2FA代码的提示框，从手机steam客户端获取2FA代码后输入即可上线机器人。\n机器人已变为在线状态：\n之后我们设置下挂卡选项即可启动自动挂卡：\n","date":"16 April 2026","externalUrl":null,"permalink":"/posts/2026/docker%E9%85%8D%E7%BD%AEarchisteamfarm%E5%AE%9E%E7%8E%B0steam%E6%B8%B8%E6%88%8F%E8%87%AA%E5%8A%A8%E6%8C%82%E5%8D%A1%E4%BB%A5%E9%A3%9E%E7%89%9Bos%E4%B8%BA%E4%BE%8B/","section":"帖子总览","summary":"","title":"Docker配置ArchiSteamFarm实现steam游戏自动挂卡（以飞牛OS为例）","type":"posts"},{"content":"","date":"16 April 2026","externalUrl":null,"permalink":"/tags/steam/","section":"标签总览","summary":"","title":"Steam","type":"tags"},{"content":"","date":"16 April 2026","externalUrl":null,"permalink":"/tags/%E6%8C%82%E6%9C%BA/","section":"标签总览","summary":"","title":"挂机","type":"tags"},{"content":"","date":"16 April 2026","externalUrl":null,"permalink":"/tags/%E8%87%AA%E5%8A%A8%E5%8C%96/","section":"标签总览","summary":"","title":"自动化","type":"tags"},{"content":" 本方案在四代英特尔平台实测可行，理论上适用于4~10代英特尔cpu\n首先确保你成功直通了核显，通过远程桌面可以在任务管理器中看到你的核显（需要核显支持WDDM驱动）\n其次你的虚拟机型号得是q35，实测在我这里选i440fx会导致没声音（ 后续发现选q35可以通过hdmi输出声音，选i440fx可以通过机箱前面板输出声音（需要调一下虚拟机内部音频驱动的设置） ）\n将核显视频和音频设备添加到直通组（如我这里核显音频设备id是8086:0c0c，视频设备是8086:040a，其中8086:8c20是我的板载音频设备id）\n重启PVE节点\n从这里下载魔改的英特尔核显vbios，或者自行修改自己的vbios，并使用scp之类的工具将其放到pve节点的/usr/share/kvm/路径下\n修改Windows虚拟机的参数如下：\n主要是添加/修改了以下三行：\nargs: -set device.hostpci0.addr=02.0 -set device.hostpci0.x-igd-gms=0x2 -set device.hostpci0.x-igd-opregion=on hostpci0: 0000:00:02.0,romfile=intel.rom vga: none 其中intel.rom的路径就是/usr/share/kvm/intel.rom\n至此，直通核显的视频输出问题就已经解决， 接下来只需要正常在web界面添加音频的pci设备给虚拟机即可解决音频输出问题：\n如果你后续测试发现虚拟机容易死机并且带着宿主机一起死机，可以尝试以下配置： agent: 1 args: -set device.hostpci0.addr=02.0 -set device.hostpci0.x-igd-gms=0x2 -set device.hostpci0.x-igd-opregion=on bios: ovmf boot: order=scsi0;ide2;net0 cores: 8 cpu: host efidisk0: local:104/vm-104-disk-0.qcow2,efitype=4m,pre-enrolled-keys=1,size=528K hostpci0: 0000:00:02.0,romfile=intel.rom,legacy-igd=on hostpci1: 0000:00:03.0,rombar=0 hostpci2: 0000:00:1b.0,rombar=0 ide2: local:iso/virtio-win-0.1.271.iso,media=cdrom,size=709474K machine: pc-i440fx-8.1 memory: 4096 meta: creation-qemu=8.1.5,ctime=1729582691 name: Windows net0: virtio=BC:24:11:54:4F:AB,bridge=vmbr0,firewall=1 numa: 1 onboot: 1 ostype: win10 scsi0: local:104/vm-104-disk-1.qcow2,iothread=1,size=64G,ssd=1 scsihw: virtio-scsi-single smbios1: uuid=eaf316a2-c59f-4622-863a-31abb96329cd sockets: 1 usb0: host=1a2c:9b03 usb1: host=04b3:310c vga: none vmgenid: 31789ab5-0b47-449f-b4f3-13833e5baa7b 即把机器类型改为i440fx，同时给显卡加上legacy-igd=on\n如果发现音频无输出，可以尝试把直通音频设备的ROM-bar的勾给去掉： ","date":"18 May 2025","externalUrl":null,"permalink":"/posts/2025/pve%E8%8B%B1%E7%89%B9%E5%B0%94%E6%A0%B8%E6%98%BE%E7%9B%B4%E9%80%9A%E7%BB%99windows%E8%BE%93%E5%87%BA%E8%A7%86%E9%A2%91%E5%92%8C%E9%9F%B3%E9%A2%91/","section":"帖子总览","summary":"","title":"PVE英特尔核显直通给Windows输出视频和音频","type":"posts"},{"content":" 先上系统配置： 再上参考文章： PVE下黑群晖核显直通无法开启硬解的解决方法\n使用lspci -n查看核显的dev id; 比如我的就是040a 接着在要直通的虚拟机的Hardware栏中将直通的显卡项删掉； 编辑虚拟机配置文件：\n# /etc/pve/qemu-server/100.conf # 这里100换成你的虚拟机的id # 这里x-pci-device-id的值要换成你的核显的id args: -device vfio-pci,host=00:02.0,addr=0x02,x-pci-device-id=0x040A # ... 保存后重启虚拟机，核显的编解码器就可以正常调用了 ps: x-pci-devide-id的值可以和宿主的不一样，它指定的是虚拟机中显卡的id，可以填和自己核显同型号的其他id； 如果这样操作解码器还是不工作的话，可以尝试把虚拟机的Display改为Standard VGA(std)\n","date":"7 May 2025","externalUrl":null,"permalink":"/posts/2025/pve%E5%9B%9B%E4%BB%A3%E8%8B%B1%E7%89%B9%E5%B0%94%E6%A0%B8%E6%98%BE%E7%9B%B4%E9%80%9A%E5%90%8E%E7%BC%96%E8%A7%A3%E7%A0%81%E5%99%A8%E4%B8%8D%E8%83%BD%E6%AD%A3%E5%B8%B8%E5%B7%A5%E4%BD%9C%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/","section":"帖子总览","summary":"","title":"pve四代英特尔核显直通后编解码器不能正常工作的解决方案","type":"posts"},{"content":"","date":"27 April 2025","externalUrl":null,"permalink":"/tags/%E5%AE%89%E5%8D%93/","section":"标签总览","summary":"","title":"安卓","type":"tags"},{"content":" 如何通过修改 platform.xml 赋予第三方应用 SD 卡写入权限 # 前提：确保你的手机已经获取了 Root 权限。\n操作步骤 # 打开 RE 文件管理器（或其他支持 Root 权限的文件管理器）。\n进入目录：/etc/permissions/，找到文件 platform.xml。\n将该文件的属性从只读修改为可读写，并用文本编辑器打开它。\n找到以下内容：\n\u0026lt;permission name=\u0026#34;android.permission.WRITE_EXTERNAL_STORAGE\u0026#34;\u0026gt; \u0026lt;group gid=\u0026#34;sdcard_r\u0026#34; /\u0026gt; \u0026lt;group gid=\u0026#34;sdcard_rw\u0026#34; /\u0026gt; \u0026lt;/permission\u0026gt; 在 \u0026lt;permission name=\u0026quot;android.permission.WRITE_EXTERNAL_STORAGE\u0026quot;\u0026gt; 标签内部，添加一行：\n\u0026lt;group gid=\u0026#34;media_rw\u0026#34; /\u0026gt; 修改后的内容应为：\n\u0026lt;permission name=\u0026#34;android.permission.WRITE_EXTERNAL_STORAGE\u0026#34;\u0026gt; \u0026lt;group gid=\u0026#34;sdcard_r\u0026#34; /\u0026gt; \u0026lt;group gid=\u0026#34;sdcard_rw\u0026#34; /\u0026gt; \u0026lt;group gid=\u0026#34;media_rw\u0026#34; /\u0026gt; \u0026lt;/permission\u0026gt; 保存并关闭文件。\n重启手机。\n成功效果 # 完成以上操作后，第三方应用将获得对 SD 卡 的完整写入权限，可以自由读写外置存储设备的数据。\n","date":"27 April 2025","externalUrl":null,"permalink":"/posts/2025/%E5%AE%89%E5%8D%936.0%E8%AE%BE%E7%BD%AEsd%E5%8D%A1%E8%AF%BB%E5%86%99%E6%9D%83%E9%99%90/","section":"帖子总览","summary":"","title":"安卓6.0设置sd卡读写权限","type":"posts"},{"content":"","date":"28 November 2024","externalUrl":null,"permalink":"/tags/shell/","section":"标签总览","summary":"","title":"Shell","type":"tags"},{"content":"","date":"28 November 2024","externalUrl":null,"permalink":"/categories/%E5%B7%A5%E5%85%B7%E8%84%9A%E6%9C%AC/","section":"类别总览","summary":"","title":"工具脚本","type":"categories"},{"content":" lnxrouter # /home/light/Scripts/lnxrouter\nroute.sh # /home/light/Scripts/route.sh\n#!/bin/bash sudo /home/light/Scripts/lnxrouter -i enp2s0 -g 0 --daemon route.service # /etc/systemd/system/route.service\n[Unit] Description=run lnxrouter Before=getty@tty1.service [Service] Type=forking ExecStart=/home/light/Scripts/route.sh StandardOutput=tty StandardError=tty [Install] WantedBy=getty.target getty@tty1.service # /etc/systemd/system/getty.target.wants/getty@tty1.service\n# SPDX-License-Identifier: LGPL-2.1-or-later # # This file is part of systemd. # # systemd is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. [Unit] Description=Getty on %I Documentation=man:agetty(8) man:systemd-getty-generator(8) Documentation=https://0pointer.de/blog/projects/serial-console.html After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target After=rc-local.service # If additional gettys are spawned during boot then we should make # sure that this is synchronized before getty.target, even though # getty.target didn\u0026#39;t actually pull it in. Before=getty.target IgnoreOnIsolate=yes # IgnoreOnIsolate causes issues with sulogin, if someone isolates # rescue.target or starts rescue.service from multi-user.target or # graphical.target. Conflicts=rescue.service Before=rescue.service # On systems without virtual consoles, don\u0026#39;t start any getty. Note # that serial gettys are covered by serial-getty@.service, not this # unit. ConditionPathExists=/dev/tty0 [Service] # the VT is cleared by TTYVTDisallocate # The \u0026#39;-o\u0026#39; option value tells agetty to replace \u0026#39;login\u0026#39; arguments with an # option to preserve environment (-p), followed by \u0026#39;--\u0026#39; for safety, and then # the entered username. ExecStart=-/sbin/agetty -o \u0026#39;-p -f -- \\\\u\u0026#39; --noclear --autologin light - $TERM Type=idle Restart=always RestartSec=0 UtmpIdentifier=%I StandardInput=tty StandardOutput=tty TTYPath=/dev/%I TTYReset=yes TTYVHangup=yes TTYVTDisallocate=yes IgnoreSIGPIPE=no SendSIGHUP=yes # Unset locale for the console getty since the console has problems # displaying some internationalized messages. UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION [Install] WantedBy=getty.target DefaultInstance=tty1 ","date":"28 November 2024","externalUrl":null,"permalink":"/posts/2024/%E6%88%91%E7%9A%84%E6%97%A0%E7%BA%BF%E8%BD%AC%E6%9C%89%E7%BA%BFlinux%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%94%A8%E5%88%B0%E7%9A%84%E8%84%9A%E6%9C%AC%E4%BB%AC/","section":"帖子总览","summary":"","title":"我的无线转有线linux服务器用到的脚本们","type":"posts"},{"content":" cp 和 mv 命令的行为总结表 # 假设以下路径设置：\n源路径：/nihao 或 /nihao/ /nihao 包含文件和子目录：file1, dir1/, file2 目标路径：/nima 或 /nima/ 表格 # cp 命令行为 # 命令 目标路径存在？ 最终路径结构 cp -r /nihao /nima/ 是 /nima/nihao/ cp -r /nihao/ /nima/ 是 /nima/file1, /nima/dir1/, /nima/file2 cp -r /nihao /nima 是（目录） /nima/nihao/ cp -r /nihao /nima 否（文件/不存在） /nima/（重命名为 /nima） cp -r /nihao/ /nima 是（目录） 同第一条 cp -r /nihao/ /nima 否（文件/不存在） /nima/file1, /nima/dir1/, /nima/file2 mv 命令行为 # 命令 目标路径存在？ 最终路径结构 mv /nihao /nima/ 是 /nima/nihao/ mv /nihao/ /nima/ 是 /nima/nihao/ mv /nihao /nima 是（目录） /nima/nihao/ mv /nihao /nima 否（文件/不存在） /nima/（重命名为 /nima） mv /nihao/ /nima 是（目录） /nima/nihao/ mv /nihao/ /nima 否（文件/不存在） /nima/（重命名为 /nima） ","date":"15 November 2024","externalUrl":null,"permalink":"/posts/2024/linux-cp%E5%92%8Cmv%E5%91%BD%E4%BB%A4-%E5%AF%B9%E4%BA%8E%E7%9B%AE%E5%BD%95%E5%A4%8D%E5%88%B6%E5%88%B0%E7%9B%AE%E5%BD%95%E7%9A%84%E6%83%85%E5%86%B5-%E7%9A%84-%E6%89%80%E6%9C%89%E6%83%85%E5%86%B5%E7%A4%BA%E4%BE%8B/","section":"帖子总览","summary":"","title":"Linux cp和mv命令 对于目录复制到目录的情况 的 所有情况示例","type":"posts"},{"content":"","date":"15 November 2024","externalUrl":null,"permalink":"/categories/%E6%A6%82%E5%BF%B5%E6%95%B4%E7%90%86/","section":"类别总览","summary":"","title":"概念整理","type":"categories"},{"content":"","date":"11 November 2024","externalUrl":null,"permalink":"/tags/nginx/","section":"标签总览","summary":"","title":"Nginx","type":"tags"},{"content":"","date":"11 November 2024","externalUrl":null,"permalink":"/tags/openresty/","section":"标签总览","summary":"","title":"Openresty","type":"tags"},{"content":" 安装resty.upload模块\nopm install ledgetech/lua-resty-upload 新建/usr/local/openresty/nginx/lua/upload.lua\n内容如下：\nlocal upload = require \u0026#34;resty.upload\u0026#34; local cjson = require \u0026#34;cjson.safe\u0026#34; local chunk_size = 4096 local form, err = upload:new(chunk_size) if not form then ngx.log(ngx.ERR, \u0026#34;failed to create upload: \u0026#34;, err) ngx.exit(500) end local file local file_name = \u0026#34;\u0026#34; local success = false -- 获取 URL 参数中的子文件夹路径（支持嵌套） local folder = ngx.var.arg_folder if not folder or folder == \u0026#34;\u0026#34; then folder = \u0026#34;\u0026#34; -- 如果未提供 folder 参数，使用默认子文件夹 end -- 构建文件存储的完整路径 local file_path = \u0026#34;/home/ubuntu/share/\u0026#34; .. folder .. \u0026#34;/\u0026#34; -- 递归创建嵌套目录（确保子文件夹路径存在） local mkdir_command = \u0026#34;mkdir -p \u0026#34; .. file_path os.execute(mkdir_command) form:set_timeout(1000) -- 设置超时时间 while true do local typ, res, err = form:read() if not typ then ngx.say(cjson.encode({success = false, msg = \u0026#34;failed to read: \u0026#34; .. err})) return end if typ == \u0026#34;header\u0026#34; then if res[1] == \u0026#34;Content-Disposition\u0026#34; then file_name = res[2]:match(\u0026#39;filename=\u0026#34;([^\u0026#34;]+)\u0026#34;\u0026#39;) if file_name then file = io.open(file_path .. file_name, \u0026#34;w+\u0026#34;) if not file then ngx.say(cjson.encode({success = false, msg = \u0026#34;failed to open file\u0026#34;})) return end end end elseif typ == \u0026#34;body\u0026#34; then if file then file:write(res) end elseif typ == \u0026#34;part_end\u0026#34; then if file then file:close() file = nil success = true end elseif typ == \u0026#34;eof\u0026#34; then break end end ngx.say(cjson.encode({success = success, file = file_name, path = file_path})) 修改/usr/local/openresty/nginx/conf/nginx.conf\nuser ubuntu; events {} http { server { listen 80; server_name localhost; # 文件上传接口 location /upload/ { content_by_lua_file /usr/local/openresty/nginx/lua/upload.lua; } # 文件下载接口 location /download/ { alias /home/ubuntu/share/; autoindex on; # 开启目录浏览 autoindex_exact_size off; # 使用MB, GB作为文件大小单位 autoindex_localtime on; # 显示本机 } } } 设置/home/ubuntu/share目录权限\nsudo chown -R www-data:www-data /home/ubuntu/share sudo chmod -R 755 /home/ubuntu/share 修改nginx运行时用户为目录owner 在nginx.conf中首行写入：\nuser 用户名 用户组; # 这里的用户名和组就是文件夹的 可以通过命令：\nps -aux | grep nginx 查看nginx worker用户。\n","date":"11 November 2024","externalUrl":null,"permalink":"/posts/2024/openresty%E9%85%8D%E7%BD%AE%E4%B8%BA%E7%AE%80%E5%8D%95%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E4%B8%8B%E8%BD%BD%E6%9C%8D%E5%8A%A1%E5%99%A8/","section":"帖子总览","summary":"","title":"openresty配置为简单文件上传下载服务器","type":"posts"},{"content":"","date":"11 November 2024","externalUrl":null,"permalink":"/tags/server/","section":"标签总览","summary":"","title":"Server","type":"tags"},{"content":" 在安装pve的时候，系统默认分配给local的空间非常小，我们可以通过以下方法把local-lvm删除，并将其空间还给local。\n注意！\n在webui的pve节点的磁盘选项中找到LVM-Thin，删除data卷。\n删除后此处为空。\n接着打开终端执行以下命令：\nlvresize --extents +100%FREE --resizefs pve/root 此命令会将剩余空间全部分配给local。\n执行完后也可以继续执行下述命令以重新4k对齐。\nresize2fs /dev/mapper/pve-root 调整后的空间：\n点击数据中心-存储，编辑local，把所有内容都勾选。\n","date":"18 October 2024","externalUrl":null,"permalink":"/posts/2024/pve%E5%AE%89%E8%A3%85%E5%90%8E%E5%88%A0%E9%99%A4local-lvm%E5%B9%B6%E6%8A%8A%E5%85%B6%E7%A9%BA%E9%97%B4%E5%85%A8%E9%83%A8%E5%88%86%E7%BB%99local/","section":"帖子总览","summary":"","title":"pve安装后删除local-lvm并把其空间全部分给local","type":"posts"},{"content":"","date":"10 October 2024","externalUrl":null,"permalink":"/tags/electron/","section":"标签总览","summary":"","title":"Electron","type":"tags"},{"content":"","date":"10 October 2024","externalUrl":null,"permalink":"/tags/javascript/","section":"标签总览","summary":"","title":"Javascript","type":"tags"},{"content":"","date":"10 October 2024","externalUrl":null,"permalink":"/tags/node.js/","section":"标签总览","summary":"","title":"Node.js","type":"tags"},{"content":" js文件中执行的exec命令出错 # 很可能是项目中使用了一些非html, css, js的源文件，比如用了Makefile来编译了cpp代码，或者执行的exec命令为cp dir/something.cpp之类的文件操作命令。\n可以使用修改forge.config.js文件配置的方式，使得npm run make的时候自动把Makefile等exec命令中用到的文件和目录复制到打包后的根目录中。\n具体来说，可以给forge.config.js文件的module.exports/packagerConfig下新增如下配置：\nafterExtract: [ (extractPath, electronVersion, platform, arch, done) =\u0026gt; { // Copy the Makefile to the build directory var makefile = path.join(__dirname, \u0026#39;Makefile\u0026#39;); var backup = path.join(__dirname, \u0026#39;backup\u0026#39;); var dest = extractPath; fs.copyFileSync(makefile, path.join(dest, \u0026#39;Makefile\u0026#39;)); // 把backup目录递归地复制到dest目录下面 copyDir(backup, path.join(dest, \u0026#39;backup\u0026#39;)); done(); } ] 其中，copyDir函数的定义为：\nfunction copyDir(src, dest) { fs.mkdirSync(dest, { recursive: true }); // 创建目标目录 // 递归遍历源目录 fs.readdirSync(src).forEach(file =\u0026gt; { const srcPath = path.join(src, file); const destPath = path.join(dest, file); // 判断是文件还是目录 const stat = fs.statSync(srcPath); if (stat.isDirectory()) { copyDir(srcPath, destPath); // 递归复制子目录 } else { fs.copyFileSync(srcPath, destPath); // 复制文件 } }); } 渲染进程中执行的node.js api出错 # 可能是打包后的浏览器内核安全设置更高？\n建议electron项目中还是尽量前后端分离比较好，在主进程中用node.js的库，即各种require，在渲染进程（浏览器进程）中就用web页面的库，即各种import和export。\n我把renderer.js中的fs模块的操作都删了，把调用的fs模块函数都转移到了main.js里面去执行，这个错误就消失了。\n比如原本在renderer.js中是这样：\ndocument.getElementById(\u0026#39;run-compare\u0026#39;).addEventListener(\u0026#39;click\u0026#39;, () =\u0026gt; { // 获取code1和code2的内容 var code1Content = editor1.state.doc.toString(); var code2Content = editor2.state.doc.toString(); // 获取项目根目录 rootPath = __dirname; // 创建文件路径 const filePath1 = path.join(rootPath, \u0026#39;code1.cpp\u0026#39;); const filePath2 = path.join(rootPath, \u0026#39;code2.cpp\u0026#39;); // 写入文件 fs.writeFile(filePath1, code1Content, (err) =\u0026gt; { if (err) { console.error(err); return; } console.log(\u0026#39;code1写入成功\u0026#39;); }); fs.writeFile(filePath2, code2Content, (err) =\u0026gt; { if (err) { console.error(err); return; } console.log(\u0026#39;code2写入成功\u0026#39;); }); ipcRenderer.send(\u0026#39;run-compare\u0026#39;); }); 现在我就改成了这样：\ndocument.getElementById(\u0026#39;run-compare\u0026#39;).addEventListener(\u0026#39;click\u0026#39;, () =\u0026gt; { // 获取code1和code2的内容 var code1Content = editor1.state.doc.toString(); var code2Content = editor2.state.doc.toString(); // 获取项目根目录 rootPath = __dirname; // 创建文件路径 const filePath1 = path.join(rootPath, \u0026#39;code1.cpp\u0026#39;); const filePath2 = path.join(rootPath, \u0026#39;code2.cpp\u0026#39;); // 发送消息给主进程 ipcRenderer.send(\u0026#39;compare-codes\u0026#39;, { code1: code1Content, code2: code2Content }); ipcRenderer.send(\u0026#39;run-compare\u0026#39;); }); 然后在main.js中新增对compare-codes事件的监听：\nipcMain.on(\u0026#39;compare-codes\u0026#39;, (event, data) =\u0026gt; { const { code1, code2 } = data; fs.writeFileSync(\u0026#39;code1.cpp\u0026#39;, code1); fs.writeFileSync(\u0026#39;code2.cpp\u0026#39;, code2); }); 总之，以后用node.js写项目，最好还是要把前后端分离，不能在被某个html文件引用的js脚本中调用node.js中的库。\n","date":"10 October 2024","externalUrl":null,"permalink":"/posts/2024/%E8%BF%90%E8%A1%8C%E4%BD%BF%E7%94%A8electron-forge%E6%89%93%E5%8C%85%E7%9A%84electron-package%E6%97%B6%E9%81%87%E5%88%B0%E5%9C%A8js%E6%96%87%E4%BB%B6%E4%B8%AD%E6%89%A7%E8%A1%8C%E7%9A%84exec%E5%91%BD%E4%BB%A4%E5%92%8C%E5%9C%A8%E6%B8%B2%E6%9F%93%E8%BF%9B%E7%A8%8B%E4%B8%AD%E6%89%A7%E8%A1%8C%E7%9A%84node.js-api%E5%87%BA%E7%8E%B0%E5%A5%87%E6%80%AA%E9%97%AE%E9%A2%98%E7%9A%84%E8%A7%A3%E5%86%B3%E6%80%9D%E8%B7%AF/","section":"帖子总览","summary":"","title":"运行使用Electron-forge打包的electron package时遇到在js文件中执行的exec命令和在渲染进程中执行的node.js api出现奇怪问题的解决思路","type":"posts"},{"content":"","date":"10 October 2024","externalUrl":null,"permalink":"/tags/systemd/","section":"标签总览","summary":"","title":"Systemd","type":"tags"},{"content":" 开机登录前自启动脚本服务 # 首先确定你的系统是否使用systemd来管理系统服务，在shell中输入systemctl命令来判断，有输出则为systemd系统。\n进入/etc/systemd/system目录，创建myservice.service，其中myservice是你要自定义的服务名。\n编辑myservice.service文件，修改其内容为：\n[Unit] Description=my service unit # 填写你的service描述 After=docker.service # 表示本服务依赖于 docker.service，即 docker.service 必须先启动，本服务才能启动。 Before=getty@tty1.service # 表示本服务必须在 getty@tty1.service 启动之前启动，即在登录界面显示之前启动 [Service] Type=forking # \u0026#34;forking\u0026#34;表示脚本执行后父进程退出，子进程在后台运行，适合daemon脚本 # \u0026#34;simple\u0026#34;表示普通脚本 # \u0026#34;oneshot\u0026#34;表示脚本执行后会立即退出，适合纯命令类型的脚本 ExecStart=/path/to/your_sript # 你要执行的脚本的路径 # User=root # 如果你想要脚本在登录后再执行，可以加上这一项 StandardOutput=tty # 在tty中打印脚本的标准输出 StandardError=tty # 在tty中打印脚本的error信息 [Install] WantedBy=getty.target # 表示本服务希望被 getty.target 所依赖。getty.target 是一个目标单元，表示系统进入多用户状态 接着，执行\nsudo systemctl enable myservice.service 重启后，即可生效配置。\n或者\nsudo sytemctl daemon-reload \u0026amp;\u0026amp; sudo systemctl enable myservice.service --now 以立即使配置生效。\n自动登录脚本 # 修改/etc/systemd/system/getty.target.wants/getty@tty1.service或者 /etc/systemd/system/getty@tty1.service.d/autologin.conf\n更改ExecStart=...这一行为：\nExecStart=-/sbin/agetty -o \u0026#39;-p -f -- \\\\u\u0026#39; --noclear --autologin username - $TERM 即新增-f --autologin username这三个参数，注意这三个参数的位置，-f参数在-o ''内部。 -/sbin/agetty：指定 agetty 命令的路径。这里的 - 表示使用绝对路径，确保命令被正确找到。 -o \u0026lsquo;-p -f \u0026ndash; \\u\u0026rsquo;：设置 agetty 的选项。 -p：在终端显示提示符。 -f：强制终端进入原始模式。 \u0026ndash;：表示命令行选项结束。 \\u：显示用户名。 \u0026ndash;noclear：在登录后不清除屏幕，保留之前的输出。 \u0026ndash;autologin username：自动登录指定用户 username。 -$TERM：使用当前终端类型的设置。 重启以使得修改生效。\n为什么已经创建了登录前自启动任务还要设置自动登录？ # 实测如果不设置自动登录，虽然脚本会显示成功运行，但是实际上似乎有问题，猜测可能是有部分网络相关的服务会在登录后才会启用。\n","date":"10 October 2024","externalUrl":null,"permalink":"/posts/2024/%E4%BD%BF%E7%94%A8systemd%E5%88%9B%E5%BB%BA%E5%BC%80%E6%9C%BA%E7%99%BB%E5%BD%95%E5%89%8D%E8%87%AA%E5%90%AF%E5%8A%A8%E8%84%9A%E6%9C%AC%E6%9C%8D%E5%8A%A1%E5%B9%B6%E8%87%AA%E5%8A%A8%E7%99%BB%E5%BD%95/","section":"帖子总览","summary":"","title":"使用Systemd创建开机登录前自启动脚本服务并自动登录","type":"posts"},{"content":"","date":"1 October 2024","externalUrl":null,"permalink":"/tags/wsl/","section":"标签总览","summary":"","title":"Wsl","type":"tags"},{"content":" 如果已经开启了Hypr-V, Windows虚拟机监控程序平台, 适用于Linux的Windows子系统, 虚拟机平台这四个Windows功能还是没能解决的话，大概率是bios中cpu虚拟化功能开启有问题或者Vmware等其他虚拟机程序与WSL产生了冲突。\n其中前者可以在任务管理器中看到：\n如果bios的cpu虚拟化功能没问题的话，那么原因大概率就是后者。\n以管理员权限运行终端，并执行以下命令即可：\nbcdedit /set hypervisorlaunchtype auto ","date":"1 October 2024","externalUrl":null,"permalink":"/posts/2024/%E5%AE%89%E8%A3%85wsl%E6%8A%A5%E9%94%990x80370102%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/","section":"帖子总览","summary":"","title":"安装WSL报错0x80370102解决方案","type":"posts"},{"content":"","date":"27 September 2024","externalUrl":null,"permalink":"/tags/codeforces/","section":"标签总览","summary":"","title":"Codeforces","type":"tags"},{"content":" E. Prefix GCD # 假设我们从一个空集合\\(b\\)开始，不断从\\(a\\)数组中选择一个元素添加到\\(b\\)集合的尾部，当把\\(a\\)数组的元素全部添加到\\(b\\)中后，得到的\\(b\\)即为所求的rearrange后的\\(a\\)。\n结论1： # 每次选择使得其和当前\\(b\\)中所有元素的最大公约数最小的那个\\(a_i\\)加入到\\(b\\)的末尾，即选择\\(a_i\\)，使得\\(gcd(b_1, b_2, ..., b_k, a_i)\\)最小（假设当前\\(b\\)的大小为\\(k\\)），这样得到的\\(b\\)是最优的。\n证明1： # 假设当前的\\(b\\)集合为： \\(b_1, b_2, ..., b_k\\)，此\\(b\\)集合即为正确答案的\\(b\\)的前\\(k\\)个元素。 使得\\(gcd(b_1, b_2, ..., b_k, a_i)\\)取得最小值的a_i是\\(A\\)。 而正确方案中这个步骤应该选择的\\(a_i\\)是\\(B\\)。那么有\\(gcd(b_1, b_2, ..., b_k, A) \u003c= gcd(b_1, b_2, ..., b_k, B)\\)。\n结论2： # 可以把正确方案的\\(b = \\{b_1, b_2, ..., b_k, B\\}\\)① 替换成： \\(b = \\{b_1, b_2, ..., b_k, A, B\\}\\)，这样结果不会变坏。②\n证明2： # 设\\(f = \\gcd(a_1) + \\gcd(a_1, a_2) + \\ldots + \\gcd(a_1, a_2, \\ldots, a_n)\\)， 则①的\\(f\\)为\\(gcd(b_1) + gcd(b_1, b_2) + ... + gcd(b_1, b_2, ..., b_k) + gcd(b_1, b_2, ..., b_k, B)\\) ②的\\(f\\)为\\(gcd(b_1) + gcd(b_1, b_2) + ..., + gcd(b_1, b_2, ..., b_k) + gcd(b_1, b_2, ..., b_k, A) + gcd(b_1, b_2, ..., A, B)\\)\n二者不同的部分满足\\(gcd(b_1, b_2, ..., b_k, B) \u003e= gcd(b_1, b_2, ..., b_k, A) + gcd(b_1, b_2, ..., b_k, A, B)\\)， 因为\\(gcd(b_1, b_2, ..., b_k, A) + gcd(b_1, b_2, ..., b_k, A, B) = gcd(b_1, b_2, ..., b_k, A) + gcd(b_1, b_2, ..., b_k, b_1, b_2, ..., b_k, A, B) = gcd(b_1, b_2, ..., b_k, A) + gcd(gcd(b_1, b_2, ..., b_k, A), gcd(b_1, b_2, ..., b_k, B)) \u003c= gcd(b_1, b_2, ..., b_k, B)\\)。\n这用到了两个点：\n\\(gcd(X, Y) \u003c= |X - Y|\\) \\(gcd(b_1, b_2, ..., b_k, A) \u003c= gcd(b_1, b_2, ..., b_k, B)\\) 因此每次选择使得其和当前\\(b\\)中所有元素的最大公约数最小的那个\\(a_i\\)加入到\\(b\\)的末尾满足题意。\n优化 # 设\\(g = gcd(a_1, a_2, ..., a_n)\\) 我们可以在开始计算前，将每个\\(a_i\\)除以\\(g\\)，因为这样处理后，\\(gcd(a_1, a_2, ..., a_n) = 1\\)，这意味着，我们选择一部分\\(a_i\\)到\\(b\\)中后，就可以使得\\(gcd(b_1, b_2, ..., b_k)\\)变为\\(1\\)， 事实上，我们每次选择一个\\(a_i\\)都会使得\\(gcd(b)\\)下降，因为如果我们某次选择的\\(a_i\\)没有使得\\(gcd(b)\\)下降，则说明\\(gcd(b)\\)已经下降到了\\(1\\)，可以退出迭代了。 最后我们再把结果乘上\\(g\\)。\n如果不进行这一步操作，则很有可能\\(gcd(b)\\)永远都到不了1。这样的话我们代码中判断到\\(1\\)的部分应该改为\\(g\\)。\n时间复杂度 # 因为每个数最多有\\(log_2{x}\\)个可能相同的质因数，因此只需要\\(log_2{10^5}\\)次添加就可以使得\\(gcd(b) = 1\\)。\nint n; int a[N]; void solve() { cin \u0026gt;\u0026gt; n; int gcd_all = 0; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; gcd_all = gcd(gcd_all, a[i]); } for (int i = 1; i \u0026lt;= n; i ++) { a[i] /= gcd_all; } int cur = 0, cnt = 0; int ans = 0; for (int i = 1; i \u0026lt;= n; i ++) { int min_v = INF; for (int j = 1; j \u0026lt;= n; j ++) { min_v = min(min_v, gcd(cur, a[j])); } cur = min_v; cnt ++; ans += cur; if (cur == 1) { ans += n - cnt; break; } } ans *= gcd_all; cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } ","date":"27 September 2024","externalUrl":null,"permalink":"/posts/2024/codeforces-round-973%E9%A2%98%E8%A7%A3e/","section":"帖子总览","summary":"","title":"Codeforces Round 973题解（E）","type":"posts"},{"content":"","date":"27 September 2024","externalUrl":null,"permalink":"/tags/gcd/","section":"标签总览","summary":"","title":"Gcd","type":"tags"},{"content":"","date":"27 September 2024","externalUrl":null,"permalink":"/categories/%E7%AE%97%E6%B3%95%E7%AB%9E%E8%B5%9B/","section":"类别总览","summary":"","title":"算法竞赛","type":"categories"},{"content":"","date":"27 September 2024","externalUrl":null,"permalink":"/tags/%E8%B4%AA%E5%BF%83/","section":"标签总览","summary":"","title":"贪心","type":"tags"},{"content":"","date":"15 September 2024","externalUrl":null,"permalink":"/tags/arch-linux/","section":"标签总览","summary":"","title":"Arch Linux","type":"tags"},{"content":"","date":"15 September 2024","externalUrl":null,"permalink":"/tags/thinkpad/","section":"标签总览","summary":"","title":"Thinkpad","type":"tags"},{"content":" 在/etc/default/grub中给GRUB_CMDLINE_LINUX_DEFAULT 添加参数：\nacpi_osi=\u0026#39;!Windows 2012\u0026#39; 然后使用命令重新生成grub配置：\ngrub-mkconfig -o /boot/grub/grub.cfg 重启即可\n","date":"15 September 2024","externalUrl":null,"permalink":"/posts/2024/thinkpad-x230%E5%AE%89%E8%A3%85arch-linux%E4%BF%AE%E5%A4%8D%E9%94%AE%E7%9B%98%E6%97%A0%E6%B3%95%E8%B0%83%E8%8A%82%E5%B1%8F%E5%B9%95%E4%BA%AE%E5%BA%A6/","section":"帖子总览","summary":"","title":"Thinkpad X230安装Arch Linux修复键盘无法调节屏幕亮度","type":"posts"},{"content":"","date":"14 September 2024","externalUrl":null,"permalink":"/tags/alsa/","section":"标签总览","summary":"","title":"Alsa","type":"tags"},{"content":"很有可能你同时装了Hyprland和KDE plasma两种桌面环境，其中一个用pipewire作为音频管理器，另一个用pulseaudio作为音频管理器，这两者会发生冲突，导致奇怪的音频问题出现。\n解决方案： 安装pipewire-pulse以替换掉pulseaudio, 解决音频管理器的冲突即可。\n","date":"14 September 2024","externalUrl":null,"permalink":"/posts/2024/go-musicfox%E6%89%93%E5%BC%80%E6%8A%A5%E9%94%99alsa-lib-pcm_dmix.c_1000_snd_pcm_dmix_open-unable-to-open-slave-%E4%B8%94%E5%A3%B0%E9%9F%B3%E4%BC%9A%E5%87%BA%E7%8E%B0%E8%8E%AB%E5%90%8D%E5%85%B6%E5%A6%99%E7%9A%84%E5%8D%A1%E9%A1%BF%E5%A3%B0%E9%9F%B3%E8%A1%8C%E4%B8%BA%E5%A5%87%E6%80%AA%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/","section":"帖子总览","summary":"","title":"go-musicfox打开报错ALSA lib pcm_dmix.c_1000_(snd_pcm_dmix_open) unable to open slave, 且声音会出现莫名其妙的卡顿、声音行为奇怪的解决方案","type":"posts"},{"content":"","date":"14 September 2024","externalUrl":null,"permalink":"/tags/pipewire/","section":"标签总览","summary":"","title":"Pipewire","type":"tags"},{"content":"","date":"14 September 2024","externalUrl":null,"permalink":"/tags/pipewire-pulse/","section":"标签总览","summary":"","title":"Pipewire-Pulse","type":"tags"},{"content":"","date":"14 September 2024","externalUrl":null,"permalink":"/tags/pulseaudio/","section":"标签总览","summary":"","title":"Pulseaudio","type":"tags"},{"content":" 本指南将介绍如何不借用archinstall脚本来安装纯命令行界面的ArchLinux到64位系统上。（UEFI+GPT）\n零、安装前准备 # 首先当然是先进入liveiso环境。\n增大字号：\nsetfont ter-132n 测试网络连接是否顺畅：\nping archlinux.org -c 5 验证系统是否在UEFI模式下启动\nls /sys/firmware/efi/efivars/ 如果输出了一大堆内容就代表是UEFI模式\n一、时间与键盘布局 # 系统时间配置 # 查看系统时间配置：\ntimedatectl status 更改时区设置：\ntimedatectl set-timezone Asia/Shanghai 设置同步服务器：\ntimedatectl set-ntp true 键盘布局 # 载入键盘布局\nloadkeys /usr/share/kbd/keymaps/i386/qwerty/us.map.gz 二、硬盘分区与挂载 # 查看所有硬盘分区及挂载点\nlsblk 硬盘分区 # 以系统只有单硬盘sda为例\ncfdisk /dev/sda 安装ArchLinux一般分三个区分别给/，/boot，和swap分区使用。\nArchWiki建议的一种分区布局：\nMount point on the installed system Partition Partition type Suggested size /boot /dev/sda1 :EFI system partition 1GiB [SWAP] /dev/sda2 :Linux swap The size of RAM to use hibernation / /dev/sda3 :Linux x86-64 root(/) At least 23-32GiB 安装时不一定要按照这个布局来，也可以给/home（家目录，存放文件），/var（主要存放pacman的下载缓存和一些变量variable），/opt（一些大型软件的默认下载目录和自定义的软件下载目录optional）分配到其他分区，分区类型都设置为:Linux filesystem即可。\n\u0026#x26a0;\u0026#xfe0f; 最好不要将/etc和/usr挂到和/不同的分区上，这两个路径和系统高度绑定，挂到不同的分区上会无法在系统启动的时候自动挂载，虽然有解决方法，但是比较麻烦且容易引发错误。\n路径挂载 # 以上面的布局为例\n建立文件系统（格式化）： # mkfs.ext4 /dev/sda3 mkswap /dev/sda2 swapon /dev/sda2 mkfs.fat -F 32 /dev/sda1 挂载分区 # mount /dev/sda3 /mnt mount /dev/sda1 /mnt/boot 三、安装ArchLinux本体 # 更换国内软件仓库镜像源 # vim /etc/pacman.d/mirrorlist 镜像源地址：\nServer = https://mirrors.ustc.edu.cn/archlinux/\\(repo/os/\\)arch # 中国科学技术大学开源镜像站 Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/\\(repo/os/\\)arch # 清华大学开源软件镜像站 Server = https://repo.huaweicloud.com/archlinux/\\(repo/os/\\)arch # 华为开源镜像站 Server = http://mirror.lzu.edu.cn/archlinux/\\(repo/os/\\)arch # 兰州大学开源镜像站 安装ArchLinux到/mnt # pacstrap -i /mnt base base-devel linux linux-lts linux-headers linux-firmware intel-ucode [amd-ucode](amd的cpu) sudo nano vim git neofetch networkmanager dhcpcd pulseaudio [bluez](蓝牙模块) [wpa_supplicant](wlan) 生成文件系统表（FSTAB） # 目前根目录被挂载到了/mnt, 但是当我们开机从主驱动器启动arch时，我们需要告诉系统将所有这些分区挂载到同一位置\ngenfstab -U /mnt \u0026gt;\u0026gt; /mnt/etc/fstab 四、系统配置 # 进入安装好的ArchLinux的根目录\narch-chroot /mnt 设置账户和密码 # 设置root用户密码\npasswd 添加新用户 # useradd -m light passwd light 为新用户添加root权限 # usermod -aG wheel,storage,power light 通过sudo执行root权限\nvisudo 将文件这一行：\n# %wheel ALL=(ALL) ALL 取消注释，并在其下面一行添加：\nDefaults timestamp_timeout=0 设置系统语言 # vim /etc/locale.gen 把需要的语言取消注释\n生成语言locale\nlocale-gen 生成locale配置：\necho LANG=en_US.UTF-8 \u0026gt; /etc/locale.conf 在当前终端环境使用系统语言：\nexport LANG=en_US.UTF-8 设置主机名 # echo ArchLinux \u0026gt; /etc/hostname 修改hosts文件内容\nvim /etc/hosts 增加新内容：\n127.0.0.1\tlocalhost ::1\tlocalhost 127.0.0.1\tArchLinux.localdomain\tlocalhost 设置时区 # 链接localtime\nln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 同步时钟 # hwclock --systohc 五、安装Grub # /dev/sda1是efi分区，grub将会被安装到这里。\n安装grub及引导相关软件包：\npacman -S grub efibootmgr dosfstools mtools grub-install # grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=grub_uefi --recheck grub-mkconfig # grub-mkconfig -o /boot/grub/grub.cfg 六、启动服务 # 启动网络服务 # systemctl enable dhcpcd.service systemctl enable NetworkManager.service 七、退出 # 回到liveiso：\nexit 卸载所有分区：\numount -lR /mnt 重启并取出u盘\nreboot ","date":"12 September 2024","externalUrl":null,"permalink":"/posts/2024/archlinux%E5%AE%89%E8%A3%85%E7%AE%80%E6%98%8E%E6%8C%87%E5%8D%97/","section":"帖子总览","summary":"","title":"ArchLinux安装简明指南","type":"posts"},{"content":" sudo pacman-key --refresh-keys # enable ntp and ensure the time correct timedatectl set-ntp 1 timedatectl status rm -fr /etc/pacman.d/gnupg # create pacman master key pacman-key --init # reload keys from keyring resources pacman-key --populate sudo rm /var/cache/pacman/pkg -r pacman -Sy archlinux-keyring \u0026amp;\u0026amp; pacman -Syu ","date":"4 September 2024","externalUrl":null,"permalink":"/posts/2024/archlinux%E9%95%BF%E6%97%B6%E9%97%B4%E4%B8%8D%E6%9B%B4%E6%96%B0%E6%B0%B8%E8%BF%9C%E4%BC%9A%E6%9C%89%E7%9A%84unknown-trust%E6%8A%A5%E9%94%99%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95%E6%B1%87%E6%80%BB/","section":"帖子总览","summary":"","title":"ArchLinux长时间不更新永远会有的unknown trust报错解决方法汇总","type":"posts"},{"content":"","date":"28 July 2024","externalUrl":null,"permalink":"/tags/powershell/","section":"标签总览","summary":"","title":"Powershell","type":"tags"},{"content":" Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass ","date":"28 July 2024","externalUrl":null,"permalink":"/posts/2024/ps1-is-not-digitally-signed.-you-cannot-run-this-script%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/","section":"帖子总览","summary":"","title":"ps1 is not digitally signed. You cannot run this script解决方案","type":"posts"},{"content":" 目的 # 把当前文件夹下的这些文件 重命名为 代码 # Get-ChildItem -Path . -Filter \u0026#34;*.mkv\u0026#34; | ForEach-Object { if ($_.Name -ne \u0026#34;rename\u0026#34;) { \\(fileName = \\)_.Name -replace \u0026#34;\\.[^.]+$\u0026#34;, \u0026#34;\u0026#34; # ?表示非贪婪模式 \\(extension = \\)_.Name -replace \u0026#39;^.+\\.\u0026#39;, \u0026#39;\u0026#39; # (\\d+\\.?\\d*) 匹配数字，包括小数点 # (\\(.*?\\))? 匹配括号内的内容 $pattern = \u0026#39;\\[(\\d+\\.?\\d*)(\\(.*?\\))?\\]\u0026#39; if (\\(fileName -match \\)pattern) { # \\(s = [regex]::Match(\\)fileName, \u0026#39;\\[(.*?)\\]\u0026#39;).Value \\(match = \\)matches[1] # matches哈希表仅包含任何匹配模式的第一个匹配项 # matches[1]即第一个()中的内容 \\(newName = \u0026#34;S01E\u0026#34; + \\)match Rename-Item -LiteralPath \\(_.Name -NewName \u0026#34;\\)newName.$extension\u0026#34; Write-Output \u0026#34;\\(newName.\\)extension\u0026#34; } else { Write-Output \u0026#34;No match found\u0026#34; } } } ","date":"18 July 2024","externalUrl":null,"permalink":"/posts/2024/powershell%E9%87%8D%E5%91%BD%E5%90%8D%E8%84%9A%E6%9C%AC/","section":"帖子总览","summary":"","title":"powershell重命名脚本","type":"posts"},{"content":" 解决方案来自https://gist.github.com/plembo/f0767e4fbcd42c6c98f8271c15ee785d?ref=techhut.tv\n首先确保宿主机开启了3d加速，并且客户机安装了vmware tools。\n编辑~/.vmware/preferences\n在最后加上一行\nmks.gl.allowBlacklistedDrivers = \u0026#34;TRUE\u0026#34; 重启虚拟机即可。\n","date":"2 July 2024","externalUrl":null,"permalink":"/posts/2024/arch-linux%E4%BD%9C%E4%B8%BAhost%E7%9A%84vmware%E4%B8%AD%E5%AE%89%E8%A3%85windows11%E5%90%AF%E7%94%A83d-acceleration%E5%A4%B1%E8%B4%A5%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/","section":"帖子总览","summary":"","title":"Arch Linux作为host的vmware中安装Windows11，启用3D acceleration失败解决方法","type":"posts"},{"content":"","date":"2 July 2024","externalUrl":null,"permalink":"/tags/vmware/","section":"标签总览","summary":"","title":"Vmware","type":"tags"},{"content":"","date":"18 June 2024","externalUrl":null,"permalink":"/tags/rofi/","section":"标签总览","summary":"","title":"Rofi","type":"tags"},{"content":" 把/usr/share/applications中的.desktop文件复制到~/.local/share/applications中修改[Exec]栏的参数即可。\n","date":"18 June 2024","externalUrl":null,"permalink":"/posts/2024/rofi-drun%E4%BF%AE%E6%94%B9%E8%BD%AF%E4%BB%B6%E5%90%AF%E5%8A%A8%E5%8F%82%E6%95%B0/","section":"帖子总览","summary":"","title":"rofi drun修改软件启动参数","type":"posts"},{"content":"","date":"17 June 2024","externalUrl":null,"permalink":"/tags/io/","section":"标签总览","summary":"","title":"Io","type":"tags"},{"content":"","date":"17 June 2024","externalUrl":null,"permalink":"/tags/python/","section":"标签总览","summary":"","title":"Python","type":"tags"},{"content":" 文件拆分脚本 # 每隔两行拆分成一个新文件。\nimport os with open(\u0026#39;Main.java\u0026#39;, \u0026#39;r\u0026#39;, encoding=\u0026#39;UTF-8\u0026#39;) as file: file_content = file.read() file_parts = file_content.split(\u0026#39;\\n\\n\u0026#39;) for i in range(len(file_parts)): fp = open(f\u0026#39;{i + 1}.txt\u0026#39;, \u0026#39;w\u0026#39;) fp.close() for row in file_parts[i].splitlines(): row = row[2:] print(row) with open(f\u0026#39;{i + 1}.txt\u0026#39;, \u0026#39;a\u0026#39;) as new: new.write(row) new.write(\u0026#39;\\n\u0026#39;) powershell移动文件脚本 # 把同名图片和文件放到同名文件夹底下。\n# 获取当前目录的所有文件 $files = Get-ChildItem -Recurse -File # 从files中删除当前脚本文件 \\(files = \\)files | Where-Object { \\(_.FullName -ne \\)MyInvocation.MyCommand.Path } foreach (\\(file in \\)files) { # 获取文件的目录 \\(targetDir = \\)file.BaseName if (Test-Path -Path $targetDir) { Move-Item -Path \\(file.FullName -Destination \\)targetDir } else { New-Item -ItemType Directory -Path $targetDir Move-Item -Path \\(file.FullName -Destination \\)targetDir } } ","date":"17 June 2024","externalUrl":null,"permalink":"/posts/2024/%E6%96%87%E4%BB%B6%E6%8B%86%E5%88%86%E8%84%9A%E6%9C%AC-ampamp-powershell%E7%A7%BB%E5%8A%A8%E6%96%87%E4%BB%B6%E8%84%9A%E6%9C%AC/","section":"帖子总览","summary":"","title":"文件拆分脚本 \u0026amp;\u0026amp; powershell移动文件脚本","type":"posts"},{"content":" A. SSeeeeiinngg DDoouubbllee # 直接将原字符串翻转一下拼到原字符串的后面就构成了回文串。\nstring s; void solve() { cin \u0026gt;\u0026gt; s; cout \u0026lt;\u0026lt; s; reverse(s.begin(), s.end()); cout \u0026lt;\u0026lt; s \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } B. XOR = Average # 分\\(n\\)的奇偶性考虑，若\\(n\\)为奇数，我们可以让所有的\\(a_i\\)相等，这样\\(a_1 \\bigoplus a_2 \\bigoplus \\cdots \\bigoplus a_n = a_1 = \\frac{n \\cdot a_1}{n}\\)。若\\(n\\)为偶数，我们可以考虑可否将\\(n\\)为奇数时的某个\\(a_i\\)拆成两个\\(a_{i_1}\\)和\\(a_{i_2}\\)，使得\\(a_{i_1} \\bigoplus a_{i_2} = a_i\\)并且\\(\\frac{a_{i_1} + a_{i_2}}{2} = a_i\\)，这样就仍然满足\\(a_1 \\bigoplus a_2 \\bigoplus \\cdots \\bigoplus a_n = a_1 = \\frac{n \\cdot a_1}{n}\\)。容易发现\\(1\\)和\\(3\\)就刚好满足这样的限制，\\(1 \\bigoplus 3 = 2\\)并且\\(\\frac{1 + 3}{2} = 2\\)。\nint n; void solve() { cin \u0026gt;\u0026gt; n; if (n \u0026amp; 1) { for (int i = 1; i \u0026lt;= n; i ++) { cout \u0026lt;\u0026lt; 1 \u0026lt;\u0026lt; \u0026#39; \u0026#39;; } cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } else { cout \u0026lt;\u0026lt; 1 \u0026lt;\u0026lt; \u0026#39; \u0026#39; \u0026lt;\u0026lt; 3 \u0026lt;\u0026lt; \u0026#39; \u0026#39;; for (int i = 1; i \u0026lt;= n - 2; i ++) { cout \u0026lt;\u0026lt; 2 \u0026lt;\u0026lt; \u0026#39; \u0026#39;; } cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } } C. Almost All Multiples # 如果\\(1\\)这个位置放的是\\(n\\)，那么一定是可以构造出来一个排列的，并且字典序最小的排列就是除了\\(1\\)和\\(n\\)这两个位置外的其他位置\\(i\\)都放\\(i\\)。\n排放形式如下： n 2 3 4 5 6 7 8 9 10 11 1(\\(n\\)取\\(12\\)的情况)\n如果\\(1\\)这个位置放的不是\\(n\\)，譬如说是\\(x\\)，那么我们就必须把\\(x\\)这个位置后面的是\\(x\\)的倍数的数放到\\(x\\)这个位置，并在那个数的位置上放上新的数。\n那么如果\\(n\\)不是\\(x\\)的倍数，则\\(n\\)也不会是\\(x * 2, x * 3, \\cdots\\)这些数的倍数，则我们按照上述的规则移动了一些数的位置后，最后\\(n\\)就无法用来补上最后移动的那个数所空缺下来的位置，所以这种情况下应该输出\\(-1\\)。\n贪心地考虑，当\\(n\\)是\\(x\\)的倍数时，我们可以选择形如\\(x, x * 2, x * 2 * 3, x * 2 * 5, n\\)这些位置，将这些位置上的数向左移动一位，最后把\\(n\\)补到最后一位。显然，将\\(\\frac{n}{x}\\)分解质因数，可以使乘的倍数的次数最大化，即让字典序较小的数字更多地靠前，且按照质因数从小到大的顺序乘，即让字典序越小的数越靠前。这样得到的排列就是最优的。\n对于\\(n = 12\\)举例，\\(x = 4\\)的话，所求排列就是这样的：\n4 2 3 12 5 6 7 8 9 10 11 1\nint n, x; void solve() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; x; if (n % x != 0) { cout \u0026lt;\u0026lt; -1 \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return; } int y = n / x; vector\u0026lt;int\u0026gt; bag; for (int i = 2; i \u0026lt;= y / i; i ++) { if (y % i == 0) { while (y % i == 0) { bag.push_back(i); y /= i; } } } if (y \u0026gt; 1) bag.push_back(y); vector\u0026lt;int\u0026gt; ans(n + 1, 0); for (int i = 1; i \u0026lt;= n; i ++) { ans[i] = i; } ans[1] = x, ans[n] = 1; int cur = x; for (int i = 0; i \u0026lt; (int)bag.size(); i ++) { int t = cur; cur *= bag[i]; ans[t] = cur; } for (int i = 1; i \u0026lt;= n; i ++) { cout \u0026lt;\u0026lt; ans[i] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; } cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } ","date":"15 June 2024","externalUrl":null,"permalink":"/posts/2024/codeforces-round-836%E9%A2%98%E8%A7%A3abc/","section":"帖子总览","summary":"","title":"Codeforces Round 836题解（A、B、C）","type":"posts"},{"content":"","date":"15 June 2024","externalUrl":null,"permalink":"/tags/%E5%88%86%E8%A7%A3%E8%B4%A8%E5%9B%A0%E6%95%B0/","section":"标签总览","summary":"","title":"分解质因数","type":"tags"},{"content":"","date":"15 June 2024","externalUrl":null,"permalink":"/tags/%E6%95%B0%E8%AE%BA/","section":"标签总览","summary":"","title":"数论","type":"tags"},{"content":"","date":"15 June 2024","externalUrl":null,"permalink":"/tags/%E5%BC%82%E6%88%96/","section":"标签总览","summary":"","title":"异或","type":"tags"},{"content":"","date":"15 June 2024","externalUrl":null,"permalink":"/tags/%E8%B4%A8%E6%95%B0/","section":"标签总览","summary":"","title":"质数","type":"tags"},{"content":"","date":"13 June 2024","externalUrl":null,"permalink":"/tags/cpp/","section":"标签总览","summary":"","title":"Cpp","type":"tags"},{"content":" 对拍器代码 # #include \u0026lt;fstream\u0026gt; void solve() { ifstream input1; // 你的代码运行的输出 input1.open(\u0026#34;../output.txt\u0026#34;); ifstream input2; // 正确的代码的输出 input2.open(\u0026#34;../compare/output.txt\u0026#34;); int n = 10, m = 10; int T = 10; for (int t = 1; t \u0026lt;= T; t ++) { for (int i = 1; i \u0026lt;= n + m + 1; i ++) { int x, y; input1 \u0026gt;\u0026gt; x; input2 \u0026gt;\u0026gt; y; if (x != y) { cout \u0026lt;\u0026lt; \u0026#34;WA on test\u0026#34; \u0026lt;\u0026lt; t \u0026lt;\u0026lt; \u0026#34; on \u0026#34; \u0026lt;\u0026lt; i \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } } cout \u0026lt;\u0026lt; \u0026#34;AC on test\u0026#34; \u0026lt;\u0026lt; t \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } } 随机数据生成 # #include \u0026lt;random\u0026gt; mt19937_64 rnd(1064); int rd(int l, int r) {return rnd() % (r - l + 1) + l;} int a[100]; void solve() { int t = 10; cout \u0026lt;\u0026lt; t \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; while (t --) { int n = 10, m = 10; cout \u0026lt;\u0026lt; n \u0026lt;\u0026lt; \u0026#39; \u0026#39; \u0026lt;\u0026lt; m \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; for (int i = 1; i \u0026lt;= n + m + 1; i ++) { a[i] = rd(1, 100); cout \u0026lt;\u0026lt; a[i] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; } cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; for (int i = 1; i \u0026lt;= n + m + 1; i ++) { int x = rd(1, 100); while (x == a[i]) x = rd(1, 100); cout \u0026lt;\u0026lt; x \u0026lt;\u0026lt; \u0026#39; \u0026#39;; } cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } } 以上代码均需要针对不同问题做修改。\n","date":"13 June 2024","externalUrl":null,"permalink":"/posts/2024/%E5%AF%B9%E6%8B%8D%E5%99%A8/","section":"帖子总览","summary":"","title":"对拍器","type":"posts"},{"content":"","date":"13 June 2024","externalUrl":null,"permalink":"/tags/%E5%AF%B9%E6%8B%8D%E5%99%A8/","section":"标签总览","summary":"","title":"对拍器","type":"tags"},{"content":" A. Hossam and Combinatorics # \\(|a_i - a_j|\\)最大的就是最大值和最小值，注意要开long long。\nint n; int a[N]; void solve() { cin \u0026gt;\u0026gt; n; int min_v = INF, max_v = 0; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; min_v = min(min_v, a[i]); max_v = max(max_v, a[i]); } int cnt_min = 0, cnt_max = 0; if (min_v == max_v) { cout \u0026lt;\u0026lt; n * (n - 1) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return; } for (int i = 1; i \u0026lt;= n; i ++) { if (a[i] == min_v) cnt_min ++; if (a[i] == max_v) cnt_max ++; } cout \u0026lt;\u0026lt; cnt_min * cnt_max * 2 \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } B. Hossam and Friends # 考虑对每一个\\(l\\)，有那些\\(r\\)满足\\([l, r]\\)中的朋友都互相认识。\n设共有\\(m\\)各互相不认识的对\\((l, r)\\)，则所有小于\\(l\\ge\\)当前枚举到的\\(i\\)的\\((l, r)\\)对中最小的\\(r\\)的下标都可以作为当前\\(i\\)的\\(r\\)。\n那么我们开个优先队列维护即可。\nint n, m; vector\u0026lt;int\u0026gt; bag[N]; priority_queue\u0026lt;PII, vector\u0026lt;PII\u0026gt;, greater\u0026lt;\u0026gt;\u0026gt; pq; void solve() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m; for (int i = 1; i \u0026lt;= n; i ++) bag[i].clear(); while (!pq.empty()) pq.pop(); for (int i = 1; i \u0026lt;= m; i ++) { int l, r; cin \u0026gt;\u0026gt; l \u0026gt;\u0026gt; r; if (l \u0026gt; r) swap(l, r); bag[l].push_back(r); } int ans = 0; for (int i = n; i \u0026gt;= 1; i --) { for (auto j : bag[i]) pq.push({j, i}); if (pq.empty()) { ans += n - i + 1; continue; } ans += pq.top().first - i; } cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } C. Hossam and Trainees # 如果\\(x\\)可以同时整除\\(a[i]\\)和\\(a[j]\\)，那么\\(x\\)的一个质因子\\(p\\)就也可以整除\\(a[i]\\)和\\(a[j]\\)。一种想法是枚举\\(\\le 1e9\\)的的所有质数，判断是否有一个质数可以同时整除\\(a[i]\\)和\\(a[j]\\)，但是\\(\\le 1e9\\)的质数的数量是\\(5.08e7\\)，就算打表，综合起来的时间复杂度也不够用。\n考虑对每个数分解质因数，那么如果\\(a[i]\\)和\\(a[j]\\)分解质因数的质因子序列有重合的话，就说明\\(a[i]\\)和\\(a[j]\\)可以被这两个重合的质因子整除。直接这样做的时间复杂度是\\(O(n \\sqrt{1e9})\\)，且质因子的跨度太大，开桶难以存下。\n我们可以先预处理出\\(\\le \\sqrt{1e9}\\)的所有质数，这样分解每个\\(a[i]\\)的质因数的时间复杂度可以优化到\\(O(\\frac{\\sqrt{1e9}}{log \\sqrt{1e9}})\\)。然后由于\\(n\\)最多只会有一个\\(\u003e \\sqrt n\\)的质因数，所以\\(\u003e \\sqrt{a[i]}\\)的质因数可以用map来存，\\(\\le \\sqrt{a[i]}\\)的质因数用普通的桶来存即可。\nint primes[N], cnt, st[N]; int a[N]; int n; int cnt_prime[N]; map\u0026lt;int, int\u0026gt; mp; void getPrimes(int n) { for (int i = 2; i \u0026lt;= n; i ++) { if (!st[i]) primes[cnt++] = i; for (int j = 0; primes[j] \u0026lt;= n / i; j ++) { st[primes[j] * i] = 1; if (i % primes[j] == 0) break; } } } void solve() { cin \u0026gt;\u0026gt; n; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; } int flag = 0; mp.clear(); memset(cnt_prime, 0, (cnt + 2) * sizeof(int)); for (int i = 1; i \u0026lt;= n; i ++) { for (int j = 0; j \u0026lt; cnt \u0026amp;\u0026amp; primes[j] \u0026lt;= a[i]; j ++) { if (a[i] % primes[j] == 0) { cnt_prime[j] ++; while (a[i] % primes[j] == 0) a[i] /= primes[j]; } } if (a[i] \u0026gt; 1) { if (mp[a[i]]) { flag = 1; break; } mp[a[i]] = 1; } } for (int i = 0; i \u0026lt; cnt; i ++) { if (cnt_prime[i] \u0026gt; 1) { flag = 1; break; } } cout \u0026lt;\u0026lt; (flag ? \u0026#34;YES\u0026#34; : \u0026#34;NO\u0026#34;) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } bool Med; signed main() { fprintf(stderr, \u0026#34;%.3lf MB\\n\u0026#34;, (\u0026amp;Med - \u0026amp;Mbe) / 1048576.0); // setIO(); int T = 1; cin \u0026gt;\u0026gt; T; getPrimes(3.4e4); while (T --) solve(); cerr \u0026lt;\u0026lt; 1e3 * clock() / CLOCKS_PER_SEC \u0026lt;\u0026lt; \u0026#34; ms\\n\u0026#34;; return 0; } ","date":"10 June 2024","externalUrl":null,"permalink":"/posts/2024/codeforces-round-837%E9%A2%98%E8%A7%A3abc/","section":"帖子总览","summary":"","title":"Codeforces Round 837题解（A、B、C）","type":"posts"},{"content":"","date":"10 June 2024","externalUrl":null,"permalink":"/tags/%E5%A0%86/","section":"标签总览","summary":"","title":"堆","type":"tags"},{"content":" 可能你装的是dkms版本的驱动，这种一般要安装linux内核对应的headers，然后会自动安装模块。\n比如，如果你用的是linux-zen，那么只要\nsudo pacman -S linux-zen-headers 即可。\n","date":"10 June 2024","externalUrl":null,"permalink":"/posts/2024/arch%E7%B3%BBlinux%E5%AE%89%E8%A3%85%E8%8B%B1%E4%BC%9F%E8%BE%BE%E6%98%BE%E5%8D%A1%E9%A9%B1%E5%8A%A8%E5%B0%8F%E9%97%AE%E9%A2%98nvidia-smi-failed/","section":"帖子总览","summary":"","title":"Arch系linux安装英伟达显卡驱动小问题（nvidia-smi failed）","type":"posts"},{"content":"","date":"10 June 2024","externalUrl":null,"permalink":"/tags/nvidia/","section":"标签总览","summary":"","title":"Nvidia","type":"tags"},{"content":" A. Absolute Maximization # 我们可以选择两个位置\\(i, j\\)来存放最大值\\(a_i\\)和最小值\\(a_j\\)，对每一位，如果从\\(a_{[1, n]}\\)的这一位有\\(1\\)，我们就可以把\\(1\\)挪到\\(a_i\\)里，如果这意味有\\(0\\)，我们就可以把\\(0\\)挪到\\(a_j\\)里，这样就可以构造出最大的\\(a_i\\)和最小的\\(a_j\\)。\nint n; int a[550]; void solve() { cin \u0026gt;\u0026gt; n; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; } int max_v = 0, min_v = 1023; for (int r = 9; r \u0026gt;= 0; r --) { for (int i = 1; i \u0026lt;= n; i ++) { if (a[i] \u0026gt;\u0026gt; r \u0026amp; 1) { max_v |= 1 \u0026lt;\u0026lt; r; } else { min_v \u0026amp;= ~(1 \u0026lt;\u0026lt; r); } } } cout \u0026lt;\u0026lt; max_v - min_v \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } B. Incinerate # 将怪物按照\\(h\\)从小到大排序，每次打出\\(k\\)的伤害就相当于把所有\\(h \\le\\)前\\(i\\)次伤害总和的怪物击倒。那么我们每次可以二分找出剩下的怪物的起始位置。\n当某一次\\(k\\)降到\\(\\le 0\\)后，就不可能再打倒新的怪物，如果此时还没有把所有怪物都打倒的话，那么就不可能打倒所有的怪物了。\n模拟这个过程，最坏情况是\\(k\\)每次都减一，因此时间复杂度最坏为\\(klogn+nlogn\\)。\nint n, k; PII a[N]; int min_v[N]; void solve() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; k; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i].first; } for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i].second; } sort(a + 1, a + 1 + n); min_v[n + 1] = INF; for (int i = n; i \u0026gt;= 1; i --) { min_v[i] = min(min_v[i + 1], a[i].second); } // for (int i = 1; i \u0026lt;= n; i ++) { // cout \u0026lt;\u0026lt; min_v[i] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; // } // cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; int cur = k; while (1) { int pos = lower_bound(a + 1, a + 1 + n, PII(cur + 1, 0)) - a - 1; // cout \u0026lt;\u0026lt; pos \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; if (pos \u0026gt;= n) { cout \u0026lt;\u0026lt; \u0026#34;YES\u0026#34; \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return; } int cnt = k - min_v[pos + 1]; // cout \u0026lt;\u0026lt; cnt \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; cur += cnt; k = cnt; if (cnt \u0026lt;= 0) { cout \u0026lt;\u0026lt; \u0026#34;NO\u0026#34; \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return; } } } C. Another Array Problem # 有点恶心的一道题，我最后\\(15\\)分钟才突然产生思路，但是分类有点小问题加没能调出来。。。结束后看分数，纳尼！竟然是\\(2000\\)分的题。。。\n观察到如果最大值所在位置左边或者右边有\\(\\ge 2\\)个元素，我们就可以把这个方向上所有元素都变成最大值。\n例如：\\(2, 5, 3, 4\\) -\u0026gt; \\(2, 5, 1, 1\\) -\u0026gt; \\(2, 5, 0, 0\\) -\u0026gt; \\(2, 5, 5, 5\\)。\n实际上，如果最大值所在位置左边或右边有\\(\\ge 2\\)个元素，我们可以把数组中所有元素都变成最大值。\n例如：\\(2, 5, 3, 4\\) -\u0026gt; \\(2, 5, 5, 5\\) -\u0026gt; \\(3, 3, 5, 5\\) -\u0026gt; \\(0, 0, 5, 5\\) -\u0026gt; \\(5, 5, 5, 5\\)。\n那么就只剩下两种特别情况了，当\\(n = 2\\)时，只有两种方案，换或不换，直接输出比较即可。\n当\\(n = 3\\)并且最大值在中间时比较恶心，我们注意到在换的过程中出现在最左边或者最右边的数都可以被用来覆盖整个数组。我们可以发现出现在左边的数最大是\\(a[1]\\)或者\\(a[2] - a[1]\\)，出现在右边的数最大是\\(a[3]\\)或\\(a[2] - a[3]\\)，因为只要换一次，原本的最大值肯定就不保了，所以把这四个情况和不换的情况综合起来取\\(max\\)就是答案。但是如果要求具体详细严谨的证明，比较麻烦。蒟蒻想不出来，想出来也表达不清楚啊。。。\nint n; int a[M]; void solve() { cin \u0026gt;\u0026gt; n; int max_v = 0; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; max_v = max(max_v, a[i]); } vector\u0026lt;int\u0026gt; bag; for (int i = 1; i \u0026lt;= n; i ++) { if (a[i] == max_v) { bag.push_back(i); } } int ans = 0; for (int i = 1; i \u0026lt;= n; i ++) { ans += a[i]; } if (n == 2) { cout \u0026lt;\u0026lt; max(ans, abs(a[1] - a[2]) * 2) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return; } if (n \u0026gt;= 4) { cout \u0026lt;\u0026lt; n * max_v \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return; } if (a[1] == max_v || a[3] == max_v) { cout \u0026lt;\u0026lt; n * max_v \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return; } cout \u0026lt;\u0026lt; max(ans, max({a[2] - a[1], a[2] - a[3], a[1], a[3]}) * 3) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } ","date":"8 June 2024","externalUrl":null,"permalink":"/posts/2024/codeforces-round-840%E9%A2%98%E8%A7%A3abc/","section":"帖子总览","summary":"","title":"Codeforces Round 840题解（A、B、C）","type":"posts"},{"content":"","date":"8 June 2024","externalUrl":null,"permalink":"/tags/%E6%9E%84%E9%80%A0/","section":"标签总览","summary":"","title":"构造","type":"tags"},{"content":"","date":"8 June 2024","externalUrl":null,"permalink":"/tags/%E7%BB%9D%E5%AF%B9%E5%80%BC/","section":"标签总览","summary":"","title":"绝对值","type":"tags"},{"content":"","date":"8 June 2024","externalUrl":null,"permalink":"/tags/%E6%95%B0%E5%AD%A6/","section":"标签总览","summary":"","title":"数学","type":"tags"},{"content":" A. Guess the Maximum # 因为\\(i \u003c j\\)，所以所有的\\([i, j]\\)区间中都至少包含两个相邻元素，所以只要求出所有相邻元素中较大值的最小值即可。\nint n; int a[N]; void solve() { cin \u0026gt;\u0026gt; n; int min_v = 1e9 + 1; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; } for (int i = 1; i \u0026lt;= n - 1; i ++) { min_v = min(min_v, max(a[i], a[i + 1])); } cout \u0026lt;\u0026lt; min_v - 1 \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } B. XOR Sequences # 观察结论，发现样例\\(4\\)的答案是\\(2^{25} = 33554432\\)，猜测所有答案都是\\(2\\)的次方。\n以样例\\(3\\)为例：\n5432 10 5432 10 57: 1110 01 37: 1001 01 0100 00 0011 00 0100 01 0011 01 0100 10 0011 10 0100 11 0011 11 发现\\(x\\)和\\(y\\)的最长公共后缀对应的位可以从\\(0\\)开始连续地填，从\\(00\\)填到\\(11\\)就走完了这两位可以提供的所有连续数值。如果从\\(000\\)填到\\(111\\)的话，因为更高位\\(x\\)和\\(y\\)的值不同，所以异或出来的值不是连续的。\n再看\\(5432\\)位，我们要保证\\(x\\)和\\(y\\)都不能填\\(0000\\)，因为\\(0000\\)会和后面两位\\(00\\)组成\\(0\\)，但是题目要求是从\\(1\\)开始。假设\\(x\\)填\\(0001\\)，如果\\(y\\)必须填\\(0000\\)才能保证前缀异或相同，那么我们可以把\\(x\\)改填\\(0011\\)，因为异或的性质，原本第\\(3\\)位取的是\\(x\\)的第三位，现在我们改成\\(1\\)，就是取\\(x\\)的第三位取反，那么\\(y\\)的第三位就也必须取反，那么\\(y\\)就得填\\(0010\\)。这样，我们总可以不用选\\(0000\\)去填。\nint x, y; void solve() { cin \u0026gt;\u0026gt; x \u0026gt;\u0026gt; y; int i; for (i = 0; i \u0026lt;= 30; i ++) { if ((x \u0026gt;\u0026gt; i \u0026amp; 1) != (y \u0026gt;\u0026gt; i \u0026amp; 1)) { cout \u0026lt;\u0026lt; (1 \u0026lt;\u0026lt; i) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return; } } cout \u0026lt;\u0026lt; (1 \u0026lt;\u0026lt; i) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } C. Earning on Bets # 吐槽：忘了删刚开始猜的判断\\(-1\\)的情况，导致赛时一直\\(WA \\ 8\\)。\n更新：刚开始猜的是对的，在判断\\(flag == 1\\)时应该这样写：\nflag \u0026gt; 1 \u0026amp;\u0026amp; fabs(flag - 1) \u0026lt; 1e-6 设\\(x\\)的总和为\\(s\\)。\n因为\\(k_i * x_i \u003e s\\)，所以\\(x_i \u003e= s / k_i + 1\\)，然后我们要保证所有的\\(s / k_i + 1\\)加起来小于等于\\(s\\)。因为这样我们可以在每个\\(s / k_i + 1\\)上加若干值使得他们的总和等于\\(s\\)，且仍然满足\\(k_i * x_i \u003e s\\)。\n那么我们可以二分查找这个\\(s\\)，找不到就输出\\(-1\\)。\nint n; int k[55]; int a[55]; bool check(int x) { int sum = x ; for (int i = 1; i \u0026lt;= n; i ++) { sum -= x / k[i]; } return sum \u0026gt;= n; } void solve() { cin \u0026gt;\u0026gt; n; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; k[i]; } // double flag = 0; // for (int i = 1; i \u0026lt;= n; i ++) { // flag += (double)1 / (double)k[i]; // } // if (flag \u0026gt; 1 || fabs(flag - 1) \u0026lt; 1e-6) { // cout \u0026lt;\u0026lt; -1 \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; // return; // } int l = n - 1, r = n * (int)1e9 + 1; while (l \u0026lt; r) { int mid = l + r \u0026gt;\u0026gt; 1; if (check(mid)) r = mid; else l = mid + 1; } int s = l; if (s == n - 1 || s == n * (int)1e9 + 1) { cout \u0026lt;\u0026lt; -1 \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return; } // cout \u0026lt;\u0026lt; l \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; int sum = 0; for (int i = 1; i \u0026lt;= n; i ++) { a[i] = s / k[i] + 1; sum += a[i]; } int cnt = l - sum; a[1] += cnt; for (int i = 1; i \u0026lt;= n; i ++) { cout \u0026lt;\u0026lt; a[i] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; } cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; // sum = 0; // for (int i = 1; i \u0026lt;= n; i ++) { // sum += a[i]; // } // for (int i = 1; i \u0026lt;= n; i ++) { // cout \u0026lt;\u0026lt; a[i] * k[i] - sum \u0026lt;\u0026lt; \u0026#39; \u0026#39;; // } // cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } ","date":"7 June 2024","externalUrl":null,"permalink":"/posts/2024/codeforces-round-961%E9%A2%98%E8%A7%A3abc/","section":"帖子总览","summary":"","title":"codeforces round 961题解（A、B、C）","type":"posts"},{"content":"","date":"7 June 2024","externalUrl":null,"permalink":"/tags/%E4%BD%8D%E8%BF%90%E7%AE%97/","section":"标签总览","summary":"","title":"位运算","type":"tags"},{"content":" A. Turtle and Piggy Are Playing a Game # 首先\\(p\\)选\\(2\\)的话除得最慢，得的分多。考虑二进制表示，如果\\(x = (1000000000)_{bin}\\)，则每次除以\\(2\\)都是相当于右移一位，除完之后仍然是\\(2\\)的倍数，变成\\(1\\)的步数就是把最高位的\\(1\\)移动到\\(0\\)位的步数。\n因为\\(2l \\le r\\)，所以\\(l \\le \\lfloor \\frac{r}{2} \\rfloor\\)，若\\(r = (1011101)_{bin}\\)，则\\(r \u003e\u003e 1 = (101110)_{bin}\\)，可以发现\\((1000000)_{bin}\\)一定是在\\([l, r]\\)区间内部，因此我们可以直接选择\\(x = 1 \u003c\u003c (lg(r))\\)。\nint l, r; void solve() { cin \u0026gt;\u0026gt; l \u0026gt;\u0026gt; r; cout \u0026lt;\u0026lt; lg(r) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } B. Turtle and an Infinite Sequence # 写下几组数据发现第\\(m\\)次改变后\\(a_n\\)的值变为\\([n - m, n + m]\\)这个区间内所有\\(a_i\\)的按位或的值。\n令\\(l = n - m\\)，\\(r = n + m\\)。\nl: 01011 0 11011 r: 01011 1 00110 设从左到右第一个不相等的位为\\(bit\\)，则\\(ans\\)的\\(bit\\)位左边的值就是\\(l\\)或\\(r\\)的这个位的值。对\\(bit\\)位及其右边的位，发现01011 0 11111在\\([l, r]\\)内，因此后面的位每一位或出来都是\\(1\\)，则加上\\((1 \u003c\u003c (bit + 1)) - 1\\)即可。\nint n, m; void solve() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m; int l = max(0, n - m); int r = n + m; int ans = 0; for (int i = 31; i \u0026gt;= 0; i --) { if ((l \u0026gt;\u0026gt; i \u0026amp; 1) != (r \u0026gt;\u0026gt; i \u0026amp; 1)) { ans += (1 \u0026lt;\u0026lt; (i + 1)) - 1; break; } else { ans |= (l \u0026gt;\u0026gt; i \u0026amp; 1) \u0026lt;\u0026lt; i; } } cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } C. Turtle and an Incomplete Sequence # 考虑所有非\\(-1\\)的位置，我们要保证这些位置中每相邻的两个位置上的数要在他们的间隔允许的步数范围内实现相互转化。\n比如\\(3, -1, -1, -1, 9, -1\\)，我们要保证\\(3\\)可以在\\(4\\)步之内转化到\\(9\\)，通过\\(/2\\)或者\\(*2\\)或者\\(*2+1\\)的方式。\n既然如此，我们可以考虑最少需要多少步，可以将\\(a\\)转化成\\(b\\)。\n3: 0011 9: 1001 首先我们可以把\\(3\\)的前缀一给删掉。\n3: 1 1 9: 1 0 01 然后，这些操作可以抽象为二进制序列右移一位、左移一位补\\(0\\)、左移一位补\\(1\\)三种。\n因为操作都是从尾部进行的，所以\\(3\\)和\\(9\\)的公共前缀部分可以不用考虑，从第一个不相同的位开始后面的所有位都会被操作（需要或者不需要修改的位都会在修改者第一个不相同的位的时候被顺便操作到），所以这个最小步数就是\\(3\\)和\\(9\\)的第一个不相同的位及其后面的位数之和的和。\n因为从\\(a\\)变成\\(b\\)，位数的变化是一定的，二每个操作都是增加一个位或者减少一个位，所以所有操作方案都和最小方案具有相同的奇偶性。\n因此我们只要判断每两个非\\(-1\\)位置的间距是否大于等于最小方案并且与其具有相同奇偶性即可。\n对于第一个非\\(-1\\)位置的左边和最后一个非\\(-1\\)位置的右边，我们连续地\\(*2\\)，\\(/2\\)即可。\nint n; int a[M]; int b[M]; int get_times_l(int x, int y) { int l = lg(x), r = lg(y); int i, j; for (i = l, j = r; i \u0026gt;= 0 \u0026amp;\u0026amp; j \u0026gt;= 0; i --, j --) { if ((x \u0026gt;\u0026gt; i \u0026amp; 1) != (y \u0026gt;\u0026gt; j \u0026amp; 1)) { return i + 1; } } return i + 1; } int get_times_r(int x, int y) { int l = lg(x), r = lg(y); int i, j; for (i = l, j = r; i \u0026gt;= 0 \u0026amp;\u0026amp; j \u0026gt;= 0; i --, j --) { if ((x \u0026gt;\u0026gt; i \u0026amp; 1) != (y \u0026gt;\u0026gt; j \u0026amp; 1)) { return j + 1; } } return j + 1; } void solve() { cin \u0026gt;\u0026gt; n; for (int i = 1; i \u0026lt;= n; i ++) { b[i] = 1; } for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; } for (int i = 1; i \u0026lt;= n; i ++) { if (a[i] != -1) { b[i] = a[i]; int j; for (j = i + 1; j \u0026lt;= n; j ++) { if (a[j] != -1) { break; } } if (j \u0026gt; n) { break; } int l = get_times_l(a[i], a[j]); int r = get_times_r(a[i], a[j]); // cout \u0026lt;\u0026lt; l \u0026lt;\u0026lt; \u0026#39; \u0026#39; \u0026lt;\u0026lt; r \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; // cout \u0026lt;\u0026lt; i \u0026lt;\u0026lt; \u0026#39; \u0026#39; \u0026lt;\u0026lt; j \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; if (j - i \u0026gt;= l + r \u0026amp;\u0026amp; (j - i - l - r) % 2 == 0) { for (int k = i + 1; k \u0026lt;= i + l; k ++) { b[k] = b[k - 1] / 2; } int rr = r; for (int k = i + l + 1; k \u0026lt;= i + l + r; k ++) { b[k] = b[k - 1] * 2 + ((a[j] \u0026gt;\u0026gt; (rr-- - 1)) \u0026amp; 1); } int cnt = 0; for (int k = i + l + r + 1; k \u0026lt; j; k ++) { if (cnt++ \u0026amp; 1) b[k] = b[k - 1] / 2; else b[k] = b[k - 1] * 2; } } else { cout \u0026lt;\u0026lt; \u0026#34;-1\\n\u0026#34;; return; } i = j - 1; } } int pos_l = -1, pos_r = -1; for (int i = 1; i \u0026lt;= n; i ++) { if (a[i] != -1) { pos_l = i; break; } } for (int i = n; i \u0026gt;= 1; i --) { if (a[i] != -1) { pos_r = i; break; } } int cnt = 0; if (pos_l != -1) { for (int i = pos_l - 1; i \u0026gt;= 1; i --) { if (cnt++ \u0026amp; 1) b[i] = b[i + 1] / 2; else b[i] = b[i + 1] * 2; } } cnt = 0; if (pos_r != -1) { for (int i = pos_r + 1; i \u0026lt;= n; i ++) { if (cnt++ \u0026amp; 1) b[i] = b[i - 1] / 2; else b[i] = b[i - 1] * 2; } } if (pos_l == -1 \u0026amp;\u0026amp; pos_r == -1) { b[1] = 1; cnt = 0; for (int i = 2; i \u0026lt;= n; i ++) { if (cnt++ \u0026amp; 1) b[i] = b[i - 1] / 2; else b[i] = b[i - 1] * 2; } } for (int i = 1; i \u0026lt;= n; i ++) { cout \u0026lt;\u0026lt; b[i] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; } cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } D. Turtle and Multiplication # \\(a_i \\cdot a_{i+1} = a_j \\cdot a_{j+1}\\)的必要条件是无序对\\((a_i, a_{i+1})\\)和无序对\\(a_j, a_{j+1}\\)相同。实际上，当\\(a_i\\)都是质数时，这个必要条件会变成充要条件。如果两个质数对中有一个质数是相同的，那么乘积相同则另一个质数也是相同的；如果两个质数对中的质数两两不同，由分解质因数可知他们的乘积为\\(2^a 3^b 5^c ...\\)，其中\\(a, b, c, ...\\)中只能有且只有两个为1，其他全为0，那么排列组合的话也就只有这一种组合，故无序对相同是充要条件。\n我们可以把\\((a_i, a_{i+1})\\)看作是一条边，则问题变为找到点数最少得无向完全图（每个点还有一个自环），是的这个完全图存在一条经过\\(n - 1\\)条边且不经过重复边的路径。\n考虑若完全图点数确定，我们如何计算这个完全图的最长不经过重复边的路径长度。\n设完全图点数为\\(m\\)，若\\(m\\)是奇数则每个点的度数都是偶数，所以这个图存在欧拉路径，路径长度等于边数等于\\(\\frac{m(m+1)}{2}\\)。\n若\\(m\\)是偶数那么每个点的度数都是奇数，我们需要删除一些边使得这个图存在欧拉路径。可以发现删掉一条边最多可使奇度数的点的数量减少\\(2\\)，所以我们至少需要删除\\(\\frac{m}{2} - 1\\)条边。发现有一种方案：删除\\((2, 3), (4, 5), ..., (m - 2, m - 1)\\)这些边即可。路径长度为\\(\\frac{m(m-1)}{2} - \\frac{m}{2} + 1 + m = \\frac{m^2}{2} + 1\\)。\n当\\(n = 10^6\\)时最小的\\(m\\)是\\(1415\\)，第\\(1415\\)小的质数是\\(11807\\)，符合\\(a_i \\le 3 \\cdot 10^5\\)。\n我们可以二分求出最小的\\(m\\)，再用Fleury等算法求出一个无向图的欧拉路径。\n时间复杂度：每个测试样例\\(O(n)\\)。\n注意，我们在添加欧拉回路的路径时，只把当前边的\\(v\\)节点（这个变到达的那个点）加到了栈中，所以最后要记得再把\\(1\\)号节点（初始的起点）也加到栈中。数组大小的\\(3N\\)是为了防止越界所设，实际上接近\\(2N\\)即可。\nint n; int h[N], e[N * 3], ne[N * 3], idx; int st[N], primes[N], cnt; int used[N * 3], tot; int ans[N * 3]; void getPrimes(int n) { for (int i = 2; i \u0026lt;= n; i ++) { if (!st[i]) primes[cnt ++] = i; for (int j = 0; primes[j] \u0026lt;= n / i; j ++) { st[primes[j] * i] = 1; if (i % primes[j] == 0) break; } } } void addEdge(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx ++; } bool check(int x) { if (x \u0026amp; 1) return x * (x + 1) / 2 \u0026gt;= n - 1; else return x * x / 2 + 1 \u0026gt;= n - 1; } void dfs(int u) { for (int i = h[u]; ~i; i = h[u]) { if (used[i]) { h[u] = ne[i]; continue; } used[i] = 1; used[i ^ 1] = 1; h[u] = ne[i]; dfs(e[i]); ans[++tot] = e[i]; } } void solve() { cin \u0026gt;\u0026gt; n; memset(h, -1, (n + 2) * 4); idx = 0; tot = 0; int l = 1, r = 10000; while (l \u0026lt; r) { int mid = l + r \u0026gt;\u0026gt; 1; if (check(mid)) r = mid; else l = mid + 1; } int m = l; for (int i = 1; i \u0026lt;= m; i ++) { for (int j = i; j \u0026lt;= m; j ++) { if (!(m \u0026amp; 1) \u0026amp;\u0026amp; j == i + 1 \u0026amp;\u0026amp; !(i \u0026amp; 1)) continue; addEdge(i, j); addEdge(j, i); } } for (int i = 0; i \u0026lt; idx; i ++) { used[i] = 0; } dfs(1); ans[++tot] = 1; reverse(ans + 1, ans + 1 + tot); // for (int i = 1; i \u0026lt;= tot; i ++) { // cout \u0026lt;\u0026lt; ans[i] \u0026lt;\u0026lt; \u0026#34; \\n\u0026#34;[i == tot]; // } for (int i = 1; i \u0026lt;= n; i ++) { cout \u0026lt;\u0026lt; primes[ans[i] - 1] \u0026lt;\u0026lt; \u0026#34; \\n\u0026#34;[i == n]; } } bool Med; int main() { fprintf(stderr, \u0026#34;%.3lf MB\\n\u0026#34;, (\u0026amp;Med - \u0026amp;Mbe) / 1048576.0); // setIO(); int T = 1; cin \u0026gt;\u0026gt; T; getPrimes(1e6); // for (int i = 0; i \u0026lt; 100; i ++) { // cout \u0026lt;\u0026lt; primes[i] \u0026lt;\u0026lt; \u0026#34; \\n\u0026#34;[i == 99]; // } while (T --) solve(); cerr \u0026lt;\u0026lt; 1e3 * clock() / CLOCKS_PER_SEC \u0026lt;\u0026lt; \u0026#34; ms\\n\u0026#34;; return 0; } ","date":"4 June 2024","externalUrl":null,"permalink":"/posts/2024/codeforces-round-949%E9%A2%98%E8%A7%A3abcd/","section":"帖子总览","summary":"","title":"Codeforces Round 949题解（A、B、C、D）","type":"posts"},{"content":"","date":"3 June 2024","externalUrl":null,"permalink":"/tags/atcoder/","section":"标签总览","summary":"","title":"Atcoder","type":"tags"},{"content":" D. Masked Popcount # 按位考虑 + 排列组合\n考虑\\(M = 10110111001\\) \\(i\\)从\\(0\\)循环到\\(N\\)\n因为求的是所有\\(i \\\u0026 M\\)的二进制表示中1的个数，所以可以按位考虑，考虑有多少个\\(i\\)的\\(bit\\)位与\\(M\\)的\\(bit\\)位\\(\\\u0026\\)出来是\\(1\\)。\n首先如果两个数\\(bit\\)位\\(\\\u0026\\)出来是\\(1\\)，那么这两个位一定都是\\(1\\)。\n假设当前\\(bit\\)是\\(7\\)\nbit: 10 9 8 7 6 5 4 3 2 1 0 M: 1 0 1 1 0 1 1 1 0 0 1 i: _ _ _ 1 _ _ _ _ _ _ _ 如果\\(bit\\)前面的位填1 0 1，则\\(bit\\)后面的位只能填0到0 1 1 1 0 0 1；方案数是\\(1 * (M \\\u0026 0x11110000000 + 1)\\) 如果\\(bit\\)前面的位填0到1 0 0，则\\(bit\\)后面的位可以填0到1 1 1 1 1 1 1。方案数是\\((i \u003e\u003e (bit + 1)) * (1 \u003c\u003c bit)\\)\n写代码时注意取模操作，传入函数中的参数也应该取模。\nint n, m; void solve() { // cout \u0026lt;\u0026lt; ((1 \u0026lt;\u0026lt; 9) + (1 \u0026lt;\u0026lt; 7) + (1 \u0026lt;\u0026lt; 6) + (1 \u0026lt;\u0026lt; 4) + 1) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m; int ans = 0ll; for (int r = 60ll; r \u0026gt;= 0ll; r --) { if (m \u0026gt;\u0026gt; r \u0026amp; 1ll) { if (n \u0026gt;\u0026gt; r \u0026amp; 1ll) { plut(ans, plu(mul((n \u0026gt;\u0026gt; r + 1ll) % mod, (1ll \u0026lt;\u0026lt; r) % mod), (n \u0026amp; ((1ll \u0026lt;\u0026lt; r) - 1ll)) % mod + 1ll)); } else { plut(ans, mul((n \u0026gt;\u0026gt; r + 1ll) % mod, (1ll \u0026lt;\u0026lt; r) % mod)); } } } cout \u0026lt;\u0026lt; ans % mod \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } E. Max/Min # 考虑如果a数组中的元素各不相同。\n观察到\\(a[i] \\le 1e6\\)，可以开个桶来存储每个\\(a[i]\\)出现的次数，可以对这个桶数组再求个前缀和，则可以用这个前缀和数组\\(O(1)\\)地求出某一连续值域中出现的\\(a[i]\\)的个数。\n现在如果我们（先将\\(a\\)排序以方便理解）从前往后枚举\\(a\\)中的每一个元素，求当前位置\\(i\\)后面有多少个\\(j\\)使得\\(\\lfloor \\frac{a_j}{a_i} \\rfloor\\)等于某一个值\\(x\\)，我们发现这个x的最大值就是\\(\\frac{max(a[i])}{a[i]}\\)。对于每个x，我们可以用\\(cnt[a[i] * x + a[i] - 1] - cnt[a[i] - 1]\\)来得出\\(a[i]\\)对答案的贡献，假设\\(m = max(a[i])\\)，那么我们这样操作的时间复杂度就是\\(\\sum\\limits_{i=1}{n} \\frac{m}{a[i]} \u003c= \\sum\\limits_{i=1}{n} \\frac{m}{i}\\)，因为后者是调和级数，所以时间复杂度就是\\(O(mlogn)\\)。\n在代码实现中要注意特判\\(a[j]\\)是\\(a[i]\\)的\\(1\\)倍的情况并且注意把相同的\\(a[i]\\)内部的贡献加上。\nint n; int cnt[(int)2e6 + 10]; int s[(int)2e6 + 10]; void solve() { cin \u0026gt;\u0026gt; n; memset(cnt, 0, sizeof cnt); vector\u0026lt;int\u0026gt; a; int max_a = 0; for (int i = 0; i \u0026lt; n; i ++) { int x; cin \u0026gt;\u0026gt; x; a.push_back(x); cnt[x] ++; max_a = max(max_a, x); } sort(a.begin(), a.end()); a.erase(unique(a.begin(), a.end()), a.end()); int ans = 0; memset(s, 0, sizeof s); for (int i = 1; i \u0026lt;= (int)1e6; i ++) { s[i] = s[i - 1] + cnt[i]; } // for (int i = 1; i \u0026lt;= 4; i ++) { // cout \u0026lt;\u0026lt; s[i] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; // } // cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; // for (int i = 0; i \u0026lt; a.size(); i ++) { // cout \u0026lt;\u0026lt; a[i] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; // } // cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; // for (int i = 0; i \u0026lt; a.size(); i ++) { // cout \u0026lt;\u0026lt; cnt[a[i]] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; // } // cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; for (int i = 0; i \u0026lt; a.size(); i ++) { int m = max_a / a[i]; for (int j = 1; j \u0026lt;= m; j ++) { // cout \u0026lt;\u0026lt; j \u0026lt;\u0026lt; \u0026#39; \u0026#39; \u0026lt;\u0026lt; j * a[i] + a[i] - 1 \u0026lt;\u0026lt; \u0026#39; \u0026#39; \u0026lt;\u0026lt; j * a[i] \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; // int ct = (s[j * a[i] + a[i] - 1] - s[(j == 1 ? j * a[i] : j * a[i] - 1)]) * cnt[a[i]] * j; // cout \u0026lt;\u0026lt; ct \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; ans += (s[min(j * a[i] + a[i] - 1, (int)1e6)] - s[min((int)1e6, (j == 1 ? j * a[i] : j * a[i] - 1))]) * cnt[a[i]] * j; } ans += cnt[a[i]] * (cnt[a[i]] - 1) / 2; } cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } ","date":"3 June 2024","externalUrl":null,"permalink":"/posts/2024/atcoder-beginner-contest-356%E9%A2%98%E8%A7%A3de/","section":"帖子总览","summary":"","title":"Atcoder Beginner Contest 356题解（D、E）","type":"posts"},{"content":"","date":"3 June 2024","externalUrl":null,"permalink":"/tags/%E8%B0%83%E5%92%8C%E7%BA%A7%E6%95%B0/","section":"标签总览","summary":"","title":"调和级数","type":"tags"},{"content":"","date":"3 June 2024","externalUrl":null,"permalink":"/tags/%E7%BB%84%E5%90%88/","section":"标签总览","summary":"","title":"组合","type":"tags"},{"content":"","date":"25 May 2024","externalUrl":null,"permalink":"/tags/%E5%8F%8D%E6%82%94%E8%B4%AA%E5%BF%83/","section":"标签总览","summary":"","title":"反悔贪心","type":"tags"},{"content":" int n; int ans = 0; PII a[N]; // 定义priority_queue的比较函数 struct cmp { bool operator() (PII a, PII b) { return a.second \u0026gt; b.second; } }; priority_queue\u0026lt;PII, vector\u0026lt;PII\u0026gt;, cmp\u0026gt; pq; void solve() { cin \u0026gt;\u0026gt; n; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i].first \u0026gt;\u0026gt; a[i].second; } sort(a + 1, a + 1 + n); for (int i = 1; i \u0026lt;= n; i ++) { if (a[i].first \u0026gt; pq.size()) { ans += a[i].second; pq.push(a[i]); } else { if (a[i].second \u0026gt; pq.top().second) { ans -= pq.top().second; pq.pop(); pq.push(a[i]); ans += a[i].second; } } } cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } ","date":"25 May 2024","externalUrl":null,"permalink":"/posts/2024/%E5%8F%8D%E6%82%94%E8%B4%AA%E5%BF%83usaco09open-work-scheduling-g/","section":"帖子总览","summary":"","title":"反悔贪心[USACO09OPEN] Work Scheduling G","type":"posts"},{"content":" E. Divide # 首先，将一个数字\\(x\\)不断进行\\(/2\\)操作最终会变成\\(0\\), 这个操作只会执行\\(log(x)\\)次。因此可以将原数组\\(a_1, a_2, ..., a_n\\)分解为\\(a_1, a_1/2, a_1/4, ..., 0, a_2, a_2/2, a_2/4, ..., 0, ..., a_n, a_n/2, a_n/4, ..., 0\\)。\n考虑将每个数的下标作为第一维度，其真实数值作为第二维度。则以上数组再转化为\\((1, a_1), (1, a_1/2), ..., (1, a_1/4), (1, 0), (2, a_2), (2, a_2/2), (2, a_2/4), ..., (2, 0), ..., (n, a_n), (n, a_n/2), (n, a_n/4), ..., (n, 0)\\)。\n可以发现，求\\([l, r]\\)中每次将最大值\\(/2\\)，执行\\(k\\)次后的最大值等价于求上述二维度数组的第二维度的第\\(k + 1\\)大值（可重集，即倒序排完序后的第\\(k + 1\\)个元素），这可以用主席树维护。\n设\\(V\\)为原数组\\(a\\)中的最大元素的值，即\\(1e5\\), 则时间复杂度为\\(O(nlog^2V)\\)。\nint n, m; int a[N]; struct Node { int lson, rson; int cnt; }tr[N * 17 * 18]; int root[N]; int c[N]; int idx; int insert(int p, int l, int r, int x) { int q = ++idx; tr[q] = tr[p]; if (l == r) { tr[q].cnt ++; return q; } int mid = l + r \u0026gt;\u0026gt; 1; if (x \u0026lt;= mid) tr[q].lson = insert(tr[p].lson, l, mid, x); else tr[q].rson = insert(tr[p].rson, mid + 1, r, x); tr[q].cnt = tr[tr[q].lson].cnt + tr[tr[q].rson].cnt; return q; } int query(int q, int p, int l, int r, int k) { if (l == r) return l; int cnt = tr[tr[q].lson].cnt - tr[tr[p].lson].cnt; int mid = l + r \u0026gt;\u0026gt; 1; if (k \u0026lt;= cnt) return query(tr[q].lson, tr[p].lson, l, mid, k); else return query(tr[q].rson, tr[p].rson, mid + 1, r, k - cnt); } void solve() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; } for (int i = 1; i \u0026lt;= n; i ++) { c[i] = c[i - 1]; int flag = 0; while (1) { // 这里注意要把所有第一维度为i的点都一次性加入到主席树中，并且顺着一个根节点root[i]加入。 if (flag == 0) { root[i] = insert(root[i - 1], 0, 1e5, a[i]); flag = 1; } else { root[i] = insert(root[i], 0, 1e5, a[i]); } c[i] ++; if (a[i] == 0) break; a[i] = a[i] / 2; } } while (m --) { int l, r, k; cin \u0026gt;\u0026gt; l \u0026gt;\u0026gt; r \u0026gt;\u0026gt; k; if (k \u0026gt; c[r] - c[l - 1]) { cout \u0026lt;\u0026lt; 0 \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; continue; } cout \u0026lt;\u0026lt; query(root[r], root[l - 1], 0, 1e5, c[r] - c[l - 1] - k) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } } I. Integer Reaction # 这题给了个惊醒。看到求最小值最大或者最大值最小一般可以往二分上考虑。虽说知道这一点但是赛时一直认为不能二分。。。\n考虑二分时check里面是判断操作后集合\\(S_2\\)中的最小元素能否\\(\u003e=x\\)或者\\(\u003c=x\\)。\n如果是\\(\u003e=\\)，则二分序列如下所示：\n如果是\\(\u003c=\\)，则二分序列如下所示：\n至于check内部的贪心，我们只需在每次\\(c[i]\\)与\\(S_1\\)中颜色不同的时候，贪心地选择\\(S_1\\)中\\(\u003e=x-a[i]\\)的最小元素与其反应即可。因为这样可以尽可能地保证最后最小值\\(\u003e=x\\)并且\\(S_1\\)中较大的元素可以留给后面的元素与之反应。\nint n; int a[N]; int c[N]; bool check(int x) { multiset\u0026lt;PII\u0026gt; s; for (int i = 1; i \u0026lt;= n; i ++) { if (!s.empty() \u0026amp;\u0026amp; c[i] != (*s.begin()).second) { auto pos = s.lower_bound(PII(x - a[i], -1)); if (pos == s.end()) return false; else s.erase(pos); } else s.insert({a[i], c[i]}); } return true; } void solve() { cin \u0026gt;\u0026gt; n; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; } for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; c[i]; } int l = 2, r = 2e8; while (l \u0026lt; r) { int mid = l + r + 1 \u0026gt;\u0026gt; 1; if (check(mid)) l = mid; else r = mid - 1; } cout \u0026lt;\u0026lt; l \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } K. Number Deletion Game # 考虑什么情况下当前局面者必赢。如果当前只有一个数，那么当前局面者可以选择\\(y = 0\\)，这样数组中删除掉这一个数，并且不会有新的数字加入，当前局面者就必赢。\n因为每次都只能选\\(x\\)为序列最大值，不妨考虑最大值，每次操作后最大值为多少，最后可以使最大值变成\\(0\\)，即将数组清空的玩家获胜。\n如果当前最大值\\(x\\)数量是奇数，如果最大值数量\\(\u003e1\\)，那么我可以选择\\(y = 0\\)使得最大值\\(x\\)数量变成偶数，把这个偶数的局面丢给对方玩家。如果最大值数量只有\\(1\\)，那么我可以根据次大值的数量来选择\\(y\\)，如果次大值有奇数个，我就选择\\(y = x - 1\\)，如果次大值有偶数个，我就选择\\(y != x - 1\\)，总是可以把最大值数量为偶数的局面丢给对方玩家。\n而对方玩家由于只能删除一个\\(x\\)，所以他丢给我的永远是最大值数目为奇数的局面。\n故如果先手最大值数目为奇数，他必胜；否则必输。\nint n; int a[(int)1e3 + 10]; void solve() { cin \u0026gt;\u0026gt; n; int max_v = 0; for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; max_v = max(max_v, a[i]); } int cnt = 0; for (int i = 1; i \u0026lt;= n; i ++) { if (a[i] == max_v) cnt ++; } if (cnt \u0026amp; 1) cout \u0026lt;\u0026lt; \u0026#34;Alice\\n\u0026#34;; else cout \u0026lt;\u0026lt; \u0026#34;Bob\\n\u0026#34;; } H. Real Estate Is All Around # 小蓝卖出一套房子不贬值，小红卖出一套房子贬值1元，小绿卖出一套房子贬值\\(\\lceil \\frac{a_i}{10} \\rceil\\)元，那么对于所有的房子，我们最后应该是让他们尽量全被小蓝卖出，如果不能全被小蓝卖出，再抽取部分让小红卖出，如果不能全被小红、小蓝卖出，再抽取部分让小绿卖出（卖出是指先存在助理手里并且最终会被卖掉）。\n如果所有的房子再被小红、小蓝小绿卖出部分后，还剩一部分，那么我们其实可以把这剩下的一部分房子都存在小绿手中，因为放在小红或者小蓝手中的话可能会影响他们的卖房策略，但是放在小绿手里的话，因为小绿每次买房狂潮时会优先卖出自己手中房子中价值最大的，所以新放入的剩下的房子再小绿手中并不会被卖掉，因此不会影响小绿的卖房策略。\n我们可以把不卖出理解为贬值\\(a_i\\)，这样每个房子都会有贬值，求最大利润可以转化为求\\(a\\)的总和\\(-\\)最小贬值的和。\n因为每个房子都会有自己的去向，不管贬值多少肯定是都要减去一个贬值的，如果不减去贬值的话就不符合题意。所以本问题就可以转化成一个最小费用最大流的模型了。\n一种建图策略是：\n建立源点\\(S\\)和汇点\\(T\\)，建立所有房子，\\(S\\)向所有房子连一条容量为\\(1\\)，费用为\\(0\\)的边，表示每个房子最多被卖\\(1\\)次；每个房子向\\(T\\)连一条容量为\\(1\\)，费用为\\(a_i\\)的边，表示每个房子如果不卖的话贬值为\\(a_i\\)。这里建立了\\(O(n)\\)个点，\\(O(2n)\\)条边。 建立每个助理的分时点\\((i, j)\\)，其中\\(i\\)代表当前助理所在时间点，\\(j\\)代表当前助理是谁，对每个\\((i, j)\\)，向\\((i + 1, j)\\)连一条容量为\\(+ \\infty\\)，费用为\\(0\\)的边，表示这个助理手中的所有房子可以顺着时间推移传到下一个时间点。这里建立了\\(O(3n)\\)个点，\\(O(3n)\\)条边。 对每次储存机会，从\\(a_i\\)房子向\\((i, 1)\\)、\\((i, 2)\\)、\\((i, 3)\\)连接容量都为\\(1\\)、费用分别为\\(1\\)，\\(\\lceil \\frac{a_i}{10} \\rceil\\)、\\(0\\)的边，表示这个房子最多只能由一个助理卖掉，贬值分别为\\(1\\)或\\(\\lceil \\frac{a_i}{10} \\rceil\\)或\\(0\\)。这里建立了\\(O(3n)\\)条边。 对每次买房狂潮，对当前时刻的每个助理\\((i, j)\\)向\\(T\\)连一条容量为\\(1\\)、费用为\\(0\\)的边，表示每个助理可以卖出最多一个房子。这里建了\\(O(3n)\\)条边。 最终点开\\(4n\\)个，边开\\(11n\\)个即可。\n注意，虽然我们策略里面是不卖的房子交给小绿，但是实际上建图时是把不卖的房子连向了\\(T\\)，因为我们通过这样的网络流算出的策略中直接给\\(T\\)的房子，在我们实际操作中，可以给小绿，所以这样连边其实和连向小绿其实是等价的。\nconst int N = 210 * 4, M = N * 11; int n; int a[N]; int h[N], e[M * 2], ne[M * 2], f[M * 2], w[M * 2], idx; int q[N], incf[N], d[N], pre[N], st[N]; int S, T; void addEdge(int a, int b, int c, int d) { e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++; e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++; } int getID(int x, int y) { return 3 * (x - 1) + y; } bool spfa() { int hh = 0, tt = 0; memset(d, 0x3f, sizeof d); memset(incf, 0, sizeof incf); memset(st, 0, sizeof st); q[tt ++] = S, d[S] = 0, incf[S] = INF; while (hh != tt) { int t = q[hh ++]; if (hh == N) hh = 0; st[t] = 0; for (int i = h[t]; ~i; i = ne[i]) { int ver = e[i]; if (f[i] \u0026amp;\u0026amp; d[ver] \u0026gt; d[t] + w[i]) { d[ver] = d[t] + w[i]; pre[ver] = i; incf[ver] = min(incf[t], f[i]); if (!st[ver]) { q[tt ++] = ver; if (tt == N) tt = 0; st[ver] = 1; } } } } return incf[T] \u0026gt; 0; } void EK(int \u0026amp;flow, int \u0026amp;cost) { flow = cost = 0; while (spfa()) { int t = incf[T]; flow += t, cost += t * d[T]; for (int i = T; i != S; i = e[pre[i] ^ 1]) { f[pre[i]] -= t; f[pre[i] ^ 1] += t; } } } void solve() { cin \u0026gt;\u0026gt; n; memset(h, -1, sizeof h); idx = 0; int cnt = 0; int tot = 3 * n; S = ++tot, T = ++tot; int sum = 0; for (int i = 1; i \u0026lt;= n - 1; i ++) { for (int j = 1; j \u0026lt;= 3; j ++) { addEdge(getID(i, j), getID(i + 1, j), INF, 0); } } for (int i = 1; i \u0026lt;= n; i ++) { int op; cin \u0026gt;\u0026gt; op; if (op == 1) { cin \u0026gt;\u0026gt; a[++cnt]; sum += a[cnt]; addEdge(S, ++tot, 1, 0); addEdge(tot, T, 1, a[cnt]); addEdge(tot, getID(i, 1), 1, 1); addEdge(tot, getID(i, 2), 1, (a[cnt] + 9) / 10); addEdge(tot, getID(i, 3), 1, 0); } else { for (int j = 1; j \u0026lt;= 3; j ++) { addEdge(getID(i, j), T, 1, 0); } } } int flow, cost; EK(flow, cost); cout \u0026lt;\u0026lt; sum - cost \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } ","date":"24 May 2024","externalUrl":null,"permalink":"/posts/2024/2024%E6%B1%9F%E8%8B%8F%E7%9C%81%E5%A4%A7%E5%AD%A6%E7%94%9F%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E7%AB%9E%E8%B5%9Bjscpc%E9%A2%98%E8%A7%A3eikh/","section":"帖子总览","summary":"","title":"2024江苏省大学生程序设计竞赛JSCPC题解（E、I、K、H）","type":"posts"},{"content":"","date":"24 May 2024","externalUrl":null,"permalink":"/tags/acm/","section":"标签总览","summary":"","title":"Acm","type":"tags"},{"content":"","date":"24 May 2024","externalUrl":null,"permalink":"/tags/jscpc/","section":"标签总览","summary":"","title":"Jscpc","type":"tags"},{"content":"","date":"24 May 2024","externalUrl":null,"permalink":"/tags/xcpc/","section":"标签总览","summary":"","title":"Xcpc","type":"tags"},{"content":"","date":"24 May 2024","externalUrl":null,"permalink":"/tags/%E7%9C%81%E8%B5%9B/","section":"标签总览","summary":"","title":"省赛","type":"tags"},{"content":"","date":"24 May 2024","externalUrl":null,"permalink":"/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/","section":"标签总览","summary":"","title":"数据结构","type":"tags"},{"content":"","date":"24 May 2024","externalUrl":null,"permalink":"/tags/%E4%B8%BB%E5%B8%AD%E6%A0%91/","section":"标签总览","summary":"","title":"主席树","type":"tags"},{"content":" 题目大意： # 求区间\\([l, r]\\)中有多少正整数满足\\(\\phi(\\phi(n)) = \\phi(n) - 1\\)，其中\\(\\phi\\)为欧拉函数。\n解： # 设\\(y=\\phi(n)\\)，则上式变为\\(\\phi(y) = y - 1\\)，易证\\(y\\)为质数（注意\\(\\phi(1) = 1\\)，\\(1\\)与任何正整数都互质）。\n故原问题转化为求\\([l, r]\\)中有多少个正整数v满足\\(\\phi(v)\\)为质数。\n首先\\(1\\)的欧拉函数是\\(1\\)，不是质数。\n考虑欧拉函数的公式\\(\\phi(n) = n(1-\\frac{1}{p_1})(1-\\frac{1}{p_2})\\cdot...\\cdot(1-\\frac{1}{p_k})=\\frac{n}{p_1p_2\\cdot...\\cdot p_k}(p_1-1)(p_2-1)\\cdot...\\cdot(p_k-1)\\)，其中\\(p_1, p_2, \\dots, p_k\\)为\\(n\\)的所有质因数。\n注意到上式中\\(\\frac{n}{p_1p_2\\cdot...\\cdot p_k}\\)必定为一个正整数\n观察质数\\(2, 3, 5, 7, 9, 11, 13, ...\\)\n若\\(n\\)的质因数中包含\\(\\ge5\\)的数时，设该数为\\(m\\)，则\\(m - 1\\)一定是一个合数（因为这个范围内的质数一定都是奇数，且每两个质数之差\\(\\ge2\\)），故\\(n\\)的欧拉函数不是质数。\n若\\(n\\)的质因数只有\\(2\\)或\\(3\\)，设\\(n = 2^a 3^b\\)，\n若\\(b\u003e1\\)，则\\(\\frac{n}{p_1p_2\\cdot...\\cdot p_k}\\)一定是3的倍数，且\\((3 - 1) = 2\\)同时又是右边的因子，故\\(n\\)的欧拉函数一定是合数，不是质数（\\(n\\)是\\(2\\times 3\\)的倍数）。 若\\(b=0\\)，则\\(\\phi(n)=\\frac{n}{2}\\)，只有当\\(a=2\\)时\\(n\\)的欧拉函数是质数。 若\\(b=1\\)， 若\\(a\u003e1\\)，则\\(\\frac{n}{p_1p_2\\cdot...\\cdot p_k}\\)一定是2的倍数，且\\((3 - 1) = 2\\)同时又是右边的因子，故\\(n\\)的欧拉函数一定是合数，不是质数。 接下来讨论\\(a=0\\)和\\(a=1\\)，最后总结得出欧拉函数为质数的正整数只有\\(3, 4, 6\\)。\nint l, r; // 返回0~x中欧拉函数是质数的数的个数 int ans(int x) { if (x \u0026gt;= 6) return 3; if (x \u0026gt;= 4) return 2; if (x \u0026gt;= 3) return 1; return 0; } void solve() { cout \u0026lt;\u0026lt; ans(r) - ans(l - 1) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } ","date":"11 May 2024","externalUrl":null,"permalink":"/posts/2024/2024%E6%B1%9F%E8%8B%8F%E7%9C%81%E5%A4%A7%E5%AD%A6%E7%94%9F%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E5%A4%A7%E8%B5%9Bjscpc%E7%83%AD%E8%BA%AB%E8%B5%9B%E9%A2%98%E8%A7%A3b/","section":"帖子总览","summary":"","title":"2024江苏省大学生程序设计大赛（JSCPC）热身赛题解（B）","type":"posts"},{"content":"","date":"11 May 2024","externalUrl":null,"permalink":"/tags/%E6%AC%A7%E6%8B%89%E5%87%BD%E6%95%B0/","section":"标签总览","summary":"","title":"欧拉函数","type":"tags"},{"content":" 注：此教程针对的是UEFI+GPT分区表的环境下进行安装，目的是安装Windows10+ArchLinux双系统，且是单硬盘安装，本人的本地环境是intel + nvidia。\n安装前确保 # 本地已经安装好Windows10，且为arch linux分好一定空间 已经用Rufus等写盘工作制作好arch linux启动u盘 用启动盘进入archiso # 输入以下命令以增大字号\nsetfont ter-132n 输入以下命令以检测机器是否正常联网\nping archlinux.org -c 5 输入以下命令以查看计算机上的网络接口\nip -c a 输入以下命令以验证系统是否已在UEFI模式下启动\nls /sys/firmware/efi/efivars/ 如果输出一大串，则说明成功以EFI模式启动\n更新系统时间配置 # 输入以下命令来查看系统时间信息\ntimedatectl status 输入timedatectl list-timezones来列出所有国家和地区\n在上述界面中按q以退出\n以中国大陆为例，使用以下命令以更改时区设置\ntimedatectl set-timezone Asia/Shanghai 设置键盘布局 # 键盘默认布局为美式键盘en_US，基本满足需求\n如需配置键盘，可执行以下步骤\n输入以下命令以列出可用的键盘布局\nls /usr/share/kbd/keymaps/i386/qwerty 输入以下命令以载入键盘布局\nloadkeys /usr/share/kbd/keymaps/i386/qwerty/us.map.gz 硬盘分区 # 输入以下命令以列出所有硬盘分区和挂载点\nlsblk sda开头的即为windows下的分区\n输入以下命令以显示硬盘具体名称和信息\nhdparm -i /dev/sda 输入fdisk -l可查看更多信息\n输入以下命令来查看硬盘sda的所有分区，并进行创建分区\ncfdisk /dev/sda 对于arch linux，我们需要建立三个分区，root和home和swap（efi和esp已经由windows创建）\n分区如上图所示，从上到下分别是/，/home和swap分区，注意swap分区要把type改为linux swap\n更改完成后选择write，将操作写入磁盘\n接下来我们要注意格式化上述分区\n输入以下命令来建立文件系统（格式化）：\nmkfs.ext4 /dev/sda5 mkfs.ext4 /dev/sda6 mkswap /dev/sda7 swapon /dev/sda7 挂载分区（挂载给live usb环境以方便在live usb环境下通过chroot进入到主系统根目录） # 输入以下命令以挂载分区\nmount /dev/sda5 /mnt mkdir /mnt/home mount /dev/sda6 /mnt/home 输入lsblk以确认挂载情况\n自动切换到快速源（可选） # 备份mirrorlist\ncp /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak 安装rankmirrors工具\npacman -Sy pacman -S pacman-contrib 生成最快的10个服务器地址并载入配置文件\nrankmirrors -n 10 /etc/pacman.d/mirrorlist.bak \u0026gt; /etc/pacman.d/mirrorlist 这会花几分钟的时间\n如果这一步卡住了或者出了问题，可以输入以下命令回滚到初始配置\ncp /etc/pacman.d/mirrorlist.bak /etc/pacman.d/mirrorlist 安装ArchLinux # 安装archlinux到挂载到/mnt的根分区\npacstrap -i /mnt base base-devel linux linux-lts linux-headers linux-firmware intel-ucode [amd-ucode](amd的cpu) sudo nano vim git neofetch networkmanager dhcpcd pulseaudio [bluez](蓝牙模块) [wpa_supplicant](wlan) 此过程需要一定时间，请耐心等待\n完成后\n生成文件系统表（FSTAB） # 目前根目录被挂载到了/mnt, 但是当我们开机从主驱动器启动arch时，我们需要告诉系统将所有这些分区挂载到同一位置\n输入以下命令来生成fstab文件\ngenfstab -U /mnt \u0026gt;\u0026gt; /mnt/etc/fstab 现在我们可以看到所有分区及其挂载点都已正确写入\n系统配置 # 进入安装好的arch linux的根目录 # arch-chroot /mnt 设置root密码 # passwd 建立一般用户 # useradd -m light passwd light 为一般用户加root权限\nusermod -aG wheel,storage,power light 通过sudo执行root权限\nvisudo 更改后：\n%wheel ALL=(ALL) ALL Defaults timestamp_timeout=0 设置系统语言 # vim /etc/locale.gen 把需要的语言解除注释\n生成语言locale\nlocale-gen 键入以下命令以生成区域设置\necho LANG=en_US.UTF-8 \u0026gt; /etc/locale.conf 键入以下命令以导出系统语言\nexport LANG=en_US.UTF-8 设置主机名（host name） # echo ArchLinux \u0026gt; /etc/hostname 修改hosts文件内容\nvim /etc/hosts 增加的新内容为：\n127.0.0.1\tlocalhost ::1\tlocalhost 127.0.0.1\tArchLinux.localdomain\tlocalhost 设置时区或地区并与本地时间链接 # ln -sf /usr/share/zoneinfo/ 按tab tab找到所在地区Asia/Shanghai\n补全命令\nln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 同步时钟\nhwclock --systohc 安装grub # sda1是efi分区，grub将会被安装到这里\n建立efi文件夹并挂载\nmkdir /boot/efi mount /dev/sda1 /boot/efi/ 安装ntfs-3g以防后续引导不了windows\npacman -S ntfs-3g 安装grub及引导相关软件包\npacman -S grub efibootmgr dosfstools mtools 修改grub配置\nvim /etc/default/grub 如图所示，将最后一行取消注释\n安装os-prober\npacman -S os-prober 使用一些参数安装grub\ngrub-install --target=x86_64-efi --bootloader-id=grub_uefi --recheck 生成grub的config文件\ngrub-mkconfig -o /boot/grub/grub.cfg 如果没有绿色这一行的话可以等会进arch后再安装ntfs-3g并重新生成grub config来修复\n启动网络服务 # systemctl enable dhcpcd.service systemctl enable NetworkManager.service 回到archiso环境 # exit 卸载所有分区\numount -lR /mnt 重启并取出u盘\nreboot 修复windows引导 # sudo pacman -S ntfs-3g sudo pacman -S nvidia-lts sudo mount /dev/sda1 /boot/efi sudo grub-mkconfig -o /boot/grub/grub.cfg 至此windows+arch双系统制作完成\n安装GUI（KDE plasma） # 更新pacman数据库\nsudo pacman -Sy 安装xorg和plasma和sddm\nsudo pacman -S xorg xorg-xinit xterm plasma plasma-desktop [plasma-wayland-session] kde-applications kdeplasma-addons sddm 时间会比较长，请耐心等待\n配置.xinitrc\n# .xinitrc exec startkde 启用sddm\nsudo systemctl enable sddm.service 重启\nreboot 安装firefox等其他软件包\npacman -S firefox gimp htop bpytop 其他重要配置 # 换源并安装yay（aur包管理器） # # /etc/pacman.d/mirrorlist Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/\\(repo/os/\\)arch sudo pacman -Syyu # /etc/pacman.conf [archlinuxcn] # The Chinese Arch Linux communities packages. # SigLevel = Optional TrustedOnly SigLevel = Optional TrustAll # 官方源 Server = http://repo.archlinuxcn.org/$arch # 163源 Server = http://mirrors.163.com/archlinux-cn/$arch # 清华大学 Server = https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/$arch 注意以上源只能添加一个\nsudo pacman -Sy sudo pacman -S archlinuxcn-keyring sudo pacamn -S yay 安装clash # yay -S clash-premium-bin clash-verge 安装qq\nyay -S linuxqq 安装nvidia驱动\nsudo pacman -S nvidia [nvidia-lts] 安装alsamixer更好地使用耳机\nsudo pacman -S alsa-utils # 解除耳机禁音后 alsactl --file ~/.config/asound.state store # resound.sh #! /bin/bash alsactl --file ~/.config/asound.state restore 安装剪贴板（i3）\nsudo pacman -S xclip 安装中文字体\nsudo pacman -S wqy-zenhei 安装中文输入法\nsudo pacman -S fcitx5 fcitx5-im fcitx5-chinese-addons 设置环境变量\n# /etc/environment GTK_IM_MODULE=fcitx QT_IM_MODULE=fcitx XMODIFIERS=@im=fcitx INPUT_METHOD=fcitx SDL_IM_MODULE=fcitx GLFW_IM_MODULE=ibus 配置完成后重启生效，然后通过fcitx5-configtool添加pinyin即可\n安装kitty\nsudo pacman -S kitty 注：kitty可能调用不了中文输入法，可以先设置上面的环境变量或者修改kitty的配置文件，让它开启时调用输入法？（我有点想不起来了，具体看archlinux wiki里对kitty的描述）\n安装paru\nsudo pacman -S paru 安装nerd font\nsudo pacman -S ttf-meslo-nerd # 可以用pacman -Ss看仓库里都有些啥 konsole可以用glassy主题 # 安装网易云音乐\nyay -S netease-cloud-music go-musicfox 安装qq音乐\nyay -S qqmusic 开启i386支持（好像没啥用把）\nsudo dpkg --add-architecture i386 通过wine安装网易云\nsudo pacman -S wine # 然后找网易云音乐的exe安装包安装 # wine [exe文件名] 这个我用的应该是deepin的wine（deepin-wine）我先安装了32位版的微信，然后安装的网易云音乐，莫名其妙就不报错了，暂时我还不清楚是怎么回事\n知道了，用的是wine-for-wechat\n一般可以通过winecfg加入atl100 mlang msls31 riched20 usp10 msvcp60 riched32 等函数来解决报错问题\nwine装网易云前需要把需要的字体全部装上\npacman -S adobe-source-han-serif-cn-fonts noto-fonts-cjk adobe-source-han-sans-cn-fonts powerline-fonts ttf-font-awesome wqy-bitmapfont wqy-microhei wqy-microhei-lite wqy-zenhei adobe-source-code-pro-fonts [ttf-apple-emoji] 顺便提一嘴，网上说的各种解决wine乱码的办法都不如这个安装字体来的简单且有效\n安装ranger\nsudo pacman -S ranger 安装cava（cava依赖pulseaudio）\nsudo pacman -S cava 安装chrome\nyay -S google-chrome 安装微软字体\nyay -S ttf-ms-fonts 安装百度网盘\nyay -S baidunetdisk-bin kitty配置\n# ~/.config/kitty/kitty.conf background_opacity\t0.7 font_family\tMesloLGL Nerd Font bold_font\tauto italic_font\tauto bold_italic_font\tauto cava配置\n# ~/.config/cava/config gradient = 1 gradient_count = 2 gradient_color_1 = \u0026#39;#2864FF\u0026#39; gradient_color_2 = \u0026#39;#C620FF\u0026#39; 安装gparted\nsudo pacman -S gparted 安装并配置neovim\nsudo pacman -S neovim git clone https://github.com/lightmon233/nvim.git ~/.config/nvim nvim 截图工具\nyay -S ksnip shotgun KDE全局主题推荐\nPlasma-Overdose zsh及oh-my-zsh安装与配置\nzsh\nsudo pacman -S zsh oh-my-zsh\nsh -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\u0026#34; powerlevel10k(主题)\ngit clone --depth=1 https://github.com/romkatv/powerlevel10k.git \\({ZSH_CUSTOM:-\\)HOME/.oh-my-zsh/custom}/themes/powerlevel10k Set ZSH_THEME=\u0026quot;powerlevel10k/powerlevel10k\u0026quot; in ~/.zshrc.\n插件：\n自动补全\ngit clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions # ~/.zshrc plugins=( # other plugins... zsh-autosuggestions ) 语法高亮\ngit clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting # ~/.zshrc plugins=( [plugins...] zsh-syntax-highlighting) linux和windows双系统时间不同步问题解决\ntimedatectl set-local-rtc 1 --adjust-system-clock 解决chrome及其他应用程序的emoji字体显示为方块的问题\nsudo pacman -S noto-fonts-emoji v2raya\nyay -S v2raya sudo systemctl enable --now v2raya.service ","date":"10 May 2024","externalUrl":null,"permalink":"/posts/2024/archlinux+windows%E5%8D%95%E7%A1%AC%E7%9B%98%E5%AE%89%E8%A3%85%E4%BB%A5%E5%8F%8A%E5%90%8E%E7%BB%AD%E9%85%8D%E7%BD%AE-uefi+gpt/","section":"帖子总览","summary":"","title":"ArchLinux+Windows单硬盘安装以及后续配置 UEFI+GPT","type":"posts"},{"content":"","date":"10 May 2024","externalUrl":null,"permalink":"/tags/bash/","section":"标签总览","summary":"","title":"Bash","type":"tags"},{"content":"","date":"10 May 2024","externalUrl":null,"permalink":"/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/","section":"标签总览","summary":"","title":"操作系统","type":"tags"},{"content":" C++算法模板 # 基础算法 # 排序 # 快速排序 # void quickSort(int q[], int l, int r) { if (l \u0026gt;= r) return; int i = l - 1, j = r + 1, x = q[l + r \u0026gt;\u0026gt; 1]; while (i \u0026lt; j) { do i ++ ; while (q[i] \u0026lt; x); do j -- ; while (q[j] \u0026gt; x); if (i \u0026lt; j) swap(q[i], q[j]); } quickSort(q, l, j), quickSort(q, j + 1, r); } 归并排序 # void mergeSort(int q[], int l, int r) { if (l \u0026gt;= r) return; int mid = l + r \u0026gt;\u0026gt; 1; mergeSort(q, l, mid); mergeSort(q, mid + 1, r); int k = 0, i = l, j = mid + 1; while (i \u0026lt;= mid \u0026amp;\u0026amp; j \u0026lt;= r) if (q[i] \u0026lt;= q[j]) tmp[k ++ ] = q[i ++ ]; else tmp[k ++ ] = q[j ++ ]; while (i \u0026lt;= mid) tmp[k ++ ] = q[i ++ ]; while (j \u0026lt;= r) tmp[k ++ ] = q[j ++ ]; for (i = l, j = 0; i \u0026lt;= r; i ++, j ++ ) q[i] = tmp[j]; } 二分 # 整数二分 # bool check(int x) {/* ... */} // 检查x是否满足某种性质 // 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用： int bSearch_1(int l, int r) { while (l \u0026lt; r) { int mid = l + r \u0026gt;\u0026gt; 1; if (check(mid)) r = mid; // check()判断mid是否满足性质 else l = mid + 1; } return l; } // 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用： int bSearch_2(int l, int r) { while (l \u0026lt; r) { int mid = l + r + 1 \u0026gt;\u0026gt; 1; if (check(mid)) l = mid; else r = mid - 1; } return l; } 浮点数二分 # bool check(double x) {/* ... */} // 检查x是否满足某种性质 double bSearch_3(double l, double r) { const double eps = 1e-6; // eps 表示精度，取决于题目对精度的要求 while (r - l \u0026gt; eps) { double mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; } return l; } 高精度 # 高精度加法 # // C = A + B, A \u0026gt;= 0, B \u0026gt;= 0 vector\u0026lt;int\u0026gt; add(vector\u0026lt;int\u0026gt; \u0026amp;A, vector\u0026lt;int\u0026gt; \u0026amp;B) { if (A.size() \u0026lt; B.size()) return add(B, A); vector\u0026lt;int\u0026gt; C; int t = 0; for (int i = 0; i \u0026lt; A.size(); i ++ ) { t += A[i]; if (i \u0026lt; B.size()) t += B[i]; C.push_back(t % 10); t /= 10; } if (t) C.push_back(t); return C; } 高精度减法 # // C = A - B, 满足A \u0026gt;= B, A \u0026gt;= 0, B \u0026gt;= 0 vector\u0026lt;int\u0026gt; sub(vector\u0026lt;int\u0026gt; \u0026amp;A, vector\u0026lt;int\u0026gt; \u0026amp;B) { vector\u0026lt;int\u0026gt; C; for (int i = 0, t = 0; i \u0026lt; A.size(); i ++ ) { t = A[i] - t; if (i \u0026lt; B.size()) t -= B[i]; C.push_back((t + 10) % 10); if (t \u0026lt; 0) t = 1; else t = 0; } while (C.size() \u0026gt; 1 \u0026amp;\u0026amp; C.back() == 0) C.pop_back(); return C; } 高精度乘低精度 # // C = A * b, A \u0026gt;= 0, b \u0026gt;= 0 vector\u0026lt;int\u0026gt; mul(vector\u0026lt;int\u0026gt; \u0026amp;A, int b) { vector\u0026lt;int\u0026gt; C; int t = 0; for (int i = 0; i \u0026lt; A.size() || t; i ++ ) { if (i \u0026lt; A.size()) t += A[i] * b; C.push_back(t % 10); t /= 10; } while (C.size() \u0026gt; 1 \u0026amp;\u0026amp; C.back() == 0) C.pop_back(); return C; } 高精度除以低精度 # // A / b = C ... r, A \u0026gt;= 0, b \u0026gt; 0 vector\u0026lt;int\u0026gt; div(vector\u0026lt;int\u0026gt; \u0026amp;A, int b, int \u0026amp;r) { vector\u0026lt;int\u0026gt; C; r = 0; for (int i = A.size() - 1; i \u0026gt;= 0; i -- ) { r = r * 10 + A[i]; C.push_back(r / b); r %= b; } reverse(C.begin(), C.end()); while (C.size() \u0026gt; 1 \u0026amp;\u0026amp; C.back() == 0) C.pop_back(); return C; } 离散化 # vector\u0026lt;int\u0026gt; alls; //存储所有待离散化的值 sort(alls.begin(), alls.end()); //将所有值排序 alls.erase(unique(alls.begin(), alls.end()), alls.end()); //去掉重复元素 //二分求出x对应的离散化的值 int find(int x) { int l = 0, r = alls.size() - 1; while(l \u0026lt; r) { int mid = l + r \u0026gt;\u0026gt; 1; if(alls[mid] \u0026gt;= x) r = mid; else l = mid + 1; } return r + 1; } 尺取法（双指针） # for (int i = 0, j = 0; i \u0026lt; n; i ++ ) { while (j \u0026lt; i \u0026amp;\u0026amp; check(i, j)) j ++ ; // 具体问题的逻辑 } //常见问题分类： //(1) 对于一个序列，用两个指针维护一段区间 //(2) 对于两个序列，维护某种次序，比如归并排序中合并两个有序序列的操作 数论与数学知识 # c++求上取整 # \\(\\lceil \\frac{l}{p} \\rceil = \\lfloor \\frac{l+p-1}{p} \\rfloor\\)\n快速幂 # int qpow(int a, int b, int c){ int res = 1; while(b){ if(b \u0026amp; 1) res = res * a % c; //不可写成res *= a % c b \u0026gt;\u0026gt;= 1; a = a * a % c; } return res; } 逆元 # 费马小定理求逆元 # 费马小定理(Fermat\u0026rsquo;s little theorem)是数论中的一个重要定理，在1636年提出。如果p是一个质数，而整数a不是p的倍数，则有\n\\(a^{p-1} \\equiv 1 (mod \\ p)\\)\n对上式变形得\n\\(a^{p-2}a\\equiv1(mod \\ p)\\)\n则a mod p的逆元是\\(a^{p-2}\\)\n接下来通过qpow(a, p - 2, p)即可求出a mod p的逆元\n快速幂求逆元 # 基于费马小定理，求\\(a \\ mod \\ p\\)的逆元。\n要求是p是一个质数，并且p与a互质。\nint qPow(int a, int b, int k) { int ans = 1; while (b) { if (b \u0026amp; 1) ans = (LL)ans * a % k; a = (LL)a * a % k; b \u0026gt;\u0026gt;= 1; } return ans; } { int res = q_pow(a, p - 2, p); if (a % p) printf(\u0026#34;%d\\n\u0026#34;, res); else puts(\u0026#34;impossible\u0026#34;); } 线性逆元 # 证明：\n线性求解n个数字的逆元，需要找到新元素的逆元同以往求解过逆元的关系。 以下面式子举例,对于要求解逆元的k,模数为p，有： \\( p=ak + b \\\\ (b \u003c a,k)\\)\n进而有： \\(ak + b \\equiv 0\\, (mod\\,p)\\)\n两边同时乘以k-1b-1,得到：\n\\(ab^{-1} + k^{-1} \\equiv 0\\,(mod\\,p) \\\\\\)\n即 \\(k^{-1}\\equiv-ab^{-1}\\,(mod\\,p)\\)\n我们知道， \\(a=\\left \\lfloor p \\over k\\right \\rfloor \\\\\\)\n\\(b=p\\,mod\\,k\\)\n因此，有\n\\(k^{-1}\\equiv - \\left \\lfloor p\\over k\\right \\rfloor(p\\,mod\\,k)^{-1}\\)\ninv[1] = 1; for (int i = 2; i \u0026lt;= n; i++) { inv[i] = (p - p / i) * inv[p % i] % p; } 求解n个不同数字的逆元\n求解n个不同数字的逆元，可以先维护一个前缀积，其最后一项是所有数字的乘积，求该项的逆元即求所有项逆元的乘积。由于逆元的特殊性质，逆元的乘积乘上其中某个元素即会消去对应的元素，因此我们可以借助前缀积来逐个迭代处理出所有数字的逆元。\n\\((∏\\limits^n\\limits_{i=1}a_i)^{−1}≡∏\\limits^n\\limits_{i=1}a_i^{-1}(mod\\ p)\\)\n或\n\\((a_1a_2...a_n)^{-1}\\equiv a_1^{-1}a_2^{-1}...a_n^{-1}(mod\\ p)\\)\n且有\n\\(∏\\limits_{i=1}\\limits^na^{−1}_i∗a_n≡∏\\limits^{n-1}_{i=1}a_i^{-1}\\)\n于是便可以处理处所有元素的逆元：\ns[1] = a[1]; for (int i = 2; i \u0026lt;= n; i ++) { s[i] = s[i - 1] * a[i] % p; } inv[n] = qpow(s[n],p - 2); for (int i = n - 1; i \u0026gt;= 1; i --) { inv[i] = inv[i + 1] * a[i + 1] % p; } for (int i = 2; i \u0026lt;= n; i ++) { inv[i] = inv[i] * s[i - 1] % p; } 扩展欧几里得算法 # int exgcd(int a, int b, int \u0026amp;x, int \u0026amp;y) // 扩展欧几里得算法, 求x, y，使得ax + by = gcd(a, b) { if (!b) { x = 1; y = 0; return a; } //d始终都是a和b的最大公约数, 倒着传参是为了简化计算 int d = exgcd(b, a % b, y, x); y -= (a / b) * x; return d; //返回d，使得能够在求最大公约数的同时完成x，y的凑整 } 高斯消元法 # int gauss() { int c, r; for (r = 1, c = 1; c \u0026lt;= n; c ++) { int t = r; for (int i = r + 1; i \u0026lt;= n; i ++) if (fabs(a[i][c]) - fabs(a[t][c]) \u0026gt; eps) t = i; if (fabs(a[t][c]) \u0026lt; eps) continue; if (t != r) swap(a[t], a[r]); for (int i = n + 1; i \u0026gt;= c; i --) a[r][i] /= a[r][c]; for (int i = r + 1; i \u0026lt;= n; i ++) { if (fabs(a[i][c]) \u0026gt; eps) for (int j = n + 1; j \u0026gt;= c; j --) a[i][j] -= a[r][j] * a[i][c]; } r ++; } if (r \u0026lt;= n) { for (int i = r; i \u0026lt;= n; i ++) { if (fabs(a[i][n + 1]) \u0026gt; eps) return 2; // 代表有无穷多组解 } return 1; // 代表无解 } for (int i = n; i \u0026gt;= 1; i --) { for (int j = i + 1; j \u0026lt;= n; j ++) { a[i][n + 1] -= a[i][j] * a[j][n + 1]; } } // 代表有唯一组解 return 0; } 筛质数 # 朴素筛法 # void getPrimes(int n) { for (int i = 2; i \u0026lt;= n; i ++) { if (!st[i]) { primes[cnt ++] = n; } for (int j = i + i; i \u0026lt;= n; j += i) st[j] = true; } } 时间复杂度分析：\n每个数的倍数都被筛掉了，因此时间复杂度为：\n\\(\\frac{n}{2}+\\frac{n}{3}+\\dots+\\frac{n}{n}\\)\n\\(=n(\\frac{1}{2}+\\frac{1}{3}+...+\\frac{1}{n})\\)\n\\(=n(ln(n)+c)\\)\t其中c是一个欧拉常数，是一个无限不循环小数，值是0.577左右\n质数定理 # 1~n中有\\(\\frac{n}{ln(n)}\\)个质数\n我们可以只用把质数的倍数删掉，这样粗劣的时间复杂度就是\\(\\frac{n(ln(n))}{ln(n)}\\)，但是这只是个粗略的估计，这个估计是不对的，真实的时间复杂度是\\(O(nlog(log(n)))\\)。\n这个算法也就是下面的埃氏筛法。\n埃氏筛法 # //O(nloglogn) int getPrimes(int n){ int idx = 1; for(int i = 2; i \u0026lt;= n; i ++){ if(!st[i]){ prime[idx++] = i; for(int j = i + i; j \u0026lt;= n; j += i) st[j] = 1; } } return idx - 1; } 线性筛法 # 线性筛法在10^7^次方的情况下会比埃氏筛法快一倍左右，在10^6^的情况下两个算法速度差不多。\n整体思路：n只会被它的最小质因子筛掉\nvoid getPrimes(int n){ for (int i = 2; i \u0026lt;= n; i++) { if (!st[i]) primes[cnt++] = i; // primes[j] \u0026lt;= n / i即primes * i \u0026lt;= n，即x \u0026lt;= n for (int j = 0; primes[j] \u0026lt;= n / i; j ++) { // 当还没有执行到下面的break语句时 // 由于我们是从小到大枚举所有质数的，且还没有枚举到i的最小质因子 // 所以当前的primes[j]一定是primes[j] * i的最小质因子 // 所以就把primes[j] * i筛掉 st[primes[j] * i] = true; // 当这段代码执行的时候就意味着primes[j]一定是i的最小质因子 // primes[j]是从小到大枚举的质数，当第一次满足i % primes[j] == 0的时候 // 说明primes[j]就一定是i的最小质因子 // 同时此时primes[j]也一定是primes[j] * i的最小质因子 // 因此上面的筛成立 // 当如果此时再继续向后枚举质数，接下来的质数就不是primes[j] * i的最小质因子了 // 因此要即时break掉 if(i % primes[j] == 0) break; } } } 证明线性：\n对于一个合数x，假设primes[j]是x的最小质因子，当i枚举到x/primes[j]的时候，我们就可以在4到18行把x给筛掉。\n第五行的判断条件不需要加j \u0026lt;= cnt\n因为当i是合数的时候，primes[j]一定会在枚举到i的最小质因子时停下来，而i的最小质因子一定是小于i的，会在i之前被标记为primes，放到primes数组中来。\n当i是质数的时候，当primes[j]=i的时候，枚举也会停下来。（是在break的时候停下来）\n欧拉函数 # \\(\\phi(n)\\): 1~n中和n互质的数的个数\n\\(\\phi(6)=2\\)\n一个数可以写成很多个质数的乘积的形式：\n\\(N=P_1^{\\alpha_1}P_2^{\\alpha_2}...P_k^{\\alpha_k}\\)\n\\(\\phi(N)=N(1-\\frac{1}{p_1})(1-\\frac{1}{p_2})...(1-\\frac{1}{{p_k}})\\)\n上式的证明会用到容斥原理。\n那么如何计算从1~N中和N互质的数的个数呢\n从1~N中去掉p1，p2，…，pk的所有倍数\n这里面就会多去一部分数，比如一个数可能既是p1的倍数又是p2的倍数\n加上所有pi*pj的倍数\n\\(N-\\frac N {P_1} - \\frac {N}{P_1} - ... - \\frac N {P_1} + \\frac N {P_1P_2} + \\frac N {P_1 P_3} + ...\\)\n减去所有三个质数的倍数\n…\n然后把最上面的式子展开，会发现这两个式子是相等的。\n时间复杂度：时间复杂度瓶颈在分解质因数上，分解质因数的时间复杂度是\\(O(\\sqrt N)\\)\n代码：\nint euler(int a) { int res = a; for (int i = 2; i \u0026lt;= a / i; i ++) { if (a % i == 0) { res = res / i * (i - 1); while (a % i == 0) a /= i; } } if (a \u0026gt; 1) res = res / a * (a - 1); return res; } 筛法求欧拉函数 # \\(φ(ab)= \\frac {φ(a)φ(b)gcd(a,b)} {φ(gcd(a,b))}\\)\nint getEuler(int n){ phi[1] = 1; for(int i = 2; i \u0026lt;= n; i ++){ if(!st[i]){ prime[cnt ++] = i; phi[i] = i - 1; } for(int j = 0; prime[j] \u0026lt;= n / i; j ++){ st[prime[j] * i] = true; if(i % prime[j] == 0){ // 此时pj是i的最小质因子，i的质因子中有j了，所以pj*i的所有(1-pj*i的质因子)都在phi[i]中计算过了，因此两者的区别就只有式子开头的N，所以phi[pj * i] = phi[i] * prime[j] phi[prime[j] * i] = phi[i] * prime[j]; break; } // 这里pj - 1就是(1 - 1 / pj) * pj phi[prime[j] * i] = phi[i] * (prime[j] - 1); } } int res = 0; for(int i = 1; i \u0026lt;= n; i ++){ res += phi[i]; } return res; } 组合数 # 组合数1 # 主要思想：\\(C_a^b = C_{a-1}^{b-1} \\times C_{a - 1}^{b}\\)\n时间复杂度：O(n^2)\nint c[N][N]; void init(){ for(int i = 0; i \u0026lt; N; i ++){ for(int j = 0; j \u0026lt;= i; j ++){ if(j == 0) c[i][j] = 1; else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod; } } } 组合数2 # 预处理阶乘\nint fact[N], infact[N];//存的分别是i的阶乘及其逆元 // 求a^k mod p int qPow(int a, int k, int p) { int res = 1; while (k) { if (k \u0026amp; 1) res = (LL)res * a % p; a = (LL)a * a % p; k \u0026gt;\u0026gt;= 1; } return res; } { fact[0] = infact[0] = 1; for (int i = 1; i \u0026lt; N; i ++) { fact[i] = (LL) fact[i - 1] * i % mod; infact[i] = (LL) infact[i - 1] * qmi(i, mod - 2, mod) % mod; } int a, b; scanf(\u0026#34;%d%d\u0026#34;, \u0026amp;a, \u0026amp;b); //这里要及时取模，否则会爆long long printf(\u0026#34;%d\\n\u0026#34;, (LL) fact[a] * infact[b] % mod * infact[a - b] % mod); } } 博弈论 # 必胜态：从这个状态总是有某种方式走到一个必败态\n必败态：从这个状态无论怎么走都是会走到必胜态\n如果先手初始处于必败态，那么先手必败，反之先手必胜\nSG函数 # 首先，我们定义一个mex函数（或者称为mex操作）：\n\\(mex(A)\\): 返回A集合中未曾出现过的最小的自然数\n\\(sg(x)\\): 如果处于最终态，则返回0；否则返回该节点可以到达的状态（A集合）中未曾出现过的最小的自然数\n图论 # 最短路 # Dijkstra求最短路(朴素版) # Dijkstra算法总体流程：\ndist[1] = 0, dist[i] = +\\(\\infty\\) for i : 1 ~ n t\\(\\leftarrow\\)不在s中的距离最近的点（其中s表示当前已确定最短距离的点的集合）\\(\\color{green}{O(n^2)}\\) s\\(\\leftarrow\\) t \\(\\ \\color{green}{n \\times O(1)}\\) 用t更新其他点的距离 \\(\\ \\color{green}{O(m)}\\) 朴素版dijkstra算法时间复杂度是O（\\(n^2\\)）（其中n代表图中点的个数）\n因此适合用于稠密图，即边多点少的图\nint n, m; int g[N][N]; //存储图，g[i][j]的值是i到j的距离，因为是稠密图，所以用邻接矩阵来存 int dist[N]; //dist[i]存的是i节点到1节点的最短距离 int st[N]; //st[i]为true说明i节点的值是最小距离 int dijkstra(){ memset(dist, 0x3f, sizeof dist); dist[1] = 0; //循环n-1次，即更新n-1次st[?]，这样就把1到1~n这n个节点的最短路都算出来了 for(int i = 1; i \u0026lt;= n - 1; i ++){ int t = -1; //找到未存入最短值且到1节点距离最短的节点t for(int j = 1; j \u0026lt;= n; j++){ if(!st[j] \u0026amp;\u0026amp; (t == -1 || dist[t] \u0026gt; dist[j])) t = j; } //if(t == n) goto end;这个优化只有这题能加，意思是当求得1到n的最短路时，即可break，因为这就是题目所求 st[t] = true; //用t节点来更新其他节点（不是t的邻点的话他的g[t][j]就为无穷） for(int j = 1; j \u0026lt;= n; j ++){ dist[j] = min(dist[j], dist[t] + g[t][j]); } } //end: if(dist[n] == 0x3f3f3f3f) return -1; return dist[n]; } 堆优化的Dijkstra算法 # 堆优化版Dijkstra算法总体流程：\ndist[1] = 0, dist[i] = +\\(\\infty\\) for i : 1 ~ n t\\(\\leftarrow\\)不在s中的距离最近的点（其中s表示当前已确定最短距离的点的集合）\\(\\color{green}{O(n)}\\) s\\(\\leftarrow\\) t \\(\\ \\color{green}{O(n)}\\) 用t更新其他点的距离 \\(\\ \\color{green}{O(mlog(n))}\\) 堆优化版dijkstra算法时间复杂度是O（\\(mlog(n)\\)）（其中n代表图中点的个数, m代表边的个数）\n\\(\\color {red} {因此适合用于稀疏图，即边少点多的图}\\)\ntypedef pair\u0026lt;int, int\u0026gt; PII; int h[N], e[N], ne[N], idx; int w[N]; int dist[N]; bool st[N]; int n, m; void add(int x, int y, int c){ w[idx] = c; e[idx] = y; ne[idx] = h[x]; h[x] = idx++; } int dijkstra(){ memset(dist, 0x3f, sizeof dist); dist[1] = 0; priority_queue\u0026lt;PII, vector\u0026lt;PII\u0026gt;, greater\u0026lt;PII\u0026gt;\u0026gt; heap; heap.push({0, 1}); while(heap.size()){ PII k = heap.top(); heap.pop(); int ver = k.second, distance = k.first; if(st[ver]) continue; st[ver] = true; for(int i = h[ver]; i != -1; i = ne[i]){ int j = e[i]; if(dist[j] \u0026gt; distance + w[i]){ dist[j] = distance + w[i]; heap.push({dist[j], j}); } } } if(dist[n] == 0x3f3f3f3f) return -1; else return dist[n]; } SPFA # spfa算法\n底子是： for 所有边a，b，w\ndist[b] = min(dist[b], dist[a] + w)\n流程是： queue \\(\\leftarrow\\) 1 while queue不空：\nt\\(\\leftarrow\\)queue.front(), q.pop();\n更新t的所有出边t\\(\\stackrel{w}{\\longrightarrow}\\)b, q\\(\\leftarrow\\)b;\nspfa求最短路不能处理有自环负权和负权环的情况，这两种情况会在while循环中卡住，所以这题数据不太强 1.stl queue\nint n, m; int h[N], w[N], e[N], ne[N], idx; int dist[N]; bool st[N]; // st数组是判断当前节点是否在队列当中，防止重复入队 void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; } int spfa() { memset(dist, 0x3f, sizeof dist); dist[1] = 0; queue\u0026lt;int\u0026gt; q; q.push(1); st[1] = true; while (q.size()) { int t = q.front(); q.pop(); st[t] = false; for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; if (dist[j] \u0026gt; dist[t] + w[i]) { dist[j] = dist[t] + w[i]; if (!st[j]) { q.push(j); st[j] = true; } } } } return dist[n]; } { memset(h, -1, sizeof h); int t = spfa(); if (t == 0x3f3f3f3f) puts(\u0026#34;impossible\u0026#34;); else printf(\u0026#34;%d\\n\u0026#34;, t); } 循环队列\nint n, m, S, T; int h[N], e[M], w[M], ne[M], idx; int dist[N], q[N]; bool st[N]; void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; } void spfa() { memset(dist, 0x3f, sizeof dist); dist[S] = 0; // hh指向队头元素，tt指向队尾元素的后一个元素。 int hh = 0, tt = 1; q[0] = S, st[S] = true; while(hh != tt){ int t = q[hh++]; if(hh == N) hh = 0; st[t] = false; for(int i = h[t]; ~i; i = ne[i]){ int j = e[i]; if(dist[j] \u0026gt; dist[t] + w[i]){ dist[j] = dist[t] + w[i]; if(!st[j]){ q[tt++] = j; if(tt == N) tt = 0; st[j] = true; } } } } } { memset(h, -1, sizeof h); spfa(); cout \u0026lt;\u0026lt; dist[T] \u0026lt;\u0026lt; endl; } 最小生成树 # kruskal算法 # int n, m; int p[N]; struct Edge { int a, b, w; bool operator\u0026lt;(const Edge \u0026amp;W) const { return w \u0026lt; W.w; } }edges[M]; int find(int x) { if (p[x] != x) p[x] = find(p[x]); // 路径压缩 return p[x]; } int kruskal() { sort(edges, edges + m); // 将边按照权重从小到大排序 for (int i = 1; i \u0026lt;= n; i ++ ) p[i] = i; // 初始化并查集 int res = 0, cnt = 0; for (int i = 0; i \u0026lt; m; i ++ ) { int a = edges[i].a, b = edges[i].b, w = edges[i].w; a = find(a), b = find(b); // 判断两个节点是否在同一个连通块中 if (a != b) { p[a] = b; // 将两个连通块合并 res += w; // 将边权重累加到结果中 cnt ++ ; // 记录加入生成树的边的数量 } } if (cnt \u0026lt; n - 1) return INF; // 判断生成树中是否包含n-1条边 return res; } { for (int i = 0; i \u0026lt; m; i ++ ) { int a, b, w; scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;a, \u0026amp;b, \u0026amp;w); edges[i] = {a, b, w}; } int t = kruskal(); if (t == INF) puts(\u0026#34;impossible\u0026#34;); // 无法生成最小生成树 else printf(\u0026#34;%d\\n\u0026#34;, t); // 输出最小生成树的权重 } Kruskal算法是一种贪心算法，其正确性可以通过贪心选择性质的证明得到。\n假设我们要构造一个无向连通图的最小生成树。我们定义一个边集\\(E\\)为“好的”，如果它是某棵最小生成树的子集。我们定义一个边集\\(E\\)为“坏的”，如果它包含了某个环上的边。\n首先我们考虑一个引理：对于任何的连通图，\\(E\\)为“好的”，则\\(E\\)中的边是没有环的。\n证明：如果\\(E\\)中存在环，则我们可以从中删去任意一条边，得到一个更小的\\(E\\)集合，仍然满足“好的”性质。因此，在“好的”边集中，所有的边都没有构成环。\n然后我们考虑另一个引理：对于任何的连通图，令\\(E\\)为“好的”边集合，令\\(e\\)为一条“好的”边，并令\\(E'=E \\backslash {e}\\)。则\\(E'\\)为某棵生成树的子集。\n证明：因为\\(e\\)为“好的”边，所以\\(E\\)中不包含\\(e\\)的时候，\\(E\\)仍然是连通的。因此，\\(E'\\)也是连通的。由于\\(E'\\)中没有环，所以它也是无向无环图，也就是一棵树。此外，\\(E'\\)中有\\(n-1\\)条边，与任何生成树的边数相同，所以\\(E'\\)也是某棵生成树的子集。\n有了上面的两个引理，我们可以得到Kruskal算法的正确性证明：\n初始化\\(E\\)为空集； 将所有边按照权值从小到大排序； 依次考虑每条边\\(e\\)，如果将其加入\\(E\\)不会产生环，则将其加入\\(E\\)中； 当加入了\\(n-1\\)条边后，停止算法。此时\\(E\\)即为一棵生成树。 根据引理1，\\(E\\)中所有的边没有环；根据引理2，算法停止时，\\(E\\)为某棵生成树的子集；根据Kruskal算法的贪心选择性质，我们知道\\(E\\)为全局最优的“好的”边集合，因此\\(E\\)中的边必定构成了一棵最小生成树。\n因此，Kruskal算法的正确性得证。\nLCA(最近公共祖先) # 倍增法求LCA的步骤为：\n把两个点跳到同一层, 即把下面的x跳到和y同一层 在depth[x] == depth[y]之后， x == y 则该点就是x和y的最近公共祖先 x != y 即他俩同层但不相同，则继续让两个点同时往上跳，一直跳到他们的最近公共祖先的下面一层 这里跳到最近公共祖先的下一层是因为不这样做的话有可能我们某一次跳过头了，跳过了lca, 来到了lca的某一个祖宗节点上，这样我们就无法判断lca在现处节点下面的哪一层上了\n// depth表示某个节点在树中的深度，其中根节点的dpeth为1, 越往下深度越大。 // fa[i][k]指节点i向上走2^k布所能到达的节点。 // fa的第二个参数，是向上走的最大距离取log下取整，这里是对点数4e4取log得15 int depth[N], fa[N][16]; // 通过宽搜来初始化depth和fa数组。 void bfs(int root) { queue\u0026lt;int\u0026gt; q; memset(depth, 0x3f, sizeof depth); // 这里depth[0] = 0起哨兵作用，以防跳出根节点之上。 depth[0] = 0, depth[root] = 1; q.push(root); while (!q.empty()) { int t = q.front(); q.pop(); for (int i = h[t]; ~i; i = ne[i]) { int j = e[i]; if (depth[j] \u0026gt; depth[t] + 1) { depth[j] = depth[t] + 1; q.push(j); fa[j][0] = t; for (int k = 1; k \u0026lt;= 15; k ++) { fa[j][k] = fa[fa[j][k - 1]][k - 1]; } } } } } int lca(int a, int b) { if (depth[a] \u0026lt; depth[b]) swap(a, b); for (int k = 15; k \u0026gt;= 0; k --) { if (depth[fa[a][k]] \u0026gt;= depth[b]) { a = fa[a][k]; } // 使a尽量跳到和b同层，并最终一定能跳到。 } if (a == b) return a; for (int k = 15; k \u0026gt;= 0; k --) { if (fa[a][k] != fa[b][k]) { a = fa[a][k]; b = fa[b][k]; } } return fa[a][0]; } dfs版本预处理 # 同时顺便求出每个节点向上对应步数的最小的边权\nvoid dfs(int u, int fa) { depth[u] = depth[fa] + 1; f[u][0] = fa; for (int i = 1; 1 \u0026lt;\u0026lt; i \u0026lt; depth[u]; i ++) { f[u][i] = f[f[u][i - 1]][i - 1]; minv[u][i] = min(minv[f[u][i - 1]][i - 1], minv[u][i - 1]); } for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (j == fa) continue; minv[j][0] = w[i]; dfs(j, u); } } 欧拉路径与欧拉回路 # 欧拉路径：如果在一张图中，可以从一点出发遍历所有的边，每条边只能遍历一次，那么遍历过程中的这条路径就叫做欧拉路径。\n欧拉回路：通过图中所有边恰好一次且行遍所有顶点的回路称为欧拉回路。\ndfs # dfs变形：在普通的dfs中，我们通常选择对点进行判重，但在求欧拉路径的时候，因为存在环，所以一个点可能被遍历多次，因此我们采取对边判重。\ndfs删边优化：因为欧拉路径中每条边只走一次，因此我们可以在每条边被遍历之后把它删除，以节省判重时间，达到时间上的优化。\n注意遍历时要写成i = h[u]，可以考虑节点1有3个自环(有向边)。 写i = h[u]的话在遍历完三条边回溯的时候，接下来要遍历的边都是h[u] = -1，即遍历结束，保证了每条边都被遍历一次，且时间是线性的。 如果写i = ne[i]的话，则在回溯到第二条边的遍历for循环时，接下来还是会枚举到ne[i]，即第三条边（之后会在第三条边的for循环中continue出来）。\n上图即为dfs的遍历顺序，可以发现蓝色的边倒序即是欧拉路径。\nint n, m, type, cnt, ans[M]; int used[2 * M]; int in[N], out[N]; int h[N], e[2 * M], ne[2 * M], idx; void addEdge(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx ++; } void dfs(int u) { // 注意这里要写成i = h[u] for (int i = h[u]; ~i; i = h[u]) { if (used[i]) { h[u] = ne[i]; continue; } used[i] = 1; if (type == 1) used[i ^ 1] = 1; // 若为无向图，则将反向边判重 h[u] = ne[i]; dfs(e[i]); if (type == 1) { if (i % 2) ans[++cnt] = -(i + 1) / 2; else ans[++cnt] = (i + 2) / 2; } else ans[++cnt] = i + 1; } } fleury # 感觉和dfs算法基本一致，该算法在有桥的时候时间复杂度为\\(O(m^2)\\)。\nvector\u0026lt;PII\u0026gt; G[10000]; void dfs(int u) { while (G[u].size()) { pii p = G[u].back(); G[u].pop_back(); if (vis[p.scd]) { continue; } vis[p.scd] = 1; dfs(p.fst); } stk[++top] = pr[u]; } 数据结构 # 链表 # 单链表（静态） # //head是头指针，e存权值，ne存下一个节点，idx分配节点 int head, e[N], ne[N], idx; void init(){ head = -1; //链表中最后一个节点的ne始终是-1 idx = 0; } void add_to_head(int x){ //在头结点前建立节点 e[idx] = x; ne[idx] = head; head = idx ++; } void add_to_k(int k, int x){ e[idx] = x; ne[idx] = ne[k]; ne[k] = idx ++; } void remove(int k){ ne[k] = ne[ne[k]]; } 双链表 # int r[N], l[N], e[N], idx; //1节点和0节点相当于变相的head指针，没有e值 void init(){ r[0] = 1; l[1] = 0; idx = 2; } //在k节点的右边插入值为x的节点 void insert(int k, int x){ e[idx] = x; l[idx] = k; r[idx] = r[k]; l[r[k]] = idx; //必须先做这个，否则后面的r[k]会被修改掉 r[k] = idx; idx ++; } void remove(int k){ r[l[k]] = r[k]; l[r[k]] = l[k]; } 队列 # 一般队列 # int hh = 0, tt = -1; int q[N]; void push(int x) { q[++tt] = x; } void pop() { hh++; } int front() { return q[hh]; } 循环队列 # 主要在bfs及spfa算法中使用\nvoid bfs(int x) { int q[N]; int hh = 0, tt = 0; q[tt++] = x; st[x] = true; while (hh != tt) { int t = q[hh++]; if (hh == N) hh = 0; for () { } } } 单调队列 # 以滑动窗口为例\nint n, k; // k代表滑动窗口的长度 int a[N], q[N]; int hh, tt; { hh = 0, tt = -1; for (int i = 1; i \u0026lt;= n; i ++) { if (hh \u0026lt;= tt \u0026amp;\u0026amp; q[hh] \u0026lt; i - k + 1) hh ++; while (hh \u0026lt;= tt \u0026amp;\u0026amp; a[q[tt]] \u0026gt;= a[i]) tt --; q[++ tt] = i; if(i \u0026gt;= k) printf(\u0026#34;%d \u0026#34;, a[q[hh]]); } } 栈 # 一般栈 # int m; int stack[N], tt; void push(int x) { stack[++ tt] = x; } void pop() { tt --; } bool empty() { if (tt \u0026gt; 0) return false; return true; } int query() { return stack[tt]; } 表达式求值 # stack\u0026lt;int\u0026gt; num; stack\u0026lt;char\u0026gt; op; void eval(){ auto b = num.top(); num.pop(); auto a = num.top(); num.pop(); auto c = op.top(); op.pop(); int x; if(c == \u0026#39;+\u0026#39;) x = a + b; else if (c == \u0026#39;-\u0026#39;) x = a - b; else if (c == \u0026#39;*\u0026#39;) x = a * b; else x = a / b; num.push(x); } { unordered_map\u0026lt;char, int\u0026gt; pr{{\u0026#39;+\u0026#39;, 1}, {\u0026#39;-\u0026#39;, 1}, {\u0026#39;*\u0026#39;, 2}, {\u0026#39;/\u0026#39;, 2}}; string str; cin \u0026gt;\u0026gt; str; for (int i = 0; i \u0026lt; str.size(); i ++) { auto c = str[i]; if (isdigit(c)) { int x = 0, j = i; while (j \u0026lt; str.size() \u0026amp;\u0026amp; isdigit(str[j])) x = x * 10 + str[j ++] - \u0026#39;0\u0026#39;; i = j - 1; num.push(x); } else if (c == \u0026#39;(\u0026#39;) op.push(c); else if (c == \u0026#39;)\u0026#39;) { while (op.top() != \u0026#39;(\u0026#39;) eval(); op.pop(); } else { while (op.size() \u0026amp;\u0026amp; pr[op.top()] \u0026gt;= pr[c]) eval(); op.push(c); } } while (op.size()) eval(); cout \u0026lt;\u0026lt; num.top() \u0026lt;\u0026lt; endl; } 单调栈 # int stk[N], tt; int n; int a[N]; { cin \u0026gt;\u0026gt; n; for (int i = 1; i \u0026lt;= n; i ++) scanf(\u0026#34;%d\u0026#34;, \u0026amp;a[i]); for (int i = 1; i \u0026lt;= n; i ++) { while (tt \u0026amp;\u0026amp; stk[tt] \u0026gt;= a[i]) tt --; if (tt) cout \u0026lt;\u0026lt; stk[tt] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; else cout \u0026lt;\u0026lt; -1 \u0026lt;\u0026lt; \u0026#39; \u0026#39;; stk[++ tt] = a[i]; } } KMP # int n, m; char s[1000010], p[100010]; int ne[100010]; // ne[1] = 0, ne[2]的值不确定，注意next数组的定义可能会有多种 // 有些定义下的ne[2]的值必定是1 { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; p + 1 \u0026gt;\u0026gt; m \u0026gt;\u0026gt; s + 1; //构造next数组，存储最大公共前后缀 //i从2开始，默认ne[1] = 0. for (int i = 2, j = 0; i \u0026lt;= n; i ++) { //求ne[i]的时候ne[i - 1]已经求出来了, 此时j和i-1对齐 while (j \u0026amp;\u0026amp; p[i] != p[j + 1]) j = ne[j]; //j回滚到当前前缀串的最大公共前缀串 if (p[i] == p[j + 1]) j ++; //p[i]与p[j + 1]匹配成功，可以匹配下一个位置, 这里让匹配好的j(p串匹配好的字符数)变成j + 1 ne[i] = j; } //匹配sp for (int i = 1, j = 0; i \u0026lt;= m; i ++) { while (j \u0026amp;\u0026amp; s[i] != p[j + 1]) j = ne[j]; if (s[i] == p[j + 1]) j++; if (j == n) { printf(\u0026#34;%d \u0026#34;, i - j); j = ne[j]; } } } TRIE # const int N = 100010; //存的是所有节点的最大个数 int n; int son[N][26], cnt[N], idx; void insert(char str[]) { int p = 0; // 每次从根节点开始找 for (int i = 0; str[i]; i ++) { int u = str[i] - \u0026#39;a\u0026#39;; if (!son[p][u]) son[p][u] = ++idx; p = son[p][u]; } cnt[p] ++; //标记不是打在要插入的字符串的最后一个字母对应的节点的下一个节点上的*** } int query(char str[]) { int p = 0; for (int i = 0; str[i]; i ++) { int u = str[i] - \u0026#39;a\u0026#39;; if (!son[p][u]) return 0; p = son[p][u]; } return cnt[p]; } 堆 # int n; int h[N], pk[N], kp[N]; int idx, len; //idx是用来维护第几个插入堆中的点 void hswap(int a, int b){ swap(h[a], h[b]); swap(pk[a], pk[b]); swap(kp[pk[a]], kp[pk[b]]); } void up(int u){ while(u / 2 \u0026amp;\u0026amp; h[u] \u0026lt; h[u / 2]){ hswap(u, u / 2); u /= 2; } } void down(int u){ int t = u; if(u * 2 \u0026lt;= len \u0026amp;\u0026amp; h[u * 2] \u0026lt; h[t]) t = u * 2; if(u * 2 + 1 \u0026lt;= len \u0026amp;\u0026amp; h[u * 2 + 1] \u0026lt; h[t]) t = u * 2 + 1; if(u != t){ hswap(u, t); down(t); } } void insert(int x){ h[++len] = x; pk[len] = ++idx; kp[idx] = len; up(len); } void delmin(){ hswap(1, len); len --; down(1); } void del(int u){ hswap(u, len); len --; down(u); up(u); } // void del(int k){ // int t = kp[k]; // hswap(kp[k], len); // len --; // up(t); // down(t); // } void change (int u, int x){ h[u] = x; up(u), down(u); } 并查集 # //p[i]存储i节点的祖宗节点，d[i]存储i节点到祖宗节点的距离 int p[N], d[N]; //初始化 void init() { for (int i = 0; i \u0026lt;= n; i ++) { p[i] = i; d[i] = 0; } } //找祖宗节点，同时更新d数组 int find(int x) { if (x != p[x]) { int t = find(p[x]); //x到祖宗的距离=x到p[x]的距离+p[x]到祖宗的距离 //这里的距离更新仅仅是更新的d[x]正确（找祖先）的情况下，在做并差集合并时还得手动更新 d[x] = d[x] + d[p[x]]; p[x] = t; } return p[x]; } int find (int x) { if(x != p[x]) p[x] = find(p[x]); return p[x]; } 哈希表 # 模拟 # 拉链法 # int h[N], e[N], ne[N], idx; void insert(int x) { int k = (x % N + N) % N; e[idx] = x; ne[idx] = h[k]; h[k] = idx++; } bool find(int x) { int k = (x % N + N) % N; for(int i = h[k]; i != -1; i = ne[i]){ if(e[i] == x) return true; } return false; } 开放寻址法 # const int N = 200003, null = 0x3f3f3f3f; int a[N]; int find(int x) { int k = (x % N + N) % N; while(a[k] != null \u0026amp;\u0026amp; a[k] != x){ k ++; if(k == N) k = 0; } return k; } 树状数组和线段树 # 树状数组 # “树状数组的下标从1开始,不可以从0开始,因为lowbit(0)=0时会出现死循环”\nint n, m; int a[N]; int tr[N]; int lowbit(int x) { //lowbit这个函数的功能就是求某一个数的二进制表示中最低的一位1，举个例子， //x = 6，它的二进制为110，那么lowbit(x)就返回2，因为最后一位1表示2 return x \u0026amp; -x; } void add(int x, int v) { for(int i = x; i \u0026lt;= n; i += lowbit(i)) tr[i] += v; } int query(int x) { //求1~x的和 int res = 0; for(int i = x; i; i -= lowbit(i)) res += tr[i]; return res; } 线段树 # int n, m; int w[N]; struct Node{ int l, r; int sum; }tr[N * 4]; void pushup(int u) { tr[u].sum = tr[u \u0026lt;\u0026lt; 1].sum + tr[u \u0026lt;\u0026lt; 1 | 1].sum; } void build(int u, int l, int r) { if (l == r) tr[u] = {l, r, w[r]}; else { tr[u] = {l, r}; int mid = l + r \u0026gt;\u0026gt; 1; build(u \u0026lt;\u0026lt; 1, l, mid), build(u \u0026lt;\u0026lt; 1 | 1, mid + 1, r); pushup(u); } } int query(int u, int l, int r) { if (tr[u].l \u0026gt;= l \u0026amp;\u0026amp; tr[u].r \u0026lt;= r) return tr[u].sum; int mid = tr[u].l + tr[u].r \u0026gt;\u0026gt; 1; int sum = 0; if (l \u0026lt;= mid) sum = query(u \u0026lt;\u0026lt; 1, l, r); if (r \u0026gt; mid) sum += query(u \u0026lt;\u0026lt; 1 | 1, l, r); return sum; } void modify(int u, int x, int v){ if (tr[u].l == tr[u].r) tr[u].sum += v; else{ int mid = tr[u].l + tr[u].r \u0026gt;\u0026gt; 1; if (x \u0026lt;= mid) modify(u \u0026lt;\u0026lt; 1, x, v); else modify(u \u0026lt;\u0026lt; 1 | 1, x, v); pushup(u); } } 树链剖分（重链剖分） # 将树中任意一条路径转化为\\(O(logn)\\)段连续区间\n将一棵树转化为一个序列 树中路径转化为\\(logn\\)段连续区间 dfs序（dfn）：优先遍历重儿子，即可保证重链上所有点的编号是连续的，注意这里的dfs是按照优先遍历重儿子来的dfs序\n定理：树中任意一条路径均可拆分成\\(O(logn)\\)条重链，即可拆分成\\(O(logn)\\)段连续区间\n重儿子：某个节点的所有子树中size最大的那个的父节点是当前节点的重儿子，其他节点为轻儿子\n重边：重儿子往他的父节点去的边\n轻边：其他所有边\n重链：重边构成的极大的一条链\n重儿子所在的重链的top是他往上的轻儿子，轻儿子所在的重链的top是他自己\nint w[N], h[N], e[M], ne[M], idx; int id[N], nw[N], cnt; int dep[N], sz[N], top[N], fa[N], son[N]; // 预处理出来每个点的重儿子，深度，所在子数大小 void dfs1(int u, int father, int depth) { dep[u] = depth, fa[u] = father, sz[u] = 1; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (j == father) continue; dfs1(j, u, depth + 1); sz[u] += sz[j]; if (sz[son[u]] \u0026lt; sz[j]) son[u] = j; } } // 算出每个节点的dfn序，每个节点所在的重链的顶点是谁 void dfs2(int u, int t) { id[u] = ++cnt, nw[cnt] = w[u], top[u] = t; if (!son[u]) return; dfs2(son[u], t); for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (j == fa[u] || j == son[u]) continue; dfs2(j, u); } } 树上启发式合并 # 用同一套数组（当然也可以是其他数据结构）来记录所有子树的信息，在更新新的子树信息前把当前子树的信息存下来。\n一般地思考的话，我们可以在每次算完当前子树信息，将要回溯的时候，把数组全部清空，但是这样时间复杂度会达到n^2级别，因此考虑优化。\n结论：每次先算轻儿子为根的子树，再算重儿子为根的子树，利用重儿子为根的子树的信息来更新当前节点为根的子树的信息。并且在算完轻儿子为根的子树的信息后清空信息。这样可以把时间复杂度压缩为\\(O(logn)\\)\n为什么呢？\n考虑每个节点对答案的贡献次数，即搜索次数, 即这个点所在的所有子树中，有多少棵会被遍历。由遍历策略导致，只有轻儿子为根的子树才会被重复遍历。因此贡献次数取决于当前节点所在的子树中有多少棵子树是他父节点的轻儿子。换句话来说，就是当前节点到根节点的路径中，有多少条边是轻边。由树链剖分的性质可知最多有\\(logn\\)条轻边。\n为什么是logn条轻边呢？\n因为我们从当前点往上走，每走一条轻边，子树的元素个数至少乘2，因为上面的父节点会有重儿子且不是当前节点。所以最多只能走logn条轻边。\nvoid dfs0(int u, int fa) { sz[u] = 1; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (j == fa) continue; dfs0(j, u); sz[u] += sz[j]; if (sz[j] \u0026gt; sz[son[u]]) son[u] = j; } } // pson代表当前u的重儿子，即不要重复算重儿子的部分 void update(int u, int fa, int sign, int pson) { cnt[c[u]] += sign; if (cnt[c[u]] \u0026gt; mx) mx = cnt[c[u]], sum = c[u]; else if (cnt[c[u]] == mx) sum += c[u]; for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (j == fa || j == pson) continue; update(j, u, sign, pson); } } // op代表u是重儿子还是轻儿子 void dfs(int u, int fa, int op) { for (int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if (j == fa || j == son[u]) continue; dfs(j, u, 0); } if (son[u]) dfs(son[u], u, 1); update(u, fa, 1, son[u]); ans[u] = sum; if (!op) update(u, fa, -1, 0), sum = mx = 0; } 莫队 # 基础莫队 # 本质是通过排序优化了普通尺取法的时间复杂度。\n考虑如果某一列询问的右端点是递增的，那么我们更新答案的时候，右指针只会从左往右移动，那么i指针的移动次数是\\(O(n)\\)的。\n当然，我们不可能让左右端点都单调来做到总体\\(O(n)\\)。\n考虑对左端点进行分块。\n莫队排序： 左端点按照分块的编号来排，如果分块编号不同的话编号较小的靠前，如果相同的话右端点小的在前。可以证明这样排完序的话时间复杂度可以做到\\(O(n \\sqrt n)\\)。\n这样我们把区间分成了\\(\\sqrt n\\)块，每块的长度都是\\(\\sqrt n\\)，在每一块内部，所有查询的右端点是递增的。\n右指针：在每一块内部，右端点递增，所以右端点走的总数不会超过\\(n\\)（注意每一块内部放的是左端点，右端点是完全有可能超出这个块的范围的，因此这里为\\(n\\)），一共有\\(\\sqrt n\\)块，所以右端点总共走的次数不会超过\\(n \\sqrt n\\)。\n左指针：先考虑每一次询问：\n左指针在块内部移动，块的长度是\\(\\sqrt n\\)，因此最多只会移动\\(\\sqrt n\\)次。\n左指针在相邻两块之间移动，最坏是从第一个块的左端点移动到第二个块的右端点，因此最坏移动\\(2 \\sqrt n\\)次。\n因为有q次询问，所以1是\\(q \\sqrt n\\)，2是\\(2n\\)。 因为一共有\\(\\sqrt n\\)个块，我们从前往后要跨过\\(\\sqrt n - 1\\)次，每次最多是\\(2 \\sqrt n\\)，所以时间复杂度是\\(2n\\)。\n所以总时间复杂度为\\(O(q \\sqrt n)\\)。\n玄学优化 # 奇数块：块内按照右端点从小到大排。\n偶数快：块内按照右端点从大到小排。\n二者可以互换。\n前一段从左到右滚，右端点从左滚到了右边接近n的位置，下一次从右到左滚，可以更方便一些，相当于更加顺路了。\n如果我们块的大小是\\(a\\)的话，那么块的数量就是\\(\\frac{n}{a}\\)，那么r的复杂度就是\\(\\frac{n}{a}n = \\frac{n^2}{a}\\)，l的复杂度是\\(qa\\)（只考虑块内），总复杂度为两者之和，当两者相等时取得最小值，解得\\(a = \\sqrt{\\frac{n^2}{q}}\\)。\n// 代码为统计一段区间上是否有不相同的数，没有输出yes int n, q; int a[N]; vector\u0026lt;array\u0026lt;int, 3\u0026gt;\u0026gt; v; int cnt[210]; int ans[N]; int len; int get(int x) { return x / len; } void adds(int x, int \u0026amp;res) { if (!cnt[x]) res ++; cnt[x] ++; } void del(int x, int \u0026amp;res) { cnt[x] --; if (!cnt[x]) res --; } void solve() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; q; len = max(1, (int)sqrt((double)n * n / q)); for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; a[i] += 100; } for (int i = 1; i \u0026lt;= q; i ++) { int l, r; cin \u0026gt;\u0026gt; l \u0026gt;\u0026gt; r; v.push_back({i, l, r}); } auto cmp = [\u0026amp;](array\u0026lt;int, 3\u0026gt; \u0026amp;a, array\u0026lt;int, 3\u0026gt; \u0026amp;b) { int i = get(a[1]), j = get(b[1]); if (i != j) return i \u0026lt; j; return a[2] \u0026lt; b[2]; }; sort(v.begin(), v.end(), cmp); // i是右指针，j是左指针 for (int k = 0, i = 0, j = 1, res = 0; k \u0026lt; q; k ++) { int id = v[k][0], l = v[k][1], r = v[k][2]; while (i \u0026lt; r) adds(a[++ i], res); while (i \u0026gt; r) del(a[i --], res); while (j \u0026lt; l) del(a[j ++], res); while (j \u0026gt; l) adds(a[-- j], res); ans[id] = res; } for (int i = 1; i \u0026lt;= q; i ++) if (ans[i] == 1) cout \u0026lt;\u0026lt; \u0026#34;YES\\n\u0026#34;; else cout \u0026lt;\u0026lt; \u0026#34;NO\\n\u0026#34;; } 对顶堆 # 双set版本 # 双set用来实现元素的删除操作比优先队列要考虑的细节要少，所以一般用双set来实现对顶堆。\n因为奇偶对应的中位数位置不一样，所以一般要进行奇偶讨论。\ns1为大根堆，s2为小跟堆，我们实现的时候保证两个堆里面的元素个数相同，堆的大小始终为偶数，其中mid来维护中位数。\n当当前维护了奇数个元素时，对顶堆由s1，s2，mid共同组成，其中mid代表游离在set之外的中位数。\n当当前维护了偶数个元素时，对顶堆由s1，s2共同维护，mid指向两个堆的堆顶之一。\nflag用来标记当前元素个数是奇数还是偶数个。如果是奇数个的话flag为1。\nmultiset\u0026lt;int\u0026gt; s1, s2; int sum_l, sum_r, mid; int flag = 0; void init() { s1.clear(); s2.clear(); s1.insert(-LLF); s2.insert(LLF); sum_l = sum_r = mid = 0; flag = 0; } void add(int x) { if (!flag) { int a = *(--s1.end()); int b = *s2.begin(); if (a \u0026lt;= x \u0026amp;\u0026amp; x \u0026lt;= b) { mid = x; } else if (a \u0026gt; x) { s1.erase(s1.find(a)); s1.insert(x); sum_l += x - a; mid = a; } else if (b \u0026lt; x) { s2.erase(s2.find(b)); s2.insert(x); sum_r += x - b; mid = b; } } else { if (x \u0026gt;= mid) { s1.insert(mid); sum_l += mid; s2.insert(x); sum_r += x; } else { s2.insert(mid); sum_r += mid; s1.insert(x); sum_l += x; } } flag ^= 1; } void del(int x) { int a = *(--s1.end()); int b = *s2.begin(); if (!flag) { if (a \u0026gt;= x) { s1.erase(s1.find(x)); sum_l -= x; s2.erase(s2.find(b)); sum_r -= b; mid = b; } else { s2.erase(s2.find(x)); sum_r -= x; s1.erase(s1.find(a)); sum_l -= a; mid = a; } } else { if (mid == x) {} else if (x \u0026gt; mid) { s2.erase(s2.find(x)); s2.insert(mid); sum_r += mid - x; } else { s1.erase(s1.find(x)); s1.insert(mid); sum_l += mid - x; } mid = 0; } flag ^= 1; } 堆带删除操作版 # bool inh[N]; priority_queue\u0026lt;PII\u0026gt; fh; priority_queue\u0026lt;PII, vector\u0026lt;PII\u0026gt;, greater\u0026lt;\u0026gt;\u0026gt; sh; int fsize, rsize; void insert(int i) { inh[i] = 1; while (sh.size() \u0026amp;\u0026amp; !inh[sh.top().second]) sh.pop(); while (fh.size() \u0026amp;\u0026amp; !inh[fh.top().second]) fh.pop(); sh.emplace(a[i], i); while (fh.size() \u0026amp;\u0026amp; sh.size() \u0026amp;\u0026amp; fh.top().first \u0026gt; sh.top().first) { res += sh.top().first - fh.top().first; fh.push(sh.top()); sh.push(fh.top()); fh.pop(); sh.pop(); while (sh.size() \u0026amp;\u0026amp; !inh[sh.top().second]) sh.pop(); while (fh.size() \u0026amp;\u0026amp; !inh[fh.top().second]) fh.pop(); } if (sh.size() \u0026amp;\u0026amp; fsize \u0026lt; k) { ++fsize; res += sh.top().first; fh.push(sh.top()); sh.pop(); while (sh.size() \u0026amp;\u0026amp; !inh[sh.top().second]) sh.pop(); while (fh.size() \u0026amp;\u0026amp; !inh[fh.top().second]) fh.pop(); } } void del(int i) { while (sh.size() \u0026amp;\u0026amp; !inh[sh.top().second]) sh.pop(); while (fh.size() \u0026amp;\u0026amp; !inh[fh.top().second]) fh.pop(); inh[i] = 0; if (!sh.size() || a[i] \u0026lt; sh.top().first) --fsize, res -= a[i]; } 堆仅插入版 # priority_queue\u0026lt;int\u0026gt; q1; // 大根堆 priority_queue\u0026lt;int, vectoor\u0026lt;int\u0026gt;, greater\u0026lt;\u0026gt;\u0026gt; q2; // 小根堆 void insert(int x) { if (!q2.size() || x \u0026gt; q2.top()) q2.push(x); else q1.push(x); if (q1.size() \u0026gt; q2.size() + 1) { q2.push(q1.top()); q1.pop(); } if (q2.size() \u0026gt; q1.size() + 1) { q1.push(q2.top()); q2.pop(); } } 主席树（可持久化权值线段树） # 主要适用于解决在线区间第k小数问题。\nstruct Node { int lson, rson; int cnt; }tr[N * 17 * 18]; int root[N]; int c[N]; int idx; int insert(int p, int l, int r, int x) { int q = ++idx; tr[q] = tr[p]; if (l == r) { tr[q].cnt ++; return q; } int mid = l + r \u0026gt;\u0026gt; 1; if (x \u0026lt;= mid) tr[q].lson = insert(tr[p].lson, l, mid, x); else tr[q].rson = insert(tr[p].rson, mid + 1, r, x); tr[q].cnt = tr[tr[q].lson].cnt + tr[tr[q].rson].cnt; return q; } int query(int q, int p, int l, int r, int k) { if (l == r) return l; int cnt = tr[tr[q].lson].cnt - tr[tr[p].lson].cnt; int mid = l + r \u0026gt;\u0026gt; 1; if (k \u0026lt;= cnt) return query(tr[q].lson, tr[p].lson, l, mid, k); else return query(tr[q].rson, tr[p].rson, mid + 1, r, k - cnt); } 查询中位数的时候直接取个数较多的堆的堆顶即可。\n动态规划 # 最长上升子序列 # 1.朴素版本\nint n; int a[N]; int dp[N]; //dp中存的是以a[i]结尾的子序列的集合中最长的子序列的长度 { for(int i = 1; i \u0026lt;= n; i ++){ dp[i] = 1; for(int j = 1; j \u0026lt; i; j ++){ if(a[j] \u0026lt; a[i]){ dp[i] = max(dp[i], dp[j] + 1); } } } int ans = 0; for(int j = 1; j \u0026lt;= n; j ++) ans = max(ans, dp[j]); cout \u0026lt;\u0026lt; ans; } 2.1模拟栈版本\n{ vector\u0026lt;int\u0026gt; arr(n); vector\u0026lt;int\u0026gt; stk;//模拟堆栈 stk.push_back(arr[0]); for (int i = 1; i \u0026lt; n; ++i) { if (arr[i] \u0026gt; stk.back()) //如果该元素大于栈顶元素,将该元素入栈 stk.push_back(arr[i]); else //替换掉第一个大于或者等于这个数字的那个数 *lower_bound(stk.begin(), stk.end(), arr[i]) = arr[i]; } cout \u0026lt;\u0026lt; stk.size() \u0026lt;\u0026lt; endl; } 2.2二分写法\nint n; int a[N]; int q[N]; { int len = 0; for (int i = 0; i \u0026lt; n; i ++) { int l = 0, r = len; while (l \u0026lt; r) { int mid = l + r + 1 \u0026gt;\u0026gt; 1; if (q[mid] \u0026lt; a[i]) l = mid; else r = mid - 1; } len = max(len, r + 1); q[r + 1] = a[i]; } printf(\u0026#34;%d\\n\u0026#34;, len); } 附：最长不下降子序列\nvoid subseq1(int a[], int l, int r) { // 对原数组的每个下标i(从1开始)，求以a[i]结尾的最长不下降子序列长度 int len = 0; // q下标从2开始才放如元素,q[i]是有长度为i-1的子序列的最小的末尾值 for (int i = l; i \u0026lt;= r; i ++) { int ll = l, rr = len + 1; while (ll \u0026lt; rr) { int mid = ll + rr + 1 \u0026gt;\u0026gt; 1; if(q[mid] \u0026lt;= a[i]) ll = mid; else rr = mid - 1; } len = max(len, rr); q[rr + 1] = a[i]; } // 二分求出以a[i]结尾的最长不下降子序列长度 for (int i = l; i \u0026lt;= r; i ++) { int l2 = 2, r2 = len + 1; while (l2 \u0026lt; r2) { int mid = l2 + r2 + 1 \u0026gt;\u0026gt; 1; if(q[mid] \u0026lt;= a[i]) l2 = mid; else r2 = mid - 1; } dp1[i] = l2 - 1; } } 背包问题 # 01背包 # 状态表示：\\(f[i][j]\\):在前i个物品中选，总体积不超过j的最大价值。\n01背包问题的二维代码 # { //动态规划问题一般下标从1开始，便于从0的情况递推结果。 for (int i = 1; i \u0026lt;= n; i ++) { for (int j = 1; j \u0026lt;= m; j ++) { f[i][j] = f[i - 1][j]; if (j \u0026gt;= v[i]) { f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]); } } } } 01背包一维优化 # { for (int i = 1; i \u0026lt;= n; i ++) { for (int j = m; j \u0026gt;= v[i]; j --) { f[j] = max(f[j], f[j - v[i]] + w[i]); } } } 完全背包问题 # 特征：一个物品可以选无数次。\n状态表示：\\(f[i][j]\\)表示从前i个物品里选择（一个物品可以选多次），总体积不超过j的最大价值。\n完全背包问题的三重循环代码 # { for (int i = 1; i \u0026lt;= n; i ++) { for (int j = 1; j \u0026lt;= m; j ++) { for (int k = 0; k * v[i] \u0026lt;= j; k ++) { f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]); } } } } 二重循环二维的优化过程 # // 01 f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]); // 完全 f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i], f[i - 1][j - 2 * v[i]] + 2 * w[i], .....) f[i][j - v[i]] = max(f[i - 1][j - v[i]], f[i - 1][j - 2 * v[i]] + 1 * w[i], .....) 三式代入二式，得： f[i][j] = max(f[i - 1][j],f[i][j - v[i]] + w[i]);\n{ for (int i = 1; i \u0026lt;= n; i ++) { for (int j = 1; j \u0026lt;= m; j ++) { f[i][j] = f[i - 1][j]; if (j \u0026gt;= v[i]) { f[i][j] = max(f[i][j], f[i][j - v[i]] + w[i]); } } } } 完全背包一维最终优化 # { for (int i = 1; i \u0026lt;= n; i ++) { for (int j = v[i]; j \u0026lt;= m; j ++) { f[j] = max(f[j], f[j - v[i]] + w[i]); } } } 多重背包1 # 特征：一个物品有严格的次数限制。 状态表示：\\(f[i][j]\\)表示从前i个物品里选择（一个物品有严格的次数限制），总体积不超过j的价值；\n多重背包问题的三重循环代码 # //s[i]表示第i种物品共有s[i]个 int f[N][N], s[N], v[N], w[N]; { for (int i = 1; i \u0026lt;= n; i ++) { for (int j = 1; j \u0026lt;= m; j ++) { for (int k = 0; k \u0026lt;= s[i] \u0026amp;\u0026amp; k * v[i] \u0026lt;= j; k ++) { f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]); } } } } 多重背包问题一维优化代码 # { for (int i = 1; i \u0026lt;= n; i ++) { for (int j = m; j \u0026gt;= v[i]; j --) { for (int k = 0; k \u0026lt;= s[i] \u0026amp;\u0026amp; k * v[i] \u0026lt;= j; k ++) { f[j] = max(f[j], f[j - k * v[i]]+ k * w[i]); } } } } 多重背包问题打包版 # 利用二进制优化的思想，把同一种类型的物品按照2的不同次幂个为一组进行打包，从而优化时间复杂度。\n时间复杂度：\\(O(nmlogs)\\)\n// s[i]存储第i种物品的个数 // a, b数组存的是原体积和原价值 int a[N], b[N]; int v[N], w[N], s[N]; { int cnt = 0; for (int i = 1; i \u0026lt;= n; i ++) { int k = 1; int ss = s[i]; while (k \u0026lt;= s) { cnt ++; v[cnt] = k * a[i]; w[cnt] = k * b[i]; s -= k; k *= 2; } if (s \u0026gt; 0) { cnt ++; v[cnt] = s * a; w[cnt] = s * b; } } for (int i = 1; i \u0026lt;= cnt; i ++) { for (int j = m; j \u0026gt;= v[i]; j --) { f[j] = max(f[j], f[j - v[i]] + w[i]); } } } 多重背包单调队列优化 # 时间复杂度：\\(O(nm)\\)\nint f[N]; int g[N]; // g是f的滚动数组 int v[N], w[N], s[N]; { for (int i = 1; i \u0026lt;= n; i ++) { memcpy(g, f, sizeof f); //把上一层的f存在g里 for (int j = 0; j \u0026lt; v[i]; j ++) { int hh = 0, tt = -1; //滑动窗口长度为s，不包括f[k]本身，因此f[k] = max(f[k], max(others)) for (int k = j; k \u0026lt;= m; k += v[i]) { if (hh \u0026lt;= tt \u0026amp;\u0026amp; q[hh] \u0026lt; k - s[i] * v[i]) hh ++; if (hh \u0026lt;= tt) f[k] = max(f[k], g[q[hh]] + (k - q[hh]) / v[i] * w[i]); //由图中表达式知g数组中的值都是不包含w的，这里我们进行比较只需比较绝对值 //因此只要营造出递减数列的效果即可 while (hh \u0026lt;= tt \u0026amp;\u0026amp; g[q[tt]] - (q[tt] - j) / v[i] * w[i] \u0026lt;= g[k] - (k - j) / v[i] * w[i]) tt --; q[++tt] = k; } } } } 分组背包问题 # 特征：一组物品只能选择其中的一个。\n状态表示：f[i][j]表示从前i组物品里选择(一组物品最多只能选择其中的一个)，总体积不超过j的最大价值； 状态计算：f[i][j]=max(f[i-1][j],f[i-1][j-w[i][k]]+v[i][k])(解释；表示从前i组物品里选择体积不超过j的物品价值的最大值是从要么不选这个物品，和要么选择这个组中第k个物品的价值之间进行选择)\n分组背包问题一维优化代码 # // w[i][j]代表第i组物品中的第j个的价值，v同 int w[N][N],v[N][N]; // s[i]代表第i组物品有多少个 int f[N],s[N]; { for (int i = 1; i \u0026lt;= n; i ++) { for (int j = m; j \u0026gt;= 0; j --) { for (int k = 1; k \u0026lt;= s[i]; k ++) { if (v[i][k] \u0026lt;= j) { f[j] = max(f[j], f[j - v[i][k]] + w[i][k]); } } } } return 0; } 背包问题中总体积最多/恰好/最少为j, 以求最大值为例(3是最小值) # 体积最多为j：memset(f, 0, sizeof f), v \u0026gt;= 0\n体积恰好为j：memset(f, -0x3f, sizeof f), ,f[0] = 0, v \u0026gt;= 0\n怎么保证是恰好装满，通过初始化来实现, 如果不存在使f[i]满足是恰好装满的，那么这个f[i] = -INF, 即一个非法答案，就不会影响最终的计算。 体积至少为j：memset(f, -0x3f, sizeof f), f[0] = 0 f[i][j]: 从前i件物品中选，总体积至少为j的最小价值。 memset(f, 0x3f, sizeof f); { for (int i = 1; i \u0026lt;= n; i ++) { for (int j = m; j \u0026gt;= 0; j --) { f[j] = min(f[j], f[max(0, j - v[i])] + w[i]); } } } 有依赖的背包问题 # \\(f[u][j]\\): 表示在以u为根的子树中选，且先选出u（因为其他节点都依赖u, 这样表示状态会简单一点，可以往分组背包(其实也不完全是分组背包，也就物品组和枚举决策的概念上比较像分组背包)上面套），总体积不超过j的最大价值。\nvoid dfs(int u) { // 相当于分组背包问题中的物品组 for (int u = h[u]; ~i; i = ne[i]) { int son = e[i]; dfs(son); // 先求一下所有子树中的最大价值, 故先在最大体积中把当前节点去掉 // 枚举体积 for (int j = m - v[u]; j \u0026gt;= 0; j --) { // 枚举决策，这里以选取体积为多少讨论决策 for (int k = 0; k \u0026lt;= j; k ++) { f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]); } } } // 最后记得把当前节点加上 for (int j = m; j \u0026gt;= v[u]; j --) f[u][j] = f[u][j - v[u]] + w[u]; for (int j = v[u] - 1; j \u0026gt;= 0; j --) f[u][j] = 0; } dfs(root); 另一种模板：\nint f[110][110];//f[x][v]表达选择以x为子树的物品，在容量不超过v时所获得的最大价值 vector\u0026lt;int\u0026gt; g[110]; int v[110], w[110]; int n, m, root; void dfs(int x) { for (int i = v[x]; i \u0026lt;= m; i ++) f[x][i] = w[x];//点x必须选，所以初始化f[x][v[x] ~ m]= w[x] for (int i = 0; i \u0026lt; g[x].size(); i ++) { int y = g[x][i]; dfs(y); for (int j = m; j \u0026gt;= v[x]; j --)//j的范围为v[x]~m, 小于v[x]无法选择以x为子树的物品 { for (int k = 0; k \u0026lt;= j - v[x]; k ++)//分给子树y的空间不能大于j-v[x],不然都无法选根物品x { f[x][j] = max(f[x][j], f[x][j - k] + f[y][k]); } } } } 混合背包问题 # 由于01背包、完全背包、多重背包的转移方程只和当前的第i个物品的类型有关，与前面的物品无关，所以可以对不同的类型物品独立地使用不同的状态转移方程。\n随机算法 # 模拟退火 # 源自冶金术语，将材料加热后再经特定速率冷却的技术，目的是增大晶粒体积，减少晶格中的缺陷，以改变材料的物理性质。\n材料中的原子原来会停留在使内能有局部最小值的位置，加热使能量变大，原子会离开原来的位置，随机在其他位置游走，退火冷却时速度较慢，使得原子有较多可能可以移动到内能比原先更低的位置。\nvoid simulate_anneal() { PDD cur(rdd(0, 10000), rdd(0, 10000)); // t为退火温度，也即随机范围。 for (double t = 1e4; t \u0026gt; 1e-4; t *= 0.9) { PDD np(rdd(cur.first - t, cur.first + t), rdd(cur.second - t, cur.second + t)); double dt = calc(np) - calc(cur); // 如果新点距离更小，则跳到新点，否则以一定概率跳到新点。 if (exp(-dt / t) \u0026gt; rdd(0, 1)) cur = np; } } 计算几何 # 前置知识 # 1. 前置知识点 (1) pi = acos(-1); (2) 余弦定理 c^2 = a^2 + b^2 - 2abcos(t) 2. 浮点数的比较 const double eps = 1e-8; int sign(double x) // 符号函数 { if (fabs(x) \u0026lt; eps) return 0; if (x \u0026lt; 0) return -1; return 1; } int cmp(double x, double y) // 比较函数 { if (fabs(x - y) \u0026lt; eps) return 0; if (x \u0026lt; y) return -1; return 1; } 3. 向量 3.1 向量的加减法和数乘运算 3.2 内积（点积） A·B = |A||B|cos(C) (1) 几何意义：向量A在向量B上的投影与B的长度的乘积。 (2) 代码实现 double dot(Point a, Point b) { return a.x * b.x + a.y * b.y; } 3.3 外积（叉积） AxB = |A||B|sin(C) (1) 几何意义：向量A与B张成的平行四边形的有向面积。B在A的逆时针方向为正。 (2) 代码实现 double cross(Point a, Point b) { return a.x * b.y - b.x * a.y; } 3.4 常用函数 3.4.1 取模 double get_length(Point a) { return sqrt(dot(a, a)); } 3.4.2 计算向量夹角 double get_angle(Point a, Point b) { return acos(dot(a, b) / get_length(a) / get_length(b)); } 3.4.3 计算两个向量构成的平行四边形有向面积 double area(Point a, Point b, Point c) { return cross(b - a, c - a); } 3.4.5 向量A顺时针旋转C的角度： Point rotate(Point a, double angle) { return Point(a.x * cos(angle) + a.y * sin(angle), -a.x * sin(angle) + a.y * cos(angle)); } 4. 点与线 4.1 直线定理 (1) 一般式 ax + by + c = 0 (2) 点向式 p0 + vt (3) 斜截式 y = kx + b 4.2 常用操作 (1) 判断点在直线上 A x B = 0 (2) 两直线相交 // cross(v, w) == 0则两直线平行或者重合 Point get_line_intersection(Point p, Vector v, Point q, vector w) { vector u = p - q; double t = cross(w, u) / cross(v, w); return p + v * t; } (3) 点到直线的距离 double distance_to_line(Point p, Point a, Point b) { vector v1 = b - a, v2 = p - a; return fabs(cross(v1, v2) / get_length(v1)); } (4) 点到线段的距离 double distance_to_segment(Point p, Point a, Point b) { if (a == b) return get_length(p - a); Vector v1 = b - a, v2 = p - a, v3 = p - b; if (sign(dot(v1, v2)) \u0026lt; 0) return get_length(v2); if (sign(dot(v1, v3)) \u0026gt; 0) return get_length(v3); return distance_to_line(p, a, b); } (5) 点在直线上的投影 Point get_line_projection(Point p, Point a, Point b) { Vector v = b - a; return a + v * (dot(v, p - a) / dot(v, v)); } (6) 点是否在线段上 bool on_segment(Point p, Point a, Point b) { return sign(cross(p - a, p - b)) == 0 \u0026amp;\u0026amp; sign(dot(p - a, p - b)) \u0026lt;= 0; } (7) 判断两线段是否相交 bool segment_intersection(Point a1, Point a2, Point b1, Point b2) { double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1); double c3 = cross(b2 - b1, a2 - b1), c4 = cross(b2 - b1, a1 - b1); return sign(c1) * sign(c2) \u0026lt;= 0 \u0026amp;\u0026amp; sign(c3) * sign(c4) \u0026lt;= 0; } 5. 多边形 5.1 三角形 5.1.1 面积 (1) 叉积 (2) 海伦公式 p = (a + b + c) / 2; S = sqrt(p(p - a) * (p - b) * (p - c)); 5.1.2 三角形四心 (1) 外心，外接圆圆心 三边中垂线交点。到三角形三个顶点的距离相等 (2) 内心，内切圆圆心 角平分线交点，到三边距离相等 (3) 垂心 三条垂线交点 (4) 重心 三条中线交点（到三角形三顶点距离的平方和最小的点，三角形内到三边距离之积最大的点） 5.2 普通多边形 通常按逆时针存储所有点 5.2.1 定义 (1) 多边形 由在同一平面且不再同一直线上的多条线段首尾顺次连接且不相交所组成的图形叫多边形 (2) 简单多边形 简单多边形是除相邻边外其它边不相交的多边形 (3) 凸多边形 过多边形的任意一边做一条直线，如果其他各个顶点都在这条直线的同侧，则把这个多边形叫做凸多边形 任意凸多边形外角和均为360° 任意凸多边形内角和为(n−2)180° 5.2.2 常用函数 (1) 求多边形面积（不一定是凸多边形） 我们可以从第一个顶点除法把凸多边形分成n − 2个三角形，然后把面积加起来。 double polygon_area(Point p[], int n) { double s = 0; for (int i = 1; i + 1 \u0026lt; n; i ++ ) s += cross(p[i] - p[0], p[i + 1] - p[i]); return s / 2; } (2) 判断点是否在多边形内（不一定是凸多边形） a. 射线法，从该点任意做一条和所有边都不平行的射线。交点个数为偶数，则在多边形外，为奇数，则在多边形内。 b. 转角法 (3) 判断点是否在凸多边形内 只需判断点是否在所有边的左边（逆时针存储多边形）。 5.3 皮克定理 皮克定理是指一个计算点阵中顶点在格点上的多边形面积公式该公式可以表示为: S = a + b/2 - 1 其中a表示多边形内部的点数，b表示多边形边界上的点数，S表示多边形的面积。 6. 圆 (1) 圆与直线交点 (2) 两圆交点 (3) 点到圆的切线 (4) 两圆公切线 (5) 两圆相交面积 作者：yxc 链接：https://www.acwing.com/activity/content/code/content/635453/ 来源：AcWing ` ","date":"10 May 2024","externalUrl":null,"permalink":"/posts/2024/acm%E7%AE%97%E6%B3%95%E7%AB%9E%E8%B5%9B%E4%BB%A3%E7%A0%81%E6%A8%A1%E6%9D%BF%E9%95%BF%E6%9C%9F%E6%9B%B4%E6%96%B0/","section":"帖子总览","summary":"","title":"ACM算法竞赛代码模板（长期更新）","type":"posts"},{"content":"","date":"10 May 2024","externalUrl":null,"permalink":"/tags/icpc/","section":"标签总览","summary":"","title":"Icpc","type":"tags"},{"content":" 部分代码define了long long，请记得开long long\nA. Calandar # 把年份、月份、单个的天数全都乘以对应的系数转化成单个的天数即可，注意最后的结果有可能是负数，要转化成正数。发现技巧是：(ans % 5 + 5) % 5。？\n还有注意不能这样写，答案不正确。或许是因为取模运算没有这样的性质？？\nans = plu(sub(plu(plu(mul(sub(y2, y), 365), mul(sub(m2, m), 30)), d2), d), x); int y, m, d; int y2, m2, d2; map\u0026lt;string, int\u0026gt; mp = { {\u0026#34;Monday\u0026#34;, 0}, {\u0026#34;Tuesday\u0026#34;, 1}, {\u0026#34;Wednesday\u0026#34;, 2}, {\u0026#34;Thursday\u0026#34;, 3}, {\u0026#34;Friday\u0026#34;, 4}, }; map\u0026lt;int, string\u0026gt; mpr = { {0, \u0026#34;Monday\u0026#34;}, {1, \u0026#34;Tuesday\u0026#34;}, {2, \u0026#34;Wednesday\u0026#34;}, {3, \u0026#34;Thursday\u0026#34;}, {4, \u0026#34;Friday\u0026#34;}, }; void solve() { cin \u0026gt;\u0026gt; y \u0026gt;\u0026gt; m \u0026gt;\u0026gt; d; string s; cin \u0026gt;\u0026gt; s; int x = mp[s]; cin \u0026gt;\u0026gt; y2 \u0026gt;\u0026gt; m2 \u0026gt;\u0026gt; d2; int ans = (y2 - y) * 365 + (m2 - m) * 30 + d2 - d + x; if (ans \u0026gt;= 0) cout \u0026lt;\u0026lt; mpr[ans % 5] \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; else cout \u0026lt;\u0026lt; mpr[(ans % 5 + 5) % 5] \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } M. Sekiro # 很简单的签到，但是把我害惨了，记得以前校某次比赛用过这题，当时就让我大脑宕机了一次。\n注意到每次操作都会使\\(n\\)减半，因此最多只会操作\\(log(n)\\)次，但是有可能减半后和减半前值相同，造成死循环，因此这种情况要特判结束循环。\nint n, k; void solve() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; k; while (k --) { if (n == (n + 1) / 2) break; n = (n + 1) / 2; } cout \u0026lt;\u0026lt; n \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } F. Stones in the bucket # 我们可以先将序列排个序，然后假设我们最后要把序列的元素全部变成\\(x\\)，那么我们可以把所有\\(\u003ex\\)的区块都一个个的撒给\\(","date":"10 May 2024","externalUrl":null,"permalink":"/posts/2024/%E7%AC%AC%E5%8D%81%E5%B1%8A%E5%B1%B1%E4%B8%9C%E7%9C%81%E5%A4%A7%E5%AD%A6%E7%94%9F%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E7%AB%9E%E8%B5%9B%E9%A2%98%E8%A7%A3afmc/","section":"帖子总览","summary":"","title":"第十届山东省大学生程序设计竞赛题解（A、F、M、C）","type":"posts"},{"content":"","date":"9 May 2024","externalUrl":null,"permalink":"/tags/%E5%88%86%E5%9D%97/","section":"标签总览","summary":"","title":"分块","type":"tags"},{"content":"","date":"9 May 2024","externalUrl":null,"permalink":"/tags/%E8%8E%AB%E9%98%9F/","section":"标签总览","summary":"","title":"莫队","type":"tags"},{"content":" 莫队 # 基础莫队 # 本质是通过排序优化了普通尺取法的时间复杂度。\n考虑如果某一列询问的右端点是递增的，那么我们更新答案的时候，右指针只会从左往右移动，那么i指针的移动次数是\\(O(n)\\)的。\n当然，我们不可能让左右端点都单调来做到总体\\(O(n)\\)。\n考虑对左端点进行分块。\n莫队排序： 左端点按照分块的编号来排，如果分块编号不同的话编号较小的靠前，如果相同的话右端点小的在前。可以证明这样排完序的话时间复杂度可以做到\\(O(n \\sqrt n)\\)。\n这样我们把区间分成了\\(\\sqrt n\\)块，每块的长度都是\\(\\sqrt n\\)，在每一块内部，所有查询的右端点是递增的。\n右指针：在每一块内部，右端点递增，所以右端点走的总数不会超过\\(n\\)（注意每一块内部放的是左端点，右端点是完全有可能超出这个块的范围的，因此这里为\\(n\\)），一共有\\(\\sqrt n\\)块，所以右端点总共走的次数不会超过\\(n \\sqrt n\\)。\n左指针：先考虑每一次询问：\n左指针在块内部移动，块的长度是\\(\\sqrt n\\)，因此最多只会移动\\(\\sqrt n\\)次。\n左指针在相邻两块之间移动，最坏是从第一个块的左端点移动到第二个块的右端点，因此最坏移动\\(2 \\sqrt n\\)次。\n因为有q次询问，所以1是\\(q \\sqrt n\\)，2是\\(2n\\)。 因为一共有\\(\\sqrt n\\)个块，我们从前往后要跨过\\(\\sqrt n - 1\\)次，每次最多是\\(2 \\sqrt n\\)，所以时间复杂度是\\(2n\\)。\n所以总时间复杂度为\\(O(q \\sqrt n)\\)。\n// 代码为统计一段区间上是否有不相同的数，没有输出yes int n, q; int a[N]; vector\u0026lt;array\u0026lt;int, 3\u0026gt;\u0026gt; v; int cnt[210]; int ans[N]; int len; int get(int x) { return x / len; } void adds(int x, int \u0026amp;res) { if (!cnt[x]) res ++; cnt[x] ++; } void del(int x, int \u0026amp;res) { cnt[x] --; if (!cnt[x]) res --; } void solve() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; q; len = max(1, (int)sqrt((double)n * n / q)); for (int i = 1; i \u0026lt;= n; i ++) { cin \u0026gt;\u0026gt; a[i]; a[i] += 100; } for (int i = 1; i \u0026lt;= q; i ++) { int l, r; cin \u0026gt;\u0026gt; l \u0026gt;\u0026gt; r; v.push_back({i, l, r}); } auto cmp = [\u0026amp;](array\u0026lt;int, 3\u0026gt; \u0026amp;a, array\u0026lt;int, 3\u0026gt; \u0026amp;b) { int i = get(a[1]), j = get(b[1]); if (i != j) return i \u0026lt; j; return a[2] \u0026lt; b[2]; }; sort(v.begin(), v.end(), cmp); // i是右指针，j是左指针 for (int k = 0, i = 0, j = 1, res = 0; k \u0026lt; q; k ++) { int id = v[k][0], l = v[k][1], r = v[k][2]; while (i \u0026lt; r) adds(a[++ i], res); while (i \u0026gt; r) del(a[i --], res); while (j \u0026lt; l) del(a[j ++], res); while (j \u0026gt; l) adds(a[-- j], res); ans[id] = res; } for (int i = 1; i \u0026lt;= q; i ++) if (ans[i] == 1) cout \u0026lt;\u0026lt; \u0026#34;YES\\n\u0026#34;; else cout \u0026lt;\u0026lt; \u0026#34;NO\\n\u0026#34;; } ","date":"9 May 2024","externalUrl":null,"permalink":"/posts/2024/%E8%8E%AB%E9%98%9F%E7%AE%97%E6%B3%95%E5%9F%BA%E7%A1%80%E8%8E%AB%E9%98%9F%E5%B0%8F%E7%BB%93%E4%B9%9F%E5%81%9Amarkdown%E6%B5%8B%E8%AF%95/","section":"帖子总览","summary":"","title":"莫队算法（基础莫队）小结","type":"posts"},{"content":"","date":"14 December 2023","externalUrl":null,"permalink":"/tags/neovim/","section":"标签总览","summary":"","title":"Neovim","type":"tags"},{"content":" 刚开始遇到这个情况我百思不得其解，检查了neovim checkhealth，以为是npm包管理的问题，然后删了下删了下 # 不但没有解决还把包管理整乱了…… # 后来发现是我没仔细看bash-language-server这个包的官方文档。。。 以下是bash-language-server的官方仓库： # https://github.com/bash-lsp/bash-language-server\n看到了dependency里面的shellcheck没？ # 我们需要的就是这个包，那么我们只需要\nsudo pacman -S shellcheck 就好了\n","date":"14 December 2023","externalUrl":null,"permalink":"/posts/2023/%E5%BD%93%E4%BD%A0%E7%94%A8neovim%E7%9A%84mason%E6%8F%92%E4%BB%B6%E7%AE%A1%E7%90%86lsp-config%E5%B9%B6%E4%B8%94%E9%85%8D%E7%BD%AE%E5%A5%BDbash%E7%9A%84bashls%E5%90%8E%E5%8D%B4%E6%B2%A1%E6%9C%89%E6%AD%A3%E5%B8%B8%E5%B7%A5%E4%BD%9C%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E5%BC%8F/","section":"帖子总览","summary":"","title":"当你用neovim的mason插件管理lsp config，并且配置好bash的bashls后，却没有正常工作的解决方式","type":"posts"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":" 关于此网站： # 网站源码：\nlightmon233/lightmon233.github.io Personal static blog site based on hugo and blowfish. HTML 0 0 日语学习博客： # 访问我的日语学习博客网站，同样基于hugo搭建。\n","externalUrl":null,"permalink":"/about/","section":"关于","summary":"","title":"关于","type":"about"}]