RegMs If %!s(int64=3) %!d(string=hai) anos
pai
achega
70f1c478e4
Modificáronse 100 ficheiros con 10330 adicións e 0 borrados
  1. 2 0
      .gitignore
  2. 1004 0
      _config.fluid.yml
  3. 0 0
      _config.landscape.yml
  4. 108 0
      _config.yml
  5. 28 0
      package.json
  6. 4 0
      scaffolds/draft.md
  7. 4 0
      scaffolds/page.md
  8. 5 0
      scaffolds/post.md
  9. 384 0
      source/_posts/coding/archlinux-installation-guide.md
  10. 506 0
      source/_posts/coding/digital-image-processing-assignment-i.md
  11. 298 0
      source/_posts/coding/digital-image-processing-assignment-ii.md
  12. 210 0
      source/_posts/coding/digital-image-processing-assignment-iii.md
  13. 292 0
      source/_posts/coding/digital-image-processing-assignment-iv.md
  14. 46 0
      source/_posts/coding/mathjax-in-wordpress.md
  15. 80 0
      source/_posts/coding/my-conkyrc.md
  16. 188 0
      source/_posts/coding/my-vimrc.md
  17. 79 0
      source/_posts/coding/optimized-input-and-output-in-c.md
  18. 15 0
      source/_posts/life/2017-jl-fighting.md
  19. BIN=BIN
      source/_posts/life/2017-jl-fighting/1.jpg
  20. BIN=BIN
      source/_posts/life/2017-jl-fighting/2.jpg
  21. BIN=BIN
      source/_posts/life/2017-jl-fighting/3.jpg
  22. 11 0
      source/_posts/life/2018.md
  23. 23 0
      source/_posts/life/give-up-sometimes.md
  24. 11 0
      source/_posts/life/happy-2020.md
  25. 11 0
      source/_posts/life/happy-childrens-day.md
  26. 11 0
      source/_posts/life/happy-graduation.md
  27. 11 0
      source/_posts/life/hello-world.md
  28. 37 0
      source/_posts/life/light-i.md
  29. 37 0
      source/_posts/life/light-ii.md
  30. 43 0
      source/_posts/life/light-iii.md
  31. 92 0
      source/_posts/life/notes-on-physics.md
  32. 31 0
      source/_posts/life/spring-i.md
  33. 41 0
      source/_posts/life/spring-ii.md
  34. 25 0
      source/_posts/life/the-beginning.md
  35. 104 0
      source/_posts/oi/another-chocolate-maniac.md
  36. 86 0
      source/_posts/oi/archipelago.md
  37. 151 0
      source/_posts/oi/arpa-and-a-game-with-mojtaba.md
  38. 147 0
      source/_posts/oi/arpa-and-a-list-of-numbers.md
  39. 73 0
      source/_posts/oi/bbq.md
  40. 83 0
      source/_posts/oi/beauty-contest.md
  41. 121 0
      source/_posts/oi/best-edge-weight.md
  42. 81 0
      source/_posts/oi/bitwise-xor.md
  43. 79 0
      source/_posts/oi/black-white-balls.md
  44. 34 0
      source/_posts/oi/book-pile.md
  45. 25 0
      source/_posts/oi/boxes.md
  46. 53 0
      source/_posts/oi/brackets.md
  47. 93 0
      source/_posts/oi/bridges-painting.md
  48. 86 0
      source/_posts/oi/broken-line.md
  49. 56 0
      source/_posts/oi/calculator.md
  50. 70 0
      source/_posts/oi/cards-sorting.md
  51. 154 0
      source/_posts/oi/ceizenpoks-formula.md
  52. 64 0
      source/_posts/oi/chef-and-sign-sequences.md
  53. 76 0
      source/_posts/oi/chess-championship.md
  54. 83 0
      source/_posts/oi/choosing-balls.md
  55. 96 0
      source/_posts/oi/choosing-the-commander.md
  56. 166 0
      source/_posts/oi/clever-y.md
  57. 65 0
      source/_posts/oi/coin-troubles.md
  58. 76 0
      source/_posts/oi/colored-balls.md
  59. 78 0
      source/_posts/oi/colorful-hats.md
  60. 98 0
      source/_posts/oi/command-network.md
  61. 109 0
      source/_posts/oi/complete-the-graph.md
  62. 96 0
      source/_posts/oi/conquer-the-polygon.md
  63. 137 0
      source/_posts/oi/count-on-a-tree-ii.md
  64. 143 0
      source/_posts/oi/counting-divisors-square.md
  65. 20 0
      source/_posts/oi/coupons.md
  66. 70 0
      source/_posts/oi/cow-program.md
  67. 128 0
      source/_posts/oi/d-tree.md
  68. 112 0
      source/_posts/oi/desert-king.md
  69. 163 0
      source/_posts/oi/destiny.md
  70. 140 0
      source/_posts/oi/digit-tree.md
  71. 74 0
      source/_posts/oi/digital-root.md
  72. 143 0
      source/_posts/oi/divisibility.md
  73. 94 0
      source/_posts/oi/dna-evolution.md
  74. 114 0
      source/_posts/oi/dna-sequence.md
  75. 146 0
      source/_posts/oi/domino.md
  76. 99 0
      source/_posts/oi/dungeon.md
  77. 159 0
      source/_posts/oi/dynamic-rankings.md
  78. 120 0
      source/_posts/oi/dzy-loves-fibonacci-numbers.md
  79. 69 0
      source/_posts/oi/dzy-loves-modification.md
  80. 95 0
      source/_posts/oi/ellipse.md
  81. BIN=BIN
      source/_posts/oi/ellipse/1.jpg
  82. 107 0
      source/_posts/oi/erasing-edges.md
  83. 22 0
      source/_posts/oi/expensive-drink.md
  84. 77 0
      source/_posts/oi/fence.md
  85. 122 0
      source/_posts/oi/fermats-last-theorem.md
  86. 179 0
      source/_posts/oi/four-loop.md
  87. 139 0
      source/_posts/oi/fox-and-dinner.md
  88. 75 0
      source/_posts/oi/fox-and-jumping.md
  89. 100 0
      source/_posts/oi/fox-and-names.md
  90. 119 0
      source/_posts/oi/friendly-points.md
  91. 116 0
      source/_posts/oi/funny-strings.md
  92. 83 0
      source/_posts/oi/games-of-chess.md
  93. 115 0
      source/_posts/oi/gcd-extreme-hard.md
  94. 90 0
      source/_posts/oi/gena-vs-petya.md
  95. 219 0
      source/_posts/oi/goodbye-souvenir.md
  96. 54 0
      source/_posts/oi/hack-it.md
  97. 96 0
      source/_posts/oi/hardwood-floor.md
  98. 230 0
      source/_posts/oi/hide.md
  99. 69 0
      source/_posts/oi/high-load.md
  100. 152 0
      source/_posts/oi/imbalanced-array.md

+ 2 - 0
.gitignore

@@ -28,3 +28,5 @@ build/Release
 # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
 node_modules
 
+public
+db.json

+ 1004 - 0
_config.fluid.yml

@@ -0,0 +1,1004 @@
+---
+#---------------------------
+# Hexo Theme Fluid
+# Author: Fluid-dev
+# Github: https://github.com/fluid-dev/hexo-theme-fluid
+#
+# 配置指南: https://hexo.fluid-dev.com/docs/guide/
+# 你可以从指南中获得更详细的说明
+#
+# Guide: https://hexo.fluid-dev.com/docs/en/guide/
+# You can get more detailed help from the guide
+#---------------------------
+
+#---------------------------
+# 全局
+# Global
+#---------------------------
+
+# 用于浏览器标签的图标
+# Icon for browser tab
+favicon: /img/favicon.png
+
+# 用于苹果设备的图标
+# Icon for Apple touch
+apple_touch_icon: /img/favicon.png
+
+# 浏览器标签页中的标题分隔符,效果: 文章名 - 站点名
+# Title separator in browser tab, eg: article - site
+tab_title_separator: " - "
+
+# 强制所有链接升级为 HTTPS(适用于图片等资源出现 HTTP 混入报错)
+# Force all links to be HTTPS (applicable to HTTP mixed error)
+force_https: true
+
+# 代码块的增强配置
+# Enhancements to code blocks
+code:
+  # 是否开启复制代码的按钮
+  # Enable copy code button
+  copy_btn: true
+
+  # 代码高亮
+  # Code highlight
+  highlight:
+    enable: true
+
+    # 代码块是否显示行号
+    # If true, the code block display line numbers
+    line_number: true
+
+    # 实现高亮的库,对应下面的设置
+    # Highlight library
+    # Options: highlightjs | prismjs
+    lib: "highlightjs"
+
+    highlightjs:
+      # 在链接中挑选 style 填入
+      # Select a style in the link
+      # See: https://highlightjs.org/static/demo/
+      style: "Github Gist"
+
+      # 是否根据 style 改变代码背景色(如果 style 是深色背景别忘了开启此项)
+      # If true, the code background will change color based on the style (If style has a dark background, don't forget to true)
+      bg_color: false
+
+    prismjs:
+      # 在下方链接页面右侧的圆形按钮挑选 style 填入,也可以直接填入 css 链接
+      # Select the style button on the right side of the link page, you can also set the CSS link
+      # See: https://prismjs.com/
+      style: "default"
+
+      # 设为 true 高亮将本地静态生成(并只支持部分 prismjs 插件),设为 false 高亮将在浏览器通过 js 生成
+      # If true, it will be generated locally (but some prismjs plugins are not supported). If false, it will be generated via JS in the browser
+      preprocess: true
+
+# 一些好玩的功能
+# Some fun features
+fun_features:
+  # 为 subtitle 添加打字机效果
+  # Typing animation for subtitle
+  typing:
+    enable: true
+
+    # 打印速度,数字越大越慢
+    # Typing speed, the larger the number, the slower
+    typeSpeed: 70
+
+    # 游标字符
+    # Cursor character
+    cursorChar: "_"
+
+    # 是否循环播放效果
+    # If true, loop animation
+    loop: false
+
+  # 为文章内容中的标题添加锚图标
+  # Add an anchor icon to the title on the post page
+  anchorjs:
+    enable: true
+    element: h1,h2,h3,h4,h5,h6
+    placement: right
+    # Options: hover | always | touch
+    visible: hover
+    # Option:§ | # | ❡
+    icon: ""
+
+  # 加载进度条
+  # Progress bar when loading
+  progressbar:
+    enable: true
+    height_px: 3
+    color: "#29d"
+    # See: https://github.com/rstacruz/nprogress
+    options: { showSpinner: false, trickleSpeed: 100 }
+
+# 主题暗色模式,开启后菜单中会出现切换按钮,用户浏览器会存储切换选项,并且会遵循 prefers-color-scheme 自动切换
+# Theme dark mode. If enable, a switch button will appear on the menu, each of the visitor's browser will store his switch option
+dark_mode:
+  enable: false
+  # 默认的选项(当用户手动切换后则不再按照默认模式),选择 `auto` 会优先遵循 prefers-color-scheme,其次按用户本地时间 18 点到次日 6 点之间进入暗色模式
+  # Default option (when the visitor switches manually, the default mode is no longer followed), choosing `auto` will give priority to prefers-color-scheme, and then enter the dark mode from 18:00 to 6:00 in the visitor’s local time
+  # Options: auto | light | dark
+  default: auto
+
+# 主题颜色配置,其他不生效的地方请使用自定义 css 解决,配色可以在下方链接中获得启发
+# Theme color, please use custom CSS to solve other colors, color schema can be inspired by the links below
+# See: https://www.webdesignrankings.com/resources/lolcolors/
+color:
+  # body 背景色
+  # Color of body background
+  body_bg_color: "#eee"
+  # 暗色模式下的 body 背景色,下同
+  # Color in dark mode, the same below
+  body_bg_color_dark: "#181c27"
+
+  # 顶部菜单背景色
+  # Color of navigation bar background
+  navbar_bg_color: "#2f4154"
+  navbar_bg_color_dark: "#1f3144"
+
+  # 顶部菜单字体色
+  # Color of navigation bar text
+  navbar_text_color: "#fff"
+  navbar_text_color_dark: "#d0d0d0"
+
+  # 全局字体色
+  # Color of global text
+  text_color: "#3c4858"
+  text_color_dark: "#c4c6c9"
+
+  # 全局次级字体色(摘要、简介等位置)
+  # Color of global secondary text (excerpt, introduction, etc.)
+  sec_text_color: "#718096"
+  sec_text_color_dark: "#a7a9ad"
+
+  # 文章正文字体色
+  # Color of post text
+  post_text_color: "#2c3e50"
+  post_text_color_dark: "#c4c6c9"
+
+  # 文章正文字体色(h1 h2 h3...)
+  # Color of Article heading (h1 h2 h3...)
+  post_heading_color: "#1a202c"
+  post_heading_color_dark: "#c4c6c9"
+
+  # 文章超链接字体色
+  # Color of post link
+  post_link_color: "#0366d6"
+  post_link_color_dark: "#1589e9"
+
+  # 超链接悬浮时字体色
+  # Color of link when hovering
+  link_hover_color: "#30a9de"
+  link_hover_color_dark: "#30a9de"
+
+  # 超链接悬浮背景色
+  # Color of link background when hovering
+  link_hover_bg_color: "#f8f9fa"
+  link_hover_bg_color_dark: "#364151"
+
+  # 主面板背景色
+  # Color of main board
+  board_color: "#fff"
+  board_color_dark: "#252d38"
+
+  # 滚动条颜色
+  # Color of scrollbar
+  scrollbar_color: "#c4c6c9"
+  scrollbar_color_dark: "#687582"
+  scrollbar_hover_color: "#a6a6a6"
+  scrollbar_hover_color_dark: "#9da8b3"
+
+# 主题字体配置
+# Font
+font:
+  font_size: 16px
+  font_family:
+  letter_spacing: 0.02em
+  code_font_size: 85%
+
+# 指定自定义 .js 文件路径,支持列表;路径是相对 source 目录,如 /js/custom.js 对应存放目录 source/js/custom.js
+# Specify the path of your custom js file, support list. The path is relative to the source directory, such as `/js/custom.js` corresponding to the directory `source/js/custom.js`
+custom_js:
+
+# 指定自定义 .css 文件路径,用法和 custom_js 相同
+# The usage is the same as custom_js
+custom_css:
+
+# 自定义 <head> 节点中的 HTML 内容
+# Customize <head> HTML content
+custom_head: ""
+
+# 自定义底部 HTML 内容(位于 footer 上方),注意不要和 `post: custom` 配置冲突
+# Customize the HTML content at the bottom (located above the footer), be careful not to conflict with `post: custom`
+custom_html: ""
+
+# 网页访问统计
+# Analysis of website visitors
+web_analytics: # 网页访问统计
+  enable: false
+
+  # 百度统计的 Key,值需要获取下方链接中 `hm.js?` 后边的字符串
+  # Baidu analytics, get the string behind `hm.js?`
+  # See: https://tongji.baidu.com/sc-web/10000033910/home/site/getjs?siteId=13751376
+  baidu:
+
+  # Google 统计的 Tracking ID
+  # Google analytics, set Tracking ID
+  # See: https://developers.google.com/analytics/devguides/collection/analyticsjs
+  google:
+
+  # Google gtag.js 的媒体资源 ID
+  # Google gtag.js GA_MEASUREMENT_ID
+  # See: https://developers.google.com/analytics/devguides/collection/gtagjs/
+  gtag:
+
+  # 腾讯统计的 H5 App ID,开启高级功能才有cid
+  # Tencent analytics, set APP ID
+  # See: https://mta.qq.com/h5/manage/ctr_app_manage
+  tencent:
+    sid:
+    cid:
+
+  # 51.la 站点统计 ID
+  # 51.la analytics
+  # See: https://www.51.la/user/site/index
+  woyaola: # 51.la 站点统计 ID,参见
+
+  # 友盟/cnzz 站点统计 web_id
+  # cnzz analytics
+  # See: https://web.umeng.com/main.php?c=site&a=show
+  cnzz:
+
+  # LeanCloud 计数统计,可用于 PV UV 展示,如果 `web_analytics: enable` 没有开启,PV UV 展示只会查询不会增加
+  # LeanCloud count statistics, which can be used for PV UV display. If `web_analytics: enable` is false, PV UV display will only query and not increase
+  leancloud:
+    app_id:
+    app_key:
+    # REST API 服务器地址,国际版不填
+    # Only the Chinese mainland users need to set
+    server_url:
+    # 统计页面时获取路径的属性
+    # Get the attribute of the page path during statistics
+    path: window.location.pathname
+    # 开启后不统计本地路径( localhost 与 127.0.0.1 )
+    # If ture, ignore localhost & 127.0.0.1
+    ignore_local: false
+
+# 对页面中的图片和评论插件进行懒加载处理,可见范围外的元素不会提前加载
+# Lazy loading of images and comment plugin on the page
+lazyload:
+  enable: true
+
+  # 加载时的占位图片
+  # The placeholder image when loading
+  loading_img: /img/loading.gif
+
+  # 开启后懒加载仅在文章页生效,如果自定义页面需要使用,可以在 Front-matter 里指定 `lazyload: true`
+  # If true, only enable lazyload on the post page. For custom pages, you can set 'lazyload: true' in front-matter
+  onlypost: false
+
+  # 触发加载的偏移倍数,基数是视窗高度,可根据部署环境的请求速度调节
+  # The factor of viewport height that triggers loading
+  offset_factor: 2
+
+# 图标库,包含了大量社交类图标,主题依赖的不包含在内,因此可自行修改,详见 https://hexo.fluid-dev.com/docs/icon/
+# Icon library, which includes many social icons, does not include those theme dependent, so your can modify link by yourself. See: https://hexo.fluid-dev.com/docs/en/icon/
+iconfont: //at.alicdn.com/t/font_1736178_lbnruvf0jn.css
+
+#---------------------------
+# 页头
+# Header
+#---------------------------
+
+# 导航栏的相关配置
+# Navigation bar
+navbar:
+  # 导航栏左侧的标题,为空则按 hexo config 中 `title` 显示
+  # The title on the left side of the navigation bar. If empty, it is based on `title` in hexo config
+  blog_title: ""
+
+  # 导航栏毛玻璃特效,实验性功能,可能会造成页面滚动掉帧和抖动,部分浏览器不支持会自动不生效
+  # Navigation bar frosted glass special animation. It is an experimental feature
+  ground_glass:
+    enable: false
+
+    # 模糊像素,只能为数字,数字越大模糊度越高
+    # Number of blurred pixel. the larger the number, the higher the blur
+    px: 3
+
+    # 不透明度,数字越大透明度越低,注意透明过度可能看不清菜单字体
+    # Ratio of opacity, 1.0 is completely opaque
+    # available: 0 - 1.0
+    alpha: 0.7
+
+  # 导航栏菜单,可自行增减,key 用来关联 languages/*.yml,如不存在关联则显示 key 本身的值;icon 是 css class,可以省略;增加 name 可以强制显示指定名称
+  # Navigation bar menu. `key` is used to associate languages/*.yml. If there is no association, the value of `key` itself will be displayed; if `icon` is a css class, it can be omitted; adding `name` can force the display of the specified name
+  menu:
+    - { key: "home", link: "/", icon: "iconfont icon-home-fill" }
+    - { key: "archive", link: "/archives/", icon: "iconfont icon-archive-fill" }
+    - {
+        key: "category",
+        link: "/categories/",
+        icon: "iconfont icon-category-fill",
+      }
+    - { key: "tag", link: "/tags/", icon: "iconfont icon-tags-fill" }
+    - { key: "about", link: "/about/", icon: "iconfont icon-user-fill" }
+    - { key: "links", link: "/links/", icon: "iconfont icon-link-fill" }
+
+# 搜索功能,基于 hexo-generator-search 插件,若已安装其他搜索插件请关闭此功能,以避免生成多余的索引文件
+# Search feature, based on hexo-generator-search. If you have installed other search plugins, please disable this feature to avoid generating redundant index files
+search:
+  enable: true
+
+  # 搜索索引文件的路径,可以是相对路径或外站的绝对路径
+  # Path for search index file, it can be a relative path or an absolute path
+  path: /local-search.xml
+
+  # 文件生成在本地的位置,必须是相对路径
+  # The location where the index file is generated locally, it must be a relative location
+  generate_path: /local-search.xml
+
+  # 搜索的范围
+  # Search field
+  # Options: post | page | all
+  field: post
+
+  # 搜索是否扫描正文
+  # If true, search will scan the post content
+  content: true
+
+# 首屏图片的相关配置
+# Config of the big image on the first screen
+banner:
+  # 视差滚动,图片与板块会随着屏幕滚动产生视差效果
+  # Scrolling parallax
+  parallax: true
+
+  # 图片最小的宽高比,以免图片两边被过度裁剪,适用于移动端竖屏时,如需关闭设为 0
+  # Minimum ratio of width to height, applicable to the vertical screen of mobile device, if you need to close it, set it to 0
+  width_height_ratio: 1.0
+
+# 向下滚动的箭头
+# Scroll down arrow
+scroll_down_arrow:
+  enable: true
+
+  # 头图高度不小于指定比例,才显示箭头
+  # Only the height of the banner image is greater than the ratio, the arrow is displayed
+  # Available: 0 - 100
+  banner_height_limit: 80
+
+  # 翻页后自动滚动
+  # Auto scroll after page turning
+  scroll_after_turning_page: true
+
+# 向顶部滚动的箭头
+# Scroll top arrow
+scroll_top_arrow:
+  enable: true
+
+# Open Graph metadata
+# See: https://hexo.io/docs/helpers.html#open-graph
+open_graph:
+  enable: true
+  twitter_card: summary_large_image
+  twitter_id:
+  twitter_site:
+  google_plus:
+  fb_admins:
+  fb_app_id:
+
+#---------------------------
+# 页脚
+# Footer
+#---------------------------
+footer:
+  # 页脚第一行文字的 HTML,建议保留 Fluid 的链接,用于向更多人推广本主题
+  # HTML of the first line of the footer, it is recommended to keep the Fluid link to promote this theme to more people
+  content: '
+    <span>Copyright © 2017-2022 RegMs If.</span>
+    <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a>
+    <i class="iconfont icon-love"></i>
+    <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a>
+  '
+
+  # 展示网站的 PV、UV 统计数
+  # Display website PV and UV statistics
+  statistics:
+    enable: false
+
+    # 统计数据来源,使用 leancloud 需要设置 `web_analytics: leancloud` 中的参数;使用 busuanzi 不需要额外设置,但是有时不稳定,另外本地运行时 busuanzi 显示统计数据很大属于正常现象,部署后会正常
+    # Data source. If use leancloud, you need to set the parameter in `web_analytics: leancloud`
+    # Options: busuanzi | leancloud
+    source: "busuanzi"
+
+    # 页面显示的文本,{}是数字的占位符(必须包含),下同
+    # Displayed text, {} is a placeholder for numbers (must be included), the same below
+    pv_format: "总访问量 {} 次"
+    uv_format: "总访客数 {} 人"
+
+  # 国内大陆服务器的备案信息
+  # For Chinese mainland website policy, other areas keep disable
+  beian:
+    enable: false
+    # ICP证号
+    icp_text: 京ICP证123456号
+    # 公安备案号,不填则只显示ICP
+    police_text: 京公网安备12345678号
+    # 公安备案的编号,用于URL跳转查询
+    police_code: 12345678
+    # 公安备案的图片. 为空时不显示备案图片
+    police_icon: /img/police_beian.png
+
+#---------------------------
+# 首页
+# Home Page
+#---------------------------
+index:
+  # 首页 Banner 头图,可以是相对路径或绝对路径,以下相同
+  # Path of Banner image, can be a relative path or an absolute path, the same on other pages
+  banner_img: /img/default.png
+
+  # 头图高度,屏幕百分比
+  # Height ratio of banner image
+  # Available: 0 - 100
+  banner_img_height: 100
+
+  # 头图黑色蒙版的不透明度,available: 0 - 1.0, 1 是完全不透明
+  # Opacity of the banner mask, 1.0 is completely opaque
+  # Available: 0 - 1.0
+  banner_mask_alpha: 0.3
+
+  # 首页副标题的独立设置
+  # Independent config of home page subtitle
+  slogan:
+    enable: true
+
+    # 为空则按 hexo config.subtitle 显示
+    # If empty, text based on `subtitle` in hexo config
+    text: ""
+
+    # 通过 API 接口作为首页副标题的内容,必须返回的是 JSON 格式,如果请求失败则按 text 字段显示,该功能必须先开启 typing 打字机功能
+    # Subtitle of the homepage through the API, must be returned a JSON. If the request fails, it will be displayed in `text` value. This feature must first enable the typing animation
+    api:
+      enable: false
+
+      # 请求地址
+      # Request url
+      url: ""
+
+      # 请求方法
+      # Request method
+      # Available: GET | POST | PUT
+      method: "GET"
+
+      # 请求头
+      # Request headers
+      headers: {}
+
+      # 从请求结果获取字符串的取值字段,最终必须是一个字符串,例如返回结果为 {"data": {"author": "fluid", "content": "An elegant theme"}}, 则取值字段为 ['data', 'content'];如果返回是列表则自动选择第一项
+      # The value field of the string obtained from the response. For example, the response content is {"data": {"author": "fluid", "content": "An elegant theme"}}, the expected `keys: ['data','content']`; if the return is a list, the first item is automatically selected
+      keys: []
+
+  # 自动截取文章摘要
+  # Auto extract post
+  auto_excerpt:
+    enable: true
+
+  # 打开文章的标签方式
+  # The browser tag to open the post
+  # Available: _blank | _self
+  post_url_target: _self
+
+  # 是否显示文章信息(时间、分类、标签)
+  # Meta information of post
+  post_meta:
+    date: true
+    category: true
+    tag: true
+
+  # 文章通过 sticky 排序后,在首页文章标题前显示图标
+  # If the posts are sorted by `sticky`, an icon is displayed in front of the post title
+  post_sticky:
+    enable: true
+    icon: "iconfont icon-top"
+
+#---------------------------
+# 文章页
+# Post Page
+#---------------------------
+post:
+  banner_img: /img/default.png
+  banner_img_height: 70
+  banner_mask_alpha: 0.3
+
+  # 文章在首页的默认封面图,当没有指定 index_img 时会使用该图片,若两者都为空则不显示任何图片
+  # Path of the default post cover when `index_img` is not set. If both are empty, no image will be displayed
+  default_index_img:
+
+  # 文章标题下方的元信息
+  # Meta information below title
+  meta:
+    # 作者,优先根据 front-matter 里 author 字段,其次是 hexo 配置中 author 值
+    # Author, based on `author` field in front-matter, if not set, based on `author` value in hexo config
+    author:
+      enable: false
+
+    # 文章日期,优先根据 front-matter 里 date 字段,其次是 md 文件日期
+    # Post date, based on `date` field in front-matter, if not set, based on create date of .md file
+    date:
+      enable: true
+      # 格式参照 ISO-8601 日期格式化
+      # ISO-8601 date format
+      # See: http://momentjs.cn/docs/#/parsing/string-format/
+      format: "LL a"
+
+    # 字数统计
+    # Word count
+    wordcount:
+      enable: true
+      # 显示的文本,{}是数字的占位符(必须包含),下同
+      # Displayed text, {} is a placeholder for numbers (must be included), the same below
+      format: "{} 字"
+
+    # 估计阅读全文需要的时长
+    # Estimated reading time
+    min2read:
+      enable: true
+      format: "{} 分钟"
+      # 每个字词的长度,建议:中文≈2,英文≈5,中英混合可自行调节
+      # Average word length (chars count in word), ZH ≈ 2, EN ≈ 5
+      awl: 2
+      # 每分钟阅读字数,如果大部分是技术文章可适度调低
+      # Words per minute
+      wpm: 60
+
+    # 浏览量计数
+    # Number of visits
+    views:
+      enable: false
+      # 统计数据来源
+      # Data Source
+      # Options: busuanzi | leancloud
+      source: "busuanzi"
+      format: "{} 次"
+
+  # 在文章开头显示文章更新时间,该时间默认是 md 文件更新时间,可通过 front-matter 中 `updated` 手动指定(和 date 一样格式)
+  # Update date is displayed at the beginning of the post. The default date is the update date of the md file, which can be manually specified by `updated` in front-matter (same format as date)
+  updated:
+    enable: false
+
+    # 描述文字
+    # Descriptive text before date
+    content: 本文最后更新于:
+
+    # 是否使用相对时间表示,比如:"3 天前"
+    # If true, it will be a relative time, such as: "3 days ago"
+    relative: false
+
+  # 文章右侧目录
+  # Table of contents (TOC)
+  toc:
+    enable: true
+    # 目录会选择这些节点作为标题
+    # TOC will select these nodes as headings
+    headingSelector: "h1,h2,h3,h4,h5,h6"
+    # 层级的折叠深度,0 是全部折叠,大于 0 后如果存在下级标题则默认展开
+    # Collapse depth. If 0, all headings collapsed. If greater than 0, it will be expanded by default if there are sub headings
+    collapseDepth: 0
+
+  # 版权声明,会显示在每篇文章的结尾
+  # Copyright, will be displayed at the end of each post
+  copyright:
+    enable: true
+    content: '本博客所有文章除特别声明外,均采用 <a href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" rel="nofollow noopener">CC BY-SA 4.0 协议</a> ,转载请注明出处!'
+
+  # 文章底部上一篇下一篇功能
+  # Link to previous/next post
+  prev_next:
+    enable: true
+
+  # 文章底部自定义区域(位于 footer 上方),支持 HTML,可插入赞赏码、公众号这类内容内容
+  # Custom content at the bottom of the post page (located above the footer)
+  custom:
+    enable: false
+    content: '<img src="https://octodex.github.com/images/jetpacktocat.png" class="rounded mx-auto d-block mt-5" style="width:150px; height:150px;">'
+
+  # 文章图片可点击放大
+  # Zoom feature of images
+  image_zoom:
+    enable: true
+    # 放大后图片链接替换规则,可用于将压缩图片链接替换为原图片链接,如 ['-slim', ''] 是将链接中 `-slim` 移除;如果想使用正则请使用 `re:` 前缀,如 ['re:\\d{3,4}\\/\\d{3,4}\\/', '']
+    # The image url replacement when zooming, the feature can be used to replace the compressed image to the original image, eg: ['-slim', ''] removes `-slim` from the image url when zooming; if you want to use regular, use prefix `re:`, eg: ['re:\\d{3,4}\\/\\d{3,4}\\/','']
+    img_url_replace: ["", ""]
+
+  # 脚注语法,会在文章底部生成脚注,如果 Markdown 渲染器本身支持,则建议关闭,否则可能会冲突
+  # Support footnote syntax, footnotes will be generated at the bottom of the post page. If the Markdown renderer itself supports it, please disable it, otherwise it may conflict
+  footnote:
+    enable: true
+    # 脚注的节标题,也可以在 front-matter 中通过 `footnote: <h2>Reference</h2>` 这种形式修改单独页面的 header
+    # The section title of the footnote, you can also modify the header of a single page in the form of `footnote: <h2>Reference</h2>` in front-matter
+    header: ""
+
+  # 数学公式,开启之前需要更换 Markdown 渲染器,否则复杂公式会有兼容问题,具体请见:https://hexo.fluid-dev.com/docs/guide/##latex-数学公式
+  # Mathematical formula. If enable, you need to change the Markdown renderer, see: https://hexo.fluid-dev.com/docs/en/guide/#math
+  math:
+    # 开启后文章默认可用,自定义页面如需使用,需在 Front-matter 中指定 `math: true`
+    # If you want to use math on the custom page, you need to set `math: true` in Front-matter
+    enable: true
+
+    # 开启后,只有在文章 Front-matter 里指定 `math: true` 才会在文章页启动公式转换,以便在页面不包含公式时提高加载速度
+    # If true, only set `math: true` in Front-matter will enable math, to load faster when the page does not contain math
+    specific: false
+
+    # Options: mathjax | katex
+    engine: mathjax
+
+  # 流程图,基于 mermaid-js,具体请见:https://hexo.fluid-dev.com/docs/guide/#mermaid-流程图
+  # Flow chart, based on mermaid-js, see: https://hexo.fluid-dev.com/docs/en/guide/#mermaid
+  mermaid:
+    # 开启后文章默认可用,自定义页面如需使用,需在 Front-matter 中指定 `mermaid: true`
+    # If you want to use mermaid on the custom page, you need to set `mermaid: true` in Front-matter
+    enable: false
+
+    # 开启后,只有在文章 Front-matter 里指定 `mermaid: true` 才会在文章页启动公式转换,以便在页面不包含公式时提高加载速度
+    # If true, only set `mermaid: true` in Front-matter will enable mermaid, to load faster when the page does not contain mermaid
+    specific: false
+
+    # See: http://mermaid-js.github.io/mermaid/
+    options: { theme: "default" }
+
+  # 评论插件
+  # Comment plugin
+  comments:
+    enable: false
+    # 指定的插件,需要同时设置对应插件的必要参数
+    # The specified plugin needs to set the necessary parameters at the same time
+    # Options: utterances | disqus | gitalk | valine | waline | changyan | livere | remark42 | twikoo | cusdis
+    type: disqus
+
+#---------------------------
+# 评论插件
+# Comment plugins
+#
+# 开启评论需要先设置上方 `post: comments: enable: true`,然后根据 `type` 设置下方对应的评论插件参数
+# Enable comments need to be set `post: comments: enable: true`, then set the corresponding comment plugin parameters below according to `type`
+#---------------------------
+
+# Utterances
+# 基于 GitHub Issues
+# Based on GitHub Issues
+# See: https://utteranc.es
+utterances:
+  repo:
+  issue_term: pathname
+  label: utterances
+  theme: github-light
+  theme_dark: github-dark
+  crossorigin: anonymous
+
+# Disqus
+# 基于第三方的服务,国内用户直接使用容易被墙,建议配合 Disqusjs
+# Based on third-party service
+# See: https://disqus.com
+disqus:
+  shortname:
+  # 以下为 Disqusjs 支持, 国内用户如果想使用 Disqus 建议配合使用
+  # The following are Disqusjs configurations, please ignore if DisqusJS is not required
+  # See: https://github.com/SukkaW/DisqusJS
+  disqusjs: false
+  apikey:
+
+# Gitalk
+# 基于 GitHub Issues
+# Based on GitHub Issues
+# See: https://github.com/gitalk/gitalk#options
+gitalk:
+  clientID:
+  clientSecret:
+  repo:
+  owner:
+  admin: ["name"]
+  language: zh-CN
+  labels: ["Gitalk"]
+  perPage: 10
+  pagerDirection: last
+  distractionFreeMode: false
+  createIssueManually: true
+  # 默认 proxy 可能会失效,解决方法请见下方链接
+  # The default proxy may be invalid, refer to the links for solutions
+  # https://github.com/gitalk/gitalk/issues/429
+  # https://github.com/Zibri/cloudflare-cors-anywhere
+  proxy: https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token
+
+# Valine
+# 基于 LeanCloud
+# Based on LeanCloud
+# See: https://valine.js.org/
+valine:
+  appId:
+  appKey:
+  path: window.location.pathname
+  placeholder:
+  avatar: "retro"
+  meta: ["nick", "mail", "link"]
+  requiredFields: []
+  pageSize: 10
+  lang: "zh-CN"
+  highlight: false
+  recordIP: false
+  serverURLs: ""
+  emojiCDN:
+  emojiMaps:
+  enableQQ: false
+
+# Waline
+# 从 Valine 衍生而来,额外增加了服务端和多种功能
+# Derived from Valine, with self-hosted service and new features
+# See: https://waline.js.org/
+waline:
+  serverURL: ""
+  path: window.location.pathname
+  placeholder:
+  meta: ["nick", "mail", "link"]
+  requiredMeta: ["nick"]
+  lang: "zh-CN"
+  emoji: ["https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo"]
+  dark: 'html[data-user-color-scheme="dark"]'
+  avatar: "retro"
+  avatarCDN: "https://seccdn.libravatar.org/avatar/"
+  avatarForce: false
+  wordLimit: 0
+  pageSize: 10
+  highlight: true
+
+# 畅言 Changyan
+# 基于第三方的服务
+# Based on third-party service, insufficient support for regions outside China
+# http://changyan.kuaizhan.com
+changyan:
+  appid: ""
+  appkey: ""
+
+# 来必力 Livere
+# 基于第三方的服务
+# Based on third-party service
+# See: https://www.livere.com
+livere:
+  uid: ""
+
+# Remark42
+# 需要自托管服务端
+# Based on self-hosted service
+# See: https://remark42.com
+remark42:
+  host:
+  site_id:
+  max_shown_comments: 10
+  locale: zh
+  components: ["embed"]
+
+# Twikoo
+# 基于腾讯云开发
+# Based on Tencent CloudBase
+# See: https://twikoo.js.org
+twikoo:
+  envId:
+  region: ap-shanghai
+  path: window.location.pathname
+
+# Cusdis
+# 基于第三方服务或自托管服务
+# Based on third-party or self-hosted service
+# See https://cusdis.com
+cusdis:
+  host:
+  app_id:
+  lang: zh-cn
+
+#---------------------------
+# 归档页
+# Archive Page
+#---------------------------
+archive:
+  banner_img: /img/default.png
+  banner_img_height: 60
+  banner_mask_alpha: 0.3
+  subtitle:
+
+#---------------------------
+# 分类页
+# Category Page
+#---------------------------
+category:
+  enable: true
+  banner_img: /img/default.png
+  banner_img_height: 60
+  banner_mask_alpha: 0.3
+  subtitle:
+
+  # 分类的排序字段,前面带减号是倒序,不带减号是正序
+  # Sort field for categories, with a minus sign is reverse order
+  # Options: length | name
+  order_by: "-length"
+
+  # 层级的折叠深度,0 是全部折叠,大于 0 后如果存在子分类则默认展开
+  # Collapse depth. If 0, all posts collapsed. If greater than 0, it will be expanded by default if there are subcategories
+  collapse_depth: 0
+
+  # 文章的排序字段,前面带减号是倒序,不带减号是正序
+  # Sort field for posts, with a minus sign is reverse order
+  # Options: date | title | or other field of front-matter
+  post_order_by: "-date"
+
+  # 单个分类中折叠展示文章数的最大值,超过限制会显示 More,0 则不限制
+  # The maximum number of posts in a single category. If the limit is exceeded, it will be displayed More. If 0 no limit
+  post_limit: 10
+
+#---------------------------
+# 标签页
+# Tag Page
+#---------------------------
+tag:
+  enable: true
+  banner_img: /img/default.png
+  banner_img_height: 80
+  banner_mask_alpha: 0.3
+  subtitle:
+  tagcloud:
+    min_font: 15
+    max_font: 30
+    unit: px
+    start_color: "#BBBBEE"
+    end_color: "#337ab7"
+
+#---------------------------
+# 关于页
+# About Page
+#---------------------------
+about:
+  enable: true
+  banner_img: /img/default.png
+  banner_img_height: 60
+  banner_mask_alpha: 0.3
+  subtitle:
+  avatar: /img/avatar.jpg
+  name: "RegMs If"
+  intro: "418 I'm a Teapot"
+  # 更多图标可从 https://hexo.fluid-dev.com/docs/icon/ 查找,`class` 代表图标的 css class,添加 `qrcode` 后,图标不再是链接而是悬浮二维码
+  # More icons can be found from https://hexo.fluid-dev.com/docs/en/icon/  `class` is the css class of the icon. If adding `qrcode`, The icon is no longer a link, but a hovering QR code
+  icons:
+    - {
+        class: "iconfont icon-github-fill",
+        link: "https://github.com/regmsif",
+        tip: "GitHub",
+      }
+
+#---------------------------
+# 自定义页
+# Custom Page
+#
+# 通过 hexo new page 命令创建的页面
+# Custom Page through `hexo new page`
+#---------------------------
+page:
+  banner_img: /img/default.png
+  banner_img_height: 60
+  banner_mask_alpha: 0.3
+
+#---------------------------
+# 404页
+# 404 Page
+#---------------------------
+page404:
+  enable: true
+  banner_img: /img/default.png
+  banner_img_height: 85
+  banner_mask_alpha: 0.3
+  subtitle: "Page not found"
+
+#---------------------------
+# 友链页
+# Links Page
+#---------------------------
+links:
+  enable: true
+  banner_img: /img/default.png
+  banner_img_height: 60
+  banner_mask_alpha: 0.3
+  subtitle:
+  # 友链的成员项
+  # Member item of page
+  items:
+    - {
+        title: "chinakevin",
+        intro: "zjzdl",
+        link: "https://www.cnblogs.com/chinakevin",
+        avatar: "/img/chinakevin.jpg",
+      }
+    - {
+        title: "Montreal",
+        intro: "天下第一寝室长",
+        link: "https://blog.moontreal.cf",
+        avatar: "/img/montreal.jpg",
+      }
+
+  # 当成员头像加载失败时,替换为指定图片
+  # When the member avatar fails to load, replace the specified image
+  onerror_avatar: /img/avatar.jpg
+
+  # 友链下方自定义区域,支持 HTML,可插入例如申请友链的文字
+  # Custom content at the bottom of the links
+  custom:
+    enable: false
+    content: "<hr><p>在下方留言申请加入我的友链,按如下格式提供信息:</p><ul><li>博客名:Fluid</li><li>简介:Fluid 主题官方博客</li><li>链接:https://hexo.fluid-dev.com</li><li>图片:https://hexo.fluid-dev.com/img/favicon.png</li></ul>"
+
+  # 评论插件
+  # Comment plugin
+  comments:
+    enable: false
+    # 指定的插件,需要同时设置对应插件的必要参数
+    # The specified plugin needs to set the necessary parameters at the same time
+    # Options: utterances | disqus | gitalk | valine | waline | changyan | livere | remark42 | twikoo | cusdis
+    type: disqus
+
+#---------------------------
+# 以下是配置 JS CSS 等静态资源的 URL 前缀,可以自定义成 CDN 地址,
+# 默认的 jsDelivr CDN 可能在部分地区无法访问,如果需要修改,最好使用与默认配置相同的版本,以避免潜在的问题,
+# ** 如果你不知道如何设置,请不要做任何改动 **
+#
+# Here is the url prefix to configure the static assets. Set CDN addresses you want to customize.
+# Be aware that you would better use the same version as default ones to avoid potential problems.
+# DO NOT EDIT THE FOLLOWING SETTINGS UNLESS YOU KNOW WHAT YOU ARE DOING
+#---------------------------
+
+static_prefix:
+  # 内部静态
+  # Internal static
+  internal_js: /js
+  internal_css: /css
+  internal_img: /img
+
+  anchor: https://cdn.jsdelivr.net/npm/anchor-js@4/
+
+  github_markdown: https://cdn.jsdelivr.net/npm/github-markdown-css@4/
+
+  jquery: https://cdn.jsdelivr.net/npm/jquery@3/dist/
+
+  bootstrap: https://cdn.jsdelivr.net/npm/bootstrap@4/dist/
+
+  highlightjs: https://cdn.jsdelivr.net/npm/highlight.js@10/
+
+  prismjs: https://cdn.jsdelivr.net/npm/prismjs@1/
+
+  tocbot: https://cdn.jsdelivr.net/npm/tocbot@4/dist/
+
+  typed: https://cdn.jsdelivr.net/npm/typed.js@2/lib/
+
+  fancybox: https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3/dist/
+
+  nprogress: https://cdn.jsdelivr.net/npm/nprogress@0/
+
+  mathjax: https://cdn.jsdelivr.net/npm/mathjax@3/es5/
+
+  katex: https://cdn.jsdelivr.net/npm/katex@0/dist/
+
+  busuanzi: https://busuanzi.ibruce.info/busuanzi/2.3/
+
+  clipboard: https://cdn.jsdelivr.net/npm/clipboard@2/dist/
+
+  mermaid: https://cdn.jsdelivr.net/npm/mermaid@8/dist/
+
+  valine: https://cdn.jsdelivr.net/npm/valine@1/dist/
+
+  waline: https://cdn.jsdelivr.net/npm/@waline/client@1/dist/
+
+  gitalk: https://cdn.jsdelivr.net/npm/gitalk@1/dist/
+
+  disqusjs: https://cdn.jsdelivr.net/npm/disqusjs@1/dist/
+
+  twikoo: https://cdn.jsdelivr.net/npm/twikoo@1/dist/
+
+  hint: https://cdn.jsdelivr.net/npm/hint.css@2/

+ 0 - 0
_config.landscape.yml


+ 108 - 0
_config.yml

@@ -0,0 +1,108 @@
+# Hexo Configuration
+## Docs: https://hexo.io/docs/configuration.html
+## Source: https://github.com/hexojs/hexo/
+
+# Site
+title: If7's Home
+subtitle: "There's Something New"
+description: ""
+keywords:
+author: RegMs If
+language: zh-CN
+timezone: Asia/Shanghai
+
+# URL
+## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project'
+url: https://regmsif.cf
+permalink: :year/:month/:day/:title/
+permalink_defaults:
+pretty_urls:
+  trailing_index: true # Set to false to remove trailing 'index.html' from permalinks
+  trailing_html: true # Set to false to remove trailing '.html' from permalinks
+
+# Directory
+source_dir: source
+public_dir: public
+tag_dir: tags
+archive_dir: archives
+category_dir: categories
+code_dir: downloads/code
+i18n_dir: :lang
+skip_render:
+
+# Writing
+new_post_name: :title.md # File name of new posts
+default_layout: post
+titlecase: false # Transform title into titlecase
+external_link:
+  enable: true # Open external links in new tab
+  field: site # Apply to the whole site
+  exclude: ""
+filename_case: 0
+render_drafts: false
+post_asset_folder: true
+marked:
+  prependRoot: true
+  postAsset: true
+relative_link: false
+future: true
+highlight:
+  enable: true
+  line_number: true
+  auto_detect: false
+  tab_replace: ""
+  wrap: true
+  hljs: false
+prismjs:
+  enable: false
+  preprocess: true
+  line_number: true
+  tab_replace: ""
+
+# Home page setting
+# path: Root path for your blogs index page. (default = '')
+# per_page: Posts displayed per page. (0 = disable pagination)
+# order_by: Posts order. (Order by date descending by default)
+index_generator:
+  path: ""
+  per_page: 10
+  order_by: -date
+
+# Category & Tag
+default_category: uncategorized
+category_map:
+tag_map:
+
+# Metadata elements
+## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
+meta_generator: true
+
+# Date / Time format
+## Hexo uses Moment.js to parse and display date
+## You can customize the date format as defined in
+## http://momentjs.com/docs/#/displaying/format/
+date_format: YYYY-MM-DD
+time_format: HH:mm:ss
+## updated_option supports 'mtime', 'date', 'empty'
+updated_option: "mtime"
+
+# Pagination
+## Set per_page to 0 to disable pagination
+per_page: 10
+pagination_dir: page
+
+# Include / Exclude file(s)
+## include:/exclude: options only apply to the 'source/' folder
+include:
+exclude:
+ignore:
+
+# Extensions
+## Plugins: https://hexo.io/plugins/
+## Themes: https://hexo.io/themes/
+theme: fluid
+
+# Deployment
+## Docs: https://hexo.io/docs/one-command-deployment
+deploy:
+  type: ""

+ 28 - 0
package.json

@@ -0,0 +1,28 @@
+{
+  "name": "hexo-site",
+  "version": "0.0.0",
+  "private": true,
+  "scripts": {
+    "build": "hexo generate",
+    "clean": "hexo clean",
+    "deploy": "hexo deploy",
+    "server": "hexo server"
+  },
+  "hexo": {
+    "version": "6.1.0"
+  },
+  "dependencies": {
+    "hexo": "^6.0.0",
+    "hexo-generator-archive": "^1.0.0",
+    "hexo-generator-category": "^1.0.0",
+    "hexo-generator-index": "^2.0.0",
+    "hexo-generator-tag": "^1.0.0",
+    "hexo-renderer-ejs": "^2.0.0",
+    "hexo-renderer-marked": "^5.0.0",
+    "hexo-renderer-stylus": "^2.0.0",
+    "hexo-server": "^3.0.0",
+    "hexo-theme-fluid": "^1.8.14",
+    "hexo-theme-landscape": "^0.0.3",
+    "nunjucks": "^3.2.3"
+  }
+}

+ 4 - 0
scaffolds/draft.md

@@ -0,0 +1,4 @@
+---
+title: {{ title }}
+tags:
+---

+ 4 - 0
scaffolds/page.md

@@ -0,0 +1,4 @@
+---
+title: {{ title }}
+date: {{ date }}
+---

+ 5 - 0
scaffolds/post.md

@@ -0,0 +1,5 @@
+---
+title: {{ title }}
+date: {{ date }}
+tags:
+---

+ 384 - 0
source/_posts/coding/archlinux-installation-guide.md

@@ -0,0 +1,384 @@
+---
+title: Archlinux Installation Guide
+tags:
+  - Linux
+id: "1543"
+categories:
+  - - Coding
+    - Operating System
+date: 2017-10-18 20:00:54
+---
+
+## 准备工作
+
+### 镜像下载 && 启动盘制作
+
+镜像文件可以直接从[官方网站](https://www.archlinux.org/download/)上下载。
+
+下载完成后,Windows 用户可以用 Ultraiso 制作启动盘,Linux 用户可以用 dd 制作启动盘,具体如下:
+
+```
+# dd if=*.iso of=/dev/sd*
+```
+
+其中`if`表示输入文件,`of`表示输出文件,`/dev/sd*`表示 U 盘。具体参数要根据实际情况确定。
+
+### 开始安装 && 分区操作
+
+使用 U 盘启动系统,进入命令行界面后,用以下命令查看硬盘状态:
+
+```
+# parted -l
+```
+
+从硬盘列表中找到希望安装到的硬盘,假设为`/dev/sda`,那么可以用以下命令进行分区操作:
+
+```
+# parted /dev/sda
+```
+
+一般来说,Linux 只需要分出`/`主分区和 swap 分区,当然也可以根据个人需要分出`/home`、`/boot`等分区。对于内存在 2GiB 以下的电脑,建议将 swap 分区的大小设为实际内存大小的 2 倍,其他电脑只需设为实际内存大小。关于 parted 的使用,这里不再赘述。
+
+分区后需进行格式化。假设将`/dev/sda1`设为`/`主分区,`/dev/sda2`设为`/home`分区,`/dev/sda3`设为 swap 分区,执行以下命令来格式化:
+
+```
+# mkfs.ext4 /dev/sda1
+# mkfs.ext4 /dev/sda2
+# mkswap /dev/sda3
+# swapon /dev/sda3
+```
+
+将分区挂载到 Linux 根目录下:
+
+```
+# mount /dev/sda1 /mnt
+# mkdir /mnt/home
+# mount /dev/sda2 /mnt/home
+```
+
+如果 BIOS 是 UEFI 的,还需要用 parted 创建 EFI 分区(假设为`/dev/sda4`),并进行以下操作:
+
+```
+# mkfs.vfat -F32 /dev/sda4
+# mkdir -p /mnt/boot/efi
+# mount /dev/sda4 /mnt/boot/efi
+```
+
+准备工作完成。
+
+## 安装系统
+
+安装过程中需要网络,可以使用以下命令连接 WiFi:
+
+```
+# wifi-menu
+```
+
+有线网的连接方法将在后面提及。
+
+使用以下命令编辑`/etc/pacman.conf`(vim 使用方法不再赘述):
+
+```
+# vim /etc/pacman.conf
+```
+
+在文件最后添加一段:
+
+```
+[archlinuxcn]
+SigLevel = Optional TrustAll
+Server   = https://mirrors.ustc.edu.cn/archlinuxcn/$arch
+```
+
+接着,需要将`/etc/pacman.d/mirrorlist`中非 China 的源删去(因为外国源访问速度较慢)。
+
+使用以下命令更新源:
+
+```
+# pacman -Syy
+```
+
+安装基本系统:
+
+```
+# pacstrap /mnt base base-devel
+```
+
+此过程持续时间较长,需要耐心等待。
+
+生成 fstab:
+
+```
+# genfstab -U -p /mnt >> /mnt/etc/fstab
+```
+
+切换主目录:
+
+```
+# arch-chroot /mnt /bin/bash
+```
+
+这时命令提示符会发生变化。首先安装 vim:
+
+```
+pacman -S vim
+```
+
+接下来设置语言环境,创建`/etc/locale.conf`,添加一行`LANG=en_US.UTF-8`,修改`/etc/locale.gen`,把`en_US.UTF-8 UTF-8`、`zh_CN.GBK GBK`、`zh_CN.UTF-8 UTF-8`和`zh_CN GB2312`前面的注释去掉。使用以下命令更新语言环境:
+
+```
+# locale-gen
+```
+
+设置时间:
+
+```
+# rm /etc/localtime
+# ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
+# hwclock --systohc --utc
+```
+
+设置主机名以及 root 密码:
+
+```
+# vim /etc/hostname
+# passwd
+```
+
+安装 net 工具:
+
+```
+# pacman -S net-tools dnsutils inetutils iproute2 dialog
+```
+
+安装 GRUB:
+
+For BIOS:
+
+```
+# pacman -S grub os-prober
+# grub-install --recheck /dev/sda
+# grub-mkconfig -o /boot/grub/grub.cfg
+```
+
+For UEFI:
+
+```
+# pacman -S dosfstools grub efibootmgr
+# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=arch_grub --recheck
+# grub-mkconfig -o /boot/grub/grub.cfg
+```
+
+卸载分区:
+
+```
+# exit
+# umount /mnt/home
+# umount /mnt
+# reboot
+```
+
+安装系统完成。重启后,输入 root 和密码即可进入系统。
+
+## 基本配置
+
+配置有线网络:
+
+使用 ifconfig 查看网卡信息,假设有线网卡名为`eth0`。创建`/etc/systemd/network/eth0.network`,添加以下内容:
+
+```
+[Match]
+Name=eth0
+[Network]
+Address=192.168.1.100/24
+Gateway=192.168.1.1
+```
+
+创建`/etc/systemd/resolved.conf`,添加以下内容:
+
+```
+[Resolve]
+DNS=114.114.114.114
+```
+
+执行以下命令:
+
+```
+# rm -f /etc/resolv.conf
+# ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
+# systemctl disable netctl.service
+# systemctl enable systemd-networkd.service
+# systemctl enable systemd-resolved.service
+# reboot
+```
+
+即可正常使用有线网。
+
+添加用户:
+
+```
+# useradd -m -k /etc/skel -G users,wheel YourName
+# passwd YourName
+```
+
+安装 Xorg 以及字体:
+
+```
+# pacman -S xorg xorg-server xorg-xinit xorg-twm xtrem ttf-dejavu wqy-zenhei wqy-microhei
+```
+
+安装触摸板驱动:
+
+```
+# pacman -S xf86-input-libinput xorg-xinput
+# libinput list-devices
+```
+
+安装显卡驱动:
+
+For Intel:
+
+```
+# pacman -S xf86-video-intel
+```
+
+For NVIDIA:
+
+```
+# pacman -S xf86-video-nouveau
+```
+
+For ATi:
+
+```
+# pacman -S xf86-video-ati
+```
+
+安装压缩软件:
+
+```
+# pacman -S p7zip zip unzip rar unrar
+```
+
+安装 NTFS 支持:
+
+```
+# pacman -S ntfs-3g
+```
+
+同步网络时间:
+
+```
+# pacman -S ntp
+# systemctl enable ntpd
+```
+
+安装桌面环境:
+
+For GNOME:
+
+```
+# pacman -S gnome gnome-extra gdm
+# systemctl enable gdm
+```
+
+For KDE:
+
+```
+# pacman -S plasma sddm
+# systemctl enable sddm
+```
+
+For Deepin:
+
+```
+# pacman -S deepin deepin-extra lightdm
+```
+
+For i3wm:
+
+```
+# pacman -S i3-gaps i3status i3blocks i3lock
+```
+
+配置 startx:
+
+编辑`/etc/X11/xinit/xinitrc`,注释掉以下内容:
+
+```
+twm &
+xclock -geometry 50x50-1+1 &
+xterm -geometry 80x50+494+51 &
+xterm -geometry 80x20+494-0 &
+exec xterm -geometry 80x66+0+0 -name login
+```
+
+添加以下内容:
+
+For GNOME:
+
+```
+exec gnome-session
+```
+
+For KDE:
+
+```
+exec startkde
+```
+
+For i3wm:
+
+```
+exec i3
+```
+
+保存后就可以用`startx`进入桌面了。
+
+## 后续优化
+
+### yaourt
+
+在`/etc/pacman.conf`最后添加一段:
+
+```
+[archlinuxcn]
+SigLevel = Optional TrustAll
+Server   = https://mirrors.ustc.edu.cn/archlinuxcn/$arch
+```
+
+更新源,然后执行:
+
+```
+# pacman -S yaourt
+```
+
+### fcitx
+
+使用以下命令安装 fcitx:
+
+```
+# pacman -S fcitx fcitx-im fcitx-libpinyin
+```
+
+创建`~/.xprofile`,添加一段:
+
+```
+export GTK_IM_MODULE=fcitx
+export QT_IM_MODULE=fcitx
+export XMODIFIERS="@im=fcitx"
+```
+
+然后使用 fcitx 设置进行配置即可。
+
+### chromium
+
+使用以下命令安装 chromium 以及 flash 插件:
+
+```
+# pacman -S chromium pepper-flash
+```
+
+## 总结
+
+Archlinux 的核心理念就是 KISS 原则(Keep It Simple, Stupid)。这个原则就是让系统保持简单。而这里的简单却不是所谓的开箱即用(out-of-the-box),而是让默认的软件与配置“能少就少”。Arch 还拥有非常强大的包管理器 pacman 以及社区用户软件仓库 AUR,软件几乎应有尽有。Arch 的 wiki 也是所有发行版中做的最好最全面的。它还支持滚动升级,一次安装可以永久使用。当然,更重要的是用户可以从中学到很多:用户几乎是从零开始安装整个系统,包括硬盘分区、设置语言及时区、安装软件包等等。只有亲手操作一遍后,才会了解 Linux 发行版的安装方式。另外,因为所有软件都是用户自己安装的,如果某个软件出了问题,可以自己尝试调试,或者卸载重装,这并不会影响操作系统本身。

+ 506 - 0
source/_posts/coding/digital-image-processing-assignment-i.md

@@ -0,0 +1,506 @@
+---
+title: Digital Image Processing Assignment I
+tags:
+  - Assignment
+id: "3418"
+categories:
+  - - Coding
+    - Digital Image Processing
+date: 2019-09-19 23:15:31
+---
+
+### Main Contents
+
+- Read a color bmp;
+- RGB->YUV;
+- Color to gray: gray=Y in YUV color space;
+- Rearrange gray intensity to lie between [0,255];
+- Write a grayscale bmp;
+- Change the luminance value Y;
+- YUV->RGB;
+- Write a color bmp.
+
+### Step One: BMP File Structure
+
+A common BMP file is comprised of four part: image file header, image information header, palette and image data.
+
+The image file header is a struct whose length is 14 bytes. Here gives its definition,
+
+```c
+typedef struct tagBITMAPFILEHEADER {
+    WORD bfType;
+    DWORD bfSize;
+    WORD bfReserved1;
+    WORD bfReserved2;
+    DWORD bfOffBits;
+} BITMAPFILEHEADER;
+```
+
+and the explanation for every variable.
+
+- bfType: must always be set to 'BM' to declare that this is a .bmp-file;
+- bfSize: specifies the size of the file in bytes;
+- bfReserved1: must always be set to zero;
+- bfReserved2: must always be set to zero;
+- bfOffBits: specifies the offset from the beginning of the file to the bitmap data.
+
+The image information header is also a struct, while its length is 40 bytes. The definition
+
+```c
+typedef struct tagBITMAPINFOHEADER {
+    DWORD biSize;
+    LONG biWidth;
+    LONG biHeight;
+    WORD biPlanes;
+    WORD biBitCount
+    DWORD biCompression;
+    DWORD biSizeImage;
+    LONG biXPelsPerMeter;
+    LONG biYPelsPerMeter;
+    DWORD biClrUsed;
+    DWORD biClrImportant;
+} BITMAPINFOHEADER;
+```
+
+The explanation
+
+- biSize: number of bytes to define BITMAPINFORHEADER structure;
+- biWidth: image width (number of pixels);
+- biHeight: image height (number of pixels), note that if it's a positive number, the image is inverted, otherwise upright;
+- biPlanes: number of planes, should always be 1;
+- biBitCount: bits per pixel, which may be 1, 4, 8, 16, 24, 32;
+- biCompression: compression type, only non-compression(BI_RGB) is discussed here;
+- biSizeImage: image size with bytes, when biCompression is BI_RGB, biSizeImage is 0;
+- biXPelsPerMeter: horizontal resolution, pixels per meter;
+- biYPelsPerMeter: vertical resolution, pixels per meter;
+- biClrUsed: number of color indices used in the bitmap, when it's 0, all the palette items are used;
+- biClrImportant: number of important color indices for image display, when it's 0, all items are important.
+
+The palette has a series of RGBQUADs, which is defined like this.
+
+```c
+typedef struct tagRGBQUAD {
+    uint8_t rgbBlue;
+    uint8_t rgbGreen;
+    uint8_t rgbRed;
+    uint8_t rgbReserved;
+} RGBQUAD;
+```
+
+Note that the order of the color is **blue, green, and red**, not the reverse. The number of RGBQUADs is decided by biBitCount and biClrUsed.
+
+Next we need to define BITMAPINFO.
+
+```c
+typedef struct tagBITMAPINFO {
+    BITMAPINFOHEADER bmiHeader;
+    RGBQUAD bmiColors[1];
+} BITMAPINFO;
+```
+
+As we know nothing about the number of RGBQUADs an image uses, the BITMAPINFO should be defined as **a pointer** so that right amount of memory can be allocated to it.
+
+The image data contains color of all pixels, and every biBitCount bit(s) represents a pixel.
+
+At last, we define a struct to storage a full BMP image.
+
+```c
+typedef struct tagBITMAP {
+    BITMAPFILEHEADER bmHeader;
+    BITMAPINFO *bmInfo;
+    uint32_t bmInfoSize;
+    uint32_t bmBytesPerRow;
+    uint8_t bmBytesPerPel;
+    uint8_t *bmData;
+} BITMAP;
+```
+
+When defining the two structs, we need to add a line `#pragma pack(push, 1)` to **avoid struct padding**.
+
+### Step Two: Read/Write a BMP File
+
+A BMP file is a binary file, so we need to add `"b"` to the second parameter when using `fopen`.
+
+Another important thing is that the number of bytes in one row must always be adjusted to fit into the border of a multiple of four, and we need to calculate how many bytes are there in one row.
+
+For convenience, we define an initialize function, which receives bmHeader and bmInfo, and initializes other variables.
+
+```c
+// given bmHeader and bmInfo, initialize others
+void init_bmp(BITMAP *bmImg) {
+    BITMAPINFOHEADER *bmiHeader = &(bmImg->bmInfo->bmiHeader);
+    bmImg->bmBytesPerRow = ((bmiHeader->biWidth * bmiHeader->biBitCount + 31) >> 5) << 2;
+    bmImg->bmBytesPerPel = bmiHeader->biBitCount >> 3;
+    bmImg->bmData = (uint8_t *) malloc(bmImg->bmBytesPerRow * bmiHeader->biHeight);
+}
+```
+
+The read function is showed below.
+
+```c
+// read a BMP from file
+void read_bmp(BITMAP *bmImg, char *filepath) {
+    FILE *fiInImg = fopen(filepath, "rb");
+    BITMAPINFOHEADER bmiHeader;
+    fread(&(bmImg->bmHeader), sizeof(BITMAPFILEHEADER), 1, fiInImg);
+    fread(&bmiHeader, sizeof(BITMAPINFOHEADER), 1, fiInImg);
+    // if biBitCount is less than 16, use all the palette, otherwise do not use palette
+    if (bmiHeader.biBitCount < 16) {
+        bmImg->bmInfoSize = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << bmiHeader.biBitCount);
+    } else {
+        bmImg->bmInfoSize = sizeof(BITMAPINFOHEADER);
+    }
+    bmImg->bmInfo = (BITMAPINFO *) malloc(bmImg->bmInfoSize);
+    bmImg->bmInfo->bmiHeader = bmiHeader;
+    if (bmiHeader.biBitCount < 16) {
+        fread(bmImg->bmInfo->bmiColors, sizeof(RGBQUAD), 1 << bmiHeader.biBitCount, fiInImg);
+    }
+    init_bmp(bmImg);
+    fread(bmImg->bmData, bmImg->bmBytesPerRow, bmiHeader.biHeight, fiInImg);
+    fclose(fiInImg);
+}
+```
+
+The write function looks similar with the read function.
+
+```c
+// write a BMP to file
+void write_bmp(BITMAP *bmImg, char *filepath) {
+    FILE *fiOutImg = fopen(filepath, "wb");
+    fwrite(&(bmImg->bmHeader), sizeof(BITMAPFILEHEADER), 1, fiOutImg);
+    fwrite(bmImg->bmInfo, bmImg->bmInfoSize, 1, fiOutImg);
+    fwrite(bmImg->bmData, bmImg->bmBytesPerRow, bmImg->bmInfo->bmiHeader.biHeight, fiOutImg);
+    fclose(fiOutImg);
+}
+```
+
+### Step Three: Change Color to Gray
+
+To make things easier, we define a function to duplicate a BMP file,
+
+```c
+// duplicate a BMP
+void copy_bmp(BITMAP *bmDes, BITMAP *bmSrc) {
+    memcpy(bmDes, bmSrc, sizeof(BITMAP));
+    bmDes->bmInfo = (BITMAPINFO *) malloc(bmSrc->bmInfoSize);
+    memcpy(bmDes->bmInfo, bmSrc->bmInfo, bmSrc->bmInfoSize);
+    BITMAPINFOHEADER *bmiHeader = &(bmSrc->bmInfo->bmiHeader);
+    bmDes->bmData = (uint8_t *) malloc(bmSrc->bmBytesPerRow * bmiHeader->biHeight);
+    memcpy(bmDes->bmData, bmSrc->bmData, bmSrc->bmBytesPerRow * bmiHeader->biHeight);
+}
+```
+
+and a function to make sure the RGB value of a pixel lie between [0, 255].
+
+```c
+// make RGB value legal
+uint8_t adjust(double val) {
+    int16_t ret = (int16_t) (val + 0.5);
+    return ret < 0 ? 0 : ret > 255 ? 255 : ret;
+}
+```
+
+We need to change RGB to YUV first, as the grayscale is determined by Y value. The formula is
+
+$$
+\begin{bmatrix} 0.299 & 0.587 & 0.114 \\\\ -0.147 & -0.289 & 0.436 \\\\ 0.615 & -0.515 & -0.100 \end{bmatrix} \times \begin{bmatrix} R \\\\ G \\\\ B \end{bmatrix} = \begin{bmatrix} Y \\\\ U \\\\ V \end{bmatrix}
+$$
+
+```c
+    // calculate YUV value
+    double *bmYUV = (double *) malloc(sizeof(double) * bmImg.bmBytesPerRow * bmiHeader->biHeight);
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
+            uint8_t *B = &bmImg.bmData[pos];
+            uint8_t *G = &bmImg.bmData[pos + 1];
+            uint8_t *R = &bmImg.bmData[pos + 2];
+            bmYUV[pos] = 0.299 * *R + 0.587 * *G + 0.114 * *B;
+            bmYUV[pos + 1] = -0.147 * *R - 0.289 * *G + 0.436 * *B;
+            bmYUV[pos + 2] = 0.615 * *R - 0.515 * *G - 0.100 * *B;
+        }
+    }
+```
+
+To change color to gray, we can simply make the R, G and B value of a pixel equal to its Y value. It would be more complex if we want to change it into an image with biBitCount equal to 8, because we need to set the palette manually.
+
+One more step, rearrange gray intensity to lie between [0,255]. It's just a math problem. So the code
+
+```c
+    // color to gray
+    BITMAP bmGray;
+    bmGray.bmHeader = bmImg.bmHeader;
+    bmGray.bmInfoSize = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << 8);
+    bmGray.bmInfo = (BITMAPINFO *) malloc(bmGray.bmInfoSize);
+    bmGray.bmInfo->bmiHeader = *bmiHeader;
+    bmGray.bmInfo->bmiHeader.biBitCount = 8;
+    for (int i = 0; i < 256; ++i) {
+        RGBQUAD *rgb = &(bmGray.bmInfo->bmiColors[i]);
+        rgb->rgbBlue = i;
+        rgb->rgbGreen = i;
+        rgb->rgbRed = i;
+        rgb->rgbReserved = 0;
+    }
+    init_bmp(&bmGray);
+    uint8_t min = 255, max = 0;
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
+            double *Y = &bmYUV[pos];
+            if (*Y < min) {
+                min = *Y;
+            }
+            if (*Y > max) {
+                max = *Y;
+            }
+        }
+    }
+    // rearrange gray indensity
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
+            double *Y = &bmYUV[pos];
+            uint32_t _pos = h * bmGray.bmBytesPerRow + w * bmGray.bmBytesPerPel;
+            bmGray.bmData[_pos] = adjust(255 * (*Y - min) / (max - min));
+        }
+    }
+```
+
+### Step Four: Change the Luminance
+
+The luminance is depend on Y value, too. What we need to do is just changing the Y value and applying the inverse formula below.
+
+$$
+\begin{bmatrix} 1.000 & 0.000 & 1.140 \\\\ 1.000 & -0.3946 & -0.5805 \\\\ 1.000 & 2.032 & -0.0005 \end{bmatrix} \times \begin{bmatrix} Y \\\\ U \\\\ V \end{bmatrix} = \begin{bmatrix} R \\\\ G \\\\ B \end{bmatrix}
+$$
+
+And the code is simple.
+
+```c
+    // change luminance
+    BITMAP bmLight, bmDark;
+    copy_bmp(&bmLight, &bmImg);
+    copy_bmp(&bmDark, &bmImg);
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
+            double *Y = &bmYUV[pos];
+            double *U = &bmYUV[pos + 1];
+            double *V = &bmYUV[pos + 2];
+            bmLight.bmData[pos] = adjust(*Y + 25 + 2.032 * *U - 0.0005 * *V);
+            bmLight.bmData[pos + 1] = adjust(*Y + 25 - 0.3946 * *U - 0.5805 * *V);
+            bmLight.bmData[pos + 2] = adjust(*Y + 25 + 1.140 * *V);
+            bmDark.bmData[pos] = adjust(*Y - 50 + 2.032 * *U - 0.0005 * *V);
+            bmDark.bmData[pos + 1] = adjust(*Y - 50 - 0.3946 * *U - 0.5805 * *V);
+            bmDark.bmData[pos + 2] = adjust(*Y - 50 + 1.140 * *V);
+        }
+    }
+```
+
+### Step Five: Complete Source Code
+
+#### bmp.h
+
+```c
+#ifndef _BMP_H_
+#define _BMP_H_
+
+#include <stdint.h>
+
+#pragma pack(push, 1) // avoid struct padding
+
+typedef struct tagBITMAPFILEHEADER {
+    uint16_t bfType;
+    uint32_t bfSize;
+    uint16_t bfReserved1;
+    uint16_t bfReserved2;
+    uint32_t bfOffBits;
+} BITMAPFILEHEADER;
+
+typedef struct tagBITMAPINFOHEADER {
+    uint32_t biSize;
+    int32_t biWidth;
+    int32_t biHeight;
+    uint16_t biPlanes;
+    uint16_t biBitCount;
+    uint32_t biCompression;
+    uint32_t biSizeImage;
+    int32_t biXPelsPerMeter;
+    int32_t biYPelsPerMeter;
+    uint32_t biClrUsed;
+    uint32_t biClrImportant;
+} BITMAPINFOHEADER;
+
+typedef struct tagRGBQUAD {
+    uint8_t rgbBlue;
+    uint8_t rgbGreen;
+    uint8_t rgbRed;
+    uint8_t rgbReserved;
+} RGBQUAD;
+
+typedef struct tagBITMAPINFO {
+    BITMAPINFOHEADER bmiHeader;
+    RGBQUAD bmiColors[1];
+} BITMAPINFO;
+
+typedef struct tagBITMAP {
+    BITMAPFILEHEADER bmHeader;
+    BITMAPINFO *bmInfo;
+    uint32_t bmInfoSize;
+    uint32_t bmBytesPerRow;
+    uint8_t bmBytesPerPel;
+    uint8_t *bmData;
+} BITMAP;
+
+#pragma pack(pop)
+
+#endif
+```
+
+#### bmp.c
+
+```c
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bmp.h"
+
+// given bmHeader and bmInfo, initialize others
+void init_bmp(BITMAP *bmImg) {
+    BITMAPINFOHEADER *bmiHeader = &(bmImg->bmInfo->bmiHeader);
+    bmImg->bmBytesPerRow = ((bmiHeader->biWidth * bmiHeader->biBitCount + 31) >> 5) << 2;
+    bmImg->bmBytesPerPel = bmiHeader->biBitCount >> 3;
+    bmImg->bmData = (uint8_t *) malloc(bmImg->bmBytesPerRow * bmiHeader->biHeight);
+}
+
+// read a BMP from file
+void read_bmp(BITMAP *bmImg, char *filepath) {
+    FILE *fiInImg = fopen(filepath, "rb");
+    BITMAPINFOHEADER bmiHeader;
+    fread(&(bmImg->bmHeader), sizeof(BITMAPFILEHEADER), 1, fiInImg);
+    fread(&bmiHeader, sizeof(BITMAPINFOHEADER), 1, fiInImg);
+    // if biBitCount is less than 16, use all the palette, otherwise do not use palette
+    if (bmiHeader.biBitCount < 16) {
+        bmImg->bmInfoSize = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << bmiHeader.biBitCount);
+    } else {
+        bmImg->bmInfoSize = sizeof(BITMAPINFOHEADER);
+    }
+    bmImg->bmInfo = (BITMAPINFO *) malloc(bmImg->bmInfoSize);
+    bmImg->bmInfo->bmiHeader = bmiHeader;
+    if (bmiHeader.biBitCount < 16) {
+        fread(bmImg->bmInfo->bmiColors, sizeof(RGBQUAD), 1 << bmiHeader.biBitCount, fiInImg);
+    }
+    init_bmp(bmImg);
+    fread(bmImg->bmData, bmImg->bmBytesPerRow, bmiHeader.biHeight, fiInImg);
+    fclose(fiInImg);
+}
+
+// duplicate a BMP
+void copy_bmp(BITMAP *bmDes, BITMAP *bmSrc) {
+    memcpy(bmDes, bmSrc, sizeof(BITMAP));
+    bmDes->bmInfo = (BITMAPINFO *) malloc(bmSrc->bmInfoSize);
+    memcpy(bmDes->bmInfo, bmSrc->bmInfo, bmSrc->bmInfoSize);
+    BITMAPINFOHEADER *bmiHeader = &(bmSrc->bmInfo->bmiHeader);
+    bmDes->bmData = (uint8_t *) malloc(bmSrc->bmBytesPerRow * bmiHeader->biHeight);
+    memcpy(bmDes->bmData, bmSrc->bmData, bmSrc->bmBytesPerRow * bmiHeader->biHeight);
+}
+
+// write a BMP to file
+void write_bmp(BITMAP *bmImg, char *filepath) {
+    FILE *fiOutImg = fopen(filepath, "wb");
+    fwrite(&(bmImg->bmHeader), sizeof(BITMAPFILEHEADER), 1, fiOutImg);
+    fwrite(bmImg->bmInfo, bmImg->bmInfoSize, 1, fiOutImg);
+    fwrite(bmImg->bmData, bmImg->bmBytesPerRow, bmImg->bmInfo->bmiHeader.biHeight, fiOutImg);
+    fclose(fiOutImg);
+}
+
+// make RGB value legal
+uint8_t adjust(double val) {
+    int16_t ret = (int16_t) (val + 0.5);
+    return ret < 0 ? 0 : ret > 255 ? 255 : ret;
+}
+
+int main(int argc, char *argv[]) {
+    // read bmp file
+    BITMAP bmImg;
+    read_bmp(&bmImg, "original.bmp");
+    BITMAPINFOHEADER *bmiHeader = &(bmImg.bmInfo->bmiHeader);
+
+    // calculate YUV value
+    double *bmYUV = (double *) malloc(sizeof(double) * bmImg.bmBytesPerRow * bmiHeader->biHeight);
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
+            uint8_t *B = &bmImg.bmData[pos];
+            uint8_t *G = &bmImg.bmData[pos + 1];
+            uint8_t *R = &bmImg.bmData[pos + 2];
+            bmYUV[pos] = 0.299 * *R + 0.587 * *G + 0.114 * *B;
+            bmYUV[pos + 1] = -0.147 * *R - 0.289 * *G + 0.436 * *B;
+            bmYUV[pos + 2] = 0.615 * *R - 0.515 * *G - 0.100 * *B;
+        }
+    }
+
+    // color to gray
+    BITMAP bmGray;
+    bmGray.bmHeader = bmImg.bmHeader;
+    bmGray.bmInfoSize = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << 8);
+    bmGray.bmInfo = (BITMAPINFO *) malloc(bmGray.bmInfoSize);
+    bmGray.bmInfo->bmiHeader = *bmiHeader;
+    bmGray.bmInfo->bmiHeader.biBitCount = 8;
+    for (int i = 0; i < 256; ++i) {
+        RGBQUAD *rgb = &(bmGray.bmInfo->bmiColors[i]);
+        // to prove that the palette works well, play a small trick
+        rgb->rgbBlue = (i >> 4) << 4;
+        rgb->rgbGreen = (i >> 4) << 4;
+        rgb->rgbRed = (i >> 4) << 4;
+        rgb->rgbReserved = 0;
+    }
+    init_bmp(&bmGray);
+    uint8_t min = 255, max = 0;
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
+            double *Y = &bmYUV[pos];
+            if (*Y < min) {
+                min = *Y;
+            }
+            if (*Y > max) {
+                max = *Y;
+            }
+        }
+    }
+    // rearrange gray indensity
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
+            double *Y = &bmYUV[pos];
+            uint32_t _pos = h * bmGray.bmBytesPerRow + w * bmGray.bmBytesPerPel;
+            bmGray.bmData[_pos] = adjust(255 * (*Y - min) / (max - min));
+        }
+    }
+    write_bmp(&bmGray, "gray.bmp");
+
+    // change luminance
+    BITMAP bmLight, bmDark;
+    copy_bmp(&bmLight, &bmImg);
+    copy_bmp(&bmDark, &bmImg);
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg.bmBytesPerRow + w * bmImg.bmBytesPerPel;
+            double *Y = &bmYUV[pos];
+            double *U = &bmYUV[pos + 1];
+            double *V = &bmYUV[pos + 2];
+            bmLight.bmData[pos] = adjust(*Y + 25 + 2.032 * *U - 0.0005 * *V);
+            bmLight.bmData[pos + 1] = adjust(*Y + 25 - 0.3946 * *U - 0.5805 * *V);
+            bmLight.bmData[pos + 2] = adjust(*Y + 25 + 1.140 * *V);
+            bmDark.bmData[pos] = adjust(*Y - 50 + 2.032 * *U - 0.0005 * *V);
+            bmDark.bmData[pos + 1] = adjust(*Y - 50 - 0.3946 * *U - 0.5805 * *V);
+            bmDark.bmData[pos + 2] = adjust(*Y - 50 + 1.140 * *V);
+        }
+    }
+    write_bmp(&bmLight, "light.bmp");
+    write_bmp(&bmDark, "dark.bmp");
+
+    return 0;
+}
+```

+ 298 - 0
source/_posts/coding/digital-image-processing-assignment-ii.md

@@ -0,0 +1,298 @@
+---
+title: Digital Image Processing Assignment II
+tags:
+  - Assignment
+id: "3453"
+categories:
+  - - Coding
+    - Digital Image Processing
+date: 2019-10-11 15:40:41
+---
+
+### Main Contents
+
+- Image binarization;
+- Binary image erosion;
+- Binary image dilation;
+- Binary image opening;
+- Binary image closing.
+
+### Step One: Define Some Functions
+
+Here we define two functions that generate a blank grayscale image from a given image,
+
+```c
+// given bmHeader and bmInfo, initialize others
+void init_bmp(BITMAP *bmImg) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    bmImg->bmBytesPerRow = ((bmiHeader->biWidth * bmiHeader->biBitCount + 31) >> 5) << 2;
+    bmImg->bmBytesPerPel = bmiHeader->biBitCount >> 3;
+    bmImg->bmData = (uint8_t *) malloc(bmImg->bmBytesPerRow * bmiHeader->biHeight);
+    memset(bmImg->bmData, 255, bmImg->bmBytesPerRow * bmiHeader->biHeight);
+}
+
+// generate a blank grayscale image
+void gen_gray(BITMAP *bmImg, BITMAP *bmGray) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    bmGray->bmHeader = bmImg->bmHeader;
+    bmGray->bmInfoSize = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) << 8); // add palette
+    bmGray->bmInfo = (BITMAPINFO *) malloc(bmGray->bmInfoSize);
+    bmGray->bmInfo->bmiHeader = *bmiHeader;
+    bmGray->bmInfo->bmiHeader.biBitCount = 8; // one byte per pixel
+    // initilize palette
+    for (int16_t i = 0; i < 256; ++i) {
+        RGBQUAD *rgb = &bmGray->bmInfo->bmiColors[i];
+        rgb->rgbBlue = i;
+        rgb->rgbGreen = i;
+        rgb->rgbRed = i;
+        rgb->rgbReserved = 0;
+    }
+    init_bmp(bmGray);
+    // IMPORTANT: MODIFY byOffBits AND bfSize
+    bmGray->bmHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + bmGray->bmInfoSize;
+    bmGray->bmHeader.bfSize = bmGray->bmHeader.bfOffBits + bmGray->bmBytesPerRow * bmiHeader->biHeight;
+}
+```
+
+and a function that change a color image into grayscale.
+
+```c
+void color2gray(BITMAP *bmImg, BITMAP *bmGray) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    gen_gray(bmImg, bmGray);
+    uint8_t minY = 255, maxY = 0;
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg->bmBytesPerRow + w * bmImg->bmBytesPerPel;
+            uint8_t *B = &bmImg->bmData[pos];
+            uint8_t *G = &bmImg->bmData[pos + 1];
+            uint8_t *R = &bmImg->bmData[pos + 2];
+            uint8_t Y = adjust(0.299 * *R + 0.587 * *G + 0.114 * *B);
+            if (Y < minY) {
+                minY = Y;
+            }
+            if (Y > maxY) {
+                maxY = Y;
+            }
+        }
+    }
+    // rearrange gray indensity
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg->bmBytesPerRow + w * bmImg->bmBytesPerPel;
+            uint8_t *B = &bmImg->bmData[pos];
+            uint8_t *G = &bmImg->bmData[pos + 1];
+            uint8_t *R = &bmImg->bmData[pos + 2];
+            uint8_t Y = adjust(0.299 * *R + 0.587 * *G + 0.114 * *B);
+            uint32_t _pos = h * bmGray->bmBytesPerRow + w;
+            bmGray->bmData[_pos] = adjust(255. * (Y - minY) / (maxY - minY));
+        }
+    }
+}
+```
+
+### Step Two: Change Gray to Binary
+
+In order to binarize the image, we need determine a threshold, and change pixels whose grayscale is below the threshold to black, and the others to white. But how to find the optimal threshold? There's an excellent algorithm called Otsu's method, which, in brief, maximize inter-class variance.
+
+```c
+void otsu_gray2binary(BITMAP *bmGray, BITMAP *bmBinary) {
+    BITMAPINFOHEADER *bmiHeader = &bmGray->bmInfo->bmiHeader;
+    gen_gray(bmGray, bmBinary);
+    uint8_t minG = 255, maxG = 0;
+    double *p = (double *) malloc(1 << 11);
+    memset(p, 0, 1 << 11);
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmGray->bmBytesPerRow + w;
+            ++p[bmGray->bmData[pos]];
+            if (bmGray->bmData[pos] < minG) {
+                minG = bmGray->bmData[pos];
+            }
+            if (bmGray->bmData[pos] > maxG) {
+                maxG = bmGray->bmData[pos];
+            }
+        }
+    }
+    double muT = 0;
+    for (int16_t k = minG; k <= maxG; ++k) {
+        p[k] /= bmiHeader->biHeight * bmiHeader->biWidth;
+        muT += k * p[k];
+    }
+    uint8_t threshold = 0;
+    double omegaK = 0, muK = 0, maxB = 0;
+    for (int16_t k = minG; k < maxG; ++k) {
+        omegaK += p[k];
+        muK += k * p[k];
+        double sigmaK = (muT * omegaK - muK) * (muT * omegaK - muK) / omegaK / (1 - omegaK);
+        if (maxB < sigmaK) {
+            maxB = sigmaK;
+            threshold = k;
+        }
+    }
+    free(p);
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmGray->bmBytesPerRow + w;
+            bmBinary->bmData[pos] = bmGray->bmData[pos] < threshold ? 0 : 255;
+        }
+    }
+}
+```
+
+Otsu's method sets the global threshold, but for some special image with different luminance, adaptive thresholding may produce better result. There are at least two ways to apply adaptive thresholding.
+
+```c
+void pixel_adaptive_gray2binary(BITMAP *bmGray, BITMAP *bmBinary, uint32_t border, uint32_t offset) {
+    BITMAPINFOHEADER *bmiHeader = &bmGray->bmInfo->bmiHeader;
+    gen_gray(bmGray, bmBinary);
+    border >>= 1;
+    uint32_t *integral = (uint32_t *) malloc(bmGray->bmBytesPerRow * bmiHeader->biHeight << 2);
+    memset(integral, 0, bmGray->bmBytesPerRow * bmiHeader->biHeight * 4);
+    // get integral image
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos1 = h * bmGray->bmBytesPerRow + w;
+            uint32_t pos2 = (h - 1) * bmGray->bmBytesPerRow + w;
+            uint32_t pos3 = h * bmGray->bmBytesPerRow + w - 1;
+            uint32_t pos4 = (h - 1) * bmGray->bmBytesPerRow + w - 1;
+            integral[pos1] = bmGray->bmData[pos1]
+                           + (h > 0 ? integral[pos2] : 0)
+                           + (w > 0 ? integral[pos3] : 0)
+                           - (h > 0 && w > 0 ? integral[pos4] : 0);
+        }
+    }
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmGray->bmBytesPerRow + w;
+            // range checking
+            uint32_t x1 = h > border ? h - border : 0;
+            uint32_t x2 = h + border + 1 < bmiHeader->biHeight ? h + border + 1 : bmiHeader->biHeight;
+            uint32_t y1 = w > border ? w - border : 0;
+            uint32_t y2 = w + border + 1 < bmiHeader->biWidth ? w + border + 1 : bmiHeader->biWidth;
+            uint32_t pos1 = (x2 - 1) * bmGray->bmBytesPerRow + y2 - 1;
+            uint32_t pos2 = (x1 - 1) * bmGray->bmBytesPerRow + y2 - 1;
+            uint32_t pos3 = (x2 - 1) * bmGray->bmBytesPerRow + y1 - 1;
+            uint32_t pos4 = (x1 - 1) * bmGray->bmBytesPerRow + y1 - 1;
+            uint64_t cnt = (x2 - x1) * (y2 - y1);
+            uint64_t sum = integral[pos1]
+                         - (x1 > 0 ? integral[pos2] : 0)
+                         - (y1 > 0 ? integral[pos3] : 0)
+                         + (x1 > 0 && y1 > 0 ? integral[pos4] : 0);
+            bmBinary->bmData[pos] = bmGray->bmData[pos] * cnt * 100 < sum * (100 - offset) ? 0 : 255;
+        }
+    }
+}
+
+void block_adaptive_gray2binary(BITMAP *bmGray, BITMAP *bmBinary, uint32_t border, double step, uint32_t offset) {
+    BITMAPINFOHEADER *bmiHeader = &bmGray->bmInfo->bmiHeader;
+    gen_gray(bmGray, bmBinary);
+    uint32_t *integral = (uint32_t *) malloc(bmGray->bmBytesPerRow * bmiHeader->biHeight << 2);
+    memset(integral, 0, bmGray->bmBytesPerRow * bmiHeader->biHeight * 4);
+    // get integral image
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos1 = h * bmGray->bmBytesPerRow + w;
+            uint32_t pos2 = (h - 1) * bmGray->bmBytesPerRow + w;
+            uint32_t pos3 = h * bmGray->bmBytesPerRow + w - 1;
+            uint32_t pos4 = (h - 1) * bmGray->bmBytesPerRow + w - 1;
+            integral[pos1] = bmGray->bmData[pos1]
+                           + (h > 0 ? integral[pos2] : 0)
+                           + (w > 0 ? integral[pos3] : 0)
+                           - (h > 0 && w > 0 ? integral[pos4] : 0);
+        }
+    }
+    for (uint32_t h = 0; h < bmiHeader->biHeight; h += border * step) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; w += border * step) {
+            uint32_t pos = h * bmGray->bmBytesPerRow + w;
+            // range checking
+            uint32_t x1 = h;
+            uint32_t x2 = h + border + 1 < bmiHeader->biHeight ? h + border + 1 : bmiHeader->biHeight;
+            uint32_t y1 = w;
+            uint32_t y2 = w + border + 1 < bmiHeader->biWidth ? w + border + 1 : bmiHeader->biWidth;
+            uint32_t pos1 = (x2 - 1) * bmGray->bmBytesPerRow + y2 - 1;
+            uint32_t pos2 = (x1 - 1) * bmGray->bmBytesPerRow + y2 - 1;
+            uint32_t pos3 = (x2 - 1) * bmGray->bmBytesPerRow + y1 - 1;
+            uint32_t pos4 = (x1 - 1) * bmGray->bmBytesPerRow + y1 - 1;
+            uint64_t cnt = (x2 - x1) * (y2 - y1);
+            uint64_t sum = integral[pos1]
+                         - (x1 > 0 ? integral[pos2] : 0)
+                         - (y1 > 0 ? integral[pos3] : 0)
+                         + (x1 > 0 && y1 > 0 ? integral[pos4] : 0);
+            for (uint32_t _h = x1; _h < x2; ++_h) {
+                for (uint32_t _w = y1; _w < y2; ++_w) {
+                    uint32_t _pos = _h * bmGray->bmBytesPerRow + _w;
+                    // when overlapping, black has the priority
+                    bmBinary->bmData[_pos] &= bmGray->bmData[_pos] * cnt * 100 < sum * (100 - offset) ? 0 : 255;
+                }
+            }
+        }
+    }
+}
+```
+
+### Step Three: Erosion and Dilation
+
+Let's say that white is the foreground and black is the background. Assume you have a solid circle, and you can only place it on white pixels, which means the circle shouldn't cover the black pixels. We make the available area where the center can be put white and the others black, and get a new image—the result of erosion.
+
+```c
+void erode(BITMAP *bmBinary, BITMAP *bmErosion, uint32_t border) {
+    BITMAPINFOHEADER *bmiHeader = &bmBinary->bmInfo->bmiHeader;
+    gen_gray(bmBinary, bmErosion);
+    border >>= 1;
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmBinary->bmBytesPerRow + w;
+            // range checking
+            uint32_t x1 = h > border ? h - border : 0;
+            uint32_t x2 = h + border + 1 < bmiHeader->biHeight ? h + border + 1 : bmiHeader->biHeight;
+            uint32_t y1 = w > border ? w - border : 0;
+            uint32_t y2 = w + border + 1 < bmiHeader->biWidth ? w + border + 1 : bmiHeader->biWidth;
+            uint8_t flag = 255;
+            for (uint32_t _h = x1; _h < x2; ++_h) {
+                for (uint32_t _w = y1; _w < y2; ++_w) {
+                    uint32_t _pos = _h * bmBinary->bmBytesPerRow + _w;
+                    flag &= bmBinary->bmData[_pos];
+                }
+            }
+            bmErosion->bmData[pos] = flag;
+        }
+    }
+}
+```
+
+This time, you want to put the center of the circle on white pixels regardless of whether other points lie on black pixels. We make the area that can be covered by the circle white and the others black. This is what dilation do. Surprisingly, the code is very very similar with erosion.
+
+```c
+void dilate(BITMAP *bmBinary, BITMAP *bmDilation, uint32_t border) {
+    BITMAPINFOHEADER *bmiHeader = &bmBinary->bmInfo->bmiHeader;
+    gen_gray(bmBinary, bmDilation);
+    border >>= 1;
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmBinary->bmBytesPerRow + w;
+            // range checking
+            uint32_t x1 = h > border ? h - border : 0;
+            uint32_t x2 = h + border + 1 < bmiHeader->biHeight ? h + border + 1 : bmiHeader->biHeight;
+            uint32_t y1 = w > border ? w - border : 0;
+            uint32_t y2 = w + border + 1 < bmiHeader->biWidth ? w + border + 1 : bmiHeader->biWidth;
+            uint8_t flag = 0;
+            for (uint32_t _h = x1; _h < x2; ++_h) {
+                for (uint32_t _w = y1; _w < y2; ++_w) {
+                    uint32_t _pos = _h * bmBinary->bmBytesPerRow + _w;
+                    flag |= bmBinary->bmData[_pos];
+                }
+            }
+            bmDilation->bmData[pos] = flag;
+        }
+    }
+}
+```
+
+### Step Four: Opening and Closing
+
+In fact, opening operation and closing operation are just the combination of the two operations we mentioned above, the only difference is the order. Opening operation is an erosion followed by a dilation, while closing operation is the reverse.
+
+Visually speaking, opening operation makes the gaps between black pixels disappeared, and closing operation makes the gaps between white pixels disappeared.
+
+Note that these two operations are both idempotent, which means performing one of them multiple times is equivalent to performing it one time.

+ 210 - 0
source/_posts/coding/digital-image-processing-assignment-iii.md

@@ -0,0 +1,210 @@
+---
+title: Digital Image Processing Assignment III
+tags:
+  - Assignment
+id: "3472"
+categories:
+  - - Coding
+    - Digital Image Processing
+date: 2019-11-09 10:40:46
+---
+
+### Main Contents
+
+- Gamma correction;
+- Visibility enhancement;
+- Histogram equalization.
+
+### Step One: Define Some Functions
+
+We add a function that generate a blank color image from a given image,
+
+```c
+// generate a blank color image
+void gen_color(BITMAP *bmImg, BITMAP *bmColor) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    bmColor->bmHeader = bmImg->bmHeader;
+    bmColor->bmInfoSize = bmImg->bmInfoSize;
+    bmColor->bmInfo = (BITMAPINFO *) malloc(bmColor->bmInfoSize);
+    bmColor->bmInfo->bmiHeader = *bmiHeader;
+    init_bmp(bmColor);
+}
+```
+
+### Step Two: Gamma Correction
+
+Gamma encoding of images is used to optimize the usage of bits when encoding an image, or bandwidth used to transport an image, by taking advantage of the non-linear manner in which humans perceive light and color. Gamma correction is a nonlinear operation used to encode and decode luminance values in video or still image systems. Gamma correction is, in the simplest cases, defined by the following power-law expression.
+
+$$
+L_d=L_w^{\frac{1}{\gamma}}
+$$
+
+```c
+void gamma_correct(BITMAP *bmImg, BITMAP *bmGamma, double gamma) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    gen_color(bmImg, bmGamma);
+    uint8_t *p = (uint8_t *) malloc(1 << 11);
+    for (int16_t i = 0; i < 256; ++i) {
+        p[i] = adjust(255 * pow(i / 255., 1 / gamma));
+    }
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg->bmBytesPerRow + w * bmImg->bmBytesPerPel;
+            bmGamma->bmData[pos] = p[bmImg->bmData[pos]];
+            bmGamma->bmData[pos + 1] = p[bmImg->bmData[pos + 1]];
+            bmGamma->bmData[pos + 2] = p[bmImg->bmData[pos + 2]];
+        }
+    }
+    free(p);
+}
+```
+
+### Step Three: Visibility Enhancement
+
+We use a logarithmic operator to adjust the pixel value,
+
+$$
+L_d=\frac{\log(L_w+1)}{\log(L_{max}+1)}
+$$
+
+where $L_d$ refers to display luminance, $L_w$ refers to original luminance, and $L_{max}$ is the maximal luminance in the original image.
+
+This mapping function make sure that, no matter the dynamic range of the scene, the maximal luminance value will be 1 (white), and the others change smoothly.
+
+```c
+void enhance(BITMAP *bmImg, BITMAP *bmEnhance) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    gen_color(bmImg, bmEnhance);
+    double *bmYUV = (double *) malloc(bmImg->bmBytesPerRow * bmiHeader->biHeight << 3);
+    double minD = 1, maxD = 0;
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg->bmBytesPerRow + w * bmImg->bmBytesPerPel;
+            uint8_t *B = &bmImg->bmData[pos];
+            uint8_t *G = &bmImg->bmData[pos + 1];
+            uint8_t *R = &bmImg->bmData[pos + 2];
+            // transform RGB into YUV
+            bmYUV[pos] = 0.299 * *R + 0.587 * *G + 0.114 * *B;
+            bmYUV[pos + 1] = -0.147 * *R - 0.289 * *G + 0.436 * *B;
+            bmYUV[pos + 2] = 0.615 * *R - 0.515 * *G - 0.100 * *B;
+            double D = log(bmYUV[pos] + 1) / log(256);
+            if (D < minD) {
+                minD = D;
+            }
+            if (D > maxD) {
+                maxD = D;
+            }
+        }
+    }
+    if (minD < maxD) {
+        for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+            for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+                uint32_t pos = h * bmImg->bmBytesPerRow + w * bmImg->bmBytesPerPel;
+                double *Y = &bmYUV[pos];
+                double *U = &bmYUV[pos + 1];
+                double *V = &bmYUV[pos + 2];
+                double D = log(*Y + 1) / log(256);
+                // histogram stretching
+                *Y = 255 * (D - minD) / (maxD - minD);
+                // transform YUV into RGB
+                bmEnhance->bmData[pos] = adjust(*Y + 2.032 * *U);
+                bmEnhance->bmData[pos + 1] = adjust(*Y - 0.395 * *U - 0.581 * *V);
+                bmEnhance->bmData[pos + 2] = adjust(*Y + 1.140 * *V);
+            }
+        }
+    }
+    free(bmYUV);
+}
+```
+
+### Step Four: Histogram Equalization
+
+Histogram equalization is a method in image processing of contrast adjustment using the image's histogram. Let $p_i$ be the probability of an occurrence of a pixel of level $i$ in the image, then its new level $s_i=\sum_{k=0}^i p_k$. The image should be converted to Lab color space or HSL/HSV color space before equalization, so that the algorithm can be applied to the luminance or value channel without resulting in changes to the hue and saturation of the image.
+
+```c
+void histeq(BITMAP *bmImg, BITMAP *bmHistEq) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    gen_color(bmImg, bmHistEq);
+    double *bmHSL = (double *) malloc(bmImg->bmBytesPerRow * bmiHeader->biHeight << 3);
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg->bmBytesPerRow + w * bmImg->bmBytesPerPel;
+            double B = bmImg->bmData[pos] / 255.;
+            double G = bmImg->bmData[pos + 1] / 255.;
+            double R = bmImg->bmData[pos + 2] / 255.;
+            double *H = &bmHSL[pos];
+            double *S = &bmHSL[pos + 1];
+            double *L = &bmHSL[pos + 2];
+            // transform RGB into HSL
+            double min = B < G ? B < R ? B : R : G < R ? G : R;
+            double max = B > G ? B > R ? B : R : G > R ? G : R;
+            if (min == max) {
+                *H = 0;
+            } else if (max == R) {
+                *H = 60 * (G - B) / (max - min);
+            } else if (max == G) {
+                *H = 60 * (2 + (B - R) / (max - min));
+            } else if (max == B) {
+                *H = 60 * (4 + (R - G) / (max - min));
+            }
+            if (*H < 0) {
+                *H += 360;
+            }
+            *L = (min + max) / 2;
+            if (min == 1 || max == 0) {
+                *S = 0;
+            } else {
+                *S = (max - *L) / (*L < 0.5 ? *L : 1 - *L);
+            }
+        }
+    }
+    // histogram equalization
+    double *p = (double *) malloc(1 << 11);
+    memset(p, 0, 1 << 11);
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg->bmBytesPerRow + w * bmImg->bmBytesPerPel;
+            ++p[adjust(bmHSL[pos + 2] * 255)];
+        }
+    }
+    for (int16_t i = 0; i < 256; ++i) {
+        p[i] /= bmiHeader->biHeight * bmiHeader->biWidth;
+        if (i > 0) {
+            p[i] += p[i - 1];
+        }
+    }
+    for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+        for (uint32_t w = 0; w < bmiHeader->biWidth; ++w) {
+            uint32_t pos = h * bmImg->bmBytesPerRow + w * bmImg->bmBytesPerPel;
+            double *H = &bmHSL[pos];
+            double *S = &bmHSL[pos + 1];
+            double *L = &bmHSL[pos + 2];
+            *L = p[adjust(*L * 255)];
+            // transform HSL into RGB
+            double C = (1 - fabs(*L * 2 - 1)) * *S;
+            double Hapos = *H / 60;
+            double X = C * (1 - fabs(fmod(Hapos, 2) - 1));
+            double R, G, B;
+            if (Hapos <= 1) {
+                R = C, G = X, B = 0;
+            } else if (Hapos <= 2) {
+                R = X, G = C, B = 0;
+            } else if (Hapos <= 3) {
+                R = 0, G = C, B = X;
+            } else if (Hapos <= 4) {
+                R = 0, G = X, B = C;
+            } else if (Hapos <= 5) {
+                R = X, G = 0, B = C;
+            } else {
+                R = C, G = 0, B = X;
+            }
+            double m = *L - C / 2;
+            bmHistEq->bmData[pos] = adjust((B + m) * 255);
+            bmHistEq->bmData[pos + 1] = adjust((G + m) * 255);
+            bmHistEq->bmData[pos + 2] = adjust((R + m) * 255);
+        }
+    }
+    free(p);
+    free(bmHSL);
+}
+```

+ 292 - 0
source/_posts/coding/digital-image-processing-assignment-iv.md

@@ -0,0 +1,292 @@
+---
+title: Digital Image Processing Assignment IV
+tags:
+  - Assignment
+id: "3518"
+categories:
+  - - Coding
+    - Digital Image Processing
+date: 2019-11-09 11:45:03
+---
+
+### Main Contents
+
+- Image translate;
+- Image mirror;
+- Image shear;
+- Image rotate;
+- Image scale.
+
+### Step One: Define Some Functions
+
+We modify our **gen_color** function in order to generate a blank image with given scale.
+
+```c
+// generate a blank color image
+void gen_color(BITMAP *bmImg, BITMAP *bmColor, uint32_t biHeight, uint32_t biWidth) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    bmColor->bmHeader = bmImg->bmHeader;
+    bmColor->bmInfoSize = bmImg->bmInfoSize;
+    bmColor->bmInfo = (BITMAPINFO *) malloc(bmColor->bmInfoSize);
+    bmColor->bmInfo->bmiHeader = *bmiHeader;
+    bmColor->bmInfo->bmiHeader.biWidth = biWidth;
+    bmColor->bmInfo->bmiHeader.biHeight = biHeight;
+    init_bmp(bmColor);
+}
+```
+
+### Step Two: Translate, Mirror and Shear
+
+Normally, we enumerate each pixel in the new image, map it back to the original image, and use the neighboring pixels to interpolate. However, for translating, mirroring and shearing, there is a one-to-one match between the pixels in two images, so interpolation is unnecessary.
+
+```c
+void translate(BITMAP *bmImg, BITMAP *bmTranslate, uint32_t disX, uint32_t disY) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    uint32_t height = bmiHeader->biHeight + disX;
+    uint32_t width = bmiHeader->biWidth + disY;
+    gen_color(bmImg, bmTranslate, height, width);
+    for (uint32_t h = 0; h < height; ++h) {
+        for (uint32_t w = 0; w < width; ++w) {
+            int32_t x = h;
+            int32_t y = w - disY;
+            uint32_t pos = x * bmImg->bmBytesPerRow + y * bmImg->bmBytesPerPel;
+            uint32_t _pos = h * bmTranslate->bmBytesPerRow + w * bmTranslate->bmBytesPerPel;
+            if (x >= 0 && x < bmiHeader->biHeight && y >= 0 && y < bmiHeader->biWidth) {
+                for (uint8_t k = 0; k < 3; ++k) {
+                    bmTranslate->bmData[_pos + k] = bmImg->bmData[pos + k];
+                }
+            }
+        }
+    }
+}
+
+void mirror(BITMAP *bmImg, BITMAP *bmMirror) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    uint32_t height = bmiHeader->biHeight << 1;
+    uint32_t width = bmiHeader->biWidth;
+    gen_color(bmImg, bmMirror, height, width);
+    for (uint32_t h = 0; h < height; ++h) {
+        for (uint32_t w = 0; w < width; ++w) {
+            int32_t x = bmiHeader->biHeight - 1 - h;
+            int32_t y = w;
+            uint32_t pos = x * bmImg->bmBytesPerRow + y * bmImg->bmBytesPerPel;
+            uint32_t _pos = h * bmMirror->bmBytesPerRow + w * bmMirror->bmBytesPerPel;
+            if (x >= 0 && x < bmiHeader->biHeight && y >= 0 && y < bmiHeader->biWidth) {
+                for (uint8_t k = 0; k < 3; ++k) {
+                    bmMirror->bmData[_pos + k] = bmImg->bmData[pos + k];
+                }
+            }
+        }
+    }
+}
+
+void shear(BITMAP *bmImg, BITMAP *bmShear, uint32_t disX) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    uint32_t height = bmiHeader->biHeight + disX;
+    uint32_t width = bmiHeader->biWidth;
+    gen_color(bmImg, bmShear, height, width);
+    for (uint32_t h = 0; h < height; ++h) {
+        for (uint32_t w = 0; w < width; ++w) {
+            int32_t x = h + disX * w / (width - 1) - disX;
+            int32_t y = w;
+            uint32_t pos = x * bmImg->bmBytesPerRow + y * bmImg->bmBytesPerPel;
+            uint32_t _pos = h * bmShear->bmBytesPerRow + w * bmShear->bmBytesPerPel;
+            if (x >= 0 && x < bmiHeader->biHeight && y >= 0 && y < bmiHeader->biWidth) {
+                for (uint8_t k = 0; k < 3; ++k) {
+                    bmShear->bmData[_pos + k] = bmImg->bmData[pos + k];
+                }
+            }
+        }
+    }
+}
+```
+
+### Step Three: Interpolate
+
+There are several methods of interpolation, such as nearest-neighbor interpolation, bilinear interpolation, and bicubic interpolation. Among these, nearest-neighbor interpolation has the highest speed, bicubic interpolation has the highest quality.
+
+The bicubic interpolation can be summarized as solving a system of linear equations with $16$ variables, i.e., find $a_{ij}$, s.t. $p(x, y)=\sum_{i=0}^3 \sum_{j=0}^3 a_{ij} x^i y^j$. An interpolator with similar properties can be obtained by applying a convolution with the following kernel in both dimensions,
+
+$$
+\begin{equation}
+    W(x)=\begin{cases}
+    (a+2)|x|^3-(a+3)|x|^2+1 & ,\ |x|\le 1 \\\\
+    a|x|^3-5a|x|^2+8a|x|-4a & ,\ 1<|x|\le 2 \\\\
+    0 & ,\ |x|>2
+    \end{cases}
+\end{equation}
+$$
+
+where $a$ is usually set to $-0.5$. At this time, the equation can be expressed in a more friendly manner,
+
+$$
+\begin{equation}
+    p(t)=\frac{1}{2}
+    \left[\begin{matrix}
+        1 & t & t^2 & t^3
+    \end{matrix}\right]
+    \left[\begin{matrix}
+        0 & 2 & 0 & 0 \\\\
+        -1 & 0 & 1 & 0 \\\\
+        2 & -5 & 4 & -1 \\\\
+        -1 & 3 & -3 & 1
+    \end{matrix}\right]
+    \left[\begin{matrix}
+        f_{-1} \\\\ f_0 \\\\ f_1 \\\\ f_2
+    \end{matrix}\right]
+\end{equation}
+$$
+
+for $t$ between $0$ and $1$ for one dimension. Note that for $1$-dimensional cubic convolution interpolation $4$ sample points are required. For each inquiry two samples are located on its left and two samples on the right. These points are indexed from $−1$ to $2$ in this text. The distance from the point indexed with $0$ to the inquiry point is denoted by $t$ here. For two dimensions first applied once in $y$ and again in $x$.
+
+```c
+double interpolate(double t1, double f0, double f1, double f2, double f3) {
+    double t2 = t1 * t1, t3 = t2 * t1;
+    double ret = (-t1 + 2 * t2 - t3) * f0;
+    ret += (2 - 5 * t2 + 3 * t3) * f1;
+    ret += (t1 + 4 * t2 - 3 * t3) * f2;
+    ret += (-t2 + t3) * f3;
+    return ret / 2;
+}
+```
+
+### Step Four: Rotate
+
+First we need to calculate the size of the new image. Then we use nearest-neighbor interpolation and bicubic interpolation separately.
+
+```c
+// nearest-neighbor interpolation
+void simple_rotate(BITMAP *bmImg, BITMAP *bmRotate, double theta) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    double minX = fmin(bmiHeader->biHeight * cos(theta), bmiHeader->biWidth * -sin(theta));
+    double maxX = fmax(bmiHeader->biHeight * cos(theta), bmiHeader->biWidth * -sin(theta));
+    double minY = fmin(bmiHeader->biHeight * sin(theta), bmiHeader->biWidth * cos(theta));
+    double maxY = fmax(bmiHeader->biHeight * sin(theta), bmiHeader->biWidth * cos(theta));
+    minX = fmin(minX, fmin(0, bmiHeader->biHeight * cos(theta) - bmiHeader->biWidth * sin(theta)));
+    maxX = fmax(maxX, fmax(0, bmiHeader->biHeight * cos(theta) - bmiHeader->biWidth * sin(theta)));
+    minY = fmin(minY, fmin(0, bmiHeader->biHeight * sin(theta) + bmiHeader->biWidth * cos(theta)));
+    maxY = fmax(maxY, fmax(0, bmiHeader->biHeight * sin(theta) + bmiHeader->biWidth * cos(theta)));
+    uint32_t height = ceil(maxX) - floor(minX);
+    uint32_t width = ceil(maxY) - floor(minY);
+    gen_color(bmImg, bmRotate, height, width);
+    for (uint32_t h = 0; h < height; ++h) {
+        for (uint32_t w = 0; w < width; ++w) {
+            int32_t x = round((h + minX) * cos(theta) + (w + minY) * sin(theta));
+            int32_t y = round((h + minX) * -sin(theta) + (w + minY) * cos(theta));
+            uint32_t pos = x * bmImg->bmBytesPerRow + y * bmImg->bmBytesPerPel;
+            uint32_t _pos = h * bmRotate->bmBytesPerRow + w * bmRotate->bmBytesPerPel;
+            if (x >= 0 && x < bmiHeader->biHeight && y >= 0 && y < bmiHeader->biWidth) {
+                for (uint8_t k = 0; k < 3; ++k) {
+                    bmRotate->bmData[_pos + k] = bmImg->bmData[pos + k];
+                }
+            }
+        }
+    }
+}
+
+// bicubic interpolation
+void rotate(BITMAP *bmImg, BITMAP *bmRotate, double theta) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    double minX = fmin(bmiHeader->biHeight * cos(theta), bmiHeader->biWidth * -sin(theta));
+    double maxX = fmax(bmiHeader->biHeight * cos(theta), bmiHeader->biWidth * -sin(theta));
+    double minY = fmin(bmiHeader->biHeight * sin(theta), bmiHeader->biWidth * cos(theta));
+    double maxY = fmax(bmiHeader->biHeight * sin(theta), bmiHeader->biWidth * cos(theta));
+    minX = fmin(minX, fmin(0, bmiHeader->biHeight * cos(theta) - bmiHeader->biWidth * sin(theta)));
+    maxX = fmax(maxX, fmax(0, bmiHeader->biHeight * cos(theta) - bmiHeader->biWidth * sin(theta)));
+    minY = fmin(minY, fmin(0, bmiHeader->biHeight * sin(theta) + bmiHeader->biWidth * cos(theta)));
+    maxY = fmax(maxY, fmax(0, bmiHeader->biHeight * sin(theta) + bmiHeader->biWidth * cos(theta)));
+    uint32_t height = ceil(maxX) - floor(minX);
+    uint32_t width = ceil(maxY) - floor(minY);
+    gen_color(bmImg, bmRotate, height, width);
+    for (uint8_t k = 0; k < 3; ++k) {
+        for (uint32_t h = 0; h < height; ++h) {
+            for (uint32_t w = 0; w < width; ++w) {
+                double x = (h + minX) * cos(theta) + (w + minY) * sin(theta);
+                double y = (h + minX) * -sin(theta) + (w + minY) * cos(theta);
+                uint32_t _pos = h * bmRotate->bmBytesPerRow + w * bmRotate->bmBytesPerPel;
+                if (x >= 0 && x < bmiHeader->biHeight && y >= 0 && y < bmiHeader->biWidth) {
+                    double g[4];
+                    for (int8_t u = -1; u < 3; ++u) {
+                        double f[4];
+                        int32_t _x = max(0, min(bmiHeader->biHeight - 1, (int32_t) x + u));
+                        for (int8_t v = -1; v < 3; ++v) {
+                            int32_t _y = max(0, min(bmiHeader->biWidth - 1, (int32_t) y + v));
+                            f[v + 1] = bmImg->bmData[_x * bmImg->bmBytesPerRow + _y * bmImg->bmBytesPerPel + k];
+                        }
+                        // interpolate horizontally
+                        g[u + 1] = interpolate(y - (int32_t) y, f[0], f[1], f[2], f[3]);
+                    }
+                    // interpolate vertically
+                    bmRotate->bmData[_pos + k] = adjust(interpolate(x - (int32_t) x, g[0], g[1], g[2], g[3]));
+                }
+            }
+        }
+    }
+}
+```
+
+### Step Five: Scale
+
+Similar to rotating, interpolation is vital for scaling. A slight difference is that, we can storage the results of all horizontal interpolations before performing the vertical, since they will be reused often.
+
+```c
+// nearest-neighbor interpolation
+void simple_scale(BITMAP *bmImg, BITMAP *bmScale, double ratioH, double ratioW) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    uint32_t height = ceil(ratioH * bmiHeader->biHeight);
+    uint32_t width = ceil(ratioW * bmiHeader->biWidth);
+    gen_color(bmImg, bmScale, height, width);
+    for (uint32_t h = 0; h < height; ++h) {
+        for (uint32_t w = 0; w < width; ++w) {
+            int32_t x = round(h / ratioH);
+            int32_t y = round(w / ratioW);
+            x = max(0, min(bmiHeader->biHeight - 1, x));
+            y = max(0, min(bmiHeader->biWidth - 1, y));
+            uint32_t pos = x * bmImg->bmBytesPerRow + y * bmImg->bmBytesPerPel;
+            uint32_t _pos = h * bmScale->bmBytesPerRow + w * bmScale->bmBytesPerPel;
+            if (x >= 0 && x < bmiHeader->biHeight && y >= 0 && y < bmiHeader->biWidth) {
+                for (uint8_t k = 0; k < 3; ++k) {
+                    bmScale->bmData[_pos + k] = bmImg->bmData[pos + k];
+                }
+            }
+        }
+    }
+}
+
+// bicubic interpolation
+void scale(BITMAP *bmImg, BITMAP *bmScale, double ratioH, double ratioW) {
+    BITMAPINFOHEADER *bmiHeader = &bmImg->bmInfo->bmiHeader;
+    uint32_t height = ceil(ratioH * bmiHeader->biHeight);
+    uint32_t width = ceil(ratioW * bmiHeader->biWidth);
+    gen_color(bmImg, bmScale, height, width);
+    double *p = (double *) malloc(bmiHeader->biHeight * width << 3);
+    for (uint8_t k = 0; k < 3; ++k) {
+        for (uint32_t h = 0; h < bmiHeader->biHeight; ++h) {
+            for (uint32_t w = 0; w < width; ++w) {
+                double y = w / ratioW;
+                double f[4];
+                for (int8_t u = -1; u < 3; ++u) {
+                    int32_t _y = max(0, min(bmiHeader->biWidth - 1, (int32_t) y + u));
+                    f[u + 1] = bmImg->bmData[h * bmImg->bmBytesPerRow + _y * bmImg->bmBytesPerPel + k];
+                }
+                // interpolate horizontally
+                p[h * width + w] = interpolate(y - (int32_t) y, f[0], f[1], f[2], f[3]);
+            }
+        }
+        for (uint32_t h = 0; h < height; ++h) {
+            for (uint32_t w = 0; w < width; ++w) {
+                double x = h / ratioH;
+                uint32_t _pos = h * bmScale->bmBytesPerRow + w * bmScale->bmBytesPerPel;
+                double f[4];
+                for (int8_t u = -1; u < 3; ++u) {
+                    int32_t _x = max(0, min(bmiHeader->biHeight - 1, (int32_t) x + u));
+                    f[u + 1] = p[_x * width + w];
+                }
+                // interpolate vertically
+                bmScale->bmData[_pos + k] = adjust(interpolate(x - (int32_t) x, f[0], f[1], f[2], f[3]));
+            }
+        }
+    }
+    free(p);
+}
+```

+ 46 - 0
source/_posts/coding/mathjax-in-wordpress.md

@@ -0,0 +1,46 @@
+---
+title: MathJax in WordPress
+tags: []
+id: "2312"
+categories:
+  - - Coding
+    - Blog Construction
+date: 2018-03-06 14:00:21
+---
+
+## Background
+
+MathJax 允许我们在 Blog 中插入漂亮的数学公式。WordPress 里有许多 MathJax 插件,但大多都不尽如人意,总是缺少这样那样的功能。下面介绍一种不使用插件的方法,可以近乎完美地解决问题。
+
+## Steps
+
+- 点击仪表盘左侧“外观”选项卡中的“编辑”选项,进入“编辑主题”;
+- 在右侧的“主题文件”中,选择“主题页眉”(header.php);
+- 找到其中的`<head>`标签,在下面插入如下代码;
+
+```html
+<script type="text/x-mathjax-config">
+  MathJax.Hub.Config({
+    extensions: ["tex2jax.js"],
+    jax: ["input/TeX", "output/HTML-CSS"],
+    tex2jax: {
+      inlineMath: [ ['$','$'], ["\\(","\\)"] ],
+      displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
+      processEscapes: true
+    },
+    "HTML-CSS": { fonts: ["TeX"] }
+  });
+</script>
+
+<script
+  type="text/javascript"
+  src="https://cdn.bootcss.com/mathjax/2.7.4/latest.js?config=TeX-MML-AM_CHTML"
+></script>
+```
+
+- 点击更新文件,完成后回到站点,查看 MathJax 效果;
+- 根据自己的需要修改配置(以上代码适用于大部分情况)。
+
+## Known Issues
+
+- 每次更换主题后需要重复以上步骤。

+ 80 - 0
source/_posts/coding/my-conkyrc.md

@@ -0,0 +1,80 @@
+---
+title: My .conkyrc
+tags:
+  - Linux
+id: "1511"
+categories:
+  - - Coding
+    - Operating System
+date: 2017-09-21 15:50:29
+---
+
+```ini
+use_spacer right
+use_xft yes
+font Microsoft YaHei:size=8
+xftfont Microsoft YaHei:size=8
+override_utf8_locale yes
+update_interval 1.0
+own_window yes
+own_window_type desktop
+own_window_transparent yes
+#own_window_hints undecorated,below,sticky,skip_taskbar,skip_pager
+own_window_argb_visual yes
+own_window_argb_value 120
+double_buffer yes
+minimum_size 206 5
+maximum_width 400
+draw_shades yes
+draw_outline no
+draw_borders no
+draw_graph_borders no
+default_color ffffff
+default_shade_color 000000
+default_outline_color 000000
+alignment top_right
+gap_x 5
+gap_y 35
+cpu_avg_samples 2
+uppercase no # set to yes if you want all text to be in uppercase
+
+TEXT
+${font Microsoft YaHei:style=Bold:pixelsize=22}${alignc}${time %H:%M:%S}
+${font Microsoft YaHei:pixelsize=16}${alignc}${time %b%d日星期%a}${alignc}
+${color #ffa200}${hr 2}
+${font Microsoft YaHei:pixelsize=12}
+${color #00ffcf}主机名:${color #00ffcf} $alignr$nodename
+${color #00ffcf}内核: ${color #00ffcf}$alignr$kernel
+${color #00ffcf}已运行时间: ${color #00ffcf}$alignr$uptime
+${color #ffd700}${stippled_hr 1}
+${font Microsoft YaHei:pixelsize=12}
+${color #00ff1e}CPU 0: ${cpu cpu0}% $alignr$acpitemp°C(T)
+${color #dcff82}${cpubar 8 cpu0}
+${color #00ff1e}CPU 1: ${cpu cpu1}%
+${color #dcff82}${cpubar 8 cpu1}
+${color #00ff1e}CPU占用:$alignr CPU%
+${color #ddaa00} ${top name 1}$alignr${top cpu 1}
+${color lightgrey} ${top name 2}$alignr${top cpu 2}
+${color lightgrey} ${top name 3}$alignr${top cpu 3}
+${color #ffd700}${stippled_hr 1}$color
+${font Microsoft YaHei:pixelsize=12}
+${color #00ff1e}SAM: $mem $alignr${color #db7093}$memperc%
+${color #78af78}${membar 8}
+${color #00ff1e}SWAP: $swap $alignr ${color #db7093}$swapperc%
+${color #78af78}${swapbar 8}
+${color #00ff1e}内存占用: $alignr MEM%
+${color #ddaa00} ${top_mem name 1}$alignr ${top_mem mem 1}
+${color lightgrey} ${top_mem name 2}$alignr ${top_mem mem 2}
+${color lightgrey} ${top_mem name 3}$alignr ${top_mem mem 3}
+${color #ffd700}${stippled_hr 1}$color
+${font Microsoft YaHei:pixelsize=12}
+${color #00ff1e}硬盘读取速度:${alignr}${diskio_read}
+${color #00ff1e}硬盘写入速度:${alignr}${diskio_write}
+${color #ffd700}${stippled_hr 1}$color
+${font Microsoft YaHei:pixelsize=12}
+${color #00ff1e}网络 $alignr ${color #00ff1e}IP地址: ${color DDAA00}${addr enp3s0}
+${voffset 1}${color #98c2c7} 上传: ${color #db7093}${upspeed enp3s0}/s ${alignr}${color #98c2c7}总共: ${color #db7093}${totalup enp3s0}
+${voffset 1}${color #98c2c7} 下载: ${color #ddaa00}${downspeed enp3s0}/s ${alignr}${color #98c2c7}总共: ${color #ddaa00}${totaldown enp3s0}
+${font Microsoft YaHei:pixelsize=12}
+${color #ffa200}${hr 2}
+```

+ 188 - 0
source/_posts/coding/my-vimrc.md

@@ -0,0 +1,188 @@
+---
+title: My .vimrc
+tags:
+  - Linux
+id: "159"
+categories:
+  - - Coding
+    - Operating System
+date: 2017-06-02 20:30:19
+---
+
+```ini
+" Basis
+"set cul
+set nowrap
+set mouse=a
+"set paste
+set history=1000
+set bg=dark
+syntax on
+set ai
+set cin
+set sw=2
+set sts=2
+set ts=2
+set nu
+set si
+set sm
+set nobk
+"set guioptions-=T
+"set guioptions-=m
+set ignorecase smartcase
+set vb t_vb=
+set ruler
+set magic
+set nohls
+set incsearch
+set et
+set sta
+"set spell
+"set ww=b,s,h,l,<,>,~,[,]
+set encoding=utf-8
+set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1
+set ambiwidth=double
+set cino=:0g0t0(sus
+set selection=inclusive
+set keymodel=startsel,stopsel
+"set wildmenu
+"set cmdheight=1
+"set laststatus=2
+"set statusline=\ %<%F[%1<em>%M%</em>%n%R%H]%=\ %y\ %0(%{&fileformat}\ %{&encoding}\ %l/%L:%c%)
+"set foldenable
+"set foldmethod=syntax
+"set foldcolumn=0
+"setlocal foldlevel=1
+"set foldclose=all
+"nnoremap <space> @=((foldclosed(line('.')) < -3) ? 'zc' : 'zo')<cr>
+colorscheme delek</cr></space>
+
+" Plugins
+set nocp
+filetype off
+
+" set the runtime path to include Vundle and initialize
+set rtp+=~/.vim/bundle/Vundle.vim
+call vundle#begin()
+" alternatively, pass a path where Vundle should install plugins
+"call vundle#begin('~/some/path/here')
+
+" let Vundle manage Vundle, required
+Plugin 'VundleVim/Vundle.vim'
+Plugin 'sCRooloose/nerdtree'
+Plugin 'Valloric/YouCompleteMe'
+Plugin 'vim-syntastic/syntastic'
+Plugin 'easymotion/vim-easymotion'
+Plugin 'fholgado/minibufexpl.vim'
+Plugin 'vim-airline/vim-airline'
+Plugin 'vim-sCRipts/taglist.vim'
+Plugin 'majutsushi/tagbar'
+Plugin 'Yggdroot/indentLine'
+Plugin 'Yggdroot/vim-mark'
+Plugin 'kien/ctrlp.vim'
+Plugin 'sCRooloose/nerdcommenter'
+Plugin 'sjl/gundo.vim'
+Plugin 'godlygeek/tabular'
+
+" The following are examples of different formats supported.
+" Keep Plugin commands between vundle#begin/end.
+" plugin on GitHub repo
+"Plugin 'tpope/vim-fugitive'
+" plugin from http://vim-sCRipts.org/vim/sCRipts.html
+"Plugin 'L9'
+" Git plugin not hosted on GitHub
+"Plugin 'git://git.wincent.com/command-t.git'
+" git repos on your local machine (i.e. when working on your own plugin)
+"Plugin 'file:///home/gmarik/path/to/plugin'
+" The sparkup vim sCRipt is in a subdirectory of this repo called vim.
+" Pass the path to set the runtimepath properly.
+"Plugin 'rstaCRuz/sparkup', {'rtp': 'vim/'}
+" Install L9 and avoid a Naming conflict if you've already installed a
+" different version somewhere else.
+"Plugin 'ascenator/L9', {'name': 'newL9'}
+
+" All of your Plugins must be added before the following line
+call vundle#end()            " required
+filetype plugin indent on    " required
+" To ignore plugin indent changes, instead use:
+"filetype plugin on
+"
+" Brief help
+" :PluginList       - lists configured plugins
+" :PluginInstall    - installs plugins; append <code>!</code> to update or just :PluginUpdate
+" :PluginSearch foo - searches for foo; append <code>!</code> to refresh local cache
+" :PluginClean      - confirms removal of unused plugins; append <code>!</code> to auto-approve removal
+"
+" see :h vundle for more details or wiki for FAQ
+" Put your non-Plugin stuff after this line
+
+let mapleader=","
+
+let g:ycm_global_ycm_extra_conf = '~/.ycm_extra_conf.py'
+nnoremap <leader>gt :YcmCompleter GoToDefinitionElseDeclaration<cr></cr></leader>
+
+let g:EasyMotion_smartcase = 1
+nnoremap <leader><leader>h <plug>(easymotion-linebackward)
+nnoremap <leader><leader>j <plug>(easymotion-j)
+nnoremap <leader><leader>k <plug>(easymotion-k)
+nnoremap <leader><leader>l <plug>(easymotion-lineforward)
+nnoremap <leader><leader>. <plug>(easymotion-repeat)</plug></leader></leader></plug></leader></leader></plug></leader></leader></plug></leader></leader></plug></leader></leader>
+
+let g:syntastic_enable_signs = 1
+let g:syntastic_error_symbol = '!'
+let g:syntastic_warning_symbol = '?'
+let g:syntastic_check_on_open = 1
+let g:syntastic_check_on_wq = 0
+let g:syntastic_enable_highlighting = 1
+
+nnoremap <leader>a= :Tabularize /=<cr>
+vnoremap <leader>a= :Tabularize /=<cr>
+nnoremap <leader>a: :Tabularize /:\zs<cr>
+vnoremap <leader>a: :Tabularize /:\zs<cr></cr></leader></cr></leader></cr></leader></cr></leader>
+
+nnoremap <f2> :NERDTreeToggle<cr>
+nnoremap <f3> :TlistToggle<cr>
+nnoremap <f4> :TagbarToggle<cr>
+nnoremap <f5> :GundoToggle<cr>
+nnoremap <f7> :w<cr>:!clang++ -std=c++11 % -o %< && time ./%<<cr>
+nnoremap <f9> :MBEbp<cr>
+nnoremap <f10> :MBEbn<cr></cr></f10></cr></f9></cr></cr></f7></cr></f5></cr></f4></cr></f3></cr></f2>
+
+inoremap ; ;<esc>a
+inoremap ' <c-r>=QuoteDelim("'")<cr><esc>a
+inoremap " <c-r>=QuoteDelim('"')<cr><esc>a
+inoremap ( ()<esc>i
+inoremap ) <c-r>=ClosePair(')')<cr><esc>a
+inoremap [ []<esc>i
+inoremap ] <c-r>=ClosePair(']')<cr><esc>a
+inoremap { {<cr>}<esc>O
+"inoremap } <c-r>=CloseBracket()<cr></cr></c-r></esc></cr></esc></cr></c-r></esc></esc></cr></c-r></esc></esc></cr></c-r></esc></cr></c-r></esc>
+
+function ClosePair(char)
+if getline('.')[col('.') - 1] == a:char
+return "\<right>"
+else
+return a:char
+endif
+endf</right>
+
+function CloseBracket()
+if match(getline(line('.') + 1), '\s*}') < 0
+return "\<cr>}"
+else
+return "\<esc>j0f}a"
+endif
+endf</esc></cr>
+
+function QuoteDelim(char)
+let line = getline('.')
+let col = col('.')
+if line[col - 2] == "\"
+return a:char
+elseif line[col - 1] == a:char
+return "\<right>"
+else
+return a:char.a:char."\<esc>i"
+endif
+endf
+```

+ 79 - 0
source/_posts/coding/optimized-input-and-output-in-c.md

@@ -0,0 +1,79 @@
+---
+title: Optimized Input and Output in C++
+tags:
+  - C/C++
+id: "2583"
+categories:
+  - - Coding
+    - Programming Language
+date: 2018-03-14 20:35:32
+---
+
+## Background
+
+C++标准库`<iostream>`中的`cin, cout`相比`<cstdio>`中的`scanf, printf`要方便许多,但速度也较为缓慢。在输入输出的数据类型不是很特殊时,可以使用以下代码来代替`cin, cout`(要包含`<cctype>, <cstdio>`,不要包含`<iostream>`)。
+
+## Code
+
+```cpp
+class InputStream {
+private:
+  static int const BUFFER_SIZE = 10000000;
+  char *buffer;
+
+public:
+  InputStream() {
+    buffer = new char[BUFFER_SIZE]();
+    fread(buffer, sizeof(char), BUFFER_SIZE, stdin);
+  }
+  InputStream &operator>>(int &n) {
+    for (; *buffer < '0' || *buffer > '9'; ++buffer)
+      ;
+    for (n = 0; *buffer >= '0' && *buffer <= '9';
+         (n *= 10) += *(buffer++) - '0')
+      ;
+    return *this;
+  }
+  InputStream &operator>>(char &c) {
+    for (; !isprint(*buffer) || *buffer == ' '; ++buffer)
+      ;
+    c = *(buffer++);
+    return *this;
+  }
+  InputStream &operator>>(char *s) {
+    char *t = s;
+    for (; !isprint(*buffer) || *buffer == ' '; ++buffer)
+      ;
+    for (; isprint(*buffer) && *buffer != ' '; *(t++) = *(buffer++))
+      ;
+    *t = '\0';
+    return *this;
+  }
+} cin;
+
+class OutputStream {
+private:
+  static int const BUFFER_SIZE = 10000000;
+  char *buffer, *cur;
+
+public:
+  OutputStream() { buffer = cur = new char[BUFFER_SIZE](); }
+  ~OutputStream() { fwrite(buffer, sizeof(char), cur - buffer, stdout); }
+  OutputStream &operator<<(int const &n) {
+    for (sprintf(cur, "%d", n); *cur; ++cur)
+      ;
+    return *this;
+  }
+  OutputStream &operator<<(char const &c) {
+    *(cur++) = c;
+    return *this;
+  }
+  OutputStream &operator<<(char *s) {
+    for (char *t = s; *t; *(cur++) = *(t++))
+      ;
+    return *this;
+  }
+} cout;
+
+static char const endl = '\n';
+```

+ 15 - 0
source/_posts/life/2017-jl-fighting.md

@@ -0,0 +1,15 @@
+---
+title: 2017.JL.Fighting!
+tags: []
+id: "448"
+categories:
+  - - Life
+    - Essay
+date: 2017-06-17 00:00:12
+---
+
+![](1.jpg)
+
+![](2.jpg)
+
+![](3.jpg)

BIN=BIN
source/_posts/life/2017-jl-fighting/1.jpg


BIN=BIN
source/_posts/life/2017-jl-fighting/2.jpg


BIN=BIN
source/_posts/life/2017-jl-fighting/3.jpg


+ 11 - 0
source/_posts/life/2018.md

@@ -0,0 +1,11 @@
+---
+title: 2018
+tags: []
+id: "1685"
+categories:
+  - - Life
+    - Essay
+date: 2018-01-01 00:00:58
+---
+
+## Goodbye 2017, Hello 2018!

+ 23 - 0
source/_posts/life/give-up-sometimes.md

@@ -0,0 +1,23 @@
+---
+title: Give Up Sometimes
+tags: []
+id: "3438"
+categories:
+  - - Life
+    - Essay
+date: 2019-09-30 01:00:21
+---
+
+> The expression "Never, never give up" means to keep trying and never stop working for your goals. Do you agree or disagree with this statement? Use specific reasons and examples to support your answer.
+>
+> _Topic_
+
+Many people believe in the saying that we should never give up casually, which tells them to work incessantly for their goals. Admittedly, this spirit-lifting saying has given countless people a pat on the back, inspired them to strive for their long-held dreams and acquire prosperity in the end. But generally speaking, I suppose we don't have to shy away from giving up. Sometimes we need to put aside our persistence and accept the abnegation.
+
+First of all, the initial orientation matters. We can’t guarantee that the first step we take is towards the right direction, which means we can be heading the wrong without perceiving it. If so, it would be better to just turn around than to press on. There’s no need to sacrifice your success for simply senseless insistence. One example of this is an ancient idiom from China, narrating a man who intended to go south steered his carriage to the north regardless of the prompting from a passer-by. He had not realized the sheer meaninglessness of his action. Of course, he has not arrived his destination yet. Had he listened to others’ advice and turned back, he would have reached it.
+
+Besides, we should pay heed to the progress. Not only are we likely to move forward to where we do not expect at the beginning, but also during the process, and thus adjusting ourselves dynamically should be attached significance to. In addition, even though we know clearly what we are doing and why we are doing it, we may lack the capacity to do it well, and the progress can fall short of our expectation. When we’re patently unsuitable for tackling certain type of things, insisting may not be the optimal option. Some classmates in my high school will be rather representative instances. As soon as they entered the school, each of them picked an Olympiad in a certain subject. Yet before long, they found themselves not very good at competition, thereby opting out, focusing on regular curriculum. It turned out ultimately that they obtained fairly high scores in the college entrance examination. We can derive from it the fact that giving up does not necessarily mean losing, but somehow means wining.
+
+What’s more, the outcome may be perplexing. When it come out, it's time to judge whether it is right or not. We can scarcely ensure that the result will always fulfil our anticipation, so when we receive an unexpected result which is against the basic facts, probably it should not be calculated in statistics. At this time, alleging that the result has no problem seems a bit stubborn. On the other hand, if the result does not correspond with what we used to deem it should be, the chances are what deceives you is not the result, but the acknowledgement. If we look back upon history of science and technology, there are innumerable examples, such as Galileo who doubted the previous law of free fall, Bohr who suspected the previous nuclear model, etc. All of these indicate that even the most seeming truth has the possibility to be broken someday. The interesting thing is that, the result and the truth were dropped alternately, which set the pace for the development of human civilization. We wouldn’t have today’s advancement if there were no giving up in the world.
+
+The old saying "Never, never give up" will continue to exert its positive attitude and lift people up. However, I take the viewpoint that people sometimes need to give up what they think, have or believe. This is not persuading you to give up everything in your life, but encouraging you to give up in a more sensible way, in which you will better encounter what you love, accomplish what you dream, and make what you desire to be.

+ 11 - 0
source/_posts/life/happy-2020.md

@@ -0,0 +1,11 @@
+---
+title: Happy 2020!
+tags: []
+id: "3565"
+categories:
+  - - Life
+    - Essay
+date: 2020-01-01 00:00:00
+---
+
+## A story that will never end.

+ 11 - 0
source/_posts/life/happy-childrens-day.md

@@ -0,0 +1,11 @@
+---
+title: Happy Children's Day!
+tags: []
+id: "1695"
+categories:
+  - - Life
+    - Essay
+date: 2017-06-01 00:00:47
+---
+
+## Keep a good heart.

+ 11 - 0
source/_posts/life/happy-graduation.md

@@ -0,0 +1,11 @@
+---
+title: Happy graduation!
+tags: []
+id: "3381"
+categories:
+  - - Life
+    - Essay
+date: 2019-06-01 17:00:17
+---
+
+## Thanks for everything.

+ 11 - 0
source/_posts/life/hello-world.md

@@ -0,0 +1,11 @@
+---
+title: Hello World!
+tags: []
+id: "1"
+categories:
+  - - Life
+    - Essay
+date: 2017-05-31 00:00:34
+---
+
+## Welcome to If7's Home!

+ 37 - 0
source/_posts/life/light-i.md

@@ -0,0 +1,37 @@
+---
+title: Light I
+tags: []
+id: "3277"
+categories:
+  - - Life
+    - Essay
+date: 2018-09-01 04:00:02
+---
+
+He was born blind.
+
+He had suffered the darkness for years which he had been accustomed to, till that day, when something mysterious happened.
+
+Abruptly he felt a soft, faint beam pierced the darkness, which seemed to be a firefly in the air, a lighthouse on the sea. Confused and attracted though, he got a sense of scare and decided not to move his feet, since he had never seen even a glimmer of light before.
+
+In an instant the star-like light brightened, and it started twinkling. His curiosity aroused, he slowly groped his way towards the source, even if he couldn't see it clearly. "The light is so gentle." He thought to himself.
+
+He recalled the period when he was surrounded by absolute darkness. It was not until his third birthday that he was told the earth was full of gorgeous scenery which he wasn't able to witness, however he hadn't got the point and he still lived joyfully. But gradually he had no notion of what people were talking when they chatted about weathers, landscapes, and even colors. This beset him a lot, and at last, he conceded the adversity reluctantly.
+
+He had plodded for... how long? He couldn't tell accurately, but he was depressed by the discovery of the fact that it was still a long distance from where the light was emitted.
+
+Drooping his head with a sigh, he slowed his pace, disheartened. "Why should I undergo all of these?" A disgruntled thought flashed in his heart, and he couldn't help asking.
+
+That people queried the cause of his blindness was one of his antipathies. He had many antipathies, for instance, talking to others, but that was the very one he detested most, for always he was laughed at and was played a trick on at school, which made him consider everybody untrustworthy.
+
+But he continued dragging himself to the light, as if he was driven by an invisible force. His mind in a turmoil, he couldn't concentrate. He submerged himself in the past.
+
+Was there anyone caring about him? He didn't know, yet he would like to believe there wasn't.
+
+Occasionally he had a strong desire to leave this unlit world, the world with no hope, no laughter, and no happiness, to which he deemed himself useless. He was convinced of the sheer meaninglessness of life, and regarded every day as suffering. He even thought up a method. Maybe... jumping off something was a practical choice?
+
+However, the explanation for the existence of this light remained a mystery. He didn't care about this. He merely wished to see the source only once, which gave out light that he had yearned for for long. His ears were replete with winds, as he was rushing so fast—he found himself flying in the air.
+
+And ultimately, he fell, asleep.
+
+Yet light was there, still, inside his heart.

+ 37 - 0
source/_posts/life/light-ii.md

@@ -0,0 +1,37 @@
+---
+title: Light II
+tags: []
+id: "3279"
+categories:
+  - - Life
+    - Essay
+date: 2018-10-02 05:20:11
+---
+
+He had no idea where he was.
+
+And a strong feeling that he had never experienced before occupied his mind little by little.
+
+The light was still there. So dim and nebulous was it that the dread of being encircled again by the darkness swiftly crept up from the bottom of his heart. Though he managed to shift his feet with all his strength, he could hardly budge an inch.
+
+Brimming with confusion, he cast a glance at the ground, but only to find blur in his sight. Apparently he was trapped in a funny dream, which was made up of what he had sustained in reality.
+
+Weak as it was, the light still produced warmth like the sun. He knew clearly the sensation of basking. The sunlight was gentle and mild. The feeling of extreme familiarity brought him back to his childhood.
+
+He vaguely remembered that all around him were glee and thrill when he was a infant. Often his parents took him out, holding him in the arms, bathed in the sunshine. Pure and innocent, he enjoyed everything he heard, smelled, touched and tasted, not being aware of the fact that there existed something called vision, which he later thirsted for.
+
+In his memories, his parents never loved him. Oh, maybe they were, before his third birthday. It was unbelievable that a three-year-old little boy could bear so many things in mind such as hearing a baby's weep and his parents' mirth, as well as realizing his disability, which had bothered him for what seemed like centuries. In fact, the voices kept reverberating in his mind, and continued pushing him to the verge of collapse. He considered that day a turning point of his life.
+
+"My third birthday." he smiled wryly, "My last birthday."
+
+He had no intention, and did not willing, to grumble at his parents. What made all the things transpire was his blindness. Even he felt guilty, and believed that himself was to blame. It was him that let his parents down, and that lost courage to live. It was his disadvantage that made him an object of ridicule, and that surrounded him with the deepest darkness. Had he not been blind, everyone would have lived a better life.
+
+Few merry moments could be found in his recollections afterwards. Or maybe.. he pretended to have forgotten them?
+
+He did not stop searching in his memories until the light began to peter out. His mental pain slightly alleviated as he pulled himself out of mind-wandering.
+
+But an old saying told by his grandpa suddenly emerged in his brain, which sent shudder down his spine, "Only when a person is about to depart this world will he look back upon his entire life."
+
+For the first time in his life, his heart was seized by such unimaginable fear, and it nearly ceased bounding.
+
+And there went off the light, silently, remaining darkness.

+ 43 - 0
source/_posts/life/light-iii.md

@@ -0,0 +1,43 @@
+---
+title: Light III
+tags: []
+id: "3294"
+categories:
+  - - Life
+    - Essay
+date: 2018-12-09 16:30:48
+---
+
+He could feel drizzle patting his face.
+
+To the best of his ability, he stood up. Anew, darkness enveloped him.
+
+He pondered about what he had just experienced, which was patently a dreadful nightmare. Yet everything was so real, that he couldn't persuade himself not to recall that dream.
+
+In a moment, he caught sight of something in the distance, something that was gigantic enough to be seen. It was hard for him to put down the desire to approach it. He started padding, but he sensed something preternatural. As if he was walking through a long narrow corridor, in which there was solely enormously faint light.
+
+When he finally reached the thing, he was astonished to find that he was facing a boundless wall which was clearly insurmountable. Scarcely had he had time to figure out what was happening when he was sucked into an exceedingly strong whirlpool, losing his consciousness.
+
+He was astounded again when he opened his eyes.
+
+In his sight there turned seasons. He could see fresh green seedlings protruding, carefree swallows singing and dancing freely in spring, and the brilliant sun creeping up gradually, tiny trees slowly growing to a huge proportion during summer, and with autumn coming the golden foliage fell, embellishing the azure blue sky, and before long clean white snowflakes began to appear, which indicated that winter had reigned the ground. It was quite amazed that he was able to recognize the various colors in no time, as well as the weathers he used to feel and the planets passing over head. What he once could only heard of was just in front of him, like an ancient but exquisite picture scroll opening before his face by degrees. Wherever his vision went, the superb landscapes appealed to him.
+
+He was immersing himself in this fabulous world when in an instant, as if a heavy gate was opened, recollections flooded his brain.
+
+He attempted to cast his mind back, and something mysterious occurred.
+
+He could see his father holding him in his arms, while his mother cradled his brother, staring at two little lives, humming a familiar song he had heard for many times. He could see he and his brother climbing up and down at home, and his brother frequently lending him a hand without which he might have fallen off the table that was still higher than him. He could see the family outing, during which his relatives did not get weary of guiding him, telling him what was going on around, such as fragrant flowers dancing with colorful butterflies. So joyful was he at that time that as he thought, in his heart a knot was untied, and the ice melted.
+
+Never had he realized that so many fantastic memories had been missing since his third birthday, for which he would have lived delightedly, appreciating the love and hope on earth, summoning up the courage and confidence, to strive for an entirely different life.
+
+But it was said there was no sense crying over every mistake, for what had been done was unchangeable. He smiled, blandly, as he floated in the air, higher and higher.
+
+Looking down, he saw himself, just lying, peacefully, on the snow-covered ground.
+
+---
+
+"There lies a wall, up to the heaven, down to the hell, left to infinity, right to where you cannot see."
+
+"This wall is called death."
+
+"Only when a person is confronted with this wall will he look back upon his entire life."

+ 92 - 0
source/_posts/life/notes-on-physics.md

@@ -0,0 +1,92 @@
+---
+title: Notes on Physics
+tags:
+  - Physics
+id: "3243"
+categories:
+  - - Life
+    - Study
+date: 2018-06-30 13:15:27
+---
+
+## One-dimensional motion
+
+$$
+\begin{align}
+v_f&=v_i+a \Delta t \\\\
+\Delta x&=v_i \Delta t+{a \Delta t^2 \over 2} \\\\
+\Delta x&={v_i+v_f \over 2} \Delta t \\\\
+v_f^2&=v_i^2+2a \Delta x
+\end{align}
+$$
+
+## Forces and Newton's laws of motion
+
+$$
+\begin{align}
+F=ma
+\end{align}
+$$
+
+## Centripetal force and gravitation
+
+$$
+\begin{align}
+a&={v^2 \over r}=\omega^2r \\\\
+F&=G{m_1m_2 \over r^2}
+\end{align}
+$$
+
+## Work and energy
+
+$$
+\begin{align}
+K&={mv^2 \over 2} \\\\
+U_g&=F_gh=mgh \\\\
+P&={W \over t}={Fx \over t}=Fv=mav
+\end{align}
+$$
+
+## Electric charge, field, and potential
+
+$$
+\begin{align}
+F&=k{Q_1Q_2 \over r^2} \\\\
+E&=k{Q \over r^2} \\\\
+E&=2 \pi k \sigma \\\\
+U_e&=k{Q_1Q_2 \over r} \\\\
+V&=k{Q \over r}
+\end{align}
+$$
+
+## Circuits
+
+$$
+\begin{align}
+R&={V \over I} \\\\
+C&={Q \over V} \\\\
+C&={A \over 4 \pi kd} \\\\
+P&={U \over t}={U \over Q} \cdot {Q \over t}=VI=I^2R \\\\
+W&=Pt=VIt=I^2Rt
+\end{align}
+$$
+
+## Magnetic forces, magnetic fields, and Faraday's law
+
+$$
+\begin{align}
+F&=QvB={Q \over t}(vt)B=ILB={B^2L^2v \over R} \\\\
+B&={\mu_0I \over 2 \pi r} \\\\
+\Phi&=BA \\\\
+E&=N{\Delta \Phi \over \Delta t}=N{B\Delta A \over \Delta t}=NBLv \\\\
+V_s&=V_p{N_s \over N_p}
+\end{align}
+$$
+
+## Momentum
+
+$$
+\begin{align}
+I=mv=QLB={B^2L^2x \over R}
+\end{align}
+$$

+ 31 - 0
source/_posts/life/spring-i.md

@@ -0,0 +1,31 @@
+---
+title: Spring I
+tags: []
+id: "3299"
+categories:
+  - - Life
+    - Essay
+date: 2019-03-30 00:00:41
+---
+
+I heard something twittering on the balcony.
+
+Walking on my tiptoe, I reached the window, through which I spotted a turtle dove standing on the stainless-steel-made railing, with a sprig in its beak. Barely had I observed it attentively when it hastily fled away, leaving behind a fuzzy silhouette.
+
+I was leaning on the windowsill, looking down, when a ball of white captivated me, which I instantly recognized as peach blossoms. Bathed in radiant sunlight, they stretched their exquisite bodies in all directions. The balmy breeze gently swayed the flowers, carrying the scent they exuded wherever it passed. Eventually here came the spring. As I was about to return to my room, my eyes rested on a half-finished bird nest that lay on the board put on the railings.
+
+It was unbelievable that the dove was planning to build a nest on our balcony. Yet this was the case. What's more, I witnessed two doves erecting their lovely home together. Before long, a complete nest came out.
+
+I could see in the mild twilight, a tiny nest lying steadily behind the huge flower pots, and an adorable dove perching on the nest. The setting sun cast its silky beams on this little cute creature through the verdant plants in the pots, resembling a warm hand patting tenderly. This tranquil scene was set off by the distant golden horizon, which formed the most enchanting landscape in the world. I could picture that under its little body, eggs were being warmed up, and new life was being brewed.
+
+But we all know that good times do not last long, which I grasped on that drizzly evening when the dove had gone away and had not returned.
+
+I stared at the empty nest vacantly, a sense of loss welling up in my heart. There lay the sole egg placidly, reflecting the cold light which the moon shed on the earth. Why didn't the dove return? Maybe, I unintentionally frightened it, or, it had found a superior habitat. But, did it desert its cherished egg? On this fairly chilly rainy night, where could it go? Would it be shivering all over in the freezing wind? As the moonlight suffused the ground, I perceived that, never would I know the answer.
+
+I leaned on the windowsill, overlooking the swaying flowers. I could hear raindrops knocking the canopy, composing a rumpled melody agitating my thoughts. I could see them trickling down the eaves, descending, sinking into puddles, feebly splattering a spray of droplets. Also I could hear the moaning wind dashing through the alley, wildly rattling loose windows and doors. I could see it wobbling the leaves furiously, severing them from branches and tugging them down. But, in the meantime, I could see the blossoms that the tree threw up in the air, being hit by the rain and being swung by the wind, while they kept emanating aroma that wafted all around. I could see the myriad scattered petals which fell on the moist tarmac, all colors draining rapidly, yet somehow, they were peculiarly appealing with the lamplight flickering in dewdrops.
+
+It suddenly occurred to me that, unlike what I considered before, what was called spring, was not a picture in a particular style. It not only meant arrival and growth, but also meant departure and wilting to some extent.
+
+So the spring is not all about a couple of turtle doves who are busy constructing a cozy neat twiggy nest where the new life will be brought, nor about the pale pink blossoms that bloom wherever you behold, adorning the corner of the bright blue sky. The essence of the spring lies in the persistence that the doves possess, even if they get so close to human beings and lose one of the only two eggs. It lies in the patience with which the dove squats in its nest unceasingly, awaiting the arrival of the baby, no matter the icy drizzle and the roaring gale. It lies in the optimism which encourages the blossoms to bloom vigorously in the frigid sprinkle. It lies in the devotion of the blossoms as they make the world transfigured despite the adversity they are confronted with.
+
+I lingered on the balcony, hearkening wind and rain, dismay dissipating gradually. And here came the spring. I could sense that, distinctly.

+ 41 - 0
source/_posts/life/spring-ii.md

@@ -0,0 +1,41 @@
+---
+title: Spring II
+tags: []
+id: "3366"
+categories:
+  - - Life
+    - Essay
+date: 2019-05-14 16:35:05
+---
+
+It was a surprise to discover a new nest opposite the window.
+
+Through the telescope, I scrupulously watched the doves in the nest, who were reposing restfully. Narrowly I discerned that they were a family comprised of a mother and two babies. Sighing with relief, I thought, "Finally the dove has returned, and luckily I can still see the family." I even expected to witness the first flight of the baby doves.
+
+I started wondering where the doves came from, as it was odd that they were inclined to construct their home here between the buildings, and after a period of cogitating, I ultimately deduced that once they might live in the decrepit houses nearby which were to be pulled down owing to the modernization and urbanization.
+
+From this viewpoint, the doves seemed to be pitiable. "It can be tough for them to find a congenial shelter in the city." I uttered in my heart, as I observed them fluttering their gray wings. It looked as if they were enjoying the serene life. Yet my brain did not cease contemplating.
+
+Living in the era with rapid technological advancements which largely hasten our pace of life, we ineluctably forgo the joyous time being close to nature. And not until the demise of the erstwhile happiness we attained from nature comes do we realize that it is deplorable to turn ourselves into a hardened machine.
+
+The blood-red sun was sinking and a livid cloud in the east received its rays. A twinge of woe surged through me. If we humanity kept pursuing interests by damaging the environment, what would the attendant consequence be? Would it only be driving the doves homeless?
+
+I couldn't help recalling the days when the city was shrouded in thick smog, and people could scarcely distinguish the things in the distance. And the rivers in the city, which were limpid before, had become turbid in recent decades. The foregoing instances were attributed to human activities. Not to mention the global warming, the ozone depletion, and the extinction of multitudinous species. Urbanization was just all of these in miniature.
+
+The sky was tinted with black inch by inch, and the doves quieted down again. I shifted my eyes from them.
+
+I leaned on the windowsill, looking up at the firmament, which used to be spangled with innumerable stars that were now unseen to us. Embedded aloft in the inky night sky, the pale yellow full moon cast its glow over the city, like an infinite piece of cloth mantling, yet faraway as the neon lights were, their harsh beams penetrated the textile that the moon carefully sewed without any difficulty, as if it was nonexistent. The dazzling rays pricked not only my eyes, but also my heart. "Perhaps the doves dread the blinding light, too."
+
+There was a time when all the doves could flit from tree to tree jovially in the dense and emerald woodlands, where they interacted with other creatures complying with the principles of nature. It was not until human beings arose that changes occurred. Trees were felled at first, and then sewage and fumes were disgorged by pipes. As the time elapsed our compunction was attenuated unawares by the profits we gained at the price of the environment we lived. We averted our gaze from what was prime, focusing on what was subordinate. The cupidity for gain and the scant heed we paid to conservation incurred the mire we confront today.
+
+We really should pause, ruminate for a while, and inquire ourselves, what is our original orientation, and does what we endeavor to do diverge from that aim. Is our purpose to make every hue around us grey? Or to fetter ourselves in the forest of reinforced concrete? Or, to alter the earth to meet human's multifarious demands regardless of the sustainability?
+
+The onus is on us to maintain an agreeable environment. We need introspection, though it may seems arduous to atone for the devastation.
+
+Fortunately, we gradually awake to the pernicious effects, and we are making a change. Eco-civilization construction has been put forward, and the departments concerned are sparing no effort to ameliorate the imbalanced ecology.
+
+It was late spring, and every plant was flourishing. I heard the birds warbling a mellifluous night chorus, my heart mollified.
+
+I entertain the hope that one day, we can thoroughly relish the felicity from nature. I envision an unspoiled environment. If we esteem nature, make optimal decision, and take prompt action.
+
+I'm looking forward to the arrival of that very day.

+ 25 - 0
source/_posts/life/the-beginning.md

@@ -0,0 +1,25 @@
+---
+title: The Beginning
+tags: []
+id: "3296"
+categories:
+  - - Life
+    - Essay
+date: 2019-01-01 00:00:03
+---
+
+The bell is about to ring. The new year is in sight.
+
+Time flies as we grow and mature, leaving naivety behind. No sooner have we relived the past year in detail than we face the coming year hurriedly. Lay down the heavy burden, repack the travelling bag, take our initial dream, step forward readily and firmly.
+
+It's said those who have received compliments should be rather careful, for other's acclaim may block the consciousness of weakness, which surely leads to failure. Yet by no means shall we belittle ourselves, since nobody will have confidence in us unless we are confident. Owning a distinct understanding of oneself, cultivating the better part, remedying what have been done that turns out a mistake, are ways to adjust our mentality.
+
+When we gaze at the vast, dark night sky, sparsely-arranged stars hang high, whispering about what they have just witnessed on this tiny, lonely, but vigorous cerulean planet, such as a large amount of teenagers burning the midnight oil, waiting for that extraordinary moment, after which they may become an adult, or may bare his or her heart to someone else, or, may just sigh with the feeling that it seems something in life, which is hard to be aware of, has vanished silently and drastically. Whatever they see, the sparkling stars keep extending their best wishes to whom they have not known and may never know. The stars, the glinting stars, the stars that split the profound night sky, for how long will we still be able to look up at you?
+
+When we overlook the pure white snowfield, accumulated snowflakes lie serenely, mirroring the warm, golden light that streetlamps cast, illumining the bottom of passers-by's hearts, which originally filled with some annoying things, like working overtime without gaining anything, or getting into a conflict with someone he or she considers dearest, or, biting, frigid wind blowing right into cheek, as if thousands of sharp knives are pricking the skin. But at the time, the light disperses the dust, and pushes the haze in the heart aside, bringing peace and quiet, awakening hope for the future, for the year approaching. The snow, the crystal snow, the snow that decorates the empty city streets, will you still be here years later?
+
+No matter how we get through the year just past, it doesn't make any sense to immerse in the memory. We shall always look forward, but don't forget, up in the sky, down on the ground, there must be someone else, caring you, supporting you, giving you hope whenever it seems impossible.
+
+Anyhow, it's a brand new beginning.
+
+Goodbye, 2018. Hello, 2019.

+ 104 - 0
source/_posts/oi/another-chocolate-maniac.md

@@ -0,0 +1,104 @@
+---
+title: Another Chocolate Maniac
+tags:
+  - Bitmask
+  - Dynamic Programming
+id: "1598"
+categories:
+  - - OI
+    - Common Skill
+date: 2017-10-27 11:50:11
+---
+
+## 题目描述
+
+Bob really LOVES chocolate. He thinks he never gets enough. Imagine his joy when his parents told him that they would buy him many rectangular chocolate pieces for his birthday. A piece of chocolate is a $2 \times 1$ or $1 \times 2$ rectangle. Bob's parents also bought him a nice birthday cake, which can be imagined as a matrix having $M$ rows and $N$ columns. Some positions on the cake are occupied by candles, the others are empty. Bob's parents asked their son to place as many chocolate pieces as he can on the empty squares on the cake, in such a manner that no two chocolate pieces overlap. However, he would like to keep the chocolate pieces to himself. That's why, he wants to place only a minimal amount of them on the cake and keep the rest. In order not to make Mon and Dad suspicious, Bob wants to place the chocolate pieces in such a way, that no other piece may be placed on the cake (that is, there won't exist any two adjacent empty squares). Find the minimal number of pieces which need to be placed on the cake, so that they do not overlap and no extra piece may be added.
+
+## 题意概述
+
+给定一个$M \times N$的网格,其中有些格子是满的。要求用$2 \times 1$和$1 \times 2$两种方块填充空格子,使得不存在两个相邻的空格子。求至少需要多少方块。
+
+数据范围:$1 \le M \le 70, \; 1 \le N \le 7$。
+
+## 算法分析
+
+令$f_{i, j, k}$表示处理到第$i$行,第$(i-1)$行的状态为$j$且第$i$行的状态为$k$的方案数。转移时要注意判断上下两行是否有相邻的空格子。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+
+using namespace std;
+
+namespace std {
+  template <typename T> void maxify(T &a, T b) { b > a && (a = b); }
+  template <typename T> void minify(T &a, T b) { b < a && (a = b); }
+}
+
+struct IOManager {
+  template <typename T> inline bool read(T &x) { char c = '\n'; bool flag = false; x = 0; while (~ c && ! isdigit(c = getchar()) && c != '-') ; c == '-' && (flag = true, c = getchar()); if (! ~ c) return false; while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar(); return (flag && (x = -x), true); }
+  inline bool read(char &c) { c = '\n'; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; return ~ c; }
+  inline int read(char s[]) { char c = '\n'; int len = 0; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; if (! ~ c) return 0; while (isprint(c) && c != ' ') s[len ++] = c, c = getchar(); return (s[len] = '\0', len); }
+  template <typename T> inline IOManager operator > (T &x) { read(x); return *this; }
+  template <typename T> inline void write(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
+  inline void write(char c) { putchar(c); }
+  inline void write(char s[]) { int pos = 0; while (s[pos] != '\0') putchar(s[pos ++]); }
+  template <typename T> inline IOManager operator < (T x) { write(x); return *this; }
+} io;
+
+struct Solver {
+private:
+  static const int N = 80;
+  static const int M = 8;
+  int n, m, f[2][1 << M][1 << M], mp[N];
+  void input() {
+    io > n > m;
+    for (int i = 1; i <= n; ++ i)
+      for (int j = 1; j <= m; ++ j) {
+        char c; io > c, (mp[i] <<= 1) += c == '*';
+      }
+    mp[0] = mp[n + 1] = mp[n + 2] = (1 << m) - 1;
+  }
+  void init() { memset(f, 0x1f, sizeof f), f[0][mp[0]][mp[1]] = 0; }
+  bool check(int s) {
+    bool flag = false;
+    for (int i = 0; i < m; s >>= 1, ++ i) {
+      if (s & 1) flag = false;
+      else { if (flag) return false; flag = true; }
+    }
+    return true;
+  }
+  void update(int x, int a, int b, int s, int val, int p) {
+    if (check(b) && ! ((1 << m) - 1 - a & (1 << m) - 1 - b)) minify(f[x][b][s], val);
+    for (int i = p; i < m; ++ i)
+      if (! (b & 1 << i)) {
+        if (! (s & 1 << i)) update(x, a, b | 1 << i, s | 1 << i, val + 1, i + 1);
+        if (i && ! (b & 1 << i - 1)) update(x, a, b | 1 << i | 1 << i - 1, s, val + 1, i + 1);
+      }
+  }
+  void process() {
+    int cur = 0;
+    for (int i = 1; i <= n + 1; cur ^= 1, ++ i) {
+      memset(f[cur ^ 1], 0x1f, sizeof f[cur ^ 1]);
+      for (int j = 0; j < 1 << m; ++ j)
+        for (int k = 0; k < 1 << m; ++ k)
+          if (f[cur][j][k] < 5e8) update(cur ^ 1, j, k, mp[i + 1], f[cur][j][k], 0);
+    }
+    io < f[cur][(1 << m) - 1][(1 << m) - 1] < '\n';
+  }
+
+public:
+  void solve() { input(), init(), process(); }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 86 - 0
source/_posts/oi/archipelago.md

@@ -0,0 +1,86 @@
+---
+title: Archipelago
+tags:
+  - Plane Geometry
+id: "1553"
+categories:
+  - - OI
+    - Computational Geometry
+date: 2017-10-19 10:50:34
+---
+
+## 题目描述
+
+Archipelago Ber-Islands consists of $N$ islands that are vertices of equiangular and equilateral $N$-gon. Islands are clockwise numerated. Coordinates of island $N_1$ are $(x_1, y_1)$, and island $N_2$ - $(x_2, y_2)$. Your task is to find coordinates of all $N$ islands.
+
+## 题意概述
+
+平面上有一个正$N$边形,它的顶点按顺时针方向依次标号。给定第$N_1$、$N_2$个点的坐标$(x_1, y_1)$、$(x_2, y_2)$,求所有顶点的坐标。
+
+数据范围:$3 \le N \le 150, \; N_1 \neq N_2, \; |x_1|, |y_1|, |x_2|, |y_2| \le 2 \times 10^6$。
+
+## 算法分析
+
+可以先计算出圆心角,根据圆心角、三角函数和向量旋转计算出圆心坐标,之后就可以用向量旋转计算出所有顶点的坐标。
+
+将$(x, y)$绕原点逆时针旋转$\theta$度得到$(x\cos(\theta)-y\sin(\theta), x\sin(\theta)+y\cos(\theta))$。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+
+using namespace std;
+
+static const double PI = acos(-1);
+static const double EPS = 1e-8;
+int cmp(double t) { return fabs(t) < EPS ? 0 : t > 0 ? 1 : -1; }
+
+struct Point {
+  double x, y;
+  Point(double _x = 0, double _y = 0) : x(_x), y(_y) {}
+  Point operator + (const Point &p) const { return Point(x + p.x, y + p.y); }
+  Point operator - (const Point &p) const { return Point(x - p.x, y - p.y); }
+  Point operator * (const double &a) const { return Point(x * a, y * a); }
+  Point operator / (const double &a) const { return Point(x / a, y / a); }
+  double operator ! () const { return sqrt(x * x + y * y); }
+  double operator & (const Point &p) const { return x * p.x + y * p.y; }
+  double operator * (const Point &p) const { return x * p.y - y * p.x; }
+  Point operator << (const double &a) const { return Point(x * cos(a) - y * sin(a), x * sin(a) + y * cos(a)); }
+};
+
+struct Solver {
+private:
+  static const int N = 160;
+  int n, a, b;
+  Point p, q, tmp, ans[N];
+  void input() { scanf("%d%d%d%lf%lf%lf%lf", &n, &a, &b, &p.x, &p.y, &q.x, &q.y); }
+  void init() {
+    if (a > b) swap(a, b), swap(p, q);
+    double angle = 2 * PI / n * (b - a);
+    if (cmp(PI - angle)) {
+      tmp = p + (q - p) / 2 / cos((PI - angle) / 2);
+      tmp = (tmp - p << (angle - PI) / 2) + p;
+    } else tmp = p + (q - p) / 2;
+  }
+  void process() {
+    for (int i = 1; i <= n; ++ i)
+      ans[a] = p, a %= n, ++ a, p = (p - tmp << -2 * PI / n) + tmp;
+    for (int i = 1; i <= n; ++ i)
+      printf("%.6lf %.6lf\n", ans[i].x, ans[i].y);
+  }
+
+public:
+  void solve() { input(), init(), process(); }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 151 - 0
source/_posts/oi/arpa-and-a-game-with-mojtaba.md

@@ -0,0 +1,151 @@
+---
+title: Arpa and a Game with Mojtaba
+tags:
+  - Bitmask
+  - Game Theory
+id: "1504"
+categories:
+  - [OI, Common Skill]
+  - [OI, Number Theory]
+date: 2017-09-21 13:50:12
+---
+
+## 题目描述
+
+Mojtaba and Arpa are playing a game. They have a list of $n$ numbers in the game.
+
+In a player's turn, he chooses a number $p^k$ (where $p$ is a prime number and $k$ is a positive integer) such that $p^k$ divides at least one number in the list. For each number in the list divisible by $p^k$, call it $x$, the player will delete $x$ and add ${x \over p^k}$ to the list. The player who can not make a valid choice of $p$ and $k$ loses.
+
+Mojtaba starts the game and the players alternatively make moves. Determine which one of players will be the winner if both players play optimally.
+
+## 算法分析
+
+给定一个长度为$n$的序列,两人轮流进行操作,每次操作给定$p, k$,其中$p$是素数,$k$是正整数,将序列中所有能被$p^k$整除的数除以$p^k$(要求至少有一个数能被整除),不能进行操作的人算输。问在两人都采取最优策略的情况下,先手必胜还是后手必胜。
+
+数据范围:$1 \le n \le 100, \; 1 \le a_i \le 10^9$。
+
+## 算法分析
+
+考虑某个质数$p$,它对其他质数不产生任何影响,因此可以分别处理每个质数。
+
+设处理到的质数是$p$,我们计算出它在序列每个数中的最高次数,并储存在二进制数`s`中。那么在进行一次$p, k$操作后,`s`会被更新成`(s>>k)(s&((1<<(k-1))-1))`。把`s`看成点,操作看成边,就构成了一张有向图。分别计算每个质数有向图的 SG 函数,异或起来,如果等于$0$则后手必胜,否则先手必胜。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+#include <map>
+
+using namespace std;
+
+namespace std {
+  template <typename T>
+  void maxify(T &a, T b) { b > a && (a = b); }
+  template <typename T>
+  void minify(T &a, T b) { b < a && (a = b); }
+}
+
+struct IOManager {
+  template <typename T>
+  inline bool read(T &x) {
+    char c; bool flag = false; x = 0;
+    while (~ c && ! isdigit(c = getchar()) && c != '-') ;
+    c == '-' && (flag = true, c = getchar());
+    if (! ~ c) return false;
+    while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar();
+    return (flag && (x = -x), true);
+  }
+  inline bool read(char &c) {
+    c = '\n';
+    while (~ c && ! (isprint(c = getchar()) && c != ' ')) ;
+    return ~ c;
+  }
+  inline int read(char s[]) {
+    char c; int len = 0;
+    while (~ c && ! (isprint(c = getchar()) && c != ' ')) ;
+    if (! ~ c) return 0;
+    while (isprint(c) && c != ' ') s[len ++] = c, c = getchar();
+    return (s[len] = '\0', len);
+  }
+  template <typename T>
+  inline IOManager operator > (T &x) {
+    read(x); return *this;
+  }
+  template <typename T>
+  inline void write(T x) {
+    x < 0 && (putchar('-'), x = -x);
+    x > 9 && (write(x / 10), true);
+    putchar(x % 10 + '0');
+  }
+  inline void write(char c) {
+    putchar(c);
+  }
+  inline void write(char s[]) {
+    int pos = 0;
+    while (s[pos] != '\0') putchar(s[pos ++]);
+  }
+  template <typename T>
+  inline IOManager operator < (T x) {
+    write(x); return *this;
+  }
+} io;
+
+struct Solver {
+private:
+  static const int N = 100;
+  int n, a[N + 1];
+  map <int, int> sg, num;
+  void input() {
+    io > n;
+    for (int i = 1; i <= n; ++ i) io > a[i];
+  }
+  void init() {
+    for (int i = 1; i <= n; ++ i)
+      if (a[i] > 1) {
+        int q = sqrt(a[i]);
+        for (int j = 2; j <= q; ++ j) {
+          if (a[i] == 1) break;
+          if (! (a[i] % j)) {
+            int cnt = 0;
+            while (! (a[i] % j)) a[i] /= j, ++ cnt;
+            num[j] |= 1 << cnt - 1;
+          }
+        }
+        if (a[i] > 1) num[a[i]] |= 1;
+      }
+  }
+  int get_sg(int t) {
+    if (! t) return 0;
+    if (sg.count(t)) return sg[t];
+    map <int, bool> vis;
+    int p = t, cnt = 0;
+    while (p) p >>= 1, ++ cnt;
+    for (int i = 1; i <= cnt; ++ i)
+      vis[get_sg((t >> i) | (t & (1 << i - 1) - 1))] = true;
+    cnt = 0;
+    while (vis[cnt]) ++ cnt;
+    return sg[t] = cnt;
+  }
+  void process() {
+    int ans = 0;
+    for (map <int, int> :: iterator it = num.begin(); it != num.end(); ++ it) ans ^= get_sg(it->second);
+    if (! ans) io < (char *) "Arpa\n";
+    else io < (char *) "Mojtaba\n";
+  }
+
+public:
+  void solve() {
+    input(), init(), process();
+  }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 147 - 0
source/_posts/oi/arpa-and-a-list-of-numbers.md

@@ -0,0 +1,147 @@
+---
+title: Arpa and a List of Numbers
+tags:
+  - Euler's Sieve
+  - GCD-LCM
+  - Prefix Sum
+  - Prime
+id: "1496"
+categories:
+  - [OI, Common Skill]
+  - [OI, Number Theory]
+date: 2017-09-20 17:55:20
+---
+
+## 题目描述
+
+Arpa has found a list containing $n$ numbers. He calls a list bad if and only if it is not empty and gcd of numbers in the list is $1$.
+
+Arpa can perform two types of operations:
+
+- Choose a number and delete it with cost $x$.
+- Choose a number and increase it by $1$ with cost $y$.
+
+Arpa can apply these operations to as many numbers as he wishes, and he is allowed to apply the second operation arbitrarily many times on the same number.
+
+Help Arpa to find the minimum possible cost to make the list good.
+
+## 题意概述
+
+给定一个长度为$n$的序列,第$i$个数字为$a_i$。有两种操作:删除一个数,消耗$x$;将一个数加$1$,消耗$y$。求使得序列中所有数的最大公约数大于$1$的最少消耗。
+
+数据范围:$1 \le n \le 5 \times 10^5, \; 1 \le x, y \le 10^9, \; 1 \le a_i \le 10^6$。
+
+## 算法分析
+
+枚举$[2, 10^6]$中的素数$p$作为最大公约数(合数作为最大公约数的消耗一定不比质数少)。对于序列中的每个数,要么将它删去,要么将它变成不小于它且最小的$p$的倍数。设$d_i=\left\lceil {a_i \over p} \right\rceil \times p -a_i$。显然,若$x \le d_i \times y$,那么将它删去更优,否则将它变成$p$的倍数更优。也就是说,对于$d_i \ge {x \over y}$的数,我们将它删去,否则将它变成$p$的倍数。预处理出前缀和,根据调和级数,可知时间复杂度为$O(a_i\log a_i)$。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+
+using namespace std;
+
+void minify(long long &a, long long b) { b < a && (a = b); }
+
+struct IOManager {
+  template <typename T>
+  inline bool read(T &x) {
+    char c; bool flag = false; x = 0;
+    while (~ c && ! isdigit(c = getchar()) && c != '-') ;
+    c == '-' && (flag = true, c = getchar());
+    if (! ~ c) return false;
+    while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar();
+    return (flag && (x = -x), true);
+  }
+  inline bool read(char &c) {
+    c = '\n';
+    while (~ c && ! (isprint(c = getchar()) && c != ' ')) ;
+    return ~ c;
+  }
+  inline int read(char s[]) {
+    char c; int len = 0;
+    while (~ c && ! (isprint(c = getchar()) && c != ' ')) ;
+    if (! ~ c) return 0;
+    while (isprint(c) && c != ' ') s[len ++] = c, c = getchar();
+    return (s[len] = '\0', len);
+  }
+  template <typename T>
+  inline IOManager operator > (T &x) {
+    read(x); return *this;
+  }
+  template <typename T>
+  inline void write(T x) {
+    x < 0 && (putchar('-'), x = -x);
+    x > 9 && (write(x / 10), true);
+    putchar(x % 10 + '0');
+  }
+  inline void write(char c) {
+    putchar(c);
+  }
+  inline void write(char s[]) {
+    int pos = 0;
+    while (s[pos] != '\0') putchar(s[pos ++]);
+  }
+  template <typename T>
+  inline IOManager operator < (T x) {
+    write(x); return *this;
+  }
+} io;
+
+struct Solver {
+private:
+  static const int N = 1000000;
+  static const int M = 1000000;
+  int n, x, y, a[N + 1], cnt[N + 1 << 1];
+  long long sum[N + 1 << 1];
+  int top, prime[N];
+  bool vis[N + 1 << 1];
+  void input() {
+    io > n > x > y;
+    for (int i = 1; i <= n; ++ i) io > a[i], ++ cnt[a[i]], sum[a[i]] += a[i];
+  }
+  void init() {
+    for (int i = 1; i <= M << 1; ++ i) cnt[i] += cnt[i - 1], sum[i] += sum[i - 1];
+    for (int i = 2; i <= M; ++ i) {
+      if (! vis[i]) prime[++ top] = i;
+      for (int j = 1; j <= top && i * prime[j] <= M; ++ j) {
+        vis[i * prime[j]] = true;
+        if (! (i % prime[j])) break;
+      }
+    }
+  }
+  void process() {
+    int p = x / y;
+    long long ans = 1000000000000000000ll;;
+    for (int i = 1; i <= top; ++ i) {
+      long long tmp = 0;
+      int delta = prime[i] - p - 1;
+      for (int j = 0; j <= N; j += prime[i]) {
+        if (delta < 0) {
+          tmp += (((long long) cnt[j + prime[i]] - cnt[j]) * (j + prime[i]) - (sum[j + prime[i]] - sum[j])) * y;
+        } else {
+          tmp += ((long long) cnt[j + delta] - cnt[j]) * x + (((long long) cnt[j + prime[i]] - cnt[j + delta]) * (j + prime[i]) - (sum[j + prime[i]] - sum[j + delta])) * y;
+        }
+      }
+      minify(ans, tmp);
+    }
+    io < ans < '\n';
+  }
+
+public:
+  void solve() {
+    input(), init(), process();
+  }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 73 - 0
source/_posts/oi/bbq.md

@@ -0,0 +1,73 @@
+---
+title: BBQ
+tags:
+  - Dynamic Programming
+id: "3617"
+categories:
+  - - OI
+    - Common Skill
+date: 2020-02-04 20:40:40
+---
+
+## 题目描述
+
+You are a master of barbeque, and now you are trying to string dumplings with a bamboo stick.
+
+The dumplings are placed in a $n \times m$ grid. Each grid contains exactly one dumpling. The color of each dumpling is red (`"R"`), green (`"G"`) or white (`"W"`).
+
+You can choose three consecutive grids from left to right, or from top to bottom, and string the corresponding dumplings into a string from left to right or from top to bottom. So there will be exactly three dumplings on a string, let's denote a string of dumpling by their colors in order.
+
+Now, you want to make strings `"RGW"` as many as possible. Note that the dumplings can not be reused in multiple strings. So how many strings `"RGW"` can you make?
+
+## 题意概述
+
+给定一个$n \times m$的字符矩阵。每次可以从上到下或从左到右取连续三个字符构成一个字符串,每个字符只能取一次。求最多能取几个`RGW`。
+
+数据范围:$1 \le n, m \le 3000$。
+
+## 算法分析
+
+只需考虑最多能取几个`G`。可以发现,只有在同一条与副对角线平行的直线上的`G`会相互影响。分别计算每一条直线,令$f_{i, 0/1/2}$表示第$i$个位置不取/从左到右取/从上到下取的情况下,前$i$个位置最多能取几个`G`。最后把所有直线的结果加起来得到答案。
+
+## 代码
+
+```cpp
+#include <cstdio>
+#include <cstring>
+#include <algorithm>
+
+int const N = 3005;
+char mp[N][N];
+int f[N][N][3];
+
+int main() {
+    int n, m;
+    scanf("%d%d", &n, &m);
+    for (int i = 1; i <= n; ++i) {
+        scanf("%s", mp[i] + 1);
+    }
+    for (int k = 2; k <= n + m; ++k) {
+        for (int i = 1; i < k; ++i) {
+            int j = k - i;
+            if (i <= n && j <= m) {
+                f[i][j][0] = std::max(f[i - 1][j + 1][0], std::max(f[i - 1][j + 1][1], f[i - 1][j + 1][2]));
+                if (mp[i][j - 1] == 'R' && mp[i][j] == 'G' && mp[i][j + 1] == 'W') {
+                    f[i][j][1] = std::max(f[i - 1][j + 1][0], f[i - 1][j + 1][1]) + 1;
+                }
+                if (mp[i - 1][j] == 'R' && mp[i][j] == 'G' && mp[i + 1][j] == 'W') {
+                    f[i][j][2] = std::max(f[i - 1][j + 1][0], f[i - 1][j + 1][2]) + 1;
+                }
+            }
+        }
+    }
+    int ans = 0;
+    for (int i = 1; i < n; ++i) {
+        ans += std::max(f[i][1][0], std::max(f[i][1][1], f[i][1][2]));
+    }
+    for (int i = 1; i <= m; ++i) {
+        ans += std::max(f[n][i][0], std::max(f[n][i][1], f[n][i][2]));
+    }
+    printf("%d\n", ans);
+    return 0;
+}
+```

+ 83 - 0
source/_posts/oi/beauty-contest.md

@@ -0,0 +1,83 @@
+---
+title: Beauty Contest
+tags:
+  - Convex Hull
+  - Plane Geometry
+  - Rotating Calipers
+id: "1722"
+categories:
+  - - OI
+    - Computational Geometry
+date: 2018-01-12 21:55:29
+---
+
+## 题目描述
+
+Bessie, Farmer John's prize cow, has just won first place in a bovine beauty contest, earning the title 'Miss Cow World'. As a result, Bessie will make a tour of $N$ farms around the world in order to spread goodwill between farmers and their cows. For simplicity, the world will be represented as a two-dimensional plane, where each farm is located at a pair of integer coordinates $(x, y)$, each having a value in the range $-10000 \ldots 10000$. No two farms share the same pair of coordinates.
+
+Even though Bessie travels directly in a straight line between pairs of farms, the distance between some farms can be quite large, so she wants to bring a suitcase full of hay with her so she has enough food to eat on each leg of her journey. Since Bessie refills her suitcase at every farm she visits, she wants to determine the maximum possible distance she might need to travel so she knows the size of suitcase she must bring. Help Bessie by computing the maximum distance among all pairs of farms.
+
+## 题意概述
+
+求$N$个点中最远点对之间的距离。
+
+数据范围:$2 \le N \le 50000$。
+
+## 算法分析
+
+显然最远点对一定在凸包上,因此可以先计算出凸包。接着考虑旋转卡壳。被一对卡壳正好卡住的点对互为对踵点,那么最远点对一定互为对踵点。只要将卡壳绕凸包旋转一周,就可以找到所有对踵点对,取其中距离最远的点对即可。
+
+## 代码
+
+```cpp
+#include <cstdio>
+#include <algorithm>
+
+int max(int a, int b) {
+  return a > b ? a : b;
+}
+
+static const int N = 50005;
+int st[N], rec[N];
+struct Point {
+  int x, y;
+  Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
+  bool operator < (const Point &p) const {
+    return x == p.x ? y < p.y : x < p.x;
+  }
+  Point operator - (const Point &p) const {
+    return Point(x - p.x, y - p.y);
+  }
+  int operator * (const Point &p) const {
+    return x * p.y - y * p.x;
+  }
+  int operator ! () const {
+    return x * x + y * y;
+  }
+} p[N];
+
+int main() {
+  int n; scanf("%d", &n);
+  for (int i = 0; i < n; ++ i) scanf("%d%d", &p[i].x, &p[i].y);
+  int top = 0, tot = 0; std :: sort(p, p + n);
+  for (int i = 0; i < n; ++ i) {
+    while (top > 1 && (p[st[top - 1]] - p[st[top - 2]]) * (p[i] - p[st[top - 2]]) <= 0) -- top;
+    st[top ++] = i;
+  }
+  for (int i = 0; i < top; ++ i) rec[tot ++] = st[i];
+  top = 0;
+  for (int i = n - 1; ~ i; -- i) {
+    while (top > 1 && (p[st[top - 1]] - p[st[top - 2]]) * (p[i] - p[st[top - 2]]) <= 0) -- top;
+    st[top ++] = i;
+  }
+  for (int i = 1; i < top - 1; ++ i) rec[tot ++] = st[i];
+  int mx = 0, pnt = 1;
+  for (int i = 0; i < tot; ++ i) {
+    int nxt = (i + 1) % tot; Point cur = p[rec[nxt]] - p[rec[i]];
+    while (cur * (p[rec[pnt]] - p[rec[i]]) < cur * (p[rec[(pnt + 1) % tot]] - p[rec[i]])) (++ pnt) %= tot;
+    mx = max(mx, max(! (p[rec[pnt]] - p[rec[i]]), ! (p[rec[pnt]] - p[rec[nxt]])));
+  }
+  printf("%d\n", mx);
+  return 0;
+}
+```

+ 121 - 0
source/_posts/oi/best-edge-weight.md

@@ -0,0 +1,121 @@
+---
+title: Best Edge Weight
+tags:
+  - Greedy
+  - Lowest Common Ancestor
+  - Minimum Spanning Tree
+  - Union Find
+id: "1279"
+categories:
+  - [OI, Common Skill]
+  - [OI, Data Structure]
+  - [OI, Graph Theory]
+date: 2017-07-22 17:50:27
+---
+
+## 题目描述
+
+You are given a connected weighted graph with $n$ vertices and $m$ edges. The graph doesn't contain loops nor multiple edges. Consider some edge with id $i$. Let's determine for this edge the maximum integer weight we can give to it so that it is contained in all minimum spanning trees of the graph if we don't change the other weights.
+
+You are to determine this maximum weight described above for each edge. You should calculate the answer for each edge independently, it means there can't be two edges with changed weights at the same time.
+
+## 题意概述
+
+给定一张$n$个点$m$条边的连通无向图,边权均为正数。对于每一条边,询问它的边权至多是多少,使得在其他边权不变的条件下,它在这张图的所有最小生成树中。
+
+数据范围:$2 \le n \le 2 \times 10^5, \; n-1 \le m \le 2 \times 10^5$。
+
+## 算法分析
+
+题目要求一条边在所有最小生成树中。我们无法求出所有最小生成树,因此先求出其中一棵。这样,图中的边就被分成了两种:一种在这棵最小生成树中,另一种不在这棵最小生成树中。
+
+对于不在这棵最小生成树中的边$(u, v)$,设它的权值为$w$。显然,它和最小生成树上从$u$到$v$的路径构成了一个环。设这条路径上边权的最大值$w_{max}$。如果$w \ge w_{max}$,那么$(u, v)$可能在这张图的一棵最小生成树中,但一定不是所有。因此,可以将$w$设成$w_{max}-1$,因为根据贪心策略,将路径上权值为$w_{max}$的边替换成$(u, v)$,会得到一棵更小的生成树。这种情况很好解决,只要在最小生成树上进行倍增就可以了。
+
+对于在这棵最小生成树中的边$(u, v)$,设它的权值为$w$。假设我们找到了一条不经过这棵最小生成树上的边从$u$到$v$的路径,设这条路径上边权的最小值为$w_{min}$。类似于不在最小生成树的边,如果$w \ge w_{min}$,那么这条边一定不在所有最小生成树中。将它的权值设为$w_{min}-1$,即可满足要求。由于直接求$w_{min}$较为困难,因此可以按边权从小到大枚举不在这棵最小生成树中的边,并用它来更新最小生成树上的一条路径。因为是从小到大枚举,所以最小生成树上的每条边只需要被更新一次,可以用并查集维护。
+
+## 代码
+
+```cpp
+#include <iostream>
+#include <cstring>
+#include <algorithm>
+using namespace std;
+struct line {
+  long long u, v, c, id; bool mst;
+  bool operator < (const line &a) const { return c < a.c; }
+} l[200001];
+struct edge {
+  long long v, c, id, nxt;
+} e[400001];
+long long n, m, nume, h[200001], fa[200001], ans[200001];
+long long depth[200001], up[200001][20], ma[200001][20], uid[200001];
+long long get_fa(long long t) { return t == fa[t] ? t : fa[t] = get_fa(fa[t]); }
+void add_edge(long long u, long long v, long long c, long long id) {
+  e[++nume].v = v, e[nume].c = c, e[nume].id = id, e[nume].nxt = h[u], h[u] = nume;
+  e[++nume].v = u, e[nume].c = c, e[nume].id = id, e[nume].nxt = h[v], h[v] = nume;
+}
+void dfs(long long t, long long fa) {
+  for (int i = h[t]; i; i = e[i].nxt)
+    if (e[i].v != fa) {
+      depth[e[i].v] = depth[t] + 1, up[e[i].v][0] = t;
+      ma[e[i].v][0] = e[i].c, uid[e[i].v] = e[i].id, dfs(e[i].v, t);
+    }
+}
+long long get_max(long long u, long long v) {
+  long long ret = 0;
+  if (depth[u] < depth[v]) u ^= v ^= u ^= v;
+  for (int i = 19; i >= 0; --i)
+    if (depth[u] - depth[v] >= (1 << i))
+      ret = max(ret, ma[u][i]), u = up[u][i];
+  if (u == v) return ret;
+  for (int i = 19; i >= 0; --i)
+    if (up[u][i] != up[v][i])
+      ret = max(ret, max(ma[u][i], ma[v][i])), u = up[u][i], v = up[v][i];
+  return max(ret, max(ma[u][0], ma[v][0]));
+}
+void update(long long u, long long v, long long val) {
+  if (depth[u] < depth[v]) u ^= v ^= u ^= v;
+  long long p = u, q = v;
+  for (int i = 19; i >= 0; --i)
+    if (depth[p] - depth[q] >= (1 << i)) p = up[p][i];
+  if (p != q) {
+    for (int i = 19; i >= 0; --i)
+      if (up[p][i] != up[q][i]) p = up[p][i], q = up[q][i];
+    p = up[p][0];
+  }
+  u = get_fa(u), v = get_fa(v);
+  while (depth[u] > depth[p])
+    ans[uid[u]] = val - 1, q = get_fa(up[u][0]), fa[u] = q, u = get_fa(u);
+  while (depth[v] > depth[p])
+    ans[uid[v]] = val - 1, q = get_fa(up[v][0]), fa[v] = q, v = get_fa(v);
+}
+int main()
+{
+  cin >> n >> m, memset(ans, -1, sizeof ans);
+  if (n == m + 1) {
+    for (int i = 1; i <= m; cout << -1 << ' ', ++i); cout << endl; return 0;
+  }
+  for (int i = 1; i <= n; fa[i] = i, ++i);
+  for (int i = 1; i <= m; cin >> l[i].u >> l[i].v >> l[i].c, l[i].id = i, ++i);
+  sort(l + 1, l + m + 1);
+  for (int i = 1; i <= m; ++i) {
+    long long u = get_fa(l[i].u), v = get_fa(l[i].v);
+    if (u != v) add_edge(l[i].u, l[i].v, l[i].c, l[i].id), fa[u] = v, l[i].mst = true;
+  }
+  depth[1] = 1, dfs(1, 0);
+  for (int i = 1; i < 20; ++i)
+    for (int j = 1; j <= n; ++j) {
+      up[j][i] = up[up[j][i - 1]][i - 1];
+      ma[j][i] = max(ma[j][i - 1], ma[up[j][i - 1]][i - 1]);
+    }
+  for (int i = 1; i <= n; fa[i] = i, ++i);
+  for (int i = 1; i <= m; ++i)
+    if (!l[i].mst) {
+      ans[l[i].id] = get_max(l[i].u, l[i].v) - 1;
+      update(l[i].u, l[i].v, l[i].c);
+    }
+  for (int i = 1; i <= m; cout << ans[i] << ' ', ++i);
+  cout << endl;
+  return 0;
+}
+```

+ 81 - 0
source/_posts/oi/bitwise-xor.md

@@ -0,0 +1,81 @@
+---
+title: Bitwise XOR
+tags:
+  - Dynamic Programming
+  - Probability
+id: "3634"
+categories:
+  - [OI, Common Skill]
+  - [OI, Number Theory]
+date: 2020-02-04 23:45:10
+---
+
+## 题目描述
+
+Given a sequence $a$ and an empty sequence $b$, each element in $a$ is added to $b$ with probability $P$ (independently to the other elements). Denote $s=\bigoplus_{i=1}^{|b|} b_i$, where $\oplus$ denotes bitwise xor and $s=0$ if $b$ is empty. Find the expected value of $s^2$.
+
+## 题意概述
+
+给定一个长度为$n$的序列$a$,其中每个数都有$P$的概率被选中。令被选中的数的异或和为$s$。求$s^2$的期望。
+
+数据范围:$1 \le n \le 10^5, \; 0 \le a_i \lt 10^9+7$
+
+## 算法分析
+
+令$P(s)$表示异或和为$s$的概率。把$s$用二进制表示,最低位为第$0$位。
+
+$$
+\begin{align} \sum_s s^2P(s) &= \sum_s (\sum_{i=0}^{29} [s二进制第i位为1] 2^i)^2P(s) \\\\ &= \sum_s P(s) \sum_{i=0}^{29} [s二进制第i位为1] 2^i \sum_{j=0}^{29} [s二进制第j位为1] 2^j \\\\ &= \sum_s P(s) \sum_{i=0}^{29} \sum_{j=0}^{29} [s二进制第i位和第j位都为1] 2^{i+j} \\\\ &= \sum_{i=0}^{29} \sum_{j=0}^{29} P(s二进制第i位和第j位都为1) 2^{i+j} \end{align}
+$$
+
+先枚举$i$和$j$,令$f_{k,0/1,0/1}$表示从前$k$个数中选,异或和第$i$位、第$j$为分别为$0/1$、$0/1$的概率,对$f_{n,1,1}2^{i+j}$求和即为答案。
+
+## 代码
+
+```cpp
+#include <cstdio>
+#include <cstring>
+#include <algorithm>
+
+int const N = 100005, MOD = 1000000007;
+
+int a[N], f[N][2][2];
+
+int power(int a, int b) {
+    int ret = 1;
+    for (; b; b >>= 1) {
+        if (b & 1) {
+            ret = 1ll * ret * a % MOD;
+        }
+        a = 1ll * a * a % MOD;
+    }
+    return ret;
+}
+
+int main() {
+    int n, x, y;
+    scanf("%d%d%d", &n, &x, &y);
+    int p = 1ll * x * power(y, MOD - 2) % MOD, q = (MOD + 1 - p) % MOD;
+    for (int i = 1; i <= n; ++i) {
+        scanf("%d", &a[i]);
+    }
+    int ans = 0, ans2 = 0;
+    f[0][0][0] = 1;
+    for (int i = 0; i < 30; ++i) {
+        for (int j = i; j < 30; ++j) {
+            for (int k = 1; k <= n; ++k) {
+                for (int _i = 0; _i < 2; ++_i) {
+                    for (int _j = 0; _j < 2; ++_j) {
+                        int __i = _i ^ (a[k] >> i & 1);
+                        int __j = _j ^ (a[k] >> j & 1);
+                        f[k][_i][_j] = (1ll * f[k - 1][_i][_j] * q + 1ll * f[k - 1][__i][__j] * p) % MOD;
+                    }
+                }
+            }
+            ans = (ans + (1ll << i + j + (i != j)) % MOD * f[n][1][1]) % MOD;
+        }
+    }
+    printf("%d\n", ans);
+    return 0;
+}
+```

+ 79 - 0
source/_posts/oi/black-white-balls.md

@@ -0,0 +1,79 @@
+---
+title: Black-White Balls
+tags:
+  - Bayes' Theorem
+  - Combinatorics
+  - Probability
+id: "3204"
+categories:
+  - - OI
+    - Number Theory
+date: 2018-05-19 08:45:44
+---
+
+## 题目描述
+
+$n$ black and white balls were put into a bag. Petya doesn't know exactly how many black balls are there among them. He knows, however, that there are $0, 1, \ldots, n$ black balls among all balls in the bag with equal probability.
+
+Petya took $l$ balls from the bag at random, and $l_1$ of them turned out black, while $l_2$ other turned out white ($l_1+l_2=l$). Now he wants to predict how many black balls were there initially in the bag. Of course, if $l \lt n$, he can't be sure in his prediction, but he wants to predict a segment $[a, b]$, such that the amount $k$ of black balls belongs to it with probability at least $p$.
+
+You are given $n, l_1, l_2$ and $p$, and you must find such $a$ and $b$ that $b-a$ is minimal possible. If there are several such pairs $(a, b)$, choose the one with the smallest $a$.
+
+## 题意概述
+
+袋子里有$n$个球,每个球可能是黑色或白色。已知黑球个数在$0, 1, \ldots, n$之间等概率分布。现在从中取出$l$个球,其中有$l_1$个黑球和$l_2$个白球($l_1+l_2=l$)。要求找到一个最短的区间$[a, b]$,使得黑球个数在$[a, b]$中的概率至少为${p \over 100}$。若有多个这样的区间,输出$a$最小的。
+
+数据范围:$1 \le n \le 50, \; 0 \le l_1 \le n, \; 0 \le l_2 \le n-l_1, \; 0 \le p \le 100$。
+
+## 算法分析
+
+令事件$A$为取出的$l$个球中有$l_1$个黑球,事件$B_i$为袋子里有$i$个黑球。根据贝叶斯定理
+
+$$
+P(B_i|A)={P(B_i)P(A|B_i) \over \sum_{j=0}^n P(B_j)P(A|B_j)}
+$$
+
+而我们知道
+
+$$
+P(B_i)={1 \over n+1}, \; P(A|B_i)={ {i \choose l_1}{n-i \choose l_2} \over {n \choose l}}
+$$
+
+因此可以计算出$P(B_i|A)$,接下来只要枚举区间即可。
+
+## 代码
+
+```cpp
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+
+static int const N = 55;
+double a[N];
+
+double get_c(int n, int m) {
+  if (n < m)
+    return 0;
+  double ret = 1;
+  for (int i = 0; i < m; ++i)
+    ret *= 1. * (n - i) / (m - i);
+  return ret;
+}
+
+int main() {
+  int n, l1, l2, p;
+  scanf("%d%d%d%d", &n, &l1, &l2, &p);
+  double b = 0;
+  for (int i = 0; i <= n; ++i)
+    b += a[i] = 1. / (n + 1) * get_c(i, l1) * get_c(n - i, l2) / get_c(n, l1 + l2);
+  for (int i = 1; i <= n + 1; ++i)
+    for (int j = 0; j + i - 1 <= n; ++j) {
+      double sum = 0;
+      for (int k = j; k <= j + i - 1; ++k)
+        sum += a[k];
+      if (sum / b * 100 + 1e-12 >= p)
+        return printf("%d %d\n", j, j + i - 1), 0;
+    }
+  return 0;
+}
+```

+ 34 - 0
source/_posts/oi/book-pile.md

@@ -0,0 +1,34 @@
+---
+title: Book Pile
+tags:
+  - Deque
+  - Self-Balancing Binary Search Tree
+id: "464"
+categories:
+  - - OI
+    - Data Structure
+date: 2017-06-17 19:00:24
+---
+
+## 题目描述
+
+There is a pile of $N$ books on the table. Two types of operations are performed over this pile:
+
+- a book is added to the top of the pile,
+- top $K$ books are rotated. If there are less than $K$ books on the table, the whole pile is rotated.
+
+First operation is denoted as $ADD(S)$ where $S$ is the name of the book, and the second operations is denoted as $ROTATE$.
+
+The maximum number of books is no more than $40000$. All book names are non-empty sequences of no more than $3$ capital Latin letters. The names of the books can be non-unique.
+
+## 题意概述
+
+桌上有$N$本书叠成一堆。有两种操作:① 往书堆上加一本书;② 将最顶上的$K$本书翻转(若不足$K$本则全部翻转)。求操作$M$次后书本的顺序。
+
+数据范围:$0 \le N \le 40000, \; 0 \le M \le 10^5, \; 0 \le K \le 40000$。
+
+## 算法分析
+
+这题可以用 Splay 来做,但其实有更简单的方法。
+
+由于只会在顶上加书,也只会翻转顶上$K$本书,所以只要维护顶上$K$本书的状态即可。建一个长度为$K$的双端队列,每次翻转相当于向队列的另一端加书,队列长度大于$K$时在加书的另一端弹出一本书(这本书不会再受到翻转影响)。

+ 25 - 0
source/_posts/oi/boxes.md

@@ -0,0 +1,25 @@
+---
+title: Boxes
+tags:
+  - GCD-LCM
+  - Implementation
+id: "1576"
+categories:
+  - - OI
+    - Number Theory
+date: 2017-10-26 10:35:00
+---
+
+## 题目描述
+
+There are two boxes. There are $A$ balls in the first box, and $B$ balls in the second box. It is possible to move balls from one box to another. From one box into another one should move as many balls as the other box already contains. You have to determine, whether it is possible to move all balls into one box.
+
+## 题意概述
+
+给定两个数$A$和$B$,每次操作可以将两个数中较大的数减去较小的数,再将较小的数翻倍。问至少经过多少次操作后两个数中有一个为$0$,若不可能则输出$-1$。
+
+数据范围:$1 \le A+B \le 2147483647$。
+
+## 算法分析
+
+可以发现在任何时候,将两个数同时除以它们的最大公约数并不会影响答案;若两个数的和为奇数,则永远不可能结束。基于以上两点,答案不会超过$\log(A+B)$,因此直接模拟即可。

+ 53 - 0
source/_posts/oi/brackets.md

@@ -0,0 +1,53 @@
+---
+title: Brackets
+tags:
+  - Dynamic Programming
+id: "302"
+categories:
+  - - OI
+    - Common Skill
+date: 2017-06-14 20:30:36
+---
+
+## 题目描述
+
+We give the following inductive definition of a "regular brackets" sequence:
+
+- the empty sequence is a regular brackets sequence,
+- if `s` is a regular brackets sequence, then `(s)` and `[s]` are regular brackets sequences,
+- if `a` and `b` are regular brackets sequences, then `ab` is a regular brackets sequence,
+- no other sequence is a regular brackets sequence.
+
+For instance, all of the following character sequences are regular brackets sequences:
+
+`(), [], (()), ()[], ()[()]`,
+
+while the following character sequences are not:
+
+`(, ], )(, ([)], ([(]`.
+
+Given a brackets sequence of characters $a_1a_2 \ldots a_n$, your goal is to find the length of the longest regular brackets sequence that is a subsequence of $s$. That is, you wish to find the largest $m$ such that for indices $i_1, i_2, \ldots, i_m$ where $1 \le i_1 \lt i_2 \lt \cdots \lt i_m \le n$, $a_{i_1}a_{i_2} \ldots a_{i_m}$ is a regular brackets sequence.
+
+Given the initial sequence `([([]])]`, the longest regular brackets subsequence is `[([])]`.
+
+## 题意概述
+
+给定一个只由`()[]`这四种字符组成的长度为$n$的字符串,求其最长的满足匹配的子序列的长度。
+
+数据范围:$1 \le n \le 100$。
+
+## 算法分析
+
+用$f_{i, j}$表示$a_ia_{i+1} \ldots a_j$中满足条件的最大长度。当$j-i \gt 0$时,有如下转移方程
+
+$$
+f_{i, j}=
+\begin{cases}
+\max(f_{i + 1, j - 1} + 2, f_{i, k} + f_{k + 1, j} \mid i \le k \lt j), & a_i \text{ matches } a_j \\\\
+\max(f_{i, k} + f_{k + 1, j} \mid i \le k \lt j), & \text{otherwise}
+\end{cases}
+$$
+
+考虑边界条件。易知$j-i \le 0$时,$f_{i, j}=0$。
+
+对于$f_{i, j}$,满足$j-i=t$的$f_{i, j}$的值取决于满足$j-i \lt t$的$f_{i, j}$的值。因此可以先从小到大枚举$j-i$,再从小到大枚举$i$,然后从$i$到$j$枚举$k$,利用转移方程解决此题。

+ 93 - 0
source/_posts/oi/bridges-painting.md

@@ -0,0 +1,93 @@
+---
+title: Bridges Painting
+tags:
+  - Constructive Algorithm
+  - Depth-First-Search
+id: "1560"
+categories:
+  - [OI, Common Skill]
+  - [OI, Graph Theory]
+date: 2017-10-19 19:45:43
+---
+
+## 题目描述
+
+New Berland consists of $N$ islands, some of them are connected by bridges. There can be no more than one bridge between any pair of islands. Mr. President issued a law to paint all bridges. A bridge can be painted white or black. Any island must have at least one white bridge and at least one black (of course if an island has more than one bridge).
+
+## 题意概述
+
+给定一张图,要求将所有边染成黑白两种颜色,使得每个度数大于$1$的点的连边都至少有一条黑色和一条白色。
+
+数据范围:$1 \le N \le 100$。
+
+## 算法分析
+
+显然,我们可以分别考虑每个连通块。如果某个连通块仅由一个奇环构成,则不存在合法方案;否则,在连通块内任选一个度数不为$2$的点,从它开始进行交错染色(DFS);如果所有点的度数均为$2$,则任选一个点开始进行交错染色,再检验合法性。证明如下:
+
+> 考虑 DFS 的过程。如果 DFS 经过一个点(进入并出去),根据交错染色的原则,入边和出边的颜色一定不相同。对于连通块内某个度数为$1$的点,它的连边可以任意染色;而对于度数大于$2$的点,如果从它开始 DFS,则一定会再经过这个点至少一次。
+
+由此得证。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+#include <vector>
+
+using namespace std;
+
+struct IOManager {
+  template <typename T> inline bool read(T &x) { char c = '\n'; bool flag = false; x = 0; while (~ c && ! isdigit(c = getchar()) && c != '-') ; c == '-' && (flag = true, c = getchar()); if (! ~ c) return false; while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar(); return (flag && (x = -x), true); }
+  inline bool read(char &c) { c = '\n'; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; return ~ c; }
+  inline int read(char s[]) { char c = '\n'; int len = 0; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; if (! ~ c) return 0; while (isprint(c) && c != ' ') s[len ++] = c, c = getchar(); return (s[len] = '\0', len); }
+  template <typename T> inline IOManager operator > (T &x) { read(x); return *this; }
+  template <typename T> inline void write(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
+  inline void write(char c) { putchar(c); }
+  inline void write(char s[]) { int pos = 0; while (s[pos] != '\0') putchar(s[pos ++]); }
+  template <typename T> inline IOManager operator < (T x) { write(x); return *this; }
+} io;
+
+struct Solver {
+private:
+  static const int N = 110;
+  int n, col[N][N];
+  vector <int> mp[N];
+  void input() {
+    io > n;
+    for (int i = 1; i <= n; ++ i) {
+      int t; io > t;
+      while (t) mp[i].push_back(t), io > t;
+    }
+  }
+  void dfs(int t, int c) {
+    for (int i = 0; i < mp[t].size(); ++ i)
+      if (! col[t][mp[t][i]]) col[t][mp[t][i]] = col[mp[t][i]][t] = c, dfs(mp[t][i], c = 3 - c);
+  }
+  void process() {
+    for (int i = 1; i <= n; ++ i) if (mp[i].size() != 2) dfs(i, 1);
+    for (int i = 1; i <= n; ++ i) dfs(i, 1);
+    for (int i = 1; i <= n; ++ i) {
+      bool w = false, b = false;
+      for (int j = 0; j < mp[i].size(); ++ j) w |= col[i][mp[i][j]] == 1, b |= col[i][mp[i][j]] == 2;
+      if (mp[i].size() > 1 && ! (w && b)) { io < (char *) "No solution\n"; return; }
+    }
+    for (int i = 1; i <= n; ++ i) {
+      for (int j = 0; j < mp[i].size(); ++ j) io < col[i][mp[i][j]] < ' ';
+      io < 0 < '\n';
+    }
+  }
+
+public:
+  void solve() { input(), process(); }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 86 - 0
source/_posts/oi/broken-line.md

@@ -0,0 +1,86 @@
+---
+title: Broken Line
+tags:
+  - Plane Geometry
+id: "1569"
+categories:
+  - - OI
+    - Computational Geometry
+date: 2017-10-23 13:10:43
+---
+
+## 题目描述
+
+There is a closed broken line on a plane with sides parallel to coordinate axes, without self-crossings and self-contacts. The broken line consists of $K$ segments. You have to determine, whether a given point with coordinates $(X_0, Y_0)$ is inside this closed broken line, outside or belongs to the broken line.
+
+## 题意概述
+
+给定一个简单$N$边形,它的每条边都平行于坐标轴。问点$(X_0, Y_0)$是否在多边形内。
+
+数据范围:$4 \le N \le 10000$。
+
+## 算法分析
+
+从$(X_0, Y_0)$向右水平射出一条射线,若射线与$N$边形有奇数个交点则该点在多边形内。
+
+判断一条边是否与射线相交就是要看该边的两个端点是否在射线异侧,在射线上或射线上方算同一侧,在射线下方算另一侧。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+
+using namespace std;
+
+struct IOManager {
+  template <typename T> inline bool read(T &x) { char c = '\n'; bool flag = false; x = 0; while (~ c && ! isdigit(c = getchar()) && c != '-') ; c == '-' && (flag = true, c = getchar()); if (! ~ c) return false; while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar(); return (flag && (x = -x), true); }
+  inline bool read(char &c) { c = '\n'; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; return ~ c; }
+  inline int read(char s[]) { char c = '\n'; int len = 0; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; if (! ~ c) return 0; while (isprint(c) && c != ' ') s[len ++] = c, c = getchar(); return (s[len] = '\0', len); }
+  template <typename T> inline IOManager operator > (T &x) { read(x); return *this; }
+  template <typename T> inline void write(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
+  inline void write(char c) { putchar(c); }
+  inline void write(char s[]) { int pos = 0; while (s[pos] != '\0') putchar(s[pos ++]); }
+  template <typename T> inline IOManager operator < (T x) { write(x); return *this; }
+} io;
+
+struct Line { int x1, y1, x2, y2; };
+
+struct Solver {
+private:
+  static const int K = 10010;
+  int k, x, y;
+  Line l[K];
+  void input() {
+    io > k;
+    for (int i = 1; i <= k; ++ i) {
+      io > l[i].x1 > l[i].y1 > l[i].x2 > l[i].y2;
+      if (l[i].x1 > l[i].x2 || l[i].y1 > l[i].y2) swap(l[i].x1, l[i].x2), swap(l[i].y1, l[i].y2);
+    }
+    io > x > y;
+  }
+  bool in(int x, int y, Line l) {
+    if (l.x1 == l.x2) return x == l.x1 && l.y1 <= y && y <= l.y2;
+    else return y == l.y1 && l.x1 <= x && x <= l.x2;
+  }
+  void process() {
+    for (int i = 1; i <= k; ++ i) if (in(x, y, l[i])) { io < (char *) "BORDER\n"; return; }
+    int cnt = 0;
+    for (int i = 1; i <= k; ++ i)
+      cnt += l[i].x1 == l[i].x2 && l[i].x1 > x && ((l[i].y1 <= y) ^ (l[i].y2 <= y));
+    if (cnt & 1) io < (char *) "INSIDE\n"; else io < (char *) "OUTSIDE\n";
+  }
+
+public:
+  void solve() { input(), process(); }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 56 - 0
source/_posts/oi/calculator.md

@@ -0,0 +1,56 @@
+---
+title: Calculator
+tags:
+  - Greedy
+id: "1142"
+categories:
+  - - OI
+    - Common Skill
+date: 2017-07-11 23:10:10
+---
+
+## 题目描述
+
+Chef has a calculator which has two screens and two buttons. Initially, each screen shows the number zero. Pressing the first button increments the number on the first screen by $1$, and each click of the first button consumes $1$ unit of energy.
+
+Pressing the second button increases the number on the second screen by the number which is currently appearing on the first screen. Each click of the second button consumes $B$ units of energy.
+
+Initially the calculator has $N$ units of energy.
+
+Now chef wonders what the maximum possible number is, that he gets on the second screen of the calculator, with the limited energy.
+
+## 题意概述
+
+有两个按钮和两个初始为$0$的数字,按下第一个按钮可以消耗$1$单位能量使第一个数字加$1$,按下第二个按钮可以消耗$B$单位能量使第二个数字加上第一个数字。总共有$N$单位能量,问第二个数字最大能达到多少。
+
+数据范围:$1 \le N, B \le 10^9$。
+
+## 算法分析
+
+根据贪心策略,按第一个按钮的操作一定在按第二个按钮的操作之前(否则交换一下顺序一定能得到更优解)。设按下第一个按钮$x$次,则第二个数字$y={N-x \over B}x$,对称轴为$x={N \over 2}$。令$a={N-x \over B}$,当$x={N \over 2}$时$a={N \over 2B}$。由于$a$是整数(否则会造成能量浪费),因此只需计算$a=\left\lfloor {N \over 2B} \right\rfloor$和$a=\left\lceil {N \over 2B} \right\rceil$时$y$的值,取较大者即可。
+
+## 代码
+
+```cpp
+#include <iostream>
+#include <cmath>
+using namespace std;
+long long T, n, b, p, q, x, ans;
+long double k;
+int main()
+{
+  cin >> T;
+  while (T--) {
+    cin >> n >> b;
+    ans = 0;
+    k = 1.0 * n / 2 / b;
+    p = floor(k), q = ceil(k);
+    x = n - b * p;
+    if (x > 0 && x < n) ans = max(ans, p * x);
+    x = n - b * q;
+    if (x > 0 && x < n) ans = max(ans, q * x);
+    cout << ans << endl;
+  }
+  return 0;
+}
+```

+ 70 - 0
source/_posts/oi/cards-sorting.md

@@ -0,0 +1,70 @@
+---
+title: Cards Sorting
+tags:
+  - Binary Indexed Tree
+  - Implementation
+id: "1200"
+categories:
+  - [OI, Common Skill]
+  - [OI, Data Structure]
+date: 2017-07-17 08:30:21
+---
+
+## 题目描述
+
+Vasily has a deck of cards consisting of $n$ cards. There is an integer on each of the cards, this integer is between $1$ and $100000$, inclusive. It is possible that some cards have the same integers on them.
+
+Vasily decided to sort the cards. To do this, he repeatedly takes the top card from the deck, and if the number on it equals the minimum number written on the cards in the deck, then he places the card away. Otherwise, he puts it under the deck and takes the next card from the top, and so on. The process ends as soon as there are no cards in the deck. You can assume that Vasily always knows the minimum number written on some card in the remaining deck, but doesn't know where this card (or these cards) is.
+
+You are to determine the total number of times Vasily takes the top card from the deck.
+
+## 题意概述
+
+有$n$张卡牌,第$i$张卡牌上的数字为$a_i$。每次从牌堆顶拿一张牌,如果它上面的数字是当前牌堆中最小的,则将它扔掉,否则将它放到牌堆底。问几次操作后牌堆为空。
+
+数据范围:$1 \le n \le 10^5, \; 1 \le a_i \le 10^5$。
+
+## 算法分析
+
+先对卡牌上的数进行计数排序,接着从小到大处理:维护一个$now$指针,表示当前走到了第$now$个数;用树状数组维护当前剩余数字个数的前缀和,这样可以快速求出从第$i$个数走到第$j$个数所需要的次数。
+
+## 代码
+
+```cpp
+#include <iostream>
+#include <vector>
+using namespace std;
+struct binary_indexed_tree {
+  long long n; vector<long long> a;
+  void init(long long t) { n = t + 10, a.clear(), a.resize(n); }
+  void add(long long p, long long t) {
+    for (long long i = p; i < n; i += i & -i) a[i] += t;
+  }
+  long long sum(long long p) {
+    long long ret = 0;
+    for (long long i = p; i; i -= i & -i) ret += a[i];
+    return ret;
+  }
+} tree;
+long long n, t, now, ans, pre;
+vector<long long> b[100001];
+int main()
+{
+  cin >> n;
+  tree.init(n);
+  for (long long i = 1; i <= n; ++i) {
+    cin >> t; tree.add(i, 1), b[t].push_back(i);
+  }
+  for (long long i = 1; i <= 100000; ++i) {
+    if (!b[i].size()) continue; long long pnt = 0;
+    while (pnt < b[i].size() && b[i][pnt] < now) ++pnt; --pnt;
+    if (pnt < 0) pnt = b[i].size() - 1;
+    if (b[i][pnt] > now) ans += tree.sum(b[i][pnt]) - tree.sum(now);
+    else ans += tree.sum(n) - tree.sum(now) + tree.sum(b[i][pnt]);
+    for (long long j = 0; j < b[i].size(); ++j) tree.add(b[i][j], -1);
+    now = b[i][pnt];
+  }
+  cout << ans << endl;
+  return 0;
+}
+```

+ 154 - 0
source/_posts/oi/ceizenpoks-formula.md

@@ -0,0 +1,154 @@
+---
+title: Ceizenpok's Formula
+tags:
+  - Chinese Remainder Theorem
+  - Combinatorics
+  - GCD-LCM
+  - Lucas's Theorem
+  - Multiplicative Inverse
+  - Prime
+id: "2209"
+categories:
+  - - OI
+    - Number Theory
+date: 2018-03-05 15:25:10
+---
+
+## 题目描述
+
+Dr. Ceizenp'ok from planet i1c5l became famous across the whole Universe thanks to his recent discovery - the Ceizenpok’s formula. This formula has only three arguments: $n, k$ and $m$, and its value is a number of $k$-combinations of a set of $n$ modulo $m$.
+
+While the whole Universe is trying to guess what the formula is useful for, we need to automate its calculation.
+
+## 题意概述
+
+求${n \choose k} \bmod m$。
+
+数据范围:$1 \le n \le 10^{18}, \; 0 \le k \le n, \; 2 \le m \le 10^6$。
+
+## 算法分析
+
+令
+
+$$
+x={n \choose k}, \; m=p_1^{a_1}p_2^{a_2} \cdots p_q^{a_q}
+$$
+
+可以列出方程组
+
+$$
+\left\{
+\begin{array}{c}
+x \equiv r_1 \pmod{p_1^{a_1}} \\\\
+x \equiv r_2 \pmod{p_2^{a_2}} \\\\
+\cdots \\\\
+x \equiv r_q \pmod{p_q^{a_q}}
+\end{array}
+\right.
+$$
+
+由于模数两两互质,所以该方程组在模$m$意义下有唯一解。
+
+考虑如何求$r_i$。实际上,我们要求的就是${n \choose k} \bmod p_i^{a_i}$。我们知道
+
+$$
+{n \choose k}={n! \over k!(n-k)!}
+$$
+
+那么只要求出$n! \bmod p_i^{a_i}, \; k! \bmod p_i^{a_i}, \; (n-k)! \bmod p_i^{a_i}$的值,就可以用逆元求出${n \choose k} \bmod p_i^{a_i}$。
+
+对于如何求$n! \bmod p_i^{a_i}$,令
+
+$$
+f(n)=n! \bmod p_i^{a_i}
+$$
+
+由$x \equiv x+p_i^{a_i} \pmod{p_i^{a_i}}$,可得
+
+$$
+f(n)=\left(f\left(\left\lfloor{n \over p_i}\right\rfloor\right)\cdot p_i^{\lfloor n/p_i \rfloor}\cdot\left(\prod_{i \in [1, p_i^{a_i}], \; p_i\not\mid i}i\right)^{\lfloor n/p_i^{a_i} \rfloor}\cdot\prod_{i \in [1, n \bmod p_i^{a_i}], \; p_i\not\mid i}i\right)\bmod p_i^{a_i}
+$$
+
+但是$k!, \; (n-k)!$在模$p_i^{a_i}$意义下可能不存在逆元,因此需要将$n!, \; k!, \; (n-k)!$中的$p_i$因子提取出来,求出逆元后再乘回去。
+
+这样就得到了所有$r_i$。用中国剩余定理求解方程组即可。
+
+## 代码
+
+```cpp
+/*
+ * Your lucky number has been disconnected.
+ */
+
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+
+#define int long long
+
+int power(int a, int b, int m) {
+  int ret = 1;
+  for (; b; b >>= 1)
+    b & 1 && ((ret *= a) %= m), (a *= a) %= m;
+  return ret;
+}
+
+void ex_gcd(int a, int b, int &x, int &y) {
+  if (!b) {
+    x = 1, y = 0;
+    return;
+  }
+  ex_gcd(b, a % b, y, x), y -= a / b * x;
+}
+
+int get_inv(int a, int b) {
+  int x, y;
+  ex_gcd(a, b, x, y);
+  return (x + b) % b;
+}
+
+int get_fac(int n, int p, int k) {
+  int m = power(p, k, 1e9), ret = 1;
+  for (; n; n /= p) {
+    if (n / m) {
+      int rec = 1;
+      for (int i = 2; i < m; ++i)
+        if (i % p)
+          (rec *= i) %= m;
+      (ret *= power(rec, n / m, m)) %= m;
+    }
+    for (int i = n % m; i > 1; --i)
+      if (i % p)
+        (ret *= i) %= m;
+  }
+  return ret;
+}
+
+int calc(int N, int K, int M, int p, int k) {
+  int a = get_fac(N, p, k), b = get_fac(K, p, k), c = get_fac(N - K, p, k),
+      cnt = 0;
+  for (int i = N; i; i /= p)
+    cnt += i / p;
+  for (int i = K; i; i /= p)
+    cnt -= i / p;
+  for (int i = N - K; i; i /= p)
+    cnt -= i / p;
+  int m = power(p, k, 1e9),
+      ret = a * get_inv(b, m) % m * get_inv(c, m) % m * power(p, cnt, m) % m;
+  return ret * (M / m) % M * get_inv(M / m, m) % M;
+}
+
+signed main() {
+  int N, K, M, ans = 0;
+  scanf("%lld%lld%lld", &N, &K, &M);
+  for (int i = 2, t = M; t > 1; ++i)
+    if (!(t % i)) {
+      int k = 0;
+      for (; !(t % i); ++k, t /= i)
+        ;
+      (ans += calc(N, K, M, i, k)) %= M;
+    }
+  printf("%lld\n", ans);
+  return 0;
+}
+```

+ 64 - 0
source/_posts/oi/chef-and-sign-sequences.md

@@ -0,0 +1,64 @@
+---
+title: Chef and Sign Sequences
+tags:
+  - Implementation
+id: "1138"
+categories:
+  - - OI
+    - Common Skill
+date: 2017-07-11 22:30:56
+---
+
+## 题目描述
+
+Chef found a strange string yesterday - a string of signs $s$, where each sign is either a '<', '=' or a '>'. Let $N$ be the length of this string. Chef wants to insert $N+1$ positive integers into this sequence and make it valid. A valid sequence is a sequence where every sign is preceded and followed by an integer, and the signs are correct. That is, if a sign '<' is preceded by the integer $a$ and followed by an integer $b$, then $a$ should be less than $b$. Likewise for the other two signs as well.
+
+Chef can take some positive integers in the range $[1, P]$ and use a number in the range as many times as he wants.
+
+Help Chef find the minimum possible $P$ with which he can create a valid sequence.
+
+## 题意概述
+
+给定一个由'<'、'='和'>'组成的字符串,要求在首尾和每两个相邻的符号间填入一个数,使得表达式成立。求数字的最小种类数。
+
+数据范围:$1 \le |s| \le 10^5$。
+
+## 算法分析
+
+将'='删去后,计算出只由'<'或'>'组成的最长连续子串的长度,加$1$即得到答案。证明如下:
+
+> '='对最终结果没有影响,可以全部忽略。设找到了一个只由'<'或'>'组成的最长连续子串,长度为$l$。在这个子串的左边填入$a$,右边填入$a+l$。由于其他只由'<'或'>'组成的连续子串的长度均不大于$l$,因此也可以分别在它们左右填入$a$和$a+l$,这样必定可以满足题目要求。
+
+由此得证。
+
+## 代码
+
+```cpp
+#include <iostream>
+#include <algorithm>
+using namespace std;
+int n, ans, len, out;
+string s;
+int main()
+{
+  cin >> n;
+  while (n--) {
+    cin >> s;
+    ans = out = 0;
+    len = s.length();
+    for (int i = 0; i < len; ++i) {
+      if (s[i] == '<') {
+        if (ans < 0) ans = 0;
+        ++ans;
+      }
+      if (s[i] == '>') {
+        if (ans > 0) ans = 0;
+        --ans;
+      }
+      out = max(out, abs(ans) + 1);
+    }
+    cout << out << endl;
+  }
+  return 0;
+}
+```

+ 76 - 0
source/_posts/oi/chess-championship.md

@@ -0,0 +1,76 @@
+---
+title: Chess Championship
+tags:
+  - Dynamic Programming
+id: "2683"
+categories:
+  - - OI
+    - Common Skill
+date: 2018-04-30 23:55:20
+---
+
+## 题目描述
+
+Chess Championship is set up in the New Basyuki City. Two national teams, of Berland and Byteland, are going to have a match there. Each team is represented by $n$ players. The championship consists of $n$ games - in each game a pair of players from different teams meets. A game victory brings $1$ point to the team and a game defeat doesn't add or subtract points.
+
+The championship starts with the sortition - the process that determines opponent pairs. Each player from the first team plays with exactly one player from the second team (and vice versa).
+
+A recent research conducted by Berland scientists showed that every player of either team is characterized by a single number - the level of his chess mastership. No two people among the $2n$ players play at the same level. Funny as it is, the game winner always is the player with the higher level.
+
+The contest organizers received information that a high-ranking Berland official Mr. B. bet $100500$ burles on the victory of his team with a $k$ points gap. Immediately an unofficial "recommendation" came from very important people to "organize" the sortition so that the Berland team gets exactly $k$ points more than the Byteland team.
+
+Write a program that finds the number of distinct sortition results after which Berland gets exactly $k$ points more than Byteland. Two sortitions are considered distinct if there is such player, that gets different opponents by the sortitions' results.
+
+## 题意概述
+
+给定两组数$a_i, b_i$,每组$n$个数。保证所有$a_i, b_i$中不会出现相同的数。求有多少个排列$p$满足$\sum_{i=1}^n [a_i \gt b_{p_i}]-\sum_{i=1}^n [a_i \lt b_{p_i}]=k$。
+
+数据范围:$1 \le k \le n \le 500$。
+
+## 算法分析
+
+当$k \not \equiv n \pmod 2$时,排列不存在。
+
+将所有数从小到大排序。令$f_{i, j, k}$表示在前$i$个数中,有$j$个$a$中的数匹配了$b$中比它小的数,有$k$个$b$中的数匹配了$a$中比它小的数的方案数。转移时只要计算前$i$个数中两组分别有几个数未匹配,枚举第$i$个数是否匹配另一组中比它小的数。最后的答案就是$f_{2n, {n+k \over 2}, {n-k \over 2}}$。
+
+需要滚动数组。
+
+## 代码
+
+```cpp
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+
+static int const N = 505;
+static int const MOD = 1000000009;
+int f[2][N][N];
+std::pair<int, int> a[N << 1];
+
+int main() {
+  int n, k;
+  scanf("%d%d", &n, &k);
+  if ((n & 1) != (k & 1))
+    return puts("0"), 0;
+  for (int i = 0; i < n; ++i)
+    scanf("%d", &a[i].first);
+  for (int i = n; i < n << 1; ++i)
+    scanf("%d", &a[i].first), a[i].second = 1;
+  std::sort(a, a + (n << 1));
+  int cur = 0, s[2] = {0, 0};
+  f[0][0][0] = 1;
+  for (int i = 0; i < n << 1; ++i, cur ^= 1) {
+    for (int j = 0; j <= n; ++j)
+      for (int k = 0; j + k <= n; ++k)
+        if (f[cur][j][k]) {
+          (f[!cur][j][k] += f[cur][j][k]) %= MOD;
+          int c[2] = {s[0] - j - k, s[1] - j - k};
+          (f[!cur][j + !a[i].second][k + a[i].second] += 1ll * f[cur][j][k] * c[!a[i].second] % MOD) %= MOD;
+          f[cur][j][k] = 0;
+        }
+    ++s[a[i].second];
+  }
+  printf("%d\n", f[cur][n + k >> 1][n - k >> 1]);
+  return 0;
+}
+```

+ 83 - 0
source/_posts/oi/choosing-balls.md

@@ -0,0 +1,83 @@
+---
+title: Choosing Balls
+tags:
+  - Dynamic Programming
+id: "1352"
+categories:
+  - - OI
+    - Common Skill
+date: 2017-08-08 18:05:39
+---
+
+## 题目描述
+
+There are $n$ balls. They are arranged in a row. Each ball has a color (for convenience an integer) and an integer value. The color of the $i$-th ball is $c_i$ and the value of the $i$-th ball is $v_i$.
+
+Squirrel Liss chooses some balls and makes a new sequence without changing the relative order of the balls. She wants to maximize the value of this sequence.
+
+The value of the sequence is defined as the sum of following values for each ball (where $a$ and $b$ are given constants):
+
+- If the ball is not in the beginning of the sequence and the color of the ball is same as previous ball's color, add $\text{the value of the ball} \times a$.
+- Otherwise, add $\text{the value of the ball} \times b$.
+
+You are given $q$ queries. Each query contains two integers $a_i$ and $b_i$. For each query find the maximal value of the sequence she can make when $a=a_i$ and $b=b_i$.
+
+Note that the new sequence can be empty, and the value of an empty sequence is defined as zero.
+
+## 题意概述
+
+$n$个球排成一行,第$i$个球的颜色是$c_i$,价值是$v_i$。一个序列的价值等于与前一个球颜色相同的球的价值之和乘以$a$加其他球的价值之和乘以$b$。空序列的价值为$0$。有$q$组询问,每组询问给定$a, b$,求这$n$个球的最大价值子序列。
+
+数据范围:$1 \le c_i \le n \le 10^5, \; 1 \le q \le 500, \; 0 \le |v_i|, |a|, |b| \le 10^5$。
+
+## 算法分析
+
+令$f_i$表示以第$i$种颜色结尾的序列的最大价值。设当前处理到第$i$个球,转移方程如下
+
+$$
+f_{c_i}=\max(\max(f_x \mid x \neq c_i, 0)+v_i \times b, f_{c_i}+v_i \times a)
+$$
+
+转移时维护当前$f_i$的最大值和次大值,这样可以直接得到$\max(f_x \mid x \neq c_i)$。
+
+## 代码
+
+```cpp
+import std.stdio;
+
+static immutable MAX_NUM = 1000000000000000;
+
+int n, q;
+int[] c;
+long[] v, f;
+
+int main() {
+  auto input = stdin;
+  auto output = stdout;
+  input.readf(" %d %d", &n, &q);
+  v.length = c.length = f.length = n + 1;
+  for (int i = 1; i <= n; ++ i) input.readf(" %d", &v[i]);
+  for (int i = 1; i <= n; ++ i) input.readf(" %d", &c[i]);
+  while (q --) {
+    long a, b, max = -MAX_NUM, sec = -MAX_NUM, ans = 0;
+    input.readf(" %d %d", &a, &b);
+    for (int i = 1; i <= n; ++ i) f[i] = -MAX_NUM;
+    for (int i = 1; i <= n; ++ i) {
+      long p = f[c[i]] == max ? sec : max, q = f[c[i]];
+      bool flag = f[c[i]] == max;
+      if (f[c[i]] < v[i] * b) f[c[i]] = v[i] * b;
+      if (f[c[i]] < p + v[i] * b) f[c[i]] = p + v[i] * b;
+      if (f[c[i]] < q + v[i] * a) f[c[i]] = q + v[i] * a;
+      if (f[c[i]] >= max) {
+        if (! flag) sec = max;
+        max = f[c[i]];
+      } else if (f[c[i]] > sec) sec = f[c[i]];
+    }
+    for (int i = 1; i <= n; ++ i) {
+      if (ans < f[i]) ans = f[i];
+    }
+    output.writeln(ans);
+  }
+  return 0;
+}
+```

+ 96 - 0
source/_posts/oi/choosing-the-commander.md

@@ -0,0 +1,96 @@
+---
+title: Choosing the Commander
+tags:
+  - Trie Tree
+id: "864"
+categories:
+  - - OI
+    - String
+date: 2017-06-30 15:20:25
+---
+
+## 题目描述
+
+As you might remember from the previous round, Vova is currently playing a strategic game known as Rage of Empires.
+
+Vova managed to build a large army, but forgot about the main person in the army - the commander. So he tries to hire a commander, and he wants to choose the person who will be respected by warriors.
+
+Each warrior is represented by his personality - an integer number $p_i$. Each commander has two characteristics — his personality $p_j$ and leadership $l_j$ (both are integer numbers). Warrior $i$ respects commander $j$ only if $p_i \oplus p_j \lt l_j$ ($x \oplus y$ is the bitwise excluding OR of $x$ and $y$).
+
+Initially Vova's army is empty. There are three different types of events that can happen with the army:
+
+- 1 $p_i$ - one warrior with personality $p_i$ joins Vova's army;
+- 2 $p_i$ - one warrior with personality $p_i$ leaves Vova's army;
+- 3 $p_i$ $l_i$ - Vova tries to hire a commander with personality $p_i$ and leadership $l_i$.
+
+For each event of the third type Vova wants to know how many warriors (counting only those who joined the army and haven't left yet) respect the commander he tries to hire.
+
+## 题意概述
+
+有一个初始为空的数字集合。有三种操作:① 向集合中加入一个数$p$;② 从集合中删除一个数$p$;③ 询问集合中与$p$的异或值小于$l$的数的个数。总共进行了$q$次操作。
+
+数据范围:$1 \le q \le 10^5, \; 1 \le p_i, l_i \le 10^8$。
+
+## 算法分析
+
+根据集合中数的二进制建立 Trie 树,记录下每个节点子树的大小。对于每次询问,从高到低枚举$p, l$二进制的第$k$位,用$p_k, l_k$表示。设当前走到的节点为$t$,下一位为$s$则走到节点$t_s$。若$l_k=1$,则节点$t_{p_k}$所表示的数一定小于$l$;若$l_k=0$,则节点$t_{1-p_k}$所表示的数一定大于$l$。
+
+## 代码
+
+```cpp
+#include <iostream>
+using namespace std;
+int q, o, p, l;
+struct trie_tree {
+    int tot = 1;
+    struct node_type {
+        int sum, child[2];
+    } a[2000001];
+    void insert(int t) {
+        int r = 1; ++a[r].sum;
+        for (int i = 26; i >= 0; --i) {
+            if (!a[r].child[(t >> i) & 1]) a[r].child[(t >> i) & 1] = ++tot;
+            r = a[r].child[(t >> i) & 1], ++a[r].sum;
+        }
+    }
+    void remove(int t) {
+        int r = 1; --a[r].sum;
+        for (int i = 26; i >= 0; --i) {
+            r = a[r].child[(t >> i) & 1], --a[r].sum;
+        }
+    }
+    int find(int p, int l) {
+        int r = 1, ret = 0;
+        for (int i = 26; i >= 0; --i) {
+            int s = (p >> i) & 1, t = (l >> i) & 1;
+            if (t && a[r].child[s]) ret += a[a[r].child[s]].sum;
+            if (t && a[r].child[!s]) r = a[r].child[!s];
+            else if (!t && a[r].child[s]) r = a[r].child[s];
+            else break;
+        }
+        return ret;
+    }
+} tree;
+int main()
+{
+    cin >> q;
+    while (q--) {
+        cin >> o;
+        switch (o) {
+            case 1: {
+                cin >> p, tree.insert(p);
+                break;
+            }
+            case 2: {
+                cin >> p, tree.remove(p);
+                break;
+            }
+            default: {
+                cin >> p >> l;
+                cout << tree.find(p, l) << endl;
+            }
+        }
+    }
+    return 0;
+}
+```

+ 166 - 0
source/_posts/oi/clever-y.md

@@ -0,0 +1,166 @@
+---
+title: Clever Y
+tags:
+  - Baby-Step Giant-Step
+  - GCD-LCM
+  - Multiplicative Inverse
+id: "2371"
+categories:
+  - - OI
+    - Number Theory
+date: 2018-03-08 10:55:16
+---
+
+## 题目描述
+
+Little Y finds there is a very interesting formula in mathematics:
+
+$$
+X^Y \bmod Z=K
+$$
+
+Given $X, Y, Z$, we all know how to figure out $K$ fast. However, given $X, Z, K$, could you figure out $Y$ fast?
+
+## 题意概述
+
+给定$X, Z, K$,求满足$X^Y \bmod Z=K$的$Y$的最小值。若不存在$0 \le Y \lt Z$满足条件,则输出无解。
+
+数据范围:$0 \le X, Z, K \le 10^9$。
+
+## 算法分析
+
+显然$K \ge Z$时无解。在其他情况下,条件可以写成
+
+$$
+X^Y \equiv K \pmod Z
+$$
+
+特殊处理两种情况:$X, K$中至少有一个为$0$时,$Y=1$或无解;$K=1$时,$Y=0$。
+
+先考虑$(X, Z)=1$的情况。令$M=\lceil \sqrt{Z} \rceil, \; Y=aM-b \; (0 \lt a, b \le M)$,那么
+
+$$
+\begin{align}
+X^{aM-b} &\equiv K \pmod Z \\\\
+X^{aM} &\equiv K \cdot X^b \pmod Z
+\end{align}
+$$
+
+可以在$O(M)$的时间内预处理出$K \cdot X^b$,存在哈希表中,接着$O(M)$枚举$a$,判断哈希表中是否存在$X^{aM}$,若存在,则找到解$Y=aM-b$。由于要求最小值,因此哈希表中有重复时应记录较大的$b$。
+
+下面考虑$(X, Z) \gt 1$的情况。令$D=(X, Z)$,易知若$K \nmid D$则无解。设$X=Dx, \; K=Dk, \; Z=Dz$,据同余的性质可得
+
+$$
+\begin{align}
+(Dx)^Y &\equiv Dk \pmod{Dz} \\\\
+x \cdot (Dx)^{Y-1} &\equiv k \pmod z
+\end{align}
+$$
+
+因为$D=(X, Z)$,所以$(x, z)=1$,即$x$在模$z$意义下有逆元,因此
+
+$$
+(Dx)^{Y-1} \equiv k \cdot x^{-1} \pmod z
+$$
+
+令$X'=Dx=X, \; Y'=Y-1, \; K'=k \cdot x^{-1}, \; Z'=z$,则
+
+$$
+(X')^{Y'} \equiv K' \pmod{Z'}
+$$
+
+这和初始时的方程具有一样的形式。若$(X', Z') \gt 1$,则重复以上过程,否则可以参照$(X, Z)=1$的情况。
+
+## 代码
+
+```cpp
+/*
+ * You will be singled out for promotion in your work.
+ */
+
+#include <algorithm>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+
+class HashTable {
+private:
+  static int const MOD = 3214567;
+  int numr, h[MOD], id[MOD], val[MOD], nxt[MOD];
+
+public:
+  void clear() { memset(h, numr = 0, sizeof h); }
+
+int count(int const &n) {
+    for (int i = h[n % MOD]; i; i = i[nxt])
+      if (i[id] == n)
+        return 1;
+    return 0;
+  }
+
+int &operator[](int const &n) {
+    for (int i = h[n % MOD]; i; i = i[nxt])
+      if (i[id] == n)
+        return i[val];
+    ++numr, numr[id] = n, numr[nxt] = h[n % MOD], h[n % MOD] = numr;
+    return numr[val];
+  }
+} mp;
+
+int ex_gcd(int a, int b, int &x, int &y) {
+  if (!b) {
+    x = 1, y = 0;
+    return a;
+  }
+  int ret = ex_gcd(b, a % b, y, x);
+  y -= a / b * x;
+  return ret;
+}
+
+int get_gcd(int a, int b) {
+  int x, y;
+  return ex_gcd(a, b, x, y);
+}
+
+int get_inv(int a, int b) {
+  int x, y;
+  return ex_gcd(a, b, x, y), (x + b) % b;
+}
+
+int bsgs(int a, int b, int c) {
+  a %= c;
+  if (a == 0)
+    return b == 0 ? 1 : -1;
+  if (b == 0)
+    return -1;
+  if (b == 1)
+    return 0;
+  int k = 0;
+  for (int t; (t = get_gcd(a, c)) > 1; ++k) {
+    if (b % t)
+      return -1;
+    c /= t, b /= t, b = 1ll * b * get_inv(a / t, c) % c;
+  }
+  mp.clear();
+  int d = a, m = sqrt(c) + 1;
+  for (int i = 1; i <= m; ++i, d = 1ll * d * a % c)
+    mp[1ll * d * b % c] = i;
+  d = 1ll * d * get_inv(a, c) % c;
+  int e = d;
+  for (int i = 1; i <= m; ++i, e = 1ll * e * d % c)
+    if (mp.count(e))
+      return i * m - mp[e] + k;
+  return -1;
+}
+
+int main() {
+  for (int a, b, c; scanf("%d%d%d", &a, &c, &b), a || b || c;) {
+    int ans = bsgs(a, b, c);
+    if (~ans)
+      printf("%d\n", ans);
+    else
+      puts("No Solution");
+  }
+  return 0;
+}
+```

+ 65 - 0
source/_posts/oi/coin-troubles.md

@@ -0,0 +1,65 @@
+---
+title: Coin Troubles
+tags:
+  - Dynamic Programming
+  - Topological-Sort
+id: "1293"
+categories:
+  - [OI, Common Skill]
+  - [OI, Graph Theory]
+date: 2017-07-27 19:00:40
+---
+
+## 题目描述
+
+In the Isle of Guernsey there are $n$ different types of coins. For each $i \; (1 \le i \le n)$, coin of type $i$ is worth $a_i$ cents. It is possible that $a_i=a_j$ for some $i$ and $j \; (i \neq j)$.
+
+Bessie has some set of these coins totaling $t$ cents. She tells Jessie $q$ pairs of integers. For each $i \; (1 \le i \le q)$, the pair $b_i, c_i$ tells Jessie that Bessie has a strictly greater number of coins of type $b_i$ than coins of type $c_i$. It is known that all $b_i$ are distinct and all $c_i$ are distinct.
+
+Help Jessie find the number of possible combinations of coins Bessie could have. Two combinations are considered different if there is some $i \; (1 \le i \le n)$, such that the number of coins Bessie has of type $i$ is different in the two combinations. Since the answer can be very large, output it modulo $1000000007$ ($10^9+7$).
+
+If there are no possible combinations of coins totaling $t$ cents that satisfy Bessie's conditions, output $0$.
+
+## 题意概述
+
+你有$n$种纸币,第$i$种纸币的价值为$a_i$(可能有多个$a_i$相等)。已知所有纸币的总价值为$t$。给定$q$对整数$b_i, c_i$,表示第$b_i$种纸币的数量严格大于第$c_i$种纸币的数量。所有$b_i$均不相等,所有$c_i$均不相等。求满足这些条件的方案数。
+
+数据范围:$1 \le n \le 300, \; 1 \le t \le 10^5, \; 1 \le a_i \le 10^5$。
+
+## 算法分析
+
+将每种硬币看成点,每条限制看成有向边,这样就构成了一张有向图。若这张图中存在环,则方案数为$0$。否则,所有边构成的一定是链。对于一条链上的点$v_1, v_2, \ldots, v_m$,我们将$a_{v_2}$变成$a_{v_1}+a_{v_2}$,将$a_{v_3}$变成$a_{v_1}+a_{v_2}+a_{v_3}$,以此类推。这样做的原因是:如果选了第$v_2$件物品一次,就必须也选第$v_1$件物品一次;如果选了第$v_3$件物品一次,就必须也选第$v_1$和第$v_2$件物品一次。这就变成一个完全背包问题了。注意到完全背包问题中的物品是可以不选的,而在这个问题中有些物品是必须选的。例如,第$v_{m-1}$件物品必须至少选一次,第$v_{m-2}$件物品必须至少选两次(因为题目中要求严格大于)。只需在$t$中减去这些必须选的就可以了。
+
+## 代码
+
+```cpp
+#include <iostream>
+using namespace std;
+const long long mod = 1000000007ll;
+long long n, q, t, ma, a[301], to[301], f[100001];
+bool m[301], in[301], vis[301];
+void dfs(long long u, long long d, long long s) {
+  ma = d, vis[u] = true;
+  if (to[u]) dfs(to[u], d + 1, s + a[u]);
+  t -= (ma - d) * a[u], a[u] += s;
+}
+int main()
+{
+  cin >> n >> q >> t, f[0] = 1;
+  for (int i = 1; i <= n; cin >> a[i], ++i);
+  for (int i = 1; i <= q; ++i) {
+    long long b, c; cin >> b >> c;
+    m[b] = m[c] = true, in[c] = true, to[b] = c;
+  }
+  for (int i = 1; i <= n; ++i)
+    if (m[i] && !in[i]) ma = 0, dfs(i, 0, 0);
+  if (t < 0) { cout << 0 << endl; return 0; }
+  for (int i = 1; i <= n; ++i)
+    if (m[i] && !vis[i]) { cout << 0 << endl; return 0; }
+  for (int i = 1; i <= n; ++i)
+    for (int j = 0; j <= t - a[i]; ++j)
+      (f[j + a[i]] += f[j]) %= mod;
+  cout << f[t] << endl;
+  return 0;
+}
+```

+ 76 - 0
source/_posts/oi/colored-balls.md

@@ -0,0 +1,76 @@
+---
+title: Colored Balls
+tags:
+  - Greedy
+id: "486"
+categories:
+  - - OI
+    - Common Skill
+date: 2017-06-18 21:10:56
+---
+
+## 题目描述
+
+There are $n$ boxes with colored balls on the table. Colors are numbered from $1$ to $n$. $i$-th box contains $a_i$ balls, all of which have color $i$. You have to write a program that will divide all balls into sets such that:
+
+- each ball belongs to exactly one of the sets,
+- there are no empty sets,
+- there is no set containing two (or more) balls of different colors (each set contains only balls of one color),
+- there are no two sets such that the difference between their sizes is greater than $1$.
+
+Print the minimum possible number of sets.
+
+## 题意概述
+
+有$n$个盒子,第$i$个盒子的大小为$a_i$。每次可以将一个盒子拆分成两个盒子满足拆分后盒子的大小之和等于拆分前盒子的大小。问至少拆分成多少个盒子使得最大盒子和最小盒子的大小之差不大于$1$。
+
+数据范围:$0 \le n \le 500, \; 1 \le a_i \le 10^9$。
+
+## 算法分析
+
+设最小盒子的大小为$r$。根据贪心策略,$r$越大,盒子数越少。
+
+如何判断$a_i=rx+(r+1)y$是否有自然数解呢?
+
+因为$a_i=a_i/r \times r+a_i \bmod r$,所以可以先把$a_i$分解成$(a_i/r)$个$r$相加,再加上$a_i \bmod r$。如果$a_i \bmod r \le a_i/r$,那么我们就可以把剩下来的$a_i \bmod r$一个一个分配到前面$(a_i/r)$个$r$上,并且满足最大值和最小值之差小于等于$1$这个条件。否则,无论怎么分配都会使得最大值和最小值之差大于$1$。
+
+这样我们就可以判断对于某一个$r$,是否所有盒子都能够被分解成若干个$r$和若干个$(r+1)$相加。
+
+接下来的问题就是如何找到这个$r$。易知$1 \le r \le \min(a_i)$。可以发现,如果$\min(a_i)$很大,在$\sqrt{\min(a_i)}$到$\min(a_i)$中可能满足条件的$r$是很少的,而且它们都在$\min(a_i)/t \; (1 \le t \le \sqrt{\min(a_i)})$附近。事实上,我们只需要从$1$到$\sqrt{\min(a_i)}$枚举$k$,分别判断$k, \min(a_i)/k, \min(a_i)/k-1$是否满足条件,取最大值即可。
+
+最后,如果求出了满足条件的$r$,那么每个盒子被拆分后至少有$(a_i-1)/(r+1)+1$个。
+
+## 代码
+
+```cpp
+#include <iostream>
+#include <algorithm>
+#include <cmath>
+using namespace std;
+long long n, m, r = 1, ans, a[501];
+bool check(long long t) {
+    for (int i = 1; i <= n; ++i)
+        if (a[i] % t > a[i] / t) return false;
+    return true;
+}
+int main()
+{
+    cin >> n;
+    m = 1e9;
+    for (int i = 1; i <= n; ++i) {
+        cin >> a[i];
+        m = min(m, a[i]);
+    }
+    long long p = (long long) sqrt(m);
+    for (long long i = 1; i <= p; ++i) {
+        if (m / i > r && check(m / i)) r = m / i;
+        if (m / i - 1 > r && check(m / i - 1)) r = m / i - 1;
+        if (i > r && check(i)) r = i;
+    }
+    for (int i = 1; i <= n; ++i) {
+        ans += (a[i] - 1) / (r + 1) + 1;
+    }
+    cout << ans << endl;
+    return 0;
+}
+```

+ 78 - 0
source/_posts/oi/colorful-hats.md

@@ -0,0 +1,78 @@
+---
+title: Colorful Hats
+tags:
+  - Implementation
+id: "570"
+categories:
+  - - OI
+    - Common Skill
+date: 2017-06-21 13:20:49
+---
+
+## 题目描述
+
+There are $N$ cats. We number them from $1$ through $N$.
+
+Each of the cats wears a hat. Cat $i$ says: "there are exactly $a_i$ different colors among the $N-1$ hats worn by the cats except me."
+
+Determine whether there exists a sequence of colors of the hats that is consistent with the remarks of the cats.
+
+## 题意概述
+
+有$N$只猫戴着帽子,每一顶帽子都有一种颜色。第$i$只猫看到了$a_i$种颜色的帽子(它看不到自己的帽子),问是否存在一种不矛盾的颜色分配方案。
+
+数据范围:$2 \le N \le 10^5, \; 1 \le a_i \le N-1$。
+
+## 算法分析
+
+首先可以发现所有数中最大值$mx$和最小值$mn$之差一定不大于$1$。证明如下:
+
+> 定义$c_i$为第$i$只猫帽子颜色出现的次数。设$i \neq j$。如果$c_i=1 \land c_j=1$,那么$a_i=a_j$;如果$c_i=1 \land c_j \gt 1$,那么$a_i+1=a_j$;如果$c_i \gt 1 \land c_j=1$,那么$a_i=a_j+1$;如果$c_i \gt 1 \land c_j \gt 1$,那么$a_i=a_j$。
+
+由此得证。
+
+如果$mx=mn$,那么有两种情况:
+
+1. 对于所有的$i$均有$c_i=1$。此时$n=mx+1$;
+2. 对于所有的$i$均有$c_i \gt 1$。此时$n \ge 2mx$。
+
+如果$mx \gt mn$,令$p$为所有数中等于$mn$的数的个数。根据上面的证明,若$a_i=mn$则$c_i=1$,若$a_i=mx$则$c_i \gt 1$。易知$p$等于满足$c_i=1$的$i$的个数,即唯一出现的颜色的个数;而$mx$等于所有颜色的个数。因此,只需要满足$mx \gt p \land n - p \ge 2(mx-p)$即可。
+
+## 代码
+
+```cpp
+#include <iostream>
+using namespace std;
+int n, a[100001], ma, mi, p;
+int main()
+{
+    cin >> n;
+    mi = 1e9;
+    for (int i = 1; i <= n; ++i) {
+        cin >> a[i];
+        ma = max(ma, a[i]);
+        mi = min(mi, a[i]);
+    }
+    if (ma - mi > 1) {
+        cout << "No" << endl;
+    } else {
+        if (ma == mi) {
+            if (ma + 1 != n && ma << 1 > n) {
+                cout << "No" << endl;
+            } else {
+                cout << "Yes" << endl;
+            }
+        } else {
+            for (int i = 1; i <= n; ++i) {
+                if (a[i] == mi) p++;
+            }
+            if (ma <= p || ma - p << 1 > n - p) {
+                cout << "No" << endl;
+            } else {
+                cout << "Yes" << endl;
+            }
+        }
+    }
+    return 0;
+}
+```

+ 98 - 0
source/_posts/oi/command-network.md

@@ -0,0 +1,98 @@
+---
+title: Command Network
+tags:
+  - Optimum Branching
+id: "1755"
+categories:
+  - - OI
+    - Graph Theory
+date: 2018-01-16 14:20:56
+---
+
+## 题目描述
+
+After a long lasting war on words, a war on arms finally breaks out between littleken’s and KnuthOcean’s kingdoms. A sudden and violent assault by KnuthOcean’s force has rendered a total failure of littleken’s command network. A provisional network must be built immediately. littleken orders snoopy to take charge of the project.
+
+With the situation studied to every detail, snoopy believes that the most urgent point is to enable littenken’s commands to reach every disconnected node in the destroyed network and decides on a plan to build a unidirectional communication network. The nodes are distributed on a plane. If littleken’s commands are to be able to be delivered directly from a node $A$ to another node $B$, a wire will have to be built along the straight line segment connecting the two nodes. Since it’s in wartime, not between all pairs of nodes can wires be built. snoopy wants the plan to require the shortest total length of wires so that the construction can be done very soon.
+
+## 题意概述
+
+给定平面上的$N$个点,其中$M$对点$(u_i, v_i)$之间可以连一条从$u_i$到$v_i$的有向边。要求使得从$1$号点出发可以到达其他所有点,求边的长度之和的最小值。
+
+数据范围:$1 \le N \le 100, \; 1 \le M \le 10000$。
+
+## 算法分析
+
+最小树形图模板题。主要思想是先给对于每个点选一条最近的邻边,若没有形成环则退出,否则将环缩成一个点,重复以上步骤。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+
+static const int N = 1000;
+
+int n, m, pre[N], id[N], vis[N];
+double in[N];
+
+struct Point {
+  double x, y;
+  Point(double _x = 0, double _y = 0) : x(_x), y(_y) {}
+  Point operator - (const Point &p) const {
+    return Point(x - p.x, y - p.y);
+  }
+  double operator ! () const {
+    return sqrt(x * x + y * y);
+  }
+} p[N];
+
+struct Line {
+  int u, v; double w;
+} l[N * N];
+
+double calc(int root, int v, int e) {
+  double ret = 0;
+  while (1) {
+    for (int i = 0; i < v; ++ i) in[i] = 1e10;
+    for (int i = 0; i < e; ++ i)
+      if (l[i].w < in[l[i].v] && l[i].u != l[i].v)
+        pre[l[i].v] = l[i].u, in[l[i].v] = l[i].w;
+    for (int i = 0; i < v; ++ i)
+      if (i != root && in[i] == 1e10) return -1;
+    int cnt = 0; in[root] = 0;
+    memset(id, -1, sizeof id), memset(vis, -1, sizeof vis);
+    for (int i = 0; i < v; ++ i) {
+      ret += in[i]; int p = i;
+      while (vis[p] != i && ! ~ id[p] && p != root) vis[p] = i, p = pre[p];
+      if (p != root && ! ~ id[p]) {
+        for (int q = pre[p]; q != p; q = pre[q]) id[q] = cnt;
+        id[p] = cnt ++;
+      }
+    }
+    if (! cnt) break;
+    for (int i = 0; i < v; ++ i) if (! ~ id[i]) id[i] = cnt ++;
+    for (int i = 0; i < e; ++ i) {
+      if (id[l[i].u] != id[l[i].v]) l[i].w -= in[l[i].v];
+      l[i].u = id[l[i].u], l[i].v = id[l[i].v];
+    }
+    v = cnt, root = id[root];
+  }
+  return ret;
+}
+
+int main() {
+  while (~ scanf("%d%d", &n, &m)) {
+    for (int i = 0; i < n; ++ i) scanf("%lf%lf", &p[i].x, &p[i].y);
+    for (int i = 0; i < m; ++ i) {
+      scanf("%d%d", &l[i].u, &l[i].v), -- l[i].u, -- l[i].v;
+      if (l[i].u != l[i].v) l[i].w = ! (p[l[i].u] - p[l[i].v]); else l[i].w = 1e10;
+    }
+    double ans = calc(0, n, m);
+    if (ans == -1) printf("poor snoopy\n");
+    else printf("%.2lf\n", ans);
+  }
+  return 0;
+}
+```

+ 109 - 0
source/_posts/oi/complete-the-graph.md

@@ -0,0 +1,109 @@
+---
+title: Complete the Graph
+tags:
+  - Shortest Path
+id: "878"
+categories:
+  - - OI
+    - Graph Theory
+date: 2017-06-30 20:00:00
+---
+
+## 题目描述
+
+ZS the Coder has drawn an undirected graph of $n$ vertices numbered from $0$ to $n-1$ and m edges between them. Each edge of the graph is weighted, each weight is a positive integer.
+
+The next day, ZS the Coder realized that some of the weights were erased! So he wants to reassign positive integer weight to each of the edges which weights were erased, so that the length of the shortest path between vertices $s$ and $t$ in the resulting graph is exactly $L$. Can you help him?
+
+## 题意概述
+
+给定一张连通无向图,其中所有边的权值均为自然数。要求将所有权值为$0$的边的权值变成任意一个不大于$10^{18}$的正整数,使得从$s$到$t$的最短路径的长度为$L$。
+
+数据范围:$2 \le n \le 1000, \; 1 \le m \le 10000, \; 1 \le L \le 10^9, \; 0 \le s, t \le n-1$。
+
+## 算法分析
+
+首先将权值为$0$的边的权值变成无穷大,求一次最短路,若$s$和$t$之间的距离$dist \lt L$则无解。
+
+若$dist=L$,则已得到可行解。
+
+若$dist \gt L$,枚举初始权值为$0$的边,将它的权值变成$1$,求一次最短路,若$dist \le L$则说明已有可行解,把这条边的权值增加$L-dist$,即得到可行解。若所有初始权值为$0$的边都已枚举且$dist \gt L$,则说明无解。
+
+## 代码
+
+```cpp
+#include <iostream>
+#include <queue>
+#include <cstring>
+using namespace std;
+long long n, m, L, s, t, dist[1001], line[1001][1001];
+bool flag, in[1001], sign[1001][1001];
+void spfa(int p = -1, int q = -1) {
+    queue<int> que;
+    while (!que.empty()) que.pop();
+    memset(in, false, sizeof(in));
+    if (p == -1) {
+        memset(dist, 0x1f, sizeof(dist));
+        que.push(s), dist[s] = 0, in[s] = true;
+    } else {
+        if (dist[p] > dist[q]) p ^= q ^= p ^= q;
+        que.push(p), in[p] = true;
+    }
+    while (!que.empty()) {
+        long long u = que.front();
+        que.pop();
+        in[u] = false;
+        for (int i = 0; i < n; ++i) {
+            if (line[u][i] && dist[i] > dist[u] + line[u][i]) {
+                dist[i] = dist[u] + line[u][i];
+                if (!in[i]) que.push(i), in[i] = true;
+            }
+        }
+    }
+}
+int main()
+{
+    cin >> n >> m >> L >> s >> t;
+    for (int i = 1; i <= m; ++i) {
+        long long x, y, w;
+        cin >> x >> y >> w;
+        line[x][y] = line[y][x] = w;
+        if (!w) sign[x][y] = sign[y][x] = true;
+    }
+    spfa();
+    if (dist[t] < L) {
+        cout << "NO" << endl;
+        return 0;
+    } else if (dist[t] > L) {
+        for (int i = 0; i < n - 1; ++i) {
+            for (int j = i + 1; j < n; ++j) {
+                if (sign[i][j]) {
+                    line[i][j] = line[j][i] = 1;
+                    sign[i][j] = sign[j][i] = false;
+                    spfa(i, j);
+                    if (dist[t] <= L) {
+                        line[i][j] = line[j][i] = L - dist[t] + 1;
+                        flag = true; break;
+                    }
+                }
+            }
+            if (flag) break;
+        }
+    } else flag = true;
+    if (!flag) cout << "NO" << endl;
+    else {
+        cout << "YES" << endl;
+        for (int i = 0; i < n - 1; ++i) {
+            for (int j = i + 1; j < n; ++j) {
+                if (line[i][j] || sign[i][j]) {
+                    cout << i << ' ' << j << ' ';
+                    if (line[i][j]) cout << line[i][j];
+                    if (sign[i][j]) cout << (long long) 1e18;
+                    cout << endl;
+                }
+            }
+        }
+    }
+    return 0;
+}
+```

+ 96 - 0
source/_posts/oi/conquer-the-polygon.md

@@ -0,0 +1,96 @@
+---
+title: Conquer the Polygon
+tags:
+  - Topological-Sort
+id: "3620"
+categories:
+  - - OI
+    - Graph Theory
+date: 2020-02-04 21:10:34
+---
+
+## 题目描述
+
+You have a convex polygon. The vertices of the polygon are successively numbered from $1$ to $n$. You also have a triangulation of this polygon, given as a list of $n-3$ diagonals.
+
+There will be $2n-3$ undirected edges in the graph: $n-3$ edges in the triangulation and $n$ edges on the side of the polygon.
+
+You can choose to conquer some vertices, and if you choose to conquer a vertex, you will conquer all the edges linked to this vertex.
+
+Write a program to determine the minimum number of vertices you need to choose such that you can conquer all the edges. Note that you are not required to conquer all the vertices.
+
+## 题意概述
+
+给定一个$n$边形的三角剖分,求它的最小点覆盖。
+
+数据范围:$4 \le n \le 10^5$。
+
+## 算法分析
+
+考虑一个度数为$2$的点:
+
+- 如果这个点已被选择,那么与它相邻的两条边都已被覆盖;
+- 否则,为了覆盖与它相邻的边,最优方案是选择与它相邻的两个点。
+
+于是可以将这个点和与它相邻的边删去,又会得到新的度数为$2$的点。可以用类似拓扑排序的方法来维护。
+
+## 代码
+
+```cpp
+#include <cstdio>
+#include <cstring>
+#include <algorithm>
+
+int const N = 100005;
+int nume, h[N], in[N], used[N], que[N];
+struct Edge {
+    int v, nxt;
+} e[N << 2];
+
+void add_edge(int u, int v) {
+    e[++nume] = (Edge) {v, h[u]};
+    h[u] = nume;
+}
+
+int main() {
+    int n;
+    scanf("%d", &n);
+    for (int i = 1; i <= 2 * n - 3; ++i) {
+        int u, v;
+        scanf("%d%d", &u, &v);
+        add_edge(u, v);
+        add_edge(v, u);
+        ++in[u];
+        ++in[v];
+    }
+    int qb = 0, qe = 0;
+    for (int i = 1; i <= n; ++i) {
+        if (in[i] == 2) {
+            que[qe++] = i;
+        }
+    }
+    for (; qb < qe;) {
+        int cur = que[qb++];
+        if (!used[cur]) {
+            for (int i = h[cur]; i; i = e[i].nxt) {
+                if (in[e[i].v] > 2) {
+                    used[e[i].v] = 1;
+                }
+            }
+        }
+        for (int i = h[cur]; i; i = e[i].nxt) {
+            --in[cur];
+            --in[e[i].v];
+            if (in[e[i].v] == 2) {
+                que[qe++] = e[i].v;
+            }
+        }
+    }
+    int ans = 0;
+    for (int i = 1; i <= n; ++i) {
+        ans += used[i];
+    }
+    printf("%d\n", ans);
+    return 0;
+}
+```

+ 137 - 0
source/_posts/oi/count-on-a-tree-ii.md

@@ -0,0 +1,137 @@
+---
+title: Count on a Tree II
+tags:
+  - Depth-First-Search
+  - Lowest Common Ancestor
+  - Mo's Algorithm
+  - Tree
+id: "2087"
+categories:
+  - [OI, Common Skill]
+  - [OI, Graph Theory]
+date: 2018-02-11 23:55:33
+---
+
+## 题目描述
+
+You are given a tree with $N$ nodes. The tree nodes are numbered from $1$ to $N$. Each node has an integer weight.
+
+We will ask you to perform the following operation:
+
+- $u$ $v$: ask for how many different integers that represent the weight of nodes there are on the path from $u$ to $v$.
+
+## 题意概述
+
+给定一棵$N$个节点的树,每个节点都有权值。有$M$次询问,每次询问给出$u, v$,求从$u$到$v$的路径上有多少种不同的权值。
+
+数据范围:$1 \le N \le 40000, \; 1 \le M \le 10^5$。
+
+## 算法分析
+
+统计权值种类数,容易想到莫队,但这是在一棵树上,所以需要稍微做些转化。
+
+求出树的括号序(DFS,访问和离开节点时均记录),那么每个询问都可以转化为对括号序上某个区间的询问。但是,如果区间内的某个节点出现了两次,那么这个节点不应该对答案产生贡献。因此,算法执行时可以用两个数组维护,一个表示节点的出现次数,另一个表示权值的出现次数。
+
+具体来讲,令$s_i, e_i$分别表示访问、离开节点$i$的时间。假设$s_u \le s_v$。若$u$是$v$祖先,那么就相当于询问区间$[s_u, s_v]$;否则,相当于询问区间$[e_u, s_v]$,但此时$u, v$的 LCA 并没有被计算到答案中,因此需要把它加上。
+
+## 代码
+
+```cpp
+/*
+ * Trap full -- please empty.
+ */
+
+#include <map>
+#include <cstdio>
+#include <cstring>
+#include <algorithm>
+
+template <typename T>
+void read(T &n) {
+  char c; for (; (c = getchar()) < '0' || c > '9'; ) ;
+  for (n = c - '0'; (c = getchar()) >= '0' && c <= '9'; (n *= 10) += c - '0') ;
+}
+
+typedef int const ic;
+typedef long long ll;
+
+static ic N = 40005;
+static ic M = 100005;
+static ic K = 300;
+std::map <int, int> num;
+int nume, h[N], w[N], base[N], seq[N << 1];
+int tim, st[N], ed[N], dep[N], up[N][16];
+int mans, ml, mr, mvis[N], mrec[N], ans[M];
+struct Edge {
+  int v, nxt;
+} e[N << 1];
+struct Query {
+  int l, r, lca, id;
+  bool operator < (const Query &q) const {
+    return l / K == q.l / K ? r < q.r : l / K < q.l / K;
+  }
+} q[M];
+
+void add_edge(ic &u, ic &v) {
+  e[++ nume] = (Edge) { v, h[u] }, h[u] = nume;
+  e[++ nume] = (Edge) { u, h[v] }, h[v] = nume;
+}
+
+void dfs(ic &t, ic &fa) {
+  seq[st[t] = ++ tim] = t, dep[t] = dep[fa] + 1, up[t][0] = fa;
+  for (int i = h[t]; i; i = e[i].nxt)
+    if (e[i].v != fa) dfs(e[i].v, t);
+  seq[ed[t] = ++ tim] = t;
+}
+
+int get_lca(int u, int v) {
+  if (dep[u] > dep[v]) std::swap(u, v);
+  for (int i = 15; ~ i; -- i)
+    if (dep[up[v][i]] >= dep[u]) v = up[v][i];
+  if (u == v) return u;
+  for (int i = 15; ~ i; -- i)
+    if (up[u][i] != up[v][i]) u = up[u][i], v = up[v][i];
+  return up[u][0];
+}
+
+void add(ic &u) {
+  if (mvis[u]) {
+    -- mrec[w[u]], mvis[u] = 0;
+    if (! mrec[w[u]]) -- mans;
+  } else {
+    if (! mrec[w[u]]) ++ mans;
+    ++ mrec[w[u]], mvis[u] = 1;
+  }
+}
+
+void get(ic &l, ic &r) {
+  for (; ml < l;) add(seq[ml ++]);
+  for (; ml > l;) add(seq[-- ml]);
+  for (; mr < r;) add(seq[++ mr]);
+  for (; mr > r;) add(seq[mr --]);
+}
+
+int main() {
+  int n, m; read(n), read(m);
+  for (int i = 1; i <= n; ++ i) {
+    read(w[i]);
+    if (! num.count(w[i])) num[w[i]] = num.size();
+    w[i] = num[w[i]];
+  }
+  for (int i = 1, u, v; i < n; ++ i) read(u), read(v), add_edge(u, v);
+  dfs(1, 0);
+  for (int i = 1; i < 16; ++ i)
+    for (int j = 1; j <= n; ++ j) up[j][i] = up[up[j][i - 1]][i - 1];
+  for (int i = 1, u, v; i <= m; ++ i) {
+    read(u), read(v), q[i].id = i;
+    if (st[u] > st[v]) std::swap(u, v);
+    if (ed[u] > ed[v]) q[i].l = st[u] + 1, q[i].r = st[v], q[i].lca = u;
+    else q[i].l = ed[u], q[i].r = st[v], q[i].lca = get_lca(u, v);
+  }
+  std::sort(q + 1, q + m + 1);
+  mans = ml = mr = mvis[1] = mrec[w[1]] = 1;
+  for (int i = 1; i <= m; ++ i) get(q[i].l, q[i].r), add(q[i].lca), ans[q[i].id] = mans, add(q[i].lca);
+  for (int i = 1; i <= m; ++ i) printf("%d\n", ans[i]);
+  return 0;
+}
+```

+ 143 - 0
source/_posts/oi/counting-divisors-square.md

@@ -0,0 +1,143 @@
+---
+title: Counting Divisors (Square)
+tags:
+  - Dirichlet Convolution
+  - Euler's Sieve
+  - Möbius Inversion Formula
+  - Prime
+id: "1982"
+categories:
+  - - OI
+    - Number Theory
+date: 2018-01-23 08:45:47
+---
+
+## 题目描述
+
+Let $\sigma_0(n)$ be the number of positive divisors of $n$.
+
+For example, $\sigma_0(1)=1, \; \sigma_0(2)=2$ and $\sigma_0(6)=4$.
+
+Let $S_2(n)=\sum_{i=1}^n \sigma_0(i^2)$.
+
+Given $N$, find $S_2(N)$.
+
+## 题意概述
+
+令$\sigma_0(n)$表示$n$的正因数个数,$S_2(n)=\sum_{i=1}^n \sigma_0(i^2)$。给定$N$,求$S_2(N)$。
+
+数据范围:$1 \le N \le 10^{12}$。
+
+## 算法分析
+
+为了方便,我们令$f(n)=\sigma_0(n^2)$,$\omega(n)$表示$n$的不同质因子个数。
+
+设$n=\prod_{k=1}^{\omega(n)} p_k^{a_k}$,其中$p_k$表示不同质数。
+
+先来推$f(n)$。对于$n$的每个因数$d$,将$d$的某些质因子$p_k$的次数加上$a_k$得到$d'$,有$d' \nmid n, \; d' \mid n^2$。这样的方案数有$(2^{\omega(d)}-1)$种,再算上$d$本身,可得
+
+$$
+\begin{align}
+f(n)=\sum_{d \mid n} 2^{\omega(d)}
+\end{align}
+$$
+
+化简$2^{\omega(d)}$。它等价于$d$的每个质因数要么选一个,要么不选。那么它就等于$d$的因数中 square-free 的数的个数。因此
+
+$$
+\begin{align}
+2^{\omega(d)}=\sum_{i \mid d} \mu^2(i)
+\end{align}
+$$
+
+回到原式
+
+$$
+\begin{align}
+S_2(n)=\sum_{i=1}^n \sum_{j \mid i} \sum_{k \mid j} \mu^2(k)
+\end{align}
+$$
+
+还是毫无头绪。我们继续化简$f(n)$。考虑它的卷积形式$f=\mu^2 \ast 1 \ast 1=\mu^2 \ast \sigma_0$。所以
+
+$$
+\begin{align}
+S_2(n)&=\sum_{i=1}^n \sum_{j \mid i} \mu^2(j) \sigma_0\left({i \over j}\right) \\\\
+&=\sum_{ij \le n} \mu^2(i) \sigma_0(j)
+\end{align}
+$$
+
+那么只要用反演求出$\mu^2$的前缀和、用分块求出$\sigma_0$的前缀和,就可以利用分块的技巧了。具体如下
+
+$$
+\begin{align}
+\sum_{i=1}^n \mu^2(i)&=\sum_{i=1}^n [i\text{最大平方因子}=1] \\\\
+&=\sum_{i=1}^n \sum_{j^2 \mid i} \mu(j) \\\\
+&=\sum_{i=1}^{\lfloor \sqrt{n} \rfloor} \mu(i) \left\lfloor {n \over i^2} \right\rfloor \\\\
+\sum_{i=1}^n \sigma_0(i)&=\sum_{i=1}^n \left\lfloor {n \over i} \right\rfloor
+\end{align}
+$$
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+
+void read(long long &t) {
+  char c; while ((c = getchar()) < '0' || c > '9') ; t = c - '0';
+  while ((c = getchar()) >= '0' && c <= '9') (t *= 10) += c - '0';
+}
+
+int N;
+char mui[100000000];
+int d[100000000], sum[100000000], prime[10000000];
+bool vis[100000000];
+long long n, sigma[100000000];
+
+void init() {
+  int top = 0; mui[1] = 1, sigma[1] = 1;
+  for (int i = 2; i < N; ++ i) {
+    if (! vis[i]) prime[top ++] = i, mui[i] = -1, sigma[i] = 2, d[i] = 1;
+    for (int j = 0; j < top; ++ j) {
+      int k = i * prime[j]; if (k >= N) break;
+      vis[k] = true;
+      if (i % prime[j]) mui[k] = -mui[i], sigma[k] = sigma[i] << 1, d[k] = 1;
+      else { mui[k] = 0, sigma[k] = sigma[i] / (d[i] + 1) * (d[i] + 2), d[k] = d[i] + 1; break; }
+    }
+  }
+  for (int i = 1; i < N; ++ i) sum[i] = sum[i - 1] + mui[i] * mui[i], sigma[i] += sigma[i - 1];
+}
+
+inline long long get_mui(long long n) {
+  if (n < N) return sum[n];
+  int q = sqrt(n); long long ret = 0;
+  for (register int i = 1; i <= q; ++ i) if (mui[i]) ret += mui[i] * (n / i / i);
+  return ret;
+}
+
+inline long long get_sigma(long long n) {
+  if (n < N) return sigma[n];
+  long long ret = 0;
+  for (register long long i = 1, j; i <= n; i = j + 1)
+    j = n / (n / i), ret += (j - i + 1) * (n / i);
+  return ret;
+}
+
+long long calc(long long n) {
+  int q = sqrt(n); long long ret = 0;
+  for (register int i = 1; i <= q; ++ i) if (mui[i]) ret += get_sigma(n / i);
+  for (register long long i = q + 1, j, cur, last = sum[q]; i <= n; i = j + 1, last = cur)
+    j = n / (n / i), cur = get_mui(j), ret += (cur - last) * get_sigma(n / i);
+  return ret;
+}
+
+signed main() {
+  long long T; read(T);
+  if (T > 800) N = 20000; else N = 100000000;
+  init();
+  while (T --) read(n), printf("%lld\n", calc(n));
+  return 0;
+}
+```

+ 20 - 0
source/_posts/oi/coupons.md

@@ -0,0 +1,20 @@
+---
+title: Coupons
+tags:
+  - Probability
+id: "226"
+categories:
+  - - OI
+    - Number Theory
+date: 2017-06-07 00:00:00
+---
+
+## 题意概述
+
+一共有$n$种不同的优惠券,每次得到每种优惠券的概率相同。问期望多少次可以得到所有种类的优惠券,以带分数形式输出。
+
+数据范围:$1 \le n \le 33$。
+
+## 算法分析
+
+假设当前已经有$k$种优惠券,那么获得新优惠券的概率是${n-k \over n}$,所以需要步数的期望是${n \over n-k}$。求和得到总步数的期望是${n \over n}+{n \over n-1}+{n \over n-2}+\cdots+{n \over 1}=n\sum_{i=1}^n{1 \over i}$。

+ 70 - 0
source/_posts/oi/cow-program.md

@@ -0,0 +1,70 @@
+---
+title: Cow Program
+tags:
+  - Depth-First-Search
+  - Implementation
+id: "1287"
+categories:
+  - - OI
+    - Common Skill
+date: 2017-07-24 19:55:45
+---
+
+## 题目描述
+
+Farmer John has just given the cows a program to play with! The program contains two integer variables, $x$ and $y$, and performs the following operations on a sequence $a_1, a_2, \ldots, a_n$ of positive integers:
+
+1. Initially, $x=1$ and $y=0$. If, after any step, $x \le 0$ or $x \gt n$, the program immediately terminates.
+2. The program increases both $x$ and $y$ by a value equal to $a_x$ simultaneously.
+3. The program now increases $y$ by $a_x$ while decreasing $x$ by $a_x$.
+4. The program executes steps 2 and 3 (first step 2, then step 3) repeatedly until it terminates (it may never terminate). So, the sequence of executed steps may start with: step 2, step 3, step 2, step 3, step 2 and so on.
+
+The cows are not very good at arithmetic though, and they want to see how the program works. Please help them!
+
+You are given the sequence $a_2, a_3, \ldots, a_n$. Suppose for each $i \; (1 \le i \le n-1)$ we run the program on the sequence $i, a_2, a_3, \ldots, a_n$. For each such run output the final value of $y$ if the program terminates or $-1$ if it does not terminate.
+
+## 题意概述
+
+有一个包含$n$个数的数列,第$i$项为$a_i$。现有两个数$x=1, \; y=0$。执行以下操作
+
+```
+while (!(x <= 0  x > n)) {
+  y += a[x], x += a[x];
+  if (!(x <= 0  x > n)) y += a[x], x -= a[x];
+}
+```
+
+给定$a_2, a_3, \ldots, a_n$,对于所有$i \in [1, n-1]$,求对数列$i, a_2, a_3, \ldots, a_n$执行操作后$y$的值。若无限循环则输出$-1$。
+
+数据范围:$2 \le n \le 2 \times 10^5, \; 1 \le a_i \le 10^9$。
+
+## 算法分析
+
+直接模拟肯定会超时。易知从一个位置开始执行操作的顺序是固定的,因此可以用记忆化搜索,记录下每一个位置向左或向右执行操作后的$y$值。如果搜索到一个已搜索过但还没有得到答案的位置,则返回$-1$。
+
+## 代码
+
+```cpp
+#include <iostream>
+using namespace std;
+long long n, a[300001], ans[300001][2], to[300001][2], vis[300001][2];
+long long dfs(long long t, long long m) {
+  if (ans[t][m]) return ans[t][m];
+  if (vis[t][m]) return ans[t][m] = -1; vis[t][m] = true;
+  if (to[t][m] < 1 || to[t][m] > n) return ans[t][m] = a[t];
+  long long ret = dfs(to[t][m], !m);
+  if (ret == -1) ans[t][m] = -1; else ans[t][m] = a[t] + ret;
+  return ans[t][m];
+}
+int main()
+{
+  cin >> n, vis[1][1] = true;
+  for (int i = 2; i <= n; ++i) cin >> a[i];
+  for (int i = 2; i <= n; ++i) to[i][0] = i - a[i], to[i][1] = i + a[i];
+  for (int i = 2; i <= n; ++i) if (!ans[i][0]) dfs(i, 0);
+  for (int i = 1; i < n; ++i)
+    if (ans[i + 1][0] == -1) cout << -1 << endl;
+    else cout << i + ans[i + 1][0] << endl;
+  return 0;
+}
+```

+ 128 - 0
source/_posts/oi/d-tree.md

@@ -0,0 +1,128 @@
+---
+title: D Tree
+tags:
+  - Centroid Decomposition
+  - Multiplicative Inverse
+  - Tree
+id: "905"
+categories:
+  - [OI, Common Skill]
+  - [OI, Graph Theory]
+  - [OI, Number Theory]
+date: 2017-07-01 16:10:58
+---
+
+## 题目描述
+
+There is a skyscraping tree standing on the playground of Nanjing University of Science and Technology. On each branch of the tree is an integer (The tree can be treated as a connected graph with $N$ vertices, while each branch can be treated as a vertex). Today the students under the tree are considering a problem: Can we find such a chain on the tree so that the multiplication of all integers on the chain (mod $10^6 + 3$) equals to $K$?
+
+Can you help them in solving this problem?
+
+## 题意概述
+
+给定一棵有$N$个节点的树,其中第$i$个节点的权值为$v_i$。求一个字典序最小的点对使得这两点之间路径上的点权之积模$10^6+3$等于$K$。
+
+数据范围:$1 \le N \le 10^5, \; 0 \le K \lt 10^6+3, \; 1 \le v_i \lt 10^6+3$。
+
+## 算法分析
+
+点分治。设当前枚举到的节点为$x$,其第$i$棵子树的根节点为$x_i$。用 map 储存下$x$前$i$棵子树中的节点到$x$儿子的可能的乘积。在计算第$(i+1)$棵子树时,设当前计算的节点到$x_{i+1}$的乘积为$p$,若 map 中存在$q$满足$p \times q \times x \equiv K \pmod {10^6+3}$,即$q \equiv K \times p^{-1} \times x^{-1} \pmod {10^6+3}$,则将这两个节点与当前答案比较,取较优解。
+
+## 代码
+
+```cpp
+#include <cstdio>
+#include <map>
+#include <algorithm>
+#include <cstring>
+#define MOD 1000003
+using namespace std;
+struct edge {
+    int v, nxt;
+} e[200001];
+long long n, k, x, y, root, nume, tot, inv[MOD], h[100001], v[100001], size[100001], f[100001], val[100001];
+bool vis[100001];
+map<long long, int> id;
+void add_edge(int u, int v) {
+    e[++nume].v = v, e[nume].nxt = h[u], h[u] = nume;
+    e[++nume].v = u, e[nume].nxt = h[v], h[v] = nume;
+}
+void get_root(int t, int fa) {
+    size[t] = 1, f[t] = 0;
+    for (int i = h[t]; i; i = e[i].nxt) {
+        if (!vis[e[i].v] && e[i].v != fa) {
+            get_root(e[i].v, t);
+            size[t] += size[e[i].v];
+            f[t] = max(f[t], size[e[i].v]);
+        }
+    }
+    f[t] = max(f[t], tot - size[t]);
+    if (f[t] < f[root]) root = t;
+}
+void update(int a, int b) {
+    if (a > b) swap(a, b);
+    if (a < x) x = a, y = b;
+    else if (a == x && b < y) y = b;
+}
+void get_dist(int t, int fa, int flag) {
+    if (!flag) {
+        if (!id.count(val[t])) id[val[t]] = t;
+        else id[val[t]] = min(id[val[t]], t);
+    } else {
+        if (val[t] * val[root] % MOD == k) {
+            if (t <= x || root <= x) update(t, root);
+        }
+        long long inverse = k * inv[val[t]] % MOD * inv[val[root]] % MOD;
+        if (id.count(inverse)) {
+            if (id[inverse] <= x || t <= x) update(id[inverse], t);
+        }
+    }
+    for (int i = h[t]; i; i = e[i].nxt) {
+        if (!vis[e[i].v] && e[i].v != fa) {
+            if (flag) val[e[i].v] = val[t] * v[e[i].v] % MOD;
+            get_dist(e[i].v, t, flag);
+        }
+    }
+}
+void solve(int t) {
+    vis[t] = true, val[t] = v[t], id.clear();
+    for (int i = h[t]; i; i = e[i].nxt) {
+        if (!vis[e[i].v]) {
+            val[e[i].v] = v[e[i].v];
+            get_dist(e[i].v, t, 1);
+            get_dist(e[i].v, t, 0);
+        }
+    }
+    for (int i = h[t]; i; i = e[i].nxt) {
+        if (!vis[e[i].v]) {
+            root = 0, tot = size[e[i].v];
+            get_root(e[i].v, t);
+            solve(root);
+        }
+    }
+}
+int main()
+{
+    inv[1] = 1;
+    for (int i = 2; i < MOD; ++i) {
+        inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD;
+    }
+    while (scanf("%lld%lld", &n, &k) != -1) {
+        x = y = 1e9, nume = 0;
+        memset(vis, 0, sizeof(vis));
+        memset(h, 0, sizeof(h));
+        for (int i = 1; i <= n; ++i) scanf("%lld", &v[i]);
+        for (int i = 1; i < n; ++i) {
+            int u, v;
+            scanf("%d%d", &u, &v);
+            add_edge(u, v);
+        }
+        tot = f[0] = n, root = 0;
+        get_root(1, 0);
+        solve(root);
+        if (y <= n) printf("%lld %lld\n", x, y);
+        else printf("No solution\n");
+    }
+    return 0;
+}
+```

+ 112 - 0
source/_posts/oi/desert-king.md

@@ -0,0 +1,112 @@
+---
+title: Desert King
+tags:
+  - Fractional Programming
+  - Minimum Spanning Tree
+id: "2172"
+categories:
+  - [OI, Graph Theory]
+  - [OI, Number Theory]
+date: 2018-03-03 22:25:50
+---
+
+## 题目描述
+
+David the Great has just become the king of a desert country. To win the respect of his people, he decided to build channels all over his country to bring water to every village. Villages which are connected to his capital village will be watered. As the dominate ruler and the symbol of wisdom in the country, he needs to build the channels in a most elegant way.
+
+After days of study, he finally figured his plan out. He wanted the average cost of each mile of the channels to be minimized. In other words, the ratio of the overall cost of the channels to the total length must be minimized. He just needs to build the necessary channels to bring water to all the villages, which means there will be only one way to connect each village to the capital.
+
+His engineers surveyed the country and recorded the position and altitude of each village. All the channels must go straight between two villages and be built horizontally. Since every two villages are at different altitudes, they concluded that each channel between two villages needed a vertical water lifter, which can lift water up or let water flow down. The length of the channel is the horizontal distance between the two villages. The cost of the channel is the height of the lifter. You should notice that each village is at a different altitude, and different channels can't share a lifter. Channels can intersect safely and no three villages are on the same line.
+
+As King David's prime scientist and programmer, you are asked to find out the best solution to build the channels.
+
+## 题意概述
+
+空间中有$N$个点。定义点$(x_0, y_0, z_0)$与点$(x_1, y_1, z_1)$之间的距离为$\sqrt{(x_0-x_1)^2+(y_0-y_1)^2}$,在它们之间连边的花费为$|z_0-z_1|$。要使得任意两点之间恰有一条路径,求总花费除以总距离的最小值。
+
+数据范围:$2 \le N \le 1000$。
+
+## 算法分析
+
+假设我们连边的集合为$S$,最小值为$k$。令$l_i$表示边的长度,$c_i$表示边的花费。那么有
+
+$$
+{\sum_{i \in S} c_i \over \sum_{i \in S} l_i} \ge k
+$$
+
+移项得
+
+$$
+\begin{align}
+\sum_{i \in S} c_i &\ge k\sum_{i \in S} l_i \\\\
+\sum_{i \in S} (c_i-kl_i) &\ge 0
+\end{align}
+$$
+
+不管怎么连边,这个不等式都成立。因此只要可以判断是否存在不满足不等式的连边方案,就可以对$k$进行二分。我们给每条边$i$赋权$c_i-kl_i$,那显然最坏情况就是新图的最小生成树。
+
+还有一种基于迭代的方法。在求出最小生成树后,将$k$赋值为这棵最小生成树的总花费除以总距离,继续迭代,直到满足精度要求为止。
+
+最小生成树需要用 Prim 来求,否则容易超时。
+
+## 代码
+
+```cpp
+/*
+ * Good news from afar can bring you a welcome visitor.
+ */
+
+#include <algorithm>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+
+#define sqr(x) (1. * (x) * (x))
+
+static int const N = 1005;
+static double const EPS = 1e-5;
+int fa[N], vis[N], pre[N];
+double dist[N], mp[N][N];
+struct Point {
+  int x, y, z;
+} p[N];
+
+int main() {
+  int n;
+  for (scanf("%d", &n); n; scanf("%d", &n)) {
+    for (int i = 1; i <= n; ++i)
+      scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].z);
+    double ans = 0;
+    for (;;) {
+      for (int i = 1; i <= n; ++i)
+        for (int j = 1; j <= n; ++j)
+          if (i != j)
+            mp[i][j] = abs(p[i].z - p[j].z) -
+                       ans * sqrt(sqr(p[i].x - p[j].x) + sqr(p[i].y - p[j].y));
+      double cost = 0, len = 0;
+      memset(vis, 0, sizeof vis), vis[1] = 1;
+      for (int i = 2; i <= n; ++i)
+        dist[i] = mp[1][i], pre[i] = 1;
+      for (int i = 2; i <= n; ++i) {
+        int pos = 0;
+        double mn = 1e15;
+        for (int j = 1; j <= n; ++j)
+          if (!vis[j] && dist[j] < mn)
+            mn = dist[pos = j];
+        vis[pos] = 1, cost += abs(p[pos].z - p[pre[pos]].z),
+        len +=
+            sqrt(sqr(p[pos].x - p[pre[pos]].x) + sqr(p[pos].y - p[pre[pos]].y));
+        for (int j = 1; j <= n; ++j)
+          if (!vis[j] && mp[pos][j] < dist[j])
+            dist[j] = mp[pos][j], pre[j] = pos;
+      }
+      if (abs(cost / len - ans) < EPS) {
+        printf("%.3lf\n", ans);
+        break;
+      }
+      ans = cost / len;
+    }
+  }
+  return 0;
+}
+```

+ 163 - 0
source/_posts/oi/destiny.md

@@ -0,0 +1,163 @@
+---
+title: Destiny
+tags:
+  - Persistent
+  - Prefix Sum
+  - Segment Tree
+id: "1484"
+categories:
+  - [OI, Common Skill]
+  - [OI, Data Structure]
+date: 2017-09-18 13:30:17
+---
+
+## 题目描述
+
+Once, Leha found in the left pocket an array consisting of $n$ integers, and in the right pocket $q$ queries of the form $l$ $r$ $k$. If there are queries, then they must be answered. Answer for the query is minimal $x$ such that $x$ occurs in the interval $l$ $r$ strictly more than ${r-l+1 \over k}$ times or $-1$ if there is no such number. Help Leha with such a difficult task.
+
+## 题意概述
+
+给定包含$n$个数的序列,有$q$次询问,每次询问给定$l, r, k$,求区间$[l, r]$中出现次数严格大于${r-l+1 \over k}$的最小数字。
+
+数据范围:$1 \le n, q \le 3 \times 10^5, \; 1 \le a_i \le n, \; 2 \le k \le 5$。
+
+## 算法分析
+
+建主席树,第$i$个位置上的线段树是前$i$个数的权值线段树。这样就可以用前缀和思想快速求出区间$[l, r]$中某个数出现的次数。对于每个询问,用第$r$个位置上线段树的值减去第$(l-1)$个位置上线段树的值,直接在主席树上暴力查找。由于$k$比较小,查找复杂度不会太高。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+
+using namespace std;
+
+struct IOManager {
+  template <typename T>
+  inline bool read(T &x) {
+    char c; bool flag = false; x = 0;
+    while (~ c && ! isdigit(c = getchar()) && c != '-') ;
+    c == '-' && (flag = true, c = getchar());
+    if (! ~ c) return false;
+    while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar();
+    return (flag && (x = -x), true);
+  }
+  inline bool read(char &c) {
+    c = '\n';
+    while (~ c && ! (isprint(c = getchar()) && c != ' ')) ;
+    return ~ c;
+  }
+  inline int read(char s[]) {
+    char c; int len = 0;
+    while (~ c && ! (isprint(c = getchar()) && c != ' ')) ;
+    if (! ~ c) return 0;
+    while (isprint(c) && c != ' ') s[len ++] = c, c = getchar();
+    return (s[len] = '\0', len);
+  }
+  template <typename T>
+  inline IOManager r(T &x) {
+    read(x); return *this;
+  }
+  template <typename T>
+  inline void write(T x) {
+    x < 0 && (putchar('-'), x = -x);
+    x > 9 && (write(x / 10), true);
+    putchar(x % 10 + '0');
+  }
+  inline void write(char c) {
+    putchar(c);
+  }
+  inline void write(char s[]) {
+    int pos = 0;
+    while (s[pos] != '\0') putchar(s[pos ++]);
+  }
+  template <typename T>
+  inline IOManager w(T x) {
+    write(x); return *this;
+  }
+} io;
+
+struct SegmentTree {
+private:
+  static const int N = 9000000;
+  int tot;
+  struct Node {
+    int val, child[2];
+  } node[N];
+  int add_point(int root, int l, int r, int p) {
+    if (l == r) {
+      node[++ tot] = (Node) { node[root].val, 0, 0 };
+      ++ node[tot].val; return tot;
+    }
+    int mid = l + r >> 1, rec = ++ tot;
+    node[rec] = (Node) { 0, 0, 0 };
+    if (p <= mid) {
+      node[rec].child[1] = node[root].child[1];
+      node[rec].child[0] = add_point(node[root].child[0], l, mid, p);
+    } else {
+      node[rec].child[0] = node[root].child[0];
+      node[rec].child[1] = add_point(node[root].child[1], mid + 1, r, p);
+    }
+    node[rec].val = node[node[rec].child[0]].val + node[node[rec].child[1]].val;
+    return rec;
+  }
+  int query_point(int root1, int root2, int l, int r, int p) {
+    if (node[root2].val - node[root1].val < p) return -1;
+    if (l == r) return node[root2].val - node[root1].val >= p ? l : -1;
+    int mid = l + r >> 1, res = -1;
+    if (node[node[root2].child[0]].val - node[node[root1].child[0]].val >= p)
+      res = query_point(node[root1].child[0], node[root2].child[0], l, mid, p);
+    if (~ res) return res;
+    res = query_point(node[root1].child[1], node[root2].child[1], mid + 1, r, p);
+    return res;
+  }
+
+public:
+  int n, cnt, root[N];
+  void add(int p) {
+    root[cnt + 1] = add_point(root[cnt], 1, n, p), ++ cnt;
+  }
+  int find(int l, int r, int p) {
+    return query_point(root[l - 1], root[r], 1, n, p);
+  }
+  void print() {
+    cout << cnt << endl;
+    for (int i = 1; i <= cnt; ++ i) cout << root[i] << ' ';
+    cout << endl << tot << endl;
+    for (int i = 1; i <= tot; ++ i) cout << i << ' ' << node[i].val << ' ' << node[i].child[0] << ' ' << node[i].child[1] << endl;
+  }
+};
+
+struct Solver {
+private:
+  int n, q;
+  SegmentTree tree;
+  void input() {
+    io.r(n).r(q);
+    tree.n = n;
+    for (int i = 1; i <= n; ++ i) {
+      int t; io.read(t), tree.add(t);
+    }
+  }
+  void process() {
+    int l, r, k; io.r(l).r(r).r(k);
+    int p = (r - l + 1) / k + 1;
+    io.w(tree.find(l, r, p)).w('\n');
+  }
+
+public:
+  void solve() {
+    input(); while (q --) process();
+  }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 140 - 0
source/_posts/oi/digit-tree.md

@@ -0,0 +1,140 @@
+---
+title: Digit Tree
+tags:
+  - Centroid Decomposition
+  - Multiplicative Inverse
+  - Tree
+id: "943"
+categories:
+  - [OI, Common Skill]
+  - [OI, Graph Theory]
+  - [OI, Number Theory]
+date: 2017-07-02 10:20:27
+---
+
+## 题目描述
+
+ZS the Coder has a large tree. It can be represented as an undirected connected graph of $n$ vertices numbered from $0$ to $n-1$ and $n-1$ edges between them. There is a single nonzero digit written on each edge.
+
+One day, ZS the Coder was bored and decided to investigate some properties of the tree. He chose a positive integer $M$, which is coprime to $10$, i.e. $(M, 10)=1$.
+
+ZS consider an ordered pair of distinct vertices $(u, v)$ interesting when if he would follow the shortest path from vertex $u$ to vertex $v$ and write down all the digits he encounters on his path in the same order, he will get a decimal representaion of an integer divisible by $M$.
+
+Formally, ZS consider an ordered pair of distinct vertices $(u, v)$ interesting if the following states true:
+
+- let $a_1=u, a_2, \ldots, a_k=v$ be the sequence of vertices on the shortest path from $u$ to $v$ in the order of encountering them;
+- let $d_i (1 \le i \lt k)$ be the digit written on the edge between vertices $a_i$ and $a_i+1$;
+- the integer $\overline{d_1d_2 \ldots d_{k-1}}=\sum_{i=1}^{k-1} 10^{k-1-i}d_i$ is divisible by $M$.
+
+Help ZS the Coder find the number of interesting pairs!
+
+## 题意概述
+
+给定一棵有$n$个节点的树和一个与$10$互质的数$M$,树上每条边的权值都是小于$10$的正整数。定义$dist_{u, v}$为依次写下从$u$到$v$路径上每条边的权值所得到的数字。求满足$M \mid dist_{u, v}$的点对个数。
+
+数据范围:$2 \le n \le 10^5, \; 1 \le M \le 10^9$。
+
+## 算法分析
+
+设当前枚举到的节点为$x$。令$depth_u$表示$u$在$x$及它子树中的深度。对于在$x$第$(i+1)$棵子树中的节点$u$和在前$i$棵子树中的节点$v$,有:
+
+$$
+\begin{align}
+M \mid dist_{u, v} \Leftrightarrow 10^{depth_v}dist_{u, x}+dist_{x, v} \equiv 0 \pmod M \\\\
+M \mid dist_{v, u} \Leftrightarrow 10^{depth_u}dist_{v, x}+dist_{x, u} \equiv 0 \pmod M
+\end{align}
+$$
+
+对于$(1)$式,化简得$dist_{u, x} \equiv -10^{-depth_v}dist_{x, v} \pmod M$;对于$(2)$式,化简得$10^{-depth_u}dist_{x, u} \equiv -dist_{v, x} \pmod M$。用两个 map 分别存下前$i$棵子树中$10^{-depth_v}dist_{x, v}$和$dist_{v, x}$的值,在处理第$(i+1)$棵子树时直接加上可行的方案数。
+
+## 代码
+
+```cpp
+#include <cstdio>
+#include <map>
+#include <algorithm>
+using namespace std;
+struct edge {
+    int v, w, nxt;
+} e[200001];
+long long n, m, ans, nume, tot, root, h[100001], size[100001], f[100001];
+long long val1[100001], val2[100001], power[100001], inv[100001];
+bool vis[100001];
+map<long long, int> id1, id2;
+void extend_gcd(int a, int b, int &x, int &y) {
+    if (!b) { x = 1, y = 0; return; }
+    extend_gcd(b, a % b, y, x);
+    y -= a / b * x;
+}
+void add_edge(int u, int v, int w) {
+    e[++nume].v = v, e[nume].w = w, e[nume].nxt = h[u], h[u] = nume;
+    e[++nume].v = u, e[nume].w = w, e[nume].nxt = h[v], h[v] = nume;
+}
+void get_root(int t, int fa) {
+    size[t] = 1, f[t] = 0;
+    for (int i = h[t]; i; i = e[i].nxt) {
+        if (!vis[e[i].v] && e[i].v != fa) {
+            get_root(e[i].v, t);
+            size[t] += size[e[i].v];
+            f[t] = max(f[t], size[e[i].v]);
+        }
+    }
+    f[t] = max(f[t], tot - size[t]);
+    if (f[t] < f[root]) root = t;
+}
+void get_dist(int t, int fa, int flag, int depth) {
+    if (!flag) ++id1[val1[t]], ++id2[val2[t] * inv[depth] % m];
+    else {
+        ans += !val1[t] + !val2[t];
+        ans += id1[(val2[t] ? m - val2[t] : 0) * inv[depth] % m];
+        ans += id2[val1[t] ? m - val1[t] : 0];
+    }
+    for (int i = h[t]; i; i = e[i].nxt) {
+        if (!vis[e[i].v] && e[i].v != fa) {
+            if (flag) {
+                val1[e[i].v] = (val1[t] + e[i].w * power[depth]) % m;
+                val2[e[i].v] = (val2[t] * 10 + e[i].w) % m;
+            }
+            get_dist(e[i].v, t, flag, depth + 1);
+        }
+    }
+}
+void solve(int t) {
+    vis[t] = true, id1.clear(), id2.clear();
+    for (int i = h[t]; i; i = e[i].nxt) {
+        if (!vis[e[i].v]) {
+            val1[e[i].v] = val2[e[i].v] = e[i].w % m;
+            get_dist(e[i].v, t, 1, 1);
+            get_dist(e[i].v, t, 0, 1);
+        }
+    }
+    for (int i = h[t]; i; i = e[i].nxt) {
+        if (!vis[e[i].v]) {
+            root = n, tot = size[e[i].v];
+            get_root(e[i].v, t);
+            solve(root);
+        }
+    }
+}
+int main()
+{
+    scanf("%lld%lld", &n, &m);
+    power[0] = 1;
+    for (int i = 1; i <= n; ++i) power[i] = power[i - 1] * 10 % m;
+    for (int i = 0; i <= n; ++i) {
+        int x, y;
+        extend_gcd(power[i], m, x, y);
+        inv[i] = (x % m + m) % m;
+    }
+    for (int i = 1; i < n; ++i) {
+        int u, v, w;
+        scanf("%d%d%d", &u, &v, &w);
+        add_edge(u, v, w);
+    }
+    tot = f[n] = n, root = n;
+    get_root(0, n);
+    solve(root);
+    printf("%lld\n", ans);
+    return 0;
+}
+```

+ 74 - 0
source/_posts/oi/digital-root.md

@@ -0,0 +1,74 @@
+---
+title: Digital Root
+tags:
+  - Digital Root
+id: "1547"
+categories:
+  - - OI
+    - Number Theory
+date: 2017-10-18 21:00:12
+---
+
+## 题目描述
+
+Let $f(n)$ be a sum of digits for positive integer $n$. If $f(n)$ is one-digit number then it is a digital root for $n$ and otherwise digital root of $n$ is equal to digital root of $f(n)$. For example, digital root of $987$ is $6$. Your task is to find digital root for expression $\sum_{i=1}^N \prod_{j=1}^i A_j$.
+
+## 题意概述
+
+给定一个长度为$N$的数列$A_i$,求$\sum_{i=1}^N \prod_{j=1}^i A_j$的数根。
+
+数据范围:$1 \le N \le 1000, \; 1 \le A_i \le 10^9$。
+
+## 算法分析
+
+数根有一个有趣的性质:如果$n$等于$0$,那么它的数根就是$0$,否则,它的数根等于$(n-1)\%9+1$。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+
+using namespace std;
+
+struct IOManager {
+  template <typename T> inline bool read(T &x) { char c = '\n'; bool flag = false; x = 0; while (~ c && ! isdigit(c = getchar()) && c != '-') ; c == '-' && (flag = true, c = getchar()); if (! ~ c) return false; while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar(); return (flag && (x = -x), true); }
+  inline bool read(char &c) { c = '\n'; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; return ~ c; }
+  inline int read(char s[]) { char c = '\n'; int len = 0; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; if (! ~ c) return 0; while (isprint(c) && c != ' ') s[len ++] = c, c = getchar(); return (s[len] = '\0', len); }
+  template <typename T> inline IOManager operator > (T &x) { read(x); return *this; }
+  template <typename T> inline void write(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
+  inline void write(char c) { putchar(c); }
+  inline void write(char s[]) { int pos = 0; while (s[pos] != '\0') putchar(s[pos ++]); }
+  template <typename T> inline IOManager operator < (T x) { write(x); return *this; }
+} io;
+
+struct Solver {
+private:
+  static const int N = 1010;
+  int n, a[N];
+  void input() { io > n; }
+  void process() {
+    int k; io > k;
+    for (int i = 1; i <= k; ++ i) io > a[i], a[i] %= 9;
+    int ans = 9;
+    for (int i = 1; i <= k; ++ i) {
+      int mul = 1;
+      for (int j = 1; j <= i; ++ j) (mul *= a[j]) %= 9;
+      ans += mul;
+    }
+    io < (ans - 1) % 9 + 1 < '\n';
+  }
+
+public:
+  void solve() { input(); while (n --) process(); }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 143 - 0
source/_posts/oi/divisibility.md

@@ -0,0 +1,143 @@
+---
+title: Divisibility
+tags:
+  - GCD-LCM
+id: "3084"
+categories:
+  - - OI
+    - Number Theory
+date: 2018-05-05 13:00:20
+---
+
+## 题目描述
+
+Inspired by Stephen Graham, the King of Berland started to study algorithms on strings. He was working days and nights, having a feeling that the full potential in this area is still to be unlocked. And he was right!
+
+One day, all the sudden, he made a huge breakthrough by discovering the fact that strings can be magically transformed into integer numbers. It was so simple! You just have to map different letters to different digits and be careful enough not to introduce any leading zeroes.
+Here is what he wrote in his textbook about the string 'lalala':
+
+- it can be transformed to an $282828$ by mapping 'l' to $2$, and 'a' to $8$,
+- it can also be transformed to $909090$ by mapping 'l' to $9$, and 'a' to $0$,
+- acouple of examples of invalid transformations are $050505$ (the resulting number has a leading zero), $333333$ (different letters are mapped to the same digit), $123456$ (no mapping to the original letters at all).
+
+But then things started to become more interesting. Obviously, it was known from very beginning that a single string can potentially be mapped to a variety of different integer numbers. But the King couldn't even imagine that all numbers produced by the same string pattern might have common properties!
+
+For example, every single number that can be produced from string 'lalala' is always divisible by $259$, irrespective of the letter-to-digit mapping you choose. Fascinating!
+
+So the King ended up with the following problem. For any given string, he wanted to come up with an algorithm to calculate the set of its divisors. A number is called a divisor of the given string if all positive integers, that could possibly be produced from the given string, are divisible by it.
+
+As usual, the King desperately wants you to help him, so stop thinking and start acting!
+
+## 题意概述
+
+给定一个字符集大小不超过$10$的字符串$s$。定义一个数字是合法的,当且仅当它没有前导零,位数等于$|s|$,且它从高到低的第$i$位和第$j$位相等当且仅当$s_i=s_j$。求出所有能整除每一个合法数字的数。有$n$组数据。
+
+数据范围:$1 \le n \le 100, \; 1 \le |s| \le 14$。
+
+## 算法分析
+
+如果我们求出了所有合法数字的 GCD,那么答案就是 GCD 的所有约数。
+
+为了方便,我们设字符串的下标从$1$开始。
+
+令$g$等于所有合法数字的 GCD。首先考虑它的下界。假设字符$i$在字符串中出现的次数为$a_i$,出现的位置是$p_{i, 1}, p_{i, 2}, \ldots, p_{i, a_i}$。定义字符$i$的价值$v_i=\sum_{j=1}^{a_i} 10^{|s|-p_{i, j}}$。显然,所有合法的数字都可以被表示为$\sum k_iv_i$,其中$k_i$表示字符$i$变成的数字。所以,$g$的下界为$(v_a, v_b, \ldots, v_z)$。
+
+考虑$g$的上界。分两种情况讨论。
+
+- **引理** $\forall a \le b, \; (a, b) \mid b-a$。
+
+当字符集大小小于$10$时,对于每一个$i$,总能找到一个合法的数$x$使得$x+v_i$也合法。这是因为总有一个合法的数包含$k_i$但不包含$k_i+1$。根据**引理**,$(x, x+v_i) \mid v_i$。由于$x$和$x+v_i$均合法,因此$g \mid (x, x+v_i)$,即$g \mid v_i$。所以,$g$的上界为$(v_a, v_b, \ldots, v_z)$。结合下界,$g=(v_a, v_b, \ldots, v_z)$。
+
+当字符集大小等于$10$时,要将某个$k_i$加一就必须将另一个$k_j$减一。如果对于不同的$i, j$,有$v_i, v_j \gt 0 \land v_i \lt v_j$,那么$g \mid v_j-v_i$。令$S=\{v_j-v_i \mid v_i, v_j \gt 0 \land v_i \lt v_j\}$。对于一个合法的数,它加上/减去$S$中的某些数后仍是一个合法的数;对于$S$中的数,存在一个合法的数加上/减去它之后仍是一个合法的数。即一个合法的数$x$与$S$中的数加减可以得到所有合法的数。令$h$等于$S$中所有数的 GCD,那么显然,$g$的下界为$(x, h)$。又因为$g \mid x$且$g$整除$S$中的所有数,所以$g$的上界也是$(x, h)$。即$g=(x, h)$。
+
+求$g$的所有约数可以先分解质因数再 DFS。
+
+## 代码
+
+```cpp
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+
+static int const L = 15;
+static int const M = 10000005;
+char s[L];
+int top, cnt, cc, p[M];
+long long v[26], dv[L], dc[L], ans[M];
+
+long long get_gcd(long long a, long long b) {
+  for (long long c; b; c = a % b, a = b, b = c)
+    ;
+  return a;
+}
+
+void init() {
+  top = 0;
+  for (int i = 2; i < M; ++i) {
+    if (!p[i])
+      p[top++] = i;
+    for (int j = 0; j < top; ++j) {
+      int k = i * p[j];
+      if (k >= M)
+        break;
+      p[k] = 1;
+      if (!(i % p[j]))
+        break;
+    }
+  }
+}
+
+void dfs(int t, long long v) {
+  if (t == cnt)
+    return void(ans[cc++] = v);
+  for (int i = 0; i <= dc[t]; ++i, v *= dv[t])
+    dfs(t + 1, v);
+}
+
+int main() {
+  int n;
+  scanf("%d", &n), init();
+  for (int _ = 1; _ <= n; ++_) {
+    printf("Case %d:", _), scanf("%s", s);
+    int len = strlen(s), st = 0;
+    for (int c = 0; c < 26; ++c) {
+      v[c] = 0;
+      for (int i = 0; i < len; ++i) {
+        v[c] *= 10;
+        if (s[i] == c + 'a')
+          ++v[c];
+      }
+      st += v[c] > 0;
+    }
+    long long gcd = 0;
+    if (st < 10)
+      for (int i = 0; i < 26; ++i)
+        gcd = get_gcd(gcd, v[i]);
+    else {
+      int k = 0;
+      for (int i = 0; i < 26; ++i)
+        if (v[i])
+          gcd += k++ * v[i];
+      for (int i = 0; i < 26; ++i)
+        for (int j = 0; j < 26; ++j)
+          if (v[i] && v[j] && v[i] < v[j])
+            gcd = get_gcd(gcd, v[j] - v[i]);
+    }
+    cnt = cc = 0;
+    for (int i = 0; i < top && 1ll * p[i] * p[i] <= gcd; ++i)
+      if (!(gcd % p[i])) {
+        dv[cnt] = p[i], dc[cnt] = 0;
+        for (; !(gcd % p[i]);)
+          gcd /= p[i], ++dc[cnt];
+        ++cnt;
+      }
+    if (gcd > 1)
+      dv[cnt] = gcd, dc[cnt] = 1, ++cnt;
+    dfs(0, 1), std::sort(ans, ans + cc);
+    for (int i = 0; i < cc; ++i)
+      printf(" %lld", ans[i]);
+    puts("");
+  }
+  return 0;
+}
+```

+ 94 - 0
source/_posts/oi/dna-evolution.md

@@ -0,0 +1,94 @@
+---
+title: DNA Evolution
+tags:
+  - Binary Indexed Tree
+id: "1177"
+categories:
+  - - OI
+    - Data Structure
+date: 2017-07-15 10:00:38
+---
+
+## 题目描述
+
+Everyone knows that DNA strands consist of nucleotides. There are four types of nucleotides: "A", "T", "G", "C". A DNA strand is a sequence of nucleotides. Scientists decided to track evolution of a rare species, which DNA strand was string $s$ initially.
+
+Evolution of the species is described as a sequence of changes in the DNA. Every change is a change of some nucleotide, for example, the following change can happen in DNA strand "AAGC": the second nucleotide can change to "T" so that the resulting DNA strand is "ATGC".
+
+Scientists know that some segments of the DNA strand can be affected by some unknown infections. They can represent an infection as a sequence of nucleotides. Scientists are interested if there are any changes caused by some infections. Thus they sometimes want to know the value of impact of some infection to some segment of the DNA. This value is computed as follows:
+
+- Let the infection be represented as a string $e$, and let scientists be interested in DNA strand segment starting from position $l$ to position $r$, inclusive.
+- Prefix of the string $eee$... (i.e. the string that consists of infinitely many repeats of string $e$) is written under the string $s$ from position $l$ to position $r$, inclusive.
+- The value of impact is the number of positions where letter of string $s$ coincided with the letter written under it.
+
+Being a developer, Innokenty is interested in bioinformatics also, so the scientists asked him for help. Innokenty is busy preparing VK Cup, so he decided to delegate the problem to the competitors. Help the scientists!
+
+## 题意概述
+
+给定一个只包含 ATGC 四种字符的字符串$s$。有两种操作:① 修改$s$某一位的字符;② 求以字符串$e$为循环节的无限长循环串与$s$中$[l, r]$这个区间的字符串有几个对应位置的字符相等。操作有$q$次。
+
+数据范围:$1 \le |s|, q \le 10^5, \; 1 \le |e| \le 10$。
+
+## 算法分析
+
+由于$e$的长度最多是$10$,因此可以考虑在模$|e|$意义下的字符数。令$f_{i, j, k}$表示在模$i$意义下,模$i$余$j$的字符$k$个数的前缀和。这可以用树状数组维护。修改和查询时只需在对应的树状数组上进行操作。
+
+## 代码
+
+```cpp
+#include <cstdio>
+#include <vector>
+#include <iostream>
+using namespace std;
+struct binary_indexed_tree {
+  int n;
+  vector<int> a;
+  void init(int t) { n = t + 10, a.clear(), a.resize(n); }
+  void add(int p, int t) { for (int i = p; i < n; i += i & -i) a[i] += t; }
+  int sum(int p) {
+    int ret = 0; if (p <= 0) return 0;
+    for (int i = p; i; i -= i & -i) ret += a[i];
+    return ret;
+  }
+} tree[11][10][4];
+int get_type(char c) {
+  if (c == 'A') return 0;
+  else if (c == 'T') return 1;
+  else if (c == 'G') return 2;
+  else if (c == 'C') return 3;
+  return -1;
+}
+int n, c, l, r, len, oper, ans;
+string s, t;
+int main()
+{
+  cin >> s; len = s.length();
+  for (int i = 0; i <= 10; ++i)
+    for (int j = 0; j < 10; ++j)
+      for (int k = 0; k < 4; ++k)
+        tree[i][j][k].init(len);
+  for (int i = 0; i < len; ++i)
+    for (int j = 1; j <= 10; ++j)
+      tree[j][(i + 1) % j][get_type(s[i])].add(i + 1, 1);
+  scanf("%d", &n);
+  while (n--) {
+    scanf("%d", &oper);
+    if (oper == 1) {
+      scanf("%d", &c); cin >> t;
+      for (int i = 1; i <= 10; ++i) {
+        tree[i][c % i][get_type(s[c - 1])].add(c, -1);
+        tree[i][c % i][get_type(t[0])].add(c, 1);
+      }
+      s[c - 1] = t[0];
+    } else {
+      scanf("%d%d", &l, &r); cin >> t;
+      len = t.length(), ans = 0;
+      for (int i = 0; i < len; ++i) {
+        ans += tree[len][(l + i) % len][get_type(t[i])].sum(r) - tree[len][(l + i) % len][get_type(t[i])].sum(l - 1);
+      }
+      printf("%d\n", ans);
+    }
+  }
+  return 0;
+}
+```

+ 114 - 0
source/_posts/oi/dna-sequence.md

@@ -0,0 +1,114 @@
+---
+title: DNA Sequence
+tags:
+  - Aho-Corasick Automaton
+  - Exponentiation by Squaring
+id: "1750"
+categories:
+  - [OI, Common Skill]
+  - [OI, String]
+date: 2018-01-15 18:00:02
+---
+
+## 题目描述
+
+It's well known that DNA Sequence is a sequence only contains A, C, T and G, and it's very useful to analyze a segment of DNA Sequence,For example, if a animal's DNA sequence contains segment ATC then it may mean that the animal may have a genetic disease. Until now scientists have found several those segments, the problem is how many kinds of DNA sequences of a species don't contain those segments.
+
+Suppose that DNA sequences of a species is a sequence that consist of A, C, T and G, and the length of sequences is a given integer $n$.
+
+## 题意概述
+
+给定$m$个长度不超过$10$的 DNA 片段,问所有长度为$n$的 DNA 序列中有几个不包含所有给定的 DNA 片段。
+
+数据范围:$0 \le m \le 10, \; 1 \le n \le 2 \times 10^9$。
+
+## 算法分析
+
+对$m$个 DNA 片段建立 AC 自动机。令$f_{i, j}$表示长度为$i$的 DNA 序列在自动机上跑到节点$j$的方案数。转移可以用一个矩阵来表示,因此可以用矩阵快速幂来加速转移。
+
+## 代码
+
+```cpp
+#include <cstdio>
+#include <cstring>
+
+static const int N = 11;
+static const int POOL = 105;
+char s[N];
+
+int get_id(char c) {
+  return c == 'A' ? 0 : c == 'C' ? 1 : c == 'T' ? 2 : 3;
+}
+
+typedef int Array[POOL][POOL];
+
+int numn = 1, que[POOL];
+Array mp, a, tmp;
+struct ACAutomaton {
+  int nxt[4], fail;
+  bool match;
+} node[POOL];
+
+void insert(char s[]) {
+  int root = 1, len = strlen(s);
+  for (int i = 0; i < len; ++ i) {
+    int p = get_id(s[i]);
+    if (! node[root].nxt[p]) node[root].nxt[p] = ++ numn;
+    root = node[root].nxt[p];
+  }
+  node[root].match = true;
+}
+
+void build() {
+  int qb = 0, qe = 1; que[0] = 1;
+  while (qb < qe) {
+    int u = que[qb ++];
+    for (int i = 0; i < 4; ++ i)
+      if (node[u].nxt[i]) {
+        node[node[u].nxt[i]].fail = 1;
+        if (u > 1) {
+          int root = node[u].fail;
+          while (root > 1 && ! node[root].nxt[i]) root = node[root].fail;
+          if (node[root].nxt[i]) {
+            node[node[u].nxt[i]].fail = node[root].nxt[i];
+            if (node[node[root].nxt[i]].match) node[node[u].nxt[i]].match = true;
+          }
+        }
+        que[qe ++] = node[u].nxt[i];
+      }
+  }
+}
+
+void mul(Array a, Array b) {
+  memset(tmp, 0, sizeof tmp);
+  for (int i = 1; i <= numn; ++ i)
+    for (int j = 1; j <= numn; ++ j)
+      if (a[i][j]) for (int k = 1; k <= numn; ++ k) (tmp[i][k] += 1ll * a[i][j] * b[j][k] % 100000) %= 100000;
+  memcpy(a, tmp, sizeof tmp);
+}
+
+void power(Array a, int b) {
+  while (b) {
+    if (b & 1) mul(a, mp);
+    mul(mp, mp), b >>= 1;
+  }
+}
+
+int main() {
+  int m, n; scanf("%d%d", &m, &n);
+  for (int i = 0; i < m; ++ i) scanf(" %s", s), insert(s);
+  build();
+  for (int i = 1; i <= numn; ++ i) if (! node[i].match)
+    for (int j = 0; j < 4; ++ j) {
+      int root = i;
+      while (root > 1 && ! node[root].nxt[j]) root = node[root].fail;
+      if (node[root].nxt[j]) ++ mp[i][node[root].nxt[j]]; else ++ mp[i][root];
+    }
+  for (int i = 1; i <= numn; ++ i) a[i][i] = 1;
+  power(a, n);
+  int ans = 0;
+  for (int i = 1; i <= numn; ++ i) if (! node[i].match) (ans += a[1][i]) %= 100000;
+  printf("%d\n", ans);
+  return 0;
+}
+```

+ 146 - 0
source/_posts/oi/domino.md

@@ -0,0 +1,146 @@
+---
+title: Domino
+tags:
+  - Constructive Algorithm
+  - Depth-First-Search
+  - Euler Path
+id: "1515"
+categories:
+  - [OI, Common Skill]
+  - [OI, Graph Theory]
+date: 2017-10-12 08:45:18
+---
+
+## 题目描述
+
+Dominoes - game played with small, rectangular blocks of wood or other material, each identified by a number of dots, or pips, on its face. The blocks usually are called bones, dominoes, or pieces and sometimes men, stones, or even cards.
+
+The face of each piece is divided, by a line or ridge, into two squares, each of which is marked as would be a pair of dice...
+
+The principle in nearly all modern dominoes games is to match one end of a piece to another that is identically or reciprocally numbered.
+
+ENCYCLOPÆDIA BRITANNICA
+
+Given a set of domino pieces where each side is marked with two digits from $0$ to $6$. Your task is to arrange pieces in a line such way, that they touch through equal marked sides. It is possible to rotate pieces changing left and right side.
+
+## 题意概述
+
+有$N$张多米诺骨牌,每张骨牌的两个面上各有一个数。现需将骨牌排成一条直线,且要求相邻骨牌上相邻的两个数相同。所有数字在$0$到$6$之间。给出一组方案。
+
+数据范围:$1 \le N \le 100$。
+
+## 算法分析
+
+将$0$到$6$看成点,骨牌看成边,就变成了经典的欧拉路径问题。直接 DFS 即可。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+#include <vector>
+
+using namespace std;
+
+struct IOManager {
+  template <typename T>
+  inline bool read(T &x) {
+    char c; bool flag = false; x = 0;
+    while (~ c && ! isdigit(c = getchar()) && c != '-') ;
+    c == '-' && (flag = true, c = getchar());
+    if (! ~ c) return false;
+    while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar();
+    return (flag && (x = -x), true);
+  }
+  inline bool read(char &c) {
+    c = '\n';
+    while (~ c && ! (isprint(c = getchar()) && c != ' ')) ;
+    return ~ c;
+  }
+  inline int read(char s[]) {
+    char c; int len = 0;
+    while (~ c && ! (isprint(c = getchar()) && c != ' ')) ;
+    if (! ~ c) return 0;
+    while (isprint(c) && c != ' ') s[len ++] = c, c = getchar();
+    return (s[len] = '\0', len);
+  }
+  template <typename T>
+  inline IOManager operator > (T &x) {
+    read(x); return *this;
+  }
+  template <typename T>
+  inline void write(T x) {
+    x < 0 && (putchar('-'), x = -x);
+    x > 9 && (write(x / 10), true);
+    putchar(x % 10 + '0');
+  }
+  inline void write(char c) {
+    putchar(c);
+  }
+  inline void write(char s[]) {
+    int pos = 0;
+    while (s[pos] != '\0') putchar(s[pos ++]);
+  }
+  template <typename T>
+  inline IOManager operator < (T x) {
+    write(x); return *this;
+  }
+} io;
+
+struct Solver {
+private:
+  static const int N = 110;
+  static const int C = 10;
+  int n, cnt, mp[C][C], in[C];
+  bool vis[N];
+  pair <int, int> line[N];
+  vector <pair <int, int> > ans;
+  void input() {
+    io > n;
+    for (int i = 1; i <= n; ++ i) {
+      io > line[i].first > line[i].second;
+      ++ mp[line[i].first][line[i].second], ++ mp[line[i].second][line[i].first];
+      ++ in[line[i].first], ++ in[line[i].second];
+    }
+  }
+  void dfs(int t) {
+    for (int i = 0; i < C; ++ i)
+      if (mp[t][i]) -- mp[t][i], --mp[i][t], dfs(i), ++ cnt, ans.push_back(make_pair(t, i));
+  }
+  void process() {
+    cnt = 0; int s = -1;
+    for (int i = 0; i < C; ++ i)
+      if (in[i] & 1) ++ cnt, s = i;
+      else if (in[i] && ! ~ s) s = i;
+    if (cnt > 2) io < (char *) "No solution\n";
+    else {
+      cnt = 0, dfs(s);
+      if (cnt < n) io < (char *) "No solution\n";
+      else {
+        for (int i = ans.size() - 1; ~ i; -- i)
+          for (int j = 1; j <= n; ++ j)
+            if (! vis[j]) {
+              if (line[j].first == ans[i].first && line[j].second == ans[i].second) {
+                io < j < ' ' < '+' < '\n', vis[j] = true; break;
+              }
+              if (line[j].first == ans[i].second && line[j].second == ans[i].first) {
+                io < j < ' ' < '-' < '\n', vis[j] = true; break;
+              }
+            }
+      }
+    }
+  }
+
+public:
+  void solve() { input(), process(); }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 99 - 0
source/_posts/oi/dungeon.md

@@ -0,0 +1,99 @@
+---
+title: Dungeon
+tags:
+  - Solid Geometry
+id: "1531"
+categories:
+  - - OI
+    - Computational Geometry
+date: 2017-10-16 11:30:22
+---
+
+## 题目描述
+
+The mission of space explorers found on planet M the vast dungeon. One of the dungeon halls is fill with the bright spheres. The explorers find out that the light rays reflect from the surface of the spheres according the ordinary law (the incidence angle is equal to the reflectance angle, the incidence ray, the reflected ray and the perpendicular to the sphere surface lay in the one plane). The ancient legend says that if the light ray will reflect from the spheres in the proper order, than the door to the room with very precious ancient knowledge will open. You are not to guess the right sequence; your task is much simpler. You are given the positions and the radii of the spheres, the place where the laser shot was made and the direction of light propagation. And you must find out the sequence in which the light will be reflected from the spheres.
+
+## 题意概述
+
+空间中有$N$个球,给定一条光线,光线在碰到球之后会发生反射,求光线碰到的前十个球。
+
+数据范围:$1 \le N \le 50$。
+
+## 算法分析
+
+对每一次分别计算出光线碰到的第一个球,也就是沿光线方向距离最近的球。设球的半径为$r$,球心到光源的距离为$l$,球心到光线的距离为$d$,那么光线到球的距离等于$\sqrt{l^2-d^2}-\sqrt{r^2-d^2}$,其中$d$可以用三维叉积来计算。找到最近的球之后需要更新光源和光线方向,这些都可以用向量运算解决。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+
+using namespace std;
+
+static const long double EPS = 1e-10;
+int cmp(long double t) { return fabs(t) < EPS ? 0 : t > 0 ? 1 : -1; }
+
+struct Point {
+  long double x, y, z;
+  Point(long double _x = 0, long double _y = 0, long double _z = 0) : x(_x), y(_y), z(_z) {}
+  Point operator + (const Point &a) const { return Point(x + a.x, y + a.y, z + a.z); }
+  Point operator - (const Point &a) const { return Point(x - a.x, y - a.y, z - a.z); }
+  Point operator * (const long double &a) const { return Point(x * a, y * a, z * a); }
+  Point operator / (const long double &a) const { return Point(x / a, y / a, z / a); }
+  Point operator * (const Point &a) const { return Point(y * a.z - z * a.y, z * a.x - x * a.z, x * a.y - y * a.x); }
+  long double operator & (const Point &a) const { return x * a.x + y * a.y + z * a.z; }
+  long double operator ! () const { return sqrt(x * x + y * y + z * z); }
+  long double operator ^ (const Point &a) const { return (*this & a) / (! *this * ! a); }
+};
+
+struct Line {
+  Point a, b;
+  Line(Point _a = Point(), Point _b = Point()) : a(_a), b(_b) {}
+  long double operator % (const Point &p) const { return ! ((b - a) * (p - a)) / ! (b - a); }
+};
+
+struct Ball { Point p; long double r; };
+
+struct Solver {
+private:
+  static const int N = 50;
+  int n;
+  Point a, b;
+  Ball ball[N];
+  void input() {
+    cin >> n;
+    for (int i = 0; i < n; ++ i) cin >> ball[i].p.x >> ball[i].p.y >> ball[i].p.z >> ball[i].r;
+    cin >> a.x >> a.y >> a.z >> b.x >> b.y >> b.z;
+  }
+  void process() {
+    int p = -1, last = -1;
+    for (int i = 0; i < 11; ++ i) {
+      long double dis = 100000; p = -1;
+      for (int j = 0; j < n; ++ j)
+        if (j != last && ~ cmp((b - a) ^ (ball[j].p - a)) && ~ cmp(ball[j].r - Line(a, b) % ball[j].p)) {
+          long double tmp = sqrt(abs(! (ball[j].p - a) * ! (ball[j].p - a) - (Line(a, b) % ball[j].p) * (Line(a, b) % ball[j].p))) - sqrt(abs(ball[j].r * ball[j].r - (Line(a, b) % ball[j].p) * (Line(a, b) % ball[j].p)));
+          if (cmp(dis - tmp) == 1) dis = tmp, p = j;
+        }
+      if (! ~ p || i == 10) break; last = p;
+      if (i) cout << ' '; cout << p + 1;
+      Point c = a + (b - a) / ! (b - a) * dis;
+      Point d = ball[p].p + (c - ball[p].p) / ! (c - ball[p].p) * (! (a - ball[p].p) * ((a - ball[p].p) ^ (c - ball[p].p)));
+      b = d * 2 - a, a = c;
+    }
+    if (~ p) cout << " etc."; cout << endl;
+  }
+
+public:
+  void solve() { input(), process(); }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 159 - 0
source/_posts/oi/dynamic-rankings.md

@@ -0,0 +1,159 @@
+---
+title: Dynamic Rankings
+tags:
+  - Binary Indexed Tree
+  - Binary-Search
+  - Discretization
+  - Persistent
+  - Prefix Sum
+  - Segment Tree
+id: "2153"
+categories:
+  - [OI, Common Skill]
+  - [OI, Data Structure]
+date: 2018-03-01 23:25:18
+---
+
+## 题目描述
+
+The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the $k$-th smallest number of the given $N$ numbers. They have developed a more powerful system such that for $N$ numbers $a_1, a_2, \ldots, a_N$, you can ask it like: what is the $k$-th smallest number of $a_i, a_{i+1}, \ldots, a_j$? (For some $i \le j$, $0 \lt k \le j+1-i$ that you have given to it). More powerful, you can even change the value of some $a_i$, and continue to query, all the same.
+
+Your task is to write a program for this computer, which
+
+- Reads $N$ numbers from the input.
+- Processes $M$ instructions of the input. These instructions include querying the $k$-th smallest number of $a_i, a_{i+1}, \ldots, a_j$ and change some $a_i$ to $t$.
+
+## 题意概述
+
+有一个长度为$N$的数列。有$M$次操作,每次操作询问数列第$i$项到第$j$项之间的第$k$小值,或者将第$i$项修改为$t$。
+
+数据范围:$1 \le N \le 50000, \; 1 \le M \le 10000$。
+
+内存限制:$32$ M。
+
+## 算法分析
+
+对于这种询问区间第$k$小值的题目,很容易想到主席树。但由于主席树是一种类似前缀和的结构,一次修改操作需要修改$O(N)$棵线段树。这显然不是我们所希望的。
+
+既然主席树类似前缀和,那么就可以借用维护前缀和的工具——树状数组。把树状数组的每个节点看成一棵线段树,这样一次修改操作就只需要修改$O(\log N)$棵线段树,时间复杂度为$O(\log^2N)$。而询问操作需要先把该询问在树状数组上用到的节点提取出来(有$O(\log N)$个),然后利用类似主席树的方法二分,时间复杂度也是$O(\log^2N)$。
+
+但是!这题的内存限制非常小,接受不了$O((N+M)\log^2N)$的空间复杂度。再考虑主席树,它的空间复杂度是$O(N\log N)$的。因此可以对原序列建立静态的主席树,对修改操作利用树状数组维护,这样空间复杂度降至$O(N\log N+M\log^2N)$,可以通过。
+
+## 代码
+
+```cpp
+/*
+ * Beware of Bigfoot!
+ */
+
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+#include <map>
+#include <vector>
+
+static int const N = 60005;
+static int const NM = 2000000;
+int n, m, nn, a[N], arr_rt[N], bit_rt[N], rec[N];
+struct Query {
+  int o, i, j, k;
+} q[N];
+struct SegmentTree {
+  int ch[2], sum;
+} nd[NM];
+
+int create(int rt = 0) { return nd[++nn] = nd[rt], nn; }
+
+int arr_insert(int rt, int p) {
+  rt = create(rt), ++nd[rt].sum;
+  int L = 0, R = N;
+  for (int cur = rt; L < R;) {
+    int MID = L + R >> 1, b = p > MID;
+    nd[cur].ch[b] = create(nd[cur].ch[b]), ++nd[nd[cur].ch[b]].sum,
+    cur = nd[cur].ch[b];
+    b ? L = MID + 1 : R = MID;
+  }
+  return rt;
+}
+
+void bit_insert(int &rt, int p, int v) {
+  if (rt == 0)
+    rt = create();
+  nd[rt].sum += v;
+  int L = 0, R = N;
+  for (int cur = rt; L < R;) {
+    int MID = L + R >> 1, b = p > MID;
+    if (nd[cur].ch[b] == 0)
+      nd[cur].ch[b] = create();
+    nd[nd[cur].ch[b]].sum += v;
+    int tmp = nd[cur].ch[b];
+    cur = tmp, b ? L = MID + 1 : R = MID;
+  }
+}
+
+int bit_add(int p, int v, int c) {
+  for (; p <= n; p += p & -p)
+    bit_insert(bit_rt[p], v, c);
+}
+
+int main() {
+  int T;
+  scanf("%d", &T);
+  for (; T--;) {
+    std::map<int, int> mp;
+    memset(arr_rt, nn = 0, sizeof arr_rt), memset(bit_rt, 0, sizeof bit_rt);
+    scanf("%d%d", &n, &m);
+    for (int i = 1; i <= n; ++i)
+      scanf("%d", &a[i]), mp[a[i]] = 0;
+    for (int i = 1; i <= m; ++i) {
+      char c;
+      scanf(" %c", &c);
+      if (c == 'Q')
+        q[i].o = 0, scanf("%d%d%d", &q[i].i, &q[i].j, &q[i].k);
+      else
+        q[i].o = 1, scanf("%d%d", &q[i].i, &q[i].j), mp[q[i].j] = 0;
+    }
+    int cnt = 0;
+    for (std::map<int, int>::iterator it = mp.begin(); it != mp.end(); ++it)
+      rec[cnt] = it->first, it->second = cnt++;
+    for (int i = 1; i <= n; ++i)
+      arr_rt[i] = arr_insert(arr_rt[i - 1], mp[a[i]]);
+    for (int i = 1; i <= m; ++i) {
+      if (q[i].o == 0) {
+        std::vector<int> a, b;
+        a.push_back(arr_rt[q[i].i - 1]), b.push_back(arr_rt[q[i].j]);
+        for (int p = q[i].i - 1; p; p -= p & -p)
+          a.push_back(bit_rt[p]);
+        for (int p = q[i].j; p; p -= p & -p)
+          b.push_back(bit_rt[p]);
+        int L = 0, R = N;
+        for (; L < R;) {
+          int sum = 0;
+          for (int i = 0; i < a.size(); ++i)
+            sum -= nd[nd[a[i]].ch[0]].sum;
+          for (int i = 0; i < b.size(); ++i)
+            sum += nd[nd[b[i]].ch[0]].sum;
+          int MID = L + R >> 1;
+          if (sum < q[i].k) {
+            L = MID + 1, q[i].k -= sum;
+            for (int i = 0; i < a.size(); ++i)
+              a[i] = nd[a[i]].ch[1];
+            for (int i = 0; i < b.size(); ++i)
+              b[i] = nd[b[i]].ch[1];
+          } else {
+            R = MID;
+            for (int i = 0; i < a.size(); ++i)
+              a[i] = nd[a[i]].ch[0];
+            for (int i = 0; i < b.size(); ++i)
+              b[i] = nd[b[i]].ch[0];
+          }
+        }
+        printf("%d\n", rec[L]);
+      } else
+        bit_add(q[i].i, mp[a[q[i].i]], -1),
+            bit_add(q[i].i, mp[a[q[i].i] = q[i].j], 1);
+    }
+  }
+  return 0;
+}
+```

+ 120 - 0
source/_posts/oi/dzy-loves-fibonacci-numbers.md

@@ -0,0 +1,120 @@
+---
+title: DZY Loves Fibonacci Numbers
+tags:
+  - Segment Tree
+id: "1233"
+categories:
+  - - OI
+    - Data Structure
+date: 2017-07-20 14:20:23
+---
+
+## 题目描述
+
+In mathematical terms, the sequence $F_n$ of Fibonacci numbers is defined by the recurrence relation:
+
+$$
+F_1=1, \; F_2=1, \; F_n=F_{n-1}+F_{n-2} \; (n \gt 2)
+$$
+
+DZY loves Fibonacci numbers very much. Today DZY gives you an array consisting of $n$ integers: $a_1, a_2, \ldots, a_n$. Moreover, there are $m$ queries, each query has one of the two types:
+
+- Format of the query "1 $l$ $r$". In reply to the query, you need to add $F_{i-l+1}$ to each element $a_i$, where $l \le i \le r$.
+- Format of the query "2 $l$ $r$". In reply to the query you should output the value of $\sum_{i=l}^r a_i$ modulo $1000000009$ ($10^9+9$).
+
+Help DZY reply to all the queries.
+
+## 题意概述
+
+给定一个长度为$n$的数列,第$i$个数为$a_i$。有两种操作:① 对于$i \in [l, r]$,将$a_i$加上$F_{i-l+1}$;② 询问区间$[l, r]$内的数字之和。其中$F_i$表示 Fibonacci 数列的第$i$项。操作有$m$次。
+
+数据范围:$1 \le n, m \le 3 \times 10^5, \; 1 \le a_i \le 10^9$。
+
+## 算法分析
+
+在一个 Fibonacci 数列上加一个 Fibonacci 数列,其结果仍然是 Fibonacci 数列。而对于一个 Fibonacci 数列,只要知道前两个数,就能知道接下来的所有数。可以用线段树来维护,对于每一个节点记录下区间和,以及可以代表这个区间上 Fibonacci 数列的前两个数。设前两个数分别为$a, b$,预处理出 Fibonacci 数列每一项$a$和$b$的系数,即可在$O(1)$时间内计算出 Fibonacci 数列的任意一项。其前缀和亦然。
+
+## 代码
+
+```cpp
+#include <cstdio>
+#include <iostream>
+using namespace std;
+const long long mod = 1000000009ll;
+struct node_type {
+  long long l, r, val[2], sum, child[2];
+} node[1000001];
+long long n, m, o, l, r, tot = 1, a[300001], x[300001][2], y[300001][2];
+void build_tree(int root) {
+  if (node[root].l == node[root].r) return; long long mid = node[root].l + node[root].r >> 1;
+  node[++tot].l = node[root].l, node[tot].r = mid, node[root].child[0] = tot, build_tree(tot);
+  node[++tot].l = mid + 1, node[tot].r = node[root].r, node[root].child[1] = tot, build_tree(tot);
+}
+void push_down(int root) {
+  if (node[root].l == node[root].r || node[root].val[0] == 0 && node[root].val[1] == 0) return;
+  if (node[node[root].child[0]].l == node[node[root].child[0]].r) node[node[root].child[0]].sum += node[root].val[0];
+  else {
+    (node[node[root].child[0]].val[0] += node[root].val[0]) %= mod, (node[node[root].child[0]].val[1] += node[root].val[1]) %= mod;
+    node[node[root].child[0]].sum += y[node[node[root].child[0]].r - node[node[root].child[0]].l + 1][0] * node[root].val[0] % mod;
+    node[node[root].child[0]].sum += y[node[node[root].child[0]].r - node[node[root].child[0]].l + 1][1] * node[root].val[1] % mod;
+  }
+  long long p = (x[node[node[root].child[0]].r - node[node[root].child[0]].l + 2][0] * node[root].val[0] + x[node[node[root].child[0]].r - node[node[root].child[0]].l + 2][1] * node[root].val[1]) % mod;
+  long long q = (x[node[node[root].child[0]].r - node[node[root].child[0]].l + 3][0] * node[root].val[0] + x[node[node[root].child[0]].r - node[node[root].child[0]].l + 3][1] * node[root].val[1]) % mod;
+  if (node[node[root].child[1]].l == node[node[root].child[1]].r) node[node[root].child[1]].sum += p;
+  else {
+    (node[node[root].child[1]].val[0] += p) %= mod, (node[node[root].child[1]].val[1] += q) %= mod;
+    node[node[root].child[1]].sum += y[node[node[root].child[1]].r - node[node[root].child[1]].l + 1][0] * p % mod;
+    node[node[root].child[1]].sum += y[node[node[root].child[1]].r - node[node[root].child[1]].l + 1][1] * q % mod;
+  }
+  node[root].val[0] = node[root].val[1] = 0;
+}
+void push_up(int root) {
+  if (node[root].l == node[root].r) return;
+  node[root].sum = node[node[root].child[0]].sum + node[node[root].child[1]].sum;
+}
+void insert_line(int root, int l, int r, int val) {
+  if (node[root].r < l || node[root].l > r) return;
+  if (node[root].l >= l && node[root].r <= r) {
+    if (node[root].l == node[root].r) node[root].sum += x[val][0] + x[val][1];
+    else {
+      (node[root].val[0] += x[val][0] + x[val][1]) %= mod;
+      (node[root].val[1] += x[val + 1][0] + x[val + 1][1]) %= mod;
+      node[root].sum += y[node[root].r - node[root].l + 1][0] * (x[val][0] + x[val][1]) % mod;
+      node[root].sum += y[node[root].r - node[root].l + 1][1] * (x[val + 1][0] + x[val + 1][1]) % mod;
+    }
+    return;
+  }
+  push_down(root);
+  insert_line(node[root].child[0], l, r, val);
+  insert_line(node[root].child[1], l, r, max(1ll, val + node[node[root].child[1]].l - max((long long) l, node[node[root].child[0]].l)));
+  push_up(root);
+}
+long long get_sum(int root, int l, int r) {
+  if (node[root].r < l || node[root].l > r) return 0;
+  if (node[root].l >= l && node[root].r <= r) return node[root].sum;
+  long long ret;
+  push_down(root);
+  ret = get_sum(node[root].child[0], l, r) + get_sum(node[root].child[1], l, r);
+  push_up(root);
+  return ret;
+}
+int main()
+{
+  scanf("%lld%lld", &n, &m);
+  x[1][0] = x[2][1] = y[1][0] = y[2][0] = y[2][1] = 1;
+  for (int i = 3; i <= n; ++i) {
+    x[i][0] = (x[i - 1][0] + x[i - 2][0]) % mod;
+    x[i][1] = (x[i - 1][1] + x[i - 2][1]) % mod;
+    y[i][0] = (y[i - 1][0] + x[i][0]) % mod;
+    y[i][1] = (y[i - 1][1] + x[i][1]) % mod;
+  }
+  node[1].l = 1, node[1].r = n, build_tree(1);
+  for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]), a[i] += a[i - 1];
+  while (m--) {
+    scanf("%lld%lld%lld", &o, &l, &r);
+    if (o == 1) insert_line(1, l, r, 1);
+    else printf("%lld\n", ((get_sum(1, l, r) + a[r] - a[l - 1]) % mod + mod) % mod);
+  }
+  return 0;
+}
+```

+ 69 - 0
source/_posts/oi/dzy-loves-modification.md

@@ -0,0 +1,69 @@
+---
+title: DZY Loves Modification
+tags:
+  - Greedy
+  - Priority Queue
+id: "1228"
+categories:
+  - [OI, Common Skill]
+  - [OI, Data Structure]
+date: 2017-07-20 13:55:19
+---
+
+## 题目描述
+
+As we know, DZY loves playing games. One day DZY decided to play with a $n \times m$ matrix. To be more precise, he decided to modify the matrix with exactly $k$ operations.
+
+Each modification is one of the following:
+
+- Pick some row of the matrix and decrease each element of the row by $p$. This operation brings to DZY the value of pleasure equal to the sum of elements of the row before the decreasing.
+- Pick some column of the matrix and decrease each element of the column by $p$. This operation brings to DZY the value of pleasure equal to the sum of elements of the column before the decreasing.
+
+DZY wants to know: what is the largest total value of pleasure he could get after performing exactly $k$ modifications? Please, help him to calculate this value.
+
+## 题意概述
+
+给定一个$n \times m$的矩阵,每次可以从这个矩阵中选取一行或一列,将这一行或一列的数字之和加到你的分数上,再将这一行或一列上的每个数字减去$p$。共进行$k$次操作,求分数的最大值。
+
+数据范围:$1 \le n, m \le 1000, \; 1 \le k \le 10^6, \; 1 \le p \le 100$。
+
+## 算法分析
+
+可以发现交换操作顺序对答案没有影响。因此,可以先计算出只取$i$行可以得到的最大值以及只取$j$列可以得到的最大值,根据贪心策略,可以用优先队列维护一行或一列的数字之和。接着枚举取了$i$行,那么也就取了$(k-i)$列,减去重复部分,取最大值,即可得到答案。
+
+## 代码
+
+```cpp
+#include <iostream>
+#include <queue>
+using namespace std;
+priority_queue<long long> r, c;
+long long n, m, k, p, s, t, ans = -1e18, a[1001][1001], rr[1000001], cc[1000001];
+int main()
+{
+  cin >> n >> m >> k >> p;
+  for (int i = 1; i <= n; ++i)
+    for (int j = 1; j <= m; ++j) cin >> a[i][j];
+  for (int i = 1; i <= n; ++i) {
+    long long s = 0;
+    for (int j = 1; j <= m; ++j) s += a[i][j];
+    r.push(s);
+  }
+  for (int j = 1; j <= m; ++j) {
+    long long s = 0;
+    for (int i = 1; i <= n; ++i) s += a[i][j];
+    c.push(s);
+  }
+  for (int i = 1; i <= k; ++i) {
+    long long u = r.top(); r.pop();
+    rr[i] = rr[i - 1] + u;
+    r.push(u - p * m);
+    u = c.top(); c.pop();
+    cc[i] = cc[i - 1] + u;
+    c.push(u - p * n);
+  }
+  for (int i = 0; i <= k; ++i) ans = max(ans, rr[i] + cc[k - i] - p * i * (k - i));
+  cout << ans << endl;
+  return 0;
+}
+```

+ 95 - 0
source/_posts/oi/ellipse.md

@@ -0,0 +1,95 @@
+---
+title: Ellipse
+tags:
+  - Simpson's Rule
+id: "2376"
+categories:
+  - - OI
+    - Number Theory
+date: 2018-03-08 14:10:13
+---
+
+## 题目描述
+
+Math is important!! Many students failed in 2+2's mathematical test, so let's AC this problem to mourn for our lost youth..
+Look at this sample picture:
+
+![](1.jpg)
+
+An ellipse in a plane centered on point $O$. The $L, R$ lines will be vertical through the $X$-axis. The problem is calculating the blue intersection area. But calculating the intersection area is dull, so I have to turn to you, a talent of programmer. Your task is telling me the result of calculation. (defined $\pi=3.14159265$, the area of an ellipse $A=\pi ab$)
+
+## 题意概述
+
+给定$a, b, l, r$,求椭圆${x^2 \over a^2}+{y^2 \over b^2}=1$在直线$x=l$与$x=r$之间的面积。
+
+数据范围:$-a \le l \le r \le a$。
+
+## 算法分析
+
+这是一个不规则图形,无法直接计算,但我们可以用积分来求面积。设$f(n)$表示直线$x=n$与椭圆的两个交点之间的距离。
+
+根据辛普森公式
+
+$$
+\int_a^b f(x) \, {\rm d}x \approx {b-a \over 6} \left(f(a)+4f\left({a+b \over 2}\right)+f(b)\right)
+$$
+
+但这样的精度并不是很高。考虑二分,令
+
+$$
+g(l, r)=\int_l^r f(x) \, {\rm d}x, \; h(l, r)={r-l \over 6} \left(f(l)+4f\left({l+r \over 2}\right)+f(r)\right)
+$$
+
+当$h(l, r)$与$h\left(l, {l+r \over 2}\right)+h\left({l+r \over 2}, r\right)$的差足够小时,令
+
+$$
+g(l, r)=h(l, r)
+$$
+
+否则
+
+$$
+g(l, r)=g\left(l, {l+r \over 2}\right)+g\left({l+r \over 2}, r\right)
+$$
+
+这样就可以达到精度要求。
+
+## 代码
+
+```cpp
+/*
+ * Today is what happened to yesterday.
+ */
+
+#include <algorithm>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+
+static double const EPS = 1e-8;
+int a, b;
+
+double get_y(double x) { return 2 * b * sqrt((1 - x * x / a / a)); }
+
+double get_s(double l, double r) {
+  return (get_y(l) + 4 * get_y((l + r) / 2) + get_y(r)) * (r - l) / 6;
+}
+
+double calc(double l, double r) {
+  double mid = (l + r) / 2;
+  if (fabs(get_s(l, r) - get_s(l, mid) - get_s(mid, r)) < EPS)
+    return get_s(l, r);
+  return calc(l, mid) + calc(mid, r);
+}
+
+int main() {
+  int T;
+  scanf("%d", &T);
+  for (; T--;) {
+    int l, r;
+    scanf("%d%d%d%d", &a, &b, &l, &r);
+    printf("%.3lf\n", calc(l, r));
+  }
+  return 0;
+}
+```

BIN=BIN
source/_posts/oi/ellipse/1.jpg


+ 107 - 0
source/_posts/oi/erasing-edges.md

@@ -0,0 +1,107 @@
+---
+title: Erasing Edges
+tags:
+  - Constructive Algorithm
+  - Plane Geometry
+id: "1600"
+categories:
+  - [OI, Common Skill]
+  - [OI, Computational Geometry]
+date: 2017-10-29 10:50:59
+---
+
+## 题目描述
+
+Little Johnny painted on a sheet of paper a polygon with $N$ vertices. Then, for every edge of the polygon, he drew the middle point of the edge. After that, he went to school. When he came back, he found out that his brother had erased the polygon (both the edges and the vertices). The only thing left were the middle points of the edges of the polygon. Help Johnny redraw his polygon.
+
+## 题意概述
+
+给定一个$N$边形各边的中点,还原这个$N$边形。边可以相交。
+
+数据范围:$3 \le N \le 10000$。
+
+## 算法分析
+
+设$N$边形第$i$个点的坐标为$(x_i, y_i)$,第$i$条边中点的坐标为$(x_i', y_i')$,可以得到以下方程
+
+$$
+\left\{
+\begin{array}{c}
+x_1+x_2=2x_1' \\\\
+x_2+x_3=2x_2' \\\\
+\cdots \\\\
+x_N+x_1=2x_N'
+\end{array}
+\right.
+$$
+
+对$y$同理。当$N$为奇数时,方程有唯一解,解出即可;当$N$为偶数时,方程无解或有无数解,只需尝试构造一组解并判断是否合法。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+#include <vector>
+
+#define random (1.0 * rand() / RAND_MAX)
+
+using namespace std;
+
+static const double EPS = 1e-6;
+int cmp(double a, double b) {
+  return fabs(a - b) < EPS ? 0 : a < b ? -1 : 1;
+}
+
+struct Point {
+  double x, y;
+  Point(double _x = 0, double _y = 0) : x(_x), y(_y) {}
+  Point operator + (const Point &p) const { return Point(x + p.x, y + p.y); }
+  Point operator - (const Point &p) const { return Point(x - p.x, y - p.y); }
+  Point operator * (const double &n) const { return Point(x * n, y * n); }
+  Point operator / (const double &n) const { return *this * (1 / n); }
+  Point operator | (const Point &p) const { return *this + (p - *this) * 2; }
+};
+
+struct Solver {
+private:
+  static const int N = 10010;
+  int n;
+  Point point[N];
+  vector <Point> ans;
+  void input() {
+    scanf("%d", &n);
+    for (int i = 1; i <= n; ++ i) scanf("%lf%lf", &point[i].x, &point[i].y);
+  }
+  void process() {
+    Point p;
+    if (n & 1) {
+      double sum = 0;
+      for (int i = 1; i <= n; ++ i) sum += point[i].x;
+      for (int i = 2; i <= n; i += 2) sum -= point[i].x * 2;
+      p.x = sum, sum = 0;
+      for (int i = 1; i <= n; ++ i) sum += point[i].y;
+      for (int i = 2; i <= n; i += 2) sum -= point[i].y * 2;
+      p.y = sum;
+    } else p = Point(random * 100000, random * 100000);
+    for (int i = 1; i <= n; ++ i) ans.push_back(p), p = p | point[i];
+    if (cmp(p.x, ans[0].x) || cmp(p.y, ans[0].y)) printf("NO\n");
+    else {
+      printf("YES\n");
+      for (int i = 0; i < ans.size(); ++ i) printf("%.6lf %.6lf\n", ans[i].x, ans[i].y);
+    }
+  }
+
+public:
+  void solve() { input(), process(); }
+} solver;
+
+int main() {
+  srand(47), solver.solve();
+  return 0;
+}
+```

+ 22 - 0
source/_posts/oi/expensive-drink.md

@@ -0,0 +1,22 @@
+---
+title: Expensive Drink
+tags:
+  - Linear Programming
+id: "249"
+categories:
+  - - OI
+    - Number Theory
+date: 2017-06-08 01:00:17
+---
+
+## 题意概述
+
+已知$0 \le c_1 \le c_2 \le c_3, \; 0 \le c_4, \; L \le a_4c_4 \le R$。$c_1, c_2, c_3, c_4$均为未知的常量。
+
+给出$L, R$和$n$组$a_1, a_2, a_3, p$,对于任意一组均满足$p=a_1c_1+a_2c_2+a_3c_3+a_4c_4$。现在给出一组$a_1, a_2, a_3$,求$p$的最大值。
+
+数据范围:$1 \le n \le 100$。
+
+## 算法分析
+
+对于每一组$a_1, a_2, a_3, p$,可以列出不等式$p-R \le a_1c_1+a_2c_2+a_3c_3 \le p-L$,其中有三个未知数$c_1, c_2, c_3$。最后要求的答案就是在满足所有约束的情况下$\max(a_1c_1+a_2c_2+a_3c_3)+R$。这是有三个变量的线性规划问题,可以用单纯形法解决此题。

+ 77 - 0
source/_posts/oi/fence.md

@@ -0,0 +1,77 @@
+---
+title: Fence
+tags:
+  - Dynamic Programming
+  - Monotonic Queue
+id: "3577"
+categories:
+  - [OI, Common Skill]
+  - [OI, Data Structure]
+date: 2020-01-21 21:00:46
+---
+
+## 题目描述
+
+A team of $K$ workers should paint a fence which contains $N$ planks numbered from $1$ to $N$ from left to right. Each worker $i$ should sit in front of the plank $S_i$ and he may paint only a compact interval (this means that the planks from the interval should be consecutive). This interval should contain the $S_i$ plank. Also a worker should not paint more than $L_i$ planks and for each painted plank he should receive $P_i$\$. A plank should be painted by no more than one worker. All the numbers $S_i$ should be distinct.
+
+Being the team's leader you want to determine for each worker the interval that he should paint, knowing that the total income should be maximal. The total income represents the sum of the workers personal income.
+
+Write a program that determines the total maximal income obtained by the $K$ workers.
+
+## 题意概述
+
+$K$个工人要粉刷一面长度为$N$的篱笆,第$i$个工人要么不粉刷,要么粉刷一段连续且长度不超过$L_i$的包含第$S_i$块木板的篱笆,他每粉刷一块木板可以获得$P_i$的报酬。每一块木板要么不被粉刷,要么仅被一个工人粉刷。求所有工人获得的总报酬的最大值。
+
+数据范围:$1 \le K \le 100, \; 1 \le N \le 16000$。
+
+## 算法分析
+
+令$f_{i, j}$表示前$i$个工人粉刷前$j$块木板(不一定全刷)的最大报酬,则可分三种情况讨论:
+
+- 第$i$个工人不粉刷,$f_{i, j}=f_{i-1, j}$;
+- 第$j$块木板不被粉刷,$f_{i, j}=f_{i, j-1}$;
+- 第$i$个工人粉刷第$(k+1)$到第$j$块木板,$f_{i, j}=f_{i-1, k}+(j-k) \times P_i$。
+
+在第三种情况中,转移方程变形后得$f_{i, j}=(f_{i-1, k}-k \times P_i)+j \times P_i$,$f_{i-1, k}-k \times P_i$与$j$无关,$j \times P_i$与$k$无关,因此对于确定的$i$和$j$,方程后半部分为常数,只要求前半部分的最大值。因为前半部分只与$k$有关,所以可以用单调队列维护其最大值。注意$k$要满足的条件是$k+1 \le S_i \le j$与$j-(k+1)+1 \le L_i$,即$j-L_i \le k \lt S_i$。算法的时间复杂度为$O(KN)$。
+
+## 代码
+
+```cpp
+#include <cstdio>
+#include <algorithm>
+
+int const K = 105, N = 16005;
+int f[K][N];
+struct Carpenter {
+    int l, p, s;
+    bool operator < (Carpenter const &t) const {
+        return s < t.s;
+    }
+} c[K];
+std::pair<int, int> que[N];
+
+int main() {
+    int n, k;
+    scanf("%d%d", &n, &k);
+    for (int i = 1; i <= k; ++i) {
+        scanf("%d%d%d", &c[i].l, &c[i].p, &c[i].s);
+    }
+    std::sort(c + 1, c + k + 1);
+    for (int i = 1; i <= k; ++i) {
+        int qb = 0, qe = 0;
+        for (int j = 0; j < c[i].s; ++j) {
+            for (; qb < qe && que[qe - 1].first <= f[i - 1][j] - c[i].p * j; --qe) ;
+            que[qe++] = std::make_pair(f[i - 1][j] - c[i].p * j, j);
+        }
+        for (int j = 1; j <= n; ++j) {
+            f[i][j] = std::max(f[i - 1][j], f[i][j - 1]);
+            if (c[i].s <= j && j < c[i].s + c[i].l) {
+                for (; qb < qe && que[qb].second < j - c[i].l; ++qb) ;
+                f[i][j] = std::max(f[i][j], que[qb].first + c[i].p * j);
+            }
+        }
+    }
+    printf("%d\n", f[k][n]);
+    return 0;
+}
+```

+ 122 - 0
source/_posts/oi/fermats-last-theorem.md

@@ -0,0 +1,122 @@
+---
+title: Fermat's Last Theorem
+tags:
+  - Primitive Root
+id: "3171"
+categories:
+  - - OI
+    - Number Theory
+date: 2018-05-18 11:15:19
+---
+
+## 题目描述
+
+Given a positive integer $n$ and a positive prime number $p$, find $x, y$ and $z$ such that $x^n+y^n \equiv z^n \pmod p$ and $x, y$ and $z$ are nonzero modulo $p$ or report that there's no such triple.
+
+## 题意概述
+
+给定一个整数$n$和一个质数$p$,判断方程$x^n+y^n \equiv z^n \pmod p \; (1 \le x, y, z \lt p)$是否存在整数解,若存在则给出一组解。有$T$组数据。
+
+数据范围:$1 \le T \le 1000, \; 3 \le n \le 10^6, \; 2 \le p \le 10^6$。
+
+## 算法分析
+
+为了方便,以下同余方程模数均为$p$。
+
+求出$p$的原根$g$,设$x=g^{k_1}, \; y=g^{k_2}, \; z=g^{k_3}$,方程变为
+
+$$
+g^{k_1n}+g^{k_2n} \equiv g^{k_3n}
+$$
+
+令$G=g^n$,则
+
+$$
+G^{k_1}+G^{k_2} \equiv G^{k_3}
+$$
+
+由于$g$是原根,可知$G \not \equiv 0$,因此
+
+$$
+\begin{align}
+G^{k_1-k_2}+1 &\equiv G^{k_3-k_2} \\\\
+G^{k_1-k_3}+G^{k_2-k_3} &\equiv 1
+\end{align}
+$$
+
+根据费马小定理,$G^{p-1} \equiv 1$,所以只要从$1$到$p-1$枚举$i$,判断是否存在$j \le i$满足
+
+$$
+G^i+1 \equiv G^j \lor G^j+1 \equiv G^i \lor G^i+G^j \equiv 1
+$$
+
+若存在,则找到了一组解。若枚举到$i$时发现存在$j \lt i$满足$G^i \equiv G^j$,说明开始循环,直接输出无解。根据抽屉原理,枚举的次数不会太多。
+
+此题时限较小,不能每组询问都清空数组,可以使用时间戳。
+
+## 代码
+
+```cpp
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+
+static int const N = 1000005;
+int a[N], b[N], pri[N];
+
+int power(int a, int b, int p) {
+  int ret = 1;
+  for (; b; b >>= 1)
+    b & 1 && (ret = 1ll * ret * a % p), a = 1ll * a * a % p;
+  return ret;
+}
+
+int get_prt(int p) {
+  int top = 0, k = p - 1;
+  for (int i = 2; i * i <= k; ++i)
+    if (!(k % i)) {
+      pri[top++] = i;
+      for (; !(k % i); k /= i)
+        ;
+    }
+  if (k > 1)
+    pri[top++] = k;
+  for (int i = 2; i <= p - 1; ++i) {
+    bool fg = 1;
+    for (int j = 0; fg && j < top; ++j)
+      fg &= power(i, (p - 1) / pri[j], p) > 1;
+    if (fg)
+      return i;
+  }
+  return 0;
+}
+
+int main() {
+  int T;
+  scanf("%d", &T);
+  for (int n, p; T--;) {
+    scanf("%d%d", &n, &p);
+    int g = get_prt(p), gn = power(g, n, p), x = 0, y = 0, z = 0;
+    for (int i = 1, G = gn; i <= p - 1; ++i, G = 1ll * G * gn % p)
+      if (b[G] == T + 1)
+        break;
+      else {
+        a[G] = i, b[G] = T + 1;
+        if (b[G + 1] == T + 1) {
+          x = power(g, i, p), y = 1, z = power(g, a[G + 1], p);
+          break;
+        }
+        if (b[G - 1] == T + 1) {
+          x = power(g, a[G - 1], p), y = 1, z = power(g, i, p);
+          break;
+        }
+        if (b[1 - G + p] == T + 1) {
+          x = power(g, i, p), y = power(g, a[1 - G + p], p), z = 1;
+          break;
+        }
+      }
+    x ? printf("%d %d %d\n", x, y, z) : puts("-1");
+  }
+  return 0;
+}
+```

+ 179 - 0
source/_posts/oi/four-loop.md

@@ -0,0 +1,179 @@
+---
+title: Four Loop
+tags:
+  - Fast Fourier Transform
+  - Fast Walsh-Hadamard Transform
+id: "3647"
+categories:
+  - - OI
+    - Number Theory
+date: 2020-02-06 02:00:52
+---
+
+## 题目描述
+
+Given two sequences $a$ and $b$ of equal length $n$, find the
+
+$$
+\sum_{x=1}^n \sum_{y=1}^n \sum_{z=1}^n \sum_{w=1}^n (a_x+a_y+a_z+a_w)^{(b_x \oplus b_y \oplus b_z \oplus b_w)}
+$$
+
+## 题意概述
+
+给定两个长度为$n$的序列$a$和$b$,求$\sum_{x=1}^n \sum_{y=1}^n \sum_{z=1}^n \sum_{w=1}^n (a_x+a_y+a_z+a_w)^{(b_x \oplus b_y \oplus b_z \oplus b_w)}$。
+
+数据范围:$1 \le n \le 10^5, \; 1 \le a_i \le 500, \; 1 \le b_i \le 500$。
+
+## 算法分析
+
+$a_x+a_y+a_z+a_w$的取值范围为$[4,2000]$,$b_x \oplus b_y \oplus b_z \oplus b_w$的取值范围为$[0,511]$,可以分别计算每种情况出现的次数再求和。
+
+令$f_{1,i,j}$表示有多少个$x$满足$a_x=i \land b_x=j$。对于$k \ge 2$,令
+
+$$
+f_{k,i,j}=\sum_{i_1+i_2=i} \sum_{j_1 \oplus j_2=j} f_{k-1,i_1,j_1}f_{1,i_2,j_2}
+$$
+
+$f_{4,i,j}$即要求的每种情况出现的次数。转移方程在一维上是乘法卷积,另一维上是异或卷积,可以分别用 FFT 和 FWT 进行处理。总时间复杂度为$O(a_{\max}b_{\max}\log(a_{\max}b_{\max}))$。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+#include <algorithm>
+
+int const N = 100005, M = 512, MOD = 998244353, G = 3, INV2 = 499122177;
+
+int a[N], b[N], rev[M << 2];
+
+int power(int a, int b) {
+    int ret = 1;
+    for (; b; b >>= 1) {
+        if (b & 1) {
+            ret = 1ll * ret * a % MOD;
+        }
+        a = 1ll * a * a % MOD;
+    }
+    return ret;
+}
+
+int wn[M << 2], A[M << 2], f[M << 2][M];
+
+void init(int n) {
+    int len = 1, p = 0;
+    for (; len < n; len <<= 1, ++p) ;
+    for (int i = 1; i < len; ++i) {
+        rev[i] = rev[i >> 1] >> 1 | (i & 1) << p - 1;
+    }
+    wn[0] = 1, wn[1] = power(G, (MOD - 1) / len);
+    for (int i = 2; i < len >> 1; ++i) {
+        wn[i] = 1ll * wn[i - 1] * wn[1] % MOD;
+    }
+}
+
+void fft(int *a, int len, bool inv = 0) {
+    for (int i = 0; i < len; ++i) {
+        if (i < rev[i]) {
+            std::swap(a[i], a[rev[i]]);
+        }
+    }
+    for (int i = 1; i < len; i <<= 1) {
+        for (int j = 0; j < len; j += i << 1) {
+            for (int k = 0; k < i; ++k) {
+                int x = a[j + k], y = 1ll * wn[len / (i << 1) * k] * a[j + i + k] % MOD;
+                a[j + k] = (x + y) % MOD;
+                a[j + i + k] = (MOD + x - y) % MOD;
+            }
+        }
+    }
+    if (inv) {
+        std::reverse(a + 1, a + len);
+        int inv = power(len, MOD - 2);
+        for (int i = 0; i < len; ++i) {
+            a[i] = 1ll * a[i] * inv % MOD;
+        }
+    }
+}
+
+void fwt(int *a, int len, bool inv = 0) {
+    for (int i = 1; i < len; i <<= 1) {
+        for (int j = 0; j < len; j += i << 1) {
+            for (int k = 0; k < i; ++k) {
+                int x = a[j + k], y = a[j + i + k];
+                a[j + k] = (x + y) % MOD;
+                a[j + i + k] = (MOD + x - y) % MOD;
+                if (inv) {
+                    a[j + k] = 1ll * a[j + k] * INV2 % MOD;
+                    a[j + i + k] = 1ll * a[j + i + k] * INV2 % MOD;
+                }
+            }
+        }
+    }
+}
+
+int main() {
+    int n;
+    scanf("%d", &n);
+    for (int i = 1; i <= n; ++i) {
+        scanf("%d", &a[i]);
+    }
+    for (int i = 1; i <= n; ++i) {
+        scanf("%d", &b[i]);
+        f[a[i]][b[i]] = f[a[i]][b[i]] + 1;
+    }
+    init(M << 2);
+    for (int i = 0; i < M << 2; ++i) {
+        for (int j = 0; j < M; ++j) {
+            A[j] = f[i][j];
+        }
+        fwt(A, M);
+        for (int j = 0; j < M; ++j) {
+            f[i][j] = A[j];
+        }
+    }
+    for (int i = 0; i < M; ++i) {
+        for (int j = 0; j < M << 2; ++j) {
+            A[j] = f[j][i];
+        }
+        fft(A, M << 2);
+        for (int j = 0; j < M << 2; ++j) {
+            f[j][i] = A[j];
+        }
+    }
+    for (int i = 0; i < M << 2; ++i) {
+        for (int j = 0; j < M; ++j) {
+            f[i][j] = power(f[i][j], 4);
+        }
+    }
+    for (int i = 0; i < M << 2; ++i) {
+        for (int j = 0; j < M; ++j) {
+            A[j] = f[i][j];
+        }
+        fwt(A, M, 1);
+        for (int j = 0; j < M; ++j) {
+            f[i][j] = A[j];
+        }
+    }
+    for (int i = 0; i < M; ++i) {
+        for (int j = 0; j < M << 2; ++j) {
+            A[j] = f[j][i];
+        }
+        fft(A, M << 2, 1);
+        for (int j = 0; j < M << 2; ++j) {
+            f[j][i] = A[j];
+        }
+    }
+    int ans = 0;
+    for (int i = 0; i < M << 2; ++i) {
+        for (int j = 0; j < M; ++j) {
+            if (f[i][j]) {
+                ans = (ans + 1ll * f[i][j] * power(i, j)) % MOD;
+            }
+        }
+    }
+    printf("%d\n", ans);
+    return 0;
+}
+```

+ 139 - 0
source/_posts/oi/fox-and-dinner.md

@@ -0,0 +1,139 @@
+---
+title: Fox and Dinner
+tags:
+  - Network Flow
+  - Prime
+id: "853"
+categories:
+  - [OI, Graph Theory]
+  - [OI, Number Theory]
+date: 2017-06-29 18:40:04
+---
+
+## 题目描述
+
+Fox Ciel is participating in a party in Prime Kingdom. There are $n$ foxes there (include Fox Ciel). The $i$-th fox is $a_i$ years old.
+
+They will have dinner around some round tables. You want to distribute foxes such that:
+
+- each fox is sitting at some table,
+- each table has at least $3$ foxes sitting around it,
+- the sum of ages of any two adjacent foxes around each table should be a prime number.
+
+If $k$ foxes $f_1, f_2, \ldots, f_k$ are sitting around table in clockwise order, then for $1 \le i \le k-1$: $f_i$ and $f_{i+1}$ are adjacent, and $f_1$ and $f_k$ are also adjacent.
+
+If it is possible to distribute the foxes in the desired manner, find out a way to do that.
+
+## 题意概述
+
+给定$n$个数$a_i$,你需要将它们分成若干个圈,使得每个圈中至少有$3$个数,且任意两个相邻的数的和为质数。
+
+数据范围:$3 \le n \le 200, \; 2 \le a_i \le 10000$。
+
+## 算法分析
+
+因为每个数都大于$1$,所以任意两个相邻的数必定是一奇一偶。对于任意一个奇数$a_i$和任意一个偶数$a_j$,如果$a_i+a_j$是质数,那么就由$a_i$向$a_j$连一条流量为$1$的边。由于每个数都必须与另外两个数相连,因此可以由一个假设的源点向每个奇数的点连一条流量为$2$的边,由每个偶数的点向一个假设的汇点连一条流量为$2$的边。找出这张图的最大流,如果源点连出的边和连向汇点的边全部满流则说明有解。最后遍历找出所有环即可。
+
+## 代码
+
+```cpp
+#include <iostream>
+#include <cstring>
+#include <cmath>
+#include <vector>
+using namespace std;
+struct edge {
+    int v, f, nxt;
+} e[50001];
+int n, nume = 1, top, src, sink, a[202], h[202], g[202], dist[202], que[202];
+bool vis[202];
+vector<int> ans[201];
+void add_edge(int u, int v, int f) {
+    e[++nume].v = v, e[nume].f = f, e[nume].nxt = h[u], h[u] = nume;
+    e[++nume].v = u, e[nume].f = 0, e[nume].nxt = h[v], h[v] = nume;
+}
+void bfs() {
+    memset(dist, 0, sizeof(dist));
+    que[1] = src, dist[src] = 1;
+    int s = 0, t = 1;
+    while (s < t) {
+        int u = que[++s];
+        for (int i = h[u]; i; i = e[i].nxt) {
+            if (e[i].f && !dist[e[i].v]) {
+                que[++t] = e[i].v, dist[e[i].v] = dist[u] + 1;
+            }
+        }
+    }
+}
+int dfs(int u, int delta) {
+    if (u == sink) return delta;
+    int ret = 0;
+    for (int i = g[u]; i; i = e[i].nxt) {
+        if (e[i].f && dist[e[i].v] == dist[u] + 1) {
+            int dd = dfs(e[i].v, min(e[i].f, delta));
+            e[i].f -= dd, e[i ^ 1].f += dd;
+            if (e[i].f) g[u] = i;
+            delta -= dd, ret += dd;
+        }
+    }
+    return ret;
+}
+int max_flow() {
+    int ret = 0;
+    while (1) {
+        bfs();
+        if (!dist[sink]) return ret;
+        for (int i = 0; i <= n + 1; ++i) g[i] = h[i];
+        ret += dfs(src, 1e9);
+    }
+}
+bool is_prime(int t) {
+    int k = (int) sqrt(t);
+    for (int i = 2; i <= k; ++i) if (!(t % i)) return false;
+    return true;
+}
+void find(int u, int t) {
+    vis[u] = true, ans[t].push_back(u);
+    for (int i = h[u]; i; i = e[i].nxt) {
+        if (e[i].v && e[i].v != n + 1 && e[i].f == !(a[u] & 1) && !vis[e[i].v]) find(e[i].v, t);
+    }
+}
+int main()
+{
+    cin >> n;
+    src = 0, sink = n + 1;
+    for (int i = 1; i <= n; ++i) {
+        cin >> a[i];
+        if (a[i] & 1) add_edge(src, i, 2);
+        else add_edge(i, sink, 2);
+    }
+    for (int i = 1; i <= n; ++i) {
+        for (int j = 1; j <= n; ++j) {
+            if (a[i] & 1 && !(a[j] & 1) && is_prime(a[i] + a[j])) add_edge(i, j, 1);
+        }
+    }
+    max_flow();
+    for (int i = h[0]; i; i = e[i].nxt) {
+        if (e[i].f) {
+            cout << "Impossible" << endl;
+            return 0;
+        }
+    }
+    for (int i = h[n + 1]; i; i = e[i].nxt) {
+        if (e[i].f < 2) {
+            cout << "Impossible" << endl;
+            return 0;
+        }
+    }
+    for (int i = 1; i <= n; ++i) if (!vis[i]) top++, find(i, top);
+    cout << top << endl;
+    for (int i = 1; i <= top; ++i) {
+        cout << ans[i].size() << ' ';
+        for (vector<int>::iterator iter = ans[i].begin(); iter != ans[i].end(); ++iter) {
+            cout << *iter << ' ';
+        }
+        cout << endl;
+    }
+    return 0;
+}
+```

+ 75 - 0
source/_posts/oi/fox-and-jumping.md

@@ -0,0 +1,75 @@
+---
+title: Fox and Jumping
+tags:
+  - Dynamic Programming
+  - GCD-LCM
+id: "665"
+categories:
+  - [OI, Common Skill]
+  - [OI, Number Theory]
+date: 2017-06-24 01:00:14
+---
+
+## 题目描述
+
+Fox Ciel is playing a game. In this game there is an infinite long tape with cells indexed by integers (positive, negative and zero). At the beginning she is standing at the cell $0$.
+
+There are also $n$ cards, each card has $2$ attributes: length $l_i$ and cost $c_i$. If she pays $c_i$ dollars then she can apply $i$-th card. After applying $i$-th card she becomes able to make jumps of length $l_i$, i.e. from cell $x$ to cell $(x-l_i)$ or cell $(x+l_i)$.
+
+She wants to be able to jump to any cell on the tape (possibly, visiting some intermediate cells). For achieving this goal, she wants to buy some cards, paying as little money as possible.
+
+If this is possible, calculate the minimal cost.
+
+## 题意概述
+
+有$n$张卡片,每张卡片都有两个属性$l_i, c_i$。你需要从中选择一些卡片,使得在满足用已选卡片的$l_i$任意加减可以得到所有整数的情况下,已选卡片的$c_i$之和最小。求出这个最小值。
+
+数据范围:$1 \le n \le 300, \; 1 \le l_i \le 10^9, \; 1 \le c_i \le 10^5$。
+
+## 算法分析
+
+可以发现,如果所有已选卡片的$l_i$的最大公约数为$1$,则满足要求。
+
+用$f_k$表示已选卡片的$l_i$的最大公约数为$k$时$c_i$之和的最小值。有如下转移方程:
+
+$$
+f_{(k, l_i)}=\min(f_k+c_i)
+$$
+
+如果最后$f_1$不存在,则说明无解。
+
+由于$k$可能会很大,但个数不会很多,可以使用 map 来节省空间。
+
+## 代码
+
+```cpp
+#include <iostream>
+#include <map>
+using namespace std;
+int n, l[301], c[301];
+map<int, int> f;
+int gcd(int a, int b) {
+    return b ? gcd(b, a % b) : a;
+}
+int main()
+{
+    cin >> n;
+    for (int i = 1; i <= n; ++i) {
+        cin >> l[i];
+    }
+    for (int i = 1; i <= n; ++i) {
+        cin >> c[i];
+    }
+    f[0] = 0;
+    for (int i = 1; i <= n; ++i) {
+        for (map<int, int>::iterator iter = f.begin(); iter != f.end(); ++iter) {
+            int t = gcd(iter->first, l[i]);
+            if (!f.count(t)) f[t] = iter->second + c[i];
+            else f[t] = min(f[t], iter->second + c[i]);
+        }
+    }
+    if (!f[1]) cout << -1 << endl;
+    else cout << f[1] << endl;
+    return 0;
+}
+```

+ 100 - 0
source/_posts/oi/fox-and-names.md

@@ -0,0 +1,100 @@
+---
+title: Fox and Names
+tags:
+  - Topological-Sort
+id: "641"
+categories:
+  - - OI
+    - Graph Theory
+date: 2017-06-24 00:40:34
+---
+
+## 题目描述
+
+Fox Ciel is going to publish a paper on FOCS (Foxes Operated Computer Systems, pronounce: "Fox"). She heard a rumor: the authors list on the paper is always sorted in the lexicographical order.
+
+After checking some examples, she found out that sometimes it wasn't true. On some papers authors' names weren't sorted in lexicographical order in normal sense. But it was always true that after some modification of the order of letters in alphabet, the order of authors becomes lexicographical!
+
+She wants to know, if there exists an order of letters in Latin alphabet such that the names on the paper she is submitting are following in the lexicographical order. If so, you should find out any such order.
+
+Lexicographical order is defined in following way. When we compare $s$ and $t$, first we find the leftmost position with differing characters: $s_i \neq t_i$. If there is no such position (i.e. $s$ is a prefix of $t$ or vice versa) the shortest string is less. Otherwise, we compare characters $s_i$ and $t_i$ according to their order in alphabet.
+
+## 题意概述
+
+给定$n$个字符串$name_1, name_2, name_3, \ldots, name_n$,要求你给出一张字母表,使得这些字符串是按照你所给出字母表的字典序从小到大排序的。
+
+数据范围:$1 \le n \le 100, \; 1 \le |name_i| \le 100$。
+
+## 算法分析
+
+很明显,对于任意两个字符串$name_i, name_j \; (i \lt j)$来说,设其第一个不同字母的位置为$p$,则有$index_{name_{i, p}} \lt index_{name_{j, p}}$,其中$index_i$表示字母$i$在字母表中的位置。可以发现,这构成了一张拓扑图,进行一次拓扑排序就能解决问题。
+
+注意到其中有些字符串是另一些字符串的前缀。如果$name_i$是$name_j$的前缀($name_i \neq name_j$),且$i \gt j$,则直接输出 Impossible。
+
+## 代码
+
+```cpp
+#include <iostream>
+#include <algorithm>
+#include <queue>
+using namespace std;
+struct author {
+    int id;
+    string name;
+} a[101];
+bool cmp(author a, author b) {
+    return a.name < b.name;
+}
+int n, in[26];
+bool graph[26][26];
+queue<int> que;
+string ans;
+int main()
+{
+    cin >> n;
+    for (int i = 1; i <= n; ++i) {
+        cin >> a[i].name;
+        a[i].id = i;
+    }
+    sort(a + 1, a + n + 1, cmp);
+    for (int i = 1; i < n; ++i) {
+        for (int j = i + 1; j <= n; ++j) {
+            int p = 0, len1 = a[i].name.length(), len2 = a[j].name.length();
+            while (p < len1 && p < len2 && a[i].name[p] == a[j].name[p]) ++p;
+            if (p == len1 && p < len2 && a[i].id > a[j].id) {
+                cout << "Impossible" << endl;
+                return 0;
+            }
+            if (p < len1) {
+                if (a[i].id < a[j].id) graph[a[i].name[p] - 'a'][a[j].name[p] - 'a'] = true;
+                else graph[a[j].name[p] - 'a'][a[i].name[p] - 'a'] = true;
+            }
+        }
+    }
+    for (int i = 0; i < 26; ++i) {
+        for (int j = 0; j < 26; ++j) {
+            if (graph[i][j]) ++in[j];
+        }
+    }
+    for (int i = 0; i < 26; ++i) {
+        if (!in[i]) que.push(i);
+    }
+    while (!que.empty()) {
+        int t = que.front();
+        que.pop();
+        for (int i = 0; i < 26; ++i) {
+            if (graph[t][i]) {
+                --in[i];
+                if (!in[i]) que.push(i);
+            }
+        }
+        ans += t + 'a';
+    }
+    if (ans.length() < 26) {
+        cout << "Impossible" << endl;
+    } else {
+        cout << ans << endl;
+    }
+    return 0;
+}
+```

+ 119 - 0
source/_posts/oi/friendly-points.md

@@ -0,0 +1,119 @@
+---
+title: Friendly Points
+tags:
+  - Binary-Search
+  - Divide-and-Conquer
+  - Monotonic Stack
+id: "3168"
+categories:
+  - [OI, Common Skill]
+  - [OI, Data Structure]
+date: 2018-05-18 10:25:30
+---
+
+## 题目描述
+
+Consider $n$ distinct points on a plane. Two points from that set are said to be _friends_, when there exists a rectangle with sides parallel to coordinate axes that contains those two points and doesn't contain any other point from the given set. A rectangle is said to _contain_ a point if the point lies within the rectangle or on its border. How many pairs of friends are there among the given points?
+
+## 题意概述
+
+给定平面上的$n$个点。定义一对点是友好点对当且仅当存在一个各边平行于坐标轴的矩形仅包含这两个点。一个点被一个矩形包含当且仅当该点在矩形内部或边界上。求有多少对友好点对。
+
+数据范围:$1 \le n \le 10^5$。
+
+## 算法分析
+
+用 CDQ 分治,把平面上的问题转化为序列上的问题。将点分成上下两个部分,考虑属于不同部分的点对有多少,递归处理两个部分。
+
+对$y$坐标分治,把两个部分中的点按$x$坐标从小到大排序,依次处理。假设处理到一个上半部分的点,当上半部分只有这个点时,下半部分可以与它构成友好点对的点的$y$坐标严格下降。因此我们可以对上半部分维护一个$y$坐标严格上升的单调栈,对下半部分维护一个$y$坐标严格下降的单调栈。在处理上半部分的某个点$p$时,用上半部分的单调栈找出$x$坐标最大的$y$坐标不大于它的点$q$,在下半部分的单调栈里二分出$x$坐标大于$q$的点有多少个,即是与$p$构成友好点对的点的个数。下半部分同理。
+
+有一些细节:$y$坐标相同的点需放在同一部分,若某一部分中所有点的$y$坐标相同,则直接加上这一部分的答案,不递归处理;若有多个$x$坐标相同的点,上半部分只考虑$y$坐标最小的,下半部分只考虑$y$坐标最大的;两个部分中$x$坐标相同的点需同时处理。
+
+## 代码
+
+```cpp
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+
+static int const N = 100005;
+static int const MAX = 1000000001;
+long long ans;
+struct Point {
+  int x, y;
+  friend bool operator<(Point const &a, Point const &b) {
+    return a.x < b.x;
+  }
+} p[N], s1[N], s2[N], tmp[N];
+
+void calc(int l, int r) {
+  if (l == r)
+    return;
+  int mid = (l + r) >> 1, L = mid - 1, R = mid + 1;
+  for (; l <= L && p[L].y == p[mid].y; --L)
+    ;
+  for (; R <= r && p[R].y == p[mid].y; ++R)
+    ;
+  if (l > L && R > r) {
+    std::sort(p + l, p + r + 1);
+    return void(ans += r - l);
+  }
+  mid = l <= L ? L : R - 1;
+  int p1 = l, p2 = mid + 1, p3 = l, t1 = 0, t2 = 0;
+  calc(l, mid), calc(mid + 1, r);
+  for (; p1 <= mid && p2 <= r;) {
+    int x = std::min(p[p1].x, p[p2].x), mx = -MAX, mn = MAX;
+    for (; p1 <= mid && p[p1].x == x; tmp[p3++] = p[p1++])
+      mx = std::max(mx, p[p1].y);
+    for (; p2 <= r && p[p2].x == x; tmp[p3++] = p[p2++])
+      mn = std::min(mn, p[p2].y);
+    for (; t1 && s1[t1 - 1].y < mx; --t1)
+      ;
+    for (; t2 && s2[t2 - 1].y > mn; --t2)
+      ;
+    Point r1 = t1 ? s1[t1 - 1] : (Point){-MAX, 0}, r2 = t2 ? s2[t2 - 1] : (Point){-MAX, 0};
+    for (; t1 && s1[t1 - 1].y == mx; --t1)
+      ;
+    for (; t2 && s2[t2 - 1].y == mn; --t2)
+      ;
+    if (mx > -MAX)
+      ans += s2 + t2 - std::upper_bound(s2, s2 + t2, r1), s1[t1++] = (Point){x, mx};
+    if (mn < MAX)
+      ans += s1 + t1 - std::upper_bound(s1, s1 + t1, r2), s2[t2++] = (Point){x, mn};
+  }
+  for (; p1 <= mid;) {
+    int x = p[p1].x, mx = -MAX;
+    for (; p1 <= mid && p[p1].x == x; tmp[p3++] = p[p1++])
+      mx = std::max(mx, p[p1].y);
+    for (; t1 && s1[t1 - 1].y < mx; --t1)
+      ;
+    Point r1 = t1 ? s1[t1 - 1] : (Point){-MAX, 0};
+    for (; t1 && s1[t1 - 1].y == mx; --t1)
+      ;
+    ans += s2 + t2 - std::upper_bound(s2, s2 + t2, r1), s1[t1++] = (Point){x, mx};
+  }
+  for (; p2 <= r;) {
+    int x = p[p2].x, mn = MAX;
+    for (; p2 <= r && p[p2].x == x; tmp[p3++] = p[p2++])
+      mn = std::min(mn, p[p2].y);
+    for (; t2 && s2[t2 - 1].y > mn; --t2)
+      ;
+    Point r2 = t2 ? s2[t2 - 1] : (Point){-MAX, 0};
+    for (; t2 && s2[t2 - 1].y == mn; --t2)
+      ;
+    ans += s1 + t1 - std::upper_bound(s1, s1 + t1, r2), s2[t2++] = (Point){x, mn};
+  }
+  for (int i = l; i <= r; ++i)
+    p[i] = tmp[i];
+}
+
+int main() {
+  int n;
+  scanf("%d", &n);
+  for (int i = 0; i < n; ++i)
+    scanf("%d%d", &p[i].x, &p[i].y);
+  std::sort(p, p + n, [](Point const &a, Point const &b) { return a.y < b.y; });
+  calc(0, n - 1), printf("%I64d\n", ans);
+  return 0;
+}
+```

+ 116 - 0
source/_posts/oi/funny-strings.md

@@ -0,0 +1,116 @@
+---
+title: Funny Strings
+tags:
+  - Constructive Algorithm
+  - Implementation
+  - Multiplicative Inverse
+id: "1605"
+categories:
+  - [OI, Common Skill]
+  - [OI, Number Theory]
+date: 2017-10-29 11:35:59
+---
+
+## 题目描述
+
+Let's consider a string of non-negative integers, containing $N$ elements. Suppose these elements are $S_1, S_2, \ldots, S_N$, in the order in which they are placed inside the string. Such a string is called 'funny' if the string $S_1+1, S_2, S_3, \ldots, S_{N-1}, S_N-1$ can be obtained by rotating the first string (to the left or to the right) several times. For instance, the strings $2, 2, 2, 3$ and $1, 2, 1, 2, 2$ are funny, but the string $1, 2, 1, 2$ is not. Your task is to find a funny string having $N$ elements, for which the sum of its elements $S_1+S_2+ \cdots +S_N$ is equal to $K$.
+
+## 题意概述
+
+定义序列的旋转操作为将序列的最后一个元素移到最前面,定义两个序列等价当且仅当可以通过若干次旋转操作使它们相同,定义一个序列是 funny 的当且仅当将它的第一个元素加一、最后一个元素减一后得到的序列和原序列等价。要求构造一个长度为$N$、元素之和为$K$的 funny 序列(所有元素均为非负整数)。
+
+数据范围:$2 \le N \le 1000, \; 1 \le K \le 30000, \; (N, K)=1$。
+
+## 算法分析
+
+我们只考虑$1 \le K \le N$的情况,因为其他情况都可以通过把所有数减去同一个数得到这种情况。
+显然,第一个数一定比最后一个数小$1$,否则新序列和原序列中某些数字出现的次数不同。假设我们有新旧序列如下:
+
+`0_______1`(旧)
+
+`1_______0`(新)
+
+新旧序列中空位上的数字是一样的。这意味着我们只要知道了旋转次数,就可以构造出序列。假设旋转次数是$a$,那么第$i$位的数就旋转到了第$((i+a-1)\%N+1)$位。下面模拟$N=9, a=7$的构造过程:
+
+第$8$位
+
+`0______11`
+
+`1______10`
+
+第$6$位
+
+`0____1_11`
+
+`1____1_10`
+
+第$4$位
+
+`0__1_1_11`
+
+`1__1_1_10`
+
+第$2$位
+
+`01_1_1_11`
+
+`11_1_1_10`
+
+第$9$位,构造完成
+
+`010101011`
+
+`110101010`
+
+由于元素之和为$K$,因此序列中有$K$个$1$,也就是说,从第$1$位开始,模拟$K$次,到达第$N$位。那么$N-1 \equiv K \times a \pmod N$,$a \equiv (N-1) \times K^{-1} \pmod N$。因为$(N, K)=1$,所以$a$一定存在,接下来只要模拟即可。当然也可以用暴力求出$a$。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+
+using namespace std;
+
+struct IOManager {
+  template <typename T> inline bool read(T &x) { char c = '\n'; bool flag = false; x = 0; while (~ c && ! isdigit(c = getchar()) && c != '-') ; c == '-' && (flag = true, c = getchar()); if (! ~ c) return false; while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar(); return (flag && (x = -x), true); }
+  inline bool read(char &c) { c = '\n'; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; return ~ c; }
+  inline int read(char s[]) { char c = '\n'; int len = 0; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; if (! ~ c) return 0; while (isprint(c) && c != ' ') s[len ++] = c, c = getchar(); return (s[len] = '\0', len); }
+  template <typename T> inline IOManager operator > (T &x) { read(x); return *this; }
+  template <typename T> inline void write(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
+  inline void write(char c) { putchar(c); }
+  inline void write(char s[]) { int pos = 0; while (s[pos] != '\0') putchar(s[pos ++]); }
+  template <typename T> inline IOManager operator < (T x) { write(x); return *this; }
+} io;
+
+struct Solver {
+private:
+  static const int N = 1010;
+  int n, k, a[N];
+  void input() { io > n > k; }
+  void init() {
+    int t = k / n;
+    for (int i = 1; i <= n; ++ i) a[i] = t, k -= t;
+  }
+  void process() {
+    int sum = n - 1;
+    while (sum % k) sum += n;
+    int p = sum / k + 1;
+    for (; p < n; p = (p - 1 + sum / k) % n + 1) ++ a[p]; ++ a[n];
+    for (int i = 1; i <= n; ++ i) io < a[i] < ' ';
+    io < '\n';
+  }
+
+public:
+  void solve() { input(), init(), process(); }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 83 - 0
source/_posts/oi/games-of-chess.md

@@ -0,0 +1,83 @@
+---
+title: Games of Chess
+tags:
+  - Constructive Algorithm
+  - Greedy
+id: "1626"
+categories:
+  - - OI
+    - Common Skill
+date: 2017-11-04 11:55:43
+---
+
+## 题目描述
+
+$N$ friends gathered in order to play chess, according to the following rules. In the first game, two of the $N$ friends will play. In the second game, the winner of the first game will play against another friend (maybe even the same friend who lost the first game). In the third game, the winner of the second game will play against someone else and so on.. No game will end as a draw (tie). Given the number of games each of the $N$ friends played, find a schedule for the games, so that the above rules are obeyed.
+
+## 题意概述
+
+有$N$个人玩游戏,第一局任意两个人参加,第一局胜利者和除他以外的一个人参加第二局(可以选择第一局失败者),第二局胜利者和除他以外的一个人参加第三局...不存在平局。给定每个人参加游戏的局数,构造一种可行的游戏方案。
+
+数据范围:$2 \le N \le 100$。
+
+## 算法分析
+
+显然每个人参加游戏的次数之和为偶数,除以$2$即是游戏局数。考虑安排每一局的胜利者和失败者。设某个人参加游戏的次数为$n$,则先安排他连续胜利$(n-1)$局,接着失败一局,直到安排了所有局的胜利者。在安排失败者的时候,要注意不能和胜利者相同,因此应尽量先安排参加游戏局数较多的人胜利,减少冲突的可能。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+#include <vector>
+
+using namespace std;
+
+struct IOManager {
+  template <typename T> inline bool read(T &x) { char c = '\n'; bool flag = false; x = 0; while (~ c && ! isdigit(c = getchar()) && c != '-') ; c == '-' && (flag = true, c = getchar()); if (! ~ c) return false; while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar(); return (flag && (x = -x), true); }
+  inline bool read(char &c) { c = '\n'; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; return ~ c; }
+  inline int read(char s[]) { char c = '\n'; int len = 0; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; if (! ~ c) return 0; while (isprint(c) && c != ' ') s[len ++] = c, c = getchar(); return (s[len] = '\0', len); }
+  template <typename T> inline IOManager operator > (T &x) { read(x); return *this; }
+  template <typename T> inline void write(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
+  inline void write(char c) { putchar(c); }
+  inline void write(char s[]) { int pos = 0; while (s[pos] != '\0') putchar(s[pos ++]); }
+  template <typename T> inline IOManager operator < (T x) { write(x); return *this; }
+} io;
+
+struct Solver {
+private:
+  static const int N = 110;
+  int n, sum;
+  pair <int, int> a[N];
+  vector <pair <int, int> > ans;
+  void input() {
+    io > n;
+    for (int i = 1; i <= n; ++ i) io > a[i].first, a[i].second = i, sum += a[i].first;
+  }
+  void process() {
+    io < sum >> 1 < '\n';
+    sort(a + 1, a + n + 1, greater <pair <int, int> >());
+    int p = 1;
+    for (int i = 0; i < sum >> 1; ++ i) {
+      if (a[p].first == 1) ans.push_back(make_pair(a[p + 1].second, a[p].second)), -- a[p].first, -- a[++ p].first;
+      else ans.push_back(make_pair(a[p].second, 0)), -- a[p].first;
+    }
+    for (int i = 0; i < sum >> 1; ++ i)
+      if (! ans[i].second)
+        for (int j = 1; j <= n; ++ j) if (a[j].first && a[j].second != ans[i].first) { ans[i].second = a[j].second, -- a[j].first; break; }
+    for (int i = 0; i < sum >> 1; ++ i) io < ans[i].first < ' ' < ans[i].second < '\n';
+  }
+
+public:
+  void solve() { input(), process(); }
+} solver;
+
+int main() {
+  solver.solve();
+  return 0;
+}
+```

+ 115 - 0
source/_posts/oi/gcd-extreme-hard.md

@@ -0,0 +1,115 @@
+---
+title: GCD Extreme (Hard)
+tags:
+  - Dirichlet Convolution
+  - Euler's Sieve
+  - GCD-LCM
+id: "1934"
+categories:
+  - - OI
+    - Number Theory
+date: 2018-01-23 07:35:32
+---
+
+## 题目描述
+
+Let $G(n)=\sum_{i=1}^n \sum_{j=i+1}^n (i, j)$.
+For example, $G(1)=0, \; G(2)=(1, 2)=1, \; G(3)=(1, 2)+(1, 3)+(2, 3)=3$.
+Given $N$, find $G(N)$ **modulo** $2^{64}$.
+
+## 题意概述
+
+令$G(n)=\sum_{i=1}^n \sum_{j=i+1}^n (i, j)$。给定$N$,求$G(N) \bmod 2^{64}$。
+
+数据范围:$1 \le N \le 235711131719$。
+
+## 算法分析
+
+推式子
+
+$$
+\begin{align}
+G(n)&=\sum_{i=1}^n \sum_{j=i+1}^n (i, j)=\sum_{i=2}^n \sum_{j=1}^{i-1} (i, j) \\\\
+&=\sum_{d=1}^n d \sum_{i=2}^{\lfloor n/d \rfloor} \sum_{j=1}^{i-1} [(i, j)=1] \\\\
+&=\sum_{d=1}^n d \sum_{i=2}^{\lfloor n/d \rfloor} \varphi(i)
+\end{align}
+$$
+
+令$S(n)=\sum_{i=1}^n \varphi(i)$。由于$\varphi \ast 1=I$,因此
+
+$$
+\begin{align}
+\sum_{i=1}^n (\varphi \ast 1)(i)&={n(n+1) \over 2} \\\\
+&=\sum_{i=1}^n \sum_{j \mid i} \varphi(j) \\\\
+&=\sum_{ij \le n} \varphi(j) \\\\
+&=\sum_{i=1}^n \sum_{j=1}^{\lfloor n/i \rfloor} \varphi(j) \\\\
+&=\sum_{i=1}^n S\left(\left\lfloor {n \over i} \right\rfloor\right)
+\end{align}
+$$
+
+那么
+
+$$
+\begin{align}
+S(n)&={n(n+1) \over 2}-\sum_{i=2}^n S\left(\left\lfloor {n \over i} \right\rfloor\right) \\\\
+G(n)&=\sum_{d=1}^n d \cdot \left(S\left(\left\lfloor {n \over d} \right\rfloor\right)-1\right)
+\end{align}
+$$
+
+接下来就可以利用分块的技巧计算。
+
+## 代码
+
+```cpp
+#include <map>
+#include <cstdio>
+
+#define int long long
+
+static const int N = 10000000;
+int prime[N], phi[N];
+bool vis[N];
+
+void init() {
+  int top = 0; phi[1] = 1;
+  for (int i = 2; i < N; ++ i) {
+    if (! vis[i]) prime[top ++] = i, phi[i] = i - 1;
+    for (int j = 0; j < top; ++ j) {
+      int k = i * prime[j]; if (k >= N) break;
+      vis[k] = true;
+      if (i % prime[j]) phi[k] = phi[i] * (prime[j] - 1); else { phi[k] = phi[i] * prime[j]; break; }
+    }
+  }
+  for (int i = 2; i < N; ++ i) phi[i] += phi[i - 1];
+}
+
+int get_sum(int n) {
+  return n & 1 ? ((n + 1) >> 1) * n : (n >> 1) * (n + 1);
+}
+
+std :: map <int, int> mp;
+
+int get_phi(int n) {
+  if (n < N) return phi[n];
+  if (mp.count(n)) return mp[n];
+  int ret = get_sum(n);
+  for (int i = 2, j; i <= n; i = j + 1)
+    j = n / (n / i), ret -= (j - i + 1) * get_phi(n / i);
+  return mp[n] = ret;
+}
+
+int calc(int n) {
+  int ret = 0;
+  for (int i = 1, j; i <= n; i = j + 1)
+    j = n / (n / i), ret += (get_sum(j) - get_sum(i - 1)) * (get_phi(n / i) - 1);
+  return ret;
+}
+
+signed main() {
+  int T; scanf("%lld", &T), init();
+  while (T --) {
+    int n; scanf("%lld", &n), printf("%llu\n", calc(n));
+  }
+  return 0;
+}
+```

+ 90 - 0
source/_posts/oi/gena-vs-petya.md

@@ -0,0 +1,90 @@
+---
+title: Gena vs Petya
+tags:
+  - Dynamic Programming
+  - Game Theory
+id: "2670"
+categories:
+  - [OI, Common Skill]
+  - [OI, Number Theory]
+date: 2018-04-30 03:15:41
+---
+
+## 题目描述
+
+Gena and Petya love playing the following game with each other. There are $n$ piles of stones, the $i$-th pile contains $a_i$ stones. The players move in turns, Gena moves first. A player moves by choosing any non-empty pile and taking an arbitrary positive number of stones from it. If the move is impossible (that is, all piles are empty), then the game finishes and the current player is considered a loser.
+
+Gena and Petya are the world famous experts in unusual games. We will assume that they play optimally.
+
+Recently Petya started to notice that Gena wins too often. Petya decided that the problem is the unjust rules as Gena always gets to move first! To even their chances, Petya decided to cheat and take and hide some stones before the game begins. Since Petya does not want Gena to suspect anything, he will take the same number of stones $x$ from each pile. This number $x$ can be an arbitrary non-negative integer, strictly less that the minimum of $a_i$ values.
+
+Your task is to find the number of distinct numbers $x$ such that Petya will win the game.
+
+## 题意概述
+
+给定$n$个正整数$a_i$,求有多少个非负整数$x$,满足$x$小于所有给定的数,且所有给定的数减去$x$之后的异或和为$0$。
+
+数据范围:$1 \le n \le 2 \times 10^5, \; 1 \le a_i \le 10^{18}$。
+
+## 算法分析
+
+Nim 游戏后手胜利的条件是所有数的异或和为$0$。
+
+枚举$x$显然不可行。可以尝试从低位到高位依次确定。
+
+考虑低位对高位的影响,易知只有当低位退位时才会改变高位。而对于每一位,若此位上有偶数个$0$,则此位可以填$1$,若有偶数个$1$则可以填$0$(这样才能使得异或和为$0$)。
+
+令$f_{i, j}$表示从低到高处理到第$i$位时,有$j$个数字在第$(i+1)$位退位的方案数。根据退位的性质,这$j$个数字一定是所有给定的数按后$i$位从小到大排序后的前$j$个。可以在枚举$i$的同时基数排序,枚举$j$的同时维护第$i$位上$0$和$1$的个数(会受退位影响),若满足条件则转移(注意此位填$1$时还要考虑此位$0$的退位)。
+
+最后需要特判一下$x$不能等于所有给定的数中最小的数。
+
+## 代码
+
+```cpp
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+
+#define int long long
+
+static int const N = 200005;
+static int const M = 65;
+int a[N], cnt[M], f[M][N], s[2][N];
+
+signed main() {
+  int n;
+  scanf("%lld", &n);
+  for (int i = 0; i < n; ++i) {
+    scanf("%lld", a + i);
+    for (int j = 0; j < M; ++j)
+      cnt[j] += a[i] >> j & 1;
+  }
+  f[0][0] = 1;
+  for (int i = 0; i < M - 1; ++i) {
+    int c0 = n - cnt[i], c1 = cnt[i], c = 0;
+    for (int j = 0; j <= n; ++j) {
+      if (j)
+        if (a[j - 1] >> i & 1)
+          ++c0, --c1;
+        else
+          --c0, ++c1, ++c;
+      if (!(c0 & 1))
+        f[i + 1][c + c0] += f[i][j];
+      if (!(c1 & 1))
+        f[i + 1][c] += f[i][j];
+    }
+    *s[0] = *s[1] = 0;
+    for (int j = 0; j < n; ++j)
+      s[a[j] >> i & 1][++*s[a[j] >> i & 1]] = a[j];
+    for (int j = 1; j <= *s[0]; ++j)
+      a[j - 1] = s[0][j];
+    for (int j = 1; j <= *s[1]; ++j)
+      a[*s[0] + j - 1] = s[1][j];
+  }
+  int sum = 0;
+  for (int i = 1; i < n; ++i)
+    sum ^= a[i] - a[0];
+  printf("%lld\n", f[M - 1][0] - (sum == 0));
+  return 0;
+}
+```

+ 219 - 0
source/_posts/oi/goodbye-souvenir.md

@@ -0,0 +1,219 @@
+---
+title: Goodbye Souvenir
+tags:
+  - Binary Indexed Tree
+  - Divide-and-Conquer
+id: "1489"
+categories:
+  - [OI, Common Skill]
+  - [OI, Data Structure]
+date: 2017-09-19 20:50:30
+---
+
+## 题目描述
+
+I won't feel lonely, nor will I be sorrowful... not before everything is buried.
+
+A string of $n$ beads is left as the message of leaving. The beads are numbered from $1$ to $n$ from left to right, each having a shape numbered by integers between $1$ and $n$ inclusive. Some beads may have the same shapes.
+
+The memory of a shape $x$ in a certain subsegment of beads, is defined to be the difference between the last position and the first position that shape $x$ appears in the segment. The memory of a subsegment is the sum of memories over all shapes that occur in it.
+
+From time to time, shapes of beads change as well as the memories. Sometimes, the past secreted in subsegments are being recalled, and you are to find the memory for each of them.
+
+## 题意概述
+
+给定一个长度为$n$的序列$a_i$,有$m$次操作。操作有两种:给定$p, x$,将第$p$个数修改成$x$;给定$l, r$,求$[l, r]$内每种数字的长度之和。某种数字长度的定义是该数字在区间内第一次出现的位置与最后一次出现的位置之差。
+
+数据范围:$1 \le n, m \le 10^5, \; 1 \le a_i, x \le n$。
+
+## 算法分析
+
+设区间$[l, r]$中某种数字出现的位置为$p_1, p_2, \ldots, p_k$,显然这个数字对答案的贡献为$p_k-p_1=(p_2-p_1)+(p_3-p_2)+ \cdots +(p_k-p_{k-1})$。令$pre_i$表示在$i$之前离$i$最近的等于$a_i$的数的位置,那么贡献就是$(p_2-pre_2)+(p_3-pre_3)+ \cdots +(p_k-pre_k)$。我们把每个数$a_i$想像成二维空间中的一个点$(pre_i, i)$,它的权值是$i-pre_i$。那么问题就变成了求在矩形$(l, l)-(r, r)$中的点权之和。这可以用传说中的 CDQ 分治来解决。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+#include <set>
+
+#define int long long
+
+using namespace std;
+
+void maxify(int &a, int b) { b > a && (a = b); }
+void minify(int &a, int b) { b < a && (a = b); }
+
+struct IOManager {
+  template <typename T>
+  inline bool read(T &x) {
+    char c; bool flag = false; x = 0;
+    while (~ c && ! isdigit(c = getchar()) && c != '-') ;
+    c == '-' && (flag = true, c = getchar());
+    if (! ~ c) return false;
+    while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar();
+    return (flag && (x = -x), true);
+  }
+  inline bool read(char &c) {
+    c = '\n';
+    while (~ c && ! (isprint(c = getchar()) && c != ' ')) ;
+    return ~ c;
+  }
+  inline int read(char s[]) {
+    char c; int len = 0;
+    while (~ c && ! (isprint(c = getchar()) && c != ' ')) ;
+    if (! ~ c) return 0;
+    while (isprint(c) && c != ' ') s[len ++] = c, c = getchar();
+    return (s[len] = '\0', len);
+  }
+  template <typename T>
+  inline IOManager operator > (T &x) {
+    read(x); return *this;
+  }
+  template <typename T>
+  inline void write(T x) {
+    x < 0 && (putchar('-'), x = -x);
+    x > 9 && (write(x / 10), true);
+    putchar(x % 10 + '0');
+  }
+  inline void write(char c) {
+    putchar(c);
+  }
+  inline void write(char s[]) {
+    int pos = 0;
+    while (s[pos] != '\0') putchar(s[pos ++]);
+  }
+  template <typename T>
+  inline IOManager operator < (T x) {
+    write(x); return *this;
+  }
+} io;
+
+struct BinaryIndexedTree {
+private:
+  static const int N = 200000;
+  int n, a[N];
+
+public:
+  void init(int t) {
+    n = t, memset(a, 0, sizeof a);
+  }
+  void add(int p, int v) {
+    for (; p <= n; p += p & -p) a[p] += v;
+  }
+  int get(int p) {
+    int ret = 0;
+    for (; p; p -= p & -p) ret += a[p];
+    return ret;
+  }
+};
+
+struct Set {
+private:
+  static const int N = 200000;
+  int a[N];
+  set <int> s[N];
+  set <int> :: iterator it;
+
+public:
+  void modify(int p, int v) {
+    if (a[p]) s[a[p]].erase(p);
+    a[p] = v;
+    s[a[p]].insert(p);
+  }
+  int prev(int p) {
+    it = s[a[p]].find(p);
+    return it == s[a[p]].begin() ? *it : *(-- it);
+  }
+  int next(int p) {
+    it = ++ s[a[p]].find(p);
+    return it == s[a[p]].end() ? *(-- it) : *it;
+  }
+};
+
+#define MODIFY 1
+#define QUERY 2
+
+struct Solver {
+private:
+  static const int N = 200000;
+  int n, m, numq, top, pos[N], pre[N], ans[N];
+  BinaryIndexedTree tree;
+  Set s;
+  struct Query {
+    int type, x, y, w, id;
+    bool operator < (const Query &q) const {
+      return x == q.x ? type < q.type : x < q.x;
+    }
+  } q[N << 2], tmp[N << 2];
+  void add_query(int type, int x, int y, int w, int id) {
+    q[++ numq] = (Query) { type, x, y, w, id };
+  }
+  void input() {
+    io > n > m;
+    for (int i = 2; i <= n + 1; ++ i) {
+      int t; io > t, s.modify(i, t);
+      int prev = s.prev(i);
+      add_query(MODIFY, prev, i, i - prev, 0);
+    }
+    for (int i = 1; i <= m; ++ i) {
+      int o; io > o;
+      if (o == 1) {
+        int p, x; io > p > x; ++ p;
+        int prev = s.prev(p), next = s.next(p);
+        if (prev < p) add_query(MODIFY, prev, p, prev - p, 0);
+        if (next > p) add_query(MODIFY, p, next, p - next, 0);
+        if (prev < p && next > p) add_query(MODIFY, prev, next, next - prev, 0);
+        s.modify(p, x);
+        prev = s.prev(p), next = s.next(p);
+        if (prev < p && next > p) add_query(MODIFY, prev, next, prev - next, 0);
+        if (prev < p) add_query(MODIFY, prev, p, p - prev, 0);
+        if (next > p) add_query(MODIFY, p, next, next - p, 0);
+      } else {
+        int l, r; io > l > r, ++ l, ++ r, ++ top;
+        add_query(QUERY, r, r, 1, top);
+        add_query(QUERY, r, l - 1, -1, top);
+        add_query(QUERY, l - 1, r, -1, top);
+        add_query(QUERY, l - 1, l - 1, 1, top);
+      }
+    }
+  }
+  void process(int l, int r) {
+    if (l == r) return;
+    int mid = l + r >> 1;
+    process(l, mid), process(mid + 1, r);
+    int s = l, t = mid + 1, pnt = l;
+    while (s <= mid && t <= r) {
+      if (q[s] < q[t]) {
+        if (q[s].type == MODIFY) tree.add(q[s].y, q[s].w);
+        tmp[pnt ++] = q[s ++];
+      } else {
+        if (q[t].type == QUERY) ans[q[t].id] += q[t].w * tree.get(q[t].y);
+        tmp[pnt ++] = q[t ++];
+      }
+    }
+    while (t <= r) {
+      if (q[t].type == QUERY) ans[q[t].id] += q[t].w * tree.get(q[t].y);
+      tmp[pnt ++] = q[t ++];
+    }
+    for (int i = l; i < s; ++ i) if (q[i].type == MODIFY) tree.add(q[i].y, -q[i].w);
+    while (s <= mid) tmp[pnt ++] = q[s ++];
+    for (int i = l; i <= r; ++ i) q[i] = tmp[i];
+  }
+
+public:
+  void solve() {
+    input(), tree.init(n + 1), process(1, numq);
+    for (int i = 1; i <= top; ++ i) io < ans[i] < '\n';
+  }
+} solver;
+
+signed main() {
+  solver.solve();
+  return 0;
+}
+```

+ 54 - 0
source/_posts/oi/hack-it.md

@@ -0,0 +1,54 @@
+---
+title: Hack It!
+tags:
+  - Constructive Algorithm
+id: "1706"
+categories:
+  - - OI
+    - Common Skill
+date: 2018-01-10 09:30:20
+---
+
+## 题目描述
+
+Little X has met the following problem recently.
+
+Let's define $f(x)$ as the sum of digits in decimal representation of number $x$ (for example, $f(1234)=1+2+3+4$). You are to calculate $\sum_{i=l}^r f(i) \bmod a$.
+
+Of course Little X has solved this problem quickly, has locked it, and then has tried to hack others. He has seen the following C++ code:
+
+```
+ans = solve(l, r) % a;
+if (ans <= 0) ans += a;
+```
+
+This code will fail only on the test with $\sum_{i=l}^r f(i) \equiv 0 \pmod a$. You are given number $a$, help Little X to find a proper test for hack.
+
+## 题意概述
+
+令$f(i)$表示$i$各位数字之和。给定$a$,要求找出一对$(l, r)$满足$\sum_{i=l}^r f(i) \equiv 0 \pmod a$。
+
+数据范围:$1 \le a \le 10^{18}$。
+
+## 算法分析
+
+思路很巧妙。有一个显而易见的结论:$\forall i \in [1, 10^{18}], \; f(i)+1=f(i+10^{18})$。它有一个推论:$\forall x \in [1, 10^{18}], \; 1+\sum_{i=x}^{x+10^{18}-1} f(i)=\sum_{i=x+1}^{x+10^{18}} f(i)$。因此只要算出$t=\sum_{i=1}^{10^{18}} f(i) \bmod a$,将区间右移$a-t$单位。
+
+## 代码
+
+```cpp
+#include <cstdio>
+
+long long mul(long long a, long long b, long long mod) {
+  long long ret = 0;
+  while (b) b & 1 && ((ret += a) %= mod), (a <<= 1) %= mod, b >>= 1;
+  return ret;
+}
+
+int main() {
+  long long n; scanf("%lld", &n);
+  long long cur = mul(1800000000000000000ll, 45, n) + 1, l = 1, r = 1000000000000000000ll;
+  l += n - cur, r += n - cur, printf("%lld %lld\n", l, r);
+  return 0;
+}
+```

+ 96 - 0
source/_posts/oi/hardwood-floor.md

@@ -0,0 +1,96 @@
+---
+title: Hardwood Floor
+tags:
+  - Bitmask
+  - Dynamic Programming
+id: "1596"
+categories:
+  - - OI
+    - Common Skill
+date: 2017-10-27 07:40:28
+---
+
+## 题目描述
+
+The banquet hall of Computer Scientists' Palace has a rectangular form of the size $M \times N$. It is necessary to lay hardwood floors in the hall. There are wood pieces of two forms:
+
+- rectangles ($2 \times 1$)
+- corners (squares $2 \times 2$ without one $1 \times 1$ square)
+
+You have to determine $X$ - the number of ways to cover the banquet hall.
+
+Remarks. The number of pieces is large enough. It is not allowed to leave empty places, or to cover any part of a surface twice, or to saw pieces.
+
+## 题意概述
+
+有一个$M \times N$的网格,要求用两种方块覆盖它,一种是$2 \times 1$的方块,一种是$2 \times 2$挖去$1 \times 1$的方块。方块可以旋转。求方案数。
+
+数据范围:$1 \le M, N \le 9$。
+
+## 算法分析
+
+用$f_{i, j}$表示前$(i-1)$行都已填满且第$i$行的状态为$j$的方案数,转移时枚举$j$可以转移到的状态。暴力计算即可发现合法的转移不会太多。
+
+## 代码
+
+```cpp
+#include <cmath>
+#include <cctype>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+
+#define int long long
+
+using namespace std;
+
+struct IOManager {
+  template <typename T> inline bool read(T &x) { char c = '\n'; bool flag = false; x = 0; while (~ c && ! isdigit(c = getchar()) && c != '-') ; c == '-' && (flag = true, c = getchar()); if (! ~ c) return false; while (isdigit(c)) x = (x << 3) + (x << 1) + c - '0', c = getchar(); return (flag && (x = -x), true); }
+  inline bool read(char &c) { c = '\n'; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; return ~ c; }
+  inline int read(char s[]) { char c = '\n'; int len = 0; while (~ c && ! (isprint(c = getchar()) && c != ' ')) ; if (! ~ c) return 0; while (isprint(c) && c != ' ') s[len ++] = c, c = getchar(); return (s[len] = '\0', len); }
+  template <typename T> inline IOManager operator > (T &x) { read(x); return *this; }
+  template <typename T> inline void write(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
+  inline void write(char c) { putchar(c); }
+  inline void write(char s[]) { int pos = 0; while (s[pos] != '\0') putchar(s[pos ++]); }
+  template <typename T> inline IOManager operator < (T x) { write(x); return *this; }
+} io;
+
+struct Solver {
+private:
+  static const int N = 11;
+  int n, m, f[N][1 << N];
+  void input() { io > n > m; }
+  void update(int x, int y, int s, int val, int p) {
+    if (y == (1 << m) - 1) { f[x][s] += val; return; }
+    for (int i = p; i < m; ++ i)
+      if (! (y & 1 << i)) {
+        if (! (s & 1 << i)) {
+          update(x, y | 1 << i, s | 1 << i, val, i + 1);
+          if (i && ! (s & 1 << i - 1)) update(x, y | 1 << i, s | 1 << i | 1 << i - 1, val, i + 1);
+          if (i < m - 1 && ! (s & 1 << i + 1)) update(x, y | 1 << i, s | 1 << i | 1 << i + 1, val, i + 1);
+        }
+        if (i < m - 1 && ! (y & 1 << i + 1)) {
+          update(x, y | 1 << i | 1 << i + 1, s, val, i + 2);
+          if (! (s & 1 << i)) update(x, y | 1 << i | 1 << i + 1, s | 1 << i, val, i + 2);
+          if (! (s & 1 << i + 1)) update(x, y | 1 << i | 1 << i + 1, s | 1 << i + 1, val, i + 2);
+        }
+      }
+  }
+  void process() {
+    f[0][(1 << m) - 1] = 1;
+    for (int i = 0; i <= n; ++ i)
+      for (int j = 0; j < 1 << m; ++ j)
+        if (f[i][j]) update(i + 1, j, 0, f[i][j], 0);
+    io < f[n + 1][0] < '\n';
+  }
+
+public:
+  void solve() { input(), process(); }
+} solver;
+
+signed main() {
+  solver.solve();
+  return 0;
+}
+```

+ 230 - 0
source/_posts/oi/hide.md

@@ -0,0 +1,230 @@
+---
+title: Hide
+tags:
+  - Centroid Decomposition
+  - Lowest Common Ancestor
+  - Priority Queue
+  - Tree
+id: "2123"
+categories:
+  - [OI, Common Skill]
+  - [OI, Data Structure]
+  - [OI, Graph Theory]
+date: 2018-02-20 19:10:22
+---
+
+## 题意概述
+
+有一棵$N$个节点的树,每个节点都是黑色或白色。有$Q$次操作,动态修改点的颜色,询问最远黑点对之间的距离。
+
+数据范围:$1 \le N \le 10^5, \; 1 \le Q \le 5 \times 10^5$。
+
+## 算法分析
+
+考虑点分治,构造一棵重心树。为了方便,下面所有距离均表示在原树上的距离,堆均为大根堆。
+
+设节点$i$在重心树上的父亲为$p_i$。对于每个节点$u$维护两个堆,第一个堆用来维护重心树上$u$子树中的所有黑点到$p_u$的距离,第二个堆用来维护重心树上$u$各个子树中距离$u$最远的黑点到$u$的距离。那么第二个堆可以借助第一个堆来维护。
+
+接着需要一个全局的堆,用来维护所有节点第二个堆的前两个元素之和(经过该点的路径)以及所有黑点第二个堆的第一个元素(以该点为端点的路径)。
+
+修改时,沿着重心树往上依次处理经过的节点,先在全局的堆中删除,再在第二个堆中删除,在第一个堆中修改后,维护第二个堆和全局的堆。
+
+## 代码
+
+```cpp
+/*
+ * Computer programmers do it byte by byte.
+ */
+
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+#include <queue>
+
+template <typename T> void read(T &n) {
+  char c;
+  for (; (c = getchar()) < '0' || c > '9';)
+    ;
+  for (n = c - '0'; (c = getchar()) >= '0' && c <= '9'; (n *= 10) += c - '0')
+    ;
+}
+
+typedef int const ic;
+
+static ic N = 100005;
+
+class heap {
+private:
+  std::priority_queue<int> que, buf;
+
+void clear() {
+    for (; !que.empty() && !buf.empty() && que.top() == buf.top();
+         que.pop(), buf.pop())
+      ;
+  }
+
+public:
+  void push(ic &n) {
+    if (n)
+      que.push(n);
+  }
+
+void erase(ic &n) {
+    if (n)
+      buf.push(n);
+  }
+
+void pop() {
+    clear();
+    if (!que.empty())
+      que.pop();
+  }
+
+int top() {
+    clear();
+    return que.empty() ? 0 : que.top();
+  }
+
+int top2() {
+    clear();
+    if (que.empty())
+      return 0;
+    int tmp = que.top(), ret = tmp;
+    que.pop(), clear();
+    if (que.empty()) {
+      que.push(tmp);
+      return 0;
+    }
+    ret += que.top(), que.push(tmp);
+    return ret;
+  }
+} global;
+
+namespace tree {
+int n, nume, h[N], col[N];
+int tim, fi[N], dep[N], lca[N << 1][20];
+int power[N << 1];
+int root, up[N], size[N], mx[N], vis[N];
+heap toup[N], me[N];
+struct Edge {
+  int v, nxt;
+} e[N << 1];
+
+void add_edge(ic &u, ic &v) {
+  e[++nume] = (Edge){v, h[u]}, h[u] = nume;
+  e[++nume] = (Edge){u, h[v]}, h[v] = nume;
+}
+
+void dfs(ic &t, ic &fa) {
+  lca[++tim][0] = t, fi[t] = tim, dep[t] = dep[fa] + 1;
+  for (int i = h[t]; i; i = e[i].nxt)
+    if (e[i].v != fa)
+      dfs(e[i].v, t), lca[++tim][0] = t;
+}
+
+void init() {
+  for (int i = 2; i <= tim; i <<= 1)
+    ++power[i];
+  for (int i = 1; i <= tim; ++i)
+    power[i] += power[i - 1];
+  for (int i = 1; i < 20; ++i)
+    for (int j = 1; j <= tim; ++j) {
+      ic k = std::min(tim, j + (1 << i - 1));
+      lca[j][i] = dep[lca[j][i - 1]] < dep[lca[k][i - 1]] ? lca[j][i - 1]
+                                                          : lca[k][i - 1];
+    }
+}
+
+int get_lca(ic &u, ic &v) {
+  ic l = std::min(fi[u], fi[v]), r = std::max(fi[u], fi[v]);
+  ic len = power[r - l + 1], k = r - (1 << len) + 1;
+  return dep[lca[l][len]] < dep[lca[k][len]] ? lca[l][len] : lca[k][len];
+}
+
+int get_dist(ic &u, ic &v) {
+  ic lca = get_lca(u, v);
+  return dep[u] + dep[v] - 2 * dep[lca];
+}
+
+int get_size(ic &t, ic &fa) {
+  int ret = 1;
+  for (int i = h[t]; i; i = e[i].nxt)
+    if (!vis[e[i].v] && e[i].v != fa)
+      ret += get_size(e[i].v, t);
+  return ret;
+}
+
+void get_root(ic &t, ic &fa, ic &tot) {
+  size[t] = 1, mx[t] = 0;
+  for (int i = h[t]; i; i = e[i].nxt)
+    if (!vis[e[i].v] && e[i].v != fa)
+      get_root(e[i].v, t, tot), size[t] += size[e[i].v],
+          mx[t] = std::max(mx[t], size[e[i].v]);
+  (mx[t] = std::max(mx[t], tot - size[t])) < mx[root] && (root = t);
+}
+
+void init_heap(ic &t, ic &fa, ic &dep, heap &hp) {
+  hp.push(dep);
+  for (int i = h[t]; i; i = e[i].nxt)
+    if (!vis[e[i].v] && e[i].v != fa)
+      init_heap(e[i].v, t, dep + 1, hp);
+}
+
+void build(int t) {
+  vis[t] = true;
+  for (int i = h[t]; i; i = e[i].nxt)
+    if (!vis[e[i].v])
+      root = 0,
+      get_root(e[i].v, 0,
+               size[e[i].v] < size[t] ? size[e[i].v] : get_size(e[i].v, t)),
+      up[root] = t, init_heap(e[i].v, t, 1, toup[root]),
+      me[t].push(toup[root].top()), build(root);
+  global.push(me[t].top()), global.push(me[t].top2());
+}
+
+void modify(int t) {
+  ic p = t;
+  if (col[t])
+    global.erase(me[t].top());
+  else
+    global.push(me[t].top());
+  for (; up[t]; t = up[t]) {
+    if (col[up[t]])
+      global.erase(me[up[t]].top());
+    global.erase(me[up[t]].top2());
+    me[up[t]].erase(toup[t].top());
+    if (col[p])
+      toup[t].erase(get_dist(p, up[t]));
+    else
+      toup[t].push(get_dist(p, up[t]));
+    me[up[t]].push(toup[t].top());
+    if (col[up[t]])
+      global.push(me[up[t]].top());
+    global.push(me[up[t]].top2());
+  }
+  col[p] = !col[p];
+}
+} // namespace tree
+
+int main() {
+  read(tree::n), tree::mx[0] = tree::n;
+  for (int i = 1, u, v; i < tree::n; ++i)
+    read(u), read(v), tree::add_edge(u, v);
+  tree::dfs(1, 0), tree::init(), tree::get_root(1, 0, tree::n),
+      tree::build(tree::root);
+  for (int i = 1; i <= tree::n; ++i)
+    tree::col[i] = 1;
+  int q;
+  for (read(q); q--;) {
+    char c;
+    scanf(" %c", &c);
+    if (c == 'G')
+      printf("%d\n", global.top());
+    else {
+      int p;
+      read(p), tree::modify(p);
+    }
+  }
+  return 0;
+}
+```

+ 69 - 0
source/_posts/oi/high-load.md

@@ -0,0 +1,69 @@
+---
+title: High Load
+tags:
+  - Greedy
+  - Tree
+id: "1153"
+categories:
+  - [OI, Common Skill]
+  - [OI, Graph Theory]
+date: 2017-07-12 04:10:10
+---
+
+## 题目描述
+
+Arkady needs your help again! This time he decided to build his own high-speed Internet exchange point. It should consist of $n$ nodes connected with minimum possible number of wires into one network (a wire directly connects two nodes). Exactly $k$ of the nodes should be exit-nodes, that means that each of them should be connected to exactly one other node of the network, while all other nodes should be connected to at least two nodes in order to increase the system stability.
+
+Arkady wants to make the system as fast as possible, so he wants to minimize the maximum distance between two exit-nodes. The distance between two nodes is the number of wires a package needs to go through between those two nodes.
+
+Help Arkady to find such a way to build the network that the distance between the two most distant exit-nodes is as small as possible.
+
+## 题意概述
+
+构造一棵有$n$个节点的树,使得这棵树恰有$k$个叶子节点,且直径最小。
+
+数据范围:$3 \le n \le 2 \times 10^5, \; 2 \le k \le n-1$。
+
+## 算法分析
+
+首先确定一个根节点,从它引出$k$个叶子节点。接着从这$k$个叶子节点一层一层往外扩展,每次扩展一个节点,直到树中的节点数为$n$。此时树的直径就是深度最大的两个节点的深度和。证明如下:
+
+> 假设根节点连出的某棵子树中有两个以上的叶子节点,我们可以找出它们的 LCA,并将 LCA 和它到根节点路径上的所有点“分裂”成两个节点。由于这个操作使得树中的节点数增加,因此需要删去一些叶子节点。这样并不会使树的直径增加。
+
+由此得证。
+
+## 代码
+
+```cpp
+#include <cstdio>
+#include <queue>
+using namespace std;
+struct edge {
+  int v, nxt;
+} e[400001];
+int n, m, nume, h[200001], depth[200001];
+queue<int> que;
+void add_edge(int u, int v) {
+  e[++nume].v = v, e[nume].nxt = h[u], h[u] = nume;
+}
+int main()
+{
+  scanf("%d%d", &n, &m);;
+  for (int i = 1; i <= m; ++i) {
+    add_edge(1, i + 1), depth[i + 1] = 1;
+    que.push(i + 1);
+  }
+  for (int i = m + 2; i <= n; ++i) {
+    int u = que.front(); que.pop();
+    add_edge(u, i), depth[i] = depth[u] + 1;
+    que.push(i);
+  }
+  printf("%d\n", depth[n] + depth[n - 1]);
+  for (int i = 1; i <= n; ++i) {
+    for (int j = h[i]; j; j = e[j].nxt) {
+      printf("%d %d\n", i, e[j].v);
+    }
+  }
+  return 0;
+}
+```

+ 152 - 0
source/_posts/oi/imbalanced-array.md

@@ -0,0 +1,152 @@
+---
+title: Imbalanced Array
+tags:
+  - Divide-and-Conquer
+  - Dynamic Programming
+  - Inclusion-Exclusion Principle
+  - Monotonic Stack
+  - Union Find
+id: "544"
+categories:
+  - [OI, Common Skill]
+  - [OI, Data Structure]
+date: 2017-06-20 18:45:12
+---
+
+## 题目描述
+
+You are given an array $a$ consisting of $n$ elements. The imbalance value of some subsegment of this array is the difference between the maximum and minimum element from this segment. The imbalance value of the array is the sum of imbalance values of all subsegments of this array.
+
+For example, the imbalance value of array $[1, 4, 1]$ is $9$, because there are $6$ different subsegments of this array:
+
+- $[1]$ (from index $1$ to index $1$), imbalance value is $0$;
+- $[1, 4]$ (from index $1$ to index $2$), imbalance value is $3$;
+- $[1, 4, 1]$ (from index $1$ to index $3$), imbalance value is $3$;
+- $[4]$ (from index $2$ to index $2$), imbalance value is $0$;
+- $[4, 1]$ (from index $2$ to index $3$), imbalance value is $3$;
+- $[1]$ (from index $3$ to index $3$), imbalance value is $0$.
+
+You have to determine the imbalance value of the array $a$.
+
+## 题意概述
+
+给定一个长度为$n$的$a$数组,求
+
+$$
+\sum_{1 \le i \le j \le n} (max_{i, j}-min_{i, j})
+$$
+
+其中$max_{i, j}$表示$a_i, a_{i+1}, a_{i+2}, \ldots, a_j$中的最大值,$min_{i, j}$表示$a_i, a_{i+1}, a_{i+2}, \ldots, a_j$中的最小值。
+
+数据范围:$1 \le n \le 10^6, \; 1 \le a_i \le 10^6$。
+
+## 算法分析 1
+
+考虑对于第$i$个数$a_i$,设它作为最大值出现的次数为$mx_i$,作为最小值出现的次数为$mn_i$。那么$ans=\sum_{i=1}^n(mx_i-mn_i)a_i$。接下来的问题就变成了求$mx_i, mn_i$。
+
+对于区间$[l, r]$,我们找出其中的最大(小)值,设为$a_j$,那么$a_j$在$[l, r]$上的$mx_i$($mn_i$)就等于$(j-l+1)(r-j+1)$。可以发现,求解区间$[l, j-1]$和$[j+1, r]$是这个问题的子问题,而且规模更小,可以递归求解。只需在开始时先将数组从大到小(从小到大)排序,令$l=1, r=n$,即可解决原问题。
+
+在求解时可以倒过来思考,即从小到大(从大到小)枚举,并将连续的区间合并,用并查集维护。理论时间复杂度为$O(n\log n)$。
+
+## 代码 1
+
+```cpp
+#include <iostream>
+#include <algorithm>
+#include <cstring>
+using namespace std;
+struct number {
+    long long num, id, rec;
+} a[1000001];
+bool cmp(number a, number b) {return a.num < b.num;}
+long long n, fa[1000002], s[1000002], ans;
+int get_fa(long long t) {
+    return t == fa[t] || fa[t] == 0 ? fa[t] : fa[t] = get_fa(fa[t]);
+}
+void process(int t) {
+    int i, j;
+    if (t == 1) i = 1, j = n + 1;
+    else i = n, j = 0;
+    for (; i != j; i += t) {
+        fa[a[i].id] = a[i].id;
+        s[a[i].id] = 1;
+        long long p = get_fa(a[i].id - 1), q = get_fa(a[i].id + 1);
+        a[i].rec = (s[p] + 1) * (s[q] + 1);
+        if (s[p]) {
+            s[p] += s[get_fa(a[i].id)];
+            fa[get_fa(a[i].id)] = p;
+        }
+        if (s[q]) {
+            s[q] += s[get_fa(a[i].id)];
+            fa[get_fa(a[i].id)] = q;
+        }
+    }
+}
+int main()
+{
+    cin >> n;
+    for (int i = 1; i <= n; ++i) {
+        cin >> a[i].num;
+        a[i].id = i;
+    }
+    sort(a + 1, a + n + 1, cmp);
+    process(1);
+    for (int i = 1; i <= n; ++i) {
+        ans += a[i].num * a[i].rec;
+    }
+    memset(fa, 0, sizeof(fa));
+    memset(s, 0, sizeof(s));
+    process(-1);
+    for (int i = 1; i <= n; ++i) {
+        ans -= a[i].num * a[i].rec;
+    }
+    cout << ans << endl;
+    return 0;
+}
+```
+
+## 算法分析 2
+
+设$l_i, r_i$分别表示$a_i$作为区间$[l, r]$中的最大(小)值时,$l$的最小值和$r$的最大值。那么对于$a_i$来讲,$mx_i(mn_i)=(i-l_i+1)(r_i-i+1)$。$l_i, r_i$均可以$O(n)$维护(DP 或单调栈),从而避免了排序。
+
+注意到有多个数相等时会有重复计算的区间。根据容斥原理,在判断时应一边取等号一边不取等号。
+
+## 代码 2
+
+```cpp
+#include <iostream>
+using namespace std;
+long long n, a[1000001], l[1000001], r[1000001], ans;
+void process(int t) {
+    for (int i = 1; i <= n; ++i) {
+        l[i] = r[i] = i;
+    }
+    for (int i = 2; i <= n; ++i) {
+        int now = i;
+        while (now > 1 && a[i] * t >= a[now - 1] * t) now = l[now - 1];
+        l[i] = now;
+    }
+    for (int i = n - 1; i; --i) {
+        int now = i;
+        while (now < n && a[i] * t > a[now + 1] * t) now = r[now + 1];
+        r[i] = now;
+    }
+}
+int main()
+{
+    cin >> n;
+    for (int i = 1; i <= n; ++i) {
+        cin >> a[i];
+    }
+    process(1);
+    for (int i = 1; i <= n; ++i) {
+        ans += a[i] * (i - l[i] + 1) * (r[i] - i + 1);
+    }
+    process(-1);
+    for (int i = 1; i <= n; ++i) {
+        ans -= a[i] * (i - l[i] + 1) * (r[i] - i + 1);
+    }
+    cout << ans << endl;
+    return 0;
+}
+```

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio