<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Dicer&apos;s Blog</title><description>Trying to reach the star.</description><link>https://dicer-zz.github.io/</link><language>zh_CN</language><item><title>Claude Code 黑科技手册：10 个大多数人不知道的隐藏玩法</title><link>https://dicer-zz.github.io/posts/claude-code-commands-guide/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/claude-code-commands-guide/</guid><description>你以为 Claude Code 只是个聊天框？自定义命令、Hooks 自动化、多 Agent 并行、手机遥控、AI 审查 AI……这些骚操作才是它真正的杀手锏。</description><pubDate>Mon, 27 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;大多数人用 Claude Code 的方式是：打开终端，输入需求，等它写完代码，然后手动检查。这就像买了一台高性能跑车却只用来上下班代步——能用，但浪费了 90% 的能力。&lt;/p&gt;
&lt;p&gt;这篇文章不讲基础命令。我要聊的是那些藏在文档深处、需要你翻好几页才能找到、但一旦用上就回不去的「黑科技」。每个技巧都配有可以直接复制的配置和代码。&lt;/p&gt;
&lt;h2&gt;1. 自定义 Slash Command：把你的工作流变成一键命令&lt;/h2&gt;
&lt;p&gt;你知道 Claude Code 的 &lt;code&gt;/&lt;/code&gt; 命令可以自己造吗？&lt;/p&gt;
&lt;p&gt;在项目根目录创建 &lt;code&gt;.claude/commands/&lt;/code&gt; 文件夹，往里面扔一个 Markdown 文件，文件名就是命令名。比如创建一个 &lt;code&gt;.claude/commands/review.md&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
description: 审查当前分支的变更
allowed-tools: Bash, Read, Grep
---

请审查当前分支与 main 分支的差异：

当前 diff：
!`git diff main...HEAD`

最近提交记录：
!`git log --oneline main...HEAD`

重点关注：
1. 是否有安全隐患（硬编码密钥、SQL 注入等）
2. 是否有性能问题（N+1 查询、不必要的循环等）
3. 是否有明显的逻辑错误
4. 代码风格是否一致

给出具体的改进建议，按严重程度排序。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后在 Claude Code 里直接输入 &lt;code&gt;/review&lt;/code&gt; 就能用了。&lt;/p&gt;
&lt;p&gt;这里有几个关键技巧。&lt;code&gt;!&lt;/code&gt; 前缀后跟反引号包裹的命令会在发送给 Claude 之前执行，把命令输出直接注入 prompt 里——Claude 拿到的是真实的 diff 内容，而不是一个&quot;请你执行 git diff&quot;的指令。&lt;code&gt;$ARGUMENTS&lt;/code&gt; 占位符可以接收你在命令后面追加的参数，比如 &lt;code&gt;/review --focus security&lt;/code&gt; 里的 &lt;code&gt;--focus security&lt;/code&gt; 就会替换到 &lt;code&gt;$ARGUMENTS&lt;/code&gt; 的位置。而 &lt;code&gt;$1&lt;/code&gt;、&lt;code&gt;$2&lt;/code&gt; 可以接收位置参数。&lt;/p&gt;
&lt;p&gt;再来一个更实用的例子——一键生成 commit message：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
description: 根据 staged changes 生成 commit
allowed-tools: Bash
model: haiku
argument-hint: &amp;lt;commit type: feat|fix|refactor|docs&amp;gt;
---

Staged changes：
!`git diff --cached`

请根据以上变更生成一条 conventional commit message。
commit 类型：$1（如未指定，请自行判断）

要求：
- 标题不超过 72 字符
- 用中文写 body 部分
- 直接执行 git commit，不要问我确认
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意 &lt;code&gt;model: haiku&lt;/code&gt; 这一行——对于这种简单任务，指定用 Haiku 模型可以大幅降低成本，响应也更快。&lt;/p&gt;
&lt;p&gt;放在 &lt;code&gt;.claude/commands/&lt;/code&gt; 里的命令跟着项目走，团队成员 clone 下来就能用。放在 &lt;code&gt;~/.claude/commands/&lt;/code&gt; 里的则是你的个人全局命令。&lt;/p&gt;
&lt;h2&gt;2. Hooks：给 Claude 装上「自动驾驶」&lt;/h2&gt;
&lt;p&gt;Hooks 是 Claude Code 最被低估的特性。它让你在 Claude 的生命周期里插入自动执行的 shell 命令——编辑文件后自动 format、执行危险命令前拦截、任务完成后发通知，全都不需要你手动干预。&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;~/.claude/settings.json&lt;/code&gt; 里加一段配置就行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;PostToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Edit|Write&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;jq -r &apos;.tool_input.file_path&apos; | xargs npx prettier --write&quot;
          }
        ]
      }
    ],
    &quot;Notification&quot;: [
      {
        &quot;matcher&quot;: &quot;&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;osascript -e &apos;display notification \&quot;Claude Code 需要你的注意\&quot; with title \&quot;Claude Code\&quot;&apos;&quot;
          }
        ]
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段配置做了两件事：每次 Claude 编辑或写入文件后，自动用 Prettier 格式化；每次 Claude 等待你输入时，弹出 macOS 系统通知——从此你可以安心切到别的窗口干活，不用盯着终端。&lt;/p&gt;
&lt;p&gt;Hooks 的生命周期事件多达 20+ 种。几个最实用的：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PreToolUse&lt;/strong&gt; 在工具调用之前触发，可以拦截危险操作。比如阻止 Claude 碰你的 &lt;code&gt;.env&lt;/code&gt; 文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;PreToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Edit|Write&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;jq -r &apos;.tool_input.file_path // empty&apos; | grep -qE &apos;(\\.env|credentials)&apos; &amp;amp;&amp;amp; echo &apos;Blocked: 不允许修改敏感文件&apos; &amp;gt;&amp;amp;2 &amp;amp;&amp;amp; exit 2 || exit 0&quot;
          }
        ]
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;退出码 2 表示「拦截」，Claude 会收到你 stderr 里的反馈并自动调整策略。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SessionStart&lt;/strong&gt; 在会话开始时触发，&lt;code&gt;compact&lt;/code&gt; matcher 专门匹配上下文压缩后的重启场景——你可以用它把被压缩掉的关键信息重新注入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;SessionStart&quot;: [
      {
        &quot;matcher&quot;: &quot;compact&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;echo &apos;提醒：本项目使用 Bun 而非 npm；提交前必须通过 bun test；当前 sprint 重点是 auth 重构。&apos;&quot;
          }
        ]
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Headless 模式：让 Claude 变成你 CI 管道里的打工人&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;claude -p&lt;/code&gt; 是一切自动化的起点。它跳过交互界面，直接给 prompt 拿结果。&lt;/p&gt;
&lt;p&gt;最基础的用法——用 Claude 自动生成 commit message：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;claude -p &quot;看看我的 staged changes，生成一条合适的 commit message 并执行 commit&quot; \
  --allowedTools &quot;Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git commit *)&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;--allowedTools&lt;/code&gt; 的通配符写法很讲究：&lt;code&gt;Bash(git diff *)&lt;/code&gt; 中空格 + &lt;code&gt;*&lt;/code&gt; 表示前缀匹配，允许所有以 &lt;code&gt;git diff &lt;/code&gt; 开头的命令。没有空格的 &lt;code&gt;Bash(git diff*)&lt;/code&gt; 会匹配到 &lt;code&gt;git diff-index&lt;/code&gt;——这是官方文档里特别提醒的坑。&lt;/p&gt;
&lt;p&gt;更强大的是结构化输出。你可以让 Claude 从代码库里提取结构化数据，输出严格符合 JSON Schema 的结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;claude -p &quot;分析 src/ 目录，列出所有暴露的 API 端点&quot; \
  --output-format json \
  --json-schema &apos;{
    &quot;type&quot;: &quot;object&quot;,
    &quot;properties&quot;: {
      &quot;endpoints&quot;: {
        &quot;type&quot;: &quot;array&quot;,
        &quot;items&quot;: {
          &quot;type&quot;: &quot;object&quot;,
          &quot;properties&quot;: {
            &quot;method&quot;: {&quot;type&quot;: &quot;string&quot;},
            &quot;path&quot;: {&quot;type&quot;: &quot;string&quot;},
            &quot;auth_required&quot;: {&quot;type&quot;: &quot;boolean&quot;}
          },
          &quot;required&quot;: [&quot;method&quot;, &quot;path&quot;, &quot;auth_required&quot;]
        }
      }
    },
    &quot;required&quot;: [&quot;endpoints&quot;]
  }&apos; | jq &apos;.structured_output&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这东西配合 CI/CD 简直是降维打击。写一个 GitHub Action，每次 PR 自动做安全审查：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# .github/workflows/ai-review.yml
name: AI Security Review
on: [pull_request]
jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: AI Review
        run: |
          gh pr diff ${{ github.event.pull_request.number }} | \
          claude -p --bare \
            --append-system-prompt &quot;你是一个安全工程师，审查代码变更中的安全漏洞。&quot; \
            --output-format json
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;--bare&lt;/code&gt; 参数跳过所有本地配置（hooks、plugins、MCP servers、CLAUDE.md），确保 CI 环境下结果完全可复现。&lt;/p&gt;
&lt;h2&gt;4. Subagents：分身术，让多个 Claude 同时替你打工&lt;/h2&gt;
&lt;p&gt;当你告诉 Claude &quot;调研一下这个项目的认证模块&quot;时，它可能会读一大堆文件，吐一大坨搜索结果到你的上下文里——然后你的 context window 就被撑爆了。&lt;/p&gt;
&lt;p&gt;Subagent 解决的就是这个问题。它在独立的上下文窗口里工作，完事后只把摘要返回给主对话。&lt;/p&gt;
&lt;p&gt;你可以创建自定义 subagent。在 &lt;code&gt;.claude/agents/&lt;/code&gt; 下写一个 Markdown 文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
name: test-runner
description: 运行测试并修复失败用例。写完代码后主动使用。
tools: Bash, Read, Edit, Grep, Glob
model: sonnet
background: true
---

你是一个测试专家。被调用时：

1. 运行完整测试套件
2. 分析失败原因
3. 修复失败的测试
4. 重新运行验证
5. 只向主对话报告最终结果（通过/失败数量 + 修复摘要）

不要在回复中输出完整的测试日志。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;background: true&lt;/code&gt; 是关键——它让这个 subagent 在后台运行，你可以继续在主对话里写代码。按 &lt;code&gt;Ctrl+B&lt;/code&gt; 也能把一个正在前台运行的 subagent 扔到后台。&lt;/p&gt;
&lt;p&gt;真正的杀手锏是并行。你可以一句话让 Claude 同时派出多个 agent：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;分别用 subagent 并行调研 authentication、database 和 API 三个模块的架构
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;三个 agent 同时开工，各自在自己的上下文里翻文件、读代码，最后 Claude 把三份报告综合起来给你。&lt;/p&gt;
&lt;h2&gt;5. Git Worktree 隔离：给 Agent 一个沙箱&lt;/h2&gt;
&lt;p&gt;让 AI 直接改你的工作目录总觉得不踏实？&lt;code&gt;isolation: worktree&lt;/code&gt; 让 subagent 在一个独立的 git worktree 里工作——它是你仓库的一个平行拷贝，agent 在里面随便改，改完如果不满意直接丢掉，不影响你的一行代码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
name: refactor-experiment
description: 在隔离环境中尝试重构方案
tools: Bash, Read, Edit, Write
isolation: worktree
---

你在一个隔离的 git worktree 中工作。大胆尝试重构方案。
完成后总结你的变更和取舍，由主对话决定是否合并。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个特性和 &lt;code&gt;/batch&lt;/code&gt; 命令搭配使用效果拉满——&lt;code&gt;/batch&lt;/code&gt; 可以自动为每个并行任务创建独立的 worktree，多个 agent 同时在不同分支上改代码，互不干扰。&lt;/p&gt;
&lt;h2&gt;6. Agent Memory：让 AI 越用越聪明&lt;/h2&gt;
&lt;p&gt;普通 subagent 每次调用都从零开始。但如果你给它加上 &lt;code&gt;memory&lt;/code&gt; 字段，它就有了跨会话的持久记忆：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
name: code-reviewer
description: 代码审查专家，会积累项目知识
tools: Read, Grep, Glob, Bash
memory: project
---

你是一个高级代码审查员。

在审查代码时，请参考你的 agent memory 中积累的项目知识。
审查完成后，将发现的模式、约定和常见问题记录到你的 memory 中。

重点关注：
- 项目特有的编码约定
- 反复出现的问题模式
- 架构决策的上下文
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;memory&lt;/code&gt; 有三个级别：&lt;code&gt;user&lt;/code&gt;（存在 &lt;code&gt;~/.claude/agent-memory/&lt;/code&gt;，跨项目通用）、&lt;code&gt;project&lt;/code&gt;（存在 &lt;code&gt;.claude/agent-memory/&lt;/code&gt;，可以提交到 git 让团队共享）、&lt;code&gt;local&lt;/code&gt;（本地项目级，不提交）。&lt;/p&gt;
&lt;p&gt;Agent 会自动维护一个 &lt;code&gt;MEMORY.md&lt;/code&gt; 文件，里面记录它在多次会话中积累的洞察。用得越久，它对你项目的理解越深。&lt;/p&gt;
&lt;h2&gt;7. Prompt-based Hooks + Agent Hooks：让 AI 审查 AI&lt;/h2&gt;
&lt;p&gt;这是我最喜欢的黑科技。你可以配置一个 Hook，在 Claude 说&quot;我做完了&quot;的时候，自动用另一个 AI 来检查它的工作：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;Stop&quot;: [
      {
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;prompt&quot;,
            &quot;prompt&quot;: &quot;检查所有被要求完成的任务是否真的完成了。如果有遗漏，返回 {\&quot;ok\&quot;: false, \&quot;reason\&quot;: \&quot;还需要做什么\&quot;}。&quot;
          }
        ]
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;type: &quot;prompt&quot;&lt;/code&gt; 表示这是一个 AI 驱动的 hook——Claude Code 会把当前上下文发给一个轻量模型（默认 Haiku），让它判断任务是否真的完成。如果判定没完成，Claude 会继续干活，把 &lt;code&gt;reason&lt;/code&gt; 当作下一步指令。&lt;/p&gt;
&lt;p&gt;更强力的是 &lt;code&gt;type: &quot;agent&quot;&lt;/code&gt;，它会启动一个有工具访问权限的 subagent 来验证：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;Stop&quot;: [
      {
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;agent&quot;,
            &quot;prompt&quot;: &quot;运行测试套件，验证所有单元测试通过。如果有失败，返回失败详情。&quot;,
            &quot;timeout&quot;: 120
          }
        ]
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这意味着 Claude 每次说&quot;搞定了&quot;，都会有另一个 agent 自动跑一遍测试来验证。再也不用人肉 review &quot;你确定改完了？&quot;&lt;/p&gt;
&lt;h2&gt;8. 手机遥控：不在电脑前也能指挥 Claude&lt;/h2&gt;
&lt;p&gt;这个特性知道的人极少。在 Claude Code 里输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/mobile
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它会生成一个二维码。用手机扫一下，你就可以在手机上给 Claude 发指令、查看输出了。&lt;/p&gt;
&lt;p&gt;想象一下：你在吃午饭的时候掏出手机，让 Claude 跑一个重构任务，回到座位时代码已经改好了。&lt;/p&gt;
&lt;p&gt;类似的还有 &lt;code&gt;/remote-control&lt;/code&gt;（从 claude.ai 网页端控制本地 Claude Code）和 &lt;code&gt;/desktop&lt;/code&gt;（切换到桌面 App 继续操作）。这三个命令组合起来，你几乎可以在任何设备上操控同一个 Claude Code 实例。&lt;/p&gt;
&lt;h2&gt;9. CLAUDE.md 分层记忆系统：比你想的强大得多&lt;/h2&gt;
&lt;p&gt;大多数人知道 CLAUDE.md 是项目配置文件，但很少有人用到它的全部能力。&lt;/p&gt;
&lt;p&gt;Claude Code 的记忆系统实际上是分层的。&lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt; 是全局记忆，对所有项目生效，适合放你的编码风格偏好和通用规则。项目根目录的 &lt;code&gt;CLAUDE.md&lt;/code&gt; 是项目级记忆，可以提交到 git 让团队共享。而子目录里还可以放自己的 &lt;code&gt;CLAUDE.md&lt;/code&gt;——当 Claude 操作到该目录下的文件时，会自动加载对应的规则。&lt;/p&gt;
&lt;p&gt;更灵活的方式是用 &lt;code&gt;.claude/rules/&lt;/code&gt; 目录存放规则文件。你可以在 frontmatter 里指定 &lt;code&gt;globs&lt;/code&gt; 让规则只对特定文件生效：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
globs: &quot;*.test.ts,*.spec.ts&quot;
---

测试文件规则：
- 使用 describe/it 结构
- 每个 it 块只测试一个行为
- mock 外部依赖，不要 mock 内部模块
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样当 Claude 编辑测试文件时会自动加载这些规则，编辑其他文件时则不会。&lt;/p&gt;
&lt;p&gt;真正的黑科技是 &lt;code&gt;/memory&lt;/code&gt; 命令配合自动记忆功能。开启后，Claude 会自动把你在对话中提到的偏好写入 CLAUDE.md——比如你说&quot;以后都用 Bun 不要用 npm&quot;，它会自己记住，下次就不用你重复了。&lt;/p&gt;
&lt;h2&gt;10. Fork Mode：从同一起点并行探索多条路径&lt;/h2&gt;
&lt;p&gt;这是最新的实验性特性（需要设置 &lt;code&gt;CLAUDE_CODE_FORK_SUBAGENT=1&lt;/code&gt;），但绝对值得尝试。&lt;/p&gt;
&lt;p&gt;Fork 不同于普通 subagent——它继承了当前对话的完整历史。这意味着你不用重新解释背景，fork 出来的 agent 已经知道你们之前聊了什么。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/fork 尝试用 Redis 做缓存层
/fork 尝试用 Memcached 做缓存层
/fork 尝试用本地 LRU cache 做缓存层
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;三个 fork 从同一个上下文出发，各自实现不同的方案。你可以在下方面板里用方向键切换、查看各自进度，最后挑一个最好的方案合并。因为 fork 共享父会话的 prompt cache，所以成本比启动三个独立 subagent 低得多。&lt;/p&gt;
&lt;p&gt;配合 &lt;code&gt;isolation: &quot;worktree&quot;&lt;/code&gt;，每个 fork 还能在独立的 git worktree 里工作，真正做到互不干扰。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;这 10 个技巧只是冰山一角。Claude Code 的能力边界远比&quot;在终端里和 AI 聊天&quot;宽广得多——它本质上是一个可编程的 AI agent 框架，Slash Commands 是你给它写的剧本，Hooks 是自动触发器，Subagents 是它的分身，而 Headless 模式让它融入你的整个工程体系。&lt;/p&gt;
&lt;p&gt;把这些拼在一起，你得到的不是一个更好的代码补全工具，而是一个可以 7x24 帮你干活的 AI 工程团队。&lt;/p&gt;
</content:encoded></item><item><title>LeetCode Biweekly Contest 92</title><link>https://dicer-zz.github.io/posts/leetcode-biweekly-contest-92/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/leetcode-biweekly-contest-92/</guid><description>Problem 1: 找规律可以发现，n为奇数时，由于不对称，无法进行过圆心的切分，所以只能切n刀；n为偶数时，可以通过切n/2刀。特殊的是n=1时不需要切分。</description><pubDate>Tue, 29 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Problem 1&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/minimum-cuts-to-divide-a-circle/description/&quot;&gt;Problem 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;找规律可以发现，n为奇数时，由于不对称，无法进行过圆心的切分，所以只能切n刀；n为偶数时，可以通过切n/2刀。特殊的是n=1时不需要切分。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def numberOfCuts(self, n: int) -&amp;gt; int:
        if n == 1:
            return 0
        if n % 2 == 0:
            return n // 2
        return n
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间复杂度：$O(1)$&lt;/p&gt;
&lt;p&gt;空间复杂度：$O(1)$&lt;/p&gt;
&lt;h2&gt;Problem 2&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/difference-between-ones-and-zeros-in-row-and-column/&quot;&gt;Problem 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;计数问题：计算每行每列的一的个数，剩下的位置就是零。对于每一个位置计算题中的公式即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def onesMinusZeros(self, grid: List[List[int]]) -&amp;gt; List[List[int]]:
        n = len(grid)
        m = len(grid[0])
        r = [sum(grid[i]) for i in range(n)]
        c = [0 for _ in range(m)]
        for j in range(m):
            tmp = 0
            for i in range(n):
                tmp += grid[i][j]
            c[j] = tmp
        ans = [[0 for i in range(m)] for j in range(n)]
        for i in range(n):
            for j in range(m):
                ans[i][j] = r[i] + c[j] - (n + m - r[i] - c[j])
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间复杂度：$O(nm)$&lt;/p&gt;
&lt;p&gt;空间复杂度：$O(nm)$&lt;/p&gt;
&lt;h2&gt;Problem 3&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/minimum-penalty-for-a-shop/&quot;&gt;Problem 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;理解一下题意就是：如果选择在第x小时关门，则答案=1-x中的N的个数 + x-n中Y的个数，枚举x求出最小值即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def bestClosingTime(self, customers: str) -&amp;gt; int:
        y = customers.count(&apos;Y&apos;)
        n = customers.count(&apos;N&apos;)
        ans, cost = -1, y
        tmp = 0
        cy, cn = 0, 0
        for i in range(len(customers)):
            if customers[i] == &apos;Y&apos;:
                cy += 1
            else:
                cn += 1
            if cost &amp;gt; cn + y - cy:
                cost = cn + y - cy
                ans = i
        return ans + 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间复杂度：$O(n)$&lt;/p&gt;
&lt;p&gt;空间复杂度：$O(1)$&lt;/p&gt;
&lt;h2&gt;Problem 4&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/count-palindromic-subsequences/&quot;&gt;Problem 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;如果问题中的子序列换成子数组还是非常简单的，直接枚举所有连续的长度为5的子串即可。&lt;/p&gt;
&lt;p&gt;对于题目中的子序列，我们可以通过贡献的角度来考虑问题，首先回文串中间的字符是无所谓的，只需要前两个字符和后两个字符对称即可，因此我们可以枚举中间字符的位置pos，计算pos之前有多少个双字符&lt;code&gt;ab&lt;/code&gt;和pos之后有多少个双字符&lt;code&gt;ba&lt;/code&gt;，其中 $0 \leq a, b \leq 9$&lt;/p&gt;
&lt;p&gt;灵神给出了一个优化空间复杂度的方法：&lt;a href=&quot;https://leetcode.cn/problems/count-palindromic-subsequences/solutions/1993115/qian-hou-zhui-fen-jie-o100-chang-shu-kon-51cv/&quot;&gt;Link&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def countPalindromes(self, s: str) -&amp;gt; int:
        suf = [0] * 10
        suf2 = [0] * 100
        for d in map(int, s[::-1]):
            for j, c in enumerate(suf):
                suf2[d * 10 + j] += c
            suf[d] += 1

        ans = 0
        prf = [0] * 10
        prf2 = [0] * 100
        for d in map(int, s):
            suf[d] -= 1
            for j, c in enumerate(suf):
                suf2[d * 10 + j] -= c
            ans += sum([c1 * c2 for c1, c2 in zip(prf2, suf2)])
            for j, c in enumerate(prf):
                prf2[d * 10 + j] += c
            prf[d] +=  1
        return ans % (10 ** 9 + 7)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间复杂度：$O(n|\Sigma|^2)$，$\Sigma$表示字符集的大小，这里是10。&lt;/p&gt;
&lt;p&gt;空间复杂度：$O(|\Sigma|^2)$&lt;/p&gt;
</content:encoded></item><item><title>LeetCode Weekly Contest 321</title><link>https://dicer-zz.github.io/posts/leetcode-weekly-contest-321/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/leetcode-weekly-contest-321/</guid><description>Problem 1: 普通做法：遍历每个数，判断左边和右边的和是否相等，时间复杂度$O(n^2)$，用等比数列求和公式可以将复杂度降到$O(n)$。从 1 到 n 枚举 x 即可。</description><pubDate>Mon, 28 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Problem 1&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/find-the-pivot-integer/&quot;&gt;Problem 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;普通做法：遍历每个数，判断左边和右边的和是否相等，时间复杂度$O(n^2)$，用等比数列求和公式可以将复杂度降到$O(n)$。
从 1 到 n 枚举 x 即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def pivotInteger(self, n: int) -&amp;gt; int:
        for i in range(1, n+1):
            a = (1 + i) * i // 2
            b = (1 + n) * n // 2 - a + i
            if a == b:
                return i
        return -1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间复杂度：$O(n)$&lt;/p&gt;
&lt;p&gt;空间复杂度：$O(1)$&lt;/p&gt;
&lt;p&gt;数学做法：1 到 x 的元素和为 $x(x+1)/2$，x 到 n 的元素和为$\frac{n(n+1) - x(x-1)}{2}$，两者相等则：$x = \sqrt{\frac{n(n+1)}{2}}$，如果x不是整数则返回-1。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def pivotInteger(self, n: int) -&amp;gt; int:
        m = n * (n + 1) // 2
        x = int(m ** 0.5)
        return x if x * x == m else -1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间复杂度：$O(1)$&lt;/p&gt;
&lt;p&gt;空间复杂度：$O(1)$&lt;/p&gt;
&lt;h2&gt;Problem 2&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/append-characters-to-string-to-make-subsequence/&quot;&gt;Problem 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;双指针：从头开始遍历s和t，如果相同则同时后移，不同则近移动s，当s遍历完时，t仍没有遍历的字符就是需要添加的字符。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def appendCharacters(self, s: str, t: str) -&amp;gt; int:
        i, j = 0, 0
        n = len(s)
        m = len(t)
        while i &amp;lt; n and j &amp;lt; m:
            if s[i] == t[j]:
                i += 1
                j += 1
            else:
                i += 1
        return m - j
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间复杂度：$O(n+m)$&lt;/p&gt;
&lt;p&gt;空间复杂度：$O(1)$&lt;/p&gt;
&lt;h2&gt;Problem 3&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/remove-nodes-from-linked-list/description/&quot;&gt;Problem 3&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;递归做法：如果只有一个节点则直接返回；不然对后续节点调用递归函数，后续节点的头节点值一定是最大的，因此比较当前节点和后续节点的头节点值，如果当前节点值小雨后续节点的头节点值则返回后续节点的头节点（删除当前节点），否则返回当前节点（保留当前节点）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def removeNodes(self, head: Optional[ListNode]) -&amp;gt; Optional[ListNode]:
        if head.next is None:
            return head
        head.next = self.removeNodes(head.next)
        if head.val &amp;lt; head.next.val:
            head = head.next
        return head
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间复杂度：$O(n)$&lt;/p&gt;
&lt;p&gt;空间复杂度：$O(n)$，需要$O(n)$的栈空间。&lt;/p&gt;
&lt;h2&gt;Problem 4&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.cn/problems/count-subarrays-with-median-k/description/&quot;&gt;Problem 4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;计数问题：对于一段区间来说，如果它的中位数是k，那么假设小于k的有p个，大于k的有q个，则一定有p == q或者p + 1 == p；如果k的位置为pos，那么从pos向左枚举，统计c1 = q - p的个数，然后从pos枚举，如果当前位置q-p=c2，那么考虑pos左边的c1，应该满足c1 + c2 = 0或者c1 + c2 = 1，则ans应该增加 -c1的个数以及1-c1的个数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def countSubarrays(self, nums: List[int], k: int) -&amp;gt; int:
        pos = nums.index(k)
        nums = [0 if num == k else (-1 if num &amp;lt; k else 1) for num in nums]
        c = defaultdict(int)
        tmp = 0
        for i in range(pos, -1, -1):
            tmp += nums[i]
            c[tmp] += 1
        tmp = 0
        ans = 0
        for i in range(pos, len(nums)):
            tmp += nums[i]
            ans += c[-tmp] + c[1-tmp]
        return ans
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间复杂度：$O(n)$&lt;/p&gt;
&lt;p&gt;空间复杂度：$O(n)$&lt;/p&gt;
</content:encoded></item><item><title>如何在 M 系芯片的 MacBook 上玩原神</title><link>https://dicer-zz.github.io/posts/play-genshim-impact-on-apple-silicon-chip/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/play-genshim-impact-on-apple-silicon-chip/</guid><description>在用上M系芯片之后苹果开始在自家的Mac系列电脑上使用新的M系芯片（也就是所谓的Apple Silicon Chip）之后，iPhone、iPad、Mac的芯片就被统一成了ARM架构，使用同一套RISC指令集。这意味着，我们甚至可能在iPhone运行MacOs。当然，我觉得刀法精准的厨子不会这么做。</description><pubDate>Thu, 28 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;在用上M系芯片之后&lt;/h2&gt;
&lt;p&gt;苹果开始在自家的Mac系列电脑上使用新的M系芯片（也就是所谓的Apple Silicon Chip）之后，iPhone、iPad、Mac的芯片就被统一成了ARM架构，使用同一套RISC指令集。这意味着，我们甚至可能在iPhone运行MacOs。当然，我觉得刀法精准的厨子不会这么做。&lt;/p&gt;
&lt;p&gt;此外，在用上M系芯片之后，只要开发者愿意，理论是可以直接将自己为iPhone或者iPad开发的软件移植到MacoOS上，开发者几乎什么都不需要做。现在有许多软件可以直接在Mac上下载安装iPad版本，虽然界面拉伸适配多少有一些问题，但是基本使用是可以的，比如抖音、Bilibili、微信读书。当然，这些软件也通常有更好用的网页版。&lt;/p&gt;
&lt;p&gt;而对于手游厂商来说，通常会直接关闭这项功能，导致我们无法直接在Mac上玩iPhone上的游戏，比如王者荣耀、原神等。&lt;/p&gt;
&lt;p&gt;但天无绝人之路，通过越狱等方式破解IPA文件之后，使用侧载的方法就可以将其安装在Mac上。IPA文件本质上就是iOS以及iPadOS使用的应用程序安装包的后缀，是zip压缩格式的扩展协议。&lt;/p&gt;
&lt;h2&gt;PlayCover&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://playcover.io/&quot;&gt;PlayCover官网&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;PlayCover是一个非常傻瓜式的IPA侧载软件，只需要从破解IPA的网站下载下来你需要的软件，然后使用Playcover加载以后就可以使用了。如果是第一次使用，可能需要安装&lt;code&gt;xcode&lt;/code&gt;。具体的安装操作可以参考PlayCover的&lt;a href=&quot;https://docs.playcover.io&quot;&gt;官方文档&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;破解IPA（或称砸壳）需要一部越狱的iPhone，也有一定的学习成本，因此推荐使用第三方的破解网站下载IPA。PlayCover文档中给出了两个推荐的网站，一个是&lt;a href=&quot;https://decrypt.day&quot;&gt;decrypt&lt;/a&gt;，这个是我比较经常用的，更新比较及时，下载速度也很快。另一个是&lt;a href=&quot;https://armconverter.com/decryptedappstore&quot;&gt;armconverter&lt;/a&gt;，这个我没用过，大家可以自行尝试。&lt;/p&gt;
&lt;p&gt;需要注意的是，这种非官方的破解软件，可能会存在一定的风险，一定要仔细甄别。&lt;/p&gt;
&lt;h2&gt;原神&lt;/h2&gt;
&lt;p&gt;在MacOS 11.3之前，是能直接在Mac上下载iPad版的原神的，但是由于原神的更新，现在已经无法直接下载了。因此，我们需要使用PlayCover来安装。&lt;/p&gt;
&lt;p&gt;首先，从&lt;a href=&quot;https://decrypt.day&quot;&gt;decrypt&lt;/a&gt;下载原神的破解IPA文件，然后使用PlayCover加载。如果安装了&lt;code&gt;xcode&lt;/code&gt;应该就能够直接打开，然后进入游戏之后有一个10G+的资源更新，静静等待之后就可以进入游戏了。&lt;/p&gt;
&lt;p&gt;此外，原神大版本更新比较频繁，而且不更新是没发进入游戏的，因此需要定期更新破解IPA文件。通常在版本更新后的半天内，decrypt就会更新破解文件。安装新版本的时候切记不要删除之前的版本（否则需要重新下载10G+的资源包），只需要将新版本的破解文件拖入PlayCover即可，会自动覆盖之前的版本。&lt;/p&gt;
</content:encoded></item><item><title>Utilize Apple Silicon&apos;s GPU by PyTorch Nightly</title><link>https://dicer-zz.github.io/posts/utilize-apple-silicon-s-gpu-by-pytorch-nightly/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/utilize-apple-silicon-s-gpu-by-pytorch-nightly/</guid><description>Pytorch nightly 已经支持了 Apple Silicon 的 GPU，可以通过以下方式来使用它： 首先，你的 Macbook 应该是使用 Apple Silicon (M系列芯片) 新款笔记本，而不是使用 Intel 的笔记本。另外，需要 Mac OS 是 12.3 或更高版本。</description><pubDate>Wed, 27 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Pytorch nightly 已经支持了 Apple Silicon 的 GPU，可以通过以下方式来使用它：&lt;/p&gt;
&lt;p&gt;首先，你的 Macbook 应该是使用 Apple Silicon (M系列芯片) 新款笔记本，而不是使用 Intel 的笔记本。另外，需要 Mac OS 是 12.3 或更高版本。&lt;/p&gt;
&lt;p&gt;然后建议使用 conda 来安装 pytorch-nightly：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# MPS acceleration is available on MacOS 12.3+
conda install pytorch torchvision torchaudio -c pytorch-nightly
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装成功之后可以通过以下方式确实是否可以使用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import torch

print(torch.backends.mps.is_available())    # True
print(torch.backends.mps.is_built())    # True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果两者都是 True 的话就没有问题，Sebastian Raschka 在他的 &lt;a href=&quot;https://sebastianraschka.com/blog/2022/pytorch-m1-gpu.html&quot;&gt;Blog&lt;/a&gt; 中提供了一些&lt;a href=&quot;https://github.com/rasbt/machine-learning-notes/tree/main/benchmark/pytorch-m1-gpu&quot;&gt;基准测试脚本&lt;/a&gt;，感兴趣的话可以自己跑一下。&lt;/p&gt;
&lt;p&gt;我在自己的 M1acbook Pro 上测试了 ResNet 和 MLP 在 mnist 上的训练速度，GPU 大概比 CPU 快个一倍左右，这个速度只能说聊胜于无。&lt;/p&gt;
</content:encoded></item><item><title>素性测试：A Survey</title><link>https://dicer-zz.github.io/posts/primailty-test-a-survey/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/primailty-test-a-survey/</guid><description>素数素数（Prime number），又称质数，指在大于1的自然数中，除了1和该数自身之外，无法被其他自然数整除的数。大于1的自然数，若不是素质，则为合数。前十个素数分别是：2、3、5、7、11、13、17、19、23、29。 RSA加密是现在网络安全系统中非常常用的一种非对称加密方法，这种方法的安全性依赖于大数质因子分解非常困难。 任何一个大于1的自然数都可以表示成素数乘积的形式，并且如果将素数</description><pubDate>Sat, 27 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;素数&lt;/h1&gt;
&lt;p&gt;素数（Prime number），又称质数，指在大于1的自然数中，除了1和该数自身之外，无法被其他自然数整除的数。大于1的自然数，若不是素质，则为合数。前十个素数分别是：2、3、5、7、11、13、17、19、23、29。&lt;/p&gt;
&lt;p&gt;RSA加密是现在网络安全系统中非常常用的一种非对称加密方法，这种方法的安全性依赖于大数质因子分解非常困难。&lt;/p&gt;
&lt;p&gt;任何一个大于1的自然数都可以表示成素数乘积的形式，并且如果将素数按顺序写出来，这个表示方法是唯一的，这被称为算数基本定理。比如，60=2&lt;em&gt;2&lt;/em&gt;3*5。将自然数表示成素数乘积的过程叫做质因子分解，可以用来判断一个数是不是素数。另外，还有一些方法可以在不进行质因子分解就能知道一个数字是不是素数，这些方法可以分为两类，分别是随机的和确定的。随机方法不一定能保证通过测试的一定是素数，只能保证通过检验的数很大可能上是一个素数。而确定的则可以完全保证通过测试的数字是一个素数。&lt;/p&gt;
&lt;p&gt;下面我们具体研究各种判断素数的方法。&lt;/p&gt;
&lt;h1&gt;试除法&lt;/h1&gt;
&lt;p&gt;试除法是可以得到质因子分解结果的验证素数的方法。&lt;/p&gt;
&lt;p&gt;对于自然数n（大于1）根据素数的定义：只有1和n两个因数，那么我们可以枚举所有小于n的自然数（大于n的数字显然不可能是n的因子）然后判断能否整除，如果都不能整除，那么说明n只有1和本身两个因子，因此n就是素数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def isPrime(n):
    if n == 1: # 1 is not prime
        return False
    elif n == 2: # 2 is prime
        return True
    else: # all other numbers
        for i in range(2, n): # check all numbers from 2 to n-1
            if n % i == 0: # if n is divisible by i
                return False # n is not prime
        return True # n is prime
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看出这个算法的时间复杂度是$O(n)$的，并不理想。&lt;/p&gt;
&lt;p&gt;我们进一步的思考，考虑$d|n$，则有$dm=n$，不妨假设$d\leq m$，则$d^2\leq dm=n$，即$d\leq \sqrt n$。因此我们只需要枚举小于$\sqrt n$的自然数即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def isPrime(n):
    if n == 1:
        return False
    i = 2
    while i*i &amp;lt;= n:
        if n % i == 0:
            return False
        i += 1
    return True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个算法的复杂度是$O(\sqrt n)$&lt;/p&gt;
&lt;p&gt;进一步思考，可以知道除了2以外所有的偶数都不是素数，并且所有3的倍数也都不是素数。所有的自然数都可以表示成$6k+i,; k\geq 0,; 0\leq i \leq 5$，但其中$6k+2, 6k+4$是2的倍数，$6k, 6k+3$是3的倍数。因此只有$6k+1,6k+5$可能是素数，这可以进一步提高试除法的效率。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def isPrime(n):
    if n == 1:
        return False
    if n == 2 or n == 3:
        return True
    if n % 2 == 0 or n % 3 == 0:
        return False
    i = 5 # i = 5, 11, 17, 23, 29, 35, 41, 47, 53, 59, ...
    while i*i &amp;lt;= n:
        if n % i == 0: # n is divisible by i
            return False # n is not prime
        if n % (i+2) == 0: # n is divisible by i+2
            return False # n is not prime
        i += 6
    return True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个算法的复杂度还是$O(\sqrt n)$，不过比上面的快了三倍，因为枚举的数字少了。&lt;/p&gt;
&lt;h1&gt;筛法&lt;/h1&gt;
&lt;p&gt;试除法一次只能找到一个素数，但有的时候我们可能需要快速的找到多个素数，虽然我们也可以多次使用试除法来得到，但我们有更快的方法：筛法。给出自然数n，这种方法可以快速求出所有小于等于n的素数。&lt;/p&gt;
&lt;h2&gt;埃拉托斯特尼筛法&lt;/h2&gt;
&lt;p&gt;埃拉托斯特尼筛法（Sieve of Eratosthenes）简称埃筛或埃式筛法，这个方法历史悠久，早在公元前2世纪就被埃拉托斯特尼提出了。&lt;/p&gt;
&lt;p&gt;其算法的主要思想是：如果一个数字$p$是素数，那么它的倍数就都不是素数。我们从2枚举到n，2是素数，因此划掉所有2的倍数。如果一个数字在被枚举到时，还没有被划掉，那么就说明这个数是素数。下面这张图很好的展示了这个过程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/b/b9/Sieve_of_Eratosthenes_animation.gif&quot; alt=&quot;https://upload.wikimedia.org/wikipedia/commons/b/b9/Sieve_of_Eratosthenes_animation.gif&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;埃筛的代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def sieveOfEratosthenes(n):
    prime = [True for _ in range(n+1)]
    p = 2
    while (p * p &amp;lt;= n):
        if (prime[p] == True): # if p is prime
            for i in range(p * 2, n+1, p): # mark all multiples of p as not prime
                prime[i] = False
        p += 1
    prime[0] = False
    prime[1] = False
    return prime
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;埃筛的复杂度证明&lt;/h3&gt;
&lt;p&gt;可以看出埃筛的总删除次数为：$\frac{n}{2}+\frac{n}{3}+\cdots+\frac{n}{p}=n\sum_{p\leq n}\frac{1}{p}$。根据 Mertens’ theorems ，$\lim_{n\to\infty}\left(\sum_{p\leq n}\frac{1}{p}-\ln \ln n -M\right)=0$，其中$M \approx 0.261497$。&lt;/p&gt;
&lt;p&gt;因此埃筛的时间复杂度为$O(n\ln\ln n)$&lt;/p&gt;
&lt;h2&gt;欧拉筛&lt;/h2&gt;
&lt;p&gt;埃式筛法还是有进步的空间，因为埃筛在划掉数字的时候可能会发生很多重复。比如对于数字6，它即是2的倍数，又是3的倍数。如果能避免这种重复，那么每个数字就只需要划掉一遍就可以了，这样我们能得到一个线性复杂度的筛法。这个方法最早被莱昂哈德·欧拉发现，所以被称为欧拉筛。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def sieveOfEuler(n):
    isPrime = [True for _ in range(n+1)]
    Prime = []
    for i in range(2, n+1):
        if isPrime[i]:
            Prime.append(i)
        for pri in Prime:
            if i * pri &amp;gt; n:
                break
            isPrime[i * pri] = False
            if i % pri == 0:
                break
    return Prime
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;素性测试&lt;/h1&gt;
&lt;p&gt;本小结的主要内容是一些快速进行素数检测的方法，分为概率型和确定型两种。概率型素性检测并能保证通过测试的数字一定是素数，因此被称为伪素数。确定型素性检测则可以保证通过检测的一定是素数。&lt;/p&gt;
&lt;h2&gt;费马素性测试&lt;/h2&gt;
&lt;p&gt;费马素性测试是一种概率素性测试，它主要使用的数学原理是费马小定理。&lt;/p&gt;
&lt;p&gt;费马小定理说：如果一个数字p是素数，那么对于任意不能被p整除的自然数a来说，满足下式：&lt;/p&gt;
&lt;p&gt;$$a^{p-1}\equiv 1\mod p$$&lt;/p&gt;
&lt;p&gt;那么我们如果发现一个这样的a，不满足上面的式子，那么我们就可以肯定p一定不是一个素数，而是一个合数。而如果p通过了一次或多次这样的测试，我们称之为 &lt;strong&gt;费马伪素数&lt;/strong&gt;，之所以叫做伪素数是因为费马测试不能保证通过测试的数字一定是素数。&lt;/p&gt;
&lt;p&gt;考虑到以下两个情况：1、如果$a\equiv 1 \mod p$，那么费马小定理一定成立。2、如果$a\equiv -1 \mod p$，那么如果p是奇数，费马小定理一定成立，而素数中只有2一个偶数。因此，我们在挑选a时，不需要考虑这两种情况。&lt;/p&gt;
&lt;p&gt;一般来说，选择范围是$1\lt a\lt p-1$。如果我们选择的a满足$gcd(a, p)=d&amp;gt;1$，那么显然此时p不是一个素数（p有因子$d=gcd(a, p)$）也可以证明此时费马小定理不成立，证明如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;从区间$[2, p-1)$选出一个自然数a，并且$gcd(a, p)=d&amp;gt;1$。假设a、p满足费马小定理，即$a^{p-1}\equiv 1\mod p$，存在整数k满足$a^{p-1}-pk=1$，因为$a|d, p|d$，所以$0\equiv 1 \mod d$，而此式仅当d=1时成立，由反证法可知a、p不满足费马小定理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但如果我们仅选择满足$gcd(a, p)=1$的数字a，即使对于所有这样的a，p都能通过费马测试，也不能保证p就一定是素数，这类数字被称为 &lt;strong&gt;卡迈克尔数&lt;/strong&gt;（Carmichael number），定义为：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在数论上，卡迈克尔数是合数n，对于任意与n互质的数字a，满足$a^{n-1}\equiv 1\mod n$&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;最小的卡迈克尔数是$561=3\times 11\times17$。但其实不用很担心，因为卡迈克尔数非常的稀疏，$10^{16}$之内只有246683个，相对于这个范围来说是非常少的。不过为了保证算法的正确性，我们还是不能仅仅判断$gcd(a, p)=1$的情况。&lt;/p&gt;
&lt;p&gt;那么我们进一步思考，是否有对于区间$[2, p-1)$中的所有的a，对满足费马小定理的伪素数n呢？&lt;/p&gt;
&lt;p&gt;其实这是不可能的，因为如果n不是素数，那么区间$[2, p-1)$一定有其因数d，当a取得d时，根据上面的证明，费马小定理一定不成立。因此，我们可以保证，枚举所有的a，一定能验证一个数字是否为素数。&lt;/p&gt;
&lt;p&gt;但是枚举所有的a，这个算法的时间复杂度为$O(n\log n)$，甚至超过了试除法的$O(\sqrt n)$，这显然不是我们想要的结果。如果我们能够知道一个数字通过费马测试的可能性有多高，那么我们就可以多次选择不同的a来进行费马测试，一定有一次没能通过费马测试，那么就说明这个数字a不是一个素数。&lt;/p&gt;
&lt;p&gt;其实有如下结论：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果n至少有一个与其互质的a能证明它不是伪素数，也就是说如果n不是卡迈克尔数，那么在与其互质的a中，至少有一半能证明n不是一个伪素数，也就是说n是一个合数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那么一次费马测试的通过率就是50%，如果我们多次进行测试就可以把概率降低至我们能接受的范围。如果测试k次，这个算法的时间复杂度就是$O(k\log n)$&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def fermatPrimailtyTest(n, k):
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    for _ in range(k):
        a = random.randint(2, n - 1)
        if pow(a, n - 1, n) != 1:   # Fermat&apos;s little theorem
            return False
    return True
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;米勒-拉宾素性测试&lt;/h2&gt;
&lt;p&gt;费马测试在数字较小的时候准确性尚可，但是当数字大到一定程度（$2^{64}$）或遇到卡迈克尔数时，准确性就不尽人意了。&lt;/p&gt;
&lt;p&gt;米勒-拉宾素性测试方法由米勒（&lt;a href=&quot;https://en.wikipedia.org/wiki/Gary_Miller_(professor)&quot;&gt;Gary L. Miller&lt;/a&gt;）在1976年提出，但原始的算法是确定型检测方法，并且正确性依赖于未被证明的扩展黎曼猜想（&lt;a href=&quot;https://en.wikipedia.org/wiki/Extended_Riemann_hypothesis&quot;&gt;extended Riemann hypothesis&lt;/a&gt;）拉宾（&lt;a href=&quot;https://en.wikipedia.org/wiki/Michael_O._Rabin&quot;&gt;Michael O. Rabin&lt;/a&gt;）在1980年将其修改为无条件的概率型检测方法。&lt;/p&gt;
&lt;p&gt;在将米勒-拉宾测试的原理之前，我们需要先介绍几个引理。&lt;/p&gt;
&lt;h3&gt;二次剩余&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;如果存在整数X，使得$X^2\equiv a \mod p$，则称a是模p的二次剩余。
如果不存在这样的X，则称a是模p的二次非剩余。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;二次探测定理&lt;/h3&gt;
&lt;p&gt;对于一个素数p，满足$X^2\equiv 1 \mod p$的解为$X_1 = 1, X_2 = p-1$。&lt;/p&gt;
&lt;p&gt;证明：&lt;/p&gt;
&lt;p&gt;$$X^2\equiv 1\bmod p \implies (X-1)(X+1)\equiv 0\bmod p$$&lt;/p&gt;
&lt;p&gt;由于p是素数，$(X-1)(X+1)$是p的倍数&lt;/p&gt;
&lt;p&gt;所以要么$X-1 \equiv 0\bmod p$，要么$X+1 \equiv 0\bmod p$&lt;/p&gt;
&lt;p&gt;即，$X=1$ 或 $X=p-1$&lt;/p&gt;
&lt;h3&gt;米勒-拉宾测试&lt;/h3&gt;
&lt;p&gt;与费马测试一样，米勒-拉宾测试是通过检测一个数字是否满足那些素数一定满足的性质来判断这个数字是否是素数的。&lt;/p&gt;
&lt;p&gt;先说方法，对于一个整数n&amp;gt;2，可以表示为$n = 2^rd+1$（r是一个整数，d是一个奇数）考虑一个整数a（$0\lt a\lt n$），作为基数，如果n、a满足：&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
a^d&amp;amp;\equiv 1 \bmod n \\
a^{2^rd}&amp;amp;\equiv -1 \bmod n\text{ for some }0\leq r\lt s
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;则称n是基a下的&lt;strong&gt;强可能素数&lt;/strong&gt; ，也就是说通过一次测试的数字也不一定就真的是素数。幸运的是，不存在能够通过所有基数a的强可能素数，如果我们枚举遍所有的基数，我们就可以得到一个缓慢的确定型素数测试方法。&lt;/p&gt;
&lt;p&gt;这个结论基于奇素数满足以下两个定理：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;费马小定理&lt;/li&gt;
&lt;li&gt;二次探测定理&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;现尝试证明为何满足上述两个定理就能满足（1）（2）式：&lt;/p&gt;
&lt;p&gt;对于$n = 2^sd+1$，根据费马小定理：&lt;/p&gt;
&lt;p&gt;$$a^{n-1}\equiv a^{2^sd}\equiv 1\mod n$$&lt;/p&gt;
&lt;p&gt;对于序列$a^{2^{s}d},a^{2^{s-1}d},\cdots,a^{2d},a^{d}$，每一项都是前一项的平方根，因为第一项与1同余，所有后面每一项都是模n意义下1的平方根，根据二次探测定理，应该与1或-1同余。如果它与-1同余，那么就满足（2）式，如果都与1同余，那么我们能得到最后一项也与1同余，即（1）式。&lt;/p&gt;
&lt;p&gt;这样我们就可以随机选择一些基数a，通过计算其是否满足（1）（2）式来确实其是否是一个素数。通过多次迭代，我们可以把假阳性的概率降低到我们能接受的范围内。&lt;/p&gt;
&lt;p&gt;有证明说明，对于和数n，最多能通过1/4的基数a的检测，也就是说，如果检测k次，米勒-拉宾检测方法的失效概率至多是$\frac{1}{4^k}$。&lt;/p&gt;
&lt;p&gt;另外，还有一些基数的集合可以保证，当n小于一定的值时，所有能通过集合中所有基数的n一定是素数，这为我们提供了一定范围内的快速素数检测。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;small than&lt;/th&gt;
&lt;th&gt;base set&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2,047&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,373,653&lt;/td&gt;
&lt;td&gt;2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9,080,191&lt;/td&gt;
&lt;td&gt;31, 73&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3,215,031,751&lt;/td&gt;
&lt;td&gt;2, 3, 5, 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;…&lt;/td&gt;
&lt;td&gt;…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3,825,123,056,546,413,051&lt;/td&gt;
&lt;td&gt;2, 3, 5, 7, 11, 13, 17, 19, 23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18,446,744,073,709,551,616&lt;/td&gt;
&lt;td&gt;2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这基本上就能满足一些普通的需求了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def MillerRabinPrimalityTest(n, k):
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    r, s = 0, n - 1
    while s % 2 == 0:
        r += 1
        s //= 2
    for _ in range(k):
        a = random.randrange(2, n - 1)
        x = pow(a, s, n)
        if x == 1 or x == n - 1:
            continue
        for _ in range(r - 1):
            x = pow(x, 2, n)
            if x == n - 1:
                break
        else:
            return False
    return True
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该代码的时间复杂度为$O(k\log^2 n)$&lt;/p&gt;
&lt;p&gt;米勒的原始版本证明了，如果枚举所有$a\in [2, min(n-2,\lfloor2\ln^2n\rfloor)]$都不能证明n是一个合数，那么n就是一个素数。但是这个证明基于拓展的黎曼猜想为真的假设。&lt;/p&gt;
&lt;h2&gt;确定素性测试&lt;/h2&gt;
&lt;p&gt;确定型素性测试不是本篇内容的重点，比较出名的一个确定型测试是AKS素性检测，由&lt;a href=&quot;https://en.wikipedia.org/wiki/Manindra_Agrawal&quot;&gt;Manindra Agrawal&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Neeraj_Kayal&quot;&gt;Neeraj Kayal&lt;/a&gt;, and &lt;a href=&quot;https://en.wikipedia.org/wiki/Nitin_Saxena&quot;&gt;Nitin Saxena&lt;/a&gt;在2002年提出，如果有兴趣大家可以自己研究一下。&lt;/p&gt;
&lt;h1&gt;工业素数生成&lt;/h1&gt;
&lt;p&gt;有了快速的素性检测方法，那么我们就可以轻松的得到一些高可能性的大素数，这些素数被称为工业级素数，可以用于一些密码学中的加密方法，比如RSA加密。&lt;/p&gt;
&lt;p&gt;如果需要一个二进制下1024位的素数，我们可以从$[2^{1023}, 2^{1024})$这个区间里随机选择一个数字，然后使用素性检测判断其是否是一个素数，如果通过测试，那么我们就得到了一个工业级素数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def largePrimeGenerator(n):
    &apos;&apos;&apos;
    Generates a large prime number.
    &apos;&apos;&apos;
    # Generate a random number
    randomNumber = random.randint(2**(n-1), 2**n)
    # Check if the number is prime
    if millerRabin(randomNumber):
        return randomNumber
    else:
        return largePrimeGenerator(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Reference&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/121395816&quot;&gt;https://zhuanlan.zhihu.com/p/121395816&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%B4%A8%E6%95%B0&quot;&gt;https://zh.wikipedia.org/wiki/质数&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes&quot;&gt;https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Mertens%27_theorems&quot;&gt;https://en.wikipedia.org/wiki/Mertens’_theorems&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mathcrypto.wordpress.com/2014/11/11/the-fermat-primality-test/&quot;&gt;https://mathcrypto.wordpress.com/2014/11/11/the-fermat-primality-test/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Euler%27s_criterion&quot;&gt;https://en.wikipedia.org/wiki/Euler’s_criterion&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>LeetCode 2081. Sum of k-Mirror Numbers</title><link>https://dicer-zz.github.io/posts/leetcode-2081-sum-of-k-mirror-numbers/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/leetcode-2081-sum-of-k-mirror-numbers/</guid><description>昨天闲来无事做了一个LeetCode的周赛，看到大佬不到二十分钟就AK了，真是Orz 本篇记录一下第四题的答案。 比赛链接</description><pubDate>Thu, 25 Nov 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;昨天闲来无事做了一个LeetCode的周赛，看到大佬不到二十分钟就AK了，真是Orz&lt;/p&gt;
&lt;p&gt;本篇记录一下第四题的答案。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode-cn.com/contest/weekly-contest-268/&quot;&gt;比赛链接&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;k 镜像数字的和&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode-cn.com/problems/sum-of-k-mirror-numbers/&quot;&gt;题目链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;可以考虑快速找到所有十进制下的回文数，然后判断在k进制下是否是回文数。&lt;/p&gt;
&lt;p&gt;快速查找回文数的方法，可以用构造法：&lt;/p&gt;
&lt;p&gt;考虑数字 99，它的下一个回文数应该是 101。&lt;/p&gt;
&lt;p&gt;考虑数字 1234，它的下一个回文数应该是 1331。&lt;/p&gt;
&lt;p&gt;考虑数字 999，它的下一个回文数应该是 1001。&lt;/p&gt;
&lt;p&gt;考虑数字 191，它的下一个回文数应该是 202。&lt;/p&gt;
&lt;p&gt;将数字的前一半（如果是奇数位，包括中间位）定义为left，然后将left+1，如果产生进位，那么我们需要直接找100…001格式的下一个数字。否则，我们只需要将left对称就可以得到下一个回文数的后半部分了（需要考虑数字的位数）&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Number&lt;/th&gt;
&lt;th&gt;Left&lt;/th&gt;
&lt;th&gt;Left+1&lt;/th&gt;
&lt;th&gt;Right&lt;/th&gt;
&lt;th&gt;NextParlindrome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;99&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;999&lt;/td&gt;
&lt;td&gt;99&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1234&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;1331&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;191&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;202&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;实现上述逻辑：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def nextParlindrome(s):
        left = s[:(len(s)+1)//2]
        carry = len(str(int(left) + 1)) != len(left)
        odd = int(len(s) % 2 == 1)
        left = str(int(left) + 1)
        if carry:	# 特判 99..99 格式的数字
            return &quot;1&quot; + &quot;0&quot;*(len(s)-1) + &quot;1&quot;
        else:
            return left + (left[:-1][::-1] if odd else left[::-1])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看出，这个复杂度是只与数字的长度相关的，因此复杂度为 $O(len(s))$&lt;/p&gt;
</content:encoded></item><item><title>知乎回答图片爬虫</title><link>https://dicer-zz.github.io/posts/the-image-spider-of-zhihu/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/the-image-spider-of-zhihu/</guid><description>有一些知乎问题下的回答中包含了很多精美的图片，比如一些壁纸、风景图。 如果想保存的话，手动一张一张的保存有太慢了。 我自己用Python爬虫实现了一个并发的知乎回答图片爬虫，只需要将知乎问题ID输入即可，还能支持多问题同时爬取。速度非常快。只需要python环境和一些很基础的网络工具包就可以了，快来试试吧！ 项目地址：ZhihuPicCrawler</description><pubDate>Thu, 30 Sep 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;有一些知乎问题下的回答中包含了很多精美的图片，比如一些壁纸、风景图。&lt;/p&gt;
&lt;p&gt;如果想保存的话，手动一张一张的保存有太慢了。&lt;/p&gt;
&lt;p&gt;我自己用Python爬虫实现了一个并发的知乎回答图片爬虫，只需要将知乎问题ID输入即可，还能支持多问题同时爬取。速度非常快。只需要python环境和一些很基础的网络工具包就可以了，快来试试吧！&lt;/p&gt;
&lt;p&gt;项目地址：&lt;a href=&quot;https://github.com/Dicer-Zz/ZhihuPicCrawler&quot;&gt;ZhihuPicCrawler&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;项目优势&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;不需要登陆。&lt;/li&gt;
&lt;li&gt;图片并发下载，速度更快。&lt;/li&gt;
&lt;li&gt;可以限制图片的数量和大小。&lt;/li&gt;
&lt;li&gt;支持一次同时爬取多个问题。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;效果展示&lt;/h1&gt;
&lt;p&gt;我们选择这个问题：&lt;a href=&quot;https://www.zhihu.com/question/41895584/answer/556880042&quot;&gt;有哪些你不舍得换的手机壁纸？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;可以看到问题ID是41895584，在命令行执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python Spider.py -q 41895584 --num_pic 1000
已获取前 10 个回答，当前链接总数为 1795
图片链接爬取完毕
50 images saved (21 MB).
100 images saved (45 MB).
150 images saved (67 MB).
200 images saved (92 MB).
250 images saved (114 MB).
300 images saved (140 MB).
350 images saved (166 MB).
400 images saved (189 MB).
450 images saved (213 MB).
500 images saved (236 MB).
550 images saved (257 MB).
600 images saved (281 MB).
650 images saved (302 MB).
700 images saved (328 MB).
750 images saved (352 MB).
800 images saved (375 MB).
850 images saved (398 MB).
900 images saved (422 MB).
950 images saved (444 MB).
1000 images saved (471 MB).
1011 images downloaded. Image size: 476 MB.
Time cost: 147.884s. 3.219 MB/s.
(base) 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到速度还是很快的！并且过程结果很清晰！&lt;/p&gt;
&lt;p&gt;爬取下来的结果：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/gallery/others/imgSpider01.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;使用方法&lt;/h1&gt;
&lt;p&gt;项目通过命令行进行参数解析，因此不需要显式的修改代码。&lt;/p&gt;
&lt;p&gt;参数的具体用途及用法如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python Spider.py -h                        
usage: Spider.py [-h] -q QIDS [QIDS ...] [--num_pic The number of pictures Default: 2000)] [--max_size The maximum size(KB) limitation of pictures (Default: 10000)] [--min_size The minimum size(KB) limitation of pictures (Default: 200)] [--num_workers The number of workers (Default: 20]

This is a script that can download images from Zhihu.

optional arguments:
  -h, --help            show this help message and exit
  -q QIDS [QIDS ...], --qIDs QIDS [QIDS ...]
  --num_pic The number of pictures (Default: 2000)
  --max_size The maximum size(KB) limitation of pictures (Default: 10000)
  --min_size The minimum size(KB) limitation of pictures (Default: 200)
  --num_workers The number of workers (Default: 20)
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>The Deviation of Cross Entropy with Softmax</title><link>https://dicer-zz.github.io/posts/the-deviation-of-cross-entropy-with-softmax/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/the-deviation-of-cross-entropy-with-softmax/</guid><pubDate>Mon, 28 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Softmax function&lt;/h1&gt;
&lt;p&gt;Softmax function is used to regularize all number of a vector to [0, 1]. It is usual appeared in classification problems. By softmax, a vector with huge number can be projected to a small number range -- from 0 to 1. That is useful to avoid gradient explosion &amp;amp; vanishing.&lt;/p&gt;
&lt;p&gt;The formula of softmax as follows:
$$
z = [z_1, z_2, \cdots, z_n] \\
a = \text{softmax}(z) = [\frac{e^{z_1}}{\sum{e^{z_k}}}, \frac{e^{z_2}}{\sum{e^{z_k}}}, \cdots, \frac{e^{z_n}}{\sum{e^{z_k}}}]
$$
By the way, softmax function is considered as a high-dimension generalization of sigmoid function and sigmoid function is also cansidered as a 2-dim version of softmax function. The formula of sigmoid as follows:
$$
\sigma(x) = \frac{1}{1+e^{-x}}
$$
&lt;img src=&quot;/gallery/others/sigmoid.png&quot; alt=&quot;sigmoid function from -5 to 5&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Cross entropy&lt;/h1&gt;
&lt;p&gt;The cross-entropy of two probability distribution $p$ and $q$ is defined as follows:
$$
H(p, q) = -\sum p_i\log q_i
$$
In classification problems, $p$ is usual a one-hot vector, which have only one position is 1, and others are 0. So, the defintion of cross-entropy is simplified as follows:
$$
H(p, q)=-p_k\log q_k = -\log q_k
$$
We also use cross-entropy as loss function. It is very intuitive because, with the increase or decrease of $q_k$, the loss will change conversely.&lt;/p&gt;
&lt;h1&gt;Calculate the derivative of loss function&lt;/h1&gt;
&lt;p&gt;The simplest classification network as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/gallery/others/corss_entropy.png&quot; alt=&quot;cross_entropy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here $z$ is the output of the former neural network, $a$ is the result of softmax $z$, $\hat y$ is the correct answer and $loss$ is the cross entropy of $a$ and $\hat y$.&lt;/p&gt;
&lt;p&gt;The formulas as follows:
$$
\begin{equation}
\begin{split}
loss
&amp;amp; = \text{cross entropy}(a, \hat y)\
&amp;amp; = \text{cross entropy}(\text{softmax}(z), \hat y)
\end{split}
\end{equation}
$$
In order to make backpropagation, we need to calculate the deviation of $loss$:
$$
\begin{equation}
\begin{split}
\frac{\partial l}{\partial \mathbf z}
&amp;amp; = \frac{\partial l}{\partial \mathbf a}\frac{\partial \mathbf a}{\partial \mathbf z}
\end{split}
\end{equation}
$$
Here $l$ is a scalar, $a$ and $z$ are vectors.&lt;/p&gt;
&lt;p&gt;First, we need to know how to calculate the derivative of a scalar $y$ by a vector $x$:
$$
\frac{\partial y}{\partial \mathbf x} = [\frac{\partial y}{\partial x_1},\frac{\partial y}{\partial x_2},\cdots, \frac{\partial y}{\partial x_n}]
$$
Second, how to calculate the derivative of a vector $y$ by a scalar $x$:
$$
\frac{\partial \mathbf y}{\partial x} =\begin{bmatrix}\frac{\partial y_1}{\partial x} \\ \frac{\partial y_2}{\partial x}\\ \vdots \\ \frac{\partial y_n}{\partial x}\end{bmatrix}
$$&lt;/p&gt;
&lt;p&gt;And, how to calculate the derivative of a vector $y$ by a vector $x$:
$$
\frac{\partial \mathbf y}{\partial \mathbf x} =
\begin{bmatrix}
\frac{\partial y_1}{\partial x_1} &amp;amp; \frac{\partial y_1}{\partial x_2} &amp;amp; \cdots &amp;amp; \frac{\partial y_1}{\partial x_n}\\
\frac{\partial y_2}{\partial x_1} &amp;amp; \frac{\partial y_2}{\partial x_2} &amp;amp; \cdots &amp;amp; \frac{\partial y_2}{\partial x_n} \\
\vdots &amp;amp; \vdots &amp;amp; \ddots &amp;amp; \vdots \\
\frac{\partial y_n}{\partial x_1} &amp;amp; \frac{\partial y_n}{\partial x_2} &amp;amp; \cdots &amp;amp; \frac{\partial y_n}{\partial x_n}\end{bmatrix}
$$
We can infer the former two by the last one.&lt;/p&gt;
&lt;p&gt;According to the above formula, we have this result as follows:
$$
\begin{split}
\frac{\partial l}{\partial \mathbf a}
&amp;amp; = [\frac{\partial l}{\partial a_1},\frac{\partial l}{\partial a_2},\cdots, \frac{\partial l}{\partial a_k}, \cdots, \frac{\partial l}{\partial a_n}] \\
&amp;amp; = [0, 0, \cdots, -\frac{1}{a_k}, \cdots, 0]
\end{split}
$$&lt;/p&gt;
&lt;p&gt;$$
\frac{\partial \mathbf a}{\partial \mathbf z} =
\begin{bmatrix}
\frac{\partial a_1}{\partial z_1} &amp;amp; \frac{\partial a_1}{\partial z_2} &amp;amp; \cdots &amp;amp; \frac{\partial a_1}{\partial z_n}\\
\frac{\partial a_2}{\partial z_1} &amp;amp; \frac{\partial a_2}{\partial z_2} &amp;amp; \cdots &amp;amp; \frac{\partial a_2}{\partial z_n} \\
\vdots &amp;amp; \vdots &amp;amp; \ddots &amp;amp; \vdots \\
\frac{\partial a_n}{\partial z_1} &amp;amp; \frac{\partial a_n}{\partial z_2} &amp;amp; \cdots &amp;amp; \frac{\partial a_n}{\partial z_n}\end{bmatrix}
$$&lt;/p&gt;
&lt;p&gt;Furthermore, when $i = j$,
$$
\begin{split}
\frac{\partial a_i}{\partial z_j}
&amp;amp; = \frac{\partial \frac{e^{z_i}}{\sum{e^{z_k}}}}{\partial z_j} \\
&amp;amp; = \frac{e^{z_i}\sum e^{z_k} - e^{z_i}e^{z_i}}{(\sum e^{z_k})^2} \\
&amp;amp; = a_i - a_i^2 \\
&amp;amp; = a_i(1-a_i)
\end{split}
$$
When $i \neq j$,
$$
\begin{split}
\frac{\partial a_i}{\partial z_j}
&amp;amp; = \frac{\partial \frac{e^{z_i}}{\sum{e^{z_k}}}}{\partial z_j} \\
&amp;amp; = \frac{0-e^{z_i}e^{z_j}}{(\sum{e^{z_k}})^2} \\
&amp;amp; = -a_ia_j
\end{split}
$$
So,
$$
\frac{\partial \mathbf a}{\partial \mathbf z} =
\begin{bmatrix}
a_1(1-a_1) &amp;amp; -a_1a_2 &amp;amp; \cdots &amp;amp; -a_1a_n\\
-a_2a_1 &amp;amp; a_2(1-a_2) &amp;amp; \cdots &amp;amp; -a_2a_n \\
\vdots &amp;amp; \vdots &amp;amp; \ddots &amp;amp; \vdots \\
-a_na_1 &amp;amp; a_na_2 &amp;amp; \cdots &amp;amp; a_n(1-a_n) \\
\end{bmatrix}
$$
But actually, due to only $\frac{\partial l}{\partial a_k} = -\frac{1}{a_k}$, and others are 0, we just need to calculate $\frac{\partial a_k}{\partial \mathbf z}$.&lt;/p&gt;
&lt;p&gt;Finally, the derivative of $l$ as follows:
$$
\begin{equation}
\begin{split}
\frac{\partial l}{\partial \mathbf z}
&amp;amp; = \frac{\partial l}{\partial \mathbf a}\frac{\partial \mathbf a}{\partial \mathbf z} \\
&amp;amp; = [a_1, a_2, \cdots, a_k-1, \cdots, a_n] \\
&amp;amp; = \mathbf a - \mathbf y
\end{split}
\end{equation}
$$
What a beautiful answer!&lt;/p&gt;
&lt;p&gt;End.&lt;/p&gt;
</content:encoded></item><item><title>基本操作</title><link>https://dicer-zz.github.io/posts/%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/</guid><pubDate>Sat, 22 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2021/5/22 下午九点，基本操作公众号发了一篇推送：解开谜题，获取基本操作的最新进展。&lt;a href=&quot;https://mp.weixin.qq.com/s/RvtIj9t1AlYD54swi1PkoA&quot;&gt;原文链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;文章是一串不明所以的字符，因为公众号的排版存在许多的&lt;code&gt;-&lt;/code&gt;，本身是不存在的。&lt;/p&gt;
&lt;p&gt;原字符串：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SULYDFBLVQHFHVVDUBIRUDQRSHQVRFLHWBLQWKHHOHFWURQLFDJHSULYDFBLVQRWVHFUHFBDSULYDWHPDWWHULVVRPHWKLQJRQHGRHVQWZDQWWKHZKROHZRUOGWRNQRZEXWDVHFUHWPDWWHULVVRPHWKLQJRQHGRHVQWZDQWDQBERGBWRNQRZSULYDFBLVWKHSRZHUWRVHOHFWLYHOBUHYHDORQHVHOIWRWKHZRUOGLIWZRSDUWLHVKDYHVRPHVRUWRIGHDOLQJVWKHQHDFKKDVDPHPRUBRIWKHLULQWHUDFWLRQHDFKSDUWBFDQVSHDNDERXWWKHLURZQPHPRUBRIWKLVKRZFRXOGDQBRQHSUHYHQWLWRQHFRXOGSDVVODZVDJDLQVWLWEXWWKHIUHHGRPRIVSHHFKHYHQPRUHWKDQSULYDFBLVIXQGDPHQWDOWRDQRSHQVRFLHWBZHVHHNQRWWRUHVWULFWDQBVSHHFKDWDOOLIPDQBSDUWLHVVSHDNWRJHWKHULQWKHVDPHIRUXPHDFKFDQVSHDNWRDOOWKHRWKHUVDQGDJJUHJDWHWRJHWKHUNQRZOHGJHDERXWLQGLYLGXDOVDQGRWKHUSDUWLHVWKHSRZHURIHOHFWURQLFFRPPXQLFDWLRQVKDVHQDEOHGVXFKJURXSVSHHFKDQGLWZLOOQRWJRDZDBPHUHOBEHFDXVHZHPLJKWZDQWLWWRVLQFHZHGHVLUHSULYDFBZHPXVWHQVXUHWKDWHDFKSDUWBWRDWUDQVDFWLRQKDYHNQRZOHGJHRQOBRIWKDWZKLFKLVGLUHFWOBQHFHVVDUBIRUWKDWWUDQVDFWLRQVLQFHDQBLQIRUPDWLRQFDQEHVSRNHQRIZHPXVWHQVXUHWKDWZHUHYHDODVOLWWOHDVSRVVLEOHLQPRVWFDVHVSHUVRQDOLGHQWLWBLVQRWVDOLHQWZKHQLSXUFKDVHDPDJDCLQHDWDVWRUHDQGKDQGFDVKWRWKHFOHUNWKHUHLVQRQHHGWRNQRZZKRLDPZKHQLDVNPBHOHFWURQLFPDLOSURYLGHUWRVHQGDQGUHFHLYHPHVVDJHVPBSURYLGHUQHHGQRWNQRZWRZKRPLDPVSHDNLQJRUZKDWLDPVDBLQJRUZKDWRWKHUVDUHVDBLQJWRPHPBSURYLGHURQOBQHHGNQRZKRZWRJHWWKHPHVVDJHWKHUHDQGKRZPXFKLRZHWKHPLQIHHVZKHQPBLGHQWLWBLVUHYHDOHGEBWKHXQGHUOBLQJPHFKDQLVPRIWKHWUDQVDFWLRQLKDYHQRSULYDFBLFDQQRWKHUHVHOHFWLYHOBUHYHDOPBVHOILPXVWDOZDBVUHYHDOPBVHOIWKHUHIRUHSULYDFBLQDQRSHQVRFLHWBUHTXLUHVDQRQBPRXVWUDQVDFWLRQVBVWHPVXQWLOQRZFDVKKDVEHHQWKHSULPDUBVXFKVBVWHPDQDQRQBPRXVWUDQVDFWLRQVBVWHPLVQRWDVHFUHWWUDQVDFWLRQVBVWHPDQDQRQBPRXVVBVWHPHPSRZHUVLQGLYLGXDOVWRUHYHDOWKHLULGHQWLWBZKHQGHVLUHGDQGRQOBZKHQGHVLUHGWKLVLVWKHHVVHQFHRISULYDFBSULYDFBLQDQRSHQVRFLHWBDOVRUHTXLUHVFUBSWRJUDSKBLILVDBVRPHWKLQJLZDQWLWKHDUGRQOBEBWKRVHIRUZKRPLLQWHQGLWLIWKHFRQWHQWRIPBVSHHFKLVDYDLODEOHWRWKHZRUOGLKDYHQRSULYDFBWRHQFUBSWLVWRLQGLFDWHWKHGHVLUHIRUSULYDFBDQGWRHQFUBSWZLWKZHDNFUBSWRJUDSKBLVWRLQGLFDWHQRWWRRPXFKGHVLUHIRUSULYDFBIXUWKHUPRUHWRUHYHDORQHVLGHQWLWBZLWKDVVXUDQFHZKHQWKHGHIDXOWLVDQRQBPLWBUHTXLUHVWKHFUBSWRJUDSKLFVLJQDWXUHZHFDQQRWHASHFWJRYHUQPHQWVFRUSRUDWLRQVRURWKHUODUJHIDFHOHVVRUJDQLCDWLRQVWRJUDQWXVSULYDFBRXWRIWKHLUEHQHILFHQFHLWLVWRWKHLUDGYDQWDJHWRVSHDNRIXVDQGZHVKRXOGHASHFWWKDWWKHBZLOOVSHDNWRWUBWRSUHYHQWWKHLUVSHHFKLVWRILJKWDJDLQVWWKHUHDOLWLHVRILQIRUPDWLRQLQIRUPDWLRQGRHVQRWMXVWZDQWWREHIUHHLWORQJVWREHIUHHLQIRUPDWLRQHASDQGVWRILOOWKHDYDLODEOHVWRUDJHVSDFHLQIRUPDWLRQLVUXPRUVBRXQJHUVWURQJHUFRXVLQLQIRUPDWLRQLVIOHHWHURIIRRWKDVPRUHHBHVNQRZVPRUHDQGXQGHUVWDQGVOHVVWKDQUXPRUZHPXVWGHIHQGRXURZQSULYDFBLIZHHASHFWWRKDYHDQBZHPXVWFRPHWRJHWKHUDQGFUHDWHVBVWHPVZKLFKDOORZDQRQBPRXVWUDQVDFWLRQVWRWDNHSODFHSHRSOHKDYHEHHQGHIHQGLQJWKHLURZQSULYDFBIRUFHQWXULHVZLWKZKLVSHUVGDUNQHVVHQYHORSHVFORVHGGRRUVVHFUHWKDQGVKDNHVDQGFRXULHUVWKHWHFKQRORJLHVRIWKHSDVWGLGQRWDOORZIRUVWURQJSULYDFBEXWHOHFWURQLFWHFKQRORJLHVGRZHWKHFBSKHUSXQNVDUHGHGLFDWHGWREXLOGLQJDQRQBPRXVVBVWHPVZHDUHGHIHQGLQJRXUSULYDFBZLWKFUBSWRJUDSKBZLWKDQRQBPRXVPDLOIRUZDUGLQJVBVWHPVZLWKGLJLWDOVLJQDWXUHVDQGZLWKHOHFWURQLFPRQHBFBSKHUSXQNVZULWHFRGHZHNQRZWKDWVRPHRQHKDVWRZULWHVRIWZDUHWRGHIHQGSULYDFBDQGVLQFHZHFDQWJHWSULYDFBXQOHVVZHDOOGRZHUHJRLQJWRZULWHLWZHSXEOLVKRXUFRGHVRWKDWRXUIHOORZFBSKHUSXQNVPDBSUDFWLFHDQGSODBZLWKLWRXUFRGHLVIUHHIRUDOOWRXVHZRUOGZLGHZHGRQWPXFKFDUHLIBRXGRQWDSSURYHRIWKHVRIWZDUHZHZULWHZHNQRZWKDWVRIWZDUHFDQWEHGHVWURBHGDQGWKDWDZLGHOBGLVSHUVHGVBVWHPFDQWEHVKXWGRZQFBSKHUSXQNVGHSORUHUHJXODWLRQVRQFUBSWRJUDSKBIRUHQFUBSWLRQLVIXQGDPHQWDOOBDSULYDWHDFWWKHDFWRIHQFUBSWLRQLQIDFWUHPRYHVLQIRUPDWLRQIURPWKHSXEOLFUHDOPHYHQODZVDJDLQVWFUBSWRJUDSKBUHDFKRQOBVRIDUDVDQDWLRQVERUGHUDQGWKHDUPRILWVYLROHQFHFUBSWRJUDSKBZLOOLQHOXFWDEOBVSUHDGRYHUWKHZKROHJOREHDQGZLWKLWWKHDQRQBPRXVWUDQVDFWLRQVVBVWHPVWKDWLWPDNHVSRVVLEOHIRUSULYDFBWREHZLGHVSUHDGLWPXVWEHSDUWRIDVRFLDOFRQWUDFWSHRSOHPXVWFRPHDQGWRJHWKHUGHSORBWKHVHVBVWHPVIRUWKHFRPPRQJRRGSULYDFBRQOBHAWHQGVVRIDUDVWKHFRRSHUDWLRQRIRQHVIHOORZVLQVRFLHWBZHWKHFBSKHUSXQNVVHHNBRXUTXHVWLRQVDQGBRXUFRQFHUQVDQGKRSHZHPDBHQJDJHBRXVRWKDWZHGRQRWGHFHLYHRXUVHOYHVZHZLOOQRWKRZHYHUEHPRYHGRXWRIRXUFRXUVHEHFDXVHVRPHPDBGLVDJUHHZLWKRXUJRDOVWKHFBSKHUSXQNVDUHDFWLYHOBHQJDJHGLQPDNLQJWKHQHWZRUNVVDIHUIRUSULYDFBOHWXVSURFHHGWRJHWKHUDSDFH
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;简单猜测是用的凯撒编码，然后就需要判断具体的偏移是多少了，有两种方法。&lt;/p&gt;
&lt;h2&gt;方法一&lt;/h2&gt;
&lt;p&gt;根据维基百科中的&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E5%AD%97%E6%AF%8D%E9%A2%91%E7%8E%87&quot;&gt;字母频率&lt;/a&gt;，可以知道英文中出现次数最多的是字母&lt;code&gt;e&lt;/code&gt;,因此如果这个字符串使用了凯撒编码，那么出现次数最多的字母代表的就应该是字母&lt;code&gt;e&lt;/code&gt;，统计结果如下图。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/gallery/others/count.png&quot; alt=&quot;字母频率统计&quot; /&gt;&lt;/p&gt;
&lt;p&gt;由图可见，出现次数最多的是字母&lt;code&gt;h&lt;/code&gt;，可知偏移为3。&lt;/p&gt;
&lt;h2&gt;方法二&lt;/h2&gt;
&lt;p&gt;可以直接遍历所有的情况，然后观察一下。也可以得到相同的结果。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;string = &apos;SULYDFBLVQHFHVVDUBIRUDQRSHQVRFLHWBLQWKHHOHFWURQLFDJHSULYDFBLVQRWVHFUHFBDSULYDWHPDWWHULVVRPHWKLQJRQHGRHVQWZDQWWKHZKROHZRUOGWRNQRZEXWDVHFUHWPDWWHULVVRPHWKLQJRQHGRHVQWZDQWDQBERGBWRNQRZSULYDFBLVWKHSRZHUWRVHOHFWLYHOBUHYHDORQHVHOIWRWKHZRUOGLIWZRSDUWLHVKDYHVRPHVRUWRIGHDOLQJVWKHQHDFKKDVDPHPRUBRIWKHLULQWHUDFWLRQHDFKSDUWBFDQVSHDNDERXWWKHLURZQPHPRUBRIWKLVKRZFRXOGDQBRQHSUHYHQWLWRQHFRXOGSDVVODZVDJDLQVWLWEXWWKHIUHHGRPRIVSHHFKHYHQPRUHWKDQSULYDFBLVIXQGDPHQWDOWRDQRSHQVRFLHWBZHVHHNQRWWRUHVWULFWDQBVSHHFKDWDOOLIPDQBSDUWLHVVSHDNWRJHWKHULQWKHVDPHIRUXPHDFKFDQVSHDNWRDOOWKHRWKHUVDQGDJJUHJDWHWRJHWKHUNQRZOHGJHDERXWLQGLYLGXDOVDQGRWKHUSDUWLHVWKHSRZHURIHOHFWURQLFFRPPXQLFDWLRQVKDVHQDEOHGVXFKJURXSVSHHFKDQGLWZLOOQRWJRDZDBPHUHOBEHFDXVHZHPLJKWZDQWLWWRVLQFHZHGHVLUHSULYDFBZHPXVWHQVXUHWKDWHDFKSDUWBWRDWUDQVDFWLRQKDYHNQRZOHGJHRQOBRIWKDWZKLFKLVGLUHFWOBQHFHVVDUBIRUWKDWWUDQVDFWLRQVLQFHDQBLQIRUPDWLRQFDQEHVSRNHQRIZHPXVWHQVXUHWKDWZHUHYHDODVOLWWOHDVSRVVLEOHLQPRVWFDVHVSHUVRQDOLGHQWLWBLVQRWVDOLHQWZKHQLSXUFKDVHDPDJDCLQHDWDVWRUHDQGKDQGFDVKWRWKHFOHUNWKHUHLVQRQHHGWRNQRZZKRLDPZKHQLDVNPBHOHFWURQLFPDLOSURYLGHUWRVHQGDQGUHFHLYHPHVVDJHVPBSURYLGHUQHHGQRWNQRZWRZKRPLDPVSHDNLQJRUZKDWLDPVDBLQJRUZKDWRWKHUVDUHVDBLQJWRPHPBSURYLGHURQOBQHHGNQRZKRZWRJHWWKHPHVVDJHWKHUHDQGKRZPXFKLRZHWKHPLQIHHVZKHQPBLGHQWLWBLVUHYHDOHGEBWKHXQGHUOBLQJPHFKDQLVPRIWKHWUDQVDFWLRQLKDYHQRSULYDFBLFDQQRWKHUHVHOHFWLYHOBUHYHDOPBVHOILPXVWDOZDBVUHYHDOPBVHOIWKHUHIRUHSULYDFBLQDQRSHQVRFLHWBUHTXLUHVDQRQBPRXVWUDQVDFWLRQVBVWHPVXQWLOQRZFDVKKDVEHHQWKHSULPDUBVXFKVBVWHPDQDQRQBPRXVWUDQVDFWLRQVBVWHPLVQRWDVHFUHWWUDQVDFWLRQVBVWHPDQDQRQBPRXVVBVWHPHPSRZHUVLQGLYLGXDOVWRUHYHDOWKHLULGHQWLWBZKHQGHVLUHGDQGRQOBZKHQGHVLUHGWKLVLVWKHHVVHQFHRISULYDFBSULYDFBLQDQRSHQVRFLHWBDOVRUHTXLUHVFUBSWRJUDSKBLILVDBVRPHWKLQJLZDQWLWKHDUGRQOBEBWKRVHIRUZKRPLLQWHQGLWLIWKHFRQWHQWRIPBVSHHFKLVDYDLODEOHWRWKHZRUOGLKDYHQRSULYDFBWRHQFUBSWLVWRLQGLFDWHWKHGHVLUHIRUSULYDFBDQGWRHQFUBSWZLWKZHDNFUBSWRJUDSKBLVWRLQGLFDWHQRWWRRPXFKGHVLUHIRUSULYDFBIXUWKHUPRUHWRUHYHDORQHVLGHQWLWBZLWKDVVXUDQFHZKHQWKHGHIDXOWLVDQRQBPLWBUHTXLUHVWKHFUBSWRJUDSKLFVLJQDWXUHZHFDQQRWHASHFWJRYHUQPHQWVFRUSRUDWLRQVRURWKHUODUJHIDFHOHVVRUJDQLCDWLRQVWRJUDQWXVSULYDFBRXWRIWKHLUEHQHILFHQFHLWLVWRWKHLUDGYDQWDJHWRVSHDNRIXVDQGZHVKRXOGHASHFWWKDWWKHBZLOOVSHDNWRWUBWRSUHYHQWWKHLUVSHHFKLVWRILJKWDJDLQVWWKHUHDOLWLHVRILQIRUPDWLRQLQIRUPDWLRQGRHVQRWMXVWZDQWWREHIUHHLWORQJVWREHIUHHLQIRUPDWLRQHASDQGVWRILOOWKHDYDLODEOHVWRUDJHVSDFHLQIRUPDWLRQLVUXPRUVBRXQJHUVWURQJHUFRXVLQLQIRUPDWLRQLVIOHHWHURIIRRWKDVPRUHHBHVNQRZVPRUHDQGXQGHUVWDQGVOHVVWKDQUXPRUZHPXVWGHIHQGRXURZQSULYDFBLIZHHASHFWWRKDYHDQBZHPXVWFRPHWRJHWKHUDQGFUHDWHVBVWHPVZKLFKDOORZDQRQBPRXVWUDQVDFWLRQVWRWDNHSODFHSHRSOHKDYHEHHQGHIHQGLQJWKHLURZQSULYDFBIRUFHQWXULHVZLWKZKLVSHUVGDUNQHVVHQYHORSHVFORVHGGRRUVVHFUHWKDQGVKDNHVDQGFRXULHUVWKHWHFKQRORJLHVRIWKHSDVWGLGQRWDOORZIRUVWURQJSULYDFBEXWHOHFWURQLFWHFKQRORJLHVGRZHWKHFBSKHUSXQNVDUHGHGLFDWHGWREXLOGLQJDQRQBPRXVVBVWHPVZHDUHGHIHQGLQJRXUSULYDFBZLWKFUBSWRJUDSKBZLWKDQRQBPRXVPDLOIRUZDUGLQJVBVWHPVZLWKGLJLWDOVLJQDWXUHVDQGZLWKHOHFWURQLFPRQHBFBSKHUSXQNVZULWHFRGHZHNQRZWKDWVRPHRQHKDVWRZULWHVRIWZDUHWRGHIHQGSULYDFBDQGVLQFHZHFDQWJHWSULYDFBXQOHVVZHDOOGRZHUHJRLQJWRZULWHLWZHSXEOLVKRXUFRGHVRWKDWRXUIHOORZFBSKHUSXQNVPDBSUDFWLFHDQGSODBZLWKLWRXUFRGHLVIUHHIRUDOOWRXVHZRUOGZLGHZHGRQWPXFKFDUHLIBRXGRQWDSSURYHRIWKHVRIWZDUHZHZULWHZHNQRZWKDWVRIWZDUHFDQWEHGHVWURBHGDQGWKDWDZLGHOBGLVSHUVHGVBVWHPFDQWEHVKXWGRZQFBSKHUSXQNVGHSORUHUHJXODWLRQVRQFUBSWRJUDSKBIRUHQFUBSWLRQLVIXQGDPHQWDOOBDSULYDWHDFWWKHDFWRIHQFUBSWLRQLQIDFWUHPRYHVLQIRUPDWLRQIURPWKHSXEOLFUHDOPHYHQODZVDJDLQVWFUBSWRJUDSKBUHDFKRQOBVRIDUDVDQDWLRQVERUGHUDQGWKHDUPRILWVYLROHQFHFUBSWRJUDSKBZLOOLQHOXFWDEOBVSUHDGRYHUWKHZKROHJOREHDQGZLWKLWWKHDQRQBPRXVWUDQVDFWLRQVVBVWHPVWKDWLWPDNHVSRVVLEOHIRUSULYDFBWREHZLGHVSUHDGLWPXVWEHSDUWRIDVRFLDOFRQWUDFWSHRSOHPXVWFRPHDQGWRJHWKHUGHSORBWKHVHVBVWHPVIRUWKHFRPPRQJRRGSULYDFBRQOBHAWHQGVVRIDUDVWKHFRRSHUDWLRQRIRQHVIHOORZVLQVRFLHWBZHWKHFBSKHUSXQNVVHHNBRXUTXHVWLRQVDQGBRXUFRQFHUQVDQGKRSHZHPDBHQJDJHBRXVRWKDWZHGRQRWGHFHLYHRXUVHOYHVZHZLOOQRWKRZHYHUEHPRYHGRXWRIRXUFRXUVHEHFDXVHVRPHPDBGLVDJUHHZLWKRXUJRDOVWKHFBSKHUSXQNVDUHDFWLYHOBHQJDJHGLQPDNLQJWKHQHWZRUNVVDIHUIRUSULYDFBOHWXVSURFHHGWRJHWKHUDSDFH&apos;

for i in range(26):
    tmp = [chr((ord(char) - ord(&apos;A&apos;) + i)%26 + ord(&apos;A&apos;)) for char in string]
    print(&apos;&apos;.join(tmp))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将所有的字母循环左移3位解码后的结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Privacy is necessary for an open society in the electronic age. And so on.
PRIVACYISNECESSARYFORANOPENSOCIETYINTHEELECTRONICAGEPRIVACYISNOTSECRECYAPRIVATEMATTERISSOMETHINGONEDOESNTWANTTHEWHOLEWORLDTOKNOWBUTASECRETMATTERISSOMETHINGONEDOESNTWANTANYBODYTOKNOWPRIVACYISTHEPOWERTOSELECTIVELYREVEALONESELFTOTHEWORLDIFTWOPARTIESHAVESOMESORTOFDEALINGSTHENEACHHASAMEMORYOFTHEIRINTERACTIONEACHPARTYCANSPEAKABOUTTHEIROWNMEMORYOFTHISHOWCOULDANYONEPREVENTITONECOULDPASSLAWSAGAINSTITBUTTHEFREEDOMOFSPEECHEVENMORETHANPRIVACYISFUNDAMENTALTOANOPENSOCIETYWESEEKNOTTORESTRICTANYSPEECHATALLIFMANYPARTIESSPEAKTOGETHERINTHESAMEFORUMEACHCANSPEAKTOALLTHEOTHERSANDAGGREGATETOGETHERKNOWLEDGEABOUTINDIVIDUALSANDOTHERPARTIESTHEPOWEROFELECTRONICCOMMUNICATIONSHASENABLEDSUCHGROUPSPEECHANDITWILLNOTGOAWAYMERELYBECAUSEWEMIGHTWANTITTOSINCEWEDESIREPRIVACYWEMUSTENSURETHATEACHPARTYTOATRANSACTIONHAVEKNOWLEDGEONLYOFTHATWHICHISDIRECTLYNECESSARYFORTHATTRANSACTIONSINCEANYINFORMATIONCANBESPOKENOFWEMUSTENSURETHATWEREVEALASLITTLEASPOSSIBLEINMOSTCASESPERSONALIDENTITYISNOTSALIENTWHENIPURCHASEAMAGAZINEATASTOREANDHANDCASHTOTHECLERKTHEREISNONEEDTOKNOWWHOIAMWHENIASKMYELECTRONICMAILPROVIDERTOSENDANDRECEIVEMESSAGESMYPROVIDERNEEDNOTKNOWTOWHOMIAMSPEAKINGORWHATIAMSAYINGORWHATOTHERSARESAYINGTOMEMYPROVIDERONLYNEEDKNOWHOWTOGETTHEMESSAGETHEREANDHOWMUCHIOWETHEMINFEESWHENMYIDENTITYISREVEALEDBYTHEUNDERLYINGMECHANISMOFTHETRANSACTIONIHAVENOPRIVACYICANNOTHERESELECTIVELYREVEALMYSELFIMUSTALWAYSREVEALMYSELFTHEREFOREPRIVACYINANOPENSOCIETYREQUIRESANONYMOUSTRANSACTIONSYSTEMSUNTILNOWCASHHASBEENTHEPRIMARYSUCHSYSTEMANANONYMOUSTRANSACTIONSYSTEMISNOTASECRETTRANSACTIONSYSTEMANANONYMOUSSYSTEMEMPOWERSINDIVIDUALSTOREVEALTHEIRIDENTITYWHENDESIREDANDONLYWHENDESIREDTHISISTHEESSENCEOFPRIVACYPRIVACYINANOPENSOCIETYALSOREQUIRESCRYPTOGRAPHYIFISAYSOMETHINGIWANTITHEARDONLYBYTHOSEFORWHOMIINTENDITIFTHECONTENTOFMYSPEECHISAVAILABLETOTHEWORLDIHAVENOPRIVACYTOENCRYPTISTOINDICATETHEDESIREFORPRIVACYANDTOENCRYPTWITHWEAKCRYPTOGRAPHYISTOINDICATENOTTOOMUCHDESIREFORPRIVACYFURTHERMORETOREVEALONESIDENTITYWITHASSURANCEWHENTHEDEFAULTISANONYMITYREQUIRESTHECRYPTOGRAPHICSIGNATUREWECANNOTEXPECTGOVERNMENTSCORPORATIONSOROTHERLARGEFACELESSORGANIZATIONSTOGRANTUSPRIVACYOUTOFTHEIRBENEFICENCEITISTOTHEIRADVANTAGETOSPEAKOFUSANDWESHOULDEXPECTTHATTHEYWILLSPEAKTOTRYTOPREVENTTHEIRSPEECHISTOFIGHTAGAINSTTHEREALITIESOFINFORMATIONINFORMATIONDOESNOTJUSTWANTTOBEFREEITLONGSTOBEFREEINFORMATIONEXPANDSTOFILLTHEAVAILABLESTORAGESPACEINFORMATIONISRUMORSYOUNGERSTRONGERCOUSININFORMATIONISFLEETEROFFOOTHASMOREEYESKNOWSMOREANDUNDERSTANDSLESSTHANRUMORWEMUSTDEFENDOUROWNPRIVACYIFWEEXPECTTOHAVEANYWEMUSTCOMETOGETHERANDCREATESYSTEMSWHICHALLOWANONYMOUSTRANSACTIONSTOTAKEPLACEPEOPLEHAVEBEENDEFENDINGTHEIROWNPRIVACYFORCENTURIESWITHWHISPERSDARKNESSENVELOPESCLOSEDDOORSSECRETHANDSHAKESANDCOURIERSTHETECHNOLOGIESOFTHEPASTDIDNOTALLOWFORSTRONGPRIVACYBUTELECTRONICTECHNOLOGIESDOWETHECYPHERPUNKSAREDEDICATEDTOBUILDINGANONYMOUSSYSTEMSWEAREDEFENDINGOURPRIVACYWITHCRYPTOGRAPHYWITHANONYMOUSMAILFORWARDINGSYSTEMSWITHDIGITALSIGNATURESANDWITHELECTRONICMONEYCYPHERPUNKSWRITECODEWEKNOWTHATSOMEONEHASTOWRITESOFTWARETODEFENDPRIVACYANDSINCEWECANTGETPRIVACYUNLESSWEALLDOWEREGOINGTOWRITEITWEPUBLISHOURCODESOTHATOURFELLOWCYPHERPUNKSMAYPRACTICEANDPLAYWITHITOURCODEISFREEFORALLTOUSEWORLDWIDEWEDONTMUCHCAREIFYOUDONTAPPROVEOFTHESOFTWAREWEWRITEWEKNOWTHATSOFTWARECANTBEDESTROYEDANDTHATAWIDELYDISPERSEDSYSTEMCANTBESHUTDOWNCYPHERPUNKSDEPLOREREGULATIONSONCRYPTOGRAPHYFORENCRYPTIONISFUNDAMENTALLYAPRIVATEACTTHEACTOFENCRYPTIONINFACTREMOVESINFORMATIONFROMTHEPUBLICREALMEVENLAWSAGAINSTCRYPTOGRAPHYREACHONLYSOFARASANATIONSBORDERANDTHEARMOFITSVIOLENCECRYPTOGRAPHYWILLINELUCTABLYSPREADOVERTHEWHOLEGLOBEANDWITHITTHEANONYMOUSTRANSACTIONSSYSTEMSTHATITMAKESPOSSIBLEFORPRIVACYTOBEWIDESPREADITMUSTBEPARTOFASOCIALCONTRACTPEOPLEMUSTCOMEANDTOGETHERDEPLOYTHESESYSTEMSFORTHECOMMONGOODPRIVACYONLYEXTENDSSOFARASTHECOOPERATIONOFONESFELLOWSINSOCIETYWETHECYPHERPUNKSSEEKYOURQUESTIONSANDYOURCONCERNSANDHOPEWEMAYENGAGEYOUSOTHATWEDONOTDECEIVEOURSELVESWEWILLNOTHOWEVERBEMOVEDOUTOFOURCOURSEBECAUSESOMEMAYDISAGREEWITHOURGOALSTHECYPHERPUNKSAREACTIVELYENGAGEDINMAKINGTHENETWORKSSAFERFORPRIVACYLETUSPROCEEDTOGETHERAPACE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到是一篇关于&lt;code&gt;Privacy&lt;/code&gt;的文章。接下来就需要把所有的英语单词分开，也就是做英文分词，这个工作非常的繁琐，可以用一些技巧。&lt;/p&gt;
&lt;h1&gt;分词&lt;/h1&gt;
&lt;p&gt;在中文自然语言处理领域，中文分词往往是第一步骤。因为英文有空格将单词隔开，因此一般不需要进行分词。但是，在某些应用中，也需要英文分词，比如：英文手写识别。这是因为在手写时，单词之间的空格由于过小或者连笔等原因，不能被识别出来，这时就需要对英文进行分词。还有就是有人故意删除了空格🐶。&lt;/p&gt;
&lt;p&gt;github上有一个第三方的Python库&lt;a href=&quot;https://github.com/yishuihanhan/wordninja&quot;&gt;wordninja&lt;/a&gt;，可以快速解决分词问题。不过不能判断句子的结束。&lt;/p&gt;
&lt;p&gt;分词结果如下（贴心的转成了小写）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;privacy is necessary for an open society in the electronic age privacy is not secrecy a private matter is something one doesnt want the whole world to know but a secret matter is something one doesnt want anybody to know privacy is the power to selectively reveal oneself to the world if two parties have some sort of dealings then each has a memory of their interaction each party can speak about their own memory of this how could anyone prevent it one could pass laws against it but the freedom of speech even more than privacy is fundamental to an open society we seek not to restrict any speech at all if many parties speak together in the same forum each can speak to all the others and aggregate together knowledge about individuals and other parties the power of electronic communications has enabled such group speech and it will not go away merely because we might want it to since we desire privacy we must ensure that each party to a transaction have knowledge only of that which is directly necessary for that transaction since any information can bespoken of we must ensure that were veal as little as possible in most cases personal identity is not salient when i purchase a magazine at astore and hand cash to the clerk there is no need to know who iam when i ask my electronic mail provider to send and receive messages my provider need not know to whom iam speaking or what iam saying or what others are saying tome my provider only need knowhow to get the message there and how much i owe them in fees when my identity is revealed by the underlying mechanism of the transaction i have no privacy i cannot here selectively reveal myself i must always reveal myself therefore privacy in an open society requires anonymous transaction systems until now cash has been the primary such system an anonymous transaction system is not a secret transaction system an anonymous system empowers individuals to reveal their identity when desired and only when desired this is the essence of privacy privacy in an open society also requires cryptography if i say something i want it heard only by those for who mi intend it if the content of my speech is available to the world i have no privacy to encrypt is to indicate the desire for privacy and to encrypt with weak cryptography is to indicate not too much desire for privacy furthermore to reveal ones identity with assurance when the default is anonymity requires the cryptographic signature we cannot expect governments corporations or other large faceless organizations to grant us privacy out of their beneficence it is to their advantage to speak of us and we should expect that they will speak to try to prevent their speech is to fight against the realities of information information does not just want to be free it longs to be free information expands to fill the available storage space information is rumors younger stronger cousin information is fleeter of foot has more eyes knows more and understands less than rumor we must defend our own privacy if we expect to have any we must come together and create systems which allow anonymous transactions to take place people have been defending their own privacy for centuries with whispers darkness envelopes closed doors secret handshakes and couriers the technologies of the past did not allow for strong privacy but electronic technologies do we the cypher punks are dedicated to building anonymous systems we are defending our privacy with cryptography with anonymous mail forwarding systems with digital signatures and with electronic money cypher punks write code we know that someone has to write software to defend privacy and since we cant get privacy unless we all do were going to write it we publish our code so that our fellow cypher punks may practice and play with it our code is free for all to use worldwide we dont much care if you dont approve of the software we write we know that software cant be destroyed and that a widely dispersed system cant be shutdown cypher punks deplore regulations on cryptography for encryption is fundamentally a private act the act of encryption in fact removes information from the public realm even laws against cryptography reach only s of aras a nations border and the arm of its violence cryptography will ineluctably spread over the whole globe and with it the anonymous transactions systems that it makes possible for privacy to be widespread it must be part of a social contract people must come and together deploy these systems for the common good privacy only extends s of aras the cooperation of ones fellows in society we the cypher punks seek your questions and your concerns and hope we may engage you so that we do not deceive ourselves we will not however be moved out of our course because some may disagree with our goals the cypher punks are actively engaged in making the networks safer for privacy let us proceed together apace
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可见分词的效果非常好，几乎没有什么问题（至少我没看出来）&lt;/p&gt;
&lt;p&gt;虽然没有句号，但是基本上也可以通读了，想做阅读理解的同学可以自己读一下。不想读的同学直接丢到翻译里就行啦。&lt;/p&gt;
&lt;p&gt;END&lt;/p&gt;
&lt;p&gt;2021/5/23 update&lt;/p&gt;
&lt;p&gt;看了一下评论区，有人说是密码朋克宣言的一部分，查了一下确实如此。&lt;/p&gt;
&lt;p&gt;这是原文：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                   A Cypherpunk&apos;s Manifesto

                        by Eric Hughes

Privacy is necessary for an open society in the electronic age.
Privacy is not secrecy.  A private matter is something one doesn&apos;t
want the whole world to know, but a secret matter is something one
doesn&apos;t want anybody to know. Privacy is the power to selectively
reveal oneself to the world.  

If two parties have some sort of dealings, then each has a memory of
their interaction.  Each party can speak about their own memory of
this; how could anyone prevent it?  One could pass laws against it,
but the freedom of speech, even more than privacy, is fundamental to
an open society; we seek not to restrict any speech at all.  If many
parties speak together in the same forum, each can speak to all the
others and aggregate together knowledge about individuals and other
parties.  The power of electronic communications has enabled such
group speech, and it will not go away merely because we might want it
to.

Since we desire privacy, we must ensure that each party to a
transaction have knowledge only of that which is directly necessary
for that transaction.  Since any information can be spoken of, we
must ensure that we reveal as little as possible.  In most cases
personal identity is not salient. When I purchase a magazine at a
store and hand cash to the clerk, there is no need to know who I am. 
When I ask my electronic mail provider to send and receive messages,
my provider need not know to whom I am speaking or what I am saying
or what others are saying to me;  my provider only need know how to
get the message there and how much I owe them in fees.  When my
identity is revealed by the underlying mechanism of the transaction,
I have no privacy.  I cannot here selectively reveal myself; I must
_always_ reveal myself.

Therefore, privacy in an open society requires anonymous transaction
systems.  Until now, cash has been the primary such system.  An
anonymous transaction system is not a secret transaction system.  An
anonymous system empowers individuals to reveal their identity when
desired and only when desired; this is the essence of privacy.

Privacy in an open society also requires cryptography.  If I say
something, I want it heard only by those for whom I intend it.  If 
the content of my speech is available to the world, I have no
privacy.  To encrypt is to indicate the desire for privacy, and to
encrypt with weak cryptography is to indicate not too much desire for
privacy.  Furthermore, to reveal one&apos;s identity with assurance when
the default is anonymity requires the cryptographic signature.

We cannot expect governments, corporations, or other large, faceless
organizations to grant us privacy out of their beneficence.  It is to
their advantage to speak of us, and  we should expect that they will
speak.  To try to prevent their speech is to fight against the
realities of information. Information does not just want to be free,
it longs to be free.  Information expands to fill the available
storage space.  Information is Rumor&apos;s younger, stronger cousin;
Information is fleeter of foot, has more eyes, knows more, and
understands less than Rumor.

We must defend our own privacy if we expect to have any.  We must
come together and create systems which allow anonymous transactions
to take place.  People have been defending their own privacy for
centuries with whispers, darkness, envelopes, closed doors, secret
handshakes, and couriers.  The technologies of the past did not allow
for strong privacy, but electronic technologies do.

We the Cypherpunks are dedicated to building anonymous systems.  We
are defending our privacy with cryptography, with anonymous mail
forwarding systems, with digital signatures, and with electronic
money.

Cypherpunks write code.  We know that someone has to write software
to defend privacy, and since we can&apos;t get privacy unless we all do,
we&apos;re going to write it. We publish our code so that our fellow
Cypherpunks may practice and play with it. Our code is free for all
to use, worldwide.  We don&apos;t much care if you don&apos;t approve of the
software we write.  We know that software can&apos;t be destroyed and that
a widely dispersed system can&apos;t be shut down. 

Cypherpunks deplore regulations on cryptography, for encryption is
fundamentally a private act.  The act of encryption, in fact, removes
information from the public realm.  Even laws against cryptography
reach only so far as a nation&apos;s border and the arm of its violence.
Cryptography will ineluctably spread over the whole globe, and with
it the anonymous transactions systems that it makes possible. 

For privacy to be widespread it must be part of a social contract.
People must come and together deploy these systems for the common
good.  Privacy only extends so far as the cooperation of one&apos;s
fellows in society.  We the Cypherpunks seek your questions and your
concerns and hope we may engage you so that we do not deceive
ourselves.  We will not, however, be moved out of our course because
some may disagree with our goals.

The Cypherpunks are actively engaged in making the networks safer for
privacy.  Let us proceed together apace.

Onward.

Eric Hughes
&amp;lt;hughes@soda.berkeley.edu&amp;gt;

9 March 1993
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cover is created by &lt;a href=&quot;https://www.logoly.pro/&quot;&gt;logoly&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>基于 Word2Vec 的红楼梦人物关系分析</title><link>https://dicer-zz.github.io/posts/%E5%9F%BA%E4%BA%8Eword2vec%E7%9A%84%E7%BA%A2%E6%A5%BC%E6%A2%A6%E4%BA%BA%E7%89%A9%E5%85%B3%E7%B3%BB%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/%E5%9F%BA%E4%BA%8Eword2vec%E7%9A%84%E7%BA%A2%E6%A5%BC%E6%A2%A6%E4%BA%BA%E7%89%A9%E5%85%B3%E7%B3%BB%E5%88%86%E6%9E%90/</guid><pubDate>Tue, 18 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;word2vec是Google公司在2013年提出的一种词嵌入算法。使用word2vec算法对词汇进行向量化后，原来的近义词在向量空间中是邻近的，因此word2vec可以很好的保留原来词汇之间的相似性。&lt;/p&gt;
&lt;p&gt;本文使用gensim库实现的word2vec算法，对红楼梦中的人物关系进行分析，得到了许多有趣的结论。&lt;/p&gt;
&lt;h1&gt;获取文本&lt;/h1&gt;
&lt;p&gt;首先我们需要获取原著的本文文件，并且需要保证文本文件足够「纯净」，可以减少文本处理的工作量。&lt;/p&gt;
&lt;p&gt;可以通过爬虫，从http://www.purepen.com获取原始文本。&lt;/p&gt;
&lt;p&gt;爬虫代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import requests
import random
import time
from bs4 import BeautifulSoup

def crawler():
    &apos;&apos;&apos;
    爬取红楼梦
    :url
    :return
    &apos;&apos;&apos;
    path = &apos;http://www.purepen.com/hlm/&apos;
    file = open(&apos;./data/红楼梦.txt&apos;, &apos;w+&apos;)
    for page in range(1, 121):
        url = path + (&apos;000&apos;+str(page))[-3:] + &apos;.htm&apos;
        print(url)
        html = requests.get(url)
        html.encoding = html.apparent_encoding
        soup = BeautifulSoup(html.text, &apos;lxml&apos;)
        title = soup.find(align = &apos;center&apos;).text
        print(title)
        content = soup.find(face = &apos;宋体&apos;).text
        file.write(title   + &apos;\t\n&apos;)
        file.write(content + &apos;\t\n&apos;)
        sec = random.randint(0, 3)
        print(&quot;Sleep %d sconds.&quot; % sec)
        time.sleep(sec)

if __name__ == &apos;__main__&apos;:
    crawler()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码中通过sleep机制简单的避免了被反爬。&lt;/p&gt;
&lt;h1&gt;文本处理&lt;/h1&gt;
&lt;p&gt;首先需要使用正则表达式将所有的标点符号去掉。红楼梦属于半白话文半文言文的问题，因此其中许多词语分词系统是不能识别的，但是因为我们这次做的只是人物关系，所以只需要把所有的人名写入用户字典就可以保证所有的人名都能被准确地识别出来。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def segment():
    start = time.time()
    content = open(&apos;./data/红楼梦.txt&apos;, &apos;r&apos;).read()
    trimed = re.sub(r&apos;[^\u4e00-\u9fa5]&apos;, &apos; &apos;, content)
    jieba.load_userdict(&apos;./data/dict.txt&apos;)
    result = &apos; &apos;.join(jieba.cut(trimed))
    file = open(&apos;./data/cut_result.txt&apos;, &apos;w+&apos;)
    file.write(&apos; &apos;.join(result.split()))
    cost = time.time() - start
    print(f&quot;Segment cost: {cost:.4f}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;按道理应该是要去掉停用词的，不过我不想做，偷个懒。😛&lt;/p&gt;
&lt;h1&gt;模型训练&lt;/h1&gt;
&lt;p&gt;使用gensim实现的word2vec算法进行向量化，因为文本不是很大，所以向量长度我选择的200，window大小选择的3。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def train_model():
    start = time.time()
    sentencePath = &apos;./data/cut_result.txt&apos;
    modelPath = &apos;./data/hlm_model&apos;
    sentence = word2vec.LineSentence(sentencePath)
    model = word2vec.Word2Vec(sentence, vector_size=200, window=3)
    model.save(modelPath)
    cost = time.time() - start
    print(f&apos;Word2Vec model training cost: {cost:.4f}&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;训练起来还是很快的，大概一秒钟就好了。&lt;/p&gt;
&lt;h1&gt;PCA降维&lt;/h1&gt;
&lt;p&gt;因为原始的向量长度太大，所以我们可以先用PCA（主成分分析）将向量降到二维，然后在坐标系中画出来。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from gensim.models import word2vec
from sklearn.decomposition import PCA
from matplotlib import pyplot

pyplot.rcParams[&apos;font.sans-serif&apos;] = [&apos;Arial Unicode MS&apos;]

model = word2vec.Word2Vec.load(&apos;./data/hlm_model&apos;)

allNames = [x.strip(&apos;\n&apos;) for x in open(&apos;./data/name.txt&apos;, &apos;r&apos;).readlines()]
X, names = [], []
for name in allNames:
    try:
        X.append(model.wv[name])
        names.append(name)
    except:
        print(&quot;missed name: &quot;, name)

pca = PCA(n_components=2)
result = pca.fit_transform(X)

pyplot.scatter(result[:, 0], result[:, 1])
for i, name in enumerate(names):
	pyplot.annotate(name, xy=(result[i, 0], result[i, 1]))
# pyplot.show()
pyplot.savefig(&apos;./data/relation.png&apos;, transparent=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为word2vec会忽略掉频次比较小的词语，所以我们在获取词向量时需要进行异常捕捉。&lt;/p&gt;
&lt;p&gt;让我们看一下效果吧！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/gallery/others/hlm-all.png&quot; alt=&quot;整体图片&quot; /&gt;&lt;/p&gt;
&lt;p&gt;因为名字有点多，整体看起来有点拥挤。我们把焦点放在又下角，可以看到宝黛钗三个人都在，我们放大一下。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/gallery/others/hlm-part.png&quot; alt=&quot;右下角局部&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们可以看到宝玉和黛玉紧紧挨着一起，宝黛一生吹好吧！&lt;/p&gt;
&lt;h1&gt;最相似分析&lt;/h1&gt;
&lt;p&gt;word2vec中提供了几个有趣的方法，我们可以用这些方法进一步分析一下人物关系。&lt;/p&gt;
&lt;p&gt;代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from gensim.models import word2vec

def analyse():
    model = word2vec.Word2Vec.load(&apos;./data/hlm_model&apos;)
    print(&apos;Nearest 宝玉:&apos;,model.wv.most_similar([&apos;宝玉&apos;]))
    print(&apos;Nearest 黛玉:&apos;,model.wv.most_similar([&apos;黛玉&apos;]))
    print(&apos;Nearest 宝钗:&apos;,model.wv.most_similar([&apos;宝钗&apos;]))
    print(&apos;Nearest 晴雯:&apos;,model.wv.most_similar([&apos;晴雯&apos;]))
    print(&apos;Nearest 袭人:&apos;,model.wv.most_similar([&apos;袭人&apos;]))
    print(&apos;Nearest 贾母:&apos;,model.wv.most_similar([&apos;贾母&apos;]))

    print(model.wv.doesnt_match(u&quot;贾宝玉 薛宝钗 林黛玉 史湘云&quot;.split()))
    print(model.wv.doesnt_match(u&quot;黛玉 元春 探春 迎春 惜春&quot;.split()))
    print(model.wv.doesnt_match(u&quot;贾琏 贾政 贾赦 贾敬&quot;.split()))

    print(model.wv.similarity(&apos;贾宝玉&apos;,&apos;林黛玉&apos;))
    print(model.wv.similarity(&apos;林黛玉&apos;,&apos;薛宝钗&apos;))
    print(model.wv.similarity(&apos;晴雯&apos;, &apos;袭人&apos;))
    
    print(model.wv.similarity(&apos;林黛玉&apos;,&apos;薛宝钗&apos;))
    print(model.wv.similarity(&apos;林黛玉&apos;,&apos;宝钗&apos;))
    print(model.wv.similarity(&apos;黛玉&apos;,&apos;薛宝钗&apos;))
    print(model.wv.similarity(&apos;黛玉&apos;,&apos;宝钗&apos;))

    who = model.wv[&apos;宝玉&apos;] - model.wv[&apos;宝钗&apos;] + model.wv[&apos;黛玉&apos;]
    print(model.wv.most_similar(positive=[who]))
    who = model.wv[&apos;宝玉&apos;] - model.wv[&apos;黛玉&apos;] + model.wv[&apos;宝钗&apos;]
    print(model.wv.most_similar(positive=[who]))
    
if __name__ == &apos;__main__&apos;:
    analyse()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Nearest 宝玉: [(&apos;黛玉&apos;, 0.9654377102851868), (&apos;袭人&apos;, 0.9432756304740906), (&apos;贾琏&apos;, 0.9404013156890869), (&apos;紫鹃&apos;, 0.9312815070152283), (&apos;晴雯&apos;, 0.9278832674026489), (&apos;鸳鸯&apos;, 0.9248123168945312), (&apos;湘云&apos;, 0.9108842015266418), (&apos;凤姐&apos;, 0.9095652103424072), (&apos;宝钗&apos;, 0.9087227582931519), (&apos;薛姨妈&apos;, 0.9083631634712219)]
Nearest 黛玉: [(&apos;宝玉&apos;, 0.9654375910758972), (&apos;宝钗&apos;, 0.9516232013702393), (&apos;湘云&apos;, 0.9490573406219482), (&apos;贾琏&apos;, 0.9195422530174255), (&apos;喜不自胜&apos;, 0.9156811237335205), (&apos;晴雯&apos;, 0.9114517569541931), (&apos;雨村&apos;, 0.9069087505340576), (&apos;鸳鸯&apos;, 0.9047612547874451), (&apos;紫鹃&apos;, 0.9041048884391785), (&apos;杜撰&apos;, 0.9008530974388123)]
Nearest 宝钗: [(&apos;探春&apos;, 0.958204448223114), (&apos;湘云&apos;, 0.9580698609352112), (&apos;黛玉&apos;, 0.9516231417655945), (&apos;贾琏&apos;, 0.9483581185340881), (&apos;惜春&apos;, 0.9394745826721191), (&apos;薛姨妈&apos;, 0.9329902529716492), (&apos;雨村&apos;, 0.9322482943534851), (&apos;杜撰&apos;, 0.9264885783195496), (&apos;紫鹃&apos;, 0.9231323003768921), (&apos;喜不自胜&apos;, 0.9216738939285278)]
Nearest 晴雯: [(&apos;紫鹃&apos;, 0.9769253730773926), (&apos;袭人&apos;, 0.9697536826133728), (&apos;凤姐儿&apos;, 0.9683317542076111), (&apos;香菱&apos;, 0.9670401215553284), (&apos;鸳鸯&apos;, 0.9620832800865173), (&apos;拍手&apos;, 0.9574575424194336), (&apos;薛姨妈&apos;, 0.956330418586731), (&apos;贾琏&apos;, 0.9559655785560608), (&apos;陪笑&apos;, 0.9534842371940613), (&apos;湘云&apos;, 0.9519651532173157)]
Nearest 袭人: [(&apos;紫鹃&apos;, 0.9749440550804138), (&apos;晴雯&apos;, 0.9697537422180176), (&apos;凤姐儿&apos;, 0.9651725888252258), (&apos;鸳鸯&apos;, 0.9604008793830872), (&apos;平儿&apos;, 0.9592967629432678), (&apos;凤姐&apos;, 0.9534151554107666), (&apos;贾琏&apos;, 0.9518244862556458), (&apos;薛姨妈&apos;, 0.9457788467407227), (&apos;宝玉&apos;, 0.9432753920555115), (&apos;香菱&apos;, 0.9285346269607544)]
Nearest 贾母: [(&apos;王夫人&apos;, 0.9712321162223816), (&apos;尤氏&apos;, 0.9515039324760437), (&apos;贾珍&apos;, 0.9488133192062378), (&apos;贾琏&apos;, 0.9456284046173096), (&apos;薛姨妈&apos;, 0.937038779258728), (&apos;凤姐&apos;, 0.9299778938293457), (&apos;鸳鸯&apos;, 0.9244546890258789), (&apos;邢夫人&apos;, 0.9219790101051331), (&apos;贾蓉&apos;, 0.9125443696975708), (&apos;雪雁&apos;, 0.9032955765724182)]
史湘云
元春
贾敬
0.9830096
0.9892198
0.96975374
0.9892198
0.6931182
0.64615595
0.95162314
[(&apos;宝玉&apos;, 0.9627019166946411), (&apos;黛玉&apos;, 0.9240608215332031), (&apos;袭人&apos;, 0.8599908351898193), (&apos;晴雯&apos;, 0.8383870720863342), (&apos;紫鹃&apos;, 0.8329888582229614), (&apos;贾琏&apos;, 0.8326756954193115), (&apos;鸳鸯&apos;, 0.829352080821991), (&apos;湘云&apos;, 0.8235012292861938), (&apos;一想&apos;, 0.8234348297119141), (&apos;香菱&apos;, 0.8150357007980347)]
[(&apos;贾琏&apos;, 0.9775873422622681), (&apos;宝钗&apos;, 0.964641809463501), (&apos;紫鹃&apos;, 0.958747923374176), (&apos;薛姨妈&apos;, 0.9540018439292908), (&apos;宝玉&apos;, 0.9533487558364868), (&apos;袭人&apos;, 0.9529389142990112), (&apos;鸳鸯&apos;, 0.9496896266937256), (&apos;凤姐&apos;, 0.9492440223693848), (&apos;晴雯&apos;, 0.9458354115486145), (&apos;凤姐儿&apos;, 0.9457259178161621)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从上面的结果可以看到，宝玉最相似的是黛玉，黛玉最相似的是宝玉，而宝钗最相似的是探春。&lt;/p&gt;
&lt;p&gt;并且可以看到「宝玉」和「黛玉」的相似度比「贾宝玉」和「黛玉」的相似度更高，说明在文中前者往往是成对出现的。&lt;/p&gt;
&lt;p&gt;另外，我们还得到了两个有趣的式子：
$$
宝玉 - 宝钗 + 黛玉 \approx 宝玉
$$&lt;/p&gt;
&lt;p&gt;$$
宝玉 - 黛玉 + 宝钗 \approx 贾琏
$$&lt;/p&gt;
&lt;p&gt;很有意思😂&lt;/p&gt;
</content:encoded></item><item><title>sklearn 踩坑</title><link>https://dicer-zz.github.io/posts/sklearn%E8%B8%A9%E5%9D%91/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/sklearn%E8%B8%A9%E5%9D%91/</guid><pubDate>Wed, 05 May 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这几天在做毕业设计，想做一个微博的情感分析，想着实现两种方式，一是朴素贝叶斯，二是用LSTM。&lt;/p&gt;
&lt;p&gt;在做朴素贝叶斯的时候，据网上看到的一些文章说，训练的速度应该是很快的。但是我的训练速度却很慢，分析了一下发现是文本分词、清洗占去了大量的时间。我的语料大概12w行，20MB左右。文本处理需要一分多钟，而朴素贝叶斯的训练时间只需要一秒钟左右。&lt;/p&gt;
&lt;p&gt;于是想把文本处理的结果，保存起来，下次直接使用，就不需要每次都多等一分钟了。&lt;/p&gt;
&lt;h1&gt;持久化&lt;/h1&gt;
&lt;p&gt;原始文本的格式（csv格式）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;label,review
1,更博了，爆照了，帅的呀，就是越来越爱你！生快傻缺[爱你][爱你][爱你]
1,@张晓鹏jonathan 土耳其的事要认真对待[哈哈]，否则直接开除。@丁丁看世界 很是细心，酒店都全部OK啦。
1,姑娘都羡慕你呢…还有招财猫高兴……//@爱在蔓延-JC:[哈哈]小学徒一枚，等着明天见您呢//@李欣芸SharonLee:大佬范儿[书呆子]
1,美~~~~~[爱你]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;处理后的文件（csv格式）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;label,review
1,更博 爆照 帅 的 呀 越来越 爱 生快 傻 缺 [爱你] [爱你] [爱你]
1,土耳其 的 事要 认真对待 [哈哈] 直接 开除 是 细心 酒店 都 全部
1,姑娘 都 羡慕 呢 招财猫 高兴 [哈哈] 小 学徒 一枚 等 着 明天 见 呢 大佬 范儿 [书呆子]
1,美 [爱你]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;处理文件的脚本：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import random
import re

import jieba
import pandas as pd


def trim(text):
    &quot;&quot;&quot;
    带有语料清洗功能的分词函数, 包含数据预处理, 可以根据自己的需求重载
    使用re保证了一些本来可能会分开的表情图标不分开
    return: [str]
    &quot;&quot;&quot;
    text = re.sub(&quot;\{%.+?%\}&quot;, &quot; &quot;, text)           # 去除 {%xxx%} (地理定位, 微博话题等)
    # text = re.sub(&quot;@.+?( |$)&quot;, &quot; &quot;, text)           # 去除 @xxx (用户名)
    text = re.sub(&quot;@.+?( |:)&quot;, &quot; &quot;, text)           # 去除 @xxx (用户名)
    text = re.sub(&quot;【.+?】&quot;, &quot; &quot;, text)              # 去除 【xx】 (里面的内容通常都不是用户自己写的)
    text = re.sub(&quot;[a-zA-Z0-9]&quot;, &quot; &quot;, text)         # 去除字母和数字
    icons = re.findall(&quot;\[.+?\]&quot;, text)             # 提取出所有表情图标
    text = re.sub(&quot;\[.+?\]&quot;, &quot;IconMark&quot;, text)      # 将文本中的图标替换为`IconMark`

    tokens = []
    # for k, w in enumerate(jieba.lcut(text)):
    jieba.load_userdict(&apos;./data/user_dict.txt&apos;)
    for w in jieba.cut(text):
        w = w.strip()
        if &quot;IconMark&quot; in w:                         # 将IconMark替换为原图标
            for i in range(w.count(&quot;IconMark&quot;)):
                tokens.append(icons.pop(0))
        elif w and w != &apos;\u200b&apos; and w.isalpha():   # 只保留有效文本
            tokens.append(w)
    return tokens


def load_corpus(csvFilePath, stopwordPath):
    &quot;&quot;&quot;
    加载语料库，并进行分词，数据清洗，去除停用词
    &quot;&quot;&quot;
    # 数据读取
    df = pd.read_csv(csvFilePath)
    stopword = load_stopword(stopwordPath)
    labels, reviews = df[&apos;label&apos;].to_list(), df[&apos;review&apos;].to_list()
    trimedReviews = []
    for review in reviews:
        # 数据清洗
        trimedReview = trim(review)
        # 去除停用词
        finalReview = []
        for word in trimedReview:
            if word not in stopword:
                finalReview.append(word)
        trimedReviews.append(finalReview)
    return labels, trimedReviews

def load_reviews(csvFilePath):
    df = pd.read_csv(csvFilePath)
    return df[&apos;label&apos;], df[&apos;review&apos;]

def load_stopword(filePath):
    &quot;&quot;&quot;
    加载停用词
    &quot;&quot;&quot;
    with open(filePath, encoding=&apos;UTF-8&apos;) as words:
        stopword = [word.strip() for word in words]
    return stopword


def data_suffle(labels, reviews):
    &quot;&quot;&quot;
    打乱数据
    &quot;&quot;&quot;
    join = list(zip(labels, reviews))
    random.shuffle(join)
    labels, reviews = zip(*join)
    return list(labels), list(reviews)

def pre_trim(csvFilePath, stopwordPath):
    &quot;&quot;&quot;
    预处理csv文本，并持久化
    &quot;&quot;&quot;
    df = pd.read_csv(csvFilePath)
    _, reviews = load_corpus(csvFilePath, stopwordPath)
    for index in range(len(reviews)):
        reviews[index] = &apos; &apos;.join(reviews[index])
    df[&apos;review&apos;] = reviews
    df.to_csv(csvFilePath[:-4] + &apos;Trimed.csv&apos;, index=False)

if __name__ == &apos;__main__&apos;:
    csvFilePath = &apos;../../corpus/100k/all.csv&apos;
    stopwordPath = &apos;./data/stopword.txt&apos;
    pre_trim(csvFilePath, stopwordPath)
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;读取文件&lt;/h1&gt;
&lt;h2&gt;读取文件并分割数据集&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import time
import pickle
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
from utils import load_reviews, data_suffle

# stopwordPath = &apos;./data/stopword.txt&apos;
# userDictPath = &apos;./data/user_dict.txt&apos;
csvFilePath = &apos;../../corpus/100k/allTrimed.csv&apos;
modelPath = &apos;./data/bayes.model&apos;

# 载入自定义字典
# jieba.load_userdict(userDictPath)

time_start = time.time()

labels, reviews = load_reviews(csvFilePath)
labels, reviews = data_suffle(labels, reviews)

# 1/4 分割数据集
n = len(labels) // 5
labels_train, reviews_train = labels[n:], reviews[n:]
labels_test, reviews_test = labels[:n], reviews[:n]

print(f&apos;Load Corpus Cost {time.time() - time_start:.4f} Sec&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;训练模型&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;time_start = time.time()

vectorizer = CountVectorizer(max_df=0.8, min_df=5)
# ⚠️坑点
vec_train = vectorizer.fit_transform([np.str_(review) for review in reviews_train])
clf = MultinomialNB().fit(vec_train, labels_train)

print(f&apos;Train Model Cost {time.time() - time_start:.4f} Sec&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个地方就是我遇到的问题，如果不将&lt;code&gt;reviews&lt;/code&gt;全部转为&lt;code&gt;np.string&lt;/code&gt;的话，会报&lt;code&gt;ValueError: np.nan is an invalid document, expected byte or unicode string.&lt;/code&gt;错误，但是根据sklearn的文档，似乎并没有说不能传入&lt;code&gt;str&lt;/code&gt;类型。&lt;/p&gt;
&lt;h2&gt;测试模型&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;vec_test = vectorizer.transform([np.str_(review) for review in reviews_test])
pred = clf.predict(vec_test)
from sklearn import metrics
print(metrics.classification_report(labels_test, pred))
print(&quot;准确率:&quot;, metrics.accuracy_score(labels_test, pred))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预测的准确率还不错，能达到85%以上。&lt;/p&gt;
</content:encoded></item><item><title>博客迁移</title><link>https://dicer-zz.github.io/posts/%E5%8D%9A%E5%AE%A2%E8%BF%81%E7%A7%BB/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/%E5%8D%9A%E5%AE%A2%E8%BF%81%E7%A7%BB/</guid><pubDate>Sun, 25 Apr 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;白嫖的阿里云服务器今年七月份就要到期了，又白嫖了两年的腾讯云，于是想着把自己的博客迁移到腾讯云上。&lt;/p&gt;
&lt;p&gt;之前在阿里云上部署&lt;code&gt;hexo&lt;/code&gt;博客的时候什么都不懂，是一篇篇教程试出来的。这次在部署写一份详细一点的教程记录一下部署过程。&lt;/p&gt;
&lt;h1&gt;原理&lt;/h1&gt;
&lt;p&gt;我们写博客用的是&lt;code&gt;markdown&lt;/code&gt;格式，使用&lt;code&gt;nodejs&lt;/code&gt;管理&lt;code&gt;hexo&lt;/code&gt;，通过&lt;code&gt;hexo&lt;/code&gt;渲染成&lt;code&gt;html&lt;/code&gt;格式的静态网页，然后通过&lt;code&gt;git&lt;/code&gt;将静态网页推送到&lt;code&gt;linux&lt;/code&gt;服务器上，并用&lt;code&gt;nginx&lt;/code&gt;驱动网页，然后就可以通过&lt;code&gt;IP&lt;/code&gt;地址访问我们的博客啦，如果需要的话可以做一下域名解析。&lt;/p&gt;
&lt;h1&gt;需要的部件&lt;/h1&gt;
&lt;h2&gt;客户端&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ node -v
v13.14.0
$ hexo version
hexo: 5.4.0
hexo-cli: 4.2.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;服务器端&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ nginx -v
nginx version: nginx/1.14.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;配置&lt;/h1&gt;
&lt;h2&gt;客户端&lt;/h2&gt;
&lt;p&gt;在&lt;code&gt;hexo&lt;/code&gt;博客的&lt;code&gt;__config.yml&lt;/code&gt;中添加以下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;deploy:
  type: git
  repo: &apos;yourUsername@yourIP:yourRepoPath&apos;
  branch: master
  message: &apos;爷爷奶奶你关注的博主更新啦！&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;服务器端&lt;/h2&gt;
&lt;h3&gt;创建git用户&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git&lt;/code&gt;用户专门用于管理&lt;code&gt;hexo&lt;/code&gt;博客。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ useradd -m git # -m 参数可以为git用户创建一个/home/git 文件夹
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;创建git私有repo&lt;/h3&gt;
&lt;p&gt;用于与客户端连接。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git init --bare hexo_blog # hexo_blog 可以替换成任何名称，但是注意⚠️__config.yml文件中的yourRepoPath
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;创建post-receive&lt;/h3&gt;
&lt;p&gt;⚠️该文件用于在上传时更新文件。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ mkdir /home/git/hexo_blog/hooks/post-receive
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写入如下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#！/bin/sh
git --work-tree=/usr/share/nginx/html/blog --git-dir=/home/git/hexo_blog checkout -f
# /usr/share/nginx/html/blog 可以修改为你想要存放静态网页的文件夹
# /home/git/hexo_blog 注意要和你的上面👆的文件名同步
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;为&lt;code&gt;post-receive&lt;/code&gt;添加执行权限，否则在执行&lt;code&gt;hexo d&lt;/code&gt;指令时，&lt;code&gt;post-receive&lt;/code&gt;无法执行。&lt;/p&gt;
&lt;p&gt;另外需要将&lt;code&gt;--work-tree&lt;/code&gt;文件的拥有者修改为&lt;code&gt;git&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ chmod +x /home/git/hexo_blog/hooks/post-receive
$ chown -R git:git /usr/share/nginx/html/blog 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;修改nginx配置信息&lt;/h3&gt;
&lt;p&gt;修改&lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo vim /etc/nginx/nginx.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写入如下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  &apos;$remote_addr - $remote_user [$time_local] &quot;$request&quot; &apos;
                      &apos;$status $body_bytes_sent &quot;$http_referer&quot; &apos;
                      &apos;&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;&apos;;

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;
    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  yourDomainName;
        # 修改为你对应的文件名
        root         /usr/share/nginx/html/blog;

        return 301 https://$server_name$request_uri;
        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }    # Settings for a TLS enabled server.
    #
    server {
        listen       443 ssl;
        listen       [::]:443 ssl;
        server_name  yourDomainName;
        # 修改为你对应的文件名
        root         /usr/share/nginx/html/blog;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }
        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }

    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证&lt;code&gt;nginx.conf&lt;/code&gt;配置是否有误：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;启动nginx服务&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ sudo service nginx start
$ sudo service nginx restart # 重启命令
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;推送博客&lt;/h1&gt;
&lt;p&gt;在客户端的&lt;code&gt;hexo&lt;/code&gt;文件夹内：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hexo clean &amp;amp;&amp;amp; hexo g &amp;amp;&amp;amp; hexo d
# 静态网页文件生成之后，输入服务器密码
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在&lt;code&gt;yourIP:80&lt;/code&gt;即可访问你的博客啦。&lt;/p&gt;
</content:encoded></item><item><title>Nginx 添加 SSL 证书</title><link>https://dicer-zz.github.io/posts/nginx-%E6%B7%BB%E5%8A%A0ssl%E8%AF%81%E4%B9%A6/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/nginx-%E6%B7%BB%E5%8A%A0ssl%E8%AF%81%E4%B9%A6/</guid><pubDate>Sun, 14 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在使用Chrome浏览器时，访问没有SSL的网页时会提醒网页不安全，添加过SSL证书之后，会有一个小锁，很好看。&lt;/p&gt;
&lt;p&gt;HTTP vs HTTPS: https://www.runoob.com/w3cnote/http-vs-https.html&lt;/p&gt;
&lt;h1&gt;申请SSL证书&lt;/h1&gt;
&lt;p&gt;阿里云和腾讯云都有免费的SSL证书可以申请，我用的是阿里云的云盾SSL证书，我用的服务器也是阿里云的，所以申请下来挺快的，大概几分钟。&lt;/p&gt;
&lt;p&gt;然后将证书下载下来，证书包含两个部分：.pem文件和.key文件，都需要上传到服务器。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ scp dicer.fun.pem /home/git/SSL/
# 输入服务器密码
$ scp dicer.fun.key/home/git/SSL/
# 输入服务器密码
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;修改nginx.conf&lt;/h1&gt;
&lt;p&gt;在ningx.conf中添加以下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
     listen       443 ssl;
     listen       [::]:443 ssl;
     server_name  dicer.fun;
     root         /usr/share/nginx/html/blog;

     # ssl                 on;
     ssl_certificate     &quot;/home/git/SSL/dicer.fun.pem&quot;;
     ssl_certificate_key &quot;/home/git/SSL/dicer.fun.key&quot;;

     ssl_session_cache shared:SSL:1m;
     ssl_session_timeout  10m;
     ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
     ssl_ciphers HIGH:!aNULL:!MD5;
     ssl_prefer_server_ciphers on;

     # Load configuration files for the default server block.
     include /etc/nginx/default.d/*.conf;

     location / {
     }
     error_page 404 /404.html;
         location = /40x.html {
     }

     error_page 500 502 503 504 /50x.html;
         location = /50x.html {
     }

 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中 server_name, root, ssl_certificate, ssl_certificate_key 需要进行对应的修改。&lt;/p&gt;
&lt;h1&gt;验证nginx.conf 并重启服务&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;nginx -t
nginx -s reload
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;重定向HTTP到HTTPS&lt;/h1&gt;
&lt;p&gt;在80端口的解析中，添加重定向。&lt;/p&gt;
&lt;p&gt;状态码301表示永久重定向。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
         listen       80 default_server;
         listen       [::]:80 default_server;
         server_name  dicer.fun;
         root         /usr/share/nginx/html/blog;
 
         return 301 https://$server_name$request_uri; # 重定向

         # Load configuration files for the default server block.
         include /etc/nginx/default.d/*.conf;
 
         location / {
         }
 
         error_page 404 /404.html;
             location = /40x.html {
         }
 
         error_page 500 502 503 504 /50x.html;
             location = /50x.html {
         }
     }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改文件后，需要重启nginx服务。&lt;/p&gt;
&lt;h1&gt;REFERENCES&lt;/h1&gt;
&lt;p&gt;https://segmentfault.com/a/1190000009363890&lt;/p&gt;
&lt;p&gt;https://www.cnblogs.com/kevingrace/p/6187072.html&lt;/p&gt;
</content:encoded></item><item><title>Python 连接 MySQL</title><link>https://dicer-zz.github.io/posts/python%E8%BF%9E%E6%8E%A5mysql/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/python%E8%BF%9E%E6%8E%A5mysql/</guid><pubDate>Wed, 10 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;前言&lt;/h1&gt;
&lt;p&gt;毕设需要保存大量数据，于是想在服务器上运行一个数据库。&lt;/p&gt;
&lt;p&gt;Cent OS 默认的 MySQL是Mariadb版本的，据说这个版本是在MySQL被Oracle并购之后，一些MySQL的老员工考虑到MySQL可能被商用而发行的一个免费开源版本。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ screenfetch
									 ..                    root@Dicer
                 .PLTJ.                  OS: CentOS 
                &amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;                 Kernel: x86_64 Linux 3.10.0-957.21.3.el7.x86_64
       KKSSV&apos; 4KKK LJ KKKL.&apos;VSSKK        Uptime: 596d 20h 16m
       KKV&apos; 4KKKKK LJ KKKKAL &apos;VKK        Packages: 569
       V&apos; &apos; &apos;VKKKK LJ KKKKV&apos; &apos; &apos;V        Shell: zsh 5.0.2
       .4MA.&apos; &apos;VKK LJ KKV&apos; &apos;.4Mb.        Disk: 12G / 41G (30%)
     . KKKKKA.&apos; &apos;V LJ V&apos; &apos;.4KKKKK .      CPU: Intel Xeon E5-2682 v4 @ 2.494GHz
   .4D KKKKKKKA.&apos;&apos; LJ &apos;&apos;.4KKKKKKK FA.    GPU: Cirrus Logic GD 5446
  &amp;lt;QDD ++++++++++++  ++++++++++++ GFD&amp;gt;   RAM: 488MiB / 1838MiB
   &apos;VD KKKKKKKK&apos;.. LJ ..&apos;KKKKKKKK FV    
     &apos; VKKKKK&apos;. .4 LJ K. .&apos;KKKKKV &apos;     
        &apos;VK&apos;. .4KK LJ KKA. .&apos;KV&apos;        
       A. . .4KKKK LJ KKKKA. . .4       
       KKA. &apos;KKKKK LJ KKKKK&apos; .4KK       
       KKSSA. VKKK LJ KKKV .4SSKK       
                &amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;&amp;lt;&amp;gt;                
                 &apos;MKKM&apos;                 
                   &apos;&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Mariadb&lt;/h1&gt;
&lt;h3&gt;查看是否安装过Mariadb&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ rpm -qs | grep mariadb
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;安装并启动Mariadb&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ yum install mariadb mariadb-server

$ systemctl enable mariadb
$ systemctl start mariadb
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;安全设置&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ mysql_secure_installation
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;远程访问&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ mysql -u root -h localhost -p &quot;mypassword&quot;
Mariadb root@localhost:mysql&amp;gt; UPDATE user SET host=&apos;%&apos; WHERE user=&apos;root&apos;;
Mariadb root@localhost:mysql&amp;gt; GRANT ALL PRIVILEGES ON *.* TO &apos;root&apos;@&apos;%&apos; IDENTIFIED BY &apos;mypassword&apos; WITH GRANT OPTION;
Mariadb root@localhost:mysql&amp;gt; FLUSH PRIVILEGES;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;关闭防火墙&lt;/h3&gt;
&lt;p&gt;如果以上步骤都完成了以后，远程连接超时的话一般都是防火墙的问题，可以将防火墙关闭：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ systemctl stop firewalld.service
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;卸载Mariadb&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ yum remove mariadb mariadb-server mariadb-libs
$ rm -rf /var/lib/mysql
$ rm /etc/my.cnf
$ yum install mariadb mariadb-server
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Python 连接 Mariadb&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;import mysql.connector as conn

db = conn.connect(
    host = &apos;myIP&apos;,
    port = &apos;myPort&apos;,
    user = &apos;root&apos;,
    password = &apos;myPassword&apos;,
    database = &apos;myDB&apos;
)

cursor = db.cursor()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;REFERENCE&lt;/h2&gt;
&lt;p&gt;https://www.linode.com/docs/guides/how-to-install-mariadb-on-centos-7/&lt;/p&gt;
</content:encoded></item><item><title>ECNU 往年机试</title><link>https://dicer-zz.github.io/posts/ecnu-%E5%BE%80%E5%B9%B4%E6%9C%BA%E8%AF%95/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/ecnu-%E5%BE%80%E5%B9%B4%E6%9C%BA%E8%AF%95/</guid><pubDate>Mon, 08 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;两场EOJ上公开的机试&lt;/h1&gt;
&lt;p&gt;EOJ上好多机试题都关闭了，只剩下这两场了。&lt;/p&gt;
&lt;p&gt;我和那一场区域赛难度的机试似乎无缘相见了。&lt;/p&gt;
&lt;p&gt;题目简单，不想写题解。&lt;/p&gt;
&lt;h1&gt;2018 研究生面试机考 (软件工程)&lt;/h1&gt;
&lt;p&gt;Link: https://acm.ecnu.edu.cn/contest/61/&lt;/p&gt;
&lt;h2&gt;A&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main() {
	int n, m;
	cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
	int sum = 0;
	for(int i = 1; i &amp;lt;= n; i++) {
		int tmp = 0, x;
		for(int j = 1; j &amp;lt;= m; j++) {
			cin &amp;gt;&amp;gt; x;
			tmp = max(tmp, x);
		}
		sum += tmp;
	}
	cout &amp;lt;&amp;lt; sum &amp;lt;&amp;lt; endl;
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;B&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int arr[21][21];

int main() {
	int n;
	cin &amp;gt;&amp;gt; n;
	for(int i = 1; i &amp;lt;= n; i++) {
		for(int j = 1; j &amp;lt;= i; j++) {
			if(j == 1 || j == i)	arr[i][j] = 1;
			else arr[i][j] = arr[i-1][j-1] + arr[i-1][j];
		}
		for(int j = 1; j &amp;lt;= i; j++) {
			printf(&quot;%d%c&quot;, arr[i][j], (i == j)?&apos;\\n&apos;:&apos; &apos;);
		}
	}
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;C&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

string s[21];

int main() {
	int n;
	cin &amp;gt;&amp;gt; n;
	for(int i = 1; i &amp;lt;= n; i++)	cin &amp;gt;&amp;gt; s[i];
	sort(s+1, s+1+n, [](string s1, string s2) -&amp;gt; bool {
		int n = min(s1.size(), s2.size());
		for(int i = 0; i &amp;lt; n; i++) {
			if(s1[i] &amp;gt; s2[i])	return 1;
			else if(s1[i] &amp;lt; s2[i])	return 0;
			else	continue;
		}
		return 1;
	});
	for(int i = 1; i &amp;lt;= n; i++)	cout &amp;lt;&amp;lt; s[i];
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;D&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main() {
	int n, k;
	cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;
	map&amp;lt;int, bool&amp;gt; disable;
	for(int i = 1; i &amp;lt;= k; i++) {
		int x; cin &amp;gt;&amp;gt; x;
		disable[x] = 1;
	}

	vector&amp;lt;int&amp;gt; dp(n+1, 0);
	dp[0] = 1;
	for(int i = 1; i &amp;lt;= n; i++) {
		int sum = 0;
		if(i - 1 &amp;gt;= 0 &amp;amp;&amp;amp; disable[i-1] == 0)	sum += dp[i-1];
		if(i - 2 &amp;gt;= 0 &amp;amp;&amp;amp; disable[i-2] == 0)	sum += dp[i-2];
		if(i - 3 &amp;gt;= 0 &amp;amp;&amp;amp; disable[i-3] == 0)	sum += dp[i-3];
		if(disable[i] == 0)	dp[i] = sum;
		else	dp[i] = 0;
	}
	cout &amp;lt;&amp;lt; dp[n] &amp;lt;&amp;lt; endl;
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;E&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main() {
	map&amp;lt;int, bool&amp;gt; one;
	one[1] = 1;
	int x = 1, p = 1;
	while(x &amp;lt;= 1e9) {
		one[x+p] = 1;
		x += p;
		p++;
	}
	int n;
	cin &amp;gt;&amp;gt; n;
	while(n--) {
		cin &amp;gt;&amp;gt; x;
		cout &amp;lt;&amp;lt; one[x] &amp;lt;&amp;lt; endl;
	}
	return 0;
}

/*
1 2 4 7 11
*/
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;F&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main() {
	int n, m, k;
	cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m &amp;gt;&amp;gt; k;
	int i = 0, j = 0;
	while(k--) {
		cout &amp;lt;&amp;lt; i + 1 &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; j + 1 &amp;lt;&amp;lt; endl;
		i++; i %= n;
		j++; j %= m;
	}
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;2018 研究生机试 (计算机系第二批)&lt;/h1&gt;
&lt;p&gt;Link: https://acm.ecnu.edu.cn/contest/66/&lt;/p&gt;
&lt;h2&gt;A&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;a = [int(x) for x in input().split()]
print(max(a[1:]))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;B&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

int main() {
	int n;
	cin &amp;gt;&amp;gt; n;
	for(int i = 7; i &amp;gt;= 0; i--)	cout &amp;lt;&amp;lt; ((n&amp;gt;&amp;gt;i)&amp;amp;1);
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;C&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;s = input()
count = 0
for i in range(len(s)):
	count += (&apos;0&apos; &amp;lt;=s[i] &amp;lt;= &apos;9&apos;)
print(count)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;D&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;arr = [int(x) for x in input().split()][1:]
arr = sorted(arr)
# print(arr)
ans = 0x7f7f7f7f
for i in range(1, len(arr)):
	ans = min(ans, arr[i] - arr[i-1])
print(ans)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;E&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;arr = [int(x) for x in input().split()][1:]
arr.sort()
arr = arr[::-1]
arr.sort(key = lambda x: abs(x))
arr = arr[::-1]
print(&apos;,&apos;.join([str(x) for x in arr]))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;F&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;import struct
f = float(input())
m = struct.pack(&apos;d&apos;, f).hex()
m = str(m).upper()
m = [m[i*2:i*2+2] for i in range(0, 8)][::-1]
m = &apos;-&apos;.join(m)
print(m)
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>一个简易的 Go Pool 的设计与实现</title><link>https://dicer-zz.github.io/posts/%E4%B8%80%E4%B8%AA%E7%AE%80%E6%98%93%E7%9A%84go-pool%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/%E4%B8%80%E4%B8%AA%E7%AE%80%E6%98%93%E7%9A%84go-pool%E7%9A%84%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</guid><pubDate>Thu, 25 Feb 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Goroutine 的优势和缺陷&lt;/h1&gt;
&lt;p&gt;Goroutine 是 Go 语言内部实现的高并发协程模型，并且可以通过 Channel 进行快捷便利的通信。&lt;/p&gt;
&lt;p&gt;但是也有一些缺陷：一，不支持限制 Goroutine 的数量。二，在运行完毕之后不会立即被GC（Garbage Collection）回收。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@riteeksrivastava/a-complete-journey-with-goroutines-8472630c7f5cs&quot;&gt;👉More About Goroutine&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Go Pool&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;/gallery/others/GoPool.png&quot; alt=&quot;Diagram&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Pool 中主要包含三种部件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Worker - 实际执行函数&lt;/li&gt;
&lt;li&gt;JobsChan - 将 job 提交给 Worker&lt;/li&gt;
&lt;li&gt;TasksChan - 用户将任务提交到 TasksChan，Pool 再将任务提交到 JobsChan。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;另外需要定义 Task 结构，至少应该包含需要执行的函数。一个样例如下:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;fn - 需要执行的函数&lt;/li&gt;
&lt;li&gt;submitTime - 提交时间&lt;/li&gt;
&lt;li&gt;User - 提交用户&lt;/li&gt;
&lt;li&gt;taskId - 任务ID&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过Pool，我们可以限制goroutine的数量，并且可以复用goroutine。在一些并发量极大，单个goroutine执行任务并不繁重的情况下，可以节约很多内存。&lt;/p&gt;
&lt;p&gt;因为虽然goroutine的初始内存只有2kb，但是如果并发量达到十万、百万级别，内存的消耗也是不容小觑的。当任务并不繁重时，一些goroutine可能被初始化之后，只执行了几行代码就被搁置了，但没有及时的GC。大量的died goroutine不必要地占用了大量内存资源。池化后，可以达到线程复用，从而节约内存。&lt;/p&gt;
&lt;h1&gt;Implementation&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;// The implementaion of a simple goroutines pool with fixed capacity
// By dicer 2/25/2021

package main

import (
   &quot;fmt&quot;
   &quot;time&quot;
)

// Define Task
type Task struct{
   fn func() error
   submitTime time.Time
   // Alternative property
   // #user
   // #taskID
}

// Return a new task with a function argFn
func NewTask(argFn func() error) *Task {
   t := Task{
      fn:         argFn,
      submitTime: time.Now(),
   }
   return &amp;amp;t
}

// Execute the fn of a Task
func (t *Task) Execute() {
   t.fn()
}

// Define Pool
type Pool struct {
   TasksChan chan *Task
   JobsChan chan *Task
   Cap uint
}

// Return a new pool with fixed capacity
func NewPool(cap uint) *Pool {
   p := Pool{
      TasksChan: make(chan *Task, cap),
      JobsChan: make(chan *Task, cap),
      Cap: cap,
   }
   return &amp;amp;p
}

// Generate a worker entity used as a coroutine
func (p *Pool) Worker(WorkerID int) {
   // take a task from JobsChan
   for t := range p.JobsChan {
      // execute this task
      t.Execute()
      // print info
      fmt.Printf(&quot;Worker %d has finished a task submited at %v.\n&quot;, WorkerID, t.submitTime)
   }
}

// Run the pool with cap_size worker
func (p *Pool) Run() {

   for i := 0; uint(i) &amp;lt; p.Cap; i++ {
      go p.Worker(i)
   }

   for  {
      p.JobsChan &amp;lt;- &amp;lt;- p.TasksChan
   }
   fmt.Println(&quot;Debug&quot;)
}

// Test the Pool
func main() {
   t := NewTask(func() error {
      _, err := fmt.Println(time.Now())
      return err
   })

   p := NewPool(5)
   go p.Run()

   for {
      p.TasksChan &amp;lt;- t
   }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>考研总结</title><link>https://dicer-zz.github.io/posts/%E8%80%83%E7%A0%94%E6%80%BB%E7%BB%93/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/%E8%80%83%E7%A0%94%E6%80%BB%E7%BB%93/</guid><pubDate>Sat, 23 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;虽然说是考研总结，但其实大概想写一下我去年都做了些什么，做一个年度复盘，其中最重要的就是考研了，占据了去年大概八九个月的时间。&lt;/p&gt;
&lt;h2&gt;决定考研&lt;/h2&gt;
&lt;p&gt;大三的人，无非几种选择：考研，考公，工作，出国。&lt;/p&gt;
&lt;p&gt;我以前只是纠结是考研还是工作。但是因为一个奇怪的想法：考研没考上还能去工作。说起来也好笑，这句话已经默认了考研就一定考不上，严密的逻辑应该是：考研没考上，还能找工作；考上了，读完研也能找工作；工作的时候也可以考研。因为身边的人的选择和一些信息，最后还是放弃了找工作，选择了考研。&lt;/p&gt;
&lt;p&gt;选择其实工作还是考研，其实也没有那么重要，有价值的人在什么地方都可以发挥自己的价值。相比选择，更重要的是坚持。但是选择有很重要，在这个时代，做互联网相关，可以说站在了风口上。&lt;/p&gt;
&lt;p&gt;我爱说一句话：尽人事，听天命。就像某地某年的语文作文题目说的一样：成功不只是努力，还有机遇也很重要。蛤曾经说过，自己不知道为什么，就坐在了现在的位子上，“组织上安排好了”&lt;/p&gt;
&lt;h2&gt;一个机遇&lt;/h2&gt;
&lt;p&gt;大概从二三月份开始初步进入复习吧，这个时候还是寒假，自己也不清楚复习的方法，只是在背单词，然后就是看书。&lt;/p&gt;
&lt;p&gt;看书，没错。这段时期疫情很严重，我没有地方去，家里也没有人，只有我自己在家。不知道是什么时候，偶尔打开了微信读书，看到了大刘的三体，鬼使神差的读了起来，上了瘾，三四天读完了三部曲。这是我第一次读长篇，很长很长，接近一百万字的书，但是读的酣畅淋漓。我迷上了书中构建的那个世界。&lt;/p&gt;
&lt;p&gt;有人说，世界上有两种人，一种是看过三体的，另一种没有。一本书可以快速打破你和另陌生人的隔阂，只需要几个字词。三体中这样的词很多：二向箔，智子，面壁人，执剑人，黑暗森林法则。不得不佩服大刘的脑洞，一本书中充斥着极致的幻想，而且逻辑严密。&lt;/p&gt;
&lt;p&gt;有传言说，大刘是误以为自己得了癌症，以为自己命不久矣，想在临死之前，把自己的巧思都留下了。最后得知是误诊。一个美好的巧合，也可以说是：机遇。为人类文明留下了如此璀璨的书籍。虽然有人说，三体中的许多巧思都可以拆成一本书，够大刘慢慢写一辈子了，但我还是觉得，那绝对抵不上一套三体，流芳百世。&lt;/p&gt;
&lt;p&gt;三体之后，就开始读各种各样的书，许多经典：1984、娱乐至死，不算有趣，也不丰富，只是符合这个时代；数学之美、从一到无穷大、上帝掷骰子吗？意外的有趣，引人深思。一只特立独行的猪、活着，名家经典，值得一读，但我觉得富贵的故事并没有多么可怜，只是一个纨绔子弟，散尽家财，幡然悔悟，命运多舛的故事。&lt;/p&gt;
&lt;p&gt;另一本大部头就是红楼梦了，看的时候废寝忘食，夜不能寐，辗转反侧。叹书中女子，义气刚节；怜书中女子，难逃时代命运；惜书中女子，终落凡尘。&lt;/p&gt;
&lt;p&gt;时常思考读书的意义，其实主要是文学作品，时间长了，书中的内容多已淡忘，读书时的感人肺腑、肝肠寸断也已忘记。只记得只言片语，大致情景。没有什么感觉了。&lt;/p&gt;
&lt;p&gt;太久没读书了，最近在忙找工作，之前考研也是忙得没有时间，读研的时候如果科研压力没那么大，还是希望能多读一些书籍。&lt;/p&gt;
&lt;h2&gt;无它，唯手熟而&lt;/h2&gt;
&lt;p&gt;四五月份是数学的时间，题海战术，一点一点的扣。我就记得自己看了张宇的基础班，刷了两边1800。&lt;/p&gt;
&lt;p&gt;这段时间过的很快，每天就只是做题。一杯茶一包烟，一道积分做一天。这话一点都不假，积分不会做是真的很久都做不出来。每天都把自己关在家里，屋子里窗户太小了，不知白天黑夜，不知雨雪风霜。&lt;/p&gt;
&lt;p&gt;感觉没能总结点东西，现在想起来，自己数学复习的太没有章法了，无章可循，别人学什么就跟着学什么。缺乏合理的规划，不过那个时候的我，也没有人能给我指点一二。&lt;/p&gt;
&lt;p&gt;数学学得慢，忘得可一点都不慢，距离考完试还不到一个月，我已经不知道自己当初学了什么了。痛心疾首。&lt;/p&gt;
&lt;h2&gt;408时间&lt;/h2&gt;
&lt;p&gt;暑假的重头戏是专业课，另一个150分。408综合科目多、知识点多、考察深入，难度很大。好在数据结构有一点基础，重难点是组成原理。&lt;/p&gt;
&lt;p&gt;在知乎上看推荐买了一本CS：APP，不到一百的价格，都说是神书。确实名不虚传，计算机中的数值表示用图表示得清清楚楚，Cache、段页式存储、虚拟内存讲的头头是道，很是受用。&lt;/p&gt;
&lt;p&gt;王道四本书看了两遍，真题没做完，只做了十几年的题目，但是因为王道后面的课后题大部分都出自真题，也算是都看过了，也正因为如此，导致我做零几年的真题总觉得在背答案，就不想写了。王道配套的模拟题也好挺好的，主要是题目很新，课后题没有出现过，做起来比较舒服。&lt;/p&gt;
&lt;p&gt;现在想想，这四本书确实让我对计算机有了更深刻的理解，但这却只是开始，还有浩如烟海的技术等着我去了解学习。&lt;/p&gt;
&lt;h2&gt;英语学习&lt;/h2&gt;
&lt;p&gt;英语是伴随考研始终的，单词一直要背，长难句一直要看，文章每天都要读。&lt;/p&gt;
&lt;p&gt;可能是因为我英语原来比较差吧，个人感觉英语提升对我的帮助是最大的，英文文章虽然说读起来还是比较费劲，但却没有以前的那种想读都读不懂的感觉了。挺好的。&lt;/p&gt;
&lt;p&gt;英语的学习也是伴随一生的。考研只考了读写译，还有听说需要自己学，这两点对交流来说是更重要的。&lt;/p&gt;
&lt;h2&gt;最后&lt;/h2&gt;
&lt;p&gt;考研让我懂了许多的知识，也磨砺了我的品格。&lt;/p&gt;
&lt;p&gt;宝剑锋从磨砺出，梅花香自苦寒来。&lt;/p&gt;
&lt;p&gt;一个更好的学位，不是终点，而是新的起点。复试加油。&lt;/p&gt;
&lt;p&gt;2021/1/23&lt;/p&gt;
&lt;p&gt;Dicer，于焦作&lt;/p&gt;
</content:encoded></item><item><title>学期总结</title><link>https://dicer-zz.github.io/posts/winter-holiday-summary/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/winter-holiday-summary/</guid><pubDate>Sat, 11 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这个学期应该算是过的最丰富多彩的一个学期。&lt;/p&gt;
&lt;p&gt;出去打了三场比赛，秦皇岛，银川，西安。有得有失。&lt;/p&gt;
&lt;p&gt;秦皇岛含泪铁首，银川惊险夺金，西安奇妙得铜，这里面的一个个惊险刺激、趣味横生的故事，只有真正经历过才能体会得到吧。&lt;/p&gt;
&lt;p&gt;在期末考试来临前的两周里，完成了两门课程设计以及五门课程的自学，希望能够得到一个好成绩。&lt;/p&gt;
&lt;h2&gt;三场比赛&lt;/h2&gt;
&lt;h4&gt;秦皇岛&lt;/h4&gt;
&lt;p&gt;这是一场CCPC的比赛，可能是我第一次也是最后一次参加CCPC的区域赛了。&lt;/p&gt;
&lt;p&gt;秦皇岛离学校很远，坐了一天一夜的火车，舟车劳顿。&lt;/p&gt;
&lt;p&gt;周六的晚上吃了宾馆楼下的一家饭店，主打鱼头。本着来都来了的消费观，点了一个鱼头，一个炒三丝，还有一个记不得了，也可能是就点了这两个菜。鱼头的吃法是泡馍，鱼头特别大，我第一次见到那么大的鱼头。鱼肉一般，汤汁特别浓郁，不过好像勾的欠有点多，导致泡馍的时候馍没有很洗汤。在我们把鱼肉吃完了之后，店家看见我们鱼头都没打开，给我们说“把鱼头翻开啊，里面才是精华”。打开一看，果然，里面都是一些软糯的肉，跟鱼身子上的肉完全不一样，非常的鲜，要不是店家提醒，我们可能就要错过这美食了。炒三丝里面有一个是海参丝，这也是我第一次吃海参，口感Q弹，但是没什么自己味道，都是汤汁的味道。&lt;/p&gt;
&lt;p&gt;比赛当天有点炸裂，签到题挂了一次，一道 tarjan 的裸题因为没学过想了很久，因此一道比较简单的 DP 很久之后才看到。决策失误，不然应该不至于成绩这么差。&lt;/p&gt;
&lt;p&gt;比赛结束之后去了心心念念的海边，光着脚踩水踩沙子，感受着海浪扑到自己的腿上，感受着脚底的沙子随着海水一起流走。所有的失意也一起流走了，剩下了金子一样的坚韧。&lt;/p&gt;
&lt;h4&gt;银川&lt;/h4&gt;
&lt;p&gt;由于银川网赛接二连三的出现问题，很多高校都拒绝参加现场赛，因此给了很多弱校偷鸡的机会，比如我校。&lt;/p&gt;
&lt;p&gt;银川那可是相当我远了，火车又是咣当咣当不停的运行了近乎一天的时间。不过幸好这次去的人比较多，路上也不是很落寞。透过火车的窗户，看到了戈壁滩上的落日。&lt;/p&gt;
&lt;p&gt;说是银川，实际上比赛是在石嘴山举办的。按照惯例周六去大吃了一顿，吃的是当地一个比较出名的羊肉店，确实很好吃。除了羊肉之外，还有一个花茶特别的好喝。&lt;/p&gt;
&lt;p&gt;比赛前夕 q 神熬夜补锅，才使得现场赛顺顺利利的进行。&lt;/p&gt;
&lt;p&gt;比赛题目确实简单，前40金，我们第四十。&lt;/p&gt;
&lt;p&gt;然后就愉快的返程了。&lt;/p&gt;
&lt;h4&gt;西安&lt;/h4&gt;
&lt;p&gt;EC 正好和四六级在同一个周末，只能周六考四六级，晚上坐一夜火车去西安，然后早上正好到赛场直接开始比赛，直接就开始比赛了。&lt;/p&gt;
&lt;p&gt;题很难，二十多分钟签上到，然后挂机了快三个小时才写出第二道题。第三道题比赛前十几分钟才交上第一发，然后 TLE 了，发现是结构体里写了个 vector ，赶紧把 vector 提出来，改到结束前20s才改好，提交，返回了 too late，很绝望。这个时候大屏幕上时间显示还有 20s ，但是比赛却已经结束了。&lt;/p&gt;
&lt;p&gt;学弟们三题拿到了铜，于是一起去领奖。颁发铜奖的时候突然听到了我们的队名，原来是重测了还通过了。惊喜。&lt;/p&gt;
&lt;h2&gt;期末&lt;/h2&gt;
&lt;p&gt;这个学期没有怎么上课，能逃的课都逃了。就导致期末考试很难过。还有两篇课程设计是真的麻烦。拼命搞了两个星期总算是复习了个差不多，课设也做好了。等着出成绩。&lt;/p&gt;
&lt;h4&gt;2020-1-19 update:&lt;/h4&gt;
&lt;p&gt;成绩都出来了，有几科分数十分不科学，低的就离谱，不知道这些老师怎么想的。&lt;/p&gt;
&lt;p&gt;总的来说还可以，绩点3.35，总评可算进前50了。&lt;/p&gt;
&lt;h5&gt;Thumbnail by &lt;a href=&quot;https://unsplash.com/@romankraft?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Roman Kraft&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/information?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/h5&gt;
</content:encoded></item><item><title>基于高德地图 API 的 Web 课程设计</title><link>https://dicer-zz.github.io/posts/webproject-base-on-gaode-map/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/webproject-base-on-gaode-map/</guid><pubDate>Sat, 28 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Web课程设计&lt;/h2&gt;
&lt;p&gt;说好的考试突然就变成了课程设计，竟然还要写一个多达20页的论文，实在是不知如何是好。&lt;/p&gt;
&lt;p&gt;Web 考试本来应该是最简单的了，想着随便看看就过了，现在可倒好，就算是把我肚子里的东西都倒出来也凑不够20页的论文。&lt;/p&gt;
&lt;p&gt;话虽然这样说，不过还是认真的做了一个网页的。主要基于高德地图的开放API，此处给阿里点个赞，高德地图应该是有口皆碑，用过的都知道很好。&lt;/p&gt;
&lt;p&gt;页面随便搞了搞，做了一个大色块撞色的设计，感觉还挺好看的。排版就是简单的几个 div 布局。&lt;/p&gt;
&lt;p&gt;然后提供了三个小功能，包括经纬度查询，地点联想查询和点击获得经纬度。都是通过 js 和提供的 API 实现的。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://47.101.66.37:8888/map&quot;&gt;项目演示&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;HTML 部分&lt;/h2&gt;
&lt;p&gt;在 head 标签里面引入高德地图的 API 和自己写好的 js 以及 css 文件。&lt;/p&gt;
&lt;p&gt;body 里面有一个项目的名称和简介，然后就是三个显眼的功能方块，对应着三个小功能。下面就是地图本身了，直接调用 API 给出一个默认地址就可以加载了。&lt;/p&gt;
&lt;p&gt;第一个功能方块提供了经纬度地图查询，默认为我校的地址。通过输入经纬度，然后点击确认按钮，通过按钮的 onclick 事件连接到一个 js 函数，实现地图查询。&lt;/p&gt;
&lt;p&gt;第二个功能方块是最复杂的一个，使用了关键字补全 API，通过检测选择事件来实现地图的重新加载。&lt;/p&gt;
&lt;p&gt;第三个功能方块分为两个部分，一个是通过监听 click 事件来实现获取点击处的经纬度，并将其填入 input 标签的内容中。然后复制按钮使用了 DOM 的 execCommand 函数进行复制。&lt;/p&gt;
&lt;h2&gt;CSS 部分&lt;/h2&gt;
&lt;p&gt;地图主题用 container id 标记，通过百分比布局实现多设备自适应。&lt;/p&gt;
&lt;p&gt;三个功能方块通过简单的媒体查询实现移动端，PC端的适应布局，个人感觉效果还是挺好的，就是在 iPad 上看有点歪，没有居中，实在是不知道是什么原因。&lt;/p&gt;
&lt;p&gt;颜色是在网上找的一套颜色，看着也还行，也可能还有更好看的配色，不过实在是懒得调了，没有太大的意义。&lt;/p&gt;
&lt;h2&gt;JAVASCRIPT 部分&lt;/h2&gt;
&lt;p&gt;三个功能方块都是 js 实现的，效果还是挺好的。工作的挺正常，有一个小 bug 是第二个功能方块在你操作过地图之后可能会失灵，我也不清楚是什么原因。&lt;/p&gt;
&lt;p&gt;还有可以提升用户体验的方法，页面刷新前应该保存地图当前的位置，防止刷新之后地图更改，可能会造成用户不必要的麻烦。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;最后说一说总结吧。整个东西做下来还是挺有意思的，好多功能不知道怎么实现就一直在 google ，看 API 的文档和样例。基本功能实现了之后就是不断的调样式，怎么才能更好看一点。虽然是一个很简单的项目，自己一个人做下来感觉还是很有成就感的。&lt;/p&gt;
</content:encoded></item><item><title>牛客练习赛 53B</title><link>https://dicer-zz.github.io/posts/niuke-exercise-53/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/niuke-exercise-53/</guid><pubDate>Sat, 12 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题目&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ac.nowcoder.com/acm/contest/1114#question&quot;&gt;题目链接&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;题意&lt;/h3&gt;
&lt;p&gt;求和式： $$\sum_{i=1}^{n}{\sum_{j=1}^{i}{i \lfloor {\frac{i}{j}} \rfloor ^j}}$$&lt;/p&gt;
&lt;h3&gt;分析&lt;/h3&gt;
&lt;p&gt;更换一下枚举顺序：&lt;/p&gt;
&lt;p&gt;$$\sum_{j=1}^{n}\sum_{i=j}^{n}{i \lfloor \frac{i}{j} \rfloor ^ j}$$&lt;/p&gt;
&lt;p&gt;可以发现 $i$ 在 区间 $[k\cdot j, (k+1)\cdot j-1]$ 内 $\lfloor \frac{i}{j} \rfloor = k$ ，因此可以对每个 $j$ 做分段处理，另外 $\lfloor \frac{i}{j} \rfloor ^ j$ 可以从上一个状态转移过来。&lt;/p&gt;
&lt;p&gt;时间复杂度： $O(n \log n)$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
  
using namespace std;
  
typedef long long ll;
const int MAXN = 3e6 + 7;
const int MOD = 1e9 + 7;
ll p[MAXN];
  
int main () {
    for (int i = 1; i &amp;lt;= 3000000; ++i) p[i] = 1;
    ll n; cin &amp;gt;&amp;gt; n;
    ll ans = 0;
    for(ll j = 1; j &amp;lt;= n; ++j) {
        ll lim = n/j, l = j, r = min(2 * j - 1, n);
        for (ll i = 1; i &amp;lt;= lim; ++i) {
            p[i] = p[i] * i % MOD;
            ans += (l+r)*(r-l+1)/2%MOD*p[i]%MOD;
            ans %= MOD;
            l += j; r = min(r + j, n);
        }
    }
    cout &amp;lt;&amp;lt; ans &amp;lt;&amp;lt; endl;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2019 ICPC 沈阳网络赛 D</title><link>https://dicer-zz.github.io/posts/2019-icpc-shenyang-contest-online/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/2019-icpc-shenyang-contest-online/</guid><pubDate>Sat, 28 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://nanti.jisuanke.com/t/41403&quot;&gt;Fish eating fruit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;点分治进阶题目，状态很多，统计起来有点复杂。&lt;/p&gt;
&lt;h2&gt;分析&lt;/h2&gt;
&lt;p&gt;首先肯定是点分治没得跑了，然后就是怎么计算的问题了。&lt;/p&gt;
&lt;p&gt;我是先统计出来所有的 $mod $ 后的值，对于每一个节点的深度，可以计算出来这个点对 三个剩余 的贡献。&lt;/p&gt;
&lt;p&gt;转移一下就好了。&lt;/p&gt;
&lt;p&gt;因为去重的时候做了减法，所有答案可能是负值，要调整回来。&lt;/p&gt;
&lt;h2&gt;代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b) for(int i = (a); i &amp;lt;= (b); ++i)
#define per(i, a, b) for(int i = (a); i &amp;gt;= (b); --i)
#define debug(x) cerr &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl;
#define size sizeeeeeeee
using namespace std;

typedef long long ll;
const int MOD = 1e9+7;
const int MAXN = 1e4 + 7;

ll ans1, ans2, ans3, cnt[4];
int n, root, size, tot = 0;
int son[MAXN], f[MAXN], head[MAXN];
int dep[MAXN]; bool vis[MAXN];
struct node{
    int u, w, nxt;
}; vector&amp;lt;node&amp;gt; E;
void add(int u, int v, int w) {
    E.push_back(node{v, w, head[u]});
    head[u] = tot++;
}
void get_rt(int x, int fa = 0) {
    son[x] = 1; f[x] = 0;
    for(int j = head[x]; ~j; j = E[j].nxt) {
        int u = E[j].u, w = E[j].w;
        if(vis[u] || u == fa)   continue;
        get_rt(u, x);
        son[x] += son[u];
        f[x] = max(f[x], son[u]);
    }
    f[x] = max(f[x], size - son[x]);
    if(f[x] &amp;lt; f[root]) root = x;
}
vector&amp;lt;int&amp;gt; v;
void get_dep(int x, int fa) {
    v.push_back(dep[x]); cnt[dep[x]%3]++;
    for(int j = head[x]; ~j; j = E[j].nxt) {
        int u = E[j].u, w = E[j].w;
        if(vis[u] || u == fa)   continue;
        dep[u] = dep[x] + w;
        get_dep(u, x);
    }
}
void calc(int x, int op) {
    memset(cnt, 0, sizeof cnt); v.clear();
    get_dep(x, 0);
    ll res0, res1, res2;
    res0 = res1 = res2 = 0;
    for(int p: v) {
        int offset = p%3;
        if(offset == 0) {
            res0 += op * (cnt[0] - 1) * p;
            res1 += op * cnt[1] * p;
            res2 += op * cnt[2] * p;
        } else if(offset == 1) {
            res0 += op * cnt[2] * p;
            res1 += op * cnt[0] * p;
            res2 += op * (cnt[1] - 1) * p;
        } else {
            res0 += op * cnt[1] * p;
            res1 += op * (cnt[2] - 1) * p;
            res2 += op * cnt[0] * p;
        }
        res0 %= MOD; res1 %= MOD; res2 %= MOD;
    }
    ans1 += res0; ans2 += res1; ans3 += res2;
    ans1 %= MOD; ans2 %= MOD; ans3 %= MOD;
}
void solve(int x) {
    dep[x] = 0; calc(x, 1); vis[x] = 1;
    for(int j = head[x]; ~j; j = E[j].nxt) {
        int u = E[j].u, w = E[j].w;
        if(vis[u])  continue;
        dep[u] = w; calc(u, -1);
        root = 0; size = son[u];
        get_rt(u);
        solve(root);
    }
}
int main(int argc, char const *argv[])
{
    while(~scanf(&quot;%d&quot;, &amp;amp;n)) {
        memset(head, -1, sizeof head);
        memset(vis, 0, sizeof vis);
        E.clear(); tot = 0;
        int u, v, w;
        rep(i, 1, n-1) {
            scanf(&quot;%d %d %d&quot;, &amp;amp;u, &amp;amp;v, &amp;amp;w);
            u++; v++;
            add(u, v, w); add(v, u, w);
        }
        root = 0; f[0] = size = n;
        get_rt(1, 0);
        ans1 = ans2 = ans3 = 0;
        solve(root);
        ans1 = ans1 * 2 % MOD;
        ans2 = ans2 * 2 % MOD;
        ans3 = ans3 * 2 % MOD;
        ans1 = (ans1 + MOD) % MOD;
        ans2 = (ans2 + MOD) % MOD;
        ans3 = (ans3 + MOD) % MOD;
        printf(&quot;%lld %lld %lld\n&quot;, ans1, ans2, ans3);
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>洛谷 P3806</title><link>https://dicer-zz.github.io/posts/luogu-p3806/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/luogu-p3806/</guid><pubDate>Sat, 28 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.luogu.org/problem/P3806&quot;&gt;洛谷P3806&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;点分治模板题。&lt;/p&gt;
&lt;p&gt;求边权树上距离为 $k$ 的点对。&lt;/p&gt;
&lt;h2&gt;分析&lt;/h2&gt;
&lt;p&gt;第一眼没看到边带权，以为直接判断树的直径是否大于等于 $k$ 就行了。233333。&lt;/p&gt;
&lt;p&gt;一道经典的点分治的题目，点分治是一种高效的求树上路径满足一定条件的路径个数，当然一条路径，对应两个端点，说成点对数也可以。&lt;/p&gt;
&lt;p&gt;这道题目本来写成了在线，有一个点 $1.02s$ 过不去，本来想着卡卡就能过去了，可是去看了看别人的代码，发现总共只需要 $200+ms$ 就够了。想着是自己写搓了，然后翻开了题解，发现可以同时对所有的询问进行查询，没有必要一个一个查。改了改，就过了。&lt;/p&gt;
&lt;p&gt;点分治还是比较考验对 $dfs$ 的理解的，因为这道题就有三个 $dfs$ ，甚至还有一个换根 $DP$ 求重心。&lt;/p&gt;
&lt;p&gt;然后就是比较考验细节，今天两道模板题都是因为几个变量名弄混了，调了几个小时的 $BUG$ 。&lt;/p&gt;
&lt;p&gt;讲一下思路吧。点分治最重要的两点，一是找重心，这样可以降低复杂度，二是统计和去重。&lt;/p&gt;
&lt;p&gt;找重心每道题目都是一样的，就不说了。&lt;/p&gt;
&lt;p&gt;这道题的统计就是，对于每一个重心树，先统计所有节点到根节点的距离，因为距离范围不超过 $1e7$ 可以用数组保存，剩下一个 $map$ 的 $log$ ，比较重要的是统计数组不能直接清空，我们考虑将所有距离先存起来，统计一下，然后再删除就好了。去重的话，就是很常规的去重。&lt;/p&gt;
&lt;p&gt;我统计的直接就是距离为 $k$ 的点对数。&lt;/p&gt;
&lt;p&gt;时间复杂度： $O(n\cdot m \log n)$&lt;/p&gt;
&lt;h2&gt;代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b)    for(int i = (a); i &amp;lt;= (b); ++i)
#define per(i, a, b)    for(int i = (a); i &amp;gt;= (b); --i)
#define debug(x)    cerr &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl
#define size sizeeeeeee
using namespace std;

typedef long long ll;
const int MAXN = 1e4 + 7;
const int MAXM = 1e7 + 7;
const int MOD = 1e9 + 7;

vector&amp;lt;pair&amp;lt;int, int&amp;gt; &amp;gt; G[MAXN];
int son[MAXN], dep[MAXN], f[MAXN], q[MAXN], ans[MAXN], k;
int root, size; int cnt[MAXM], n, m;
bool vis[MAXN];

void get_rt(int x, int fa) {
    son[x] = 1; f[x] = 0;
    for(auto p: G[x]) {
        int u = p.first;
        if(u == fa || vis[u])   continue;
        get_rt(u, x);
        son[x] += son[u];
        f[x] = max(f[x], son[u]);
    }
    f[x] = max(f[x], size - son[x]);
    if(f[x] &amp;lt; f[root])  root = x;
}
vector&amp;lt;int&amp;gt; v;
void get_dep(int x, int fa) {
    v.push_back(dep[x]);
    for(auto p: G[x]) {
        int u = p.first, w = p.second;
        if(u == fa || vis[u])   continue;
        dep[u] = dep[x] + w;
        get_dep(u, x);
    }
}
void calc(int x, int w, int op) {
    v.clear(); dep[x] = w; get_dep(x, 0);
    for(int p: v) {
        rep(i, 1, m) if(p &amp;lt;= q[i]) ans[i] += op * cnt[q[i] - p];
        cnt[p]++;
    }
    for(int p: v) cnt[p]--;
}
void solve(int x) {
    calc(x, 0, 1); vis[x] = 1;
    for(auto p: G[x]) {
        int u = p.first, w = p.second;
        if(vis[u])  continue;
        calc(u, w, -1);
        root = 0; size = son[u];
        get_rt(u, 0);
        solve(root);
    }
}
int main() {
    scanf(&quot;%d %d&quot;, &amp;amp;n, &amp;amp;m);
    int u, v, w;
    rep(i, 1, n-1) {
        scanf(&quot;%d %d %d&quot;, &amp;amp;u, &amp;amp;v, &amp;amp;w);
        G[u].push_back({v, w});
        G[v].push_back({u, w});
    }
    rep(i, 1, m) scanf(&quot;%d&quot;, &amp;amp;q[i]);
    f[0] = INT_MAX; size = n; root = 0;
    get_rt(1, 0); solve(root);
    rep(i, 1, m) {
        if(ans[i])  printf(&quot;AYE\n&quot;);
        else printf(&quot;NAY\n&quot;);
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>洛谷 P1131 时态同步</title><link>https://dicer-zz.github.io/posts/luogu-p1131/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/luogu-p1131/</guid><pubDate>Wed, 18 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.luogu.org/problem/P1131&quot;&gt;时态同步&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;一道双 $dfs$ 的题目。&lt;/p&gt;
&lt;h2&gt;分析&lt;/h2&gt;
&lt;p&gt;题意就是给出一棵树，可以增加任意一条边的权值，问最少增加多少可以让所有叶子节点到跟节点的距离相等。&lt;/p&gt;
&lt;p&gt;先跑一边 $dfs$ 求出所有节点到根节点的距离，再统计出以每个节点为根的子树上的所有点到根节点的距离的最大值。&lt;/p&gt;
&lt;p&gt;然后再做一次 $dfs$ ，当一棵子树中离根节点最远的那个节点到根节点的距离都小于所有树中的距离最大的那个的话就可以考虑增加当前子树的根节点与它父亲节点之间的距离了，因为子树中所有节点的距离都需要增大，增大这条边一定是消耗最少的，因为这条边被所有子树上的节点复用了。然后递归下去。&lt;/p&gt;
&lt;p&gt;说起来有点复杂，举个例子吧：&lt;/p&gt;
&lt;p&gt;比如对于：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;5 1&lt;/p&gt;
&lt;p&gt;1 2 1&lt;/p&gt;
&lt;p&gt;1 3 5&lt;/p&gt;
&lt;p&gt;2 4 2&lt;/p&gt;
&lt;p&gt;2 5 3&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;先把图画出来，有三个叶子节点，3 4 5，你会发现 1 到 3 的距离是最远的，因此 4 5 到根节点需要增加，而它们到根节点的路径在 1 2 这条边上重合了，因此我们可以优先在这条边上加，加到子树上有些叶子节点已经达到的最大值。然后向下递归。&lt;/p&gt;
&lt;h2&gt;代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b) for(int i = (a); i &amp;lt;= (b); ++i)
#define per(i, a, b) for(int i = (a); i &amp;gt;= (b); --i)
#define debug(x) cerr &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl;
using namespace std;

typedef long long ll;
const int mod = 1e9+7;
const int MAXN = 5e5 + 7;

vector&amp;lt;pair&amp;lt;int, int&amp;gt; &amp;gt; G[MAXN];
ll dis[MAXN], msd[MAXN], maxdis, ans;
void dfs(int x, int fa) {
    for(auto p: G[x]) {
        int v = p.first, w = p.second;
        if(v == fa) continue;
        dis[v] = dis[x] + w;
        dfs(v, x);
        msd[x] = max(msd[x], msd[v] + w);
    }
}

void dfs2(int x, int fa) {
    for (auto p: G[x]) {
        int v = p.first, w = p.second;
        if(v == fa) continue;
        ans += msd[x] - msd[v] - w;
        dfs2(v, x);
    }
}
int main(int argc, char const *argv[])
{
    int n, rt;
    scanf(&quot;%d %d&quot;, &amp;amp;n, &amp;amp;rt);
    int u, v, w;
    rep(i, 1, n-1){
        scanf(&quot;%d %d %d&quot;, &amp;amp;u, &amp;amp;v, &amp;amp;w);
        G[u].push_back({v, w});
        G[v].push_back({u, w});
    }
    dfs(rt, 0);

    dfs2(rt, 0);
    printf(&quot;%lld\n&quot;, ans);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>洛谷 P1879 玉米田 &amp; P2051 中国象棋</title><link>https://dicer-zz.github.io/posts/luogu-p1879-p2051/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/luogu-p1879-p2051/</guid><pubDate>Wed, 18 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.luogu.org/problem/P2051&quot;&gt;中国象棋&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.luogu.org/problem/P1879&quot;&gt;玉米田Corn Fields&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;两道DP。&lt;/p&gt;
&lt;h2&gt;中国象棋&lt;/h2&gt;
&lt;p&gt;以为是状压DP，但是不知道怎么做。&lt;/p&gt;
&lt;p&gt;换了另一种做法。&lt;/p&gt;
&lt;p&gt;显然，每列不可能有两个以上的炮，设 $f[i][j][k]$ 表示第 $i$ 行有 $j$ 列有一个炮车，有 $k$ 列有两个炮车。那么一个炮车也没有个列数就是 $m-j-k$。&lt;/p&gt;
&lt;p&gt;定义完状态，再来想怎么转移。考虑从 $i-1$ 行向 $i$ 行转移。第 $i$ 行最多只能有两个炮车分情况讨论：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;不放炮车，$f[i][j][k] = f[i-1][j][k]$&lt;/li&gt;
&lt;li&gt;放一个炮车，放在没有跑车的列或者有一个炮车列：$f[i][j+1][k] = f[j-1][j][k] * (m-j-k), f[i][j][k+1] = f[i-1][j][k]*j$&lt;/li&gt;
&lt;li&gt;放两个炮车的情况，讨论一下就好了，看代码吧。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b) for(int i = (a); i &amp;lt;= (b); ++i)
#define per(i, a, b) for(int i = (a); i &amp;gt;= (b); --i)
#define debug(x) cerr &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl;
using namespace std;

typedef long long ll;
const int MOD = 9999973;
const int MAXN = 2e5 + 7;

//第i行有j个单跑k个双炮
ll f[105][105][105];
ll C[105][105];

void init(){
    C[0][0] = 1;
    rep(i, 1, 100){
        rep(j, 0, i){
            C[i][j] = C[i-1][j] + C[i-1][j-1];
        }
    }
}
int main(int argc, char const *argv[])
{
    init();
    int n, m;
    scanf(&quot;%d %d&quot;, &amp;amp;n, &amp;amp;m);
    f[0][0][0] = 1;
    rep(i, 1, n){
        rep(j, 0, m){
            rep(k, 0, m){
                if(j + k &amp;gt; m) continue;
                //不选
                f[i][j][k] += f[i-1][j][k];
                f[i][j][k] %= MOD;
                
                //选一个 选在空列
                f[i][j+1][k] += f[i-1][j][k] * (m - j - k);
                f[i][j+1][k] %= MOD;
                // 选一个 选在单炮列
                f[i][j-1][k+1] += f[i-1][j][k] * j;
                f[i][j-1][k+1] %= MOD;
                
                // 选两个 都选在空列
                f[i][j+2][k] += f[i-1][j][k] * C[m - j - k][2];
                f[i][j+2][k] %= MOD;
                // 选两个 都选在单炮列
                f[i][j-2][k+2] += f[i-1][j][k] * C[j][2];
                f[i][j-2][k+2] %= MOD;
                // 选两个 一个在空列一个在单炮列
                f[i][j][k+1] += f[i-1][j][k] * (m - j - k) * j;
                f[i][j][k+1] %= MOD;
            }
        }
    }
    ll ans = 0;
    rep(i, 0, m) rep(j, 0, m) {
        ans += f[n][i][j];
        ans %= MOD;
    }
    printf(&quot;%lld\n&quot;, ans);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;玉米田&lt;/h2&gt;
&lt;p&gt;状压DP，先保存每一行的状态，在求出有效的状态。&lt;/p&gt;
&lt;p&gt;枚举有效的状态，判断是否和土地状况冲突，不冲突再枚举上一行的状态，如果和当前行不冲突，那么就是有效的转移。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b) for(int i = (a); i &amp;lt;= (b); ++i)
#define per(i, a, b) for(int i = (a); i &amp;gt;= (b); --i)
#define debug(x) cerr &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl;
using namespace std;

typedef long long ll;
const int MOD = 1e9;
const int MAXN = 2e5 + 7;

ll f[15][5005];
bool g[5005];
int mp[15][15];
int field[15];
int main(int argc, char const *argv[])
{
    int n, m;
    scanf(&quot;%d %d&quot;, &amp;amp;n, &amp;amp;m);
    rep(i, 1, n){
        rep(j, 1, m){
            scanf(&quot;%d&quot;, &amp;amp;mp[i][j]);
            field[i] &amp;lt;&amp;lt;= 1;
            field[i] |= mp[i][j];
        }
    }
    //求出可行状态集
    vector&amp;lt;int&amp;gt; sta;
    rep(i, 0, (1&amp;lt;&amp;lt;m)-1) if(!((i &amp;amp; i&amp;gt;&amp;gt;1)||(i &amp;amp; i&amp;lt;&amp;lt;1)))   sta.push_back(i);
    
    f[0][0] = 1;
    rep(i, 1, n) {
        //枚举当前行的状态
        for(int j: sta) {
            //如果和土地状况不冲突
            if((j &amp;amp; field[i]) == j) {
                //枚举上一行的状态
                for(int k: sta) {
                    //如果也不冲突
                    if((j &amp;amp; k) == 0) {
                        //可行的转移
                        f[i][j] += f[i-1][k], f[i][j] %= MOD;
                    }
                }
            }
        }
    }
    ll ans = 0;
    rep(i, 0, (1&amp;lt;&amp;lt;m)-1) {
        ans += f[n][i];
        ans %= MOD;
    }
    printf(&quot;%lld\n&quot;, ans);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2019 ICPC 徐州网赛</title><link>https://dicer-zz.github.io/posts/2019-icpc-xuzhou-online-contest/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/2019-icpc-xuzhou-online-contest/</guid><pubDate>Mon, 09 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jisuanke.com/contest/3005&quot;&gt;2019-icpc-徐州网赛&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;手速场，都是一些很经典的题目。&lt;/p&gt;
&lt;p&gt;重新思考了一下二维偏序究竟是什么。&lt;/p&gt;
&lt;p&gt;&amp;lt;!---more--&amp;gt;&lt;/p&gt;
&lt;h2&gt;A. Who is better?&lt;/h2&gt;
&lt;p&gt;生硬的套了两个知识点，扩展中国剩余定理和Fibonacci博弈。&lt;/p&gt;
&lt;p&gt;套个板子就完事了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def egcd(a, b):
    &quot;&quot;&quot;扩展欧几里得&quot;&quot;&quot;
    if 0 == b:
        return 1, 0, a
    x, y, q = egcd(b, a % b)
    x, y = y, (x - a // b * y)
    return x, y, q
 
n = int(input())
flag = False
 
a1, r1 = map(int, input().split())
 
for _ in range(n-1):
    a2, r2 = map(int, input().split())
    R = r2-r1
    x, y, d = egcd(a1, a2)
    tmp = a2//d
    if R%d != 0:
        flag = True
    r1=((x*R//d)%tmp+tmp)%tmp*a1+r1
    a1=a1*(a2//d)
 
lcm = a1
ans = (r1%lcm+lcm)%lcm

if flag:
    print(&quot;Tankernb!&quot;)
    exit(0)

fac = [1, 2]
cur = 2
while True:
    tmp = fac[cur-1] + fac[cur-2]
    if tmp &amp;gt; ans:
        break
    fac.append(tmp)
    cur += 1

flag = False
for v in fac:
    if v == ans:
        flag = True
        break

if flag:
    print(&quot;Lbnb!&quot;)
else:
    print(&quot;Zgxnb!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;E. XKC&apos;s basketball team&lt;/h2&gt;
&lt;p&gt;一个比较有趣的题目。&lt;/p&gt;
&lt;p&gt;考虑从后往前维护一个单调递增的队列，之所以这样维护是因为：&lt;/p&gt;
&lt;p&gt;当 $i &amp;lt; j$ 且 $a[i] &amp;lt; a[j]$ 时 $a[i]$ 是没有任何用处的，因为它又小又靠前，在它前面的那些值如果比 $a[i]$ 小那么肯定也比 $a[j]$ 小，而且 $i &amp;lt; j$ 所以肯定会选择 $j$ 而不是 $i$ 。&lt;/p&gt;
&lt;p&gt;然后我们二分单调队列就能得到最先进入队列的满足的那个值的位置，最先进入队列的当然是原序列中最靠后的满足要求的值。&lt;/p&gt;
&lt;p&gt;时间复杂度：$O(n \cdot \log n)$&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b) for(int i = (a); i &amp;lt;= (b); ++i)
#define per(i, a, b) for(int i = (a); i &amp;gt;= (b); --i)
using namespace std;

typedef long long ll;
const int mod = 1e9+7;
const int MAXN = 5e5 + 7;

int a[MAXN], ans[MAXN];
int main(int argc, char const *argv[])
{
	int n, m;
	scanf(&quot;%d %d&quot;, &amp;amp;n, &amp;amp;m);
	rep(i, 1, n)	scanf(&quot;%d&quot;, &amp;amp;a[i]);
	vector&amp;lt;int&amp;gt; v, vp;
	v.push_back(a[n]); vp.push_back(n);
	ans[n] = -1;
	per(i, n-1, 1){
		auto it = lower_bound(v.begin(), v.end(), a[i]+m);
		if(it == v.end()){
			ans[i] = -1;
		} else {
			int pos = it - v.begin();
			pos = vp[pos];
			ans[i] = pos - i - 1;
		}
		if(a[i] &amp;gt; v.back()){
			v.push_back(a[i]);
			vp.push_back(i);
		}
	}
	rep(i, 1, n)	printf(&quot;%d%c&quot;, ans[i], i == n?&apos;\n&apos;:&apos; &apos;);
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;I. query&lt;/h2&gt;
&lt;p&gt;又是一道二维偏序的问题。&lt;/p&gt;
&lt;p&gt;偏序关系是全序关系的子集。&lt;/p&gt;
&lt;p&gt;全序关系是指对于一个集合，其中的所有对 $&amp;lt;i, j&amp;gt;$ 都会有这种关系，也就是两两可以比较，比如整数域上的大小关系。&lt;/p&gt;
&lt;p&gt;偏序关系是指对于一个集合，其中存在一些（注意：不是所有）对 $&amp;lt;i, j&amp;gt;$ 有这种关系，也就是不需要两两可以比较，比如整数域上的整除关系。&lt;/p&gt;
&lt;p&gt;偏序关系有严格偏序和非严格偏序两种，各有自己的三条基本性质。&lt;/p&gt;
&lt;p&gt;回到这个问题，$min(p_i, p_j) = \gcd (p_i, p_j)$ 相当于 $p_i, p_j$存在整除关系。显然 $n$ 个数的全排列中满足整除关系的对数小于 $n\cdot \log n$对。&lt;/p&gt;
&lt;p&gt;我们先将所有的满足条件的对数直接暴力筛出来，然后就抽象成了这样一个问题：&lt;/p&gt;
&lt;p&gt;给出一些点，查询在区间 $[l, r]$ 内点的数量。&lt;/p&gt;
&lt;p&gt;这就是一个经典的二位偏序问题了，直接对一维排序，二维用树状数组维护一下就好了。&lt;/p&gt;
&lt;p&gt;时间复杂度：$O(n\cdot \log n)$&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b) for(int i = (a); i &amp;lt;= (b); ++i)
#define per(i, a, b) for(int i = (a); i &amp;gt;= (b); --i)
#define debug(x)	cerr &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl;
using namespace std;

typedef long long ll;
const int mod = 1e9+7;
const int MAXN = 2e5 + 7;

int a[MAXN], id[MAXN];
struct node{
	int l, r, val, id;
	friend bool operator &amp;lt;(const node &amp;amp;a, const node &amp;amp;b){
		if(a.r == b.r)	return a.l &amp;lt; b.l;
		return a.r &amp;lt; b.r;
	}
}s[MAXN];
struct BIT{
	int e[MAXN], n;
	int lowbit(int x){return x&amp;amp;-x;}
	void add(int x, int v){
		while(x &amp;lt;= n){
			e[x] += v;
			x += lowbit(x);
		}
	}
	int get(int x){
		int res = 0;
		while(x){
			res += e[x];
			x -= lowbit(x);
		}
		return res;
	}
}bit;

vector&amp;lt;int&amp;gt; v[MAXN];
int main(int argc, char const *argv[])
{
	int n, m;
	scanf(&quot;%d %d&quot;, &amp;amp;n, &amp;amp;m);
	rep(i, 1, n){
		scanf(&quot;%d&quot;, &amp;amp;a[i]);
		id[a[i]] = i;
	}
	rep(i, 1, n){
		for(int j = 2*a[i]; j &amp;lt;= n; j += a[i]){
			if(id[j] &amp;lt; i)	v[i].push_back(id[j]);
			else v[id[j]].push_back(i);
		}
	}
	rep(i, 1, m){
		scanf(&quot;%d %d&quot;, &amp;amp;s[i].l, &amp;amp;s[i].r);
		s[i].id = i;
	}
	sort(s + 1, s + 1 + m);

	bit.n = n;
	int cur = 1;
	rep(i, 1, n){
		rep(j, 0, (int)v[i].size()-1){
			bit.add(v[i][j], 1);
		}
		while(cur &amp;lt;= m &amp;amp;&amp;amp; s[cur].r == i){
			s[cur].val = bit.get(i) - bit.get(s[cur].l-1);
			cur++;
		}
	}
	sort(s + 1, s + 1 + m, [](const node &amp;amp;a, const node &amp;amp;b){return a.id &amp;lt; b.id;});

	rep(i, 1, m)	printf(&quot;%d\n&quot;, s[i].val);
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;J. Random Access Iterator&lt;/h3&gt;
&lt;p&gt;感觉这个题目属于很简单的树上递推的题目。&lt;/p&gt;
&lt;p&gt;题目的意思就是从树的根结点出发，每次在节点的儿子中随机选择一个，递归，重复子节点个数次。&lt;/p&gt;
&lt;p&gt;我们先一次 $DFS$ 更新出来子树的高度和节点的子节点树，$h[x]$ 和 $son[x]$。&lt;/p&gt;
&lt;p&gt;首先当一个子节点子树的高度加一小于当前节点的高度的时候这个子节点是没用的，因为它更新不到最大值。&lt;/p&gt;
&lt;p&gt;而如果可以的话就加上这个子树的递归求出最大值的概率。叶子节点概率为一。&lt;/p&gt;
&lt;p&gt;形式化来说就是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function DFS(x)
    k &amp;lt;- the number of sons of x
    if  k is 0 do
        return 1
    p &amp;lt;- 0
    for son of x do
        if h[x] equal to h[son] + 1 do 
          p += DFS(son)
    p &amp;lt;- p divided by k
    res &amp;lt;- 1 - pow(1-p, k)
    return res
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后半段的处理是基于这样一个原因：&lt;/p&gt;
&lt;p&gt;假设节点取到最大值的概率为p，那么对于一个节点 $x$ 有：&lt;/p&gt;
&lt;p&gt;$$p[x] = 1 - (1 - \frac{\sum_{son \in sons[x]}p[son]}{|sons[x]|})^{|sons[x]|} $$&lt;/p&gt;
&lt;p&gt;就是这样一个公式。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b) for(ll i = (a); i &amp;lt;= (b); ++i)
#define per(i, a, b) for(ll i = (a); i &amp;gt;= (b); --i)
#define debug(x) cerr &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl;
using namespace std;

typedef long long ll;
const ll MAXN = 1e6 + 7;
const ll mod = 1e9 + 7;
ll qpow(ll a, ll b){
    ll res = 1;
    while(b){
        if(b&amp;amp;1) res = 1ll * res * a % mod;
        a = 1ll * a * a % mod;
        b &amp;gt;&amp;gt;= 1;
    }
    res = res%mod + mod;
    return res%mod;
}

ll inv(ll x){return qpow(x, mod-2);}
vector&amp;lt;ll&amp;gt; G[MAXN];

ll cor[MAXN], son[MAXN];

void dfs(ll x, ll fa = 0){
    ll res = 0;
    for(ll p: G[x]){
        if(p == fa) continue;
        dfs(p, x);
        res = max(res, cor[p]);
        son[x]++;
    }
    cor[x] = res + 1;
}
ll dfs2(ll x, ll fa = 0){
    if(son[x] == 0) return 1;
    ll p = 0;
    for(ll pp: G[x]){
        if(pp == fa)    continue;
        if(cor[x] == cor[pp] + 1){
            p += dfs2(pp, x);
        }
    }
    p = p * inv(son[x]) % mod;
    return (1 - qpow(1-p+mod, son[x]) + mod) % mod;
}
int main() {
    ll n;
    scanf(&quot;%lld&quot;, &amp;amp;n);
    ll u, v;
    rep(i, 1, n-1){
        scanf(&quot;%lld %lld&quot;, &amp;amp;u, &amp;amp;v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1);
    printf(&quot;%lld\n&quot;, dfs2(1));
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2019 ICPC 南京网络赛</title><link>https://dicer-zz.github.io/posts/2019-icpc-nanjing/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/2019-icpc-nanjing/</guid><pubDate>Thu, 05 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jisuanke.com/contest/3004?view=challenges&quot;&gt;计蒜客&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;A. The beautiful values of the palace&lt;/h2&gt;
&lt;p&gt;知识点：坐标映射，离散化，树状数组，离线查询&lt;/p&gt;
&lt;h3&gt;坐标映射&lt;/h3&gt;
&lt;p&gt;找一下规律。（虽然我不太会找这个规律&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ll get_int(int n, int x, int y){
    x = x - n/2 - 1;
    y = y - n/2 - 1;
    ll t = max(abs(x), abs(y));
    if(x &amp;gt;= y)  return 1ll * n * n - 4 * t * t - 2 * t - x - y;
    else return 1ll * n * n - 4 * t * t + 2 * t + x + y;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;离散化和离线查询&lt;/h3&gt;
&lt;p&gt;只离散化一维就行了，当然两个维度都离散化也是没有问题的。&lt;/p&gt;
&lt;p&gt;假设只离散化&lt;code&gt;x&lt;/code&gt;轴，那么我们对建点和查询操作进行&lt;code&gt;x&lt;/code&gt;优先排序，扫描&lt;code&gt;x&lt;/code&gt;轴，当操作的&lt;code&gt;x&lt;/code&gt;与当前枚举的&lt;code&gt;x&lt;/code&gt;相同时进行操作，优先建点，然后查询。这样是为了保证顺序。&lt;/p&gt;
&lt;p&gt;查询的时候，相当于查询一个一维前缀和，用树状数组维护就可以了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b)    for(int i = (a); i &amp;lt;= (int)(b); ++i)
#define per(i, a, b)    for(int i = (a); i &amp;gt;= (int)(b); --i)
#define debug(x)    cerr &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl;
using namespace std;

typedef long long ll;
const int MAXN = 1e5 + 7;
const int MOD = 998244353;

struct node{
    int x, y, id;
    ll val;
    friend bool operator &amp;lt;(const node &amp;amp;a, const node &amp;amp;b){
        return a.x &amp;lt; b.x;
    }
}s[MAXN], need[MAXN * 4];

struct ask{
    int a, b, c, d;
}op[MAXN];

ll get_int(int n, int x, int y){
    x = x - n/2 - 1;
    y = y - n/2 - 1;
    ll t = max(abs(x), abs(y));
    if(x &amp;gt;= y)  return 1ll * n * n - 4 * t * t - 2 * t - x - y;
    else return 1ll * n * n - 4 * t * t + 2 * t + x + y;
}

int get_val(ll val){
    int ans = 0;
    while(val){
        ans += val%10;
        val /= 10;
    }
    return ans;
}
struct BIT {
    int e[MAXN], n;
    void clear(){rep(i, 0, n)    e[i] = 0;}
    int lowbit(int x){return x &amp;amp; (-x);}
    void add(int x, int v){
        while(x &amp;lt;= n){
            e[x] += v;
            x += lowbit(x);
        }
    }
    int get(int x){
        int ans = 0;
        while(x){
            ans += e[x];
            x -= lowbit(x);
        }
        return ans;
    }
}bit;

int hx[MAXN * 10], hy[MAXN * 10];

int main() {
    int T;
    scanf(&quot;%d&quot;, &amp;amp;T);
    while(T--){
        int n, m, p;
        scanf(&quot;%d %d %d&quot;, &amp;amp;n, &amp;amp;m, &amp;amp;p);
        vector&amp;lt;int&amp;gt; vx, vy;
        rep(i, 1, m){
            scanf(&quot;%d %d&quot;, &amp;amp;s[i].x, &amp;amp;s[i].y);
            s[i].val = get_val(get_int(n, s[i].x, s[i].y));
            vx.push_back(s[i].x);
            vy.push_back(s[i].y);
        }
        int x1, y1, x2, y2;
        rep(i, 0, p-1){
            scanf(&quot;%d %d %d %d&quot;, &amp;amp;x1, &amp;amp;y1, &amp;amp;x2, &amp;amp;y2);
            vx.push_back(x1-1);
            vx.push_back(x2);
            vy.push_back(y1-1);
            vy.push_back(y2);
            need[i * 4 + 1] = node{x1-1, y1-1, i * 4 + 1};
            need[i * 4 + 2] = node{x1-1, y2, i * 4 + 2};
            need[i * 4 + 3] = node{x2, y1-1, i * 4 + 3};
            need[i * 4 + 4] = node{x2, y2, i * 4 + 4};
            op[i] = ask{i*4 + 1, i*4 + 2, i*4 + 3, i*4 + 4};
        }
        sort(vx.begin(), vx.end());
        vx.erase(unique(vx.begin(), vx.end()), vx.end());
        sort(vy.begin(), vy.end());
        vy.erase(unique(vy.begin(), vy.end()), vy.end());
        rep(i, 0, vx.size()-1){hx[vx[i]] = i + 1;}
        rep(i, 0, vy.size()-1){hy[vy[i]] = i + 1;}
        rep(i, 1, m){
            s[i].x = hx[s[i].x];
            s[i].y = hy[s[i].y];
        }
        rep(i, 0, p-1){
            need[i * 4 + 1].x = hx[need[i * 4 + 1].x]; need[i * 4 + 1].y = hy[need[i * 4 + 1].y];
            need[i * 4 + 2].x = hx[need[i * 4 + 2].x]; need[i * 4 + 2].y = hy[need[i * 4 + 2].y];
            need[i * 4 + 3].x = hx[need[i * 4 + 3].x]; need[i * 4 + 3].y = hy[need[i * 4 + 3].y];
            need[i * 4 + 4].x = hx[need[i * 4 + 4].x]; need[i * 4 + 4].y = hy[need[i * 4 + 4].y];
        }
        sort(s + 1, s + 1 + m);
        sort(need + 1, need + 1 + 4 * p);
        bit.n = vy.size();
        bit.clear();
        int cur = 1, curs = 1;
        rep(i, 0, vx.size()){
            while(curs &amp;lt;= m &amp;amp;&amp;amp; s[curs].x == i){
                bit.add(s[curs].y, s[curs].val);
                curs++;
            }
            while(cur &amp;lt;= 4 * p &amp;amp;&amp;amp; need[cur].x == i){
                need[cur].val = bit.get(need[cur].y);
                cur++;
            }
        }
        sort(need + 1, need + 1 + 4 * p, [](const node &amp;amp;a, const node &amp;amp;b){return a.id &amp;lt; b.id;});
        rep(i, 0, p-1){
            printf(&quot;%lld\n&quot;, need[op[i].a].val + need[op[i].d].val - need[op[i].b].val - need[op[i].c].val);
        }
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;B. super_log&lt;/h2&gt;
&lt;p&gt;知识点：递归，欧拉函数，欧拉定理&lt;/p&gt;
&lt;h3&gt;扩展欧拉定理&lt;/h3&gt;
&lt;p&gt;$$a^b \equiv \begin{cases} a^b &amp;amp; b &amp;lt; \varphi(m) \ a^{b \bmod \varphi(m) + \varphi(m)} &amp;amp; b \ge \varphi(m) \end{cases}\bmod m$$&lt;/p&gt;
&lt;h3&gt;快速幂&lt;/h3&gt;
&lt;p&gt;也不知道为什么，快速幂写成这样就可以直接递归求解了。&lt;/p&gt;
&lt;p&gt;这其中一定隐藏了什么不为人知的数论之谜。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int up(ll a, int mod){
    return a &amp;lt; mod ? a : a % mod + mod;
}
int qpow(int a, int b, int mod){
    int res = 1;
    while(b){
        if(b&amp;amp;1) res = up(1ll * res * a, mod);
        a = up(1ll * a * a, mod);
        b &amp;gt;&amp;gt;= 1;
    }
    return res;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b) for(int i = (a); i &amp;lt;= (b); ++i)
#define per(i, a, b) for(int i = (a); i &amp;gt;= (b); --i)
using namespace std;

typedef long long ll;
const int MAXN = 1e6 + 7;

int pri[MAXN], tot;
int phi[MAXN];
bool mark[MAXN];
void init(){
    tot = 0;
    phi[1] = 1;
    for(int i = 2; i &amp;lt;= 1000000; ++i){
        if(!mark[i]){
            phi[i] = i - 1; 
            pri[++tot] = i;
        }
        for(int j = 1; j &amp;lt;= tot; ++j){
            int x = pri[j];
            if(i * x &amp;gt; 1000000)    break;
            mark[i * x] = 1;
            if(i%x == 0){
                phi[i*x] = phi[i] * x;
                break;
            } else {
                phi[i*x] = phi[i] * phi[x];
            }
        }
    }
}

int up(ll a, int mod){
	return a &amp;lt; mod ? a : a % mod + mod;
}
int qpow(int a, int b, int mod){
	int res = 1;
	while(b){
		if(b&amp;amp;1)	res = up(1ll * res * a, mod);
		a = up(1ll * a * a, mod);
		b &amp;gt;&amp;gt;= 1;
	}
	return res;
}

int solve(int a, int b, int m){
	//calculate a^a^...^a mod m
	//			|&amp;lt;- b -&amp;gt;|
	if(!b || m == 1)	return 1;
	return qpow(a, solve(a, b-1, phi[m]), m);
}

int main()
{
	init();
	int T, a, b, m;
	scanf(&quot;%d&quot;, &amp;amp;T);
	while(T--){
		scanf(&quot;%d %d %d&quot;, &amp;amp;a, &amp;amp;b, &amp;amp;m);
		printf(&quot;%d\n&quot;, solve(a, b, m) % m);
	}
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>TJOI 2013 松鼠聚会</title><link>https://dicer-zz.github.io/posts/tjoi2013-songshujuhui/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/tjoi2013-songshujuhui/</guid><pubDate>Thu, 05 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.luogu.org/problem/P3964&quot;&gt;P3964 [TJOI2013]松鼠聚会&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;关于曼哈顿坐标系和切比雪夫坐标系之间的相互转换。&lt;/p&gt;
&lt;h2&gt;前置技能&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;曼哈顿坐标系是通过切比雪夫坐标系旋转$45 ^\circ $后，再缩小到原来的一半得到的。&lt;/li&gt;
&lt;li&gt;将一个点 $(x, y)$ 的坐标变为 $(x+y, x-y)$ 后，原坐标系中的曼哈顿距离等于新坐标系中的切比雪夫距离&lt;/li&gt;
&lt;li&gt;将一个点 $(x, y)$ 的坐标变为 $(\frac{x+y}{2}, \frac{x-y}{2})$ 后，原坐标系中的切比雪夫距离等于新坐标系中的曼哈顿距离&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;分析&lt;/h2&gt;
&lt;p&gt;首先，原题中定义的距离是切比雪夫距离，看着非常奇怪。&lt;/p&gt;
&lt;p&gt;我们先考虑如果是曼哈顿距离怎么做。&lt;/p&gt;
&lt;p&gt;考虑一些点： $$(x_1, y_1), (x_2, y_2), ... , (x_n, y_n)$$&lt;/p&gt;
&lt;p&gt;只需求出： $$\min_{j=1}^{n}\sum_{i=1}^{n}|x_i-x_j|+|y_i-y_j|$$。&lt;/p&gt;
&lt;p&gt;而： $$\sum_{i=1}^{n}|x_i-x_j|+|y_i-y_j| = \sum_{i=1}^{n}|x_i-x_j| + \sum_{i=1}^{n}|y_i-y_j|$$&lt;/p&gt;
&lt;p&gt;发现这两个维度互不影响，可以分别求出 $x, y$ 轴的。&lt;/p&gt;
&lt;p&gt;先对 $x$ 从小到大排序，设 $pre_i$ 前 $i-1$ 个点到点 $i$ 的 $x$ 轴距离前缀和，即： $$pre_i = \sum_{j=1}^{i-1}|x_i - x_j|$$&lt;/p&gt;
&lt;p&gt;同理做后缀和： $$suf_i = \sum_{j=i+1}^{n}|x_i - x_j|$$&lt;/p&gt;
&lt;p&gt;则： $$\sum_{i=1}^{n}|x_i-x_j| = pre_i + suf_i$$&lt;/p&gt;
&lt;p&gt;$y$ 轴同理。&lt;/p&gt;
&lt;p&gt;所以只需要先将原切比雪夫坐标系转化为曼哈顿左边系就行了。&lt;/p&gt;
&lt;p&gt;注意转化的时候先不要除 $2$，最后在除 $2$，可以防止产生小数。&lt;/p&gt;
&lt;h2&gt;代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b) for(int i = (a); i &amp;lt;= (b); ++i)
#define per(i, a, b) for(int i = (a); i &amp;gt;= (b); --i)
using namespace std;

typedef long long ll;
const int MAXN = 2e5 + 7;

ll suf[MAXN], pre[MAXN];

struct node{
    int x, y, id;
    ll ansx, ansy;
}s[MAXN];

int main() {
    int n, a, b;
    scanf(&quot;%d&quot;, &amp;amp;n);
    rep(i, 1, n){
        scanf(&quot;%d %d&quot;, &amp;amp;a, &amp;amp;b);
        s[i].x = a + b;
        s[i].y = a - b;
        s[i].id = i;
    }
    
    sort(s + 1, s + 1 + n, [](const node &amp;amp;a, const node &amp;amp;b){return a.x &amp;lt; b.x;});

    suf[1] = 0; pre[n] = 0;
    rep(i, 2, n)    suf[i] = suf[i-1] + 1ll * (i-1) * (s[i].x - s[i-1].x);
    per(i, n-1, 1)    pre[i] = pre[i+1] + 1ll * (n-i) * (s[i+1].x - s[i].x);
    rep(i, 1, n)    s[i].ansx = suf[i] + pre[i];
    
    sort(s + 1, s + 1 + n, [](const node &amp;amp;a, const node &amp;amp;b){return a.y &amp;lt; b.y;});
    
    suf[1] = 0; pre[n] = 0;
    rep(i, 2, n)    suf[i] = suf[i-1] + 1ll * (i-1) * (s[i].y - s[i-1].y);
    per(i, n-1, 1)    pre[i] = pre[i+1] + 1ll * (n-i) * (s[i+1].y - s[i].y);
    rep(i, 1, n)    s[i].ansy = suf[i] + pre[i];

    ll ans = 1ll&amp;lt;&amp;lt;60;
    rep(i, 1, n)    ans = min(ans, s[i].ansx + s[i].ansy);
    printf(&quot;%lld\n&quot;, ans/2);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Codeforces 1207F Remainder Problem</title><link>https://dicer-zz.github.io/posts/codeforces-1207-f/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/codeforces-1207-f/</guid><pubDate>Thu, 29 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题目&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://codeforces.com/contest/1207/problem/F&quot;&gt;题目链接&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;分析&lt;/h2&gt;
&lt;p&gt;以前从来没有做过这种问法的题目，觉得很有意思。&lt;/p&gt;
&lt;p&gt;本来想的是可不可以把操作转化一下改到线段树上去，结果没有想到，好像也真的不行。&lt;/p&gt;
&lt;p&gt;然后就灵光一闪发现当询问的 $x$ ，比较大的时候暴力查询也是跑的飞快，于是就开始想大数据暴力查询。&lt;/p&gt;
&lt;p&gt;小数据可以发现直接维护一个二维数组 $sum[x][y]$ 表示答案。&lt;/p&gt;
&lt;p&gt;然后大小的分隔本来以为就是 $sqrt(5 \cdot 10^5)$ ，但其实并不是，因为这道题目并不是传统意义上的&lt;strong&gt;分块&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;而是一种用空间换时间的方法，当 $x$ 比较大的时候我们很难去更新 $sum[x][y]$，也开不了那么大的空间。&lt;/p&gt;
&lt;p&gt;思考一下，假如分割点是 $p$，那么对于更新操作来说，因为我们需要更新所有的 $sum[x][y]$ 以便查询小数据。&lt;/p&gt;
&lt;p&gt;所以更新的复杂度就是：$O(p)$。&lt;/p&gt;
&lt;p&gt;对于查询操作来说：如果查询的 $x \leq p$ ，那么我们可以直接输出答案也就是 $O(1)$。&lt;/p&gt;
&lt;p&gt;如果查询的 $ x &amp;gt; p$，那么我们可以暴力查询，复杂度是：$O(\frac{5\cdot 10^5}{x})$。具体复杂度是：$\Omega(\frac{5\cdot 10^5}{p}) - O(1)$。&lt;/p&gt;
&lt;p&gt;分析一下 $p$ 取 300~1000 都是可以的。&lt;/p&gt;
&lt;h2&gt;代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b) for(int i = (a); i &amp;lt;= (b); ++i)
#define per(i, a, b) for(int i = (a); i &amp;gt;= (b); --i)
using namespace std;

typedef long long ll;
const int mod = 1e9+7;
const int MAXN = 5e5 + 7;

int a[MAXN];
int sum[1001][1001];
int main(int argc, char const *argv[])
{
	int q;
	scanf(&quot;%d&quot;, &amp;amp;q);
	int op, x, y;
	while(q--){
		scanf(&quot;%d %d %d&quot;, &amp;amp;op, &amp;amp;x, &amp;amp;y);
		if(op == 1){
			a[x] += y;
			for(int i = 1; i &amp;lt;= 1000; ++i){
				sum[i][x%i] += y;
			}
		} else {
			if(x &amp;lt;= 1000){
				printf(&quot;%d\n&quot;, sum[x][y]);
			} else {
				int res = 0;
				while(y &amp;lt;= 500000){
					res += a[y];
					y += x;
				}
				printf(&quot;%d\n&quot;, res);
			}
		}
	}
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>CODEVS 2370 小机房的树</title><link>https://dicer-zz.github.io/posts/codevs-2370-%E5%B0%8F%E6%9C%BA%E6%88%BF%E7%9A%84%E6%A0%91/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/codevs-2370-%E5%B0%8F%E6%9C%BA%E6%88%BF%E7%9A%84%E6%A0%91/</guid><pubDate>Tue, 06 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;codevs.cn/problem/2370/&quot;&gt;CODEVS-2370-小机房的树&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;一道经典的LCA问题。&lt;/p&gt;
&lt;h2&gt;题解&lt;/h2&gt;
&lt;p&gt;题目就是求$u,v$两点之间的最短距离，利用$lca$的性质可以在$O(log(n))$的时间内求出答案。&lt;/p&gt;
&lt;p&gt;也就是$dis[u] + dis[v] - 2 * dis[lca(u, v)]$，其中 $dis[u]$ 数组表示从根节点到点 $u$ 的距离。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b)    for(int i = (a); i &amp;lt;= (int)(b); ++i)
#define per(i, a, b)    for(int i = (a); i &amp;gt;= (int)(b); --i)
#define debug(x)    cerr &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl;
using namespace std;

typedef long long ll;
typedef pair&amp;lt;int, int&amp;gt; pii;
const int MAXN = 2e5 + 7;

int dep[MAXN], dis[MAXN], f[MAXN][20];
vector&amp;lt;pii&amp;gt; G[MAXN];

void dfs(int x, int last = 0, int fa = -1) {
    f[x][0] = fa;
    dep[x] = dep[fa] + 1;
    dis[x] = dis[fa] + last;
    for(int i = 1; (1&amp;lt;&amp;lt;i) &amp;lt;= dep[x]; ++i){
        f[x][i] = f[f[x][i-1]][i-1];
    }
    rep(i, 0, G[x].size()-1){
        pii p = G[x][i];
        if(p.first == fa)   continue;
        dfs(p.first, p.second, x);
    }
}

int lca(int u, int v) {
    if(dep[u] &amp;lt; dep[v]) swap(u, v);
    int dif = dep[u] - dep[v];
    per(i, 20, 0){
        if((1&amp;lt;&amp;lt;i) &amp;lt;= dif){
            dif -= (1&amp;lt;&amp;lt;i);
            u = f[u][i];
        }
    }
    if(u == v)  return u;
    per(i, 20, 0){
        if(dep[u] &amp;gt;= (1&amp;lt;&amp;lt;i) &amp;amp;&amp;amp; f[u][i] != f[v][i]){
            u = f[u][i];
            v = f[v][i];
        }
    }
    return f[u][0];
}
int main() {
    int n;
    scanf(&quot;%d&quot;, &amp;amp;n);
    int u, v, c;
    rep(i, 1, n-1){
        scanf(&quot;%d %d %d&quot;, &amp;amp;u, &amp;amp;v, &amp;amp;c);
        G[u].push_back(make_pair(v, c));
        G[v].push_back(make_pair(u, c));
    }
    dfs(0);
    int T;
    scanf(&quot;%d&quot;, &amp;amp;T);
    while(T--){
        scanf(&quot;%d %d&quot;, &amp;amp;u, &amp;amp;v);
        printf(&quot;%d\n&quot;, dis[u] + dis[v] - 2 * dis[lca(u, v)]);
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>BZOJ 2956 模积和</title><link>https://dicer-zz.github.io/posts/bzoj-2956/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/bzoj-2956/</guid><pubDate>Tue, 06 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.lydsy.com/JudgeOnline/problem.php?id=2956&quot;&gt;BZOJ-2056&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;一道有点复杂的数论分块。&lt;/p&gt;
&lt;h2&gt;公式推导&lt;/h2&gt;
&lt;p&gt;所求即为：&lt;/p&gt;
&lt;p&gt;$$\sum_{i=1}^{n}\sum_{j=1}^{m}{(n \bmod i )\cdot (m\bmod j)(i \neq j)}$$&lt;/p&gt;
&lt;p&gt;先不管$i\neq j$的情况:
$$\sum_{i=1}^{n}(\sum_{j=1}^{m}{(n\bmod i) \cdot (m\bmod j)})$$&lt;/p&gt;
&lt;p&gt;$$\sum_{i=1}^{n}\sum_{j=1}^{m}{(n-i\lfloor \frac{n}{i} \rfloor)(m-j\lfloor \frac{m}{j} \rfloor)}$$&lt;/p&gt;
&lt;p&gt;$$\sum_{i=1}^{n}\sum_{j=1}^{m}{(n m - n\cdot j\lfloor \frac{m}{j} \rfloor - m\cdot i\lfloor \frac{n}{i} \rfloor + ij\lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{j} \rfloor)}$$&lt;/p&gt;
&lt;p&gt;$$n^2m^2 - n^2\sum_{j=1}^{m}{j\lfloor \frac{m}{j} \rfloor} - m^2\sum_{i=1}^{n}i\lfloor \frac{n}{i} \rfloor + \sum_{i=1}^{n}i\lfloor \frac{n}{i} \rfloor\sum_{i=1}^{m}j\lfloor \frac{m}{j} \rfloor$$&lt;/p&gt;
&lt;p&gt;然后在讨论$i = j$的情况:&lt;/p&gt;
&lt;p&gt;$$\sum_{i=1}^{min(n, m)}{(n\bmod i)\cdot (m\bmod i)}$$&lt;/p&gt;
&lt;p&gt;$$\sum_{i=1}^{min(n, m)}{(n - i\lfloor \frac{n}{i} \rfloor)(m - i\lfloor \frac{m}{i} \rfloor)} $$&lt;/p&gt;
&lt;p&gt;$$\sum_{i=1}^{min(n, m)}{n m - m\cdot i\lfloor \frac{n}{i} \rfloor - n\cdot i\lfloor \frac{m}{i} \rfloor + i^2 \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor} $$&lt;/p&gt;
&lt;p&gt;然后就可以愉快的分块了。&lt;/p&gt;
&lt;h2&gt;代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;/**************************************************************
    Problem: 2956
    User: Dicer
    Language: C++
    Result: Accepted
    Time:184 ms
    Memory:1292 kb
****************************************************************/
 
#include&amp;lt;bits/stdc++.h&amp;gt;
#define rep(i, a, b)    for(int i = (a); i &amp;lt;= (int)(b); ++i)
#define per(i, a, b)    for(int i = (a); i &amp;gt;= (int)(b); --i)
#define debug(x)    cout &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; x &amp;lt;&amp;lt; endl;
using namespace std;
 
typedef long long ll;
const int MAXN = 1e5 + 7;
const int MOD = 19940417;
const int INV = 3323403;
 
ll cal(ll n){
    ll ans = 0;
    for(ll i = 1, j; i &amp;lt;= n; i = j + 1){
        j = n / ( n / i);
        ans += (i + j) * (j - i + 1) / 2 * (n / i);
        ans %= MOD;
    }
    return ans;
}
ll cal(ll n, ll k){
    ll ans = 0;
    for(ll i = 1, j; i &amp;lt;= k; i = j + 1){
        j = min(k, n / ( n / i));
        ans += (i + j) * (j - i + 1) / 2 * (n / i);
        ans %= MOD;
    }
    return ans;
}
 
ll sum(ll n){
    return n * (n + 1) % MOD * (2 * n + 1) % MOD * INV % MOD;
}
 
ll cal(ll n, ll m, ll k){
    ll ans = 0;
    for(ll i = 1, j; i &amp;lt;= k; i = j + 1){
        j = min(k, min(n / (n / i), m / (m / i)));
        ans += (sum(j) - sum(i-1)) * (n/i) % MOD * (m/i) % MOD;
        ans %= MOD;
    }
    return ans;
}
 
ll mul(ll a, ll b){
    a %= MOD; b %= MOD;
    return a * b % MOD;
}
 
int main(){
    ll n, m, k;
    scanf(&quot;%lld%lld&quot;, &amp;amp;n, &amp;amp;m);
    k = min(n, m);
    long long ans = 0;
    ans += mul(n * n, m * m);
    ans -= mul(n * n, cal(m));
    ans -= mul(m * m, cal(n));
    ans += mul(cal(n), cal(m));
    ans = ((ans % MOD) + MOD) % MOD;
    ans -= mul(k, n * m);
    ans += mul(n, cal(m, k));
    ans += mul(m, cal(n, k));
    ans -= cal(n, m, k);
    ans = ((ans % MOD) + MOD) % MOD;
    printf(&quot;%lld\n&quot;, ans);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>拉格朗日乘数法</title><link>https://dicer-zz.github.io/posts/lagrange-multiplier/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/lagrange-multiplier/</guid><pubDate>Fri, 19 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;简单来说，拉格朗日乘子法可以解决$f(\hat x)$在一些限制条件$g_k(\hat x) = c_k$下的极值。&lt;/p&gt;
&lt;h2&gt;Mushroom Scientists&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://codeforces.com/contest/186/problem/D&quot;&gt;Link&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Prove&lt;/h3&gt;
&lt;p&gt;本题题意就是给了一个函数$f(x,y,z) = x^a + y^b + z^c$，求这个函数在约束条件$g(x,y,z) = x + y + z - S$下的最大值。&lt;/p&gt;
&lt;p&gt;首先，设拉格朗日函数：&lt;/p&gt;
&lt;p&gt;$$L(x,y,z,\lambda) = f - \lambda \cdot g$$&lt;/p&gt;
&lt;p&gt;然后对各个变量求偏导数：&lt;/p&gt;
&lt;p&gt;$$\frac{\partial L}{\partial x} = y^b\cdot z^c \cdot ax^{a-1} - \lambda$$&lt;/p&gt;
&lt;p&gt;$$\frac{\partial L}{\partial y} = x^a\cdot z^c \cdot by^{b-1} - \lambda$$&lt;/p&gt;
&lt;p&gt;$$\frac{\partial L}{\partial z} = x^a\cdot y^b \cdot cz^{c-1} - \lambda$$&lt;/p&gt;
&lt;p&gt;$$\frac{\partial L}{\partial \lambda} = x + y + z - S$$&lt;/p&gt;
&lt;p&gt;可以解出：&lt;/p&gt;
&lt;p&gt;$$\frac{x}{a} = \frac{y}{b} = \frac{z}{c}$$&lt;/p&gt;
&lt;p&gt;所以：&lt;/p&gt;
&lt;p&gt;$$x = \frac{S\cdot a}{a+b+c}, y = \frac{S\cdot b}{a+b+c}, z = \frac{S\cdot c}{a+b+c}$$&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;iostream&amp;gt;
#include&amp;lt;cstdio&amp;gt;
#include&amp;lt;vector&amp;gt;
#include&amp;lt;iomanip&amp;gt;
using namespace std;
 
const int MAXN = 1e5 + 7;
const int mod = 1e9 + 7;
typedef long long ll;
 
int main(){
    cout &amp;lt;&amp;lt; fixed;
    int s;
    int a, b, c;
    cin &amp;gt;&amp;gt; s &amp;gt;&amp;gt; a &amp;gt;&amp;gt; b &amp;gt;&amp;gt; c;
    if(a + b + c == 0){
        cout &amp;lt;&amp;lt; 0 &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; 0 &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; 0 &amp;lt;&amp;lt; endl;
    } else {
        cout &amp;lt;&amp;lt; setprecision(12) &amp;lt;&amp;lt; (1.0*s*a/(a+b+c)) &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; (1.0*s*b/(a+b+c)) &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; (1.0*s*c/(a+b+c)) &amp;lt;&amp;lt; endl;
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Samantha and Portfolio Management&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.hackerrank.com/contests/morgan-stanley-2016/challenges/samantha-and-portfolio-management&quot;&gt;Link&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Prove&lt;/h3&gt;
&lt;p&gt;约束条件下的极值问题，可以用拉格朗日乘子法解决。&lt;/p&gt;
&lt;p&gt;约束条件：
$$g(\boldsymbol w) = \sum_{i=1}^{n}{w_i} - 1$$&lt;/p&gt;
&lt;p&gt;极值方程：&lt;/p&gt;
&lt;p&gt;$$f(\boldsymbol w) = \sum_{i=1}^{n}{(w_i^2 \cdot \sigma_i^2)} = \sum_{i=1}^{n}(w_i^2 \cdot \frac{1}{i})$$&lt;/p&gt;
&lt;p&gt;拉格朗日方程：&lt;/p&gt;
&lt;p&gt;$$L(\boldsymbol w, \lambda) = f(\boldsymbol w) - \lambda \cdot g(\boldsymbol w)$$&lt;/p&gt;
&lt;p&gt;分别对 $\boldsymbol w , \lambda$ 求导：&lt;/p&gt;
&lt;p&gt;$$\frac{\partial(L)}{\partial(w_i)} = 2 \frac{w_i}{i} - \lambda$$&lt;/p&gt;
&lt;p&gt;$$\frac{\partial(L)}{\partial(\lambda)} = g(\boldsymbol{w}) = 1 - \sum_{i=1}^{n}{w_i}$$&lt;/p&gt;
&lt;p&gt;则有：&lt;/p&gt;
&lt;p&gt;$$w_i = \frac{\lambda \cdot i}{2}$$&lt;/p&gt;
&lt;p&gt;移项：&lt;/p&gt;
&lt;p&gt;$$\sum_{i=1}^{n}{w_i} = \frac{\lambda \cdot \sum_{i=1}^{n}(i)}{2} = \frac
{\lambda \cdot n(n+1)}{4} = 1$$&lt;/p&gt;
&lt;p&gt;则：
$$\lambda = \frac{4}{n(n+1)}$$&lt;/p&gt;
&lt;p&gt;$$w_i = \frac{\lambda \cdot i}{2} = \frac{2\cdot i}{n(n+1)}$$&lt;/p&gt;
&lt;p&gt;所以：&lt;/p&gt;
&lt;p&gt;$$V = \sum_{i=1}^{n}{w_i^2\times \sigma_i^2 } = \sum_{i=1}^{n}{\frac{4\cdot i}{n^2(n+1)^2}} = \frac{1}{n(n+1)}$$&lt;/p&gt;
&lt;p&gt;$$E = \sum_{i=1}^{n}{w_i \times \bar r_i} = \sum_{i=1}^{n}\frac{(2\cdot i \cdot \bar r_i)}{n(n+1)}$$&lt;/p&gt;
&lt;p&gt;然后就可以愉快的$O(n)$解决了。&lt;/p&gt;
&lt;h3&gt;代码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;

const int MAXN = 1e5 + 7;
int r[MAXN];

void print(long long a, long long b) {
    long long g = __gcd(a, b);
    a /= g; b /= g;
    printf(&quot;%d %d\n&quot;, a, b);
}
int main(){
    int n;
    scanf(&quot;%d&quot;, &amp;amp;n);
    long long sum = 0;
    for(int i = 1; i &amp;lt;= n; ++i) {
        scanf(&quot;%d&quot;, r+i);
        sum += 1LL * i * r[i];
    }
    print(2 * sum, 1LL * n * (n+1));
    print(2LL, n * (n+1));
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;参考&lt;/h1&gt;
&lt;p&gt;1.&lt;a href=&quot;https://en.wikipedia.org/wiki/Lagrange_multiplier&quot;&gt;Wikipedia-Lagrange-Multiplier&lt;/a&gt;
2.http://jermmy.xyz/2017/07/27/2017-7-27-understand-lagrange-multiplier/&lt;/p&gt;
</content:encoded></item><item><title>使用 HTTP 协议实现内网文件传输</title><link>https://dicer-zz.github.io/posts/transfer-file-use-http/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/transfer-file-use-http/</guid><pubDate>Tue, 25 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;需求&lt;/h1&gt;
&lt;p&gt;虽然说现在各种网盘、QQ，已经可以满足我们日常基本的小文件传输的需要了，但是当文件比较大的时候还是很慢，因此我们需要一种更快的，更简洁的传输方式来做&lt;strong&gt;局域网&lt;/strong&gt;内的文件传输。&lt;/p&gt;
&lt;h1&gt;解决方案&lt;/h1&gt;
&lt;p&gt;考虑使用本地的一台主机做服务器，为其他同局域网内的设备（如：手机、平板电脑）提供文件传输服务，使用&lt;code&gt;http&lt;/code&gt;协议。&lt;/p&gt;
&lt;h2&gt;工具&lt;/h2&gt;
&lt;p&gt;MobaXterm、python3。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/download/MobaXterm.exe&quot;&gt;下载MobaXterm（Windows）&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;步骤&lt;/h2&gt;
&lt;h3&gt;使用MobaXterm&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;打开Xterm的Servers，选择http。&lt;/li&gt;
&lt;li&gt;设置端口号、文件所在地址和开启时间。&lt;/li&gt;
&lt;li&gt;开启服务。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/gallery/others/mobaxterm-http1.png&quot; alt=&quot;Step1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/gallery/others/mobaxterm-http2.png&quot; alt=&quot;Step2&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;使用Python&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;打开cmd&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;输入&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ python -m http.server	# python3&lt;/p&gt;
&lt;p&gt;$ python -m SimpleHTTPServers	# python2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/gallery/others/python2-http.png&quot; alt=&quot;python2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/gallery/others/python3-http.png&quot; alt=&quot;python3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果想要选择端口号的话，可以直接在后面添加。&lt;/p&gt;
&lt;h4&gt;Tip&lt;/h4&gt;
&lt;p&gt;python方法对中文路径很不友好，不建议使用中文文件名，可能会访问不了。MobaXterm没有问题，中文文件也可以正常访问。&lt;/p&gt;
&lt;h2&gt;浏览器访问&lt;/h2&gt;
&lt;p&gt;经过上述步骤，你已经可以在其他设备上访问你服务器上的文件了。&lt;/p&gt;
&lt;p&gt;在浏览器输入你服务器的本地IP即可（如果不是80端口，则需要自己手动输入）。&lt;/p&gt;
&lt;p&gt;经测试，网页直接下载速度约为30Mb/s。&lt;/p&gt;
&lt;p&gt;另外，意想不到的一点是，视频文件（MP4）可以直接播放，而且丝毫没有卡顿，因此，我们还可以把它当作视频服务器，下载到电脑上的视频可以在手机📱、平板上看啦。&lt;/p&gt;
</content:encoded></item><item><title>Codeforces 568G1 First-Come-First-Served</title><link>https://dicer-zz.github.io/posts/codeforces-568-g1/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/codeforces-568-g1/</guid><pubDate>Mon, 24 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://hpuoj.com/problem/74/&quot;&gt;hpuoj-74&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codeforces.com/contest/1185/problem/G1&quot;&gt;codeforces-568-G1&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;题解&lt;/h1&gt;
&lt;p&gt;两个题目都是状压DP，有一定的相似之处，放在一起写。&lt;/p&gt;
&lt;h2&gt;第一题&lt;/h2&gt;
&lt;p&gt;显然，对于每个宿舍楼内部的宿舍来说，当顺序固定了以后，可以简单的通过贪心得到最少抗议的人数。&lt;/p&gt;
&lt;p&gt;那么，我们的主要问题就变成了，求解宿舍楼安装空调最合适的顺序。&lt;/p&gt;
&lt;p&gt;定义$dp[sta]$表示状态为$sta$时的最少抗议人数，$cost[i][j]$表示第$i$幢寝室楼第$j$个空调的贪心值。&lt;/p&gt;
&lt;p&gt;那么我们可以这样更新：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for sta := 0 to (1&amp;lt;&amp;lt;n)-1 do
	cal = sta.count(1)
	for  nex := 0 to n-1 do
		if (sta&amp;gt;&amp;gt;nex)&amp;amp;1
			then continue
		else
			dp[sta|1&amp;lt;&amp;lt;nex] = min(dp[sta|1&amp;lt;&amp;lt;nex], dp[sta] + cost[nex][cal+1])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果就是$dp[(1&amp;lt;&amp;lt;n)-1] $。&lt;/p&gt;
&lt;p&gt;这样更新的理由是：&lt;/p&gt;
&lt;p&gt;对于一个状态$sta$，用1表示宿舍楼已经被安排过位置了，0表示还没有安排，统计一下1的个数就能知道在这个状态下，下一个宿舍楼是第几个被装的，而且这个宿舍楼之前一定是没有装过。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;p&gt;$n = 4, sta = 1001$，下一个要装第三个宿舍楼，而二号、三号宿舍楼还没有装。&lt;/p&gt;
&lt;p&gt;因此可以更新到$sta = 1101, 1011$这两个状态。&lt;/p&gt;
&lt;h2&gt;第二题&lt;/h2&gt;
&lt;h3&gt;方法一&lt;/h3&gt;
&lt;p&gt;定义$dp[cap][sta][last]$表示当前总和为$cap$，状态为$sta$，最后一首歌的类型为$last$，然后直接维护更新就好了。&lt;/p&gt;
&lt;p&gt;时间复杂度：O(T*n*2^n)&lt;/p&gt;
&lt;h3&gt;方法二&lt;/h3&gt;
&lt;p&gt;定义$dp[sta][last]$表示当前状态为$sta$，最后一首歌的类型为$last$，进行一下更新：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for sta := 0 to (1&amp;lt;&amp;lt;n)-1 do
	for i := 1 to n do
		if (sta&amp;gt;&amp;gt;i)&amp;amp;1
			then continue
		for type := 1 to 3 do
			if type == last[i]
				then continue
			dp[sta|1&amp;lt;&amp;lt;(i-1)][last[i]] += dp[sta][type]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就能得到所有的情况，但是不一定每一种情况的$cap$都是$T$，所以我们在求和的时候进行判断：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sum = 0
for sta := 0 to (1&amp;lt;&amp;lt;n)-1 do
	cap = 0
	for i := 1 to n do
		if (sta&amp;gt;&amp;gt;i)&amp;amp;1
			then	cap += t[i]
	if cap == T
		then
			for type := 1 to 3 do
				sum += dp[sta][type]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间复杂度：O(n*2^n)&lt;/p&gt;
&lt;h1&gt;代码&lt;/h1&gt;
&lt;h2&gt;第一题&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
typedef long long ll;
typedef pair&amp;lt;int, int &amp;gt; pii;
inline ll qpow(ll a,ll b,ll mod){ll res=1;while(b){if(b&amp;amp;1)res = (res*a)%mod;a=(a*a)%mod;b&amp;gt;&amp;gt;=1;}return res;}
const int mod = 1e9 + 7;
const int MAXN = 2e5 + 7;

int dp[1&amp;lt;&amp;lt;23];
int f[23][2333];
int a[23][23];

int main(){
    #ifndef ONLINE_JUDGE         
        freopen(&quot;in.txt&quot;, &quot;r&quot;, stdin);
        freopen(&quot;out.txt&quot;, &quot;w&quot;, stdout);
    #endif 
    
    int n, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    for(int i = 1; i &amp;lt;= n; ++i){
        for(int j = 1; j &amp;lt;= m; ++j){
            cin &amp;gt;&amp;gt; f[i][j];
        }
        sort(f[i]+1, f[i]+1+m);
    }
    for(int i = 1; i &amp;lt;= n; ++i){
        for(int j = 1; j &amp;lt;= n; ++j){
            int cur = (j-1)*m + 1, res = 0;
            for(int k = 1; k &amp;lt;= m; ++k){
                if(f[i][k] &amp;gt;= cur){
                    cur++;
                } else res++;
            }
            a[i][j] = res;
        }
    }
    memset(dp, 0x3f, sizeof dp);
    dp[0] = 0;
    function&amp;lt;int(int)&amp;gt; cal = [&amp;amp;](int x){
        int res = 0;
        while(x){
            res += x&amp;amp;1;
            x &amp;gt;&amp;gt;= 1;
        }
        return res;
    };
    for(int i = 0; i &amp;lt; (1&amp;lt;&amp;lt;n); ++i){
        int cur = cal(i) + 1;
        for(int j = 0; j &amp;lt; n; ++j){
            if((i&amp;gt;&amp;gt;j)&amp;amp;1)    continue;
            dp[i|1&amp;lt;&amp;lt;j] = min(dp[i|1&amp;lt;&amp;lt;j], dp[i] + a[j+1][cur]);
        }
    }
    cout &amp;lt;&amp;lt; dp[(1&amp;lt;&amp;lt;n)-1] &amp;lt;&amp;lt; endl;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第二题&lt;/h2&gt;
&lt;h3&gt;方法一&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
typedef long long ll;
typedef pair&amp;lt;int, int &amp;gt; pii;
inline ll qpow(ll a,ll b,ll mod){ll res=1;while(b){if(b&amp;amp;1)res = (res*a)%mod;a=(a*a)%mod;b&amp;gt;&amp;gt;=1;}return res;}
const int mod = 1e9 + 7;
const int MAXN = 2e5 + 7;

int t[16], g[16];
int dp[226][1&amp;lt;&amp;lt;15][3];
int main(){
    #ifndef ONLINE_JUDGE         
        freopen(&quot;in.txt&quot;, &quot;r&quot;, stdin);
        freopen(&quot;out.txt&quot;, &quot;w&quot;, stdout);
    #endif 
    
    int n, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    for(int i = 1; i &amp;lt;= n; ++i){
        cin &amp;gt;&amp;gt; t[i] &amp;gt;&amp;gt; g[i];
        g[i]--;
    }
    dp[0][0][0] = 1;
    dp[0][0][1] = 1;
    dp[0][0][2] = 1;
    for(int i = 0; i &amp;lt;= m; ++i){
        for(int j = 0; j &amp;lt; (1&amp;lt;&amp;lt;n); ++j){
            for(int k = 0; k &amp;lt; n; ++k){
                if((j&amp;gt;&amp;gt;k)&amp;amp;1)    continue;
                if(i+t[k+1] &amp;gt; m)    continue;
                for(int type = 0; type &amp;lt; 3; ++type){
                    if(type == g[k+1])    continue;
                    // cout &amp;lt;&amp;lt; i+t[k+1] &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; (j|1&amp;lt;&amp;lt;k) &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; g[k+1] &amp;lt;&amp;lt; endl;
                    dp[i+t[k+1]][j|1&amp;lt;&amp;lt;k][g[k+1]] += dp[i][j][type];
                    dp[i+t[k+1]][j|1&amp;lt;&amp;lt;k][g[k+1]] %= mod;
                }
            }
        }
    }
    ll res = 0;
    for(int i = 0; i &amp;lt; (1&amp;lt;&amp;lt;n); ++i){
        for(int j = 0; j &amp;lt; 3; ++j){
            res += dp[m][i][j];
            res %= mod;
        }
    }
    cout &amp;lt;&amp;lt; res*qpow(2, mod-2, mod)%mod &amp;lt;&amp;lt; endl;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;方法二&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
typedef long long ll;
typedef pair&amp;lt;int, int &amp;gt; pii;
inline ll qpow(ll a,ll b,ll mod){ll res=1;while(b){if(b&amp;amp;1)res = (res*a)%mod;a=(a*a)%mod;b&amp;gt;&amp;gt;=1;}return res;}
const int mod = 1e9 + 7;
const int MAXN = 2e5 + 7;

int t[16], g[16];
int dp[1&amp;lt;&amp;lt;16][3];
int main(){
    #ifndef ONLINE_JUDGE         
        freopen(&quot;in.txt&quot;, &quot;r&quot;, stdin);
        freopen(&quot;out.txt&quot;, &quot;w&quot;, stdout);
    #endif 
    
    int n, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    for(int i = 1; i &amp;lt;= n; ++i){
        cin &amp;gt;&amp;gt; t[i] &amp;gt;&amp;gt; g[i];
    }
    dp[0][1] = dp[0][2] = dp[0][3] = 1;
    for(int i = 0; i &amp;lt; (1&amp;lt;&amp;lt;n); ++i){
        for(int j = 0; j &amp;lt; n; ++j){
            if((i&amp;gt;&amp;gt;j)&amp;amp;1)    continue;
            for(int k = 1; k &amp;lt;= 3; ++k){
                if(k == g[j+1]) continue;
                dp[i|1&amp;lt;&amp;lt;j][g[j+1]] += dp[i][k];
                dp[i|1&amp;lt;&amp;lt;j][g[j+1]] %= mod;
            }
        }
    }
    ll res = 0;
    for(int i = 0; i &amp;lt; (1&amp;lt;&amp;lt;n); ++i){
        for(int j = 1; j &amp;lt;= 3; ++j){
            int tmp = 0;
            for(int k = 0; k &amp;lt; n; ++k){
                if((i&amp;gt;&amp;gt;k)&amp;amp;1){
                    tmp += t[k+1];
                }
            }
            if(tmp == m){
                res += dp[i][j];
                res %= mod;
            }
        }
    }
    cout &amp;lt;&amp;lt; res*qpow(2, mod-2, mod)%mod &amp;lt;&amp;lt; endl;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Codeforces 622F The Sum of the k-th Powers</title><link>https://dicer-zz.github.io/posts/codeforces-622-f/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/codeforces-622-f/</guid><pubDate>Tue, 18 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://codeforces.com/contest/622/problem/F&quot;&gt;F. The Sum of the k-th Powers&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;题解&lt;/h1&gt;
&lt;p&gt;设$F(n) =  \sum _{i=1}^n{i^k} $，当k很小的时候，可以找到一些通项公式，比如：当$k= 1 $时，$F(n) = \frac{(n \cdot (n+1))}{2} $，当$k=2 $时，$F(n)= \frac{n \cdot \ (2 \cdot n + 1) \cdot (n+1)}{6} $。&lt;/p&gt;
&lt;p&gt;可以发现，$F(n)$实际上是一个$k+1 $次多项式函数。&lt;/p&gt;
&lt;p&gt;因此，我们就可以使用拉格朗日插值法来推导了。&lt;/p&gt;
&lt;p&gt;确定一个k+1次多项式需要k+2个点，我们很容易通过打表得到$F(1)  \sim F(k+2)$的值。&lt;/p&gt;
&lt;p&gt;然后代入插值公式：&lt;/p&gt;
&lt;p&gt;$$F(n) = \sum_{i=1}^{k+2}F(i) \times P(i) $$&lt;/p&gt;
&lt;p&gt;$$P(i) = \prod_{j=1, j \neq i}^{k+2}\frac{n-j}{i-j}$$&lt;/p&gt;
&lt;p&gt;所以&lt;/p&gt;
&lt;p&gt;$$\begin{aligned}F(n) &amp;amp;= \sum_{i=1}^{k+2}F(i) \times \prod_{j=1, j \neq i}^{k+2}\frac{n-j}{i-j} \ &amp;amp;= \sum_{i=1}^{k+2}F(i) \times \frac{\prod_{j=1, j \neq i}^{k+2}n-j}{\prod_{j=1, j \neq i}^{k+2}{i-j}}\end{aligned}$$&lt;/p&gt;
&lt;p&gt;但是这个公式的复杂度时$O(k^2)$的，我们再优化一下。&lt;/p&gt;
&lt;p&gt;设$T = \prod_{j=1}^{k+2}{n-j}$，则&lt;/p&gt;
&lt;p&gt;$$\prod_{j=1, j \neq i}^{k+2}n-j = \frac{T}{n-i}$$&lt;/p&gt;
&lt;p&gt;对于$\prod_{j=1, j \neq i}^{k+2}{i-j}$，我们将它分成$i\lt j$和$i\gt j$两部分来考虑，&lt;/p&gt;
&lt;p&gt;$$\prod_{j=1, j \neq i}^{k+2}{i-j} = (i-1)!\times(k+2-i)!\times (-1)^{k+2-i}$$&lt;/p&gt;
&lt;p&gt;带入原式，得：&lt;/p&gt;
&lt;p&gt;$$F(n) = \sum_{i=1}^{k+2}(F(i)\times\frac{T}{n-i}\times\frac{1}{(i-1)!\times(k+2-i)!\times (-1)^{k+2-i}})$$&lt;/p&gt;
&lt;p&gt;至此，我们可以通过打表的方式，在$O(k)$时间内得到$F(n)$了。&lt;/p&gt;
&lt;h2&gt;注意&lt;/h2&gt;
&lt;p&gt;当$n \leq k+2$时，该公式时不适用的，因为$T$会为$0$。&lt;/p&gt;
&lt;p&gt;但是我们可以直接通过$F(n) =  \sum _{i=1}^n{i^k} $计算。&lt;/p&gt;
&lt;h1&gt;代码&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;/*---------------------------------

 @Author:   Dicer
 @DateTime: 2019-06-18 10:16:23

---------------------------------*/

#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
typedef long long ll;
typedef pair&amp;lt;int, int&amp;gt; pii;
inline ll qpow(ll a,ll b,ll mod){ll res=1;while(b){if(b&amp;amp;1)res = (res*a)%mod;a=(a*a)%mod;b&amp;gt;&amp;gt;=1;}return res;}
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
const int MAXN = 1e6 + 7;

ll F[MAXN], fac[MAXN], T;
int n, k;

ll inv(ll x){
	return qpow(x, mod-2, mod);
}
void init(){
	F[1] = 1;
	for(int i = 2; i &amp;lt;= k+2; ++i){
		F[i] = F[i-1] + qpow(i, k, mod);
		F[i] %= mod;
	}
	fac[0] = 1;
	for(int i = 1; i &amp;lt;= k+2; ++i){
		fac[i] = fac[i-1] * i;
		fac[i] %= mod;
	}
	T = 1;
	for(int i = 1; i &amp;lt;= k+2; ++i){
		T *= n - i;
		T %= mod;
	}
}
void solve(){
	if(n &amp;lt;= k+2){
		ll res = 0;
		for(int i = 1; i &amp;lt;= n; ++i){
			res += qpow(i, k, mod);
			res %= mod;
		}
		cout &amp;lt;&amp;lt; res &amp;lt;&amp;lt; endl;
		return;
	}
	ll res = 0;
	for(int i = 1; i &amp;lt;= k+2; ++i){
		res += F[i] * T % mod * inv(n-i) % mod * inv(fac[i-1]) %mod * inv(fac[k+2-i]) %mod * ((k-i)%2 == 0?1:-1) %mod;
		res += mod;
		res %= mod;
	}
	cout &amp;lt;&amp;lt; res &amp;lt;&amp;lt; endl;
}
int main(int argc, char const *argv[])
{
	cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k;
	init();
	solve();
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Reference&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;本文题图由User:Glosser.ca - Self-made, based on Image:Lagrangepolys.png，CC BY-SA 3.0，https://commons.wikimedia.org/w/index.php?curid=5538041&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.lightning34.cn/?p=115&quot;&gt;参考文章&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.mohu.org/info/symbols/symbols.htm&quot;&gt;LaTex&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>牛客小白赛 15</title><link>https://dicer-zz.github.io/posts/niukexiaobai15/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/niukexiaobai15/</guid><pubDate>Sun, 16 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://ac.nowcoder.com/acm/contest/917#question&quot;&gt;牛客小白赛15&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;题解&lt;/h1&gt;
&lt;h2&gt;E. 希望&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;希望是什么，希望是我们这个时代最珍贵的东西。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;直接用一颗线段树维护区间最小值就可以了。&lt;/p&gt;
&lt;p&gt;然后在做一个背包。&lt;/p&gt;
&lt;h2&gt;H.数据结构题&lt;/h2&gt;
&lt;p&gt;一个很神奇的思路。&lt;/p&gt;
&lt;p&gt;我们将每一个出现x的位置，放进G[x]中，然后查找第一个比r大的位置，和第一个大于定于l的位置，然后这两个位置做差就可以得到x在这段区间中的出现次数了。&lt;/p&gt;
&lt;h1&gt;代码&lt;/h1&gt;
&lt;h2&gt;E. 希望&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;/*---------------------------------

 @Author:   Dicer
 @DateTime: 2019-06-16 11:54:32

---------------------------------*/

#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
typedef long long ll;
typedef pair&amp;lt;int, int&amp;gt; pii;
inline ll qpow(ll a,ll b,ll mod){ll res=1;while(b){if(b&amp;amp;1)res = (res*a)%mod;a=(a*a)%mod;b&amp;gt;&amp;gt;=1;}return res;}
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
const int MAXN = 2e5 + 7;

struct node{
    int l, r;
    int mid(){return (l+r)&amp;gt;&amp;gt;1;}
    int val;
}s[MAXN&amp;lt;&amp;lt;1];
ll a[MAXN], b[MAXN], dp[MAXN];
void build(int l, int r, int x){
    s[x].l = l; s[x].r = r; s[x].val = 500;
    int mid = (l+r)&amp;gt;&amp;gt;1;
    if(l == r)  return;
    build(l, mid, x&amp;lt;&amp;lt;1);
    build(mid+1, r, x&amp;lt;&amp;lt;1|1);
}
inline void upd(int l, int r, int x, int v){
    // cout &amp;lt;&amp;lt; l &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; r &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; s[x].l &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; s[x].r &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; v &amp;lt;&amp;lt; endl;
    if(s[x].l &amp;gt;= l &amp;amp;&amp;amp; s[x].r &amp;lt;= r){
        s[x].val = min(s[x].val, v);
        return;
    }
    int mid = s[x].mid();
    if(r &amp;gt; mid) upd(l, r, x&amp;lt;&amp;lt;1|1, v);
    if(l &amp;lt;= mid) upd(l, r, x&amp;lt;&amp;lt;1, v);
}
inline void pushdown(int x){
    if(s[x].l == s[x].r){
        a[s[x].l] = s[x].val;
        return;
    }
    s[x&amp;lt;&amp;lt;1].val = min(s[x&amp;lt;&amp;lt;1].val, s[x].val);
    s[x&amp;lt;&amp;lt;1|1].val = min(s[x&amp;lt;&amp;lt;1|1].val, s[x].val);
    pushdown(x&amp;lt;&amp;lt;1);
    pushdown(x&amp;lt;&amp;lt;1|1);
}
int main(int argc, char const *argv[])
{
    #ifndef ONLINE_JUDGE
        freopen(&quot;in.txt&quot;, &quot;r&quot;, stdin);
        freopen(&quot;out.txt&quot;, &quot;w&quot;, stdout);
    #endif
    
    int n, k, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; k &amp;gt;&amp;gt; m;
    for(int i = 1; i &amp;lt;= n; ++i) cin &amp;gt;&amp;gt; b[i];
    build(1, n, 1);
    int l, r; ll c;
    for(int i = 1; i &amp;lt;= m; ++i){
        cin &amp;gt;&amp;gt; l &amp;gt;&amp;gt; r &amp;gt;&amp;gt; c;
        upd(l, r, 1, c);
    }
    pushdown(1);
    // for(int i = 1; i &amp;lt;= n; ++i) cout &amp;lt;&amp;lt; a[i] &amp;lt;&amp;lt; &apos; &apos;;
    // cout &amp;lt;&amp;lt; endl;
    //dp
	ll sum = 0;
    for(int i = 1; i &amp;lt;= n; ++i){
    	sum += b[i];
    	if(b[i] &amp;gt;= 0)	continue;
    	for(int j = k; j &amp;gt;= a[i]; --j){
    		dp[j] = max(dp[j], dp[j-a[i]] - b[i]);
    	}
    }
    cout &amp;lt;&amp;lt; sum+dp[k] &amp;lt;&amp;lt; endl;
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;H.数据结构题&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;/*---------------------------------

 @Author:   Dicer
 @DateTime: 2019-06-16 01:55:25

---------------------------------*/

#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
typedef long long ll;
typedef pair&amp;lt;int, int&amp;gt; pii;
inline ll qpow(ll a,ll b,ll mod){ll res=1;while(b){if(b&amp;amp;1)res = (res*a)%mod;a=(a*a)%mod;b&amp;gt;&amp;gt;=1;}return res;}
const double eps = 1e-8;
const int INF = 0x3f3f3f3f;
const int mod = 20180623;
const int MAXN = 2e5 + 7;

int a[MAXN];
vector&amp;lt;int&amp;gt; G[MAXN];
int main(int argc, char const *argv[])
{
    #ifndef ONLINE_JUDGE
        freopen(&quot;in.txt&quot;, &quot;r&quot;, stdin);
        freopen(&quot;out.txt&quot;, &quot;w&quot;, stdout);
    #endif
    
    int n, m;
    cin &amp;gt;&amp;gt; n &amp;gt;&amp;gt; m;
    for(int i = 1; i &amp;lt;= n; ++i){
        cin &amp;gt;&amp;gt; a[i];
        G[a[i]].push_back(i);
    }
    ll x, a, b, l1, l2, r1, r2;
    for(int i = 1; i &amp;lt;= m; ++i){
        cin &amp;gt;&amp;gt; l1 &amp;gt;&amp;gt; r1 &amp;gt;&amp;gt; l2 &amp;gt;&amp;gt; r2 &amp;gt;&amp;gt; x;
        if(l1 &amp;gt; r1) swap(l1, r1);
        if(l2 &amp;gt; r2) swap(l2, r2);
        a = upper_bound(G[x].begin(), G[x].end(), r1) - lower_bound(G[x].begin(), G[x].end(), l1);
        b = upper_bound(G[x].begin(), G[x].end(), r2) - lower_bound(G[x].begin(), G[x].end(), l2);
        cout &amp;lt;&amp;lt; a &amp;lt;&amp;lt; endl;
        cout &amp;lt;&amp;lt; b &amp;lt;&amp;lt; endl;
        cout &amp;lt;&amp;lt; (a%mod)*(b%mod)%mod &amp;lt;&amp;lt; endl;
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>SCOI 2005 骑士精神</title><link>https://dicer-zz.github.io/posts/scoi2005/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/scoi2005/</guid><pubDate>Tue, 11 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.luogu.org/problemnew/show/P2324&quot;&gt;luoguP2324&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;题解&lt;/h1&gt;
&lt;p&gt;首先很容易想到的是，应该用空格去跳，而不是用马，因为马的数量太多了。&lt;/p&gt;
&lt;p&gt;第二，因为搜索状态太多，考虑用使用IDDFS + A*，有一个比较简单的估价函数就是当前状态和终态的不同元素的个数。&lt;/p&gt;
&lt;p&gt;考虑到折返是没有任何价值的，因此在搜索过程中保留上一次搜索的方向，在本次搜索中如果是折返操作则跳过。&lt;/p&gt;
&lt;p&gt;未跳过折返时时间：1509ms，跳过后：66ms。&lt;/p&gt;
&lt;p&gt;可以看出来优化还是很巨大的。&lt;/p&gt;
&lt;h1&gt;代码&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;// luogu-judger-enable-o2
#include&amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
const int n = 5;
string final = &quot;111110111100*110000100000&quot;;
int dx[] = {1, 1, 2, 2, -2, -2, -1, -1};
int dy[] = {2, -2, 1, -1, 1, -1, 2, -2};
int dif(string s){
    int res = 0;
    for(int i = 0; i &amp;lt; n*n; ++i){
        if(s[i] != final[i])	res++;
    }
    return res;
}
string s;
int lim;
bool suc = 0;
void dfs(int cur, int last){
    if(cur == lim){		//是否搜索成功
        if(dif(s) == 0){
            suc = 1;
        }
        return;
    }
    if(suc)	return;		//最优解剪枝
    if(cur + max(0, dif(s) - 1) &amp;gt; lim)	return;		//当前值 + 最优值 &amp;gt; 迭代深度	
    int f = s.find(&apos;*&apos;);
    int x = f/n, y = f%n;
    int xx, yy;
    for(int i = 0; i &amp;lt; 8; ++i){
        if(i + last == 7)	continue;		//防止回头，优化了很多
        xx = x + dx[i];
        yy = y + dy[i];
        if(xx &amp;gt;= 0 &amp;amp;&amp;amp; xx &amp;lt; n &amp;amp;&amp;amp; yy &amp;gt;= 0 &amp;amp;&amp;amp; yy &amp;lt; n){
            swap(s[xx*n + yy], s[x*n + y]);
            dfs(cur+1, i);
            swap(s[xx*n + yy], s[x*n + y]);		//回溯
        }
    }
}
int main(){
    int T;
    cin &amp;gt;&amp;gt; T;
    while(T--){
        s = &quot;&quot;;
        string tmp;
        for(int i = 1; i &amp;lt;= n; ++i){
            cin &amp;gt;&amp;gt; tmp;
            s += tmp;
        }
        if(dif(s) == 0)	cout &amp;lt;&amp;lt; 0 &amp;lt;&amp;lt; endl;
        else{
            suc = 0;
            for(int i = 1; i &amp;lt;= 15; ++i){
                lim = i;
                dfs(0, 9);
                if(suc){
                    cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; endl;
                    break;
                }
            }
            if(!suc)	cout &amp;lt;&amp;lt; -1 &amp;lt;&amp;lt; endl;
        }
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>ZJOI 2008 骑士</title><link>https://dicer-zz.github.io/posts/zjoi2008/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/zjoi2008/</guid><pubDate>Tue, 11 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;题目&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.luogu.org/problemnew/show/P2607&quot;&gt;luoguP2607&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.lydsy.com/JudgeOnline/problem.php?id=1040&quot;&gt;bzoj1040&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;题解&lt;/h1&gt;
&lt;p&gt;基环树就是一颗多了一条边的树，多了这条边，就会产生一个环。&lt;/p&gt;
&lt;p&gt;考虑找到这个环上的任意一条边，断掉这条边，然后图形就又变回了树。（可以证明，断环上的哪条边对结果并没有影响）&lt;/p&gt;
&lt;p&gt;然后分别都被断掉的这条边的两个端点u、v，做树形动规。&lt;/p&gt;
&lt;p&gt;$dp[i][0/1]$表示取不取第$i$个点的最大值。&lt;/p&gt;
&lt;p&gt;这和&lt;a href=&quot;https://www.luogu.org/problemnew/show/P1352&quot;&gt;luoguP1352&lt;/a&gt;，没有上司的舞会一样。&lt;/p&gt;
&lt;p&gt;则这颗基环树的最大值为$max(dp[u][0], dp[v][0])$，当然因为树形动规的特点，一次动规是不能同时求出这两个值的，因此要分别对u、v进行动规。&lt;/p&gt;
&lt;p&gt;==两个注意事项==&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;非常重要的一点是，两个骑士可能互相憎恨，因此会存在重边，需要特判。&lt;/li&gt;
&lt;li&gt;记得开long long。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;处理重边的方法：&lt;/p&gt;
&lt;p&gt;按照我的建图方式，如果存在重边&amp;lt;u，v&amp;gt;，那么u的可到点集合中会出现两次v。&lt;/p&gt;
&lt;p&gt;根据这个特点就可以进行特判了。&lt;/p&gt;
&lt;p&gt;另外，这份代码在最后一个测试点TLE了。&lt;/p&gt;
&lt;h1&gt;代码&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;// luogu-judger-enable-o2
#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
typedef long long ll;
typedef pair&amp;lt;int, int &amp;gt; pii;
inline ll qpow(ll a,ll b,ll mod){ll res=1;while(b){if(b&amp;amp;1)res = (res*a)%mod;a=(a*a)%mod;b&amp;gt;&amp;gt;=1;}return res;}
const int mod = 1e9 + 7;
const int MAXN = 1e6 + 7;

int curx, cury;
bool vis[MAXN];
vector&amp;lt;int&amp;gt; G[MAXN];
ll dp[MAXN][2];
int val[MAXN];
void dfs(int x, int fa){
    if(vis[x]){
        curx = x;
        cury = fa;
        return;
    }
    vis[x] = 1;
    for(int i = 0; i &amp;lt; G[x].size(); ++i){
        int u = G[x][i];
        if(u == fa) continue;
        dfs(u, x);
    }
}
void go(int x, int fa){
    dp[x][0] = 0; dp[x][1] = val[x];
    for(int i = 0; i &amp;lt; G[x].size(); ++i){
        int u = G[x][i];
        if(u == fa) continue;
        if(x == curx &amp;amp;&amp;amp; u == cury)  continue;
        if(u == curx &amp;amp;&amp;amp; x == cury)  continue;
        go(u, x);
        dp[x][0] += max(dp[u][0], dp[u][1]);
        dp[x][1] += dp[u][0];
    }
}
int main(){
    #ifndef ONLINE_JUDGE         
        freopen(&quot;in.txt&quot;, &quot;r&quot;, stdin);
        freopen(&quot;out.txt&quot;, &quot;w&quot;, stdout);
    #endif 
    
    int n, x;
    scanf(&quot;%d&quot;, &amp;amp;n);
    for(int i = 1; i &amp;lt;= n; ++i){
        scanf(&quot;%d %d&quot;, &amp;amp;val[i], &amp;amp;x);
        G[x].push_back(i);
        G[i].push_back(x);
    }
    ll res = 0, tmp;
    for(int i = 1; i &amp;lt;= n; ++i){
        if(vis[i])  continue;
        dfs(i, 0);
        // cout &amp;lt;&amp;lt; curx &amp;lt;&amp;lt; &apos; &apos; &amp;lt;&amp;lt; cury &amp;lt;&amp;lt; endl;
        // cout &amp;lt;&amp;lt; count(G[curx].begin(), G[curx].end(), cury) &amp;lt;&amp;lt; endl;
        if(count(G[curx].begin(), G[curx].end(), cury) == 2){
            go(curx, 0);
            go(cury, 0);
            res += max(dp[curx][0] + dp[cury][1], dp[curx][1] + dp[cury][0]);
            continue;
        }
        go(curx, 0);
        tmp = dp[curx][0];
        go(cury, 0);
        tmp = max(tmp, dp[cury][0]);
        res += tmp;
    }
    printf(&quot;%lld\n&quot;, res);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2019 蓝桥杯国赛</title><link>https://dicer-zz.github.io/posts/2019-lanqiao-guosai/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/2019-lanqiao-guosai/</guid><pubDate>Thu, 30 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;碎碎念&lt;/h1&gt;
&lt;h2&gt;Day1&lt;/h2&gt;
&lt;p&gt;第一年没比赛，本来以为是需要去报道的，结果不用。也没有其他的安排，就去故宫了。下午还去了北大，自己瞎逛没见到图书馆。&lt;/p&gt;
&lt;h2&gt;Day2&lt;/h2&gt;
&lt;p&gt;上午在宾馆宅了一上午，下午比赛。&lt;/p&gt;
&lt;p&gt;比赛题目突变，暴力能过杯变成了暴力骗分杯。&lt;/p&gt;
&lt;p&gt;DP、搜索很多，甚至还有一道复杂的数据结构，蓝桥杯真的转型了。&lt;/p&gt;
&lt;p&gt;写的很差，对答案的时候发现就没写对几道题，大概三四十分，心想凉凉。&lt;/p&gt;
&lt;h2&gt;Day3&lt;/h2&gt;
&lt;p&gt;早上出了成绩发现竟然国一，激动得不行。也发现蓝桥杯还是那么水，这都能国一。&lt;/p&gt;
&lt;p&gt;早上的招聘会约了但是没起来没去，只去了下午的颁奖，本来以后国一还能有个U盘，结果什么都没有。&lt;/p&gt;
&lt;p&gt;颁奖典礼是真的体面，表白民族大学的小姐姐。魔术师贼帅。蓝桥大气，羡慕国特笔记本+手机。&lt;/p&gt;
&lt;p&gt;因为是晚上十点的车，颁完奖才四点多，又去逛了逛圆明园，当天下了雨，圆明园是真的好看。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/gallery/photos/ymy.png&quot; alt=&quot;ymy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;完。&lt;/p&gt;
</content:encoded></item><item><title>红楼梦札记</title><link>https://dicer-zz.github.io/posts/reading-dream-of-the-red-chamber/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/reading-dream-of-the-red-chamber/</guid><pubDate>Tue, 23 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;小记&lt;/h2&gt;
&lt;p&gt;昨天晚上看了一集木鱼水心做的87版红楼梦讲解，颇有兴趣，于是决定写一篇博客，记录个人感受。&lt;/p&gt;
&lt;p&gt;说起来，曾经多次想要认真读完红楼梦原著却都不了了之，究其根本可能是我本人对其中复杂人物关系招架不住，毕竟在生活中我就不擅长这些事情。还有可能是因为书中前篇铺垫冗长，这种慢热的写法导致我还没看到精彩的部分就已经没了耐心。&lt;/p&gt;
&lt;p&gt;确实如木鱼水心在第一篇中所说的那样，虽然同为四大名著，红楼梦却没用其他三本书那样人尽皆知。&lt;/p&gt;
&lt;p&gt;至于我为什么想读这本书，可能因为高中的时候同学老师的不经意提起，对为何它能被评为“小说的巅峰”感到好奇，还有就是对书中所描述的美食十分感兴趣。&lt;/p&gt;
&lt;p&gt;我为何不选择自己品读名著，观看电视剧？&lt;/p&gt;
&lt;p&gt;原因有二：１、没有太多的时间。２、觉得自己品读原著读不出来什么东西，很可能很快就放弃了。&lt;/p&gt;
&lt;p&gt;本篇博客小标题与木鱼水心分p标题相同。&lt;/p&gt;
&lt;h2&gt;通灵宝玉入红尘，宝黛初会续前盟&lt;/h2&gt;
&lt;h3&gt;抄录&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;满纸荒唐言，一把辛酸泪。&lt;/p&gt;
&lt;p&gt;都云作者痴，谁解其中味。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;红尘之中美中不足且好事多磨，乐极生悲之时又人非物换，不过是到头一梦，万境归空。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;好了歌&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;世人都道神仙好&lt;/p&gt;
&lt;p&gt;惟有功名忘不了&lt;/p&gt;
&lt;p&gt;古今将相在何方&lt;/p&gt;
&lt;p&gt;荒冢一堆草末了&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>2019 CCPC 河南省省赛</title><link>https://dicer-zz.github.io/posts/2019-henan-ccpc-provincial-competition/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/2019-henan-ccpc-provincial-competition/</guid><pubDate>Sat, 13 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;碎碎念&lt;/h2&gt;
&lt;p&gt;早上８点才出发，本来以为时间会很紧张，不过９点多一点就到了。不过没有参加开幕式。&lt;/p&gt;
&lt;h2&gt;复盘&lt;/h2&gt;
&lt;p&gt;然后比赛开始，前两道水题签到很快就过了，然后发现有人在交A，但是都没过，mengshen猜了半天的假结论，直接被hack掉了，然后被通知A重判了，发现过了好几个队，然后仔细想了下，发现直接把每个行当做元素进行LIS就行了。然后我写了半天的LCS过了样例就交了一发，１WA，然后甚至还觉得自己写的很完美，演了半天，然后改抄白书模板自作聪明改了一点，过了样例，又交，２WA，演了大概30min，然后mengshen上机抄了一下模板，然后改了一下check两行之间关系的函数，成功３AC。&lt;/p&gt;
&lt;p&gt;期间我看了F题，看了一下题面直接丢给了老毕，然后听他说好像有一个边界判错了浪费了好长时间DEBUG，不过最后还是过了，１A。&lt;/p&gt;
&lt;p&gt;这个时候四题已经在金区了（好水），发现C，H都有人过，于是看C，H，以为Ｃ是跟前几天训练一样的multiset写法，然后发现不会两个multiset的合并，内存时间都可能爆炸。Ｈ是一个奇奇怪怪的搜索顺序，到最后都没弄明白。然后觉得可能还有其他题目能开，于是开始一道一道看，发现了Ｉ题是一个暴力剪枝很可行的题，而且很难造能卡主暴力的数据，于是我上机敲了一发暴力，1Ａ，之后一直在Ｃ，Ｈ两题之间倒腾但是并不会写。&lt;/p&gt;
&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;最后５题水了个金，赞助商爸爸大气，每题一血还有５００现金，Ｉ题差４min一血，好可惜。&lt;/p&gt;
&lt;p&gt;面包牛奶香蕉好评，就是面包有点油。上次天梯赛在郑轻的时候，键盘&lt;code&gt;Ctrl&lt;/code&gt;特别硬，都摁不下去，这次好像机房升级了，全部ＨＰ的键盘鼠标，县显示器１０８０分辨率超级舒服，i7-8700的电脑用起来真的爽，郑州轻工业大学牛逼！体验极佳。&lt;/p&gt;
</content:encoded></item><item><title>2017 浙江省赛</title><link>https://dicer-zz.github.io/posts/2017-zhejiang-provincial-competition/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/2017-zhejiang-provincial-competition/</guid><pubDate>Fri, 12 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;碎碎念&lt;/h2&gt;
&lt;p&gt;明天就又去郑轻了（噫，我为什么要说又，哦，上次天梯就是在郑轻），希望这次能取得一个好成绩！不能再这么菜下去了。&lt;/p&gt;
&lt;p&gt;老毕再次KILL一道数位DP，这也太强了。我贡献一发构造题。&lt;/p&gt;
&lt;h2&gt;复盘&lt;/h2&gt;
&lt;p&gt;早上起晚了，迟到30min，AB两题我到的时候已经Ａ掉了，然后mengshen在上机写Ｃ，然后我看Ｄ，老毕再看Ｇ，说是博弈，然而我根本不会博弈，于是继续看我的Ｄ，看完发现是一个普通的线段合并，觉得可写，此时mengshen还在改Ｃ，于是我给老毕讲了一下题意想让他写，但是讲着讲着发下原来的思路复杂了，其实直接两个人的线段全部丢在一起，直接判断重复区间就可以了，就把mengshen赶下来写Ｄ，写完测样例通过直接就交上去了，然后ＷＡ了，发现ＤＥＢＵＧ的输出没删除，删除掉ＡＣ。然后mengshen又上机写Ｃ，并改变了做法，３ＡＣ。然后Ｅ题数位ＤＰ丢给老毕没管过，看Ｆ，本来用的优先队列写了一个假算法，演了一发，然后找到了一组数据ｈａｃｋ了自己，发现是没有贪心，改用multiset贪心，写完测过样例交了，TLE，然后mengshen提议讲ＳＴＬ的lower_bound()换成multiset自带的，说是曾经在CF上看到过一个被lower_bound()卡时间的帖子，遂改，3ＡＣ。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;最终６题，听说这套省赛题目难度挺大的，感觉打的还可以。&lt;/p&gt;
&lt;p&gt;由于前天睡得太晚导致迟到实在不应该，老毕还是强啊，精通数位ＤＰ，还是要继续加油。&lt;/p&gt;
</content:encoded></item><item><title>Hexo 博客搭建总结</title><link>https://dicer-zz.github.io/posts/hexo_blog_construct_summary/</link><guid isPermaLink="true">https://dicer-zz.github.io/posts/hexo_blog_construct_summary/</guid><pubDate>Thu, 11 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;序言&lt;/h2&gt;
&lt;p&gt;之前使用jekyll在github上搭建了一个博客。
但是，原博客不支持归档和搜索功能，主题也不是特别喜欢，于是决定重新搭建自己的博客站点。
这次放弃了jekyll，使用hexo搭建。主要原因是hexo支持很多插件，且性能强悍。(&lt;s&gt;相中了一个特别好看的主题&lt;/s&gt;)。&lt;/p&gt;
&lt;h2&gt;背景&lt;/h2&gt;
&lt;p&gt;本文代码均基于阿里云&lt;code&gt;Centos7&lt;/code&gt;服务器，&lt;code&gt;Manjaro Linux&lt;/code&gt;客户端，与&lt;code&gt;hexo&lt;/code&gt;引擎。&lt;/p&gt;
&lt;h2&gt;选择主题&lt;/h2&gt;
&lt;p&gt;在搭建博客之前当然要选择一个自己喜欢的主题了。&lt;/p&gt;
&lt;p&gt;可以在hexo的主题官方上挑选一个。&lt;a href=&quot;https://hexo.io/themes/&quot;&gt;戳我&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;注意挑选的时候不仅要看&lt;code&gt;颜值&lt;/code&gt;，还要看一下自己需要的功能这个主题是否支持，当然如果你自己会添加插件，那就挑你最喜欢的就好了。&lt;/p&gt;
&lt;h2&gt;客户机配置&lt;/h2&gt;
&lt;h3&gt;安装Git&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;下载Git.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;修改用户名和邮箱。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 将此处的&quot;yourname&quot;替换成自己的用户名
git config --global user.name &quot;yourname&quot;

# 将此处的&quot;youremail&quot;替换成自己的邮箱
git config --global user.email &quot;youremail&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;创建SSH钥匙对&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;检查是否存在钥匙对。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ cd ~/.ssh&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;若没有钥匙对，则创建钥匙对。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ ssh-keygen -t rsa -C &quot;youremail&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;安装Nodejs&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;使用命令行下载Nodejs。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;检查是否下载完成。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ node -v&lt;/p&gt;
&lt;p&gt;$ npm -v&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;安装Hexo框架&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;使用&lt;code&gt;npm&lt;/code&gt;下载&lt;code&gt;hexo&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ npm install -g hexo-cli&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因为一下原因，&lt;code&gt;npm&lt;/code&gt;的下载速度十分感人，建议更换&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;选择一个你想要存放文件的文件夹，初始化blog。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ hexo init blog&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;初始化完毕之后，打开博客根目录的package.json文件，在dependencies的配置中，追加一项：&lt;code&gt;&quot;hexo-deployer-git&quot;: &quot;^0.3.1&quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;运行&lt;code&gt;hexo&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ hexo s&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然后在浏览器输入：｀localhost:4000｀，如果出现hexo的界面就说明已经搭建成功了。&lt;/p&gt;
&lt;p&gt;如果有包缺失，根据提示下载相应的包，再次尝试即可。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;服务端配置&lt;/h2&gt;
&lt;p&gt;首先，进行服务端的系统更新。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ yum update -y&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;更新完系统后，输入一下代码查看系统版本。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;$ cat /etc/centos-release&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;安装Nginx&lt;/h3&gt;
&lt;p&gt;安装Nginx分为以下几步。第一，配置Nginx官方源。第二，安装Nginx。第三，配置Nginx配置文件。
1、配置Nginx官方源
输入以下代码，新建一个文件以配置Nginx源&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vi /etc/yum.repos.d/nginx.repo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在打开的文件中输入以下代码，输入完毕之后，按 “esc” 键退出编辑模式， 输入 “:wq” 保存退出。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、安装Nginx
输入以下代码进行安装。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ yum install nginx -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、启动Nginx并设置开机自启
输入以下代码:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ systemctl start nginx
$ systemctl enable nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进行到这里，你已经可以把服务器ip复制到浏览器就可以看到Nginx的欢迎界面了。&lt;/p&gt;
&lt;p&gt;4、配置Nginx
接下来，需要修改一下nginx的相关配置，包括设置网站根目录以及配置域名。输入以下代码，打开Nginx的配置文件。(注：此处假定读者已完成了域名备案以及域名解析。)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vi /etc/nginx/conf.d/default.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;依照下图进行修改，将“/usr/share/nginx/html”改为“/usr/share/nginx/html/blog”。
&lt;img src=&quot;https://segmentfault.com/img/remote/1460000012907517?w=698&amp;amp;h=768&quot; alt=&quot;nginx配置&quot; /&gt;&lt;/p&gt;
&lt;p&gt;至此，Nginx的配置就基本完成了。&lt;/p&gt;
&lt;h2&gt;安装Nodejs&lt;/h2&gt;
&lt;p&gt;输入以下代码进行Nodejs的安装。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ yum install nodejs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可输入&lt;code&gt;node -v&lt;/code&gt;以及&lt;code&gt;npm -v&lt;/code&gt;查看node的版本。
至此，Nodejs的安装就完成了。&lt;/p&gt;
&lt;h2&gt;安装Git以及进行相关配置&lt;/h2&gt;
&lt;p&gt;1、输入以下代码，进行Git的安装&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ yum install git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2、创建git用户以及设置密码
输入以下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 创建用户,用户名为git
$ adduser git
# 设置密码
$ passwd git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3、把git用户添加到sudo用户组中
输入以下代码&lt;code&gt;sudo vi /etc/sudoers&lt;/code&gt;，打开sudoers文件，输入&lt;code&gt;:/root&lt;/code&gt;进行搜索，搜索到代码行&lt;code&gt;root ALL=(ALL) ALL&lt;/code&gt;,然后在这一行下添加以下代码&lt;code&gt;git ALL=(ALL) ALL&lt;/code&gt;。输入完毕之后，按&lt;code&gt;wq!&lt;/code&gt;强制保存退出vi。&lt;/p&gt;
&lt;p&gt;4、切换到git用户，添加SSH Key文件并且设置相应的读写与执行权限。
输入以下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 切换用户
$ su git
# 创建目录
$ mkdir ~/.ssh
# 新建文件
$ vim ~/.ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后把之前在客户端设置的SSH Key,复制到authorized_keys文件中，保存后退出。&lt;/p&gt;
&lt;p&gt;接下来设置文件权限，把authorized_keys文件设置成只有属主有读写权限，把ssh目录设置为只有属主有读、写、执行权限。代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ chmod 600 ~/.ssh/authorized_keys
$ chmod 700 ~/.ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;设置完后，返回客户端，打开Git Bash，输入以下代码，测试是否能连接上服务器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ServerIP为你自己服务器的ip
$ ssh -v git@ServerIP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;SSH使用非对称加密，服务端不需要生成密钥对，而只需要一个&lt;code&gt;authorized_keys&lt;/code&gt;文件，里面存放的是你客户端的&lt;strong&gt;公钥&lt;/strong&gt;。客户端应该有密钥对&lt;code&gt;id_rsa&lt;/code&gt;和&lt;code&gt;id_rsa.pub&lt;/code&gt;文件，客户端的&lt;code&gt;authorized_keys&lt;/code&gt;文件应该和&lt;code&gt;id_rsa.pub&lt;/code&gt;文件内容一致。如果SSH免密码登录失败，请反复确认上述两点。&lt;/p&gt;
&lt;p&gt;5、重新回到服务器，在网站根目录新建一个blog文件夹，用于客户端上传文件，并且把该文件授权给git用户。代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 使用sudo指令，需要输入git用户的密码
$ sudo mkdir -p /usr/share/nginx/html/blog
$ sudo chown -R git:git /usr/share/nginx/html/blog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;6、在服务器上初始化一个git裸库
切换到git用户，然后切换到git用户目录，接着初始化裸库，代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ su git
$ cd ~
$ git init --bare blog.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接着新建一个post-receive文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vim ~/blog.git/hooks/post-receive
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在该文件中输入以下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#！/bin/sh
git --work-tree=/usr/share/nginx/html/blog --git-dir=/home/git/blog.git checkout -f
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;保存退出之后，再输入以下代码，赋予该文件可执行权限。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ chmod +x ~/blog.git/hooks/post-receive
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;7、返回客户端，设置博客根目录下的_config.yml文件。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;deploy:
    type: git
    repo: git@SERVER:/home/git/blog.git       #此处的SERVER需改为你自己服务器的ip
    branch: master                            #这里填写分支
    message:                                  #提交的信息
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;保存后，在博客根目录打开Git Bash，输入以下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hexo clean
$ hexo g
$ hexo d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;部署完毕之后，即可在浏览器输入你的服务器ip进行访问你的博客了。&lt;/p&gt;
&lt;h2&gt;引用&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://segmentfault.com/a/1190000012907499&quot;&gt;fogcrane-基于CentOS搭建Hexo博客&lt;/a&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>