diff --git a/2021/02/11/at1899-hua-xiang-chu-li-gao-qiao-jun-ti-jie/index.html b/2021/02/11/at1899-hua-xiang-chu-li-gao-qiao-jun-ti-jie/index.html index 1eef05ce0a..b3d9492fc2 100644 --- a/2021/02/11/at1899-hua-xiang-chu-li-gao-qiao-jun-ti-jie/index.html +++ b/2021/02/11/at1899-hua-xiang-chu-li-gao-qiao-jun-ti-jie/index.html @@ -468,30 +468,37 @@

AT1899 画像処理高橋君 题
-

AT1899 画像処理高橋君 题解

题目链接:AT1899 画像処理高橋君

+

AT1899 画像処理高橋君 题解

+

题目链接:AT1899 +画像処理高橋君

原题是日文的,我就不翻译了(

-

题意:给出压缩后的图像,求压缩前的图像
压缩是指对于各个像素,在其周围8个方向的像素中,只要有一个黑色像素,其像素就会变黑的处理

+

题意:给出压缩后的图像,求压缩前的图像 +压缩是指对于各个像素,在其周围8个方向的像素中,只要有一个黑色像素,其像素就会变黑的处理

-

从题意中第二句话可以初步推断出,只要是周围8个方向上都是黑色的像素就是压缩前存在的黑色像素
例如

+

从题意中第二句话可以初步推断出,只要是周围8个方向上都是黑色的像素就是压缩前存在的黑色像素 +例如

###..
 ###..
 .....
-

压缩前的图像就是

##...
+

压缩前的图像就是

##...
 .....
-.....

_这么说来只要扫一遍,把和白色像素相接的黑色像素全部变为白色不就好了?_

-

但是如果是下面这种情况

###.
+.....
+这么说来只要扫一遍,把和白色像素相接的黑色像素全部变为白色不就好了?

+

但是如果是下面这种情况

###.
 ##.#
 ..##
-..##

用刚才的思路做,会得到这样的图像
#...
+..##
用刚才的思路做,会得到这样的图像 +
#...
 ....
 ....
-...#

这样就出现了问题

-

如果把得到的这个图像压缩,得到的应该是

#...          ##..
+...#
这样就出现了问题

+

如果把得到的这个图像压缩,得到的应该是

#...          ##..
 ....    ->    ##..
 ....          ..##
-...#          ..##

这样就还得再检查一遍得到的图像是否合法

-

代码如下

#include<bits/stdc++.h>
+...#          ..##
+这样就还得再检查一遍得到的图像是否合法

+

代码如下

#include<bits/stdc++.h>
 using namespace std;
 int n,m;
 char a[205][205];//记录压缩后的图像(即输入的图像)
@@ -905,7 +912,7 @@ 

 站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/02/11/cf652b-z-sort-ti-jie/index.html b/2021/02/11/cf652b-z-sort-ti-jie/index.html index 5274c4c771..37a8d6ffb7 100644 --- a/2021/02/11/cf652b-z-sort-ti-jie/index.html +++ b/2021/02/11/cf652b-z-sort-ti-jie/index.html @@ -472,7 +472,9 @@

CF652B z-sort 题解

-

CF652B z-sort 题解

题目链接:CF652B z-sort

+

CF652B z-sort 题解

+

题目链接:CF652B +z-sort

题意:一种叫Z排序的方法,奇数位递增,偶数位递减,给定数组请用此方法排序

@@ -864,7 +866,7 @@

  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/02/11/cf676a-nicholas-and-permutation-ti-jie/index.html b/2021/02/11/cf676a-nicholas-and-permutation-ti-jie/index.html index 165bd215ff..ce6afce8e0 100644 --- a/2021/02/11/cf676a-nicholas-and-permutation-ti-jie/index.html +++ b/2021/02/11/cf676a-nicholas-and-permutation-ti-jie/index.html @@ -468,17 +468,29 @@

CF676A Nicholas and Permutation
-

CF676A Nicholas and Permutation 题解

题目链接:CF676A Nicholas and Permutation

+

CF676A Nicholas and +Permutation 题解

+

题目链接:CF676A +Nicholas and Permutation

题意:给定数组,可以让两个数的位置交换,让最大值和最小值的位置的差的绝对值最大

-

先用$c$记录最大值位置,$d$记录最小值位置

-

然后取$4$种情况中绝对值最大的

-

第$1$种,$d$与第$1$个元素交换

-

第$2$种,$d$与第$n$个元素交换

-

第$3$种,$c$与第$1$个元素交换

-

第$4$种,$c$与第$n$个元素交换

-

代码实现还是比较简单的

#include<bits/stdc++.h>
+

先用\(c\)记录最大值位置,\(d\)记录最小值位置

+

然后取\(4\)种情况中绝对值最大的

+

\(1\)种,\(d\)与第\(1\)个元素交换

+

\(2\)种,\(d\)与第\(n\)个元素交换

+

\(3\)种,\(c\)与第\(1\)个元素交换

+

\(4\)种,\(c\)与第\(n\)个元素交换

+

代码实现还是比较简单的

#include<bits/stdc++.h>
 using namespace std;
 #define R register
 int n,k,a=-1,b=INT_MAX,c,d;//最大值初始化为-1,最小值初始化为很大的数(2147483647)
@@ -859,7 +871,7 @@ 

 站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/02/11/cf708a-letters-cyclic-shift-ti-jie/index.html b/2021/02/11/cf708a-letters-cyclic-shift-ti-jie/index.html index b586d32c48..65c6ca3dbf 100644 --- a/2021/02/11/cf708a-letters-cyclic-shift-ti-jie/index.html +++ b/2021/02/11/cf708a-letters-cyclic-shift-ti-jie/index.html @@ -472,7 +472,10 @@

CF708A Letters Cyclic Shift 题
-

CF708A Letters Cyclic Shift 题解

题目链接:CF708A Letters Cyclic Shift

+

CF708A Letters Cyclic Shift +题解

+

题目链接:CF708A +Letters Cyclic Shift

题意:一次变换指将字母变为它前面一个字母,例如a变成zb变成a,给定字符串,找出一个非空子串进行变换使得改变后字典序尽可能小

@@ -481,7 +484,8 @@

子串,因此只能改变到下一个不为a的位置

+

再看题目,要求改变的是非空子串,因此只能改变到下一个不为a的位置

因此aabcdefabb改变后变为aaabcdeabb

还有一种情况要特判 例如aaaaa

题目要求你必须选择一个非空子串,这种情况只要把最后一个字符改变就行了

@@ -865,7 +869,7 @@

 站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/02/17/ubuntu20.04-zhuo-mian-tu-biao-xian-shi-yi-chang-ji-jie-jue-fang-fa/index.html b/2021/02/17/ubuntu20.04-zhuo-mian-tu-biao-xian-shi-yi-chang-ji-jie-jue-fang-fa/index.html index fb5958a860..0455c622cf 100644 --- a/2021/02/17/ubuntu20.04-zhuo-mian-tu-biao-xian-shi-yi-chang-ji-jie-jue-fang-fa/index.html +++ b/2021/02/17/ubuntu20.04-zhuo-mian-tu-biao-xian-shi-yi-chang-ji-jie-jue-fang-fa/index.html @@ -468,31 +468,39 @@

ubuntu20.04 桌面图标显示
-

ubuntu20.04 桌面图标显示异常及解决方法

前言

更新至ubuntu20.04后,出现了一些以前没有的问题

+

ubuntu20.04 +桌面图标显示异常及解决方法

+

前言

+

更新至ubuntu20.04后,出现了一些以前没有的问题

桌面上有些图标不显示


- -

一、具体表现

例如有一次我在做备忘录时

+

一、具体表现

+

例如有一次我在做备忘录时

我习惯地打开终端

cd 桌面
 gedit 账号.txt

桌面效果

-


然后我写了一些东西,保存后,桌面变成了这样 (注:并非每次都会出现这种问题)

-


打开文件夹,显示我桌面上的文件都存在,但是桌面上不显示,双击文件原来的位置也没有用

+

+然后我写了一些东西,保存后,桌面变成了这样 +(注:并非每次都会出现这种问题)

+

+打开文件夹,显示我桌面上的文件都存在,但是桌面上不显示,双击文件原来的位置也没有用


- - -

二、原因

这种情况是$\text{gnome\ shell}$出现了异常

+

二、原因

+

这种情况是\(\text{gnome\ shell}\)出现了异常

众所周知重启电脑能解决大部分问题,但是总不能动不动就重启吧?


- - -

三、解决方法

按下alt+F2,会跳出一个窗口,然后输入一个r(重启gnome shell)

+

三、解决方法

+

按下alt+F2,会跳出一个窗口,然后输入一个r(重启gnome +shell)

然后再看一眼桌面,就恢复了


- - -

总结

碰到gnome shell问题,重启它一般能解决

+

总结

+

碰到gnome shell问题,重启它一般能解决

@@ -850,7 +858,7 @@

总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/02/23/zheng-shu-de-hua-fen-dong-tai-gui-hua/index.html b/2021/02/23/zheng-shu-de-hua-fen-dong-tai-gui-hua/index.html index b3ccf7aadd..4e1ba04d5b 100644 --- a/2021/02/23/zheng-shu-de-hua-fen-dong-tai-gui-hua/index.html +++ b/2021/02/23/zheng-shu-de-hua-fen-dong-tai-gui-hua/index.html @@ -468,28 +468,31 @@

整数的划分 动态规划

-

整数的划分 动态规划

题目描述

+

整数的划分 动态规划

+

题目描述

每个非负整数都可以被拆分,比如说

2 = 2
 2 = 1+1
3 = 3
 3 = 2+1
 3 = 1+1+1
-

输入格式
一个非负整数$n(0 \leq n \leq 100)$

-

输出格式
输出可以被拆分的方案数

-

输入样例

4

-

输出样例

5

-

样例解释

4 = 4
+

输入格式 一个非负整数\(n(0 +\leq n \leq 100)\)

+

输出格式 输出可以被拆分的方案数

+

输入样例

4

+

输出样例

5

+

样例解释

4 = 4
 4 = 3+1
 4 = 2+2
 4 = 2+1+1
 4 = 1+1+1+1
 共5种

-

本题是我在学 $dp$ 时候做的一道题

-

看到题目首先来一发暴力搜索

+

本题是我在学 \(dp\) +时候做的一道题

+看到题目首先来一发暴力搜索
- -

40分解法

#include<bits/stdc++.h>
+

40分解法

+
#include<bits/stdc++.h>
 using namespace std;
 #define int long long
 #define R register
@@ -524,27 +527,85 @@ 

printf("%lld\n",ans+1);//不拆也是一种 return 0; }

-

如果认为暴力能过,那请看以下数据

输入: 100
+

如果认为暴力能过,那请看以下数据

输入: 100
 输出: 190569292

-

综上所述,这道题正解是$dp$

+

综上所述,这道题正解是\(dp\)


- -

100分 解法一

设 $dp[i][j]$ 表示 $i$ 拆分成 $j$ 个数的方案数

-

我们可以把 $j$ 看作 $j$ 个桶,$i$ 就是 $i$ 个$1$

-

1.$dp[i-1][j-1]$
一个桶里已经放了一个 $1$ 了,还有 $i-1$ 个 $1$ 要划分到 $j-1$ 个桶考虑$i-1$的$j-1$划分($1$本身作为一个划分)保证划分中包含$1$

-
    -
  1. $dp[i-j][j]$
    先在每一个桶里都放个 $1$,还有 $i-j$ 个 $1$ 要划分到这 $j$ 个桶里($i-j$不作为单独作为划分,因此还是划分为$j$组),考虑 $i-j$ 的 $j$ 划分,在合法的情况下保证划分中不包含$1$
    $[$ 对于 $i$ 的 $j$ 划分$a_k$( $\sum\limits_{k=1}^{j}{a_k}=i,a_k\in \mathbb{Z}_+$ ),都有 $a_k>0$ ,因此 $a_k-1$ 对应了 $i-j$ 的 $j$ 划分 $]$
  2. +

    100分 解法一

    +

    \(dp[i][j]\) 表示 \(i\) 拆分成 \(j\) 个数的方案数

    +

    我们可以把 \(j\) 看作 \(j\) 个桶,\(i\) 就是 \(i\)\(1\)

    +

    1.\(dp[i-1][j-1]\) +一个桶里已经放了一个 \(1\) 了,还有 +\(i-1\)\(1\) 要划分到 \(j-1\) 个桶考虑\(i-1\)\(j-1\)划分(\(1\)本身作为一个划分)保证划分中包含\(1\)

    +
      +
    1. \(dp[i-j][j]\) 先在每一个桶里都放个 +\(1\),还有 \(i-j\)\(1\) 要划分到这 \(j\) 个桶里(\(i-j\)不作为单独作为划分,因此还是划分为\(j\)组),考虑 \(i-j\)\(j\) +划分,在合法的情况下保证划分中不包含\(1\) \([\) +对于 \(i\)\(j\) 划分\(a_k\)\(\sum\limits_{k=1}^{j}{a_k}=i,a_k\in +\mathbb{Z}_+\) ),都有 \(a_k>0\) ,因此 \(a_k-1\) 对应了 \(i-j\)\(j\) 划分 \(]\)

    所以状态转移方程就是

    -

    $dp[i][j]=dp[i-j][j]+dp[i-1][j-1]$

    -

    注:对于当前状态的划分中包含 $1$ ($dp[i-1][j-1]$),不保证状态转移后仍有 $1$

    -

    例如 $dp[7][3]$ 的一种划分方式 $1+2+4$

    -

    $dp[7][3]$ $\xrightarrow{}$ $1+dp[6][2]$ $\xrightarrow{}$ $1+\frac{dp[4][2]}{+1}$ $\xrightarrow{}$ $1+\frac{1+dp[3][1]}{+1}$ $\xrightarrow{}$ $1+\frac{1}{+1}+\frac{3}{+1}$ $\xrightarrow{}$ $1+2+4$

    +

    \(dp[i][j]=dp[i-j][j]+dp[i-1][j-1]\)

    +

    注:对于当前状态的划分中包含 \(1\)\(dp[i-1][j-1]\)),不保证状态转移后仍有 +\(1\)

    +

    例如 \(dp[7][3]\) 的一种划分方式 +\(1+2+4\)

    +

    \(dp[7][3]\) \(\xrightarrow{}\) \(1+dp[6][2]\) \(\xrightarrow{}\) \(1+\frac{dp[4][2]}{+1}\) \(\xrightarrow{}\) \(1+\frac{1+dp[3][1]}{+1}\) \(\xrightarrow{}\) \(1+\frac{1}{+1}+\frac{3}{+1}\) \(\xrightarrow{}\) \(1+2+4\)

    边界:

    -

    1.$0$ 的总方案数为 $1$
    2.$j=1$ ,方案数就是 $1$
    3.$i<j$ 时无法划分成 $j$ 个数,方案数为 $0$
    4.$i=j$ 时方案数为 $1$

    -

    最后答案就是 $\sum\limits_{i=1}^{n}{dp[n][i]}$

    -

    时间复杂度 $O(n^2)$

    -

    空间复杂度 $O(n^2)$

    +

    1.\(0\) 的总方案数为 \(1\) 2.\(j=1\) ,方案数就是 \(1\) 3.\(i<j\) 时无法划分成 \(j\) 个数,方案数为 \(0\) 4.\(i=j\) 时方案数为 \(1\)

    +

    最后答案就是 \(\sum\limits_{i=1}^{n}{dp[n][i]}\)

    +

    时间复杂度 \(O(n^2)\)

    +

    空间复杂度 \(O(n^2)\)

    #include<bits/stdc++.h>
     using namespace std;
     #define int long long
    @@ -565,12 +626,17 @@ 

    return 0; }


    - -

    100分 解法二

    设 $dp[j]$ 为 $j$ 的所有划分方案数

    -

    对于 $\forall j \in \mathbb{Z}_+$,$dp[j]=\sum\limits_{i=1}^{j}{dp[j-i]}$

    -

    边界: $0$ 的方案数为 $1$

    -

    时间复杂度 $O(n^2)$

    -

    空间复杂度 $O(n)$

    #include<bits/stdc++.h>
    +

    100分 解法二

    +

    \(dp[j]\)\(j\) 的所有划分方案数

    +

    对于 \(\forall j \in +\mathbb{Z}_+\)\(dp[j]=\sum\limits_{i=1}^{j}{dp[j-i]}\)

    +

    边界: \(0\) +的方案数为 \(1\)

    +

    时间复杂度 \(O(n^2)\)

    +

    空间复杂度 \(O(n)\) +

    #include<bits/stdc++.h>
     using namespace std;
     #define int long long
     #define R register
    @@ -937,7 +1003,7 @@ 

     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/03/02/ubuntu-nei-cun-zhan-yong-guo-gao-dao-zhi-qia-si-jie-jue-ban-fa/index.html b/2021/03/02/ubuntu-nei-cun-zhan-yong-guo-gao-dao-zhi-qia-si-jie-jue-ban-fa/index.html index 0cb84c90cd..9c44eb57e2 100644 --- a/2021/03/02/ubuntu-nei-cun-zhan-yong-guo-gao-dao-zhi-qia-si-jie-jue-ban-fa/index.html +++ b/2021/03/02/ubuntu-nei-cun-zhan-yong-guo-gao-dao-zhi-qia-si-jie-jue-ban-fa/index.html @@ -468,41 +468,64 @@

    ubuntu 内存占用过高导致
    -

    ubuntu 内存占用过高导致卡死 解决办法

    一、具体表现

    例如下图

    -

    在这里插入图片描述

    +

    ubuntu +内存占用过高导致卡死 解决办法

    +

    一、具体表现

    +

    例如下图

    +
    + + +

    注:图示版本为ubuntu18.04,现在我用的是ubuntu20.04


    - -

    二、原因

    查阅到了一些资料

    +

    二、原因

    +

    查阅到了一些资料

    +
    +

    在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然。这是Linux内存管理的一个优秀特性,在这方 +面,区别于 Windows的内存管理。主要特点是,无论物理内存有多大,Linux +都将其充份利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能。

    +
    +
    +

    Linux 的这一特性,主要是利用空闲的物理内存,划分出一部份空间,做为 +cache 和 buffers ,以此提高数据访问性能。

    +
    -

    在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然。这是Linux内存管理的一个优秀特性,在这方 面,区别于 Windows的内存管理。主要特点是,无论物理内存有多大,Linux 都将其充份利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能。

    -

    Linux 的这一特性,主要是利用空闲的物理内存,划分出一部份空间,做为 cache 和 buffers ,以此提高数据访问性能。

    -

    Linux 优先使用物理内存,当物理内存还有空闲时,linux是不会施放内存的,即时占用内存的程序已经被关闭了(这部分内存就用来做缓存了)。也就是说,即使你有很多内存,用过一段时间后,也会被占满。这样做的好处是,启动那些刚开启过的程序、或是读取刚存取过得数据会比较快,对于服务器很有好处。

    +

    Linux +优先使用物理内存,当物理内存还有空闲时,linux是不会施放内存的,即时占用内存的程序已经被关闭了(这部分内存就用来做缓存了)。也就是说,即使你有很多内存,用过一段时间后,也会被占满。这样做的好处是,启动那些刚开启过的程序、或是读取刚存取过得数据会比较快,对于服务器很有好处。

    总结一下,我的这种情况就是swap空间开的太小了


    - -

    三、解决方案

    把swap空间调大(建议在物理内存的两倍以上)

    +

    三、解决方案

    +

    把swap空间调大(建议在物理内存的两倍以上)

    首先用gparted改一下磁盘分区,然后再配置(这两步缺一不可)

    -

    可以参考一下这篇博客,写的很好(感谢)

    +

    可以参考一下这篇博客,写的很好(感谢)

    顺便提醒一下,每个人电脑都存在差异,我在参考那篇博客时就出现了一些不同之处

    -

    因为我写本文的时候买的16GB内存条还没到,所以提前调成60GB的swap空间了 (好像大了点)

    +

    因为我写本文的时候买的16GB内存条还没到,所以提前调成60GB的swap空间了 +(好像大了点)

    经过实验证明,目前该方法完全解决了之前的问题(再次道歉 qwq)

    如果要释放swap空间的话,可以用以下指令

    sudo su
     swapoff -a
     swapon swapfile 		注:也有用 swapon -a的,不过我这不行
    -

    放几张图吧…(开了一堆窗口,然后不会卡死了)

    -

    在这里插入图片描述

    +

    放几张图吧...(开了一堆窗口,然后不会卡死了)

    +
    + + +

    全部关掉以后,一切正常!!成功!!


    - -

    四、其他优化

    有时候缓存会过高,可以写个脚本及时释放缓存

    -

    释放缓存脚本

    +

    四、其他优化

    +

    有时候缓存会过高,可以写个脚本及时释放缓存

    +

    释放缓存脚本


    - - -

    总结

    调大swap空间

    +

    总结

    +

    调大swap空间

    @@ -856,7 +879,7 @@

    总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/03/11/linux-ji-windows-dui-pai-cheng-xu-c/index.html b/2021/03/11/linux-ji-windows-dui-pai-cheng-xu-c/index.html index 164b3295c3..231857b017 100644 --- a/2021/03/11/linux-ji-windows-dui-pai-cheng-xu-c/index.html +++ b/2021/03/11/linux-ji-windows-dui-pai-cheng-xu-c/index.html @@ -468,18 +468,20 @@

    linux及windows对拍程序 C++<
    -

    linux及windows对拍程序 C++

    前言

    OI赛制的比赛中,选手不能看到自己的成绩,那么如何保证代码正确呢?

    -

    1.水品高 秒切
    2.暴力+对拍 尝试调正解

    +

    linux及windows对拍程序 C++

    +

    前言

    +

    OI赛制的比赛中,选手不能看到自己的成绩,那么如何保证代码正确呢?

    +

    1.水品高 秒切 2.暴力+对拍 尝试调正解

    本文给出了linux和windows的对拍程序


    - -

    一、什么是对拍?

    在比赛中,某道题已经写出了暴力解法(须保证正确),开始尝试写正解

    +

    一、什么是对拍?

    +

    在比赛中,某道题已经写出了暴力解法(须保证正确),开始尝试写正解

    我们就可以用到对拍程序,用于对比暴力解法和尝试解法的输出结果,以判断该解法的是否是正解

    注:暴力解法通常跑不了很大的点,因此对拍时数据不强,即使正确也未必是正解

    那么对拍程序怎么写呢?


    - -

    二、怎么写对拍程序?

    对拍程序的写法有很多种,但结构基本一致

    +

    二、怎么写对拍程序?

    +

    对拍程序的写法有很多种,但结构基本一致

    while(1)//不一定要用死循环
     {
     	生成随机数据
    @@ -488,7 +490,7 @@ 

    }

    接下来,我将以A+B Problem为例

    -

    首先,写暴力程序std.cpp

    #include<bits/stdc++.h>
    +

    首先,写暴力程序std.cpp

    #include<bits/stdc++.h>
     using namespace std;
     #define int long long
     #define R register
    @@ -504,7 +506,8 @@ 

    while(b){a++,b--;} printf("%lld\n",a); return 0; -}


    接下来,尝试写正解my.cpp
    #include<bits/stdc++.h>
    +}
    +接下来,尝试写正解my.cpp
    #include<bits/stdc++.h>
     using namespace std;
     #define int long long
     #define R register
    @@ -540,11 +543,13 @@ 

    }

    最后,记得要把这些文件都编译哦!

    注:需要有可执行文件才能对拍,因此需要编译

    -

    1.linux下的对拍程序

    linux下的比对命令是diff
    ./std的意思可以简单地认为是运行当前目录下std.cpp编译后生成的可执行文件std

    +

    1.linux下的对拍程序

    +

    linux下的比对命令是diff +./std的意思可以简单地认为是运行当前目录下std.cpp编译后生成的可执行文件std

    本人使用的是vscode,编译后生成的可执行文件就是文件名

    -

    注:如果您没有用类似的软件,您可以手动编译

    cd code 注:该文件所在目录,我的叫code
    -g++ std.cpp -o std

    -

    C++对拍程序

    #include<bits/stdc++.h>
    +

    注:如果您没有用类似的软件,您可以手动编译

    cd code 注:该文件所在目录,我的叫code
    +g++ std.cpp -o std
    #### +C++对拍程序
    #include<bits/stdc++.h>
     using namespace std;
     #define int long long
     #define R register
    @@ -564,8 +569,7 @@ 

    } } return 0; -}

    -

    Bash脚本

    #!/bin/bash
    +}
    #### Bash脚本
    #!/bin/bash
     i=0
     while true
     do
    @@ -579,11 +583,13 @@ 

    break fi done

    -

    注:可以用以下指令编辑.sh文件

    cd code 注:该文件所在目录
    +注:可以用以下指令编辑.sh文件 
    cd code 注:该文件所在目录
     gedit checker.sh 注:新建文件
     sh checker.sh 注:运行

    -

    2.windows下的对拍程序

    本人使用的Dev C++

    -

    C++对拍程序

    #include<bits/stdc++.h>
    +

    2.windows下的对拍程序

    +

    本人使用的Dev C++

    +

    C++对拍程序

    +
    #include<bits/stdc++.h>
     using namespace std;
     #define int long long
     #define R register
    @@ -604,7 +610,8 @@ 

    } return 0; }

    -

    bat脚本

    @echo off
    +

    bat脚本

    +
    @echo off
     ::指不显示命令
     set /a i=0
     :loop
    @@ -622,8 +629,8 @@ 


    - -

    总结

    本文介绍了linux和windows下对拍程序的写法(共4种)

    +

    总结

    +

    本文介绍了linux和windows下对拍程序的写法(共4种)

    对拍程序的思想简单,实现多样

    在竞赛中能有所帮助

    @@ -979,7 +986,7 @@

    总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/03/20/ubuntu-xian-shi-jian-pan-an-jian/index.html b/2021/03/20/ubuntu-xian-shi-jian-pan-an-jian/index.html index 95c282408e..ed870350d6 100644 --- a/2021/03/20/ubuntu-xian-shi-jian-pan-an-jian/index.html +++ b/2021/03/20/ubuntu-xian-shi-jian-pan-an-jian/index.html @@ -468,29 +468,32 @@

    ubuntu 显示键盘按键

    -

    ubuntu 显示键盘按键

    前言

    在看一些主播玩游戏时,他们屏幕上会有一个虚拟键盘,可以显示按键

    +

    ubuntu 显示键盘按键

    +

    前言

    +

    在看一些主播玩游戏时,他们屏幕上会有一个虚拟键盘,可以显示按键

    当时觉得很神奇,就想着给ubuntu也弄一个

    庆幸的是,ubuntu的确有这种软件


    - -

    一、KeyMon简介

    这个软件叫key-mon,全称Keyboard Status Monitor,即键盘状态监视器

    -

    在这里插入图片描述
    注:截图时我按住了shfit键

    +

    一、KeyMon简介

    +

    这个软件叫key-mon,全称Keyboard Status +Monitor,即键盘状态监视器

    +

    注:截图时我按住了shfit键

    不过相对于别的软件,这个就显得有些简单

    -

    但不可否认的确有效果

    +

    但不可否认的确有效果


    - -

    二、安装步骤

    打开终端

    +

    二、安装步骤

    +

    打开终端

    sudo apt install key-mon

    安装即可

    适用于ubuntu18.04,目前ubuntu20.04也可以用


    - - -

    三、自定义设置

    希望更好的体验?

    +

    三、自定义设置

    +

    希望更好的体验?

    右键选择Settings...设置就可以了


    - -

    总结

    本文介绍了KeyMon的安装方法

    +

    总结

    +

    本文介绍了KeyMon的安装方法

    显示键盘按键就这么简单

    @@ -845,7 +848,7 @@

    总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/03/27/ubuntu-zhui-zhu-shu-biao-zhi-zhen-de-xiao-mao-oneko/index.html b/2021/03/27/ubuntu-zhui-zhu-shu-biao-zhi-zhen-de-xiao-mao-oneko/index.html index 70c4fcfc12..cbf1600a27 100644 --- a/2021/03/27/ubuntu-zhui-zhu-shu-biao-zhi-zhen-de-xiao-mao-oneko/index.html +++ b/2021/03/27/ubuntu-zhui-zhu-shu-biao-zhi-zhen-de-xiao-mao-oneko/index.html @@ -468,20 +468,28 @@

    ubuntu 追逐鼠标指针的小
    -

    ubuntu 追逐鼠标指针的小猫~Oneko

    前言

    最近发现了一个有趣的软件 Oneko

    +

    ubuntu +追逐鼠标指针的小猫~Oneko

    +

    前言

    +

    最近发现了一个有趣的软件 Oneko

    可以让一只小猫追着鼠标指针跑

    是不是很有趣?

    -

    一、下载Oneko

    打开终端

    sudo apt install oneko

    安装即可

    +

    一、下载Oneko

    +

    打开终端

    sudo apt install oneko
    安装即可

    然后它就会在应用程序中了


    - -

    二、使用Oneko

    只要输入这个指令就行

    oneko

    然后就会有以下效果

    -

    Oneko

    +

    二、使用Oneko

    +

    只要输入这个指令就行

    oneko
    然后就会有以下效果

    +
    + + +

    或者也可以添加到收藏夹后直接单击

    不过关闭需要右键,选择Oneko STOP


    - -

    三、自定义Oneko

    官方给的参数如下

    +

    三、自定义Oneko

    +

    官方给的参数如下

    Usage: oneko [<options>]
     
     Options are:
    @@ -506,7 +514,8 @@ 

    1.常用指令

    其他指令使用较少,您可以自行研究

    +

    1.常用指令

    +

    其他指令使用较少,您可以自行研究

    -fg <color>设置前景色

    -bg <color>设置背景色

    -speed <dots>设置跑步速度

    @@ -516,17 +525,29 @@

    2.实际效果

    例如:

    +

    2.实际效果

    +

    例如:

    oneko -fg red
     oneko -bg green
     oneko -tora
     oneko -dog
     oneko -sakura
     oneko -tomoyo
    -

    效果如下
    oneko -fg redoneko -bg greenoneko -toraoneko -dogoneko -sakuraoneko -tomoyo

    +

    效果如下


    - -

    总结

    本文仅介绍了常用功能

    +

    总结

    +

    本文仅介绍了常用功能

    Oneko真的很好玩呢!

    安装和使用也挺简单的

    @@ -882,7 +903,7 @@

    总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/05/07/ubuntu-vmware-bao-cuo-could-not-open...jie-jue-fang-fa/index.html b/2021/05/07/ubuntu-vmware-bao-cuo-could-not-open...jie-jue-fang-fa/index.html index 20c851578f..c9989746c5 100644 --- a/2021/05/07/ubuntu-vmware-bao-cuo-could-not-open...jie-jue-fang-fa/index.html +++ b/2021/05/07/ubuntu-vmware-bao-cuo-could-not-open...jie-jue-fang-fa/index.html @@ -468,31 +468,44 @@

    ubuntu VMware报错could not ope
    -

    ubuntu VMware报错could not open…解决方法

    前言

    本人在使用VMware时报了这个错

    +

    ubuntu +VMware报错could not open...解决方法

    +

    前言

    +

    本人在使用VMware时报了这个错

    could not open /dev/vmmon:??????

    经过多方查阅资料,为大家提供一个有效的解决办法


    - -

    一、具体表现

    安装好VMware,虚拟机也创建完了

    +

    一、具体表现

    +

    安装好VMware,虚拟机也创建完了

    一运行结果就报错了

    -

    在这里插入图片描述
    一开始以为权限问题,用root打开也一样(显然没用)

    +

    +一开始以为权限问题,用root打开也一样(显然没用)


    - -

    二、解决方法

    需要关闭安全启动 ($\mathrm{\color{black}{secure \ boot}}$)

    -

    安全启动设计之初作用是防止恶意软件侵入

    +

    二、解决方法

    +

    需要关闭安全启动 (\(\mathrm{\color{black}{secure \ +boot}}\))

    +

    安全启动设计之初作用是防止恶意软件侵入

    事实上它能够做到的仅仅是当电脑引导器被病毒修改之后,它会给出提醒并拒绝启动

    因此基本上没什么用

    什么?在哪里关闭?? 当然是BIOS

    -

    进入BIOS (开机后出现电脑品牌的图标时狂按F12)

    -

    下面看我拍的照片就懂了吧…

    -

    注:我的电脑是DELL的,其他品牌的我不知道

    -

    在这里插入图片描述
    在这里插入图片描述

    +

    进入BIOS +(开机后出现电脑品牌的图标时狂按F12)

    +

    下面看我拍的照片就懂了吧...

    +

    注:我的电脑是DELL的,其他品牌的我不知道

    +

    图片可能有点糊,还请谅解

    改完记得保存哦!

    然后打开ubuntu,虚拟机就可以正常运行了


    - -

    总结

    本文介绍了因安全启动导致的报错的解决方法

    +

    总结

    +

    本文介绍了因安全启动导致的报错的解决方法

    @@ -846,7 +859,7 @@

    总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/05/15/ubuntu-ibus-shu-ru-fa-tu-ran-wu-fa-shu-ru-yan-chi-guo-gao-jie-jue-fang-fa/index.html b/2021/05/15/ubuntu-ibus-shu-ru-fa-tu-ran-wu-fa-shu-ru-yan-chi-guo-gao-jie-jue-fang-fa/index.html index d03f6eda14..80a589a361 100644 --- a/2021/05/15/ubuntu-ibus-shu-ru-fa-tu-ran-wu-fa-shu-ru-yan-chi-guo-gao-jie-jue-fang-fa/index.html +++ b/2021/05/15/ubuntu-ibus-shu-ru-fa-tu-ran-wu-fa-shu-ru-yan-chi-guo-gao-jie-jue-fang-fa/index.html @@ -468,35 +468,41 @@

    ubuntu ibus输入法 突然无
    -

    ubuntu ibus输入法 突然无法输入 (延迟过高) 解决方法

    前言

    ibus拼音输入法最近不知道为何出现了一些异常问题(经常卡死)

    +

    ubuntu +ibus输入法 突然无法输入 (延迟过高) 解决方法

    +

    前言

    +

    ibus拼音输入法最近不知道为何出现了一些异常问题(经常卡死)

    被困扰了很久后,终于找到了解决办法

    upd.20220423

    在中文维基上查到了这样的资料

    由于读取sqlite词库时有大量的IO操作,ibus-pinyin在系统高负载时输入时有卡住的现象。

    -

    upd.20220423

    +

    upd.20220423

    fcitx差点把你们可爱的q779给整没了,现在他继续用ibus了(重装了一遍)


    - -

    一、具体表现

    写代码时,突然键盘像失灵了一样打不出字,过了好一会才慢慢显示出来

    +

    一、具体表现

    +

    写代码时,突然键盘像失灵了一样打不出字,过了好一会才慢慢显示出来

    可以说延迟太高了

    在此期间终端却可以打开,鼠标也可以正常移动

    关闭窗口什么的都没问题,就是打不出字


    - - -

    二、解决方法

    如果您的ubuntu中除了ibus拼音输入还有英文输入,那这个方法必定有效

    -

    在这里插入图片描述
    首先切换到en,即英语输入(右上角wifi图标旁边)

    +

    二、解决方法

    +

    如果您的ubuntu中除了ibus拼音输入还有英文输入,那这个方法必定有效

    +

    +首先切换到en,即英语输入(右上角wifi图标旁边)

    然后ctrl+alt+t打开终端

    -

    输入

    ibus restart

    重启ibus拼音输入法

    +

    输入

    ibus restart
    重启ibus拼音输入法

    切换回ibus输入法,然后你原先输入的就出现了!

    而且可以正常使用了!

    -

    注意:如果重启时显示无法连接至ibus,可以看这里

    +

    注意:如果重启时显示无法连接至ibus,可以看这里


    - -

    总结

    本文介绍了ibus输入法突然无法输入(卡死)的解决方法

    -

    众所周知,重启能解决大部分问题

    +

    总结

    +

    本文介绍了ibus输入法突然无法输入(卡死)的解决方法

    +

    众所周知,重启能解决大部分问题

    @@ -850,7 +856,7 @@

    总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/07/08/qian-tan-bu-ma-de-yuan-li-he-zheng-que-xing/index.html b/2021/07/08/qian-tan-bu-ma-de-yuan-li-he-zheng-que-xing/index.html index d57226d3e1..28db00b2c5 100644 --- a/2021/07/08/qian-tan-bu-ma-de-yuan-li-he-zheng-que-xing/index.html +++ b/2021/07/08/qian-tan-bu-ma-de-yuan-li-he-zheng-que-xing/index.html @@ -468,167 +468,324 @@

    浅谈补码的原理和正确
    -

    浅谈补码的原理和正确性

    前言

    upd 2022.2.14 我就该早点看《计算机组成原理》,补码的定义就是

    +

    浅谈补码的原理和正确性

    +

    前言

    +

    upd 2022.2.14 +我就该早点看《计算机组成原理》,补码的定义就是

    -

    一个 $n$ 位二进制数 $N$ 的二进制补码定义为 $2^n-N$

    +

    一个 \(n\) 位二进制数 \(N\) 的二进制补码定义为 \(2^n-N\)

    不过本文还是严谨证明了它的正确性,以下为原文

    补码是怎么来的?

    -

    负数的补码为什么是按位取反(除了符号位)再加 $1$ ?

    +

    负数的补码为什么是按位取反(除了符号位)再加 \(1\) ?

    补码的正确性能保证吗?

    补码背后的数学原理是什么?

    本文围绕原理和正确性介绍了补码


    - -

    一、我们为什么用补码?

    我们以 $8$ 位二进制数为例

    -

    $8$ 位无符号数,可以表示 $0\sim255$ 以内的数(即 $00000000 \sim 11111111$ ),那么如果我们要表示负数呢?

    -

    我们都知道正、负号能够表示正、负数,可是计算机“看不懂“,它只能识别 $0,1$

    -

    于是,就出现了原码

    -

    最高位表示数的正负,$0$ 表示 ,$1$ 表示

    -

    $8$ 位有符号数,以原码形式存储则可以表示 $0 \sim 127$(即 $00000000 \sim 01111111$ ) 和 $-127 \sim 0$ (即 $11111111 \sim 10000000$ )

    -

    相信看到这里,你应该发现了:以原码形式存储时,$0$ 的值不唯一

    -

    我们把 $\pm 0$ 的问题放一边,先来讨论一下编码的正确性

    -

    计算 $1+(-1)$

    -

    $00000001 + 10000001 = 10000010$

    -

    然而 $10000010$ 转成十进制是 $-2$ !

    +

    一、我们为什么用补码?

    +

    我们以 \(8\) 位二进制数为例

    +

    \(8\) +位无符号数,可以表示 \(0\sim255\) 以内的数(即 \(00000000 \sim 11111111\) +),那么如果我们要表示负数呢?

    +

    我们都知道正、负号能够表示正、负数,可是计算机“看不懂“,它只能识别 +\(0,1\)

    +

    于是,就出现了原码

    +

    最高位表示数的正负,\(0\) 表示 +\(1\) +表示

    +

    \(8\) +位有符号数,以原码形式存储则可以表示 \(0 \sim 127\)(即 \(00000000 \sim 01111111\) ) 和 \(-127 \sim 0\) (即 \(11111111 \sim 10000000\)

    +

    相信看到这里,你应该发现了:以原码形式存储时,\(0\) 的值不唯一

    +

    我们把 \(\pm 0\) +的问题放一边,先来讨论一下编码的正确性

    +

    计算 \(1+(-1)\)

    +

    \(00000001 + 10000001 = +10000010\)

    +

    然而 \(10000010\) 转成十进制是 \(-2\)

    由此可知原码不可以直接进行运算,或者可以认为以原码的编码方式运算是错误

    那么问题来了,正数的表示都符合我们的习惯,那么问题一定出在负数上


    - -

    二、补码是怎么来的?

    各大教科书上都会告诉你,正数的补码就是它的原码,负数的补码就是原码按位取反(除了符号位)再加 $1$

    +

    二、补码是怎么来的?

    +

    各大教科书上都会告诉你,正数的补码就是它的原码,负数的补码就是原码按位取反(除了符号位)再加 +\(1\)

    你是不是也很迷惑?补码到底为什么是这么算出来的?

    -

    我们还是以 $8$ 位有符号数为例

    -

    我们知道,两个数如果互为相反数则和为$0$ ,例如 $1+(-1)=0$

    -

    那么正确的编码方式中 $1+(-1)$ 一定等于 $0$

    -

    我们设 $-1$ 的编码为 $\phi$ ,可得 $00000001 + \phi = 0$

    -

    $\therefore \phi = 11111111$(注:计算过程将在下文中讲述,这里只需要知道结果)

    -

    那么就很清楚了,如果我们已知编码 $\phi$ ,它的相反数的编码就是 $0 - \phi$

    -

    现在我们来讨论一下 $\pm 0$ 的问题,显而易见,$10000000$ 肯定是不符合常理的( $00000000 + 10000000 \ne 00000000$ )

    -

    那么 $10000000$ 对应的相反数的编码是什么呢?

    -

    设 $10000000 + \phi = 0$ 解得 $\phi = 100000000$ (注意位数!)

    -

    我们会发现 $00000000 \sim 11111111$ 已经无法表示了

    -

    那 $0$ 呢?$0$ 的相反数还是 $0$ ,就是它本身

    -

    事已至此,干脆就让它们单身着吧。 既然 $10000000$ 的第一位是 $1$ ,表示负数,那就规定它为负数,因此 $10000000$ 就代替了 $-128$

    -

    因为 $10000000$ 没有对应的相反数的编码,所以没有 $+128$

    -

    然后每个数都有了自己对应的编码,$1 \sim 127$ 对应 $-1 \sim -127$ ,加上两个单身汉 $0$ 和 $-128$

    +

    我们还是以 \(8\) +位有符号数为例

    +

    我们知道,两个数如果互为相反数则和为\(0\) ,例如 \(1+(-1)=0\)

    +

    那么正确的编码方式中 \(1+(-1)\) +一定等于 \(0\)

    +

    我们设 \(-1\) 的编码为 \(\phi\) ,可得 \(00000001 + \phi = 0\)

    +

    \(\therefore \phi = +11111111\)(注:计算过程将在下文中讲述,这里只需要知道结果)

    +

    那么就很清楚了,如果我们已知编码 \(\phi\) ,它的相反数的编码就是 \(0 - \phi\)

    +

    现在我们来讨论一下 \(\pm 0\) +的问题,显而易见,\(10000000\) +肯定是不符合常理的( \(00000000 + 10000000 \ne +00000000\)

    +

    那么 \(10000000\) +对应的相反数的编码是什么呢?

    +

    \(10000000 + \phi = 0\) 解得 +\(\phi = 100000000\) (注意位数!)

    +

    我们会发现 \(00000000 \sim +11111111\) 已经无法表示了

    +

    \(0\) 呢?\(0\) 的相反数还是 \(0\) ,就是它本身

    +

    事已至此,干脆就让它们单身着吧。 既然 \(10000000\) 的第一位是 \(1\) ,表示负数,那就规定它为负数,因此 +\(10000000\) 就代替了 \(-128\)

    +

    因为 \(10000000\) +没有对应的相反数的编码,所以没有 \(+128\)

    +

    然后每个数都有了自己对应的编码,\(1 \sim +127\) 对应 \(-1 \sim -127\) +,加上两个单身汉 \(0\)\(-128\)

    这种编码就叫补码

    -

    那么为什么负数的补码是原码按位取反再加 $1$ 呢?

    -

    我们再看 $1+(-1)=0$ 的例子

    -

    设 $00000001 + \phi = 00000000$

    -

    $\therefore \phi = 00000000-00000001$

    +

    那么为什么负数的补码是原码按位取反再加 \(1\) 呢?

    +

    我们再看 \(1+(-1)=0\) 的例子

    +

    \(00000001 + \phi = +00000000\)

    +

    \(\therefore \phi = +00000000-00000001\)

    一个小的数减一个大的数,怎么办?

    首先我们应该都知道溢出这一概念

    -

    那么 $00000000$ 就可以看作 $11111111 + 00000001$

    -

    $\therefore\ \phi = (11111111 + 00000001) - 00000001$

    -

    $\qquad= (11111111-00000001) + 00000001$

    +

    那么 \(00000000\) 就可以看作 \(11111111 + 00000001\)

    +

    \(\therefore\ \phi = (11111111 + 00000001) +- 00000001\)

    +

    \(\qquad= (11111111-00000001) + +00000001\)

    注:这个就是简单的结合律

    我们看前面一部分,不就是按位取反嘛(反码)!

    -

    相信你现在应该理解了,如果我们要求正数 $\lambda$ 的相反数 $\mu$ 的补码

    -

    $\mu = (11111111-\lambda) + 00000001$ ,

    -

    即 $\lambda$)按位取反(包括符号位)再加 $1$ ,当然也是 $\mu$ 按位取反(除了符号位)再加 $1$ ,一样的

    +

    相信你现在应该理解了,如果我们要求正数 \(\lambda\) 的相反数 \(\mu\) 的补码

    +

    \(\mu = (11111111-\lambda) + +00000001\)

    +

    \(\lambda\))按位取反(包括符号位)再加 \(1\) ,当然也是 \(\mu\) 按位取反(除了符号位)再加 \(1\) ,一样的


    - -

    三、补码的正确性能保证吗?

    1.符号位能保证本身的值、运算结果的值正确吗?

    本身的值肯定正确,运算结果的值也正确

    +

    三、补码的正确性能保证吗?

    +

    1.符号位能保证本身的值、运算结果的值正确吗?

    +

    本身的值肯定正确,运算结果的值也正确

    运算都是从最低位运算到最高位,这个符号位又加在最高位,不可能影响运算,只可能被影响(注:接下来我们就来证明这个影响不会对运算结果的符号正确性产生影响)

    -

    2.数学运算结果的符号能保证正确吗?

    为了方便理解和证明,我们以 $4$ 位有符号数为例(即 $-8 \sim 7$ )

    +

    2.数学运算结果的符号能保证正确吗?

    +

    为了方便理解和证明,我们以 \(4\) +位有符号数为例(即 \(-8 \sim +7\)

    注意:正确性指在数学运算结果不溢出的情况下位运算的结果和数学运算的结果正确

    -

    首先,易知任何合法的数与 $0$ 作运算结果一定正确

    -

    1) 值的位运算结果不溢出时

    1.正数+正数

    数学运算结果:一定为正数,符号为

    -

    位运算结果:值的位运算不溢出,符号位 $0+0=0$,仍为

    -

    设这里两个数补码分别为$[0a_2a_1a_0], [0b_2b_1b_0]$

    -

    令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

    -

    原式可化为 $A+B$

    -

    且 $0 < A+B \le 7$

    -

    值的位运算结果不溢出即 $-8 \le A+B \le 7$

    -

    $\{x|0 < x \le 7\}\subsetneqq\{x|-8 \le x \le 7\}$

    -

    故该情况$\color{red}{正确}$

    -
    2.负数+负数

    数学运算结果:一定为负数,符号为

    -

    位运算结果:值的位运算不溢出,符号位 $1+1=0$ ,变成了

    -

    设这里两个数补码分别为$[1a_2a_1a_0], [1b_2b_1b_0]$

    -

    令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

    -

    原式可化为 $-8+A+(-8)+B = -16+A+B$

    -

    且 $-8 \le A+B-16 < 0 \Rightarrow 8 \le A+B < 16$

    -

    值的位运算结果不溢出即 $-8 \le A+B \le 7$

    -

    $\{x|-8 \le x \le 7\}\cap\{x|8 \le x < 16\} = \varnothing$

    -

    故该情况$\color{red}{不存在}$

    -
    3.一正一负
    a.正数绝对值大于负数绝对值

    数学运算结果:一定为正数,符号为

    -

    位运算结果:值的位运算结果不溢出,符号位 $0+1=1$ ,变成了

    -

    设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

    -

    令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

    -

    原式可化为$A+(-8)+B = A+B-8$

    -

    且 $0 < A+B-8 \le 7 \Rightarrow 8 < A+B \le 15$

    -

    值的位运算的结果不溢出即 $-8 \le A+B \le 7$

    -

    $\{x|-8 \le x \le 7\}\cap\{x|8 < x \le 15\} = \varnothing$

    -

    故该情况$\color{red}{不存在}$

    -
    b.正数绝对值等于负数绝对值

    数学运算结果:$0$ ,符号为

    -

    位运算结果:值的位运算结果不溢出,符号位 $0+1=1$ ,变成了

    -

    设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

    -

    令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

    -

    原式可化为$A+(-8)+B = A+B-8 = 0 \Rightarrow A+B=8$

    -

    值的位运算的结果不溢出即 $-8 \le A+B \le 7$

    -

    $\{x|-8 \le x \le 7\}\cap\{x|x=8\} = \varnothing$

    -

    故该情况$\color{red}{不存在}$

    -
    c.正数绝对值小于负数绝对值

    数学运算结果:一定为负数,符号为

    -

    位运算结果:值的位运算结果不溢出,符号为 $0 + 1 = 1$ ,仍为

    -

    设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

    -

    令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

    -

    原式可化为 $A+(-8)+B = A+B-8$

    -

    且 $-8 \le A+B-8 < 0 \Rightarrow 0 \le A+B < 8$

    -

    值的位运算结果不溢出即 $-8 \le A+B \le 7$

    -

    $\{x|0 \le x < 8\}\subsetneqq\{x|-8 \le x \le 7\}$

    -

    故该情况$\color{red}{正确}$

    -

    2) 值的位运算结果溢出时

    1.正数+正数

    数学运算结果:一定为正数,符号为

    -

    位运算结果:值的位运算结果溢出,符号位 $0+0+1=1$ ,变成了

    -

    设这里两个数补码分别为$[0a_2a_1a_0], [0b_2b_1b_0]$

    -

    令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

    -

    原式可化为 $A+B$

    -

    且 $0 < A+B \le 7$

    -

    值的位运算结果溢出即 $A+B>7$

    -

    $\{x|0 < x \le 7\}\cap\{x|x>7\} = \varnothing$

    -

    故该情况$\color{red}{不存在}$

    -
    2.负数+负数

    数学运算结果:一定为负数,符号为

    -

    位运算结果:值的位运算结果溢出,符号位 $1+1+1=1$ ,仍为

    -

    设这里两个数补码分别为$[1a_2a_1a_0], [1b_2b_1b_0]$

    -

    令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

    -

    原式可化为 $-8+A+(-8)+B = A+B-16$

    -

    且 $-8 \le A+B -16< 0 \Rightarrow 8 \le A+B < 16$

    -

    值的位运算结果溢出即 $A+B>7$

    -

    $\{x|8\le x < 16\}\subsetneqq\{x|x>7\}$

    -

    故该情况$\color{red}{正确}$

    -
    3.一正一负
    a.正数绝对值大于负数绝对值

    数学运算结果:一定为正数,符号为

    -

    位运算结果:值的位运算结果溢出,符号位 $0+1+1=0$ ,仍为

    -

    设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

    -

    令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

    -

    原式可化为 $A+(-8)+B = A+B-8$

    -

    且 $0<A+B-8\le 7 \Rightarrow 8<A+B\le15$

    -

    值的位运算结果溢出即 $A+B>7$

    -

    $\{x|8< x \le 15\}\subsetneqq\{x|x>7\}$

    -

    故该情况$\color{red}{正确}$

    -
    b.正数绝对值等于负数绝对值

    数学运算结果:$0$ ,符号为

    -

    位运算结果:值的位运算结果溢出,符号位 $0+1+1=0$ ,仍为

    -

    设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

    -

    令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

    -

    原式可化为 $A+(-8)+B = A+B-8 = 0 \Rightarrow A+B=8$

    -

    值的位运算结果溢出即$A+B>7$

    -

    $\{x|x=8\}\subsetneqq\{x|x>7\}$

    -

    故该情况$\color{red}{正确}$

    -
    c.正数绝对值小于负数绝对值

    数学运算结果:一定为负数 ,符号为

    -

    位运算结果:值的位运算结果溢出,符号位 $0+1+1=0$ ,变成了

    -

    设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

    -

    令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

    -

    原式可化为 $A+(-8)+B = A+B-8$

    -

    且 $-8\le A+B - 8<0 \Rightarrow 0 \le A+B <8$

    -

    值的位运算结果溢出即 $A+B>7$

    -

    $\{x|0 < x < 8\}\cap\{x|x>7\} = \varnothing$

    -

    故该情况$\color{red}{不存在}$

    -

    综上所述,所有存在的情况中,符号都是正确的,因此补码的正确性是可以保证的

    +

    首先,易知任何合法的数\(0\) 作运算结果一定正确

    +

    1) 值的位运算结果不溢出时

    +
    1.正数+正数
    +

    数学运算结果:一定为正数,符号为

    +

    位运算结果:值的位运算不溢出,符号位 \(0+0=0\),仍为

    +

    设这里两个数补码分别为\([0a_2a_1a_0], +[0b_2b_1b_0]\)

    +

    \(A = \sum\limits_{i=0}^{2}{a_i\times +2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

    +

    原式可化为 \(A+B\)

    +

    \(0 < A+B \le 7\)

    +

    值的位运算结果不溢出即 \(-8 \le A+B \le +7\)

    +

    \(\{x|0 < x \le 7\}\subsetneqq\{x|-8 \le +x \le 7\}\)

    +

    故该情况\(\color{red}{正确}\)

    +
    2.负数+负数
    +

    数学运算结果:一定为负数,符号为

    +

    位运算结果:值的位运算不溢出,符号位 \(1+1=0\) ,变成了

    +

    设这里两个数补码分别为\([1a_2a_1a_0], +[1b_2b_1b_0]\)

    +

    \(A = \sum\limits_{i=0}^{2}{a_i\times +2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

    +

    原式可化为 \(-8+A+(-8)+B = +-16+A+B\)

    +

    \(-8 \le A+B-16 < 0 \Rightarrow 8 \le +A+B < 16\)

    +

    值的位运算结果不溢出即 \(-8 \le A+B \le +7\)

    +

    \(\{x|-8 \le x \le 7\}\cap\{x|8 \le x < +16\} = \varnothing\)

    +

    故该情况\(\color{red}{不存在}\)

    +
    3.一正一负
    +
    a.正数绝对值大于负数绝对值
    +

    数学运算结果:一定为正数,符号为

    +

    位运算结果:值的位运算结果不溢出,符号位 \(0+1=1\) ,变成了

    +

    设这里两个数补码分别为\([0a_2a_1a_0], +[1b_2b_1b_0]\)

    +

    \(A = \sum\limits_{i=0}^{2}{a_i\times +2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

    +

    原式可化为\(A+(-8)+B = A+B-8\)

    +

    \(0 < A+B-8 \le 7 \Rightarrow 8 < +A+B \le 15\)

    +

    值的位运算的结果不溢出即 \(-8 \le A+B \le +7\)

    +

    \(\{x|-8 \le x \le 7\}\cap\{x|8 < x \le +15\} = \varnothing\)

    +

    故该情况\(\color{red}{不存在}\)

    +
    b.正数绝对值等于负数绝对值
    +

    数学运算结果:\(0\) +,符号为

    +

    位运算结果:值的位运算结果不溢出,符号位 \(0+1=1\) ,变成了

    +

    设这里两个数补码分别为\([0a_2a_1a_0], +[1b_2b_1b_0]\)

    +

    \(A = \sum\limits_{i=0}^{2}{a_i\times +2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

    +

    原式可化为\(A+(-8)+B = A+B-8 = 0 +\Rightarrow A+B=8\)

    +

    值的位运算的结果不溢出即 \(-8 \le A+B \le +7\)

    +

    \(\{x|-8 \le x \le 7\}\cap\{x|x=8\} = +\varnothing\)

    +

    故该情况\(\color{red}{不存在}\)

    +
    c.正数绝对值小于负数绝对值
    +

    数学运算结果:一定为负数,符号为

    +

    位运算结果:值的位运算结果不溢出,符号为 \(0 + 1 = 1\) ,仍为

    +

    设这里两个数补码分别为\([0a_2a_1a_0], +[1b_2b_1b_0]\)

    +

    \(A = \sum\limits_{i=0}^{2}{a_i\times +2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

    +

    原式可化为 \(A+(-8)+B = A+B-8\)

    +

    \(-8 \le A+B-8 < 0 \Rightarrow 0 \le +A+B < 8\)

    +

    值的位运算结果不溢出即 \(-8 \le A+B \le +7\)

    +

    \(\{x|0 \le x < 8\}\subsetneqq\{x|-8 \le +x \le 7\}\)

    +

    故该情况\(\color{red}{正确}\)

    +

    2) 值的位运算结果溢出时

    +
    1.正数+正数
    +

    数学运算结果:一定为正数,符号为

    +

    位运算结果:值的位运算结果溢出,符号位 \(0+0+1=1\) ,变成了

    +

    设这里两个数补码分别为\([0a_2a_1a_0], +[0b_2b_1b_0]\)

    +

    \(A = \sum\limits_{i=0}^{2}{a_i\times +2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

    +

    原式可化为 \(A+B\)

    +

    \(0 < A+B \le 7\)

    +

    值的位运算结果溢出即 \(A+B>7\)

    +

    \(\{x|0 < x \le 7\}\cap\{x|x>7\} = +\varnothing\)

    +

    故该情况\(\color{red}{不存在}\)

    +
    2.负数+负数
    +

    数学运算结果:一定为负数,符号为

    +

    位运算结果:值的位运算结果溢出,符号位 \(1+1+1=1\) ,仍为

    +

    设这里两个数补码分别为\([1a_2a_1a_0], +[1b_2b_1b_0]\)

    +

    \(A = \sum\limits_{i=0}^{2}{a_i\times +2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

    +

    原式可化为 \(-8+A+(-8)+B = +A+B-16\)

    +

    \(-8 \le A+B -16< 0 \Rightarrow 8 \le +A+B < 16\)

    +

    值的位运算结果溢出即 \(A+B>7\)

    +

    \(\{x|8\le x < +16\}\subsetneqq\{x|x>7\}\)

    +

    故该情况\(\color{red}{正确}\)

    +
    3.一正一负
    +
    a.正数绝对值大于负数绝对值
    +

    数学运算结果:一定为正数,符号为

    +

    位运算结果:值的位运算结果溢出,符号位 \(0+1+1=0\) ,仍为

    +

    设这里两个数补码分别为\([0a_2a_1a_0], +[1b_2b_1b_0]\)

    +

    \(A = \sum\limits_{i=0}^{2}{a_i\times +2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

    +

    原式可化为 \(A+(-8)+B = A+B-8\)

    +

    \(0<A+B-8\le 7 \Rightarrow +8<A+B\le15\)

    +

    值的位运算结果溢出即 \(A+B>7\)

    +

    \(\{x|8< x \le +15\}\subsetneqq\{x|x>7\}\)

    +

    故该情况\(\color{red}{正确}\)

    +
    b.正数绝对值等于负数绝对值
    +

    数学运算结果:\(0\) +,符号为

    +

    位运算结果:值的位运算结果溢出,符号位 \(0+1+1=0\) ,仍为

    +

    设这里两个数补码分别为\([0a_2a_1a_0], +[1b_2b_1b_0]\)

    +

    \(A = \sum\limits_{i=0}^{2}{a_i\times +2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

    +

    原式可化为 \(A+(-8)+B = A+B-8 = 0 +\Rightarrow A+B=8\)

    +

    值的位运算结果溢出即\(A+B>7\)

    +

    \(\{x|x=8\}\subsetneqq\{x|x>7\}\)

    +

    故该情况\(\color{red}{正确}\)

    +
    c.正数绝对值小于负数绝对值
    +

    数学运算结果:一定为负数 ,符号为

    +

    位运算结果:值的位运算结果溢出,符号位 \(0+1+1=0\) ,变成了

    +

    设这里两个数补码分别为\([0a_2a_1a_0], +[1b_2b_1b_0]\)

    +

    \(A = \sum\limits_{i=0}^{2}{a_i\times +2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

    +

    原式可化为 \(A+(-8)+B = A+B-8\)

    +

    \(-8\le A+B - 8<0 \Rightarrow 0 \le +A+B <8\)

    +

    值的位运算结果溢出即 \(A+B>7\)

    +

    \(\{x|0 < x < 8\}\cap\{x|x>7\} = +\varnothing\)

    +

    故该情况\(\color{red}{不存在}\)

    +综上所述,所有存在的情况中,符号都是正确的,因此补码的正确性是可以保证的
    - -

    总结

    本文简单解释了补码的原理

    +

    总结

    +

    本文简单解释了补码的原理

    并证明了补码的正确性

    -



    参考文献

    -

    [1] 《补码正确性的证明》

    -

    [2] 《补码(为什么按位取反再加一):告诉你一个其实很简单的问题》

    +
    +

    参考文献

    +

    [1] 《补码正确性的证明》

    +

    [2] 《补码(为什么按位取反再加一):告诉你一个其实很简单的问题》

    @@ -986,7 +1143,7 @@

    总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/07/09/wu-xu-shu-zu-jiao-huan-ren-yi-liang-ge-yuan-su-zui-shao-jiao-huan-ci-shu/index.html b/2021/07/09/wu-xu-shu-zu-jiao-huan-ren-yi-liang-ge-yuan-su-zui-shao-jiao-huan-ci-shu/index.html index 69a63e456c..96de0f0742 100644 --- a/2021/07/09/wu-xu-shu-zu-jiao-huan-ren-yi-liang-ge-yuan-su-zui-shao-jiao-huan-ci-shu/index.html +++ b/2021/07/09/wu-xu-shu-zu-jiao-huan-ren-yi-liang-ge-yuan-su-zui-shao-jiao-huan-ci-shu/index.html @@ -472,14 +472,33 @@

    无序数组交换任意两个
    -

    无序数组交换任意两个元素 最少交换次数

    题目描述

    给定长度为 $n$ 的无序数组,将数组中的元素按从小到大的顺序排列,每次可以交换任意两个元素,最少要交换几次?

    +

    无序数组交换任意两个元素 +最少交换次数

    +

    题目描述

    +给定长度为 \(n\) +的无序数组,将数组中的元素按从小到大的顺序排列,每次可以交换任意两个元素,最少要交换几次?
    - -

    解题方法

    解法一(较繁琐)

    我们可以遍历一遍原数组,如果当前元素不在正确位置上,将该元素和此时在它正确位置上的元素交换

    +

    解题方法

    +

    解法一(较繁琐)

    +

    我们可以遍历一遍原数组,如果当前元素不在正确位置上,将该元素和此时在它正确位置上的元素交换

    这里有一个要注意的,某次交换后我们可能没有将交换和被交换数都放在正确位置上,且有可能在接下来的遍历中不会再次遍历到被交换数,因此交换后还要再判断,直到当前位置的数正确,显然不这么做不会影响正确结果

    很容易证明这是最少的。

    -

    设当前元素为 $\alpha$ (不在正确位置上),它与非 $\alpha$ 的正确位置上的元素 $\beta$ 交换,会有两种情况:第一种, $\beta$ 在正确位置上。由于最后 $\beta$ 一定会在正确位置上,且一定是 $\beta$ 和在 $\beta$ 正确位置上的元素交换(或 $\beta$ 本来就在正确位置),所以交换 $\alpha$ 和 $\beta$ 就是多余的;第二种, $\beta$ 不在正确位置上。显然对于每次必要交换,我们不在乎正确位置上的元素是什么,我们只需要将正确的元素与其交换就可以了,至于被交换的元素,我们会接下来处理它,所以交换 $\alpha$ 和 $\beta$ 是多余的

    -

    由于我们要知道正确位置,所以要排序好的数组记录正确位置,时间复杂度 $O(n\log n)$

    +

    设当前元素为 \(\alpha\) +(不在正确位置上),它与非 \(\alpha\) +的正确位置上的元素 \(\beta\) +交换,会有两种情况:第一种, \(\beta\) +在正确位置上。由于最后 \(\beta\) +一定会在正确位置上,且一定是 \(\beta\) +和在 \(\beta\) 正确位置上的元素交换(或 +\(\beta\) 本来就在正确位置),所以交换 +\(\alpha\)\(\beta\) 就是多余的;第二种, \(\beta\) +不在正确位置上。显然对于每次必要交换,我们不在乎正确位置上的元素是什么,我们只需要将正确的元素与其交换就可以了,至于被交换的元素,我们会接下来处理它,所以交换 +\(\alpha\)\(\beta\) 是多余的

    +

    由于我们要知道正确位置,所以要排序好的数组记录正确位置,时间复杂度 +\(O(n\log n)\)

    主要代码如下

    int fun(vector<int> a)
     {
    @@ -499,15 +518,24 @@ 

    return ans; }


    - -

    解法二

    对于每个元素,我们将该元素和它的正确位置建边

    -

    最后一定是 $1\sim n$ 个环(自环也算)

    -

    对于有 $k$ 个元素的环,最少交换次数为 $k-1$

    -

    假设共有 $p$ 个环,对于第 $i$ 个环,有 $k_i$ 个元素,则它的最少交换次数为 $k_i - 1$

    -

    因此 $ans = \sum\limits_{i=1}^{p}{(k_i-1)} = \sum\limits_{i=1}^{p}{k_i} - p$

    -

    显然 $n = \sum\limits_{i=1}^{p}{k_i}$

    -

    所以答案就是 $n-p$ ,即元素个数 - 环的个数

    -

    由于我们要知道正确位置,所以要排序好的数组记录正确位置,时间复杂度 $O(n\log n)$

    +

    解法二

    +

    对于每个元素,我们将该元素和它的正确位置建边

    +

    最后一定是 \(1\sim n\) +个环(自环也算)

    +

    对于有 \(k\) +个元素的环,最少交换次数为 \(k-1\)

    +

    假设共有 \(p\) 个环,对于第 \(i\) 个环,有 \(k_i\) 个元素,则它的最少交换次数为 \(k_i - 1\)

    +

    因此 \(ans = \sum\limits_{i=1}^{p}{(k_i-1)} += \sum\limits_{i=1}^{p}{k_i} - p\)

    +

    显然 \(n = +\sum\limits_{i=1}^{p}{k_i}\)

    +

    所以答案就是 \(n-p\) ,即元素个数 - +环的个数

    +

    由于我们要知道正确位置,所以要排序好的数组记录正确位置,时间复杂度 +\(O(n\log n)\)

    主要代码如下(注:其实可以不用递归)

    vector<int> a;
     map<int,int>mp;
    @@ -885,7 +913,7 @@ 

      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/07/10/quan-yuan-zui-duan-lu-johnson-suan-fa/index.html b/2021/07/10/quan-yuan-zui-duan-lu-johnson-suan-fa/index.html index d894bf6ba2..3921e8ab53 100644 --- a/2021/07/10/quan-yuan-zui-duan-lu-johnson-suan-fa/index.html +++ b/2021/07/10/quan-yuan-zui-duan-lu-johnson-suan-fa/index.html @@ -468,48 +468,130 @@

    全源最短路 Johnson算法
    -

    全源最短路 Johnson算法

    本文写于较早时期,之前对Dijkstra的理解不是很透彻

    +

    全源最短路 Johnson算法

    +

    本文写于较早时期,之前对Dijkstra的理解不是很透彻

    已经修改了部分显然错误的内容,有空会再仔细检查的

    -

    模板题P5905 【模板】Johnson 全源最短路

    -

    题意简述:给定一个包含 $n$ 个结点和 $m$ 条带权边的有向图,求全源最短路,可能有负权重的边重边自环,部分数据卡 SPFA 算法(还特地针对了SLF优化)

    +

    模板题P5905 【模板】Johnson +全源最短路

    +

    题意简述:给定一个包含 \(n\) 个结点和 \(m\) +条带权边的有向图,求全源最短路,可能有负权重的边重边自环,部分数据卡 +SPFA 算法(还特地针对了SLF优化)

    题意很明确,就是卡SPFA、Dijkstra、Floyd的

    -

    这题用SPFA可以被卡成$O(n^2m)$,Dijkstra不能处理负权重的边,Floyd$O(n^3)$肯定超时

    +

    这题用SPFA可以被卡成\(O(n^2m)\),Dijkstra不能处理负权重的边,Floyd\(O(n^3)\)肯定超时

    说了这么多,就是为了引出我们要讲的 Johnson 算法

    -

    Johnson算法可以判断给定的图中有无负环,在无负环时能给出全源最短路时间复杂度 $O(n^2\log m)$ (Dijkstra采用优先队列优化,详细的分析可以看这边link

    +

    Johnson算法可以判断给定的图中有无负环,在无负环时能给出全源最短路时间复杂度 +\(O(n^2\log m)\) +(Dijkstra采用优先队列优化,详细的分析可以看这边link

    Johnson算法的核心为重新赋予权值,使得边权变成非负的新边权,而在运行过程中以Bellman-Ford和Dijkstra(以下提到的Dijkstra都用优先队列优化)作为自己的子程序

    -

    一、算法原理

    以下推导过程部分参照《算法导论》

    -

    对于给定有向图 $G=(V,E)$ ,权重函数为 $w:E \rightarrow R$ ,如果所有的边权重都为非负值,则只需在每一个结点上跑Dijkstra即可,时间复杂度$O(V^2\log E)$;如果存在边权重为负值的边,但没有负环,则我们只需要预处理出一种新的权重函数 $w^{\prime}:E \rightarrow R$ 使得所有的边权重为非负值再跑Dijkstra即可

    -

    则新的权重函数 $w^{\prime}$ 一定满足以下两个条件:

    -
      -
    1. 对于结点 $u,v \in V$ ,路径 $p$ 为使用权重函数 $w^{\prime}$ 时 $u$ 到 $v$ 的一条最短路径,当且仅当 $p$ 为使用权重函数 $w$ 时 $u$ 到 $v$ 的一条最短路径
    2. -
    3. 对于任何边 $(u,v) \in E$ ,$w^{\prime}(u,v)$ 为非负值 (Dijkstra不可以处理负权重的边)
    4. +

      一、算法原理

      +

      以下推导过程部分参照《算法导论》

      +

      对于给定有向图 \(G=(V,E)\) +,权重函数为 \(w:E \rightarrow R\) +,如果所有的边权重都为非负值,则只需在每一个结点上跑Dijkstra即可,时间复杂度\(O(V^2\log +E)\);如果存在边权重为负值的边,但没有负环,则我们只需要预处理出一种新的权重函数 +\(w^{\prime}:E \rightarrow R\) +使得所有的边权重为非负值再跑Dijkstra即可

      +

      则新的权重函数 \(w^{\prime}\) +一定满足以下两个条件:

      +
        +
      1. 对于结点 \(u,v \in V\) ,路径 \(p\) 为使用权重函数 \(w^{\prime}\)\(u\)\(v\) 的一条最短路径,当且仅当 \(p\) 为使用权重函数 \(w\)\(u\)\(v\) 的一条最短路径
      2. +
      3. 对于任何边 \((u,v) \in E\)\(w^{\prime}(u,v)\) 为非负值 +(Dijkstra不可以处理负权重的边)
      -

      设函数 $h:V\rightarrow \mathbb{R}$ 将结点映射到实数上

      -

      对于任何边$(u,v) \in E$,定义 $w^{\prime}(u,v) = w(u,v) + h(u) - h(v)$

      -

      我们先假设图 $G$ 没有负环,设路径 $p$ 为使用权重函数 $w^{\prime}$ 时 $v_0$ 到 $v_k$的一条最短路径$(v_0,v_k \in V)$ ,当且仅当 $p$ 为使用权重函数 $w$ 时 $v_0$ 到 $v_k$ 的一条最短路径

      -

      则可以证明 $w^{\prime}(p) = w(p) + h(v_0)-h(v_k)$

      -

      因为 $h$ 为将结点映射到实数上的函数,函数的值不受边权重的影响,所以对于一条为使用权重函数 $w$ 时 $v_0$ 到 $v_k$ 比 $p$ 要长的路径 $q$ ,在使用权重函数 $w^{\prime}$ 时 $q$ 一定比 $p$ 长

      -

      我们再来看看负环的问题,对于环$c = \langle v_0, v_1, … v_k\rangle$ ,其中 $v_0 = v_k$ ,则有 $w^{\prime}(c) = w(c) + h(v_0) -h(v_k) = w(c)$

      -

      则对于 $c$ 使用权重函数 $w$ 时为负环,当且仅当使用权重函数 $w$ 时为负环

      -

      那么 $h$ 函数该如何处理呢?

      -

      我们可以新建一个虚拟结点 $s$ ,并将 $s$ 和其他所有结点建边权重为 $0$ 的边,在 $s$ 结点跑一次Bellman-Ford(SPFA当然可以),求出 $s$ 到其他结点的最短路,则对于任何 $v \in V$ , $h(v)$ 的值即为 $s$ 到 $v$ 的最短路径长,在跑的过程中如果一条路径中某个结点被松弛超过 $n$ 次,则一定存在负环,并返回存在负环的信息(SPFA判负环的方法就不延伸了)

      -

      因为 $s$ 结点入度为 $0$ ,所以其他的路径中不会包含 $s$ 结点

      -

      那么为什么无负环时使用权重函数 $w^{\prime}$ ,边权重一定为非负呢?

      -

      设现在的图为 $G^{\prime} = (V^{\prime},E^{\prime})$ ,$V^{\prime} = V \cup \{s\}$ ,$E^{\prime} = E \cup \{(s,v),v\in E\}$

      -

      根据三角形不等式,可知对于任意结点 $u,v \in V^{\prime}$ ,若 $\exists (u,v) \in E^{\prime}$,则有

      -

      现在我们只需要在每个结点上跑一次Dijkstra即可

      -

      时间复杂度 $O(V^2 \log E)$ (SPFA时间复杂度最坏 $O(VE)$ ,预处理时间复杂度 $O(VE)$ )

      -

      二、参考代码

      模板题代码如下 (注:输出按该题要求写的)

      +h(u) + w(u,v) \ge h(v)&\Rightarrow w(u,v) + h(u) - h(v) \ge 0 +\\\\&\Rightarrow w^{\prime}(u,v) \ge 0 +\end{aligned} +\] 现在我们只需要在每个结点上跑一次Dijkstra即可

      +

      时间复杂度 \(O(V^2 \log +E)\) (SPFA时间复杂度最坏 \(O(VE)\) ,预处理时间复杂度 \(O(VE)\)

      +

      二、参考代码

      +

      模板题代码如下 (注:输出按该题要求写的)

      #include <bits/stdc++.h>
       using namespace std;
       #define int long long
      @@ -627,6 +709,7 @@ 

      } return 0; }

      +

    @@ -983,7 +1066,7 @@

      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/07/21/meng-ti-huo-er-wen-ti-ji-qi-tui-guang/index.html b/2021/07/21/meng-ti-huo-er-wen-ti-ji-qi-tui-guang/index.html index 96b2a5d17e..b5d9847dc6 100644 --- a/2021/07/21/meng-ti-huo-er-wen-ti-ji-qi-tui-guang/index.html +++ b/2021/07/21/meng-ti-huo-er-wen-ti-ji-qi-tui-guang/index.html @@ -468,53 +468,89 @@

    蒙提霍尔问题及其推广
    -

    蒙提霍尔问题及其推广

    前言

    蒙提霍尔问题在《人教版A版数学选择性必修三》上作为阅读与思考的材料出现

    +

    蒙提霍尔问题及其推广

    +

    前言

    +

    蒙提霍尔问题在《人教版A版数学选择性必修三》上作为阅读与思考的材料出现

    本文会提供一种简单的解法并推广这个著名的问题


    - -

    蒙提霍尔问题

    一、背景

    三门问题(Monty Hall problem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目Let’s Make a Deal。问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)

    +

    蒙提霍尔问题

    +

    一、背景

    +三门问题(Monty Hall +problem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目Let's +Make a Deal。问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)
    - -

    二、简介

    参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门是否会增加参赛者赢得汽车的几率

    -


    (注:以上摘自百度)

    +

    二、简介

    +

    参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门是否会增加参赛者赢得汽车的几率

    +

    +(注:以上摘自百度)


    - -

    三、分析

    题面没什么好说的,我们直接来讨论概率问题

    +

    三、分析

    +

    题面没什么好说的,我们直接来讨论概率问题

    根据直觉,我们可能会有以下两种判断

    -
      -
    1. 三扇门中有车的概率都是 $\dfrac{1}{3}$ ,因此不必换门
    2. -
    3. 既然打开了一扇山羊门,那车在剩下两扇门中的概率都为 $\dfrac{1}{2}$ ,因此不必换门
    4. +
        +
      1. 三扇门中有车的概率都是 \(\dfrac{1}{3}\) ,因此不必换门
      2. +
      3. 既然打开了一扇山羊门,那车在剩下两扇门中的概率都为 \(\dfrac{1}{2}\) ,因此不必换门

      那么到底哪个是对的呢?(其实都不对)

      首先我们先来明确概率是什么

      我们称第一次选择后并在剩余的门中开启了一扇有山羊的门后可以选择的门的数量为可选门

      (注:说的有一些绕,但是这样的思路对于该问题的推广很有帮助)

      -

      可选门中换到车门的概率就是 可选门中车门的数量除以可选门的数量

      +

      可选门中换到车门的概率就是 +可选门中车门的数量除以可选门的数量

      接下来我们来分类讨论选择换门后得到车的概率(注:车门指门后是汽车,山羊门指门后是山羊)

      -
        -
      1. 第一次选择的是车门(概率为 $\dfrac{1}{3}$ ),可选门有 $1$ 扇,其中还有 $0$ 扇车门,因此 $P_1=\dfrac{1}{3}\times\dfrac{0}{1} = 0$
      2. -
      3. 第一次选择的是山羊门(概率为 $\dfrac{2}{3}$ ),可选门有 $1$ 扇,其中还有 $1$ 扇车门,因此 $P_2=\dfrac{2}{3}\times\dfrac{1}{1} = \dfrac{2}{3}$
      4. +
          +
        1. 第一次选择的是车门(概率为 \(\dfrac{1}{3}\) ),可选门有 \(1\) 扇,其中还有 \(0\) 扇车门,因此 \(P_1=\dfrac{1}{3}\times\dfrac{0}{1} = +0\)
        2. +
        3. 第一次选择的是山羊门(概率为 \(\dfrac{2}{3}\) ),可选门有 \(1\) 扇,其中还有 \(1\) 扇车门,因此 \(P_2=\dfrac{2}{3}\times\dfrac{1}{1} = +\dfrac{2}{3}\)
        -

        加起来就是选择换门后得到车的概率 $\dfrac{2}{3}$

        -

        而不换门得到车的概率为 $\dfrac{1}{3}$

        -

        是不是很反直觉?

        +

        加起来就是选择换门后得到车的概率 \(\dfrac{2}{3}\)

        +

        而不换门得到车的概率为 \(\dfrac{1}{3}\)

        +是不是很反直觉?
        - -

        四、推广

        现在我们来讨论蒙提霍尔问题的推广(uva10491)

        -

        设有 $a$ 扇山羊门, $b$ 扇车门,主持人打开 $c$ 扇山羊门( $a,b,c\in \mathbb{Z},1\le a,b \le 10000 , 0\le c < a$ )

        -

        显然不换门得到车的概率为 $\dfrac{b}{a+b}$

        +

        四、推广

        +

        现在我们来讨论蒙提霍尔问题的推广(uva10491)

        +

        设有 \(a\) 扇山羊门, \(b\) 扇车门,主持人打开 \(c\) 扇山羊门( \(a,b,c\in \mathbb{Z},1\le a,b \le 10000 , 0\le c +< a\)

        +

        显然不换门得到车的概率为 \(\dfrac{b}{a+b}\)

        那么选择换门后得到车的概率是多少呢?

        -

        第一次选择后剩余的门为 $(a+b-1)$ 扇,可选门的数量为 $(a+b-c-1)$

        +

        第一次选择后剩余的门为 \((a+b-1)\) +扇,可选门的数量为 \((a+b-c-1)\)

        继续来分类讨论

        -
          -
        1. 第一次选择的是车门(概率为 $\dfrac{b}{a+b}$),可选门中车门的数量为 $(b-1)$ 扇,因此 $P_1 = \dfrac{b}{a+b}\times\dfrac{b-1}{a+b-c-1}$
        2. -
        3. 第一次选择的是山羊门(概率为 $\dfrac{a}{a+b}$ ),可选门中车门的数量为 $b$ 扇,因此 $P_2 = \dfrac{a}{a+b}\times\dfrac{b}{a+b-c-1}$
        4. +
            +
          1. 第一次选择的是车门(概率为 \(\dfrac{b}{a+b}\)),可选门中车门的数量为 +\((b-1)\) 扇,因此 \(P_1 = +\dfrac{b}{a+b}\times\dfrac{b-1}{a+b-c-1}\)
          2. +
          3. 第一次选择的是山羊门(概率为 \(\dfrac{a}{a+b}\) ),可选门中车门的数量为 +\(b\) 扇,因此 \(P_2 = +\dfrac{a}{a+b}\times\dfrac{b}{a+b-c-1}\)
          -

          加起来就是选择换门后得到车的概率为 $\dfrac{ab+b(b-1)}{(a+b)(a+b-c-1)}$

          -

          很容易证明换门后得到车的概率一定更大(除了c=0时概率相等)

          +

          加起来就是选择换门后得到车的概率为 \(\dfrac{ab+b(b-1)}{(a+b)(a+b-c-1)}\)

          +很容易证明换门后得到车的概率一定更大(除了c=0时概率相等)
          - -

          总结

          本文介绍并推广了蒙提霍尔问题

          +

          总结

          +

          本文介绍并推广了蒙提霍尔问题

    @@ -867,7 +903,7 @@

    总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/07/22/dao-shu-de-ji-ben-gong-shi-tui-dao/index.html b/2021/07/22/dao-shu-de-ji-ben-gong-shi-tui-dao/index.html index 10686fc3ad..7b3b670a20 100644 --- a/2021/07/22/dao-shu-de-ji-ben-gong-shi-tui-dao/index.html +++ b/2021/07/22/dao-shu-de-ji-ben-gong-shi-tui-dao/index.html @@ -468,170 +468,308 @@

    导数的基本公式推导

    -

    导数的基本公式推导

    主要推导了人教版A版数学选择性必修二上直接给出的基本的导数公式

    +

    导数的基本公式推导

    +

    主要推导了人教版A版数学选择性必修二上直接给出的基本的导数公式

    本文写于作者初三暑假,更新于高一暑假

    可能含有很多不足,如果您方便的话可以联系我修改 awa

    大概率会在高二暑假再更新一次吧

    -
    -

    一、导数的四则运算法则

    设 $f(x),~g(x)$ 均为可导函数

    -

    命题:${[f(x) \pm g(x)]}^{\prime} = f^{\prime}(x) \pm g^{\prime}(x)$

    +
    +

    一、导数的四则运算法则

    +

    \(f(x),~g(x)\) +均为可导函数

    +

    命题\({[f(x) \pm +g(x)]}^{\prime} = f^{\prime}(x) \pm g^{\prime}(x)\)

    证明

    -
    -

    命题:$[f(x)g(x)]^{\prime}=f^{\prime}(x)g(x)+f(x)g^{\prime}(x)$

    +{[f(x) \pm g(x)]}^{\prime}&=\lim\limits_{\Delta x\to +0}{\dfrac{f(x+\Delta x)-f(x) \pm\left[g(x+\Delta x)-g(x)\right]}{\Delta +x}} +\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{f(x+\Delta x)-f(x)}{\Delta +x}} \pm \lim\limits_{\Delta x\to 0}{\dfrac{g(x+\Delta x)-g(x)}{\Delta +x}} +\\\\&=f^{\prime}(x)\pm g^{\prime}(x) +\end{aligned} +\]

    +
    +

    命题\([f(x)g(x)]^{\prime}=f^{\prime}(x)g(x)+f(x)g^{\prime}(x)\)

    证明

    -
    -

    命题:$\left[\dfrac{f(x)}{g(x)}\right]^{\prime} = \dfrac{f^{\prime}(x)g(x)-f(x)g^{\prime}(x)}{\left[g(x)\right]^2}$

    +&=\lim\limits_{\Delta x \to 0}{\dfrac{f(x+\Delta x)g(x+\Delta +x)-f(x)g(x)}{\Delta x}} +\\\\&=\lim\limits_{\Delta x \to 0}{\dfrac{f(x+\Delta x)g(x+\Delta +x)-f(x)g(x+\Delta x)+f(x)g(x+\Delta x)-f(x)g(x)}{\Delta x}} +\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{f(x+\Delta x)-f(x)}{\Delta +x}}\lim\limits_{\Delta x\to 0}{g(x+\Delta x)}+f(x)\lim\limits_{\Delta +x\to 0}{\dfrac{g(x+\Delta x)-g(x)}{\Delta x}} +\\\\&=f^{\prime}(x)g(x)+f(x)g^{\prime}(x) +\end{aligned} +\]

    +
    +

    命题\(\left[\dfrac{f(x)}{g(x)}\right]^{\prime} = +\dfrac{f^{\prime}(x)g(x)-f(x)g^{\prime}(x)}{\left[g(x)\right]^2}\)

    证明

    -
    -

    二、复合函数的求导法则(链式法则)

    命题:设函数 $y=f(u)$ 在 $u=g(x)$ 处可导,而 $u=g(x)$ 在 $x$ 处可导,

    -

    则函数 $y=f(g(x))$ 在 $x$ 处可导,且

    -

    证明

    -

    因为 $u=g(x)$ 在 $x$ 处可导,所以它在这一点也是连续的

    -

    也就是 $\Delta u \to 0$ 当 $\Delta x \to 0$ ,故

    -
    -

    三、基本初等函数的导数公式

    命题:若 $f(x)=x^a(a\in \mathbb{N})$ ,则 $f^{\prime}(x) = a x^{a-1}$

    +{\left[f(g(x))\right]}^{\prime}&=\lim\limits_{\Delta u \to +0}\dfrac{\Delta y}{\Delta u}\lim\limits_{\Delta x \to 0}\dfrac{\Delta +u}{\Delta x} +\\\\&=f^{\prime}(u)g^{\prime}(x) +\\\\&=f^{\prime}(g(x))\cdot g^{\prime}(x) +\end{aligned} +\]

    +
    +

    三、基本初等函数的导数公式

    +

    命题:若 \(f(x)=x^a(a\in +\mathbb{N})\) ,则 \(f^{\prime}(x) = a +x^{a-1}\)

    证明

    -

    可以推广到 $a\in \mathbb{R}$ 的情况,即

    -

    命题:若 $f(x)=x^a(a\in \mathbb{R})$ ,则 $f^{\prime}(x) = a x^{a-1}$

    +f^{\prime}(x)&=\lim\limits_{\Delta x\to 0}{\dfrac{(x+\Delta +x)^{a}-(x)^{a}}{\Delta x}} +\\\\&=\lim\limits_{\Delta x\to +0}{\dfrac{\sum\limits_{r=0}^{a}{C_{a}^{r}x^{a-r}\Delta +x^{r}}-C_{a}^{0}x^{a}}{\Delta x}} +\\\\&=\lim\limits_{\Delta x\to +0}{\dfrac{\sum\limits_{r=1}^{a}{C_{a}^{r}x^{a-r}\Delta x^{r}}}{\Delta +x}} +\\\\&=\lim\limits_{\Delta x\to +0}{\sum\limits_{r=2}^{a}{C_{a}^{r}x^{a-r}\Delta +x^{r-1}+C_{a}^{1}x^{a-1}}} +\\\\&=C_a^{1}x^{a-1} +\\\\&=a x^{a-1} +\end{aligned} +\]

    +

    可以推广到 \(a\in \mathbb{R}\) +的情况,即

    +

    命题:若 \(f(x)=x^a(a\in +\mathbb{R})\) ,则 \(f^{\prime}(x) = a +x^{a-1}\)

    证明

    -

    令 $t=(1+\frac{\Delta x}{x})^a-1$ ,则

    -
    -

    命题:若 $f(x)=\sin x$ ,则 $f^{\prime}(x)=\cos x$

    +f^{\prime}(x)&=x^a\lim\limits_{\Delta x \to +0}\dfrac{t}{\ln\left(1+t\right)} \cdot \dfrac{a\ln\left(1+\frac{\Delta +x}{x}\right)}{\Delta x} +\\\\&=x^a\lim\limits_{\Delta x \to 0}\dfrac{t}{\ln\left(1+t\right)} +\cdot \dfrac{\ln\left(1+\frac{\Delta x}{x}\right)}{\frac{\Delta x}{x}} +\cdot\dfrac{a}{x} +\\\\&=ax^{a-1} +\end{aligned} +\]

    +
    +

    命题:若 \(f(x)=\sin +x\) ,则 \(f^{\prime}(x)=\cos +x\)

    证明

    -
    -

    命题:若 $f(x)=\cos x$ ,则 $f^{\prime}(x) = -\sin x$

    +f^{\prime}(x)&=\lim\limits_{\Delta x\to 0}{\dfrac{\sin (x+\Delta +x)-\sin x}{\Delta x}} +\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{\sin x \cos\Delta x+\sin +\Delta x \cos x-\sin x}{\Delta x}} +\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{\sin x(\cos\Delta +x-1)+\sin\Delta x\cos x}{\Delta x}} +\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{\sin +x\left[\left(1-2\sin^2\frac{\Delta x}{2}\right)-1\right]+\cos +x\left(2\sin\frac{\Delta x}{2}\cos \frac{\Delta x}{2}\right)}{\Delta x}} +\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{2\sin\frac{\Delta +x}{2}\cos\frac{\Delta x}{2}\cos x-2\sin^2\frac{\Delta x}{2}\sin +x}{\Delta x}} +\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{2\sin\frac{\Delta +x}{2}\left(\cos\frac{\Delta x}{2}\cos x-\sin x\sin \frac{\Delta +x}{2}\right)}{\Delta x}} +\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{2\sin\frac{\Delta +x}{2}\cos\left(\frac{\Delta x}{2}+x\right)}{\Delta x}} +\\\\&=\lim\limits_{\Delta x\to 0}{\cos\left(\dfrac{\Delta +x}{2}+x\right)\dfrac{\sin\frac{\Delta x}{2}}{\frac{\Delta x}{2}}} +\\\\&=\lim\limits_{\Delta x\to 0}\cos\left(\dfrac{\Delta +x}{2}+x\right) +\\\\&=\cos x +\end{aligned} +\]

    +
    +

    命题:若 \(f(x)=\cos +x\) ,则 \(f^{\prime}(x) = -\sin +x\)

    证明

    -
    -

    命题:若 $f(x)=a^x (a>0,\text{且}a \ne 1)$ ,则 $f^{\prime}(x)=a^x\ln a$

    -

    证明

    -

    令 $t=a^{\Delta x}-1$ ,则$\Delta x = \log_a(t+1)$

    -

    -

    则原极限可化为

    -

    特别地,当 $a=e$ 时,即 $f(x)=e^x$ ,有 $f^{\prime}(x)=e^x$

    -
    -

    命题:若 $f(x)=\log_ax(a>0,\text{且}a \ne 1)$ ,则 $f^{\prime}(x) = \dfrac{1}{x\ln a}$

    -

    证明

    -

    令 $t=\dfrac{\Delta x}{x}$ ,则 $\dfrac{1}{\Delta x} = \dfrac{1}{tx}$

    -

    $\therefore$ 原极限可化为

    -

    特别地,当 $a=e$ 时,即 $f(x)=\ln x$ ,有 $f^{\prime}(x)=\dfrac{1}{x}$

    -
    -

    命题:若 $f(x)=\varphi ( \varphi \texttt{为常数})$ ,则 $f^{\prime}(x)=0$

    -

    证明

    -
    +f^{\prime}(x) &=\lim\limits_{\Delta x\to +0}{\dfrac{\varphi-\varphi}{\Delta x}} +\\&=0 +\end{aligned} +\]

    +

    参考文献

    -

    [1] 复合函数的导数(链式法则)

    -

    [2] 基本初等函数的导数

    +

    [1] 复合函数的导数(链式法则)

    +

    [2] 基本初等函数的导数

    @@ -989,7 +1127,7 @@

     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/09/qian-tan-wu-dao-lian-dlx/index.html b/2021/08/09/qian-tan-wu-dao-lian-dlx/index.html index 642dd440c6..162adf5044 100644 --- a/2021/08/09/qian-tan-wu-dao-lian-dlx/index.html +++ b/2021/08/09/qian-tan-wu-dao-lian-dlx/index.html @@ -472,37 +472,125 @@

    浅谈舞蹈链(DLX)

    -

    浅谈舞蹈链(DLX)

    前言

    舞蹈链的名字真好玩…

    +

    浅谈舞蹈链(DLX)

    +

    前言

    +

    舞蹈链的名字真好玩...


    - -

    一、舞蹈链概述

    舞蹈链 (Dancing links),也叫 DLX ,是由 Donald Knuth 提出的数据结构,目的是快速实现他提出的X算法。X算法是一种递归算法,时间复杂度不确定,深度优先,通过回溯寻找精确覆盖问题所有可能的解

    +

    一、舞蹈链概述

    +

    舞蹈链 (Dancing links),也叫 DLX +,是由 Donald Knuth +提出的数据结构,目的是快速实现他提出的X算法。X算法是一种递归算法,时间复杂度不确定,深度优先,通过回溯寻找精确覆盖问题所有可能的解

    (以上摘自维基百科)

    舞蹈链的主要思想来源于双向链表

    -

    我们设 $l[x]$ 表示元素 $x$ 的左指针, $r[x]$ 表示元素 $x$ 的右指针

    -

    显然,如果想要删除元素 $x$ ,我们可以做以下操作

    +

    我们设 \(l[x]\) 表示元素 \(x\) 的左指针, \(r[x]\) 表示元素 \(x\) 的右指针

    +

    显然,如果想要删除元素 \(x\) +,我们可以做以下操作

    r[l[x]]=r[x]; // x左侧的元素的右指针指向x右侧的元素
     l[r[x]]=l[x]; // x右侧的元素的左指针指向x左侧的元素
    -

    那恢复元素 $x$ 呢? 我们可以发现删除 $x$ 的时候, $x$ 的左右指针并没有改变,即 $l[x]$ 和 $r[x]$ 并没有改变,于是我们可以做以下操作

    +

    那恢复元素 \(x\) 呢? +我们可以发现删除 \(x\) 的时候, \(x\) 的左右指针并没有改变,即 \(l[x]\)\(r[x]\) +并没有改变,于是我们可以做以下操作

    r[l[x]]=x; // x左侧的元素的右指针重新指向x
     l[r[x]]=x; // x右侧的元素的左指针重新指向x
    -

    这样如果 $x$ 左右两侧没有改变,我们就可以恢复 $x$ 所在的位置

    +

    这样如果 \(x\) +左右两侧没有改变,我们就可以恢复 \(x\) +所在的位置

    那么精确覆盖问题又是什么呢?

    -

    给定矩阵,要求选出一个由若干行组成的集合,使得每一列上都有且仅有一个 $1$

    -

    例如该矩阵选出的行为 $1,4,5$ 行

    +

    给定矩阵,要求选出一个由若干行组成的集合,使得每一列上都有且仅有一个 +\(1\)

    +

    \[\begin{pmatrix}0&0&1&0&1&1&0 +\\ 1&0&0&1&0&0&1 \\ +0&1&1&0&0&1&0 \\ +1&0&0&1&0&0&0 \\ +0&1&0&0&0&0&1 \\ +0&0&0&1&1&0&1\end{pmatrix}\]

    +

    例如该矩阵选出的行为 \(1,4,5\) +行

    我们来模拟一下朴素X算法求解的过程

    以下过程用红色表示选择了这一行,绿色表示存在冲突的元素,灰色表示删除的行

    1.选择第一行

    -

    2.标记所有和第一行冲突的元素

    -

    3.删除存在冲突的行

    -

    4.接着选择第二行

    -

    5.标记与第二行冲突的元素

    -

    6.删除存在冲突的行

    -

    7.发现没有可以选择的行了,而已选的不满足要求,回溯,选择第四行

    -

    8.接下来的同理,不断执行,直到找到答案

    -

    我们会发现, $X$ 算法花了大量的时间在找 $1$ ,而且删改很不方便

    +

    \[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0 +\\ 1&0&0&1&0&0&1 \\ +0&1&1&0&0&1&0 \\ +1&0&0&1&0&0&0 \\ +0&1&0&0&0&0&1 \\ +0&0&0&1&1&0&1\end{pmatrix}\]

    +

    2.标记所有和第一行冲突的元素

    +

    \[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0 +\\ 1&0&0&1&0&0&1 \\ +0&1&\color{green}1&0&0&\color{green}1&0 \\ +1&0&0&1&0&0&0 \\ +0&1&0&0&0&0&1 \\ +0&0&0&1&\color{green}1&0&1\end{pmatrix}\]

    +

    3.删除存在冲突的行

    +

    \[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0 +\\ 1&0&0&1&0&0&1 \\ +\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0 +\\ 1&0&0&1&0&0&0 \\ +0&1&0&0&0&0&1 \\ +\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}1\end{pmatrix}\]

    +

    4.接着选择第二行

    +

    \[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0 +\\ +\color{red}1&\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}0&\color{red}1 +\\ +\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0 +\\ 1&0&0&1&0&0&0 \\ +0&1&0&0&0&0&1 \\ +\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}1\end{pmatrix}\]

    +

    5.标记与第二行冲突的元素

    +

    \[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0 +\\ +\color{red}1&\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}0&\color{red}1 +\\ +\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0 +\\ \color{green}1&0&0&\color{green}1&0&0&0 \\ +0&1&0&0&0&0&\color{green}1 \\ +\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}1\end{pmatrix}\]

    +

    6.删除存在冲突的行

    +

    \[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0 +\\ +\color{red}1&\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}0&\color{red}1 +\\ +\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0 +\\ +\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}0 +\\ +\color{grey}0&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1 +\\ +\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}1\end{pmatrix}\]

    +

    7.发现没有可以选择的行了,而已选的不满足要求,回溯,选择第四行

    +

    \[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0 +\\ 1&0&0&1&0&0&1 \\ +\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0 +\\ +\color{red}1&\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}0&\color{red}0 +\\ 0&1&0&0&0&0&1 \\ +\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}1\end{pmatrix}\] +8.接下来的同理,不断执行,直到找到答案

    +

    我们会发现, \(X\) +算法花了大量的时间在找 \(1\) +,而且删改很不方便

    为了解决这个问题,舞蹈链就产生了

    -

    模板题 $\to$ P4929 【模板】舞蹈链(DLX)

    -

    (注:为了方便讲述,以下引用这篇博客中的图片(感谢图片的作者!))

    +

    模板题 \(\to\) P4929 +【模板】舞蹈链(DLX)

    +

    (注:为了方便讲述,以下引用这篇博客中的图片(感谢图片的作者!))

    舞蹈链的结构即交叉十字循环双向链,本文中以数组形式实现链表

    int n,m; // 行、列数
     int u[MAXN],d[MAXN],l[MAXN],r[MAXN],h[MAXN]; 
    @@ -510,9 +598,13 @@ 

    int row[MAXN],col[MAXN],s[MAXN],ansk[MAXN],pos; // 每个结点原先所在的行、列;每一列的结点个数;ansk记录搜索信息;结点总数

    -

    如下图所示
    在这里插入图片描述
    别急!我们一步一步来实现这个复杂的数据结构

    -

    首先初始化上方的列头结点

    -

    我们可以称列头结点为限制,行头结点为决策 (注:这个做题的时候有用)

    void init()
    +

    如下图所示 +别急!我们一步一步来实现这个复杂的数据结构

    +

    首先初始化上方的列头结点

    +

    我们可以称列头结点为限制,行头结点为决策 (注:这个做题的时候有用) +

    void init()
     {
     	for(R int i=0; i<=m; i++)
     	{
    @@ -524,7 +616,9 @@ 

    memset(h,-1,sizeof(h)); //每一行的头结点都为空 memset(s,0,sizeof(s)); //每一列的结点数都为0 pos=m+1; //已经搭建好了m个列头结点,下一个加入的结点从m+1开始编号 -}


    接下来,我们来把插入结点的功能完成(注:这里比较复杂,可以感性理解一下)
    void link(R int x,R int y)
    +}
    +接下来,我们来把插入结点的功能完成(注:这里比较复杂,可以感性理解一下) +
    void link(R int x,R int y)
     {
     	s[y]++; //所在的列结点数加1
     	row[pos]=x;col[pos]=y; //记录编号为pos的结点(即新加入的结点)的行和列
    @@ -541,7 +635,7 @@ 

    [h[x]]=pos; } pos++; //下一个结点不要标错号了 -}


    现在我们来完成删除和恢复操作(差不多的)
    inline void rm(R int y)
    +}
    现在我们来完成删除和恢复操作(差不多的)
    inline void rm(R int y)
     {
     	l[r[y]]=l[y];r[l[y]]=r[y];//删除y列的结点(这个位置已经填满了)
     	for(R int i=d[y]; i!=y; i=d[i])
    @@ -562,7 +656,8 @@ 

    [col[j]]++; } r[l[y]]=y;l[r[y]]=y; -}


    然后可以开始跳舞了
    主体部分,就是深度优先搜索
    bool dance(R int dep)
    +}
    +然后可以开始跳舞了 主体部分,就是深度优先搜索
    bool dance(R int dep)
     {
     	if(!r[0])//所有的列头结点都被选了,说明成功了
     	{
    @@ -583,10 +678,20 @@ 

    } rv(y); return 0; -}


    如果您不太理解的话,可以看看下面的动图(注:其实和之前模拟的有些相似)

    -

    算法执行过程 (注:图片是这篇博客的)

    -

    在这里插入图片描述

    -

    最终的答案即下图 (选择 $1,4,5$ )
    在这里插入图片描述
    最后贴上完整代码

    #include <bits/stdc++.h>
    +}
    +如果您不太理解的话,可以看看下面的动图(注:其实和之前模拟的有些相似)

    +

    算法执行过程 (注:图片是这篇博客的)

    +
    + + +
    +

    最终的答案即下图 (选择 \(1,4,5\) ) + 最后贴上完整代码

    #include <bits/stdc++.h>
     using namespace std;
     #define int long long
     #define R register
    @@ -689,26 +794,31 @@ 

    return 0; }


    - -

    二、舞蹈链例题

    如果您看到这里,还是很明白的话,那么我们来讲个例题

    -

    题目链接:SP13980 SUDOGOB - Sudoku goblin

    -

    题意:给定一个 $9 \times 9$ 的数独,输出可填的方案数,多组数据

    -

    选择这个例题当然不是让你写暴搜的

    +

    二、舞蹈链例题

    +

    如果您看到这里,还是很明白的话,那么我们来讲个例题

    +

    题目链接:SP13980 +SUDOGOB - Sudoku goblin

    +

    题意:给定一个 \(9 \times +9\) 的数独,输出可填的方案数,多组数据

    +

    选择这个例题当然不是让你写暴搜的

    首先考虑决策

    -

    每个格子上填数字,至多有 $9\times 9\times 9 = 729$ 种决策

    +

    每个格子上填数字,至多有 \(9\times 9\times +9 = 729\) 种决策

    再考虑限制

    -
      +
      1. 每个点只能填一个数
      2. 每行一个数只能填一次
      3. 每列一个数只能填一次
      4. 每个九宫格一个数只能填一次
      -

      限制数为 $9\times 9 \times 4 = 324$

      +

      限制数为 \(9\times 9 \times 4 = +324\)

      对于精准覆盖问题,我们本质上是在选择若干决策,使其恰好满足所有限制条件

      因此这题可以用 DLX 求解

      -

      那么 MAXN只要开到 729*324 就行了(注:不过开大点保险)

      -

      这题唯一的细节是插入结点的行、列,还是很简单的题目

      -

      代码如下

      #include <bits/stdc++.h>
      +

      那么 MAXN只要开到 729*324 +就行了(注:不过开大点保险)

      +

      这题唯一的细节是插入结点的行、列,还是很简单的题目

      +

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
       #define int long long
       #define R register
      @@ -839,11 +949,12 @@ 

      return 0; }

      当然,如果您有兴趣的话,可以去做下这道题

      -

      题目链接:SP1110 SUDOKU - Sudoku

      -

      这道题就是上一题的加强版,其实没什么区别,如果您理解了例题的话,这题就很简单了

      +

      题目链接:SP1110 +SUDOKU - Sudoku

      +

      这道题就是上一题的加强版,其实没什么区别,如果您理解了例题的话,这题就很简单了


      - -

      总结

      本文介绍了舞蹈链

      +

      总结

      +

      本文介绍了舞蹈链

    @@ -1200,7 +1311,7 @@

    总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/10/ubuntu-qiang-zhi-xie-zai-vmware-player/index.html b/2021/08/10/ubuntu-qiang-zhi-xie-zai-vmware-player/index.html index f2612e9cf3..3c4c19caa4 100644 --- a/2021/08/10/ubuntu-qiang-zhi-xie-zai-vmware-player/index.html +++ b/2021/08/10/ubuntu-qiang-zhi-xie-zai-vmware-player/index.html @@ -468,12 +468,17 @@

    ubuntu 强制卸载vmware player
    -

    ubuntu 强制卸载vmware player

    不知道什么时候下了vmware-player,然后怎么都删不掉

    -

    解决方法

    打开终端,输入以下指令

    locate vmware-player

    然后会出现一大堆vmware player的文件

    -

    复制一下,再把每一行都加上 sudo rmsudo rmdir

    +

    ubuntu 强制卸载vmware player

    +

    不知道什么时候下了vmware-player,然后怎么都删不掉

    +

    解决方法

    +

    打开终端,输入以下指令

    locate vmware-player
    然后会出现一大堆vmware +player的文件

    +

    复制一下,再把每一行都加上 +sudo rmsudo rmdir

    (注:这里注意一下删除顺序,rmdir似乎不能直接删除非空文件夹)

    -

    非常简单,非常暴力,直接删除这些文件

    -

    当然如果能正常卸载的话还是这个更好一点

    sudo vmware-installer -u vmware-player

    +

    非常简单,非常暴力,直接删除这些文件

    +

    当然如果能正常卸载的话还是这个更好一点 +

    sudo vmware-installer -u vmware-player

    @@ -831,7 +836,7 @@

     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/13/pei-shu-ding-li-ji-qi-zheng-ming/index.html b/2021/08/13/pei-shu-ding-li-ji-qi-zheng-ming/index.html index e84dfd996d..4c9c6fa5c9 100644 --- a/2021/08/13/pei-shu-ding-li-ji-qi-zheng-ming/index.html +++ b/2021/08/13/pei-shu-ding-li-ji-qi-zheng-ming/index.html @@ -468,36 +468,64 @@

    裴蜀定理及其证明

    -

    裴蜀定理及其证明

    前言

    原来裴蜀是法国数学家QwQ

    +

    裴蜀定理及其证明

    +

    前言

    +

    原来裴蜀是法国数学家QwQ


    - -

    一、裴蜀定理

    对于 $x,y$ 的二元一次不定方程 $ax+by=c$ ,其有解的充要条件为 $\gcd(a,b)\mid c$

    -

    1.充分性证明

    充分性:若 $\gcd(a,b)\mid c$ ,则 $ax+by=c$ 有解

    -

    设 $k$ 为 $a,b$ 线性组合的最小非负解

    -

    令 $q=\left\lfloor\dfrac{a}{k}\right\rfloor$ ,则有 $r=a \mod k = a-qk = a-q(ax+by) = a(1-qx)+b(-qy)$

    -

    显然 $r$ 也为 $a,b$ 线性组合的解,且 $0\le r \le k$

    -

    $\because k$ 为最小非负解

    -

    $\therefore r=0$

    -

    $\therefore k\mid a$

    -

    同理 $k\mid b$

    -

    令 $d=\gcd(a,b)$ ,则 $s\mid d$ 且 $d \ge s$

    -

    $\because d\mid a,d\mid b$ 且 $s$ 为 $a,b$ 线性组合的解

    -

    $\therefore d\mid s$

    -

    $\because s>0$

    -

    $\therefore d=s$

    -

    则 $ax+by=c$ 的最小非负解为 $\gcd(a,b)$

    -

    显然 $\forall c=k\gcd(a,b),k\in \mathbb{Z}^+$ 是原方程的解

    -

    2.必要性证明

    必要性:若 $ax+by=c$ 有解,则 $\gcd(a,b)\mid c$
    证明:
    令 $d=\gcd(a,b)$ ,则 $d\mid a,d\mid b$

    -

    $\because ax+by=c$ 有解

    -

    $\therefore d\mid ax,d\mid by$

    -

    $\therefore d\mid ax+by=c$

    -

    3.推广

    对于不定方程 $x_1y_1+x_2y_2+…+x_ny_n=k, \forall y_i \in \mathbb{Z}$ ,其有解的充要条件为 $\gcd\{x_i\}\mid k$

    +

    一、裴蜀定理

    +

    对于 \(x,y\) +的二元一次不定方程 \(ax+by=c\) ,其有解的充要条件为 \(\gcd(a,b)\mid c\)

    +

    1.充分性证明

    +

    充分性:若 \(\gcd(a,b)\mid c\) ,则 +\(ax+by=c\) 有解

    +

    \(k\)\(a,b\) 线性组合的最小非负解

    +

    \(q=\left\lfloor\dfrac{a}{k}\right\rfloor\) +,则有 \(r=a \mod k = a-qk = a-q(ax+by) = +a(1-qx)+b(-qy)\)

    +

    显然 \(r\) 也为 \(a,b\) 线性组合的解,且 \(0\le r \le k\)

    +

    \(\because k\) 为最小非负解

    +

    \(\therefore r=0\)

    +

    \(\therefore k\mid a\)

    +

    同理 \(k\mid b\)

    +

    \(d=\gcd(a,b)\) ,则 \(s\mid d\)\(d +\ge s\)

    +

    \(\because d\mid a,d\mid b\) 且 +\(s\)\(a,b\) 线性组合的解

    +

    \(\therefore d\mid s\)

    +

    \(\because s>0\)

    +

    \(\therefore d=s\)

    +

    \(ax+by=c\) 的最小非负解为 \(\gcd(a,b)\)

    +

    显然 \(\forall c=k\gcd(a,b),k\in +\mathbb{Z}^+\) 是原方程的解

    +

    2.必要性证明

    +

    必要性:若 \(ax+by=c\) 有解,则 +\(\gcd(a,b)\mid c\) 证明: 令 \(d=\gcd(a,b)\) ,则 \(d\mid a,d\mid b\)

    +

    \(\because ax+by=c\) 有解

    +

    \(\therefore d\mid ax,d\mid by\)

    +

    \(\therefore d\mid ax+by=c\)

    +

    3.推广

    +

    对于不定方程 \(x_1y_1+x_2y_2+...+x_ny_n=k, +\forall y_i \in \mathbb{Z}\) ,其有解的充要条件为 \(\gcd\{x_i\}\mid k\)

    证明略


    - -

    二、裴蜀定理模板题

    题目链接:P4549 【模板】裴蜀定理

    +

    二、裴蜀定理模板题

    +

    题目链接:P4549 +【模板】裴蜀定理

    即推广结论裸题

    -

    要注意负数要转成正数再算 $\gcd$

    +

    要注意负数要转成正数再算 \(\gcd\)

    代码如下

    #include <bits/stdc++.h>
     using namespace std;
    @@ -519,8 +547,8 @@ 

    return 0; }


    - -

    总结

    本文简单介绍了裴蜀定理

    +

    总结

    +

    本文简单介绍了裴蜀定理

    @@ -873,7 +901,7 @@

    总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/15/qian-tan-ke-duo-li-shu-odt/index.html b/2021/08/15/qian-tan-ke-duo-li-shu-odt/index.html index 57333abc69..64403aadfe 100644 --- a/2021/08/15/qian-tan-ke-duo-li-shu-odt/index.html +++ b/2021/08/15/qian-tan-ke-duo-li-shu-odt/index.html @@ -463,25 +463,45 @@

    浅谈珂朵莉树(ODT)

    -

    浅谈珂朵莉树(ODT)

    前言

    珂学家狂喜(

    -
    -

    一、珂朵莉树来源

    珂朵莉树,原名老司机树(Old Driver Tree),在某场CF比赛中提出

    +

    浅谈珂朵莉树(ODT)

    +

    前言

    +

    珂学家狂喜(

    +
    +

    一、珂朵莉树来源

    +

    珂朵莉树,原名老司机树(Old Driver +Tree),在某场CF比赛中提出

    因为题目背景是《末日时在做什么?有没有空?可以来拯救吗?》中的珂朵莉,所以就叫珂朵莉树了

    -
    -

    二、珂朵莉树

    题目链接:CF896C Willem, Chtholly and Seniorious

    +
    +

    二、珂朵莉树

    +

    题目链接:CF896C +Willem, Chtholly and Seniorious

    题目要求维护一种数据结构,支持以下操作

    -
      -
    1. 将 $[l,r]$ 区间内所有数加上 $x$
    2. -
    3. 将 $[l,r]$ 区间内所有数改成 $x$
    4. -
    5. 求 $[l,r]$ 区间第 $k$ 小的数
    6. -
    7. 求 $[l,r]$ 区间内所有数的 $x$ 次方的和取模 $y$
    8. +
        +
      1. \([l,r]\) 区间内所有数加上 \(x\)
      2. +
      3. \([l,r]\) +区间内所有数改成 \(x\)
      4. +
      5. \([l,r]\) 区间第 \(k\) 小的数
      6. +
      7. \([l,r]\) 区间内所有数的 \(x\) 次方的和取模 \(y\)

      值得注意的是,数据为随机数据,说明没有故意构造卡的数据

      -

      1.珂朵莉树有什么用?

      最主要的就是区间内的推平操作了,即本题中的操作 $2$

      +

      1.珂朵莉树有什么用?

      +

      最主要的就是区间内的推平操作了,即本题中的操作 \(2\)

      当然还有别的操作,那就不是最主要的了

      -

      2.原理是什么?

      写在前面:这个数据结构唯一前置知识就只有set

      -

      a.存储

      我们可以把区间看作若干个结点,每个结点都有自己的左端点 $l$ 、右端点 $r$ 以及值 $v$

      -

      显然,一开始的时候每个结点的 $l=r=idx$ , $idx$ 指该元素在数组中的下标

      +

      2.原理是什么?

      +

      写在前面:这个数据结构唯一前置知识就只有set

      +

      a.存储

      +

      我们可以把区间看作若干个结点,每个结点都有自己的左端点 \(l\) 、右端点 \(r\) 以及值 \(v\)

      +

      显然,一开始的时候每个结点的 \(l=r=idx\)\(idx\) 指该元素在数组中的下标

      我们可以把这些结点按左端点顺序存储在一个set

      struct node
       {
      @@ -492,19 +512,35 @@ 

      a. return l<o.l; } };

      -

      注意 int v前的关键字 mutable,这个关键字和const恰好相反,意思是使v始终允许修改,即使它是个常量

      +

      注意 int v前的关键字 +mutable,这个关键字和const恰好相反,意思是使v始终允许修改,即使它是个常量

      那么我们为什么要多次一举呢?过会再说(

      -

      b.分割结点

      显然每次推平操作并不能保证区间左、右端点恰好就在一个结点上,因此我们还需要对结点进行分割

      -

      考虑查找一个结点,使得其左端点恰好为 $pos$ ,$pos$ 指某次操作中的一个分割点

      -

      我们可以用set中的lower_bound()函数来找到第一个左端点大于等于 $pos$ 的结点

      +

      b.分割结点

      +

      显然每次推平操作并不能保证区间左、右端点恰好就在一个结点上,因此我们还需要对结点进行分割

      +

      考虑查找一个结点,使得其左端点恰好为 \(pos\)\(pos\) 指某次操作中的一个分割点

      +

      我们可以用set中的lower_bound()函数来找到第一个左端点大于等于 +\(pos\) 的结点

      (注:因为我们是按左端点顺序排序的)

      这个lower_bound()会返回一个set常量迭代器

      现在我们找到了一个结点,那么会出现以下三种情况

      -

      第一种情况: $pos$ 恰好为一个结点的左端点,直接返回这个端点的迭代器(显然前提是这个结点不是s.end()

      -

      那么其他情况得到的这个结点的左端点一定比 $pos$ 大

      +

      第一种情况\(pos\) +恰好为一个结点的左端点,直接返回这个端点的迭代器(显然前提是这个结点不是s.end()

      +

      那么其他情况得到的这个结点的左端点一定\(pos\)

      因此可以尝试分割前一个结点,即把迭代器it--

      -

      第二种情况:这个结点的右端点小于 $pos$ ,由于结点的端点一定是连续的,说明 $pos$ 是新加入的结点,直接 return s.end()

      -

      第三种情况:最普遍的情况,找到了一个结点恰好包含 $pos$ ,因为我们要的是以 $pos$ 为左端点的结点,显然我们要把这个结点分割成 $node\{l,pos-1,v\}$ 和 $node\{pos,r,v\}$

      +

      第二种情况:这个结点的右端点小于 \(pos\) ,由于结点的端点一定是连续的,说明 +\(pos\) 是新加入的结点,直接 +return s.end()

      +

      第三种情况:最普遍的情况,找到了一个结点恰好包含 +\(pos\) ,因为我们要的是以 \(pos\) +为左端点的结点,显然我们要把这个结点分割成 \(node\{l,pos-1,v\}\)\(node\{pos,r,v\}\)

      set<node>::iterator split(R int pos)
       {
       	set<node>::iterator it=s.lower_bound({pos});
      @@ -517,12 +553,15 @@ 

      return s.insert({pos,r,v}).first; // insert函数的返回值是pair类型的,而它的first恰好使我们需要的(新插入结点的指针) }

      -

      c.推平

      解决了区间的端点问题,我们只要获取要求修改区间左、右端点的结点,把这一段删除,再插入要求赋的值和修改区间的左、右端点作为新的结点

      +

      c.推平

      +

      解决了区间的端点问题,我们只要获取要求修改区间左、右端点的结点,把这一段删除,再插入要求赋的值和修改区间的左、右端点作为新的结点

      注意一定要先找右端点所在结点,再找左端点所在结点

      为什么?因为我们再分割结点时大概率删除了部分结点,并加入新的结点,如果我们先找左端点所在结点,再找右端点所在结点,很有左端点所在结点的迭代器已经失效了

      -

      例如有一个结点 $node\{l=1,r=5\}$ ,修改区间的左端点为 $1$ ,右端点为 $3$

      +

      例如有一个结点 \(node\{l=1,r=5\}\) +,修改区间的左端点为 \(1\) ,右端点为 +\(3\)

      按先左再右的顺序,我们先会得到左端点所在结点

      -

      $node\{l=1,r=5\}$

      +

      \(node\{l=1,r=5\}\)

      显然如果我们找右端点所在结点,会将左端点所在结点进行分割,那么原来的结点就没了,迭代器失效,然后RE

      inline void assign(R int l,R int r,R int k)
       {
      @@ -530,7 +569,8 @@ 

      c. s.erase(itl,itr); // 删掉[itl,itr)中所有结点 s.insert({l,r,k}); // 插入新结点 }

      -

      d.剩余操作

      区间加,十分暴力,十分简单,我们只要找到左、右端点所在结点,然后直接把每个结点修改就行

      +

      d.剩余操作

      +

      区间加,十分暴力,十分简单,我们只要找到左、右端点所在结点,然后直接把每个结点修改就行

      现在知道为什么要写mutable了吧!因为split()返回的是常量迭代器

      inline void add(R int l,R int r,R int k)
       {
      @@ -538,8 +578,14 @@ 

      for(R set<node>::iterator it=itl; it!=itr; it++) it->v+=k; // 直接加 }

      -

      区间第 $k$ 小数,我们只要把区间内的所有结点取出来从大到小排序即可

      -

      注意每个结点指代的可能是一段区间,而我们要求的是第 $k$ 小的数,因此每遍历一个结点,如果 $k$ 大于该结点指代的区间长,则让 $k$ 减去该结点指代的区间长,否则第 $k$ 小的数就在该结点区间内,直接输出即可

      +

      区间第 \(k\) +小数,我们只要把区间内的所有结点取出来从大到小排序即可

      +

      注意每个结点指代的可能是一段区间,而我们要求的是第 \(k\) 小的数,因此每遍历一个结点,如果 \(k\) 大于该结点指代的区间长,则让 \(k\) 减去该结点指代的区间长,否则第 \(k\) +小的数就在该结点区间内,直接输出即可

      struct Rank
       {
       	int num,cnt;
      @@ -569,9 +615,13 @@ 

      =(ans%y+qpow(it->v,x,y)%y*(it->r-it->l+1)%y)%y; // 快速幂qpow()就不贴了 return ans; }

      -

      3.复杂度分析

      set实现的珂朵莉树复杂度为 $O(n\log^2n)$

      -

      不过本人不是很会分析,这篇文章分析证明的很好,大家可以看看

      -

      完整代码 (注:原题的随机数据是给定 $seed$ 等自行生成)

      +

      3.复杂度分析

      +

      set实现的珂朵莉树复杂度为 \(O(n\log^2n)\)

      +

      不过本人不是很会分析,这篇文章分析证明的很好,大家可以看看

      +

      完整代码 (注:原题的随机数据是给定 \(seed\) 等自行生成)

      #include <bits/stdc++.h>
       using namespace std;
       #define int long long
      @@ -696,14 +746,22 @@ 


      - - -

      三、珂朵莉树例题

      (注:都是洛谷上的题~)

      -

      1.P4979 矿洞:坍塌

      题目链接:P4979 矿洞:坍塌

      +

      三、珂朵莉树例题

      +

      (注:都是洛谷上的题~)

      +

      1.P4979 矿洞:坍塌

      +

      题目链接:P4979 +矿洞:坍塌

      题意:要求维护一个数据结构,支持对给定字符串进行如下操作

      -

      A x y op表示替换材料,将 $x$ 到 $y(1\le x\le y\le N)$
      区间内的材料替换为opop为$A,B,C$ 三种材料字符中的一个

      -

      B x y表示是否询问,即询问 $x$ 到 $y(1\le x\le y\le
      N)$区间内的材料是否合法,合法输出Yes,不合法输出No

      +

      A x y op表示替换材料,将 \(x\)\(y(1\le +x\le y\le N)\) +区间内的材料替换为opop\(A,B,C\) 三种材料字符中的一个

      +

      B x y表示是否询问,即询问 \(x\)\(y(1\le +x\le y\le +N)\)区间内的材料是否合法,合法输出Yes,不合法输出No

      合法指该区间连续且材料相等,并且该区间前一个和后一个材料不相同

      几乎是板子题,没什么特别的,只要跟题目意思写查询操作就行

      @@ -797,21 +855,35 @@

      } return 0; }

    -

    2.P5350 序列

    题目链接:P5350 序列

    +

    2.P5350 序列

    +

    题目链接:P5350 +序列

    题意:要求维护一个数据结构,支持对给定数组进行以下操作

    -

    1 l r求 $[l,r]$ 的区间和

    -

    2 l r v将 $[l,r]$ 赋值为 $v$

    -

    3 l r v将 $[l,r]$ 加上 $v$

    -

    4 l1 r1 l2 r2将 $[l_1,r_1]$ 复制到 $[l_2,r_2]$

    -

    5 l1 r1 l2 r2将 $[l_1,r_1]$ 和 $[l_2,r_2]$ 交换

    -

    6 l r将 $[l,r]$翻转

    +

    1 l r\([l,r]\) +的区间和

    +

    2 l r v\([l,r]\) +赋值为 \(v\)

    +

    3 l r v\([l,r]\) +加上 \(v\)

    +

    4 l1 r1 l2 r2\([l_1,r_1]\) 复制到 \([l_2,r_2]\)

    +

    5 l1 r1 l2 r2\([l_1,r_1]\)\([l_2,r_2]\) 交换

    +

    6 l r\([l,r]\)翻转

    我直呼神仙题

    本题前三个操作就是基本操作

    后三个操作值得探讨

    -

    首先是将 $[l_1,r_1]$ 中所有数复制到 $[l_2,r_2]$

    -

    我们可以把 $[l_1,r_1]$ 中所有结点记录,然后直接用这些结点推平 $[l_2,r_2]$

    +

    首先是将 \([l_1,r_1]\) +中所有数复制到 \([l_2,r_2]\)

    +

    我们可以把 \([l_1,r_1]\) +中所有结点记录,然后直接用这些结点推平 \([l_2,r_2]\)

    struct THR
     {
     	int l,r,v;
    @@ -826,8 +898,10 @@ 

    for(R int i=0; i<vec.size(); i++) assign(vec[i].l,vec[i].r,vec[i].v); }

    -

    其次是将 $[l_1,r_1]$ 和 $[l_2,r_2]$ 交换

    -

    我比较懒,直接把 $[l_1,r_1]$ 复制到 $[n+1,n+r_1-l_1+1]$

    +

    其次是将 \([l_1,r_1]\) 和 +\([l_2,r_2]\) 交换

    +

    我比较懒,直接把 \([l_1,r_1]\) +复制到 \([n+1,n+r_1-l_1+1]\)

    然后就像当年 int c=a;a=b;b=c;一样交换就行了

    void Swap(R int l1,R int r1,R int l2,R int r2)
     {
    @@ -967,12 +1041,23 @@ 

    } return 0; }

    -

    3.CF343D Water Tree

    题目链接:CF343D Water Tree

    +

    3.CF343D Water Tree

    +

    题目链接:CF343D +Water Tree

    -

    题意:给出一棵以 $1$ 为根节点的 $n$ 个节点的有根树。每个点有一个权值,初始为 $0$ ,支持以下操作

    -

    1 u将点 $u$ 和其子树上的所有节点的权值改为 $1$

    -

    2 u将点 $u$ 到 $1$ 的路径上的所有节点的权值改为 $0$

    -

    3 u询问 $u$ 的权值

    +

    题意:给出一棵以 \(1\) 为根节点的 \(n\) +个节点的有根树。每个点有一个权值,初始为 \(0\) ,支持以下操作

    +

    1 u将点 \(u\) +和其子树上的所有节点的权值改为 \(1\)

    +

    2 u将点 \(u\)\(1\) 的路径上的所有节点的权值改为 \(0\)

    +

    3 u询问 \(u\) +的权值

    简单的树链剖分+珂朵莉树

    单点查询只要lower_bound()就行了

    @@ -1100,9 +1185,16 @@

    } return 0; }

    -

    4.CF915E Physical Education Lessons

    题目链接:CF915E Physical Education Lessons

    +

    4.CF915E Physical Education +Lessons

    +

    题目链接:CF915E +Physical Education Lessons

    -

    题意:区间赋值为 $1$ 或 $0$ ,求 $1$ 个数 (我用 $1$ 表示题目中的工作日)

    +

    题意:区间赋值为 \(1\)\(0\) ,求 \(1\) 个数 (我用 \(1\) 表示题目中的工作日)

    这题比板子题还要简单

    唯一要注意的是每次输出结果不能去扫一遍(复杂度爆炸),而是在每次修改时统计

    @@ -1163,17 +1255,17 @@

    return 0; }


    - - -

    总结

    本文简单介绍了珂朵莉树

    +

    总结

    +

    本文简单介绍了珂朵莉树

    顺便讲了几道简单的例题


    - - -

    题外话

    作为珂学家,怎么能少了这环节呢(

    +

    题外话

    +

    作为珂学家,怎么能少了这环节呢(

    送上本人找到的高清无水印壁纸一份(

    -


    再来一个(

    -


    如果幸福有颜色的话,那一定是终末之红染尽的蓝色!

    +

    +再来一个(

    +

    +如果幸福有颜色的话,那一定是终末之红染尽的蓝色!

    @@ -1543,7 +1635,7 @@

      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/18/luo-gu-p1047-noip2005-pu-ji-zu-xiao-men-wai-de-shu-ti-jie/index.html b/2021/08/18/luo-gu-p1047-noip2005-pu-ji-zu-xiao-men-wai-de-shu-ti-jie/index.html index 3a0637d27e..3f5aa37ac3 100644 --- a/2021/08/18/luo-gu-p1047-noip2005-pu-ji-zu-xiao-men-wai-de-shu-ti-jie/index.html +++ b/2021/08/18/luo-gu-p1047-noip2005-pu-ji-zu-xiao-men-wai-de-shu-ti-jie/index.html @@ -480,16 +480,23 @@

    洛谷P1047 [NOIP2005 普及组]
    -

    洛谷P1047 [NOIP2005 普及组] 校门外的树 题解

    前言

    如何把一道入门题写成省选题?(手动滑稽)

    +

    洛谷P1047 [NOIP2005 +普及组] 校门外的树 题解

    +

    前言

    +

    如何把一道入门题写成省选题?(手动滑稽)

    本题解是我在练习分块时突发奇想写的,真就把入门题写成省选题的感觉(

    才发现原来这些简单题这么有趣(

    -
    -

    题目链接: P1047 [NOIP2005 普及组] 校门外的树

    +
    +

    题目链接: P1047 +[NOIP2005 普及组] 校门外的树

    -

    题意:马路上砍树,问砍了 $m$ 次还有几棵树

    +

    题意:马路上砍树,问砍了 \(m\) 次还有几棵树

    -

    一、模拟解法(正常解法)

    我们只要把每次砍掉的树标记一下,最后统计未标记的数量即可

    -

    注意下标从 $0$ 开始,共 $n+1$ 个数

    +

    一、模拟解法(正常解法)

    +

    我们只要把每次砍掉的树标记一下,最后统计未标记的数量即可

    +

    注意下标从 \(0\) 开始,共 \(n+1\) 个数

    代码如下:

    #include <bits/stdc++.h>
     using namespace std;
    @@ -511,7 +518,8 @@ 

    printf("%lld\n",ans); return 0; }

    -

    二、线段树解法(开始奇怪起来)

    直接用线段树维护区间,没什么特别的

    +

    二、线段树解法(开始奇怪起来)

    +

    直接用线段树维护区间,没什么特别的

    代码如下:

    #include <bits/stdc++.h>
     using namespace std;
    @@ -590,7 +598,8 @@ 

    printf("%lld\n",query(1,n,1,n,1)); return 0; }

    -

    三、分块解法 (开始毒瘤起来)

    只有一个要注意的,就是每次暴力减的时候要防止减为负数
    代码如下:

    +

    三、分块解法 (开始毒瘤起来)

    +

    只有一个要注意的,就是每次暴力减的时候要防止减为负数 代码如下:

    #include <bits/stdc++.h>
     using namespace std;
     #define int long long
    @@ -667,9 +676,11 @@ 

    printf("%lld\n",sum(1,n)); return 0; }

    -

    四、珂朵莉树解法 (非常珂学)

    一看这个推平操作,立马就想到了珂朵莉树

    -

    如果不知道珂朵莉树的可以看看我写的这篇文章

    -

    代码如下:

    #include <bits/stdc++.h>
    +

    四、珂朵莉树解法 (非常珂学)

    +

    一看这个推平操作,立马就想到了珂朵莉树

    +

    如果不知道珂朵莉树的可以看看我写的这篇文章

    +代码如下:
    #include <bits/stdc++.h>
     using namespace std;
     #define int long long
     #define R register
    @@ -728,15 +739,18 @@ 

    } printf("%lld\n",sum(1,n)); return 0; -}

    +}

    - -

    总结

    本文简单介绍了几种解法来解决此题

    +

    总结

    +

    本文简单介绍了几种解法来解决此题


    - -

    题外话

    数据太水了,所以这几种做法的速度都很快

    -

    附上提交记录叭…

    -

    模拟解法 线段树解法 分块解法 珂朵莉树解法

    +

    题外话

    +

    数据太水了,所以这几种做法的速度都很快

    +

    附上提交记录叭...

    +

    模拟解法 线段树解法 分块解法 珂朵莉树解法

    @@ -1105,7 +1119,7 @@

      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/26/luo-gu-p1581-a-b-problem-sheng-ji-ban-ti-jie/index.html b/2021/08/26/luo-gu-p1581-a-b-problem-sheng-ji-ban-ti-jie/index.html index a411928c60..ac031d474d 100644 --- a/2021/08/26/luo-gu-p1581-a-b-problem-sheng-ji-ban-ti-jie/index.html +++ b/2021/08/26/luo-gu-p1581-a-b-problem-sheng-ji-ban-ti-jie/index.html @@ -472,14 +472,21 @@

    洛谷P1581 A+B Problem(升级
    -

    洛谷P1581 A+B Problem(升级版)题解

    题目链接:P1581 A+B Problem(升级版)

    +

    洛谷P1581 A+B +Problem(升级版)题解

    +

    题目链接:P1581 A+B +Problem(升级版)

    -

    题意:每一位进制不同,第一位进制为 $2$ ,第二位进制为 $3$ ,第 $i$ 位进制为第 $i$ 个质数,求A+B

    +

    题意:每一位进制不同,第一位进制为 \(2\) ,第二位进制为 \(3\) ,第 \(i\) 位进制为第 \(i\) 个质数,求A+B

    本题就是以普通的高精度加法为基础,改了一下进位的方式而已

    这里我用的vector<int>写的高精度

    我们可以把每一位的进制打个表,在进位的时候取模就行了

    -

    代码如下

    #include<bits/stdc++.h>
    +

    代码如下

    #include<bits/stdc++.h>
     using namespace std;
     #define int long long
     #define R register
    @@ -895,7 +902,7 @@ 

     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/26/luo-gu-p2865-usaco06nov-roadblocks-g-ti-jie/index.html b/2021/08/26/luo-gu-p2865-usaco06nov-roadblocks-g-ti-jie/index.html index 3df728f731..863a6a8594 100644 --- a/2021/08/26/luo-gu-p2865-usaco06nov-roadblocks-g-ti-jie/index.html +++ b/2021/08/26/luo-gu-p2865-usaco06nov-roadblocks-g-ti-jie/index.html @@ -472,15 +472,32 @@

    洛谷P2865 [USACO06NOV]Roadbloc
    -

    洛谷P2865 [USACO06NOV]Roadblocks G 题解

    题目链接:P2865 [USACO06NOV]Roadblocks G

    +

    洛谷P2865 +[USACO06NOV]Roadblocks G 题解

    +

    题目链接:P2865 +[USACO06NOV]Roadblocks G

    -

    题意:求结点 $1$ 到结点 $n$ 的次短路,所有边有非负权重,边可以重复经过,无向图

    +

    题意:求结点 \(1\) +到结点 \(n\) +的次短路,所有边有非负权重,边可以重复经过,无向图

    如果是求最短路,那么一个dijkstra直接搞定

    但是这题要求的是次短路,同样也可以dijkstra解决

    -

    我们观察 $1$ 到 $v$ 的一条次短路 $p$

    -

    要么存在 $1$ 到 $u$ 的一条最短路 $k_1$ ,使得 $k_1$ 加上 $(u,v)$ 等于 $p$

    -

    要么存在 $1$ 到 $u$ 的一条次短路 $k_2$ ,使得 $k_2$ 加上 $(u,v)$ 等于 $p$

    +

    我们观察 \(1\)\(v\) 的一条次短路 \(p\)

    +

    要么存在 \(1\)\(u\) 的一条最短路 \(k_1\) ,使得 \(k_1\) 加上 \((u,v)\) 等于 \(p\)

    +

    要么存在 \(1\)\(u\) 的一条次短路 \(k_2\) ,使得 \(k_2\) 加上 \((u,v)\) 等于 \(p\)

    因此我们只需要记录每个结点的最短距离次短距离即可

    (注:由于最短路径可能有多条,但是并不影响结果,因此我们假设只有一条路径)

    由于每个结点都可以访问多次,那么怎么防止死循环呢?

    @@ -924,7 +941,7 @@

     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/26/luo-gu-p4878-usaco05dec-layout-g-ti-jie/index.html b/2021/08/26/luo-gu-p4878-usaco05dec-layout-g-ti-jie/index.html index d218a3b4d5..cd6dd60065 100644 --- a/2021/08/26/luo-gu-p4878-usaco05dec-layout-g-ti-jie/index.html +++ b/2021/08/26/luo-gu-p4878-usaco05dec-layout-g-ti-jie/index.html @@ -472,35 +472,61 @@

    洛谷P4878 [USACO05DEC]Layout G
    -

    洛谷P4878 [USACO05DEC]Layout G 题解

    题目链接:P4878 [USACO05DEC]Layout G

    +

    洛谷P4878 [USACO05DEC]Layout +G 题解

    +

    题目链接:P4878 +[USACO05DEC]Layout G

    -

    题意:按编号排了 $n$ 只奶牛,有的奶牛间必须相距小于等于一个距离,有的奶牛间必须相距大于等于一个距离,问 $1$ 到 $n$ 的距离最大值

    +

    题意:按编号排了 \(n\) +只奶牛,有的奶牛间必须相距小于等于一个距离,有的奶牛间必须相距大于等于一个距离,问 +\(1\)\(n\) 的距离最大值

    -

    我们可以发现题目给的数据本质上就是 $v-u\ge d_i$ 或 $v-u \le d_i$

    +

    我们可以发现题目给的数据本质上就是 \(v-u\ge +d_i\)\(v-u \le d_i\)

    这是什么?差分约束!

    -

    差分约束:根据三角不等式 $d_u + w(u,v) \ge d_v$ 的构成,我们可以把形如 $v-u\ge d_i$ 的不等式转化为

    -

    $v-d_i\ge u$ (可以看作 $v$ 向 $u$ 连了一条边权为 $-d_i$ 的有向边),同理 $v-u \le d_i$ 转化为 $u+d_i \ge v$

    +

    差分约束:根据三角不等式 \(d_u + w(u,v) \ge d_v\) +的构成,我们可以把形如 \(v-u\ge d_i\) +的不等式转化为

    +

    \(v-d_i\ge u\) (可以看作 \(v\)\(u\) 连了一条边权为 \(-d_i\) 的有向边),同理 \(v-u \le d_i\) 转化为 \(u+d_i \ge v\)

    这里不过多讲解差分约束了

    那么为什么差分约束后求最短路就是最大值呢?

    因为求最短路是由无穷大向下不断约束得到的,因此得到的是最大值(同理求最长路就是最小值)

    那我们只要按题意建图就行了

    -

    要注意的是,每个奶牛 $i$ 满足 $d_{i-1} \le d_{i}$ ,其中 $d$ 表示所在位置,因此相邻的也要建边

    +

    要注意的是,每个奶牛 \(i\) 满足 +\(d_{i-1} \le d_{i}\) ,其中 \(d\) 表示所在位置,因此相邻的也要建边

    为什么要强调这一点呢?

    如下图:

    -

    -

    如果没有建边,会发现答案为 $5$ (如上图所示)

    -

    +

    +

    如果没有建边,会发现答案为 \(5\) +(如上图所示)

    +

    如果建了边,答案才正确(该情况无解)

    -

    那现在只要从 $1$ 开始跑SPFA就好了

    -

    但是还有问题, $1$ 并不能保证与所有结点连通,而我们知道差分约束无解的情况就是图中有负环

    -

    这个问题好解决,我们先在原图上建一个超级结点 $0$ 与所有结点相连(边权为 $0$ ),在 $0$ 跑一次SPFA判断即可判断解的情况

    +

    那现在只要从 \(1\) +开始跑SPFA就好了

    +

    但是还有问题, \(1\) +并不能保证与所有结点连通,而我们知道差分约束无解的情况就是图中有负环

    +

    这个问题好解决,我们先在原图上建一个超级结点 \(0\) 与所有结点相连(边权为 \(0\) ),在 \(0\) 跑一次SPFA判断即可判断解的情况

    说了这么多,是不是有点晕(

    理一下思路:

    -
      +
      1. 差分约束建图
      2. 相邻编号建图
      3. -
      4. 建 $0$ 结点判断解的情况
      5. -
      6. 有解则从 $1$ 开始跑SPFA
      7. +
      8. \(0\) 结点判断解的情况
      9. +
      10. 有解则从 \(1\) 开始跑SPFA

      代码如下

      #include <bits/stdc++.h>
      @@ -939,7 +965,7 @@ 

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/26/poj3723-conscription-ti-jie/index.html b/2021/08/26/poj3723-conscription-ti-jie/index.html index fa34861f92..ad13feb9e7 100644 --- a/2021/08/26/poj3723-conscription-ti-jie/index.html +++ b/2021/08/26/poj3723-conscription-ti-jie/index.html @@ -472,15 +472,24 @@

      POJ3723 Conscription 题解

      -

      POJ3723 Conscription 题解

      题目链接:POJ3723 Conscription

      +

      POJ3723 Conscription 题解

      +

      题目链接:POJ3723 +Conscription

      -

      题意:要招 $n$ 个女的, $m$ 个男的,原价 $10000$,如果招了关系亲密的(男女)人可以降价,求最小花费

      +

      题意:要招 \(n\) +个女的, \(m\) 个男的,原价 \(10000\),如果招了关系亲密的(男女)人可以降价,求最小花费

      -

      首先,题目给出的格式 $x_i\ y_i\ d_i$ 很像图论题

      -

      我们可以把每个人看作一个结点,为了防止男女编号相同的人搞混了,我们可以让女的编号为 $1 \sim n$ ,男的编号为 $n+1 \sim m$ ,显然这题要我们选出所有的结点且花费最小

      +

      首先,题目给出的格式 \(x_i\ y_i\ +d_i\) 很像图论题

      +

      我们可以把每个人看作一个结点,为了防止男女编号相同的人搞混了,我们可以让女的编号为 +\(1 \sim n\) ,男的编号为 \(n+1 \sim m\) +,显然这题要我们选出所有的结点且花费最小

      我们可以发现答案如果存在环则会产生矛盾,例如:a招了b,b招了c,c招了a,这说不通

      那是最小生成树吗?不一定

      -

      我们可以发现给定的这张图不一定是连通图,而我们要求的是花费最小的树型结构(感性理解下),那么这就是最大权森林问题,同样可以用最小生成树来求解(注:边权设为 $-d_i$)

      +

      我们可以发现给定的这张图不一定是连通图,而我们要求的是花费最小的树型结构(感性理解下),那么这就是最大权森林问题,同样可以用最小生成树来求解(注:边权设为 +\(-d_i\)

      由于不一定是树,所以不需要记录连了多少边

      代码如下

      #include <cstdio>
      @@ -916,7 +925,7 @@ 

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/27/shu-lun-ti-xia-zuo-1/index.html b/2021/08/27/shu-lun-ti-xia-zuo-1/index.html index 37ed57102c..eb0449af45 100644 --- a/2021/08/27/shu-lun-ti-xia-zuo-1/index.html +++ b/2021/08/27/shu-lun-ti-xia-zuo-1/index.html @@ -468,58 +468,96 @@

      数论题瞎做[1]

      -

      数论题瞎做[1]

      某个学MO的朋友给我看的题

      +

      数论题瞎做[1]

      +

      某个学MO的朋友给我看的题

      这题似乎是1994年国家数学集训队选拔考试D1T1

      怪不得我做了好久(

      upd.20220513 害,他半途而废退役了

      -
      +

      题面

      -

      求四个所有的由四个自然数 $a,b,c,d$ 组成的数组,使数组中任意三个数的乘积除以剩下的一个数余数为 $1$

      +

      求四个所有的由四个自然数 \(a,b,c,d\) +组成的数组,使数组中任意三个数的乘积除以剩下的一个数余数为 \(1\)

      题解

      (注:我写的可能有点不太正式,毕竟我只是个OIer)

      由题意可得

      -

      $abc \equiv 1 \mod d$

      -

      $abd \equiv 1 \mod c$

      -

      $acd \equiv 1 \mod b$

      -

      $bcd \equiv 1 \mod a$

      +

      \(abc \equiv 1 \mod d\)

      +

      \(abd \equiv 1 \mod c\)

      +

      \(acd \equiv 1 \mod b\)

      +

      \(bcd \equiv 1 \mod a\)

      可化为

      -

      $d\mid (abc-1)$

      -

      $c\mid (abd-1)$

      -

      $b\mid (acd-1)$

      -

      $a\mid (bcd-1)$

      -

      $\therefore ab\mid (acd-1)(bcd-1) \Rightarrow ab\mid (abc^2d^2-acd-bcd+1)$

      -

      $\therefore ab\mid (acd+bcd-1)$

      -

      同理 $cd\mid (abc+abd-1)$

      -

      $\therefore abcd\mid (a^2bc^2d+ab^2c^2d+a^2bc^2d+ab^2cd^2-abc-abd-acd-bcd+1)$

      -

      $\therefore abcd\mid (abc+abd+acd+bcd-1)$

      -

      设 $t=\dfrac{abc+abd+acd+bcd-1}{abcd}$

      -

      $\therefore t=\dfrac{1}{a}+\dfrac{1}{b}+\dfrac{1}{c}+\dfrac{1}{d}-\dfrac{1}{abcd}$

      -

      显然 $a,b,c,d$ 两两互质,且 $a \ge 2$

      -

      由于不考虑顺序,则假设 $2\le a < b < c < d$

      -

      $\therefore t< \dfrac{4}{a} \le 2$

      -

      $\because t\in \mathbb{Z}^+$

      -

      $\therefore a=2,3,t=1$

      -

      当 $a=3$ 时,$t_{max} = \dfrac{1}{3}+\dfrac{1}{4}+\dfrac{1}{5}+\dfrac{1}{6}-\dfrac{1}{360} = \dfrac{341}{360}<1$$\quad\therefore$舍去

      -

      当 $a=2$ 时,$t_{max} = \dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{5}+\dfrac{1}{7}-\dfrac{1}{210} = \dfrac{246}{210}>1$

      -

      $\therefore a =2$

      -

      $\therefore \dfrac{1}{2}<\dfrac{3}{b}$

      -

      $\therefore b=3,5$

      -

      当 $b=5$ 时,$t_{max} = \dfrac{1}{2}+\dfrac{1}{5}+\dfrac{1}{7}+\dfrac{1}{9}-\dfrac{1}{630} = \dfrac{600}{630}<1$$\quad\therefore$舍去

      -

      当 $b=3$ 时,$t_{max} = \dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{5}+\dfrac{1}{7}-\dfrac{1}{210} = \dfrac{246}{210}>1$

      -

      $\therefore b=3$

      -

      $\therefore \dfrac{1}{6} < \dfrac{2}{c}$

      -

      $\therefore c=7,11$

      -

      当 $c=7$ 时,$t_{max} = \dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{7}+\dfrac{1}{11}-\dfrac{1}{462} = \dfrac{492}{462}>1$

      -

      当 $c=11$ 时,$t_{max} = \dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{11}+\dfrac{1}{13}-\dfrac{1}{858} = \dfrac{858}{858}=1$

      -

      显然当 $c=11$ 时 $d=13$

      -

      则一组解为 $2,3,11,13$

      -

      当 $c=7$ 时

      -

      $\dfrac{1}{42}<\dfrac{1}{d}$

      -

      $\therefore d=41$

      -

      则另一组解为 $2,3,7,41$

      -

      综上所述,答案为 $2,3,11,13$ 或 $2,3,7,41$

      -
      -

      为了验算结果我直接敲了个 $O(n^4)$ 的暴力(傻)

      +

      \(d\mid (abc-1)\)

      +

      \(c\mid (abd-1)\)

      +

      \(b\mid (acd-1)\)

      +

      \(a\mid (bcd-1)\)

      +

      \(\therefore ab\mid (acd-1)(bcd-1) +\Rightarrow ab\mid (abc^2d^2-acd-bcd+1)\)

      +

      \(\therefore ab\mid +(acd+bcd-1)\)

      +

      同理 \(cd\mid (abc+abd-1)\)

      +

      \(\therefore abcd\mid +(a^2bc^2d+ab^2c^2d+a^2bc^2d+ab^2cd^2-abc-abd-acd-bcd+1)\)

      +

      \(\therefore abcd\mid +(abc+abd+acd+bcd-1)\)

      +

      \(t=\dfrac{abc+abd+acd+bcd-1}{abcd}\)

      +

      \(\therefore +t=\dfrac{1}{a}+\dfrac{1}{b}+\dfrac{1}{c}+\dfrac{1}{d}-\dfrac{1}{abcd}\)

      +

      显然 \(a,b,c,d\) 两两互质,且 \(a \ge 2\)

      +

      由于不考虑顺序,则假设 \(2\le a < b < +c < d\)

      +

      \(\therefore t< \dfrac{4}{a} \le +2\)

      +

      \(\because t\in \mathbb{Z}^+\)

      +

      \(\therefore a=2,3,t=1\)

      +

      \(a=3\) 时,\(t_{max} = +\dfrac{1}{3}+\dfrac{1}{4}+\dfrac{1}{5}+\dfrac{1}{6}-\dfrac{1}{360} = +\dfrac{341}{360}<1\)\(\quad\therefore\)舍去

      +

      \(a=2\) 时,\(t_{max} = +\dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{5}+\dfrac{1}{7}-\dfrac{1}{210} = +\dfrac{246}{210}>1\)

      +

      \(\therefore a =2\)

      +

      \(\therefore +\dfrac{1}{2}<\dfrac{3}{b}\)

      +

      \(\therefore b=3,5\)

      +

      \(b=5\) 时,\(t_{max} = +\dfrac{1}{2}+\dfrac{1}{5}+\dfrac{1}{7}+\dfrac{1}{9}-\dfrac{1}{630} = +\dfrac{600}{630}<1\)\(\quad\therefore\)舍去

      +

      \(b=3\) 时,\(t_{max} = +\dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{5}+\dfrac{1}{7}-\dfrac{1}{210} = +\dfrac{246}{210}>1\)

      +

      \(\therefore b=3\)

      +

      \(\therefore \dfrac{1}{6} < +\dfrac{2}{c}\)

      +

      \(\therefore c=7,11\)

      +

      \(c=7\) 时,\(t_{max} = +\dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{7}+\dfrac{1}{11}-\dfrac{1}{462} = +\dfrac{492}{462}>1\)

      +

      \(c=11\) 时,\(t_{max} = +\dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{11}+\dfrac{1}{13}-\dfrac{1}{858} = +\dfrac{858}{858}=1\)

      +

      显然当 \(c=11\)\(d=13\)

      +

      则一组解为 \(2,3,11,13\)

      +

      \(c=7\)

      +

      \(\dfrac{1}{42}<\dfrac{1}{d}\)

      +

      \(\therefore d=41\)

      +

      则另一组解为 \(2,3,7,41\)

      +

      综上所述,答案为 \(2,3,11,13\) 或 +\(2,3,7,41\)

      +
      +

      为了验算结果我直接敲了个 \(O(n^4)\) +的暴力(傻)

      @@ -881,7 +919,7 @@

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/27/uva10006-carmichael-numbers-ti-jie/index.html b/2021/08/27/uva10006-carmichael-numbers-ti-jie/index.html index 5ea89ba56b..5bb8ff0215 100644 --- a/2021/08/27/uva10006-carmichael-numbers-ti-jie/index.html +++ b/2021/08/27/uva10006-carmichael-numbers-ti-jie/index.html @@ -472,20 +472,39 @@

      UVA10006 Carmichael Numbers

      -

      UVA10006 Carmichael Numbers

      题目链接:UVA10006 Carmichael Numbers

      +

      UVA10006 Carmichael Numbers

      +

      题目链接:UVA10006 Carmichael +Numbers

      -

      题意:若 $\forall x (1<x<n) ,x^n\equiv x \mod n$ ,且 $n$ 为合数,则称 $n$ 为Carmichael Numbers,多组数据判断 $n$ 是否是这种数

      -

      数据范围 $2<n<65000$

      +

      题意:若 \(\forall x +(1<x<n) ,x^n\equiv x \mod n\) ,且 \(n\) 为合数,则称 \(n\) +为Carmichael Numbers,多组数据判断 \(n\) 是否是这种数

      +

      数据范围 \(2<n<65000\)

      -

      合数好处理,直接 $O(\sqrt{n})$ 暴力判断即可

      -

      那么 $x^n$ 怎么处理呢?

      -

      如果我们去一个一个乘,时间复杂度 $O(n^2)$ ,直接TLE

      +

      合数好处理,直接 \(O(\sqrt{n})\) +暴力判断即可

      +

      那么 \(x^n\) 怎么处理呢?

      +

      如果我们去一个一个乘,时间复杂度 \(O(n^2)\) ,直接TLE

      因此,我们要用到快速幂

      -

      对于 $x^k$ ,根据整数的唯一分解定理和常识,$k$ 可以写成唯一的二进制形式

      -

      则 $x^k$ 可以被分解为它二进制位上每一个 $1$ 的幂的乘积

      -

      例如: $x^{(5)_{10}} = x^{(101)_2} = x^{(100)_2} \times x^{(1)_2} = x^{(4)_{10}} \times x^{(1)_{10}}$

      -

      那么我们只需要把 $k$ 不断右移一位,看它最后一位是否为 $1$ ,是就乘上对应的数即可

      -

      因此我们就可以在 $O(n\log{n})$的时间搞定这题了

      +

      对于 \(x^k\) +,根据整数的唯一分解定理和常识,\(k\) +可以写成唯一的二进制形式

      +

      \(x^k\) +可以被分解为它二进制位上每一个 \(1\) +的幂的乘积

      +

      例如: \(x^{(5)_{10}} = x^{(101)_2} = +x^{(100)_2} \times x^{(1)_2} = x^{(4)_{10}} \times +x^{(1)_{10}}\)

      +

      那么我们只需要把 \(k\) +不断右移一位,看它最后一位是否为 \(1\) +,是就乘上对应的数即可

      +

      因此我们就可以在 \(O(n\log{n})\)的时间搞定这题了

      代码如下:

      #include <bits/stdc++.h>
       using namespace std;
      @@ -892,7 +911,7 @@ 

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/28/luo-gu-p1006-noip2008-ti-gao-zu-chuan-zhi-tiao-ti-jie/index.html b/2021/08/28/luo-gu-p1006-noip2008-ti-gao-zu-chuan-zhi-tiao-ti-jie/index.html index 4cf249b13b..55ad65ca35 100644 --- a/2021/08/28/luo-gu-p1006-noip2008-ti-gao-zu-chuan-zhi-tiao-ti-jie/index.html +++ b/2021/08/28/luo-gu-p1006-noip2008-ti-gao-zu-chuan-zhi-tiao-ti-jie/index.html @@ -472,37 +472,60 @@

      洛谷P1006 [NOIP2008 提高组]
      -

      洛谷P1006 [NOIP2008 提高组] 传纸条

      题目链接:P1006 [NOIP2008 提高组] 传纸条

      +

      洛谷P1006 [NOIP2008 提高组] +传纸条

      +

      题目链接:P1006 +[NOIP2008 提高组] 传纸条

      -

      题意:网格图, $(1,1)$ 到 $(n,m)$ 找两条不重合的路径,最大价值
      注:原题是 $(m,n)$ ,但我不习惯,所以就用 $(n,m)$ 替代(即 $n$ 行 $m$ 列)!

      +

      题意:网格图, \((1,1)\)\((n,m)\) 找两条不重合的路径,最大价值 +注:原题是 \((m,n)\) +,但我不习惯,所以就用 \((n,m)\) +替代(即 \(n\)\(m\) 列)!

      -

      解法一

      最烂解法 ,但是能通过此题

      -

      设 $dp[i][j][k][l]$ 表示第一条路径到 $(i,j)$ 的位置,第二条路径到 $(k,l)$ 的位置,能获得的最大价值

      -

      由于两条路径正着走反着走都一样,所以我们可以看作从 $(1,1)$ 到 $(n,m)$ 的两条不重合的路径

      -

      那么显然

      -

      $dp[i][j][k][l] = \\ \max\{dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]\}+a[i][j]+a[k][l]$

      -

      因为不重复,所以当 $i=k,j=l$ 时要减去 $a[i][j]$

      -

      时间复杂度 $O(n^4)$

      -

      空间复杂度 $O(n^4)$

      -

      伪代码如下(懒的写了…)

      +

      解法一

      +

      最烂解法 ,但是能通过此题

      +

      \(dp[i][j][k][l]\) +表示第一条路径到 \((i,j)\) +的位置,第二条路径到 \((k,l)\) +的位置,能获得的最大价值

      +

      由于两条路径正着走反着走都一样,所以我们可以看作从 \((1,1)\)\((n,m)\) 的两条不重合的路径

      +

      那么显然

      +

      \(dp[i][j][k][l] = \\ +\max\{dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]\}+a[i][j]+a[k][l]\)

      +

      因为不重复,所以当 \(i=k,j=l\) +时要减去 \(a[i][j]\)

      +

      时间复杂度 \(O(n^4)\)

      +

      空间复杂度 \(O(n^4)\)

      +

      伪代码如下(懒的写了...)

      for i=1 to n:
       	for j=1 to m:
       		for k=1 to n:
       			for l=1 to m:
       dp[i][j][k][l]=max{dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]}+a[i][j]+!((i==k&&j==l))*a[k][l];
      -

      答案就是 $dp[n][m][n][m]$

      +

      答案就是 \(dp[n][m][n][m]\)

      呕~这代码够烂的

      -

      解法二

      我们观察每个路径,由于它一定是一个斜线方向的(即只能右移下移)

      +

      解法二

      +

      我们观察每个路径,由于它一定是一个斜线方向的(即只能右移下移)

      我们知道一个点的纵坐标,那么它的横坐标就是横纵坐标之和减去纵坐标

      而两条路径的长度一定是相等的

      那么就不用记录点的坐标了

      -

      设 $dp[k][i][j]$ 表示横纵坐标和为 $k$ ,第一条路径终点纵坐标为 $i$ ,第二条路径终点纵坐标为 $j$ 时能获得的最大价值

      +

      \(dp[k][i][j]\) 表示横纵坐标和为 +\(k\) ,第一条路径终点纵坐标为 \(i\) ,第二条路径终点纵坐标为 \(j\) 时能获得的最大价值

      那么显然

      -

      $dp[k][i][j]=\\\max\{dp[k-1][i-1][j],dp[k-1][i][j-1],dp[k-1][i][j],dp[k-1][i-1][j-1]\}+a[k-i][i]+a[k-j][j]$

      -

      我们发现 $k$ 和 $k-1$ 的答案有关

      +

      \(dp[k][i][j]=\\\max\{dp[k-1][i-1][j],dp[k-1][i][j-1],dp[k-1][i][j],dp[k-1][i-1][j-1]\}+a[k-i][i]+a[k-j][j]\)

      +

      我们发现 \(k\)\(k-1\) 的答案有关

      那么我们可以弄个滚动数组,注意顺序即可通过此题

      -

      时间复杂度 $O(n^3)$

      -

      空间复杂度 $O(n^2)$

      +

      时间复杂度 \(O(n^3)\)

      +

      空间复杂度 \(O(n^2)\)

      代码:

      #include <bits/stdc++.h>
       using namespace std;
      @@ -894,7 +917,7 @@ 

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/28/luo-gu-p4085-usaco17dec-haybale-feast-g-ti-jie/index.html b/2021/08/28/luo-gu-p4085-usaco17dec-haybale-feast-g-ti-jie/index.html index ee692b7a4c..d99a2badba 100644 --- a/2021/08/28/luo-gu-p4085-usaco17dec-haybale-feast-g-ti-jie/index.html +++ b/2021/08/28/luo-gu-p4085-usaco17dec-haybale-feast-g-ti-jie/index.html @@ -472,16 +472,31 @@

      洛谷P4085 [USACO17DEC]Haybale
      -

      洛谷P4085 [USACO17DEC]Haybale Feast G 题解

      题目链接:P4085 [USACO17DEC]Haybale Feast G

      +

      洛谷P4085 +[USACO17DEC]Haybale Feast G 题解

      +

      题目链接:P4085 +[USACO17DEC]Haybale Feast G

      -

      题意:给定 $2$ 个由 $N$ 个数字组成的数列 $F,S$ ,需要找到使得 $\sum_{k=i}^{j}F_k\ge M$ 的 $i,j$ 并输出在所有满足条件的 $i,j$ 中,$\max(S_i,S_{i+1},…S_{j-1},S_{j}$)的最小值,数列里每个数非负

      +

      题意:给定 \(2\) +个由 \(N\) 个数字组成的数列 \(F,S\) ,需要找到使得 \(\sum_{k=i}^{j}F_k\ge M\)\(i,j\) 并输出在所有满足条件的 \(i,j\) 中,\(\max(S_i,S_{i+1},...S_{j-1},S_{j}\))的最小值,数列里每个数非负

      -

      解法一

      由非负这个条件我们可以得到一个重要结论

      -

      对于区间 $A,B$ ,若 $A \subsetneqq B$ ,则 $A$ 的最大值小于等于 $B$ 的最大值

      +

      解法一

      +

      由非负这个条件我们可以得到一个重要结论

      +

      对于区间 \(A,B\) ,若 \(A \subsetneqq B\) ,则 \(A\) 的最大值小于等于 \(B\) 的最大值

      因此具有单调性

      那么我们可以二分答案解决此题

      -

      每次判断就 $O(n)$ 扫一遍看看有没有满足条件的区间即可

      -

      时间复杂度 $O(n\log \max \{S_i\})$

      +

      每次判断就 \(O(n)\) +扫一遍看看有没有满足条件的区间即可

      +

      时间复杂度 \(O(n\log \max +\{S_i\})\)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -522,11 +537,13 @@ 

      proc(0,inf); return 0; }

      -

      解法二

      显然第一维限制我们可以用尺取法搞定

      -

      因为我们要找到的 $i,j$ 一定是尽可能短的区间

      +

      解法二

      +

      显然第一维限制我们可以用尺取法搞定

      +

      因为我们要找到的 \(i,j\) +一定是尽可能短的区间

      那么第二维限制我们可以用单调队列搞定

      为什么?因为尺取法的端点是单调不下降的

      -

      时间复杂度 $O(n)$

      +

      时间复杂度 \(O(n)\)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -935,7 +952,7 @@ 

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/28/ubuntu-wu-fa-lian-jie-zhi-ibus-jie-jue-fang-fa/index.html b/2021/08/28/ubuntu-wu-fa-lian-jie-zhi-ibus-jie-jue-fang-fa/index.html index e3c78ba49f..141daf5cda 100644 --- a/2021/08/28/ubuntu-wu-fa-lian-jie-zhi-ibus-jie-jue-fang-fa/index.html +++ b/2021/08/28/ubuntu-wu-fa-lian-jie-zhi-ibus-jie-jue-fang-fa/index.html @@ -468,7 +468,9 @@

      ubuntu 无法连接至ibus 解
      -

      ubuntu 无法连接至ibus 解决方法

      在终端中输入以下指令

      +

      ubuntu 无法连接至ibus +解决方法

      +

      在终端中输入以下指令

      ibus-daemon -r -d -x

      大概的意思就是把原来的进程杀掉然后启动ibus

      然后ibus它就回来了

      @@ -830,7 +832,7 @@

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/28/uva1121-subsequence-ti-jie/index.html b/2021/08/28/uva1121-subsequence-ti-jie/index.html index 2c299bde04..0f1e3faed2 100644 --- a/2021/08/28/uva1121-subsequence-ti-jie/index.html +++ b/2021/08/28/uva1121-subsequence-ti-jie/index.html @@ -468,14 +468,21 @@

      UVA1121 Subsequence 题解

      -

      UVA1121 Subsequence 题解

      题目链接:UVA1121 Subsequence

      +

      UVA1121 Subsequence 题解

      +

      题目链接:UVA1121 +Subsequence

      -

      题意:给定数组,找最短连续子序列使其和大于 $S$ ,多组数据

      +

      题意:给定数组,找最短连续子序列使其和大于 \(S\) ,多组数据

      -

      解法一

      对于区间 $[l,r]$ ,若 $\sum_{i=l}^{r}a[i]\ge S$ ,那么 $\forall r’>r (r’ \le n)$ 该条件均成立

      +

      解法一

      +

      对于区间 \([l,r]\) ,若 \(\sum_{i=l}^{r}a[i]\ge S\) ,那么 \(\forall r'>r (r' \le n)\) +该条件均成立

      那么我们只要枚举左端点,二分查找满足条件的最左的右端点,然后找个最小值就行了

      区间和用前缀和优化即可

      -

      时间复杂度 $O(n\log n)$

      +

      时间复杂度 \(O(n\log n)\)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -511,18 +518,29 @@ 

      while(Input())proc(); return 0; }

      -

      解法二

      本解法就是尺取法

      +

      解法二

      +

      本解法就是尺取法

      根据上面的结论,我们再想一想,有没有必要枚举左端点呢?

      对于每个左端点,当且仅当右端点在尽可能左的位置时对答案有贡献

      或者说对答案有贡献的每个左端点所对应的右端点是唯一确定

      显然右端点也是单调不降

      -

      我们可以设 $l$ 表示当前左端点的位置, $r$ 不断自增 $1$ 直到 $[l,r]$ 的和大于等于 $S$

      -

      这时 $l,r$ 一定是对答案有贡献的一组,和最小值取个 $\min$

      +

      我们可以设 \(l\) +表示当前左端点的位置, \(r\) 不断自增 +\(1\) 直到 \([l,r]\) 的和大于等于 \(S\)

      +

      这时 \(l,r\) +一定是对答案有贡献的一组,和最小值取个 \(\min\)

      那么怎么继续呢?

      -

      我们把左端点 $l$ 不断自增 $1$ 直到此时 $[l,r]$ 的和小于 $S$ ,然后再继续拓展右端点

      +

      我们把左端点 \(l\) 不断自增 \(1\) 直到此时 \([l,r]\) 的和小于 \(S\) ,然后再继续拓展右端点

      不断重复以上过程直到右端点无法拓展且左端点无解时结束

      -

      由于每个点只遍历至多 $2$ 次,因此

      -

      时间复杂度 $O(n)$

      +

      由于每个点只遍历至多 \(2\) +次,因此

      +

      时间复杂度 \(O(n)\)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -910,7 +928,7 @@ 

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/28/vijos1659-he-xie-wang-guo-ti-jie/index.html b/2021/08/28/vijos1659-he-xie-wang-guo-ti-jie/index.html index 8b200ed96a..b8d8d41aad 100644 --- a/2021/08/28/vijos1659-he-xie-wang-guo-ti-jie/index.html +++ b/2021/08/28/vijos1659-he-xie-wang-guo-ti-jie/index.html @@ -468,7 +468,9 @@

      Vijos1659 河蟹王国 题解
      -

      Vijos1659 河蟹王国 题解

      题目链接:Vijos1659 河蟹王国

      +

      Vijos1659 河蟹王国 题解

      +

      题目链接:Vijos1659 +河蟹王国

      题意:维护一个数据结构,支持区间最大值查询、区间加操作

      @@ -476,7 +478,7 @@

      我们在建树时将最大值搞好查询就好了

      那么区间加怎么办?

      显然区间加操作会将影响到的最大值增加同一个值,而改变后的最大值在上传时进行更新即可

      -

      等于就把线段树板子搬过来改一改就过了… 因为基本原理差不多

      +

      等于就把线段树板子搬过来改一改就过了... 因为基本原理差不多

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -559,6 +561,7 @@ 

      } return 0; }

      +


      @@ -915,7 +918,7 @@

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/29/luo-gu-p1985-usaco07open-fliptile-s-ti-jie/index.html b/2021/08/29/luo-gu-p1985-usaco07open-fliptile-s-ti-jie/index.html index b34237a8b1..95a045d946 100644 --- a/2021/08/29/luo-gu-p1985-usaco07open-fliptile-s-ti-jie/index.html +++ b/2021/08/29/luo-gu-p1985-usaco07open-fliptile-s-ti-jie/index.html @@ -468,22 +468,36 @@

      洛谷P1985 [USACO07OPEN] Flipti
      -

      洛谷P1985 [USACO07OPEN] Fliptile S 题解

      题目链接:P1985 [USACO07OPEN] Fliptile S

      +

      洛谷P1985 [USACO07OPEN] +Fliptile S 题解

      +

      题目链接:P1985 +[USACO07OPEN] Fliptile S

      题意:二维的开关问题,一次改变该格和四连通格

      -

      注:原题是 $(m,n)$ ,但本人不习惯这种表示,所以就用 $(n,m)$ 替代(即 $n$ 行 $m$ 列)!

      +

      注:原题是 \((m,n)\) +,但本人不习惯这种表示,所以就用 \((n,m)\) 替代(即 \(n\)\(m\) 列)!

      -

      首先看到题意马上想到搜索,发现状态数 $2^{nm}$ ,直接起飞

      -

      显然我们可以发现对于一个位置,翻转次数至多 $2$ 次,否则是没有意义的

      -

      且翻转的顺序不影响结果,这个性质类似于这题题解

      +

      首先看到题意马上想到搜索,发现状态数 \(2^{nm}\)直接起飞

      +

      显然我们可以发现对于一个位置,翻转次数至多 \(2\) 次,否则是没有意义的

      +

      且翻转的顺序不影响结果,这个性质类似于这题题解

      不同于链接中那题,本题的四连通会有一定影响,不可直接确定

      如果您没做过那题没有关系 qwq

      -

      我们考虑 $(1,1)$ ,如果它要翻转,那么一定会影响到 $(1,2)$ 和 $(2,1)$

      +

      我们考虑 \((1,1)\) +,如果它要翻转,那么一定会影响到 \((1,2)\)\((2,1)\)

      这样我们又要考虑它右边的,又要考虑它下面的,很麻烦

      于是提出设想,如果第一行已经确定了,那是不是可以推出第二行?

      答案是可以的

      那么我们就可以枚举第一行的翻转状态,然后再逐行推结果

      -

      时间复杂度 $O(nm2^m)$

      +

      时间复杂度 \(O(nm2^m)\)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -903,7 +917,7 @@ 

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/29/luo-gu-p2882-usaco07mar-face-the-right-way-g-ti-jie/index.html b/2021/08/29/luo-gu-p2882-usaco07mar-face-the-right-way-g-ti-jie/index.html index 7276566651..34aa4cc333 100644 --- a/2021/08/29/luo-gu-p2882-usaco07mar-face-the-right-way-g-ti-jie/index.html +++ b/2021/08/29/luo-gu-p2882-usaco07mar-face-the-right-way-g-ti-jie/index.html @@ -468,30 +468,53 @@

      洛谷P2882 [USACO07MAR]Face The
      -

      洛谷P2882 [USACO07MAR]Face The Right Way G 题解

      题目链接:P2882 [USACO07MAR]Face The Right Way G

      +

      洛谷P2882 +[USACO07MAR]Face The Right Way G 题解

      +

      题目链接:P2882 +[USACO07MAR]Face The Right Way G

      题意: 固定长度转向,求最小次数和相应的长度

      (注:下文中的转向操作均以翻转一词替代 qwq)

      -

      首先可以想到搜索,状态数 $2^n$ ,直接起飞

      -

      那么我们来分析一下,可以看出对于一个位置,如果它被翻转了大于 $2$ 次,那这个一定不是必要的操作

      -

      也就是说,一个位置只可能被翻转至多 $2$ 次

      +

      首先可以想到搜索,状态数 \(2^n\) +,直接起飞

      +

      那么我们来分析一下,可以看出对于一个位置,如果它被翻转了大于 \(2\) 次,那这个一定不是必要的操作

      +

      也就是说,一个位置只可能被翻转至多 \(2\)

      而且通过观察样例,我们可以发现其实翻转的顺序和答案无关

      -

      很容易想到我们要枚举 $k$ (转向的区间长度),然后进行检查

      -

      怎么检查呢?我们要从左开始扫一遍,则至多翻转 $n-k+1$ 次,而每次翻转要 $k$ 个位置

      -

      时间复杂度$O(n^3)$ ,显然 $n\le 5000$ 是过不了的

      +

      很容易想到我们要枚举 \(k\) +(转向的区间长度),然后进行检查

      +

      怎么检查呢?我们要从左开始扫一遍,则至多翻转 \(n-k+1\) 次,而每次翻转要 \(k\) 个位置

      +

      时间复杂度\(O(n^3)\) ,显然 \(n\le 5000\) 是过不了的

      考虑优化翻转操作

      -

      为了方便起见,我们可以把 B的位置记为 1,表示要翻转这个位置

      -

      记 $f[i]$ 表示 $[i,i+k-1]$ 是否被翻转

      -

      那么如果从左往右扫到一个位置 $i$ ,怎么判断这个位置要不要再翻转呢?

      -

      我们可以发现,如果 $\sum_{j=i-k+1}^{i-1}f[j]$ 为奇数则该点需要翻转(这个东西自己想一想就知道了

      +

      为了方便起见,我们可以把 B的位置记为 +1,表示要翻转这个位置

      +

      \(f[i]\) 表示 \([i,i+k-1]\) 是否被翻转

      +

      那么如果从左往右扫到一个位置 \(i\) +,怎么判断这个位置要不要再翻转呢?

      +

      我们可以发现,如果 \(\sum_{j=i-k+1}^{i-1}f[j]\) +为奇数则该点需要翻转(这个东西自己想一想就知道了

      那么我们怎么把这个东西求出来呢?

      -

      我们看下一个位置 $i+1$ ,它要求的就是 $\sum_{j=i-k+2}^{i}f[j]$

      -

      我们来拆一下可以得到 $\sum_{j=i-k+2}^{i}f[j] = \sum_{j=i-k+1}^{i-1}f[j]+f[i-1]-f[i-k+1]$

      -

      有没有发现它可以从 $i$ 的位置转移过来?

      -

      那么我们就可以 $O(1)$ 求解这个东西了

      +

      我们看下一个位置 \(i+1\) +,它要求的就是 \(\sum_{j=i-k+2}^{i}f[j]\)

      +

      我们来拆一下可以得到 \(\sum_{j=i-k+2}^{i}f[j] = +\sum_{j=i-k+1}^{i-1}f[j]+f[i-1]-f[i-k+1]\)

      +

      有没有发现它可以从 \(i\) +的位置转移过来?

      +

      那么我们就可以 \(O(1)\) +求解这个东西了

      来算一下时间复杂度

      -

      枚举 $k$ ,从左往右扫一遍,时间复杂度 $O(n^2)$

      +

      枚举 \(k\) +,从左往右扫一遍,时间复杂度 \(O(n^2)\)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -894,7 +917,7 @@ 

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/08/31/uva11524-values-whose-sum-is-0-ti-jie/index.html b/2021/08/31/uva11524-values-whose-sum-is-0-ti-jie/index.html index 329e28bd55..4c163af70e 100644 --- a/2021/08/31/uva11524-values-whose-sum-is-0-ti-jie/index.html +++ b/2021/08/31/uva11524-values-whose-sum-is-0-ti-jie/index.html @@ -468,15 +468,26 @@

      UVA11524 Values whose Sum is 0
      -

      UVA11524 Values whose Sum is 0 题解

      题目链接:UVA1152 4 Values whose Sum is 0

      +

      UVA11524 Values whose Sum +is 0 题解

      +

      题目链接:UVA1152 +4 Values whose Sum is 0

      -

      题意:给定四个数组,求 $a_i+b_j+c_k+d_l = 0$ 的个数,多组数据+奇怪的输出格式

      +

      题意:给定四个数组,求 \(a_i+b_j+c_k+d_l = 0\) +的个数,多组数据+奇怪的输出格式

      -

      解法一

      $O(n^4)$ 枚举肯定过不去

      -

      想一想,我们有必要在枚举 $i,j$ 的时候再去枚举 $k,l$ 吗?

      -

      没有,因为 $k,l$ 的组合是有限的,只有这么多

      -

      我们可以先 $O(n^2)$ 把所有 $c_k+d_l$ 枚举出来,然后排个序,就可以二分找出所有的 $-(a_i+b_j)$ 了

      -

      时间复杂度 $O(n^2\log n)$

      +

      解法一

      +

      \(O(n^4)\) 枚举肯定过不去

      +

      想一想,我们有必要在枚举 \(i,j\) +的时候再去枚举 \(k,l\) 吗?

      +

      没有,因为 \(k,l\) +的组合是有限的,只有这么多

      +

      我们可以先 \(O(n^2)\) 把所有 \(c_k+d_l\) +枚举出来,然后排个序,就可以二分找出所有的 \(-(a_i+b_j)\)

      +

      时间复杂度 \(O(n^2\log n)\)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -526,22 +537,30 @@ 

      while(Q--)solve(); return 0; }

      -

      解法二

      通过刚才的分析,我们会惊奇的发现这个 $i,j$ 的组合其实是有限的!

      -

      那么我们就可以预处理所有的 $a_i+b_j$ 和 $c_k+d_l$

      +

      解法二

      +

      通过刚才的分析,我们会惊奇的发现这个 \(i,j\) 的组合其实是有限的!

      +

      那么我们就可以预处理所有的 \(a_i+b_j\)\(c_k+d_l\)

      然后用哈希表搞一搞,每次去暴力查表(注:查有没有相反数)

      -

      时间复杂度 $O(n^2)$

      +

      时间复杂度 \(O(n^2)\)

      真的有这么简单吗?

      我们来重新分析一下时间复杂度

      -

      首先题目中说明给的数小于等于 $2^{28}$

      -

      那么每个组合(和)小于$2^{29}$

      +

      首先题目中说明给的数小于等于 \(2^{28}\)

      +

      那么每个组合(和)小于\(2^{29}\)

      如果用哈希表搞,理论上来说可以构造hack数据,特别是简单的哈希函数很容易hack

      -

      为什么?因为如果哈希函数不是很好,那么至多在一个位置放了 $n^2$ 个数

      -

      好耶时间复杂度退化为 $O(n^4)$ 直接TLE

      -

      什么你要表内排序? $O(n^2\log n^2)$ + 可能较大常数 (来自本文作者的奇思妙想)

      +

      为什么?因为如果哈希函数不是很好,那么至多在一个位置放了 \(n^2\) 个数

      +

      好耶时间复杂度退化为 \(O(n^4)\) 直接TLE

      +

      什么你要表内排序? \(O(n^2\log +n^2)\) + 可能较大常数 (来自本文作者的奇思妙想)

      所以其实理论上来说这个解法是行不通的

      如果数据生成器能够分析提交的代码原理并构造hack数据,那么直接玩完

      好在没有那么恶心这题,数据比较简单,手写哈希表也能过

      -

      代码就不贴了,个人感觉实现很简单而且这解法不是很好 qwq

      +

      代码就不贴了,个人感觉实现很简单而且这解法不是很好 qwq

      @@ -895,7 +914,7 @@

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/09/05/luo-gu-p6186-noi-online-1-ti-gao-zu-mou-pao-pai-xu-ti-jie/index.html b/2021/09/05/luo-gu-p6186-noi-online-1-ti-gao-zu-mou-pao-pai-xu-ti-jie/index.html index 88522f317b..100f039b37 100644 --- a/2021/09/05/luo-gu-p6186-noi-online-1-ti-gao-zu-mou-pao-pai-xu-ti-jie/index.html +++ b/2021/09/05/luo-gu-p6186-noi-online-1-ti-gao-zu-mou-pao-pai-xu-ti-jie/index.html @@ -472,26 +472,50 @@

      洛谷P6186 [NOI Online #1 提
      -

      洛谷P6186 [NOI Online #1 提高组] 冒泡排序 题解

      题目链接:P6186 [NOI Online #1 提高组] 冒泡排序

      +

      洛谷P6186 [NOI +Online #1 提高组] 冒泡排序 题解

      +

      题目链接:P6186 [NOI +Online #1 提高组] 冒泡排序

      -

      题意:支持交换 $a_x$ 和 $a_{x+1}$ 和查询 $k$ 轮冒泡排序后的逆序对个数, $a$ 数组是长度为 $n$ 的排列

      +

      题意:支持交换 \(a_x\)\(a_{x+1}\) 和查询 \(k\) 轮冒泡排序后的逆序对个数, \(a\) 数组是长度为 \(n\) 的排列

      首先逆序对这个东西可以用树状数组搞定

      题目给的是排列,因此求逆序对的伪代码如下

      for i=1~n:
           f[i]=i-sum(a[i])-1,add(a[i],1)
      -

      当然如果不是排列会稍微烦一点,可以看下我的这篇文章

      -

      设 $f[i]$ 表示在 $i$ 左侧比 $i$ 大的数的个数,那么逆序对就是 $\sum\limits_{i=1}^{n}{f[i]}$

      -

      (注:这里的 $f[i]$ 就是上面伪代码中的 qwq)

      -

      我们看看冒泡排序了 $k$ 次以后会发生什么事

      -

      首先每次冒泡排序所有的 $f[i]$ 都会减少 $1$ 当且仅当 $f[i]>0$

      -

      那么 $k$ 次之后所有小于等于 $k$ 的 $f[i]$ 都会变成 $0$

      -

      因此我们只要每次就查询 $[k+1,n]$ 中 $f[i]$ 的个数就是答案了

      +

      当然如果不是排列会稍微烦一点,可以看下我的这篇文章

      +

      \(f[i]\) 表示在 \(i\) 左侧比 \(i\) 大的数的个数,那么逆序对就是 \(\sum\limits_{i=1}^{n}{f[i]}\)

      +

      (注:这里的 \(f[i]\) +就是上面伪代码中的 qwq)

      +

      我们看看冒泡排序了 \(k\) +次以后会发生什么事

      +

      首先每次冒泡排序所有的 \(f[i]\) +都会减少 \(1\) 当且仅当 \(f[i]>0\)

      +

      那么 \(k\) 次之后所有小于等于 \(k\)\(f[i]\) 都会变成 \(0\)

      +

      因此我们只要每次就查询 \([k+1,n]\) +中 \(f[i]\) 的个数就是答案了

      这个我们可以用两个树状数组来维护答案

      再看看这个交换操作,可能会影响到其他的答案

      -

      那么怎么办呢?根据刚才我们发现的性质,我们可以先把它们两个的贡献减掉 $1$ ,然后按 $a_x$ 和 $a_{x+1}$ 的大小关系分别处理答案的变化,再把原先的贡献补回来就好了

      +

      那么怎么办呢?根据刚才我们发现的性质,我们可以先把它们两个的贡献减掉 +\(1\) ,然后按 \(a_x\)\(a_{x+1}\) +的大小关系分别处理答案的变化,再把原先的贡献补回来就好了

      代码实现有点烦,主要是有点乱

      -

      代码如下

      +

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
       #define int long long
      @@ -925,7 +949,7 @@ 

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/09/05/ni-xu-dui-de-san-chong-qiu-fa/index.html b/2021/09/05/ni-xu-dui-de-san-chong-qiu-fa/index.html index f3f3a0be27..143f6b2422 100644 --- a/2021/09/05/ni-xu-dui-de-san-chong-qiu-fa/index.html +++ b/2021/09/05/ni-xu-dui-de-san-chong-qiu-fa/index.html @@ -472,10 +472,15 @@

      逆序对的三种求法

      -

      逆序对的三种求法

      一、什么是逆序对?

      对于给定的一段正整数序列,逆序对就是序列中 $a_i>a_j$ 且 $i<j$ 的有序对

      +

      逆序对的三种求法

      +

      一、什么是逆序对?

      +

      对于给定的一段正整数序列,逆序对就是序列中 \(a_i>a_j\)\(i<j\) 的有序对


      - -

      二、怎么求逆序对

      1.归并排序解法

      归并排序可以很好的解决逆序对问题

      +

      二、怎么求逆序对

      +

      1.归并排序解法

      +

      归并排序可以很好的解决逆序对问题

      我们只需要计算跨越分界线的贡献,并保证分界线两端的答案已被统计,显然根据归并的性质是可以保证的

      对于跨分界线的贡献计算很简单,只要在归并排序上加一句就行了

      这个东西请您自己理解,没什么好讲的 qwq

      @@ -530,13 +535,17 @@

      return 0; }


      - -

      2.树状数组解法

      有人要说了,你这文章写的太烂了 好吧我承认我只是想讲这个方法,稍微铺垫一下…

      +

      2.树状数组解法

      +

      有人要说了,你这文章写的太烂了 +好吧我承认我只是想讲这个方法,稍微铺垫一下...

      那么树状数组跟逆序对有什么关系?

      我们可以观察每一个元素,假设它有属性idx[i]表示在原数组中的位置,b[i]表示它的大小

      我们把元素按照b[i]降序排序,然后从大到小取出这些元素并++f[idx[i]]

      这样有什么性质呢?显然越小的数越晚标记

      -

      或者说,$\sum\limits_{j=1}^{\text{idx[i]-1}}{f[j]}$ 就是 $ia_j$ 的个数

      +

      或者说,\(\sum\limits_{j=1}^{\text{idx[i]-1}}{f[j]}\) +就是 \(i<j\)\(a_i>a_j\) 的个数

      这个东西一看就是树状数组处理

      代码如下

      #include <bits/stdc++.h>
      @@ -599,15 +608,16 @@ 

      return 0; }


      - -

      3.动态开点权值线段树解法

      有点类似树状数组的解法,维护区间和

      -

      我们可以基于值域 $[1,1e9]$ 建一棵线段树

      +

      3.动态开点权值线段树解法

      +

      有点类似树状数组的解法,维护区间和

      +

      我们可以基于值域 \([1,1e9]\) +建一棵线段树

      显然不能把所有的值都建结点,不然就MLE了

      怎么办呢?我们会发现,其实每次要查的其实就几个点

      那么就可以使用动态开点的思想,开一棵残缺的线段树,只保留我们会用到的结点

      其他的结点就是虚拟结点啦!根本不用管,查到的时候返回0就行了

      这题用这个解法有点容易被卡(? 而且有点大材小用

      -

      因此写的权值线段树可能有点小区别(因为实在太简单了…)

      +

      因此写的权值线段树可能有点小区别(因为实在太简单了...)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -1028,7 +1038,7 @@ 

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/09/05/qian-tan-shu-zhuang-shu-zu-qu-jian-xiu-gai-qu-jian-cha-xun/index.html b/2021/09/05/qian-tan-shu-zhuang-shu-zu-qu-jian-xiu-gai-qu-jian-cha-xun/index.html index a63eb857af..a548b55cdd 100644 --- a/2021/09/05/qian-tan-shu-zhuang-shu-zu-qu-jian-xiu-gai-qu-jian-cha-xun/index.html +++ b/2021/09/05/qian-tan-shu-zhuang-shu-zu-qu-jian-xiu-gai-qu-jian-cha-xun/index.html @@ -468,10 +468,17 @@

      浅谈树状数组 区间修改&
      -

      浅谈树状数组 区间修改&区间查询

      一、区间修改,单点查询

      首先我们可以先来想一下,树状数组的区间修改,单点查询怎么弄

      +

      浅谈树状数组 +区间修改&区间查询

      +

      一、区间修改,单点查询

      +

      首先我们可以先来想一下,树状数组的区间修改,单点查询怎么弄

      我们可以维护一个关于原数组的差分数组

      -

      很容易知道 $a_i=\sum\limits_{j=1}^{i}b_j$,其中 $b$ 为差分数组

      -

      那么区间修改时和差分的差不多,就是add(l,x),add(r+1,-x)表示 $[l,r]$ 加上 $x$

      +

      很容易知道 \(a_i=\sum\limits_{j=1}^{i}b_j\),其中 \(b\) 为差分数组

      +

      那么区间修改时和差分的差不多,就是add(l,x),add(r+1,-x)表示 +\([l,r]\) 加上 \(x\)

      单点查询根据上面的结论,就是sum(i)

      区间修改,单点查询的代码如下(注:这个不是LOJ132的代码 qwq)

      #include <bits/stdc++.h>
      @@ -526,17 +533,23 @@ 

      } return 0; }

      -

      二、区间修改,区间查询

      模板题LOJ132

      +

      二、区间修改,区间查询

      +

      模板题LOJ132

      那么区间修改,区间查询怎么搞呢?

      -

      我们先假设查询的区间为 $[1,r]$ ,因为 $[l,r]$ 的区间和就是 $[1,r]$ 的和减去 $[1,l]$ 的和

      -

      那么

      -

      那么我们只需要用两个树状数组分别维护左右两边的式子就行了

      +\text{ans} &= \sum\limits_{i=1}^{r}a_i +\\&= \sum\limits_{i=1}^{r}\sum\limits_{j=1}^{i}b_j +\\&=\sum\limits_{i=1}^{r}{b_i \times (r-i+1)} +\\&=\sum\limits_{i=1}^{r}{b_i\times (r+1)} - +\sum\limits_{i=1}^{r}{b_i\times i} +\end{aligned} +\] 那么我们只需要用两个树状数组分别维护左右两边的式子就行了

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -956,7 +969,7 @@ 

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/09/11/loj10050-the-xor-largest-pair-ti-jie/index.html b/2021/09/11/loj10050-the-xor-largest-pair-ti-jie/index.html index a151133d4f..7ae1a6bfde 100644 --- a/2021/09/11/loj10050-the-xor-largest-pair-ti-jie/index.html +++ b/2021/09/11/loj10050-the-xor-largest-pair-ti-jie/index.html @@ -476,15 +476,24 @@

      LOJ10050 The XOR Largest Pair
      -

      LOJ10050 The XOR Largest Pair

      题目链接:LOJ10050 The XOR Largest Pair

      +

      LOJ10050 The XOR Largest +Pair

      +

      题目链接:LOJ10050 The XOR Largest +Pair

      -

      题意:给定 $n$ 个非负整数,任取两个数,使得这两个数的异或结果最大,求这个最大值

      +

      题意:给定 \(n\) +个非负整数,任取两个数,使得这两个数的异或结果最大,求这个最大值

      肯定不是朴素枚举啊qwq

      直接用Trie存每个数的二进制就可以了(注:把二进制看作字符串)

      -

      根据异或的定义,对于每个 $a_j$ ,我们从最高位开始,尽可能取与 $a_i$ 这一位相反的那一条路,一定可以得到最大值

      -

      至于最高位,我们只要不够的补 $0$ 即可

      -

      因为它题目中说了每个数最大不大于 $2^{31}-1$ ,那么我们直接保存为30位二进制即可

      +

      根据异或的定义,对于每个 \(a_j\) +,我们从最高位开始,尽可能取与 \(a_i\) +这一位相反的那一条路,一定可以得到最大值

      +

      至于最高位,我们只要不够的补 \(0\) +即可

      +

      因为它题目中说了每个数最大不大于 \(2^{31}-1\) +,那么我们直接保存为30位二进制即可

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -912,7 +921,7 @@ 

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/09/18/qian-tan-fen-kuai-qu-jian-zhong-shu/index.html b/2021/09/18/qian-tan-fen-kuai-qu-jian-zhong-shu/index.html index 7d7dfb59ce..4d295ebbe0 100644 --- a/2021/09/18/qian-tan-fen-kuai-qu-jian-zhong-shu/index.html +++ b/2021/09/18/qian-tan-fen-kuai-qu-jian-zhong-shu/index.html @@ -472,25 +472,37 @@

      浅谈分块 区间众数

      -

      浅谈分块 区间众数

      前言

      分块大法好(

      +

      浅谈分块 区间众数

      +

      前言

      +

      分块大法好(

      本文直接讲例题了 qwq


      - -

      P4168 [Violet]蒲公英

      题目链接:P4168 [Violet]蒲公英

      +

      P4168 [Violet]蒲公英

      +

      题目链接:P4168 +[Violet]蒲公英

      题意: 找到区间内编号最小的众数,强制在线

      -

      解法一

      直接分块

      -

      设块长为 $len$ ,块的总数为 $t$ 个

      +

      解法一

      +

      直接分块

      +

      设块长为 \(len\) ,块的总数为 \(t\)

      我们先预处理出所有以块的端点为端点的区间众数和众数的出现次数

      或者说就是把每个连续块组成的区间都算出编号最小众数和它的出现次数

      别忘了离散化,不然MLE咯

      -

      总共有 $t^2$ 个这样的区间,每次处理 $O(n)$ ,则预处理时间复杂度为 $O(nt^2)$

      +

      总共有 \(t^2\) +个这样的区间,每次处理 \(O(n)\) +,则预处理时间复杂度为 \(O(nt^2)\)

      暴力查询时可以在预处理过的数组基础上临时更新数组,以统计答案,再复原数组

      -

      总时间复杂度 $O(nt^2+mn/t)$

      -

      假设 $m$ 和 $n$ 数量级相同,解 $nt^2=mn/t$,$t \approx \sqrt[3]{n}$

      -

      则时间复杂度可以控制在 $O(n^{5/3})$ 内

      -

      空间复杂度比较大,$O(nt^2)$ (注:预处理所需)

      +

      总时间复杂度 \(O(nt^2+mn/t)\)

      +

      假设 \(m\)\(n\) 数量级相同,解 \(nt^2=mn/t\)\(t +\approx \sqrt[3]{n}\)

      +

      则时间复杂度可以控制在 \(O(n^{5/3})\)

      +

      空间复杂度比较大,\(O(nt^2)\) +(注:预处理所需)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -596,7 +608,8 @@ 

      } return 0; }

      -

      解法二

      还是分块,只不过维护的东西不一样

      +

      解法二

      +

      还是分块,只不过维护的东西不一样

      我们发现,解法一的空间就多在了次数统计这个东西上

      次数统计?不是可以二分吗?

      对每个数值建个vector保存它出现的下标,出现次数就是upper_bd-lower_bd

      @@ -604,10 +617,14 @@

      暴力查询结果和众数的结果比一下就行了

      别忘了离散化哦

      有点时间换空间的感觉,但是还是很快

      -

      时间复杂度 $O(nt+mn/t\times \log n)$

      -

      假设 $m$ 和 $n$ 数量级相同,还是解方程,$t \approx \sqrt{n\log n}$

      -

      则时间复杂度可控制在 $O(n\sqrt{n\log n})$

      -

      空间复杂度 $O(t^2)$ ,不错

      +

      时间复杂度 \(O(nt+mn/t\times \log +n)\)

      +

      假设 \(m\)\(n\) 数量级相同,还是解方程,\(t \approx \sqrt{n\log n}\)

      +

      则时间复杂度可控制在 \(O(n\sqrt{n\log +n})\)

      +

      空间复杂度 \(O(t^2)\) ,不错

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -721,22 +738,38 @@ 

      然后我们就想到

      如果只需要知道众数的出现次数,有没有什么好的办法呢?


      - -

      [Ynoi2019 模拟赛] Yuno loves sqrt technology III

      题目链接:[Ynoi2019 模拟赛] Yuno loves sqrt technology III

      +

      [Ynoi2019 +模拟赛] Yuno loves sqrt technology III

      +

      题目链接:[Ynoi2019 +模拟赛] Yuno loves sqrt technology III

      题意:区间众数出现次数,强制在线

      -

      类似于上一题的解法二,这题 $t=\sqrt{n}$ 即可

      +

      类似于上一题的解法二,这题 \(t=\sqrt{n}\) 即可

      不同的是,我们要预处理出连续块的众数出现次数

      然后还是每个数值建一个vector,统计出现的下标

      -

      再记录每个存的下标在vector中的下标 $ax_i$

      -

      显然非暴力查询的区域,答案 $res=f[x][y]$

      -

      因为边界数最多对答案有 $2\sqrt{n}$ 的贡献

      -

      所以暴力查询时,对于每个左侧元素 $k$ ,我们只要找到 $k$ 的vector中 $ax_k+res$ 的位置的元素,看它是不是小于 $r$ ,如果小于 $r$ ,说明它一定更接近最终答案且在该区间内,更新 $res$

      -

      对于每个右侧元素 $k$ ,我们只要找到 $k$ 的vector中 $ax_k-res$ 的位置的元素,看它是不是大于 $l$ ,原理同上

      -

      最终答案即为 $res$

      -

      时间复杂度 $O((n+m)\sqrt{n})$

      -

      空间复杂度 $O(n)$

      +

      再记录每个存的下标在vector中的下标 \(ax_i\)

      +

      显然非暴力查询的区域,答案 \(res=f[x][y]\)

      +

      因为边界数最多对答案有 \(2\sqrt{n}\) +的贡献

      +

      所以暴力查询时,对于每个左侧元素 \(k\) ,我们只要找到 \(k\) 的vector中 \(ax_k+res\) 的位置的元素,看它是不是小于 +\(r\) ,如果小于 \(r\) +,说明它一定更接近最终答案且在该区间内,更新 \(res\)

      +

      对于每个右侧元素 \(k\) +,我们只要找到 \(k\) 的vector中 \(ax_k-res\) 的位置的元素,看它是不是大于 +\(l\) ,原理同上

      +

      最终答案即为 \(res\)

      +

      时间复杂度 \(O((n+m)\sqrt{n})\)

      +

      空间复杂度 \(O(n)\)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -843,10 +876,8 @@ 

      return 0; }


      - - - -

      总结

      本文简单讲了下区间众数的分块解法

      +

      总结

      +

      本文简单讲了下区间众数的分块解法

      @@ -1212,7 +1243,7 @@

      总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/09/23/jun-zhi-bu-deng-shi-ji-qi-zheng-ming/index.html b/2021/09/23/jun-zhi-bu-deng-shi-ji-qi-zheng-ming/index.html index 1d8d8d6e5c..7e025d7293 100644 --- a/2021/09/23/jun-zhi-bu-deng-shi-ji-qi-zheng-ming/index.html +++ b/2021/09/23/jun-zhi-bu-deng-shi-ji-qi-zheng-ming/index.html @@ -468,47 +468,78 @@

      均值不等式及其证明

      -

      均值不等式及其证明

      前言

      还有很多证明方法,等我学了再写 qwq

      +

      均值不等式及其证明

      +

      前言

      +

      还有很多证明方法,等我学了再写 qwq


      - -

      均值不等式

      引理1:若 $a\ge 0,b\ge 0$ ,则 $(a+b)^n\ge a^n+na^{n-1}b,n\in \Z_+$

      +

      均值不等式

      +

      引理1:若 \(a\ge 0,b\ge +0\) ,则 \((a+b)^n\ge +a^n+na^{n-1}b,n\in \Z_+\)

      直接二项式展开,证明略


      - -

      命题1:求证 $\dfrac{\sum_{i=1}^{n}a_i}{n}\ge \sqrt[n]{\prod_{i=1}^na_i}$

      -

      数学归纳法证明:原命题等价于 $\left(\dfrac{\sum_{i=1}^{n}a_i}{n}\right)^n \ge \prod\limits_{i=1}^{n}a_i$

      -

      设 $n=k$ 时原不等式成立

      -

      不妨假设 $\forall i<j,a_i<a_j$

      -

      记 $S_n = \sum\limits_{i=1}^{n}a_i$

      -

      当 $n=k+1$ 时,原不等式为

      -

      $\left(\dfrac{S_k+a_{k+1}}{k+1}\right)^{k+1} \ge \prod\limits_{i=1}^{k+1}a_i$

      -

      $\left(\dfrac{S_k}{k}+\dfrac{ka_{k+1}-S_k}{k(k+1)}\right)^{k+1}\ge \prod\limits_{i=1}^{k+1}a_i$

      -

      由引理1可知 $\text{LHS}\ge \left(\dfrac{S_k}{k}\right)^{k+1}+(k+1)\left(\dfrac{S_k}{k}\right)^k\dfrac{ka_{k+1}-S_k}{k(k+1)}$

      -

      $\because a_{k+1}=\max\limits_{1\le i \le k+1}\{a_i\}$

      -

      $\therefore ka_{k+1}-S_k\ge 0$

      -

      故 $\text{LHS} \ge \left(\dfrac{S_k}{k}\right)^{k}\times a_{k+1} \ge \text{RHS}$

      +

      命题1:求证 \(\dfrac{\sum_{i=1}^{n}a_i}{n}\ge +\sqrt[n]{\prod_{i=1}^na_i}\)

      +

      数学归纳法证明:原命题等价于 \(\left(\dfrac{\sum_{i=1}^{n}a_i}{n}\right)^n \ge +\prod\limits_{i=1}^{n}a_i\)

      +

      \(n=k\) 时原不等式成立

      +

      不妨假设 \(\forall +i<j,a_i<a_j\)

      +

      \(S_n = +\sum\limits_{i=1}^{n}a_i\)

      +

      \(n=k+1\) 时,原不等式为

      +

      \(\left(\dfrac{S_k+a_{k+1}}{k+1}\right)^{k+1} \ge +\prod\limits_{i=1}^{k+1}a_i\)

      +

      \(\left(\dfrac{S_k}{k}+\dfrac{ka_{k+1}-S_k}{k(k+1)}\right)^{k+1}\ge +\prod\limits_{i=1}^{k+1}a_i\)

      +

      由引理1可知 \(\text{LHS}\ge +\left(\dfrac{S_k}{k}\right)^{k+1}+(k+1)\left(\dfrac{S_k}{k}\right)^k\dfrac{ka_{k+1}-S_k}{k(k+1)}\)

      +

      \(\because a_{k+1}=\max\limits_{1\le i \le +k+1}\{a_i\}\)

      +

      \(\therefore ka_{k+1}-S_k\ge 0\)

      +

      \(\text{LHS} \ge +\left(\dfrac{S_k}{k}\right)^{k}\times a_{k+1} \ge +\text{RHS}\)

      故原命题得证


      - -

      命题2:求证 $\sqrt{\dfrac{\sum_{i=1}^{n}a_i^2}{n}} \ge \dfrac{\sum_{i=1}^{n}a_i}{n}$

      -

      证明:令 $c = \dfrac{\sum_{i=1}^{n}a_i}{n},x_i=a_i-c$

      -

      则 $\sum_{i=1}^{n}a_i = \sum_{i=1}^{n}x_i+nc = \sum_{i=1}^{n}x_i+\sum_{i=1}^{n}a_i$

      -

      $\therefore \sum_{i=1}^{n}x_i = 0$

      -

      $\therefore \text{LHS}=\sqrt{\dfrac{\sum_{i=1}^{n}\left(x_i+c\right)^2}{n}}$

      -

      $= \sqrt{c^2+\dfrac{\sum_{i=1}^{n}x_i^2}{n}} \ge \sqrt{c^2} = \text{RHS}$

      +

      命题2:求证 \(\sqrt{\dfrac{\sum_{i=1}^{n}a_i^2}{n}} \ge +\dfrac{\sum_{i=1}^{n}a_i}{n}\)

      +

      证明:令 \(c = +\dfrac{\sum_{i=1}^{n}a_i}{n},x_i=a_i-c\)

      +

      \(\sum_{i=1}^{n}a_i = +\sum_{i=1}^{n}x_i+nc = \sum_{i=1}^{n}x_i+\sum_{i=1}^{n}a_i\)

      +

      \(\therefore \sum_{i=1}^{n}x_i = +0\)

      +

      \(\therefore +\text{LHS}=\sqrt{\dfrac{\sum_{i=1}^{n}\left(x_i+c\right)^2}{n}}\)

      +

      \(= +\sqrt{c^2+\dfrac{\sum_{i=1}^{n}x_i^2}{n}} \ge \sqrt{c^2} = +\text{RHS}\)

      故原命题得证


      - -

      命题3:求证 $\sqrt[n]{\prod_{i=1}^na_i} \ge \dfrac{n}{\sum_{i=1}^{n}a_i^{-1}}$

      +

      命题3:求证 \(\sqrt[n]{\prod_{i=1}^na_i} \ge +\dfrac{n}{\sum_{i=1}^{n}a_i^{-1}}\)

      证明:由命题1可知

      -

      $\dfrac{\sum_{i=1}^{n}a_i^{-1}}{n} \ge \sqrt[n]{\prod_{i=1}^{n}a_i^{-1}} = \dfrac{1}{\sqrt[n]{\prod_{i=1}^{n}a_i}}$

      -

      $\therefore \sqrt[n]{\prod_{i=1}^{n}a_i} \ge \dfrac{n}{\sum_{i=1}^{n}a_i^{-1}}$

      +

      \(\dfrac{\sum_{i=1}^{n}a_i^{-1}}{n} \ge +\sqrt[n]{\prod_{i=1}^{n}a_i^{-1}} = +\dfrac{1}{\sqrt[n]{\prod_{i=1}^{n}a_i}}\)

      +

      \(\therefore \sqrt[n]{\prod_{i=1}^{n}a_i} +\ge \dfrac{n}{\sum_{i=1}^{n}a_i^{-1}}\)

      故原命题得证


      - -

      $\therefore$ 综上所述,可得结论(均值不等式)

      -

      $\dfrac{n}{\sum_{i=1}^{n}a_i^{-1}} \le \sqrt[n]{\prod_{i=1}^{n}a_i} \le \dfrac{\sum_{i=1}^{n}a_i}{n} \le \sqrt{\dfrac{\sum_{i=1}^{n}a_i^2}{n}}$

      -

      当且仅当 $a_1=a_2=…=a_n$ 时等号成立

      +

      \(\therefore\) +综上所述,可得结论(均值不等式)

      +

      \(\dfrac{n}{\sum_{i=1}^{n}a_i^{-1}} \le +\sqrt[n]{\prod_{i=1}^{n}a_i} \le \dfrac{\sum_{i=1}^{n}a_i}{n} \le +\sqrt{\dfrac{\sum_{i=1}^{n}a_i^2}{n}}\)

      +

      当且仅当 \(a_1=a_2=...=a_n\) +时等号成立

      也就是

      调和平均数不超过几何平均数不超过算术平均数不超过平方平均数

      @@ -868,7 +899,7 @@

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/10/03/ubuntu-xie-zai-fcitx-shu-ru-fa/index.html b/2021/10/03/ubuntu-xie-zai-fcitx-shu-ru-fa/index.html index 026caea2af..3a7f4e6de5 100644 --- a/2021/10/03/ubuntu-xie-zai-fcitx-shu-ru-fa/index.html +++ b/2021/10/03/ubuntu-xie-zai-fcitx-shu-ru-fa/index.html @@ -468,7 +468,8 @@

      ubuntu 卸载fcitx输入法

      -

      ubuntu 卸载fcitx输入法

      我又来水文章了

      +

      ubuntu 卸载fcitx输入法

      +

      我又来水文章了

      打开终端

      输入

      sudo apt-get remove fcitx
      @@ -478,7 +479,7 @@ 

      如果您原来使用的就是fcitx输入法,那再输入一句

      sudo reboot

      重启以后默认就是ibus输入法啦!

      -

      ibus大法好!

      +

      ibus大法好!

      @@ -825,7 +826,7 @@

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/10/10/luo-gu-p1486-noi2004-yu-men-de-chu-na-yuan-ti-jie/index.html b/2021/10/10/luo-gu-p1486-noi2004-yu-men-de-chu-na-yuan-ti-jie/index.html index 7917d8b2eb..40f182aa03 100644 --- a/2021/10/10/luo-gu-p1486-noi2004-yu-men-de-chu-na-yuan-ti-jie/index.html +++ b/2021/10/10/luo-gu-p1486-noi2004-yu-men-de-chu-na-yuan-ti-jie/index.html @@ -468,30 +468,47 @@

      洛谷P1486 [NOI2004] 郁闷的
      -

      洛谷P1486 [NOI2004] 郁闷的出纳员 题解

      题目链接:P1486 [NOI2004] 郁闷的出纳员

      +

      洛谷P1486 [NOI2004] +郁闷的出纳员 题解

      +

      题目链接:P1486 +[NOI2004] 郁闷的出纳员

      题意:维护一个数据结构,支持

      -
        -
      1. 插入一个大小为 $k$ 的值,小于下界时不插入
      2. -
      3. 所有元素加上 $k$
      4. -
      5. 所有元素减去 $k$ ,小于下界的删除
      6. -
      7. 查询所有元素中的 $k$ 大值
      8. +
          +
        1. 插入一个大小为 \(k\) +的值,小于下界时不插入
        2. +
        3. 所有元素加上 \(k\)
        4. +
        5. 所有元素减去 \(k\) +,小于下界的删除
        6. +
        7. 查询所有元素中的 \(k\) 大值

        最后输出删除的元素个数(两元素相等算两个)

      可以用平衡树来解决本题

      听说正解是splay,不过我来讲一种替罪羊树的写法

      -

      我们可以维护一个 $d$ ,表示每次加减的变化

      -

      这样每个元素就可以用 $x+d$ 的形式表示了

      -

      在平衡树里放原来的数减去插入时的 $d$ ,即 $k-d$

      +

      我们可以维护一个 \(d\) +,表示每次加减的变化

      +

      这样每个元素就可以用 \(x+d\) +的形式表示了

      +

      在平衡树里放原来的数减去插入时的 \(d\) ,即 \(k-d\)

      这样输出的时候就是正常的数了

      那小于下界的怎么搞呢?

      根据替罪羊树的性质,我们可以直接把整课树重构

      -

      Flatten操作时判断当前元素 $x$ 加上 $d$ 是否超过下界,且存在,否则就直接不管它了

      -

      $k$ 大值直接查 $num1-k+1$ 小值就好了

      -

      这个 $num1$ 表示每次操作后的剩余结点数(两元素相等算两个)

      -

      因为它最后还有个询问删除的,再记录一下总插入结点数(两元素相等算两个) $num2$ ,答案就是 $num2-num1$

      -

      这样整个算法的时间复杂度为 $O(n\log n)$

      +

      Flatten操作时判断当前元素 \(x\) 加上 \(d\) +是否超过下界,且存在,否则就直接不管它了

      +

      \(k\) 大值直接查 \(num1-k+1\) 小值就好了

      +

      这个 \(num1\) +表示每次操作后的剩余结点数(两元素相等算两个)

      +

      因为它最后还有个询问删除的,再记录一下总插入结点数(两元素相等算两个) +\(num2\) ,答案就是 \(num2-num1\)

      +

      这样整个算法的时间复杂度为 \(O(n\log +n)\)

      本题在luogu上的数据是windows下造的,所以还要注意换行符是\r\n,不是linux下的\n

      代码如下

      #include <bits/stdc++.h>
      @@ -1007,7 +1024,7 @@ 

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/11/12/ubuntu-wps-zi-ti-que-shi-jie-jue-fang-fa/index.html b/2021/11/12/ubuntu-wps-zi-ti-que-shi-jie-jue-fang-fa/index.html index a962f59f52..efbbd8fbed 100644 --- a/2021/11/12/ubuntu-wps-zi-ti-que-shi-jie-jue-fang-fa/index.html +++ b/2021/11/12/ubuntu-wps-zi-ti-que-shi-jie-jue-fang-fa/index.html @@ -472,19 +472,28 @@

      ubuntu WPS字体缺失 解决方
      -

      ubuntu WPS字体缺失 解决方法

      前言

      请保证您还有一台windows

      +

      ubuntu WPS字体缺失 解决方法

      +

      前言

      +

      请保证您还有一台windows


      - -

      一、在windows复制字体

      首先在windows下载好WPS,然后找到字体,复制

      -

      在这里插入图片描述

      -

      二、复制到ubuntu

      我的ubuntu上装了WPS2019,目录在/usr/share/fonts/wps-office

      +

      一、在windows复制字体

      +

      首先在windows下载好WPS,然后找到字体,复制

      +
      + + +
      +

      二、复制到ubuntu

      +

      我的ubuntu上装了WPS2019,目录在/usr/share/fonts/wps-office

      因此只要在终端输入

      sudo cp ./* /usr/share/fonts/wps-office

      就好了

      -

      三、重启WPS

      关掉再打开

      +

      三、重启WPS

      +

      关掉再打开


      - -

      总结

      从windows上复制到ubuntu

      +

      总结

      +

      从windows上复制到ubuntu

      @@ -842,7 +851,7 @@

      总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/11/13/ubuntu-gimp-zi-ti-xian-shi-yi-chang-jie-jue-fang-fa/index.html b/2021/11/13/ubuntu-gimp-zi-ti-xian-shi-yi-chang-jie-jue-fang-fa/index.html index d4e1bf8d2c..a0598d13e2 100644 --- a/2021/11/13/ubuntu-gimp-zi-ti-xian-shi-yi-chang-jie-jue-fang-fa/index.html +++ b/2021/11/13/ubuntu-gimp-zi-ti-xian-shi-yi-chang-jie-jue-fang-fa/index.html @@ -468,7 +468,9 @@

      ubuntu GIMP 字体显示异常
      -

      ubuntu GIMP 字体显示异常 解决方法

      今天用GIMP的时候发现字体出了问题

      +

      ubuntu GIMP 字体显示异常 +解决方法

      +

      今天用GIMP的时候发现字体出了问题

      报错如下

      Fontconfig warning: FcPattern object width does not accept value [75 100)
       /snap/gimp/380/usr/bin/gimp: Gimp-Text-严重: gimp_font_factory_load_names: assertion 'fontset' failed
      @@ -855,7 +857,7 @@ 

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/11/19/windows-she-zhi-xu-yao-guan-li-yuan-quan-xian-shi-shu-ru-mi-ma-cai-neng-cao-zuo/index.html b/2021/11/19/windows-she-zhi-xu-yao-guan-li-yuan-quan-xian-shi-shu-ru-mi-ma-cai-neng-cao-zuo/index.html index 98ecc5140b..80435b205f 100644 --- a/2021/11/19/windows-she-zhi-xu-yao-guan-li-yuan-quan-xian-shi-shu-ru-mi-ma-cai-neng-cao-zuo/index.html +++ b/2021/11/19/windows-she-zhi-xu-yao-guan-li-yuan-quan-xian-shi-shu-ru-mi-ma-cai-neng-cao-zuo/index.html @@ -468,14 +468,18 @@

      windows 设置需要管理员权
      -

      windows 设置需要管理员权限时输入密码才能操作

      步骤如下

      +

      windows +设置需要管理员权限时输入密码才能操作

      +

      步骤如下

      首先 win+r,输入regedit打开注册表编辑器

      然后找到

      计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorAdmin

      将值修改为 3 (十六进制)

      不明白的话可以看看下面的图

      -

      -

      +

      +

      然后尝试打开一下任务管理器

      就发现需要PIN啦!

      @@ -831,7 +835,7 @@

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/11/20/windows-matlab-r2020b-an-zhuang-jiao-cheng/index.html b/2021/11/20/windows-matlab-r2020b-an-zhuang-jiao-cheng/index.html index e8a4296c81..e7b6c4cd7d 100644 --- a/2021/11/20/windows-matlab-r2020b-an-zhuang-jiao-cheng/index.html +++ b/2021/11/20/windows-matlab-r2020b-an-zhuang-jiao-cheng/index.html @@ -468,62 +468,131 @@

      windows Matlab R2020b 安装教
      -

      windows Matlab R2020b 安装教程

      前言

      本文其实是我自己在安装过程中截图的,现在搞定了整理出来给大家参考一下

      +

      windows Matlab R2020b +安装教程

      +

      前言

      +

      本文其实是我自己在安装过程中截图的,现在搞定了整理出来给大家参考一下

      使用的是matlab R2020b

      如果本文有什么错误或对本文有什么问题,欢迎留言 qwq

      要是图挂了可以直接私信我,只要我还在


      - -

      一、下载文件

      下载好文件,我就放张图

      +

      一、下载文件

      +

      下载好文件,我就放张图

      要尽量支持正版哦,这是正版软件下载链接

      -

      https://ww2.mathworks.cn/products/matlab.html

      -

      在这里插入图片描述

      +

      https://ww2.mathworks.cn/products/matlab.html

      +
      + + +

      - -

      二、解压文件

      选中上面的那些.rar文件,然后解压(我建议用360压缩,挺好用的,不是打广告qwq

      -

      在这里插入图片描述

      +

      二、解压文件

      +

      选中上面的那些.rar文件,然后解压(我建议用360压缩,挺好用的,不是打广告qwq

      +
      + + +

      然后稍微整理一下,留下有用的,如下

      注:第二个文件夹就是解压后的东西,我不放心就先留着了

      -

      在这里插入图片描述

      +
      + + +

      - -

      三、开始安装

      打开.iso的文件,然后双击setup文件

      -

      在这里插入图片描述
      在这里插入图片描述

      +

      三、开始安装

      +

      打开.iso的文件,然后双击setup文件

      +


      - - -

      四、安装细节

      在这里插入图片描述

      +

      四、安装细节

      +
      + + +

      安装密钥是

      09806-07443-53955-64350-21751-41297
      -

      在这里插入图片描述

      +
      + + +

      许可证就选择刚刚下载的文件夹里...\crack\license_standalone.lic

      -

      在这里插入图片描述

      +
      + + +

      强烈建议安装在其他的盘,比如D盘

      -

      在这里插入图片描述

      -

      跳过几个不用设置的步骤….

      +
      + + +
      +

      跳过几个不用设置的步骤....

      开始安装,安装时间会比较长

      -

      在这里插入图片描述

      +
      + + +

      - - -

      五、激活

      安装好的不可以直接用,要先激活

      +

      五、激活

      +

      安装好的不可以直接用,要先激活

      就两个步骤,很简单

      首先把crack文件夹下的license.lic复制到安装目录下的license文件夹中

      -

      然后把那个dll文件复制下,直接看图吧…

      -

      在这里插入图片描述

      -

      在这里插入图片描述

      -

      在这里插入图片描述

      +

      然后把那个dll文件复制下,直接看图吧...

      +
      + + +
      +
      + + +
      +
      + + +

      - -

      六、运行

      它默认不会把快捷方式放桌面上,手动弄一下就好了

      +

      六、运行

      +

      它默认不会把快捷方式放桌面上,手动弄一下就好了

      位置在R2020b\bin

      运行后可以看到下图所示

      -

      在这里插入图片描述

      +
      + + +

      - -

      总结

      安装安装安装,没了qwq

      -



      参考文献

      -

      [1] 《最新MATLAB R2020b超详细安装教程(附完整安装文件)》

      +

      总结

      +

      安装安装安装,没了qwq

      +
      +

      参考文献

      +

      [1] 《最新MATLAB +R2020b超详细安装教程(附完整安装文件)》

      @@ -881,7 +950,7 @@

      总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/11/21/ubuntu-he-windows-chrome-liu-lan-qi-guan-bi-what-s-new-ye-mian/index.html b/2021/11/21/ubuntu-he-windows-chrome-liu-lan-qi-guan-bi-what-s-new-ye-mian/index.html index 1ad279b1e6..a7eeec1b53 100644 --- a/2021/11/21/ubuntu-he-windows-chrome-liu-lan-qi-guan-bi-what-s-new-ye-mian/index.html +++ b/2021/11/21/ubuntu-he-windows-chrome-liu-lan-qi-guan-bi-what-s-new-ye-mian/index.html @@ -472,9 +472,12 @@

      ubuntu和windows chrome浏览器
      -

      ubuntu和windows chrome浏览器 关闭what‘s new页面

      在搜索栏里输入chrome://flags

      +

      ubuntu和windows +chrome浏览器 关闭what‘s new页面

      +

      在搜索栏里输入chrome://flags

      修改操作如下

      -

      +

      @@ -832,7 +835,7 @@

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/11/24/luo-gu-p2633-count-on-a-tree-ti-jie/index.html b/2021/11/24/luo-gu-p2633-count-on-a-tree-ti-jie/index.html index 39fe95b0e4..80b0958639 100644 --- a/2021/11/24/luo-gu-p2633-count-on-a-tree-ti-jie/index.html +++ b/2021/11/24/luo-gu-p2633-count-on-a-tree-ti-jie/index.html @@ -468,23 +468,31 @@

      洛谷P2633 Count on a tree 题
      -

      洛谷P2633 Count on a tree 题解

      题目链接:P2633 Count on a tree

      +

      洛谷P2633 Count on a tree +题解

      +

      题目链接:P2633 +Count on a tree

      -

      题意:给定一棵树和 $u,v,k$ ,求 $u,v$ 结点间的第 $k$ 小点权

      +

      题意:给定一棵树和 \(u,v,k\) ,求 \(u,v\) 结点间的第 \(k\) 小点权

      本来以为是个树链剖分+主席树,结果发现并不可做

      观察到主席树本质上利用了前缀和来计算线性的区间和

      现在就是把求和搬到了树上

      可以立即想到树上差分

      不过这道题不是树上差分,可能应该叫树上前缀和更恰当一些

      -

      对于一条根结点(无根树就假定是 $1$ 好了)到叶子结点的简单路径,就是一个线性区间上的主席树

      +

      对于一条根结点(无根树就假定是 \(1\) +好了)到叶子结点的简单路径,就是一个线性区间上的主席树

      也就是在树上建立了类似前缀和体系的主席树群,每一个主席树维护了一条根结点到叶子结点的路径上的结点的信息

      感性理解一下,有点“前缀主席树”的感觉

      -

      那么 $u$ 到 $v$ 的信息就是

      +

      那么 \(u\)\(v\) 的信息就是

      sum[u]+sum[v]-sum[lca(u,v)]-sum[fa[lca(u,v)][0]]

      是不是很像树上差分了

      其他的比如离散化什么的都是基本操作了,这里不再赘述了 qwq

      -

      时间复杂度 $O(n\log n)$

      +

      时间复杂度 \(O(n\log n)\)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -963,7 +971,7 @@ 

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/12/03/qian-tan-la-ge-lang-ri-cha-zhi-fa/index.html b/2021/12/03/qian-tan-la-ge-lang-ri-cha-zhi-fa/index.html index f1042ab5df..d17fd9759c 100644 --- a/2021/12/03/qian-tan-la-ge-lang-ri-cha-zhi-fa/index.html +++ b/2021/12/03/qian-tan-la-ge-lang-ri-cha-zhi-fa/index.html @@ -472,21 +472,48 @@

      浅谈拉格朗日插值法

      -

      浅谈拉格朗日插值法

      模板题链接P4781 【模板】拉格朗日插值

      +

      浅谈拉格朗日插值法

      +

      模板题链接P4781 +【模板】拉格朗日插值

      -

      题意:给定 $n$ 个点 $P_i(x_i,y_i)$ ,将过该 $n$ 个点的最多 $n-1$ 次多项式记为 $f(x)$

      -

      给出 $k$ ,求 $f(k) \mod 998244353$

      +

      题意:给定 \(n\) +个点 \(P_i(x_i,y_i)\) ,将过该 \(n\) 个点的最多 \(n-1\) 次多项式记为 \(f(x)\)

      +

      给出 \(k\) ,求 \(f(k) \mod 998244353\)

      -

      $f(k) = \sum\limits_{i=1}^{n}y_i\prod_{j\ne i}{ \dfrac{k-x_j}{x_i-x_j} }$

      -

      证明:过 $P_i$ 作垂线交 $x$ 轴于 $H_i(x_i,0)$

      -

      构造 $g_i(x),i=1,2,\cdots ,n$ ,使得 $g_i(x)$ 的图像过 $\begin{cases}P_i(x_i,y_i) \\ H_j(x_j,0),j\ne i\end{cases}$

      -

      可以发现,$g_i(x) = a \prod_{j\ne i}{(x-x_j)}$ ,其中 $a$ 是未知数

      -

      因为 $g_i(x)$ 过 $P_i(x_i,y_i)$ ,代入并整理可得

      -

      $a = \dfrac{y_i}{ \prod_{j\ne i}{(x_i-x_j)} }$

      -

      把 $a$ 代入 $g_i(x)$ 得 $g_i(x) = y_i\times\dfrac{ \prod_{j\ne i}{(x-x_j)} }{ \prod_{j\ne i}{(x_i-x_j)} } = y_i\prod_{j\ne i}\dfrac{x-x_j}{x_i-x_j}$

      -

      $\therefore f(x) = \sum\limits_{i=1}^{n}g_i(x) = \sum\limits_{i=1}^{n}y_i\prod_{j\ne i}\dfrac{x-x_j}{x_i-x_j}$

      -

      把 $k$ 代入即可得

      -

      $f(k) = \sum\limits_{i=1}^{n}y_i\prod_{j\ne i}{ \dfrac{k-x_j}{x_i-x_j}}$

      +

      \(f(k) = +\sum\limits_{i=1}^{n}y_i\prod_{j\ne i}{ \dfrac{k-x_j}{x_i-x_j} +}\)

      +

      证明:过 \(P_i\) 作垂线交 \(x\) 轴于 \(H_i(x_i,0)\)

      +

      构造 \(g_i(x),i=1,2,\cdots ,n\) +,使得 \(g_i(x)\) 的图像过 \(\begin{cases}P_i(x_i,y_i) \\ H_j(x_j,0),j\ne +i\end{cases}\)

      +

      可以发现,\(g_i(x) = a \prod_{j\ne +i}{(x-x_j)}\) ,其中 \(a\) +是未知数

      +

      因为 \(g_i(x)\)\(P_i(x_i,y_i)\) ,代入并整理可得

      +

      \(a = \dfrac{y_i}{ \prod_{j\ne +i}{(x_i-x_j)} }\)

      +

      \(a\) 代入 \(g_i(x)\)\(g_i(x) = y_i\times\dfrac{ \prod_{j\ne i}{(x-x_j)} +}{ \prod_{j\ne i}{(x_i-x_j)} } = y_i\prod_{j\ne +i}\dfrac{x-x_j}{x_i-x_j}\)

      +

      \(\therefore f(x) = +\sum\limits_{i=1}^{n}g_i(x) = \sum\limits_{i=1}^{n}y_i\prod_{j\ne +i}\dfrac{x-x_j}{x_i-x_j}\)

      +

      \(k\) 代入即可得

      +

      \(f(k) = +\sum\limits_{i=1}^{n}y_i\prod_{j\ne i}{ +\dfrac{k-x_j}{x_i-x_j}}\)

      求解时可以先分别求出分子和分母,最后分子乘上分母的逆元即可

      代码如下

      #include <bits/stdc++.h>
      @@ -899,7 +926,7 @@ 

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/12/04/luo-gu-p4315-yue-xia-mao-jing-shu-ti-jie/index.html b/2021/12/04/luo-gu-p4315-yue-xia-mao-jing-shu-ti-jie/index.html index d1b013bfe2..e35c70e6fe 100644 --- a/2021/12/04/luo-gu-p4315-yue-xia-mao-jing-shu-ti-jie/index.html +++ b/2021/12/04/luo-gu-p4315-yue-xia-mao-jing-shu-ti-jie/index.html @@ -468,14 +468,21 @@

      洛谷P4315 月下“毛景树
      -

      洛谷P4315 月下“毛景树” 题解

      题目链接:P4315 月下“毛景树”

      +

      洛谷P4315 月下“毛景树” 题解

      +

      题目链接:P4315 +月下“毛景树”

      题意:请维护一个数据结构,支持

      -
        -
      1. 改第 $k$ 条边的边权
      2. -
      3. 结点 $u$ 到 $v$ 路径上的边权改为 $k$
      4. -
      5. 结点 $u$ 到 $v$ 路径上的边权增加 $k$
      6. -
      7. 询问结点 $u$ 到 $v$ 路径上的边权最大值
      8. +
          +
        1. 改第 \(k\) 条边的边权
        2. +
        3. 结点 \(u\)\(v\) 路径上的边权改为 \(k\)
        4. +
        5. 结点 \(u\)\(v\) 路径上的边权增加 \(k\)
        6. +
        7. 询问结点 \(u\)\(v\) 路径上的边权最大值

      一看树链剖分,敲了个板子发现给的是边权

      @@ -483,7 +490,12 @@

      \(u\) 和 \(v\) +的lca,这个lca的边权显然不是 \(u\)\(v\) 路径上的边

      所以维护id[x]+1

      然后就是少见的区间赋值操作,别跟我说ODT,怕被卡

      主要难在代码实现上,直接贴了(3.79KB)

      @@ -1052,7 +1064,7 @@

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/12/19/ubuntu-shi-fang-huan-cun-jiao-ben/index.html b/2021/12/19/ubuntu-shi-fang-huan-cun-jiao-ben/index.html index ff4fea72f3..6fdc43f841 100644 --- a/2021/12/19/ubuntu-shi-fang-huan-cun-jiao-ben/index.html +++ b/2021/12/19/ubuntu-shi-fang-huan-cun-jiao-ben/index.html @@ -468,8 +468,11 @@

      ubuntu 释放缓存脚本

      -

      ubuntu 释放缓存脚本

      注意:缓存$\ne$内存

      -

      内存占用过高解决办法

      +

      ubuntu 释放缓存脚本

      +

      注意:缓存\(\ne\)内存

      +

      内存占用过高解决办法

      首先在主目录新建一个free.sh,输入

      echo 3 > /proc/sys/vm/drop_caches

      保存后打开终端

      @@ -832,7 +835,7 @@

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2021/12/26/san-jiao-han-shu-chang-yong-gong-shi-zong-jie/index.html b/2021/12/26/san-jiao-han-shu-chang-yong-gong-shi-zong-jie/index.html index f4bc01a602..af68924c35 100644 --- a/2021/12/26/san-jiao-han-shu-chang-yong-gong-shi-zong-jie/index.html +++ b/2021/12/26/san-jiao-han-shu-chang-yong-gong-shi-zong-jie/index.html @@ -468,82 +468,157 @@

      三角函数常用公式总结
      -

      三角函数常用公式总结

      基本公式

      诱导公式

      $\sin ^{2} \alpha+\cos ^{2} \alpha=1$

      -

      $\tan \alpha=\dfrac{\sin \alpha}{\cos \alpha}$

      -

      $\tan\alpha=\dfrac{1}{\cot \alpha}$

      -

      $\sin (\alpha+2 \pi)=\sin \alpha$

      -

      $\cos (\alpha+2 \pi)=\cos \alpha$

      -

      $\tan (\alpha+2 \pi)=\tan \alpha$

      -

      $\sin (\pi+\alpha)=-\sin \alpha$

      -

      $\cos (\pi+\alpha)=-\cos \alpha$

      -

      $\tan (\pi+\alpha)=\tan \alpha$

      -

      $\sin (-\alpha)=-\sin \alpha$

      -

      $\cos (-\alpha)=\cos \alpha$

      -

      $\tan (-\alpha)=-\tan \alpha$

      -

      $\sin (\pi-\alpha)=\sin \alpha$

      -

      $\cos (\pi-\alpha)=-\cos \alpha$

      -

      $\tan (\pi -\alpha)=-\tan \alpha$

      -

      $\sin \left(\dfrac{\pi}{2}-\alpha\right)=\cos \alpha$

      -

      $\cos \left(\dfrac{\pi}{2}-\alpha\right)=\sin \alpha$

      -

      $\sin \left(\dfrac{\pi}{2}+\alpha\right)=\cos \alpha$

      -

      $\cos \left(\dfrac{\pi}{2}+\alpha\right)=-\sin \alpha$

      -

      $\sin\left(\dfrac{3\pi}{2}+\alpha\right)=-\cos\alpha$

      -

      $\cos\left(\dfrac{3\pi}{2}+\alpha\right)=\sin\alpha$

      -

      $\tan\left(\dfrac{3\pi}{2}+\alpha\right)=-\cot\alpha$

      -

      $\sin\left(\dfrac{3\pi}{2}-\alpha\right)=-\cos\alpha$

      -

      $\cos\left(\dfrac{3\pi}{2}-\alpha\right)=-\sin\alpha$

      -

      $\tan\left(\dfrac{3\pi}{2}-\alpha\right)=\cot\alpha$

      +

      三角函数常用公式总结

      +

      基本公式

      +

      诱导公式

      +

      \(\sin ^{2} \alpha+\cos ^{2} +\alpha=1\)

      +

      \(\tan \alpha=\dfrac{\sin \alpha}{\cos +\alpha}\)

      +

      \(\tan\alpha=\dfrac{1}{\cot +\alpha}\)

      +

      \(\sin (\alpha+2 \pi)=\sin +\alpha\)

      +

      \(\cos (\alpha+2 \pi)=\cos +\alpha\)

      +

      \(\tan (\alpha+2 \pi)=\tan +\alpha\)

      +

      \(\sin (\pi+\alpha)=-\sin +\alpha\)

      +

      \(\cos (\pi+\alpha)=-\cos +\alpha\)

      +

      \(\tan (\pi+\alpha)=\tan +\alpha\)

      +

      \(\sin (-\alpha)=-\sin \alpha\)

      +

      \(\cos (-\alpha)=\cos \alpha\)

      +

      \(\tan (-\alpha)=-\tan \alpha\)

      +

      \(\sin (\pi-\alpha)=\sin +\alpha\)

      +

      \(\cos (\pi-\alpha)=-\cos +\alpha\)

      +

      \(\tan (\pi -\alpha)=-\tan +\alpha\)

      +

      \(\sin +\left(\dfrac{\pi}{2}-\alpha\right)=\cos \alpha\)

      +

      \(\cos +\left(\dfrac{\pi}{2}-\alpha\right)=\sin \alpha\)

      +

      \(\sin +\left(\dfrac{\pi}{2}+\alpha\right)=\cos \alpha\)

      +

      \(\cos +\left(\dfrac{\pi}{2}+\alpha\right)=-\sin \alpha\)

      +

      \(\sin\left(\dfrac{3\pi}{2}+\alpha\right)=-\cos\alpha\)

      +

      \(\cos\left(\dfrac{3\pi}{2}+\alpha\right)=\sin\alpha\)

      +

      \(\tan\left(\dfrac{3\pi}{2}+\alpha\right)=-\cot\alpha\)

      +

      \(\sin\left(\dfrac{3\pi}{2}-\alpha\right)=-\cos\alpha\)

      +

      \(\cos\left(\dfrac{3\pi}{2}-\alpha\right)=-\sin\alpha\)

      +

      \(\tan\left(\dfrac{3\pi}{2}-\alpha\right)=\cot\alpha\)

      总结:

      -

      $\sin\left(\dfrac{k\pi}{2}\pm \alpha\right),k\in\Z$

      +

      \(\sin\left(\dfrac{k\pi}{2}\pm +\alpha\right),k\in\Z\)

      “奇变偶不变,符号看象限”

      -
      -

      和差角公式

      $\sin (\alpha\pm\beta)=\sin \alpha \cos \beta \pm \cos \alpha \sin \beta$

      -

      $\cos (\alpha \pm \beta)=\cos \alpha \cos \beta \mp \sin \alpha \sin \beta$

      -

      $\tan (\alpha\pm\beta)=\dfrac{\tan \alpha \pm \tan \beta}{1 \mp \tan \alpha \tan \beta}$

      -
      -

      辅助角公式

      $a \sin x+b \cos x=\sqrt{a^{2}+b^{2}} \sin \left(x+\arctan \dfrac{b}{a}\right)$

      -

      推广

      $\sin x \pm \cos x=\sqrt{2} \sin \left(x \pm \dfrac{\pi}{4}\right)$

      -

      $\cos x \pm \sin x=\sqrt{2} \cos \left(x \mp \dfrac{\pi}{4}\right)$

      -
      -

      推广公式

      基本

      $\sin \alpha=\pm \sqrt{1-\cos ^{2} \alpha}$

      -

      $\cos \alpha=\pm \sqrt{1-\sin ^{2} \alpha}$

      -

      $(\sin \alpha+\cos \alpha)^{2}+(\sin \alpha-\cos \alpha)^{2}=2$

      -

      $(\sin \alpha \pm \cos \alpha)^{2}=1 \pm \sin 2 \alpha$

      -
      -

      二倍角公式

      $\sin 2 \alpha=2 \sin \alpha \cos \alpha$

      -

      $\cos 2 \alpha=\cos ^{2} \alpha-\sin ^{2} \alpha=1-2 \sin ^{2} \alpha=2 \cos ^{2} \alpha-1$

      -

      $\tan 2 \alpha=\dfrac{2 \tan \alpha}{1-\tan ^{2} \alpha}$

      -
      -

      半角公式

      $\sin \dfrac{\alpha}{2}=\pm \sqrt{\dfrac{1-\cos \alpha}{2}}$

      -

      $\cos \dfrac{\alpha}{2}=\pm \sqrt{\dfrac{1+\cos \alpha}{2}}$

      -

      $\tan \dfrac{\alpha}{2}=\pm \sqrt{\dfrac{1-\cos \alpha}{1+\cos \alpha}}$

      -

      推广

      $\sin ^{2} \alpha=\dfrac{1-\cos 2 \alpha}{2}$

      -

      $\cos ^{2} \alpha=\dfrac{1+\cos 2 \alpha}{2}$

      -

      $1+\cos \alpha=2 \cos ^{2} \dfrac{\alpha}{2}$

      -

      $1-\cos\alpha = 2\sin^2\dfrac{\alpha}{2}$

      -

      $\tan\dfrac{\alpha}{2} = \dfrac{1-\cos\alpha}{\sin\alpha}$

      -
      -

      积化和差公式

      $\sin x \cos y = \dfrac{1}{2}\left[\sin(x+y) + \sin(x-y)\right]$

      -

      $\cos x \sin y = \dfrac{1}{2}\left[\sin(x+y) - \sin(x-y)\right]$

      -

      $\cos x\cos y = \dfrac{1}{2}\left[\cos (x+y)+\cos(x-y)\right]$

      -

      $\sin x \sin y = -\dfrac{1}{2}\left[\cos(x+y) - \cos(x-y)\right]$

      -
      -

      和差化积公式

      $\sin x + \sin y = 2\sin\dfrac{x+y}{2}\cos\dfrac{x-y}{2}$

      -

      $\sin x -\sin y = 2\cos\dfrac{x+y}{2}\sin\dfrac{x-y}{2}$

      -

      $\cos x + \cos y = 2\cos\dfrac{x+y}{2}\cos\dfrac{x-y}{2}$

      -

      $\cos x-\cos y = -2\sin\dfrac{x+y}{2}\sin\dfrac{x-y}{2}$

      -
      -

      其他

      $\dfrac{\cos \alpha}{1-\sin \alpha}=\dfrac{1+\sin \alpha}{\cos \alpha}$

      -

      $\sin \alpha=\dfrac{\sin 2 \alpha}{2 \cos \alpha}$

      -

      $\cos \alpha=\dfrac{\sin 2 \alpha}{2 \sin \alpha}$

      -

      $\tan \alpha+\tan \beta=\tan (\alpha+\beta)(1-\tan \alpha \tan \beta)$

      -
      -

      常用角的变换

      $\alpha=(\alpha+\beta)-\beta$

      -

      $\alpha=\beta-(\beta-\alpha)$

      -

      $\alpha=\dfrac{1}{2}\left[(\alpha+\beta)+(\alpha-\beta)\right]$

      -

      $\alpha=\dfrac{1}{2}\left[(\alpha+\beta)-(\beta-\alpha)\right]$

      -

      $\dfrac{\alpha+\beta}{2}=\left(\alpha-\dfrac{\beta}{2}\right)-\left(\dfrac{\alpha}{2}-\beta\right)$

      -

      $\alpha-\gamma=(\alpha-\beta)+(\beta-\gamma)$

      +
      +

      和差角公式

      +

      \(\sin (\alpha\pm\beta)=\sin \alpha \cos +\beta \pm \cos \alpha \sin \beta\)

      +

      \(\cos (\alpha \pm \beta)=\cos \alpha \cos +\beta \mp \sin \alpha \sin \beta\)

      +

      \(\tan (\alpha\pm\beta)=\dfrac{\tan \alpha +\pm \tan \beta}{1 \mp \tan \alpha \tan \beta}\)

      +
      +

      辅助角公式

      +

      \(a \sin x+b \cos x=\sqrt{a^{2}+b^{2}} \sin +\left(x+\arctan \dfrac{b}{a}\right)\)

      +

      推广

      +

      \(\sin x \pm \cos x=\sqrt{2} \sin \left(x +\pm \dfrac{\pi}{4}\right)\)

      +

      \(\cos x \pm \sin x=\sqrt{2} \cos \left(x +\mp \dfrac{\pi}{4}\right)\)

      +
      +

      推广公式

      +

      基本

      +

      \(\sin \alpha=\pm \sqrt{1-\cos ^{2} +\alpha}\)

      +

      \(\cos \alpha=\pm \sqrt{1-\sin ^{2} +\alpha}\)

      +

      \((\sin \alpha+\cos \alpha)^{2}+(\sin +\alpha-\cos \alpha)^{2}=2\)

      +

      \((\sin \alpha \pm \cos \alpha)^{2}=1 \pm +\sin 2 \alpha\)

      +
      +

      二倍角公式

      +

      \(\sin 2 \alpha=2 \sin \alpha \cos +\alpha\)

      +

      \(\cos 2 \alpha=\cos ^{2} \alpha-\sin ^{2} +\alpha=1-2 \sin ^{2} \alpha=2 \cos ^{2} \alpha-1\)

      +

      \(\tan 2 \alpha=\dfrac{2 \tan +\alpha}{1-\tan ^{2} \alpha}\)

      +
      +

      半角公式

      +

      \(\sin \dfrac{\alpha}{2}=\pm +\sqrt{\dfrac{1-\cos \alpha}{2}}\)

      +

      \(\cos \dfrac{\alpha}{2}=\pm +\sqrt{\dfrac{1+\cos \alpha}{2}}\)

      +

      \(\tan \dfrac{\alpha}{2}=\pm +\sqrt{\dfrac{1-\cos \alpha}{1+\cos \alpha}}\)

      +

      推广

      +

      \(\sin ^{2} \alpha=\dfrac{1-\cos 2 +\alpha}{2}\)

      +

      \(\cos ^{2} \alpha=\dfrac{1+\cos 2 +\alpha}{2}\)

      +

      \(1+\cos \alpha=2 \cos ^{2} +\dfrac{\alpha}{2}\)

      +

      \(1-\cos\alpha = +2\sin^2\dfrac{\alpha}{2}\)

      +

      \(\tan\dfrac{\alpha}{2} = +\dfrac{1-\cos\alpha}{\sin\alpha}\)

      +
      +

      积化和差公式

      +

      \(\sin x \cos y = +\dfrac{1}{2}\left[\sin(x+y) + \sin(x-y)\right]\)

      +

      \(\cos x \sin y = +\dfrac{1}{2}\left[\sin(x+y) - \sin(x-y)\right]\)

      +

      \(\cos x\cos y = \dfrac{1}{2}\left[\cos +(x+y)+\cos(x-y)\right]\)

      +

      \(\sin x \sin y = +-\dfrac{1}{2}\left[\cos(x+y) - \cos(x-y)\right]\)

      +
      +

      和差化积公式

      +

      \(\sin x + \sin y = +2\sin\dfrac{x+y}{2}\cos\dfrac{x-y}{2}\)

      +

      \(\sin x -\sin y = +2\cos\dfrac{x+y}{2}\sin\dfrac{x-y}{2}\)

      +

      \(\cos x + \cos y = +2\cos\dfrac{x+y}{2}\cos\dfrac{x-y}{2}\)

      +

      \(\cos x-\cos y = +-2\sin\dfrac{x+y}{2}\sin\dfrac{x-y}{2}\)

      +
      +

      其他

      +

      \(\dfrac{\cos \alpha}{1-\sin +\alpha}=\dfrac{1+\sin \alpha}{\cos \alpha}\)

      +

      \(\sin \alpha=\dfrac{\sin 2 \alpha}{2 \cos +\alpha}\)

      +

      \(\cos \alpha=\dfrac{\sin 2 \alpha}{2 \sin +\alpha}\)

      +

      \(\tan \alpha+\tan \beta=\tan +(\alpha+\beta)(1-\tan \alpha \tan \beta)\)

      +
      +

      常用角的变换

      +

      \(\alpha=(\alpha+\beta)-\beta\)

      +

      \(\alpha=\beta-(\beta-\alpha)\)

      +

      \(\alpha=\dfrac{1}{2}\left[(\alpha+\beta)+(\alpha-\beta)\right]\)

      +

      \(\alpha=\dfrac{1}{2}\left[(\alpha+\beta)-(\beta-\alpha)\right]\)

      +

      \(\dfrac{\alpha+\beta}{2}=\left(\alpha-\dfrac{\beta}{2}\right)-\left(\dfrac{\alpha}{2}-\beta\right)\)

      +

      \(\alpha-\gamma=(\alpha-\beta)+(\beta-\gamma)\)

      @@ -897,7 +972,7 @@

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/01/01/chrome-zi-dong-cao-zong-gu-ge-xiao-kong-long/index.html b/2022/01/01/chrome-zi-dong-cao-zong-gu-ge-xiao-kong-long/index.html index 8b959a059b..6c6cb1c1e4 100644 --- a/2022/01/01/chrome-zi-dong-cao-zong-gu-ge-xiao-kong-long/index.html +++ b/2022/01/01/chrome-zi-dong-cao-zong-gu-ge-xiao-kong-long/index.html @@ -468,8 +468,10 @@

      chrome 自动操纵谷歌小恐
      -

      chrome 自动操纵谷歌小恐龙

      全自动谷歌小恐龙 qwq

      -

      代码来自网络(链接

      +

      chrome 自动操纵谷歌小恐龙

      +

      全自动谷歌小恐龙 qwq

      +

      代码来自网络(链接

      function TrexRunnerBot() {
         const makeKeyArgs = (keyCode) => {
           const preventDefault = () => void 0;
      @@ -551,7 +553,12 @@ 

      +
      + + +

      q779太弱了才玩到2364分 QAQ

      @@ -906,7 +913,7 @@

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/01/01/luo-gu-p1204-usaco1.2-ji-niu-nai-milking-cows-ti-jie/index.html b/2022/01/01/luo-gu-p1204-usaco1.2-ji-niu-nai-milking-cows-ti-jie/index.html index 696e3eba27..2ff5251c74 100644 --- a/2022/01/01/luo-gu-p1204-usaco1.2-ji-niu-nai-milking-cows-ti-jie/index.html +++ b/2022/01/01/luo-gu-p1204-usaco1.2-ji-niu-nai-milking-cows-ti-jie/index.html @@ -468,12 +468,16 @@

      洛谷P1204 [USACO1.2]挤牛奶M
      -

      洛谷P1204 [USACO1.2]挤牛奶Milking Cows 题解

      题目链接:P1204 [USACO1.2]挤牛奶Milking Cows

      +

      洛谷P1204 +[USACO1.2]挤牛奶Milking Cows 题解

      +

      题目链接:P1204 +[USACO1.2]挤牛奶Milking Cows

      -

      题意:给定 $n$ 个区间,求这些区间覆盖后最长的区间和未被覆盖的最长区间(未被覆盖区间的范围在最左端到最右端内,否则就没有意义了)

      +

      题意:给定 \(n\) +个区间,求这些区间覆盖后最长的区间和未被覆盖的最长区间(未被覆盖区间的范围在最左端到最右端内,否则就没有意义了)

      正如简化的题意,我们可以使用类似于差分的思想,如图

      -

      +

      我们可以保存左右端点,然后从左向右遍历(排序一下就行,如果左右端点相同左端点优先)

      每次碰到端点加一下就行,和为0时更新答案即可

      代码:

      @@ -869,7 +873,7 @@

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/01/01/luo-gu-p5116-usaco18dec-mixing-milk-b-ti-jie/index.html b/2022/01/01/luo-gu-p5116-usaco18dec-mixing-milk-b-ti-jie/index.html index dbfde11a9d..0d7991dae9 100644 --- a/2022/01/01/luo-gu-p5116-usaco18dec-mixing-milk-b-ti-jie/index.html +++ b/2022/01/01/luo-gu-p5116-usaco18dec-mixing-milk-b-ti-jie/index.html @@ -468,7 +468,10 @@

      洛谷P5116 [USACO18DEC]Mixing M
      -

      洛谷P5116 [USACO18DEC]Mixing Milk B 题解

      题目链接:P5116 [USACO18DEC]Mixing Milk B

      +

      洛谷P5116 +[USACO18DEC]Mixing Milk B 题解

      +

      题目链接:P5116 +[USACO18DEC]Mixing Milk B

      题意:三个桶有一定牛奶和容积,要循环倒100次

      @@ -854,7 +857,7 @@

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/01/16/rmb-zhao-ling-wen-ti/index.html b/2022/01/16/rmb-zhao-ling-wen-ti/index.html index 8e4d30b84a..c943cfeb93 100644 --- a/2022/01/16/rmb-zhao-ling-wen-ti/index.html +++ b/2022/01/16/rmb-zhao-ling-wen-ti/index.html @@ -476,14 +476,21 @@

      RMB找零问题

      -

      RMB找零问题

      来自某次研究性学习的作业

      -

      前言

      可以先考虑这样的问题

      +

      RMB找零问题

      +

      来自某次研究性学习的作业

      +

      前言

      +

      可以先考虑这样的问题

      -

      给定 $n$ 种足量多的纸币,每种面额为 $a_i$ 元 $(0<a[i]≤10000,a[i]\in \Z,1≤i≤n≤100)$

      -

      给出需要找零的金额 $m (0≤m≤10000)$

      +

      给定 \(n\) +种足量多的纸币,每种面额为 \(a_i\) 元 +\((0<a[i]≤10000,a[i]\in +\Z,1≤i≤n≤100)\)

      +

      给出需要找零的金额 \(m +(0≤m≤10000)\)

      请找到一种方法使得找零所需的纸币数量尽可能地少,输出最小数量

      -

      例如:$68=50+10+5+2+1,12=10+2$

      -

      尽管 $8=10-2$ ,但是收银员是不能支付 $-2$ 元的

      +

      例如:\(68=50+10+5+2+1,12=10+2\)

      +

      尽管 \(8=10-2\) +,但是收银员是不能支付 \(-2\) 元的

      可以看出这就是一道完全背包模板题

      代码如下

      @@ -508,64 +515,97 @@

      前言}

      而本文讨论的是关于RMB(人民币)的找零问题


      - -

      特殊的找零问题

      +

      特殊的找零问题

      +

      考虑这样的问题

      -

      给定足量多的纸币,面额为 $1,2,5,10,20,50,100$ 元

      -

      给出需要找零的金额 $m(0≤m≤10^9)$

      +

      给定足量多的纸币,面额为 \(1,2,5,10,20,50,100\)

      +

      给出需要找零的金额 \(m(0≤m≤10^9)\)

      请找到一种方法使得找零所需的纸币数量尽可能地少,输出最小数量

      -

      例如:$68=50+10+5+2+1,12=10+2$

      -

      尽管 $8=10-2$ ,但是收银员是不能支付 $-2$ 元的

      +

      例如:\(68=50+10+5+2+1,12=10+2\)

      +

      尽管 \(8=10-2\) +,但是收银员是不能支付 \(-2\) 元的

      根据生活常识,我们可以发现这其实是个贪心问题

      那我们来尝试证明

      先把概念放一下

      -

      首先能用贪心解决的问题需要具备贪心选择最优子结构的性质

      +首先能用贪心解决的问题需要具备贪心选择最优子结构的性质
      -

      贪心选择

      -

      所谓贪心选择是指应用同一规则,将原问题转变成一个相似的但规模更小的子问题,而后的每一步
      都是当前看似最佳的选择,且这种选择只依赖于已做出的选择,不依赖于未做出的选择

      +

      所谓贪心选择是指应用同一规则,将原问题转变成一个相似的但规模更小的子问题,而后的每一步 +都是当前看似最佳的选择,且这种选择只依赖于已做出的选择,不依赖于未做出的选择

      也就是说所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到

      这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划的主要区别。

      在动态规划中,每步所做的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才能做出选择。

      而在贪心算法中,仅在当前状态下做出最好选择,即局部最优选择。然后再去解出做出这个选择后产生的相应的子问题。贪心算法所做的贪心选择可以依赖于以往所做过的选择,但决不依赖于将来所做的选择,也不依赖于子问题的解。正是由于这种差别,动态规划通常以自底向上的方式解各个问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式做出相继的贪心选择,每做一次贪心选择就将所求问题简化为规模更小的子问题


      -

      最优子结构

      执行算法时,每一次得到的结果虽然都是当前问题的最优解(即局部最优解),但只有满足全局最

      -

      优解包含局部最优解时,才能保证最终得到的结果是最优解

      +优解包含局部最优解时,才能保证最终得到的结果是最优解
      - -

      证明

      设 $c_i$ 为第 $i$ 种纸币的面额, $S_i$ 为总金额为 $i$ 时的最小花费

      -

      设总金额为 $n$ ,则原问题的最优解即为 $S_n=k$

      -

      现在将某一面值的纸币 $c_j$ 减少 $1$ ,则有 $S_{n-c_j} = k-1$ 为总金额 $n-c_j$ 时的最优解

      -

      否则设 $T_{n-c_j} = m$ 为总金额 $n-c_j$ 时的最优解,即 $m<k-1$

      -

      则 $T_{n-c_j} + 1$ 应为原问题的最优解,即 $m+1<k-1+1=k$ ,与最小花费为 $k$ 相矛盾

      +

      证明

      +

      \(c_i\) 为第 \(i\) 种纸币的面额, \(S_i\) 为总金额为 \(i\) 时的最小花费

      +

      设总金额为 \(n\) +,则原问题的最优解即为 \(S_n=k\)

      +

      现在将某一面值的纸币 \(c_j\) 减少 +\(1\) ,则有 \(S_{n-c_j} = k-1\) 为总金额 \(n-c_j\) 时的最优解

      +

      否则设 \(T_{n-c_j} = m\) 为总金额 +\(n-c_j\) 时的最优解,即 \(m<k-1\)

      +

      \(T_{n-c_j} + 1\) +应为原问题的最优解,即 \(m+1<k-1+1=k\) ,与最小花费为 \(k\) 相矛盾

      又由于纸币的数量足够多,因此问题间相互独立

      故问题满足最优子结构性质

      -

      设 $c_i$ 为第 $i$ 种纸币的面额,$x_i$ 为贪心得到的最优解 $\sum x_i$ 中使用第 $i$ 种纸币的数量,

      -

      $y_i$ 为原问题最优解 $\sum y_i$ 中使用第 $i$ 种纸币的数量,令 $x_i<x_{i+1},y_i<y_{i+1}$

      -

      假设贪心的得到的最优解不是原问题的最优解,则存在一个最大的 $k$ ,使得 $x_k\ne y_k$

      -

      可以发现 $k\ne 1$

      -

      情况1:若 $x_k<y_k$ ,由于贪心算法每次选择的都是最大面额的纸币,因此不存在

      -

      情况2:若 $x_k>y_k$ ,此时一定使用了较小面额的纸币来替代 $c_k$ ,即 $\sum{c_i}=c_k$

      -

      显然对于任意一个币值,任意 $2$ 张小于它的相加都小于它,例如 $1+2<5$

      -

      因此此时 $\sum x_k < \sum y_k$ ,与 $\sum y_k$ 是最优解相矛盾,因此不成立

      +

      \(c_i\) 为第 \(i\) 种纸币的面额,\(x_i\) 为贪心得到的最优解 \(\sum x_i\) 中使用第 \(i\) 种纸币的数量,

      +

      \(y_i\) 为原问题最优解 \(\sum y_i\) 中使用第 \(i\) 种纸币的数量,令 \(x_i<x_{i+1},y_i<y_{i+1}\)

      +

      假设贪心的得到的最优解不是原问题的最优解,则存在一个最大的 \(k\) ,使得 \(x_k\ne y_k\)

      +

      可以发现 \(k\ne 1\)

      +

      情况1:若 \(x_k<y_k\) +,由于贪心算法每次选择的都是最大面额的纸币,因此不存在

      +

      情况2:若 \(x_k>y_k\) +,此时一定使用了较小面额的纸币来替代 \(c_k\) ,即 \(\sum{c_i}=c_k\)

      +

      显然对于任意一个币值,任意 \(2\) +张小于它的相加都小于它,例如 \(1+2<5\)

      +

      因此此时 \(\sum x_k < \sum y_k\) +,与 \(\sum y_k\) +是最优解相矛盾,因此不成立

      故问题满足贪心选择性质

      -

      因此我们就可以 $O(1)$ 求解了

      -

      代码不放了比较简单

      +

      因此我们就可以 \(O(1)\) 求解了

      +代码不放了比较简单
      - -

      其他情况

      可以猜测,可能存在其他的情况也满足贪心的性质

      +

      其他情况

      +

      可以猜测,可能存在其他的情况也满足贪心的性质

      还没研究 qwq 但是找到了一篇论文

      -

      https://arxiv.org/pdf/0809.0400v1.pdf

      -

      是关于怎样的面额才能使用贪心算法的

      +

      https://arxiv.org/pdf/0809.0400v1.pdf

      +是关于怎样的面额才能使用贪心算法的
      - -

      总结

      简单证明了一下某些特定找零问题贪心算法的正确性

      -
      +

      总结

      +

      简单证明了一下某些特定找零问题贪心算法的正确性

      +

      参考文献

      -

      [1] 人民币找零的贪婪算法最有解的证明

      +

      [1] 人民币找零的贪婪算法最有解的证明

      @@ -931,7 +971,7 @@

      总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/01/28/luo-gu-p3121-usaco15feb-censoring-g-ti-jie/index.html b/2022/01/28/luo-gu-p3121-usaco15feb-censoring-g-ti-jie/index.html index fa72b3ef7a..2008a14aec 100644 --- a/2022/01/28/luo-gu-p3121-usaco15feb-censoring-g-ti-jie/index.html +++ b/2022/01/28/luo-gu-p3121-usaco15feb-censoring-g-ti-jie/index.html @@ -476,16 +476,27 @@

      洛谷P3121 [USACO15FEB] Censori
      -

      洛谷P3121 [USACO15FEB] Censoring G 题解

      题目链接:P3121 [USACO15FEB] Censoring G

      +

      洛谷P3121 [USACO15FEB] +Censoring G 题解

      +

      题目链接:P3121 +[USACO15FEB] Censoring G

      -

      题意:给出文本串 $t$ 和$n$ 个模式串 $s$ ,删除在 $t$ 中第一次出现的子串 $s_i$ ,并重复这个过程(在产生的新串 $t’$ 上继续删除操作),求最后的结果(所有模式串都要删掉)

      +

      题意:给出文本串 \(t\)\(n\) 个模式串 \(s\) ,删除在 \(t\) 中第一次出现的子串 \(s_i\) ,并重复这个过程(在产生的新串 \(t’\) +上继续删除操作),求最后的结果(所有模式串都要删掉)

      多模式串匹配很容易想到是AC自动机

      那删除操作怎么处理呢?

      注意到删除子串后,在删除的子段后继续匹配可能需要该子段前的已经算出(显然)的匹配值

      那么我们可以用两个栈分别维护匹配时的经过的点和答案

      在每次删除后我们只要跳回原来的点即可

      -

      时间复杂度 $O(|t| + \sum |s_i|)$

      +

      时间复杂度 \(O(|t| + \sum +|s_i|)\)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -924,7 +935,7 @@ 

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/01/28/luo-gu-p3796-mo-ban-ac-zi-dong-ji-jia-qiang-ban-ti-jie/index.html b/2022/01/28/luo-gu-p3796-mo-ban-ac-zi-dong-ji-jia-qiang-ban-ti-jie/index.html index 6fdc47c315..6cb41d7f20 100644 --- a/2022/01/28/luo-gu-p3796-mo-ban-ac-zi-dong-ji-jia-qiang-ban-ti-jie/index.html +++ b/2022/01/28/luo-gu-p3796-mo-ban-ac-zi-dong-ji-jia-qiang-ban-ti-jie/index.html @@ -472,9 +472,16 @@

      洛谷P3796 【模板】AC 自
      -

      洛谷P3796 【模板】AC 自动机(加强版) 题解

      题目链接:P3796 【模板】AC 自动机(加强版)

      +

      洛谷P3796 【模板】AC +自动机(加强版) 题解

      +

      题目链接:P3796 +【模板】AC 自动机(加强版)

      -

      题意: $T$ 组数据,每组 $N$ 个模式串 $s_i$ ,要求输出这些模式串在文本串 $S$ 中出现次数最多的模式串以及它们的出现次数,按输入顺序输出

      +

      题意\(T\) +组数据,每组 \(N\) 个模式串 \(s_i\) ,要求输出这些模式串在文本串 \(S\) +中出现次数最多的模式串以及它们的出现次数,按输入顺序输出

      可以发现这是个AC自动机的裸题

      那怎么处理出现次数呢?

      @@ -486,10 +493,15 @@

      \(O(\sum|s_i|+|S|)\)

      +

      而我们统计的时候是暴力跳fail指针,那么最坏时间复杂度 \(O(S\times \max\{|s_i|\})\)

      +

      所以整个算法的时间复杂度 \(O(T|S|\max\{|s_i|\})\)

      +

      然后我们发现它的数据范围 \(T\le50,S\le10^6,s_i\le70\) ,时间有 \(3\)

      而且这道题也没有故意卡这个,那我们就可以水过去了(别急着关掉这个网页啊!)

      先贴一下代码:qwq

      #include <bits/stdc++.h>
      @@ -576,8 +588,10 @@ 

      return 0; }

      那么问题来了,这题为什么不卡呢?

      -

      因为还有个 P5357 【模板】AC 自动机(二次加强版)

      -

      题解在此

      +

      因为还有个 P5357 +【模板】AC 自动机(二次加强版)

      +

      题解在此

      @@ -955,7 +969,7 @@

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/01/28/luo-gu-p4824-usaco15feb-censoring-s-ti-jie/index.html b/2022/01/28/luo-gu-p4824-usaco15feb-censoring-s-ti-jie/index.html index d26934a724..418c7fac54 100644 --- a/2022/01/28/luo-gu-p4824-usaco15feb-censoring-s-ti-jie/index.html +++ b/2022/01/28/luo-gu-p4824-usaco15feb-censoring-s-ti-jie/index.html @@ -476,17 +476,26 @@

      洛谷P4824 [USACO15FEB] Censori
      -

      洛谷P4824 [USACO15FEB] Censoring S 题解

      题目链接:P4824 [USACO15FEB] Censoring S

      +

      洛谷P4824 [USACO15FEB] +Censoring S 题解

      +

      题目链接:P4824 +[USACO15FEB] Censoring S

      -

      题意:给出字符串 $t$ 和 $s$ ,删除在 $t$ 中第一次出现的子串 $s$ ,并重复这个过程(在产生的新串 $t’$ 上继续删除操作),求最后的结果

      +

      题意:给出字符串 \(t\)\(s\) ,删除在 \(t\) 中第一次出现的子串 \(s\) ,并重复这个过程(在产生的新串 \(t’\) 上继续删除操作),求最后的结果

      匹配问题可以用KMP来解决

      怎么处理删除操作呢?

      可以观察下样例

      注意到删除子串后,在删除的子段后继续匹配可能需要该子段前的已经算出(显然)的匹配值

      那么我们可以用两个栈分别维护KMP的匹配值和答案

      -

      在每次删除后我们只要让top-=m即可,其中m为 $s$ 的长度

      -

      时间复杂度 $O(|t|+|s|)$

      +

      在每次删除后我们只要让top-=m即可,其中m为 +\(s\) 的长度

      +

      时间复杂度 \(O(|t|+|s|)\)

      代码如下

      #include <bits/stdc++.h>
       using namespace std;
      @@ -516,8 +525,10 @@ 

      printf("%s",ans+1); return 0; }

      -

      当然这个题还有个加强版

      -

      做法类似,只不过变成多模式串匹配了而已,题解

      +

      当然这个题还有个加强版

      +

      做法类似,只不过变成多模式串匹配了而已,题解

      @@ -899,7 +910,7 @@

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/01/28/luo-gu-p5357-mo-ban-ac-zi-dong-ji-er-ci-jia-qiang-ban-ti-jie/index.html b/2022/01/28/luo-gu-p5357-mo-ban-ac-zi-dong-ji-er-ci-jia-qiang-ban-ti-jie/index.html index b049a29e75..3a386e3d47 100644 --- a/2022/01/28/luo-gu-p5357-mo-ban-ac-zi-dong-ji-er-ci-jia-qiang-ban-ti-jie/index.html +++ b/2022/01/28/luo-gu-p5357-mo-ban-ac-zi-dong-ji-er-ci-jia-qiang-ban-ti-jie/index.html @@ -480,14 +480,24 @@

      洛谷P5357 【模板】AC 自
      -

      洛谷P5357 【模板】AC 自动机(二次加强版)题解

      题目链接:P5357 【模板】AC 自动机(二次加强版)

      +

      洛谷P5357 【模板】AC +自动机(二次加强版)题解

      +

      题目链接:P5357 +【模板】AC 自动机(二次加强版)

      -

      题意:$n$ 个模式串 $s_i$(不保证互异),要求输出这些模式串在文本串 $S$ 中出现的次数

      +

      题意\(n\) +个模式串 \(s_i\)不保证互异),要求输出这些模式串在文本串 +\(S\) 中出现的次数

      -

      建议大家先去做下加强版的,题解在此

      +

      建议大家先去做下加强版的,题解在此

      我们已经在加强版初步解决了次数统计的问题

      -

      可以发现本题的数据范围 $n\le2\times10^5,\sum |s_i|\le2\times10^5,|S|\le2\times10^5$

      -

      而原来算法的时间复杂度是 $O\left(|S|\left|\max\{s_i\}\right|\right)$,T飞了

      +

      可以发现本题的数据范围 \(n\le2\times10^5,\sum +|s_i|\le2\times10^5,|S|\le2\times10^5\)

      +

      而原来算法的时间复杂度是 \(O\left(|S|\left|\max\{s_i\}\right|\right)\),T飞了

      那么考虑怎么优化暴力跳fail的问题

      注意到所有fail连出的有向边构成了一个DAG(有向无环图)

      证明很简单,最长后缀一定是单调递减的

      @@ -497,14 +507,16 @@

      这个我没写代码 qwq

      解法二:拓扑排序

      我们只要在拓扑排序的过程中统计答案即可

      -

      这样我们就可以把时间复杂度压到 $O\left(\sum|s_i|+|S|\right)$ 了!

      +

      这样我们就可以把时间复杂度压到 \(O\left(\sum|s_i|+|S|\right)\) 了!

      其他注意点:

      由于可能存在相同的模式串,显然它们的出现次数相同

      那我们原来的e[u]=id就不可用了

      咋办?并查集啊!

      而在本题中较为特殊,合并产生的图一定是个菊花图

      所以不用并查集,直接用数组也可(这样常数小一点)

      -

      但是我一开始写的并查集懒地改,就这样吧 qwq 反正影响很小

      +

      但是我一开始写的并查集懒地改,就这样吧 qwq +反正影响很小

      代码如下:

      #include <bits/stdc++.h>
       using namespace std;
      @@ -958,7 +970,7 @@ 

        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/01/29/uva11475-extend-to-palindrome-ti-jie/index.html b/2022/01/29/uva11475-extend-to-palindrome-ti-jie/index.html index 5981226d0a..125b55eaa4 100644 --- a/2022/01/29/uva11475-extend-to-palindrome-ti-jie/index.html +++ b/2022/01/29/uva11475-extend-to-palindrome-ti-jie/index.html @@ -472,24 +472,34 @@

      UVA11475 Extend to Palindrome
      -

      UVA11475 Extend to Palindrome 题解

      题目链接:UVA11475 Extend to Palindrome

      +

      UVA11475 Extend to +Palindrome 题解

      +

      题目链接:UVA11475 Extend to +Palindrome

      题意

      输入多个字符串。

      -

      对于每个字符串 $S^{}$ ,求出一个字符串$S^{}$, $S^{*}$ 需要满足:

      -
        -
      1. $S^$为 $S^{}$ 的前缀;
      2. -
      3. $S^*$ 是一个回文字符串;
      4. -
      5. $|S^{*}|$应尽可能小;
      6. +

        对于每个字符串 \(S^{*}\) +,求出一个字符串\(S^{*}\)\(S^{*}\) 需要满足:

        +
          +
        1. \(S^*\)\(S^{*}\) 的前缀;
        2. +
        3. \(S^*\) 是一个回文字符串;
        4. +
        5. \(|S^{*}|\)应尽可能小;
        -

        对于每个 $S$ ,输出 $S^{*}$ ,每行输出以换行符结尾。

        +

        对于每个 \(S\) ,输出 \(S^{*}\) ,每行输出以换行符结尾。

      可以发现我们似乎要从原字符串中找到一个最长的回文串

      然后以该回文串的中心为对称轴对称过去

      amanaplanacanal要变换成amanaplanacanalpanama

      但是还有一个问题,比如下面这个例子

      axxxxxxxxxxxxxxdyyy

      -

      由于 $S$ 为 $S^{}$ 的前缀,所以我们要找的是*最长的后缀回文串

      +

      由于 \(S\)\(S^{*}\) +的前缀,所以我们要找的是最长的后缀回文串

      使用Manacher算法即可

      代码如下

      #include <bits/stdc++.h>
      @@ -893,7 +903,7 @@ 

       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/02/luo-gu-p1462-tong-wang-ao-ge-rui-ma-de-dao-lu-ti-jie/index.html b/2022/02/02/luo-gu-p1462-tong-wang-ao-ge-rui-ma-de-dao-lu-ti-jie/index.html index b325723d07..7e6642a4e7 100644 --- a/2022/02/02/luo-gu-p1462-tong-wang-ao-ge-rui-ma-de-dao-lu-ti-jie/index.html +++ b/2022/02/02/luo-gu-p1462-tong-wang-ao-ge-rui-ma-de-dao-lu-ti-jie/index.html @@ -468,21 +468,32 @@

      洛谷P1462 通往奥格瑞玛
      -

      洛谷P1462 通往奥格瑞玛的道路 题解

      题目链接:P1462 通往奥格瑞玛的道路

      +

      洛谷P1462 通往奥格瑞玛的道路 +题解

      +

      题目链接:P1462 +通往奥格瑞玛的道路

      -

      题意:在艾泽拉斯,有 $n$ 个城市。编号为 $1,2,3,\ldots,n$ 。

      -

      城市之间有 $m$ 条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。

      +

      题意:在艾泽拉斯,有 \(n\) 个城市。编号为 \(1,2,3,\ldots,n\)

      +

      城市之间有 \(m\) +条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。

      每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。

      -

      假设 $1$ 为暴风城,$n$ 为奥格瑞玛,而他的血量最多为 $b$ ,出发时他的血量是满的。

      +

      假设 \(1\) 为暴风城,\(n\) 为奥格瑞玛,而他的血量最多为 \(b\) ,出发时他的血量是满的。

      歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。

      -

      本题的数据是真的水,而且也没说清楚 $b$ 最后可不可以为 $0$ ,瞎搞的做法都能写 81pts qwq

      +

      本题的数据是真的水,而且也没说清楚 \(b\) 最后可不可以为 \(0\) ,瞎搞的做法都能写 81pts qwq

      看到最大值最小,首先可以想到二分

      事实上,由于我们不清楚血量和最小值究竟有何关系(没关系),也只能用二分

      注:二分的是这个最小值

      在二分了一个值后,我们把伤害看作边权,然后跑个最短路,找一下最少伤害的路径,判断一下能不能到达终点即可

      细节还是有点多的 qwq 懒得讲了看代码吧

      -

      Dijkstra

      +

      Dijkstra

      +

      关于SPFA,它死了

      实测(C++14 O2)SPFA 755ms,dijkstra 208ms

      @@ -571,8 +582,9 @@

      Di write(t[l]);pc('\n'); return 0; }

      -

      SPFA

      好吧我知道有写SPFA的

      -

      贴下代码吧…

      +

      SPFA

      +

      好吧我知道有写SPFA的

      +

      贴下代码吧...

      #include <bits/stdc++.h>
       using namespace std;
       #define int long long
      @@ -1009,7 +1021,7 @@ 

      SPFA


        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/03/luo-gu-p1522-usaco2.4-niu-de-lu-xing-cow-tours-ti-jie/index.html b/2022/02/03/luo-gu-p1522-usaco2.4-niu-de-lu-xing-cow-tours-ti-jie/index.html index 8f33705621..cff7d52ce6 100644 --- a/2022/02/03/luo-gu-p1522-usaco2.4-niu-de-lu-xing-cow-tours-ti-jie/index.html +++ b/2022/02/03/luo-gu-p1522-usaco2.4-niu-de-lu-xing-cow-tours-ti-jie/index.html @@ -472,21 +472,34 @@

      洛谷P1522 [USACO2.4]牛的旅
      -

      洛谷P1522 [USACO2.4]牛的旅行 Cow Tours 题解

      题目链接:P1522 [USACO2.4]牛的旅行 Cow Tours

      +

      洛谷P1522 +[USACO2.4]牛的旅行 Cow Tours 题解

      +

      题目链接:P1522 +[USACO2.4]牛的旅行 Cow Tours

      -

      题意:给定一张无向图,有至少 $2$ 个连通分量,定义直径每个连通分量中任意两结点的最短路径的最大值,其中相邻结点边权为它们的欧几里德距离,现在要求添加一条边使得连通分量的数量减少 $1$ ,并使得这个新连通分量的直径尽可能地小,求这个最小值

      +

      题意:给定一张无向图,有至少 \(2\) +个连通分量,定义直径每个连通分量中任意两结点的最短路径的最大值,其中相邻结点边权为它们的欧几里德距离,现在要求添加一条边使得连通分量的数量减少 +\(1\) +,并使得这个新连通分量的直径尽可能地小,求这个最小值

      题意已经简化过了 应该很好懂

      -

      由于 $N\le 150$ 显然我们可以用 Floyd 来求这个最短路径

      -

      任意两个连通块 $A,B$ 连边,可能出现以下三种情况

      -
        -
      1. $A$ 的直径最大
      2. -
      3. $B$ 的直径最大
      4. -
      5. $A$ 和 $B$ 相连后的新直径最大,即 $w(i,j) + d(i) + d(j)$ , 其中 $d(i)$ 指从结点 $i$ 出发的最长路径的长度
      6. +

        由于 \(N\le 150\) 显然我们可以用 +Floyd 来求这个最短路径

        +

        任意两个连通块 \(A,B\) +连边,可能出现以下三种情况

        +
          +
        1. \(A\) 的直径最大
        2. +
        3. \(B\) 的直径最大
        4. +
        5. \(A\)\(B\) 相连后的新直径最大,即 \(w(i,j) + d(i) + d(j)\) , 其中 \(d(i)\) 指从结点 \(i\) 出发的最长路径的长度

        那我们求一下就好了 qwq 就这么简单

        -

        注意这个最大值不能在Floyd的时候更新,因为那个时候还不是最短路径…

        -

        时间复杂度 $O(n^3)$

        +

        注意这个最大值不能在Floyd的时候更新,因为那个时候还不是最短路径...

        +

        时间复杂度 \(O(n^3)\)

        代码如下(无视我的垃圾卡常

        #include <bits/stdc++.h>
         using namespace std;
        @@ -908,7 +921,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/03/luo-gu-p2804-shen-mi-shu-zi-ti-jie/index.html b/2022/02/03/luo-gu-p2804-shen-mi-shu-zi-ti-jie/index.html index 4e9ebf420b..4f399395e6 100644 --- a/2022/02/03/luo-gu-p2804-shen-mi-shu-zi-ti-jie/index.html +++ b/2022/02/03/luo-gu-p2804-shen-mi-shu-zi-ti-jie/index.html @@ -472,18 +472,28 @@

        洛谷P2804 神秘数字 题解<
        -

        洛谷P2804 神秘数字 题解

        题目链接:P2804 神秘数字

        +

        洛谷P2804 神秘数字 题解

        +

        题目链接:P2804 +神秘数字

        -

        题意:询问有多少段连续区间的平均值大于 $m$

        +

        题意:询问有多少段连续区间的平均值大于 \(m\)

        -

        可以发现将每个数都减去 $m$ 后任意和大于 $0$ 的连续区间都满足题意

        -

        区间和可以用前缀和优化,记为 $s$

        -

        问题转化为了求 $s_j-s_i>0,0\le i <j$

        -

        移项可得 $s_i<s_j,0\le i <j$

        +

        可以发现将每个数都减去 \(m\) +后任意和大于 \(0\) +的连续区间都满足题意

        +

        区间和可以用前缀和优化,记为 \(s\)

        +

        问题转化为了求 \(s_j-s_i>0,0\le i +<j\)

        +

        移项可得 \(s_i<s_j,0\le i +<j\)

        问题又转化为了求顺序对(与逆序对相反)

        -

        于是可以用树状数组解决 更多解法?

        -

        注意当 $s_j>0$ 时 $i=0$ 也是一个解

        -

        时间复杂度 $O(n\log n)$

        +

        于是可以用树状数组解决 更多解法?

        +

        注意当 \(s_j>0\)\(i=0\) 也是一个解

        +

        时间复杂度 \(O(n\log n)\)

        代码如下

        #include <bits/stdc++.h>
         using namespace std;
        @@ -895,7 +905,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/03/luo-gu-p5764-cqoi2005-xin-nian-hao-ti-jie/index.html b/2022/02/03/luo-gu-p5764-cqoi2005-xin-nian-hao-ti-jie/index.html index 97a60e3630..e0477758b5 100644 --- a/2022/02/03/luo-gu-p5764-cqoi2005-xin-nian-hao-ti-jie/index.html +++ b/2022/02/03/luo-gu-p5764-cqoi2005-xin-nian-hao-ti-jie/index.html @@ -472,16 +472,24 @@

        洛谷P5764 [CQOI2005]新年好
        -

        洛谷P5764 [CQOI2005]新年好 题解

        题目链接:P5764 [CQOI2005]新年好

        +

        洛谷P5764 [CQOI2005]新年好 +题解

        +

        题目链接:P5764 +[CQOI2005]新年好

        -

        题意:从 $1$ 号结点出发,要访问其他 $5$ 个结点,顺序随意,访问一个结点后不用返回

        +

        题意:从 \(1\) +号结点出发,要访问其他 \(5\) +个结点,顺序随意,访问一个结点后不用返回

        -

        注意到 $5! = O(1)$

        -

        那我们可以在 $1$ 和其他 $5$ 个点跑一下dijkstra,然后枚举所有可能的顺序即可

        +

        注意到 \(5! = O(1)\)

        +

        那我们可以在 \(1\) 和其他 \(5\) +个点跑一下dijkstra,然后枚举所有可能的顺序即可

        这里给出了两种枚举方式,分别为dfsnext_permutation()

        实测(C++14 O2)后者快30ms左右,不过均可轻松通过此题

        我才不会说我是因为先写后者没调出来才有的这篇题解呢 QAQ

        -

        dfs

        #include <bits/stdc++.h>
        +

        dfs

        +
        #include <bits/stdc++.h>
         using namespace std;
         #define int long long
         #define INF (int)(5e10+233)
        @@ -577,7 +585,8 @@ 

        dfs

        write(ans);pc('\n');
             return 0;
         }
        -

        next_permutation()

        #include <bits/stdc++.h>
        +

        next_permutation()

        +
        #include <bits/stdc++.h>
         using namespace std;
         #define int long long
         #define INF (int)(5e10+233)
        @@ -1031,7 +1040,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/05/luo-gu-p1234-xiao-a-de-kou-tou-shan-ti-jie/index.html b/2022/02/05/luo-gu-p1234-xiao-a-de-kou-tou-shan-ti-jie/index.html index 256088e03b..7a2e5d24a0 100644 --- a/2022/02/05/luo-gu-p1234-xiao-a-de-kou-tou-shan-ti-jie/index.html +++ b/2022/02/05/luo-gu-p1234-xiao-a-de-kou-tou-shan-ti-jie/index.html @@ -468,13 +468,16 @@

        洛谷P1234 小A的口头禅 题
        -

        洛谷P1234 小A的口头禅 题解

        题目链接:P1234 小A的口头禅

        +

        洛谷P1234 小A的口头禅 题解

        +

        题目链接:P1234 +小A的口头禅

        给出了一个矩形,让你求出里面有几个hehe(方向无所谓,斜着不算)

        数据范围很良心,嗯~

        所以暴力枚举即可

        -

        值得注意的是 $\tt{eheh}$ 这种也算

        +

        值得注意的是 \(\tt{eheh}\) +这种也算

        顺便说一句,关了同步流的cin,cout还是很快的 qwq

        鬼畜的代码如下

        #include <bits/stdc++.h>
        @@ -500,7 +503,8 @@ 

        << ans; return 0; }

        -

        为什么我要写这么简单的题的题解呢?因为这题只要qwq就能过了 qwq qqqqqqwqqqqq

        +

        为什么我要写这么简单的题的题解呢?因为这题只要qwq就能过了 qwq +qqqqqqwqqqqq

        @@ -858,7 +862,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/07/at4284-luo-gu-p1969-p3078-p5019-ti-jie/index.html b/2022/02/07/at4284-luo-gu-p1969-p3078-p5019-ti-jie/index.html index 07138b5f48..38e8261452 100644 --- a/2022/02/07/at4284-luo-gu-p1969-p3078-p5019-ti-jie/index.html +++ b/2022/02/07/at4284-luo-gu-p1969-p3078-p5019-ti-jie/index.html @@ -468,14 +468,23 @@

        AT4284 & 洛谷 P1969 P3078
        -

        AT4284 & 洛谷 P1969 P3078 P5019 题解

        题目链接:AT4284 P1969 P3078 P5019

        +

        AT4284 & 洛谷 P1969 +P3078 P5019 题解

        +

        题目链接:AT4284 P1969 P3078 P5019

        题意:若干次区间减一,使所有数相等,求最小次数

        -

        这几道题就是一个std编出来的吧 $😅$

        -

        对于相邻两数 $a_i>a_{i-1}$ ,显然在一次操作中将它们同时减去是最优的

        -

        对答案的贡献为 $a_i-a_{i-1}$

        -

        推广一下其实是等价的,故时间复杂度 $O(n)$ 即可解决

        +

        这几道题就是一个std编出来的吧 \(😅\)

        +

        对于相邻两数 \(a_i>a_{i-1}\) +,显然在一次操作中将它们同时减去是最优的

        +

        对答案的贡献为 \(a_i-a_{i-1}\)

        +

        推广一下其实是等价的,故时间复杂度 \(O(n)\) 即可解决

        代码如下

        #include <bits/stdc++.h>
         using namespace std;
        @@ -852,7 +861,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/13/qian-tan-kuai-su-cheng/index.html b/2022/02/13/qian-tan-kuai-su-cheng/index.html index 22686ee1e0..ce1e78be8b 100644 --- a/2022/02/13/qian-tan-kuai-su-cheng/index.html +++ b/2022/02/13/qian-tan-kuai-su-cheng/index.html @@ -472,16 +472,21 @@

        浅谈快速乘

        -

        浅谈快速乘

        前言

        想必大家都听说过快速幂

        +

        浅谈快速乘

        +

        前言

        +

        想必大家都听说过快速幂

        那快速乘是个什么东西呢?

        考虑取模操作a*b%p1^10 ≤ a,b,p ≤ 2^10

        可以发现在 long long情况下,我们直接取模会溢出

        那么怎么办呢?

        -

        题目链接:https://www.luogu.com.cn/problem/U203580

        +

        题目链接:https://www.luogu.com.cn/problem/U203580


        - -

        快速乘

        注:本文根据q779本人的习惯,所有的int都宏定义为了long long

        -

        标准快速乘

        时间复杂度 $O(\log b)$,且正确性完备

        +

        快速乘

        +

        注:本文根据q779本人的习惯,所有的int都宏定义为了long long

        +

        标准快速乘

        +

        时间复杂度 \(O(\log +b)\),且正确性完备

        类似于快速幂,具体见代码

        #define int long long
         
        @@ -496,19 +501,22 @@ 

        return ans; }


        - -

        __int128_t

        这是个自带的数据类型,而且根据我查阅到的资料来看,有一点点不太靠谱的感觉

        -

        它能存到 $2^{128}$ 的数量级,且NOIP等赛事中是明确可以使用的

        -

        而且本机编译通过了 (g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

        +

        __int128_t

        +

        这是个自带的数据类型,而且根据我查阅到的资料来看,有一点点不太靠谱的感觉

        +

        它能存到 \(2^{128}\) +的数量级,且NOIP等赛事中是明确可以使用的

        +

        而且本机编译通过了 +(g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

        #define int long long
         
         cout << (int)((__int128_t)a*b%p) << endl;
         cout << (int)((__int128)a*b%p) << endl; // 这样写也是可以的
        -

        据说win上不能用,因此建议还是使用标准快速乘吧

        +据说win上不能用,因此建议还是使用标准快速乘吧
        - -

        O(1)快速乘

        一个正确性不太靠谱但是在这个数据范围下能算出正确答案的神奇算法

        -

        好像出自 2009年全国信息学奥林匹克冬令营论文 《论程序底层优化的一些方法与技巧》骆可强

        +

        O(1)快速乘

        +

        一个正确性不太靠谱但是在这个数据范围下能算出正确答案的神奇算法

        +

        好像出自 2009年全国信息学奥林匹克冬令营论文 +《论程序底层优化的一些方法与技巧》骆可强

        它利用了long double的范围来算的

        #define int long long
         
        @@ -520,20 +528,23 @@ 

        if(ans>=p)ans-=p; return ans; }

        -

        由于$ab-p\left\lfloor\dfrac{ab}{p}\right\rfloor$的值是“固定”的

        +

        由于\(ab-p\left\lfloor\dfrac{ab}{p}\right\rfloor\)的值是“固定”的

        即这两部分都会溢出,但long long保证了它们的差值基本不变

        因此溢出也不会影响计算

        -

        但是因为使用了除法操作,会存在精度问题,不建议在模数大于1e+12时使用(即$10^{12}$)

        +但是因为使用了除法操作,会存在精度问题,不建议在模数大于1e+12时使用(即\(10^{12}\)
        - -

        Montgomery算法

        目前还没有学会,先留个坑以后补

        +

        Montgomery算法

        +

        目前还没有学会,先留个坑以后补

        估计得到大学才会去研究吧。


        - -

        总结

        介绍了一些快速算乘法取模的算法

        +

        总结

        +

        介绍了一些快速算乘法取模的算法

        本文目前还没有彻底完工,有待更新

        参考文献

        -

        [1] 快速乘总结

        +

        [1] 快速乘总结

        @@ -895,7 +906,7 @@

        总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/24/luo-gu-p1501-guo-jia-ji-xun-dui-tree-ii-ti-jie/index.html b/2022/02/24/luo-gu-p1501-guo-jia-ji-xun-dui-tree-ii-ti-jie/index.html index d763c8f079..9f0319eabd 100644 --- a/2022/02/24/luo-gu-p1501-guo-jia-ji-xun-dui-tree-ii-ti-jie/index.html +++ b/2022/02/24/luo-gu-p1501-guo-jia-ji-xun-dui-tree-ii-ti-jie/index.html @@ -472,19 +472,25 @@

        洛谷P1501 [国家集训队]Tre
        -

        洛谷P1501 [国家集训队]Tree II 题解

        题目链接:P1501 [国家集训队]Tree II

        +

        洛谷P1501 [国家集训队]Tree II +题解

        +

        题目链接:P1501 +[国家集训队]Tree II

        题意:树上区间加&乘&link&cut

        显然LCT模板题,因为树链剖分并不能维护动态连边

        考虑如何维护区间乘

        不知道大家有没有做过洛谷的线段树2,没做过也没关系

        -

        对于每个结点,维护一个 $kx+b$ 的懒标记

        -

        每次做乘法就整体(包括两个部分)乘上 $c$ ,每次做加法就直接加上 $c$

        +

        对于每个结点,维护一个 \(kx+b\) +的懒标记

        +

        每次做乘法就整体(包括两个部分)乘上 \(c\) ,每次做加法就直接加上 \(c\)

        就可以维护标记了

        由于加法的维护需要知道这条链的长,那我们只要维护每个结点的子树大小即可

        那么这题就很容易解决了(就是函数有点多)

        -

        时间复杂度 $O(n\log n)$

        +

        时间复杂度 \(O(n\log n)\)

        #include <bits/stdc++.h>
         using namespace std;
         #define int long long
        @@ -1031,7 +1037,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/24/luo-gu-p2147-sdoi2008-dong-xue-kan-ce-ti-jie/index.html b/2022/02/24/luo-gu-p2147-sdoi2008-dong-xue-kan-ce-ti-jie/index.html index acb3aebfda..14d5040422 100644 --- a/2022/02/24/luo-gu-p2147-sdoi2008-dong-xue-kan-ce-ti-jie/index.html +++ b/2022/02/24/luo-gu-p2147-sdoi2008-dong-xue-kan-ce-ti-jie/index.html @@ -472,7 +472,10 @@

        洛谷P2147 [SDOI2008] 洞穴勘
        -

        洛谷P2147 [SDOI2008] 洞穴勘测 题解

        题目链接:P2147 [SDOI2008] 洞穴勘测

        +

        洛谷P2147 [SDOI2008] 洞穴勘测 +题解

        +

        题目链接:P2147 +[SDOI2008] 洞穴勘测

        题意:给定若干个点,动态连接(无向边),询问连通性

        @@ -958,7 +961,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/25/lct-qiu-jie-zui-xiao-sheng-cheng-shu/index.html b/2022/02/25/lct-qiu-jie-zui-xiao-sheng-cheng-shu/index.html index df2ec84602..4a5a719404 100644 --- a/2022/02/25/lct-qiu-jie-zui-xiao-sheng-cheng-shu/index.html +++ b/2022/02/25/lct-qiu-jie-zui-xiao-sheng-cheng-shu/index.html @@ -476,15 +476,21 @@

        LCT求解最小生成树

        -

        LCT求解最小生成树

        前言

        最小生成树模板: P3366 【模板】最小生成树

        +

        LCT求解最小生成树

        +

        前言

        +

        最小生成树模板: P3366 +【模板】最小生成树

        朴素的kruskal为主流最小生成树算法

        而LCT(link cut tree)也是可以维护最小生成树的

        由于LCT动态维护最小生成树,加上常数较大

        在实际测试中比kruskal慢了很多倍


        - -

        最小生成树

        维护结点的连通性 可以看看这个

        -

        显然我们不是把它和kruskal连用,那样反倒变成了 $\log^2$ 的劣解了

        +

        最小生成树

        +

        维护结点的连通性 可以看看这个

        +

        显然我们不是把它和kruskal连用,那样反倒变成了 \(\log^2\) 的劣解了

        而LCT它是动态的,因此难以使用树链剖分的做法,将边权映射到点权上

        那么我们可以把通过建“边点”将边权转化为点权

        所谓的边点指直接将每条边当做点,那么连边的操作就变成了

        @@ -1017,7 +1023,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/25/luo-gu-p2387-noi2014-mo-fa-sen-lin-ti-jie/index.html b/2022/02/25/luo-gu-p2387-noi2014-mo-fa-sen-lin-ti-jie/index.html index 3bcbe729e4..2062b8f0c4 100644 --- a/2022/02/25/luo-gu-p2387-noi2014-mo-fa-sen-lin-ti-jie/index.html +++ b/2022/02/25/luo-gu-p2387-noi2014-mo-fa-sen-lin-ti-jie/index.html @@ -476,42 +476,74 @@

        洛谷P2387 [NOI2014] 魔法森
        -

        洛谷P2387 [NOI2014] 魔法森林 题解

        题目链接:P2387 [NOI2014] 魔法森林

        +

        洛谷P2387 [NOI2014] 魔法森林 +题解

        +

        题目链接:P2387 +[NOI2014] 魔法森林

        -

        题意:每条边有边权 $a,b$ 两个,求 $1$ 到 $n$ 的路径使得所经过的边中 $\max\{a\}+\max\{b\}$ 尽可能小

        +

        题意:每条边有边权 \(a,b\) 两个,求 \(1\)\(n\) 的路径使得所经过的边中 \(\max\{a\}+\max\{b\}\) 尽可能小

        由于下面写了很多解题思路上的东西,导致有些冗长,因此这里先放简化版题解

        -

        如果不理解可以跳过这一段简化版,直接看下面的完整版

        -

        简化版

        因为不方便同时处理 $a,b$ 两维,我们可以枚举 $a$ 然后尽可能的选择较小的 $b$

        -

        这个 $b$ 的一定在连接 $1$ 和 $n$ 的最小生成树上

        +

        如果不理解可以跳过这一段简化版,直接看下面的完整版 +### 简化版 因为不方便同时处理 \(a,b\) +两维,我们可以枚举 \(a\) +然后尽可能的选择较小的 \(b\)

        +

        这个 \(b\) 的一定在连接 \(1\)\(n\) 的最小生成树上

        因此这道题就是个动态加边的LCT裸题了

        -

        代码在后面,时间复杂度 $O(m\log m)$

        +

        代码在后面,时间复杂度 \(O(m\log +m)\)


        - -

        完整版

        这种“二维”的问题常见的解法就是枚举第一维,然后优化第二维

        +

        完整版

        +

        这种“二维”的问题常见的解法就是枚举第一维,然后优化第二维

        不知道这个套路也没关系

        首先看到这个题意,最大值的和最小,显然我们无法确定这是个多大的值

        -

        似乎可以二分?但是再一想,二分 $a+b$ 肯定是不对的

        -

        那先二分 $a$ 再二分 $b$ (二分套二分)呢?

        -

        看上去似乎也不行,这个 $b$ 不是很好二分,而且复杂度似乎是 $O(n\log^3n)$ 的

        -

        那二分 $a$ 呢?好像要对 $a$ 这一维从小到大排个序,然后在 $a$ 相等时,第二维也从小到大排序

        -

        而每次我们检验 $a$ 时,都要把在 $a$ 所在位置(数组中)左边的边都去连连看有没有用什么的

        -

        那还不如直接从小到大枚举 $a$ 呢!

        +

        似乎可以二分?但是再一想,二分 \(a+b\) 肯定是不对的

        +

        那先二分 \(a\) 再二分 \(b\) (二分套二分)呢?

        +

        看上去似乎也不行,这个 \(b\) +不是很好二分,而且复杂度似乎是 \(O(n\log^3n)\)

        +

        那二分 \(a\) 呢?好像要对 \(a\) 这一维从小到大排个序,然后在 \(a\) 相等时,第二维也从小到大排序

        +

        而每次我们检验 \(a\) 时,都要把在 +\(a\) +所在位置(数组中)左边的边都去连连看有没有用什么的

        +

        那还不如直接从小到大枚举 \(a\) +呢!

        -

        于是我们可以枚举 $a$ ,然后发现如果要保证 $b$ 尽可能的小

        -

        我们需要在所有 $a’<a$ 的边中找出一棵生成树

        -

        也就是在用 $a$ 所在位置(数组中)左边的边建出的图中建最小生成森林(因为这个子图不一定连通)

        +

        于是我们可以枚举 \(a\) +,然后发现如果要保证 \(b\) +尽可能的小

        +

        我们需要在所有 \(a’<a\) +的边中找出一棵生成树

        +

        也就是在用 \(a\) +所在位置(数组中)左边的边建出的图中建最小生成森林(因为这个子图不一定连通)

        其实我们并不是在为了维护最小生成森林而维护它的

        -

        而是在维护包括结点 $1$ 和 $n$ 最小生成树的过程中所必需的 qwq

        -

        当这个包括结点 $1$ 和 $n$ 的最小生成树建出来的时候,我们就可以更新答案了

        -

        注意这里是更新答案,不是最终结果,因为最终结果它和 $a$ 的大小没有直接的关系

        -

        举个例子就知道了,a=1,b=998244353a=2,b=1,显然是后者更小

        +

        而是在维护包括结点 \(1\)\(n\) 最小生成树的过程中所必需的 qwq

        +

        当这个包括结点 \(1\)\(n\) +的最小生成树建出来的时候,我们就可以更新答案了

        +

        注意这里是更新答案,不是最终结果,因为最终结果它和 \(a\) 的大小没有直接的关系

        +

        举个例子就知道了,a=1,b=998244353和 +a=2,b=1,显然是后者更小

        那么怎么动态的维护一棵最小生成树呢?我们可以使用LCT

        -

        如果不会LCT动态维护最小生成树,可以看看这篇

        +

        如果不会LCT动态维护最小生成树,可以看看这篇

        那么,其实这道题就解决了

        -

        也就是枚举 $a$ ,动态维护最小生成树

        -

        时间复杂度 $O(m\log m)$

        +

        也就是枚举 \(a\) +,动态维护最小生成树

        +

        时间复杂度 \(O(m\log m)\)

        代码:

        #include <bits/stdc++.h>
         using namespace std;
        @@ -1045,7 +1077,7 @@ 

          站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/02/25/luo-gu-p4234-zui-xiao-chai-zhi-sheng-cheng-shu-ti-jie/index.html b/2022/02/25/luo-gu-p4234-zui-xiao-chai-zhi-sheng-cheng-shu-ti-jie/index.html index a689740916..313926eaa3 100644 --- a/2022/02/25/luo-gu-p4234-zui-xiao-chai-zhi-sheng-cheng-shu-ti-jie/index.html +++ b/2022/02/25/luo-gu-p4234-zui-xiao-chai-zhi-sheng-cheng-shu-ti-jie/index.html @@ -476,13 +476,21 @@

        洛谷P4234 最小差值生成
        -

        洛谷P4234 最小差值生成树 题解

        题目链接:P4234 最小差值生成树

        +

        洛谷P4234 最小差值生成树 +题解

        +

        题目链接:P4234 +最小差值生成树

        -

        题意:给定一个点标号从 $1$ 到 $n$ 的、有 $m$ 条边的无向图,求边权最大值与最小值的差值最小的生成树,图可能存在自环

        +

        题意:给定一个点标号从 \(1\)\(n\) 的、有 \(m\) +条边的无向图,求边权最大值与最小值的差值最小的生成树,图可能存在自环

        这个题不太好利用kruskal来维护(其实kruskal来做就是暴力枚举)

        考虑使用LCT动态维护生成树

        -

        可以先回顾一下如何用LCT来求解最小生成树 link

        +

        可以先回顾一下如何用LCT来求解最小生成树 link

        因为边之间没有什么本质区别,我们可以先把边按大小排序,以保证编号越小的边点,权值越小

        然后我们可以从小到大枚举生成树中的最大值(从大到小其实是类似的)

        那么我们只要保证生成树中边权的最小值尽可能的大即可

        @@ -492,7 +500,7 @@

        \(O(m\log m)\)

        代码:

        #include <bits/stdc++.h>
         using namespace std;
        @@ -650,7 +658,8 @@ 

        write(ans);pc('\n'); return 0; }

        -

        顺便贴一下kruskal的暴力代码吧 时间复杂度是 $O(m^2)$ 的

        +

        顺便贴一下kruskal的暴力代码吧 时间复杂度是 \(O(m^2)\)

        这道题数据水过头了所以暴力也能过(已经反馈了

        注:这个暴力是我去年(2021.2.6)写的,所以码风区别较大((

        #include<bits/stdc++.h>
        @@ -1089,7 +1098,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/03/01/cf1200e-compress-words-ti-jie/index.html b/2022/03/01/cf1200e-compress-words-ti-jie/index.html index 79c3bbc39c..f1b62fb800 100644 --- a/2022/03/01/cf1200e-compress-words-ti-jie/index.html +++ b/2022/03/01/cf1200e-compress-words-ti-jie/index.html @@ -472,11 +472,15 @@

        CF1200E Compress Words 题解
        -

        CF1200E Compress Words 题解

        题目链接:CF1200E Compress Words

        +

        CF1200E Compress Words 题解

        +

        题目链接:CF1200E +Compress Words

        -

        题意:给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀 与 待插入串的前缀的最大匹配串

        +

        题意:给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀 +与 待插入串的前缀的最大匹配串

        -

        解法一 KMP

        这个解法常数比较大

        +

        解法一 KMP

        +

        这个解法常数比较大

        可以想到,暂时把待插入串放到答案串前,然后跑一遍KMP

        最后一个值就是新串的最长前后缀,也就是我们要求的最大匹配串的长度

        由于直接拼接新串会导致某些时候答案超出原长

        @@ -484,7 +488,8 @@

        \(O(\sum |s_i|) = +O(n)\)

        代码如下

        #include <bits/stdc++.h>
         using namespace std;
        @@ -520,31 +525,52 @@ 

        << ans+1 << endl; return 0; }

        -

        解法二 字符串哈希

        这里主要扯扯字符串哈希的东西

        +

        解法二 字符串哈希

        +

        这里主要扯扯字符串哈希的东西

        虽然之前没怎么研究,一直用的unordered_map<string,int>mp

        但是这次就手写个字符串哈希吧

        -

        一般字符串哈希有两种写法,这里就按照OI WIKI默认的写法来讲了

        -

        定义哈希函数 $f(s) = \sum\limits_{i=1}^{l}s[i]\times b^{l-i} \mod M$

        -

        一般来讲我们不会只用一个大质数 $M$ ,而是一堆 qwq

        -

        因为这样它的 $n$ 次比较的错误率会降到 $1-\left(1-\dfrac{1}{\prod M_i}\right)^n$

        +

        一般字符串哈希有两种写法,这里就按照OI WIKI默认的写法来讲了

        +

        定义哈希函数 \(f(s) = +\sum\limits_{i=1}^{l}s[i]\times b^{l-i} \mod M\)

        +

        一般来讲我们不会只用一个大质数 \(M\) +,而是一堆 qwq

        +

        因为这样它的 \(n\) +次比较的错误率会降到 \(1-\left(1-\dfrac{1}{\prod +M_i}\right)^n\)

        -

        这个数有多小呢?我们假设只有两个大质数 1e9+7,998244353,比较1e6

        -

        那么它的值约为 $0.00000000000100175873$

        +

        这个数有多小呢?我们假设只有两个大质数 +1e9+7,998244353,比较1e6

        +

        那么它的值约为 \(0.00000000000100175873\)

        这错误的概率还没计算机出故障的几率高吧

        因此我们直接忽略不计

        -

        那么对于一个字符串 $\tt{xyz}$,它的值就等于 $(xb^2+yb+z) \mod M$

        -

        类比于一个 $b$ 进制的数,应该还算比较好理解的

        -

        根据这个式子,如果我们要求它的一个子串 $s[l\dots r]$ 的哈希值怎么办

        -

        注意到 $f(s[l\dots r]) = \sum\limits_{i=l}^{r}s[i]\times b^{r-i} \mod M$

        -

        因此可以得到结论 $f(s[l\dots r]) = f(s[1\dots r]))-f(s[1\dots l-1])\times b^{r-l+1} \mod M$

        -

        注意这里,由于做了取模操作,$f(s[l\dots r])$ 的值可能为负数,要注意把它转成正的

        -

        这样我们就可以 $O(n)$ 预处理 $b$ , $O(1)$ 查询了(也可以就快速幂 $O(\log n)$ 查询)

        +

        那么对于一个字符串 \(\tt{xyz}\),它的值就等于 \((xb^2+yb+z) \mod M\)

        +

        类比于一个 \(b\) +进制的数,应该还算比较好理解的

        +

        根据这个式子,如果我们要求它的一个子串 \(s[l\dots r]\) 的哈希值怎么办

        +

        注意到 \(f(s[l\dots r]) = +\sum\limits_{i=l}^{r}s[i]\times b^{r-i} \mod M\)

        +

        因此可以得到结论 \(f(s[l\dots r]) = +f(s[1\dots r]))-f(s[1\dots l-1])\times b^{r-l+1} \mod M\)

        +

        注意这里,由于做了取模操作,\(f(s[l\dots +r])\) 的值可能为负数,要注意把它转成正的

        +

        这样我们就可以 \(O(n)\) 预处理 \(b\)\(O(1)\) 查询了(也可以就快速幂 \(O(\log n)\) 查询)

        好的相信你已经理解了字符串哈希的一些基本东西,下面来做个简单的小例题

        好吧就是这题,看它题意

        很简单,直接暴力枚举+哈希就OK了,

        -

        而朴素的解法,语句 str1==str2 ,它实质上是 $O(n)$ 级别的

        -

        因此我们把朴素解法的 $O(n^2)$ 就可以压到 $O(n)$ 了

        +

        而朴素的解法,语句 str1==str2 ,它实质上是 \(O(n)\) 级别的

        +

        因此我们把朴素解法的 \(O(n^2)\) +就可以压到 \(O(n)\)

        代码如下

        #include <bits/stdc++.h>
         using namespace std;
        @@ -999,7 +1025,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/03/01/luo-gu-p4357-cqoi2016-k-yuan-dian-dui-ti-jie/index.html b/2022/03/01/luo-gu-p4357-cqoi2016-k-yuan-dian-dui-ti-jie/index.html index 945c2645ae..79f7953db9 100644 --- a/2022/03/01/luo-gu-p4357-cqoi2016-k-yuan-dian-dui-ti-jie/index.html +++ b/2022/03/01/luo-gu-p4357-cqoi2016-k-yuan-dian-dui-ti-jie/index.html @@ -476,9 +476,14 @@

        洛谷P4357 [CQOI2016]K 远点
        -

        洛谷P4357 [CQOI2016]K 远点对 题解

        题目链接:P4357 [CQOI2016]K 远点对

        +

        洛谷P4357 [CQOI2016]K 远点对 +题解

        +

        题目链接:P4357 +[CQOI2016]K 远点对

        -

        题意:给定平面内 $n$ 个点的坐标,求欧几里德距离第 $k$ 远的点对

        +

        题意:给定平面内 \(n\) 个点的坐标,求欧几里德距离第 \(k\) 远的点对

        本题的正解应该是旋转卡壳、分治等算法,不会

        而kd-tree上搜索在本题中相当于搜索+天然剪枝

        @@ -486,12 +491,19 @@

        \(k\) +大值怎么去维护呢?

        +

        我们可以弄一个有 \(k\) +个结点的小根堆,这些结点默认为一个极小值(本题中设为 \(0\) 就可以了)

        +

        这样在堆首的就是第 \(k\) 大值了

        这样我们只要枚举每个点,分别搜它们所对应的点,统计它们的贡献

        -

        容易发现,因为是无序的点,因此这些点之间的距离会被计算 $2$ 次,那我们只要把 $k$ 变成 $2k$ 就好了

        -

        时间复杂度 比较玄学,可以分析的是建树 $O(n\log n)$

        +

        容易发现,因为是无序的点,因此这些点之间的距离会被计算 \(2\) 次,那我们只要把 \(k\) 变成 \(2k\) 就好了

        +

        时间复杂度 比较玄学,可以分析的是建树 \(O(n\log n)\)

        代码如下

        #include <bits/stdc++.h>
         using namespace std;
        @@ -977,7 +989,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/03/02/kd-tree-kdt-shi-jian-fu-za-du-zheng-ming/index.html b/2022/03/02/kd-tree-kdt-shi-jian-fu-za-du-zheng-ming/index.html index e44773fc37..8f8615b51e 100644 --- a/2022/03/02/kd-tree-kdt-shi-jian-fu-za-du-zheng-ming/index.html +++ b/2022/03/02/kd-tree-kdt-shi-jian-fu-za-du-zheng-ming/index.html @@ -468,58 +468,90 @@

        kd-tree(KDT) 时间复杂度证
        -

        kd-tree(KDT) 时间复杂度证明

        kd-tree 是一种可以高效处理 $k$ 维空间的数据结构

        -

        在算法竞赛类的题目中一般有 $k=2$

        -

        还有个比较有趣的结论,当 $k=1$ 时其实它就是一棵线段树

        -

        下文中的 $n$ 为kd-tree中的结点数量, $k$ 为kd-tree的维度

        +

        kd-tree(KDT) 时间复杂度证明

        +

        kd-tree 是一种可以高效处理 \(k\) +维空间的数据结构

        +

        在算法竞赛类的题目中一般有 \(k=2\)

        +

        还有个比较有趣的结论,当 \(k=1\) +时其实它就是一棵线段树

        +

        下文中的 \(n\) +为kd-tree中的结点数量, \(k\) +为kd-tree的维度


        - -

        一、建树

        一般建树有三种

        -
          +

          一、建树

          +

          一般建树有三种

          +
          1. 随机划分(玄学)
          2. 轮换划分:每个维度轮着划分
          3. 方差划分:优先划分方差较大的维度

          我可不想分析玄学的划分

          -

          1. 轮换划分

          显然有递推式 $T(n) = 2T(n/2) + O(n)$

          -

          根据主定理,有 $a=2,b=2,\log_b{a}=1,f(n)=O(n)$

          -

          $\because \exist \epsilon \ge 0$ 使得 $f(n) = \Theta(n^{\log_b{a}}\log^{\epsilon}n)$ ,此时 $\epsilon = 0$

          -

          $\therefore T(n) = \Theta(n\log n)$

          -

          2. 方差划分

          注意到 $T(n) = 2T(n/2) + O(kn)$

          -

          因此时间复杂度为 $T(n) = \Theta(nk\log n)$

          -

          一般在算法竞赛中,由于 $k$ 很小(一般 $k=2$ ),因此有

          -

          $T(n) = \Theta(n\log n)$

          +

          1. 轮换划分

          +

          显然有递推式 \(T(n) = 2T(n/2) + +O(n)\)

          +

          根据主定理,有 \(a=2,b=2,\log_b{a}=1,f(n)=O(n)\)

          +

          \(\because \exist \epsilon \ge 0\) +使得 \(f(n) = +\Theta(n^{\log_b{a}}\log^{\epsilon}n)\) ,此时 \(\epsilon = 0\)

          +

          \(\therefore T(n) = \Theta(n\log +n)\)

          +

          2. 方差划分

          +

          注意到 \(T(n) = 2T(n/2) + +O(kn)\)

          +

          因此时间复杂度为 \(T(n) = \Theta(nk\log +n)\)

          +

          一般在算法竞赛中,由于 \(k\) +很小(一般 \(k=2\) ),因此有

          +

          \(T(n) = \Theta(n\log n)\)

          本划分方法能较好保证树高,且不易被卡


          - -

          二、插入&删除

          一般采用替罪羊树的插入&删除操作

          +

          二、插入&删除

          +

          一般采用替罪羊树的插入&删除操作

          利用重构子树的方式维持平衡

          -

          均摊复杂度为 $O(\log n)$

          +

          均摊复杂度为 \(O(\log n)\)

          证明可以去看替罪羊树的,这里先留个坑以后补上


          - -

          三、Range Query

          个人感觉算kd-tree的核心操作吧

          -

          支持将一个超长方体区域内的点划分为 $O(\sqrt{n})$ 个点所管辖的区域

          -

          称 $R$ 为待查询的超长方体,则对于树上结点所管辖的超长方体分类,存在以下三种情况

          -
            -
          1. 与 $R$ 的交集为空
          2. -
          3. 全部包含于 $R$ 内

            -
          4. -
          5. 与 $R$ 有交集且不包含于 $R$

            -
          6. +

            三、Range Query

            +

            个人感觉算kd-tree的核心操作吧

            +

            支持将一个超长方体区域内的点划分为 \(O(\sqrt{n})\) 个点所管辖的区域

            +

            \(R\) +为待查询的超长方体,则对于树上结点所管辖的超长方体分类,存在以下三种情况

            +
              +
            1. \(R\) 的交集为空

            2. +
            3. 全部包含于 \(R\)

            4. +
            5. \(R\) 有交集且不包含于 \(R\)

            算法在查询过程中,碰到第1,2类点不会继续递归其子树

            因此算法的时间复杂度就与第3类点的数量有关

            -

            我们以轮换划分来分析,则在相邻的 $k$ 轮中,分别对每一维进行了划分

            -

            显然会产生 $2^k$ 个部分,每个部分的大小均为原来的 $\dfrac{1}{2^k}$

            -

            由于一个用于划分的超平面至多跨越 $2^{k-1}$ 个部分(可以由归纳法证明,此处略)

            -

            则有 $T(n) = 2^{k-1}T(n/2^k)+O(1)$

            -

            根据主定理,有 $a=2^{k-1},b=2^k,\log_b{a} = \dfrac{k-1}{k},f(n)=O(1)$

            -

            $\because \exist \epsilon > 0$ 使得 $f(n) = O(n^{\log_b{a}-\epsilon})$ ,此时 $\epsilon = \dfrac{k-1}{k}$

            -

            $\therefore T(n) = \Theta\left(n^{1-\frac{1}{k}}\right)$

            -

            当 $k=2$ 时, $T(n) = \Theta(\sqrt{n})$

            -



            参考文献

            -

            [1] KDT小记

            +

            我们以轮换划分来分析,则在相邻的 \(k\) 轮中,分别对每一维进行了划分

            +

            显然会产生 \(2^k\) +个部分,每个部分的大小均为原来的 \(\dfrac{1}{2^k}\)

            +

            由于一个用于划分的超平面至多跨越 \(2^{k-1}\) +个部分(可以由归纳法证明,此处略)

            +

            则有 \(T(n) = +2^{k-1}T(n/2^k)+O(1)\)

            +

            根据主定理,有 \(a=2^{k-1},b=2^k,\log_b{a} += \dfrac{k-1}{k},f(n)=O(1)\)

            +

            \(\because \exist \epsilon > 0\) +使得 \(f(n) = +O(n^{\log_b{a}-\epsilon})\) ,此时 \(\epsilon = \dfrac{k-1}{k}\)

            +

            \(\therefore T(n) = +\Theta\left(n^{1-\frac{1}{k}}\right)\)

            +

            \(k=2\) 时, \(T(n) = \Theta(\sqrt{n})\)

            +
            +

            参考文献

            +

            [1] KDT小记

        @@ -881,7 +913,7 @@

          站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/03/12/luo-gu-p1129-zjoi2007-ju-zhen-you-xi-ti-jie/index.html b/2022/03/12/luo-gu-p1129-zjoi2007-ju-zhen-you-xi-ti-jie/index.html index 91aaebe311..2fb9d9831e 100644 --- a/2022/03/12/luo-gu-p1129-zjoi2007-ju-zhen-you-xi-ti-jie/index.html +++ b/2022/03/12/luo-gu-p1129-zjoi2007-ju-zhen-you-xi-ti-jie/index.html @@ -468,18 +468,28 @@

        洛谷P1129 [ZJOI2007] 矩阵游
        -

        洛谷P1129 [ZJOI2007] 矩阵游戏 题解

        题目链接:P1129 [ZJOI2007] 矩阵游戏

        +

        洛谷P1129 [ZJOI2007] 矩阵游戏 +题解

        +

        题目链接:P1129 +[ZJOI2007] 矩阵游戏

        题意:给定一张有黑白棋子的正方形棋盘,问存不存在解法使得经过若干次交换行或列的操作后,左上角至右下角的对角线上所有的点放着黑色棋子

        说实话第一眼看到这个题我连暴力都不知道咋打

        这道题的建模挺有意思的

        -

        对于 $\forall i \in \mathbb{Z}, 1\le i \le n$ ,要求第 $i$ 行第 $i$ 列为黑色

        -

        不妨可以看作行结点 $i$ 与列结点 $i$ 之间有一条无向边(显然没有权重)

        -

        则交换行 $u,v$ 的操作,就可以看作重命名的操作

        -

        问题就可以转化为行结点集 $V_a$ 与列结点集 $V_b$ 求二分图最大匹配

        -

        当最大匹配值为 $n$ 时,存在解

        -

        时间复杂度 $O(Qn^2)$ ,$Q$ 为数据的组数

        +

        对于 \(\forall i \in \mathbb{Z}, 1\le i \le +n\) ,要求第 \(i\) 行第 \(i\) 列为黑色

        +

        不妨可以看作行结点 \(i\) 与列结点 +\(i\) +之间有一条无向边(显然没有权重)

        +

        则交换行 \(u,v\) +的操作,就可以看作重命名的操作

        +

        问题就可以转化为行结点集 \(V_a\) +与列结点集 \(V_b\) 求二分图最大匹配

        +

        当最大匹配值为 \(n\) 时,存在解

        +

        时间复杂度 \(O(Qn^2)\)\(Q\) 为数据的组数

        代码如下(忽略那个快读,其实没有什么大用处 qwq)

        #include <bits/stdc++.h>
         using namespace std;
        @@ -900,7 +910,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/03/14/luo-gu-p3919-mo-ban-ke-chi-jiu-hua-xian-duan-shu-1-ke-chi-jiu-hua-shu-zu-ti-jie/index.html b/2022/03/14/luo-gu-p3919-mo-ban-ke-chi-jiu-hua-xian-duan-shu-1-ke-chi-jiu-hua-shu-zu-ti-jie/index.html index 414a10b88e..0c89624791 100644 --- a/2022/03/14/luo-gu-p3919-mo-ban-ke-chi-jiu-hua-xian-duan-shu-1-ke-chi-jiu-hua-shu-zu-ti-jie/index.html +++ b/2022/03/14/luo-gu-p3919-mo-ban-ke-chi-jiu-hua-xian-duan-shu-1-ke-chi-jiu-hua-shu-zu-ti-jie/index.html @@ -472,18 +472,23 @@

        洛谷P3919 【模板】可持
        -

        洛谷P3919 【模板】可持久化线段树 1(可持久化数组) 题解

        题目链接:P3919 【模板】可持久化线段树 1(可持久化数组)

        +

        洛谷P3919 +【模板】可持久化线段树 1(可持久化数组) 题解

        +

        题目链接:P3919 +【模板】可持久化线段树 1(可持久化数组)

        -

        题意:如题,你需要维护这样的一个长度为 NN 的数组,支持如下几种操作

        -
          +

          题意:如题,你需要维护这样的一个长度为 NN +的数组,支持如下几种操作

          +
          1. 在某个历史版本上修改某一个位置上的值
          2. 访问某个历史版本上的某一位置的值

          此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)

        -

        解法一、主席树

        注意到历史版本+单点修改,容易想到可持久化线段树

        -

        和区间 $k$ 小值稍微有些不同

        -

        时间复杂度 $O(n\log n)$

        +

        解法一、主席树

        +

        注意到历史版本+单点修改,容易想到可持久化线段树

        +

        和区间 \(k\) 小值稍微有些不同

        +

        时间复杂度 \(O(n\log n)\)

        本题的数据有点极限,我的写法开 long long会挂 😓

        代码如下

        #include <bits/stdc++.h>
        @@ -573,17 +578,18 @@ 

        } return 0; }

        -

        解法二、建树

        这个解法是luogu题解区的Elegia神仙提出的

        +

        解法二、建树

        +

        这个解法是luogu题解区的Elegia神仙提出的

        具体地,注意到每次修改都依赖于先前的版本

        考虑建立关系树,离线处理修改操作

        对于每个询问,直接记录答案,对于每个修改,则临时修改,然后dfs完恢复

        不难看出这个解法可以看出有两个前提

        -
          +
          1. 不强制在线
          2. 修改操作可逆

          显然这题满足

          -

          时间复杂度 $O(n)$

          +

          时间复杂度 \(O(n)\)

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -1006,7 +1012,7 @@ 

            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/03/26/luo-gu-p5826-mo-ban-zi-xu-lie-zi-dong-ji-ti-jie/index.html b/2022/03/26/luo-gu-p5826-mo-ban-zi-xu-lie-zi-dong-ji-ti-jie/index.html index 5ba6860f93..554b5dfcf7 100644 --- a/2022/03/26/luo-gu-p5826-mo-ban-zi-xu-lie-zi-dong-ji-ti-jie/index.html +++ b/2022/03/26/luo-gu-p5826-mo-ban-zi-xu-lie-zi-dong-ji-ti-jie/index.html @@ -468,7 +468,9 @@

          洛谷P5826 【模板】子序
          -

          洛谷P5826 【模板】子序列自动机

          题目链接:P5826 【模板】子序列自动机

          +

          洛谷P5826 【模板】子序列自动机

          +

          题目链接:P5826 +【模板】子序列自动机

          题意:给定一个主序列,每次给出一个序列,判断是否为其子序列

          @@ -477,7 +479,8 @@

          \(O(|S| + +\sum|s_i|\log|S|)\)

          代码如下(稍微卡了卡常)

          #include <bits/stdc++.h>
           using namespace std;
          @@ -893,7 +896,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/03/27/zui-xiao-shu-xing-tu-tarjan-de-dmst-suan-fa/index.html b/2022/03/27/zui-xiao-shu-xing-tu-tarjan-de-dmst-suan-fa/index.html index 13596f01d2..edeacc96c7 100644 --- a/2022/03/27/zui-xiao-shu-xing-tu-tarjan-de-dmst-suan-fa/index.html +++ b/2022/03/27/zui-xiao-shu-xing-tu-tarjan-de-dmst-suan-fa/index.html @@ -472,30 +472,50 @@

          最小树形图 Tarjan的DMST算
          -

          最小树形图 Tarjan的DMST算法

          前言

          网上怎么都是朱刘算法啊?

          -

          那我来写一篇 TarjanDMST 算法吧 qwq

          +

          最小树形图 Tarjan的DMST算法

          +

          前言

          +

          网上怎么都是朱刘算法啊?

          +

          那我来写一篇 TarjanDMST 算法吧 +qwq

          注:本文的DMST采用左偏树+并查集实现

          -

          时间复杂度为 $O(E+V\log E)$

          -

          如果采用斐波那契堆则为 $O(E+V\log V)$

          +

          时间复杂度为 \(O(E+V\log E)\)

          +

          如果采用斐波那契堆则为 \(O(E+V\log +V)\)

          由于差别不大(其实是q779不会斐波那契堆),因此本文不会提及

          如果需要模板题数据的可以私信我( qwq

          -


          -

          最小树形图

          模板题:P4716 【模板】最小树形图

          +
          +

          最小树形图

          +

          模板题:P4716 +【模板】最小树形图

          题目描述

          -

          给定包含 $n$ 个结点, $m$ 条有向边的一个图。试求一棵以结点 $r$ 为根的最小树形图,并输出最小树形图每条边的权值之和,如果没有以 $r$ 为根的最小树形图,输出 $-1$。

          +

          给定包含 \(n\) 个结点, \(m\) 条有向边的一个图。试求一棵以结点 \(r\) +为根的最小树形图,并输出最小树形图每条边的权值之和,如果没有以 \(r\) 为根的最小树形图,输出 \(-1\)

          -

          1.朱刘算法(Edmonds算法)

          有向图上的最小生成树(Directed Minimum Spanning Tree)称为最小树形图。

          -

          常用的算法是朱刘算法(也称 Edmonds 算法),可以在 $O(nm)$ 时间内解决最小树形图问题。

          +

          1.朱刘算法(Edmonds算法)

          +

          有向图上的最小生成树(Directed Minimum Spanning +Tree)称为最小树形图。

          +

          常用的算法是朱刘算法(也称 Edmonds 算法),可以在 \(O(nm)\) 时间内解决最小树形图问题。

          注意到有根最小树形图满足从根结点可以到达任意结点的性质

          算法流程:

          -
            -
          1. 求出图中的最短弧集合 $E_0$
          2. -
          3. 若 $E_0$ 中存在环,则对图进行缩点和重构,包括边权的修改和点的处理,转到步骤1
          4. -
          5. 若 $E_0$ 中不存在环,则展开收缩点,即求得最小树形图,算法结束。
          6. +
              +
            1. 求出图中的最短弧集合 \(E_0\)
            2. +
            3. \(E_0\) +中存在环,则对图进行缩点和重构,包括边权的修改和点的处理,转到步骤1
            4. +
            5. \(E_0\) +中不存在环,则展开收缩点,即求得最小树形图,算法结束。
            -

            对于环上结点 $v$ 的入边,因为选择了一条这样的边相当于删除了一条环边

            -

            所以将边权设为 $w-\text{ine[v]}$,其中 $\text{ine[v]}$ 表示结点 $v$ 的最短弧(权值最小的入边)

            +

            对于环上结点 \(v\) +的入边,因为选择了一条这样的边相当于删除了一条环边

            +

            所以将边权设为 \(w-\text{ine[v]}\),其中 \(\text{ine[v]}\) 表示结点 \(v\) 的最短弧(权值最小的入边)

            本文不过多叙述朱刘算法,仅给出代码

            #include <bits/stdc++.h>
             using namespace std;
            @@ -601,18 +621,22 @@ 

            return 0; }

            可以发现这就是个暴力 qwq

            -


            -

            2.Tarjan的DMST算法(Tarjan的优化算法)

            观察朱刘算法的实现

            +
            +

            2.Tarjan的DMST算法(Tarjan的优化算法)

            +

            观察朱刘算法的实现

            算法流程:

            -
              -
            1. 求出图中的最短弧集合 $E_0$
            2. -
            3. 若 $E_0$ 中存在环,则对图进行缩点和重构,包括边权的修改和点的处理,转到步骤1
            4. -
            5. 若 $E_0$ 中不存在环,则展开收缩点,即求得最小树形图,算法结束。
            6. +
                +
              1. 求出图中的最短弧集合 \(E_0\)
              2. +
              3. \(E_0\) +中存在环,则对图进行缩点和重构,包括边权的修改和点的处理,转到步骤1
              4. +
              5. \(E_0\) +中不存在环,则展开收缩点,即求得最小树形图,算法结束。

            可以发现这是三种操作

            -
              +
              1. 查询最小值
              2. 整体减去一个数
              3. 合并若干集合
              4. @@ -621,32 +645,48 @@

                \(n\) 条边权为 \(+\infty\) 就可以让它强连通了

                形象地描述,如果我们最后“迫不得已”选择了这些额外的边,则原图不存在最小树形图

                本质上该算法就是不断将最短弧加入当前的最小树形图

                当树形图中出现环时将环进行缩点处理

                算法实现:

                -

                我们可以维护一个栈,每次将栈顶元素 $v$ 的最短弧的起点 $u$ 入栈

                -

                如果 $u$ 已经在栈中,则出现了环,将环进行缩点即可

                -

                注意如果计算答案时存在根节点 $r$ 的入边,显然不可计入答案

                -

                引理:下文代码中,建树时间复杂度为 $O(m)$

                +

                我们可以维护一个栈,每次将栈顶元素 \(v\) 的最短弧的起点 \(u\) 入栈

                +

                如果 \(u\) +已经在栈中,则出现了环,将环进行缩点即可

                +

                注意如果计算答案时存在根节点 \(r\) +的入边,显然不可计入答案

                +

                引理:下文代码中,建树时间复杂度为 \(O(m)\)

                证明:

                -

                对于结点 $k$ ,设其共有 $d_k$ 条入边的

                -

                对于第 $i$ 轮合并,需合并 $\dfrac{d_k}{2^i}$ 次,且该轮合并左偏树上各有 $i$ 个结点

                -

                则复杂度为

                -

                $\because \sum d_k = m$

                -

                $\therefore$ 总复杂度为

                -

                证毕 。

                +

                对于结点 \(k\) ,设其共有 \(d_k\) 条入边的

                +

                对于第 \(i\) 轮合并,需合并 \(\dfrac{d_k}{2^i}\) +次,且该轮合并左偏树上各有 \(i\) +个结点

                +

                则复杂度为 \[ +O\left(\sum\limits_{i=1}^{\log_2 d_k}{\dfrac{d_k}{2^i}\times +\left(2\log_2{i} + 1\right)}\right) = O(d_k) +\] \(\because \sum d_k = m\)

                +

                \(\therefore\) 总复杂度为 \[ +O\left(\sum_{d_k}\sum\limits_{i=1}^{\log_2 d_k}{\dfrac{d_k}{2^i}\times +\left(2\log_2{i} + 1\right)}\right) = O(m) +\] 证毕 。

                -

                时间复杂度为 $O(m+n\log m)$

                +

                时间复杂度为 \(O(m+n\log m)\)

                证明:

                -

                由引理可知,建树复杂度为 $O(m)$

                -

                因为每个结点至多入栈一次,每次查询最短弧复杂度为 $O(\log m)$

                -

                则总复杂度为 $O(m+n\log m)$

                +

                由引理可知,建树复杂度为 \(O(m)\)

                +

                因为每个结点至多入栈一次,每次查询最短弧复杂度为 \(O(\log m)\)

                +

                则总复杂度为 \(O(m+n\log m)\)

                证毕。

                代码如下

                @@ -798,11 +838,14 @@

                write(ans);pc('\n'); return 0; }

          -


          -

          3.加强版模板题

          这里有一道我写的模板题

          +
          +

          3.加强版模板题

          +

          这里有一道我写的模板题

          可以卡掉暴力朱刘算法

          大家可以在这里调试代码

          -

          题目链接U210116 【模板】最小树形图(加强版)

          +

          题目链接U210116 +【模板】最小树形图(加强版)

          这里是std

          #include <bits/stdc++.h>
           using namespace std;
          @@ -962,12 +1005,18 @@ 

          // outfile.close(); return 0; }

          -


          -

          总结

          本文简单介绍了Tarjan的DMST算法及左偏树实现方法

          -



          参考文献

          -

          [1] OI Wiki 最小树形图

          -

          [2] CHiCO的博客 题解 P4716

          -

          [3] 左偏树简介

          +
          +

          总结

          +

          本文简单介绍了Tarjan的DMST算法及左偏树实现方法

          +
          +

          参考文献

          +

          [1] OI Wiki +最小树形图

          +

          [2] CHiCO的博客 +题解 P4716

          +

          [3] 左偏树简介

          @@ -1329,7 +1378,7 @@

          总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/03/28/dijkstra-ji-qi-fu-za-du-zheng-ming/index.html b/2022/03/28/dijkstra-ji-qi-fu-za-du-zheng-ming/index.html index 39d39bc6c9..1994aa9f70 100644 --- a/2022/03/28/dijkstra-ji-qi-fu-za-du-zheng-ming/index.html +++ b/2022/03/28/dijkstra-ji-qi-fu-za-du-zheng-ming/index.html @@ -472,11 +472,14 @@

          Dijkstra及其复杂度证明
          -

          Dijkstra及其复杂度证明

          前言

          本文主要围绕易混淆的复杂度分析进行讨论

          +

          Dijkstra及其复杂度证明

          +

          前言

          +

          本文主要围绕易混淆的复杂度分析进行讨论


          - -

          Dijkstra

          其实这个不叫迪杰斯特拉,这个叫/ˈdɛɪkstra/ qwq

          -

          一、小概念

          先放几个简单概念

          +

          Dijkstra

          +

          其实这个不叫迪杰斯特拉,这个叫/ˈdɛɪkstra/ qwq

          +

          一、小概念

          +

          先放几个简单概念

          无向图:图中所有的边都是两端可达的,也就是可以从任意一端通过这条边

          有向图:图中所有的边都是单向的,有一个起点和一个终点,方向为起点指向终点

          重边:两个结点间多条存在完全一样的边,通常它们可能拥有不同的权重或属性

          @@ -484,35 +487,53 @@

          这里 ,这里只介绍了一些对本文有用的概念

          +

          更多内容可以参考这里 +,这里只介绍了一些对本文有用的概念


          - -

          二、时间复杂度分析

          给定一张具有非负权重的图和一个源点 $s$ (起点),问结点 $t$ 与 $s$ 的最短距离是多少

          -

          设图中的结点数为 $n$ ,边数为 $m$ ,$n\le m$

          +

          二、时间复杂度分析

          +

          给定一张具有非负权重的图和一个源点 \(s\) (起点),问结点 \(t\)\(s\) 的最短距离是多少

          +

          设图中的结点数为 \(n\) ,边数为 +\(m\)\(n\le m\)

          算法流程:

          -
            -
          1. $\text{dis[s]=0} \land \forall u \in V\backslash S , \text{dis[}u\text{]}=+\infty$

            -
          2. -
          3. 从未确定最短路的点集 $T$ 中找到一个结点满足其最短路长度最小

            -
          4. -
          5. 将已确定最短路的点集 $S$ 的所有出边进行松弛操作
          6. -
          7. 如果 $T\ne \varnothing$ ,重复1,2操作
          8. +
              +
            1. \(\text{dis[s]=0} \land \forall u \in +V\backslash S , \text{dis[}u\text{]}=+\infty\)

            2. +
            3. 从未确定最短路的点集 \(T\) +中找到一个结点满足其最短路长度最小

            4. +
            5. 将已确定最短路的点集 \(S\) +的所有出边进行松弛操作

            6. +
            7. 如果 \(T\ne \varnothing\) +,重复1,2操作

            -

            1. 朴素dijkstra

            操作1复杂度为 $O(n)$ ,操作2复杂度为 $O(n)$ ,每条都会被松弛一次

            -

            因此总时间复杂度为 $O(m+n^2)$

            -

            2.优先队列优化

            显然操作1可以优化

            +

            1. 朴素dijkstra

            +

            操作1复杂度为 \(O(n)\) +,操作2复杂度为 \(O(n)\) +,每条都会被松弛一次

            +

            因此总时间复杂度为 \(O(m+n^2)\)

            +

            2.优先队列优化

            +

            显然操作1可以优化

            考虑一般多重图,使用优先队列优化存在弊端

            -

            对于队首的元素,至多遍历 $d_i$ 条边 ,$\sum d_i = m$

            -

            注意到一次操作2中,同一结点可能入队多次,则时间复杂度为 $O(m)$

            +

            对于队首的元素,至多遍历 \(d_i\) +条边 ,\(\sum d_i = m\)

            +

            注意到一次操作2中,同一结点可能入队多次,则时间复杂度为 \(O(m)\)

            假设按某种固定顺序遍历出边,每次松弛操作都会使被松弛结点入队

            -

            因为存在重边,考虑构造重边以权重大小降序排列,即 $w(e_{k-1})\ge w(e_k),k\in\mathbb{Z}\land k\in[1,d_i]$

            -

            这样最坏可以做到 $O(m)$

            +

            因为存在重边,考虑构造重边以权重大小降序排列,即 \(w(e_{k-1})\ge w(e_k),k\in\mathbb{Z}\land +k\in[1,d_i]\)

            +

            这样最坏可以做到 \(O(m)\)

            -

            因此找最短路最小的结点时间为 $O(\log m)$

            +

            因此找最短路最小的结点时间为 \(O(\log +m)\)

            每个结点至少被遍历一次

            -

            则总时间复杂度为 $O\left((n+m)\log m\right)$

            +

            则总时间复杂度为 \(O\left((n+m)\log +m\right)\)

            优先队列优化的代码

            priority_queue<node> q;
             void dijkstra(int st)
            @@ -536,16 +557,22 @@ 

            } } }

            -

            3.二叉堆优化

            对于堆顶的元素,至多遍历 $d_i$ 条边 ,$\sum d_i = m$

            +

            3.二叉堆优化

            +

            对于堆顶的元素,至多遍历 \(d_i\) +条边 ,\(\sum d_i = m\)

            由于二叉堆支持修改,我们每次遇到重复入队的情况只要修改就好了

            -

            因此找最短路最小的结点时间为 $O(\log n)$

            +

            因此找最短路最小的结点时间为 \(O(\log +n)\)

            每个结点至少被遍历一次

            -

            则总时间复杂度为 $O\left((n+m)\log n\right)$

            -

            4.斐波那契堆优化

            原理同二叉堆,这个更快但是更难写,一般不使用

            -

            理论时间复杂度 $O(m + n\log n)$

            +

            则总时间复杂度为 \(O\left((n+m)\log +n\right)\)

            +

            4.斐波那契堆优化

            +

            原理同二叉堆,这个更快但是更难写,一般不使用

            +

            理论时间复杂度 \(O(m + n\log +n)\)


            - -

            总结

            本文简单讨论了一下复杂度的问题

            +

            总结

            +

            本文简单讨论了一下复杂度的问题

          @@ -911,7 +938,7 @@

          总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/04/17/luo-gu-p3299-sdoi2013-bao-hu-chu-ti-ren-ti-jie/index.html b/2022/04/17/luo-gu-p3299-sdoi2013-bao-hu-chu-ti-ren-ti-jie/index.html index 96e695a88f..ece8efe977 100644 --- a/2022/04/17/luo-gu-p3299-sdoi2013-bao-hu-chu-ti-ren-ti-jie/index.html +++ b/2022/04/17/luo-gu-p3299-sdoi2013-bao-hu-chu-ti-ren-ti-jie/index.html @@ -472,31 +472,61 @@

          洛谷P3299 [SDOI2013]保护出
          -

          洛谷P3299 [SDOI2013]保护出题人 题解

          题目链接:P3299 [SDOI2013]保护出题人

          +

          洛谷P3299 +[SDOI2013]保护出题人 题解

          +

          题目链接:P3299 +[SDOI2013]保护出题人

          题意:出题人铭铭认为给SDOI2012出题太可怕了,因为总要被骂,于是他又给SDOI2013出题了。

          参加SDOI2012的小朋友们释放出大量的僵尸,企图攻击铭铭的家。而你作为SDOI2013的参赛者,你需要保护出题人铭铭。

          僵尸从唯一一条笔直道路接近,你们需要在铭铭的房门前放置植物攻击僵尸,避免僵尸碰到房子。

          -

          第一关,一只血量为 $a_1$ 点的墦尸从距离房子 $x_1$ 米处速接近,你们放置了攻击力为 $y_1$ 点/秒的植物进行防御;第二关,在上一关基础上,僵尸队列排头增加一只血量为 $a_2$ 点的僵尸,与后一只僵尸距离 $d$ 米,从距离房 $x_2$ 米处匀速接近,你们重新放置攻击力为 $y_2$ 点/秒的植物;……;第 $n$ 关,僵尸队列共有 $n$ 只僵尸,相邻两只僵尸距离 $d$ 米,排头僵尸血量 $a_n$ 点,排第二的 僵尸血量 $a_{n-1}$ ,以此类推,排头僵尸从距离房子 $x_n$ 米处匀速接近,其余僵尸跟随排头同时接近,你们重新放置攻击力为 $y_n$ 点/秒的植物。

          -

          每只僵尸直线移动速度均为 $1$ 米/秒,由于植物射击速度远大于僵尸移动速度,可忽略植物子弹在空中的时间。所有僵尸同时出现并接近,因此当一只僵尸死亡后,下一只僵尸立刻开始受到植物子弹的伤害。

          -

          游戏得分取决于你们放置的植物攻击力的总和 $\sum \limits _{i=1} ^{n} y_i$,和越小分数越高,为了追求分数上界,你们每关都要放置攻击力尽量小的植物。

          +

          第一关,一只血量为 \(a_1\) +点的墦尸从距离房子 \(x_1\) +米处速接近,你们放置了攻击力为 \(y_1\) +点/秒的植物进行防御;第二关,在上一关基础上,僵尸队列排头增加一只血量为 +\(a_2\) 点的僵尸,与后一只僵尸距离 +\(d\) 米,从距离房 \(x_2\) 米处匀速接近,你们重新放置攻击力为 +\(y_2\) 点/秒的植物;……;第 \(n\) 关,僵尸队列共有 \(n\) 只僵尸,相邻两只僵尸距离 \(d\) 米,排头僵尸血量 \(a_n\) 点,排第二的 僵尸血量 \(a_{n-1}\) ,以此类推,排头僵尸从距离房子 +\(x_n\) +米处匀速接近,其余僵尸跟随排头同时接近,你们重新放置攻击力为 \(y_n\) 点/秒的植物。

          +

          每只僵尸直线移动速度均为 \(1\) +米/秒,由于植物射击速度远大于僵尸移动速度,可忽略植物子弹在空中的时间。所有僵尸同时出现并接近,因此当一只僵尸死亡后,下一只僵尸立刻开始受到植物子弹的伤害。

          +

          游戏得分取决于你们放置的植物攻击力的总和 \(\sum \limits _{i=1} ^{n} +y_i\),和越小分数越高,为了追求分数上界,你们每关都要放置攻击力尽量小的植物。

          作为SDOI2013的参赛选手,你们能保护出题人么?

          -

          对于第 $i$ 轮的第 $j$ 只僵尸,打死它的充要条件为它前面的僵尸全部被打死

          -

          它需要走过的距离为 $x_i+d\times (i-j+1)$ ,则最小的攻击为

          -

          则第 $i$ 轮进攻的最小攻击

          -

          其中,$S_i = \sum_{k=1}^{i} a_k$

          -

          稍微观察一下式子,可以发现

          -

          即点 $(x_i+d\times i,i)$ 和 $(d\times(j-1),j-1)$ 的直线斜率

          -

          而前者在仅与 $i$ 有关,可以看作定点;后者则与 $i$ 无关,仅与每只僵尸有关

          -

          考虑对 $(d\times(j-1),j-1)$ 维护一个下凸壳

          +

          对于第 \(i\) 轮的第 \(j\) +只僵尸,打死它的充要条件为它前面的僵尸全部被打死

          +

          它需要走过的距离为 \(x_i+d\times +(i-j+1)\) ,则最小的攻击为 \[ +\dfrac{\sum_{k=j}^{i}a_k}{x_i+d\times(i-j+1)} +\] 则第 \(i\) 轮进攻的最小攻击 +\[ +y_i = \max\left(\dfrac{S_i-S_{j-1}}{x_i+d\times(i-j+1)}\right) +\] 其中,\(S_i = \sum_{k=1}^{i} +a_k\)

          +

          稍微观察一下式子,可以发现 \[ +y_i = \max\left(\dfrac{S_i-S_{j-1}}{(x_i+d\times i)-d\times(j-1)}\right) +\] 即点 \((x_i+d\times i,i)\) 和 +\((d\times(j-1),j-1)\) 的直线斜率

          +

          而前者在仅与 \(i\) +有关,可以看作定点;后者则与 \(i\) +无关,仅与每只僵尸有关

          +

          考虑对 \((d\times(j-1),j-1)\) +维护一个下凸壳

          每一轮二分一个点使得其和定点所成直线斜率最大

          把这些斜率加起来就是答案了

          注意本题是四舍五入,而不是向下取整

          -

          时间复杂度 $O(n\log n)$

          +

          时间复杂度 \(O(n\log n)\)

          代码如下

          #include <bits/stdc++.h>
           using namespace std;
          @@ -920,7 +950,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/04/19/luo-gu-p2491-sdoi2011-xiao-fang-ti-jie/index.html b/2022/04/19/luo-gu-p2491-sdoi2011-xiao-fang-ti-jie/index.html index ac45f6b5b3..daa740a2f8 100644 --- a/2022/04/19/luo-gu-p2491-sdoi2011-xiao-fang-ti-jie/index.html +++ b/2022/04/19/luo-gu-p2491-sdoi2011-xiao-fang-ti-jie/index.html @@ -476,17 +476,27 @@

          洛谷P2491 [SDOI2011] 消防
          -

          洛谷P2491 [SDOI2011] 消防 题解

          题目链接:P2491 [SDOI2011] 消防

          +

          洛谷P2491 [SDOI2011] 消防 +题解

          +

          题目链接:P2491 +[SDOI2011] 消防

          -

          题意:某个国家有 $n$ 个城市,这 $n$ 个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为 $z_i$ 。

          +

          题意:某个国家有 \(n\) 个城市,这 \(n\) +个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为 +\(z_i\)

          这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的行业是消防业。由于政府对国民的热情忍无可忍(大量的消防经费开销)可是却又无可奈何(总统竞选的国民支持率),所以只能想尽方法提高消防能力。

          -

          现在这个国家的经费足以在一条边长度和不超过 $s$ 的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。

          +

          现在这个国家的经费足以在一条边长度和不超过 \(s\) +的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。

          你受命监管这个项目,你当然需要知道应该把枢纽建立在什么位置上。

          看到这是一棵树,而且还要求路径的最大值

          很容易想到这个枢纽要建在直径上

          -

          当 $s$ 大于等于直径时显然成立

          -

          考虑 $s$ 小于直径的情况,枢纽全部建在直径上仍是最优解

          +

          \(s\) 大于等于直径时显然成立

          +

          考虑 \(s\) +小于直径的情况,枢纽全部建在直径上仍是最优解

          以下为证明:

          假设只有一个枢纽结点(在直径上),我们以它为中心扩展出一条路径

          离这个点最远的结点一定在直径上,而且是直径的端点

          @@ -495,21 +505,25 @@

          \(u\) 作为枢纽的起点进行扩展

          +

          找到满足条件且最远的 \(v\) +,并对每组 \((u,v)\) +计算出直径的两个端点到枢纽的最大距离

          +

          再计算出非直径的结点到枢纽的最大距离即可,时间复杂度 \(O(n^2)\)

          注:因为两端到枢纽的距离不一定是最大值,可以参考下图

          -

          +

          那么怎么优化呢

          注意到选择的枢纽长度随结点数量的增加单调递增

          因此我们可以考虑使用尺取法

          这样,算法的流程就变成了

          -
            +
            1. 找出直径
            2. 对于每个极大枢纽,统计两端到其距离的最大值的最小值
            3. 统计非直径结点到枢纽距离的最大值
            -

            时间复杂度 $O(n)$

            +

            时间复杂度 \(O(n)\)

            代码如下

            #include <bits/stdc++.h>
             using namespace std;
            @@ -942,7 +956,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/04/22/luo-gu-p3236-hnoi2014-hua-kuang-ti-jie/index.html b/2022/04/22/luo-gu-p3236-hnoi2014-hua-kuang-ti-jie/index.html index 72600c612e..ead559e423 100644 --- a/2022/04/22/luo-gu-p3236-hnoi2014-hua-kuang-ti-jie/index.html +++ b/2022/04/22/luo-gu-p3236-hnoi2014-hua-kuang-ti-jie/index.html @@ -484,21 +484,38 @@

            洛谷P3236 [HNOI2014]画框 题
            -

            洛谷P3236 [HNOI2014]画框 题解

            题目链接:P3236 [HNOI2014]画框

            +

            洛谷P3236 [HNOI2014]画框 题解

            +

            题目链接:P3236 +[HNOI2014]画框

            -

            题意:小 T 准备在家里摆放几幅画,为此他买来了 $N$ 幅画和 $N$ 个画框。为了体现他的品味,小 T 希望能合理地搭配画与画框,使得其显得既不过于平庸也不太违和。

            -

            对于第 $i$ 幅画与第 $j$ 个画框的配对,小 T 都给出了这个配对的平凡度 $A_{i, j}$ 与违和度 $B_{i, j}$ 。整个搭配方案的总体不和谐度为每对画与画框平凡度之和与每对画与画框违和度的乘积。具体来说,设搭配方案中第 $i$ 幅画与第 $P_i$ 个画框配对,则总体不和谐度为

            -

            小 T 希望知道通过搭配能得到的最小的总体不和谐度是多少。

            +

            题意:小 T 准备在家里摆放几幅画,为此他买来了 \(N\) 幅画和 \(N\) 个画框。为了体现他的品味,小 T +希望能合理地搭配画与画框,使得其显得既不过于平庸也不太违和。

            +

            对于第 \(i\) 幅画与第 \(j\) 个画框的配对,小 T +都给出了这个配对的平凡度 \(A_{i, j}\) +与违和度 \(B_{i, j}\) +。整个搭配方案的总体不和谐度为每对画与画框平凡度之和与每对画与画框违和度的乘积。具体来说,设搭配方案中第 +\(i\) 幅画与第 \(P_i\) 个画框配对,则总体不和谐度为 \[ +\mathrm{disharmony}=\sum_{i=1}^{N}A_{i,p_i}\times +\sum_{i=1}^{N}B_{i,p_i} +\] 小 T 希望知道通过搭配能得到的最小的总体不和谐度是多少。

            -

            q779在水文章$🐒$

            -

            这道题和P5540 [BalkanOI2011] timeismoney | 最小乘积生成树 几乎就是一道题

            -

            可以先看下这篇题解

            +

            q779在水文章\(🐒\)

            +

            这道题和P5540 +[BalkanOI2011] timeismoney | 最小乘积生成树 几乎就是一道题

            +

            可以先看下这篇题解

            变化不是很大,基本上就是改一下邻接矩阵啥的

            只不过算法换成了KM求完全二分图最大权完美匹配

            -

            时间复杂度大概在 $O(kn^4)$ 左右,$k$ 为一个小常数

            +

            时间复杂度大概在 \(O(kn^4)\) +左右,\(k\) 为一个小常数

            多测不清空,爆零两行泪!!!!

            -

            说句题外话,dfs版本好像是 $O(n^4)$ 的,建议不要写

            +

            说句题外话,dfs版本好像是 \(O(n^4)\) +的,建议不要写

            代码如下

            #include <bits/stdc++.h>
             using namespace std;
            @@ -1012,7 +1029,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/04/22/luo-gu-p5540-balkanoi2011-timeismoney-zui-xiao-cheng-ji-sheng-cheng-shu-ti-jie/index.html b/2022/04/22/luo-gu-p5540-balkanoi2011-timeismoney-zui-xiao-cheng-ji-sheng-cheng-shu-ti-jie/index.html index d2be12f505..88e08ec9e0 100644 --- a/2022/04/22/luo-gu-p5540-balkanoi2011-timeismoney-zui-xiao-cheng-ji-sheng-cheng-shu-ti-jie/index.html +++ b/2022/04/22/luo-gu-p5540-balkanoi2011-timeismoney-zui-xiao-cheng-ji-sheng-cheng-shu-ti-jie/index.html @@ -476,41 +476,78 @@

            洛谷P5540 [BalkanOI2011] timei
            -

            洛谷P5540 [BalkanOI2011] timeismoney | 最小乘积生成树 题解

            题目链接:P5540 [BalkanOI2011] timeismoney | 最小乘积生成树

            +

            洛谷P5540 +[BalkanOI2011] timeismoney | 最小乘积生成树 题解

            +

            题目链接:P5540 +[BalkanOI2011] timeismoney | 最小乘积生成树

            -

            题意:给出一个 $n$ 个点 $m$ 条边的无向图,第 $i$ 条边有两个权值 $a_i$ 和 $b_i$ 。

            -

            求该图的一棵生成树 $T$ ,使得

            -

            最小

            +

            题意:给出一个 \(n\) 个点 \(m\) 条边的无向图,第 \(i\) 条边有两个权值 \(a_i\)\(b_i\)

            +

            求该图的一棵生成树 \(T\) ,使得 +\[ +\left(\sum_{e\in T}a_e\right)\times\left(\sum_{e\in T}b_e\right) +\]

            +

            最小

            注意到对于一棵生成树,式子中的左右两边均为常数

            -

            考虑将生成树映射到平面上的点 $(x,y)$

            -

            其中 $x=\sum\limits_{e\in T}a_e,y=\sum\limits_{e\in T}b_e$

            -

            那么问题就转化为了求一个点 $(x,y)$ 使得 $x\times y$ 最小

            +

            考虑将生成树映射到平面上的点 \((x,y)\)

            +

            其中 \(x=\sum\limits_{e\in +T}a_e,y=\sum\limits_{e\in T}b_e\)

            +

            那么问题就转化为了求一个点 \((x,y)\) +使得 \(x\times y\) 最小

            可以发现这些点的分布存在边界

            -

            即 $x$ 特别大 $y$ 特别小的时候以及 $y$ 特别大 $x$ 特别小的时候

            -

            这两种情况分别对应点 $A$ 和 $B$

            -

            则有 $A$ 为距离 $x$ 轴最近的点, $B$ 为距离 $y$ 轴最近的点

            -

            对于直线 $AB$ ,显然在 $AB$ 上方的不是优解

            -

            根据反比例函数的性质,可以发现这个解一定在以 $A,B$ 为端点的下凸包上

            +

            \(x\) 特别大 \(y\) 特别小的时候以及 \(y\) 特别大 \(x\) 特别小的时候

            +

            这两种情况分别对应点 \(A\)\(B\)

            +

            则有 \(A\) 为距离 \(x\) 轴最近的点, \(B\) 为距离 \(y\) 轴最近的点

            +

            对于直线 \(AB\) ,显然在 \(AB\) 上方的不是优解

            +

            根据反比例函数的性质,可以发现这个解一定在以 \(A,B\) 为端点的下凸包上

            由于我们不可能枚举出所有的生成树然后求二维凸包

            考虑递归求解,不知道大家知不知道割圆术,不知道也没关系

            这个解法有点像割圆术

            -

            首先找出距离直线 $AB$ 最远的点 $C$ (在 $AB$ 下方)

            -

            然后递归 $AC$ 和 $CB$ ,即可求出解

            -

            如何寻找 $C$ 点呢?可以发现此时 $S_{\triangle ABC}$ 取到了最大值

            -

            而 $S_{\triangle ABC} = -\dfrac{1}{2}\overset{\longrightarrow}{AB}\times \overset{\longrightarrow}{AC}$

            -

            (注:这里写的不是很规范,其实应该除以一个单位向量 $\boldsymbol{z}$ 的,不重要不管了

            -

            推柿子环节 qwq

            -

            可以发现后面两项是定值

            -

            因此我们将边权设为 $(x_B-x_A)b_i+(y_A-y_B)a_i$ ,然后跑最小生成树即可

            -

            由于 $n$ 很小,且凸包上的期望点数分析极其复杂

            -

            本题复杂度可以近似看作 $O(km\log m)$,其中 $k$ 为一个小常数,据说是 $\sqrt{\ln n}$ ,不太清楚

            +\overset{\longrightarrow}{AB}\times \overset{\longrightarrow}{AC} +&= (x_B-x_A)(y_C-y_A)-(y_B-y_A)(x_C-x_A)\\ +&=(x_B-x_A)\times y_C + (y_A-y_B)\times x_C + y_Bx_A-x_By_A +\end{aligned} +\] 可以发现后面两项是定值

            +

            因此我们将边权设为 \((x_B-x_A)b_i+(y_A-y_B)a_i\) +,然后跑最小生成树即可

            +

            由于 \(n\) +很小,且凸包上的期望点数分析极其复杂

            +

            本题复杂度可以近似看作 \(O(km\log +m)\),其中 \(k\) +为一个小常数,据说是 \(\sqrt{\ln n}\) +,不太清楚

            代码如下

            #include <bits/stdc++.h>
             using namespace std;
            @@ -993,7 +1030,7 @@ 

              站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/04/23/luo-gu-p3194-hnoi2008-shui-ping-ke-jian-zhi-xian-ti-jie/index.html b/2022/04/23/luo-gu-p3194-hnoi2008-shui-ping-ke-jian-zhi-xian-ti-jie/index.html index 557bc6f504..066700bc6a 100644 --- a/2022/04/23/luo-gu-p3194-hnoi2008-shui-ping-ke-jian-zhi-xian-ti-jie/index.html +++ b/2022/04/23/luo-gu-p3194-hnoi2008-shui-ping-ke-jian-zhi-xian-ti-jie/index.html @@ -476,22 +476,44 @@

            洛谷P3194 [HNOI2008]水平可
            -

            洛谷P3194 [HNOI2008]水平可见直线 题解

            题目链接:P3194 [HNOI2008]水平可见直线

            +

            洛谷P3194 +[HNOI2008]水平可见直线 题解

            +

            题目链接:P3194 +[HNOI2008]水平可见直线

            -

            题意:在$ x-y$ 直角坐标平面上有 $n$ 条直线 $L_1,L_2,…L_n$,若在 $y$ 值为正无穷大处往下看,能见到 $L_i$ 的某个子线段,则称 $L_i$ 为可见的,否则 $L_i$ 为被覆盖的。 例如,对于直线: $L_1:y=x$; $L_2:y=-x$; $L_3:y=0$; 则 $L_1$ 和 $L_2$ 是可见的,$L_3$ 是被覆盖的。给出 $n$ 条直线,表示成 $y=Ax+B$ 的形式($|A|,|B| \le 500000$),且 $n$ 条直线两两不重合,求出所有可见的直线。

            +

            题意:在$ x-y$ 直角坐标平面上有 \(n\) 条直线 \(L_1,L_2,…L_n\),若在 \(y\) 值为正无穷大处往下看,能见到 \(L_i\) 的某个子线段,则称 \(L_i\) 为可见的,否则 \(L_i\) 为被覆盖的。 例如,对于直线: \(L_1:y=x\); \(L_2:y=-x\); \(L_3:y=0\); 则 \(L_1\)\(L_2\) 是可见的,\(L_3\) 是被覆盖的。给出 \(n\) 条直线,表示成 \(y=Ax+B\) 的形式(\(|A|,|B| \le 500000\)),且 \(n\) +条直线两两不重合,求出所有可见的直线。

            一条直线C被另外两条直线A和B所覆盖

            它的充分必要条件为直线C在A,B交点处的值更小一些

            反之,直线C也可能对答案有贡献

            -

            那么交点的坐标是什么呢?

            -

            -

            可以发现这个式子很像斜率

            -

            如果记 $P_i(A_i,B_i)$

            -

            那么答案就是点集 $\{P_i\}$ 构成的上凸壳

            +x=\dfrac{B_2-B_1}{A_1-A_2} +\] 则 \[ +A_1\dfrac{B_2-B_1}{A_1-A_2} + B_1 \ge A_3\dfrac{B_2-B_1}{A_1-A_2} + +B_3\\ +\color{red}{\dfrac{B_2-B_1}{A_2-A_1}\ge\dfrac{B_3-B_1}{A_3-A_1}} +\] 可以发现这个式子很像斜率

            +

            如果记 \(P_i(A_i,B_i)\)

            +

            那么答案就是点集 \(\{P_i\}\) +构成的上凸壳

            为什么是上凸壳?显然我们选定的是斜率大且尽可能截距大

            代码如下

            #include <bits/stdc++.h>
            @@ -923,7 +945,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/04/25/luo-gu-p3829-shoi2012-xin-yong-qia-tu-bao-ti-jie/index.html b/2022/04/25/luo-gu-p3829-shoi2012-xin-yong-qia-tu-bao-ti-jie/index.html index f655cea619..3f4c048fe9 100644 --- a/2022/04/25/luo-gu-p3829-shoi2012-xin-yong-qia-tu-bao-ti-jie/index.html +++ b/2022/04/25/luo-gu-p3829-shoi2012-xin-yong-qia-tu-bao-ti-jie/index.html @@ -472,20 +472,33 @@

            洛谷P3829 [SHOI2012]信用卡
            -

            洛谷P3829 [SHOI2012]信用卡凸包 题解

            题目链接:P3829 [SHOI2012]信用卡凸包

            +

            洛谷P3829 +[SHOI2012]信用卡凸包 题解

            +

            题目链接:P3829 +[SHOI2012]信用卡凸包

            题意: 给定若干个“信用卡”,求其“凸包”周长

            -

            imgimg

            +

            这个题其实看上去很不可做,其实很简单

            -

            注意到(搬了一张图,来自link,不过这篇题解的代码好像挂了)

            -

            在这里插入图片描述

            +

            注意到(搬了一张图,来自link,不过这篇题解的代码好像挂了)

            +
            + + +

            显然,由图可知,我们求的所谓凸包

            就是信用卡上面那四个点组成的点集的凸包加上一个圆的面积

            关于如何理解这个图形的周长恰好包括一个圆

            大家就想象一只小乌龟从一点开始走,走了一大圈又回到了起点

            -

            它旋转的角度正好为 $2\pi$

            +

            它旋转的角度正好为 \(2\pi\)

            那么这道题就变成数学题了,如何处理这4个点?

            再看图,可以发现这个圆对求凸包真的一点用都没有

            @@ -938,7 +951,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/04/25/luo-gu-p4159-scoi2009-mi-lu-ti-jie/index.html b/2022/04/25/luo-gu-p4159-scoi2009-mi-lu-ti-jie/index.html index 77dacae3a2..fa1b8e4390 100644 --- a/2022/04/25/luo-gu-p4159-scoi2009-mi-lu-ti-jie/index.html +++ b/2022/04/25/luo-gu-p4159-scoi2009-mi-lu-ti-jie/index.html @@ -472,35 +472,51 @@

            洛谷P4159 [SCOI2009] 迷路
            -

            洛谷P4159 [SCOI2009] 迷路 题解

            题目链接:P4159 [SCOI2009] 迷路

            +

            洛谷P4159 [SCOI2009] 迷路 +题解

            +

            题目链接:P4159 +[SCOI2009] 迷路

            -

            题意:该有向图有 $n$ 个节点,节点从 $1$ 至 $n$ 编号,windy 从节点 $1$ 出发,他必须恰好在 $t$ 时刻到达节点 $n$。

            +

            题意:该有向图有 \(n\) 个节点,节点从 \(1\)\(n\) 编号,windy 从节点 \(1\) 出发,他必须恰好在 \(t\) 时刻到达节点 \(n\)

            现在给出该有向图,你能告诉 windy 总共有多少种不同的路径吗?

            -

            答案对 $2009$ 取模。

            -

            注意:windy 不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

            +

            答案对 \(2009\) 取模。

            +

            注意:windy +不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

            题目给出了邻接矩阵

            首先,根据矩阵乘法的性质,我们可以发现

            不考虑题目的限制,当矩阵仅由01构成时

            -

            $M^2$ 的意义就是只通过两条边(此时边权均为 $1$ )能到达每个结点的方案数

            -

            则此时答案就是 $M^t$

            -

            可是题目给出的矩阵,至多有 $10$

            +

            \(M^2\) +的意义就是只通过两条边(此时边权均为 \(1\) )能到达每个结点的方案数

            +

            则此时答案就是 \(M^t\)

            +

            可是题目给出的矩阵,至多有 \(10\)

            怎么办呢?考虑把它转化新的矩阵(仅有01构成的)

            -

            对于初始矩阵的每个节点,把它化为 $x_i$ 个结点

            -

            其中 $x_i$ 表示出边边权的最大值

            +

            对于初始矩阵的每个节点,把它化为 \(x_i\) 个结点

            +

            其中 \(x_i\) +表示出边边权的最大值

            如下图

            在这里插入图片描述

            “拆点”操作

            在这里插入图片描述

            可以发现这样的话仍然是满足题意的

            为什么呢?因为可以更新答案的那条路径

            -

            一定走到了 $x_i$ 号节点,正准备走到原来的另一个节点

            +

            一定走到了 \(x_i\) +号节点,正准备走到原来的另一个节点

            不是很好解释,自己多画几张图试试就知道了(我知道你们懒,给你们画一个

            在这里插入图片描述

            “拆点”操作

            在这里插入图片描述

            那么这题就很简单了

            -

            时间复杂度 $O(n^3\log t)$

            +

            时间复杂度 \(O(n^3\log t)\)

            代码如下

            #include <bits/stdc++.h>
             using namespace std;
            @@ -940,7 +956,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/04/27/uva12307-smallest-enclosing-rectangle-ti-jie/index.html b/2022/04/27/uva12307-smallest-enclosing-rectangle-ti-jie/index.html index 843fd4dad8..15a242950f 100644 --- a/2022/04/27/uva12307-smallest-enclosing-rectangle-ti-jie/index.html +++ b/2022/04/27/uva12307-smallest-enclosing-rectangle-ti-jie/index.html @@ -476,32 +476,57 @@

            UVA12307 Smallest Enclosing Rect
            -

            UVA12307 Smallest Enclosing Rectangle 题解

            upd.20220429 由于q779太菜,导致代码出了点锅,已经重新修改

            -

            题目链接:UVA12307 Smallest Enclosing Rectangle

            +

            UVA12307 Smallest +Enclosing Rectangle 题解

            +

            upd.20220429 +由于q779太菜,导致代码出了点锅,已经重新修改

            +

            题目链接:UVA12307 Smallest +Enclosing Rectangle

            -

            题意:多组数据,给定平面上的 $n$ 个点,分别求出周长最小、面积最小的矩形,使得所有的点均被矩形覆盖

            -

            数据范围: $3\le n \le 10^5$ ,$-10^5 \le x,y \le 10^5$

            +

            题意:多组数据,给定平面上的 \(n\) +个点,分别求出周长最小、面积最小的矩形,使得所有的点均被矩形覆盖

            +

            数据范围: \(3\le n \le 10^5\) +,\(-10^5 \le x,y \le 10^5\)

            -

            看到本题,容易想到 P3187

            +

            看到本题,容易想到 P3187

            解法是旋转卡壳

            要求的是矩形,因此考虑维护三个点:

            -

            $a$ 代表当前枚举直线对面的边

            -

            $b$ 代表当前枚举直线右侧最远的点

            -

            $c$ 代表当前枚举直线左侧最远的点

            +

            \(a\) 代表当前枚举直线对面的边

            +

            \(b\) +代表当前枚举直线右侧最远的点

            +

            \(c\) +代表当前枚举直线左侧最远的点

            如下图所示

            -

            在这里插入图片描述

            -

            其中,$a$ 使用叉积比较,$b,c$ 则使用点积比较

            -

            方便起见,我们称当前的底边长为 $\text{T}$ (即图中的q[1]到q[2])

            -

            可以发现 $\text{T = L}+\text{R}-\text{d}$

            -

            $\text{L}$ 表示向量 $s_i\to c$ 在 $\text{T}$ 上投影向量的模长

            -

            $\text{R}$ 表示向量 $s_{i-1} \to b$ 在 $\text{T}$ 上投影向量的模长

            -

            $\text{d}$ 表示向量 $s_{i-1}\to s_i$ 的模长(即图中的绿色段)

            +
            + + +
            +

            其中,\(a\) 使用叉积比较,\(b,c\) 则使用点积比较

            +

            方便起见,我们称当前的底边长为 \(\text{T}\) (即图中的q[1]到q[2])

            +

            可以发现 \(\text{T = +L}+\text{R}-\text{d}\)

            +

            \(\text{L}\) 表示向量 \(s_i\to c\)\(\text{T}\) 上投影向量的模长

            +

            \(\text{R}\) 表示向量 \(s_{i-1} \to b\)\(\text{T}\) 上投影向量的模长

            +

            \(\text{d}\) 表示向量 \(s_{i-1}\to s_i\) +的模长(即图中的绿色段)

            那么面积就可以计算出来了

            关于周长的计算

            通过向量旋转和数乘运算来求出矩形的四个顶点坐标

            然后直接用两点距离公式计算即可

            因为周长和面积不一定同时最小,所以只要两个都分别fmin一下就好了

            -

            时间复杂度 $O(n\log n)$

            +

            时间复杂度 \(O(n\log n)\)

            代码如下

            #include <bits/stdc++.h>
             using namespace std;
            @@ -605,7 +630,9 @@ 

            return 0; }

            友情提醒:多测不清空,爆零两行泪!

            -

            部分参考了 这篇博客 的写法,但是他写的那个挂了,会出现除以0的情况

            +

            部分参考了 这篇博客 +的写法,但是他写的那个挂了,会出现除以0的情况

            代码确实蛮难写的,这道题我研究了一整天才搞出来 QAQ

            @@ -979,7 +1006,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/04/28/ban-ping-mian-jiao-de-jiao-ji-wen-ti-poj2451-uyuw-s-concert-ti-jie/index.html b/2022/04/28/ban-ping-mian-jiao-de-jiao-ji-wen-ti-poj2451-uyuw-s-concert-ti-jie/index.html index 934a7b6ff5..9d71823d86 100644 --- a/2022/04/28/ban-ping-mian-jiao-de-jiao-ji-wen-ti-poj2451-uyuw-s-concert-ti-jie/index.html +++ b/2022/04/28/ban-ping-mian-jiao-de-jiao-ji-wen-ti-poj2451-uyuw-s-concert-ti-jie/index.html @@ -476,20 +476,47 @@

            半平面交的交集问题&
            -

            半平面交的交集问题&POJ2451 Uyuw’s Concert 题解

            前言

            初学半平面交,就遇到了这样巨大的坑,也是挺无语…

            +

            半平面交的交集问题&POJ2451 +Uyuw's Concert 题解

            +

            前言

            +

            初学半平面交,就遇到了这样巨大的坑,也是挺无语...


            - -

            判断交集为空的情况

            poj2451 Uyuw’s Concert

            +

            判断交集为空的情况

            +

            poj2451 Uyuw's +Concert

            Description

            -

            Prince Remmarguts solved the CHESS puzzle successfully. As an award, Uyuw planned to hold a concert in a huge piazza named after its great designer Ihsnayish.

            -

            The piazza in UDF - United Delta of Freedom’s downtown was a square of [0, 10000] * [0, 10000]. Some basket chairs had been standing there for years, but in a terrible mess. Look at the following graph.

            In this case we have three chairs, and the audiences face the direction as what arrows have pointed out. The chairs were old-aged and too heavy to be moved. Princess Remmarguts told the piazza’s current owner Mr. UW, to build a large stage inside it. The stage must be as large as possible, but he should also make sure the audience in every position of every chair would be able to see the stage without turning aside (that means the stage is in the forward direction of their own).

            -

            To make it simple, the stage could be set highly enough to make sure even thousands of chairs were in front of you, as long as you were facing the stage, you would be able to see the singer / pianist – Uyuw.

            -

            Being a mad idolater, can you tell them the maximal size of the stage?

            +

            Prince Remmarguts solved the CHESS puzzle successfully. As an award, +Uyuw planned to hold a concert in a huge piazza named after its great +designer Ihsnayish.

            +

            The piazza in UDF - United Delta of Freedom’s downtown was a square +of [0, 10000] * [0, 10000]. Some basket chairs had been standing there +for years, but in a terrible mess. Look at the following graph. +In this case we have three chairs, and the audiences face the direction +as what arrows have pointed out. The chairs were old-aged and too heavy +to be moved. Princess Remmarguts told the piazza's current owner Mr. UW, +to build a large stage inside it. The stage must be as large as +possible, but he should also make sure the audience in every position of +every chair would be able to see the stage without turning aside (that +means the stage is in the forward direction of their own).

            +

            To make it simple, the stage could be set highly enough to make sure +even thousands of chairs were in front of you, as long as you were +facing the stage, you would be able to see the singer / pianist – +Uyuw.

            +

            Being a mad idolater, can you tell them the maximal size of the +stage?

            Input

            -

            In the first line, there’s a single non-negative integer N (N <= 20000), denoting the number of basket chairs. Each of the following lines contains four floating numbers x1, y1, x2, y2, which means there’s a basket chair on the line segment of (x1, y1) – (x2, y2), and facing to its LEFT (That a point (x, y) is at the LEFT side of this segment means that (x – x1) (y – y2) – (x – x2) (y – y1) >= 0).

            +

            In the first line, there's a single non-negative integer N (N <= +20000), denoting the number of basket chairs. Each of the following +lines contains four floating numbers x1, y1, x2, y2, which means there’s +a basket chair on the line segment of (x1, y1) – (x2, y2), and facing to +its LEFT (That a point (x, y) is at the LEFT side of this segment means +that (x – x1) * (y – y2) – (x – x2) * (y – y1) >= 0).

            Output

            -

            Output a single floating number, rounded to 1 digit after the decimal point. This is the maximal area of the stage.

            +

            Output a single floating number, rounded to 1 digit after the decimal +point. This is the maximal area of the stage.

            Sample Input

            3
             10000 10000 0 5000
            @@ -498,13 +525,18 @@ 

            Sample Output

            54166666.7

            Hint

            -

            Sample input is the same as the graph above, while the correct solution for it is as below:

            -


            I suggest that you use Extended in pascal and long double in C / C++ to avoid precision error. But the standard program only uses double.

            +

            Sample input is the same as the graph above, while the correct +solution for it is as below:

            +

            +I suggest that you use Extended in pascal and long double in C / C++ to +avoid precision error. But the standard program only uses double.

            显然是半平面交的板子题

            这里我们不讲半平面交,我们只讲一些特例

            下面有几个比较特殊的情况

            -

            1.向量共线

            hack1:(存在向量相反且共线,且交集不是给出向量围成的多边形)

            +

            1.向量共线

            +

            hack1:(存在向量相反且共线,且交集不是给出向量围成的多边形)

            input:
             3
             9 8 9 0
            @@ -524,16 +556,21 @@ 

            +
          1. 如果极角大于 \(0\) 小于 \(\pi\) ,就取较左的那个
          2. +
          3. 如果极角小于 \(0\) 大于 \(-\pi\) ,就取较右的那个
          4. +
          5. 如果极角等于 \(0\) +就取较上的那个
          6. +
          7. 如果极角等于 \(\pi\) +就取较下的那个

          这么一听好复杂哇,其实只要一个叉积判断下就好了

          预处理一下这些烦人的共线向量,就可以放心halfplane()

          具体看后面的代码,不急(逃

          -

          2.存在零向量

          hack:(这种情况会卡极角排序)

          +

          2.存在零向量

          +

          hack:(这种情况会卡极角排序)

          input:
           1
           1 1 1 1
          @@ -541,12 +578,16 @@ 

          100000000.0

          这种情况一般题目会避免出现,但是不排除有的出题人搞了这种

          其实这个情况容易判断,只要在读入的时候特判一下就好了

          -

          3.其他hack

          目前没有发现什么其他hack,欢迎大家来hack我

          -

          4.AC代码

          +

          3.其他hack

          +

          目前没有发现什么其他hack,欢迎大家来hack我

          +

          4.AC代码

          +

          poj请选择C++提交,g++提交的话容易作死(poj的g++要用%f输入输出double,pojC++用%lf)

          -

          这个出题人不写spj十分恶心人(无意冒犯),反正我搞了好久(6h+) $😓$

          -

          upd.2022-07-25 代码2不知道为什么在POJ上CE了(之前用g++交的)

          +

          这个出题人不写spj十分恶心人(无意冒犯),反正我搞了好久(6h+) \(😓\)

          +

          upd.2022-07-25 +代码2不知道为什么在POJ上CE了(之前用g++交的)

          现在更新了一下原来的代码(现在是下面的代码1),用了相对比较老的语法,可以AC(Run ID:23615953)

          代码1:(请使用C++提交)

          #include <cstdio>
          @@ -758,8 +799,9 @@ 

          return 0; }


          - -

          总结

          半平面交、旋转卡壳,这俩绝对是我目前见过最毒瘤的算法了 $😅$

          +

          总结

          +

          半平面交、旋转卡壳,这俩绝对是我目前见过最毒瘤的算法了 \(😅\)

          啥?你说仙人掌?这不是毒瘤本瘤吗(逃

          @@ -1134,7 +1176,7 @@

          总结   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/04/29/ji-suan-ji-he-shi-yong-cha-ji-ji-suan-zhi-xian-jiao-dian/index.html b/2022/04/29/ji-suan-ji-he-shi-yong-cha-ji-ji-suan-zhi-xian-jiao-dian/index.html index f8418f7bfd..ea6a2bdc19 100644 --- a/2022/04/29/ji-suan-ji-he-shi-yong-cha-ji-ji-suan-zhi-xian-jiao-dian/index.html +++ b/2022/04/29/ji-suan-ji-he-shi-yong-cha-ji-ji-suan-zhi-xian-jiao-dian/index.html @@ -472,11 +472,20 @@

          [计算几何] 使用叉积计
          -

          [计算几何] 使用叉积计算直线交点

          使用叉积计算直线交点

          -

          已知直线 $AB$ 和 $CD$ ,其交点为 $P$ , 求 $P$ 的坐标

          +

          [计算几何] +使用叉积计算直线交点

          +

          使用叉积计算直线交点

          +
          +

          已知直线 \(AB\)\(CD\) ,其交点为 \(P\) , 求 \(P\) 的坐标

          -

          计算几何中一般存储向量 $\vec{p},\vec{v}$ 表示起点的向量表示和方向向量

          -

          例如直线 $AB$ 一般存储为 $\overset{\longrightarrow}{OA},\overset{\longrightarrow}{AB}$

          +

          计算几何中一般存储向量 \(\vec{p},\vec{v}\) +表示起点的向量表示和方向向量

          +

          例如直线 \(AB\) 一般存储为 \(\overset{\longrightarrow}{OA},\overset{\longrightarrow}{AB}\)

          const double eps=1e-10;
           struct vct{double x,y;}p[N];
           #define pf(x) ((x)*(x))
          @@ -502,20 +511,24 @@ 

          } }a[N];

          因此,可以得到下图

          -

          -

          -

          其中 $v_1,v_2$ 为方向向量

          -

          则 $P=p_1 + tv_1$

          -

          $\because (p_1+tv_1-p_2) \times v_2 = 0$

          -

          $\therefore t = \dfrac{(p_2-p_1)\times v_2}{v_1\times v_2}$

          -

          则代入 $P=p_1 + tv_1$ 即可

          +

          +

          +

          其中 \(v_1,v_2\) 为方向向量

          +

          \(P=p_1 + tv_1\)

          +

          \(\because (p_1+tv_1-p_2) \times v_2 = +0\)

          +

          \(\therefore t = \dfrac{(p_2-p_1)\times +v_2}{v_1\times v_2}\)

          +

          则代入 \(P=p_1 + tv_1\) 即可

          代码如下

          vct intersect(line a,line b)
           {
               double x=cross(b.way,a.p-b.p)/cross(a.way,b.way);
               return a.p+a.way*x;
           }
          -

          完整代码摘自这里 其实是懒得再打一遍 qwq

          +

          完整代码摘自这里 +其实是懒得再打一遍 qwq

          @@ -880,7 +893,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/at3913-xor-tree-ti-jie/index.html b/2022/05/25/at3913-xor-tree-ti-jie/index.html index 8c9d5cbb26..84ccad7ebc 100644 --- a/2022/05/25/at3913-xor-tree-ti-jie/index.html +++ b/2022/05/25/at3913-xor-tree-ti-jie/index.html @@ -472,64 +472,102 @@

          AT3913 XOR Tree 题解

          -

          AT3913 XOR Tree 题解

          题目链接:AT3913 XOR Tree

          +

          AT3913 XOR Tree 题解

          +

          题目链接:AT3913 +XOR Tree

          -

          题意:给你一棵有 $N$ 个节点的树,节点编号从 $0$ 到 $N-1$ , 树边编号从 $1$ 到 $N-1$ 。第 $i$ 条边连接节点 $x_i$ 和 $y_i$ ,其权值为 $a_i$。

          -

          你可以对树执行任意次操作,每次操作选取一条链和一个非负整数 $x$,将链上的边的权值与 $x$ 异或成为该边的新权值。

          -

          问最少需要多少次操作,使得所有边的权值都为 $0$ 。

          -

          $2\le N \le10^5,0\le x_i,y_i \le N-1,0\le a_i \le 15$

          +

          题意:给你一棵有 \(N\) 个节点的树,节点编号从 \(0\)\(N-1\) , 树边编号从 \(1\)\(N-1\) 。第 \(i\) 条边连接节点 \(x_i\)\(y_i\) ,其权值为 \(a_i\)

          +

          你可以对树执行任意次操作,每次操作选取一条链和一个非负整数 \(x\),将链上的边的权值与 \(x\) 异或成为该边的新权值。

          +

          问最少需要多少次操作,使得所有边的权值都为 \(0\)

          +

          \(2\le N \le10^5,0\le x_i,y_i \le N-1,0\le +a_i \le 15\)

          首先可以想到:

          边权不好处理,将其转化为点权

          不过如果直接转化为点权似乎还是不好弄 q779想到这就没了

          -

          我们知道异或有自反性 $(p\oplus q \oplus p = q )$ 等性质

          -

          则考虑将点权定义为 $\text{val}(u) = \bigoplus\limits_{v_i \in V \land (u,v_i) \in E}w(u,v_i)$

          +

          我们知道异或有自反性 \((p\oplus q \oplus p += q )\) 等性质

          +

          则考虑将点权定义为 \(\text{val}(u) = +\bigoplus\limits_{v_i \in V \land (u,v_i) \in E}w(u,v_i)\)

          也就是和每个结点直接相邻的边的边权的异或和

          此时我们就将边权映射到了点权上(注意本题可以看作一棵无向无根树)

          -

          路径 $u-v$ 的修改转化为了 $u,v$ 两个点的修改

          +

          路径 \(u-v\) 的修改转化为了 \(u,v\) 两个点的修改

          因为点权是异或和

          所以同时修改路径上某个非端点结点所连的两条边,异或和不变(自反性)

          首先,我们来证明几个结论

          -
          +
          -

          命题1 :当且仅当 $\sum_{u_i\in V} \text{val}(u_i)=0$ 时,$\sum_{l_i\in E} w(l_i) = 0$。

          +

          命题1 :当且仅当 \(\sum_{u_i\in V} \text{val}(u_i)=0\) +时,\(\sum_{l_i\in E} w(l_i) = +0\)

          证明

            -
          • 必要性证明:显然当边权和为 $0$ 时,点权和也为 $0$ 。

            -
          • -
          • 充分性证明:设树上的结点数为 $n$ ,若叶子结点 $u$ 的点权为 $0$

            -

            则与其相连的边的边权也为 $0$ ,因此这条边不会影响到 $u$ 的父结点,归纳可得

            -
          • +
          • 必要性证明:显然当边权和为 \(0\) +时,点权和也为 \(0\)

          • +
          • 充分性证明:设树上的结点数为 \(n\) ,若叶子结点 \(u\) 的点权为 \(0\)

          -
          +

          则与其相连的边的边权也为 \(0\) +,因此这条边不会影响到 \(u\) +的父结点,归纳可得

          +

          \[ +\sum_{u_i\in V} \text{val}(u_i)=0 \Rightarrow \sum_{l_i\in E} w(l_i) = 0 +\]

          +
          -

          命题2:一个数字集合通过不断取两个数字并异或上同一个数有解,当且仅当这个数字集合异或和为 $0$ 。

          +

          命题2:一个数字集合通过不断取两个数字并异或上同一个数有解,当且仅当这个数字集合异或和为 +\(0\)

          证明:显然,因为每次修改不会改变该集合的异或和。

          -
          +
          -

          命题3:$\bigoplus\limits_{u_i\in V} \text{val}(u_i) = 0$ 。

          +

          命题3\(\bigoplus\limits_{u_i\in V} \text{val}(u_i) = +0\)

          证明:显然,每条边都有两个端点,根据自反性可得。

          -
          +

          这样,我们就可以贪心地选择两个点权相同的结点消掉了

          可是如果没有没有点权相同的结点了怎么办呢?

          -

          可以发现,此时最多有 $16$ 个互不相同的数字,而 $0$ 不用考虑,因此最多只有 $15$ 个数字

          +

          可以发现,此时最多有 \(16\) +个互不相同的数字,而 \(0\) +不用考虑,因此最多只有 \(15\) +个数字

          考虑状压dp

          -

          令 $dp_s$ 为将数字集合 $s$ 全部变为 $0$ 的最小操作数

          -

          显然至多要 $|s|-1$ 次操作(也就是原图中一条条边消)

          +

          \(dp_s\) 为将数字集合 \(s\) 全部变为 \(0\) 的最小操作数

          +

          显然至多要 \(|s|-1\) +次操作(也就是原图中一条条边消)

          直接去搞是有后效性的,所以考虑如何转移

          -

          我们可以枚举 $s$ 的一个子集 $k$ ,则有 $dp_s = \min(dp_s,dp_k+dp_{s/k})$

          -

          复杂度怎么样呢?

          -

          代码如下

          +O\left( \sum\limits_{i=1}^{15}\operatorname{C}_{15}^i2^i + n\right) +&= O\left((1+2)^{15}-1 + n\right) \\ +&= O(3^{15}+n) \\\\ +&= O(n) +\end{aligned} +\] 代码如下

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -560,8 +598,10 @@ 

          }

          讲个笑话,写完自己读一遍差点没读懂

          参考文献

          -

          [1] https://www.luogu.com.cn/blog/ShadowassIIXVIIIIV/solution-at3913

          -

          [2] https://www.luogu.com.cn/blog/xzc/solution-at3913

          +

          [1] https://www.luogu.com.cn/blog/ShadowassIIXVIIIIV/solution-at3913

          +

          [2] https://www.luogu.com.cn/blog/xzc/solution-at3913

          @@ -927,7 +967,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/cf149d-coloring-brackets-ti-jie/index.html b/2022/05/25/cf149d-coloring-brackets-ti-jie/index.html index 9faa2b2e26..5360e34526 100644 --- a/2022/05/25/cf149d-coloring-brackets-ti-jie/index.html +++ b/2022/05/25/cf149d-coloring-brackets-ti-jie/index.html @@ -472,26 +472,43 @@

          CF149D Coloring Brackets 题解<
          -

          CF149D Coloring Brackets 题解

          题目链接:CF149D Coloring Brackets

          +

          CF149D Coloring Brackets +题解

          +

          题目链接:CF149D +Coloring Brackets

          -

          题意:给出一个配对的括号序列(如 “$\texttt{(())()}$”、“$\texttt{()}$” 等,“$\texttt{)()}$”、“$\texttt{(()}$”是不符合要求的),对该序列按照以下方法染色。

          -
            +

            题意:给出一个配对的括号序列(如 “\(\texttt{(())()}\)”、“\(\texttt{()}\)” 等,“\(\texttt{)()}\)”、“\(\texttt{(()}\)”是不符合要求的),对该序列按照以下方法染色。

            +
            1. 一个括号可以染成红色、蓝色或者不染色。
            2. 一对匹配的括号需要且只能将其中一个染色。
            3. 相邻两个括号颜色不能相同(但都可以不染色)。
            -

            求符合条件的染色方案数,对 $1000000007$ 取模。

            +

            求符合条件的染色方案数,对 \(1000000007\) 取模。

          容易发现这个题目就是个区间dp

          -

          但是仅仅用 $dp[l][r]$ 并不能表示出两侧括号的颜色

          -

          因此设 $dp[l][r][i][j]$ 为区间 $[l,r]$ 内且左右两侧的颜色分别为 $i,j$ 时的方案数 (0为不染色,1为红,2为蓝)

          -

          考虑边界:当 $l+1=r$ 时,有

          -
          dp[l][r][0][1]=dp[l][r][0][2]=dp[l][r][1][0]=dp[l][r][2][0]=1;
          +

          但是仅仅用 \(dp[l][r]\) +并不能表示出两侧括号的颜色

          +

          因此设 \(dp[l][r][i][j]\) 为区间 +\([l,r]\) 内且左右两侧的颜色分别为 +\(i,j\) 时的方案数 +(0为不染色,1为红,2为蓝)

          +

          考虑边界:当 \(l+1=r\) 时,有 \[ +\text{()} +\]

          +
          dp[l][r][0][1]=dp[l][r][0][2]=dp[l][r][1][0]=dp[l][r][2][0]=1;

          一般地,

          -

          当 $l$ 与 $r$ 配对时,有

          -
          for(int i=0; i<=2; i++)
          +

          \(l\)\(r\) 配对时,有 \[ +\text{(}\dots\text{)} +\]

          +
          for(int i=0; i<=2; i++)
               for(int j=0; j<=2; j++)
               {
                   if(j!=1) dp[l][r][0][1]=(dp[l][r][0][1]+dp[l+1][r-1][i][j])%mod;
          @@ -499,11 +516,13 @@ 

          if(i!=1) dp[l][r][1][0]=(dp[l][r][1][0]+dp[l+1][r-1][i][j])%mod; if(i!=2) dp[l][r][2][0]=(dp[l][r][2][0]+dp[l+1][r-1][i][j])%mod; }

          -

          当 $l$ 与 $r$ 不配对时,有

          -

          以及

          -

          等情况,不过本质上是一样的

          +

          \(l\)\(r\) 不配对时,有 \[ +\text{(}\dots\text{)}\dots\text{(}\dots\text{)} +\] 以及 \[ +\text{(}\dots\text{)}\text{(}\dots\text{)} +\] 等情况,不过本质上是一样的

          for(int i=0; i<=2; i++)
               for(int j=0; j<=2; j++)
                   for(int p=0; p<=2; p++)
          @@ -568,7 +587,9 @@ 

          printf("%lld\n",ans); return 0; }

          -

          本文很多地方参考了这篇文章,写的非常好$✅$

          +

          本文很多地方参考了这篇文章,写的非常好\(✅\)

          @@ -934,7 +955,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/cf19b-checkout-assistant-ti-jie/index.html b/2022/05/25/cf19b-checkout-assistant-ti-jie/index.html index 51fae9e0b9..c705644820 100644 --- a/2022/05/25/cf19b-checkout-assistant-ti-jie/index.html +++ b/2022/05/25/cf19b-checkout-assistant-ti-jie/index.html @@ -472,28 +472,51 @@

          CF19B Checkout Assistant 题解<
          -

          CF19B Checkout Assistant 题解

          题目链接:CF19B Checkout Assistant

          +

          CF19B Checkout Assistant +题解

          +

          题目链接:CF19B +Checkout Assistant

          -

          题意:Bob 来到一家现购自运商店,将 $n$ 件商品放入了他的手推车,然后到收银台付款。每件商品由它的价格 $c_i$ 和收银员扫描它的时间 $t_i$ 秒定义。

          -

          当收银员正在扫描某件商品时,Bob 可以从他的手推车中偷走某些其它商品。Bob 需要恰好 $1$ 秒来偷走一件商品。Bob 需要付给收银员的最少钱数是多少?请记住,收银员扫描商品的顺序由 Bob 决定。

          +

          题意:Bob 来到一家现购自运商店,将 \(n\) +件商品放入了他的手推车,然后到收银台付款。每件商品由它的价格 \(c_i\) 和收银员扫描它的时间 \(t_i\) 秒定义。

          +

          当收银员正在扫描某件商品时,Bob +可以从他的手推车中偷走某些其它商品。Bob 需要恰好 \(1\) 秒来偷走一件商品。Bob +需要付给收银员的最少钱数是多少?请记住,收银员扫描商品的顺序由 Bob +决定。

          -

          这个题确实有点难想,因为它的背包容量和物品都是与 $n$ 有关的

          -

          观察题目的性质,我们需要带走所有的 $n$ 件物品

          -

          而第 $i$ 件物品可以花费 $c_i$ 的价格带走 $t_i+1$ 件物品

          -

          为什么是 $+1$ ?因为它本身也会被带走

          -

          题目要求的是带走这 $n$ 件物品的最小花费,可以看出这是一个01背包

          +

          这个题确实有点难想,因为它的背包容量和物品都是与 \(n\) 有关的

          +

          观察题目的性质,我们需要带走所有的 \(n\) 件物品

          +

          而第 \(i\) 件物品可以花费 \(c_i\) 的价格带走 \(t_i+1\) 件物品

          +

          为什么是 \(+1\) +?因为它本身也会被带走

          +

          题目要求的是带走这 \(n\) +件物品的最小花费,可以看出这是一个01背包

          但是背包的大小并不是很好确定

          -

          因为选择的物品它们的 $\sum (t_i+1)$ 很可能会超过 $n$

          -

          那么最多会超过多少呢?不难发现背包容量最多为 $n+\max(v_i)+1$

          +

          因为选择的物品它们的 \(\sum +(t_i+1)\) 很可能会超过 \(n\)

          +

          那么最多会超过多少呢?不难发现背包容量最多为 \(n+\max(v_i)+1\)

          如果超过了这个数,那么当前的选择一定不是优的

          -

          由于我们并不知道最后会超过多少,我们就把每一种情况都算出来,然后取个 $\min$ 就好了

          -

          设 $dp[i][j]$ 表示只考虑前 $i$ 件物品,恰好装满 $j$ 时的最小花费,则有

          -

          滚动数组一下,就是

          -

          于是答案就是

          -

          代码:

          +

          由于我们并不知道最后会超过多少,我们就把每一种情况都算出来,然后取个 +\(\min\) 就好了

          +

          \(dp[i][j]\) 表示只考虑前 \(i\) 件物品,恰好装满 \(j\) 时的最小花费,则有 \[ +dp[i][j]=\min(dp[i-1][j],dp[i-1][j-t_i-1]+c_i) +\] 滚动数组一下,就是 \[ +dp[j]=\min(dp[j],dp[j-t_i-1]+c_i) +\] 于是答案就是 \[ +\min_{n \le i \le n+\max(v_k)+1}(dp[i]) +\] 代码:

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -697,13 +720,13 @@ 

          - +
          - CF294B Shaass and Bookshelf 题解 + CF219D Choosing Capital for Treeland 题解 - CF294B Shaass and Bookshelf 题解 + CF219D Choosing Capital for Treeland 题解
          @@ -735,8 +758,8 @@

          算法 - - 数学 + + DP

          @@ -892,7 +915,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/cf219d-choosing-capital-for-treeland-ti-jie/index.html b/2022/05/25/cf219d-choosing-capital-for-treeland-ti-jie/index.html index 6a9e3a926d..7250b5a744 100644 --- a/2022/05/25/cf219d-choosing-capital-for-treeland-ti-jie/index.html +++ b/2022/05/25/cf219d-choosing-capital-for-treeland-ti-jie/index.html @@ -472,12 +472,17 @@

          CF219D Choosing Capital for Tree
          -

          CF219D Choosing Capital for Treeland 题解

          题目链接:CF219D Choosing Capital for Treeland

          +

          CF219D Choosing +Capital for Treeland 题解

          +

          题目链接:CF219D +Choosing Capital for Treeland

          题意:Treeland国有n个城市,这n个城市连成了一颗树,有n-1条道路连接了所有城市。每条道路只能单向通行。现在政府需要决定选择哪个城市为首都。假如城市i成为了首都,那么为了使首都能到达任意一个城市,不得不将一些道路翻转方向,记翻转道路的条数为k。你的任务是找到所有满足k最小的首都。

          -

          随便找一个点 $rt$ 跑一遍dfs,求出这个 $dp[rt]$ 的值

          -

          然后 $dp[u] \to dp[v]$ 的转移显然与 $(u,v)$ 有关

          +

          随便找一个点 \(rt\) +跑一遍dfs,求出这个 \(dp[rt]\) 的值

          +

          然后 \(dp[u] \to dp[v]\) +的转移显然与 \((u,v)\) 有关

          判断一下然后转移一下就没了

          题外话:真的离了谱了,我换电脑之前写的这篇文章没发博客上,然后今天20220606花了2小时用爬虫和自己写的比对程序搞了半天才发现(恼

          @@ -662,13 +667,13 @@

           上一篇

          - +
          - CF374C Inna and Dima 题解 + CF19B Checkout Assistant 题解 - CF374C Inna and Dima 题解 + CF19B Checkout Assistant 题解
          @@ -715,13 +720,13 @@

          - +
          - CF41D Pawn 题解 + CF346B Lucky Common Subsequence 题解 - CF41D Pawn 题解 + CF346B Lucky Common Subsequence 题解
          @@ -910,7 +915,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/cf294b-shaass-and-bookshelf-ti-jie/index.html b/2022/05/25/cf294b-shaass-and-bookshelf-ti-jie/index.html index d3949041ea..f5e36d67e0 100644 --- a/2022/05/25/cf294b-shaass-and-bookshelf-ti-jie/index.html +++ b/2022/05/25/cf294b-shaass-and-bookshelf-ti-jie/index.html @@ -472,20 +472,44 @@

          CF294B Shaass and Bookshelf 题
          -

          CF294B Shaass and Bookshelf 题解

          题目链接:CF294B Shaass and Bookshelf

          +

          CF294B Shaass and Bookshelf +题解

          +

          题目链接:CF294B +Shaass and Bookshelf

          -

          题意:Shaass has $n$ books. He wants to make a bookshelf for all his books. He wants the bookshelf’s dimensions to be as small as possible. The thickness of the $i$ -th book is $t_{i}$ and its pages’ width is equal to $w_{i}$ . The thickness of each book is either $1$ or $2$ . All books have the same page heights.

          -

          -

          Shaass puts the books on the bookshelf in the following way. First he selects some of the books and put them vertically. Then he puts the rest of the books horizontally above the vertical books. The sum of the widths of the horizontal books must be no more than the total thickness of the vertical books. A sample arrangement of the books is depicted in the figure.

          -

          -

          Help Shaass to find the minimum total thickness of the vertical books.

          -

          $1\le n \le 100,1 \le t_i \le 2,1 \le w_i \le 100$

          +

          题意:Shaass has \(n\) books. He wants to make a bookshelf for +all his books. He wants the bookshelf's dimensions to be as small as +possible. The thickness of the \(i\) +-th book is \(t_{i}\) and its pages' +width is equal to \(w_{i}\) . The +thickness of each book is either \(1\) +or \(2\) . All books have the same page +heights.

          +

          +

          Shaass puts the books on the bookshelf in the following way. First he +selects some of the books and put them vertically. Then he puts the rest +of the books horizontally above the vertical books. The sum of the +widths of the horizontal books must be no more than the total thickness +of the vertical books. A sample arrangement of the books is depicted in +the figure.

          +

          +

          Help Shaass to find the minimum total thickness of the vertical +books.

          +

          \(1\le n \le 100,1 \le t_i \le 2,1 \le w_i +\le 100\)

          -

          翻译一下就是说要求竖直摆放时厚度和 $\sum t_i$ 最小,同时不竖直摆放的书的宽度和 $\sum w_i$ 不超过厚度和 $\sum t_i$,高度均相等

          +

          翻译一下就是说要求竖直摆放时厚度和 \(\sum +t_i\) 最小,同时不竖直摆放的书的宽度和 \(\sum w_i\) 不超过厚度和 \(\sum t_i\),高度均相等

          注意到这个厚度很小,因此我们可以枚举一下厚度和

          -

          然后问题就转化为了,厚度和为 $j$ ,要求竖直摆放的那些书宽度和尽可能的大(这样放在上面的宽度和就小了)

          +

          然后问题就转化为了,厚度和为 \(j\) +,要求竖直摆放的那些书宽度和尽可能的大(这样放在上面的宽度和就小了)

          于是就转化为了恰好装满的01背包问题

          -

          时间复杂度 $O(n^2)$

          +

          时间复杂度 \(O(n^2)\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -639,13 +663,13 @@ 

           上一篇

          - +
          - CF19B Checkout Assistant 题解 + CF374C Inna and Dima 题解 - CF19B Checkout Assistant 题解 + CF374C Inna and Dima 题解
          @@ -692,13 +716,13 @@

          - +
          - CF374C Inna and Dima 题解 + CF41D Pawn 题解 - CF374C Inna and Dima 题解 + CF41D Pawn 题解
          @@ -887,7 +911,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/cf346b-lucky-common-subsequence-ti-jie/index.html b/2022/05/25/cf346b-lucky-common-subsequence-ti-jie/index.html index d327d6e46f..455e497fe7 100644 --- a/2022/05/25/cf346b-lucky-common-subsequence-ti-jie/index.html +++ b/2022/05/25/cf346b-lucky-common-subsequence-ti-jie/index.html @@ -472,22 +472,42 @@

          CF346B Lucky Common Subsequence
          -

          CF346B Lucky Common Subsequence 题解

          题目链接:CF346B Lucky Common Subsequence

          +

          CF346B Lucky Common +Subsequence 题解

          +

          题目链接:CF346B +Lucky Common Subsequence

          -

          题意:通过删除一个字符串中的某些元素而不改变其余元素的顺序,可以派生出该字符串的一个子序列。 例如,序列BDF是ABCDEF的子序列。 字符串的子字符串是该字符串的连续子序列。 例如,BCD是ABCDEF的子串。 你得到了两个字符串s1,s2和另一个名为virus的字符串。你的任务是找到s1和s2的最长公共子序列,同时不包含virus子字符串。

          +

          题意:通过删除一个字符串中的某些元素而不改变其余元素的顺序,可以派生出该字符串的一个子序列。 +例如,序列BDF是ABCDEF的子序列。 字符串的子字符串是该字符串的连续子序列。 +例如,BCD是ABCDEF的子串。 +你得到了两个字符串s1,s2和另一个名为virus的字符串。你的任务是找到s1和s2的最长公共子序列,同时不包含virus子字符串。

          升级版的最长公共子序列问题(LCS问题)

          -

          考虑增加一维,称为与 $c$ 串匹配度,即

          -

          $dp[i][j][k]$ 表示:

          -

          $a$ 串匹配到第 $i$ 位,$b$ 串匹配到第 $j$ 位最长公共子序列与 $c$ 串匹配到第 $k$ 位。

          -

          显然当 $a[i]\ne b[j]$ 时,有 $dp[i][j][k]=\max(dp[i][j][k],dp[i][j-1][k],dp[i-1][j][k])$

          -

          当 $a[i]=b[j]$ 时,直接去想 $dp[i][j][k]$ 怎么转移不太好搞

          -

          考虑反过来想此时 $dp[i-1][j-1][k]$ 的贡献

          -

          显然此时在最长公共子序列上增加一个 $a[i]$ ,可能会改变匹配的程度

          -

          设加上 $a[i]$ 后的最长公共子序列与 $c$ 串匹配到第 $p$ 位

          -

          则有 $dp[i][j][p] = dp[i][j][p],dp[i-1][j-1][k]+a[i]$

          -

          注意到这个 $p$ 可以通过跳fail数组得到,考虑KMP

          -

          则时间复杂度 $O(n^3)$

          +

          考虑增加一维,称为与 \(c\) +串匹配度,即

          +

          \(dp[i][j][k]\) 表示:

          +

          \(a\) 串匹配到第 \(i\) 位,\(b\) 串匹配到第 \(j\) 位最长公共子序列与 \(c\) 串匹配到第 \(k\) 位。

          +

          显然当 \(a[i]\ne b[j]\) 时,有 \(dp[i][j][k]=\max(dp[i][j][k],dp[i][j-1][k],dp[i-1][j][k])\)

          +

          \(a[i]=b[j]\) 时,直接去想 \(dp[i][j][k]\) 怎么转移不太好搞

          +

          考虑反过来想此时 \(dp[i-1][j-1][k]\) +的贡献

          +

          显然此时在最长公共子序列上增加一个 \(a[i]\) ,可能会改变匹配的程度

          +

          设加上 \(a[i]\) 后的最长公共子序列与 +\(c\) 串匹配到第 \(p\)

          +

          则有 \(dp[i][j][p] = +dp[i][j][p],dp[i-1][j-1][k]+a[i]\)

          +

          注意到这个 \(p\) +可以通过跳fail数组得到,考虑KMP

          +

          则时间复杂度 \(O(n^3)\)

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -653,13 +673,13 @@ 

           上一篇

          - +
          - CF41D Pawn 题解 + CF219D Choosing Capital for Treeland 题解 - CF41D Pawn 题解 + CF219D Choosing Capital for Treeland 题解
          @@ -706,13 +726,13 @@

          - +
          - CF526B Om Nom and Dark Park 题解 + CF374C Inna and Dima 题解 - CF526B Om Nom and Dark Park 题解 + CF374C Inna and Dima 题解
          @@ -744,8 +764,8 @@

          算法 - - 图论 + + DP

          @@ -901,7 +921,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/cf374c-inna-and-dima-ti-jie/index.html b/2022/05/25/cf374c-inna-and-dima-ti-jie/index.html index 7930dc40cc..8ca814c65f 100644 --- a/2022/05/25/cf374c-inna-and-dima-ti-jie/index.html +++ b/2022/05/25/cf374c-inna-and-dima-ti-jie/index.html @@ -472,15 +472,18 @@

          CF374C Inna and Dima 题解

          -

          CF374C Inna and Dima 题解

          题目链接:CF374C Inna and Dima

          +

          CF374C Inna and Dima 题解

          +

          题目链接:CF374C +Inna and Dima

          题意

          -

          Inna和Dima在商店买了一张 n * m 的桌子,桌子的每一个单元格上都有一个字符,字符集为(“D”,”I”,”M”,”A”)。

          +

          Inna和Dima在商店买了一张 n * m +的桌子,桌子的每一个单元格上都有一个字符,字符集为("D","I","M","A")。

          Inna实在是太爱Dima了,所以她想在桌子上找出一条最长的路线,使得这条路线中包含尽可能多的Dima的名字(有序,即必须是连续的DIMA)。

          Inna的路线选取方式会遵循以下原则:

          -

          1.一开始,Inna会在桌子上找到所有的”D”,把他们作为起点。

          -

          2.Inna会在当前格子的周围四个相邻的格子中选取一个格子,她会从”D”走到”I”,从”I”走到”M”,从”M”走到”A”。这样,她就认为自己走完了一次Dima的名字。

          -

          3.Inna每走完一个格子,就会在该格子四周相邻的四个格子中选一个走,即上下左右四个方向,(当然,她不能走出这张桌子)。Inna从不跳过一个字母。因此,从字母”D”开始,她总是走到字母”I”,从字母”I”开始,她总是走到字母”M”,从字母”M”开始,她总是走到字母”A”,从字母”A”开始,她总是走到字母”D”。

          +

          1.一开始,Inna会在桌子上找到所有的"D",把他们作为起点。

          +

          2.Inna会在当前格子的周围四个相邻的格子中选取一个格子,她会从"D"走到"I",从"I"走到"M",从"M"走到"A"。这样,她就认为自己走完了一次Dima的名字。

          +

          3.Inna每走完一个格子,就会在该格子四周相邻的四个格子中选一个走,即上下左右四个方向,(当然,她不能走出这张桌子)。Inna从不跳过一个字母。因此,从字母"D"开始,她总是走到字母"I",从字母"I"开始,她总是走到字母"M",从字母"M"开始,她总是走到字母"A",从字母"A"开始,她总是走到字母"D"。

          因为桌子的不同,Inna能走的Dima的名字的次数也不同,有时她可以走有限次,有时她可以走无限次,有时她甚至不能走完一次Dima的名字。

          现在,她想知道她能否至少走完一次Dima的名字。若可以,她最多能走多少次或者她能否走无限次?

          @@ -660,13 +663,13 @@

           上一篇

          - +
          - CF219D Choosing Capital for Treeland 题解 + CF294B Shaass and Bookshelf 题解 - CF219D Choosing Capital for Treeland 题解 + CF294B Shaass and Bookshelf 题解
          @@ -751,8 +754,8 @@

          算法 - - DP + + 数学

          @@ -908,7 +911,7 @@

            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/cf41d-pawn-ti-jie/index.html b/2022/05/25/cf41d-pawn-ti-jie/index.html index 48d6a8ddfb..e4b1cdc637 100644 --- a/2022/05/25/cf41d-pawn-ti-jie/index.html +++ b/2022/05/25/cf41d-pawn-ti-jie/index.html @@ -472,19 +472,33 @@

          CF41D Pawn 题解

          -

          CF41D Pawn 题解

          题目链接:CF41D Pawn

          +

          CF41D Pawn 题解

          +

          题目链接:CF41D +Pawn

          -

          题意:国际象棋棋盘最底行站了一个兵。 它只有两种行动方式: 向上左或向上右走。 它可以选择从最低行哪个节点开始他的旅程。 每个格子上有0-9颗豌豆,而士兵想移动到最上一行并且积累到尽可能多的豌豆。同时,因为这个士兵必须把豌豆平均分给自己和他的k个兄弟,他所收集到的豌豆必须是k+1的倍数。请找到他可以收集到的最多豌豆,并确定他的操作序列。

          +

          题意:国际象棋棋盘最底行站了一个兵。 +它只有两种行动方式: 向上左或向上右走。 +它可以选择从最低行哪个节点开始他的旅程。 +每个格子上有0-9颗豌豆,而士兵想移动到最上一行并且积累到尽可能多的豌豆。同时,因为这个士兵必须把豌豆平均分给自己和他的k个兄弟,他所收集到的豌豆必须是k+1的倍数。请找到他可以收集到的最多豌豆,并确定他的操作序列。

          规定士兵不能手动扔出豌豆,并且他必须捡起所到达的每一个格子的所有豌豆。

          -

          输入格式: 第一行三个整数 $n,m,k(2 \le n,m \le 100, 0 \le k \le 10)$ 行数、列数、士兵的兄弟们。 接下来一个 $n \times m$ 的矩阵,每个元素均是0-9的整数(不空格),描述该格的豌豆。第一行被认为是最上一行,最后一行被认为是最下一行。

          +

          输入格式: 第一行三个整数 \(n,m,k(2 \le n,m +\le 100, 0 \le k \le 10)\) 行数、列数、士兵的兄弟们。 接下来一个 +\(n \times m\) +的矩阵,每个元素均是0-9的整数(不空格),描述该格的豌豆。第一行被认为是最上一行,最后一行被认为是最下一行。

          输出格式:

          -

          如果不能收集到k+1倍数的豌豆,输出-1. 否则,输出第一行一个整数,为最多豌豆数;第二行一个整数,为士兵开始移动的位置;第三行包括n-1个字母L 或 R,表示士兵的行动序列。

          +

          如果不能收集到k+1倍数的豌豆,输出-1. +否则,输出第一行一个整数,为最多豌豆数;第二行一个整数,为士兵开始移动的位置;第三行包括n-1个字母L +或 R,表示士兵的行动序列。

          如果有多种收集到相同且是k+1倍数数量的豌豆,你可以任意输出一种方案。

          -

          令 $dp[i][j][k]$ 为走到第 $i$ 行第 $j$ 列且恰好取了 $k$ 个的方案数

          +

          \(dp[i][j][k]\) 为走到第 \(i\) 行第 \(j\) 列且恰好取了 \(k\) 个的方案数

          这里不用求方案数,就用bool即可

          不习惯从下往上跑就翻转一下

          -

          答案就是所有的最后一行所有的 $dp[1][j][c] \bold{\ s.t. \ } k\mid c$

          +

          答案就是所有的最后一行所有的 \(dp[1][j][c] +\bold{\ s.t. \ } k\mid c\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -658,13 +672,13 @@ 

           上一篇

          - +
          - CF346B Lucky Common Subsequence 题解 + CF526B Om Nom and Dark Park 题解 - CF346B Lucky Common Subsequence 题解 + CF526B Om Nom and Dark Park 题解
          @@ -749,8 +763,8 @@

          算法 - - DP + + 图论

          @@ -906,7 +920,7 @@

            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/cf526b-om-nom-and-dark-park-ti-jie/index.html b/2022/05/25/cf526b-om-nom-and-dark-park-ti-jie/index.html index 2dc5075021..f95823e156 100644 --- a/2022/05/25/cf526b-om-nom-and-dark-park-ti-jie/index.html +++ b/2022/05/25/cf526b-om-nom-and-dark-park-ti-jie/index.html @@ -472,19 +472,27 @@

          CF526B Om Nom and Dark Park 题
          -

          CF526B Om Nom and Dark Park 题解

          题目链接:CF526B Om Nom and Dark Park

          +

          CF526B Om Nom and Dark Park +题解

          +

          题目链接:CF526B Om +Nom and Dark Park

          -

          题意:给定 $2^{n+1}-1$ 个结点的满二叉树,求保证根到每一个叶子节点的路径权值和相等的情况下,增加权值的最小值

          +

          题意:给定 \(2^{n+1}-1\) +个结点的满二叉树,求保证根到每一个叶子节点的路径权值和相等的情况下,增加权值的最小值

          显然不能从根节点开始修改权值,不然也不知道改成什么才行

          因此主要思路是从叶子结点向上修改权值,直到根结点为止

          -

          方便起见,用 a[x]表示结点 $x$ 到它的父结点的那条边的权值

          +

          方便起见,用 a[x]表示结点 \(x\) 到它的父结点的那条边的权值

          下文中提到的路径长指一个结点到它叶子结点的最终的距离

          -

          可以发现,对于一个待修改结点 $x$ ,假设我们已经知道了它的左右子结点的路径长ls,rs(已经被修改了的路径长,因为是从叶子结点向上修改的)

          -

          则结点 $x$ 到左子结点修改前的路径长为a[2*x]+ls,同理右子结点a[2*x+1]+rs

          +

          可以发现,对于一个待修改结点 \(x\) +,假设我们已经知道了它的左右子结点的路径长ls,rs(已经被修改了的路径长,因为是从叶子结点向上修改的)

          +

          则结点 \(x\) +到左子结点修改前的路径长为a[2*x]+ls,同理右子结点a[2*x+1]+rs

          保证根到叶子节点路径权值和相等,因此要把上面这两个路径长中较小的那个补成较大的那个,这样花费肯定是最小的,又保证了以这个结点为根的子树满足题目要求

          于是从下往上传递,过程中统计答案

          -

          时间复杂度 $O(2^n)$

          +

          时间复杂度 \(O(2^n)\)

          主要代码很短

          #define MAXN (int)(2e6+5)
           int n,ans,a[MAXN],MX;
          @@ -504,7 +512,8 @@ 

          return 0; }

          题外话

          -

          这题面好可爱 qwq

          +

          这题面好可爱 qwq

          @@ -622,13 +631,13 @@

           上一篇

          - +
          - CF346B Lucky Common Subsequence 题解 + CF41D Pawn 题解 - CF346B Lucky Common Subsequence 题解 + CF41D Pawn 题解
          @@ -870,7 +879,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/deng-chai-shu-lie-deng-bi-shu-lie-xiao-jie/index.html b/2022/05/25/deng-chai-shu-lie-deng-bi-shu-lie-xiao-jie/index.html index e2cac883bb..00ee6ee060 100644 --- a/2022/05/25/deng-chai-shu-lie-deng-bi-shu-lie-xiao-jie/index.html +++ b/2022/05/25/deng-chai-shu-lie-deng-bi-shu-lie-xiao-jie/index.html @@ -468,118 +468,158 @@

          等差数列&等比数列小
          -

          等差数列&等比数列小结

          高一自学的时候瞎总结写的(好吧我现在还是高一 2022.5.7)

          +

          等差数列&等比数列小结

          +

          高一自学的时候瞎总结写的(好吧我现在还是高一 +2022.5.7)

          感觉丢在文件夹里吃灰没啥用,就放上来了

          -

          欢迎各位指出我的错误(我数学真的烂 $😓$

          +

          欢迎各位指出我的错误(我数学真的烂 \(😓\)


          - -

          等差数列

          等差数列通项公式

          -

          $S_n$ 表示等差数列 $\{a_n\}$ 的 前 $n$ 项和

          -

          易知

          -

          $\{a_n\}$ 为等差数列当且仅当

          -

          命题1:若 $S_m = S_p \land m\ne p$ 则有 $S_{m+p}=0$

          -

          证:

          -

          命题2:若 $S_m=p,S_p=m\land m\ne p$ 则有 $S_{m+p}=-(m+p)$

          -

          证:

          -

          等比数列

          等比数列通项公式

          -

          有性质

          -

          等比数列前 $n$ 项和公式

          -

          易知

          -

          可知性质

          -

          当 $q\ne -1$ 时,有等比数列 $\left\{S_{(k+1)m}-S_{km}\right\}$

          -
            -
          1. 有递推式
          2. -
          -

          其中,$c\ne1,cd\ne 0$

          -

          构造等比数列求通项公式

          -

          即利用 $d=\dfrac{(1-c)d}{1-c}$ ,得

          -

          则当 $a_n-\dfrac{d}{1-c}\ne 0$ 时,数列 $\left\{a_n-\dfrac{d}{1-c}\right\}$ 为等比数列

          -
            -
          1. 有递推式

            -

            其中,$c\ne1, cd\ne 0, n\ge 2$

            -

            消常数项求通项公式

            -

            则当 $a_2\ne a_1$ 时,数列 $\left\{a_{n+1}-a_n\right\}$ 为等比数列

            -
          2. -
          -
            -
          1. 有递推式

            -

            其中,$c\ne d,cd\ne0$

            -

            化归求通项公式

            -

            则数列 $\left\{a_n-\dfrac{d^n}{d-c}\right\}$ 为等比数列

            -

            或者两侧同除以 $d^{n+1}$ 化为情况1

            -

            或者两侧同除以 $c^{n+1}$ ,累加求通项

            -
          2. -
          -
            -
          1. 有递推式

            -

            其中,$cdt\ne0,c\ne 1$

            -

            化归求通项公式

            -

            则转化为情况2

            -
          2. -
          -
            -
          1. 有递推式

            -

            其中,$pqk\ne0,p\ne1,q\ne1$

            -

            可使用待定系数法,后略

            -

            也可以两边同除以 $q^{n+1}$ ,得

            -

            进而化归为等比数列

            -

            还可以两边同除以 $p^{n+1}$ ,得

            -
          2. +a_{n+1}-a_n = c(a_n-a_{n-1}) +\] 则当 \(a_2\ne a_1\) 时,数列 +\(\left\{a_{n+1}-a_n\right\}\) +为等比数列

            +
          3. 有递推式 \[ +a_{n+1}=ca_n+d^n +\] 其中,\(c\ne d,cd\ne0\)

            +

            化归求通项公式 \[ +a_{n+1}-\dfrac{d^{n+1}}{d-c}=c\left(a_n-\dfrac{d^n}{d-c}\right) +\] 则数列 \(\left\{a_n-\dfrac{d^n}{d-c}\right\}\) +为等比数列

            +

            或者两侧同除以 \(d^{n+1}\) +化为情况1

            +

            或者两侧同除以 \(c^{n+1}\) +,累加求通项

          4. +
          5. 有递推式 \[ +a_{n+1}=ca_n+d^n+t +\] 其中,\(cdt\ne0,c\ne 1\)

            +

            化归求通项公式 \[ +a_{n+1}-\dfrac{t}{1-c}=c\left(a_n-\dfrac{t}{1-c}\right)+d^n +\] 则转化为情况2

          6. +
          7. 有递推式 \[ +a_{n+1}=pa_n+kq^{n+1} +\] 其中,\(pqk\ne0,p\ne1,q\ne1\)

            +

            可使用待定系数法,后略

            +

            也可以两边同除以 \(q^{n+1}\) ,得 +\[ +\dfrac{a_{n+1}}{p^{n+1}} = \dfrac{p}{q}\times\dfrac{a_n}{q^n}+k +\] 进而化归为等比数列

            +

            还可以两边同除以 \(p^{n+1}\) ,得 +\[ +\dfrac{a_{n+1}}{p^{n+1}} = +\dfrac{a_n}{p^n}+k\left(\dfrac{q}{p}\right)^{n+1} +\]


          -

          相信一定也有和我一样的懒人不喜欢对着博客手敲的

          我直接把上面那一堆的源码贴上来吧

          ## 等差数列
          @@ -942,13 +982,13 @@ 

          - +
          - 洛谷P6216 回文匹配 题解 + 线段树空间开4倍的原因 - 洛谷P6216 回文匹配 题解 + 线段树空间开4倍的原因
          @@ -976,12 +1016,12 @@

          - - 算法 + + 数学 - - 字符串 + + 数据结构

          @@ -1137,7 +1177,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p1108-di-jie-gou-mai-ti-jie/index.html b/2022/05/25/luo-gu-p1108-di-jie-gou-mai-ti-jie/index.html index 4d3e8805f2..253e80efe8 100644 --- a/2022/05/25/luo-gu-p1108-di-jie-gou-mai-ti-jie/index.html +++ b/2022/05/25/luo-gu-p1108-di-jie-gou-mai-ti-jie/index.html @@ -472,9 +472,13 @@

          洛谷P1108 低价购买 题解<
          -

          洛谷P1108 低价购买 题解

          题目链接:P1108 低价购买

          +

          洛谷P1108 低价购买 题解

          +

          题目链接:P1108 +低价购买

          -

          题意:“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购买它。买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数。你将被给出一段时间内一支股票每天的出售价( $2^{16}$ 范围内的正整数),你可以选择在哪些天购买这支股票。每次购买都必须遵循“低价购买;再低价购买”的原则。写一个程序计算最大购买次数。

          +

          题意:“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购买它。买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数。你将被给出一段时间内一支股票每天的出售价( +\(2^{16}\) +范围内的正整数),你可以选择在哪些天购买这支股票。每次购买都必须遵循“低价购买;再低价购买”的原则。写一个程序计算最大购买次数。

          这里是某支股票的价格清单:

          日期 1,2,3,4,5,6,7,8,9,10,11,12

          价格 68,69,54,64,68,64,70,67,78,62,98,87

          @@ -482,16 +486,21 @@

          \(N(1 \le N \le 5000)\) +,股票发行天数

          +

          第2行: \(N\)个数,是每天的股票价格。

          输出格式

          -

          两个数:
          最大购买次数和拥有最大购买次数的方案数( $\le 2^{31}$ )当二种方案“看起来一样”时(就是说它们构成的价格队列一样的时候),这2种方案被认为是相同的。

          +

          两个数: 最大购买次数和拥有最大购买次数的方案数( \(\le 2^{31}\) +)当二种方案“看起来一样”时(就是说它们构成的价格队列一样的时候),这2种方案被认为是相同的。

          第一问就是纯纯的最长下降子序列

          就看第二问吧

          显然我们不能把每个子序列记下来,然后暴力比对

          -

          注意到对于每个 $dp[i]$ ,

          -

          我们并不知道除了当前最后一位 $a[i]$ 以外的任何子序列的元素

          +

          注意到对于每个 \(dp[i]\)

          +

          我们并不知道除了当前最后一位 \(a[i]\) 以外的任何子序列的元素

          但是这足以判断是否重复计算了

          详见代码:

          #include <bits/stdc++.h>
          @@ -557,7 +566,8 @@ 

          return 0; }

          参考文献

          -

          [1] https://www.luogu.com.cn/blog/wjyyy/solution-p1108

          +

          [1] https://www.luogu.com.cn/blog/wjyyy/solution-p1108

          @@ -728,13 +738,13 @@

          - +
          - 洛谷P1171 售货员的难题 题解 + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 - 洛谷P1171 售货员的难题 题解 + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法
          @@ -923,7 +933,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p1156-la-ji-xian-jing-ti-jie-qian-tan-shua-biao-fa-yu-tian-biao-fa/index.html b/2022/05/25/luo-gu-p1156-la-ji-xian-jing-ti-jie-qian-tan-shua-biao-fa-yu-tian-biao-fa/index.html index b9218e350e..c92cae72c3 100644 --- a/2022/05/25/luo-gu-p1156-la-ji-xian-jing-ti-jie-qian-tan-shua-biao-fa-yu-tian-biao-fa/index.html +++ b/2022/05/25/luo-gu-p1156-la-ji-xian-jing-ti-jie-qian-tan-shua-biao-fa-yu-tian-biao-fa/index.html @@ -472,28 +472,45 @@

          洛谷P1156 垃圾陷阱 题解&
          -

          洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法

          填表法 :就是一般的动态规划,当前点的状态,可以直接用状态方程,根据之前点的状态推导出来。

          +

          洛谷P1156 垃圾陷阱 +题解&浅谈刷表法与填表法

          +

          填表法 +:就是一般的动态规划,当前点的状态,可以直接用状态方程,根据之前点的状态推导出来。

          刷表法:由当前点的状态,更新其他点的状态。需要注意:只用当每个状态所依赖的状态对它的影响相互独立。

          下面会用一道题目给出两种写法的区别


          - -

          题目链接:P1156 垃圾陷阱

          +

          题目链接:P1156 +垃圾陷阱

          题意吃垃圾就完了

          -

          卡门――农夫约翰极其珍视的一条Holsteins奶牛――已经落了到“垃圾井”中。“垃圾井”是农夫们扔垃圾的地方,它的深度为$D(2 \le D \le 100)$英尺。

          +

          卡门――农夫约翰极其珍视的一条Holsteins奶牛――已经落了到“垃圾井”中。“垃圾井”是农夫们扔垃圾的地方,它的深度为\(D(2 \le D \le 100)\)英尺。

          卡门想把垃圾堆起来,等到堆得与井同样高时,她就能逃出井外了。另外,卡门可以通过吃一些垃圾来维持自己的生命。

          每个垃圾都可以用来吃或堆放,并且堆放垃圾不用花费卡门的时间。

          -

          假设卡门预先知道了每个垃圾扔下的时间$t(0< t \le 1000)$,以及每个垃圾堆放的高度$h(1 \le h \le 25$)和吃进该垃圾能维持生命的时间$f(1 \le f \le 30)$,要求出卡门最早能逃出井外的时间,假设卡门当前体内有足够持续$10$小时的能量,如果卡门$10$小时内没有进食,卡门就将饿死。

          -

          注:如果能量为 $0$ 时恰好有垃圾可以吃,则不会饿死

          +

          假设卡门预先知道了每个垃圾扔下的时间\(t(0< t \le +1000)\),以及每个垃圾堆放的高度\(h(1 +\le h \le 25\))和吃进该垃圾能维持生命的时间\(f(1 \le f \le +30)\),要求出卡门最早能逃出井外的时间,假设卡门当前体内有足够持续\(10\)小时的能量,如果卡门\(10\)小时内没有进食,卡门就将饿死。

          +

          注:如果能量为 \(0\) +时恰好有垃圾可以吃,则不会饿死

          注意到对于每个垃圾,只有吃掉和堆起来两种方法

          -

          设 $dp[i][j]$ 表示只考虑前 $i$ 个垃圾,堆到高度 $j$ 时,最多可以活到什么时候

          -

          容易推出方程

          -

          1.填表法

          -

          求出dp数组以后扫一遍就好了,细节较多(其实好像可以滚动数组,但是懒 qwq)

          -

          注意这里垃圾的高度可能会超过 $m$ ,但是数据好像没卡这个东西

          -

          处理方法就是把 $m+\max(h_i)$ 都dp一下

          +

          \(dp[i][j]\) 表示只考虑前 \(i\) 个垃圾,堆到高度 \(j\) 时,最多可以活到什么时候

          +

          容易推出方程 \[ +dp[i][j]=\max(dp[i-1][j]+v_i,dp[i-1][j-h_i]) +\] 1.填表法

          +

          求出dp数组以后扫一遍就好了,细节较多(其实好像可以滚动数组,但是懒 +qwq)

          +

          注意这里垃圾的高度可能会超过 \(m\) +,但是数据好像没卡这个东西

          +

          处理方法就是把 \(m+\max(h_i)\) +都dp一下

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -543,7 +560,8 @@ 

          2.刷表法

          这里可以滚动数组了,不过要两个

          否则会导致数据更新错误

          -

          当然也可以不用滚动数组 位运算那么可爱为什么不写她呐?对不对?

          +

          当然也可以不用滚动数组 +位运算那么可爱为什么不写她呐?对不对?

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -704,13 +722,13 @@ 

           上一篇

          - +
          - 洛谷P1282 多米诺骨牌 题解 + 洛谷P1108 低价购买 题解 - 洛谷P1282 多米诺骨牌 题解 + 洛谷P1108 低价购买 题解
          @@ -757,13 +775,13 @@

          - +
          - 洛谷P1284 三角形牧场 题解 + 洛谷P1171 售货员的难题 题解 - 洛谷P1284 三角形牧场 题解 + 洛谷P1171 售货员的难题 题解
          @@ -952,7 +970,7 @@

            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p1171-shou-huo-yuan-de-nan-ti-ti-jie/index.html b/2022/05/25/luo-gu-p1171-shou-huo-yuan-de-nan-ti-ti-jie/index.html index 3d362eec65..1f18b0a796 100644 --- a/2022/05/25/luo-gu-p1171-shou-huo-yuan-de-nan-ti-ti-jie/index.html +++ b/2022/05/25/luo-gu-p1171-shou-huo-yuan-de-nan-ti-ti-jie/index.html @@ -472,14 +472,26 @@

          洛谷P1171 售货员的难题
          -

          洛谷P1171 售货员的难题 题解

          题目链接:P1171 售货员的难题

          +

          洛谷P1171 售货员的难题 题解

          +

          题目链接:P1171 +售货员的难题

          题意:TSP问题。

          -

          某乡有$n$个村庄($1<n \le 20$),有一个售货员,他要到各个村庄去售货,各村庄之间的路程$s(0<s<1000)$是已知的,且$A$村到$B$村与$B$村到$A$村的路大多不同。为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为$1$,他不知道选择什么样的路线才能使所走的路程最短。请你帮他选择一条最短的路。

          +

          某乡有\(n\)个村庄(\(1<n \le +20\)),有一个售货员,他要到各个村庄去售货,各村庄之间的路程\(s(0<s<1000)\)是已知的,且\(A\)村到\(B\)村与\(B\)村到\(A\)村的路大多不同。为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为\(1\),他不知道选择什么样的路线才能使所走的路程最短。请你帮他选择一条最短的路。

          题目应该保证了有解。

          经典的状压dp

          -

          设 $dp[i][j]$ 表示目前走过的结点的状态为 $i$ ,现在在 $j$ 结点

          +

          \(dp[i][j]\) +表示目前走过的结点的状态为 \(i\) +,现在在 \(j\) 结点

          则有

          dp[i][j]=min(dp[i][j],dp[i^(1<<(j-1))][k]+g[k][j])

          代码:

          @@ -631,13 +643,13 @@

           上一篇

          - +
          - 洛谷P1108 低价购买 题解 + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 - 洛谷P1108 低价购买 题解 + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法
          @@ -879,7 +891,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p1282-duo-mi-nuo-gu-pai-ti-jie/index.html b/2022/05/25/luo-gu-p1282-duo-mi-nuo-gu-pai-ti-jie/index.html index 094aaf0dee..4b97d90d3b 100644 --- a/2022/05/25/luo-gu-p1282-duo-mi-nuo-gu-pai-ti-jie/index.html +++ b/2022/05/25/luo-gu-p1282-duo-mi-nuo-gu-pai-ti-jie/index.html @@ -472,20 +472,47 @@

          洛谷P1282 多米诺骨牌 题
          -

          洛谷P1282 多米诺骨牌 题解

          题目链接:P1282 多米诺骨牌

          +

          洛谷P1282 多米诺骨牌 题解

          +

          题目链接:P1282 +多米诺骨牌

          题意

          -

          多米诺骨牌由上下 $2$ 个方块组成,每个方块中有 $1\sim6$ 个点。现有排成行的上方块中点数之和记为 $S_1$,下方块中点数之和记为 $S_2$,它们的差为 $\left|S_1-S_2\right|$。如图,$S_1=6+1+1+1=9$,$S_2=1+5+3+2=11$,$\left|S_1-S_2\right|=2$。每个多米诺骨牌可以旋转 $180°$,使得上下两个方块互换位置。请你计算最少旋转多少次才能使多米诺骨牌上下 $2$ 行点数之差达到最小。

          -

          -

          对于图中的例子,只要将最后一个多米诺骨牌旋转 $180°$,即可使上下 $2$ 行点数之差为 $0$。

          +

          多米诺骨牌由上下 \(2\) +个方块组成,每个方块中有 \(1\sim6\) +个点。现有排成行的上方块中点数之和记为 \(S_1\),下方块中点数之和记为 \(S_2\),它们的差为 \(\left|S_1-S_2\right|\)。如图,\(S_1=6+1+1+1=9\)\(S_2=1+5+3+2=11\)\(\left|S_1-S_2\right|=2\)。每个多米诺骨牌可以旋转 +\(180°\),使得上下两个方块互换位置。请你计算最少旋转多少次才能使多米诺骨牌上下 +\(2\) 行点数之差达到最小。

          +

          +

          对于图中的例子,只要将最后一个多米诺骨牌旋转 \(180°\),即可使上下 \(2\) 行点数之差为 \(0\)

          -

          注意到我们其实并不关心 $S_1,S_2$ 的值,而是关心他们的差值

          +

          注意到我们其实并不关心 \(S_1,S_2\) +的值,而是关心他们的差值

          这个绝对值似乎不好处理,其实只要把正负的情况取个min就可以了

          -

          设 $dp[i][j]$ 表示只考虑前 $i$ 个骨牌,差值为 $j$ 时的最小翻转数

          -

          这里的 $j$ 定义为 $S_1-S_2$ 或者 $S_2-S_1$ 其实没啥区别,不过下面代码为前者

          +

          \(dp[i][j]\) 表示只考虑前 \(i\) 个骨牌,差值为 \(j\) 时的最小翻转数

          +

          这里的 \(j\) 定义为 \(S_1-S_2\) 或者 \(S_2-S_1\) +其实没啥区别,不过下面代码为前者

          不难发现

          -

          注意到差值最大 $5000$ ,为了避免乱七八糟讨论,就直接循环 $-5000 \sim 5000$ 就好啦

          +

          \[ +dp[i][j]=\min(dp[i-1][j-a[i]+b[i]]+1,dp[i-1][j+a[i]-b[i]]) +\]

          +

          注意到差值最大 \(5000\) +,为了避免乱七八糟讨论,就直接循环 \(-5000 +\sim 5000\) 就好啦

          差值可能为负所以数组整体平移一下,然后整个填表法滚一滚啥的就好了

          这里好像刷表法写起来不太安全就不写了,虽然没啥问题,但是不算简洁吧(逃

          代码:

          @@ -690,13 +717,13 @@

          - +
          - 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + 洛谷P1284 三角形牧场 题解 - 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + 洛谷P1284 三角形牧场 题解
          @@ -885,7 +912,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p1284-san-jiao-xing-mu-chang-ti-jie/index.html b/2022/05/25/luo-gu-p1284-san-jiao-xing-mu-chang-ti-jie/index.html index da08dd6789..426002dc5d 100644 --- a/2022/05/25/luo-gu-p1284-san-jiao-xing-mu-chang-ti-jie/index.html +++ b/2022/05/25/luo-gu-p1284-san-jiao-xing-mu-chang-ti-jie/index.html @@ -472,16 +472,25 @@

          洛谷P1284 三角形牧场 题
          -

          洛谷P1284 三角形牧场 题解

          题目链接:P1284 三角形牧场

          +

          洛谷P1284 三角形牧场 题解

          +

          题目链接:P1284 +三角形牧场

          -

          题意:和所有人一样,奶牛喜欢变化。它们正在设想新造型的牧场。奶牛建筑师 Hei 想建造围有漂亮白色栅栏的三角形牧场。她拥有 $n$ 块木板,每块的长度 $l_i$ 都是整数,她想用所有的木板围成一个三角形使得牧场面积最大。

          +

          题意:和所有人一样,奶牛喜欢变化。它们正在设想新造型的牧场。奶牛建筑师 +Hei 想建造围有漂亮白色栅栏的三角形牧场。她拥有 \(n\) 块木板,每块的长度 \(l_i\) +都是整数,她想用所有的木板围成一个三角形使得牧场面积最大。

          请帮助 Hei 小姐构造这样的牧场,并计算出这个最大牧场的面积。

          显然我们可以枚举两条边的长度,然后用总和减去这两条边的长度就是第三条边的长度

          -

          设 $dp[k][i][j]$ 表示只考虑前 $k$ 个木板是否可以形成 $(i,j,S-i-j)$ 的划分

          -

          则有

          -

          其中 $\mid$ 表示二进制的或

          +

          \(dp[k][i][j]\) 表示只考虑前 +\(k\) 个木板是否可以形成 \((i,j,S-i-j)\) 的划分

          +

          则有 \[ +dp[k][i][j]=dp[k-1][i][j]\mid dp[k-1][i-a[k]][j]\mid dp[k-1][i][j-a[k]] +\] 其中 \(\mid\) +表示二进制的或

          然后滚动数组一下就完了

          代码:

          #include <bits/stdc++.h>
          @@ -645,13 +654,13 @@ 

           上一篇

          - +
          - 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + 洛谷P1282 多米诺骨牌 题解 - 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + 洛谷P1282 多米诺骨牌 题解
          @@ -897,7 +906,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p1640-scoi2010-lian-xu-gong-ji-you-xi-ti-jie/index.html b/2022/05/25/luo-gu-p1640-scoi2010-lian-xu-gong-ji-you-xi-ti-jie/index.html index 7608ebe61d..d4ec850de8 100644 --- a/2022/05/25/luo-gu-p1640-scoi2010-lian-xu-gong-ji-you-xi-ti-jie/index.html +++ b/2022/05/25/luo-gu-p1640-scoi2010-lian-xu-gong-ji-you-xi-ti-jie/index.html @@ -476,15 +476,31 @@

          洛谷P1640 [SCOI2010]连续攻
          -

          洛谷P1640 [SCOI2010]连续攻击游戏 题解

          题目链接:P1640 [SCOI2010]连续攻击游戏

          +

          洛谷P1640 +[SCOI2010]连续攻击游戏 题解

          +

          题目链接:P1640 +[SCOI2010]连续攻击游戏

          -

          题意:lxhgww 最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有 $2$ 个属性,这些属性的值用 $[1,10000]$ 之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。游戏进行到最后,lxhgww 遇到了终极 boss,这个终极 boss 很奇怪,攻击他的装备所使用的属性值必须从 $1$ 开始连续递增地攻击,才能对 boss 产生伤害。也就是说一开始的时候,lxhgww 只能使用某个属性值为 $1$ 的装备攻击 boss,然后只能使用某个属性值为 $2$ 的装备攻击 boss,然后只能使用某个属性值为 $3$ 的装备攻击 boss……以此类推。现在 lxhgww 想知道他最多能连续攻击 boss 多少次?

          +

          题意:lxhgww +最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有 \(2\) 个属性,这些属性的值用 \([1,10000]\) +之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。游戏进行到最后,lxhgww +遇到了终极 boss,这个终极 boss 很奇怪,攻击他的装备所使用的属性值必须从 +\(1\) 开始连续递增地攻击,才能对 boss +产生伤害。也就是说一开始的时候,lxhgww 只能使用某个属性值为 \(1\) 的装备攻击 +boss,然后只能使用某个属性值为 \(2\) +的装备攻击 boss,然后只能使用某个属性值为 \(3\) 的装备攻击 boss……以此类推。现在 lxhgww +想知道他最多能连续攻击 boss 多少次?

          说实话,这题一眼二分图。

          左侧点为属性值,右侧点为装备

          然后跑个匈牙利算法就好了

          如果匹配某个属性值失败就直接跑路退出循环即可

          -

          时间复杂度 $O(k^2),k=$ 最大属性值

          +

          时间复杂度 \(O(k^2),k=\) +最大属性值

          但是实际上很难跑到这个上界,所以还是蛮快的

          代码1:

          #include <bits/stdc++.h>
          @@ -551,29 +567,33 @@ 

          printf("%lld\n",n); return 0; }

          -

          但是这个题还有一个超强的做法

          +

          但是这个题还有一个超强的做法

          注意到我们并不关心选择了哪些装备

          直接把每个武器的两个属性值连无向边

          用并查集判断连通块是否存在环

          -

          对于一个大小为 $d$ 的连通块

          +

          对于一个大小为 \(d\) 的连通块

            -
          • 如果其不存在环,则一定有办法使得其中 $d-1$ 个点被选到

            -

            显然我们不会选择最大的那个点

            -
          • -
          • 如果其存在环,则一定可以使这 $d$ 个点被选到

            -
          • +
          • 如果其不存在环,则一定有办法使得其中 \(d-1\) 个点被选到

            +

            显然我们不会选择最大的那个点

          • +
          • 如果其存在环,则一定可以使这 \(d\) 个点被选到

          -

          当然,这些点不一定是连续的 $1,2,3\dots$

          -

          因此我们从 $1$ 到 $k+1$ (与上文同义)扫一遍

          +

          当然,这些点不一定是连续的 \(1,2,3\dots\)

          +

          因此我们从 \(1\)\(k+1\) (与上文同义)扫一遍

          -

          $k+1$ 的原因是防止正好这所有的属性值都可以选然后就没输出,见代码2

          +

          \(k+1\) +的原因是防止正好这所有的属性值都可以选然后就没输出,见代码2

          -

          对于不存在环的结点 $i$ ,只要看它是不是这个连通块最大的点即可

          +

          对于不存在环的结点 \(i\) +,只要看它是不是这个连通块最大的点即可

          这个最大点怎么判断呢?

          我们只要维护一下它所在连通块的大小

          显然最大的点会最后一次被扫到

          那么就搞定了

          -

          时间复杂度 $O(k)$ ,跑得飞快

          +

          时间复杂度 \(O(k)\) ,跑得飞快

          代码2:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -819,13 +839,13 @@ 

          @@ -1014,7 +1038,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p1772-zjoi2006-wu-liu-yun-shu-ti-jie/index.html b/2022/05/25/luo-gu-p1772-zjoi2006-wu-liu-yun-shu-ti-jie/index.html index 4cdc1d5236..6f3b02c4a1 100644 --- a/2022/05/25/luo-gu-p1772-zjoi2006-wu-liu-yun-shu-ti-jie/index.html +++ b/2022/05/25/luo-gu-p1772-zjoi2006-wu-liu-yun-shu-ti-jie/index.html @@ -476,17 +476,40 @@

          洛谷P1772 [ZJOI2006]物流运
          -

          洛谷P1772 [ZJOI2006]物流运输 题解

          题目链接:P1772 [ZJOI2006]物流运输

          +

          洛谷P1772 [ZJOI2006]物流运输 +题解

          +

          题目链接:P1772 +[ZJOI2006]物流运输

          -

          题意:物流公司要把一批货物从码头 A 运到码头 B。由于货物量比较大,需要 $n$ 天才能运完。货物运输过程中一般要转停好几个码头。

          +

          题意:物流公司要把一批货物从码头 A 运到码头 +B。由于货物量比较大,需要 \(n\) +天才能运完。货物运输过程中一般要转停好几个码头。

          物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪。由于各种因素的存在,有的时候某个码头会无法装卸货物。这时候就必须修改运输路线,让货物能够按时到达目的地。

          -

          但是修改路线是一件十分麻烦的事情,会带来额外的成本。因此物流公司希望能够订一个 $n$ 天的运输计划,使得总成本尽可能地小。

          +

          但是修改路线是一件十分麻烦的事情,会带来额外的成本。因此物流公司希望能够订一个 +\(n\) +天的运输计划,使得总成本尽可能地小。

          输入格式

          -

          第一行是四个整数 $n,m,k,e$ 。$n$ 表示货物运输所需天数,$m$ 表示码头总数,$k$ 表示每次修改运输路线所需成本,$e$ 表示航线条数。

          -

          接下来 $e$ 行每行是一条航线描述,包括了三个整数,依次表示航线连接的两个码头编号以及航线长度。其中码头 A 编号为 $1$ ,码头 B 编号为 $m$ 。单位长度的运输费用为 $1$。航线是双向的。

          -

          再接下来一行是一个整数 $d$,后面的 $d$ 行每行是三个整数 $p,a,b$。表示编号为 $p$ 的码头在 $[a,b]$ 天之内无法装卸货物。同一个码头有可能在多个时间段内不可用。但任何时间都存在至少一条从码头 A 到码头 B 的运输路线。

          +

          第一行是四个整数 \(n,m,k,e\)\(n\) 表示货物运输所需天数,\(m\) 表示码头总数,\(k\) 表示每次修改运输路线所需成本,\(e\) 表示航线条数。

          +

          接下来 \(e\) +行每行是一条航线描述,包括了三个整数,依次表示航线连接的两个码头编号以及航线长度。其中码头 +A 编号为 \(1\) ,码头 B 编号为 \(m\) 。单位长度的运输费用为 \(1\)。航线是双向的。

          +

          再接下来一行是一个整数 \(d\),后面的 +\(d\) 行每行是三个整数 \(p,a,b\)。表示编号为 \(p\) 的码头在 \([a,b]\) +天之内无法装卸货物。同一个码头有可能在多个时间段内不可用。但任何时间都存在至少一条从码头 +A 到码头 B 的运输路线。

          输出格式

          -

          包括了一个整数表示最小的总成本。
          总成本为 $n$ 天运输路线长度之和 $+k\times$改变运输路线的次数。

          +

          包括了一个整数表示最小的总成本。 总成本为 \(n\) 天运输路线长度之和 \(+k\times\)改变运输路线的次数。

          输入输出样例

          输入 #1

          5 5 10 8
          @@ -506,24 +529,39 @@ 

          32

          说明/提示

          -

          【数据范围】 对于 $100\%$ 的数据,$1 \le n \le 100$,$1\le m \le 20$。

          +

          【数据范围】 对于 \(100\%\) +的数据,\(1 \le n \le 100\)\(1\le m \le 20\)

          【样例输入说明】

          -

          img

          -

          上图依次表示第 $1$ 至第 $5$ 天的情况,阴影表示不可用的码头。

          +
          +img + +
          +

          上图依次表示第 \(1\) 至第 \(5\) 天的情况,阴影表示不可用的码头。

          【样例输出说明】

          -

          前三天走 $1 \to 4 \to 5$,后两天走 $1 \to 3 \to 5$,这样总成本为 $(2+2)\times 3+(3+2)\times 2+10=32$ 。

          +

          前三天走 \(1 \to 4 \to 5\),后两天走 +\(1 \to 3 \to 5\),这样总成本为 \((2+2)\times 3+(3+2)\times 2+10=32\)

          先不考虑图的问题

          -

          令 $s[i][j]$ 为第 $i$ 天到第 $j$ 天不换路的最小花费

          -

          设 $dp[i]$ 为前 $i$ 天的最小花费

          -

          则有

          -

          即前 $j-1$ 天都不改变路线,第 $j$ 天到第 $i$ 天走同一条路线

          +

          \(s[i][j]\) 为第 \(i\) 天到第 \(j\) 天不换路的最小花费

          +

          \(dp[i]\) 为前 \(i\) 天的最小花费

          +

          则有 \[ +dp[i]=\min_{1\le j\le i}(dp[i],dp[j-1]+s[i][j]\times(i-j+1)+k) +\] 即前 \(j-1\) +天都不改变路线,第 \(j\) 天到第 \(i\) 天走同一条路线

          那么直接dp就完了

          -

          现在问题是怎么求出 $s[i][j]$

          -

          因为要求最小花费,那么一定是这几天都可以走的结点所成的最短路的花费 $\times$ 天数( $i-j+1$ )

          +

          现在问题是怎么求出 \(s[i][j]\)

          +

          因为要求最小花费,那么一定是这几天都可以走的结点所成的最短路的花费 +\(\times\) 天数( \(i-j+1\)

          直接暴力跑dijkstra就好了

          -

          时间复杂度 $O(n^3m)$

          +

          时间复杂度 \(O(n^3m)\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -754,13 +792,13 @@ 

           上一篇

          @@ -1006,7 +1044,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p1858-duo-ren-bei-bao-ti-jie/index.html b/2022/05/25/luo-gu-p1858-duo-ren-bei-bao-ti-jie/index.html index 957954c200..e320b46b34 100644 --- a/2022/05/25/luo-gu-p1858-duo-ren-bei-bao-ti-jie/index.html +++ b/2022/05/25/luo-gu-p1858-duo-ren-bei-bao-ti-jie/index.html @@ -472,20 +472,29 @@

          洛谷P1858 多人背包 题解<
          -

          洛谷P1858 多人背包 题解

          题目链接:P1858 多人背包

          +

          洛谷P1858 多人背包 题解

          +

          题目链接:P1858 +多人背包

          题意:求01背包前k优解的价值和

          建议先去读一读《背包九讲》再来看

          -

          注意到朴素的 $01$ 背包是这样转移的

          -

          我们可以将其看作两个长度为 $1$ 的有序队列合并的结果

          -

          则考虑记录 $k$ 优解

          -

          在 $dp[j]$ 的基础上增加一维 $k$ ,即 $dp[j][k]$ ,表示 $k$ 优解

          -

          则每次将 $dp[j][k_1]$ 与 $dp[j-w[i]][k_2]+c[i]$ 合并

          -

          取最优的 $k$ 个解记录到 $dp[j][k_i]$ 即可

          +

          注意到朴素的 \(01\) 背包是这样转移的 +\[ +dp[j]=\max(dp[j],dp[j-w[i]]+v[i]) +\] 我们可以将其看作两个长度为 \(1\) 的有序队列合并的结果

          +

          则考虑记录 \(k\) 优解

          +

          \(dp[j]\) 的基础上增加一维 \(k\) ,即 \(dp[j][k]\) ,表示 \(k\) 优解

          +

          则每次将 \(dp[j][k_1]\)\(dp[j-w[i]][k_2]+c[i]\) 合并

          +

          取最优的 \(k\) 个解记录到 \(dp[j][k_i]\) 即可

          显然可以用归并排序来搞定

          -

          时间复杂度 $O(nmk)$

          +

          时间复杂度 \(O(nmk)\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -647,13 +656,13 @@ 

           上一篇

          - +
          - 洛谷P1640 [SCOI2010]连续攻击游戏 题解 + 洛谷P1772 [ZJOI2006]物流运输 题解 - 洛谷P1640 [SCOI2010]连续攻击游戏 题解 + 洛谷P1772 [ZJOI2006]物流运输 题解
          @@ -685,8 +694,8 @@

          算法 - - 图论 + + DP @@ -704,13 +713,13 @@

          - +
          - 洛谷P1772 [ZJOI2006]物流运输 题解 + 洛谷P1944 最长括号匹配 题解 - 洛谷P1772 [ZJOI2006]物流运输 题解 + 洛谷P1944 最长括号匹配 题解
          @@ -746,8 +755,8 @@

          DP - - 数据结构 + + 字符串

          @@ -903,7 +912,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p1944-zui-chang-gua-hao-pi-pei-ti-jie/index.html b/2022/05/25/luo-gu-p1944-zui-chang-gua-hao-pi-pei-ti-jie/index.html index d0691009d8..78a580dd41 100644 --- a/2022/05/25/luo-gu-p1944-zui-chang-gua-hao-pi-pei-ti-jie/index.html +++ b/2022/05/25/luo-gu-p1944-zui-chang-gua-hao-pi-pei-ti-jie/index.html @@ -476,7 +476,8 @@

          洛谷P1944 最长括号匹配
          -

          洛谷P1944 最长括号匹配 题解

          +

          洛谷P1944 最长括号匹配 题解

          +

          题意:对一个由(,),[,]括号组成的字符串,求出其中最长的括号匹配子串。具体来说,满足如下条件的字符串成为括号匹配的字符串:

          1.(),[]是括号匹配的字符串。

          2.若A是括号匹配的串,则(A),[A]是括号匹配的字符串。

          @@ -486,18 +487,30 @@

          \(dp[i]\) 表示以 \(s[i]\) 结尾的最长括号匹配长度

          +

          显然当 \(s[i]\) 为左括号时 \(dp[i]=0\)

          +

          \(s[i]\) 为右括号时,\(dp[i]\) 一定与 \(dp[i-1]\) 有关

          +

          容易发现 \(s[i-dp[i-1]]\)\(dp[i-1]\) 的起始字符

          +

          则当 \(s[i-dp[i-1]-1]\)\(s[i]\) 合法匹配时,有

          +

          \(dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2]\)

          +

          这里的 \(dp[i-dp[i-1]-2]\) +只需要一个例子就好理解了

          []([])
           123456
          -

          假设此时为 $dp[5]=2$ ,对于以 $s[5]$ 结尾的最长括号匹配显然是[]

          -

          然后到了 $dp[6]=6$ ,注意到此时 $s[3]$ 被合法利用了,

          -

          这样就造成了 $s[3]$ 之前的 $dp[6-dp[5]-2] = dp[2]$ 被重新利用

          +

          假设此时为 \(dp[5]=2\) ,对于以 +\(s[5]\) +结尾的最长括号匹配显然是[]

          +

          然后到了 \(dp[6]=6\) ,注意到此时 +\(s[3]\) 被合法利用了,

          +

          这样就造成了 \(s[3]\) 之前的 \(dp[6-dp[5]-2] = dp[2]\) 被重新利用

          应该很好理解了

          代码:

          #include <bits/stdc++.h>
          @@ -649,13 +662,13 @@ 

           上一篇

          @@ -706,13 +715,13 @@

          - +
          - 洛谷P2170 选学霸 题解 + 洛谷P2158 [SDOI2008] 仪仗队 题解 - 洛谷P2170 选学霸 题解 + 洛谷P2158 [SDOI2008] 仪仗队 题解
          @@ -744,8 +753,8 @@

          算法 - - DP + + 数学

          @@ -901,7 +910,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2158-sdoi2008-yi-zhang-dui-ti-jie/index.html b/2022/05/25/luo-gu-p2158-sdoi2008-yi-zhang-dui-ti-jie/index.html index da77fd1532..9733de4e4a 100644 --- a/2022/05/25/luo-gu-p2158-sdoi2008-yi-zhang-dui-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2158-sdoi2008-yi-zhang-dui-ti-jie/index.html @@ -472,24 +472,42 @@

          洛谷P2158 [SDOI2008] 仪仗队
          -

          洛谷P2158 [SDOI2008] 仪仗队 题解

          题目链接:P2158 [SDOI2008] 仪仗队

          +

          洛谷P2158 [SDOI2008] 仪仗队 +题解

          +

          题目链接:P2158 +[SDOI2008] 仪仗队

          -

          题意:作为体育委员,C 君负责这次运动会仪仗队的训练。仪仗队是由学生组成的 $N \times N$ 的方阵,为了保证队伍在行进中整齐划一,C 君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。

          -

          +

          题意:作为体育委员,C +君负责这次运动会仪仗队的训练。仪仗队是由学生组成的 \(N \times N\) +的方阵,为了保证队伍在行进中整齐划一,C +君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。

          +

          现在,C 君希望你告诉他队伍整齐时能看到的学生人数。

          -

          稍微画一画图,可以发现我们只要求对角线以下的个数,然后把答案乘 $2$ 加 $1$ 即可

          -

          +

          稍微画一画图,可以发现我们只要求对角线以下的个数,然后把答案乘 \(2\)\(1\) 即可

          +

          把连线看成直角三角形的斜边

          -

          可以发现图中对角线以下一共有 $9$ 个互不成比例的三角形 ( $(1,1)$ 那个算进去,$(1,0)$ 那个不算)

          -

          注:因为把 $(1,0)$ 和 $(1,1)$ 那个交换一下,答案不变(不太好讲,意会一下就行

          +

          可以发现图中对角线以下一共有 \(9\) +个互不成比例的三角形 ( \((1,1)\) +那个算进去,\((1,0)\) 那个不算)

          +

          注:因为把 \((1,0)\)\((1,1)\) +那个交换一下,答案不变(不太好讲,意会一下就行

          考虑怎样的三角形可以对答案有贡献

          显然是满足底和高互质的三角形,而我们只要计算底>高的情况

          -

          问题就转化为了求 $\sum\limits_{i=1}^{n-1} \varphi(i)$ ,这里的 $\varphi(i)$ 表示欧拉函数

          -

          注意是 $n-1$ 哦!(边数=点数-1)

          -

          那么搞一个前缀和就好了,注意特判 $n=1$ 的情况

          +

          问题就转化为了求 \(\sum\limits_{i=1}^{n-1} +\varphi(i)\) ,这里的 \(\varphi(i)\) 表示欧拉函数

          +

          注意是 \(n-1\) +哦!(边数=点数-1)

          +

          那么搞一个前缀和就好了,注意特判 \(n=1\) 的情况

          这题暴力可过,代码如下

          -

          时间复杂度 $O(n\sqrt{n})$

          +

          时间复杂度 \(O(n\sqrt{n})\)

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -521,7 +539,7 @@ 

          << phi[n-1]*2+1 << endl; return 0; }

          -

          当然 $O(n)$ 的欧拉筛肯定快啦

          +

          当然 \(O(n)\) 的欧拉筛肯定快啦

          代码在这

          #include <bits/stdc++.h>
           using namespace std;
          @@ -685,13 +703,13 @@ 

           上一篇

          @@ -738,13 +760,13 @@

          - +
          - 洛谷P2257 YY的GCD 题解 + 洛谷P2170 选学霸 题解 - 洛谷P2257 YY的GCD 题解 + 洛谷P2170 选学霸 题解
          @@ -776,8 +798,8 @@

          算法 - - 数学 + + DP

          @@ -933,7 +955,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2170-xuan-xue-ba-ti-jie/index.html b/2022/05/25/luo-gu-p2170-xuan-xue-ba-ti-jie/index.html index c61d8bdd0a..d9e333b8b2 100644 --- a/2022/05/25/luo-gu-p2170-xuan-xue-ba-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2170-xuan-xue-ba-ti-jie/index.html @@ -472,9 +472,16 @@

          洛谷P2170 选学霸 题解

          -

          洛谷P2170 选学霸 题解

          题目链接:P2170 选学霸

          +

          洛谷P2170 选学霸 题解

          +

          题目链接:P2170 +选学霸

          -

          题意:老师想从 $n$ 名学生中选 $m$ 人当学霸,但有 $k$ 人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的 $m$ 尽可能接近。

          +

          题意:老师想从 \(n\) 名学生中选 \(m\) 人当学霸,但有 \(k\) +人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的 +\(m\) 尽可能接近。

          把所有连通块的大小求出来,统计每个大小各有几个连通块

          然后二进制优化的多重背包,不过这道题是可行性问题

          @@ -651,13 +658,13 @@

           上一篇

          - +
          - 洛谷P2158 [SDOI2008] 仪仗队 题解 + 洛谷P2216 [HAOI2007]理想的正方形 题解 - 洛谷P2158 [SDOI2008] 仪仗队 题解 + 洛谷P2216 [HAOI2007]理想的正方形 题解
          @@ -746,8 +749,8 @@

          算法 - - 数学 + + 数据结构

          @@ -903,7 +906,7 @@

            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2216-haoi2007-li-xiang-de-zheng-fang-xing-ti-jie/index.html b/2022/05/25/luo-gu-p2216-haoi2007-li-xiang-de-zheng-fang-xing-ti-jie/index.html index 3614110edc..eeac85b7fe 100644 --- a/2022/05/25/luo-gu-p2216-haoi2007-li-xiang-de-zheng-fang-xing-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2216-haoi2007-li-xiang-de-zheng-fang-xing-ti-jie/index.html @@ -472,19 +472,28 @@

          洛谷P2216 [HAOI2007]理想的
          -

          洛谷P2216 [HAOI2007]理想的正方形 题解

          题目链接:P2216 [HAOI2007]理想的正方形

          +

          洛谷P2216 +[HAOI2007]理想的正方形 题解

          +

          题目链接:P2216 +[HAOI2007]理想的正方形

          -

          题意: 有一个 $a \times b$ 的整数组成的矩阵,现请你从中找出一个 $n \times n$的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

          +

          题意: 有一个 \(a \times +b\) 的整数组成的矩阵,现请你从中找出一个 \(n \times +n\)的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

          去年集训写过这题,也是去年集训唯一过了的(那个数据水

          upd.20220722 今年集训又来了。

          当时的解法稍微改了一下也可以过,放在本文最后

          正解:单调队列

          考虑先对每一行跑一次单调队列

          -

          构造一个 $m \times (n-k+1)$ 的矩阵(设 $n$ 行 $m$ 列)

          -

          再交换一下行列,跑出一个 $(m-k+1)\times (n-k+1)$ 的矩阵

          +

          构造一个 \(m \times (n-k+1)\) +的矩阵(设 \(n\)\(m\) 列)

          +

          再交换一下行列,跑出一个 \((m-k+1)\times +(n-k+1)\) 的矩阵

          然后就直接枚举就好了,其实没啥难的

          -

          时间复杂度 $O(n^2)$

          +

          时间复杂度 \(O(n^2)\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -564,7 +573,8 @@ 

          }

          这里是奇怪的解法

          构建一个二维的st表,和上面思路差不多

          -

          时间复杂度 $O(n^2\log n)$ ,也可以过

          +

          时间复杂度 \(O(n^2\log n)\) +,也可以过

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -787,13 +797,13 @@ 

           上一篇

          - +
          - 洛谷P2261 [CQOI2007]余数求和 题解 + 洛谷P2224 [HNOI2001]产品加工 题解 - 洛谷P2261 [CQOI2007]余数求和 题解 + 洛谷P2224 [HNOI2001]产品加工 题解
          @@ -874,8 +888,8 @@

          算法 - - 数学 + + DP

          @@ -1031,7 +1045,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2224-hnoi2001-chan-pin-jia-gong-ti-jie/index.html b/2022/05/25/luo-gu-p2224-hnoi2001-chan-pin-jia-gong-ti-jie/index.html index 6a5e2a0b3b..7dec76b54f 100644 --- a/2022/05/25/luo-gu-p2224-hnoi2001-chan-pin-jia-gong-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2224-hnoi2001-chan-pin-jia-gong-ti-jie/index.html @@ -472,18 +472,29 @@

          洛谷P2224 [HNOI2001]产品加
          -

          洛谷P2224 [HNOI2001]产品加工 题解

          题目链接:P2224 [HNOI2001]产品加工

          +

          洛谷P2224 [HNOI2001]产品加工 +题解

          +

          题目链接:P2224 +[HNOI2001]产品加工

          题意

          -

          某加工厂有 A、B 两台机器,来加工的产品可以由其中任何一台机器完成,或者两台机器共同完成。由于受到机器性能和产品特性的限制,不同的机器加工同一产品所需的时间会不同,若同时由两台机器共同进行加工,所完成任务又会不同。

          -

          某一天,加工厂接到 $n$ 个产品加工的任务,每个任务的工作量不尽一样。

          -

          你的任务就是:已知每个任务在 A 机器上加工所需的时间 $t_1$,B 机器上加工所需的时间 $t_2$ 及由两台机器共同加工所需的时间 $t_3$,请你合理安排任务的调度顺序,使完成所有 $n$ 个任务的总时间最少。

          +

          某加工厂有 A、B +两台机器,来加工的产品可以由其中任何一台机器完成,或者两台机器共同完成。由于受到机器性能和产品特性的限制,不同的机器加工同一产品所需的时间会不同,若同时由两台机器共同进行加工,所完成任务又会不同。

          +

          某一天,加工厂接到 \(n\) +个产品加工的任务,每个任务的工作量不尽一样。

          +

          你的任务就是:已知每个任务在 A 机器上加工所需的时间 \(t_1\),B 机器上加工所需的时间 \(t_2\) 及由两台机器共同加工所需的时间 \(t_3\),请你合理安排任务的调度顺序,使完成所有 +\(n\) 个任务的总时间最少。

          显然dp

          -

          设 $dp[i][j]$ 表示只考虑前 $i$ 个物品,第一台机器花了 $j$ 时间,第二台机器花的最少时间

          -

          不难发现

          -

          然后滚动数组一下就好了

          +

          \(dp[i][j]\) 表示只考虑前 \(i\) 个物品,第一台机器花了 \(j\) 时间,第二台机器花的最少时间

          +

          不难发现 \[ +dp[i][j]=\min(dp[i-1][j]+b[i],dp[i-1][j-a[i]],dp[i-1][j-c[i]]+c[i]) +\] 然后滚动数组一下就好了

          注意这题卡常很恶心(恼

          代码:

          #include <bits/stdc++.h>
          @@ -662,13 +673,13 @@ 

           上一篇

          - +
          - 洛谷P2260 [清华集训2012]模积和 题解 + 洛谷P2257 YY的GCD 题解 - 洛谷P2260 [清华集训2012]模积和 题解 + 洛谷P2257 YY的GCD 题解
          @@ -749,6 +760,10 @@

          + + 算法 + + 数学 @@ -906,7 +921,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2257-yy-de-gcd-ti-jie/index.html b/2022/05/25/luo-gu-p2257-yy-de-gcd-ti-jie/index.html index 296635149a..980fd96007 100644 --- a/2022/05/25/luo-gu-p2257-yy-de-gcd-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2257-yy-de-gcd-ti-jie/index.html @@ -472,32 +472,51 @@

          洛谷P2257 YY的GCD 题解

          -

          洛谷P2257 YY的GCD 题解

          +

          洛谷P2257 YY的GCD 题解

          +

          题意

          多组询问。

          -

          给定 $N, M$,求 $1 \leq x \leq N$,$1 \leq y \leq M$ 且 $\gcd(x,y)$ 为质数的 $(x,y)$ 有多少对。

          +

          给定 \(N, M\),求 \(1 \leq x \leq N\)\(1 \leq y \leq M\)\(\gcd(x,y)\) 为质数的 \((x,y)\) 有多少对。

          一句话题意

          -

          令 $\mathcal{P}$ 为所有素数组成的集合,求

          -

          不妨假设 $n\le m$

          +

          \(\mathcal{P}\) +为所有素数组成的集合,求 \[ +\sum_{i=1}^{n}\sum_{j=1}^{m}\left[\gcd(i,j)\in\mathcal{P}\right] +\] 不妨假设 \(n\le m\)

          然后么推推柿子

          友情提醒,这道题涉及经典操作:转化枚举量、构造莫比乌斯反演

          -

          不知道的可以先去看看别的题,比如这个题 link

          -

          注:$\land$ 表示逻辑与,可认为其等价于 “且”

          -

          令 $f(x) = \sum\limits_{k \mid x\land k \in \mathcal{P}}\mu\left(\dfrac{x}{k}\right)$

          +&\sum_{i=1}^{n}\sum_{j=1}^{m}\left[\gcd(i,j)\in\mathcal{P}\right] +\\= +&\sum_{k=1}^{n}\sum_{i=1}^{n}\sum_{j=1}^m\left[\gcd(i,j)=k\right][k +\in \mathcal{P}] +\\= +&\sum_{k=1}^{n}\sum_{i=1}^{\left\lfloor{\frac{n}{k}}\right\rfloor}\sum_{j=1}^{\left\lfloor{\frac{m}{k}}\right\rfloor}[\gcd(i,j)=1][k +\in \mathcal{P}] +\\=&\sum_{k=1}^{n}\sum_{i=1}^{\left\lfloor{\frac{n}{k}}\right\rfloor}\sum_{j=1}^{\left\lfloor{\frac{m}{k}}\right\rfloor}\sum_{d\mid +\gcd(i,j)}\mu(d)[k \in \mathcal{P}] +\\=&\sum_{k=1}^{n}\sum_{d=1}^{\left\lfloor{\frac{n}{k}}\right\rfloor}\mu(d)\left\lfloor{\dfrac{n}{kd}}\right\rfloor\left\lfloor{\dfrac{m}{kd}}\right\rfloor[k +\in \mathcal{P}] +\\=&\sum_{l=1}^{n}\left\lfloor{\dfrac{n}{l}}\right\rfloor\left\lfloor{\dfrac{m}{l}}\right\rfloor\sum_{k +\mid l\land k \in \mathcal{P}}\mu\left(\dfrac{l}{k}\right) +\end{aligned} +\] 注:\(\land\) +表示逻辑与,可认为其等价于 “且”

          +

          \(f(x) = \sum\limits_{k \mid x\land k +\in \mathcal{P}}\mu\left(\dfrac{x}{k}\right)\)

          然后这个题其实到这里就差不多了

          前面的用数论分块

          -

          后面那个 $\mu$ 可以在筛法里面求

          -

          时间复杂度 $O(n\log \log n + Q\sqrt{n})$

          +

          后面那个 \(\mu\) +可以在筛法里面求

          +

          时间复杂度 \(O(n\log \log n + +Q\sqrt{n})\)

          #include <bits/stdc++.h>
           using namespace std;
           // #define int long long
          @@ -589,44 +608,72 @@ 

          } return 0; }

          -

          这份代码很慢,跑了7.74s, 目前最优解就跑了 5.69s (截止20220511)

          -

          其实这个 $O(n \log\log n)$ 的枚举并没有必要

          -

          考虑在线性筛的过程中更新答案

          -

          设 $x=ab$ ,其中 $a$ 为 $x$ 的最小质因数

          -

          称 $p$ 为当前线性筛枚举的一个用于标记的素数(prime[j]

          +

          这份代码很慢,跑了7.74s, 目前最优解就跑了 +5.69s (截止20220511)

          +

          其实这个 \(O(n \log\log n)\) +的枚举并没有必要

          +

          考虑在线性筛的过程中更新答案 \[ +f(x) = \sum\limits_{k \mid x\land k \in +\mathcal{P}}\mu\left(\dfrac{x}{k}\right) +\]\(x=ab\) ,其中 \(a\)\(x\) 的最小质因数

          +

          \(p\) +为当前线性筛枚举的一个用于标记的素数(prime[j]

            -
          • 若 $x \in \mathcal{P}$ ,则显然 $f(x) = \mu(1) = 1$

            -
          • -
          • 若 $x\notin \mathcal P \land b \bmod a = 0$

            -

            则有 $x = a^p b^{\prime},b=a^{p-1}b^{\prime},p$ 为大于 $1$ 的正整数 (即 $x$ 有多个最小质因数 )

            +
          • \(x \in \mathcal{P}\) +,则显然 \(f(x) = \mu(1) = 1\)

          • +
          • \(x\notin \mathcal P \land b \bmod a += 0\)

            +

            则有 \(x = a^p +b^{\prime},b=a^{p-1}b^{\prime},p\) 为大于 \(1\) 的正整数 (即 \(x\) 有多个最小质因数 )

              -
            • 若 $b$ 没有多个最小质因数,则当且仅当 $p=a$ 时 $\mu\left(\dfrac{x}{p}\right) = \mu(b) \ne 0$

              -

              因此有 $f(x) = \mu(b)$

              -
            • -
            • 若 $b$ 含有多个最小质因数,则 $\forall p \in \mathcal{P}^{\prime},\mu\left(\dfrac{x}{p}\right)=0,\mathcal{P}^{\prime}$ 为当前筛出的素数集

              -

              不过我们无须单独考虑这种情况,因为此时仍然有 $\mu\left(\dfrac{x}{p}\right) = \mu(b)$

              -
            • +
            • \(b\) +没有多个最小质因数,则当且仅当 \(p=a\)\(\mu\left(\dfrac{x}{p}\right) = \mu(b) \ne +0\)

              +

              因此有 \(f(x) = \mu(b)\)

            • +
            • \(b\) +含有多个最小质因数,则 \(\forall p \in +\mathcal{P}^{\prime},\mu\left(\dfrac{x}{p}\right)=0,\mathcal{P}^{\prime}\) +为当前筛出的素数集

              +

              不过我们无须单独考虑这种情况,因为此时仍然有 \(\mu\left(\dfrac{x}{p}\right) = +\mu(b)\)

            • +
          • +
          • \(x\notin \mathcal{P} \land b \bmod +a \ne 0\)

            +

            则此时 \(x\) 有唯一且最小的质因数 +\(a\)

            +

            显然有 \(\mu(x) = -\mu(b)\) +,因为它仅仅多了一个本质不同的质因子

            +

            则对于 \(\mu\left(\dfrac{x}{p}\right) = +-\mu\left(\dfrac{b}{p}\right)\) 也是成立的

            +

            因此 \(f(b)\) 中的每一项在 \(f(x)\) 中都能找到对应的

            +

            注意到 \[ +f(x) = f(ab) =\sum\limits_{k \mid ab\land k \in +\mathcal{P}}\mu\left(\dfrac{ab}{k}\right) +\] 由于 \(\gcd(a,b)=1\) +(显然),则 \(f(x)\) 仅仅比 \(f(b)\) 多了一项 \(\mu\left(\dfrac{ab}{a}\right) = +\mu(b)\)

            +

            则有 \(f(x) = +-f(b)+\mu(b)\)

          - -
        1. 若 $x\notin \mathcal{P} \land b \bmod a \ne 0$

          -

          则此时 $x$ 有唯一且最小的质因数 $a$

          -

          显然有 $\mu(x) = -\mu(b)$ ,因为它仅仅多了一个本质不同的质因子

          -

          则对于 $\mu\left(\dfrac{x}{p}\right) = -\mu\left(\dfrac{b}{p}\right)$ 也是成立的

          -

          因此 $f(b)$ 中的每一项在 $f(x)$ 中都能找到对应的

          -

          注意到

          -

          由于 $\gcd(a,b)=1$ (显然),则 $f(x)$ 仅仅比 $f(b)$ 多了一项 $\mu\left(\dfrac{ab}{a}\right) = \mu(b)$

          -

          则有 $f(x) = -f(b)+\mu(b)$

          -
        2. - -

          综上,有

          -

          于是我们就可以在 $O(n + Q\sqrt{n})$ 的时间复杂度内解决这个问题了

          +\mu(1),&x \in \mathcal{P}, +\\\\\mu(b),&x \notin \mathcal{P} \land b \bmod a=0, +\\\\-f(b)+\mu(b),&x\notin \mathcal{P} \land b\bmod a \ne 0. +\end{cases} +\] 于是我们就可以在 \(O(n + +Q\sqrt{n})\) 的时间复杂度内解决这个问题了

          然后稍微卡一卡常,就跑的飞快

          对了,目前最优解其实是我的代码哦(虽然没啥用

          代码如下

          @@ -718,8 +765,10 @@

          }

          如果有什么错误或者问题的话,欢迎留言

          参考文献

          -

          [1] https://www.luogu.com.cn/blog/An-Amazing-Blog/solution-p2257

          -

          [2] https://siyuan.blog.luogu.org/solution-p2257

          +

          [1] https://www.luogu.com.cn/blog/An-Amazing-Blog/solution-p2257

          +

          [2] https://siyuan.blog.luogu.org/solution-p2257

          @@ -837,13 +886,13 @@

           上一篇

          - +
          - 洛谷P2224 [HNOI2001]产品加工 题解 + 洛谷P2260 [清华集训2012]模积和 题解 - 洛谷P2224 [HNOI2001]产品加工 题解 + 洛谷P2260 [清华集训2012]模积和 题解
          @@ -924,12 +973,8 @@

          - 算法 - - - - DP + + 数学

          @@ -1085,7 +1130,7 @@

            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2260-qing-hua-ji-xun-2012-mo-ji-he-ti-jie/index.html b/2022/05/25/luo-gu-p2260-qing-hua-ji-xun-2012-mo-ji-he-ti-jie/index.html index 6e11aaba5a..e02da2353d 100644 --- a/2022/05/25/luo-gu-p2260-qing-hua-ji-xun-2012-mo-ji-he-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2260-qing-hua-ji-xun-2012-mo-ji-he-ti-jie/index.html @@ -468,24 +468,35 @@

          洛谷P2260 [清华集训2012]
          -

          洛谷P2260 [清华集训2012]模积和 题解

          题目链接:P2260 [清华集训2012]模积和

          +

          洛谷P2260 +[清华集训2012]模积和 题解

          +

          题目链接:P2260 +[清华集训2012]模积和

          题意

          -

          -
          -

          根据容斥原理

          -

          然后推推柿子

          -

          有一个小的公式,这里就不证明了(逃

          -

          然后到这里就没啥难度了

          +&\sum_{i=1}^{n}\sum_{j=1}^{m}(n\bmod i)\times(m\bmod j) - +\sum_{i=1}^{n}(n\bmod i)\times(m\bmod i) +\\&=\sum_{i=1}^{n}\left(n-i\left\lfloor{\dfrac{n}{i}}\right\rfloor\right)\times\sum_{j=1}^{m}\left(m-j\left\lfloor{\dfrac{m}{j}}\right\rfloor\right) +- +\sum_{i=1}^{n}\left(n-i\left\lfloor{\dfrac{n}{i}}\right\rfloor\right)\times\left(m-i\left\lfloor{\dfrac{m}{i}}\right\rfloor\right) +\\&=\left(n^2-\sum_{i=1}^{n}i\left\lfloor{\dfrac{n}{i}}\right\rfloor\right)\times\left(m^2-\sum_{i=1}^{m}i\left\lfloor{\dfrac{m}{i}}\right\rfloor\right)-\sum_{i=1}^{n}\left(nm-mi\left\lfloor{\dfrac{n}{i}}\right\rfloor-ni\left\lfloor{\dfrac{m}{i}}\right\rfloor+i^2\left\lfloor{\dfrac{n}{i}}\right\rfloor\left\lfloor{\dfrac{m}{i}}\right\rfloor\right) +\\&=\left(n^2-\sum_{i=1}^{n}i\left\lfloor{\dfrac{n}{i}}\right\rfloor\right)\times\left(m^2-\sum_{i=1}^{m}i\left\lfloor{\dfrac{m}{i}}\right\rfloor\right)-n^2m+\sum_{i=1}^{n}\left(mi\left\lfloor{\dfrac{n}{i}}\right\rfloor+ni\left\lfloor{\dfrac{m}{i}}\right\rfloor-i^2\left\lfloor{\dfrac{n}{i}}\right\rfloor\left\lfloor{\dfrac{m}{i}}\right\rfloor\right) +\end{aligned} +\] 有一个小的公式,这里就不证明了(逃 \[ +\sum_{i=1}^{n}i^2 = \dfrac{n(n+1)(2n+1)}{6} +\] 然后到这里就没啥难度了

          考虑数论分块

          代码如下

          #include <bits/stdc++.h>
          @@ -637,13 +648,13 @@ 

           上一篇

          - +
          - 洛谷P2216 [HAOI2007]理想的正方形 题解 + 洛谷P2261 [CQOI2007]余数求和 题解 - 洛谷P2216 [HAOI2007]理想的正方形 题解 + 洛谷P2261 [CQOI2007]余数求和 题解
          @@ -728,8 +739,8 @@

          算法 - - 数据结构 + + 数学

          @@ -885,7 +896,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2261-cqoi2007-yu-shu-qiu-he-ti-jie/index.html b/2022/05/25/luo-gu-p2261-cqoi2007-yu-shu-qiu-he-ti-jie/index.html index 0112cf6930..8baf57f152 100644 --- a/2022/05/25/luo-gu-p2261-cqoi2007-yu-shu-qiu-he-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2261-cqoi2007-yu-shu-qiu-he-ti-jie/index.html @@ -472,22 +472,32 @@

          洛谷P2261 [CQOI2007]余数求
          -

          洛谷P2261 [CQOI2007]余数求和 题解

          题目链接:P2261 [CQOI2007]余数求和

          +

          洛谷P2261 [CQOI2007]余数求和 +题解

          +

          题目链接:P2261 +[CQOI2007]余数求和

          题意

          -

          给定 $n,k$ ,求

          -
          +

          给定 \(n,k\) ,求 \[ +G(n,k)= \sum_{i=1}^{n}k \bmod i +\]

          +

          考虑数论分块

          -

          注意到

          -

          若 $k < n$ ,显然 $\forall i > k$ 有 $\left\lfloor\dfrac{k}{i}\right\rfloor=0$

          -

          故可直接计算

          -

          若 $k \ge n$ ,则判断一下右边界不要超过 $n$ 即可

          +\sum_{i=1}^{n}k \bmod i &= +\sum_{i=1}^{n}\left(k-i\left\lfloor\dfrac{k}{i}\right\rfloor\right)\\ +&=nk-\sum_{i=1}^{n}i\left\lfloor\dfrac{k}{i}\right\rfloor +\end{aligned} +\] 若 \(k < n\) ,显然 \(\forall i > k\)\(\left\lfloor\dfrac{k}{i}\right\rfloor=0\)

          +

          故可直接计算 \[ +nk-\sum_{i=1}^{k}i\left\lfloor\dfrac{k}{i}\right\rfloor +\]\(k \ge n\) +,则判断一下右边界不要超过 \(n\) +即可

          代码如下

          #include <bits/stdc++.h>
           using namespace std;
          @@ -623,13 +633,13 @@ 

           上一篇

          - +
          - 洛谷P2216 [HAOI2007]理想的正方形 题解 + 洛谷P2260 [清华集训2012]模积和 题解 - 洛谷P2216 [HAOI2007]理想的正方形 题解 + 洛谷P2260 [清华集训2012]模积和 题解
          @@ -657,12 +667,8 @@

          - - 算法 - - - - 数据结构 + + 数学

          @@ -871,7 +877,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2350-haoi2012-wai-xing-ren-ti-jie/index.html b/2022/05/25/luo-gu-p2350-haoi2012-wai-xing-ren-ti-jie/index.html index c87cbc0d3e..cdea4d0ba4 100644 --- a/2022/05/25/luo-gu-p2350-haoi2012-wai-xing-ren-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2350-haoi2012-wai-xing-ren-ti-jie/index.html @@ -472,27 +472,44 @@

          洛谷P2350 [HAOI2012]外星人
          -

          洛谷P2350 [HAOI2012]外星人 题解

          题目链接:P2350 [HAOI2012]外星人

          +

          洛谷P2350 [HAOI2012]外星人 +题解

          +

          题目链接:P2350 +[HAOI2012]外星人

          题意

          多组数据

          -

          给出正整数 $N$ 的标准分解形式 $\prod_{i=1}^{m}p_i^{q_i}$

          -

          求最小的正整数 $x$ 使得 $\varphi^{x}(N) = 1$

          +

          给出正整数 \(N\) 的标准分解形式 +\(\prod_{i=1}^{m}p_i^{q_i}\)

          +

          求最小的正整数 \(x\) 使得 \(\varphi^{x}(N) = 1\)

          -

          注意到

          -

          对于含质因子 $2$ 的数,迭代一次会消去一个 $2$

          -

          对于不含质因子 $2$ 的数,迭代一次会产生至少一个 $2$

          -

          不难发现,答案与产生的 $2$ 的个数有关

          -

          则对于所有含质因子 $2$ 的 $N$ 答案即为迭代产生的所有的 $2$ 的个数

          -

          否则答案就是额外迭代一次后,再重复迭代产生的所有的 $2$ 的个数

          -

          设 $f(n)$ 为 $n$ 在迭代过程中产生的 $2$ 的个数 $\left(n=\sum_{i=1}^{s}p_i^{q_i}\right)$

          -

          当 $n$ 为素数时,有

          -

          当 $n$ 为合数时,有

          -

          这个东西可以用筛法去更新答案,预处理即可

          -

          时间复杂度 $O(\max(p))$

          +

          注意到 \[ +\varphi\left(\prod\limits_{i=1}^{m}p_i^{q_i}\right) = +\prod_{i=1}^{m}(p_i-1)\times p_i^{q_i-1} +\] 对于含质因子 \(2\) +的数,迭代一次会消去一个 \(2\)

          +

          对于不含质因子 \(2\) +的数,迭代一次会产生至少一个 \(2\)

          +

          不难发现,答案与产生的 \(2\) +的个数有关

          +

          则对于所有含质因子 \(2\)\(N\) 答案即为迭代产生的所有的 \(2\) 的个数

          +

          否则答案就是额外迭代一次后,再重复迭代产生的所有的 \(2\) 的个数

          +

          \(f(n)\)\(n\) 在迭代过程中产生的 \(2\) 的个数 \(\left(n=\sum_{i=1}^{s}p_i^{q_i}\right)\)

          +

          \(n\) 为素数时,有 \[ +f(n)=f(n-1) +\]\(n\) 为合数时,有 \[ +f(n)=\sum_{i=1}^{s} q_i f(p_i) +\] 这个东西可以用筛法去更新答案,预处理即可

          +

          时间复杂度 \(O(\max(p))\)

          代码如下

          #include <bits/stdc++.h>
           using namespace std;
          @@ -907,7 +924,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2522-haoi2011-problem-b-ti-jie/index.html b/2022/05/25/luo-gu-p2522-haoi2011-problem-b-ti-jie/index.html index eff48de846..e332291be5 100644 --- a/2022/05/25/luo-gu-p2522-haoi2011-problem-b-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2522-haoi2011-problem-b-ti-jie/index.html @@ -472,31 +472,47 @@

          洛谷P2522 [HAOI2011]Problem b
          -

          洛谷P2522 [HAOI2011]Problem b 题解

          题目链接:P2522 [HAOI2011]Problem b

          +

          洛谷P2522 [HAOI2011]Problem b +题解

          +

          题目链接:P2522 +[HAOI2011]Problem b

          -

          题意:对于给出的 $n$ 个询问,每次求有多少个数对 $(x,y)$,满足 $a \le x \le b$,$c \le y \le d$,且 $\gcd(x,y) = k$,$\gcd(x,y)$ 函数为 $x$ 和 $y$ 的最大公约数。

          +

          题意:对于给出的 \(n\) 个询问,每次求有多少个数对 \((x,y)\),满足 \(a +\le x \le b\)\(c \le y \le +d\),且 \(\gcd(x,y) = k\)\(\gcd(x,y)\) 函数为 \(x\)\(y\) 的最大公约数。

          一句话题意:

          -

          -

          根据容斥原理,不妨令

          -

          则答案为

          -

          然后推推柿子

          -

          考虑变换求和顺序,得

          -

          根据 $1\sim n$ 中 $d$ 的倍数有 $\left\lfloor\dfrac{n}{d}\right\rfloor$ 个,

          -

          以及 $\left\lfloor\dfrac{n}{kd}\right\rfloor=\left\lfloor\dfrac{\left\lfloor\frac{n}{k}\right\rfloor}{d}\right\rfloor$

          -

          可得

          -

          然后数论分块就好了

          -

          时间复杂度 $O(N+Q\sqrt{n})$

          +&\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=k] +\\&=\sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[\gcd(i,j)=1] +\\&=\sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}\sum_{d\mid +\gcd(i,j)}\mu(d) +\end{aligned} +\] 考虑变换求和顺序,得 \[ +\sum_{d=1}^{\min\left(\left\lfloor\frac{n}{k}\right\rfloor,\left\lfloor\frac{m}{k}\right\rfloor\right)}\mu(d)\sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}[d\mid +i]\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[d\mid j] +\] 根据 \(1\sim n\)\(d\) 的倍数有 \(\left\lfloor\dfrac{n}{d}\right\rfloor\) +个,

          +

          以及 \(\left\lfloor\dfrac{n}{kd}\right\rfloor=\left\lfloor\dfrac{\left\lfloor\frac{n}{k}\right\rfloor}{d}\right\rfloor\)

          +

          可得 \[ +\sum_{d=1}^{\min\left(\left\lfloor\frac{n}{k}\right\rfloor,\left\lfloor\frac{m}{k}\right\rfloor\right)}\mu(d)\left\lfloor\dfrac{n}{kd}\right\rfloor\left\lfloor\dfrac{m}{kd}\right\rfloor +\] 然后数论分块就好了

          +

          时间复杂度 \(O(N+Q\sqrt{n})\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -950,7 +966,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2568-gcd-ti-jie/index.html b/2022/05/25/luo-gu-p2568-gcd-ti-jie/index.html index 7d1e33a997..62560c8ad4 100644 --- a/2022/05/25/luo-gu-p2568-gcd-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2568-gcd-ti-jie/index.html @@ -472,22 +472,37 @@

          洛谷P2568 GCD 题解

          -

          洛谷P2568 GCD 题解

          题目链接:P2568 GCD

          +

          洛谷P2568 GCD 题解

          +

          题目链接:P2568 +GCD

          题意

          -

          给定正整数 $n$,求 $1\le x,y\le n$ 且 $\gcd(x,y)$为素数的数对 $(x,y)$ 有多少对。

          -

          $1 \le n\le 10^7$

          +

          给定正整数 \(n\),求 \(1\le x,y\le n\)\(\gcd(x,y)\)为素数的数对 \((x,y)\) 有多少对。

          +

          \(1 \le n\le 10^7\)

          正着去想不太好搞,那就反过来

          考虑一个素数对答案的贡献

          -

          显然所有满足条件的 $(x,y)$ 都可以写作 $(ap_i,bp_i)$ 的形式,其中 $\gcd(x,y)=p_i$

          -

          注意到 $\gcd(a,b)=1$

          -

          则对于每个素数 $p\ (p\le n)$ ,它对答案的贡献就是

          -

          其中,$k=\left\lfloor\dfrac{n}{p}\right\rfloor$, $\varphi(n)$ 为欧拉函数

          -

          $\times 2$ 是因为 $(x,y)$ 为有序点对,$-1$ 是因为 $(1,1)$ 会被多算一次

          -

          直接线性筛 $\varphi(n)$ 然后前缀和一下就好了

          -

          时间复杂度 $O(n)$ ,记得开 $\text{long long}$ 哦

          +

          显然所有满足条件的 \((x,y)\) +都可以写作 \((ap_i,bp_i)\) 的形式,其中 +\(\gcd(x,y)=p_i\)

          +

          注意到 \(\gcd(a,b)=1\)

          +

          则对于每个素数 \(p\ (p\le n)\) +,它对答案的贡献就是 \[ +2\sum\limits_{i=1}^{k}\varphi(i)-1 +\] 其中,\(k=\left\lfloor\dfrac{n}{p}\right\rfloor\), +\(\varphi(n)\) 为欧拉函数

          +

          \(\times 2\) 是因为 \((x,y)\) 为有序点对,\(-1\) 是因为 \((1,1)\) 会被多算一次

          +

          直接线性筛 \(\varphi(n)\) +然后前缀和一下就好了

          +

          时间复杂度 \(O(n)\) ,记得开 \(\text{long long}\)

          代码如下

          #include <bits/stdc++.h>
           using namespace std;
          @@ -902,7 +917,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2613-mo-ban-you-li-shu-qu-yu-ti-jie/index.html b/2022/05/25/luo-gu-p2613-mo-ban-you-li-shu-qu-yu-ti-jie/index.html index f0f2136f61..9bdeccbf92 100644 --- a/2022/05/25/luo-gu-p2613-mo-ban-you-li-shu-qu-yu-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2613-mo-ban-you-li-shu-qu-yu-ti-jie/index.html @@ -472,29 +472,45 @@

          洛谷P2613 【模板】有理
          -

          洛谷P2613 【模板】有理数取余 题解

          题目链接:P2613 【模板】有理数取余

          +

          洛谷P2613 【模板】有理数取余 +题解

          +

          题目链接:P2613 +【模板】有理数取余

          -

          题意:给出 $c=\frac{a}{b}$ ,求 $\frac{a}{b} \bmod 19260817$

          -

          $0 \le a \le 10^{10001},1\le b \le 10^{10001}$

          +

          题意:给出 \(c=\frac{a}{b}\) ,求 \(\frac{a}{b} \bmod 19260817\)

          +

          \(0 \le a \le 10^{10001},1\le b \le +10^{10001}\)

          -

          令 $m=19260817$

          -

          注意这里模意义下的有理数其实就是

          -

          简单推推柿子吧

          -

          显然有

          -

          -

          当 $b_0 \ne 0$ 即 $m \nmid b$ 时,有

          -

          那么exgcd一下就出来了

          -

          当 $b_0=0$ 即 $m \mid b$ 时,分类讨论

          +

          \(m=19260817\)

          +

          注意这里模意义下的有理数其实就是 \[ +\frac{a}{b} \pmod m = ab^{-1} \pmod m +\] 简单推推柿子吧

          +

          显然有 \[ +bc \equiv a \pmod{m} +\]

          +

          \[ +b_0=b \bmod m,a_0 = a \bmod m +\]

          +

          \(b_0 \ne 0\)\(m \nmid b\) 时,有 \[ +c \equiv a_0 b_0^{-1} \pmod{m} +\]

          +

          那么exgcd一下就出来了

          +

          \(b_0=0\)\(m \mid b\) 时,分类讨论

            -
          • 当 $m \mid a$ 时, $\forall c \in \Z,bc \equiv a \pmod{m}$ 恒成立,题目保证了没有这种情况
          • -
          • 当 $m\nmid a$ 时,$bc \equiv a \pmod{m}$ 无解
          • +
          • \(m \mid a\) 时, \(\forall c \in \Z,bc \equiv a \pmod{m}\) +恒成立,题目保证了没有这种情况
          • +
          • \(m\nmid a\) 时,\(bc \equiv a \pmod{m}\) 无解
          -

          因此特判 $b_0 = 0$ 的情况即可

          -

          注意读入 $a,b$ 的时候要一边读一边取模

          +

          因此特判 \(b_0 = 0\) 的情况即可

          +

          注意读入 \(a,b\) +的时候要一边读一边取模

          代码如下

          #include <bits/stdc++.h>
           using namespace std;
          @@ -901,7 +917,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2738-usaco4.1-li-ba-hui-lu-fence-loops-ti-jie/index.html b/2022/05/25/luo-gu-p2738-usaco4.1-li-ba-hui-lu-fence-loops-ti-jie/index.html index a94a0ab391..4ee1c1ee86 100644 --- a/2022/05/25/luo-gu-p2738-usaco4.1-li-ba-hui-lu-fence-loops-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2738-usaco4.1-li-ba-hui-lu-fence-loops-ti-jie/index.html @@ -472,9 +472,13 @@

          洛谷P2738 [USACO4.1]篱笆回
          -

          洛谷P2738 [USACO4.1]篱笆回路Fence Loops 题解

          题目链接:P2738 [USACO4.1]篱笆回路Fence Loops

          +

          洛谷P2738 +[USACO4.1]篱笆回路Fence Loops 题解

          +

          题目链接:P2738 +[USACO4.1]篱笆回路Fence Loops

          -

          题意:农夫布朗的牧场上的篱笆已经失去控制了。它们分成了1~200英尺长的线段。只有在线段的端点处才能连接两个线段,有时给定的一个端点上会有两个以上的篱笆。结果篱笆形成了一张网分割了布朗的牧场。布朗想将牧场恢复原样,出于这个考虑,他首先得知道牧场上哪一块区域的周长最小。 布朗将他的每段篱笆从1到N进行了标号(N=线段的总数)。他知道每段篱笆有如下属性:

          +

          题意:农夫布朗的牧场上的篱笆已经失去控制了。它们分成了1~200英尺长的线段。只有在线段的端点处才能连接两个线段,有时给定的一个端点上会有两个以上的篱笆。结果篱笆形成了一张网分割了布朗的牧场。布朗想将牧场恢复原样,出于这个考虑,他首先得知道牧场上哪一块区域的周长最小。 +布朗将他的每段篱笆从1到N进行了标号(N=线段的总数)。他知道每段篱笆有如下属性:

          该段篱笆的长度

          该段篱笆的一端所连接的另一段篱笆的标号

          该段篱笆的另一端所连接的另一段篱笆的标号

          @@ -494,10 +498,14 @@

          最小环就好了

          -

          时间复杂度 $O(n^3)$

          +

          考虑将第 \(i\) 条边的两个端点记为 +\(2i-1\)\(2i\) ,记录其连接的边的编号

          +

          然后 \(O(n^2)\) +枚举一下这些边,将本质相同端点合并(用冰茶姬并查集)

          +

          去个重然后跑个最小环就好了

          +

          时间复杂度 \(O(n^3)\)

          注意 INF 不可以开太大,不然会爆掉的

          边去重好像不必要,但是保险起见就去一去

          代码:

          @@ -961,7 +969,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2888-usaco07nov-cow-hurdles-s-ti-jie/index.html b/2022/05/25/luo-gu-p2888-usaco07nov-cow-hurdles-s-ti-jie/index.html index 295fe70d67..35e4f518d3 100644 --- a/2022/05/25/luo-gu-p2888-usaco07nov-cow-hurdles-s-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2888-usaco07nov-cow-hurdles-s-ti-jie/index.html @@ -472,14 +472,39 @@

          洛谷P2888 [USACO07NOV]Cow Hurd
          -

          洛谷P2888 [USACO07NOV]Cow Hurdles S 题解

          题目链接:P2888 [USACO07NOV]Cow Hurdles S

          +

          洛谷P2888 +[USACO07NOV]Cow Hurdles S 题解

          +

          题目链接:P2888 +[USACO07NOV]Cow Hurdles S

          -

          题意:Farmer John 想让她的奶牛准备郡级跳跃比赛,贝茜和她的伙伴们正在练习跨栏。她们很累,所以她们想消耗最少的能量来跨栏。 显然,对于一头奶牛跳过几个矮栏是很容易的,但是高栏却很难。于是,奶牛们总是关心路径上最高的栏的高度。 奶牛的训练场中有 $N (1 ≤ N ≤ 300)$ 个站台,分别标记为 $1\dots N$ 。所有站台之间有 $M (1 ≤ M ≤ 25,000)$ 条单向路径,第i条路经是从站台 $S_i$ 开始,到站台 $E_i$ ,其中最高的栏的高度为$H_i (1 ≤ H_i ≤ 1,000,000)$。无论如何跑,奶牛们都要跨栏。 奶牛们有 $T (1 ≤ T ≤ 40,000)$ 个训练任务要完成。第 $i$ 个任务包含两个数字 $A_i$ 和 $B_i (1 ≤ A_i ≤ N; 1 ≤ B_i ≤ N)$,表示奶牛必须从站台 $A_i$ 跑到站台 $B_i$ ,可以路过别的站台。奶牛们想找一条路径从站台 $A_i$ 到站台 $B_i$ ,使路径上最高的栏的高度最小。 你的任务就是写一个程序,计算出路径上最高的栏的高度的最小值。

          +

          题意:Farmer John +想让她的奶牛准备郡级跳跃比赛,贝茜和她的伙伴们正在练习跨栏。她们很累,所以她们想消耗最少的能量来跨栏。 +显然,对于一头奶牛跳过几个矮栏是很容易的,但是高栏却很难。于是,奶牛们总是关心路径上最高的栏的高度。 +奶牛的训练场中有 \(N (1 ≤ N ≤ 300)\) +个站台,分别标记为 \(1\dots N\) +。所有站台之间有 \(M (1 ≤ M ≤ 25,000)\) +条单向路径,第i条路经是从站台 \(S_i\) +开始,到站台 \(E_i\) +,其中最高的栏的高度为\(H_i (1 ≤ H_i ≤ +1,000,000)\)。无论如何跑,奶牛们都要跨栏。 奶牛们有 \(T (1 ≤ T ≤ 40,000)\) 个训练任务要完成。第 +\(i\) 个任务包含两个数字 \(A_i\)\(B_i (1 +≤ A_i ≤ N; 1 ≤ B_i ≤ N)\),表示奶牛必须从站台 \(A_i\) 跑到站台 \(B_i\) +,可以路过别的站台。奶牛们想找一条路径从站台 \(A_i\) 到站台 \(B_i\) ,使路径上最高的栏的高度最小。 +你的任务就是写一个程序,计算出路径上最高的栏的高度的最小值。

          分析题目可以发现这是一张具有非负权重的有向图

          我们只需要找到一条路径使得这条路径上所有的边权中最大值最小即可

          -

          Floyd解法

          别看这个 $n\le300$ ,跑起来还是飞快地(数据有点水)

          -

          容易推出方程 f[i][j]=min(f[i][j],max(f[i][k],f[k][j]));

          +

          Floyd解法

          +

          别看这个 \(n\le300\) +,跑起来还是飞快地(数据有点水)

          +

          容易推出方程 +f[i][j]=min(f[i][j],max(f[i][k],f[k][j]));

          不过我有点不放心就加了个小剪枝 qwq 虽然只快了3ms

          代码如下

          //Floyd
          @@ -526,15 +551,16 @@ 

          } return 0; }

          -

          其他解法

          +

          其他解法

          +

          关于SPFA,她死了。

          开个玩笑而已

          不过我们可以用理论复杂度较低的dijkstra

          -

          注意到 $N\le300$

          +

          注意到 \(N\le300\)

          那么离线解决即可

          -

          时间复杂度 $O(N^2\log M + T)$

          -

          不过实际情况下由于dijkstra常数较大,跑的甚至比Floyd慢…

          +

          时间复杂度 \(O(N^2\log M + T)\)

          +

          不过实际情况下由于dijkstra常数较大,跑的甚至比Floyd慢...

          代码如下

          #include <bits/stdc++.h>
           using namespace std;
          @@ -981,7 +1007,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p2912-usaco08oct-pasture-walking-g-ti-jie/index.html b/2022/05/25/luo-gu-p2912-usaco08oct-pasture-walking-g-ti-jie/index.html index d386da3b17..88601c3515 100644 --- a/2022/05/25/luo-gu-p2912-usaco08oct-pasture-walking-g-ti-jie/index.html +++ b/2022/05/25/luo-gu-p2912-usaco08oct-pasture-walking-g-ti-jie/index.html @@ -472,23 +472,46 @@

          洛谷P2912 [USACO08OCT]Pasture
          -

          洛谷P2912 [USACO08OCT]Pasture Walking G 题解

          题目链接:P2912 [USACO08OCT]Pasture Walking G

          +

          洛谷P2912 +[USACO08OCT]Pasture Walking G 题解

          +

          题目链接:P2912 +[USACO08OCT]Pasture Walking G

          -

          题意:有 $N(2\le N\le1000)$ 头奶牛,编号为 $1$ 到 $W$ ,它们正在同样编号为 $1$ 到 $N$ 的牧场上行走.为了方便,我们假设编号为 $i$ 的牛恰好在第 $i$ 号牧场上.

          -

          有一些牧场间每两个牧场用一条双向道路相连,道路总共有 $N - 1$ 条,奶牛可以在这些道路 上行走.第 $i$ 条道路把第 $A_i$ 个牧场和第 $B_i$ 个牧场连了起来 $(1 \le A_i \le N; 1 \le B_i \le N)$ ,而它的长度 是 $1 \le L_i \le 10,000$.在任意两个牧场间,有且仅有一条由若干道路组成的路径相连.也就是说,所有的道路构成了一棵树.

          -

          奶牛们十分希望经常互相见面.它们十分着急,所以希望你帮助它们计划它们的行程,你只 需要计算出 $Q(1 < Q < 1000)$ 对点之间的路径长度•每对点以一个询问 $p_1,p_2 (1 \le p_1 \le N; 1 \le p_2 \le N)$ 的形式给出.

          +

          题意:有 \(N(2\le +N\le1000)\) 头奶牛,编号为 \(1\) +到 \(W\) ,它们正在同样编号为 \(1\)\(N\) 的牧场上行走.为了方便,我们假设编号为 +\(i\) 的牛恰好在第 \(i\) 号牧场上.

          +

          有一些牧场间每两个牧场用一条双向道路相连,道路总共有 \(N - 1\) 条,奶牛可以在这些道路 上行走.第 +\(i\) 条道路把第 \(A_i\) 个牧场和第 \(B_i\) 个牧场连了起来 \((1 \le A_i \le N; 1 \le B_i \le N)\) +,而它的长度 是 \(1 \le L_i \le +10,000\).在任意两个牧场间,有且仅有一条由若干道路组成的路径相连.也就是说,所有的道路构成了一棵树.

          +

          奶牛们十分希望经常互相见面.它们十分着急,所以希望你帮助它们计划它们的行程,你只 +需要计算出 \(Q(1 < Q < 1000)\) +对点之间的路径长度•每对点以一个询问 \(p_1,p_2 +(1 \le p_1 \le N; 1 \le p_2 \le N)\) 的形式给出.

          按照解题的思路,首先想到的是Floyd

          你看它多简单多好用

          -

          然而它的时间复杂度是 $O(n^3)$

          +

          然而它的时间复杂度是 \(O(n^3)\)

          我们来观察一下Floyd

          for(int k=1; k<=n; k++)
               for(int i=1; i<=n; i++)
                   for(int j=1; j<=n; j++)
                       f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

          由于给定的是一棵树(且无向边),那么

          -

          $\forall u \in V$ 有且仅有 $2$ 个的 $v_1,v_2 \in V$ 使得 $(u,v_1) , (u,v_2) \in E$

          -

          且 $v_1,v_2$ 只可能是 $u$ 的父亲或儿子

          +

          \(\forall u \in V\) 有且仅有 \(2\) 个的 \(v_1,v_2 \in V\) 使得 \((u,v_1) , (u,v_2) \in E\)

          +

          \(v_1,v_2\) 只可能是 \(u\) 的父亲或儿子

          说人话就是每个结点最多连出去两个结点

          那么很多时候f[i][k]都是无效的枚举(都是INF啊)

          于是我们可以剪枝

          @@ -497,7 +520,8 @@

          if(f[i][k]!=INF) for(int j=1; j<=n; j++) f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

          -

          于是时间复杂度就降到了 $O(n^2)$

          +

          于是时间复杂度就降到了 \(O(n^2)\)

          代码如下 (好丑啊qwq)

          #include <bits/stdc++.h>
           using namespace std;
          @@ -908,7 +932,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p3188-hnoi2007-meng-huan-dao-bao-zhu-ti-jie/index.html b/2022/05/25/luo-gu-p3188-hnoi2007-meng-huan-dao-bao-zhu-ti-jie/index.html index b20a2f6819..0562ec546a 100644 --- a/2022/05/25/luo-gu-p3188-hnoi2007-meng-huan-dao-bao-zhu-ti-jie/index.html +++ b/2022/05/25/luo-gu-p3188-hnoi2007-meng-huan-dao-bao-zhu-ti-jie/index.html @@ -472,36 +472,81 @@

          洛谷P3188 [HNOI2007]梦幻岛
          -

          洛谷P3188 [HNOI2007]梦幻岛宝珠 题解

          题目链接:P3188 [HNOI2007]梦幻岛宝珠

          +

          洛谷P3188 +[HNOI2007]梦幻岛宝珠 题解

          +

          题目链接:P3188 +[HNOI2007]梦幻岛宝珠

          题意

          -

          给你 $n$ 颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过 $W$,且总价值最大,并输出最大的总价值。

          -

          【数据范围】
          对于 $100\%$ 的数据,$1\le n \le 100$,$1\le W,w_i,v_i \le 2^{30}$。
          保证每个 $w_i$ 能写成 $a \times 2^b\space (a,b \in \mathbb N)$ 的形式,$a \leq 10$ , $b \leq 30$,且答案不超过 $2^{30}$。

          +

          给你 \(n\) +颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过 +\(W\),且总价值最大,并输出最大的总价值。

          +

          【数据范围】 对于 \(100\%\) +的数据,\(1\le n \le 100\)\(1\le W,w_i,v_i \le 2^{30}\)。 保证每个 +\(w_i\) 能写成 \(a \times 2^b\space (a,b \in \mathbb N)\) +的形式,\(a \leq 10\) , \(b \leq 30\),且答案不超过 \(2^{30}\)

          -

          注:下文中将使用 $m$ 代替 $W$

          +

          注:下文中将使用 \(m\) 代替 +\(W\)

          这么大的背包容量,肯定不是朴素的背包问题

          -

          可以发现数据范围里强调了 “保证每个 $w_i$ 能写成 $a \times 2^b\space (a,b \in \mathbb N)$ 的形式”

          -

          尽管有

          -

          但是这里的 $a\le 10$ 却十分特殊,这意味着我们可以从这个角度思考解法

          +

          可以发现数据范围里强调了 "保证每个 \(w_i\) 能写成 \(a +\times 2^b\space (a,b \in \mathbb N)\) 的形式"

          +

          尽管有 \[ +\forall\, x \in \mathbb{N},\exist\, a,b \in \mathbb{N},\ \bold{s.t. }\ \ +x=a\times 2^b +\] 但是这里的 \(a\le 10\) +却十分特殊,这意味着我们可以从这个角度思考解法

          考虑利用泛化物品的思想

          -

          首先将每件物品按 $b$ 进行分组,在每一组内dp

          -

          设 $f[i][j]$ 为在所有满足 $b=i$ 的物品中选择若干(不妨设为 $k$ )件物品,且总体积 $j^{\prime}$ 满足

          -

          时的最大价值。

          -

          然后考虑合并泛化物品,即 $b$ 不同的物品之间的转移

          -

          令 $g[i][j]$ 表示仅使用 $b \in [0,i]$ 的物品,且 $b=i$ 的物品所占空间为 $j$ ,$b\in [0,i-1]$ 的物品所占空间为 $m\&(2^i-1)$ 时的最大价值

          -

          这里的 $\&$ 表示二进制按位与,不难发现 $m\&(2^i-1)$ 就是 $m$ 的第 $1$ 到 $i-1$ 位

          -

          转移方程如下

          -

          注:这里的第 $i$ 位是从第 $1$ 位开始数的

          +

          首先将每件物品按 \(b\) +进行分组,在每一组内dp

          +

          \(f[i][j]\) 为在所有满足 \(b=i\) 的物品中选择若干(不妨设为 \(k\) )件物品,且总体积 \(j^{\prime}\) 满足 \[ +\sum{a_k}\times 2^i \le j \times 2^i \Rightarrow \sum a_k \le j +\Rightarrow j^{\prime} \le j +\] 时的最大价值。

          +

          然后考虑合并泛化物品,即 \(b\) +不同的物品之间的转移

          +

          \(g[i][j]\) 表示仅使用 \(b \in [0,i]\) 的物品,且 \(b=i\) 的物品所占空间为 \(j\)\(b\in +[0,i-1]\) 的物品所占空间为 \(m\&(2^i-1)\) 时的最大价值

          +

          这里的 \(\&\) +表示二进制按位与,不难发现 \(m\&(2^i-1)\) 就是 \(m\) 的第 \(1\)\(i-1\)

          +

          转移方程如下 \[ +g[i][j]=\max_{0\le k \le j}(g[i][j],f[i][j-k]+g[i-1][\min(10n,2k + [m +\text{ 第 }i\text{ 位为 }1]]) +\] 注:这里的第 \(i\) 位是从第 +\(1\) 位开始数的

          写成代码就是

          g[i][j]=max(g[i][j],f[i][j-k]+g[i-1][min(10*n,k*2+((m>>(i-1))&1))]);
          -

          即从 $j\times 2^i$ 中抽出来 $k\times 2^i$ 的体积给 $b \in[0,i-1]$ 的物品

          -

          这里加上 $\min(10n,\dots)$ 的原因是 $(\dots)$ 处可能超出了 $\sum\limits_{0\le k \le i-1} j_k$ 的一个较宽松的上限 $10n$

          -

          但是这个上界并不容易达到,因此为了避免讨论,使用 $\min$ 即可

          -

          因此答案为 $g[m\text{ 的位数}][1]$

          -

          时间复杂度 $O(Qn^2\log m)$ ,但是常数巨大无比(别急,后面还有…

          +

          即从 \(j\times 2^i\) 中抽出来 \(k\times 2^i\) 的体积给 \(b \in[0,i-1]\) 的物品

          +

          这里加上 \(\min(10n,\dots)\) +的原因是 \((\dots)\) 处可能超出了 \(\sum\limits_{0\le k \le i-1} j_k\) +的一个较宽松的上限 \(10n\)

          +

          但是这个上界并不容易达到,因此为了避免讨论,使用 \(\min\) 即可

          +

          因此答案为 \(g[m\text{ +的位数}][1]\)

          +

          时间复杂度 \(O(Qn^2\log m)\) +,但是常数巨大无比(别急,后面还有...

          代码1:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -573,10 +618,14 @@ 

          \(10n\) +一般不容易达到(前面提到过

          因此我们会做很多无效枚举

          -

          可以开一个数组 $s[i]$ 表示所有 $b=i$ 的物品的 $a$ 之和

          -

          同时,注意到 $f$ 其实可以重复利用,考虑改变一下枚举顺序

          +

          可以开一个数组 \(s[i]\) 表示所有 +\(b=i\) 的物品的 \(a\) 之和

          +

          同时,注意到 \(f\) +其实可以重复利用,考虑改变一下枚举顺序

          这样就可以在 39ms 左右跑出来啦!

          代码2:

          #include <bits/stdc++.h>
          @@ -652,8 +701,10 @@ 

          https://www.luogu.com.cn/blog/youare251-1/solution-p3188

          -

          [2] https://www.luogu.com.cn/user/50047

          +

          [1] https://www.luogu.com.cn/blog/youare251-1/solution-p3188

          +

          [2] https://www.luogu.com.cn/user/50047

          @@ -1023,7 +1074,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p3297-sdoi2013-tao-kao-ti-jie/index.html b/2022/05/25/luo-gu-p3297-sdoi2013-tao-kao-ti-jie/index.html index 643dae7ae1..dba2549ba8 100644 --- a/2022/05/25/luo-gu-p3297-sdoi2013-tao-kao-ti-jie/index.html +++ b/2022/05/25/luo-gu-p3297-sdoi2013-tao-kao-ti-jie/index.html @@ -476,14 +476,28 @@

          洛谷P3297 [SDOI2013] 逃考
          -

          洛谷P3297 [SDOI2013] 逃考 题解

          题目链接:P3297 [SDOI2013] 逃考

          +

          洛谷P3297 [SDOI2013] 逃考 +题解

          +

          题目链接:P3297 +[SDOI2013] 逃考

          -

          题意:髙考又来了,对于不认真读书的小杨来讲,真不是个好消息。为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶、外公外婆、大舅、大嫂、阿姨……

          +

          题意:髙考又来了,对于不认真读书的小杨来讲,真不是个好消息。为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶、外公外婆、大舅、大嫂、阿姨......

          小杨实在是忍无可忍了,这种生活跟监狱有什么区别!为了他亲爱的小红,为了他的dota,他决定越狱!

          -

          假设小杨的家是个 $x_1\times y_1$ 的矩阵,左下角坐标为 $(0,0)$ ,右上角坐标为 $(x_1,y_1)$ 。小杨有 $n$ 个亲戚,驻扎在矩阵里(位置不同,且不在矩阵的边上)。小杨家里的每个地方都被亲戚监控着,而且只被距离最近的亲戚监控:

          -

          也就是说假设小杨所在的位置是 $(3,3)$ ,亲戚A在 $(3,0)$ , A距离小杨距离是 $3$;亲戚B在 $(6,7)$ ,则B距离小杨距离是 $5$ 。距离A<距离B,所以 $(3,3)$ 位置由A监控。

          +

          假设小杨的家是个 \(x_1\times y_1\) +的矩阵,左下角坐标为 \((0,0)\) +,右上角坐标为 \((x_1,y_1)\) 。小杨有 +\(n\) +个亲戚,驻扎在矩阵里(位置不同,且不在矩阵的边上)。小杨家里的每个地方都被亲戚监控着,而且只被距离最近的亲戚监控:

          +

          也就是说假设小杨所在的位置是 \((3,3)\) ,亲戚A在 \((3,0)\) , A距离小杨距离是 \(3\);亲戚B在 \((6,7)\) ,则B距离小杨距离是 \(5\) 。距离A<距离B,所以 \((3,3)\) 位置由A监控。

          如果“最近距离”出现同时有几个亲戚,那么那个位置同时被那几个亲戚监控。

          -

          给出小杨的坐标 $(x_0,y_0)$ 。因为被发现的人数越少,越狱成功的机会越大,所以小杨需要你设计一条越狱路线到达矩形的边上,且被发现的人数最少。

          +

          给出小杨的坐标 \((x_0,y_0)\) +。因为被发现的人数越少,越狱成功的机会越大,所以小杨需要你设计一条越狱路线到达矩形的边上,且被发现的人数最少。

          ps:小杨走的方向是任意的,也就是说路线上的任意位置需要是实数。

          保证一开始小杨只被一个亲戚监控着。

          注:这题目题面和数据有点问题,要注意特判掉那些在矩形外面的点

          @@ -509,21 +523,25 @@

          -

          本题解法:半平面交+最短路

          +

          本题解法:半平面交+最短路

          注意到每个亲戚都有一个管辖的范围

          -

          因此我们可以把每个亲戚向与其管辖范围相邻的亲戚连一条边权为 $1$ 的边

          +

          因此我们可以把每个亲戚向与其管辖范围相邻的亲戚连一条边权为 \(1\) 的边

          然后从第一个监管小杨的亲戚那里跑一下最短路就好了

          那么管辖范围怎么求呢?

          -

          注意到对于亲戚 $u,v$ ,当小杨从 $u$ 的管辖范围转移到 $v$ 的范围时

          +

          注意到对于亲戚 \(u,v\) ,当小杨从 +\(u\) 的管辖范围转移到 \(v\) 的范围时

          一定经过了一条分界线

          -

          这条分界线就是直线 $u-v$ 的中垂线(垂直平分线)

          +

          这条分界线就是直线 \(u-v\) +的中垂线(垂直平分线)

          这样,每个亲戚的管辖范围就是他与所有其他亲戚的中垂线的半平面交

          对于上面那个样例,我画了个图,(没画不合法的点)

          看在q779花了1个小时的分上,点个赞吧

          -

          +

          其实这个图形就是个泰森多边形(知道也没用

          那么这个题好像就没啥难度了啊

          -

          时间复杂度 $O(Qn^2\log n)$

          +

          时间复杂度 \(O(Qn^2\log n)\)

          代码如下

          #include <bits/stdc++.h>
           using namespace std;
          @@ -890,13 +908,13 @@ 

          感觉我前几篇题解的无解释推导有点不可读

          所以这篇还是写的清楚一点叭

          这题需要一个引理(感觉没必要记住证明的方法)

          -

          引理:设 $d(x)$ 为 $x$ 的约数个数,则有

          -
          -

          证明:摘自 https://siyuan.blog.luogu.org/solution-p3327

          +

          引理:设 \(d(x)\) +为 \(x\) 的约数个数,则有 \[ +d(ij)=\sum_{x \mid i}\sum_{y \mid j}[\gcd(x,y)=1] +\]

          +
          +

          证明:摘自 https://siyuan.blog.luogu.org/solution-p3327

          我们考虑把每个因子一一映射。

          -

          如果 $ij$ 的因子 $k$ 中有一个因子 $p^c$,$i$ 中有因子 $p^a$,$j$ 中有因子 $p^b$。我们规定:

          +

          如果 \(ij\) 的因子 \(k\) 中有一个因子 \(p^c\)\(i\) 中有因子 \(p^a\)\(j\) 中有因子 \(p^b\)。我们规定:

            -
          • 如果 $c\le a$,那么在 $i$ 中选择。
          • -
          • 如果 $c>a$,那么我们把 $c$ 减去 $a$,在 $j$ 中选择 $p^{c-a}$(在 jj 中选择 $p^e$ 表示的是 $p^{a+e}$ )
          • +
          • 如果 \(c\le a\),那么在 \(i\) 中选择。
          • +
          • 如果 \(c>a\),那么我们把 \(c\) 减去 \(a\),在 \(j\) 中选择 \(p^{c-a}\)(在 jj 中选择 \(p^e\) 表示的是 \(p^{a+e}\)
          -

          对于 $ij$ 的因子 $k$ 的其他因子同理。于是对于任何一个 $k$ 有一个唯一的映射,且每一个选择对应着唯一的 $k$。

          -

          通过如上过程,我们发现:对于 $ij$的因子 $k=\prod {p_i}^{c_i}$,我们不可能同时在 $i$ 和 $j$ 中选择 $p_i$(优先在 $i$ 中选择,如果不够就只在 $j$ 中选择不够的指数),故 $x$ 和 $y$ 必须互质。

          +

          对于 \(ij\) 的因子 \(k\) 的其他因子同理。于是对于任何一个 \(k\) +有一个唯一的映射,且每一个选择对应着唯一的 \(k\)

          +

          通过如上过程,我们发现:对于 \(ij\)的因子 \(k=\prod {p_i}^{c_i}\),我们不可能同时在 +\(i\)\(j\) 中选择 \(p_i\)(优先在 \(i\) 中选择,如果不够就只在 \(j\) 中选择不够的指数),故 \(x\)\(y\) 必须互质。

          等式得证。

          有了这个引理,就可以开始搞了

          -

          不妨假设 $n \le m$

          -

          代入

          -

          莫比乌斯反演

          -

          这个 $d \mid \gcd(x,y)$ 看着很难受,把它换掉

          -

          啊它怎么又回来了,不急,继续推

          -

          移一下

          -

          可以发现这个正整数 $x$ 的取值范围在 $[1,n]$

          -

          而它的出现次数其实就是 $[1,n]$ 中有多少个数有因数 $x$

          -

          也就是 $\sum_{x=1}^{n}[x \mid n] = \sum_{x=1}^{n}\left\lfloor\frac{n}{x}\right\rfloor$

          -

          $y$ 同理,则可以得到

          -

          考虑消掉后面那个东西

          -

          注意到 $d \mid \gcd(dx,dy)$ 恒成立,又因为 $\sum_{k\in K}a_k = \sum_{p(k) \in K}a_{p(k)}$

          -

          -

          整理一下就是

          -

          然后就可以数论分块啦!

          +

          不妨假设 \(n \le m\) \[ +\sum_{i=1}^{n}\sum_{j=1}^{m}d(ij) +\] 代入 \[ +\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y \mid j}[\gcd(x,y)=1] +\] 莫比乌斯反演 \[ +\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y \mid j}\sum_{d\mid +\gcd(x,y)}\mu(d) +\] 这个 \(d \mid \gcd(x,y)\) +看着很难受,把它换掉 \[ +\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y \mid +j}\sum_{d=1}^{n}\mu(d)[d\mid \gcd(x,y)] +\] 啊它怎么又回来了,不急,继续推

          +

          移一下 \[ +\sum_{d=1}^{n}\mu(d)\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y +\mid j}[d\mid \gcd(x,y)] +\] 可以发现这个正整数 \(x\) +的取值范围在 \([1,n]\)

          +

          而它的出现次数其实就是 \([1,n]\) +中有多少个数有因数 \(x\)

          +

          也就是 \(\sum_{x=1}^{n}[x \mid n] = +\sum_{x=1}^{n}\left\lfloor\frac{n}{x}\right\rfloor\)

          +

          \(y\) 同理,则可以得到 \[ +\sum_{d=1}^{n}\mu(d)\sum_{x=1}^{n}\sum_{y=1}^{m}\left\lfloor{\dfrac{n}{x}}\right\rfloor\left\lfloor{\dfrac{m}{y}}\right\rfloor[d\mid +\gcd(x,y)] +\] 考虑消掉后面那个东西

          +

          注意到 \(d \mid \gcd(dx,dy)\) +恒成立,又因为 \(\sum_{k\in K}a_k = \sum_{p(k) +\in K}a_{p(k)}\)

          +

          \[ +\sum_{d=1}^{n}\mu(d)\sum_{dx=1}^{n}\sum_{dy=1}^{m}\left\lfloor{\dfrac{n}{dx}}\right\rfloor\left\lfloor{\dfrac{m}{dy}}\right\rfloor +\] 整理一下就是 \[ +\sum_{d=1}^{n}\mu(d)\sum_{x=1}^{\left\lfloor{\frac{n}{d}}\right\rfloor}\left\lfloor{\dfrac{n}{dx}}\right\rfloor\sum_{y=1}^{\left\lfloor{\frac{m}{d}}\right\rfloor}\left\lfloor{\dfrac{m}{dy}}\right\rfloor +\] 然后就可以数论分块啦!

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -728,13 +776,13 @@ 

           上一篇

          @@ -980,7 +1028,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p3336-zjoi2013-hua-jiu-ti-jie/index.html b/2022/05/25/luo-gu-p3336-zjoi2013-hua-jiu-ti-jie/index.html index df4c18fc00..40e60642ff 100644 --- a/2022/05/25/luo-gu-p3336-zjoi2013-hua-jiu-ti-jie/index.html +++ b/2022/05/25/luo-gu-p3336-zjoi2013-hua-jiu-ti-jie/index.html @@ -472,21 +472,42 @@

          洛谷P3336 [ZJOI2013]话旧 题
          -

          洛谷P3336 [ZJOI2013]话旧 题解

          题目链接:P3336 [ZJOI2013]话旧

          +

          洛谷P3336 [ZJOI2013]话旧 题解

          +

          题目链接:P3336 +[ZJOI2013]话旧

          -

          题意:小林跟着银河队选手去了一趟宇宙比赛,耳濡目染,变得学术起来。回来后,他发现世界大变样了。比丘兽究级进化,成了凤凰兽;金先生因为发了一篇 paper,一跃成为教授,也成为了银河队选拔委员会成员。

          +

          题意:小林跟着银河队选手去了一趟宇宙比赛,耳濡目染,变得学术起来。回来后,他发现世界大变样了。比丘兽究级进化,成了凤凰兽;金先生因为发了一篇 +paper,一跃成为教授,也成为了银河队选拔委员会成员。

          一日,小林与金教授聊天。金教授回忆起过去的岁月,那些年他学过的电路原理。他曾经对一种三角波很感兴趣,并且进行了一些探究。小林感到很好奇,于是金教授就将课题形式化地说了一遍。

          -

          有一定义在 $[0,N]$ 的连续函数 $f(x)$,其中 $N$ 是整数,满足 $f(0)=f(N)=0$,它的所有极值点在整数处取到,且 $f(x)$ 的极小值均是 $0$。对于任意的 $0$ 到 $N-1$ 间的整数 $I$,$f(x)$ 在 $(I, I+1)$ 上是斜率为 $1$ 或 $-1$ 的一次函数。

          -

          金先生研究的是,若他知道其中 $K$ 个整点的函数值,那么:

          -
            +

            有一定义在 \([0,N]\) 的连续函数 +\(f(x)\),其中 \(N\) 是整数,满足 \(f(0)=f(N)=0\),它的所有极值点在整数处取到,且 +\(f(x)\)极小值均是 +\(0\)。对于任意的 \(0\)\(N-1\) 间的整数 \(I\)\(f(x)\)\((I, +I+1)\) 上是斜率为 \(1\)\(-1\) 的一次函数。

            +

            金先生研究的是,若他知道其中 \(K\) +个整点的函数值,那么:

            +
            1. 有多少个函数满足条件?
            2. -
            3. 满足条件的函数中,$f(x)$ 的最大值,最大能是多少?
            4. +
            5. 满足条件的函数中,\(f(x)\) +的最大值,最大能是多少?

            小林思考了一下,便想出了很好的算法。那么作为经过多年训练的你呢?

            输入格式

            -

            第一行包含两个用空格隔开的整数 $N,K$。接下来 $K$ 行,每行两个整数,表示 $x_i$ 和 $f(x_i)$。

            +

            第一行包含两个用空格隔开的整数 \(N,K\)。接下来 \(K\) 行,每行两个整数,表示 \(x_i\)\(f(x_i)\)

            输出格式

            -

            一行两个整数,分别对应两个问题的答案。考虑到第一问答案可能很大,你只要输出它除以 $19940417$ 的余数。

            +

            一行两个整数,分别对应两个问题的答案。考虑到第一问答案可能很大,你只要输出它除以 +\(19940417\) 的余数。

            输入输出样例

            输入 #1

            2 0
            @@ -507,38 +528,58 @@

            1 2

          说明/提示

            -
          • 对于 $10\%$ 的数据,$N \leq 10$。
          • -
          • 对于 $20\%$ 的数据,$N \leq 50$。
          • -
          • 对于 $30\%$ 的数据,$N \leq 100$,$K \leq 100$。
          • -
          • 对于 $50\%$ 的数据,$N \leq 10^3$,$K \leq 10^3$。
          • -
          • 对于 $70\%$ 的数据,$N \leq 10^5$。
          • -
          • 另有 $10\%$ 的数据,$K=0$。
          • -
          • 对于 $100\%$ 的数据,$0 \leq N \leq 10^9$,$0 \leq K \leq 10^6$。
          • +
          • 对于 \(10\%\) 的数据,\(N \leq 10\)
          • +
          • 对于 \(20\%\) 的数据,\(N \leq 50\)
          • +
          • 对于 \(30\%\) 的数据,\(N \leq 100\)\(K +\leq 100\)
          • +
          • 对于 \(50\%\) 的数据,\(N \leq 10^3\)\(K \leq 10^3\)
          • +
          • 对于 \(70\%\) 的数据,\(N \leq 10^5\)
          • +
          • 另有 \(10\%\) 的数据,\(K=0\)
          • +
          • 对于 \(100\%\) 的数据,\(0 \leq N \leq 10^9\)\(0 \leq K \leq 10^6\)
          -

          题目说了极小值为 $0$ ,说明每次下降都是直接一口气到底的

          +

          题目说了极小值为 \(0\) +,说明每次下降都是直接一口气到底的

          从文字中可以看出题目保证了有解(不然怎么输出最大值)

          -

          考虑设计状态 $dp[i][0/1]$ 表示是需要访问到 $i$ 结点时的路径是向上还是向下的

          +

          考虑设计状态 \(dp[i][0/1]\) +表示是需要访问到 \(i\) +结点时的路径是向上还是向下的

          -

          故方案数为 $2^2 = 4$

          - -
        3. $dp[i][1]\to dp[i+1][1]$

          -

          与上一种情况类似,将 $i+1$ 与其向下的路径看作一个点

          -

          右侧的齿孔可以扩展,而此时左侧的不可,故方案数为 $2^k$

          -
        4. -
        5. $dp[i][0] \to dp[i+1][1]$

          -

          考虑将 $i+1$ 与其向下的路径看作一个点

          +

          故方案数为 \(2^2 = 4\)

        6. +
        7. \(dp[i][1]\to dp[i+1][1]\)

          +

          与上一种情况类似,将 \(i+1\) +与其向下的路径看作一个点

          +

          右侧的齿孔可以扩展,而此时左侧的不可,故方案数为 \(2^k\)

        8. +
        9. \(dp[i][0] \to dp[i+1][1]\)

          +

          考虑将 \(i+1\) +与其向下的路径看作一个点

          则此时左侧的齿孔可以扩展,而右侧的不可以

          -

          故方案数为 $2^k$

          -
        10. +

          故方案数为 \(2^k\)

          -

          然而有些情况会导致 $k<0$ ,因此要特判一下

          -

          还有一些特殊的情况,比如 $i+1$ 恰好在 $i$ 的向上/下的路径上,也需要特判

          +

          然而有些情况会导致 \(k<0\) +,因此要特判一下

          +

          还有一些特殊的情况,比如 \(i+1\) +恰好在 \(i\) +的向上/下的路径上,也需要特判

          然后最大值的求解,根据贪心,一定先选掉所有的上升

          -

          设有 $a$ 个上升, $b$ 个下降,则有

          -

          则最大值为

          -

          但是要注意,如果可以选择向上,一定有 $dp[i][0]\ne0$

          -

          因此对于所有的 $i$ 到 $i+1$ 的转移,都额外计算一下先走到底再上升的情况,即

          -

          代码:

          +\end{cases} +\] 则最大值为 \[ +y_1+a = \dfrac{1}{2}(x_2+y_2-x_1 + y_1) +\] 但是要注意,如果可以选择向上,一定有 \(dp[i][0]\ne0\)

          +

          因此对于所有的 \(i\)\(i+1\) +的转移,都额外计算一下先走到底再上升的情况,即 \[ +\dfrac{1}{2}(x_2+y_2-x_1-y_1) +\] 代码:

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -774,13 +824,13 @@ 

           上一篇

          @@ -1026,7 +1076,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p3747-liu-sheng-lian-kao-2017-xiang-feng-shi-wen-hou-ti-jie/index.html b/2022/05/25/luo-gu-p3747-liu-sheng-lian-kao-2017-xiang-feng-shi-wen-hou-ti-jie/index.html index f768a3c29d..6e4d3caaab 100644 --- a/2022/05/25/luo-gu-p3747-liu-sheng-lian-kao-2017-xiang-feng-shi-wen-hou-ti-jie/index.html +++ b/2022/05/25/luo-gu-p3747-liu-sheng-lian-kao-2017-xiang-feng-shi-wen-hou-ti-jie/index.html @@ -476,38 +476,74 @@

          洛谷P3747 [六省联考 2017]
          -

          洛谷P3747 [六省联考 2017] 相逢是问候 题解

          题目链接:洛谷P3747 [六省联考 2017] 相逢是问候

          +

          洛谷P3747 [六省联考 +2017] 相逢是问候 题解

          +

          题目链接:洛谷P3747 +[六省联考 2017] 相逢是问候

          -

          题意:B 君希望以维护一个长度为 $n$ 的数组,这个数组的下标为从 $1$ 到 $n$ 的正整数。

          -

          一共有 $m$ 个操作,可以分为两种:

          +

          题意:B 君希望以维护一个长度为 \(n\) 的数组,这个数组的下标为从 \(1\)\(n\) 的正整数。

          +

          一共有 \(m\) +个操作,可以分为两种:

            -
          • 0 l r 表示将第 $l$ 个到第 $r$ 个数( $a_l,a_{l+1} …a_r$)中的每一个数 $a_i$ 替换为 $c^{a_i}$ ,即 $c$ 的 $a_i$ 次方,其中 $c$ 是输入的一个常数,也就是执行赋值 $a_i = c^{a_i}$。
          • -
          • 1 l r 求第 $l$ 个到第 $r$ 个数的和,也就是输出: $\sum_{i=l}^{r}a_i$
          • +
          • 0 l r 表示将第 \(l\) +个到第 \(r\) 个数( \(a_l,a_{l+1} ...a_r\))中的每一个数 \(a_i\) 替换为 \(c^{a_i}\) ,即 \(c\)\(a_i\) 次方,其中 \(c\) 是输入的一个常数,也就是执行赋值 \(a_i = c^{a_i}\)
          • +
          • 1 l r 求第 \(l\) +个到第 \(r\) 个数的和,也就是输出: +\(\sum_{i=l}^{r}a_i\)
          -

          因为这个结果可能会很大,所以你只需要输出结果 $\bmod \ p$ 的值即可。

          +

          因为这个结果可能会很大,所以你只需要输出结果 \(\bmod \ p\) 的值即可。

          -

          建议先去做一做 P4139P4145 ,这道题就是这俩的综合加强版本 题解在这里 link1 link2

          +

          建议先去做一做 P4139P4145 +,这道题就是这俩的综合加强版本 题解在这里 link1 +link2

          有个结论

          -

          $O(\varphi^* (p)=1)=O(\log p)$

          -

          注:这里左边指的是最小的一个正整数 $x$ 使得

          -

          这里不再证明,见上面的link1

          -

          不难发现,对于某个数至多修改 $O(\log p)$ 次就会变成一个常数

          +\end{aligned} +\] 这里不再证明,见上面的link1

          +

          不难发现,对于某个数至多修改 \(O(\log +p)\) 次就会变成一个常数

          考虑直接在线段树上暴力更新信息

          -

          更新的时候不要直接用快速幂,这样会多一个 $O(\log p)$

          -

          考虑预处理 $c^i$ 和 $c^{ik}$ ,然后就可以 $O(1)$ 查询了(这个就是光速幂的原理)

          -

          $k$ 一般取到 $\sqrt{p}$ ,这里我取了32768,即 $2^{15}$

          -

          然后 $p$ 是不变的,所以可以预处理一下 $\varphi^i(p)$

          +

          更新的时候不要直接用快速幂,这样会多一个 \(O(\log p)\)

          +

          考虑预处理 \(c^i\)\(c^{ik}\) ,然后就可以 \(O(1)\) 查询了(这个就是光速幂的原理)

          +

          \(k\) 一般取到 \(\sqrt{p}\) +,这里我取了32768,即 \(2^{15}\)

          +

          然后 \(p\) +是不变的,所以可以预处理一下 \(\varphi^i(p)\)

          其他的,详见代码

          -

          时间复杂度是 $O(n \log n \log p)$ 的

          -

          证明:(势能分析)

          -

          代码如下,写的很烂 qwq

          +

          时间复杂度是 \(O(n \log n \log p)\) +的

          +

          证明:(势能分析) \[ +O\left(2\sqrt{p}+\sqrt{p}{\log p}+n \times \dfrac{n \log p}{n} \times +\log n\right) = O(n \log n \log p) +\] 代码如下,写的很烂 qwq

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -650,10 +686,12 @@ 

          https://www.luogu.com.cn/blog/s-r-f/solution-p3747

          +

          [1] https://www.luogu.com.cn/blog/s-r-f/solution-p3747

          @@ -775,13 +813,13 @@

           上一篇

          - +
          - 洛谷P3336 [ZJOI2013]话旧 题解 + 洛谷P3327 [SDOI2015]约数个数和 题解 - 洛谷P3336 [ZJOI2013]话旧 题解 + 洛谷P3327 [SDOI2015]约数个数和 题解
          @@ -1023,7 +1061,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p3935-calculating-ti-jie/index.html b/2022/05/25/luo-gu-p3935-calculating-ti-jie/index.html index 265a0c51a8..0f162d8892 100644 --- a/2022/05/25/luo-gu-p3935-calculating-ti-jie/index.html +++ b/2022/05/25/luo-gu-p3935-calculating-ti-jie/index.html @@ -472,26 +472,32 @@

          洛谷P3935 Calculating 题解
          -

          洛谷P3935 Calculating 题解

          题目链接:P3935 Calculating

          +

          洛谷P3935 Calculating 题解

          +

          题目链接:P3935 +Calculating

          题意

          -

          若 $n$ 的分解质因数结果为 $\prod_{i=1}^{s}p_i^{k_i}$

          -

          令 $f(n) = \prod_{i=1}^{s}(k_i+1)$

          -

          -
          -

          根据容斥原理,可以把答案分为两个部分

          -

          然后推推柿子

          -

          于是有

          -

          考虑数论分块

          -

          时间复杂度 $O(\sqrt{r})$

          +\sum_{i=1}^{r}f(i) &= \sum_{i=1}^{r}\prod_{j=1}^{s_i}{(k_{ij}+1)} +\\&=\sum_{i=1}^{r}\sum_{d\mid i}1 +\\&=\sum_{i=1}^{r}\left\lfloor\dfrac{r}{i}\right\rfloor +\end{aligned} +\] 于是有 \[ +\sum_{i=1}^{n}f(i) = +\sum_{i=1}^{n}\left\lfloor{\dfrac{n}{i}}\right\rfloor +\] 考虑数论分块

          +

          时间复杂度 \(O(\sqrt{r})\)

          代码如下

          #include <bits/stdc++.h>
           using namespace std;
          @@ -889,7 +895,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p3985-bu-kai-xin-de-jin-ming-ti-jie/index.html b/2022/05/25/luo-gu-p3985-bu-kai-xin-de-jin-ming-ti-jie/index.html index 09854788ad..c663b1e310 100644 --- a/2022/05/25/luo-gu-p3985-bu-kai-xin-de-jin-ming-ti-jie/index.html +++ b/2022/05/25/luo-gu-p3985-bu-kai-xin-de-jin-ming-ti-jie/index.html @@ -472,21 +472,42 @@

          洛谷P3985 不开心的金明
          -

          洛谷P3985 不开心的金明 题解

          题目链接:P3985 不开心的金明

          +

          洛谷P3985 不开心的金明 题解

          +

          题目链接:P3985 +不开心的金明

          题意

          -

          金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说了不算(有很大的限制),而且不超过 $W$ 元钱。”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 $W$ 元。于是,他把每件物品规定了一个重要度整数 $p_i$ 表示。他还从因特网上查到了每件物品的价格 $v_i$ (都是整数元)。

          -

          妈妈看到购物单后进行了审查,要求购物单上所有的物品价格的极差(最贵的减去最便宜的)不超过$3$(当然金明至今不知道为什么会这样)。他希望在不超过 $W$ 元(可以等于 $W$ 元)的前提下,使购买的重要度总和 $\sum p_i$ 的最大。

          +

          金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说了不算(有很大的限制),而且不超过 +\(W\) +元钱。”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 +\(W\) +元。于是,他把每件物品规定了一个重要度整数 \(p_i\) +表示。他还从因特网上查到了每件物品的价格 \(v_i\) (都是整数元)。

          +

          妈妈看到购物单后进行了审查,要求购物单上所有的物品价格的极差(最贵的减去最便宜的)不超过\(3\)(当然金明至今不知道为什么会这样)。他希望在不超过 +\(W\) 元(可以等于 \(W\) 元)的前提下,使购买的重要度总和 \(\sum p_i\) 的最大。

          请你帮助金明设计一个满足要求的购物单,你只需要告诉我们重要度的最大的和。

          -

          数据保证:$\max(v_i)-\min(v_i) \le 3$

          -

          数据范围:$1 \le N \le 100 ,1\le p_i \le 10^7,1 \le W,v_i \le 10^9$

          +

          数据保证:\(\max(v_i)-\min(v_i) \le +3\)

          +

          数据范围:\(1 \le N \le 100 ,1\le p_i \le +10^7,1 \le W,v_i \le 10^9\)

          容易发现是01背包

          -

          这个极差不超过 $3$ 根本不用管,都保证了。

          -

          注意到这个费用很大但是极差很小,于是把所有的花费全部减去 $\min(v_i)$

          -

          但是这个 $W$ 巨大,不可能按照 $j=W \to 1$ 去枚举

          -

          于是我们找到 $\sum v_i^\prime$ ,从它开始枚举,并增加一维 $k$ 表示装了 $k$ 个物品

          -

          这样我们只要判断 $j+k\times\min(v_i) \le W$ 就知道我们枚举的背包容量是否合法了

          +

          这个极差不超过 \(3\) +根本不用管,都保证了。

          +

          注意到这个费用很大但是极差很小,于是把所有的花费全部减去 \(\min(v_i)\)

          +

          但是这个 \(W\) 巨大,不可能按照 +\(j=W \to 1\) 去枚举

          +

          于是我们找到 \(\sum v_i^\prime\) +,从它开始枚举,并增加一维 \(k\) +表示装了 \(k\) 个物品

          +

          这样我们只要判断 \(j+k\times\min(v_i) \le +W\) 就知道我们枚举的背包容量是否合法了

          代码:(变量名和上面的陈述完全不一样)

          #include <bits/stdc++.h>
           using namespace std;
          @@ -893,7 +914,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p4095-heoi2013-eden-de-xin-bei-bao-wen-ti-ti-jie/index.html b/2022/05/25/luo-gu-p4095-heoi2013-eden-de-xin-bei-bao-wen-ti-ti-jie/index.html index a07323e36a..f4e6647891 100644 --- a/2022/05/25/luo-gu-p4095-heoi2013-eden-de-xin-bei-bao-wen-ti-ti-jie/index.html +++ b/2022/05/25/luo-gu-p4095-heoi2013-eden-de-xin-bei-bao-wen-ti-ti-jie/index.html @@ -476,24 +476,44 @@

          洛谷P4095 [HEOI2013]Eden 的
          -

          洛谷P4095 [HEOI2013]Eden 的新背包问题 题解

          题目链接:P4095 [HEOI2013]Eden 的新背包问题

          +

          洛谷P4095 +[HEOI2013]Eden 的新背包问题 题解

          +

          题目链接:P4095 +[HEOI2013]Eden 的新背包问题

          题意

          -

          失忆的 Eden 总想努力地回忆起过去,然而总是只能清晰地记得那种思念的感觉,却不能回忆起她的音容笑貌。

          -

          记忆中,她总是喜欢给 Eden 出谜题:在 valentine’s day 的夜晚,两人在闹市中闲逛时,望着礼品店里精巧玲珑的各式玩偶,她突发奇想,问了 Eden 这样的一个问题:有 $n$ 个玩偶,每个玩偶有对应的价值、价钱,每个玩偶都可以被买有限次,在携带的价钱 $m$ 固定的情况下,如何选择买哪些玩偶以及每个玩偶买多少个,才能使得选择的玩偶总价钱不超过 $m$,且价值和最大。

          -

          众所周知的,这是一个很经典的多重背包问题,Eden 很快解决了,不过她似乎因为自己的问题被飞快解决感到了一丝不高兴,于是她希望把问题加难:多次询问,每次询问都将给出新的总价钱,并且会去掉某个玩偶(即这个玩偶不能被选择),再问此时的多重背包的答案(即前一段所叙述的问题)。

          +

          失忆的 Eden +总想努力地回忆起过去,然而总是只能清晰地记得那种思念的感觉,却不能回忆起她的音容笑貌。

          +

          记忆中,她总是喜欢给 Eden 出谜题:在 valentine's day +的夜晚,两人在闹市中闲逛时,望着礼品店里精巧玲珑的各式玩偶,她突发奇想,问了 +Eden 这样的一个问题:有 \(n\) +个玩偶,每个玩偶有对应的价值、价钱,每个玩偶都可以被买有限次,在携带的价钱 +\(m\) +固定的情况下,如何选择买哪些玩偶以及每个玩偶买多少个,才能使得选择的玩偶总价钱不超过 +\(m\),且价值和最大。

          +

          众所周知的,这是一个很经典的多重背包问题,Eden +很快解决了,不过她似乎因为自己的问题被飞快解决感到了一丝不高兴,于是她希望把问题加难:多次询问,每次询问都将给出新的总价钱,并且会去掉某个玩偶(即这个玩偶不能被选择),再问此时的多重背包的答案(即前一段所叙述的问题)。

          这下 Eden 犯难了,不过 Eden 不希望自己被难住,你能帮帮他么?

          数据范围:

            -
          • 对于 $100\%$ 的数据,保证 $1 \leq n \leq 1000$,$1 \leq q \leq 3\times 10^5$, $1 \leq a_i,b_i,c_i \leq 100$,$0 \leq d_i < n$,$0 \leq e_i \leq 1000$。
          • -
          • $\sum e_i \le 2\times 10^7$ (讨论区补充的)
          • +
          • 对于 \(100\%\) 的数据,保证 \(1 \leq n \leq 1000\)\(1 \leq q \leq 3\times 10^5\)\(1 \leq a_i,b_i,c_i \leq 100\)\(0 \leq d_i < n\)\(0 \leq e_i \leq 1000\)
          • +
          • \(\sum e_i \le 2\times 10^7\) +(讨论区补充的)
          -

          不难发现,如果不考虑第 $p$ 个物品

          -

          最大的价值其实就是 $1\sim p-1$ 和 $p+1 \sim n$ 的最大价值

          +

          不难发现,如果不考虑第 \(p\) +个物品

          +

          最大的价值其实就是 \(1\sim p-1\) 和 +\(p+1 \sim n\) 的最大价值

          考虑正反分别跑一遍多重背包,然后对于每个询问暴力合并泛化物品

          单调队列优化更快一点,当然二进制拆分也是可以过的。

          -

          二进制拆分写法,时间复杂度 $O(m \sum_{i=1}^{n}\log c_i + \sum e_i)$

          +

          二进制拆分写法,时间复杂度 \(O(m +\sum_{i=1}^{n}\log c_i + \sum e_i)\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -577,7 +597,8 @@ 

          } return 0; }

          -

          单调队列优化写法,时间复杂度 $O(nm+\sum e_i)$

          +

          单调队列优化写法,时间复杂度 \(O(nm+\sum +e_i)\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -1039,7 +1060,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p4139-shang-di-yu-ji-he-de-zheng-que-yong-fa-ti-jie/index.html b/2022/05/25/luo-gu-p4139-shang-di-yu-ji-he-de-zheng-que-yong-fa-ti-jie/index.html index 2de119d287..23837e7af4 100644 --- a/2022/05/25/luo-gu-p4139-shang-di-yu-ji-he-de-zheng-que-yong-fa-ti-jie/index.html +++ b/2022/05/25/luo-gu-p4139-shang-di-yu-ji-he-de-zheng-que-yong-fa-ti-jie/index.html @@ -472,50 +472,70 @@

          洛谷P4139 上帝与集合的
          -

          洛谷P4139 上帝与集合的正确用法 题解

          题目链接:P4139 上帝与集合的正确用法

          +

          洛谷P4139 +上帝与集合的正确用法 题解

          +

          题目链接:P4139 +上帝与集合的正确用法

          题意

          -

          有无穷数列

          -

          当 $n \to +\infty$ 时,计算

          -

          -

          可以证明 $a_n\bmod p$在 $n$ 足够大时为常数

          -

          多组数据 $1\le Q\le 10^3$ ,$1 \le p\le 10^7$

          +

          有无穷数列 \[ +a_0=1,a_n=2^{a_{n-1}} +\]\(n \to +\infty\) 时,计算 +\[ +a_n \bmod p +\]\[ +{2}^{ {2}^{ {2}^{ {.}^{ {.}^{ {.}^{2} } } } } } \bmod p +\] 可以证明 \(a_n\bmod p\)在 +\(n\) 足够大时为常数

          +

          多组数据 \(1\le Q\le 10^3\)\(1 \le p\le 10^7\)

          其实这个题很简单

          -

          根据扩展欧拉定理,有

          -

          显然当 $n$ 足够大时,有 $a_{n-1} > \varphi(p)$

          -

          -

          可以发现原问题转化为了求解

          -

          递归处理即可

          -

          关于这个递归为什么是 $O(\log p)$

          -

          显然每一次递归 $p$ 都会变成 $\varphi(p)$

          +\end{aligned} +\] 显然当 \(n\) 足够大时,有 +\(a_{n-1} > \varphi(p)\)

          +

          \[ +a_{n}\bmod p = 2^{a_{n-1}\bmod\, \varphi(p)+\varphi(p)}\bmod p +\] 可以发现原问题转化为了求解 \[ +a_{n-1} \bmod \varphi(p) + \varphi(p) +\] 递归处理即可

          +

          关于这个递归为什么是 \(O(\log +p)\)

          +

          显然每一次递归 \(p\) 都会变成 \(\varphi(p)\)

          根据欧拉函数的公式

          -

          设 $n=\prod_{i=1}^{s}p_i^{k_i}$

          -

          观察函数,可以发现

          +

          \(n=\prod_{i=1}^{s}p_i^{k_i}\) +\[ +\varphi(n) = n \prod\limits_{i=1}^s\left(\dfrac{p_i-1}{p_i}\right) +\] 观察函数,可以发现

            -
          • 当 $n$ 为偶数时,一定会把 $2$ 提出来变成 $1$ ,也就是至少除以 $2$
          • -
          • 当 $n$ 为奇数时,一定会把一个素因子提出来变成一个偶数,也就是产生了 $2$ 这个因子
          • -
          • 重复以上的过程直到 $n=1$ 结束
          • +
          • \(n\) 为偶数时,一定会把 \(2\) 提出来变成 \(1\) ,也就是至少除以 \(2\)
          • +
          • \(n\) +为奇数时,一定会把一个素因子提出来变成一个偶数,也就是产生了 \(2\) 这个因子
          • +
          • 重复以上的过程直到 \(n=1\) +结束
          -

          因此是 $O(\log p)$ 的

          -

          所以总的时间复杂度为 $O(Q\sqrt{p}\log p)$

          +

          因此是 \(O(\log p)\)

          +

          所以总的时间复杂度为 \(O(Q\sqrt{p}\log +p)\)

          -

          不用线性筛跑得反而快,因为 $p$ 蛮大的,用了就变成 $O(p+Q\log p)$ 了

          +

          不用线性筛跑得反而快,因为 \(p\) +蛮大的,用了就变成 \(O(p+Q\log p)\) +了

          -

          数比较大,快速幂可能会爆了 $\text{long long}$ ,所以要用龟速乘或者__int128

          +

          数比较大,快速幂可能会爆了 \(\text{long +long}\) ,所以要用龟速乘或者__int128

          代码如下

          #include <bits/stdc++.h>
           using namespace std;
          @@ -564,7 +584,8 @@ 

          } return 0; }

          -

          用筛法求 $\varphi(p)$ 跑了 1.52s,非筛法反倒就跑了114ms

          +

          用筛法求 \(\varphi(p)\) 跑了 +1.52s,非筛法反倒就跑了114ms

          @@ -934,7 +955,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p4141-xiao-shi-zhi-wu-ti-jie/index.html b/2022/05/25/luo-gu-p4141-xiao-shi-zhi-wu-ti-jie/index.html index 0d95d608f6..a13c621a74 100644 --- a/2022/05/25/luo-gu-p4141-xiao-shi-zhi-wu-ti-jie/index.html +++ b/2022/05/25/luo-gu-p4141-xiao-shi-zhi-wu-ti-jie/index.html @@ -472,42 +472,66 @@

          洛谷P4141 消失之物 题解<
          -

          洛谷P4141 消失之物 题解

          题目链接:P4141 消失之物

          +

          洛谷P4141 消失之物 题解

          +

          题目链接:P4141 +消失之物

          -

          题意:ftiasch 有 $n$ 个物品, 体积分别是 $w_1,w_2,\dots,w_n$ 。由于她的疏忽,第 $i$ 个物品丢失了。

          -

          “要使用剩下的 $n-1$ 物品装满容积为 $x$ 的背包,有几种方法呢?”——这是经典的问题了。

          -

          她把答案记为 $\text{cnt}(i,x)$,想要得到所有 $i \in [1,n] ,x \in [1,m]$ 的 $\text{cnt}(i,x)$ 表格。

          +

          题意:ftiasch 有 \(n\) 个物品, 体积分别是 \(w_1,w_2,\dots,w_n\) 。由于她的疏忽,第 +\(i\) 个物品丢失了。

          +

          “要使用剩下的 \(n-1\) 物品装满容积为 +\(x\) +的背包,有几种方法呢?”——这是经典的问题了。

          +

          她把答案记为 \(\text{cnt}(i,x)\),想要得到所有 \(i \in [1,n] ,x \in [1,m]\)\(\text{cnt}(i,x)\) 表格。

          只需要输出末位数字。

          -

          设 $f[i][j]$ 为只用前 $i$ 件物品,不考虑删除任何物品时,恰好装满容量为 $j$ 的背包的方案数,显然有

          -

          滚动数组一下就是

          +

          \(f[i][j]\) 为只用前 \(i\) +件物品,不考虑删除任何物品时,恰好装满容量为 \(j\) 的背包的方案数,显然有 \[ +f[i][j]=f[i-1][j]+f[i-1][j-w[i]] +\] 滚动数组一下就是

          f[0]=1;
           for(int i=1; i<=n; i++)
           	for(int j=m; j>=w[i]; j--)
           		f[j]+=f[j-w[i]];
          -

          那么现在求出来的 $f[i]$ 也就是原来的 $f[n][i]$ 了

          -

          于是设 $g[i][j]$ 为不考虑物品 $i$ 的贡献恰好装满容量为 $j$ 的背包的方案数

          -

          首先容易想到一个看起来正确的柿子(其实是错的)

          -

          为什么错呢?因为 $f[n][j-w[i]]$ 中也可能含有 $w[i]$ 的贡献!

          -

          那么其实稍微改一改就对了

          -

          此时的 $g[i][j-w[i]]$ 恰好删除了物品 $i$ 的贡献,不错

          -

          注意这个 $g[i][j]$ 需要 $j$ 顺推,因为我们需要 $g[i][j-w[i]]$

          +

          那么现在求出来的 \(f[i]\) +也就是原来的 \(f[n][i]\)

          +

          于是设 \(g[i][j]\) 为不考虑物品 +\(i\) 的贡献恰好装满容量为 \(j\) 的背包的方案数

          +

          首先容易想到一个看起来正确的柿子(其实是错的) \[ +g[i][j]=f[n][j]-f[n][j-w[i]] +\] 为什么错呢?因为 \(f[n][j-w[i]]\) 中也可能含有 \(w[i]\) 的贡献!

          +

          那么其实稍微改一改就对了 \[ +g[i][j]=f[n][j]-g[i][j-w[i]] +\] 此时的 \(g[i][j-w[i]]\) +恰好删除了物品 \(i\) 的贡献,不错

          +

          注意这个 \(g[i][j]\) 需要 \(j\) 顺推,因为我们需要 \(g[i][j-w[i]]\)

          然后这个柿子还是不够精简

          -

          注意到我们其实并不需要记录 $i$ 这一状态,因为我们其实不需要保存这些信息

          -

          加上之前的滚动数组优化,柿子可以化简为

          -

          嗯,其实还可以化简。

          +f[j]-g[j-w[i]],&j \ge w[i], +\\f[j],&\text{default.} +\end{cases} +\] 嗯,其实还可以化简。

          memcpy(g,f,sizeof(f));
           for(int j=w[i]; j<=m; j++)
           	g[j]-=g[j-w[i]];
          -

          时间复杂度 $O(nm)$

          +

          时间复杂度 \(O(nm)\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -909,7 +933,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p4145-shang-di-zao-ti-de-qi-fen-zhong-2-hua-shen-you-li-ge-guo-ti-jie/index.html b/2022/05/25/luo-gu-p4145-shang-di-zao-ti-de-qi-fen-zhong-2-hua-shen-you-li-ge-guo-ti-jie/index.html index 08ef9d7571..11ff862b05 100644 --- a/2022/05/25/luo-gu-p4145-shang-di-zao-ti-de-qi-fen-zhong-2-hua-shen-you-li-ge-guo-ti-jie/index.html +++ b/2022/05/25/luo-gu-p4145-shang-di-zao-ti-de-qi-fen-zhong-2-hua-shen-you-li-ge-guo-ti-jie/index.html @@ -476,31 +476,40 @@

          洛谷P4145 上帝造题的七
          -

          洛谷P4145 上帝造题的七分钟 2 / 花神游历各国 题解

          题目链接:P4145 上帝造题的七分钟 2 / 花神游历各国

          +

          洛谷P4145 +上帝造题的七分钟 2 / 花神游历各国 题解

          +

          题目链接:P4145 +上帝造题的七分钟 2 / 花神游历各国

          题意:维护一个数据结构

          -
            -
          1. 区间sqrt()(对每个数开平方+下取整)

            -
          2. -
          3. 询问区间和

            -
          4. +
              +
            1. 区间sqrt()(对每个数开平方+下取整)

            2. +
            3. 询问区间和

          -

          $\sqrt{a+b} \ne \sqrt{a}+\sqrt{b}$

          +

          \(\sqrt{a+b} \ne +\sqrt{a}+\sqrt{b}\)

          因此无法用线段树维护

          -

          那么怎么搞呢?注意到数据范围中,每个数都为正整数,且不超过 $10^{12}$

          +

          那么怎么搞呢?注意到数据范围中,每个数都为正整数,且不超过 \(10^{12}\)

          -

          $\sqrt{1}=1$

          -

          $\left(10^{12}\right)^{2^{-6}} \approx 1$

          +

          \(\sqrt{1}=1\)

          +

          \(\left(10^{12}\right)^{2^{-6}} \approx +1\)

          -

          可以发现,对于一个数,最多进行 $6$ 次sqrt()操作,它就变成 $1$ 不变了

          +

          可以发现,对于一个数,最多进行 \(6\) +次sqrt()操作,它就变成 \(1\) 不变了

          于是我们还是用线段树,每次修改直接暴力递归到叶子节点进行修改

          诶?不是说不能用线段树吗?怎么又用了?

          -

          因为区间和是可以用线段树维护的呀!qwq 只不过这个开平方没法维护,直接“暴力”

          +

          因为区间和是可以用线段树维护的呀!qwq +只不过这个开平方没法维护,直接“暴力”

          根据势能分析,可知

          -

          时间复杂度( $n,m$ 同阶 )

          -

          代码如下

          +

          时间复杂度( \(n,m\) 同阶 ) \[ +O\left(n+n\times \dfrac{6n}n\log n\right) = O(n + 6n \log n) = O(n\log +n) +\] 代码如下

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -966,7 +975,7 @@ 

            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p4158-scoi2009-fen-shua-jiang-ti-jie/index.html b/2022/05/25/luo-gu-p4158-scoi2009-fen-shua-jiang-ti-jie/index.html index fc3add0223..46d3129ab7 100644 --- a/2022/05/25/luo-gu-p4158-scoi2009-fen-shua-jiang-ti-jie/index.html +++ b/2022/05/25/luo-gu-p4158-scoi2009-fen-shua-jiang-ti-jie/index.html @@ -472,21 +472,35 @@

          洛谷P4158 [SCOI2009]粉刷匠
          -

          洛谷P4158 [SCOI2009]粉刷匠 题解

          题目链接:P4158 [SCOI2009]粉刷匠

          +

          洛谷P4158 [SCOI2009]粉刷匠 +题解

          +

          题目链接:P4158 +[SCOI2009]粉刷匠

          -

          题意:windy有 N 条木板需要被粉刷。 每条木板被分为 M 个格子。 每个格子要被刷成红色或蓝色。

          -

          windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。 每个格子最多只能被粉刷一次。

          +

          题意:windy有 N 条木板需要被粉刷。 每条木板被分为 M +个格子。 每个格子要被刷成红色或蓝色。

          +

          windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。 +每个格子最多只能被粉刷一次。

          如果windy只能粉刷 T 次,他最多能正确粉刷多少格子?

          一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。

          比较简单的dp

          -

          设 $f[i][j]$ 为前 $i$ 块木板粉刷 $j$ 次的情况下能正确粉刷的最大格子数

          -

          设 $g[i][j][k]$ 为第 $i$ 条木板粉刷 $j$ 次且只涂了前 $k$ 个格子的情况下能正确粉刷的最大格子数

          -

          设 $S[i][j]$ 为第 $i$ 条木板前 $j$ 个格子的蓝色格子数

          -

          不难发现

          -

          时间复杂度 $O(n^4+n^2T)$ ( $n,m$ 同阶)

          +\\g[i][j][k]=\max(g[i][j][k],g[i][j-1][l]+\max(S[i][k]-S[i][l],k-l-(S[i][k]-S[i][l]))) +\] 时间复杂度 \(O(n^4+n^2T)\) ( +\(n,m\) 同阶)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -896,7 +910,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p4310-jue-shi-hao-ti-ti-jie/index.html b/2022/05/25/luo-gu-p4310-jue-shi-hao-ti-ti-jie/index.html index cca1fd7346..c852bcf1d7 100644 --- a/2022/05/25/luo-gu-p4310-jue-shi-hao-ti-ti-jie/index.html +++ b/2022/05/25/luo-gu-p4310-jue-shi-hao-ti-ti-jie/index.html @@ -472,15 +472,29 @@

          洛谷P4310 绝世好题 题解<
          -

          洛谷P4310 绝世好题 题解

          题目链接:P4310 绝世好题

          +

          洛谷P4310 绝世好题 题解

          +

          题目链接:P4310 +绝世好题

          -

          题意:给定一个长度为 $n$ 的数列 $a_i$ ,求 $a_i$ 的子序列 $b_i$ 的最长长度 $k$,满足 $b_i \& b_{i-1} \ne 0$,其中 $2\leq i\leq k$, $\&$ 表示位运算取与。

          +

          题意:给定一个长度为 \(n\) 的数列 \(a_i\) ,求 \(a_i\) 的子序列 \(b_i\) 的最长长度 \(k\),满足 \(b_i +\& b_{i-1} \ne 0\),其中 \(2\leq +i\leq k\)\(\&\) +表示位运算取与。

          -

          设 $dp[i][j]$ 为前 $i$ 个数的每个子序列的最后一个数第 $j$ 位为 $1$ 的最大长度

          +

          \(dp[i][j]\) 为前 \(i\) 个数的每个子序列的最后一个数第 \(j\) 位为 \(1\) 的最大长度

          然后对于每个数枚举一遍dp,再转移一遍dp即可

          注意到这个递推式可以滚动数组,然后搞一搞就好了

          -

          答案就是 $\max(dp[i][j])$

          -

          时间复杂度 $O(n \log \max(a_i))$

          +

          答案就是 \(\max(dp[i][j])\)

          +

          时间复杂度 \(O(n \log +\max(a_i))\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -874,7 +888,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p4342-ioi1998-polygon-ti-jie/index.html b/2022/05/25/luo-gu-p4342-ioi1998-polygon-ti-jie/index.html index 44df27a7ec..2f3fdf26fc 100644 --- a/2022/05/25/luo-gu-p4342-ioi1998-polygon-ti-jie/index.html +++ b/2022/05/25/luo-gu-p4342-ioi1998-polygon-ti-jie/index.html @@ -472,37 +472,53 @@

          洛谷P4342 [IOI1998]Polygon 题
          -

          洛谷P4342 [IOI1998]Polygon 题解

          题目链接:P4342 [IOI1998]Polygon

          +

          洛谷P4342 [IOI1998]Polygon +题解

          +

          题目链接:P4342 +[IOI1998]Polygon

          -

          题意:多边形是一个玩家在一个有 $n$ 个顶点的多边形上的游戏,如图所示,其中 $n=4$ 。每个顶点用整数标记,每个边用符号+(加)或符号*(乘积)标记。

          -

          +

          题意:多边形是一个玩家在一个有 \(n\) 个顶点的多边形上的游戏,如图所示,其中 +\(n=4\) +。每个顶点用整数标记,每个边用符号+(加)或符号*(乘积)标记。

          +

          第一步,删除其中一条边。随后每一步:

          选择一条边连接的两个顶点V1和V2,用边上的运算符计算V1和V2得到的结果来替换这两个顶点。

          游戏结束时,只有一个顶点,没有多余的边。

          如图所示,玩家先移除编号为3的边。之后,玩家选择计算编号为1的边,然后计算编号为4的边,最后,计算编号为2的边。结果是0。

          -

          +

          这里每条边的运算符旁边的数字为边的编号,不拿来计算

          编写一个程序,给定一个多边形,计算最高可能的分数。

          -

          $3 \le n\le 50$

          -

          对于任何一系列的操作,顶点数字都在 $[-32768,32767]$ 的范围内。

          +

          \(3 \le n\le 50\)

          +

          对于任何一系列的操作,顶点数字都在 \([-32768,32767]\) 的范围内。

          容易发现我们需要断环为链,然后区间dp

          注意到存在负数,而负负得正,说明不是简单的区间dp

          不难发现两个负的极小值之乘积对答案有更大贡献

          考虑维护区间最大值的同时维护区间最小值

          -

          即,设 $f[i][j]$ 为区间 $[i,j]$ 的最大值,$g[i][j]$ 为区间 $[i,j]$ 的最小值

          -

          当符号为”加”时,显然有

          -

          当符号为”乘”时

          -

          因为我们并不知道最大值是否非负,直接把所有情况都列出即可

          -

          时间复杂度 $O(n^3)$

          +f[i][j]=&\max_{i\le k < j}(f[i][j],f[i][k]\times +f[k+1][j],g[i][k]\times g[k+1][j],f[i][k]\times g[k+1][j],g[i][k]\times +f[k+1][j])\\ +g[i][j]=&\min_{i\le k < j}(g[i][j],f[i][k]\times +f[k+1][j],g[i][k]\times g[k+1][j],f[i][k]\times g[k+1][j],g[i][k]\times +f[k+1][j]) +\end{aligned} +\] 时间复杂度 \(O(n^3)\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -732,13 +748,13 @@ 

          - +
          - 洛谷P4395 [BOI2003]Gem 气垫车 题解 + 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 - 洛谷P4395 [BOI2003]Gem 气垫车 题解 + 洛谷P4390 [BOI2007]Mokia 摩基亚 题解
          @@ -770,12 +786,8 @@

          算法 - - DP - - - - 图论 + + 数据结构

          @@ -931,7 +943,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p4390-boi2007-mokia-mo-ji-ya-ti-jie/index.html b/2022/05/25/luo-gu-p4390-boi2007-mokia-mo-ji-ya-ti-jie/index.html index acd76654ed..a706892186 100644 --- a/2022/05/25/luo-gu-p4390-boi2007-mokia-mo-ji-ya-ti-jie/index.html +++ b/2022/05/25/luo-gu-p4390-boi2007-mokia-mo-ji-ya-ti-jie/index.html @@ -472,21 +472,45 @@

          洛谷P4390 [BOI2007]Mokia 摩
          -

          洛谷P4390 [BOI2007]Mokia 摩基亚 题解

          题目链接:P4390 [BOI2007]Mokia 摩基亚

          +

          洛谷P4390 [BOI2007]Mokia +摩基亚 题解

          +

          题目链接:P4390 +[BOI2007]Mokia 摩基亚

          -

          题意:摩尔瓦多的移动电话公司摩基亚(Mokia)设计出了一种新的用户定位系统。和其他的定位系统一样,它能够迅速回答任何形如“用户 C 的位置在哪?”的问题,精确到毫米。但其真正高科技之处在于,它能够回答形如“给定区域内有多少名用户?”的问题。

          -

          在定位系统中,世界被认为是一个 $w×w$ 的正方形区域,由 $1\times 1$ 的方格组成。每个方格都有一个坐标 $(x,y)$,$1\leq x,y\leq w$。坐标的编号从 $1$ 开始。对于一个 $4\times 4$ 的正方形,就有 $1\leq x\leq 4$,$1\leq y\leq 4$(如图):

          -

          img

          +

          题意:摩尔瓦多的移动电话公司摩基亚(Mokia)设计出了一种新的用户定位系统。和其他的定位系统一样,它能够迅速回答任何形如“用户 +C +的位置在哪?”的问题,精确到毫米。但其真正高科技之处在于,它能够回答形如“给定区域内有多少名用户?”的问题。

          +

          在定位系统中,世界被认为是一个 \(w×w\) 的正方形区域,由 \(1\times 1\) +的方格组成。每个方格都有一个坐标 \((x,y)\)\(1\leq +x,y\leq w\)。坐标的编号从 \(1\) +开始。对于一个 \(4\times 4\) +的正方形,就有 \(1\leq x\leq 4\)\(1\leq y\leq 4\)(如图):

          +
          + + +

          请帮助 Mokia 公司编写一个程序来计算在某个矩形区域内有多少名用户。

          这道题的题解区一片互相抄袭的烂货,实在受不了了就来水一篇题解

          -

          可以发现这道题目就是个二维数点问题,不知道的推荐看看 这篇博客

          +

          可以发现这道题目就是个二维数点问题,不知道的推荐看看 +这篇博客

          然后这道题目,我们不能用归并排序来做

          为什么呢?因为这个题目它还有一个维度:时间顺序

          -

          因此我们可以使用CDQ分治(第一维是时间,第二维是 $x$ ,第三维是 $y$ )

          +

          因此我们可以使用CDQ分治(第一维是时间,第二维是 \(x\) ,第三维是 \(y\)

          这里我们甚至不用按时间排序,它本来就是顺序的

          -

          注意到 $w\le 2\times 10^6$ ,有一丢丢大,所以可以不必要地离散化一下 $y$

          -

          因此时间复杂度 $O(n\log^2 n)$

          +

          注意到 \(w\le 2\times 10^6\) +,有一丢丢大,所以可以不必要地离散化一下 \(y\)

          +

          因此时间复杂度 \(O(n\log^2 n)\)

          然后就跑到了最优解第一页,嘿嘿树状数组跑的飞快🤤(逃~

          代码如下

          #include <bits/stdc++.h>
          @@ -839,13 +863,13 @@ 

           上一篇

          @@ -896,13 +916,13 @@

          @@ -476,19 +476,26 @@

          洛谷P4395 [BOI2003]Gem 气垫
          -

          洛谷P4395 [BOI2003]Gem 气垫车 题解

          题目链接:P4395 [BOI2003]Gem 气垫车

          +

          洛谷P4395 [BOI2003]Gem 气垫车 +题解

          +

          题目链接:P4395 +[BOI2003]Gem 气垫车

          题意:给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数

          唯一的限制条件是相临的两个结点不能标上相同的权值,要求一种方案,使得整棵树的总价值最小。

          -

          $N \le 10000$

          +

          \(N \le 10000\)

          考虑树形dp

          -

          设 $dp[u][i]$ 表示结点 $u$ 的权值为 $i$ 时其所在子树的最小总权值

          -

          则有

          -

          那么这个权值最大有多少呢

          -

          不难发现最多为 $\left\lceil\log n\right\rceil + 1$

          -

          那么就很简单了,时间复杂度 $O(n\log n)$

          +

          \(dp[u][i]\) 表示结点 \(u\) 的权值为 \(i\) 时其所在子树的最小总权值

          +

          则有 \[ +dp[u][i]=\min_{k \ne i}(dp[v][k])+i +\] 那么这个权值最大有多少呢

          +

          不难发现最多为 \(\left\lceil\log +n\right\rceil + 1\)

          +

          那么就很简单了,时间复杂度 \(O(n\log +n)\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -612,14 +619,14 @@ 

          算法 - - DP - - 图论 + + DP + +

          @@ -665,13 +672,13 @@

           上一篇

          - +
          - 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 + 洛谷P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 题解 - 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 + 洛谷P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 题解
          @@ -752,10 +759,6 @@

          - - 算法 - - 数据结构 @@ -913,7 +916,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p4556-vani-you-yue-hui-yu-tian-de-wei-ba-mo-ban-xian-duan-shu-he-bing-ti-jie/index.html b/2022/05/25/luo-gu-p4556-vani-you-yue-hui-yu-tian-de-wei-ba-mo-ban-xian-duan-shu-he-bing-ti-jie/index.html index 35ea652d47..e5221fc902 100644 --- a/2022/05/25/luo-gu-p4556-vani-you-yue-hui-yu-tian-de-wei-ba-mo-ban-xian-duan-shu-he-bing-ti-jie/index.html +++ b/2022/05/25/luo-gu-p4556-vani-you-yue-hui-yu-tian-de-wei-ba-mo-ban-xian-duan-shu-he-bing-ti-jie/index.html @@ -468,20 +468,37 @@

          洛谷P4556 [Vani有约会]雨
          -

          洛谷P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 题解

          题目链接:P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

          +

          洛谷P4556 +[Vani有约会]雨天的尾巴 /【模板】线段树合并 题解

          +

          题目链接:P4556 +[Vani有约会]雨天的尾巴 /【模板】线段树合并

          -

          题意:村落里的一共有 $n$ 座房屋,并形成一个树状结构。然后救济粮分 $m$ 次发放,每次选择两个房屋 $(x,y)$,然后对于 $x$ 到 $y$ 的路径上(含 $x$ 和 $y$)每座房子里发放一袋 $z$ 类型的救济粮。

          +

          题意:村落里的一共有 \(n\) +座房屋,并形成一个树状结构。然后救济粮分 \(m\) 次发放,每次选择两个房屋 \((x,y)\),然后对于 \(x\)\(y\) 的路径上(含 \(x\)\(y\))每座房子里发放一袋 \(z\) 类型的救济粮。

          然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮

          显然这是板子题

          我们利用树上差分的思想,自底向上统计答案

          同时一路合并每个节点的线段树,即可

          本文主要讲一讲为什么数组要开 1e5*70 左右

          -

          首先 $z \le 10^5$ ,取最大值 $Z=10^5$ (权值线段树嘛)

          +

          首先 \(z \le 10^5\) ,取最大值 \(Z=10^5\) (权值线段树嘛)

          其次,动态开点线段树一次从根节点到叶子节点的修改操作

          -

          modify() 新建的点数为 $O(\log Z)$ 的,在本题中 $\log Z \approx \log_2 10^5 \approx 17$

          -

          而观察代码可以发现,我们做树上差分的时候,一次差分会修改至多 $4$ 个节点

          -

          因此开的空间大小就是 $Z\log Z\times 4$ ,稍微取个整就是 1e5*70

          +

          modify() 新建的点数为 \(O(\log Z)\) 的,在本题中 \(\log Z \approx \log_2 10^5 \approx 17\)

          +

          而观察代码可以发现,我们做树上差分的时候,一次差分会修改至多 \(4\) 个节点

          +

          因此开的空间大小就是 \(Z\log Z\times +4\) ,稍微取个整就是 1e5*70

          贴个代码,方便各位食用

          #include <bits/stdc++.h>
           using namespace std;
          @@ -751,13 +768,13 @@ 

           上一篇

          - +
          - 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 + 洛谷P4395 [BOI2003]Gem 气垫车 题解 - 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 + 洛谷P4395 [BOI2003]Gem 气垫车 题解
          @@ -789,8 +806,12 @@

          算法 - - 数据结构 + + 图论 + + + + DP

          @@ -999,7 +1020,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p5020-noip2018-ti-gao-zu-huo-bi-xi-tong-ti-jie/index.html b/2022/05/25/luo-gu-p5020-noip2018-ti-gao-zu-huo-bi-xi-tong-ti-jie/index.html index ee790e4088..c5189117fc 100644 --- a/2022/05/25/luo-gu-p5020-noip2018-ti-gao-zu-huo-bi-xi-tong-ti-jie/index.html +++ b/2022/05/25/luo-gu-p5020-noip2018-ti-gao-zu-huo-bi-xi-tong-ti-jie/index.html @@ -472,12 +472,40 @@

          洛谷P5020 [NOIP2018 提高组]
          -

          洛谷P5020 [NOIP2018 提高组] 货币系统 题解

          题目链接:P5020 [NOIP2018 提高组] 货币系统

          +

          洛谷P5020 [NOIP2018 +提高组] 货币系统 题解

          +

          题目链接:P5020 +[NOIP2018 提高组] 货币系统

          -

          题意:在网友的国度中共有 $n$ 种不同面额的货币,第 $i$ 种货币的面额为 $a[i]$,你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 $n$ 、面额数组为 $a[1..n]$ 的货币系统记作 $(n,a)$ 。

          -

          在一个完善的货币系统中,每一个非负整数的金额 $x$ 都应该可以被表示出,即对每一个非负整数 $x$ ,都存在 $n$ 个非负整数 $t[i]$ 满足 $a[i] \times t[i]$ 的和为 $x$ 。然而, 在网友的国度中,货币系统可能是不完善的,即可能存在金额 $x$ 不能被该货币系统表示出。例如在货币系统 $n=3 ,a=[2,5,9]$ 中,金额 $1,3$ 就无法被表示出来。

          -

          两个货币系统 $(n,a)$ 和 $(m,b)$ 是等价的,当且仅当对于任意非负整数 $x$ ,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。

          -

          现在网友们打算简化一下货币系统。他们希望找到一个货币系统 $(m,b)$ ,满足 $(m,b)$ 与原来的货币系统 $(n,a)$ 等价,且 $m$ 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 $m$ 。

          +

          题意:在网友的国度中共有 \(n\) 种不同面额的货币,第 \(i\) 种货币的面额为 \(a[i]\),你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 +\(n\) 、面额数组为 \(a[1..n]\) 的货币系统记作 \((n,a)\)

          +

          在一个完善的货币系统中,每一个非负整数的金额 \(x\) 都应该可以被表示出,即对每一个非负整数 +\(x\) ,都存在 \(n\) 个非负整数 \(t[i]\) 满足 \(a[i] \times t[i]\) 的和为 \(x\) 。然而, +在网友的国度中,货币系统可能是不完善的,即可能存在金额 \(x\) 不能被该货币系统表示出。例如在货币系统 +\(n=3 ,a=[2,5,9]\) 中,金额 \(1,3\) 就无法被表示出来。

          +

          两个货币系统 \((n,a)\)\((m,b)\) 是等价的,当且仅当对于任意非负整数 +\(x\) +,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。

          +

          现在网友们打算简化一下货币系统。他们希望找到一个货币系统 \((m,b)\) ,满足 \((m,b)\) 与原来的货币系统 \((n,a)\) 等价,且 \(m\) +尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 \(m\)

          输入输出样例

          输入 #1

          2 
          @@ -490,12 +518,17 @@ 

          观察第一组数据,可以发现其等价于 3 10

          -

          因为 $19$ 和 $6$ 均可以用 $3$ 和 $10$ 替代

          +

          因为 \(19\)\(6\) 均可以用 \(3\)\(10\) 替代

          而第二组,每一种都是有价值的,不可替代

          -

          考虑 $dp$ ,

          -

          设 $dp[i]$ 表示凑出 $i$ 最多需要花费的金额种类

          +

          考虑 \(dp\)

          +

          \(dp[i]\) 表示凑出 \(i\) 最多需要花费的金额种类

          显然这个是个完全背包。

          -

          $dp[a[i]]=1$ 则说明 $a[i]$ 是有价值的面额

          +

          \(dp[a[i]]=1\) 则说明 \(a[i]\) 是有价值的面额

          然后就没了,代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -552,7 +585,8 @@ 

          } return 0; }

          -

          其实这个 $b$ 是可以证明有 $b \subseteq a $ 的

          +

          其实这个 \(b\) 是可以证明有 $b a $ +的

          但是,OI不需要严谨证明。OI靠得是OI直觉。

          @@ -919,7 +953,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p5110-kuai-su-di-tui-ti-jie/index.html b/2022/05/25/luo-gu-p5110-kuai-su-di-tui-ti-jie/index.html index 52d79397f9..98b5b54dc7 100644 --- a/2022/05/25/luo-gu-p5110-kuai-su-di-tui-ti-jie/index.html +++ b/2022/05/25/luo-gu-p5110-kuai-su-di-tui-ti-jie/index.html @@ -472,44 +472,66 @@

          洛谷P5110 块速递推 题解<
          -

          洛谷P5110 块速递推 题解

          题目链接:P5110 块速递推

          +

          洛谷P5110 块速递推 题解

          +

          题目链接:P5110 +块速递推

          -

          题意:给定一个数列 $a$ 满足递推式

          -

          求 $a_n \bmod (10^9+7)$

          +\\a_n = 233a_{n-1}+666a_{n-2} +\] 求 \(a_n \bmod (10^9+7)\)

          多组询问

          -

          这个题是有个循环节的,正好是 $10^9+6$ ,据出题人说是凑好的

          +

          这个题是有个循环节的,正好是 \(10^9+6\) ,据出题人说是凑好的

          关于循环节怎么求的我也不太清楚,先留个坑,研究好了就补上来

          -

          upd.20220721 现在知道怎么求循环节了,详见本文最后

          -

          也就是,如果数列 $a_n$ 在模 $M$ 意义下存在循环节 $p$ ,则有 $a_n \equiv a_{n\,\bmod\, p} \bmod M$

          +

          upd.20220721 +现在知道怎么求循环节了,详见本文最后

          +

          也就是,如果数列 \(a_n\) 在模 \(M\) 意义下存在循环节 \(p\) ,则有 \(a_n +\equiv a_{n\,\bmod\, p} \bmod M\)

          本题的解法就是手推通项公式

          具体方法如下

          -

          对于二阶线性递推数列

          -

          考虑使用特征方程求解

          -

          $a_n=pa_{n-1}+qa_{n-2}$ 的特征方程为

          -

          可以求出两个特解(不一定是实数) $x_1,x_2$ ,则

          -
          -

          注意,如果数列从 $a_1$ 开始,这里就是 $a_n=\alpha x_1^{n-1} + \beta x_2^{n-1}$

          +\\a_n = pa_{n-1}+qa_{n-2} ,n\ge 2 +\] 考虑使用特征方程求解

          +

          \(a_n=pa_{n-1}+qa_{n-2}\) +的特征方程为 \[ +x^2=px+q +\] 可以求出两个特解(不一定是实数) \(x_1,x_2\) ,则 \[ +a_n=\alpha x_1^{n} + \beta x_2^{n} +\]

          +
          +

          注意,如果数列从 \(a_1\) 开始,这里就是 \(a_n=\alpha x_1^{n-1} + \beta +x_2^{n-1}\)

          -

          然后将 $a_0=A,a_1=B$ 代入可得

          -

          解出 $\alpha,\beta$ 即可

          -

          本题的通项公式为

          -

          注意到这里有个 $\sqrt{56953}$ ,而我们要求它模意义下的值

          -

          也就是求出所有的 $x$

          -

          考虑二次剩余求解

          -

          什么?不会二次剩余? 那么就打个暴力好了,最多几秒钟就跑出来了

          +\end{cases} +\] 解出 \(\alpha,\beta\) +即可

          +

          本题的通项公式为 \[ +a_n=\dfrac{1}{\sqrt{56953}}\left(\left(\dfrac{233+\sqrt{56953}}{2}\right)^n-\left(\dfrac{233-\sqrt{56953}}{2}\right)^n\right) +\] 注意到这里有个 \(\sqrt{56953}\) +,而我们要求它模意义下的值

          +

          也就是求出所有的 \(x\) \[ +x^2\equiv 56953 \bmod(10^9+7) +\] 考虑二次剩余求解

          +

          什么?不会二次剩余? +那么就打个暴力好了,最多几秒钟就跑出来了

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -519,12 +541,16 @@ 

          if(i*i%1000000007==56953)cout << i << endl; return 0; }

          -

          然后有两个解 $188305837,811694170$ 取个小点的代入就好了

          -

          则有

          -

          注意到询问有 $10^7$ 个,快速幂过不了

          -

          考虑光速幂,$O(\sqrt{n})$ 预处理 $f(x)=x^{65536t},g(x)=x^t$

          -

          询问直接查询 $f(x/65536)\times g(n\%65536)$ 即可

          +

          然后有两个解 \(188305837,811694170\) +取个小点的代入就好了

          +

          则有 \[ +a_n \equiv 233230706 \times(94153035^n-905847205^n) +\] 注意到询问有 \(10^7\) +个,快速幂过不了

          +

          考虑光速幂,\(O(\sqrt{n})\) 预处理 +\(f(x)=x^{65536t},g(x)=x^t\)

          +

          询问直接查询 \(f(x/65536)\times +g(n\%65536)\) 即可

          这里可以用位运算加速

          代码:

          #include <bits/stdc++.h>
          @@ -591,24 +617,32 @@ 

          //233230706×(94153035^n−905847205^n)

          如何求解循环节?

          -

          如果数列 $a_n$ 在模 $M$ 意义下存在循环节 $p$ ,则有 $a_n \equiv a_{n\,\bmod\, p} \bmod M$

          +

          如果数列 \(a_n\) 在模 \(M\) 意义下存在循环节 \(p\) ,则有 \(a_n +\equiv a_{n\,\bmod\, p} \bmod M\)

          -

          问了 @zx2017 老师 CCOrz

          +

          问了 @zx2017 老师 CCOrz

          这个东西其实求起来非常简单,

          -

          在已知 $a_n$ 递推式和 $M$ 的情况下

          +

          在已知 \(a_n\) 递推式和 \(M\) 的情况下

          直接暴力去枚举就好了

          -

          具体的,这道题

          -

          注意到 $a_n$ 是从 $a_{n-1}$ 和 $a_{n-2}$ 推来的

          -

          那我们枚举 $n$ ,直到出现了这样的情况

          -

          那么 $i-2$ 就是循环节

          +\mod M +\] 那么 \(i-2\) 就是循环节

          是不是很神奇?我太菜了根本没想到 QAQ

          当然可能会有更优秀的做法(比起暴力枚举)

          不过起码知道怎么简单求就很好了 qwq

          @@ -973,7 +1007,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p5322-bjoi2019-pai-bing-bu-zhen-ti-jie/index.html b/2022/05/25/luo-gu-p5322-bjoi2019-pai-bing-bu-zhen-ti-jie/index.html index d9f727d058..09b9906c17 100644 --- a/2022/05/25/luo-gu-p5322-bjoi2019-pai-bing-bu-zhen-ti-jie/index.html +++ b/2022/05/25/luo-gu-p5322-bjoi2019-pai-bing-bu-zhen-ti-jie/index.html @@ -472,17 +472,36 @@

          洛谷P5322 [BJOI2019] 排兵布
          -

          洛谷P5322 [BJOI2019] 排兵布阵 题解

          -

          题意:小 C 正在玩一款排兵布阵的游戏。在游戏中有 $n$ 座城堡,每局对战由两名玩家来争夺这些城堡。每名玩家有 $m$ 名士兵,可以向第 $i$ 座城堡派遣 $a_i$ 名士兵去争夺这个城堡,使得总士兵数不超过 $m$。

          -

          如果一名玩家向第 $i$ 座城堡派遣的士兵数严格大于对手派遣士兵数的两倍,那么这名玩家就占领了这座城堡,获得 $i$ 分。

          -

          现在小 C 即将和其他 $s$ 名玩家两两对战,这 $s$ 场对决的派遣士兵方案必须相同。小 C 通过某些途径得知了其他 $s$ 名玩家即将使用的策略,他想知道他应该使用什么策略来最大化自己的总分。

          +

          洛谷P5322 [BJOI2019] 排兵布阵 +题解

          +
          +

          题意:小 C 正在玩一款排兵布阵的游戏。在游戏中有 +\(n\) +座城堡,每局对战由两名玩家来争夺这些城堡。每名玩家有 \(m\) 名士兵,可以向第 \(i\) 座城堡派遣 \(a_i\) +名士兵去争夺这个城堡,使得总士兵数不超过 \(m\)

          +

          如果一名玩家向第 \(i\) +座城堡派遣的士兵数严格大于对手派遣士兵数的两倍,那么这名玩家就占领了这座城堡,获得 +\(i\) 分。

          +

          现在小 C 即将和其他 \(s\) +名玩家两两对战,这 \(s\) +场对决的派遣士兵方案必须相同。小 C 通过某些途径得知了其他 \(s\) +名玩家即将使用的策略,他想知道他应该使用什么策略来最大化自己的总分。

          由于答案可能不唯一,你只需要输出小 C 总分的最大值。

          -

          设 $dp[i][j]$ 表示第 $i$ 个城堡放 $j$ 个士兵可以获得的最大分数

          -

          显然我们会选择恰好为某名玩家放的兵数 $x$ 的 $2x+1$ ,不然多的都是浪费

          +

          \(dp[i][j]\) 表示第 \(i\) 个城堡放 \(j\) 个士兵可以获得的最大分数

          +

          显然我们会选择恰好为某名玩家放的兵数 \(x\)\(2x+1\) ,不然多的都是浪费

          因此这就是个类似背包的问题

          没什么难度,直接看代码就能明白了吧(逃

          -

          时间复杂度 $O(nms)$

          +

          时间复杂度 \(O(nms)\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -877,7 +896,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p5365-snoi2017-ying-xiong-lian-meng-ti-jie/index.html b/2022/05/25/luo-gu-p5365-snoi2017-ying-xiong-lian-meng-ti-jie/index.html index 28abec7ef4..13429ad718 100644 --- a/2022/05/25/luo-gu-p5365-snoi2017-ying-xiong-lian-meng-ti-jie/index.html +++ b/2022/05/25/luo-gu-p5365-snoi2017-ying-xiong-lian-meng-ti-jie/index.html @@ -472,24 +472,42 @@

          洛谷P5365 [SNOI2017] 英雄联
          -

          洛谷P5365 [SNOI2017] 英雄联盟 题解

          题目链接:P5365 [SNOI2017] 英雄联盟

          +

          洛谷P5365 [SNOI2017] 英雄联盟 +题解

          +

          题目链接:P5365 +[SNOI2017] 英雄联盟

          题意:正在上大学的小皮球热爱英雄联盟这款游戏,而且打的很菜,被网友们戏称为「小学生」。

          现在,小皮球终于受不了网友们的嘲讽,决定变强了,他变强的方法就是:买皮肤!

          -

          小皮球只会玩 $\text{N}$ 个英雄,因此,他也只准备给这 $\text{N}$ 个英雄买皮肤,并且决定,以后只玩有皮肤的英雄。

          -

          这 $\text{N}$ 个英雄中,第 $\text{i}$ 个英雄有 $K_i$ 款皮肤,价格是每款 $C_i$ Q 币(同一个英雄的皮肤价格相同)。

          +

          小皮球只会玩 \(\text{N}\) +个英雄,因此,他也只准备给这 \(\text{N}\) +个英雄买皮肤,并且决定,以后只玩有皮肤的英雄。

          +

          \(\text{N}\) 个英雄中,第 \(\text{i}\) 个英雄有 \(K_i\) 款皮肤,价格是每款 \(C_i\) Q +币(同一个英雄的皮肤价格相同)。

          为了让自己看起来高大上一些,小皮球决定给同学们展示一下自己的皮肤,展示的思路是这样的:对于有皮肤的每一个英雄,随便选一个皮肤给同学看。

          -

          比如,小皮球共有 5 个英雄,这 5 个英雄分别有 $\text{0,0,3,2,4}$ 款皮肤,那么,小皮球就有 $3 \times 2 \times 4 = 24$ 种展示的策略。

          -

          现在,小皮球希望自己的展示策略能够至少达到 $\text{M}$ 种,请问,小皮球至少要花多少钱呢?

          +

          比如,小皮球共有 5 个英雄,这 5 个英雄分别有 \(\text{0,0,3,2,4}\) 款皮肤,那么,小皮球就有 +\(3 \times 2 \times 4 = 24\) +种展示的策略。

          +

          现在,小皮球希望自己的展示策略能够至少达到 \(\text{M}\) +种,请问,小皮球至少要花多少钱呢?

          -

          题目要求的就是数量要超过 $m$ ,并要求价格最少

          +

          题目要求的就是数量要超过 \(m\) +,并要求价格最少

          如果直接dp会带上一些乘法除法的转移,不太好

          考虑转化问题,求出每种价格可以获得的最多的数量

          那么这就变成了一个类似于多重背包的dp问题

          -

          其中价格是费用,即 $w[i]$

          -

          状态转移方程:

          -

          代码:

          +

          其中价格是费用,即 \(w[i]\)

          +

          状态转移方程: \[ +dp[j]=\max_{0 \le k \le +\min\left(v[i],\left\lfloor\frac{j}{w[i]}\right\rfloor\right)}(dp[j],dp[j-k\times +w[i]]\times k) +\] 代码:

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -888,7 +906,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p5656-mo-ban-er-yuan-yi-ci-bu-ding-fang-cheng-exgcd-ti-jie/index.html b/2022/05/25/luo-gu-p5656-mo-ban-er-yuan-yi-ci-bu-ding-fang-cheng-exgcd-ti-jie/index.html index a28ca8d894..1a42dad831 100644 --- a/2022/05/25/luo-gu-p5656-mo-ban-er-yuan-yi-ci-bu-ding-fang-cheng-exgcd-ti-jie/index.html +++ b/2022/05/25/luo-gu-p5656-mo-ban-er-yuan-yi-ci-bu-ding-fang-cheng-exgcd-ti-jie/index.html @@ -472,61 +472,127 @@

          洛谷P5656 【模板】二元
          -

          洛谷P5656 【模板】二元一次不定方程 (exgcd) 题解

          题目链接:P5656 【模板】二元一次不定方程 (exgcd)

          +

          洛谷P5656 +【模板】二元一次不定方程 (exgcd) 题解

          +

          题目链接:P5656 +【模板】二元一次不定方程 (exgcd)

          -

          题意:给定不定方程

          -

          若该方程无整数解,输出 $-1$。
          若该方程有整数解,且有正整数解,则输出其正整数解的数量,所有正整数解中 $x$ 的最小值,所有正整数解中 $y$ 的最小值,所有正整数解中 $x$ 的最大值,以及所有正整数解中 $y$ 的最大值。
          若方程有整数解,但没有正整数解,你需要输出所有整数解中 $x$ 的最小正整数值, $y$ 的最小正整数值。

          -

          正整数解即为 $x, y$ 均为正整数的解,$\boldsymbol{0}$ 不是正整数
          整数解即为 $x,y$ 均为整数的解。
          $x$ 的最小正整数值即所有 $x$ 为正整数的整数解中 $x$ 的最小值,$y$ 同理。

          -

          $1\le a,b,c\le 10^9$

          +

          题意:给定不定方程 \[ +ax+by=c +\] 若该方程无整数解,输出 \(-1\)。 +若该方程有整数解,且有正整数解,则输出其正整数解的数量,所有正整数解中 +\(x\) +的最小值,所有正整数解中 \(y\) +的最小值,所有正整数解中 \(x\) +的最大值,以及所有正整数解中 \(y\) 的最大值。 +若方程有整数解,但没有正整数解,你需要输出所有整数解中 +\(x\) 的最小正整数值, \(y\) 的最小正整数值。

          +

          正整数解即为 \(x, y\) +均为正整数的解,\(\boldsymbol{0}\) +不是正整数。 整数解即为 \(x,y\) 均为整数的解。 \(x\) 的最小正整数值即所有 \(x\) 为正整数的整数解中 \(x\) 的最小值,\(y\) 同理。

          +

          \(1\le a,b,c\le 10^9\)

          根据裴蜀定理

          -

          对于 $x,y$ 的二元一次不定方程 $ax+by=c$ ,其有解的充要条件为 $\gcd(a,b) \mid c$

          -

          可知方程无解当且仅当 $\gcd(a,b) \nmid c$

          +

          对于 \(x,y\) +的二元一次不定方程 \(ax+by=c\) ,其有解的充要条件为 \(\gcd(a,b) \mid c\)

          +

          可知方程无解当且仅当 \(\gcd(a,b) \nmid +c\)

          那么有解的时候,我们可以用扩展欧几里德算法求出一组特解

          -

          即 $x^{\prime},y^{\prime}$ 满足

          -

          考虑转化为原方程的一组特解。令 $d=\gcd(a,b)$

          -

          则特解 $x_0=x^{\prime}\times \dfrac{c}{d},y_0 = y’ \times \dfrac{c}{d}$ 满足

          -

          此时的 $x_0,y_0$ 就是一个平凡的解,考虑转化为更有价值的解

          -

          考虑增大 $x_0$ 为 $x_0+p$ ,则 $y_0$ 需减小至 $y_0-q$

          -

          -

          可得 $p =\dfrac{bq}{a}$

          -

          可以发现这样的解有无数个,我们只要找到一个最小正整数解即可(即 $p,q\in \mathbb{Z}_+$ 且 $p,q$ 尽可能小)

          -

          $\because a\mid bq,b\mid bq$

          -

          $\therefore (bq)_{\min}=\operatorname{lcm}(a,b) = \dfrac{ab}{\gcd(a,b)}$

          -

          -

          于是我们尝试将 $x_0$ 调至最小正整数( $y_0$ 同时也会改变 )

          -

          即找到一个最小的整数 $k$ 使得 $x_0 + kp \ge 1$

          -

          -

          然后将 $x_0$ 变为 $x_0 + kp$ 即可,此时 $y_0$ 变化为 $y_0-qk$

          -

          若此时 $y_0 > 0$ ,则存在正整数解

          -
            -
          1. 正整数解的个数:使 $y_0$ 不停地减 $q$ 就是所有可行正整数解,故答案为 $\left\lfloor{\dfrac{y_0-1}{q}}\right\rfloor+1$
          2. -
          3. $x$ 的最小正整数值:$x_0$
          4. -
          5. $y$ 的最小正整数值:使 $y$ 不停地减 $q$ ,最小的那个正整数就是答案,即 $(y_0-1)\bmod q + 1$

            -
          6. -
          7. $x$ 的最大正整数值:$y$ 最小时 $x$ 最大

            -
          8. -
          9. $y$ 的最大正整数值: $y_0$
          10. +q_{\min}&=\dfrac{a}{d} +\end{cases}\end{aligned} +\] 于是我们尝试将 \(x_0\) +调至最小正整数( \(y_0\) 同时也会改变 +)

            +

            即找到一个最小的整数 \(k\) 使得 +\(x_0 + kp \ge 1\)

            +

            \[ +k= \left\lceil{\dfrac{1-x_0}{p}}\right\rceil +\] 然后将 \(x_0\) 变为 \(x_0 + kp\) 即可,此时 \(y_0\) 变化为 \(y_0-qk\)

            +

            若此时 \(y_0 > 0\) +,则存在正整数解

            +
              +
            1. 正整数解的个数:使 \(y_0\) 不停地减 \(q\) 就是所有可行正整数解,故答案为 \(\left\lfloor{\dfrac{y_0-1}{q}}\right\rfloor+1\)

            2. +
            3. \(x\) +的最小正整数值\(x_0\)

            4. +
            5. \(y\) +的最小正整数值:使 \(y\) 不停地减 \(q\) ,最小的那个正整数就是答案,即 \((y_0-1)\bmod q + 1\)

            6. +
            7. \(x\) +的最大正整数值\(y\) +最小时 \(x\) 最大

            8. +
            9. \(y\) +的最大正整数值\(y_0\)

            -

            若此时 $y_0 \le 0$ ,则仅存在整数解

            -
              -
            1. $x$ 的最小正整数值: $x_0$
            2. -
            3. $y$ 的最小正整数值:构造 $y_0+k’q \ge 1$ ,易知 $k^{\prime} = \left\lceil{\dfrac{1-y_0}{q}}\right\rceil$ ,故答案为 $y_0+q\times \left\lceil{\dfrac{1-y_0}{q}}\right\rceil$
            4. +

              若此时 \(y_0 \le 0\) +,则仅存在整数解

              +
                +
              1. \(x\) +的最小正整数值\(x_0\)
              2. +
              3. \(y\) +的最小正整数值:构造 \(y_0+k'q \ge 1\) ,易知 \(k^{\prime} = +\left\lceil{\dfrac{1-y_0}{q}}\right\rceil\) ,故答案为 \(y_0+q\times +\left\lceil{\dfrac{1-y_0}{q}}\right\rceil\)
              -

              据说要开 $\text{long long}$ ,反正我#define int long long丝毫不慌(逃

              +

              据说要开 \(\text{long long}\) +,反正我#define int long long丝毫不慌(逃

              主要代码如下(省略了快读和多组数据)

              #define int long long
               int exgcd(int a,int b,int &x,int &y)
              @@ -560,7 +626,8 @@ 

              pc('\n'); }

              参考文献

              -

              [1] https://www.luogu.com.cn/blog/McHf/p5656-exgcd

              +

              [1] https://www.luogu.com.cn/blog/McHf/p5656-exgcd

          @@ -769,14 +836,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -934,7 +1001,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p5658-csp-s2019-gua-hao-shu-ti-jie/index.html b/2022/05/25/luo-gu-p5658-csp-s2019-gua-hao-shu-ti-jie/index.html index 2d8189d155..bd12fd3935 100644 --- a/2022/05/25/luo-gu-p5658-csp-s2019-gua-hao-shu-ti-jie/index.html +++ b/2022/05/25/luo-gu-p5658-csp-s2019-gua-hao-shu-ti-jie/index.html @@ -418,14 +418,14 @@

          洛谷P5658 [CSP-S2019] 括号 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -480,13 +480,20 @@

          洛谷P5658 [CSP-S2019] 括号
          -

          洛谷P5658 [CSP-S2019] 括号树 题解

          题目链接:P5658 [CSP-S2019] 括号树

          +

          洛谷P5658 [CSP-S2019] 括号树 +题解

          +

          题目链接:P5658 +[CSP-S2019] 括号树

          题意:略。

          注意到本题中,链的部分分很高,就先考虑这种情况

          -

          不难发现,每个与 $l$ 配对的右括号 $r$ 的贡献为 $dp[r]=dp[l-1]+1 = dp[fa[l]]+1$

          -

          则 $k_i = dp[i] + \sum dp[j]$ ,$j$ 是 $i$ 的祖先结点

          +

          不难发现,每个与 \(l\) 配对的右括号 +\(r\) 的贡献为 \(dp[r]=dp[l-1]+1 = dp[fa[l]]+1\)

          +

          \(k_i = dp[i] + \sum dp[j]\) +,\(j\)\(i\) 的祖先结点

          显然这个性质可以推广到树上

          这样维护一个括号栈就好了,怎么维护呢?

          为了防止栈在回溯过程中被破坏,考虑回溯时执行相反操作

          @@ -612,14 +619,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -917,7 +924,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p5851-usaco19dec-greedy-pie-eaters-p-ti-jie/index.html b/2022/05/25/luo-gu-p5851-usaco19dec-greedy-pie-eaters-p-ti-jie/index.html index 068eaed871..9af90a6397 100644 --- a/2022/05/25/luo-gu-p5851-usaco19dec-greedy-pie-eaters-p-ti-jie/index.html +++ b/2022/05/25/luo-gu-p5851-usaco19dec-greedy-pie-eaters-p-ti-jie/index.html @@ -472,32 +472,68 @@

          洛谷P5851 [USACO19DEC]Greedy P
          -

          洛谷P5851 [USACO19DEC]Greedy Pie Eaters P 题解

          题目链接:P5851 [USACO19DEC]Greedy Pie Eaters P

          +

          洛谷P5851 +[USACO19DEC]Greedy Pie Eaters P 题解

          +

          题目链接:P5851 +[USACO19DEC]Greedy Pie Eaters P

          题意

          -

          Farmer John 有 $M$ 头奶牛,为了方便,编号为 $1,\dots,M$。这些奶牛平时都吃青草,但是喜欢偶尔换换口味。Farmer John 一天烤了 $N$ 个派请奶牛吃,这 $N$ 个派编号为 $1,\dots,N$。第 $i$ 头奶牛喜欢吃编号在 $\left[ l_i,r_i \right]$ 中的派(包括两端),并且没有两头奶牛喜欢吃相同范围的派。第 $i$ 头奶牛有一个体重 $w_i$,这是一个在 $\left[ 1,10^6 \right]$ 中的正整数。

          -

          Farmer John 可以选择一个奶牛序列 $c_1,c_2,\dots,c_K$,并让这些奶牛按这个顺序轮流吃派。不幸的是,这些奶牛不知道分享!当奶牛 吃派时,她会把她喜欢吃的派都吃掉——也就是说,她会吃掉编号在 $[l_{c_i},r_{c_i}]$ 中所有剩余的派。Farmer John 想要避免当轮到一头奶牛吃派时,她所有喜欢的派在之前都被吃掉了这样尴尬的情况。因此,他想让你计算,要使奶牛按 $c_1,c_2,\dots,c_K$ 的顺序吃派,轮到这头奶牛时她喜欢的派至少剩余一个的情况下,这些奶牛的最大可能体重($w_{c_1}+w_{c_2}+\ldots+w_{c_K}$)是多少。

          +

          Farmer John 有 \(M\) +头奶牛,为了方便,编号为 \(1,\dots,M\)。这些奶牛平时都吃青草,但是喜欢偶尔换换口味。Farmer +John 一天烤了 \(N\) 个派请奶牛吃,这 +\(N\) 个派编号为 \(1,\dots,N\)。第 \(i\) 头奶牛喜欢吃编号在 \(\left[ l_i,r_i \right]\) +中的派(包括两端),并且没有两头奶牛喜欢吃相同范围的派。第 \(i\) 头奶牛有一个体重 \(w_i\),这是一个在 \(\left[ 1,10^6 \right]\) 中的正整数。

          +

          Farmer John 可以选择一个奶牛序列 \(c_1,c_2,\dots,c_K\),并让这些奶牛按这个顺序轮流吃派。不幸的是,这些奶牛不知道分享!当奶牛 +吃派时,她会把她喜欢吃的派都吃掉——也就是说,她会吃掉编号在 \([l_{c_i},r_{c_i}]\) 中所有剩余的派。Farmer +John +想要避免当轮到一头奶牛吃派时,她所有喜欢的派在之前都被吃掉了这样尴尬的情况。因此,他想让你计算,要使奶牛按 +\(c_1,c_2,\dots,c_K\) +的顺序吃派,轮到这头奶牛时她喜欢的派至少剩余一个的情况下,这些奶牛的最大可能体重(\(w_{c_1}+w_{c_2}+\ldots+w_{c_K}\))是多少。

          显然是一个区间dp

          -

          设 $f[i][j]$ 表示吃完 $[i,j]$ 能获得的最大 $\sum w$

          -

          设 $g[k][i][j]$ 表示 $k$ 还没吃,准备吃掉 $k$ 时 $(i \le l_d \le k \le r_d \le j)$ 的最大 $w$

          -

          首先不尝试增加奶牛 $d$ ,即不考虑吃 $k$ ,有

          -

          考虑吃 $k$ ,尝试增加奶牛 $d$ ,有

          -
            -
          • 关于 $g[k][i][j]$ 如何限制 $(l_d \le k \le r_d)$

            -

            考虑读入时处理每头奶牛 $g[t][l][r]=w,l \le t \le r$

            -
          • -
          • 如何更新 $g[k][i][j]$

            -

            显然当 $[i,j]=[l_d,r_d]$ 时,已经在读入时更新了

            -

            当 $i \le l_d$ 或 $j \ge r_d$ 时,我们可以通过

            -

            进行更新

            -
          • +\\g[k][i][j+1]=\max(g[k][i][j+1],g[k][i][j]) +\] 进行更新

          -

          时间复杂度 $O(n^3)$

          +

          时间复杂度 \(O(n^3)\)

          代码:

          #include <bits/stdc++.h>
           using namespace std;
          @@ -693,14 +729,14 @@ 

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -716,13 +752,13 @@

          - +
          - 洛谷P6327 区间加区间sin和 题解 + 洛谷P6216 回文匹配 题解 - 洛谷P6327 区间加区间sin和 题解 + 洛谷P6216 回文匹配 题解
          @@ -750,8 +786,12 @@

          - - 数据结构 + + 算法 + + + + 字符串

          @@ -907,7 +947,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p6216-hui-wen-pi-pei-ti-jie/index.html b/2022/05/25/luo-gu-p6216-hui-wen-pi-pei-ti-jie/index.html index 9a0b9023e3..1ed9a8b4a2 100644 --- a/2022/05/25/luo-gu-p6216-hui-wen-pi-pei-ti-jie/index.html +++ b/2022/05/25/luo-gu-p6216-hui-wen-pi-pei-ti-jie/index.html @@ -472,21 +472,36 @@

          洛谷P6216 回文匹配 题解<
          -

          洛谷P6216 回文匹配 题解

          题目链接:P6216 回文匹配

          +

          洛谷P6216 回文匹配 题解

          +

          题目链接:P6216 +回文匹配

          -

          题意:对于一对字符串 $(s_1,s_2)$,若 $s_1$ 的长度为奇数的子串 $(l,r)$ 满足 $(l,r)$ 是回文的,那么 $s_1$ 的“分数”会增加 $s_2$ 在 $(l,r)$ 中出现的次数。

          -

          现在给出一对 $(s_1,s_2)$,请计算出 $s_1$ 的“分数”。

          -

          答案对 $2 ^ {32}$ 取模。

          +

          题意:对于一对字符串 \((s_1,s_2)\),若 \(s_1\) 的长度为奇数的子串 +\((l,r)\) 满足 \((l,r)\) 是回文的,那么 \(s_1\) 的“分数”会增加 \(s_2\)\((l,r)\) 中出现的次数。

          +

          现在给出一对 \((s_1,s_2)\),请计算出 +\(s_1\) 的“分数”。

          +

          答案对 \(2 ^ {32}\) 取模。

          容易发现这就是个Manacher+KMP

          -

          Manacher用于找出奇回文串,KMP用于找出 $s_2$ 在 $(l,r)$ 中的出现位置

          +

          Manacher用于找出奇回文串,KMP用于找出 \(s_2\)\((l,r)\) 中的出现位置

          难点在于如何计算这个次数

          -

          对于每个极大回文串(长度小于 $|s_2|$ 的不考虑)

          +

          对于每个极大回文串(长度小于 \(|s_2|\) 的不考虑)

          它一定包含了在同一回文中心的更小的回文串

          abbba还包含了bbb,b

          -

          如果暴力去求解,时间复杂度为 $O(n^2)$

          +

          如果暴力去求解,时间复杂度为 \(O(n^2)\)

          于是想到前缀和来加快运算

          -

          我们可以在匹配成功时将 $s_2$ 左端点位置标记为 $1$

          +

          我们可以在匹配成功时将 \(s_2\) +左端点位置标记为 \(1\)

          考虑一个回文串x为任意字符

          str:xxxxxxx
           pos:1234567
          @@ -495,7 +510,7 @@

          \(O(n)\) 了

          代码如下

          #include <bits/stdc++.h>
           using namespace std;
          @@ -545,7 +560,9 @@ 

          return 0; }

          参考文献

          -

          [1] ZCETHAN’S BLOGS - P6216 回文匹配

          +

          [1] ZCETHAN'S +BLOGS - P6216 回文匹配

          @@ -663,13 +680,13 @@

           上一篇

          - +
          - 线段树空间开4倍的原因 + 洛谷P6327 区间加区间sin和 题解 - 线段树空间开4倍的原因 + 洛谷P6327 区间加区间sin和 题解
          @@ -746,10 +767,6 @@

          - - 数学 - - 数据结构 @@ -907,7 +924,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/luo-gu-p6327-qu-jian-jia-qu-jian-sin-he-ti-jie/index.html b/2022/05/25/luo-gu-p6327-qu-jian-jia-qu-jian-sin-he-ti-jie/index.html index d733b7b94f..33216f5dcd 100644 --- a/2022/05/25/luo-gu-p6327-qu-jian-jia-qu-jian-sin-he-ti-jie/index.html +++ b/2022/05/25/luo-gu-p6327-qu-jian-jia-qu-jian-sin-he-ti-jie/index.html @@ -468,22 +468,26 @@

          洛谷P6327 区间加区间sin
          -

          洛谷P6327 区间加区间sin和 题解

          题目链接:洛谷P6327 区间加区间sin和 题解

          +

          洛谷P6327 区间加区间sin和 +题解

          +

          题目链接:洛谷P6327 +区间加区间sin和 题解

          题意:维护一个数据结构,支持

          -
            -
          1. 区间加 $v$
          2. -
          3. 询问区间 $\sum\limits_{i=1}^{r}\sin a_i$
          4. +
              +
            1. 区间加 \(v\)
            2. +
            3. 询问区间 \(\sum\limits_{i=1}^{r}\sin +a_i\)
          -

          注意到

          -

          直接维护即可两个量即可

          +\sin(a+x)&=\sin a\cos x+\cos a \sin x\\ +\cos(a+x)&=\cos a\cos x-\sin a \sin x +\end{aligned} +\] 直接维护即可两个量即可

          注意点

          -
            +
            1. 更新的时候拿之前更新过的sin去更新cos,爆零。
            2. 不开long long,爆零。
            3. 不下传标记,爆零。
            4. @@ -719,13 +723,13 @@

               上一篇

          - +
          - 洛谷P5851 [USACO19DEC]Greedy Pie Eaters P 题解 + 洛谷P6216 回文匹配 题解 - 洛谷P5851 [USACO19DEC]Greedy Pie Eaters P 题解 + 洛谷P6216 回文匹配 题解
          @@ -757,8 +761,8 @@

          算法 - - DP + + 字符串

          @@ -963,7 +967,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/xian-duan-shu-kong-jian-kai-4-bei-de-yuan-yin/index.html b/2022/05/25/xian-duan-shu-kong-jian-kai-4-bei-de-yuan-yin/index.html index 6c59ffd670..f4269b7fac 100644 --- a/2022/05/25/xian-duan-shu-kong-jian-kai-4-bei-de-yuan-yin/index.html +++ b/2022/05/25/xian-duan-shu-kong-jian-kai-4-bei-de-yuan-yin/index.html @@ -472,44 +472,61 @@

          线段树空间开4倍的原因<
          -

          线段树空间开4倍的原因

          如果证明有错欢迎指出。

          -

          对于长为 $n$ 的序列,显然以其构建的线段树有 $n$ 个叶子节点

          -

          此时线段树的高度为 $k=\left\lceil{\log_2 n}\right\rceil+1$ (第一层的高度为 $1$ )

          -

          通过等比数列求和公式

          -

          可知

          -

          至多有 $\dfrac{1\times(1-2^k)}{1-2} = 2^k-1$ 个结点

          -

          即有 $2^{\left\lceil{\log_2 n}\right\rceil+1}-1$ 个结点

          -

          根据

          -

          可知

          +0,&\text{if } x \in \mathbb{Z},\\\\ +1,&\text{if }x \notin \mathbb{Z}. +\end{cases}\end{aligned} +\] 可知

            -
          • 当 $\log_2 n$ 为整数时,结点数为 $2n-1$ 个节点

            -
          • -
          • 当 $\log_2 n$ 不为整数时,结点数为 $4 \times 2^{\left\lfloor{\log_2 n}\right\rfloor}-1$

            -

            $\because \left\lfloor{\log_2 n}\right\rfloor < \log_2 n$

            -

            $\therefore 4 \times 2^{\left\lfloor{\log_2 n}\right\rfloor}-1 < 4n-1 < 4n$

            -
          • +
          • \(\log_2 n\) +为整数时,结点数为 \(2n-1\) +个节点

          • +
          • \(\log_2 n\) +不为整数时,结点数为 \(4 \times +2^{\left\lfloor{\log_2 n}\right\rfloor}-1\)

            +

            \(\because \left\lfloor{\log_2 +n}\right\rfloor < \log_2 n\)

            +

            \(\therefore 4 \times +2^{\left\lfloor{\log_2 n}\right\rfloor}-1 < 4n-1 < +4n\)

          -

          很多时候 $n$ 并不恰好为 $2^k$ ,因此开 $4$ 倍空间正好

          +

          很多时候 \(n\) 并不恰好为 \(2^k\) ,因此开 \(4\) 倍空间正好

          值得注意的是,对于 ls(at) = at*2 的这种写法

          -

          一定要避免在叶子结点调用 ls(at)

          -

          因为此时有可能会越界到 $8n$ 的位置

          -

          这里贴一个线段树1的代码 卡了点常啥的,无视它(逃

          +

          一定要避免在叶子结点调用 ls(at)

          +

          因为此时有可能会越界到 \(8n\) +的位置

          +

          这里贴一个线段树1的代码 +卡了点常啥的,无视它(逃

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -740,13 +757,13 @@ 

           上一篇

          - +
          - 洛谷P6216 回文匹配 题解 + 等差数列&等比数列小结 - 洛谷P6216 回文匹配 题解 + 等差数列&等比数列小结
          @@ -774,12 +791,8 @@

          - - 算法 - - - - 字符串 + + 数学

          @@ -984,7 +997,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/25/zhu-ding-li/index.html b/2022/05/25/zhu-ding-li/index.html index a5c2a0abf4..0ac2c6337c 100644 --- a/2022/05/25/zhu-ding-li/index.html +++ b/2022/05/25/zhu-ding-li/index.html @@ -468,20 +468,44 @@

          主定理

          -

          主定理

          证明先不写

          -

          将一个规模为 $n$ 的问题,通过分治得到 $a$ 个规模为 $n/b$ 的子问题,每个递归带来的额外计算为 $f(n)$ ,则有

          -

          $T(n)=aT(n/b)+f(n)$

          -

          其中 $a,b$ 为常数, $n\in \mathbb{N}^*,a\ge 1,b > 1,n/b$ 解释为 $\left\lfloor{\dfrac{n}{b}}\right\rfloor$ 或 $\left\lceil\dfrac{n}{b}\right\rceil$

          -
            -
          1. 若 $\exists \epsilon > 0$ 使得 $f(n)=O(n^{\log_b{a}-\epsilon})$,则 $T(n)=\Theta(n^{\log_ba})$
          2. -
          3. 若 $\exists \epsilon \ge 0$ 使得 $f(n)=\Theta(n^{\log_ba}\log^\epsilon n)$,则 $T(n)=\Theta(n^{\log_ba}\log^{\epsilon+1}n)$
          4. -
          5. 若 $\exists \epsilon>0$ 使得 $f(n)=\Omega(n^{\log_ba+\epsilon})$ ,且 $\exists c<1 , \forall n \gg 1$ 使得 $af(n/b)\le cf(n)$ ,则 $T(n)=\Theta(f(n))$
          6. +

            主定理

            +

            证明先不写

            +

            将一个规模为 \(n\) +的问题,通过分治得到 \(a\) 个规模为 +\(n/b\) +的子问题,每个递归带来的额外计算为 \(f(n)\) ,则有

            +

            \(T(n)=aT(n/b)+f(n)\)

            +

            其中 \(a,b\) 为常数, \(n\in \mathbb{N}^*,a\ge 1,b > 1,n/b\) +解释为 \(\left\lfloor{\dfrac{n}{b}}\right\rfloor\) +或 \(\left\lceil\dfrac{n}{b}\right\rceil\)

            +
              +
            1. \(\exists \epsilon > 0\) 使得 +\(f(n)=O(n^{\log_b{a}-\epsilon})\),则 +\(T(n)=\Theta(n^{\log_ba})\)
            2. +
            3. \(\exists \epsilon \ge 0\) 使得 +\(f(n)=\Theta(n^{\log_ba}\log^\epsilon +n)\),则 \(T(n)=\Theta(n^{\log_ba}\log^{\epsilon+1}n)\)
            4. +
            5. \(\exists \epsilon>0\) 使得 +\(f(n)=\Omega(n^{\log_ba+\epsilon})\) +,且 \(\exists c<1 , \forall n \gg +1\) 使得 \(af(n/b)\le cf(n)\) +,则 \(T(n)=\Theta(f(n))\)
            -

            其中 $\epsilon,c$ 均为常数

            -

            常用结论

            $T(n) = 2T(\frac{n}{2}) + \Theta(n) = \Theta(n \log n)$

            -

            $T(n) = T(\frac{n}{2}) + \Theta (n) = \Theta (n)$

            -

            $T(n) = T(\frac{n}{2}) +\Theta (1) = \Theta(\log n)$

            -

            $T(n) = 2T(\frac{n}{2}) + \Theta(n \sqrt{n}) = \Theta ( n \sqrt{n} )$

            +

            其中 \(\epsilon,c\) 均为常数

            +

            常用结论

            +

            \(T(n) = 2T(\frac{n}{2}) + \Theta(n) = +\Theta(n \log n)\)

            +

            \(T(n) = T(\frac{n}{2}) + \Theta (n) = +\Theta (n)\)

            +

            \(T(n) = T(\frac{n}{2}) +\Theta (1) = +\Theta(\log n)\)

            +

            \(T(n) = 2T(\frac{n}{2}) + \Theta(n +\sqrt{n}) = \Theta ( n \sqrt{n} )\)

          @@ -843,7 +867,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/26/luo-gu-p2851-usaco06dec-the-fewest-coins-g-ti-jie/index.html b/2022/05/26/luo-gu-p2851-usaco06dec-the-fewest-coins-g-ti-jie/index.html index 4ac0074fae..d7a3ef9e1d 100644 --- a/2022/05/26/luo-gu-p2851-usaco06dec-the-fewest-coins-g-ti-jie/index.html +++ b/2022/05/26/luo-gu-p2851-usaco06dec-the-fewest-coins-g-ti-jie/index.html @@ -472,55 +472,126 @@

          洛谷P2851 [USACO06DEC]The Fewe
          -

          洛谷P2851 [USACO06DEC]The Fewest Coins G 题解

          题目链接:P2851 [USACO06DEC]The Fewest Coins G

          +

          洛谷P2851 +[USACO06DEC]The Fewest Coins G 题解

          +

          题目链接:P2851 [USACO06DEC]The +Fewest Coins G

          题意

          -

          Farmer John has gone to town to buy some farm supplies. Being a very efficient man, he always pays for his goods in such a way that the smallest number of coins changes hands, i.e., the number of coins he uses to pay plus the number of coins he receives in change is minimized. Help him to determine what this minimum number is.

          -

          FJ wants to buy T (1 ≤ T ≤ 10,000) cents of supplies. The currency system has N (1 ≤ N ≤ 100) different coins, with values V1, V2, …, VN (1 ≤ Vi ≤ 120). Farmer John is carrying C1 coins of value V1, C2 coins of value V2, …., and CN coins of value VN (0 ≤ Ci ≤ 10,000). The shopkeeper has an unlimited supply of all the coins, and always makes change in the most efficient manner (although Farmer John must be sure to pay in a way that makes it possible to make the correct change).

          +

          Farmer John has gone to town to buy some farm supplies. Being a very +efficient man, he always pays for his goods in such a way that the +smallest number of coins changes hands, i.e., the number of coins he +uses to pay plus the number of coins he receives in change is minimized. +Help him to determine what this minimum number is.

          +

          FJ wants to buy T (1 ≤ T ≤ 10,000) cents of supplies. The currency +system has N (1 ≤ N ≤ 100) different coins, with values V1, V2, ..., VN +(1 ≤ Vi ≤ 120). Farmer John is carrying C1 coins of value V1, C2 coins +of value V2, ...., and CN coins of value VN (0 ≤ Ci ≤ 10,000). The +shopkeeper has an unlimited supply of all the coins, and always makes +change in the most efficient manner (although Farmer John must be sure +to pay in a way that makes it possible to make the correct change).

          农夫John想到镇上买些补给。为了高效地完成任务,他想使硬币的转手次数最少。即使他交付的硬币数与找零得到的的硬币数最少。

          -

          John想要买价值为T的东西。有N(1<=n<=100)种不同的货币参与流通,面值分别为V1,V2..Vn (1<=Vi<=120)。John有Ci个面值为Vi的硬币(0<=Ci<=10000)。

          -

          我们假设店主有无限多的硬币, 并总按最优方案找零。注意无解输出-1。

          +

          John想要买价值为T的东西。有N(1<=n<=100)种不同的货币参与流通,面值分别为V1,V2..Vn +(1<=Vi<=120)。John有Ci个面值为Vi的硬币(0<=Ci<=10000)。

          +

          我们假设店主有无限多的硬币, +并总按最优方案找零。注意无解输出-1。

          不妨把题目转化成下图的形式

          -

          -

          设 $f[j]$ 表示付钱的总额为 $j$ 时所需的最小硬币数

          +

          +

          \(f[j]\) 表示付钱的总额为 \(j\) 时所需的最小硬币数

          显然这是一个多重背包,直接求解即可

          -

          然后设 $g[j]$ 表示找零的总额为 $j$ 时的最小硬币数

          +

          然后设 \(g[j]\) 表示找零的总额为 +\(j\) 时的最小硬币数

          显然这是一个完全背包,直接求解即可

          然后将其合并就好了

          本题的关键在于背包容量的上界如何确定

          -

          结论:$f$ 背包容量上界为 $T+2V_{\max}^2$ ,即 $g$ 背包容量上界为 $2V_{\max}^2$

          -

          证明:翻译+适当修改自 prove ,有错欢迎指出

          -

          方便起见,下文中将使用 $V$ 表示 $V_{\max}$

          +

          结论\(f\) +背包容量上界为 \(T+2V_{\max}^2\) ,即 +\(g\) 背包容量上界为 \(2V_{\max}^2\)

          +

          证明:翻译+适当修改自 prove +,有错欢迎指出

          +

          方便起见,下文中将使用 \(V\) 表示 +\(V_{\max}\)

          -

          引理:对于背包容量 $m$ 模 $V$ 意义下的每个剩余类,我们都可以使用总额不超过 $V^2$ 的若干硬币来表示。

          +

          引理:对于背包容量 \(m\)\(V\) +意义下的每个剩余类,我们都可以使用总额不超过 \(V^2\) 的若干硬币来表示。

          证明:考虑反证法。

          -

          设某种硬币组合的总和 $S \ge V^2$ 并且不可能产生总和为 $S-k\times V,k \in \mathbb{N}$ 的硬币组合

          -

          令这种硬币组合形成的多重集为 $\{x_1,x_2,\dots,x_k\}$

          -

          因为总和至少为 $V^2$ ,所以 $k\ge V$

          -

          考虑对这个多重集做前缀和,也就是把多重集的每个元素以某种方式排列,然后计算 $x_1,x_1+x_2,x_1+x_2+x_3,\dots$

          -

          记 $s_n = \sum_{1\le i \le n} x_i$

          -

          根据 $S$ 的定义可知, $s_i$ 在模 $V$ 意义下不可能为 $0$

          -

          因此 $s_i$ 只有 $V-1$ 种可能性

          -

          根据鸽巢原理,至少存在一对 $i,j(1 \le i < j \le k)$ 使得

          -

          也就是排列后,区间 $[i,j]$ 的和为 $V$ 的倍数

          -

          因此我们可以考虑删除一些能被 $V$ 整除的子集,产生总和为 $S-k\times V,k \in \mathbb{N}$ 的多重集

          +

          设某种硬币组合的总和 \(S \ge V^2\) +并且不可能产生总和为 \(S-k\times V,k \in +\mathbb{N}\) 的硬币组合

          +

          令这种硬币组合形成的多重集为 \(\{x_1,x_2,\dots,x_k\}\)

          +

          因为总和至少为 \(V^2\) ,所以 \(k\ge V\)

          +

          考虑对这个多重集做前缀和,也就是把多重集的每个元素以某种方式排列,然后计算 +\(x_1,x_1+x_2,x_1+x_2+x_3,\dots\)

          +

          \(s_n = \sum_{1\le i \le n} +x_i\)

          +

          根据 \(S\) 的定义可知, \(s_i\) 在模 \(V\) 意义下不可能为 \(0\)

          +

          因此 \(s_i\) 只有 \(V-1\) 种可能性

          +

          根据鸽巢原理,至少存在一对 \(i,j(1 \le i +< j \le k)\) 使得 \[ +s_j-s_i = d \pmod{V} +\] 也就是排列后,区间 \([i,j]\) +的和为 \(V\) 的倍数

          +

          因此我们可以考虑删除一些能被 \(V\) +整除的子集,产生总和为 \(S-k\times V,k \in +\mathbb{N}\) 的多重集

          显然这与我们的假设相矛盾

          -

          因此对于背包容量 $m$ 模 $V$ 意义下的每个剩余类,我们都可以使用总额不超过 $V^2$ 的若干硬币来表示。

          -

          同时,我们还证明了对于所有总和大于 $V^2$ 的多重集,至少有一个 $V$ 硬币

          +

          因此对于背包容量 \(m\)\(V\) +意义下的每个剩余类,我们都可以使用总额不超过 \(V^2\) 的若干硬币来表示。

          +

          同时,我们还证明了对于所有总和大于 \(V^2\) 的多重集,至少有一个 \(V\) 硬币

          -

          现在我们知道,对于所有的目标总额 $S \ge 2V^2$ ,使得 $S \bmod V = d$ ,由于我们一定会选择更优的多重集使得 $\sum x_i \bmod V = d$ ,于是在 $d$ 上添加 $V$ 类型的硬币直到达到 $S$ (这里用到了贪心的思想)

          -

          我们回到问题,假设 Farmer John 得到 $X$ 元的找零,且 $X \ge 2V^2 + v$

          +

          现在我们知道,对于所有的目标总额 \(S \ge +2V^2\) ,使得 \(S \bmod V = d\) +,由于我们一定会选择更优的多重集使得 \(\sum +x_i \bmod V = d\) ,于是在 \(d\) +上添加 \(V\) 类型的硬币直到达到 \(S\) (这里用到了贪心的思想)

          +

          我们回到问题,假设 Farmer John 得到 \(X\) 元的找零,且 \(X \ge 2V^2 + v\)

          还是刚才那张图

          -

          -

          根据 $X$ 的定义,右边黑色的一段至少为 $2V^2$ (这里的 $v$ 表示中间那个被 $T$ “切成”两半的块的超过 $T$ 的部分,这样假设是为了忽略 $T$ 的干扰)

          -

          在黑色块中,总和至少为 $V^2$ (实际上是 $2V^2$ ,但只考虑 $V^2$ ),因此我们证明了存在一个黑色块子集可以被 $V$ 整除

          -

          此外,找零的总额大于等于 $2V^2$ ,故右边蓝色的一段中, 所有的 $V$ 中至少有 $V^2$ (原文有点拗口,其实意思是:至少用了 $V$ 个 $V$ )

          +

          +

          根据 \(X\) +的定义,右边黑色的一段至少为 \(2V^2\) +(这里的 \(v\) 表示中间那个被 \(T\) “切成”两半的块的超过 \(T\) 的部分,这样假设是为了忽略 \(T\) 的干扰)

          +

          在黑色块中,总和至少为 \(V^2\) +(实际上是 \(2V^2\) ,但只考虑 \(V^2\) +),因此我们证明了存在一个黑色块子集可以被 \(V\) 整除

          +

          此外,找零的总额大于等于 \(2V^2\) +,故右边蓝色的一段中, 所有的 \(V\) +中至少有 \(V^2\) +(原文有点拗口,其实意思是:至少用了 \(V\)\(V\)

          因此我们用蓝色块去消掉了黑色块,而枚举这样的情况并没有意义,它等价于消掉的那种情况

          而上面提到了一个更优的方法

          -

          因此最优解的找零最多不大于 $2V^2$

          -

          也就是 $f$ 背包容量上界为 $T+2V_{\max}^2$ ,即 $g$ 背包容量上界为 $2V_{\max}^2$ 。

          +

          因此最优解的找零最多不大于 \(2V^2\)

          +

          也就是 \(f\) 背包容量上界为 \(T+2V_{\max}^2\) ,即 \(g\) 背包容量上界为 \(2V_{\max}^2\)

          代码:

          #include <iostream>
           #include <string>
          @@ -945,7 +1016,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/26/luo-gu-p4170-cqoi2007-tu-se-ti-jie/index.html b/2022/05/26/luo-gu-p4170-cqoi2007-tu-se-ti-jie/index.html index 1e3f5174ed..3567b3c2e2 100644 --- a/2022/05/26/luo-gu-p4170-cqoi2007-tu-se-ti-jie/index.html +++ b/2022/05/26/luo-gu-p4170-cqoi2007-tu-se-ti-jie/index.html @@ -472,25 +472,43 @@

          洛谷P4170 [CQOI2007]涂色 题
          -

          洛谷P4170 [CQOI2007]涂色 题解

          题目链接:P4170 [CQOI2007]涂色

          +

          洛谷P4170 [CQOI2007]涂色 题解

          +

          题目链接:P4170 +[CQOI2007]涂色

          题意

          -

          假设你有一条长度为 $5$ 的木板,初始时没有涂过任何颜色。你希望把它的 $5$ 个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 $5$ 的字符串表示这个目标:$\texttt{RGBGR}$。

          -

          每次你可以把一段连续的木板涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木板涂成 $\texttt{RRRRR}$,第二次涂成 $\texttt{RGGGR}$,第三次涂成 $\texttt{RGBGR}$,达到目标。

          +

          假设你有一条长度为 \(5\) +的木板,初始时没有涂过任何颜色。你希望把它的 \(5\) +个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 \(5\) 的字符串表示这个目标:\(\texttt{RGBGR}\)

          +

          每次你可以把一段连续的木板涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木板涂成 +\(\texttt{RRRRR}\),第二次涂成 \(\texttt{RGGGR}\),第三次涂成 \(\texttt{RGBGR}\),达到目标。

          用尽量少的涂色次数达到目标。

          考虑区间dp

          -

          设 $dp[i][j]$ 表示涂区间 $[i,j]$ 的最小花费

          -

          当 $i=j$ 时,有

          -

          当 $i\ne j$ 时,

          +

          \(dp[i][j]\) 表示涂区间 \([i,j]\) 的最小花费

          +

          \(i=j\) 时,有 \[ +dp[i][j]=1 +\]\(i\ne j\) 时,

            -
          • 若 $a[i]=a[j]$ ,此时只需要 $[i+1,j]$ 或 $[i,j-1]$ 首次涂的时候多涂一格即可

            -
          • -
          • 若 $a[i]\ne a[j]$ , $a[i],a[j]$ 都可以尝试向内扩展,显然 $[i,j]$ 首次涂会涂两种颜色,考虑枚举其断点 $k$

            -
          • +
          • \(a[i]=a[j]\) ,此时只需要 +\([i+1,j]\)\([i,j-1]\) 首次涂的时候多涂一格即可 \[ +dp[i][j]=\min(dp[i+1][j],dp[i][j-1]) +\]

          • +
          • \(a[i]\ne a[j]\)\(a[i],a[j]\) 都可以尝试向内扩展,显然 \([i,j]\) 首次涂会涂两种颜色,考虑枚举其断点 +\(k\) \[ +dp[i][j]=\min(dp[i][j],dp[i][k]+dp[k+1][j]) +\]

          代码:

          #include <iostream>
          @@ -893,7 +911,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/27/luo-gu-p2890-usaco07open-cheapest-palindrome-g-ti-jie/index.html b/2022/05/27/luo-gu-p2890-usaco07open-cheapest-palindrome-g-ti-jie/index.html index cbf5643651..f496710c57 100644 --- a/2022/05/27/luo-gu-p2890-usaco07open-cheapest-palindrome-g-ti-jie/index.html +++ b/2022/05/27/luo-gu-p2890-usaco07open-cheapest-palindrome-g-ti-jie/index.html @@ -472,38 +472,73 @@

          洛谷P2890 [USACO07OPEN]Cheapes
          -

          洛谷P2890 [USACO07OPEN]Cheapest Palindrome G 题解

          题目链接:P2890 [USACO07OPEN]Cheapest Palindrome G

          +

          洛谷P2890 +[USACO07OPEN]Cheapest Palindrome G 题解

          +

          题目链接:P2890 +[USACO07OPEN]Cheapest Palindrome G

          题意

          -

          Keeping track of all the cows can be a tricky task so Farmer John has installed a system to automate it. He has installed on each cow an electronic ID tag that the system will read as the cows pass by a scanner. Each ID tag’s contents are currently a single string with length M (1 ≤ M ≤ 2,000) characters drawn from an alphabet of N (1 ≤ N ≤ 26) different symbols (namely, the lower-case roman alphabet).

          -

          Cows, being the mischievous creatures they are, sometimes try to spoof the system by walking backwards. While a cow whose ID is “abcba” would read the same no matter which direction the she walks, a cow with the ID “abcb” can potentially register as two different IDs (“abcb” and “bcba”).

          -

          FJ would like to change the cows’s ID tags so they read the same no matter which direction the cow walks by. For example, “abcb” can be changed by adding “a” at the end to form “abcba” so that the ID is palindromic (reads the same forwards and backwards). Some other ways to change the ID to be palindromic are include adding the three letters “bcb” to the begining to yield the ID “bcbabcb” or removing the letter “a” to yield the ID “bcb”. One can add or remove characters at any location in the string yielding a string longer or shorter than the original string.

          -

          Unfortunately as the ID tags are electronic, each character insertion or deletion has a cost (0 ≤ cost ≤ 10,000) which varies depending on exactly which character value to be added or deleted. Given the content of a cow’s ID tag and the cost of inserting or deleting each of the alphabet’s characters, find the minimum cost to change the ID tag so it satisfies FJ’s requirements. An empty ID tag is considered to satisfy the requirements of reading the same forward and backward. Only letters with associated costs can be added to a string.

          +

          Keeping track of all the cows can be a tricky task so Farmer John has +installed a system to automate it. He has installed on each cow an +electronic ID tag that the system will read as the cows pass by a +scanner. Each ID tag's contents are currently a single string with +length M (1 ≤ M ≤ 2,000) characters drawn from an alphabet of N (1 ≤ N ≤ +26) different symbols (namely, the lower-case roman alphabet).

          +

          Cows, being the mischievous creatures they are, sometimes try to +spoof the system by walking backwards. While a cow whose ID is "abcba" +would read the same no matter which direction the she walks, a cow with +the ID "abcb" can potentially register as two different IDs ("abcb" and +"bcba").

          +

          FJ would like to change the cows's ID tags so they read the same no +matter which direction the cow walks by. For example, "abcb" can be +changed by adding "a" at the end to form "abcba" so that the ID is +palindromic (reads the same forwards and backwards). Some other ways to +change the ID to be palindromic are include adding the three letters +"bcb" to the begining to yield the ID "bcbabcb" or removing the letter +"a" to yield the ID "bcb". One can add or remove characters at any +location in the string yielding a string longer or shorter than the +original string.

          +

          Unfortunately as the ID tags are electronic, each character insertion +or deletion has a cost (0 ≤ cost ≤ 10,000) which varies depending on +exactly which character value to be added or deleted. Given the content +of a cow's ID tag and the cost of inserting or deleting each of the +alphabet's characters, find the minimum cost to change the ID tag so it +satisfies FJ's requirements. An empty ID tag is considered to satisfy +the requirements of reading the same forward and backward. Only letters +with associated costs can be added to a string.

          字串S长M,由N个小写字母构成。欲通过增删字母将其变为回文串,增删特定字母花费不同,求最小花费。

          显然,一个串的最小花费可以看作:从串内某个字符开始,一边修改,一边在左右添加原串中相邻的字符,最后总的花费

          如果用线性dp,会丢失串的左右端点信息,而且无法从子串转移

          -

          因此考虑使用区间dp(其实上面都是废话,题写多了这种就是一眼的事情 qwq)

          -

          设 $dp[i][j]$ 表示区间 $[i,j]$ 被修改好的最小花费

          -

          不难发现,$i=j$ 时,$dp[i][j]$

          -

          当 $i \ne j$ 时,

          +

          因此考虑使用区间dp(其实上面都是废话,题写多了这种就是一眼的事情 +qwq)

          +

          \(dp[i][j]\) 表示区间 \([i,j]\) 被修改好的最小花费

          +

          不难发现,\(i=j\) 时,\(dp[i][j]\)

          +

          \(i \ne j\) 时,

            -
          • 若 $s[i]=s[j]$ ,显然有

            -

            注意特判 $j-i=1$ 的情况

            -
          • -
          • 若 $s[i] \ne s[j]$

            -

            考虑 $[i+1,j]$ 的转移,显然我们有两种选择,即

            +
          • \(s[i]=s[j]\) ,显然有 \[ +dp[i][j]=\min(dp[i][j],dp[i+1][j-1]) +\] 注意特判 \(j-i=1\) +的情况

          • +
          • \(s[i] \ne s[j]\)

            +

            考虑 \([i+1,j]\) +的转移,显然我们有两种选择,即

              -
            • 删除左侧新加上的 $s[i]$
            • -
            • 右侧再添加一个 $s[i]$
            • +
            • 删除左侧新加上的 \(s[i]\)
            • +
            • 右侧再添加一个 \(s[i]\)
            -

            而这个选择并不会影响后续的转移,所以我们只要选个 $\min$ 就好了

            -

            $[i,j-1]$ 的转移同理

            -

            故有转移方程

            -

            $\text{val}[s[i]]$ 表示 $s[i]$ 字符的最小花费,就是上面说的那个 $\min$

            -
          • +

            而这个选择并不会影响后续的转移,所以我们只要选个 \(\min\) 就好了

            +

            \([i,j-1]\) 的转移同理

            +

            故有转移方程 \[ +dp[i][j]=\min(dp[i+1][j]+\text{val}[s[i]],dp[i][j-1]+\text{val}[s[j]]) +\] \(\text{val}[s[i]]\) 表示 +\(s[i]\) +字符的最小花费,就是上面说的那个 \(\min\)

          然后就转移就完了

          代码:

          @@ -915,7 +950,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/27/luo-gu-p3205-hnoi2010-he-chang-dui-ti-jie/index.html b/2022/05/27/luo-gu-p3205-hnoi2010-he-chang-dui-ti-jie/index.html index 7772feae08..1182eb1bab 100644 --- a/2022/05/27/luo-gu-p3205-hnoi2010-he-chang-dui-ti-jie/index.html +++ b/2022/05/27/luo-gu-p3205-hnoi2010-he-chang-dui-ti-jie/index.html @@ -472,49 +472,82 @@

          洛谷P3205 [HNOI2010]合唱队
          -

          洛谷P3205 [HNOI2010]合唱队 题解

          题目链接:P3205 [HNOI2010]合唱队

          +

          洛谷P3205 [HNOI2010]合唱队 +题解

          +

          题目链接:P3205 +[HNOI2010]合唱队

          题意

          -

          为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A 需要将合唱队的人根据他们的身高排出一个队形。假定合唱队一共 $n$ 个人,第 $i$ 个人的身高为 $h_i$ 米($1000 \le h_i \le 2000$),并已知任何两个人的身高都不同。假定最终排出的队形是 $A$ 个人站成一排,为了简化问题,小 A 想出了如下排队的方式:他让所有的人先按任意顺序站成一个初始队形,然后从左到右按以下原则依次将每个人插入最终棑排出的队形中:

          +

          为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A +需要将合唱队的人根据他们的身高排出一个队形。假定合唱队一共 \(n\) 个人,第 \(i\) 个人的身高为 \(h_i\) 米(\(1000 +\le h_i \le +2000\)),并已知任何两个人的身高都不同。假定最终排出的队形是 +\(A\) 个人站成一排,为了简化问题,小 A +想出了如下排队的方式:他让所有的人先按任意顺序站成一个初始队形,然后从左到右按以下原则依次将每个人插入最终棑排出的队形中:

            -
          • 第一个人直接插入空的当前队形中。

            -
          • -
          • 对从第二个人开始的每个人,如果他比前面那个人高($h$ 较大),那么将他插入当前队形的最右边。如果他比前面那个人矮($h$ 较小),那么将他插入当前队形的最左边。

            -
          • +
          • 第一个人直接插入空的当前队形中。

          • +
          • 对从第二个人开始的每个人,如果他比前面那个人高(\(h\) +较大),那么将他插入当前队形的最右边。如果他比前面那个人矮(\(h\) +较小),那么将他插入当前队形的最左边。

          -

          当 $n$ 个人全部插入当前队形后便获得最终排出的队形。

          -

          例如,有 $6$ 个人站成一个初始队形,身高依次为 $1850, 1900, 1700, 1650, 1800, 1750$,
          那么小 A 会按以下步骤获得最终排出的队形:

          +

          \(n\) +个人全部插入当前队形后便获得最终排出的队形。

          +

          例如,有 \(6\) +个人站成一个初始队形,身高依次为 \(1850, 1900, +1700, 1650, 1800, 1750\)
          +那么小 A 会按以下步骤获得最终排出的队形:

            -
          • $1850$。

            -
          • -
          • $1850, 1900$,因为 $1900 > 1850$。

            -
          • -
          • $1700, 1850, 1900$,因为 $1700 < 1900$。

            -
          • -
          • $1650, 1700, 1850, 1900$,因为 $1650 < 1700$。

            -
          • -
          • $1650, 1700, 1850, 1900, 1800$,因为 $1800 > 1650$。

            -
          • -
          • $1750, 1650, 1700, 1850, 1900, 1800$,因为 $1750 < 1800$。

            -
          • +
          • \(1850\)

          • +
          • \(1850, 1900\),因为 \(1900 > 1850\)

          • +
          • \(1700, 1850, 1900\),因为 \(1700 < 1900\)

          • +
          • \(1650, 1700, 1850, 1900\),因为 +\(1650 < 1700\)

          • +
          • \(1650, 1700, 1850, 1900, +1800\),因为 \(1800 > +1650\)

          • +
          • \(1750, 1650, 1700, 1850, 1900, +1800\),因为 \(1750 < +1800\)

          -

          因此,最终排出的队形是 $1750, 1650, 1700, 1850, 1900, 1800$。

          -

          小 A 心中有一个理想队形,他想知道多少种初始队形可以获得理想的队形。

          -

          请求出答案对 $19650827$ 取模的值。

          +

          因此,最终排出的队形是 \(1750, 1650, 1700, +1850, 1900, 1800\)

          +

          小 A +心中有一个理想队形,他想知道多少种初始队形可以获得理想的队形。

          +

          请求出答案对 \(19650827\) +取模的值。

          容易发现方案数与区间的长度有关

          并且一个区间的两侧都可以扩展

          考虑使用区间dp

          这个题看上去还挺乱的,所以我们抓住关键点

          -

          如果某一排列中, $b$ 比它前面的 $a$ 小,那么 $b$ 会从左边加入区间

          -

          而加入 $b$ 之前, $a$ 只有可能在区间的左端点或右端点

          -

          设 $dp[i][j][0/1]$ 表示区间 $[i,j]$ 的方案数,其中:

          +

          如果某一排列中, \(b\) 比它前面的 +\(a\) 小,那么 \(b\) 会从左边加入区间

          +

          而加入 \(b\) 之前, \(a\) 只有可能在区间的左端点或右端点

          +

          \(dp[i][j][0/1]\) 表示区间 \([i,j]\) 的方案数,其中:

            -
          • $0$ 表示 $a[i]$ 是本次转移中 从左侧 向区间内加入的数
          • -
          • $1$ 表示 $a[j]$ 是本次转移中 从右侧 向区间内加入的数
          • +
          • \(0\) 表示 \(a[i]\) 是本次转移中 从左侧 +向区间内加入的数
          • +
          • \(1\) 表示 \(a[j]\) 是本次转移中 从右侧 +向区间内加入的数
          -

          对于左侧加入的数,即 $a[i]$ ,它的前一个数要么是 $a[i+1]$ ,要么是 $a[j]$

          -

          对于右侧加入的数,即 $a[j]$ ,它的前一个数要么是 $a[j-1]$ ,要么是 $a[i]$

          +

          对于左侧加入的数,即 \(a[i]\) +,它的前一个数要么是 \(a[i+1]\) +,要么是 \(a[j]\)

          +

          对于右侧加入的数,即 \(a[j]\) +,它的前一个数要么是 \(a[j-1]\) +,要么是 \(a[i]\)

          如果满足条件即可转移,如下

          if(a[i]<a[i+1]) dp[i][j][0]+=dp[i+1][j][0];
           if(a[i]<a[j]) dp[i][j][0]+=dp[i+1][j][1];
          @@ -522,16 +555,22 @@ 

          if(a[j]>a[i]) dp[i][j][1]+=dp[i][j-1][0]; dp[i][j][0]%=p; dp[i][j][1]%=p; dp[i][j][0]%=p; dp[i][j][1]%=p; // p=19650827

          -

          考虑边界 $i=j$ 时

          +

          考虑边界 \(i=j\)

          此时认为它们都是左侧加入的即可(认为是右侧也可)

          -

          dp[i][i][0]=1,而不是dp[i][i][0]=dp[i][i][1]=1

          +

          即 +dp[i][i][0]=1,而不是dp[i][i][0]=dp[i][i][1]=1

          为什么前者是正确的,而后者是错误的?

          -

          因为区间长度为 $2$ 时,有 $i+1=j$ 且 $j-1=i$

          -

          也就是后面的会多转移一次,根据乘法原理,最后答案会 $\times 2$

          -

          因此要去掉这个 $2$ (当然你可以去求个逆元啥的 qwq)

          +

          因为区间长度为 \(2\) 时,有 \(i+1=j\)\(j-1=i\)

          +

          也就是后面的会多转移一次,根据乘法原理,最后答案会 \(\times 2\)

          +

          因此要去掉这个 \(2\) +(当然你可以去求个逆元啥的 qwq)

          -

          然后我们就可以 $O(n^2)$ 解决这个题目辣!

          +

          然后我们就可以 \(O(n^2)\) +解决这个题目辣!

          代码:

          #include <iostream>
           #include <string>
          @@ -724,14 +763,14 @@ 

          算法 - - DP - - 图论 + + DP + +

          @@ -938,7 +977,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/27/uva1437-string-painter-ti-jie/index.html b/2022/05/27/uva1437-string-painter-ti-jie/index.html index 0abb2683f2..a8be139e16 100644 --- a/2022/05/27/uva1437-string-painter-ti-jie/index.html +++ b/2022/05/27/uva1437-string-painter-ti-jie/index.html @@ -472,48 +472,75 @@

          UVA1437 String painter 题解
          -

          UVA1437 String painter 题解

          题目链接:UVA1437 String painter

          +

          UVA1437 String painter 题解

          +

          题目链接:UVA1437 +String painter

          -

          题意:There are two strings A and B with equal length. Both strings are made up of lower case letters. Now you have a powerful string painter. With the help of the painter, you can change a segment of characters of a string to any other character you want. That is, after using the painter, the segment is made up of only one kind of character. Now your task is to change A to B using string painter. What’s the minimum number of operations?

          +

          题意:There are two strings A and B with equal +length. Both strings are made up of lower case letters. Now you have a +powerful string painter. With the help of the painter, you can change a +segment of characters of a string to any other character you want. That +is, after using the painter, the segment is made up of only one kind of +character. Now your task is to change A to B using string painter. +What’s the minimum number of operations?

          1 <= |A|=|B| <= 100

          显然区间dp

          考虑A与B完全不同

          -

          设 $f[i][j]$ 表示暴力(不管A的情况)修改 $[i,j]$ 的最小花费

          -

          那就是这个题目

          -

          当 $i=j$ 时,有

          -

          当 $i\ne j$ 时,

          +

          \(f[i][j]\) +表示暴力(不管A的情况)修改 \([i,j]\) +的最小花费

          +

          那就是这个题目

          +

          \(i=j\) 时,有 \[ +f[i][j]=1 +\]\(i\ne j\) 时,

            -
          • 若 $b[i]=b[j]$ ,此时只需要 $[i+1,j]$ 或 $[i,j-1]$ 首次涂的时候多涂一格即可

            -
          • -
          • 若 $b[i]\ne b[j]$ , $b[i],b[j]$ 都可以尝试向内扩展,显然 $[i,j]$ 首次涂会涂两种颜色,考虑枚举其断点 $k$

            -
          • +
          • \(b[i]=b[j]\) ,此时只需要 +\([i+1,j]\)\([i,j-1]\) 首次涂的时候多涂一格即可 \[ +f[i][j]=\min(f[i+1][j],f[i][j-1]) +\]

          • +
          • \(b[i]\ne b[j]\)\(b[i],b[j]\) 都可以尝试向内扩展,显然 \([i,j]\) 首次涂会涂两种颜色,考虑枚举其断点 +\(k\) \[ +f[i][j]=\min(f[i][j],f[i][k]+f[k+1][j]) +\]

          显然有时候我们的暴力修改时不必要的

          -

          设 $g[i]$ 表示 $[1,i]$ 的最小修改,则 $g[i]$ 至多为 $f[1][i]$ ,且有 $g[0]=0$

          +

          \(g[i]\) 表示 \([1,i]\) 的最小修改,则 \(g[i]\) 至多为 \(f[1][i]\) ,且有 \(g[0]=0\)

            -
          • 若 $a[i]=b[i]$ ,则

            -

            显然这个 $g[i-1]$ 一定不大于 $f[1][i]$ ,直接转移即可

            -
          • -
          • 若 $a[i] \ne b[i]$ ,则此时 $a[i]$ 是必须得暴力修改的了

            +
          • \(a[i]=b[i]\) ,则 \[ +g[i]=g[i-1] +\] 显然这个 \(g[i-1]\) +一定不大于 \(f[1][i]\) +,直接转移即可

          • +
          • \(a[i] \ne b[i]\) ,则此时 +\(a[i]\) 是必须得暴力修改的了

            但是这个修改长度我们不知道

            -

            考虑枚举一个断点 $k$ ,也就是 $[k+1,i]$ 第一次涂色涂成 $a[i]$

            +

            考虑枚举一个断点 \(k\) ,也就是 +\([k+1,i]\) 第一次涂色涂成 \(a[i]\)

            为什么不是上个极大公共子串的末尾开始呢?

            考虑这种情况

            zzzzfzzzz
             abcdfdcba
            -

            最小花费为 $5$

            +

            最小花费为 \(5\)

            -

            故转移方程为

            -
          • +

            故转移方程为 \[ +g[i]=\min_{0 \le k \le i}(g[i],g[k]+f[k+1][i]) +\]

          -

          时间复杂度 $O(Qn^2)$

          +

          时间复杂度 \(O(Qn^2)\)

          代码:

          #include <iostream>
           #include <string>
          @@ -926,7 +953,7 @@ 

            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/27/uva1629-qie-dan-gao-cake-slicing-ti-jie/index.html b/2022/05/27/uva1629-qie-dan-gao-cake-slicing-ti-jie/index.html index 1bb45a140c..f3f2191f02 100644 --- a/2022/05/27/uva1629-qie-dan-gao-cake-slicing-ti-jie/index.html +++ b/2022/05/27/uva1629-qie-dan-gao-cake-slicing-ti-jie/index.html @@ -472,22 +472,36 @@

          UVA1629 切蛋糕 Cake slicing
          -

          UVA1629 切蛋糕 Cake slicing 题解

          题目链接:UVA1629 切蛋糕 Cake slicing

          +

          UVA1629 切蛋糕 Cake slicing +题解

          +

          题目链接:UVA1629 +切蛋糕 Cake slicing

          -

          题意:这个翻译够烂的,直接看pdf

          +

          题意:这个翻译够烂的,直接看pdf

          翻译:有一个n行m列(1<=n,m<=20)的网络蛋糕上有k个樱桃。每次可以用一刀沿着网络线把蛋糕切成两块,并且只能够直切不能拐弯。要求最后每一块蛋糕上恰好有一个樱桃,且切割线总长度最小。

          -

          输入输出格式 输入格式:每次输入有若干组数据。每组数据第一行有三个正整数n m k(行,列,樱桃个数),之后的k行每行两个正整数(樱桃的坐标) 输出格式:输出有若干行,对应每组数据。每行输出两个正整数(id,最小的切割长度)

          +

          输入输出格式 +输入格式:每次输入有若干组数据。每组数据第一行有三个正整数n m +k(行,列,樱桃个数),之后的k行每行两个正整数(樱桃的坐标) +输出格式:输出有若干行,对应每组数据。每行输出两个正整数(id,最小的切割长度)

          输入输出样例 输入样例: 3 4 3 1 2 2 3 3 2 输出样例: Case 1: 5

          首先普通的二维枚举好像不行

          数据范围这么小,于是考虑从小的矩形向大的矩形更新

          -

          设 $dp[x_1][y_1][x_2][y_2]$ 表示矩形的左下角为 $(x_1,y_1)$ ,右上角为 $(x_2,y_2)$ 时的最小花费

          -

          方便起见,下面转移方程中的 $dp[x_1][y_1][x_2][y_2]$ 用 $d$ 替代

          -

          考虑纵切,则有

          -

          考虑横切,则有

          -

          由于顺推无法保证dp顺序,考虑记忆化搜索

          +

          \(dp[x_1][y_1][x_2][y_2]\) +表示矩形的左下角为 \((x_1,y_1)\) +,右上角为 \((x_2,y_2)\) +时的最小花费

          +

          方便起见,下面转移方程中的 \(dp[x_1][y_1][x_2][y_2]\)\(d\) 替代

          +

          考虑纵切,则有 \[ +d=\min_{x_1 \le i < +x_2}(d,dp[x_1][y_1][i][y_2]+dp[i+1][y_1][x_2][y_2]+y_2-y_1+1) +\] 考虑横切,则有 \[ +d=\min_{y_1 \le i < +y_2}(d,dp[x_1][y_1][x_2][i]+dp[x_1][i+1][x_2][y_2]+x_2-x_1+1) +\] 由于顺推无法保证dp顺序,考虑记忆化搜索

          代码:

          #include <iostream>
           #include <string>
          @@ -901,7 +915,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/28/luo-gu-p1122-zui-da-zi-shu-he-ti-jie/index.html b/2022/05/28/luo-gu-p1122-zui-da-zi-shu-he-ti-jie/index.html index 5d05ea1f0e..df2e6e4e61 100644 --- a/2022/05/28/luo-gu-p1122-zui-da-zi-shu-he-ti-jie/index.html +++ b/2022/05/28/luo-gu-p1122-zui-da-zi-shu-he-ti-jie/index.html @@ -418,14 +418,14 @@

          洛谷P1122 最大子树和 题 算法 - - DP - - 图论 + + DP + +

          @@ -476,24 +476,34 @@

          洛谷P1122 最大子树和 题
          -

          洛谷P1122 最大子树和 题解

          题目链接:P1122 最大子树和

          +

          洛谷P1122 最大子树和 题解

          +

          题目链接:P1122 +最大子树和

          题意

          小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花卉的问题。于是当日课后,小明就向老师提出了这个问题:

          -

          一株奇怪的花卉,上面共连有 $N$ 朵花,共有 $N-1$ 条枝干将花儿连在一起,并且未修剪时每朵花都不是孤立的。每朵花都有一个“美丽指数”,该数越大说明这朵花越漂亮,也有“美丽指数”为负数的,说明这朵花看着都让人恶心。所谓“修剪”,意为:去掉其中的一条枝条,这样一株花就成了两株,扔掉其中一株。经过一系列“修剪“之后,还剩下最后一株花(也可能是一朵)。老师的任务就是:通过一系列“修剪”(也可以什么“修剪”都不进行),使剩下的那株(那朵)花卉上所有花朵的“美丽指数”之和最大。

          +

          一株奇怪的花卉,上面共连有 \(N\) +朵花,共有 \(N-1\) +条枝干将花儿连在一起,并且未修剪时每朵花都不是孤立的。每朵花都有一个“美丽指数”,该数越大说明这朵花越漂亮,也有“美丽指数”为负数的,说明这朵花看着都让人恶心。所谓“修剪”,意为:去掉其中的一条枝条,这样一株花就成了两株,扔掉其中一株。经过一系列“修剪“之后,还剩下最后一株花(也可能是一朵)。老师的任务就是:通过一系列“修剪”(也可以什么“修剪”都不进行),使剩下的那株(那朵)花卉上所有花朵的“美丽指数”之和最大。

          老师想了一会儿,给出了正解。小明见问题被轻易攻破,相当不爽,于是又拿来问你。

          一眼树形dp

          这道题就是要在树上找一个极大权值连通块

          显然我们不知道连通块是什么样的,但是不急

          -

          树形dp的核心是从子树转移状态

          -

          设 $dp[u]$ 表示 $u$ 所在子树中包含 $u$ 的极大连通块大小

          -

          在本题中,如果 $u$ 的子结点 $v$ 有 $dp[v] > 0$ ,那么可以把 $v$ 给拼接到 $u$ 上

          -

          于是有

          -

          最后答案就是 $\max(dp[u])$

          -

          时间复杂度 $O(n^2)$

          +

          树形dp的核心是从子树转移状态 \[ +dp[u] \leftarrow f(dp[v]) +\]\(dp[u]\) 表示 \(u\) 所在子树中包含 \(u\) 的极大连通块大小

          +

          在本题中,如果 \(u\) 的子结点 \(v\)\(dp[v] +> 0\) ,那么可以把 \(v\) +给拼接到 \(u\)

          +

          于是有 \[ +dp[u]=\sum_{dp[v]>0} dp[v] + \text{val}[u] +\] 最后答案就是 \(\max(dp[u])\)

          +

          时间复杂度 \(O(n^2)\)

          代码:

          #include <iostream>
           #include <string>
          @@ -608,14 +618,14 @@ 

          算法 - - DP - - 图论 + + DP + +

          @@ -699,14 +709,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -917,7 +927,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/28/luo-gu-p2016-zhan-lue-you-xi-ti-jie/index.html b/2022/05/28/luo-gu-p2016-zhan-lue-you-xi-ti-jie/index.html index 03d4e5d3b3..eafc21c304 100644 --- a/2022/05/28/luo-gu-p2016-zhan-lue-you-xi-ti-jie/index.html +++ b/2022/05/28/luo-gu-p2016-zhan-lue-you-xi-ti-jie/index.html @@ -472,18 +472,21 @@

          洛谷P2016 战略游戏 题解<
          -

          洛谷P2016 战略游戏 题解

          题目链接:P2016 战略游戏

          +

          洛谷P2016 战略游戏 题解

          +

          题目链接:P2016 +战略游戏

          -

          题意: Bob 要建立一个古城堡,城堡中的路形成一棵无根树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。

          +

          题意: Bob +要建立一个古城堡,城堡中的路形成一棵无根树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。

          注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到。

          请你编一程序,给定一树,帮 Bob 计算出他需要放置最少的士兵。

          注意这里是相邻的,不是相邻的结点

          显然我们需要记录与子结点所连的边是否被监视

          -

          于是有

          -

          然后就没了

          +dp[u][1]=\sum \min(dp[v][0],dp[v][1]) +\] 然后就没了

          代码:

          #include <iostream>
           #include <string>
          @@ -688,14 +691,14 @@ 

          算法 - - DP - - 图论 + + DP + +

          @@ -745,14 +748,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -910,7 +913,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/28/luo-gu-p2458-sdoi2006-bao-an-zhan-gang-ti-jie/index.html b/2022/05/28/luo-gu-p2458-sdoi2006-bao-an-zhan-gang-ti-jie/index.html index 0cfb3ed550..b0003689e8 100644 --- a/2022/05/28/luo-gu-p2458-sdoi2006-bao-an-zhan-gang-ti-jie/index.html +++ b/2022/05/28/luo-gu-p2458-sdoi2006-bao-an-zhan-gang-ti-jie/index.html @@ -418,14 +418,14 @@

          洛谷P2458 [SDOI2006]保安站 算法 - - DP - - 图论 + + DP + +

          @@ -476,7 +476,10 @@

          洛谷P2458 [SDOI2006]保安站
          -

          洛谷P2458 [SDOI2006]保安站岗 题解

          题目链接:P2458 [SDOI2006]保安站岗

          +

          洛谷P2458 [SDOI2006]保安站岗 +题解

          +

          题目链接:P2458 +[SDOI2006]保安站岗

          题意

          五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序。

          @@ -487,23 +490,30 @@

          \(dp[u][0/1/2]\) 表示 \(u\) 结点:

            -
          • $0$ 表示被自己覆盖(自己放保安)
          • -
          • $1$ 表示被儿子覆盖(某个(或某些)儿子放保安)
          • -
          • $2$ 表示被父亲覆盖(父亲放保安)
          • +
          • \(0\) +表示被自己覆盖(自己放保安)
          • +
          • \(1\) +表示被儿子覆盖(某个(或某些)儿子放保安)
          • +
          • \(2\) +表示被父亲覆盖(父亲放保安)
          -

          容易推出 $0,2$ 的转移方程

          -

          而 $1$ 相对较麻烦

          -

          首先有从子结点的转移

          -

          但是如果所有的儿子都没有在他们自己那放保安,

          +dp[u][2]=\sum \min(dp[v][0/1]) +\] 而 \(1\) 相对较麻烦

          +

          首先有从子结点的转移 \[ +dp[u][2]=\sum \min(dp[v][0/1]) +\] 但是如果所有的儿子都没有在他们自己那放保安,

          则还要加上在在某个儿子放保安的最小花费

          -

          -

          为什么要减呢?因为在某个 $v$ 放了保安后,就不用 $dp[v][1]$ 的花费了

          +

          \[ +\min(dp[v][0]-dp[v][1]) +\] 为什么要减呢?因为在某个 \(v\) 放了保安后,就不用 \(dp[v][1]\) 的花费了

          代码:

          #include <iostream>
           #include <string>
          @@ -628,14 +638,14 @@ 

          算法 - - DP - - 图论 + + DP + +

          @@ -933,7 +943,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/28/luo-gu-p4084-usaco17dec-barn-painting-g-ti-jie/index.html b/2022/05/28/luo-gu-p4084-usaco17dec-barn-painting-g-ti-jie/index.html index 1f4a3dc0ab..82e9735143 100644 --- a/2022/05/28/luo-gu-p4084-usaco17dec-barn-painting-g-ti-jie/index.html +++ b/2022/05/28/luo-gu-p4084-usaco17dec-barn-painting-g-ti-jie/index.html @@ -418,14 +418,14 @@

          洛谷P4084 [USACO17DEC]Barn Pai 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -480,18 +480,24 @@

          洛谷P4084 [USACO17DEC]Barn Pai
          -

          洛谷P4084 [USACO17DEC]Barn Painting G 题解

          题目链接:P4084 [USACO17DEC]Barn Painting G

          +

          洛谷P4084 +[USACO17DEC]Barn Painting G 题解

          +

          题目链接:P4084 +[USACO17DEC]Barn Painting G

          题意:题意:给定一颗N个节点组成的树,3种颜色,其中K个节点已染色,要求任意两相邻节点颜色不同,求合法染色方案数。

          这个题太水了

          -

          设 $dp[u][1/2/3]$ 表示 $u$ 结点涂 $1/2/3$ 颜色的方案数

          +

          \(dp[u][1/2/3]\) 表示 \(u\) 结点涂 \(1/2/3\) 颜色的方案数

          根据加法原理,

          -

          如果 $u$ 已经有颜色了,那么

          -

          否则

          -

          时间复杂度 $O(n)$

          +

          如果 \(u\) 已经有颜色了,那么 \[ +dp[u][j] = \sum dp[v][k]\times [k\ne j]\times [j=\text{val[u]}] +\] 否则 \[ +dp[u][j] = \sum dp[v][k]\times [k\ne j] +\] 时间复杂度 \(O(n)\)

          代码:

          #include <iostream>
           #include <string>
          @@ -620,14 +626,14 @@ 

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -768,14 +774,14 @@

          算法 - - DP - - 图论 + + DP + +

          @@ -929,7 +935,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/30/luo-gu-p2015-er-cha-ping-guo-shu-ti-jie/index.html b/2022/05/30/luo-gu-p2015-er-cha-ping-guo-shu-ti-jie/index.html index fae83c6dbb..0c77ea7bcd 100644 --- a/2022/05/30/luo-gu-p2015-er-cha-ping-guo-shu-ti-jie/index.html +++ b/2022/05/30/luo-gu-p2015-er-cha-ping-guo-shu-ti-jie/index.html @@ -418,14 +418,14 @@

          洛谷P2015 二叉苹果树 题 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -480,29 +480,43 @@

          洛谷P2015 二叉苹果树 题
          -

          洛谷P2015 二叉苹果树 题解

          题目链接:P2015 二叉苹果树

          +

          洛谷P2015 二叉苹果树 题解

          +

          题目链接:P2015 +二叉苹果树

          题意

          有一棵苹果树,如果树枝有分叉,一定是分二叉(就是说没有只有一个儿子的结点)

          -

          这棵树共有 $N$ 个结点(叶子点或者树枝分叉点),编号为 $1 \sim N$,树根编号一定是 $1$。

          -

          我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有 $4$ 个树枝的树:

          +

          这棵树共有 \(N\) +个结点(叶子点或者树枝分叉点),编号为 \(1 +\sim N\),树根编号一定是 \(1\)

          +

          我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有 +\(4\) 个树枝的树:

          2   5
            \ / 
             3   4
              \ /
               1

          现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。

          -

          给定需要保留的树枝数量 $m$ ,求出最多能留住多少苹果。

          +

          给定需要保留的树枝数量 \(m\) +,求出最多能留住多少苹果。

          经典的树形依赖背包(树上背包)

          -

          设 $dp[u][j]$ 表示 $u$ 结点所在子树保留 $j$ 条边所能得到的最大苹果树

          -

          则有

          -

          为什么是 $j-k-1$ 呢

          -

          因为 $k$ 是 $v$ 所在子树的分配边数,但是你要花一条边去连上 $v$

          -

          不然你给了 $v$ 一共 $k$ 条边最后没连上它啥也没有

          -

          但是填表法会被卡到 $O(nm^2)$ ,所以考虑刷表法

          -

          时间复杂度 $O(nm)$

          +

          \(dp[u][j]\) 表示 \(u\) 结点所在子树保留 \(j\) 条边所能得到的最大苹果树

          +

          则有 \[ +dp[u][j]=\max(dp[u][j],dp[u][j-k-1]+dp[v][k]+w(u,v)) +\] 为什么是 \(j-k-1\)

          +

          因为 \(k\)\(v\) +所在子树的分配边数,但是你要花一条边去连上 \(v\)

          +

          不然你给了 \(v\) 一共 \(k\) 条边最后没连上它啥也没有

          +

          但是填表法会被卡到 \(O(nm^2)\) +,所以考虑刷表法

          +

          时间复杂度 \(O(nm)\)

          代码:

          #include <iostream>
           #include <string>
          @@ -632,14 +646,14 @@ 

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -727,14 +741,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -788,14 +802,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -953,7 +967,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/30/luo-gu-p2279-hnoi2003-xiao-fang-ju-de-she-li-ti-jie/index.html b/2022/05/30/luo-gu-p2279-hnoi2003-xiao-fang-ju-de-she-li-ti-jie/index.html index 7612f0e5b8..8258656cef 100644 --- a/2022/05/30/luo-gu-p2279-hnoi2003-xiao-fang-ju-de-she-li-ti-jie/index.html +++ b/2022/05/30/luo-gu-p2279-hnoi2003-xiao-fang-ju-de-she-li-ti-jie/index.html @@ -418,14 +418,14 @@

          洛谷P2279 [HNOI2003]消防局 算法 - - DP - - 图论 + + DP + +

          @@ -476,19 +476,31 @@

          洛谷P2279 [HNOI2003]消防局
          -

          洛谷P2279 [HNOI2003]消防局的设立 题解

          题目链接:P2279 [HNOI2003]消防局的设立

          +

          洛谷P2279 +[HNOI2003]消防局的设立 题解

          +

          题目链接:P2279 +[HNOI2003]消防局的设立

          题意

          -

          2020 年,人类在火星上建立了一个庞大的基地群,总共有 $n$ 个基地。起初为了节约材料,人类只修建了 $n-1$ 条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地 $A$ 到基地 $B$ 至少要经过 $d$ 条道路的话,我们称基地A到基地B的距离为 $d$。

          -

          由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过 $2$ 的基地的火灾。

          +

          2020 年,人类在火星上建立了一个庞大的基地群,总共有 \(n\) 个基地。起初为了节约材料,人类只修建了 +\(n-1\) +条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地 +\(A\) 到基地 \(B\) 至少要经过 \(d\) 条道路的话,我们称基地A到基地B的距离为 +\(d\)

          +

          由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过 +\(2\) 的基地的火灾。

          你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾。

          一开始以为是树形dp(确实是),结果发现贪心可做。

          树形dp的状态十分恶心,贪心相对简单

          -

          注意到距离不大于 $2$ 的除了爷爷、父亲、儿子、孙子结点,还有它的兄弟结点

          +

          注意到距离不大于 \(2\) +的除了爷爷、父亲、儿子、孙子结点,还有它的兄弟结点

          对于一个结点,如果我们在它的爷爷放了一个消防站,则可以将其父亲和兄弟全部覆盖

          则我们可以每次选出深度最大的结点,把它爷爷放个消防站,然后暴力修改一下

          -

          时间复杂度 $O(n^2)$

          +

          时间复杂度 \(O(n^2)\)

          代码:

          #include <iostream>
           #include <string>
          @@ -622,14 +634,14 @@ 

          算法 - - DP - - 图论 + + DP + +

          @@ -770,14 +782,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -935,7 +947,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/30/luo-gu-p3174-haoi2009-mao-mao-chong-ti-jie/index.html b/2022/05/30/luo-gu-p3174-haoi2009-mao-mao-chong-ti-jie/index.html index 3c874db3f9..2f63e06fef 100644 --- a/2022/05/30/luo-gu-p3174-haoi2009-mao-mao-chong-ti-jie/index.html +++ b/2022/05/30/luo-gu-p3174-haoi2009-mao-mao-chong-ti-jie/index.html @@ -418,14 +418,14 @@

          洛谷P3174 [HAOI2009] 毛毛虫 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -480,19 +480,26 @@

          洛谷P3174 [HAOI2009] 毛毛虫
          -

          洛谷P3174 [HAOI2009] 毛毛虫 题解

          题目链接:P3174 [HAOI2009] 毛毛虫

          +

          洛谷P3174 [HAOI2009] 毛毛虫 +题解

          +

          题目链接:P3174 +[HAOI2009] 毛毛虫

          题意

          -

          对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大。例如下图左边的树(图 $1$)抽出一部分就变成了右边的一个毛毛虫了(图 $2$)。

          -

          +

          对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大。例如下图左边的树(图 +\(1\))抽出一部分就变成了右边的一个毛毛虫了(图 +\(2\))。

          +

          -

          这道题是可以树形dp的,具体看这里 link,不是本文重点。

          -

          设 $f[u]$ 为 $u$ 结点所在子树的最大毛毛虫🐛大小

          -

          则有

          -

          然后还要维护个次大值啥的,挺麻烦的

          +

          这道题是可以树形dp的,具体看这里 link,不是本文重点。

          +

          \(f[u]\)\(u\) 结点所在子树的最大毛毛虫🐛大小

          +

          则有 \[ +f[u]=\max(f[v])+1+\max(\text{cnt}[u]-1,0) +\] 然后还要维护个次大值啥的,挺麻烦的


          -

          其实这道题就是树的直径。两遍dfs就能搞定的问题

          为什么树的直径对呢

          考虑两条路径,长度相等,则答案一定相等,这个很显然

          @@ -609,14 +616,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -704,14 +711,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -926,7 +933,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/30/luo-gu-p3177-haoi2015-shu-shang-ran-se-ti-jie/index.html b/2022/05/30/luo-gu-p3177-haoi2015-shu-shang-ran-se-ti-jie/index.html index fc4de11ffd..0636581244 100644 --- a/2022/05/30/luo-gu-p3177-haoi2015-shu-shang-ran-se-ti-jie/index.html +++ b/2022/05/30/luo-gu-p3177-haoi2015-shu-shang-ran-se-ti-jie/index.html @@ -418,14 +418,14 @@

          洛谷P3177 [HAOI2015] 树上染 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -480,31 +480,44 @@

          洛谷P3177 [HAOI2015] 树上染
          -

          洛谷P3177 [HAOI2015] 树上染色 题解

          题目链接:P3177 [HAOI2015] 树上染色

          +

          洛谷P3177 [HAOI2015] 树上染色 +题解

          +

          题目链接:P3177 +[HAOI2015] 树上染色

          题意

          -

          有一棵点数为 $n$ 的树,树边有边权。给你一个在 $0 \sim n$ 之内的正整数 $m$ ,你要在这棵树中选择 $m$ 个点,将其染成黑色,并将其他 的 $n-m$ 个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。

          +

          有一棵点数为 \(n\) +的树,树边有边权。给你一个在 \(0 \sim +n\) 之内的正整数 \(m\) +,你要在这棵树中选择 \(m\) +个点,将其染成黑色,并将其他 的 \(n-m\) +个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。

          -

          upd.20220531 由于本人对树上背包掌握不熟,导致复杂度写假了

          +

          upd.20220531 +由于本人对树上背包掌握不熟,导致复杂度写假了

          其实是题解区普遍写错罢了。

          不过原文只是没用刷表法

          于是在原文基础上做了修改,不影响观感,请放心食用(逃


          -

          显然树上背包

          -

          设 $dp[u][j]$ 表示 $u$ 所在子树染了 $j$ 个黑点的最大价值

          -

          容易推出一个大概的方程

          -

          注:下文会提到这个转移方程是有点问题的

          +

          \(dp[u][j]\) 表示 \(u\) 所在子树染了 \(j\) 个黑点的最大价值

          +

          容易推出一个大概的方程 \[ +dp[u][j]=\max(dp[u][j],dp[u][j-k]+dp[v][k]+\text{val}) +\] 注:下文会提到这个转移方程是有点问题的

          黑点两两距离+白点两两距离

          -

          直接去算就是 $O(n^2)$ 的了

          +

          直接去算就是 \(O(n^2)\) 的了

          考虑更好的计算方法

          一条路径会包括若干条边

          因为是树所以有很多的边会被重复走过

          考虑将距离计算转化为边重复经过次数

          -

          根据乘法原理,可知边 $(u,v)$ ( $u$ 为父结点)的贡献为

          -

          upd.20220531 然后填表法写出来就是这样的(原文代码)

          +

          根据乘法原理,可知边 \((u,v)\) ( +\(u\) 为父结点)的贡献为 \[ +\text{val}=w(u,v)\times(k(m-k)+(\text{sz}[v]-k)(n-m-\text{sz}[v]+k)) +\] upd.20220531 +然后填表法写出来就是这样的(原文代码)

          for(int i=head[u]; i; i=e[i].next)
           {
               int v=e[i].v;
          @@ -523,11 +536,15 @@ 

          } } }

          -

          不难发现这个其实复杂度是可以被卡到 $O(nm^2)$ 的

          -

          所以考虑刷表法,即

          -

          这里的 $\text{tmp}[j]$ 是上一轮的 $dp[u][j]$ ,刷表的过程中会被刷坏,所以要先存一下

          -

          这样时间复杂度才是严格的 $O(nm)$

          +

          不难发现这个其实复杂度是可以被卡到 \(O(nm^2)\)

          +

          所以考虑刷表法,即 \[ +dp[u][j+k]=\max(dp[u][j+k],\text{tmp}[j]+dp[v][k]+\text{val}) +\] 这里的 \(\text{tmp}[j]\) +是上一轮的 \(dp[u][j]\) +,刷表的过程中会被刷坏,所以要先存一下

          +

          这样时间复杂度才是严格的 \(O(nm)\)

          代码:

          #include <iostream>
           #include <string>
          @@ -593,17 +610,25 @@ 

          return 0; }

          -

          这一段可以跳过,只是详细解释了link的东西,

          +

          这一段可以跳过,只是详细解释了link的东西,

          而且ta的代码复杂度也是假的,因为都是填表法

          -

          注意到有些人 $k$ 倒序枚举,要先转移 $k=0$ 的情况,这里解释一下

          +

          注意到有些人 \(k\) +倒序枚举,要先转移 \(k=0\) +的情况,这里解释一下

          -

          关于为什么要先将 $k=0$ 的转移

          -

          观察方程,因为如果直接倒序枚举,最后一次 $k=0$ 的枚举

          -

          会出现 $dp[u][j]=\max(dp[u][j],dp[u][j]+\text{val})$ 的情况

          -

          显然此时的 $dp[u][j]$ 已经被更新过了

          -

          因此会导致答案有误(偏大)

          +

          关于为什么要先将 \(k=0\) 的转移

          +

          观察方程,因为如果直接倒序枚举,最后一次 \(k=0\) 的枚举

          +

          会出现 \(dp[u][j]=\max(dp[u][j],dp[u][j]+\text{val})\) +的情况

          +

          显然此时的 \(dp[u][j]\) +已经被更新过了

          +

          因此会导致答案有误(偏大)

          -

          除了 $k=0$ 的特殊情况,其他时候 $k$ 随便啥顺序枚举都是可以的

          +

          除了 \(k=0\) 的特殊情况,其他时候 +\(k\) 随便啥顺序枚举都是可以的

          @@ -673,14 +698,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -768,14 +793,14 @@

          算法 - - DP - - 图论 + + DP + +

          @@ -825,14 +850,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -990,7 +1015,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/30/luo-gu-p3621-apio2007-feng-ling-ti-jie/index.html b/2022/05/30/luo-gu-p3621-apio2007-feng-ling-ti-jie/index.html index 866a11a50a..0080ceef83 100644 --- a/2022/05/30/luo-gu-p3621-apio2007-feng-ling-ti-jie/index.html +++ b/2022/05/30/luo-gu-p3621-apio2007-feng-ling-ti-jie/index.html @@ -476,40 +476,54 @@

          洛谷P3621 [APIO2007] 风铃
          -

          洛谷P3621 [APIO2007] 风铃 题解

          题目链接:P3621 [APIO2007] 风铃

          +

          洛谷P3621 [APIO2007] 风铃 +题解

          +

          题目链接:P3621 +[APIO2007] 风铃

          题意

          -

          你准备给弟弟 Ike 买一件礼物,但是,Ike 挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西。

          -

          你准备给 Ike 买一个风铃。风铃是一种多层的装饰品,一般挂在天花板上。

          +

          你准备给弟弟 Ike 买一件礼物,但是,Ike +挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西。

          +

          你准备给 Ike +买一个风铃。风铃是一种多层的装饰品,一般挂在天花板上。

          每个风铃都包含一些由竖直线连起来的水平杆。每根杆的两头都有线连接,下面或者挂着另一根水平杆,或者挂着一个玩具。下面是一个风铃的例子:

          为了满足弟弟,你需要选一个满足下面两个条件的风铃:

          -
            -
          1. 所有的玩具都在同一层(也就是说,每个玩具到天花板之间的杆的个数是一样的)或至多相差一层。

            -
          2. -
          3. 对于两个相差一层的玩具,左边的玩具比右边的玩具要更靠下一点。

            -
          4. +
              +
            1. 所有的玩具都在同一层(也就是说,每个玩具到天花板之间的杆的个数是一样的)或至多相差一层。

            2. +
            3. 对于两个相差一层的玩具,左边的玩具比右边的玩具要更靠下一点。

            风铃可以按照下面的规则重新排列:任选一根杆,将杆两头的线“交换”。也就是解开一根杆左右两头的线,然后将它们绑到杆的另一头。这个操作不会改变更下面的杆上线的排列顺序。

            -

            正在训练信息学奥林匹克的你,决定设计一个算法,判断能否通过重新排列,将一个给定的风铃变为 Ike 喜欢的样子。

            -

            考虑上面的例子,上图中的风铃满足条件 $1$,却不满足条件 $2$ ——最左边的那个玩具比它右边的要高。

            +

            正在训练信息学奥林匹克的你,决定设计一个算法,判断能否通过重新排列,将一个给定的风铃变为 +Ike 喜欢的样子。

            +

            考虑上面的例子,上图中的风铃满足条件 \(1\),却不满足条件 \(2\) ——最左边的那个玩具比它右边的要高。

            但是,我们可以通过下面的步骤把这个风铃变成一个 Ike 喜欢的:

            -
              -
            1. 第一步,将杆 $1$ 的左右两边交换,这使得杆 $2$ 和杆 $3$ 的位置互换,交换的结果如下图所示:
            2. +
                +
              1. 第一步,将杆 \(1\) +的左右两边交换,这使得杆 \(2\) 和杆 +\(3\) +的位置互换,交换的结果如下图所示:
              -

              -
                -
              1. 第二步,也是最后一步,将杆 $2$ 的左右两边交换,这使得杆 $4$ 到了左边,原来在左边的玩具到了右边,交换的结果发下图所示:
              2. +

                +
                  +
                1. 第二步,也是最后一步,将杆 \(2\) +的左右两边交换,这使得杆 \(4\) +到了左边,原来在左边的玩具到了右边,交换的结果发下图所示:
                -

                +

                现在的这个风铃就满足 Ike 的条件了。

                -

                你的任务是:给定一个风铃的描述,求出最少需要多少次交换才能使这风铃满足 Ike 的条件(如果可能)。

                +

                你的任务是:给定一个风铃的描述,求出最少需要多少次交换才能使这风铃满足 +Ike 的条件(如果可能)。

          感觉不算正经树形dp,但是包含树形dp的思想

          题目其实就是在问通过最少次交换某些结点的左右子树使原树变成完全二叉树

          分类讨论

            -
          • 如果最深的深度和最浅的深度相差超过 $1$ ,无解。
          • -
          • 如果是满二叉树,答案为 $0$ 。
          • +
          • 如果最深的深度和最浅的深度相差超过 \(1\) ,无解。
          • +
          • 如果是满二叉树,答案为 \(0\) +。

          上面这两种用一遍dfs就可以搞定

          @@ -737,14 +753,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -798,14 +814,14 @@

          算法 - - DP - - 图论 + + DP + +

          @@ -959,7 +975,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/30/luo-gu-p4099-heoi2013-sao-ti-jie/index.html b/2022/05/30/luo-gu-p4099-heoi2013-sao-ti-jie/index.html index 322cdcf6ca..5ccccdd978 100644 --- a/2022/05/30/luo-gu-p4099-heoi2013-sao-ti-jie/index.html +++ b/2022/05/30/luo-gu-p4099-heoi2013-sao-ti-jie/index.html @@ -476,55 +476,85 @@

          洛谷P4099 [HEOI2013]SAO 题解
          -

          洛谷P4099 [HEOI2013]SAO 题解

          题目链接:P4099 [HEOI2013]SAO

          +

          洛谷P4099 [HEOI2013]SAO 题解

          +

          题目链接:P4099 +[HEOI2013]SAO

          题意

          -

          Welcome to SAO ( Strange and Abnormal Online)。这是一个 VR MMORPG, 含有 $n$ 个关卡。但是,挑战不同关卡的顺序是一个很大的问题。

          -

          某款游戏有 $n-1$ 个对于挑战关卡的限制,诸如第 $i$ 个关卡必须在第 $j$ 个关卡前挑战,或者完成了第 $k$ 个关卡才能挑战第 $l$ 个关卡。并且,如果不考虑限制的方向性,那么在这 $n-1$ 个限制的情况下,任何两个关卡都存在某种程度的关联性。即,我们不能把所有关卡分成两个非空且不相交的子集,使得这两个子集之间没有任何限制。

          +

          Welcome to SAO ( Strange and Abnormal Online)。这是一个 VR MMORPG, +含有 \(n\) +个关卡。但是,挑战不同关卡的顺序是一个很大的问题。

          +

          某款游戏有 \(n-1\) +个对于挑战关卡的限制,诸如第 \(i\) +个关卡必须在第 \(j\) +个关卡前挑战,或者完成了第 \(k\) +个关卡才能挑战第 \(l\) +个关卡。并且,如果不考虑限制的方向性,那么在这 \(n-1\) +个限制的情况下,任何两个关卡都存在某种程度的关联性。即,我们不能把所有关卡分成两个非空且不相交的子集,使得这两个子集之间没有任何限制。

          高质量好题👍 做了我一下午+晚上

          首先这个题面的意思:给定一棵有方向的树,求拓扑序总数

          注意到这个特殊的性质,可以从树形dp的角度去解决这个问题

          -

          设 $dp[u][i]$ 表示结点 $u$ 在其子树的拓扑序中位于第 $i$ 位的方案数

          -

          对于每个 $u$ 的儿子 $v$ ,将 $v$ 不断合并到 $u$ 上,则有两种情况

          +

          \(dp[u][i]\) 表示结点 \(u\) 在其子树的拓扑序中位于第 \(i\) 位的方案数

          +

          对于每个 \(u\) 的儿子 \(v\) ,将 \(v\) 不断合并到 \(u\) 上,则有两种情况

            -
          • 新拓扑序中 $v$ 在 $u$ 前

            -

            考虑枚举一个 $j$ 表示 $v$ 的子树中有 $j$ 个结点合并到了 $u$ 的前面

            -

            则新的状态为 $dp[u][i+j]$

            +
          • 新拓扑序中 \(v\)\(u\)

            +

            考虑枚举一个 \(j\) 表示 \(v\) 的子树中有 \(j\) 个结点合并到了 \(u\) 的前面

            +

            则新的状态为 \(dp[u][i+j]\)

              -
            • 合并后 $u$ 前面有 $i+j-1$ 个元素,

              -

              其中 $j$ 个是 $v$ 的,所以 $i+j-1$ 个格子取 $j$ 个,则为 $\dbinom{i+j-1}{j}$

              -
            • -
            • 同理, $u$ 后面有 $\text{sz}[u]+\text{sz}[v]-i-j$ 个元素,

              -

              其中 $\text{sz}[v]-j$ 个是 $v$ 的,则为 $\dbinom{\text{sz}[u]+\text{sz}[v]-i-j}{\text{sz}[v]-j}$

              -
            • +
            • 合并后 \(u\) 前面有 \(i+j-1\) 个元素,

              +

              其中 \(j\) 个是 \(v\) 的,所以 \(i+j-1\) 个格子取 \(j\) 个,则为 \(\dbinom{i+j-1}{j}\)

            • +
            • 同理, \(u\) 后面有 \(\text{sz}[u]+\text{sz}[v]-i-j\) +个元素,

              +

              其中 \(\text{sz}[v]-j\) 个是 \(v\) 的,则为 \(\dbinom{\text{sz}[u]+\text{sz}[v]-i-j}{\text{sz}[v]-j}\)

            -

            故转移方程为

            -

            也就是

            +

            故转移方程为 \[ +dp[u][i+j]=dp[u][i+j]+\dbinom{i+j-1}{j}\times +\dbinom{\text{sz}[u]+\text{sz}[v]-i-j}{\text{sz}[v]-j} \times +dp[u][i]\times dp[v][k] +\] 也就是

            for (int i=1;i<=sz[u];++i)
                 for (int k=1;j<=sz[v];++k)
                     for (int j=k;j<=sz[v];++j)
                         dp[u][i+j]+=dp[u][i]*dp[v][k]*C[i+j-1][i-1]*C[sz[u]+sz[v]-i-j][sz[u]-i]);
            -

            考虑变换一下 $j,k$ 的枚举顺序

            +

            考虑变换一下 \(j,k\) 的枚举顺序

            for (int i=1;i<=sz[u];++i)
                 for (int j=1;j<=sz[v];++j)
                     for (int k=1;k<=j;++k)
                         dp[u][i+j]+=dp[u][i]*dp[v][k]*C[i+j-1][i-1]*C[sz[u]+sz[v]-i-j][sz[u]-i];
            -

            然后就可以愉快的前缀和优化啦!

            -
          • -
          • 新拓扑序中 $v$ 在 $u$ 后

            +

            然后就可以愉快的前缀和优化啦!

          • +
          • 新拓扑序中 \(v\)\(u\)

            与上一种情况类似

            for (int i=1;i<=sz[u];++i)
                 for (int j=0;j<=sz[v];++j)
                     for (int k=i+1;k<=sz[v];++k)
                         dp[u][i+j]+=dp[u][i]*dp[v][k]*C[i+j-1][i-1]*C[sz[u]+sz[v]-i-j][sz[u]-i];
            -

            也可以前缀和优化

            -
          • +

            也可以前缀和优化

          -

          然后组合数 $O(n^2)$ 预处理

          -

          注意由于 dp[u][i+j] 的更新,我们需要记录临时记录旧的 dp[u][i]

          -

          所以总的时间复杂度为 $O(n^2)$ (类似于树上背包的复杂度证明,此处略)

          +

          然后组合数 \(O(n^2)\) 预处理

          +

          注意由于 dp[u][i+j] 的更新,我们需要记录临时记录旧的 +dp[u][i]

          +

          所以总的时间复杂度为 \(O(n^2)\) +(类似于树上背包的复杂度证明,此处略)

          别忘了多组数据哦! qwq

          代码:

          #include <iostream>
          @@ -624,9 +654,12 @@ 

          return 0; }

          参考文献

          -

          [1] https://www.luogu.com.cn/blog/i-am-zhiyangfan/solution-p4099

          -

          [2] https://www.luogu.com.cn/blog/ShadowassIIXVIIIIV/solution-p4099

          -

          [3] https://m-sea.blog.luogu.org/solution-p4099

          +

          [1] https://www.luogu.com.cn/blog/i-am-zhiyangfan/solution-p4099

          +

          [2] https://www.luogu.com.cn/blog/ShadowassIIXVIIIIV/solution-p4099

          +

          [3] https://m-sea.blog.luogu.org/solution-p4099

          @@ -786,14 +819,14 @@

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -847,14 +880,14 @@

          算法 - - DP - - 图论 + + DP + +

          @@ -1008,7 +1041,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/05/31/luo-gu-p1273-you-xian-dian-shi-wang-ti-jie/index.html b/2022/05/31/luo-gu-p1273-you-xian-dian-shi-wang-ti-jie/index.html index 3ae2664540..89adc79493 100644 --- a/2022/05/31/luo-gu-p1273-you-xian-dian-shi-wang-ti-jie/index.html +++ b/2022/05/31/luo-gu-p1273-you-xian-dian-shi-wang-ti-jie/index.html @@ -418,14 +418,14 @@

          洛谷P1273 有线电视网 题 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -480,7 +480,9 @@

          洛谷P1273 有线电视网 题
          -

          洛谷P1273 有线电视网 题解

          题目链接:P1273 有线电视网

          +

          洛谷P1273 有线电视网 题解

          +

          题目链接:P1273 +有线电视网

          题意

          某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。

          @@ -491,24 +493,31 @@

          \(dp[i][u][j]\) 表示以 \(u\) 为根的子树,仅用前 \(i\) 个儿子,满足 \(j\) 个客户能获得的最大价值

          -

          事实上我们并不是非常关心是否能获得的最大价值,我们只要不亏本的情况下尽可能多让人看罢了(好良心有没有

          +

          事实上我们并不是非常关心是否能获得的最大价值,我们只要不亏本的情况下尽可能多让人看罢了(好良心有没有

          -

          则有转移方程

          -

          可以发现这个东西可以滚动数组优化

          -

          然后就有

          -
          sz[u]+=sz[v];
          +

          则有转移方程 \[ +dp[i][u][j]=\max(dp[i][u][j],dp[i-1][u][j-k]+dp[\text{sz}[v]][v][k]-w(u,v)) +\] 可以发现这个东西可以滚动数组优化

          +

          然后就有 \[ +dp[u][j]=\max(dp[u][j],dp[u][j-k]+dp[v][k]-w(u,v)) +\]

          +
          sz[u]+=sz[v];
           for(int j=sz[u]; j>=0; j--)
               for(int k=1; k<=min(sz[v],j); k++)
                   dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-e[i].w);
          -

          AC?不,这样会TLE。参考讨论 link

          -

          因为这样的复杂度并不能保证为严格 $O(n^2)$ 的,这也就是所谓的实现不好。

          -

          改成下面的刷表法就好了

          -
          for(int j=0; j<=sz[u]; j++)
          +

          AC?不,这样会TLE。参考讨论 link

          +

          因为这样的复杂度并不能保证为严格 \(O(n^2)\) 的,这也就是所谓的实现不好。

          +

          改成下面的刷表法就好了 \[ +dp[u][j+k]=\max(dp[u][j+k],dp[u][j]+dp[v][k]-w(u,v)) +\]

          +
          for(int j=0; j<=sz[u]; j++)
               tmp[j]=dp[u][j];
           for(int j=0; j<=sz[u]; j++)
               for(int k=0; k<=sz[v]; k++)
          @@ -656,14 +665,14 @@ 

          算法 - - DP - - 图论 + + DP + + 数据结构 @@ -965,7 +974,7 @@

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/02/luo-gu-p3237-hnoi2014-mi-te-yun-shu-ti-jie/index.html b/2022/06/02/luo-gu-p3237-hnoi2014-mi-te-yun-shu-ti-jie/index.html index dee9e88776..9405db2988 100644 --- a/2022/06/02/luo-gu-p3237-hnoi2014-mi-te-yun-shu-ti-jie/index.html +++ b/2022/06/02/luo-gu-p3237-hnoi2014-mi-te-yun-shu-ti-jie/index.html @@ -472,7 +472,10 @@

          洛谷P3237 [HNOI2014]米特运
          -

          洛谷P3237 [HNOI2014]米特运输 题解

          题目链接:P3237 [HNOI2014]米特运输

          +

          洛谷P3237 [HNOI2014]米特运输 +题解

          +

          题目链接:P3237 +[HNOI2014]米特运输

          题意

          米特是D星球上一种非常神秘的物质,蕴含着巨大的能量。在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题。

          @@ -482,40 +485,43 @@

          +
        11. 不能让某个储存器到了晚上六点传输结束时还处于非空但又未满的状态,这个时候储存器仍然会启动自动收集米特的程序,而给已经储存有米特的储存器启动收集程序可能导致危险,也就是说要让储存器到了晚上六点时要么空要么满;

        12. +
        13. 关于首都——即1号城市的特殊情况, +每天早上六点到七点间1号城市中的米特储存器里的米特会自动被消耗殆尽,即运输方案不需要考虑首都的米特怎么运走;

        14. +
        15. 除了1号城市,每个节点必须在其子节点城市向它运输米特之前将这座城市的米特储存器中原本存有的米特全部运出去给父节点,不允许储存器中残存的米特与外来的米特发生混合;

        16. +
        17. 运向某一个城市的若干个来源的米特数量必须完全相同,不然,这些来源不同的米特按不同比例混合之后可能发生危险。

        现在D星人已经建立好高速通道,每个城市也有了一定储存容量的米特储存器。为了满足上面的限制条件,可能需要重建一些城市中的米特储存器。你可以,也只能,将某一座城市(包括首都)中原来存在的米特储存器摧毁,再新建一座任意容量的新的米特储存器,其容量可以是小数(在输入数据中,储存器原始容量是正整数,但重建后可以是小数),不能是负数或零,使得需要被重建的米特储存器的数目尽量少。

        题目讲了半天其实就是说

          -
        • 给定一棵树,树上每个结点有权值

          -
        • -
        • 求最少修改结点权值的次数,使得 $\text{val}[u]=\sum \text{val}[v]$

          -

          其中 $v$ 为 $u$ 的儿子。

          -
        • +
        • 给定一棵树,树上每个结点有权值

        • +
        • 求最少修改结点权值的次数,使得 \(\text{val}[u]=\sum \text{val}[v]\)

          +

          其中 \(v\)\(u\) 的儿子。

        不知道谁把它放在树形dp题单里的(恼

        手推一下就可以发现,只要确定了一个结点,其他所有的结点都可以确定了

        考虑把每个结点确定后形成的树都给算出来

        -

        然后在这一堆树里面找到相同树的个数的最大值 $\text{mx}$

        -

        答案就是 $n-\text{mx}$

        -

        这个树怎么算呢,这里有一张图,来自link

        +

        然后在这一堆树里面找到相同树的个数的最大值 \(\text{mx}\)

        +

        答案就是 \(n-\text{mx}\)

        +

        这个树怎么算呢,这里有一张图,来自link

        。。。

        -

        即设 $f[u]$ 为 $u$ 所形成的树的”特征值”,则有

        -

        其中 $d$ 为 $u$ 的祖先结点。

        +

        即设 \(f[u]\)\(u\) 所形成的树的"特征值",则有 \[ +f[u]=\prod \text{sz}[d] \times \text{val}[u] +\] 其中 \(d\)\(u\) 的祖先结点。

        似乎只要把这个值算出来就好了,不过它会爆long long

        -

        考虑用 $\log$ 转化计算( $\log (ab) = \log a + \log b$ )

        +

        考虑用 \(\log\) 转化计算( \(\log (ab) = \log a + \log b\)

        然后比较的时候只要排序一下就好了

        -

        时间复杂度 $O(n\log n)$

        +

        时间复杂度 \(O(n\log n)\)

        代码:

        #include <iostream>
         #include <string>
        @@ -732,14 +738,14 @@ 

        算法 - - DP - - 图论 + + DP + +

        @@ -789,14 +795,14 @@

        算法 - - DP - - 图论 + + DP + + 数据结构 @@ -954,7 +960,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/03/luo-gu-p3354-ioi2005-riv-he-liu-ti-jie/index.html b/2022/06/03/luo-gu-p3354-ioi2005-riv-he-liu-ti-jie/index.html index b1b26df0b6..9cd0843c46 100644 --- a/2022/06/03/luo-gu-p3354-ioi2005-riv-he-liu-ti-jie/index.html +++ b/2022/06/03/luo-gu-p3354-ioi2005-riv-he-liu-ti-jie/index.html @@ -418,14 +418,14 @@

        洛谷P3354 [IOI2005]Riv 河流 算法 - - DP - - 图论 + + DP + +

        @@ -476,37 +476,80 @@

        洛谷P3354 [IOI2005]Riv 河流
        -

        洛谷P3354 [IOI2005]Riv 河流 题解

        题目链接:P3354 [IOI2005]Riv 河流

        +

        洛谷P3354 [IOI2005]Riv 河流 +题解

        +

        题目链接:P3354 +[IOI2005]Riv 河流

        题意

        -

        几乎整个 Byteland 王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海。这条大河的入海口处有一个村庄——名叫 Bytetown。

        -

        在 Byteland 国,有 $n$ 个伐木的村庄,这些村庄都座落在河边。目前在 Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料。木料被砍下后,顺着河流而被运到 Bytetown 的伐木场。Byteland 的国王决定,为了减少运输木料的费用,再额外地建造 $k$ 个伐木场。这 $k$ 个伐木场将被建在其他村庄里。这些伐木场建造后,木料就不用都被送到 Bytetown 了,它们可以在运输过程中第一个碰到的新伐木场被处理。显然,如果伐木场座落的那个村子就不用再付运送木料的费用了。它们可以直接被本村的伐木场处理。

        +

        几乎整个 Byteland +王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海。这条大河的入海口处有一个村庄——名叫 +Bytetown。

        +

        在 Byteland 国,有 \(n\) +个伐木的村庄,这些村庄都座落在河边。目前在 +Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料。木料被砍下后,顺着河流而被运到 +Bytetown 的伐木场。Byteland +的国王决定,为了减少运输木料的费用,再额外地建造 \(k\) 个伐木场。这 \(k\) +个伐木场将被建在其他村庄里。这些伐木场建造后,木料就不用都被送到 +Bytetown +了,它们可以在运输过程中第一个碰到的新伐木场被处理。显然,如果伐木场座落的那个村子就不用再付运送木料的费用了。它们可以直接被本村的伐木场处理。

        注:所有的河流都不会分叉,形成一棵树,根结点是 Bytetown。

        -

        国王的大臣计算出了每个村子每年要产多少木料,你的任务是决定在哪些村子建设伐木场能获得最小的运费。其中运费的计算方法为:每一吨木料每千米 $1$ 分钱。

        +

        国王的大臣计算出了每个村子每年要产多少木料,你的任务是决定在哪些村子建设伐木场能获得最小的运费。其中运费的计算方法为:每一吨木料每千米 +\(1\) 分钱。

          -
        • 对于 $50\%$ 的数据,保证 $n\le 20$。
        • -
        • 对于 $100\%$ 的数据,保证 $2\le n\le 100$,$1\le k\le \min(n,50)$,$0\le v_i\le n$,$0\le w_i\le 10^4$,$1\le d_i\le 10^4$。
        • -
        • 保证每年所有的木料流到 bytetown 的运费不超过 $2\times 10^9$ 分。
        • +
        • 对于 \(50\%\) 的数据,保证 \(n\le 20\)
        • +
        • 对于 \(100\%\) 的数据,保证 \(2\le n\le 100\)\(1\le k\le \min(n,50)\)\(0\le v_i\le n\)\(0\le w_i\le 10^4\)\(1\le d_i\le 10^4\)
        • +
        • 保证每年所有的木料流到 bytetown 的运费不超过 \(2\times 10^9\) 分。

        题解区写的啥啊 那我来一篇正经的树上背包吧

        -

        注意到经典的 $dp[u][j]$ 表示 $u$ 所在子树建了 $j$ 个伐木场的最小花费,是无法计算的,因为我们并不知道子树有多少木头,以及他们会运到哪里

        -

        考虑记录一下 $u$ 结点是否放伐木场,同时把最小花费转化为最大价值

        +

        注意到经典的 \(dp[u][j]\) 表示 \(u\) 所在子树建了 \(j\) +个伐木场的最小花费,是无法计算的,因为我们并不知道子树有多少木头,以及他们会运到哪里

        +

        考虑记录一下 \(u\) +结点是否放伐木场,同时把最小花费转化为最大价值

        怎么转化?对于一个结点,在它上面建伐木场,则从该结点向叶子结点走,

        -

        每次遇到的第一个伐木场之前的,都会将木材运至 $u$ ,而不是根节点

        -

        设 $f[u][j]$ 表示 $u$ 所在子树建了 $j$ 个伐木场,且 $u$ 不建伐木场的最大价值(也就是最多节省的路费)

        -

        设 $g[u][j]$ 表示 $u$ 所在子树建了 $j$ 个伐木场,且 $u$ 必须建伐木场的最大价值

        +

        每次遇到的第一个伐木场之前的,都会将木材运至 \(u\) ,而不是根节点

        +

        \(f[u][j]\) 表示 \(u\) 所在子树建了 \(j\) 个伐木场,\(u\) +不建伐木场的最大价值(也就是最多节省的路费)

        +

        \(g[u][j]\) 表示 \(u\) 所在子树建了 \(j\) 个伐木场,\(u\) 必须建伐木场的最大价值

        那么对于每个结点,木头要上传(根节点方向)到哪个结点呢

        一种写法是增加一维记录最近的祖先伐木场在哪里,然后用栈维护

        注:我本来写的是上面的,但是复杂度有点高(虽然能过)

        这里讲一种不用增加维度的方法

        -

        我们每次假定 $u$ 一定建伐木场,然后对于每个 $u$ 都求出它的 $f$ 数组

        -

        同时设一个 $h[u][j]= \max(f[u][j],g[u][j])$ ,

        -

        当然实际上我们不需要单独设数组,直接合并到 $f$ 里就行了

        -

        然后将 $f$ (也就是 $h$ )转移到 $g$ 上,清空 $f$ 数组

        +

        我们每次假定 \(u\) +一定建伐木场,然后对于每个 \(u\) +都求出它的 \(f\) 数组

        +

        同时设一个 \(h[u][j]= +\max(f[u][j],g[u][j])\)

        +

        当然实际上我们不需要单独设数组,直接合并到 \(f\) 里就行了

        +

        然后将 \(f\) (也就是 \(h\) )转移到 \(g\) 上,清空 \(f\) 数组

        重复这样的过程,然后根节点单独处理即可

        -

        时间复杂度 $O(n^2k)$ ,目前没卡常rank3(rank1是假的不管了

        -

        好像官方题解是 $O(n^2k^2)$ 的,不过那都是多老的东西了

        +

        时间复杂度 \(O(n^2k)\) +,目前没卡常rank3(rank1是假的不管了

        +

        好像官方题解是 \(O(n^2k^2)\) +的,不过那都是多老的东西了

        代码:

        #include <iostream>
         #include <string>
        @@ -664,14 +707,14 @@ 

        算法 - - DP - - 图论 + + DP + +

        @@ -755,14 +798,14 @@

        算法 - - DP - - 图论 + + DP + +

        @@ -969,7 +1012,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/05/luo-gu-p1270-fang-wen-mei-zhu-guan-ti-jie/index.html b/2022/06/05/luo-gu-p1270-fang-wen-mei-zhu-guan-ti-jie/index.html index c87100e6c5..e4988d1d16 100644 --- a/2022/06/05/luo-gu-p1270-fang-wen-mei-zhu-guan-ti-jie/index.html +++ b/2022/06/05/luo-gu-p1270-fang-wen-mei-zhu-guan-ti-jie/index.html @@ -418,14 +418,14 @@

        洛谷P1270 “访问”美术 算法 - - DP - - 图论 + + DP + +

        @@ -476,20 +476,28 @@

        洛谷P1270 “访问”美术
        -

        洛谷P1270 “访问”美术馆 题解

        题目链接:P1270 “访问”美术馆

        +

        洛谷P1270 “访问”美术馆 题解

        +

        题目链接:P1270 +“访问”美术馆

        题意

        -

        经过数月的精心准备,Peer Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。Peer知道每个展室里藏画的数量,并且他精确测量了通过每条走廊的时间。由于经验老到,他拿下一幅画需要5秒的时间。你的任务是编一个程序,计算在警察赶来之前,他最多能偷到多少幅画。

        -

        +

        经过数月的精心准备,Peer +Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。Peer知道每个展室里藏画的数量,并且他精确测量了通过每条走廊的时间。由于经验老到,他拿下一幅画需要5秒的时间。你的任务是编一个程序,计算在警察赶来之前,他最多能偷到多少幅画。

        +

        显然的树上背包题,还是个二叉树

        读入可以用dfs,然后走廊耗时肯定要变原来两倍

        -

        设 $dp[u][j]$ 表示 $u$ 所在子树花不超过 $j$ 秒能获得的最大价值,则

        -

        如果 $u$ 是叶子结点的话,可以枚举它的耗时然后算能拿几幅画

        +

        \(dp[u][j]\) 表示 \(u\) 所在子树花不超过 \(j\) 秒能获得的最大价值,则 \[ +dp[u][j] = +\max(dp[u][j],dp[\text{ls}][j-k-\text{w}[u]]+dp[\text{rs}][k]) +\] 如果 \(u\) +是叶子结点的话,可以枚举它的耗时然后算能拿几幅画

        for(int i=0; i/5<=val[u]&&w[u]+i<=m; i++)
             dp[u][w[u]+i]=i/5;
        -

        时间复杂度 $O(nm)$

        +

        时间复杂度 \(O(nm)\)

        代码:

        #include <iostream>
         #include <string>
        @@ -603,14 +611,14 @@ 

        算法 - - DP - - 图论 + + DP + +

        @@ -747,14 +755,14 @@

        算法 - - DP - - 图论 + + DP + +

        @@ -908,7 +916,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/06/luo-gu-p1272-chong-jian-dao-lu-ti-jie/index.html b/2022/06/06/luo-gu-p1272-chong-jian-dao-lu-ti-jie/index.html index 1dbd4ab120..cd7651d4a5 100644 --- a/2022/06/06/luo-gu-p1272-chong-jian-dao-lu-ti-jie/index.html +++ b/2022/06/06/luo-gu-p1272-chong-jian-dao-lu-ti-jie/index.html @@ -418,14 +418,14 @@

        洛谷P1272 重建道路 题解< 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -480,23 +480,43 @@

        洛谷P1272 重建道路 题解<
        -

        洛谷P1272 重建道路 题解

        题目链接:P1272 重建道路

        +

        洛谷P1272 重建道路 题解

        +

        题目链接:P1272 +重建道路

        题意

        -

        一场可怕的地震后,人们用 $N$ 个牲口棚(编号 $1\sim N$)重建了农夫 John 的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的。因此,牧场运输系统可以被构建成一棵树。

        -

        John 想要知道另一次地震会造成多严重的破坏。有些道路一旦被毁坏,就会使一棵含有 $P$ 个牲口棚的子树和剩余的牲口棚分离,John 想知道这些道路的最小数目。

        -

        $1\le N\le 150$,$1\le P\le N$,保证给出的是一棵树。

        +

        一场可怕的地震后,人们用 \(N\) +个牲口棚(编号 \(1\sim N\))重建了农夫 +John +的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的。因此,牧场运输系统可以被构建成一棵树。

        +

        John +想要知道另一次地震会造成多严重的破坏。有些道路一旦被毁坏,就会使一棵含有 +\(P\) +个牲口棚的子树和剩余的牲口棚分离,John 想知道这些道路的最小数目。

        +

        \(1\le N\le 150\)\(1\le P\le N\),保证给出的是一棵树。

        -

        题目有点难懂,意思就是说切最少刀切出一个大小为 $p$ 的块,显然树上背包

        -

        设 $dp[u][j]$ 表示 $u$ 所在子树切出大小为 $j$ 的块并强制包含 $u$ 的最小花费,则

        -

        其中 $\text{tmp}[j]$ 为上一轮的 $dp[u][j]$ 。

        +

        题目有点难懂,意思就是说切最少刀切出一个大小为 \(p\) 的块,显然树上背包

        +

        \(dp[u][j]\) 表示 \(u\) 所在子树切出大小为 \(j\) 的块并强制包含 \(u\) 的最小花费,则 \[ +dp[u][j+k]=\min(dp[u][j+k],\text{tmp}[j]+dp[v][k]-1) +\] 其中 \(\text{tmp}[j]\) +为上一轮的 \(dp[u][j]\)

        后面一个柿子比较难懂

        -

        考虑一个大小为 $j$ 的块(包含 $u$ )和一个大小为 $k$ 的块(包含 $v$ )合并,显然要把和 $v$ 相连的那条边连上,但是本来是算在里面的(虽然是在最后才算的)

        +

        考虑一个大小为 \(j\) 的块(包含 +\(u\) )和一个大小为 \(k\) 的块(包含 \(v\) )合并,显然要把和 \(v\) +相连的那条边连上,但是本来是算在里面的(虽然是在最后才算的)

        解释可能不是很清楚,直接看代码好了,细节比较多的

        这道题的题解真是误导大众,填表法的树上背包是假的啊!

        所有sz在dp前加的都是假复杂度。别怪我没提醒你们哦

        -

        时间复杂度 $O(np)$

        +

        时间复杂度 \(O(np)\)

        代码:

        #include <iostream>
         #include <string>
        @@ -620,14 +640,14 @@ 

        算法 - - DP - - 图论 + + DP + + 数据结构 @@ -921,7 +941,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/06/luo-gu-p1734-zui-da-yue-shu-he-ti-jie/index.html b/2022/06/06/luo-gu-p1734-zui-da-yue-shu-he-ti-jie/index.html index 51391b40ea..64ebdf3641 100644 --- a/2022/06/06/luo-gu-p1734-zui-da-yue-shu-he-ti-jie/index.html +++ b/2022/06/06/luo-gu-p1734-zui-da-yue-shu-he-ti-jie/index.html @@ -472,14 +472,20 @@

        洛谷P1734 最大约数和 题
        -

        洛谷P1734 最大约数和 题解

        题目链接:P1734 最大约数和

        +

        洛谷P1734 最大约数和 题解

        +

        题目链接:P1734 +最大约数和

        题意:选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。

        -

        设 $dp[i][j]$ 表示只考虑前 $i$ 个数总和不超过 $j$ 的最大价值,则

        -

        $v[i]$ 可以 $O(n \log n)$ 预处理

        -

        时间复杂度 $O(n^2)$

        +

        \(dp[i][j]\) 表示只考虑前 \(i\) 个数总和不超过 \(j\) 的最大价值,则 \[ +dp[i][j]= \max(dp[i-1][j],dp[i-1][j-i]+v[i]) +\] \(v[i]\) 可以 \(O(n \log n)\) 预处理

        +

        时间复杂度 \(O(n^2)\)

        代码:

        #include <iostream>
         #include <string>
        @@ -671,14 +677,14 @@ 

        算法 - - DP - - 图论 + + DP + + 数据结构 @@ -732,14 +738,14 @@

        算法 - - DP - - 图论 + + DP + + 01分数规划 @@ -897,7 +903,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/06/luo-gu-p2868-usaco07dec-sightseeing-cows-g-ti-jie/index.html b/2022/06/06/luo-gu-p2868-usaco07dec-sightseeing-cows-g-ti-jie/index.html index 2a32be2c6f..4315076889 100644 --- a/2022/06/06/luo-gu-p2868-usaco07dec-sightseeing-cows-g-ti-jie/index.html +++ b/2022/06/06/luo-gu-p2868-usaco07dec-sightseeing-cows-g-ti-jie/index.html @@ -480,47 +480,64 @@

        洛谷P2868 [USACO07DEC]Sightsee
        -

        洛谷P2868 [USACO07DEC]Sightseeing Cows G 题解

        题目链接:P2868 [USACO07DEC]Sightseeing Cows G

        +

        洛谷P2868 +[USACO07DEC]Sightseeing Cows G 题解

        +

        题目链接:P2868 +[USACO07DEC]Sightseeing Cows G

        题意

        -

        给定有向图,有点权 $F_i$ 和边权 $T_i$ ,找一条回路(不一定是简单回路),使得

        -

        尽可能的大,求出这个值

        +

        给定有向图,有点权 \(F_i\) 和边权 +\(T_i\) +,找一条回路(不一定是简单回路),使得 \[ +\dfrac{\sum F_i}{\sum T_i} +\] 尽可能的大,求出这个值

        若回路的结点序列中含相同结点,只计算一次点权

        -

        $2 \le n \le 10^3,1\le m \le 5 \times 10^3,1 \le F_i,T_i \le 10^3$

        +

        \(2 \le n \le 10^3,1\le m \le 5 \times +10^3,1 \le F_i,T_i \le 10^3\)

        比较经典的01分数规划问题

        -

        -

        移项可得

        -

        取个反可得

        -

        然后就变成了判负环的问题。

        -

        二分一个 $x$ ,然后每次跑个spfa检验一下

        -

        时间复杂度 $O(\log_2 10^7 \times nm)$

        -

        为什么大于也可以求出答案呢?因为当 $x$ 足够精确时,也就是答案了

        +

        \[ +\sum F_i \times \dfrac{1}{\sum T_i} > x +\] 移项可得 \[ +\sum (F_i - x T_i) > 0 +\] 取个反可得 \[ +\sum (xT_i - F_i) < 0 +\] 然后就变成了判负环的问题。

        +

        二分一个 \(x\) +,然后每次跑个spfa检验一下

        +

        时间复杂度 \(O(\log_2 10^7 \times +nm)\)

        +

        为什么大于也可以求出答案呢?因为当 \(x\) 足够精确时,也就是答案了

        这里有个问题,题目说的是回路,不是环(简单回路),

        不过事实上答案中都是环(简单回路)

        -

        这里有个证明,比较繁琐,因此我来提供一种简单证明

        -
        +

        这里有个证明,比较繁琐,因此我来提供一种简单证明

        +

        证:考虑最简单的情况,

        -

        设两个环 $A,B$ 的交集为结点 $u$ (此时图可以是一个8字形)

        -

        设 $A$ 的答案是 $x$ ,$B$ 的答案是 $y$

        +

        设两个环 \(A,B\) 的交集为结点 \(u\) (此时图可以是一个8字形)

        +

        \(A\) 的答案是 \(x\)\(B\) 的答案是 \(y\)

          -
        • 若 $x=y$ ,

          -

          则若合并 $A,B$ ,多出来的几条连接 $u$ 的边会使分母增加

          -

          则答案小于 $x$

          -

          只有这几条边的 $T_i$ 都为 $0$ 时,

          -

          合并后的答案才能达到 $x$ ,但是数据范围里没有这种情况。

          -

          故合并 $A,B$ 一定不是最优的。

          -
        • -
        • 若 $x>y$ 或 $x<y$

          -

          那显然合并了就更烂了哇。肯定不是最优的。

          -
        • +
        • \(x=y\)

          +

          则若合并 \(A,B\) ,多出来的几条连接 +\(u\) 的边会使分母增加

          +

          则答案小于 \(x\)

          +

          只有这几条边的 \(T_i\) 都为 \(0\) 时,

          +

          合并后的答案才能达到 \(x\) +,但是数据范围里没有这种情况。

          +

          故合并 \(A,B\) +一定不是最优的。

        • +
        • \(x>y\)\(x<y\)

          +

          那显然合并了就更烂了哇。肯定不是最优的。

        证毕。

        -
        +

        代码:

        #include <iostream>
         #include <string>
        @@ -775,14 +792,14 @@ 

        算法 - - DP - - 图论 + + DP + + 01分数规划 @@ -993,7 +1010,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/06/luo-gu-p4322-jsoi2016-zui-jia-tuan-ti-ti-jie/index.html b/2022/06/06/luo-gu-p4322-jsoi2016-zui-jia-tuan-ti-ti-jie/index.html index afd82614ff..952aa55514 100644 --- a/2022/06/06/luo-gu-p4322-jsoi2016-zui-jia-tuan-ti-ti-jie/index.html +++ b/2022/06/06/luo-gu-p4322-jsoi2016-zui-jia-tuan-ti-ti-jie/index.html @@ -418,14 +418,14 @@

        洛谷P4322 [JSOI2016]最佳团 算法 - - DP - - 图论 + + DP + + 01分数规划 @@ -480,34 +480,74 @@

        洛谷P4322 [JSOI2016]最佳团
        -

        洛谷P4322 [JSOI2016]最佳团体 题解

        题目链接:P4322 [JSOI2016]最佳团体

        +

        洛谷P4322 [JSOI2016]最佳团体 +题解

        +

        题目链接:P4322 +[JSOI2016]最佳团体

        题意

        -

        JSOI 信息学代表队一共有 $N$ 名候选人,这些候选人从 $1$ 到 $N$ 编号。方便起见,JYY 的编号是 $0$ 号。每个候选人都由一位编号比他小的候选人$R_i$ 推荐。如果 $R_i = 0$,则说明这个候选人是 JYY 自己看上的。

        -

        为了保证团队的和谐,JYY 需要保证,如果招募了候选人 $i$,那么候选人 $R_i$ 也一定需要在团队中。当然了,JYY 自己总是在团队里的。每一个候选人都有一个战斗值 $P_i$ ,也有一个招募费用 $S_i$ 。JYY 希望招募 $K$ 个候选人(JYY 自己不算),组成一个性价比最高的团队。也就是,这 $K$ 个被 JYY 选择的候选人的总战斗值与总招募费用的比值最大。

        -

        对于100%的数据满足$1≤K≤N≤2500$,$0<S_i,P_i≤10^4$ ,
        $0$ $≤$ $R_i$ $<$ $i$

        -

        下面的讲述用 $m$ 代替 $K$ ,$n$ 代替 $N$ 。

        +

        JSOI 信息学代表队一共有 \(N\) +名候选人,这些候选人从 \(1\)\(N\) 编号。方便起见,JYY 的编号是 \(0\) +号。每个候选人都由一位编号比他小的候选人\(R_i\) 推荐。如果 \(R_i = 0\),则说明这个候选人是 JYY +自己看上的。

        +

        为了保证团队的和谐,JYY 需要保证,如果招募了候选人 \(i\),那么候选人 \(R_i\) 也一定需要在团队中。当然了,JYY +自己总是在团队里的。每一个候选人都有一个战斗值 \(P_i\) ,也有一个招募费用 \(S_i\) 。JYY 希望招募 \(K\) 个候选人(JYY +自己不算),组成一个性价比最高的团队。也就是,这 \(K\) 个被 JYY +选择的候选人的总战斗值与总招募费用的比值最大。

        +

        对于100%的数据满足\(1≤K≤N≤2500\),\(0<S_i,P_i≤10^4\) , \(0\) \(≤\) +\(R_i\) \(<\) \(i\)

        +

        下面的讲述用 \(m\) 代替 \(K\)\(n\) 代替 \(N\)

        这个分数怎么dp呢?

        其实这题就是01分数规划+树上背包的简单有趣题

        -

        设某次选择的答案为

        -

        -

        移项可得

        -

        二分一个 $x$ ,注意 $x$ 为实数且 $x \in [1,10^4]$

        -

        则dp的价值就变成了价值为 $C_i = P_i - x S_i$ 的人 $i$

        -

        设 $dp[u][j]$ 表示 $u$ 子树选恰好 $j$ 个人时能获得的最大价值

        -

        则有转移方程

        -

        其中 $\text{tmp}[j]$ 为上一轮的 $dp[u][j]$

        -

        因为选了儿子一定要选父亲,因此转移时 $j$ 要从 $1$ 开始

        -

        然后我们直接把 $0$ 当做一个没有价值的人,然后把m=m+1

        -

        每次二分时判断 $dp[u][m]\ge0$ 是否成立,成立就l=mid

        +

        设某次选择的答案为 \[ +\sum P_i \times \dfrac{1}{\sum S_i} +\]\[ +\sum P_i \times \dfrac{1}{\sum S_i} \ge x +\] 移项可得 \[ +\sum(P_i-xS_i) \ge 0 +\] 二分一个 \(x\) ,注意 \(x\)实数\(x \in [1,10^4]\)

        +

        则dp的价值就变成了价值为 \(C_i = P_i - x +S_i\) 的人 \(i\)

        +

        \(dp[u][j]\) 表示 \(u\) 子树选恰好 \(j\) 个人时能获得的最大价值

        +

        则有转移方程 \[ +dp[u][j+k]=\max_{1 \le j \le \min(m,\text{sz}[u]),0 \le k \le +\min(m,\text{sz}[v])}(dp[u][j+k],\text{tmp}[j] + dp[v][k]) +\] 其中 \(\text{tmp}[j]\) +为上一轮的 \(dp[u][j]\)

        +

        因为选了儿子一定要选父亲,因此转移时 \(j\) 要从 \(1\) 开始

        +

        然后我们直接把 \(0\) +当做一个没有价值的人,然后把m=m+1

        +

        每次二分时判断 \(dp[u][m]\ge0\) +是否成立,成立就l=mid

        然后每次dp的时候sz都要重新算,否则复杂度是假的。

        细节比较多,详见代码。

        -

        时间复杂度 $O(\log_2 10^4 \times nm)$

        +

        时间复杂度 \(O(\log_2 10^4 \times +nm)\)

        代码:

        #include <iostream>
         #include <string>
        @@ -642,14 +682,14 @@ 

        算法 - - DP - - 图论 + + DP + + 01分数规划 @@ -955,7 +995,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/06/oi-mo-ban-shu-xue/index.html b/2022/06/06/oi-mo-ban-shu-xue/index.html index d93378e034..ec221ec57f 100644 --- a/2022/06/06/oi-mo-ban-shu-xue/index.html +++ b/2022/06/06/oi-mo-ban-shu-xue/index.html @@ -468,16 +468,21 @@

        OI模板-数学

        -

        OI模板-数学

        待补全

        -

        排列的计算

        排列 $A_n^m$ 直接算可以 $O(n)$

        -

        例如求解 $A_{n-m+1}^{m} \bmod p$

        +

        OI模板-数学

        +

        待补全

        +

        排列的计算

        +

        排列 \(A_n^m\) 直接算可以 \(O(n)\)

        +

        例如求解 \(A_{n-m+1}^{m} \bmod +p\)

        int res=1;
         for(int i=n-m+1; i>=n-2*m+2; i--)
             res=res*i%p;
         cout << res << '\n'
        -
        -

        快速幂

        时间复杂度 $O(\log n)$

        -

        空间复杂度 $O(1)$

        +
        +

        快速幂

        +

        时间复杂度 \(O(\log n)\)

        +

        空间复杂度 \(O(1)\)

        #include <bits/stdc++.h>
         using namespace std;
         #define int long long
        @@ -508,14 +513,16 @@ 

        write(qpow(a,b));pc('\n'); return 0; }

        -
        -

        矩阵快速幂

        注意点:

        -
          -
        1. $O(n^3\log n)$
        2. +
          +

          矩阵快速幂

          +

          注意点:

          +
            +
          1. \(O(n^3\log n)\)
          2. 初始状态
          3. 临时变量不可开过大
          -

          新的板子(upd.20220730) P1962 斐波那契数列

          +

          新的板子(upd.20220730) P1962 斐波那契数列

          #include <iostream>
           #include <string>
           #include <vector>
          @@ -620,8 +627,10 @@ 

          } return ans; }

          -
          -

          判断素数

          判断素数 朴素版

          时间复杂度 $O(Q\sqrt{n})$

          +
          +

          判断素数

          +

          判断素数 朴素版

          +

          时间复杂度 \(O(Q\sqrt{n})\)

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -644,7 +653,8 @@ 

          << (ck(x)?'Y':'N') << endl; return 0; }

          -

          MillerRabin

          $O(Q\log a_i)$

          +

          MillerRabin

          +

          \(O(Q\log a_i)\)

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -694,9 +704,10 @@ 

          << (MillerRabin(x)?'Y':'N') << endl; return 0; }

          -
          -

          线性筛

          时间复杂度 近似$O(n)$

          -

          空间复杂度 $O(n)$

          +
          +

          线性筛

          +

          时间复杂度 近似\(O(n)\)

          +

          空间复杂度 \(O(n)\)

          #include <bits/stdc++.h>
           using namespace std;
           #define int long long
          @@ -736,9 +747,11 @@ 

          } return 0; }

          -
          -

          求欧拉函数

            -
          1. 暴力 $O(Q\sqrt{n})$ poj2407
          2. +
            +

            求欧拉函数

            +
              +
            1. 暴力 \(O(Q\sqrt{n})\) poj2407
            int Euler_phi(int n)
             {
            @@ -764,10 +777,11 @@ 

            return 0; }

            -
              -
            1. 线性筛求欧拉函数 $O(n)$

              -

              poj2478 (这个题要改成 $\varphi$ 的前缀和)

              -
            2. +
                +
              1. 线性筛求欧拉函数 \(O(n)\)

                +

                poj2478 +(这个题要改成 \(\varphi\) +的前缀和)

              int phi[N],prime[N],pcnt;
               bool ck[N];
              @@ -798,8 +812,9 @@ 

              } } }

              -
              -

              二次剩余

              #include <bits/stdc++.h>
              +
              +

              二次剩余

              +
              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
               #define INF 0x3f3f3f3f3f3f3f3f
              @@ -867,12 +882,16 @@ 

              } return 0; }

              -
              -

              数论分块

              板子题 [UVA11526] 待写题解

              -

              简单例题1 洛谷P2261 [CQOI2007]余数求和

              +
              +

              数论分块

              +

              板子题 [UVA11526] 待写题解

              +

              简单例题1 洛谷P2261 +[CQOI2007]余数求和

              简单例题2 [P2424 约数和] 待写题解

              -
              -

              康托展开

              时间复杂度 $O(n\log n)$

              +
              +

              康托展开

              +

              时间复杂度 \(O(n\log n)\)

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -938,8 +957,10 @@ 

              write(ans);pc('\n'); return 0; }

              -
              -

              逆康托展开

              理论复杂度是 $O(n\log n)$ 的,但是实际上用暴力就行了(n!那么大

              +
              +

              逆康托展开

              +

              理论复杂度是 \(O(n\log n)\) +的,但是实际上用暴力就行了(n!那么大

              这个纯属瞎写的。根本用不到,但是复杂度确实是对的

              #include <bits/stdc++.h>
               using namespace std;
              @@ -1108,8 +1129,10 @@ 

              } return 0; }

              -
              -

              高斯消元

              P3389 【模板】高斯消元法

              +
              +

              高斯消元

              +

              P3389 +【模板】高斯消元法

              #include <iostream>
               #include <string>
               #include <vector>
              @@ -1368,14 +1391,14 @@ 

              算法 - - DP - - 图论 + + DP + + 数据结构 @@ -1533,7 +1556,7 @@

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/06/poj2976-dropping-tests-ti-jie/index.html b/2022/06/06/poj2976-dropping-tests-ti-jie/index.html index 8e90e1e685..48e47c412f 100644 --- a/2022/06/06/poj2976-dropping-tests-ti-jie/index.html +++ b/2022/06/06/poj2976-dropping-tests-ti-jie/index.html @@ -472,31 +472,54 @@

              POJ2976 Dropping tests 题解
              -

              POJ2976 Dropping tests 题解

              题目链接:POJ2976 Dropping tests

              +

              POJ2976 Dropping tests 题解

              +

              题目链接:POJ2976 Dropping +tests

              题意

              Description

              -

              In a certain course, you take n tests. If you get ai out of bi questions correct on test i, your cumulative average is defined to be

              -

              .

              -

              Given your test scores and a positive integer k, determine how high you can make your cumulative average if you are allowed to drop any k of your test scores.

              -

              Suppose you take 3 tests with scores of 5/5, 0/1, and 2/6. Without dropping any tests, your cumulative average is . However, if you drop the third test, your cumulative average becomes .

              +

              In a certain course, you take n tests. If you get +ai out of bi questions correct on test i, +your cumulative average is defined to be

              +

              .

              +

              Given your test scores and a positive integer k, determine +how high you can make your cumulative average if you are allowed to drop +any k of your test scores.

              +

              Suppose you take 3 tests with scores of 5/5, 0/1, and 2/6. Without +dropping any tests, your cumulative average is . However, if you drop the +third test, your cumulative average becomes .

              Input

              -

              The input test file will contain multiple test cases, each containing exactly three lines. The first line contains two integers, 1 ≤ n ≤ 1000 and 0 ≤ k < n. The second line contains n integers indicating ai for all i. The third line contains n positive integers indicating bi for all i. It is guaranteed that 0 ≤ aibi ≤ 1, 000, 000, 000. The end-of-file is marked by a test case with n = k = 0 and should not be processed.

              +

              The input test file will contain multiple test cases, each containing +exactly three lines. The first line contains two integers, 1 ≤ +n ≤ 1000 and 0 ≤ k < n. The second line +contains n integers indicating ai for all i. +The third line contains n positive integers indicating +bi for all i. It is guaranteed that 0 ≤ ai ≤ +bi ≤ 1, 000, 000, 000. The end-of-file is marked by a test case +with n = k = 0 and should not be processed.

              Output

              -

              For each test case, write a single line with the highest cumulative average possible after dropping k of the given test scores. The average should be rounded to the nearest integer.

              +

              For each test case, write a single line with the highest cumulative +average possible after dropping k of the given test scores. The +average should be rounded to the nearest integer.

              经典的01分数规划问题

              -

              注意到题目要求的就是选 $n-k$ 个物品使得

              -

              尽可能的大

              -

              不妨假设

              -

              移项可得

              -

              于是可以二分一个 $x$

              -

              每次暴力检验 $ps[i] = a_i - xb_i$

              -

              注意要排个序然后取最大的 $n-k$ 个 $ps[i]$

              -

              时间复杂度 $O(\log_2 10^6 \times Q n \log n)$

              +

              注意到题目要求的就是选 \(n-k\) +个物品使得 \[ +\sum a_i \times \dfrac{1}{\sum b_i} +\] 尽可能的大

              +

              不妨假设 \[ +\sum a_i \times \dfrac{1}{\sum b_i} \ge x +\] 移项可得 \[ +\sum a_i - x \sum b_i \ge 0 +\] 于是可以二分一个 \(x\)

              +

              每次暴力检验 \(ps[i] = a_i - +xb_i\)

              +

              注意要排个序然后取最大的 \(n-k\) 个 +\(ps[i]\)

              +

              时间复杂度 \(O(\log_2 10^6 \times Q n \log +n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -763,14 +786,14 @@ 

              算法 - - DP - - 图论 + + DP + +

        @@ -924,7 +947,7 @@

          站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/07/luo-gu-p4037-jsoi2008-mo-shou-di-tu-ti-jie/index.html b/2022/06/07/luo-gu-p4037-jsoi2008-mo-shou-di-tu-ti-jie/index.html index 88f40d9c5b..f0b03358d7 100644 --- a/2022/06/07/luo-gu-p4037-jsoi2008-mo-shou-di-tu-ti-jie/index.html +++ b/2022/06/07/luo-gu-p4037-jsoi2008-mo-shou-di-tu-ti-jie/index.html @@ -476,48 +476,83 @@

        洛谷P4037 [JSOI2008]魔兽地
        -

        洛谷P4037 [JSOI2008]魔兽地图 题解

        题目链接:P4037 [JSOI2008]魔兽地图

        +

        洛谷P4037 [JSOI2008]魔兽地图 +题解

        +

        题目链接:P4037 +[JSOI2008]魔兽地图

        题意

        -

        DotR (Defense of the Robots) Allstars是一个风靡全球的魔兽地图,他的规则简单与同样流行的地图DotA (Defense of the Ancients) Allstars。

        +

        DotR (Defense of the Robots) +Allstars是一个风靡全球的魔兽地图,他的规则简单与同样流行的地图DotA +(Defense of the Ancients) Allstars。

        DotR里面的英雄只有一个属性——力量。他们需要购买装备来提升自己的力量值,每件装备都可以使佩戴它的英雄的力量值提高固定的点数,所以英雄的力量值等于它购买的所有装备的力量值之和。装备分为基本装备和高级装备两种。基本装备可以直接从商店里面用金币购买,而高级装备需要用基本装备或者较低级的高级装备来合成,合成不需要附加的金币。装备的合成路线可以用一棵树来表示。

        -

        比如,Sange and Yasha的合成需要Sange,Yasha和Sange and Yasha Recipe Scroll三样物品。其中Sange又要用Ogre Axe, Belt of Giant Strength和 Sange Recipe Scroll合成。每件基本装备都有数量限制,这限制了你不能无限制地合成某些性价比很高的装备。

        +

        比如,Sange and Yasha的合成需要Sange,Yasha和Sange and Yasha Recipe +Scroll三样物品。其中Sange又要用Ogre Axe, Belt of Giant Strength和 Sange +Recipe +Scroll合成。每件基本装备都有数量限制,这限制了你不能无限制地合成某些性价比很高的装备。

        现在,英雄Spectre有M个金币,他想用这些钱购买装备使自己的力量值尽量高。你能帮帮他吗?他会教你魔法Haunt(幽灵附体)作为回报的。

        容易发现这是一个十分有(dú)趣(liú)的树上背包问题

        高级装备和基本装备显然有树形的依赖关系

        -

        按照高级->高级->基本的方式进行建图,$(u,v)$ 的边权就是合成 $u$ 需要的 $v$ 个数

        +

        按照高级->高级->基本的方式进行建图,\((u,v)\) 的边权就是合成 \(u\) 需要的 \(v\) 个数

        于是可以由此建出一个森林,因此最后dp的时候要加个虚拟结点啥的

        -

        设 $P[x],L[x],M[x]$ 分别表示物品 $x$ 的价值、购买上限和花费

        +

        \(P[x],L[x],M[x]\) 分别表示物品 +\(x\) 的价值、购买上限和花费

        对于基本装备(叶子结点),显然要进行L[x]=min(L[x],m/M[x])的操作

        -

        设 $f[u][j][k]$ 表示 $u$ 装备所在子树,上传 $j$ 个 $u$ 装备用于给上层合成(也就是不计算这 $j$ 个的贡献),且所在子树花费的总金额为 $k$ 是能获得的最大价值

        -

        对于一个 $u$ ,它对父亲的贡献分为两方面

        +

        \(f[u][j][k]\) 表示 \(u\) 装备所在子树,上传 \(j\)\(u\) 装备用于给上层合成(也就是不计算这 +\(j\) +个的贡献),且所在子树花费的总金额为 \(k\) 是能获得的最大价值

        +

        对于一个 \(u\) +,它对父亲的贡献分为两方面

        • 它所在的子树的贡献(价值),包括儿子的以及自己没上传的
        • -
        • $u$ 上传的 $u$ 装备的个数,以用于父亲的合成
        • +
        • \(u\) 上传的 \(u\) 装备的个数,以用于父亲的合成

        则我们首先要知道对于每个结点究竟合成几个

        -

        考虑枚举 $l$ ,表示 $u$ 要合成 $l$ 个(上传+自己私藏的)

        +

        考虑枚举 \(l\) ,表示 \(u\) 要合成 \(l\) 个(上传+自己私藏的)

        然后我们就可以枚举上传几个以及花费多少了

        -

        如何知道对 $u$ 所在子树花费 $k$ 能获得的最大价值是多少呢

        +

        如何知道对 \(u\) 所在子树花费 \(k\) 能获得的最大价值是多少呢

        这个需要我们再单独做一个临时的dp

        -

        设 $g[i][j]$ 表示对于 $u$ 所在子树,只考虑 $u$ 的前 $i$ 个儿子所在子树,花费为 $j$ 时能获得的最大价值(这里不用记录 $u$ ,因为只是临时的dp)

        -

        则有

        -
          -
        • 这里为什么是 $l\times w(u,v)$ ?因为已经枚举了当前要合成 $l$ 个

          -
        • -
        • 这里为什么max的第一个不是 $g[i-1][j+k]$ ?因为我们每个子树都要拿材料啊

          +

          \(g[i][j]\) 表示对于 \(u\) 所在子树,只考虑 \(u\) 的前 \(i\) 个儿子所在子树,花费为 \(j\) 时能获得的最大价值(这里不用记录 \(u\) ,因为只是临时的dp)

          +

          则有 \[ +g[i][j+k]=\max(g[i][j+k],g[i-1][j]+f[v][l\times w(u,v)][k]) +\]

          +
            +
          • 这里为什么是 \(l\times w(u,v)\) +?因为已经枚举了当前要合成 \(l\) +个

          • +
          • 这里为什么max的第一个不是 \(g[i-1][j+k]\) +?因为我们每个子树都要拿材料啊

            所以这里只是代码这么写而已,

            -

            相当于 $g[i][j+k]=\max\{g[i-1][j]+f[v][l\times w(u,v)][k]\}$

            -
          • -
          • 这里为什么是 $j+k$ ?因为我喜欢刷表法,当然可以填表法

            -
          • +

            相当于 \(g[i][j+k]=\max\{g[i-1][j]+f[v][l\times +w(u,v)][k]\}\)

            +
          • 这里为什么是 \(j+k\) +?因为我喜欢刷表法,当然可以填表法

          -

          然后就可以愉快地推出 $f$ 的转移方程了

          -

          细节巨多,详见代码(这次有注释了qwq

          -

          时间复杂度的宽松上界为 $O(100\times nm^2)$

          +

          然后就可以愉快地推出 \(f\) +的转移方程了 \[ +f[u][j][k]=\max(f[u][j][k],g[k]+(l-j) \times P[u]) +\] 细节巨多,详见代码(这次有注释了qwq

          +

          时间复杂度的宽松上界为 \(O(100\times +nm^2)\)

          实际剪枝+时限3.00s,可以444ms跑过所有点(嘿嘿所以我是目前最优解

          代码:

          #include <iostream>
          @@ -786,14 +821,14 @@ 

          算法 - - DP - - 图论 + + DP + +

        @@ -996,7 +1031,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/08/luo-gu-p3047-usaco12feb-nearby-cows-g-ti-jie/index.html b/2022/06/08/luo-gu-p3047-usaco12feb-nearby-cows-g-ti-jie/index.html index 0d0355c75f..e4c066f8c7 100644 --- a/2022/06/08/luo-gu-p3047-usaco12feb-nearby-cows-g-ti-jie/index.html +++ b/2022/06/08/luo-gu-p3047-usaco12feb-nearby-cows-g-ti-jie/index.html @@ -418,14 +418,14 @@

        洛谷P3047 [USACO12FEB]Nearby C 算法 - - DP - - 图论 + + DP + +

        @@ -476,25 +476,45 @@

        洛谷P3047 [USACO12FEB]Nearby C
        -

        洛谷P3047 [USACO12FEB]Nearby Cows G 题解

        题目链接:P3047 [USACO12FEB]Nearby Cows G

        +

        洛谷P3047 +[USACO12FEB]Nearby Cows G 题解

        +

        题目链接:P3047 +[USACO12FEB]Nearby Cows G

        题意

        -

        给你一棵 $n$ 个点的树,点带权,对于每个节点求出距离它不超过 $k$ 的所有节点权值和 $m_i$。

        -

        「数据范围」
        对于 $100\%$ 的数据:$1 \le n \le 10^5$,$1 \le k \le 20$,$0 \le c_i \le 1000$

        +

        给你一棵 \(n\) +个点的树,点带权,对于每个节点求出距离它不超过 \(k\) 的所有节点权值和 \(m_i\)

        +

        「数据范围」 对于 \(100\%\) +的数据:\(1 \le n \le 10^5\)\(1 \le k \le 20\)\(0 \le c_i \le 1000\)

        换根dp,也叫二次扫描法

        这题随便找一个根当做树根,然后扫两遍才能出答案

        -

        首先设 $f[u][j]$ 表示 $u$ 所在子树,与 $u$ 相距恰好 $j$ 的结点个数,则

        -

        这是第一遍dfs,可以发现我们没有从非 $u$ 所在子树获取答案

        -

        考虑第二遍dfs,设 $g[u][j]$ 表示在整棵树中与 $u$ 相距恰好 $j$ 的结点个数

        -

        这个答案一定是从父节点的 $g$ 转移而来

        -

        但是这里会有一个问题,$g[fa][j-1]$ 包含了从 $f[u][j-2]$ 转移来的答案

        +

        首先设 \(f[u][j]\) 表示 \(u\) 所在子树,与 \(u\) 相距恰好 \(j\) 的结点个数,则 \[ +f[u][j]=\sum_{v \in \text{son}[u]} f[v][j-1] +\] 这是第一遍dfs,可以发现我们没有从非 \(u\) 所在子树获取答案

        +

        考虑第二遍dfs,设 \(g[u][j]\) +表示在整棵树中与 \(u\) +相距恰好 \(j\) 的结点个数

        +

        这个答案一定是从父节点的 \(g\) +转移而来

        +

        但是这里会有一个问题,\(g[fa][j-1]\) +包含了从 \(f[u][j-2]\) 转移来的答案

        直接加的话会导致重复,考虑容斥

        -

        不懂的话建议画个图,别像我一样一开始干瞪着 qwq

        -

        然后 $g$ 可以直接在 $f$ 上搞,节约空间

        -

        时间复杂度 $O(n)$

        +

        不懂的话建议画个图,别像我一样一开始干瞪着 qwq\[ +g[u][j]=f[u][j]+g[fa][j-1]-f[u][j-2] +\] 然后 \(g\) 可以直接在 \(f\) 上搞,节约空间

        +

        时间复杂度 \(O(n)\)

        代码:

        #include <iostream>
         #include <string>
        @@ -636,14 +656,14 @@ 

        算法 - - DP - - 图论 + + DP + +

        @@ -945,7 +965,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/08/luo-gu-p3647-apio2014-lian-zhu-xian-ti-jie/index.html b/2022/06/08/luo-gu-p3647-apio2014-lian-zhu-xian-ti-jie/index.html index 8aa2da6cdb..6b1fd28969 100644 --- a/2022/06/08/luo-gu-p3647-apio2014-lian-zhu-xian-ti-jie/index.html +++ b/2022/06/08/luo-gu-p3647-apio2014-lian-zhu-xian-ti-jie/index.html @@ -476,45 +476,88 @@

        洛谷P3647 [APIO2014] 连珠线
        -

        洛谷P3647 [APIO2014] 连珠线 题解

        题目链接:P3647 [APIO2014] 连珠线

        +

        洛谷P3647 [APIO2014] 连珠线 +题解

        +

        题目链接:P3647 +[APIO2014] 连珠线

        题意

        -

        在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 $1$ 到 $n$。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:

        -

        Append(w, v):一个新的珠子 $w$ 和一个已经添加的珠子 $v$ 用红线连接起来。

        -

        Insert(w, u, v):一个新的珠子 $w$ 插入到用红线连起来的两个珠子 $u, v$ 之间。具体过程是删去 $u, v$ 之间红线,分别用蓝线连接 $u, w$ 和 $w, v$。

        +

        在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 +\(1\)\(n\)。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:

        +

        Append(w, v):一个新的珠子 \(w\) 和一个已经添加的珠子 \(v\) 用红线连接起来。

        +

        Insert(w, u, v):一个新的珠子 \(w\) 插入到用红线连起来的两个珠子 \(u, v\) 之间。具体过程是删去 \(u, v\) 之间红线,分别用蓝线连接 \(u, w\)\(w, +v\)

        每条线都有一个长度。游戏结束后,你的最终得分为蓝线长度之和。

        给你连珠线游戏结束后的游戏局面,只告诉了你珠子和链的连接方式以及每条线的长度,没有告诉你每条线分别是什么颜色。

        你需要写一个程序来找出最大可能得分。即,在所有以给出的最终局面结束的连珠线游戏中找出那个得分最大的,然后输出最大可能得分。

        -

        $1 \leq n \leq 200000$。

        +

        \(1 \leq n \leq 200000\)

        开学前两天准备狂刷题,结果在这题上卡了一下午(😭

        -

        本文主要参考了link,所以主要是自己的总结型题解(q779太菜了

        +

        本文主要参考了link,所以主要是自己的总结型题解(q779太菜了

        容易发现这是一道树形dp

        -

        首先可以想到一个瞎七搭八的dp,也就是设结点 $u$ 和父亲的连边涂什么颜色

        -

        但是这个dp不好判断合法性,这篇博客讲的比较详细,不过这个也比较容易发现

        +

        首先可以想到一个瞎七搭八的dp,也就是设结点 \(u\) 和父亲的连边涂什么颜色

        +

        但是这个dp不好判断合法性,这篇博客讲的比较详细,不过这个也比较容易发现

        于是考虑避免那种“/\形”的蓝色边,只考虑“\类型”的,也就是fa[u]-u-son[u]的

        可以发现此时如果固定了一个结点做根,就可以避免“/\形”的蓝色边出现

        同时也方便dp了(并且有一种经典换根dp的感觉了,虽然我当时看不出

        -

        设 $f[u][0/1]$ 表示 $u$ 所在子树中,$u$ 作为或不作为蓝线的中点是能得到的最大价值

        -

        前者比较简单,不是蓝线中点,则可以连蓝线中点,也可以连红线。

        -

        后者较为麻烦,首先它和前者唯一的区别在于,它必须有一个儿子作为这条蓝线的终点。显然这个儿子是某个 $f[v][0]$ 。因此我们可以把 $f[u][0]$ 的直接初始化到 $f[u][1]$ 上,然后去掉这个 $v$ 的贡献,并加上它的新贡献。

        -

        有点长,不过后面那个max就是 $f[u][0]$ 里面的那个max

        -

        然后我们就有了一个固定根情况下的dp,时间复杂度 $O(n^2)$ ,还过不了

        +

        \(f[u][0/1]\) 表示 \(u\) 所在子树中,\(u\) +作为或不作为蓝线的中点是能得到的最大价值

        +

        前者比较简单,不是蓝线中点,则可以连蓝线中点,也可以连红线。 \[ +f[u][0]=\sum_{v \in \text{son}[u]} \max(f[v][0],f[v][1]+w(u,v)) \tag{1} +\] +后者较为麻烦,首先它和前者唯一的区别在于,它必须有一个儿子作为这条蓝线的终点。显然这个儿子是某个 +\(f[v][0]\) 。因此我们可以把 \(f[u][0]\) 的直接初始化到 \(f[u][1]\) 上,然后去掉这个 \(v\) 的贡献,并加上它的新贡献。 \[ +f[u][1]=f[u][0]+\max_{v \in +\text{son}[u]}(f[v][0]+w(u,v)-\max(f[v][0],f[v][1]+w(u,v))) \tag{2} +\] 有点长,不过后面那个max就是 \(f[u][0]\) 里面的那个max

        +

        然后我们就有了一个固定根情况下的dp,时间复杂度 \(O(n^2)\) ,还过不了

        然后这里就要换根dp发挥作用了。

        -

        我们考虑一个点 $u$ 的儿子变成了 $u$ 的父亲会有什么影响

        -

        首先这个儿子对 $u$ 的贡献没了,并且有可能转移方程中的最大值也没了。

        +

        我们考虑一个点 \(u\) 的儿子变成了 +\(u\) 的父亲会有什么影响

        +

        首先这个儿子对 \(u\) +的贡献没了,并且有可能转移方程中的最大值也没了。

        (于是我们记录一个次大值,貌似是经典套路)

        -

        然后 $u$ 会变成新的儿子给原来的儿子(现在的父亲)贡献

        -

        方便起见,我们在第一遍dp的时候用vector记录一个 $g[u][0/1][j]$

        -

        表示对于 $u$ ,不考虑第 $j$ 个儿子的贡献时能获得的最大价值

        -

        $g[u][0][j]$ 直接减去 $j$ 的贡献就好了,

        -

        对于 $g[u][1][j]$ ,如果 $j$ 恰好是最大值,那就加上次大值,否则不变

        -

        这里的最大值、次大值就是指 $(2)$ 里的

        -

        换根的过程中,枚举 $u$ 的儿子作为整棵树的根。值得注意的是,换根以后 $u$ 的父亲就会变成 $u$ 的儿子。因此我们要先重新算出 $u$ 的父亲对 $u$ 的贡献,然后再换根

        -

        时间复杂度 $O(n)$

        +

        然后 \(u\) +会变成新的儿子给原来的儿子(现在的父亲)贡献

        +

        方便起见,我们在第一遍dp的时候用vector记录一个 \(g[u][0/1][j]\)

        +

        表示对于 \(u\) ,不考虑第 \(j\) 个儿子的贡献时能获得的最大价值

        +

        \(g[u][0][j]\) 直接减去 \(j\) 的贡献就好了,

        +

        对于 \(g[u][1][j]\) ,如果 \(j\) +恰好是最大值,那就加上次大值,否则不变

        +

        这里的最大值、次大值就是指 \((2)\) +里的 \[ +\max\limits_{v \in +\text{son}[u]}(f[v][0]+w(u,v)-\max(f[v][0],f[v][1]+w(u,v))) +\] 换根的过程中,枚举 \(u\) +的儿子作为整棵树的根。值得注意的是,换根以后 \(u\) 的父亲就会变成 \(u\) 的儿子。因此我们要先重新算出 \(u\) 的父亲对 \(u\) 的贡献,然后再换根

        +

        时间复杂度 \(O(n)\)

        代码:

        #include <iostream>
         #include <string>
        @@ -817,14 +860,14 @@ 

        算法 - - DP - - 图论 + + DP + +

        @@ -978,7 +1021,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/16/luo-gu-p1083-noip2012-ti-gao-zu-jie-jiao-shi-ti-jie/index.html b/2022/06/16/luo-gu-p1083-noip2012-ti-gao-zu-jie-jiao-shi-ti-jie/index.html index 909cb880df..1ffde71fcd 100644 --- a/2022/06/16/luo-gu-p1083-noip2012-ti-gao-zu-jie-jiao-shi-ti-jie/index.html +++ b/2022/06/16/luo-gu-p1083-noip2012-ti-gao-zu-jie-jiao-shi-ti-jie/index.html @@ -468,23 +468,43 @@

        洛谷P1083 [NOIP2012 提高组]
        -

        洛谷P1083 [NOIP2012 提高组] 借教室 题解

        题目链接:P1083 [NOIP2012 提高组] 借教室

        +

        洛谷P1083 [NOIP2012 +提高组] 借教室 题解

        +

        题目链接:P1083 +[NOIP2012 提高组] 借教室

        题意

        在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。

        面对海量租借教室的信息,我们自然希望编程解决这个问题。

        -

        我们需要处理接下来 $n$ 天的借教室信息,其中第 $i$ 天学校有 $r_i$ 个教室可供租借。共有 $m$ 份订单,每份订单用三个正整数描述,分别为 $d_j,s_j,t_j$,表示某租借者需要从第 $s_j$ 天到第 $t_j$ 天租借教室(包括第 $s_j$ 天和第 $t_j$ 天),每天需要租借 $d_j$ 个教室。

        -

        我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供 $d_j$ 个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

        -

        借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第 $s_j$ 天到第 $t_j$ 天中有至少一天剩余的教室数量不足 $d_j$ 个。

        +

        我们需要处理接下来 \(n\) +天的借教室信息,其中第 \(i\) 天学校有 +\(r_i\) 个教室可供租借。共有 \(m\) +份订单,每份订单用三个正整数描述,分别为 \(d_j,s_j,t_j\),表示某租借者需要从第 \(s_j\) 天到第 \(t_j\) 天租借教室(包括第 \(s_j\) 天和第 \(t_j\) 天),每天需要租借 \(d_j\) 个教室。

        +

        我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供 +\(d_j\) +个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

        +

        借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第 +\(s_j\) 天到第 \(t_j\) 天中有至少一天剩余的教室数量不足 +\(d_j\) 个。

        现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

        -

        对于 100% 的数据,有$1 ≤ n,m ≤ 10^6,0 ≤ r_i,d_j≤ 10^9,1 ≤ s_j≤ t_j≤ n$

        +

        对于 100% 的数据,有\(1 ≤ n,m ≤ 10^6,0 ≤ +r_i,d_j≤ 10^9,1 ≤ s_j≤ t_j≤ n\)

        考虑差分处理每个需求

        -

        然后 $O(n)$ 扫一遍,如果不合法,就从最后一个人开始消去影响

        +

        然后 \(O(n)\) +扫一遍,如果不合法,就从最后一个人开始消去影响

        直到某个人消去以后没问题了,这个人就是罪魁祸首,输出他就好了

        为什么倒着消去影响呢?因为罪魁祸首是所有导致不合法的人里面最靠前的

        我们只要求这个不合法的人,因此他后面的人不会影响他的坏(感性理解即可

        -

        时间复杂度 $O(n+m)$

        +

        时间复杂度 \(O(n+m)\)

        代码:

        #include <iostream>
         #include <string>
        @@ -911,7 +931,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/16/luo-gu-p1314-noip2011-ti-gao-zu-cong-ming-de-zhi-jian-yuan-ti-jie/index.html b/2022/06/16/luo-gu-p1314-noip2011-ti-gao-zu-cong-ming-de-zhi-jian-yuan-ti-jie/index.html index 7038616d9a..7e450e68ab 100644 --- a/2022/06/16/luo-gu-p1314-noip2011-ti-gao-zu-cong-ming-de-zhi-jian-yuan-ti-jie/index.html +++ b/2022/06/16/luo-gu-p1314-noip2011-ti-gao-zu-cong-ming-de-zhi-jian-yuan-ti-jie/index.html @@ -472,34 +472,66 @@

        洛谷P1314 [NOIP2011 提高组]
        -

        洛谷P1314 [NOIP2011 提高组] 聪明的质监员 题解

        题目链接:P1314 [NOIP2011 提高组] 聪明的质监员

        +

        洛谷P1314 [NOIP2011 +提高组] 聪明的质监员 题解

        +

        题目链接:P1314 +[NOIP2011 提高组] 聪明的质监员

        -

        题意

        -

        小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 $n$ 个矿石,从 $1$ 到 $n$ 逐一编号,每个矿石都有自己的重量 $w_i$ 以及价值 $v_i$ 。检验矿产的流程是:

        -

        1 、给定 $m$ 个区间 $[l_i,r_i]$;

        -

        2 、选出一个参数 $W$;

        -

        3 、对于一个区间 $[l_i,r_i]$,计算矿石在这个区间上的检验值 $y_i$:

        -

        其中 $j$ 为矿石编号。

        -

        这批矿产的检验结果 $y$ 为各个区间的检验值之和。即:$\sum\limits_{i=1}^m y_i$

        -

        若这批矿产的检验结果与所给标准值 $s$ 相差太多,就需要再去检验另一批矿产。小T 不想费时间去检验另一批矿产,所以他想通过调整参数 $W$ 的值,让检验结果尽可能的靠近标准值 $s$,即使得 $|s-y|$ 最小。请你帮忙求出这个最小值。

        -

        对于 $100\%$ 的数据,有 $1 ≤n ,m≤200,000$,$0 < w_i,v_i≤10^6$,$0 < s≤10^{12}$,$1 ≤l_i ≤r_i ≤n$ 。

        +

        题意

        +

        小T +是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 \(n\) 个矿石,从 \(1\)\(n\) 逐一编号,每个矿石都有自己的重量 \(w_i\) 以及价值 \(v_i\) 。检验矿产的流程是:

        +

        1 、给定 \(m\) 个区间 \([l_i,r_i]\)

        +

        2 、选出一个参数 \(W\)

        +

        3 、对于一个区间 \([l_i,r_i]\),计算矿石在这个区间上的检验值 +\(y_i\)\[ +y_i=\sum\limits_{j=l_i}^{r_i}[w_j \ge W] \times +\sum\limits_{j=l_i}^{r_i}[w_j \ge W]v_j +\] 其中 \(j\) 为矿石编号。

        +

        这批矿产的检验结果 \(y\) +为各个区间的检验值之和。即:\(\sum\limits_{i=1}^m y_i\)

        +

        若这批矿产的检验结果与所给标准值 \(s\) +相差太多,就需要再去检验另一批矿产。小T +不想费时间去检验另一批矿产,所以他想通过调整参数 \(W\) 的值,让检验结果尽可能的靠近标准值 +\(s\),即使得 \(|s-y|\) 最小。请你帮忙求出这个最小值。

        +

        对于 \(100\%\) 的数据,有 \(1 ≤n ,m≤200,000\)\(0 < w_i,v_i≤10^6\)\(0 < s≤10^{12}\)\(1 ≤l_i ≤r_i ≤n\)

        -

        注意到其实我们就是要算这个式子

        -

        $\sum_{j=l_i}^{r_i}[w_j \ge W]$ 是可以前缀和优化的

        -

        但是显然我们要知道 $W$ 才能计算这个柿子

        -

        考虑二分一个 $W \in [0,w_{\max}]$

        +

        注意到其实我们就是要算这个式子 \[ +\left|s-\sum_{i=1}^{m}\sum_{j=l_i}^{r_i}[w_j \ge +W]\sum_{j=l_i}^{r_i}[w_j \ge W]v_j\right|_{\min} +\] \(\sum_{j=l_i}^{r_i}[w_j \ge +W]\) 是可以前缀和优化的

        +

        但是显然我们要知道 \(W\) +才能计算这个柿子

        +

        考虑二分一个 \(W \in +[0,w_{\max}]\)

        对于绝对值的二分,我们直接拆掉绝对值

          -
        • 当 $s-y<0$ 时, $y$ 减小, $W$ 增大。
        • -
        • 当 $s-y>0$ 时, $y$ 增大, $W$ 减小。

          -
        • -
        • 当 $s-y=0$ 时 ,直接退出即可。

          -
        • +
        • \(s-y<0\) 时, \(y\) 减小, \(W\) 增大。

        • +
        • \(s-y>0\) 时, \(y\) 增大, \(W\) 减小。

        • +
        • \(s-y=0\) 时 +,直接退出即可。

        答案只要在二分的过程中卑微的记录一下就好了

        -

        时间复杂度 $O(n \log W)$

        +

        时间复杂度 \(O(n \log W)\)

        代码:

        #include <iostream>
         #include <string>
        @@ -918,7 +950,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/16/luo-gu-p1453-cheng-shi-huan-lu-ti-jie/index.html b/2022/06/16/luo-gu-p1453-cheng-shi-huan-lu-ti-jie/index.html index 04e0859f3f..6516f1147d 100644 --- a/2022/06/16/luo-gu-p1453-cheng-shi-huan-lu-ti-jie/index.html +++ b/2022/06/16/luo-gu-p1453-cheng-shi-huan-lu-ti-jie/index.html @@ -472,14 +472,32 @@

        洛谷P1453 城市环路 题解<
        -

        洛谷P1453 城市环路 题解

        题目链接:P1453 城市环路

        +

        洛谷P1453 城市环路 题解

        +

        题目链接:P1453 +城市环路

        题意

        -

        整个城市可以看做一个 $n$ 个点,$n$ 条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有 $2$ 条简单路径互通。图中的其它部分皆隶属城市郊区。

        -

        现在,有一位名叫 Jim 的同学想在 B 市开店,但是任意一条边的 $2$ 个点不能同时开店,每个点都有一定的人流量,第 $i$ 个点的人流量是 $p_i$,在该点开店的利润就等于 $p_i×k$,其中 $k$ 是一个常数。

        +

        整个城市可以看做一个 \(n\) +个点,\(n\) +条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有 +\(2\) +条简单路径互通。图中的其它部分皆隶属城市郊区。

        +

        现在,有一位名叫 Jim 的同学想在 B 市开店,但是任意一条边的 \(2\) +个点不能同时开店,每个点都有一定的人流量,第 \(i\) 个点的人流量是 \(p_i\),在该点开店的利润就等于 \(p_i×k\),其中 \(k\) 是一个常数。

        Jim 想尽量多的赚取利润,请问他应该在哪些地方开店?

          -
        • 对于 $100\%$ 的数据,保证 $1 \leq n \leq 10^5$,$1 \leq p_i \leq 10^4$,$0 \leq u, v < n$,$0 \leq k \leq 10^4$,$k$ 的小数点后最多有 $6$ 位数字。
        • +
        • 对于 \(100\%\) 的数据,保证 \(1 \leq n \leq 10^5\)\(1 \leq p_i \leq 10^4\)\(0 \leq u, v < n\)\(0 \leq k \leq 10^4\)\(k\) 的小数点后最多有 \(6\) 位数字。

        显然这是一个基环树dp

        @@ -704,14 +722,14 @@

        算法 - - DP - - 图论 + + DP + + 数据结构 @@ -918,7 +936,7 @@

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/16/luo-gu-p2678-noip2015-ti-gao-zu-tiao-shi-tou-ti-jie/index.html b/2022/06/16/luo-gu-p2678-noip2015-ti-gao-zu-tiao-shi-tou-ti-jie/index.html index 84189a764c..3e9308fc16 100644 --- a/2022/06/16/luo-gu-p2678-noip2015-ti-gao-zu-tiao-shi-tou-ti-jie/index.html +++ b/2022/06/16/luo-gu-p2678-noip2015-ti-gao-zu-tiao-shi-tou-ti-jie/index.html @@ -468,47 +468,65 @@

        洛谷P2678 [NOIP2015 提高组]
        -

        洛谷P2678 [NOIP2015 提高组] 跳石头 题解

        题目链接:P2678 [NOIP2015 提高组] 跳石头

        +

        洛谷P2678 [NOIP2015 +提高组] 跳石头 题解

        +

        题目链接:P2678 +[NOIP2015 提高组] 跳石头

        题意

        -

        这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 $N$ 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

        -

        为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 $M$ 块岩石(不能移走起点和终点的岩石)。

        -

        对于 $100\%$的数据,$0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000$。

        +

        这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 +\(N\) +块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

        +

        为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 +\(M\) +块岩石(不能移走起点和终点的岩石)。

        +

        对于 \(100\%\)的数据,\(0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ +1,000,000,000\)

        求最小值最大,二分经典题。

        为什么是二分呢?因为我们并不知道最小值是多少

        我们可以枚举一个最小值,然后使它尽可能的大

        对于有单调性的题目可以使用二分,显然这题有单调性

        然后就没啥了,二分一下就好了

        -

        注意终点不是 $N$ 。时间复杂度 $O(N \log L)$

        +

        注意终点不是 \(N\) 。时间复杂度 +\(O(N \log L)\)

        相信大家做这题的时候应该二分不太熟,

        这里给一个二分的板子(我是在《算法竞赛进阶指南》上学到的)

        -
        +
        • 求枚举值的最小值,则符合条件就使r=mid

          -

          表示答案在 $[l,\text{mid}]$ 间,注意这里 $\text{mid}$ 是可行的。

          -

          例如:在单调递增序列 $a$ 中查找 $\ge x$ 的数中最小的一个 ( $x$ 或 $x$ 的后继)

          +

          表示答案在 \([l,\text{mid}]\) +间,注意这里 \(\text{mid}\) +是可行的。

          +

          例如:在单调递增序列 \(a\) 中查找 +\(\ge x\) 的数中最小的一个 ( \(x\)\(x\) 的后继)

          while(l<r)
           {
               int mid=(l+r)/2; // (l+r)>>1;
               if(a[mid] >= x) r=mid;
               else l=mid+1;
           }
          -return a[l];
          -
        • +return a[l];

      7. 求枚举值的最大值,则符合条件就使l=mid

        -

        表示答案在 $[\text{mid},r]$ 间,注意这里 $\text{mid}$ 是可行的。

        -

        例如:在单调递增序列 $a$ 中查找 $\le x$ 的数中最大的一个( $x$ 或 $x$ 的前驱)

        +

        表示答案在 \([\text{mid},r]\) +间,注意这里 \(\text{mid}\) +是可行的。

        +

        例如:在单调递增序列 \(a\) 中查找 +\(\le x\) 的数中最大的一个( \(x\)\(x\) 的前驱)

        while(l<r)
         {
             int mid=(l+r+1)/2; // (l+r+1)>>1;
             if(a[mid] <= x) l=mid;
             else r=mid-1;
         }
        -return a[l];
        -
      8. +return a[l];

        -
        +

        题目代码:

        #include <iostream>
         #include <string>
        @@ -916,7 +934,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/16/luo-gu-p3131-usaco16jan-subsequences-summing-to-sevens-s-ti-jie/index.html b/2022/06/16/luo-gu-p3131-usaco16jan-subsequences-summing-to-sevens-s-ti-jie/index.html index 1d2c3bd719..7a312ab545 100644 --- a/2022/06/16/luo-gu-p3131-usaco16jan-subsequences-summing-to-sevens-s-ti-jie/index.html +++ b/2022/06/16/luo-gu-p3131-usaco16jan-subsequences-summing-to-sevens-s-ti-jie/index.html @@ -472,19 +472,27 @@

        洛谷P3131 [USACO16JAN]Subseque
        -

        洛谷P3131 [USACO16JAN]Subsequences Summing to Sevens S 题解

        题目链接:P3131 [USACO16JAN]Subsequences Summing to Sevens S

        +

        洛谷P3131 +[USACO16JAN]Subsequences Summing to Sevens S 题解

        +

        题目链接:P3131 +[USACO16JAN]Subsequences Summing to Sevens S

        -

        题意:给你n个数,分别是a[1],a[2],…,a[n]。求一个最长的区间[x,y],使得区间中的数(a[x],a[x+1],a[x+2],…,a[y-1],a[y])的和能被7整除。输出区间长度。若没有符合要求的区间,输出0。

        +

        题意:给你n个数,分别是a[1],a[2],...,a[n]。求一个最长的区间[x,y],使得区间中的数(a[x],a[x+1],a[x+2],...,a[y-1],a[y])的和能被7整除。输出区间长度。若没有符合要求的区间,输出0。

        属于简单萌萌题叭。思路还是挺有趣的。

        -

        区间和用个前缀和 $S_i$ ,直接枚举 $l,r$ 肯定不行

        -

        这里用个小技巧,如果存在 $j < i$ 满足

        -

        -

        所以我们把所有前缀和都去模一下 $7$ 然后找一下最长的区间就好啦

        -

        时间复杂度 $O(n)$

        -

        注意这里有个坑,$j$ 可以为 $0$

        +

        区间和用个前缀和 \(S_i\) ,直接枚举 +\(l,r\) 肯定不行

        +

        这里用个小技巧,如果存在 \(j < +i\) 满足 \[ +S_i \equiv S_j \pmod 7 +\]\[ +S_i-S_j \equiv 0 \pmod 7 +\] 所以我们把所有前缀和都去模一下 \(7\) 然后找一下最长的区间就好啦

        +

        时间复杂度 \(O(n)\)

        +

        注意这里有个坑,\(j\) 可以为 \(0\)

        代码:

        #include <iostream>
         #include <string>
        @@ -886,7 +894,7 @@ 

          站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/16/luo-gu-p4552-poetize6-incdec-sequence-ti-jie/index.html b/2022/06/16/luo-gu-p4552-poetize6-incdec-sequence-ti-jie/index.html index 96a1f2944f..afb33138ea 100644 --- a/2022/06/16/luo-gu-p4552-poetize6-incdec-sequence-ti-jie/index.html +++ b/2022/06/16/luo-gu-p4552-poetize6-incdec-sequence-ti-jie/index.html @@ -468,37 +468,72 @@

        洛谷P4552 [Poetize6] IncDec Se
        -

        洛谷P4552 [Poetize6] IncDec Sequence 题解

        题目链接:P4552 [Poetize6] IncDec Sequence

        +

        洛谷P4552 [Poetize6] +IncDec Sequence 题解

        +

        题目链接:P4552 +[Poetize6] IncDec Sequence

        题意

        -

        给定一个长度为 $n$ 的数列 ${a_1,a_2,\cdots,a_n}$,每次可以选择一个区间$[l,r]$,使这个区间内的数都加 $1$ 或者都减 $1$。

        +

        给定一个长度为 \(n\) 的数列 \({a_1,a_2,\cdots,a_n}\),每次可以选择一个区间\([l,r]\),使这个区间内的数都加 \(1\) 或者都减 \(1\)

        请问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。

        -

        对于 $100\%$ 的数据,$n\le 100000, 0 \le a_i \le 2^{31}$。

        +

        对于 \(100\%\) 的数据,\(n\le 100000, 0 \le a_i \le 2^{31}\)

        这道题思路比较有趣,代码很简单但是相对来说不好想

        -

        本文比较长,建议耐心阅读(当然也可以直接 ⌘+w 或者 ctrl+w

        -

        差分,常用于 $O(1)$ 区间加减,全局询问

        +

        本文比较长,建议耐心阅读(当然也可以直接 ⌘+w 或者 +ctrl+w

        +

        差分,常用于 \(O(1)\) +区间加减,全局询问

        对于这种看上去也不是用数据结构做的题目,

        -

        考虑差分。差分数组 $b_i$ 的定义为 $b_i = a_i-a_{i-1}$

        -

        容易发现 $a_i = \sum_{1 \le j \le i}b_i$ ,值得注意的是, $a_1 = b_1$

        -

        对于区间 $[l,r]$ 加 $1$ 的修改,只要把 $b_l$ 加上 $1$ ,$b_{r+1}$ 减去 $1$ 就可以了

        -

        然后问题就转化为了将 $b_2,b_3,\dots,b_n$ 都变成 $0$ 的最小花费

        -

        观察差分的性质,其实每次修改就是在搬运一个 $1$ ,

        -

        也就是把它从 $b_l$ 搬到 $b_{r+1}$ ,或者反过来搬

        +

        考虑差分。差分数组 \(b_i\) 的定义为 +\(b_i = a_i-a_{i-1}\)

        +

        容易发现 \(a_i = \sum_{1 \le j \le +i}b_i\) ,值得注意的是, \(a_1 = +b_1\)

        +

        对于区间 \([l,r]\)\(1\) 的修改,只要把 \(b_l\) 加上 \(1\)\(b_{r+1}\) 减去 \(1\) 就可以了

        +

        然后问题就转化为了将 \(b_2,b_3,\dots,b_n\) 都变成 \(0\) 的最小花费

        +

        观察差分的性质,其实每次修改就是在搬运一个 \(1\)

        +

        也就是把它从 \(b_l\) 搬到 \(b_{r+1}\) ,或者反过来搬

        对于每个合法的修改,一定可以完成这样的操作

        -

        可以发现当 $r=n$ 时会出现从外面搬过来或者搬到外面去的情况

        -

        先不急,我们先考虑 $r<n$ 时

        -

        显然我们可以把所有的 $b_i<0$ 和 $b_j >0$ 配对( $2 \le i \le j \le n$ ),然后相互抵消

        -

        可以发现这样配对消去的最小花费为 $\min(p,q)$

        -

        其中 $p= \sum_{1 \le i \le n} [b_i<0],~q= \sum_{1 \le i \le n} [b_i>0]$

        +

        可以发现当 \(r=n\) +时会出现从外面搬过来或者搬到外面去的情况

        +

        先不急,我们先考虑 \(r<n\)

        +

        显然我们可以把所有的 \(b_i<0\) 和 +\(b_j >0\) 配对( \(2 \le i \le j \le n\) ),然后相互抵消

        +

        可以发现这样配对消去的最小花费为 \(\min(p,q)\)

        +

        其中 \(p= \sum_{1 \le i \le n} +[b_i<0],~q= \sum_{1 \le i \le n} [b_i>0]\)

        这样配对也会出现最后消不完的情况,

        -

        这个时候我们就可以把它们与 $n+1$ 配对,或者 $b_1$

        -

        这样的答案是 $\operatorname{abs}(p-q)$

        -

        则最后的答案就是 $\min(p,q)+\operatorname{abs}(p-q)=\max(p,q)$

        +

        这个时候我们就可以把它们与 \(n+1\) +配对,或者 \(b_1\)

        +

        这样的答案是 \(\operatorname{abs}(p-q)\)

        +

        则最后的答案就是 \(\min(p,q)+\operatorname{abs}(p-q)=\max(p,q)\)

        再看第二问,其实很简单

        -

        看我们的最后一步,如果和 $b_1$ 配对 $k$ 次,最后的 $a_i$ 就会变成 $b_1+k$

        -

        所以方案数就是 $\operatorname{abs}(p-q)+1$ (不与 $b_1$ 配对也是一种情况)

        -

        然后就好了,时间复杂度 $O(n)$

        +

        看我们的最后一步,如果和 \(b_1\) +配对 \(k\) 次,最后的 \(a_i\) 就会变成 \(b_1+k\)

        +

        所以方案数就是 \(\operatorname{abs}(p-q)+1\) (不与 \(b_1\) 配对也是一种情况)

        +

        然后就好了,时间复杂度 \(O(n)\)

        代码:

        #include <iostream>
         #include <string>
        @@ -893,7 +928,7 @@ 

         站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/16/luo-gu-p5521-yloi2019-mei-shen-bu-jian-dong-ti-jie/index.html b/2022/06/16/luo-gu-p5521-yloi2019-mei-shen-bu-jian-dong-ti-jie/index.html index e2edb4bac0..2e3bcd32c3 100644 --- a/2022/06/16/luo-gu-p5521-yloi2019-mei-shen-bu-jian-dong-ti-jie/index.html +++ b/2022/06/16/luo-gu-p5521-yloi2019-mei-shen-bu-jian-dong-ti-jie/index.html @@ -476,44 +476,68 @@

        洛谷P5521 [yLOI2019] 梅深不
        -

        洛谷P5521 [yLOI2019] 梅深不见冬 题解

        题目链接:P5521 [yLOI2019] 梅深不见冬

        +

        洛谷P5521 [yLOI2019] +梅深不见冬 题解

        +

        题目链接:P5521 +[yLOI2019] 梅深不见冬

        -

        题意

        -

        给定一棵树,根节点为 $1$ ,有点权 $w_i$ ,在结点上放满梅花

        -

        放的要求是,只有一个结点 $u$ 的所有儿子 $v$ 都放满了 $w_v$ 梅花才可以放

        +

        题意

        +

        给定一棵树,根节点为 \(1\) ,有点权 +\(w_i\) ,在结点上放满梅花

        +

        放的要求是,只有一个结点 \(u\) +的所有儿子 \(v\) +都放满了 \(w_v\) 梅花才可以放

        梅花可以随时收回,求每个结点放梅花的最小花费

        -
        +

        这个背景蛮有趣的,留着(逃

        -

        风,吹起梅岭的深冬;霜,如惊涛一样汹涌;
        雪,飘落后把所有烧成空,像这场,捕捉不到的梦。
        醒来时已是多年之久,宫门铜环才长了铁锈,也开始生出离愁。

        +

        风,吹起梅岭的深冬;霜,如惊涛一样汹涌;
        +雪,飘落后把所有烧成空,像这场,捕捉不到的梦。
        +醒来时已是多年之久,宫门铜环才长了铁锈,也开始生出离愁。

        ——银临《梅深不见冬》

        -

        $1 \leq n \leq 10^5 + 4,~1 \leq w_i \leq 1000$。

        +

        \(1 \leq n \leq 10^5 + 4,~1 \leq w_i \leq +1000\)

        -

        设第 $i$ 个结点放梅花的最小花费为 $f_i$ ,不难发现

        -

        后者是最小花费的下界

        -

        观察第一部分

        -

        这部分显然与 $\text{son}(u)$ 的排列有关

        +

        设第 \(i\) 个结点放梅花的最小花费为 +\(f_i\) ,不难发现 \[ +f_u = \max\left\{ \max_{v \in \text{son(u)}} \left\{f_v + \sum_{k \in +\text{son}(u)\, \land \,k \text{ before } v} w_k\right\},w_u + \sum_{v +\in \text{son}(u)}w_v\right\} +\] 后者是最小花费的下界

        +

        观察第一部分 \[ +\max_{v \in \text{son(u)}} \left\{f_v + \sum_{k \in \text{son}(u)\, +\land \,k \text{ before } v} w_k\right\} +\] 这部分显然与 \(\text{son}(u)\) 的排列有关

        如何求出这个最优排列?

        -

        贪心,按 $g_u = f_u - w_u$ 降序排序即可

        -

        考虑 $u$ 仅有 $2$ 个子结点 $x,y$ 的情况,

        -

        设 $x$ 在 $y$ 之前更优,则

        -
          -
        • 若 $f_y + w_x \ge f_x$ ,

          -

          因为 $f_y + w_x > f_y$ ,所以右侧取 $f_x+w_y$ 才可能成立

          -

          则可得

          -

          移项得

          -
        • -
        • 若 $f_y + w_x < f_x$

          -

          则 $f_x > f_y$,故右侧取 $f_x+w_y$ 时才可能成立

          -

          显然有 $f_x < f_x + w_y$

          -
        • +

          贪心,按 \(g_u = f_u - w_u\) +降序排序即可

          +

          考虑 \(u\) 仅有 \(2\) 个子结点 \(x,y\) 的情况,

          +

          \(x\)\(y\) 之前更优,则 \[ +f_u=\max\{f_x,f_y+w_x\}\le \max\{f_y,f_x+w_y\} +\]

          +
            +
          • \(f_y + w_x \ge f_x\)

            +

            因为 \(f_y + w_x > f_y\) +,所以右侧取 \(f_x+w_y\) 才可能成立

            +

            则可得 \[ +f_y + w_x \le f_x + w_y +\] 移项得 \[ +f_x-w_x \le f_y-w_y +\]

          • +
          • \(f_y + w_x < f_x\)

            +

            \(f_x > f_y\),故右侧取 \(f_x+w_y\) 时才可能成立

            +

            显然有 \(f_x < f_x + +w_y\)

          -

          综上所述,按 $g_u = f_u - w_u$ 降序排序可以获得最优解

          -

          时间复杂度 $O(n \log n)$

          +

          综上所述,按 \(g_u = f_u - w_u\) +降序排序可以获得最优解

          +

          时间复杂度 \(O(n \log n)\)

          代码:

          #include <iostream>
           #include <string>
          @@ -920,7 +944,7 @@ 

           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/16/sp1716-gss3-can-you-answer-these-queries-iii-ti-jie/index.html b/2022/06/16/sp1716-gss3-can-you-answer-these-queries-iii-ti-jie/index.html index 63bd392986..280acaa996 100644 --- a/2022/06/16/sp1716-gss3-can-you-answer-these-queries-iii-ti-jie/index.html +++ b/2022/06/16/sp1716-gss3-can-you-answer-these-queries-iii-ti-jie/index.html @@ -468,24 +468,33 @@

          SP1716 GSS3 - Can you answer the
          -

          SP1716 GSS3 - Can you answer these queries III 题解

          题目链接:SP1716 GSS3 - Can you answer these queries III

          +

          SP1716 GSS3 +- Can you answer these queries III 题解

          +

          题目链接:SP1716 +GSS3 - Can you answer these queries III

          题意

          -

          $n$ 个数, $q$ 次操作

          -

          操作0 x y把 $A_x$ 修改为 $y$

          -

          操作1 l r询问区间 $[l, r]$ 的最大子段和

          -

          $n \le 5\times 10^4,~q \le 5 \times 10^4$

          +

          \(n\) 个数, \(q\) 次操作

          +

          操作0 x y\(A_x\) +修改为 \(y\)

          +

          操作1 l r询问区间 \([l, +r]\) 的最大子段和

          +

          \(n \le 5\times 10^4,~q \le 5 \times +10^4\)

          线段树维护最大子段和去年就看到了一直没去学

          -

          考虑两个相邻区间合并,设答案为 $\text{res}_u$

          +

          考虑两个相邻区间合并,设答案为 \(\text{res}_u\)

            -
          • 若不考虑中间的分界线,则有

            -
          • -
          • 若考虑中间的分界线,于是尝试合并,则?

            -
          • +
          • 若不考虑中间的分界线,则有 \[ +\text{res}_u = \max\{\text{res}_l , \text{res}_r\} +\]

          • +
          • 若考虑中间的分界线,于是尝试合并,则?

          -

          可是我们并不知道 $\text{res}_l$ 和 $\text{res}_r$ 对应的序列,也不知道可否合并等等

          +

          可是我们并不知道 \(\text{res}_l\) 和 +\(\text{res}_r\) +对应的序列,也不知道可否合并等等

          注意到合并后的区间包含三种本质不同的子段

          • 全部在左区间
          • @@ -495,17 +504,22 @@

            +

            不妨设这两个东西分别为 \(\text{prel},\text{prer}\)

            +

            同时我们还需要记录区间和、区间最大子段和 \(\text{sum},\text{res}\)

            +

            设当前要合并的左右两个区间分别为 \(l,r\) ,合并后的区间为 \(u\)

            +

            则有 \[ \text{sum}_u = \text{sum}_l + \text{sum}_r \\\text{prel}_u = \max\{\text{prel}_l,~\text{sum}_l+\text{prel}_r\} \\\text{prer}_u = \max\{\text{prer}_r,~\text{sum}_r+\text{prer}_l\} -\\\text{res}_u = \max\{\text{res}_l,~\text{res}_r,~\text{prer}_l+\text{prel}_r\}

            然后就可以上传信息啦

            +\\\text{res}_u = +\max\{\text{res}_l,~\text{res}_r,~\text{prer}_l+\text{prel}_r\} +\] 然后就可以上传信息啦

            对于每个询问,也要维护这些信息,可以用结构体来搞

            -

            时间复杂度 $O(n \log n)$

            +

            时间复杂度 \(O(n \log n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -979,7 +993,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/17/cf125e-mst-company-ti-jie/index.html b/2022/06/17/cf125e-mst-company-ti-jie/index.html index c1f8a287fc..17d9a0b8c9 100644 --- a/2022/06/17/cf125e-mst-company-ti-jie/index.html +++ b/2022/06/17/cf125e-mst-company-ti-jie/index.html @@ -476,29 +476,43 @@

            CF125E MST Company 题解

            -

            CF125E MST Company 题解

            题目链接:CF125E MST Company

            +

            CF125E MST Company 题解

            +

            题目链接:CF125E +MST Company

            题意

            -

            给你一个有 $n$ 个节点,$m$ 条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足节点 $1$ 正好连了 $k$ 条边。

            +

            给你一个有 \(n\) 个节点,\(m\) +条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足节点 \(1\) 正好连了 \(k\) 条边。

            输出最优方案所选的边

            -

            $1 \le n,k \le 5000,~0 \le m \le 10^5,~1 \le w \le 10^5$

            +

            \(1 \le n,k \le 5000,~0 \le m \le 10^5,~1 +\le w \le 10^5\)

            -

            这道题的加强版,题解(下文我摘抄过来了一部分,反正都是我写的

            -

            我们把与 $s$ 相连的所有边标记为白边,其他边为黑边

            -

            然后问题就转化为了选恰好 $k$ 条白边的最小花费

            +

            这道题的加强版,题解(下文我摘抄过来了一部分,反正都是我写的

            +

            我们把与 \(s\) +相连的所有边标记为白边,其他边为黑边

            +

            然后问题就转化为了选恰好 \(k\) +条白边的最小花费

            考虑wqs二分,最好用归并优化一下

            这题的难点在无解的情况如何判断

              -
            • 白边不够用,如果与 $s$ 相连的边小于 $k$ 条,无解

              -
            • -
            • 黑边不够用,也就是生成树不得不包括大于 $k$ 条白边,无解

              -
            • +
            • 白边不够用,如果与 \(s\) +相连的边小于 \(k\) 条,无解

            • +
            • 黑边不够用,也就是生成树不得不包括大于 \(k\) 条白边,无解

            那道弱化版的题目只要求输出最优解的花费

            -

            但是我们都知道wqs二分的那个切线可能同时切到多个点

            +

            但是我们都知道wqs二分的那个切线可能同时切到多个点

            然后我的代码的写法是权值相等时优先取白边

            -

            所以答案中选了 $k$ 条白边后就不要再选白边了(当然这样不会改变权值)

            -

            时间复杂度 $O(n\log n + n\log {\max\{w_i\}})$

            +

            所以答案中选了 \(k\) +条白边后就不要再选白边了(当然这样不会改变权值)

            +

            时间复杂度 \(O(n\log n + n\log +{\max\{w_i\}})\)

            可以通过所有的hack数据,请放心食用 qwq

            代码:

            #include <iostream>
            @@ -991,7 +1005,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/17/luo-gu-p2607-zjoi2008-qi-shi-ti-jie/index.html b/2022/06/17/luo-gu-p2607-zjoi2008-qi-shi-ti-jie/index.html index ab1f910739..8d5a79ac1b 100644 --- a/2022/06/17/luo-gu-p2607-zjoi2008-qi-shi-ti-jie/index.html +++ b/2022/06/17/luo-gu-p2607-zjoi2008-qi-shi-ti-jie/index.html @@ -418,14 +418,14 @@

            洛谷P2607 [ZJOI2008] 骑士 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -480,20 +480,33 @@

            洛谷P2607 [ZJOI2008] 骑士
            -

            洛谷P2607 [ZJOI2008] 骑士 题解

            题目链接:P2607 [ZJOI2008] 骑士

            +

            洛谷P2607 [ZJOI2008] 骑士 +题解

            +

            题目链接:P2607 +[ZJOI2008] 骑士

            题意

            -

            Z 国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。

            -

            最近发生了一件可怕的事情,邪恶的 Y 国发动了一场针对 Z 国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的 Z 国又怎能抵挡的住 Y 国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。

            +

            Z +国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。

            +

            最近发生了一件可怕的事情,邪恶的 Y 国发动了一场针对 Z +国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的 Z +国又怎能抵挡的住 Y +国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。

            骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。

            战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。

            -

            为了描述战斗力,我们将骑士按照 $1$ 至 $n$ 编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

            -

            对于 $100\%$ 的测试数据,满足 $1\le n \le 10^6$,每名骑士的战斗力都是不大于 $10^6$ 的正整数。

            +

            为了描述战斗力,我们将骑士按照 \(1\) +至 \(n\) +编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

            +

            对于 \(100\%\) 的测试数据,满足 +\(1\le n \le +10^6\),每名骑士的战斗力都是不大于 \(10^6\) 的正整数。

            经典的基环树dp题

            因为结点只有一个出边,所以一定是基环树

            值得注意的是,这道题给出的是一个基环树森林

            -

            就多跑几遍就好了,时间复杂度 $O(n)$

            +

            就多跑几遍就好了,时间复杂度 \(O(n)\)

            这里是并查集判环写法,常数还算可以接受

            #include <iostream>
             #include <string>
            @@ -653,14 +666,14 @@ 

            算法 - - DP - - 图论 + + DP + + 数据结构 @@ -958,7 +971,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/17/luo-gu-p2619-guo-jia-ji-xun-dui-tree-i-ti-jie/index.html b/2022/06/17/luo-gu-p2619-guo-jia-ji-xun-dui-tree-i-ti-jie/index.html index 7fb01e5d6f..56abbc27b5 100644 --- a/2022/06/17/luo-gu-p2619-guo-jia-ji-xun-dui-tree-i-ti-jie/index.html +++ b/2022/06/17/luo-gu-p2619-guo-jia-ji-xun-dui-tree-i-ti-jie/index.html @@ -472,33 +472,57 @@

            洛谷P2619 [国家集训队]Tre
            -

            洛谷P2619 [国家集训队]Tree I 题解

            题目链接:P2619 [国家集训队]Tree I

            +

            洛谷P2619 [国家集训队]Tree I +题解

            +

            题目链接:P2619 +[国家集训队]Tree I

            题意

            -

            给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 $\text{need}$ 条白色边的生成树。

            +

            给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 +\(\text{need}\) 条白色边的生成树。

            题目保证有解。

            -

            对于 $100\%$ 的数据,$n\leq 5\times10^4,m\leq 10^5$。

            -

            所有数据边权为 $[1,100]$ 中的正整数。

            +

            对于 \(100\%\) 的数据,\(n\leq 5\times10^4,m\leq 10^5\)

            +

            所有数据边权为 \([1,100]\) +中的正整数。

            -

            直接 $O(nm)$ 的dp肯定是不行的,考虑wqs二分。

            +

            直接 \(O(nm)\) +的dp肯定是不行的,考虑wqs二分。

            在这题中,wqs二分做的其实就是给白边增加额外权值

            -

            使最优方案恰好选满 $\text{need}$ 条白边 。

            -

            假设二分的权值为 $\text{mid}$ ,

            -

            若选了大于 $\text{need}$ 条白边,则将白边边权增大,反之同理。

            -

            具体的,设选 $x$ 条白边的最小花费 $f(x)$

            -

            注意到点集 $(x,f(x))$ 形成一个下凸壳

            -

            也就是 $f(x)$ 为一个整数域的下凸函数

            -

            我们二分一个斜率 $k$ ,用这条直线去尝试切凸壳

            -

            显然这条直线在不确定 $y$ 轴截距时可以与这个凸壳有很多交点,

            -

            由斜截式方程得 $y$ 轴截距公式为

            -

            代入得

            -

            而 $b(x)$ 就是所有白边的边权减去 $k$ 以后选 $x$ 条白边的最小花费(最小生成树)

            -

            然后不考虑 $x$ 的限制跑一遍kruskal求出一个修改后的最优解,

            -

            根据这个最优解(也就是最小的 $b(x)$ )对应的 $x$ 改变左右端点

            -

            因为边权在 $[1,100]$ 间,所以设个 $l=-111,r=111$ 即可

            -

            时间复杂度 $O(n\log n\log 222)$

            +

            使最优方案恰好选满 \(\text{need}\) +条白边 。

            +

            假设二分的权值为 \(\text{mid}\) +,

            +

            若选了大于 \(\text{need}\) +条白边,则将白边边权增大,反之同理。

            +

            具体的,设选 \(x\) 条白边的最小花费 +\(f(x)\)

            +

            注意到点集 \((x,f(x))\) +形成一个下凸壳

            +

            也就是 \(f(x)\) +为一个整数域的下凸函数

            +

            我们二分一个斜率 \(k\) +,用这条直线去尝试切凸壳

            +

            显然这条直线在不确定 \(y\) +轴截距时可以与这个凸壳有很多交点,

            +

            由斜截式方程得 \(y\) 轴截距公式为 +\[ +b=y-kx +\] 代入得 \[ +b(x)=f(x)-kx +\]\(b(x)\) +就是所有白边的边权减去 \(k\) 以后选 +\(x\) +条白边的最小花费(最小生成树)

            +

            然后不考虑 \(x\) +的限制跑一遍kruskal求出一个修改后的最优解,

            +

            根据这个最优解(也就是最小的 \(b(x)\) )对应的 \(x\) 改变左右端点

            +

            因为边权在 \([1,100]\) 间,所以设个 +\(l=-111,r=111\) 即可

            +

            时间复杂度 \(O(n\log n\log +222)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -777,14 +801,14 @@ 

            算法 - - DP - - 图论 + + DP + + 数据结构 @@ -942,7 +966,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/17/luo-gu-p5633-zui-xiao-du-xian-zhi-sheng-cheng-shu-ti-jie/index.html b/2022/06/17/luo-gu-p5633-zui-xiao-du-xian-zhi-sheng-cheng-shu-ti-jie/index.html index e9877a3a25..18d09e1adc 100644 --- a/2022/06/17/luo-gu-p5633-zui-xiao-du-xian-zhi-sheng-cheng-shu-ti-jie/index.html +++ b/2022/06/17/luo-gu-p5633-zui-xiao-du-xian-zhi-sheng-cheng-shu-ti-jie/index.html @@ -472,25 +472,40 @@

            洛谷P5633 最小度限制生
            -

            洛谷P5633 最小度限制生成树 题解

            题目链接:P5633 最小度限制生成树

            +

            洛谷P5633 最小度限制生成树 +题解

            +

            题目链接:P5633 +最小度限制生成树

            题意

            -

            给你一个有 $n$ 个节点,$m$ 条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足编号为 $s$ 的节点正好连了 $k$ 条边。

            +

            给你一个有 \(n\) 个节点,\(m\) +条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足编号为 +\(s\) 的节点正好连了 \(k\) 条边。

            可能会出现无解的情况,如果无解,则输出 Impossible

            -

            对于 $100\%$ 的数据,$1\leq n \le 5\times 10^4$,$1\leq m \le 5\times 10^5$,$1\leq k \le 100$,$0\leq w\leq 3\times 10^4$。

            +

            对于 \(100\%\) 的数据,\(1\leq n \le 5\times 10^4\)\(1\leq m \le 5\times 10^5\)\(1\leq k \le 100\)\(0\leq w\leq 3\times 10^4\)

            -

            如果做过这道题应该会觉得很简单了

            -

            我们把与 $s$ 相连的所有边标记为白边,其他边为黑边

            -

            然后问题就转化为了选恰好 $k$ 条白边的最小花费

            +

            如果做过这道题应该会觉得很简单了

            +

            我们把与 \(s\) +相连的所有边标记为白边,其他边为黑边

            +

            然后问题就转化为了选恰好 \(k\) +条白边的最小花费

            考虑wqs二分,最好用归并优化一下

            这题的难点在无解的情况如何判断

              -
            • 白边不够用,如果与 $s$ 相连的边小于 $k$ 条,无解

              -
            • -
            • 黑边不够用,也就是生成树不得不包括大于 $k$ 条白边,无解

              -
            • +
            • 白边不够用,如果与 \(s\) +相连的边小于 \(k\) 条,无解

            • +
            • 黑边不够用,也就是生成树不得不包括大于 \(k\) 条白边,无解

            -

            时间复杂度 $O(n\log n + n\log {\max\{w_i\}})$

            +

            时间复杂度 \(O(n\log n + n\log +{\max\{w_i\}})\)

            可以通过所有的hack数据,请放心食用 qwq

            代码:

            #include <iostream>
            @@ -964,7 +979,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/18/luo-gu-p3195-hnoi2008-wan-ju-zhuang-xiang-ti-jie/index.html b/2022/06/18/luo-gu-p3195-hnoi2008-wan-ju-zhuang-xiang-ti-jie/index.html index 627e560854..e9647d6ab9 100644 --- a/2022/06/18/luo-gu-p3195-hnoi2008-wan-ju-zhuang-xiang-ti-jie/index.html +++ b/2022/06/18/luo-gu-p3195-hnoi2008-wan-ju-zhuang-xiang-ti-jie/index.html @@ -476,70 +476,108 @@

            洛谷P3195 [HNOI2008]玩具装
            -

            洛谷P3195 [HNOI2008]玩具装箱 题解

            题目链接:P3195 [HNOI2008]玩具装箱

            +

            洛谷P3195 [HNOI2008]玩具装箱 +题解

            +

            题目链接:P3195 +[HNOI2008]玩具装箱

            题意

            -

            有 $n$ 个玩具,第 $i$ 个玩具的价值为 $c_i$ 。要求将这 $n$ 个玩具排成一排,分成若干段。对于一段 $[l,r]$ ,它的代价为 $(r-l+\sum_{i=l}^{r}c_i-L)^2$ ,其中 $L$ 为一个常量,求分段的最小代价。

            -

            $1 \leq n \leq 5 \times 10^4$,$1 \leq L \leq 10^7$,$1 \leq C_i \leq 10^7$

            +

            \(n\) 个玩具,第 \(i\) 个玩具的价值为 \(c_i\) 。要求将这 \(n\) 个玩具排成一排,分成若干段。对于一段 +\([l,r]\) ,它的代价为 \((r-l+\sum_{i=l}^{r}c_i-L)^2\) ,其中 \(L\) 为一个常量,求分段的最小代价。

            +

            \(1 \leq n \leq 5 \times +10^4\)\(1 \leq L \leq +10^7\)\(1 \leq C_i \leq +10^7\)

            upd. 新增斜率优化新的理解。

            -

            令 $f_i$ 表示前 $i$ 个物品分成若干段的最小代价,则

            -

            其中 $S_i$ 为前 $i$ 个物品的价值和,即 $S_i = \sum_{j=1}^{i}c_j$

            -

            直接去做是 $O(n^2)$ ,考虑斜率优化

            -

            令 $a_i = S_i + i,~b_i = S_i + i + L+1$ ,则

            -

            移项得

            -

            注意到右边的部分有一个 $2a_i$ 导致我们无法单调队列优化

            -

            但是我们可以斜率优化,也就是吧 $2a_i$ 看作 $k$

            -

            根据斜截式方程可得

            -

            可知

            -

            则转移方程可以写成如下形式

            -

            把 $(x_j,y_j)$ 映射到平面上的点,那么问题就转化为了

            -

            找到一个 $1\le j<i$ 最小化过点 $(x_j,y_j)$ 且斜率为 $k_i$ 的直线的 $y$ 轴截距

            -

            由于这个 $k_i$ 是严格单调递增的,

            -

            不难发现这个点一定在 $(x_j,y_j)$ 点集形成的下凸壳上,

            -

            并且这个点 $P_j$ 的 $j$ 是满足直线 $P_jP_{j+1}$ 的斜率 $k_j^{\prime}>k_i$ 最小的 $j$

            -

            或者说,这个点和下一个在下凸壳上的点所连产生的直线是第一个斜率比 $k_i$ 大的

            +b^\prime_i&=f_i-a_i^2 +\\k_i&=2a_i +\\x_j&=b_j +\\y_j &= f_j + b_j^2 +\end{aligned} +\] 则转移方程可以写成如下形式 \[ +b_i^\prime = \min\left\{ y_j-k_ix_j\right\} +\]\((x_j,y_j)\) +映射到平面上的点,那么问题就转化为了

            +

            找到一个 \(1\le j<i\) +最小化过点 \((x_j,y_j)\) +且斜率为 \(k_i\) 的直线的 \(y\) 轴截距

            +

            由于这个 \(k_i\) +是严格单调递增的,

            +

            不难发现这个点一定在 \((x_j,y_j)\) +点集形成的下凸壳上,

            +

            并且这个点 \(P_j\)\(j\) 是满足直线 \(P_jP_{j+1}\) 的斜率 \(k_j^{\prime}>k_i\) 最小的 \(j\)

            +

            或者说,这个点和下一个在下凸壳上的点所连产生的直线是第一个斜率比 +\(k_i\) 大的

            考虑如何维护这个下凸壳

            -

            刚才我们也说了 $k_i$ 是单调递增的,所以这个下凸壳是一个“动态”的

            -

            当凸壳上的某个点 $u$ 和下一个点 $v$ 所成的直线斜率小于 $k_i$ 时,

            -

            $u$ 就没用了,该被踢掉了

            +

            刚才我们也说了 \(k_i\) +是单调递增的,所以这个下凸壳是一个“动态”的

            +

            当凸壳上的某个点 \(u\) 和下一个点 +\(v\) 所成的直线斜率小于 \(k_i\) 时,

            +

            \(u\) 就没用了,该被踢掉了

            因此我们可以用单调队列来维护,而不是单调栈

            然后每次加点就和二维凸包那个差不多,这里不细讲了(

            -
            +

            对于斜率优化,还有另一种理解

            -

            首先刚刚的柿子还是要用到的

            -

            我们不用刚刚那个

            -

            考虑一个 $j~(j>k)$ 在什么情况下比 $k$ 优

            -

            显然有

            -

            然后推推柿子

            -

            注意到 $b_i = S_i + i + L+1$ 是单调递增的

            -

            因此有 $b_j > b_k$

            -

            则柿子可以化简为

            -

            左边这个东西长的很像斜率的定义

            +

            首先刚刚的柿子还是要用到的 \[ +f_i-a_i^2=\min_{j < i}\left\{ f_j - 2a_ib_j + b_j^2\right\} +\] 我们不用刚刚那个

            +

            考虑一个 \(j~(j>k)\) +在什么情况下比 \(k\)

            +

            显然有 \[ +f_j-2a_ib_j+b_j^2 < f_k-2a_ib_k+b_k^2 +\] 然后推推柿子 \[ +f_j+b_j^2-f_k-b_k^2 < 2a_i(b_j-b_k) +\] 注意到 \(b_i = S_i + i + +L+1\) 是单调递增的

            +

            因此有 \(b_j > b_k\)

            +

            则柿子可以化简为 \[ +\dfrac{f_j+b_j^2-f_k-b_k^2}{b_j-b_k} < 2a_i +\] 左边这个东西长的很像斜率的定义

            因此我们可以维护一个点集,然后两两点去判断

            但是这样肯定是不优的

            -

            仔细思考一下,可能成为答案的点一定在这个点集的下凸壳上(因为我们要最小化 $ f_j - 2a_ib_j + b_j^2$ ,所以是下凸壳)

            +

            仔细思考一下,可能成为答案的点一定在这个点集的下凸壳上(因为我们要最小化 +$ f_j - 2a_ib_j + b_j^2$ ,所以是下凸壳)

            于是我们维护一个下凸壳,依次加入点,再踢出那些不优的点

            -
            -

            时间复杂度 $O(n)$

            +
            +

            时间复杂度 \(O(n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -585,6 +623,7 @@ 

            << (int)dp[n] << '\n'; return 0; }

            +

            @@ -957,7 +996,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/18/luo-gu-p4983-wang-qing-ti-jie/index.html b/2022/06/18/luo-gu-p4983-wang-qing-ti-jie/index.html index 55ec3c6853..6ad3367c78 100644 --- a/2022/06/18/luo-gu-p4983-wang-qing-ti-jie/index.html +++ b/2022/06/18/luo-gu-p4983-wang-qing-ti-jie/index.html @@ -472,51 +472,75 @@

            洛谷P4983 忘情 题解

            -

            洛谷P4983 忘情 题解

            题目链接:P4983 忘情

            +

            洛谷P4983 忘情 题解

            +

            题目链接:P4983 +忘情

            题意

            “为什么要离开我!”

            -

            “因为你没玩儿转!”

            -

            “我玩儿转了!”

            -

            “那好,你现在就给我维护这么一个式子!”

            -

            “为什么要出这么毒瘤的东西。”

            -

            “为了恶心你。”

            -

            “……”

            -

            $……………………………$

            -

            你的 $npy$ 为了恶心你,特地请了四位大神和一个辣鸡!

            -

            $\rm hdxrie$ 说:“我们得求和。”于是有了 $\sum\limits_{i=1}^{n}x_i$ 。

            -

            $\rm Imagine$ 说:“我们得有平均数。”于是有了 $\bar x$ 。

            -

            $\rm TimeTraveller$ 说:“我们得有加减乘除。”于是有了一些恶心的组合。

            -

            $\rm Althen·Way·Satan$ 说:“我们还得有平方。”于是我们将它平方。

            -

            最垃圾的 $\rm ZredXNy$ 说:“那我帮你们整合一下。”

            -

            于是,我们得到了这么一个式子 $:$

            -

            我们定义一段序列的值为这个,其中 $n$为此序列的元素个数。

            -

            我们给定一段长度为 $n$ 的序列,现在要求将它分成 $m$ 段,要求每一段的值的总和最小,求出这个最小值。

            -

            对于 $100 \%$ 的数据,$m≤n≤100000$,$1≤x_i≤1000$。

            +

            “因为你没玩儿转!”

            +

            “我玩儿转了!”

            +

            “那好,你现在就给我维护这么一个式子!”

            +

            “为什么要出这么毒瘤的东西。”

            +

            “为了恶心你。”

            +

            “......”

            +

            \(……………………………\)

            +

            你的 \(npy\) +为了恶心你,特地请了四位大神和一个辣鸡!

            +

            \(\rm hdxrie\) +说:“我们得求和。”于是有了 \(\sum\limits_{i=1}^{n}x_i\)

            +

            \(\rm Imagine\) +说:“我们得有平均数。”于是有了 \(\bar +x\)

            +

            \(\rm TimeTraveller\) +说:“我们得有加减乘除。”于是有了一些恶心的组合。

            +

            \(\rm Althen·Way·Satan\) +说:“我们还得有平方。”于是我们将它平方。

            +

            最垃圾的 \(\rm ZredXNy\) +说:“那我帮你们整合一下。”

            +

            于是,我们得到了这么一个式子 \(:\)

            +

            \[ +\frac{\left((\sum\limits_{i=1}^{n}x_i×\bar x)+\bar x\right)^2}{\bar x^2} +\]

            +

            我们定义一段序列的值为这个,其中 \(n\)为此序列的元素个数。

            +

            我们给定一段长度为 \(n\) +的序列,现在要求将它分成 \(m\) +段,要求每一段的值的总和最小,求出这个最小值。

            +

            对于 \(100 \%\) 的数据,\(m≤n≤100000\)\(1≤x_i≤1000\)

            柿子化简懒得讲了,太简单了

            -

            最后化简出来每一段的花费就是

            -

            其中 $S_k = \sum_{i=1}^{k}x_i$

            -

            设 $f_{i,k}$ 表示只考虑前 $i$ 个物品分成 $k$ 段的最小花费

            -

            显然可以wqs二分去掉 $k$ 维(证明略)

            -

            令 $a_i =S_i+1,b_j=S_j$ ,则

            -

            移项得

            -

            -

            斜率优化 $O(n\log 10^{16})$

            +k_i &= 2a_i +\\x_j &= b_j +\\y_j &= f_j+b_j^2 +\\b_i &= f_i-a_i^2 +\end{aligned} +\] 斜率优化 \(O(n\log +10^{16})\)

            我的写法是二分的那条直线相等时优先选分的段最多的(所谓尽可能多选)

            也就是斜率相等时,优先切最右边的点,显然这个点分的段更多一些

            代码:

            @@ -637,7 +661,7 @@

            << f[n]-m*l; return 0; }

            -

            有一说一,这个题面怎么有点…(懂得都懂)

            +

            有一说一,这个题面怎么有点...(懂得都懂)

            @@ -1007,7 +1031,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/21/luo-gu-p1378-you-di-kuo-zhan-ti-jie/index.html b/2022/06/21/luo-gu-p1378-you-di-kuo-zhan-ti-jie/index.html index df44fab0cb..494160747f 100644 --- a/2022/06/21/luo-gu-p1378-you-di-kuo-zhan-ti-jie/index.html +++ b/2022/06/21/luo-gu-p1378-you-di-kuo-zhan-ti-jie/index.html @@ -472,18 +472,28 @@

            洛谷P1378 油滴扩展 题解<
            -

            洛谷P1378 油滴扩展 题解

            题目链接:P1378 油滴扩展

            +

            洛谷P1378 油滴扩展 题解

            +

            题目链接:P1378 +油滴扩展

            题意

            -

            在一个长方形框子里,最多有 $N$ 个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界。必须等一个油滴扩展完毕才能放置下一个油滴。那么应该按照怎样的顺序在这 $N$ 个点上放置油滴,才能使放置完毕后所有油滴占据的总体积最大呢?(不同的油滴不会相互融合)

            -

            注:圆的面积公式 $V = \pi r^2$,其中 $r$ 为圆的半径。

            -

            对于 $100\%$ 的数据,$1 \le N \le 6$,坐标范围在 $[-1000, 1000]$ 内。

            +

            在一个长方形框子里,最多有 \(N\) +个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界。必须等一个油滴扩展完毕才能放置下一个油滴。那么应该按照怎样的顺序在这 +\(N\) +个点上放置油滴,才能使放置完毕后所有油滴占据的总体积最大呢?(不同的油滴不会相互融合)

            +

            注:圆的面积公式 \(V = \pi +r^2\),其中 \(r\) +为圆的半径。

            +

            对于 \(100\%\) 的数据,\(1 \le N \le 6\),坐标范围在 \([-1000, 1000]\) 内。

            数据范围告诉我们这就是一道暴搜题

            枚举油滴扩展的顺序,然后和已经扩散好的油滴算一下距离

            -

            注意距离可能为负,此时新油滴在某油滴内部,此时认为 $r=0$ 即可

            +

            注意距离可能为负,此时新油滴在某油滴内部,此时认为 \(r=0\) 即可

            最后的答案就是总面积减去油滴面积并

            -

            时间复杂度 $O(n\times n!)$

            +

            时间复杂度 \(O(n\times n!)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -915,7 +925,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/21/luo-gu-p1514-noip2010-ti-gao-zu-yin-shui-ru-cheng-ti-jie/index.html b/2022/06/21/luo-gu-p1514-noip2010-ti-gao-zu-yin-shui-ru-cheng-ti-jie/index.html index 2f030938f5..4684f2d6d4 100644 --- a/2022/06/21/luo-gu-p1514-noip2010-ti-gao-zu-yin-shui-ru-cheng-ti-jie/index.html +++ b/2022/06/21/luo-gu-p1514-noip2010-ti-gao-zu-yin-shui-ru-cheng-ti-jie/index.html @@ -468,14 +468,25 @@

            洛谷P1514 [NOIP2010 提高组]
            -

            洛谷P1514 [NOIP2010 提高组] 引水入城 题解

            题目链接:P1514 [NOIP2010 提高组] 引水入城

            +

            洛谷P1514 [NOIP2010 +提高组] 引水入城 题解

            +

            题目链接:P1514 +[NOIP2010 提高组] 引水入城

            题意

            -

            在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个$N$ 行$\times M$ 列的矩形,如上图所示,其中每个格子都代表一座城市,每座城市都有一个海拔高度。

            -

            +

            在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个\(N\)\(\times +M\) +列的矩形,如上图所示,其中每个格子都代表一座城市,每座城市都有一个海拔高度。

            +

            为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。

            -

            因此,只有与湖泊毗邻的第$1$ 行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。由于第$N$ 行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。

            -

            +

            因此,只有与湖泊毗邻的第\(1\) +行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。由于第\(N\) +行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。

            +

            首先可以想到我们直接在每个岸边的点跑dfs

            同时记录每个点能覆盖的城市数目

            @@ -484,18 +495,29 @@

            \(u\) +能覆盖的最左和最右城市分别为 \(l,r\)

            +

            \(u,l,r\) +三点一定构成一个类似于三角形的封闭图形

            +

            显然其他蓄水厂如果要覆盖 \([l,r]\) +中的点,一定会经过三角形的某条边

            +

            如果某个点 \(x \in [l,r]\) +不能被这个三角形内任意一点流出的水覆盖

            +

            则外界流入的水无论从哪里经过三角形,都无法流到 \(x\) ,那么就无解了

            因此有解的时候任意蓄水厂能覆盖的城市构成一条线段

            知道了线段,怎么算答案呢?

            -

            我们可以设一个 $L$ 表示目前覆盖到的城市位置(从左向右覆盖)

            -

            而任意一条线段的左端点 $l_i\le L$ 均可以使 $L$ 右移至 $r_i$

            -

            于是我们贪心地选择所有满足 $l_i\le L$ 的线段中最大的 $r_i$ ,去更新 $L$

            +

            我们可以设一个 \(L\) +表示目前覆盖到的城市位置(从左向右覆盖)

            +

            而任意一条线段的左端点 \(l_i\le L\) +均可以使 \(L\) 右移至 \(r_i\)

            +

            于是我们贪心地选择所有满足 \(l_i\le +L\) 的线段中最大的 \(r_i\) +,去更新 \(L\)

            然后就没了

            -

            时间复杂度 $O(nm)$

            +

            时间复杂度 \(O(nm)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -921,7 +943,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/21/luo-gu-p1535-usaco08mar-cow-travelling-s-ti-jie/index.html b/2022/06/21/luo-gu-p1535-usaco08mar-cow-travelling-s-ti-jie/index.html index 31fa716617..5acd386f9c 100644 --- a/2022/06/21/luo-gu-p1535-usaco08mar-cow-travelling-s-ti-jie/index.html +++ b/2022/06/21/luo-gu-p1535-usaco08mar-cow-travelling-s-ti-jie/index.html @@ -472,20 +472,47 @@

            洛谷P1535 [USACO08MAR]Cow Trav
            -

            洛谷P1535 [USACO08MAR]Cow Travelling S 题解

            题目链接:P1535 [USACO08MAR]Cow Travelling S

            +

            洛谷P1535 +[USACO08MAR]Cow Travelling S 题解

            +

            题目链接:P1535 +[USACO08MAR]Cow Travelling S

            题意

            -

            奶牛们在被划分成 $N$ 行 $M$ 列($2 \leq N,M \leq 100$)的草地上游走, 试图找到整块草地中最美味的牧草。

            -

            Farmer John 在某个时刻看见贝茜在位置 $(R_1, C_1)$,恰好 $T$($0 \lt T \leq 15$)秒后,FJ 又在位置 $(R_2, C_2)$ 与贝茜撞了正着。FJ 并不知道在这 $T$ 秒内贝茜是否曾经到过 $(R_2, C_2)$,他能确定的只是,现在贝茜在那里。

            -

            设 $S$ 为奶牛在 $T$ 秒内从 $(R_1, C_1)$ 走到 $(R_2, C_2)$ 所能选择的路径总数,FJ 希望有 一个程序来帮他计算这个值。每一秒内,奶牛会水平或垂直地移动 $1$ 单位距离(奶牛总是在移动,不会在某秒内停在它上一秒所在的点)。草地上的某些地方有树,自然,奶牛不能走到树所在的位置,也不会走出草地。

            -

            现在你拿到了一张整块草地的地形图,其中 . 表示平坦的草地,* 表示挡路的树。你的任务是计算出,一头在 $T$ 秒内从 $(R_1, C_1)$ 移动到 $(R_2, C_2)$ 的奶牛可能经过的路径有哪些。

            +

            奶牛们在被划分成 \(N\)\(M\) 列(\(2 \leq +N,M \leq 100\))的草地上游走, +试图找到整块草地中最美味的牧草。

            +

            Farmer John 在某个时刻看见贝茜在位置 \((R_1, C_1)\),恰好 \(T\)\(0 \lt T +\leq 15\))秒后,FJ 又在位置 \((R_2, +C_2)\) 与贝茜撞了正着。FJ 并不知道在这 \(T\) 秒内贝茜是否曾经到过 \((R_2, +C_2)\),他能确定的只是,现在贝茜在那里。

            +

            \(S\) 为奶牛在 \(T\) 秒内从 \((R_1, C_1)\) 走到 \((R_2, C_2)\) 所能选择的路径总数,FJ 希望有 +一个程序来帮他计算这个值。每一秒内,奶牛会水平或垂直地移动 \(1\) +单位距离(奶牛总是在移动,不会在某秒内停在它上一秒所在的点)。草地上的某些地方有树,自然,奶牛不能走到树所在的位置,也不会走出草地。

            +

            现在你拿到了一张整块草地的地形图,其中 . +表示平坦的草地,* 表示挡路的树。你的任务是计算出,一头在 +\(T\) 秒内从 \((R_1, C_1)\) 移动到 \((R_2, C_2)\) +的奶牛可能经过的路径有哪些。

            一眼看成组合数有救吗

            注意到这是个计数dp

            -

            设 $f_{i,j,k}$ 表示用 $k$ 步恰好走到 $(i,j)$ 的方案数

            +

            \(f_{i,j,k}\) 表示用 \(k\) 步恰好走到 \((i,j)\) 的方案数

            这里要用记搜来算dp,刷表法更新(也就是用当前结点的答案去更新别的结点)

            为什么用记忆化搜索呢?因为朴素的循环无法保证dp计算的顺序

            -

            时间复杂度的宽松上界为 $O(nmt)$

            +

            时间复杂度的宽松上界为 \(O(nmt)\)

            这题数据范围比较小所以据说暴搜剪枝都能过

            代码:

            #include <iostream>
            @@ -913,7 +940,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/21/luo-gu-p1608-lu-jing-tong-ji-ti-jie/index.html b/2022/06/21/luo-gu-p1608-lu-jing-tong-ji-ti-jie/index.html index cfaa7be326..c125369cc1 100644 --- a/2022/06/21/luo-gu-p1608-lu-jing-tong-ji-ti-jie/index.html +++ b/2022/06/21/luo-gu-p1608-lu-jing-tong-ji-ti-jie/index.html @@ -468,24 +468,46 @@

            洛谷P1608 路径统计 题解<
            -

            洛谷P1608 路径统计 题解

            题目链接:P1608 路径统计

            +

            洛谷P1608 路径统计 题解

            +

            题目链接:P1608 +路径统计

            题意

            一句话题意:最短路计数。

            -

            “RP 餐厅” 的员工素质就是不一般,在齐刷刷的算出同一个电话号码之后,就准备让 HZH,TZY 去送快餐了,他们将自己居住的城市画了一张地图,已知在他们的地图上,有 $N$ 个地方,而且他们目前处在标注为 “1” 的小镇上,而送餐的地点在标注为 “N” 的小镇。(有点废话)除此之外还知道这些道路都是单向的,从小镇 $I$ 到 $J$ 需要花费 $D[I, J]$ 的时间,为了更高效快捷的将快餐送到顾客手中,他们想走一条从小镇 $1$ 到小镇 $N$ 花费最少的一条路,但是他们临出发前,撞到因为在路上堵车而生气的 FYY,深受启发,不能仅知道一条路线,万一。。。于是,他们邀请 FYY 一起来研究起了下一个问题:这个最少花费的路径有多少条?

            -

            对于 $100\%$ 的数据 $1\leq N\leq 2000$,$0\leq E\leq N\times (N-1)$,$1\leq C\leq 10$.

            +

            “RP 餐厅” +的员工素质就是不一般,在齐刷刷的算出同一个电话号码之后,就准备让 HZH,TZY +去送快餐了,他们将自己居住的城市画了一张地图,已知在他们的地图上,有 +\(N\) 个地方,而且他们目前处在标注为 +“1” 的小镇上,而送餐的地点在标注为 “N” +的小镇。(有点废话)除此之外还知道这些道路都是单向的,从小镇 \(I\)\(J\) 需要花费 \(D[I, J]\) +的时间,为了更高效快捷的将快餐送到顾客手中,他们想走一条从小镇 \(1\) 到小镇 \(N\) +花费最少的一条路,但是他们临出发前,撞到因为在路上堵车而生气的 +FYY,深受启发,不能仅知道一条路线,万一。。。于是,他们邀请 FYY +一起来研究起了下一个问题:这个最少花费的路径有多少条?

            +

            对于 \(100\%\) 的数据 \(1\leq N\leq 2000\)\(0\leq E\leq N\times (N-1)\)\(1\leq C\leq 10\).

            最短路计数据说不能用spfa(spfa已死

            -

            设 $f_u$ 表示从起点到 $u$ 结点的最短路数量

            +

            \(f_u\) 表示从起点到 \(u\) 结点的最短路数量

            显然用dijkstra刷表

            -

            若 $(u,v)$ 松弛成功,也就是

            +

            \((u,v)\) 松弛成功,也就是

            d[v]>d[u]+w

            我们就让f[v]=f[u]

            否则如果

            d[v]==d[u]+w

            就让f[v]+=f[u]

            没了,就这么水

            -

            时间复杂度 $O(n \log m)$ ,这里我写的邻接矩阵所以 $m \approx n^2$

            +

            时间复杂度 \(O(n \log m)\) +,这里我写的邻接矩阵所以 \(m \approx +n^2\)

            代码:

            #include <iostream>
             #include <string>
            @@ -910,7 +932,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/21/luo-gu-p2466-sdoi2008-sue-de-xiao-qiu-ti-jie/index.html b/2022/06/21/luo-gu-p2466-sdoi2008-sue-de-xiao-qiu-ti-jie/index.html index 8bd35d4594..d340da5f33 100644 --- a/2022/06/21/luo-gu-p2466-sdoi2008-sue-de-xiao-qiu-ti-jie/index.html +++ b/2022/06/21/luo-gu-p2466-sdoi2008-sue-de-xiao-qiu-ti-jie/index.html @@ -472,38 +472,71 @@

            洛谷P2466 [SDOI2008] Sue 的
            -

            洛谷P2466 [SDOI2008] Sue 的小球 题解

            题目链接:P2466 [SDOI2008] Sue 的小球

            +

            洛谷P2466 [SDOI2008] Sue +的小球 题解

            +

             题目链接:P2466 +[SDOI2008] Sue 的小球

            题意

            -

            Sue 和 Sandy 最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue 有一支轻便小巧的小船。然而,Sue 的目标并不是当一个海盗,而是要收集空中漂浮的彩蛋,Sue 有一个秘密武器,只要她将小船划到一个彩蛋的正下方,然后使用秘密武器便可以在瞬间收集到这个彩蛋。然而,彩蛋有一个魅力值,这个魅力值会随着彩蛋在空中降落的时间而降低,Sue 要想得到更多的分数,必须尽量在魅力值高的时候收集这个彩蛋,而如果一个彩蛋掉入海中,它的魅力值将会变成一个负数,但这并不影响 Sue 的兴趣,因为每一个彩蛋都是不同的,Sue 希望收集到所有的彩蛋。

            -

            然而 Sandy 就没有 Sue 那么浪漫了,Sandy 希望得到尽可能多的分数,为了解决这个问题,他先将这个游戏抽象成了如下模型:

            -

            将大海近似的看做 $x$ 轴,以 Sue 所在的初始位置作为坐标原点建立一个竖直的平面直角坐标系。

            -

            一开始空中有 $N$ 个彩蛋,对于第 $i$ 个彩蛋,他的初始位置用整数坐标 $(x_{i}, y_{i})$ 表示,游戏开始后,它匀速沿 $y$ 轴负方向下落,速度为 $v_{i}$ 单位距离/单位时间。Sue 的初始位置为 $(x_{0}, 0)$,Sue 可以沿 $x$ 轴的正方向或负方向移动,Sue 的移动速度是 $1$ 单位距离/单位时间,使用秘密武器得到一个彩蛋是瞬间的,得分为当前彩蛋的 $y$ 坐标的千分之一。

            -

            现在,Sue 和 Sandy 请你来帮忙,为了满足 Sue 和 Sandy 各自的目标,你决定在收集到所有彩蛋的基础上,得到的分数最高。

            -

            对于 $100\%$ 的数据,$-10^4 \leq x_{i},y_{i},v_{i} \leq 10^4$,$N \leq 1000$

            +

            Sue 和 Sandy +最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue +有一支轻便小巧的小船。然而,Sue +的目标并不是当一个海盗,而是要收集空中漂浮的彩蛋,Sue +有一个秘密武器,只要她将小船划到一个彩蛋的正下方,然后使用秘密武器便可以在瞬间收集到这个彩蛋。然而,彩蛋有一个魅力值,这个魅力值会随着彩蛋在空中降落的时间而降低,Sue +要想得到更多的分数,必须尽量在魅力值高的时候收集这个彩蛋,而如果一个彩蛋掉入海中,它的魅力值将会变成一个负数,但这并不影响 +Sue 的兴趣,因为每一个彩蛋都是不同的,Sue 希望收集到所有的彩蛋。

            +

            然而 Sandy 就没有 Sue 那么浪漫了,Sandy +希望得到尽可能多的分数,为了解决这个问题,他先将这个游戏抽象成了如下模型:

            +

            将大海近似的看做 \(x\) 轴,以 Sue +所在的初始位置作为坐标原点建立一个竖直的平面直角坐标系。

            +

            一开始空中有 \(N\) 个彩蛋,对于第 +\(i\) 个彩蛋,他的初始位置用整数坐标 +\((x_{i}, y_{i})\) +表示,游戏开始后,它匀速沿 \(y\) +轴负方向下落,速度为 \(v_{i}\) +单位距离/单位时间。Sue 的初始位置为 \((x_{0}, +0)\),Sue 可以沿 \(x\) +轴的正方向或负方向移动,Sue 的移动速度是 \(1\) +单位距离/单位时间,使用秘密武器得到一个彩蛋是瞬间的,得分为当前彩蛋的 +\(y\) 坐标的千分之一。

            +

            现在,Sue 和 Sandy 请你来帮忙,为了满足 Sue 和 Sandy +各自的目标,你决定在收集到所有彩蛋的基础上,得到的分数最高。

            +

            对于 \(100\%\) 的数据,\(-10^4 \leq x_{i},y_{i},v_{i} \leq +10^4\)\(N \leq 1000\)

            -

            首先先将彩蛋按 $x$ 排序

            +

            首先先将彩蛋按 \(x\) 排序

            因为可以左右走,可以想到这是一个区间dp

            -

            设 $f_{0/1,i,j}$ 表示区间 $[i,j]$ 的彩蛋都被收集后的最小花费(代价),0/1表示此时在 $i$ 还是 $j$

            +

            \(f_{0/1,i,j}\) 表示区间 \([i,j]\) +的彩蛋都被收集后的最小花费(代价),0/1表示此时在 \(i\) 还是 \(j\)

            转移方程?

            注意到当前的决策会受到之前决策的影响,也就是当前的时刻我们并不清楚

            考虑增加一维?那就是暴力解法了

            那怎么处理这个时刻的问题呢?

            我们转化一下刚才的问题,

            -

            “当前的决策会受到之前决策的影响”

            +

            "当前的决策会受到之前决策的影响"

            换句话说,就是当前的决策会影响之后的决策

            也就是,现在的时刻会影响之后转移时的物品价值

            考虑每次决策时,就把之后的代价先算进去

            -

            设 $w_{i,j}$ 表示区间 $[i,j]$ 对之后决策造成的影响(代价),不难发现

            -

            显然可以前缀和优化

            -

            当然最重要的是,我们可以很容易地推出转移方程了

            -

            其中 $S_k = \sum_{i=1}^{k}v_k$

            -

            时间复杂度 $O(n^2)$

            -

            答案就是 $\sum y_i -\min\left\{f_{0,1,n},f_{1,1,n}\right\}$

            + f_{1,i,j-1}+(x_j-x_{j-1})\times(S_{i-1}+S_n-S-{j-1})\right\} +\] 其中 \(S_k = +\sum_{i=1}^{k}v_k\)

            +

            时间复杂度 \(O(n^2)\)

            +

            答案就是 \(\sum y_i +-\min\left\{f_{0,1,n},f_{1,1,n}\right\}\)

            代码:

            #include <iostream>
             #include <string>
            @@ -552,7 +585,8 @@ 

            return 0; }

            参考文献

            -

            [1] https://www.luogu.com.cn/blog/Bartholomew/solution-p2466

            +

            [1] https://www.luogu.com.cn/blog/Bartholomew/solution-p2466

            @@ -918,7 +952,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/21/luo-gu-p5440-xr-2-qi-ji-ti-jie/index.html b/2022/06/21/luo-gu-p5440-xr-2-qi-ji-ti-jie/index.html index 5f65fd3ced..b3f71eda44 100644 --- a/2022/06/21/luo-gu-p5440-xr-2-qi-ji-ti-jie/index.html +++ b/2022/06/21/luo-gu-p5440-xr-2-qi-ji-ti-jie/index.html @@ -468,20 +468,27 @@

            洛谷P5440 【XR-2】奇迹 题
            -

            洛谷P5440 【XR-2】奇迹 题解

            题目链接:P5440 【XR-2】奇迹

            +

            洛谷P5440 【XR-2】奇迹 题解

            +

            题目链接:P5440 +【XR-2】奇迹

            题意

            -

            我们称一个日期为一个八位数,第 1~4 位构成年,第 5~6 位构成月,第 7~8 位构成日,不足位数用 0 补足。同时,要求日期所代表的这一天真实存在,且年的范围为 1~9999。

            +

            我们称一个日期为一个八位数,第 1~4 位构成年,第 5~6 +位构成月,第 7~8 位构成日,不足位数用 0 +补足。同时,要求日期所代表的这一天真实存在,且年的范围为 1~9999。

            出现奇迹的日期都存在相同的特点:由“日”组成的两位数,由“月+日”组成的四位数,由“年+月+日”组成的八位数均为质数。但并不是所有存在这样特点的日期都一定会出现奇迹。

            现在,你得到了一个可能会出现奇迹的日期,然而不幸的是这个日期却是残缺的,八位中可能有若干位无法确定。你需要知道这个日期有多少种可能,这样你才能做好充足的准备去迎接奇迹的到来。

            -

            对 $100\%$ 的数据保证 $1 \le T \le 10$。

            +

            \(100\%\) 的数据保证 \(1 \le T \le 10\)

            一个朴素的解法:

            预处理所有合法日期,

            然后对于每个询问暴力枚举所有合法日期

            常数巨大,但是写起来不容易错

            其实我也想了很多剪枝方法不过懒得写

            -

            代码是学的这篇题解 ,感觉十分简洁

            +

            代码是学的这篇题解 +,感觉十分简洁

            代码:

            #include <iostream>
             #include <string>
            @@ -897,7 +904,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/25/luo-gu-p3426-poi2005-sza-template-ti-jie/index.html b/2022/06/25/luo-gu-p3426-poi2005-sza-template-ti-jie/index.html index b12e243b8f..c6ba118e2d 100644 --- a/2022/06/25/luo-gu-p3426-poi2005-sza-template-ti-jie/index.html +++ b/2022/06/25/luo-gu-p3426-poi2005-sza-template-ti-jie/index.html @@ -476,13 +476,20 @@

            洛谷P3426 [POI2005]SZA-Templat
            -

            洛谷P3426 [POI2005]SZA-Template 题解

            题目链接:P3426 [POI2005]SZA-Template

            +

            洛谷P3426 +[POI2005]SZA-Template 题解

            +

            题目链接:P3426 +[POI2005]SZA-Template

            题意:你打算在纸上印一串字母。

            为了完成这项工作,你决定刻一个印章。印章每使用一次,就会将印章上的所有字母印到纸上。

            -

            同一个位置的相同字符可以印多次。例如:用 aba 这个印章可以完成印制 ababa 的工作(中间的 a 被印了两次)。但是,因为印上去的东西不能被抹掉,在同一位置上印不同字符是不允许的。例如:用 aba 这个印章不可以完成印制 abcba 的工作。

            +

            同一个位置的相同字符可以印多次。例如:用 aba +这个印章可以完成印制 ababa 的工作(中间的 a +被印了两次)。但是,因为印上去的东西不能被抹掉,在同一位置上印不同字符是不允许的。例如:用 +aba 这个印章不可以完成印制 abcba 的工作。

            因为刻印章是一个不太容易的工作,你希望印章的字符串长度尽可能小。

            -

            输入一个长度不超过 $5 \times 10^5$ 的非空字符串(只包含小写字母),代表要在纸上印的字符。

            +

            输入一个长度不超过 \(5 \times 10^5\) +的非空字符串(只包含小写字母),代表要在纸上印的字符。

            因为印章可以随便印,也就是无所谓什么顺序和数量

            所以我们可以考虑什么样的情况可以继续

            @@ -497,22 +504,33 @@

            \(s\) 和 \(0 \le r < |s|\)

            +

            \(s\) 长度为 \(r\) 的前缀和长度为 \(r\) 的后缀相等,就称 \(s\) 长度为 \(r\) 的前缀是 \(s\) 的 border。

            摘自oi-wiki

            考虑先求出border,然后用dp来推

            -

            设 $f_i$ 表示印刷 $s$ 的第 $i$ 个前缀的最小印章长度

            -

            上界为 $f_i=i$

            -

            这里 $f_i$ 在一定条件下是可以从 $\text{fail}_i$ 转移的

            -

            第一个看上去复杂,

            +f_{\text{fail}_i},& \exists \, j \ge i-\text{fail}_i,f_j = +f_{\text{fail}_i}, +\\\\i,&\text{default.} +\end{cases} +\] 第一个看上去复杂,

            其实就是 ababbababbaba 这样border有重叠的情况

            用个桶维护即可,然后转移一下就好了

            -

            时间复杂度 $O(n)$

            +

            时间复杂度 \(O(n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -922,7 +940,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/26/luo-gu-p2939-usaco09feb-revamping-trails-g-ti-jie/index.html b/2022/06/26/luo-gu-p2939-usaco09feb-revamping-trails-g-ti-jie/index.html index cf14717cdb..50a54d5190 100644 --- a/2022/06/26/luo-gu-p2939-usaco09feb-revamping-trails-g-ti-jie/index.html +++ b/2022/06/26/luo-gu-p2939-usaco09feb-revamping-trails-g-ti-jie/index.html @@ -476,25 +476,46 @@

            洛谷P2939 [USACO09FEB]Revampin
            -

            洛谷P2939 [USACO09FEB]Revamping Trails G 题解

            题目链接:P2939 [USACO09FEB]Revamping Trails G

            +

            洛谷P2939 +[USACO09FEB]Revamping Trails G 题解

            +

            题目链接:P2939 +[USACO09FEB]Revamping Trails G

            题意

            -

            约翰一共有 $N$ 个牧场.由 $M$ 条布满尘埃的小径连接。小径可以双向通行。每天早上约翰从牧场 $1$ 出发到牧场 $N$ 去给奶牛检查身体。

            -

            通过每条小径都需要消耗一定的时间。约翰打算升级其中 $K$ 条小径,使之成为高速公路。在高速公路上的通行几乎是瞬间完成的,所以高速公路的通行时间为 $0$。

            -

            请帮助约翰决定对哪些小径进行升级,使他每天从 $1$ 号牧场到第 $N$ 号牧场所花的时间最短。

            -

            $1 \le n \le 10^4,~1\le m \le 5 \times 10^4 ,~ 1\le k \le 20$

            +

            约翰一共有 \(N\) 个牧场.由 \(M\) +条布满尘埃的小径连接。小径可以双向通行。每天早上约翰从牧场 \(1\) 出发到牧场 \(N\) 去给奶牛检查身体。

            +

            通过每条小径都需要消耗一定的时间。约翰打算升级其中 \(K\) +条小径,使之成为高速公路。在高速公路上的通行几乎是瞬间完成的,所以高速公路的通行时间为 +\(0\)

            +

            请帮助约翰决定对哪些小径进行升级,使他每天从 \(1\) 号牧场到第 \(N\) 号牧场所花的时间最短。

            +

            \(1 \le n \le 10^4,~1\le m \le 5 \times +10^4 ,~ 1\le k \le 20\)

            考虑建分层图跑最短路

            -

            首先建 $k+1$ 个同样的图

            -

            第 $i$ 层的结点 $u_i$ 向 第 $i+1$ 层的 $v_i$ 连边

            -

            表示已经用了 $i$ 次修改,现在是第 $i+1$ 次修改,修改的是 $(u,v)$ 这条边

            +

            首先建 \(k+1\) 个同样的图

            +

            \(i\) 层的结点 \(u_i\) 向 第 \(i+1\) 层的 \(v_i\) 连边

            +

            表示已经用了 \(i\) 次修改,现在是第 +\(i+1\) 次修改,修改的是 \((u,v)\) 这条边

            然后跑一个单源最短路就好了

            -

            注意存边的数组一般要开 $M \times (K+1) \times 4$

            -

            然后有点时候可能会出现走了不到 $k$ 条边就到了的情况

            -

            所以要把每个 $in$ 和 $(i+1)n$ 连边

            -

            最后答案就是 $d_{kn+n}$

            -

            注意 $d$ 数组应该要开long long

            -

            时间复杂度 $O(kn\log km)$

            +

            注意存边的数组一般要开 \(M \times (K+1) +\times 4\)

            +

            然后有点时候可能会出现走了不到 \(k\) +条边就到了的情况

            +

            所以要把每个 \(in\)\((i+1)n\) 连边

            +

            最后答案就是 \(d_{kn+n}\)

            +

            注意 \(d\) 数组应该要开long long

            +

            时间复杂度 \(O(kn\log km)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -942,7 +963,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/06/26/luo-gu-p4683-ioi2008-type-printer-ti-jie/index.html b/2022/06/26/luo-gu-p4683-ioi2008-type-printer-ti-jie/index.html index 47731a7066..ca1f59af38 100644 --- a/2022/06/26/luo-gu-p4683-ioi2008-type-printer-ti-jie/index.html +++ b/2022/06/26/luo-gu-p4683-ioi2008-type-printer-ti-jie/index.html @@ -472,24 +472,31 @@

            洛谷P4683 [IOI2008] Type Print
            -

            洛谷P4683 [IOI2008] Type Printer 题解

            题目链接:P4683 [IOI2008] Type Printer

            +

            洛谷P4683 [IOI2008] Type +Printer 题解

            +

            题目链接:P4683 +[IOI2008] Type Printer

            题意

            -

            你需要利用一台可移动的打印机打印出$N$个单词。这种可移动式打印机是一种老式打印机,它需要你将一些小的金属块(每个包含一个字母)放到打印机上以组成单词。然后将这些小金属块压在一张纸上以打印出这个词。这种打印机允许你进行下列操作:

            +

            你需要利用一台可移动的打印机打印出\(N\)个单词。这种可移动式打印机是一种老式打印机,它需要你将一些小的金属块(每个包含一个字母)放到打印机上以组成单词。然后将这些小金属块压在一张纸上以打印出这个词。这种打印机允许你进行下列操作:

              -
            • 在打印机当前词的末端(尾部)添加一个字母;
            • +
            • 在打印机当前词的末端(尾部)添加一个字母;
            • 在打印机当前词的尾部删去一个字母(将打印机当前词的最后一个字母删去)。仅当打印机当前至少有一个字母时才允许进行该操作;
            • -
            • 将打印机上的当前词打印出来。
              初始时打印机为空,或者说它不含任何带字母的金属块。打印结束时,允许有部分字母留在打印机内。同时也允许你按照任意的次序打印单词。
            • +
            • 将打印机上的当前词打印出来。 +初始时打印机为空,或者说它不含任何带字母的金属块。打印结束时,允许有部分字母留在打印机内。同时也允许你按照任意的次序打印单词。

            由于每一个操作都需要一定时间,所以需要你尽可能减少所需操作的总数目(将操作的总数最小化)。

            -

            你需要编写一个程序,给定所要打印的$N$个单词,找出以任意次序打印所有单词所需操作的最小数目,并输出一种这样的操作序列。

            -

            对于所有数据,$1\leq N\leq25000$。

            +

            你需要编写一个程序,给定所要打印的\(N\)个单词,找出以任意次序打印所有单词所需操作的最小数目,并输出一种这样的操作序列。

            +

            对于所有数据,\(1\leq +N\leq25000\)

            显然可以用trie来做(trie上dfs)

            因为最后一个输出的单词不用删除

            所以我们考虑贪心地把最长的单词最后输出

            具体的做法就是把这个最长单词的路径标记,先跑别的最后跑它

            -

            时间复杂度 $O(n \Sigma)$

            +

            时间复杂度 \(O(n \Sigma)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -961,7 +968,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/02/luo-gu-p3065-usaco12dec-first-g-ti-jie/index.html b/2022/07/02/luo-gu-p3065-usaco12dec-first-g-ti-jie/index.html index 65643c6afc..7faa9b85fd 100644 --- a/2022/07/02/luo-gu-p3065-usaco12dec-first-g-ti-jie/index.html +++ b/2022/07/02/luo-gu-p3065-usaco12dec-first-g-ti-jie/index.html @@ -476,23 +476,31 @@

            洛谷P3065 [USACO12DEC]First! G
            -

            洛谷P3065 [USACO12DEC]First! G 题解

            题目链接:P3065 [USACO12DEC]First! G

            +

            洛谷P3065 [USACO12DEC]First! G +题解

            +

            题目链接:P3065 +[USACO12DEC]First! G

            题意

            Bessie一直在研究字符串。她发现,通过改变字母表的顺序,她可以按改变后的字母表来排列字符串(字典序大小排列)。

            例如,Bessie发现,对于字符串串“omm”,“moo”,“mom”和“ommnom”,她可以使用标准字母表使“mom”排在第一个(即字典序最小),她也可以使用字母表“abcdefghijklonmpqrstuvwxyz”使得“omm”排在第一个。然而,Bessie想不出任何方法(改变字母表顺序)使得“moo”或“ommnom”排在第一个。

            接下来让我们通过重新排列字母表的顺序来计算输入中有哪些字符串可以排在第一个(即字典序最小),从而帮助Bessie。

            -

            要计算字符串X和字符串Y按照重新排列过的字母表顺序来排列的顺序,先找到它们第一个不同的字母X[i]与Y[i],按重排后的字母表顺序比较,若X[i]比Y[i]先,则X的字典序比Y小,即X排在Y前;若没有不同的字母,则比较X与Y长度,若X比Y短,则X的字典序比Y小,即X排在Y前。

            -
            +

            要计算字符串X和字符串Y按照重新排列过的字母表顺序来排列的顺序,先找到它们第一个不同的字母X[i]与Y[i],按重排后的字母表顺序比较,若X[i]比Y[i]先,则X的字典序比Y小,即X排在Y前;若没有不同的字母,则比较X与Y长度,若X比Y短,则X的字典序比Y小,即X排在Y前。 +\[ +\sum \left|S_i\right| \le 3 \times 10^5 +\]

            +

            显然trie树存字符串

            对于每个字符串,我们都假设它可以成为最小字典序的字符

            然后依次遍历它的每个字符,尽可能让它们成为最小的

            -

            具体的,对于字符 $u$ ,如果我们想让它比 $v$ 字典序小,

            -

            我们可以把 $u$ 向 $v$ 连一条有向边

            +

            具体的,对于字符 \(u\) +,如果我们想让它比 \(v\) 字典序小,

            +

            我们可以把 \(u\)\(v\) 连一条有向边

            显然无解的情况会成环,这个可以用topo来判

            值得注意的是,如果一个串的子串也在输入数据中存在,那么它一定无解

            -

            时间复杂度 $O(26 \times \sum |S_i|)$

            +

            时间复杂度 \(O(26 \times \sum +|S_i|)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -954,7 +962,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/02/luo-gu-p5194-usaco05dec-scales-s-ti-jie/index.html b/2022/07/02/luo-gu-p5194-usaco05dec-scales-s-ti-jie/index.html index ea66b20834..e37fcb7070 100644 --- a/2022/07/02/luo-gu-p5194-usaco05dec-scales-s-ti-jie/index.html +++ b/2022/07/02/luo-gu-p5194-usaco05dec-scales-s-ti-jie/index.html @@ -468,13 +468,24 @@

            洛谷P5194 [USACO05DEC]Scales S
            -

            洛谷P5194 [USACO05DEC]Scales S 题解

            题目链接:P5194 [USACO05DEC]Scales S

            +

            洛谷P5194 [USACO05DEC]Scales +S 题解

            +

            题目链接:P5194 +[USACO05DEC]Scales S

            题意

            -

            约翰有一架用来称牛的体重的天平。与之配套的是 $N$ ( $1 \leq N \leq 1000$ )个已知质量的砝码(所有砝码质量的数值都在32位带符号整数范围内)。

            +

            约翰有一架用来称牛的体重的天平。与之配套的是 \(N\) ( \(1 \leq N +\leq 1000\) +)个已知质量的砝码(所有砝码质量的数值都在32位带符号整数范围内)。

            每次称牛时,他都把某头奶牛安置在天平的某一边,然后往天平另一边加砝码,直到天平平衡,于是此时砝码的总质量就是牛的质量(约翰不能把砝码放到奶牛的那边,因为奶牛不喜欢称体重,每当约翰把砝码放到她的蹄子底下,她就会尝试把砝码踢到约翰脸上)。

            -

            天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于 $C$ ( $1 \leq C \leq 2^{30}$ )时,天平就会被损坏。砝码按照它们质量的大小被排成一行。并且,这一行中从第3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。

            -

            约翰想知道,用他所拥有的这些砝码以及这架天平,能称出的质量最大是多少。由于天平的最大承重能力为 $C$ ,他不能把所有砝码都放到天平上。

            +

            天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于 \(C\) ( \(1 \leq C +\leq 2^{30}\) +)时,天平就会被损坏。砝码按照它们质量的大小被排成一行。并且,这一行中从第3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。

            +

            约翰想知道,用他所拥有的这些砝码以及这架天平,能称出的质量最大是多少。由于天平的最大承重能力为 +\(C\) +,他不能把所有砝码都放到天平上。

            现在约翰告诉你每个砝码的质量,以及天平能承受的最大质量,你的任务是选出一些砝码,使它们的质量和在不压坏天平的前提下是所有组合中最大的。

            显然暴搜+剪枝

            @@ -891,7 +902,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/03/luo-gu-p4047-jsoi2010-bu-luo-hua-fen-ti-jie/index.html b/2022/07/03/luo-gu-p4047-jsoi2010-bu-luo-hua-fen-ti-jie/index.html index ec30c5ee7b..227f299874 100644 --- a/2022/07/03/luo-gu-p4047-jsoi2010-bu-luo-hua-fen-ti-jie/index.html +++ b/2022/07/03/luo-gu-p4047-jsoi2010-bu-luo-hua-fen-ti-jie/index.html @@ -472,40 +472,58 @@

            洛谷P4047 [JSOI2010]部落划
            -

            洛谷P4047 [JSOI2010]部落划分 题解

            题目链接:P4047 [JSOI2010]部落划分

            +

            洛谷P4047 [JSOI2010]部落划分 +题解

            +

            题目链接:P4047 +[JSOI2010]部落划分

            题意

            聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落之间则经常发生争斗。只是,这一切都成为谜团了——聪聪根本就不知道部落究竟是如何分布的。

            -

            不过好消息是,聪聪得到了一份荒岛的地图。地图上标注了 $n$ 个野人居住的地点(可以看作是平面上的坐标)。我们知道,同一个部落的野人总是生活在附近。我们把两个部落的距离,定义为部落中距离最近的那两个居住点的距离。聪聪还获得了一个有意义的信息——这些野人总共被分为了 $k$ 个部落!这真是个好消息。聪聪希望从这些信息里挖掘出所有部落的详细信息。他正在尝试这样一种算法:

            +

            不过好消息是,聪聪得到了一份荒岛的地图。地图上标注了 \(n\) +个野人居住的地点(可以看作是平面上的坐标)。我们知道,同一个部落的野人总是生活在附近。我们把两个部落的距离,定义为部落中距离最近的那两个居住点的距离。聪聪还获得了一个有意义的信息——这些野人总共被分为了 +\(k\) +个部落!这真是个好消息。聪聪希望从这些信息里挖掘出所有部落的详细信息。他正在尝试这样一种算法:

            对于任意一种部落划分的方法,都能够求出两个部落之间的距离,聪聪希望求出一种部落划分的方法,使靠得最近的两个部落尽可能远离。

            例如,下面的左图表示了一个好的划分,而右图则不是。请你编程帮助聪聪解决这个难题。

            -

            -

            对于 $100\%$ 的数据,保证 $2 \leq k \leq n \leq 10^3$,$0 \leq x, y \leq 10^4$。

            +

            +

            对于 \(100\%\) 的数据,保证 \(2 \leq k \leq n \leq 10^3\)\(0 \leq x, y \leq 10^4\)

            最小距离最大,一眼二分

            但是这里给出一个kruskal的解法

            -

            我们先把 $O(n^2)$ 条边建出来

            +

            我们先把 \(O(n^2)\) 条边建出来

            然后找到最小生成树

            -

            贪心地把最小生成树划分为 $k$ 个连通块

            +

            贪心地把最小生成树划分为 \(k\) +个连通块

            具体的,我们只要依次合并树上最小边

            -

            最后一共合并掉 $(n-1)-(k-1) = n-k$ 条边

            -

            所以答案就是第 $n-k+1$ 条边的边权

            +

            最后一共合并掉 \((n-1)-(k-1) = n-k\) +条边

            +

            所以答案就是第 \(n-k+1\) +条边的边权

            口糊一个正确性证明(可能不是很严谨)

            考虑反证法

            -

            设最小生成树上的一条边 $w_1(u,v)$ ,将其替换为 $w_2(u,v^{\prime})$ ($w_2>w_1$)

            +

            设最小生成树上的一条边 \(w_1(u,v)\) +,将其替换为 \(w_2(u,v^{\prime})\) +(\(w_2>w_1\)

              -
            • 如果 $w_1$ 会被合并而我们合并了 $w_2$ ,好像看不出什么变化

              -
            • -
            • 如果 $w_1$ 会被合并而我们没有合并 $w_2$ ,

              -

              那么我们一定合并了一条比 $w_1$ 大的边,而且是最小生成树上的边

              -

              因此答案会偏大

              -
            • -
            • 如果 $w_1$ 不会被合并而我们合并了 $w_2$ ,没有这种情况。

              -
            • -
            • 如果 $w_1$ 不会被合并而我们没有合并 $w_2$ ,没影响。

              -
            • +
            • 如果 \(w_1\) +会被合并而我们合并了 \(w_2\) +,好像看不出什么变化

            • +
            • 如果 \(w_1\) +会被合并而我们没有合并 \(w_2\)

              +

              那么我们一定合并了一条比 \(w_1\) +大的边,而且是最小生成树上的边

              +

              因此答案会偏大

            • +
            • 如果 \(w_1\) +不会被合并而我们合并了 \(w_2\) +,没有这种情况。

            • +
            • 如果 \(w_1\) +不会被合并而我们没有合并 \(w_2\) +,没影响。

            -

            时间复杂度 $O(n^2 \log n)$

            +

            时间复杂度 \(O(n^2 \log n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -927,7 +945,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/03/luo-gu-p5018-noip2018-pu-ji-zu-dui-cheng-er-cha-shu-ti-jie/index.html b/2022/07/03/luo-gu-p5018-noip2018-pu-ji-zu-dui-cheng-er-cha-shu-ti-jie/index.html index 0451060135..b41bb890c9 100644 --- a/2022/07/03/luo-gu-p5018-noip2018-pu-ji-zu-dui-cheng-er-cha-shu-ti-jie/index.html +++ b/2022/07/03/luo-gu-p5018-noip2018-pu-ji-zu-dui-cheng-er-cha-shu-ti-jie/index.html @@ -472,25 +472,46 @@

            洛谷P5018 [NOIP2018 普及组]
            -

            洛谷P5018 [NOIP2018 普及组] 对称二叉树 题解

            题目链接:P5018 [NOIP2018 普及组] 对称二叉树

            +

            洛谷P5018 [NOIP2018 +普及组] 对称二叉树 题解

            +

            题目链接:P5018 +[NOIP2018 普及组] 对称二叉树

            题意

            -

            一棵有点权的有根树如果满足以下条件,则被轩轩称为对称二叉树:

            -
              -
            1. 二叉树;
            2. -
            3. 将这棵树所有节点的左右子树交换,新树和原树对应位置的结构相同且点权相等。
            4. +

              一棵有点权的有根树如果满足以下条件,则被轩轩称为对称二叉树:

              +
                +
              1. 二叉树;
              2. +
              3. 将这棵树所有节点的左右子树交换,新树和原树对应位置的结构相同且点权相等。
              -

              下图中节点内的数字为权值,节点外的 $id$ 表示节点编号。

              -

              -

              现在给出一棵二叉树,希望你找出它的一棵子树,该子树为对称二叉树,且节点数 最多。请输出这棵子树的节点数。

              -

              注意:只有树根的树也是对称二叉树。本题中约定,以节点 $T$ 为子树根的一棵“子 树”指的是:节点$T$ 和它的全部后代节点构成的二叉树。

              -

              【数据规模与约定】
              共 $25$ 个测试点。
              $v_i ≤ 1000$。
              测试点 $1 \sim 3, n ≤ 10$,保证根结点的左子树的所有节点都没有右孩子,根结点的右 子树的所有节点都没有左孩子。
              测试点 $4 \sim 8, n ≤ 10$。
              测试点 $9 \sim 12, n ≤ 10^5$,保证输入是一棵“满二叉树” 。
              测试点 $13 \sim 16, n ≤ 10^5$,保证输入是一棵“完全二叉树”。
              测试点 $17 \sim 20, n ≤ 10^5$,保证输入的树的点权均为 $1$。
              测试点 $21 \sim 25, n ≤ 10^6$。

              +

              下图中节点内的数字为权值,节点外的 \(id\) 表示节点编号。

              +

              +

              现在给出一棵二叉树,希望你找出它的一棵子树,该子树为对称二叉树,且节点数 +最多。请输出这棵子树的节点数。

              +

              注意:只有树根的树也是对称二叉树。本题中约定,以节点 \(T\) 为子树根的一棵“子 树”指的是:节点\(T\) 和它的全部后代节点构成的二叉树。

              +

              【数据规模与约定】 共 \(25\) +个测试点。 \(v_i ≤ 1000\)。 测试点 +\(1 \sim 3, n ≤ +10\),保证根结点的左子树的所有节点都没有右孩子,根结点的右 +子树的所有节点都没有左孩子。 测试点 \(4 \sim +8, n ≤ 10\)
              +测试点 \(9 \sim 12, n ≤ +10^5\),保证输入是一棵“满二叉树” 。
              +测试点 \(13 \sim 16, n ≤ +10^5\),保证输入是一棵“完全二叉树”。
              +测试点 \(17 \sim 20, n ≤ +10^5\),保证输入的树的点权均为 \(1\)
              +测试点 \(21 \sim 25, n ≤ 10^6\)

            对于每个点直接暴力dfs它的左右子树判断即可

            注意对称的方向不要写错了

            -

            时间复杂度 $O(n \log n)$

            +

            时间复杂度 \(O(n \log n)\)

            为什么链不会被卡呢,因为中间判断的时候已经相当于剪枝掉了

            -

            只有完全二叉树才会到 $O(n \log n)$

            +

            只有完全二叉树才会到 \(O(n \log +n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -913,7 +934,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/03/luo-gu-p5536-xr-3-he-xin-cheng-shi-ti-jie/index.html b/2022/07/03/luo-gu-p5536-xr-3-he-xin-cheng-shi-ti-jie/index.html index 04295488c0..2157150e43 100644 --- a/2022/07/03/luo-gu-p5536-xr-3-he-xin-cheng-shi-ti-jie/index.html +++ b/2022/07/03/luo-gu-p5536-xr-3-he-xin-cheng-shi-ti-jie/index.html @@ -472,28 +472,43 @@

            洛谷P5536 【XR-3】核心城
            -

            洛谷P5536 【XR-3】核心城市 题解

            题目链接:P5536 【XR-3】核心城市

            +

            洛谷P5536 【XR-3】核心城市 +题解

            +

            题目链接:P5536 +【XR-3】核心城市

            题意

            -

            X 国有 $n$ 座城市,$n - 1$ 条长度为 $1$ 的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和道路形成了一棵树。

            -

            X 国国王决定将 $k$ 座城市钦定为 X 国的核心城市,这 $k$ 座城市需满足以下两个条件:

            -
              -
            1. 这 $k$ 座城市可以通过道路,在不经过其他城市的情况下两两相互到达。
            2. -
            3. 定义某个非核心城市与这 $k$ 座核心城市的距离为,这座城市与 $k$ 座核心城市的距离的最小值。那么所有非核心城市中,与核心城市的距离最大的城市,其与核心城市的距离最小。你需要求出这个最小值。
            4. +

              X 国有 \(n\) 座城市,\(n - 1\) 条长度为 \(1\) +的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和道路形成了一棵树。

              +

              X 国国王决定将 \(k\) 座城市钦定为 X +国的核心城市,这 \(k\) +座城市需满足以下两个条件:

              +
                +
              1. \(k\) +座城市可以通过道路,在不经过其他城市的情况下两两相互到达。
              2. +
              3. 定义某个非核心城市与这 \(k\) +座核心城市的距离为,这座城市与 \(k\) +座核心城市的距离的最小值。那么所有非核心城市中,与核心城市的距离最大的城市,其与核心城市的距离最小。你需要求出这个最小值。

              数据范围:

                -
              • $1 \le k < n \le 10 ^ 5$。
              • -
              • $1 \le u,v \le n, u \ne v$,保证城市与道路形成一棵树。
              • +
              • \(1 \le k < n \le 10 ^ +5\)
              • +
              • \(1 \le u,v \le n, u \ne +v\),保证城市与道路形成一棵树。

            不要被树这个东西坑了,其实和树关系不大

            我们感性地分析一下,这些核心城市一定是在比较中间的位置

            -

            然后这些核心城市到所有边缘结点的距离是相差不超过 $1$ 的

            +

            然后这些核心城市到所有边缘结点的距离是相差不超过 \(1\)

            那么我们是不是可以把这个树从外面一圈开始消

            -

            然后一个个点的删,最后剩下 $k$ 个点,就是核心城市

            +

            然后一个个点的删,最后剩下 \(k\) +个点,就是核心城市

            这是什么?拓扑排序对吧

            -

            时间复杂度 $O(n)$

            +

            时间复杂度 \(O(n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -921,7 +936,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/05/cf1036c-classy-numbers-ti-jie/index.html b/2022/07/05/cf1036c-classy-numbers-ti-jie/index.html index dd179e49a3..12653e7ffa 100644 --- a/2022/07/05/cf1036c-classy-numbers-ti-jie/index.html +++ b/2022/07/05/cf1036c-classy-numbers-ti-jie/index.html @@ -472,18 +472,33 @@

            CF1036C Classy Numbers 题解
            -

            CF1036C Classy Numbers 题解

            题目链接:CF1036C Classy Numbers

            +

            CF1036C Classy Numbers 题解

            +

            题目链接:CF1036C +Classy Numbers

            -

            题意:定义一个数字是“好数”,当且仅当它的十进制表示下有不超过$3$个数字$1 \sim 9$

            -

            举个例子:$4,200000,10203$是“好数”,然而$4231,102306,7277420000$不是

            -

            给定$[l,r]$,问有多少个$x$使得$l \le x \le r$,且$x$是“好数”

            -

            一共有$T(1 \le T \le 10^{4})$组数据,对于每次的询问,输出一行一个整数表示答案

            -

            $1 \le l_i \le r_i \le 10^{18}$

            +

            题意:定义一个数字是“好数”,当且仅当它的十进制表示下有不超过\(3\)个数字\(1 \sim +9\)

            +

            举个例子:\(4,200000,10203\)是“好数”,然而\(4231,102306,7277420000\)不是

            +

            给定\([l,r]\),问有多少个\(x\)使得\(l \le x +\le r\),且\(x\)是“好数”

            +

            一共有\(T(1 \le T \le +10^{4})\)组数据,对于每次的询问,输出一行一个整数表示答案

            +

            \(1 \le l_i \le r_i \le +10^{18}\)

            -

            一般数位dp可以用来解决 $\sum _{i=l}^{r} f(i)$ 的问题,

            -

            其中 $f(i)$ 为与 $i$ 的数位有关的某个函数或判定式

            +

            一般数位dp可以用来解决 \(\sum _{i=l}^{r} +f(i)\) 的问题,

            +

            其中 \(f(i)\) 为与 \(i\) 的数位有关的某个函数或判定式

            这题稍微变形了一下,那我们就理所当然地记录一下出现数字的情况

            -

            设 $f_{i,j}$ 表示满 $i$ 位数,有 $j$ 个非 $0$ 位

            +

            \(f_{i,j}\) 表示满 \(i\) 位数,有 \(j\) 个非 \(0\)

            采用记忆化搜索,详见代码

            #include <iostream>
             #include <string>
            @@ -541,7 +556,8 @@ 

            return 0; }

            参考文献

            -

            [1] https://www.luogu.com.cn/blog/rated/solution-cf1036c

            +

            [1] https://www.luogu.com.cn/blog/rated/solution-cf1036c

            @@ -907,7 +923,7 @@

              站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/05/luo-gu-p1836-shu-ye-ma-ti-jie/index.html b/2022/07/05/luo-gu-p1836-shu-ye-ma-ti-jie/index.html index 0ad4530b54..45048a3fe3 100644 --- a/2022/07/05/luo-gu-p1836-shu-ye-ma-ti-jie/index.html +++ b/2022/07/05/luo-gu-p1836-shu-ye-ma-ti-jie/index.html @@ -472,15 +472,22 @@

            洛谷P1836 数页码 题解

            -

            洛谷P1836 数页码 题解

            题目链接:P1836 数页码

            +

            洛谷P1836 数页码 题解

            +

            题目链接:P1836 +数页码

            题意

            -

            一本书的页码是从 $1\sim n$ 编号的连续整数:$1,2,3,\cdots,n$。请你求出全部页码中所有单个数字的和,例如第 $123$ 页,它的和就是 $1+2+3=6$。

            -

            $1\le n\le 10^9$

            +

            一本书的页码是从 \(1\sim n\) +编号的连续整数:\(1,2,3,\cdots,n\)。请你求出全部页码中所有单个数字的和,例如第 +\(123\) 页,它的和就是 \(1+2+3=6\)

            +

            \(1\le n\le 10^9\)

            容易发现这个东西可以转化为

            -

            $1 \sim n$ 中每个数字有多少个

            -

            考虑数位dp,详见link

            +

            \(1 \sim n\) 中每个数字有多少个

            +

            考虑数位dp,详见link

            然后把每个数字乘一乘就好了

            代码:

            #include <iostream>
            @@ -908,7 +915,7 @@ 

              站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/05/luo-gu-p4127-ahoi2009-tong-lei-fen-bu-ti-jie/index.html b/2022/07/05/luo-gu-p4127-ahoi2009-tong-lei-fen-bu-ti-jie/index.html index 2d225e9f2d..1f161db68c 100644 --- a/2022/07/05/luo-gu-p4127-ahoi2009-tong-lei-fen-bu-ti-jie/index.html +++ b/2022/07/05/luo-gu-p4127-ahoi2009-tong-lei-fen-bu-ti-jie/index.html @@ -472,27 +472,47 @@

            洛谷P4127 [AHOI2009]同类分
            -

            洛谷P4127 [AHOI2009]同类分布 题解

            题目链接:P4127 [AHOI2009]同类分布

            +

            洛谷P4127 [AHOI2009]同类分布 +题解

            +

            题目链接:P4127 +[AHOI2009]同类分布

            题意

            -

            给出两个数 $a,b$ ,求出 $[a,b]$ 中各位数字之和能整除原数的数的个数。

            -

            对于所有的数据, $1 ≤ a ≤ b ≤ 10^{18}$。

            +

            给出两个数 \(a,b\) ,求出 \([a,b]\) +中各位数字之和能整除原数的数的个数。

            +

            对于所有的数据, \(1 ≤ a ≤ b ≤ +10^{18}\)

            明天就期末考了非但不复习还在刷题 qwqqwqwq

            因为我们不能确切知道每个数的数位和

            -

            但是我们知道它们一定在 $[1,9 \times l]$ 的范围内( $l$ 为最长的位数)

            +

            但是我们知道它们一定在 \([1,9 \times +l]\) 的范围内( \(l\) +为最长的位数)

            我们枚举所有的数位和并计算每个数位和对应的答案

            -

            把这些答案加起来就是 $[0,x]$ 的答案

            -

            $[a,b]$ 的答案可以拆分为两个询问 $[0,b]$ 和 $[0,a-1]$

            -

            而这里 $a,b \le 10^{18}$ ,直接压入状态显然不可接受

            +

            把这些答案加起来就是 \([0,x]\) +的答案

            +

            \([a,b]\) 的答案可以拆分为两个询问 +\([0,b]\)\([0,a-1]\)

            +

            而这里 \(a,b \le 10^{18}\) +,直接压入状态显然不可接受

            于是我们可以考虑记录模当前枚举的数位和意义下的数

            -

            方便起见,我们称当前枚举的数位和为 $m$

            -

            设 $f_{i,j,k}$ 表示只考虑前 $i$ 位数(包括前导零),前 $i$ 位的数位和为 $j$ ,当前数模 $m$ 为 $k$ 时的答案

            -

            不难发现

            -

            其中 $t$ 为第 $i+1$ 位的高位限制

            +

            方便起见,我们称当前枚举的数位和为 \(m\)

            +

            \(f_{i,j,k}\) 表示只考虑前 \(i\) 位数(包括前导零),前 \(i\) 位的数位和为 \(j\) ,当前数模 \(m\)\(k\) 时的答案

            +

            不难发现 \[ +f_{i,j,k} = \sum_{0 \le d \le t} f_{i+1,j+d,\left(k\times 10 + d \, +\bmod \, m\right)} +\] 其中 \(t\) 为第 \(i+1\) 位的高位限制

            用记搜写法能简洁不少

            -

            时间复杂度 $O(9^3\times l^4)$

            +

            时间复杂度 \(O(9^3\times l^4)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -907,7 +927,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/05/sp3928-mdigits-counting-digits-ti-jie/index.html b/2022/07/05/sp3928-mdigits-counting-digits-ti-jie/index.html index 876b36ee0a..4523e7250b 100644 --- a/2022/07/05/sp3928-mdigits-counting-digits-ti-jie/index.html +++ b/2022/07/05/sp3928-mdigits-counting-digits-ti-jie/index.html @@ -472,33 +472,73 @@

            SP3928 MDIGITS - Counting Digits
            -

            SP3928 MDIGITS - Counting Digits 题解

            题目链接:SP3928 MDIGITS - Counting Digits

            +

            SP3928 MDIGITS - Counting +Digits 题解

            +

            题目链接:SP3928 +MDIGITS - Counting Digits

            题意

            -

            给定两个整数 $a$ 和 $b$,求 $a$ 和 $b$ 之间的所有数字中 $0$ ~ $9$ 出现次数。

            -

            例如,$a$ = $1024$,$b$ = $1032$,则 $a$ 和 $b$ 之间共有 $9$ 个数如下:

            +

            给定两个整数 \(a\)\(b\),求 \(a\)\(b\) 之间的所有数字中 \(0\) ~ \(9\) 出现次数。

            +

            例如,\(a\) = \(1024\)\(b\) = \(1032\),则 \(a\)\(b\) 之间共有 \(9\) 个数如下:

            1024 1025 1026 1027 1028 1029 1030 1031 1032

            -

            其中 0 出现 $10$ 次,1 出现 $10$ 次,2 出现 $7$ 次,3 出现 $3$ 次等等……

            +

            其中 0 出现 \(10\) +次,1 出现 \(10\) +次,2 出现 \(7\) +次,3 出现 \(3\) +次等等……

            数位dp的基础题

            -

            设 $f_i$ 表示满 $i$ 位数字(包括前导零),每种数字的出现次数

            -

            其中 $f_{i-1}\times 10$ 是 $i-1$ 位及以下位的贡献

            -

            例如 $\tt{7000\sim7999,~8000\sim8999}$

            -

            显然 $\tt{000\sim999}$出现了 $10$ 次

            -

            而 $10^{i-1}$ 是第 $i$ 位的贡献,比如 $\tt{9000 \sim 9999}$ ,第 $4$ 位的 $\tt{9}$ 出现了 $10^3$ 次

            -

            然后我们再考虑怎么获得答案

            -

            首先 $[l,r]$ 可以拆分为 $[0,l-1],~[0,r]$ 两个询问(基本的容斥)

            -

            然后考虑一个数 $\tt{\overline{ABC}}$ ,不难发现,$\tt{0}$ 到 $\tt{\overline{A00}}$ 每个非最高位数都出现了 $\tt{A}$ $\times f_2$ 次

            -

            而最高位 $\tt{0\sim A-1}$ 都各出现了 $10^2$ 次

            -

            注:这里 $\tt 0$ 是前导零,所以其实不会算进去,这里只是为了方便分析

            -

            那么 $\tt{A}$ 呢?不难发现它出现了 $\tt{\overline{BC}+1}$ 次

            -

            对于 $\tt{B}$ ,同样的处理方式。

            +\\f_i = f_{i-1} \times 10 + 10^{i-1} +\] 其中 \(f_{i-1}\times 10\) 是 +\(i-1\) 位及以下位的贡献

            +

            例如 \(\tt{7000\sim7999,~8000\sim8999}\)

            +

            显然 \(\tt{000\sim999}\)出现了 \(10\)

            +

            \(10^{i-1}\) 是第 \(i\) 位的贡献,比如 \(\tt{9000 \sim 9999}\) ,第 \(4\) 位的 \(\tt{9}\) 出现了 \(10^3\)

            +

            然后我们再考虑怎么获得答案

            +

            首先 \([l,r]\) 可以拆分为 \([0,l-1],~[0,r]\) 两个询问(基本的容斥)

            +

            然后考虑一个数 \(\tt{\overline{ABC}}\) ,不难发现,\(\tt{0}\)\(\tt{\overline{A00}}\) +每个非最高位数都出现了 \(\tt{A}\) \(\times f_2\)

            +

            而最高位 \(\tt{0\sim A-1}\) +都各出现了 \(10^2\)

            +

            注:这里 \(\tt 0\) +是前导零,所以其实不会算进去,这里只是为了方便分析

            +

            那么 \(\tt{A}\) 呢?不难发现它出现了 +\(\tt{\overline{BC}+1}\)

            +

            对于 \(\tt{B}\) +,同样的处理方式。

            怎么一股机翻的味道

            然后我们就搞定这道题了

            -

            时间复杂度 $O(Qlb)$

            -

            其中 $l$ 表示最大位数, $b$ 表示进制,这题里为 $10$

            +

            时间复杂度 \(O(Qlb)\)

            +

            其中 \(l\) 表示最大位数, \(b\) 表示进制,这题里为 \(10\)

            代码:(非dfs写法)

            #include <iostream>
             #include <string>
            @@ -932,7 +972,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/08/luo-gu-p2822-noip2016-ti-gao-zu-zu-he-shu-wen-ti-ti-jie/index.html b/2022/07/08/luo-gu-p2822-noip2016-ti-gao-zu-zu-he-shu-wen-ti-ti-jie/index.html index 9ea73f83d0..88d6e0666f 100644 --- a/2022/07/08/luo-gu-p2822-noip2016-ti-gao-zu-zu-he-shu-wen-ti-ti-jie/index.html +++ b/2022/07/08/luo-gu-p2822-noip2016-ti-gao-zu-zu-he-shu-wen-ti-ti-jie/index.html @@ -468,23 +468,43 @@

            洛谷P2822 [NOIP2016 提高组]
            -

            洛谷P2822 [NOIP2016 提高组] 组合数问题 题解

            题目链接:P2822 [NOIP2016 提高组] 组合数问题

            +

            洛谷P2822 [NOIP2016 +提高组] 组合数问题 题解

            +

            题目链接:P2822 +[NOIP2016 提高组] 组合数问题

            题意

            -

            组合数 $\binom{n}{m}$ 表示的是从 $n$ 个物品中选出 $m$ 个物品的方案数。举个例子,从 $(1,2,3)$ 三个物品中选择两个物品可以有 $(1,2),(1,3),(2,3)$ 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 $\binom{n}{m}$ 的一般公式:

            -

            其中 $n!=1\times2\times\cdots\times n$;特别地,定义 $0!=1$。

            -

            小葱想知道如果给定 $n,m$ 和 $k$,对于所有的 $0\leq i\leq n,0\leq j\leq \min \left ( i, m \right )$ 有多少对 $(i,j)$ 满足 $k|\binom{i}{j}$。

            +

            组合数 \(\binom{n}{m}\) 表示的是从 +\(n\) 个物品中选出 \(m\) 个物品的方案数。举个例子,从 \((1,2,3)\) 三个物品中选择两个物品可以有 +\((1,2),(1,3),(2,3)\) +这三种选择方法。根据组合数的定义,我们可以给出计算组合数 \(\binom{n}{m}\) 的一般公式:

            +

            \[ +\binom{n}{m}=\frac{n!}{m!(n-m)!} +\] 其中 \(n!=1\times2\times\cdots\times +n\);特别地,定义 \(0!=1\)

            +

            小葱想知道如果给定 \(n,m\)\(k\),对于所有的 \(0\leq i\leq n,0\leq j\leq \min \left ( i, m \right +)\) 有多少对 \((i,j)\) 满足 +\(k|\binom{i}{j}\)

              -
            • 对于全部的测试点,保证 $0 \leq n, m \leq 2 \times 10^3$,$1 \leq t \leq 10^4$。
            • +
            • 对于全部的测试点,保证 \(0 \leq n, m \leq +2 \times 10^3\)\(1 \leq t \leq +10^4\)

            显然杨辉三角的那个预处理

            询问可以用二维前缀和来搞

            这里的前缀和有个有趣的地方

            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(c[i][j]==0)
            -

            这里在获取 $s_{i-1,j}$ 的时候,如果 $j=i$ ,那 $s_{i-1,j}$ 就没有算,会导致出错

            -

            解决的方法很简单,直接在每次计算时把 $s_{i,i+1}\leftarrow s_{i,i}$ 就好了

            +

            这里在获取 \(s_{i-1,j}\) +的时候,如果 \(j=i\) ,那 \(s_{i-1,j}\) 就没有算,会导致出错

            +

            解决的方法很简单,直接在每次计算时把 \(s_{i,i+1}\leftarrow s_{i,i}\) 就好了

            这样的话没有影响前缀和数组的意义,蛮好

            代码:

            #include <iostream>
            @@ -893,7 +913,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/08/luo-gu-p5520-yloi2019-qing-yuan-ying-ti-jie/index.html b/2022/07/08/luo-gu-p5520-yloi2019-qing-yuan-ying-ti-jie/index.html index 645da6e9e7..7480257e17 100644 --- a/2022/07/08/luo-gu-p5520-yloi2019-qing-yuan-ying-ti-jie/index.html +++ b/2022/07/08/luo-gu-p5520-yloi2019-qing-yuan-ying-ti-jie/index.html @@ -472,18 +472,25 @@

            洛谷P5520 [yLOI2019] 青原樱
            -

            洛谷P5520 [yLOI2019] 青原樱 题解

            题目链接:P5520 [yLOI2019] 青原樱

            +

            洛谷P5520 [yLOI2019] 青原樱 +题解

            +

            题目链接:P5520 +[yLOI2019] 青原樱

            题意

            -

            $n$ 个空放 $m$ 个物品,两两物品不能直接相邻,至少空一格

            +

            \(n\) 个空放 \(m\) +个物品,两两物品不能直接相邻,至少空一格

            纯数学题。

            -

            看看几个空不能放,啊 $m-1$

            -

            那能放的就有 $n-m+1$ 个空

            -

            没了,水吧。直接算这个就好了。

            -

            为什么对呢,因为你在这 $n-m+1$ 个空里面可以随便放

            -

            时间复杂度 $O(n)$

            +

            看看几个空不能放,啊 \(m-1\)

            +

            那能放的就有 \(n-m+1\) 个空 \[ +A_{n-m+1}^{m} +\] 没了,水吧。直接算这个就好了。

            +

            为什么对呢,因为你在这 \(n-m+1\) +个空里面可以随便放

            +

            时间复杂度 \(O(n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -668,14 +675,14 @@ 

            算法 - - DP - - 图论 + + DP + + 数据结构 @@ -882,7 +889,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/09/luo-gu-p1005-noip2007-ti-gao-zu-ju-zhen-qu-shu-you-xi-ti-jie/index.html b/2022/07/09/luo-gu-p1005-noip2007-ti-gao-zu-ju-zhen-qu-shu-you-xi-ti-jie/index.html index f5dfce5fa6..b41013a08a 100644 --- a/2022/07/09/luo-gu-p1005-noip2007-ti-gao-zu-ju-zhen-qu-shu-you-xi-ti-jie/index.html +++ b/2022/07/09/luo-gu-p1005-noip2007-ti-gao-zu-ju-zhen-qu-shu-you-xi-ti-jie/index.html @@ -472,26 +472,46 @@

            洛谷P1005 [NOIP2007 提高组]
            -

            洛谷P1005 [NOIP2007 提高组] 矩阵取数游戏 题解

            题目链接:P1005 [NOIP2007 提高组] 矩阵取数游戏

            +

            洛谷P1005 [NOIP2007 +提高组] 矩阵取数游戏 题解

            +

            题目链接:P1005 +[NOIP2007 提高组] 矩阵取数游戏

            题意

            -

            帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 $n \times m$ 的矩阵,矩阵中的每个元素 $a_{i,j}$ 均为非负整数。游戏规则如下:

            -
              -
            1. 每次取数时须从每行各取走一个元素,共 $n$ 个。经过 $m$ 次后取完矩阵内所有元素;
            2. +

              帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 \(n \times m\) 的矩阵,矩阵中的每个元素 \(a_{i,j}\) 均为非负整数。游戏规则如下:

              +
                +
              1. 每次取数时须从每行各取走一个元素,共 \(n\) 个。经过 \(m\) 次后取完矩阵内所有元素;
              2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
              3. -
              4. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 $\times 2^i$,其中 $i$ 表示第 $i$ 次取数(从 $1$ 开始编号);
              5. -
              6. 游戏结束总得分为 $m$ 次取数得分之和。
              7. +
              8. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = +被取走的元素值 \(\times 2^i\),其中 +\(i\) 表示第 \(i\) 次取数(从 \(1\) 开始编号);
              9. +
              10. 游戏结束总得分为 \(m\) +次取数得分之和。

              帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

              -

              对于 $60\%$ 的数据,满足 $1\le n,m\le 30$,答案不超过 $10^{16}$。
              对于 $100\%$ 的数据,满足 $1\le n,m\le 80$,$0\le a_{i,j}\le1000$。

              +

              对于 \(60\%\) 的数据,满足 \(1\le n,m\le 30\),答案不超过 \(10^{16}\)。 对于 \(100\%\) 的数据,满足 \(1\le n,m\le 80\)\(0\le a_{i,j}\le1000\)

            注意到每一行的决策与其他行无关

            -

            考虑区间dp然后把 $n$ 行答案加起来

            -

            设 $f_{i,j}$ 表示区间变为 $[i,j]$ 时的最大分数

            -

            不难发现

            -

            要用高精度。比较恶心。

            +\\f_{i,j}=\max\left\{f_{i-1,j}+a_{i-1} \times +2^{m-j+i-1},f_{i,j+1}+a_{j+1} \times 2^{m-j+i-1}\right\} +\] 要用高精度。比较恶心。

            代码:

            #include <iostream>
             #include <string>
            @@ -815,14 +835,14 @@ 

            算法 - - DP - - 图论 + + DP + + 数据结构 @@ -980,7 +1000,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/09/luo-gu-p2585-zjoi2006-san-se-er-cha-shu-ti-jie/index.html b/2022/07/09/luo-gu-p2585-zjoi2006-san-se-er-cha-shu-ti-jie/index.html index 1607ebebbc..b384698e18 100644 --- a/2022/07/09/luo-gu-p2585-zjoi2006-san-se-er-cha-shu-ti-jie/index.html +++ b/2022/07/09/luo-gu-p2585-zjoi2006-san-se-er-cha-shu-ti-jie/index.html @@ -418,14 +418,14 @@

            洛谷P2585 [ZJOI2006]三色二 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -480,27 +480,44 @@

            洛谷P2585 [ZJOI2006]三色二
            -

            洛谷P2585 [ZJOI2006]三色二叉树 题解

            题目链接:P2585 [ZJOI2006]三色二叉树

            +

            洛谷P2585 +[ZJOI2006]三色二叉树 题解

            +

            题目链接:P2585 +[ZJOI2006]三色二叉树

            题意

            -

            一棵二叉树可以按照如下规则表示成一个由 $0$、$1$、$2$ 组成的字符序列,我们称之为“二叉树序列 $S$”:

            -

            例如,下图所表示的二叉树可以用二叉树序列 $S=\texttt{21200110}$ 来表示。

            -

            +0& \text表示该树没有子节点\\ +1S_1& 表示该树有一个节点,S_1 为其子树的二叉树序列\\ +2S_1S_2& 表示该树由两个子节点,S_1 和 S_2 +分别表示其两个子树的二叉树序列 +\end{cases}\]

            +

            例如,下图所表示的二叉树可以用二叉树序列 \(S=\texttt{21200110}\) 来表示。

            +

            你的任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不同。给定一颗二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。

            -

            对于全部的测试点,保证 $1 \leq |s| \leq 5 \times 10^5$,$s$ 中只含字符 0 1 2

            +

            对于全部的测试点,保证 \(1 \leq |s| \leq 5 +\times 10^5\)\(s\) 中只含字符 +0 1 2

            入门级的树形dp

            -

            设 $f_i$ 表示染 $i$ 所在子树,$i$ 染绿色的最大/最小绿色结点数,

            -

            $g_i$ 表示染 $i$ 所在子树,$i$ 不染绿色的最大/最小绿色结点数。

            -

            跑个dfs建树即可

            -

            时间复杂度 $O(n)$

            +\\g_i = \max/\min\left\{f_l+g_r,~g_l+f_l\right\} +\] 跑个dfs建树即可

            +

            时间复杂度 \(O(n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -618,14 +635,14 @@ 

            算法 - - DP - - 图论 + + DP + + 数据结构 @@ -923,7 +940,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/09/luo-gu-p3698-cqoi2017-xiao-q-de-qi-pan-ti-jie/index.html b/2022/07/09/luo-gu-p3698-cqoi2017-xiao-q-de-qi-pan-ti-jie/index.html index 9ef3ca35a8..0ef16ccb68 100644 --- a/2022/07/09/luo-gu-p3698-cqoi2017-xiao-q-de-qi-pan-ti-jie/index.html +++ b/2022/07/09/luo-gu-p3698-cqoi2017-xiao-q-de-qi-pan-ti-jie/index.html @@ -476,23 +476,42 @@

            洛谷P3698 [CQOI2017]小Q的棋
            -

            洛谷P3698 [CQOI2017]小Q的棋盘 题解

            题目链接:P3698 [CQOI2017]小Q的棋盘

            +

            洛谷P3698 [CQOI2017]小Q的棋盘 +题解

            +

            题目链接:P3698 +[CQOI2017]小Q的棋盘

            题意

            小 Q 正在设计一种棋类游戏。

            -

            在小 Q 设计的游戏中,棋子可以放在棋盘上的格点中。某些格点之间有连线,棋子只能在有连线的格点之间移动。整个棋盘上共有 $V$ 个格点,编号为$0,1,2,\dots , V−1$,它们是连通的,也就是说棋子从任意格点出发,总能到达所有的格点。小 Q 在设计棋盘时,还保证棋子从一个格点移动到另外任一格点的路径是唯一的。

            -

            小 Q 现在想知道,当棋子从格点 $0$ 出发,移动 $N$ 步最多能经过多少格点。格点可以重复经过多次,但不重复计数。

            -

            对于 $100\%$ 的测试点,$N,V \le 100,~0 \le a_i,b_i< V$ 。

            +

            在小 Q +设计的游戏中,棋子可以放在棋盘上的格点中。某些格点之间有连线,棋子只能在有连线的格点之间移动。整个棋盘上共有 +\(V\) 个格点,编号为\(0,1,2,\dots , +V−1\),它们是连通的,也就是说棋子从任意格点出发,总能到达所有的格点。小 +Q +在设计棋盘时,还保证棋子从一个格点移动到另外任一格点的路径是唯一的。

            +

            小 Q 现在想知道,当棋子从格点 \(0\) +出发,移动 \(N\) +步最多能经过多少格点。格点可以重复经过多次,但不重复计数。

            +

            对于 \(100\%\) 的测试点,\(N,V \le 100,~0 \le a_i,b_i< V\)

            显然树上背包

            -

            值得注意的是,我们并不知道走的路线有没有回到 $u$

            +

            值得注意的是,我们并不知道走的路线有没有回到 \(u\)

            因此要多加一维

            -

            设 $f_{u,j,0/1}$ 表示 $u$ 所在子树,走了 $j$ 步,$0/1$ 表示是/否回到 $u$

            -

            不难发现

            -

            注意一下边界问题就好了

            -

            时间复杂度 $O(nm^2)$

            +

            \(f_{u,j,0/1}\) 表示 \(u\) 所在子树,走了 \(j\) 步,\(0/1\) 表示是/否回到 \(u\)

            +

            不难发现 \[ +f_{u,j,0}=\max_{v \in +\text{son}(u)}\left\{f_{u,j,0},f_{u,j-k,0}+f_{v,k-2,0}\right\} +\\f_{u,j,1}=\max_{v \in +\text{son}(u)}\left\{f_{u,j,1},f_{u,j-k,1}+f_{v,k-2,0},f_{u,j-k,0}+f_{v,k-1,1}\right\} +\] 注意一下边界问题就好了

            +

            时间复杂度 \(O(nm^2)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -566,8 +585,10 @@ 

            << res << '\n';

            而不用

            cout << g[1][m] << '\n';
            -

            从 $m$ 的角度看,多走肯定不会减少答案

            -

            但是如果 $m > 2(n-1)$ ,那么按照正常的dp是走不出这样的步数的

            +

            \(m\) +的角度看,多走肯定不会减少答案

            +

            但是如果 \(m > 2(n-1)\) +,那么按照正常的dp是走不出这样的步数的

            所以可以用前者输出答案,或者读入后加一句

            m=min(m,2*n-2);

            然后其实memset没什么必要,具体看下面代码

            @@ -1008,7 +1029,7 @@

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/09/luo-gu-p4551-zui-chang-yi-huo-lu-jing-ti-jie/index.html b/2022/07/09/luo-gu-p4551-zui-chang-yi-huo-lu-jing-ti-jie/index.html index c1ea5537b1..44b2e51668 100644 --- a/2022/07/09/luo-gu-p4551-zui-chang-yi-huo-lu-jing-ti-jie/index.html +++ b/2022/07/09/luo-gu-p4551-zui-chang-yi-huo-lu-jing-ti-jie/index.html @@ -476,18 +476,25 @@

            洛谷P4551 最长异或路径
            -

            洛谷P4551 最长异或路径 题解

            题目链接:P4551 最长异或路径

            +

            洛谷P4551 最长异或路径 题解

            +

            题目链接:P4551 +最长异或路径

            -

            题意:给定一棵 $n$ 个点的带权树,结点下标从 $1$ 开始到 $n$。寻找树中找两个结点,求最长的异或路径。

            +

            题意:给定一棵 \(n\) 个点的带权树,结点下标从 \(1\) 开始到 \(n\)。寻找树中找两个结点,求最长的异或路径。

            异或路径指的是指两个结点之间唯一路径上的所有边权的异或。

            -

            $1\le n \le 100000,~0 < u,v \le n,~0 \le w < 2^{31}$。

            +

            \(1\le n \le 100000,~0 < u,v \le n,~0 +\le w < 2^{31}\)

            首先可以想到一个常用trick:前缀和处理边权异或和

            然后对于每个异或和,我们都要找到一个和它异或值尽可能大的前缀和

            直接枚举是不行的,考虑建01trie

            01trie其实很简单,就是把每个数字的二进制形式看成字符串去建trie

            贪心地选择高位,具体看代码就可以了,很简单

            -

            时间复杂度 $O(n \log \max\{w_i\})$ ,其实就是 $O(30 \times n)$

            +

            时间复杂度 \(O(n \log \max\{w_i\})\) +,其实就是 \(O(30 \times n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -942,7 +949,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/10/luo-gu-p2375-noi2014-dong-wu-yuan-ti-jie/index.html b/2022/07/10/luo-gu-p2375-noi2014-dong-wu-yuan-ti-jie/index.html index eeee35c9d1..daa30f8286 100644 --- a/2022/07/10/luo-gu-p2375-noi2014-dong-wu-yuan-ti-jie/index.html +++ b/2022/07/10/luo-gu-p2375-noi2014-dong-wu-yuan-ti-jie/index.html @@ -472,27 +472,60 @@

            洛谷P2375 [NOI2014] 动物园
            -

            洛谷P2375 [NOI2014] 动物园 题解

            题目链接:P2375 [NOI2014] 动物园

            +

            洛谷P2375 [NOI2014] 动物园 +题解

            +

            题目链接:P2375 +[NOI2014] 动物园

            题意

            近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法。

            某天,园长给动物们讲解KMP算法。

            -

            园长:“对于一个字符串$S$,它的长度为$L$。我们可以在$O(L)$的时间内,求出一个名为next的数组。有谁预习了next数组的含义吗?”

            -

            熊猫:“对于字符串$S$的前$i$个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作$next[i]$。”

            +

            园长:“对于一个字符串\(S\),它的长度为\(L\)。我们可以在\(O(L)\)的时间内,求出一个名为next的数组。有谁预习了next数组的含义吗?”

            +

            熊猫:“对于字符串\(S\)的前\(i\)个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作\(next[i]\)。”

            园长:“非常好!那你能举个例子吗?”

            -

            熊猫:“例$S$为abcababc,则$next[5]=2$。因为$S$的前$5$个字符为abcabab既是它的后缀又是它的前缀,并且找不到一个更长的字符串满足这个性质。同理,还可得出$next[1] = next[2] = next[3] = 0$,$next[4] = next[6] = 1$,$next[7] = 2$,$next[8] = 3$。”

            -

            园长表扬了认真预习的熊猫同学。随后,他详细讲解了如何在$O(L)$的时间内求出next数组。

            -

            下课前,园长提出了一个问题:“KMP算法只能求出next数组。我现在希望求出一个更强大num数组一一对于字符串$S$的前$i$个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作$num[i]$。例如$S$为aaaaa,则$num[4] = 2$。这是因为$S$的前$4$个字符为aaaa,其中aaa都满足性质‘既是后缀又是前缀’,同时保证这个后缀与这个前缀不重叠。而aaa虽然满足性质‘既是后缀又是前缀’,但遗憾的是这个后缀与这个前缀重叠了,所以不能计算在内。同理,$num[1] = 0,num[2] = num[3] = 1,num[5] = 2$。”

            -

            最后,园长给出了奖励条件,第一个做对的同学奖励巧克力一盒。听了这句话,睡了一节课的企鹅立刻就醒过来了!但企鹅并不会做这道题,于是向参观动物园的你寻求帮助。你能否帮助企鹅写一个程序求出$num$数组呢?

            -

            特别地,为了避免大量的输出,你不需要输出$num[i]$分别是多少,你只需要输出所有$(num[i]+1)$的乘积,对$1,000,000,007$取模的结果即可。

            -

            $N ≤ 5, L ≤ 1,000,000$

            +

            熊猫:“例\(S\)abcababc,则\(next[5]=2\)。因为\(S\)的前\(5\)个字符为abcabab既是它的后缀又是它的前缀,并且找不到一个更长的字符串满足这个性质。同理,还可得出\(next[1] = next[2] = next[3] = 0\)\(next[4] = next[6] = 1\)\(next[7] = 2\)\(next[8] = 3\)。”

            +

            园长表扬了认真预习的熊猫同学。随后,他详细讲解了如何在\(O(L)\)的时间内求出next数组。

            +

            下课前,园长提出了一个问题:“KMP算法只能求出next数组。我现在希望求出一个更强大num数组一一对于字符串\(S\)的前\(i\)个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作\(num[i]\)。例如\(S\)aaaaa,则\(num[4] = 2\)。这是因为\(S\)的前\(4\)个字符为aaaa,其中aaa都满足性质‘既是后缀又是前缀’,同时保证这个后缀与这个前缀不重叠。而aaa虽然满足性质‘既是后缀又是前缀’,但遗憾的是这个后缀与这个前缀重叠了,所以不能计算在内。同理,\(num[1] = 0,num[2] = num[3] = 1,num[5] = +2\)。”

            +

            最后,园长给出了奖励条件,第一个做对的同学奖励巧克力一盒。听了这句话,睡了一节课的企鹅立刻就醒过来了!但企鹅并不会做这道题,于是向参观动物园的你寻求帮助。你能否帮助企鹅写一个程序求出\(num\)数组呢?

            +

            特别地,为了避免大量的输出,你不需要输出\(num[i]\)分别是多少,你只需要输出所有\((num[i]+1)\)的乘积,对\(1,000,000,007\)取模的结果即可。

            +

            \(N ≤ 5, L ≤ 1,000,000\)

            注:border为公共前后缀

            -

            考虑 $S$ 的最长border $s$

            -

            显然 $s$ 的最长border一定也是 $S$ 的border

            -

            则 $\text{num}_{S} = \text{num}_s+1$(先不考虑重叠问题)

            +

            考虑 \(S\) 的最长border \(s\)

            +

            显然 \(s\) 的最长border一定也是 +\(S\) 的border

            +

            \(\text{num}_{S} = +\text{num}_s+1\)(先不考虑重叠问题)

            对于重叠的情况直接跳fail数组即可

            -

            时间复杂度 $O(\sum|s_i|)$

            +

            时间复杂度 \(O(\sum|s_i|)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -906,7 +939,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/10/luo-gu-p3435-poi2006-okr-periods-of-words-ti-jie/index.html b/2022/07/10/luo-gu-p3435-poi2006-okr-periods-of-words-ti-jie/index.html index 9e29205beb..99f1c9827e 100644 --- a/2022/07/10/luo-gu-p3435-poi2006-okr-periods-of-words-ti-jie/index.html +++ b/2022/07/10/luo-gu-p3435-poi2006-okr-periods-of-words-ti-jie/index.html @@ -472,27 +472,50 @@

            洛谷P3435 [POI2006] OKR-Period
            -

            洛谷P3435 [POI2006] OKR-Periods of Words 题解

            题目链接:P3435 [POI2006] OKR-Periods of Words

            +

            洛谷P3435 [POI2006] +OKR-Periods of Words 题解

            +

            题目链接:P3435 +[POI2006] OKR-Periods of Words

            题意

            -

            对于一个仅含小写字母的字符串 $a$,$p$ 为 $a$ 的前缀且 $p\ne a$,那么我们称 $p$ 为 $a$ 的 proper 前缀。

            -

            规定字符串 $Q$(可以是空串)表示 $a$ 的周期,当且仅当 $Q$ 是 $a$ 的 proper 前缀且 $a$ 是 $Q+Q$ 的前缀。

            -

            例如 ababab 的一个周期,因为 ababab 的 proper 前缀,且 ababab+ab 的前缀。

            +

            对于一个仅含小写字母的字符串 \(a\)\(p\) +为 \(a\) 的前缀且 \(p\ne a\),那么我们称 \(p\)\(a\) 的 proper 前缀。

            +

            规定字符串 \(Q\)(可以是空串)表示 +\(a\) 的周期,当且仅当 \(Q\)\(a\) 的 proper 前缀且 \(a\)\(Q+Q\) 的前缀。

            +

            例如 ababab 的一个周期,因为 +ababab 的 proper 前缀,且 +ababab+ab 的前缀。

            求给定字符串所有前缀的最大周期长度之和。

            -

            $1\le |a| \le 10^6$

            +

            \(1\le |a| \le 10^6\)

            -

            考虑如何最大化周期 $Q$

            -

            对于某个前缀,例如 $s = \tt{aabaaaab}$

            -

            不难发现它的最长周期就是 $\tt{aabaa}$

            -

            仔细观察 $\tt{\color{red}{aab}\color{blue}{aa}\color{red}{aab}}$

            -

            其实这个 $Q = \tt{\color{red}{aab}\color{blue}{aa}}$

            -

            就是 $s$ 的最短非空border $\tt{\color{red}{aab}}$ 加上中间那一段东西

            -

            这样对于每个 $i$ ,我们只要找到它的最短border就好了

            +

            考虑如何最大化周期 \(Q\)

            +

            对于某个前缀,例如 \(s = +\tt{aabaaaab}\)

            +

            不难发现它的最长周期就是 \(\tt{aabaa}\)

            +

            仔细观察 \(\tt{\color{red}{aab}\color{blue}{aa}\color{red}{aab}}\)

            +

            其实这个 \(Q = +\tt{\color{red}{aab}\color{blue}{aa}}\)

            +

            就是 \(s\) +的最短非空border \(\tt{\color{red}{aab}}\) +加上中间那一段东西

            +

            这样对于每个 \(i\) +,我们只要找到它的最短border就好了

            这个东西可以通过跳fail数组实现

            但是如果每次都暴力跳,显然会有很多重复步骤

            这个跳的过程,是不是很熟悉?并查集也是这么跳的对吧!

            并查集可以路径压缩,这里fail数组我们也可以路径压缩。

            -

            时间复杂度 $O(n)$

            +

            时间复杂度 \(O(n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -895,7 +918,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/10/luo-gu-p4287-shoi2011-shuang-bei-hui-wen-ti-jie/index.html b/2022/07/10/luo-gu-p4287-shoi2011-shuang-bei-hui-wen-ti-jie/index.html index 01e63bf47e..3c09060915 100644 --- a/2022/07/10/luo-gu-p4287-shoi2011-shuang-bei-hui-wen-ti-jie/index.html +++ b/2022/07/10/luo-gu-p4287-shoi2011-shuang-bei-hui-wen-ti-jie/index.html @@ -472,27 +472,64 @@

            洛谷P4287 [SHOI2011]双倍回
            -

            洛谷P4287 [SHOI2011]双倍回文 题解

            题目链接:P4287 [SHOI2011]双倍回文

            +

            洛谷P4287 [SHOI2011]双倍回文 +题解

            +

            题目链接:P4287 +[SHOI2011]双倍回文

            题意

            -

            记字符串 $w$ 的倒置为 $w^R$ 。例如 $(\tt{abcd})^R=\tt{dcba}$ , $(\tt{abba})^R=\tt{abba}$ 。

            -

            对字符串 $x$ ,如果 $x$ 满足 $x^R=x$ ,则称之为回文;例如 $\tt{abba}$ 是一个回文,而 $\tt{abed}$ 不是。

            -

            如果 $x$ 能够写成的 $ww^Rww^R$ 形式,则称它是一个“双倍回文”。换句话说,若要 $x$ 是双倍回文,它的长度必须是 $4$ 的倍数,而且 $x$ , $x$ 的前半部分, $x$ 的后半部分都要是回文。例如 $\tt{abbaabba}$ 是一个双倍回文,而 $\tt{abaaba}$ 不是,因为它的长度不是 $4$ 的倍数。

            -

            $x$ 的子串是指在$x$中连续的一段字符所组成的字符串。例如 $\tt{be}$ 是 $\tt{abed}$ 的子串,而 $\tt{ac}$ 不是。

            -

            $x$ 的回文子串,就是指满足回文性质的 $x$ 的子串。

            -

            $x$ 的双倍回文子串,就是指满足双倍回文性质的 $x$ 的子串。

            +

            记字符串 \(w\) 的倒置为 \(w^R\) 。例如 \((\tt{abcd})^R=\tt{dcba}\)\((\tt{abba})^R=\tt{abba}\)

            +

            对字符串 \(x\) ,如果 \(x\) 满足 \(x^R=x\) ,则称之为回文;例如 \(\tt{abba}\) 是一个回文,而 \(\tt{abed}\) 不是。

            +

            如果 \(x\) 能够写成的 \(ww^Rww^R\) +形式,则称它是一个“双倍回文”。换句话说,若要 \(x\) 是双倍回文,它的长度必须是 \(4\) 的倍数,而且 \(x\)\(x\) 的前半部分, \(x\) 的后半部分都要是回文。例如 \(\tt{abbaabba}\) 是一个双倍回文,而 \(\tt{abaaba}\) 不是,因为它的长度不是 \(4\) 的倍数。

            +

            \(x\) 的子串是指在\(x\)中连续的一段字符所组成的字符串。例如 +\(\tt{be}\)\(\tt{abed}\) 的子串,而 \(\tt{ac}\) 不是。

            +

            \(x\) +的回文子串,就是指满足回文性质的 \(x\) +的子串。

            +

            \(x\) +的双倍回文子串,就是指满足双倍回文性质的 \(x\) 的子串。

            你的任务是,对于给定的字符串,计算它的最长双倍回文子串的长度。

            -

            $n \le 500000$

            +

            \(n \le 500000\)

            考虑Manacher求解

            -

            对于修改后字符串的 $i$ 位置的初始回文半径 $p_i$ (暴力拓展前的半径)

            -

            如果 $i-p_i+1 \le \text{mid}$ ,则存在一个极大双倍回文子串

            +

            对于修改后字符串的 \(i\) +位置的初始回文半径 \(p_i\) +(暴力拓展前的半径)

            +

            如果 \(i-p_i+1 \le \text{mid}\) +,则存在一个极大双倍回文子串

            这个极大双倍回文子串在修改后的串中,

            -

            以「以 $\text{mid}$ 为中心的回文串」的形式存在

            -

            注意题目要求的是 $4$ 倍数的串,则对于刚才的求解方法,

            -

            只要限制 $i$ 为奇数时更新答案、$\text{mid}$ 和 $r$

            -

            此时 $i$ 的回文半径 $p_i^{\prime}-1$ 一定是偶串的长度

            -

            注意 $r$ 相同时,不要优先更新 $\text{mid}$

            +

            以「以 \(\text{mid}\) +为中心的回文串」的形式存在

            +

            注意题目要求的是 \(4\) +倍数的串,则对于刚才的求解方法,

            +

            只要限制 \(i\) +为奇数时更新答案、\(\text{mid}\) 和 +\(r\)

            +

            此时 \(i\) 的回文半径 \(p_i^{\prime}-1\) 一定是偶串的长度

            +

            注意 \(r\) 相同时,不要优先更新 +\(\text{mid}\)

            否则根据贪心,不难发现会失去某些极大值,导致答案出错

            这里给个hack(想了我几个小时才想出来

            input:
            @@ -501,7 +538,7 @@ 

            -

            时间复杂度 $O(n)$

            +

            时间复杂度 \(O(n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -917,7 +954,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/10/luo-gu-p4568-jloi2011-fei-xing-lu-xian-ti-jie/index.html b/2022/07/10/luo-gu-p4568-jloi2011-fei-xing-lu-xian-ti-jie/index.html index eef352a361..46ae04aaf5 100644 --- a/2022/07/10/luo-gu-p4568-jloi2011-fei-xing-lu-xian-ti-jie/index.html +++ b/2022/07/10/luo-gu-p4568-jloi2011-fei-xing-lu-xian-ti-jie/index.html @@ -472,28 +472,47 @@

            洛谷P4568 [JLOI2011] 飞行路
            -

            洛谷P4568 [JLOI2011] 飞行路线 题解

            题目链接:P4568 [JLOI2011] 飞行路线

            +

            洛谷P4568 [JLOI2011] 飞行路线 +题解

            +

            题目链接:P4568 +[JLOI2011] 飞行路线

            题意

            -

            Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 $n$ 个城市设有业务,设这些城市分别标记为 $0$ 到 $n-1$,一共有 $m$ 种航线,每种航线连接两个城市,并且航线有一定的价格。

            -

            Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 $k$ 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?

            -

            对于 $100\%$ 的数据,$2 \le n \le 10^4$,$1 \le m \le 5\times 10^4$,$0 \le k \le 10$,$0\le s,t,a,b\le n$,$a\ne b$,$0\le c\le 10^3$。

            +

            Alice 和 Bob +现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 +\(n\) +个城市设有业务,设这些城市分别标记为 \(0\)\(n-1\),一共有 \(m\) +种航线,每种航线连接两个城市,并且航线有一定的价格。

            +

            Alice 和 Bob +现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 +\(k\) 种航线上搭乘飞机。那么 Alice 和 +Bob 这次出行最少花费多少?

            +

            对于 \(100\%\) 的数据,\(2 \le n \le 10^4\)\(1 \le m \le 5\times 10^4\)\(0 \le k \le 10\)\(0\le s,t,a,b\le n\)\(a\ne b\)\(0\le +c\le 10^3\)

            分层图板子题

            不写具体做法了,就总结几个易错点

              -
            • 无向图的e数组开

              -

              $M$ 稍微大一点(比如5e4+5)用于后面的额外边

              -
            • -
            • 额外边:如果 $s\to t$ 的路径,结点数小于 $k$

              +
            • 无向图的e数组开 \[ +M\times (K+1) \times 4 +\] \(M\) +稍微大一点(比如5e4+5)用于后面的额外边

            • +
            • 额外边:如果 \(s\to t\) +的路径,结点数小于 \(k\)

              则需要单独建边

              for(int i=1; i<=k; i++)
                   addEdge((i-1)*n+t,i*n+t,0);
              -

              这样答案才是 d[k*n+t]

              -
            • +

              这样答案才是 d[k*n+t]

            -

            时间复杂度 $O(nk\log mk)$

            +

            时间复杂度 \(O(nk\log mk)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -939,7 +958,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/10/luo-gu-p4799-ceoi2015-day2-shi-jie-bing-qiu-jin-biao-sai-ti-jie/index.html b/2022/07/10/luo-gu-p4799-ceoi2015-day2-shi-jie-bing-qiu-jin-biao-sai-ti-jie/index.html index bc458016de..008b4626a7 100644 --- a/2022/07/10/luo-gu-p4799-ceoi2015-day2-shi-jie-bing-qiu-jin-biao-sai-ti-jie/index.html +++ b/2022/07/10/luo-gu-p4799-ceoi2015-day2-shi-jie-bing-qiu-jin-biao-sai-ti-jie/index.html @@ -468,46 +468,65 @@

            洛谷P4799 [CEOI2015 Day2] 世
            -

            洛谷P4799 [CEOI2015 Day2] 世界冰球锦标赛 题解

            题目链接:P4799 [CEOI2015 Day2] 世界冰球锦标赛

            +

            洛谷P4799 [CEOI2015 +Day2] 世界冰球锦标赛 题解

            +

            题目链接:P4799 +[CEOI2015 Day2] 世界冰球锦标赛

            题意

            -

            译自 CEOI2015 Day2 T1「Ice Hockey World Championship

            +

            译自 CEOI2015 Day2 T1「Ice Hockey +World Championship

            -

            今年的世界冰球锦标赛在捷克举行。Bobek 已经抵达布拉格,他不是任何团队的粉丝,也没有时间观念。他只是单纯的想去看几场比赛。如果他有足够的钱,他会去看所有的比赛。不幸的是,他的财产十分有限,他决定把所有财产都用来买门票。

            +

            今年的世界冰球锦标赛在捷克举行。Bobek +已经抵达布拉格,他不是任何团队的粉丝,也没有时间观念。他只是单纯的想去看几场比赛。如果他有足够的钱,他会去看所有的比赛。不幸的是,他的财产十分有限,他决定把所有财产都用来买门票。

            -

            给出 Bobek 的预算和每场比赛的票价,试求:如果总票价不超过预算,他有多少种观赛方案。如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。

            -
            +

            给出 Bobek +的预算和每场比赛的票价,试求:如果总票价不超过预算,他有多少种观赛方案。如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。

            - + - - - - + + + + - - - - - - + + + + + + - - - - - - + + + + + +
            数据组号$1-2$$3-4$$5-7$$8-10$\(1-2\)\(3-4\)\(5-7\)\(8-10\)
            $N \leq$$10$$20$$40$$40$
            \(N \leq\)\(10\)\(20\)\(40\)\(40\)
            $M \leq$$10^6$$10^{18}$$10^6$$10^{18}$
            \(M \leq\)\(10^6\)\(10^{18}\)\(10^6\)\(10^{18}\)
            -
            -

            首先这个 $20$ 就很有趣 显然暴搜是吧

            -

            那么 $40$ 的情况怎么处理呢

            +

            首先这个 \(20\) 就很有趣 +显然暴搜是吧

            +

            那么 \(40\) 的情况怎么处理呢

            考虑折半搜索。

            折半搜索的思想就是

            把原来的问题拆分成两部分分别暴搜

            @@ -521,10 +540,13 @@

            for(int i=1; i<=cnt1; i++) res+=upper_bound(sum2+1,sum2+1+cnt2,m-sum1[i])-sum2-1; cout << res << '\n';

            -

            这里的 $\tt{upper_bound}$ 其实很好理解

            -

            就是严格大于 $m-s_1$ 的那个 $s_2$ 的位置

            -

            显然那个 $s_2$ 之前的都可以取

            -

            时间复杂度 $O(2^{\frac{n}{2}} \log 2^{\frac{n}{2}}) \approx O(n2^{\frac{n}{2}})$

            +

            这里的 \(\tt{upper\_bound}\) +其实很好理解

            +

            就是严格大于 \(m-s_1\) 的那个 \(s_2\) 的位置

            +

            显然那个 \(s_2\) 之前的都可以取

            +

            时间复杂度 \(O(2^{\frac{n}{2}} \log +2^{\frac{n}{2}}) \approx O(n2^{\frac{n}{2}})\)

            代码:

            #include <iostream>
             #include <string>
            @@ -932,7 +954,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/11/luo-gu-p1868-ji-e-de-nai-niu-ti-jie/index.html b/2022/07/11/luo-gu-p1868-ji-e-de-nai-niu-ti-jie/index.html index da914d6afa..85f0e4af05 100644 --- a/2022/07/11/luo-gu-p1868-ji-e-de-nai-niu-ti-jie/index.html +++ b/2022/07/11/luo-gu-p1868-ji-e-de-nai-niu-ti-jie/index.html @@ -472,21 +472,34 @@

            洛谷P1868 饥饿的奶牛 题
            -

            洛谷P1868 饥饿的奶牛 题解

            题目链接:P1868 饥饿的奶牛

            +

            洛谷P1868 饥饿的奶牛 题解

            +

            题目链接:P1868 +饥饿的奶牛

            题意

            有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字。

            现用汉语翻译为:

            -

            有 $N$ 个区间,每个区间 $x,y$ 表示提供的 $x\sim y$ 共 $y-x+1$ 堆优质牧草。你可以选择任意区间但不能有重复的部分。

            +

            \(N\) 个区间,每个区间 \(x,y\) 表示提供的 \(x\sim y\)\(y-x+1\) +堆优质牧草。你可以选择任意区间但不能有重复的部分。

            对于奶牛来说,自然是吃的越多越好,然而奶牛智商有限,现在请你帮助他。

            -

            $1 \leq n \leq 1.5 \times 10^5$,$0 \leq x \leq y \leq 3 \times 10^6$。

            +

            \(1 \leq n \leq 1.5 \times +10^5\)\(0 \leq x \leq y \leq 3 \times +10^6\)

            -

            设 $f_i$ 表示只考虑前 $i$ 个位置能吃到的最多牧草

            -

            这里的 $\text{Edge}(i)$ 表示以 $i$ 结尾的所有区间组成的集合

            -

            对于每个区间 $[x,y]$ ,我们建一条 $y \to x$ 的边

            -

            这样就可以枚举 $j$ 了

            -

            时间复杂度 $O(m)$

            +

            \(f_i\) 表示只考虑前 \(i\) 个位置能吃到的最多牧草 \[ +f_i = \max_{j \in \text{Edge}(i)}\left\{f_{j-1} + i-j+1\right\} +\] 这里的 \(\text{Edge}(i)\) +表示以 \(i\) +结尾的所有区间组成的集合

            +

            对于每个区间 \([x,y]\) ,我们建一条 +\(y \to x\) 的边

            +

            这样就可以枚举 \(j\)

            +

            时间复杂度 \(O(m)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -900,7 +913,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/11/luo-gu-p2679-noip2015-ti-gao-zu-zi-chuan-ti-jie/index.html b/2022/07/11/luo-gu-p2679-noip2015-ti-gao-zu-zi-chuan-ti-jie/index.html index 119031a2f9..8c35b195b9 100644 --- a/2022/07/11/luo-gu-p2679-noip2015-ti-gao-zu-zi-chuan-ti-jie/index.html +++ b/2022/07/11/luo-gu-p2679-noip2015-ti-gao-zu-zi-chuan-ti-jie/index.html @@ -476,34 +476,64 @@

            洛谷P2679 [NOIP2015 提高组]
            -

            洛谷P2679 [NOIP2015 提高组] 子串 题解

            题目链接:P2679 [NOIP2015 提高组] 子串

            +

            洛谷P2679 [NOIP2015 提高组] +子串 题解

            +

            题目链接:P2679 +[NOIP2015 提高组] 子串

            题意

            -

            有两个仅包含小写英文字母的字符串 $A$ 和 $B$。

            -

            现在要从字符串 $A$ 中取出 $d$ 个互不重叠的非空子串,然后把这 $d$ 个子串按照其在字符串 $A$ 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 $B$ 相等?

            +

            有两个仅包含小写英文字母的字符串 \(A\)\(B\)

            +

            现在要从字符串 \(A\) 中取出 \(d\) 个互不重叠的非空子串,然后把这 \(d\) 个子串按照其在字符串 \(A\) +中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 +\(B\) 相等?

            注意:子串取出的位置不同也认为是不同的方案。

            -

            输出答案对 $10^9+7$ 取模的结果。

            -

            对于所有 $10$ 组数据: $1\le n \le 1000,~1 \le m \le 200,~1 \le d \le m$。

            +

            输出答案对 \(10^9+7\) +取模的结果。

            +

            对于所有 \(10\) 组数据: \(1\le n \le 1000,~1 \le m \le 200,~1 \le d \le +m\)

            显然的字符串类型dp

            一个原始的思路是:

            -

            设 $f_{i,j,k}$ 表示仅考虑 $A$ 的前 $i$ 个字符,$B$ 匹配到第 $j$ 个字符,用了 $k$ 个子串的方案数。

            +

            \(f_{i,j,k}\) 表示仅考虑 \(A\) 的前 \(i\) 个字符,\(B\) 匹配到第 \(j\) 个字符,用了 \(k\) 个子串的方案数。

            -

            然后发现我们并不知道 $A_i$ 是否与 $B_j$ 匹配,无法转移

            +

            然后发现我们并不知道 \(A_i\) 是否与 +\(B_j\) 匹配,无法转移

            于是状态就变成了

            -

            设 $f_{i,j,k}$ 表示仅考虑 $A$ 的前 $i$ 个字符,$B$ 匹配到第 $j$ 个字符,用了 $k$ 个子串,且 $A_i$ 为最后一个匹配字符的方案数。

            -

            这样就可以转移了

            -

            显然 $i$ 这一维可以用滚动数组滚掉

            -

            那么 $\sum_{t=1}^{p+1} f_{i-t,j-t,k-1}$ 怎么处理呢

            -

            其实我们已经在之前的计算中把 $f_{i-t,j-t,k-1}$ 计算出来了

            -

            考虑动态维护一个前缀和,在遇到 $A_i\ne B_j$ 的时候中断,具体见代码

            -

            时间复杂度 $O(nmk)$

            +1,&,i=j=k=0 +\\\\f_{i-1,j,k}&,A_i \ne B_j +\\\\f_{i-1,j,k}+\sum_{t=1}^{p+1} f_{i-t,j-t,k-1}&,\forall d \in +[0,p], A_{i-d} = B_{j-d} \, \land \, A_{i-p-1} \ne B_{j-p-1} +\end{cases} +\] 显然 \(i\) +这一维可以用滚动数组滚掉

            +

            那么 \(\sum_{t=1}^{p+1} +f_{i-t,j-t,k-1}\) 怎么处理呢

            +

            其实我们已经在之前的计算中把 \(f_{i-t,j-t,k-1}\) 计算出来了

            +

            考虑动态维护一个前缀和,在遇到 \(A_i\ne +B_j\) 的时候中断,具体见代码

            +

            时间复杂度 \(O(nmk)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -912,7 +942,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/12/luo-gu-p1168-zhong-wei-shu-ti-jie/index.html b/2022/07/12/luo-gu-p1168-zhong-wei-shu-ti-jie/index.html index e81e61f275..078030bc31 100644 --- a/2022/07/12/luo-gu-p1168-zhong-wei-shu-ti-jie/index.html +++ b/2022/07/12/luo-gu-p1168-zhong-wei-shu-ti-jie/index.html @@ -472,11 +472,20 @@

            洛谷P1168 中位数 题解

            -

            洛谷P1168 中位数 题解

            题目链接:P1168 中位数

            +

            洛谷P1168 中位数 题解

            +

            题目链接:P1168 +中位数

            题意

            -

            给出一个长度为$N$的非负整数序列$A_i$,对于所有$1 ≤ k ≤ (N + 1) / 2$,输出$A_1, A_1 \sim A_3, …,A_1 \sim A_{2k - 1}$的中位数。即前$1,3,5,…$个数的中位数。

            -

            对于$100\%$的数据,$N ≤ 100000$。

            +

            给出一个长度为\(N\)的非负整数序列\(A_i\),对于所有\(1 ≤ k ≤ (N + 1) / 2\),输出\(A_1, A_1 \sim A_3, …,A_1 \sim A_{2k - +1}\)的中位数。即前\(1,3,5,…\)个数的中位数。

            +

            对于\(100\%\)的数据,\(N ≤ 100000\)

            当然可以用vector来搞啦,但是这样就常数大而且没劲了

            考虑对顶堆(那篇讲对顶堆的是在绕口令吧。明明很好懂的)

            @@ -487,8 +496,9 @@

            但是我们要中位数,这就需要q1和q2的size只相差一,才好处理

            怎么搞呢,比如q1的size太大了,那我们就把q1的堆首一个个扔到q2里去

            这样仍然是满足性质的

            -

            注意到每个元素最多入堆2次,因此单次插入操作是 $O(\log n)$ 的

            -

            时间复杂度 $O(n\log n)$

            +

            注意到每个元素最多入堆2次,因此单次插入操作是 \(O(\log n)\)

            +

            时间复杂度 \(O(n\log n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -898,7 +908,7 @@ 

              站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/12/luo-gu-p1816-zhong-cheng-ti-jie/index.html b/2022/07/12/luo-gu-p1816-zhong-cheng-ti-jie/index.html index bde9879100..2d9805e606 100644 --- a/2022/07/12/luo-gu-p1816-zhong-cheng-ti-jie/index.html +++ b/2022/07/12/luo-gu-p1816-zhong-cheng-ti-jie/index.html @@ -468,15 +468,21 @@

            洛谷P1816 忠诚 题解

            -

            洛谷P1816 忠诚 题解

            题目链接:P1816 忠诚

            +

            洛谷P1816 忠诚 题解

            +

            题目链接:P1816 +忠诚

            题意:区间min。

            BIT写法也就图一乐(其实不建议)

            主要在询问上有些区别,

            -

            例如如果 x-lowbit(x) 小于 $l$ ,则此时不能直接 x-=lowbit(x)

            -

            因为这样会把 $[l,r]$ 之外的东西给弄进来,所以这部分直接暴力处理

            -

            复杂度不清楚,应该还是 $O(\log n)$

            +

            例如如果 x-lowbit(x) 小于 \(l\) ,则此时不能直接 +x-=lowbit(x)

            +

            因为这样会把 \([l,r]\) +之外的东西给弄进来,所以这部分直接暴力处理

            +

            复杂度不清楚,应该还是 \(O(\log +n)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -899,7 +905,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/12/luo-gu-p2085-zui-xiao-han-shu-zhi-ti-jie/index.html b/2022/07/12/luo-gu-p2085-zui-xiao-han-shu-zhi-ti-jie/index.html index 4ff5a04b59..07757334e8 100644 --- a/2022/07/12/luo-gu-p2085-zui-xiao-han-shu-zhi-ti-jie/index.html +++ b/2022/07/12/luo-gu-p2085-zui-xiao-han-shu-zhi-ti-jie/index.html @@ -472,21 +472,36 @@

            洛谷P2085 最小函数值 题
            -

            洛谷P2085 最小函数值 题解

            题目链接:P2085 最小函数值

            +

            洛谷P2085 最小函数值 题解

            +

            题目链接:P2085 +最小函数值

            题意

            -

            有 $n$ 个函数,分别为 $F_1,F_2,\dots,F_n$。定义 $F_i(x)=A_ix^2+B_ix+C_i(x\in\mathbb N^*)$。给定这些 $A_i$、$B_i$ 和 $C_i$,请求出所有函数的所有函数值中最小的 $m$ 个(如有重复的要输出多个)。

            -

            $1 \leq A_i\le10,~10 \le B_i\le100,~100 \le C_i\le10^4$。

            +

            \(n\) 个函数,分别为 \(F_1,F_2,\dots,F_n\)。定义 \(F_i(x)=A_ix^2+B_ix+C_i(x\in\mathbb +N^*)\)。给定这些 \(A_i\)\(B_i\)\(C_i\),请求出所有函数的所有函数值中最小的 +\(m\) 个(如有重复的要输出多个)。

            +

            \(1 \leq A_i\le10,~10 \le B_i\le100,~100 +\le C_i\le10^4\)

            这是一篇随机化优化的奇怪题解

            题面十分不清楚。所以稍微修改了一下下。

            注意这里的数据范围

            -

            根据二次函数对称轴 $x=-\dfrac{b}{2a}$

            -

            不难发现这些函数均在 $[0,+\infty)$ 单调递增

            -

            那就很简单了,直接用个大根堆维护 $k$ 大值的方法搞一搞就好了

            -

            然后发现时间复杂度最坏似乎 $O(nm\log m)$

            -

            因此我们可以用随机化乱搞一下,防止特意构造的数据 $😎$

            -

            时间复杂度上界其实还是 $O(nm\log m)$

            +

            根据二次函数对称轴 \(x=-\dfrac{b}{2a}\)

            +

            不难发现这些函数均在 \([0,+\infty)\) +单调递增

            +

            那就很简单了,直接用个大根堆维护 \(k\) 大值的方法搞一搞就好了

            +

            然后发现时间复杂度最坏似乎 \(O(nm\log +m)\)

            +

            因此我们可以用随机化乱搞一下,防止特意构造的数据 \(😎\)

            +

            时间复杂度上界其实还是 \(O(nm\log +m)\)

            但是实际的复杂度比这个要低一些(然而因为常数又慢了一些

            代码:

            #include <iostream>
            @@ -907,7 +922,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/12/luo-gu-p3586-poi2015-log-ti-jie/index.html b/2022/07/12/luo-gu-p3586-poi2015-log-ti-jie/index.html index d607c4a508..9e37b71ccc 100644 --- a/2022/07/12/luo-gu-p3586-poi2015-log-ti-jie/index.html +++ b/2022/07/12/luo-gu-p3586-poi2015-log-ti-jie/index.html @@ -472,40 +472,60 @@

            洛谷P3586 [POI2015] LOG 题解
            -

            洛谷P3586 [POI2015] LOG 题解

            题目链接:P3586 [POI2015] LOG

            +

            洛谷P3586 [POI2015] LOG 题解

            +

            题目链接:P3586 +[POI2015] LOG

            题意

            -

            维护一个长度为 $n$ 的序列,一开始都是 $0$,支持以下两种操作:

            -
              -
            1. U k a 将序列中第 $k$ 个数修改为 $a$。
            2. -
            3. Z c s 在这个序列上,每次选出 $c$ 个正数,并将它们都减去 $1$,询问能否进行 $s$ 次操作。
            4. +

              维护一个长度为 \(n\) +的序列,一开始都是 \(0\),支持以下两种操作:

              +
                +
              1. U k a 将序列中第 \(k\) +个数修改为 \(a\)
              2. +
              3. Z c s 在这个序列上,每次选出 \(c\) 个正数,并将它们都减去 \(1\),询问能否进行 \(s\) 次操作。

              每次询问独立,即每次询问不会对序列进行修改。

              【数据范围】

              -

              对于 $100\%$ 的数据,$1\leq n,m\leq 10^6$,$1\leq k,c\leq n$,$0\leq a\leq 10^9$,$1\leq s\leq 10^9$。

              +

              对于 \(100\%\) 的数据,\(1\leq n,m\leq 10^6\)\(1\leq k,c\leq n\)\(0\leq a\leq 10^9\)\(1\leq s\leq 10^9\)

            不是区间询问。注意了嗷。

            先考虑2操作

            -

            对于大于 $s$ 的数,显然最多只能减 $s$ 次

            -

            对于小于等于 $s$ 的数,肯定要把它全部减成 $0$

            -

            设 $S=\sum x_i [x_i\le s]$ ,则剩下的 $c\times s - S$ 由大于 $s$ 的数贡献

            -

            设 $\text{cnt} = \sum [x_i \le s]$ ,则大于 $s$ 的数共有 $(n-\text{cnt})$ 个

            -

            于是我们只要判断下面的柿子是否成立

            -

            -

            问题就转化为了如何求解 $S$ 和 $\text{cnt}$

            +

            对于大于 \(s\) 的数,显然最多只能减 +\(s\)

            +

            对于小于等于 \(s\) +的数,肯定要把它全部减成 \(0\)

            +

            \(S=\sum x_i [x_i\le s]\) +,则剩下的 \(c\times s - S\) 由大于 +\(s\) 的数贡献

            +

            \(\text{cnt} = \sum [x_i \le s]\) +,则大于 \(s\) 的数共有 \((n-\text{cnt})\)

            +

            于是我们只要判断下面的柿子是否成立 \[ +(n-\text{cnt}) \times s \ge c \times s -S +\]\[ +S \ge s \times (c-n+\text{cnt}) +\] 问题就转化为了如何求解 \(S\) +和 \(\text{cnt}\)

              -
            • 「比一个数小的数的数量」,显然树状数组

              -
            • +
            • 「比一个数小的数的数量」,显然树状数组

            • 「比一个数小的数的和」,我们把这个和看成这些数的权值

              -

              那么这个就是上个问题的 add(x,1) 改成 add(x,x),还是树状数组

              -
            • +

              那么这个就是上个问题的 add(x,1) 改成 +add(x,x),还是树状数组

            -

            当然,我们直接用树状数组肯定是不行的,因为 $a\le 10^9$

            -

            考虑把原数组(本题中只有 $0$ )和所有询问中的 $a,s$ 离散化

            +

            当然,我们直接用树状数组肯定是不行的,因为 \(a\le 10^9\)

            +

            考虑把原数组(本题中只有 \(0\) +)和所有询问中的 \(a,s\) 离散化

            相信学过主席树的都知道这个操作吧(其实没学过也没关系)

            -

            时间复杂度 $O(m \log m)$

            +

            时间复杂度 \(O(m \log m)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -941,7 +961,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/12/luo-gu-p3879-tjoi2010-yue-du-li-jie-ti-jie/index.html b/2022/07/12/luo-gu-p3879-tjoi2010-yue-du-li-jie-ti-jie/index.html index 6dfc53d5b6..b0d83772b8 100644 --- a/2022/07/12/luo-gu-p3879-tjoi2010-yue-du-li-jie-ti-jie/index.html +++ b/2022/07/12/luo-gu-p3879-tjoi2010-yue-du-li-jie-ti-jie/index.html @@ -472,20 +472,38 @@

            洛谷P3879 [TJOI2010] 阅读理
            -

            洛谷P3879 [TJOI2010] 阅读理解 题解

            题目链接:P3879 [TJOI2010] 阅读理解

            +

            洛谷P3879 [TJOI2010] 阅读理解 +题解

            +

            题目链接:P3879 +[TJOI2010] 阅读理解

            题意

            -

            英语老师留了 $N$ 篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些生词都在哪几篇短文中出现过。

            -

            第一行为整数 $N$ ,表示短文篇数,其中每篇短文只含空格和小写字母。

            -

            按下来的 $N$ 行,每行描述一篇短文。每行的开头是一个整数 $L$ ,表示这篇短文由 $L$ 个单词组成。接下来是 $L$ 个单词,单词之间用一个空格分隔。

            -

            然后为一个整数 $M$ ,表示要做几次询问。后面有 $M$ 行,每行表示一个要统计的生词。

            -

            对于 $100\%$ 的数据,$1\le M\le 10^4$,$1\le N\le 10^3$ 。

            -

            每篇短文长度(含相邻单词之间的空格)$\le 5\times 10^3$ 字符,每个单词长度 $\le 20$ 字符。

            +

            英语老师留了 \(N\) +篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些生词都在哪几篇短文中出现过。

            +

            第一行为整数 \(N\) +,表示短文篇数,其中每篇短文只含空格和小写字母。

            +

            按下来的 \(N\) +行,每行描述一篇短文。每行的开头是一个整数 \(L\) ,表示这篇短文由 \(L\) 个单词组成。接下来是 \(L\) 个单词,单词之间用一个空格分隔。

            +

            然后为一个整数 \(M\) +,表示要做几次询问。后面有 \(M\) +行,每行表示一个要统计的生词。

            +

            对于 \(100\%\) 的数据,\(1\le M\le 10^4\)\(1\le N\le 10^3\)

            +

            每篇短文长度(含相邻单词之间的空格)\(\le +5\times 10^3\) 字符,每个单词长度 \(\le +20\) 字符。

            -

            要写 $\tt{trie}$ 也不是不行。但是懒。

            -

            直接用 $\tt{unordered_map+vector}$ 水过

            +

            要写 \(\tt{trie}\) +也不是不行。但是懒。

            +

            直接用 \(\tt{unordered\_map+vector}\) 水过

            跑的还蛮快。注意要去重(不想用set,常数太大)

            -

            时间复杂度 $O(\sum |s_i|\times m)$

            +

            时间复杂度 \(O(\sum |s_i|\times +m)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -900,7 +918,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/12/luo-gu-p3958-noip2017-ti-gao-zu-nai-luo-ti-jie/index.html b/2022/07/12/luo-gu-p3958-noip2017-ti-gao-zu-nai-luo-ti-jie/index.html index fa3ae1c9ae..0d42177481 100644 --- a/2022/07/12/luo-gu-p3958-noip2017-ti-gao-zu-nai-luo-ti-jie/index.html +++ b/2022/07/12/luo-gu-p3958-noip2017-ti-gao-zu-nai-luo-ti-jie/index.html @@ -472,20 +472,41 @@

            洛谷P3958 [NOIP2017 提高组]
            -

            洛谷P3958 [NOIP2017 提高组] 奶酪 题解

            题目链接:P3958 [NOIP2017 提高组] 奶酪

            +

            洛谷P3958 [NOIP2017 提高组] +奶酪 题解

            +

            题目链接:P3958 +[NOIP2017 提高组] 奶酪

            题意

            -

            现有一块大奶酪,它的高度为 $h$,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中,奶酪的下表面为 $z = 0$,奶酪的上表面为 $z = h$。

            -

            现在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交,Jerry 则可以从奶酪下表面跑进空洞;如果一个空洞与上表面相切或是相交,Jerry 则可以从空洞跑到奶酪上表面。

            -

            位于奶酪下表面的 Jerry 想知道,在不破坏奶酪的情况下,能否利用已有的空洞跑 到奶酪的上表面去?

            -

            空间内两点 $P_1(x_1,y_1,z_1)$、$P_2(x_2,y_2,z_2)$ 的距离公式如下:

            -

            对于 $100\%$ 的数据,$1 \le n \le 1\times 10^3$,$1 \le h , r \le 10^9$,$T \le 20$,坐标的绝对值不超过 $10^9$。

            +

            现有一块大奶酪,它的高度为 \(h\),它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中,奶酪的下表面为 +\(z = 0\),奶酪的上表面为 \(z = h\)

            +

            现在,奶酪的下表面有一只小老鼠 +Jerry,它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 +Jerry +可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交,Jerry +则可以从奶酪下表面跑进空洞;如果一个空洞与上表面相切或是相交,Jerry +则可以从空洞跑到奶酪上表面。

            +

            位于奶酪下表面的 Jerry +想知道,在不破坏奶酪的情况下,能否利用已有的空洞跑 到奶酪的上表面去?

            +

            空间内两点 \(P_1(x_1,y_1,z_1)\)\(P_2(x_2,y_2,z_2)\) 的距离公式如下:

            +

            \[ +\mathrm{dist}(P_1,P_2)=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2} +\]

            +

            对于 \(100\%\) 的数据,\(1 \le n \le 1\times 10^3\)\(1 \le h , r \le 10^9\)\(T \le 20\),坐标的绝对值不超过 \(10^9\)

            基本思想使用并查集维护连通性

            -

            单独处理与边界相交的空洞,分别用数组 $c_1,c_2$ 记录

            +

            单独处理与边界相交的空洞,分别用数组 \(c_1,c_2\) 记录

            然后对于每个空洞两两判断是否相交,如果相交则并查集合并

            -

            时间复杂度 $O(Qn^2)$

            +

            时间复杂度 \(O(Qn^2)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -897,7 +918,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/12/luo-gu-p4185-usaco18jan-mootube-g-ti-jie/index.html b/2022/07/12/luo-gu-p4185-usaco18jan-mootube-g-ti-jie/index.html index 12c9a2a600..eef51a9752 100644 --- a/2022/07/12/luo-gu-p4185-usaco18jan-mootube-g-ti-jie/index.html +++ b/2022/07/12/luo-gu-p4185-usaco18jan-mootube-g-ti-jie/index.html @@ -476,19 +476,29 @@

            洛谷P4185 [USACO18JAN]MooTube
            -

            洛谷P4185 [USACO18JAN]MooTube G 题解

            题目链接:P4185 [USACO18JAN]MooTube G

            +

            洛谷P4185 +[USACO18JAN]MooTube G 题解

            +

            题目链接:P4185 +[USACO18JAN]MooTube G

            题意

            -

            给出一棵无根树, $m \le 10^5$ 次询问,给出 $k,v$ ,询问共有多少结点 $u$ 满足 $u$ 到 $v$ 的路径上最小边权不小于 $k$ 。

            +

            给出一棵无根树, \(m \le 10^5\) +次询问,给出 \(k,v\) ,询问共有多少结点 +\(u\) 满足 \(u\)\(v\) 的路径上最小边权不小于 \(k\)

            考虑把所有相互满足条件的结点用并查集合并

            -

            注意到当 $k$ 减小时,合并后的连通块数量单调递减

            -

            考虑将询问按 $k$ 降序排序,离线处理

            +

            注意到当 \(k\) +减小时,合并后的连通块数量单调递减

            +

            考虑将询问按 \(k\) +降序排序,离线处理

            每次去找没合并过的边显然太麻烦

            继续利用单调性,将所有边按边权降序排序

            这样我们扫一遍就能处理所有询问了

            复杂度瓶颈在于排序。

            -

            时间复杂度 $O(m \log m)$

            +

            时间复杂度 \(O(m \log m)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -905,7 +915,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/13/luo-gu-p2922-usaco08dec-secret-message-g-ti-jie/index.html b/2022/07/13/luo-gu-p2922-usaco08dec-secret-message-g-ti-jie/index.html index 9a87ea381c..8b516e1311 100644 --- a/2022/07/13/luo-gu-p2922-usaco08dec-secret-message-g-ti-jie/index.html +++ b/2022/07/13/luo-gu-p2922-usaco08dec-secret-message-g-ti-jie/index.html @@ -472,19 +472,40 @@

            洛谷P2922 [USACO08DEC]Secret M
            -

            洛谷P2922 [USACO08DEC]Secret Message G 题解

            题目链接:P2922 [USACO08DEC]Secret Message G

            +

            洛谷P2922 +[USACO08DEC]Secret Message G 题解

            +

            题目链接:P2922 +[USACO08DEC]Secret Message G

            题意

            -

            一句话题意:$m$ 次询问,查询已有的 $n$ 个 $\tt{01}$ 串中,有多少串 $t_i$ 能使询问串 $s$ 成为其前缀。

            +

            一句话题意:\(m\) 次询问,查询已有的 +\(n\)\(\tt{01}\) 串中,有多少串 \(t_i\) 能使询问串 \(s\) 成为其前缀。

            贝茜正在领导奶牛们逃跑.为了联络,奶牛们互相发送秘密信息.

            -

            信息是二进制的,共有 $M$($1 \le M \le 50000$)条,反间谍能力很强的约翰已经部分拦截了这些信息,知道了第 $i$ 条二进制信息的前 $b_i$($l \le b_i \le 10000$)位,他同时知道,奶牛使用 $N$($1 \le N \le 50000$)条暗号.但是,他仅仅知道第 $j$ 条暗号的前 $c_j$($1 \le c_j \le 10000$)位。

            -

            对于每条暗号 $j$,他想知道有多少截得的信息能够和它匹配。也就是说,有多少信息和这条暗号有着相同的前缀。当然,这个前缀长度必须等于暗号和那条信息长度的较小者。

            -

            在输入文件中,位的总数(即 $\sum b_i + \sum c_i$)不会超过 $500000$。

            +

            信息是二进制的,共有 \(M\)\(1 \le M \le +50000\))条,反间谍能力很强的约翰已经部分拦截了这些信息,知道了第 +\(i\) 条二进制信息的前 \(b_i\)\(l \le +b_i \le 10000\))位,他同时知道,奶牛使用 \(N\)\(1 \le N +\le 50000\))条暗号.但是,他仅仅知道第 \(j\) 条暗号的前 \(c_j\)\(1 \le +c_j \le 10000\))位。

            +

            对于每条暗号 \(j\),他想知道有多少截得的信息能够和它匹配。也就是说,有多少信息和这条暗号有着相同的前缀。当然,这个前缀长度必须等于暗号和那条信息长度的较小者。

            +

            在输入文件中,位的总数(即 \(\sum b_i + +\sum c_i\))不会超过 \(500000\)

            -

            考虑建 $\tt{trie}$ 树处理

            +

            考虑建 \(\tt{trie}\) 树处理

            没了,就这么简单。

            具体见代码,有一些小的细节要注意

            -

            时间复杂度 $O(\sum b_i + M \sum c_i)$

            +

            时间复杂度 \(O(\sum b_i + M \sum +c_i)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -921,7 +942,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/13/luo-gu-p3966-tjoi2013-dan-ci-ti-jie/index.html b/2022/07/13/luo-gu-p3966-tjoi2013-dan-ci-ti-jie/index.html index 8009b54538..b8a37b4424 100644 --- a/2022/07/13/luo-gu-p3966-tjoi2013-dan-ci-ti-jie/index.html +++ b/2022/07/13/luo-gu-p3966-tjoi2013-dan-ci-ti-jie/index.html @@ -472,18 +472,23 @@

            洛谷P3966 [TJOI2013]单词 题
            -

            洛谷P3966 [TJOI2013]单词 题解

            题目链接:P3966 [TJOI2013]单词

            +

            洛谷P3966 [TJOI2013]单词 题解

            +

            题目链接:P3966 +[TJOI2013]单词

            题意

            小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次。

            -

            $1 \leq n \leq 200$,单词总长度不超过 $10^6$。

            +

            \(1 \leq n \leq +200\),单词总长度不超过 \(10^6\)

            瞎搞做法能跑到最优解前3页也是有趣

            这个文章看样例应该是单词间有空格的

            于是我们就把所有单词全部加上去,用#分隔

            然后就变成 P5357 AC自动机二次加强版 了

            具体的就是把暴力跳fail数组改成topo排序更新

            -

            时间复杂度 $O(26 \times \sum s_i)$

            +

            时间复杂度 \(O(26 \times \sum +s_i)\)

            代码:

            #include <iostream>
             #include <string>
            @@ -944,7 +949,7 @@ 

             站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/13/luo-gu-p4407-jsoi2009-dian-zi-zi-dian-ti-jie/index.html b/2022/07/13/luo-gu-p4407-jsoi2009-dian-zi-zi-dian-ti-jie/index.html index 27da69c767..d1f6ce458b 100644 --- a/2022/07/13/luo-gu-p4407-jsoi2009-dian-zi-zi-dian-ti-jie/index.html +++ b/2022/07/13/luo-gu-p4407-jsoi2009-dian-zi-zi-dian-ti-jie/index.html @@ -476,50 +476,87 @@

            洛谷P4407 [JSOI2009] 电子字
            -

            洛谷P4407 [JSOI2009] 电子字典 题解

            题目链接:P4407 [JSOI2009] 电子字典

            +

            洛谷P4407 [JSOI2009] 电子字典 +题解

            +

            题目链接:P4407 +[JSOI2009] 电子字典

            -

            题意:人们在英文字典中查找某个单词的时候可能不知道该单词的完整拼法,而只知道该单词的一个错误的近似拼法,这时人们可能陷入困境,为了查找一个单词而浪费大量的时间。带有模糊查询功能的电子字典能够从一定程度上解决这一问题:用户只要输入一个字符串,电子字典就返回与该单词编辑距离最小的几个单词供用户选择。

            -

            字符串a与字符串 $b$ 的编辑距离是指:允许对 $a$ 或 $b$ 串进行下列“编辑”操作,将 $a$ 变为 $b$ 或 $b$ 变为 $a$ ,最少“编辑”次数即为距离。

            -
              +

              题意:人们在英文字典中查找某个单词的时候可能不知道该单词的完整拼法,而只知道该单词的一个错误的近似拼法,这时人们可能陷入困境,为了查找一个单词而浪费大量的时间。带有模糊查询功能的电子字典能够从一定程度上解决这一问题:用户只要输入一个字符串,电子字典就返回与该单词编辑距离最小的几个单词供用户选择。

              +

              字符串a与字符串 \(b\) +的编辑距离是指:允许对 \(a\)\(b\) 串进行下列“编辑”操作,将 \(a\) 变为 \(b\)\(b\) 变为 \(a\) ,最少“编辑”次数即为距离。

              +
              1. 删除串中某个位置的字母;
              2. 添加一个字母到串中某个位置;
              3. 替换串中某一位置的一个字母为另一个字母;
              -

              给定 $n$ 个不同的单词,$m$ 次询问,对于一个待查询字符串,如果它是单词,则返回 $-1$;如果它不是单词,则返回字典中有多少个单词与它的编辑距离为 $1$ 。

              -

              $n,m \le 10,000$ ,每个单词长度 $\le 20$ ,每个待查询字符串长度 $\le 20$

              +

              给定 \(n\) 个不同的单词,\(m\) +次询问,对于一个待查询字符串,如果它是单词,则返回 \(-1\);如果它不是单词,则返回字典中有多少个单词与它的编辑距离为 +\(1\)

              +

              \(n,m \le 10,000\) ,每个单词长度 +\(\le 20\) ,每个待查询字符串长度 \(\le 20\)

            -

            首先建 $\tt{trie}$ 树很容易想到

            -

            注意到这个数据范围很小,尝试在 $\tt{trie}$ 树上暴搜。

            +

            首先建 \(\tt{trie}\) +树很容易想到

            +

            注意到这个数据范围很小,尝试在 \(\tt{trie}\) 树上暴搜。

            显然我们需要记录的状态有:

            -
              -
            1. 当前所在结点 $u$
            2. -
            3. 当前枚举的字符串长度 $l$ (实际上并不完全是,只是判断长度用的)
            4. -
            5. 是否使用修改 $\text{ck}=0/1$ ( $0$ 表示没有使用修改)
            6. +
                +
              1. 当前所在结点 \(u\)
              2. +
              3. 当前枚举的字符串长度 \(l\) +(实际上并不完全是,只是判断长度用的)
              4. +
              5. 是否使用修改 \(\text{ck}=0/1\) ( +\(0\) 表示没有使用修改)

              下面是算法分析,比较抽象,建议直接看代码。所以为什么我要写

              -

              方便起见,记当前字符为 $s_l$

              -

              当 $\text{ck}=1$ 时,如果 $u$ 存在字符为 $s_l$ 的子结点,直接搜。

              -

              当 $\text{ck}=0$ 时,除了搜子结点,还需要考虑每个操作的处理

              +

              方便起见,记当前字符为 \(s_l\)

              +

              \(\text{ck}=1\) 时,如果 \(u\) 存在字符为 \(s_l\) 的子结点,直接搜。

              +

              \(\text{ck}=0\) +时,除了搜子结点,还需要考虑每个操作的处理

                -
              • 操作1:删除某个字符。直接搜 $l+1$ 就好了
              • -
              • 操作2:增加某个字符。直接枚举字符,搜 $l$ 就好了
              • -
              • 操作3:替换某个字符。枚举一个不同于 $s_{l+1}$ 的字符,搜 $l+1$
              • +
              • 操作1:删除某个字符。直接搜 \(l+1\) +就好了
              • +
              • 操作2:增加某个字符。直接枚举字符,搜 \(l\) 就好了
              • +
              • 操作3:替换某个字符。枚举一个不同于 \(s_{l+1}\) 的字符,搜 \(l+1\)

              注意去重!!!!

              -

              因为 $\tt{abc}$ 在 $\tt{a}$ 前后增加一个 $\tt{a}$ ,都会变成 $\tt{aabc}$ !!!

              +

              因为 \(\tt{abc}\)\(\tt{a}\) 前后增加一个 \(\tt{a}\) ,都会变成 \(\tt{aabc}\) !!!

              时间复杂度分析

              比较有趣。

              -

              对于一个长度为 $20$ 的串,思考我们至多枚举了多少修改串

              -

              一共有 $21$ 个位置可添加字符,$20$ 个字符可替换,$20$ 个字符可删除,则

              -

              $\tt{trie}$ 树树高至多在 $20$ ,$m\le 10^4$

              -

              则粗略算一下,计算量为

              -

              当然这个估计显然是过于大了。

              +

              对于一个长度为 \(20\) +的串,思考我们至多枚举了多少修改串

              +

              一共有 \(21\) +个位置可添加字符,\(20\) +个字符可替换,\(20\) 个字符可删除,则 +\[ +(21+20) \times 26 + 20 = 1086 +\] \(\tt{trie}\) 树树高至多在 +\(20\)\(m\le 10^4\)

              +

              则粗略算一下,计算量为 \[ +1086 \times 2\times 10^5 = 2.172\times 10^8 +\] 当然这个估计显然是过于大了。

              因为很多枚举的修改串拥有相同前缀

              -

              也就是说,这些串并不是每个都要从上到下跑 $\tt{trie}$ 树的

              +

              也就是说,这些串并不是每个都要从上到下跑 \(\tt{trie}\) 树的

              而且我们有很多的修改串会被剪掉,这样看来完全可过

              -

              强行写个时间复杂度,那可能是 $O(\max\{s_i\}\times\sum |s_i|^2)$ 吧

              +

              强行写个时间复杂度,那可能是 \(O(\max\{s_i\}\times\sum |s_i|^2)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -965,7 +1002,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/14/luo-gu-p2444-poi2000-bing-du-ti-jie/index.html b/2022/07/14/luo-gu-p2444-poi2000-bing-du-ti-jie/index.html index 8b7e0d0e04..ffc06842c0 100644 --- a/2022/07/14/luo-gu-p2444-poi2000-bing-du-ti-jie/index.html +++ b/2022/07/14/luo-gu-p2444-poi2000-bing-du-ti-jie/index.html @@ -472,28 +472,43 @@

              洛谷P2444 [POI2000]病毒 题
              -

              洛谷P2444 [POI2000]病毒 题解

              题目链接:P2444 [POI2000]病毒

              +

              洛谷P2444 [POI2000]病毒 题解

              +

              题目链接:P2444 +[POI2000]病毒

              题意

              二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

              示例:

              -

              例如如果 $\{011, 11, 00000\}$ 为病毒代码段,那么一个可能的无限长安全代码就是 $010101 \ldots$。如果 $\{01, 11, 000000\}$ 为病毒代码段,那么就不存在一个无限长的安全代码。

              +

              例如如果 \(\{011, 11, 00000\}\) +为病毒代码段,那么一个可能的无限长安全代码就是 \(010101 \ldots\)。如果 \(\{01, 11, 000000\}\) +为病毒代码段,那么就不存在一个无限长的安全代码。

              现在给出所有的病毒代码段,判断是否存在无限长的安全代码。

              -

              $1 \leq n \leq 2000$,所有病毒代码段的总长度不超过 $3 \times 10^4$。

              +

              \(1 \leq n \leq +2000\),所有病毒代码段的总长度不超过 \(3 \times 10^4\)

              -

              不难发现这是一个 $\tt{AC}$ 自动机的题目

              -

              考虑一个安全代码在 $\tt{AC}$ 自动机上是怎么跑的

              -

              显然它会从 $0$ 结点开始跑,然后最后在一个环上面跑

              +

              不难发现这是一个 \(\tt{AC}\) +自动机的题目

              +

              考虑一个安全代码在 \(\tt{AC}\) +自动机上是怎么跑的

              +

              显然它会从 \(0\) +结点开始跑,然后最后在一个环上面跑

              这个环显然不能包含「是字符串结尾的结点」

              仔细想一想,其实不止这些结点!

              -

              对于一个结点 $u$ ,如果它的 $\tt{fail}$ 指针所指向的结点是「是字符串结尾的结点」

              -

              那么 $u$ 也是不能走的我们称这些结点为 「含病毒后缀的结点」

              -

              当然,如果一个结点的 $\tt{fail}$ 指针所指向的结点是「含病毒后缀的结点」,

              +

              对于一个结点 \(u\) ,如果它的 \(\tt{fail}\) +指针所指向的结点是「是字符串结尾的结点」

              +

              那么 \(u\) +也是不能走的我们称这些结点为 「含病毒后缀的结点」

              +

              当然,如果一个结点的 \(\tt{fail}\) +指针所指向的结点是「含病毒后缀的结点」,

              那么这个结点也是「含病毒后缀的结点」,同样不可以走

              说了这么多,其实只需要在板子上加一句

              if(ed[fail[trie[u][i]]])++ed[trie[u][i]];

              就好了

              -

              时间复杂度 $O(\sum |s_i|)$

              +

              时间复杂度 \(O(\sum |s_i|)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -947,7 +962,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/14/luo-gu-p3294-scoi2016-bei-dan-ci-ti-jie/index.html b/2022/07/14/luo-gu-p3294-scoi2016-bei-dan-ci-ti-jie/index.html index 76926c28d1..b5720e827c 100644 --- a/2022/07/14/luo-gu-p3294-scoi2016-bei-dan-ci-ti-jie/index.html +++ b/2022/07/14/luo-gu-p3294-scoi2016-bei-dan-ci-ti-jie/index.html @@ -472,42 +472,68 @@

              洛谷P3294 [SCOI2016]背单词
              -

              洛谷P3294 [SCOI2016]背单词 题解

              题目链接:P3294 [SCOI2016]背单词

              +

              洛谷P3294 [SCOI2016]背单词 +题解

              +

              题目链接:P3294 +[SCOI2016]背单词

              -

              题意:给定 $n$ 个不同的字符串,求一个最优顺序使得花费最小

              -

              设当前字符串为 $a$ ,位于排列中的 $x$ 位置

              +

              题意:给定 \(n\) +个不同的字符串,求一个最优顺序使得花费最小

              +

              设当前字符串为 \(a\) ,位于排列中的 +\(x\) 位置

                -
              • 如果 $a$ 存在后缀且 $a$ 的后缀在 $a$ 之后,花费增加 $n^2$
              • -
              • 如果 $a$ 不存在后缀,则花费增加 $x$
              • -
              • 设 $y$ 为 $a$ 之前与其相距最小的,且是 $a$ 的后缀的字符串的位置(前提是 $a$ 存在后缀),则花费增加 $x-y$
              • +
              • 如果 \(a\) 存在后缀且 \(a\) 的后缀在 \(a\) 之后,花费增加 \(n^2\)
              • +
              • 如果 \(a\) 不存在后缀,则花费增加 +\(x\)
              • +
              • \(y\)\(a\) +之前与其相距最小的,且是 \(a\) 的后缀的字符串的位置(前提是 \(a\) 存在后缀),则花费增加 \(x-y\)

              根据贪心,前两个条件基本上没有用

              同时为了方便处理,把所有字符串反转,考虑其前缀。

              原问题转化为:

              -

              设某个字符串为 $a_i$ ,位于排列中的 $x_i$ 位置

              +

              设某个字符串为 \(a_i\) +,位于排列中的 \(x_i\) 位置

                -
              • $a_i$ 所有前缀必须在 $a_i$ 之前

                -
              • -
              • 设 $y_i$ 为 $a_i$ 之前与其相距最小的,且是 $a_i$ 的前缀的字符串的位置

                -

                如果 $a$ 不存在前缀,则 $y_i=0$ ,记 $v_i=x_i-y_i$

                -
              • +
              • \(a_i\) 所有前缀必须在 \(a_i\) 之前

              • +
              • \(y_i\)\(a_i\) +之前与其相距最小的,且是 \(a_i\) 的前缀的字符串的位置

              -

              给定 $n$ 个字符串,求一种排列顺序,使得 $\sum v_i$ 最小,求出这个最小值。

              +

              如果 \(a\) 不存在前缀,则 \(y_i=0\) ,记 \(v_i=x_i-y_i\)

              +

              给定 \(n\) +个字符串,求一种排列顺序,使得 \(\sum +v_i\) 最小,求出这个最小值。

              -

              考虑建 $\tt{trie}$ 树以存储字符串

              +

              考虑建 \(\tt{trie}\) +树以存储字符串

              然后重构整棵树,只保留「是字符串结尾的结点」,注意根节点也要保留

              然后对于每个结点,将其子结点按子树大小从小到大排序

              然后跑一遍dfs就好了

              重构是为了方便排序子结点。

              排序的原因:

              -

              设当前结点为 $u$ ,则 $u$ 的所有子结点都有相同的前缀

              -

              这个前缀就是根节点到 $u$ 形成的字符串。

              -

              对于排序后在后面的子结点,想在排列中距离 $u$ 尽可能小

              +

              设当前结点为 \(u\) ,则 \(u\) 的所有子结点都有相同的前缀

              +

              这个前缀就是根节点到 \(u\) +形成的字符串。

              +

              对于排序后在后面的子结点,想在排列中距离 \(u\) 尽可能小

              就需要前面的子结点所在子树大小尽可能小,这样他们之间夹着的字符串就少

              根据贪心,不难发现这样可以最小化答案

              -

              时间复杂度 $O(L \log L)$ ,其中 $L=\sum |s_i|$

              +

              时间复杂度 \(O(L \log L)\) ,其中 +\(L=\sum |s_i|\)

              代码:

              #include <iostream>
               #include <string>
              @@ -963,7 +989,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/15/luo-gu-p4052-jsoi2007-wen-ben-sheng-cheng-qi-ti-jie/index.html b/2022/07/15/luo-gu-p4052-jsoi2007-wen-ben-sheng-cheng-qi-ti-jie/index.html index 682b3fd4dc..e929ce8c2b 100644 --- a/2022/07/15/luo-gu-p4052-jsoi2007-wen-ben-sheng-cheng-qi-ti-jie/index.html +++ b/2022/07/15/luo-gu-p4052-jsoi2007-wen-ben-sheng-cheng-qi-ti-jie/index.html @@ -480,33 +480,61 @@

              洛谷P4052 [JSOI2007]文本生
              -

              洛谷P4052 [JSOI2007]文本生成器 题解

              题目链接:P4052 [JSOI2007]文本生成器

              +

              洛谷P4052 +[JSOI2007]文本生成器 题解

              +

              题目链接:P4052 +[JSOI2007]文本生成器

              题意

              -

              JSOI 交给队员 ZYX 一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是 GW 文本生成器 v6 版。

              -

              该软件可以随机生成一些文章——总是生成一篇长度固定且完全随机的文章。 也就是说,生成的文章中每个字符都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章 $s$ 包含单词 $t$,当且仅当单词 $t$ 是文章 $s$ 的子串)。但是,即使按照这样的标准,使用者现在使用的 GW 文本生成器 v6 版所生成的文章也是几乎完全不可读的。ZYX 需要指出 GW 文本生成器 v6 生成的所有文本中,可读文本的数量,以便能够成功获得 v7 更新版。你能帮助他吗?

              -

              答案对 $10^4 + 7$ 取模。

              +

              JSOI 交给队员 ZYX +一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是 +GW 文本生成器 v6 版。

              +

              该软件可以随机生成一些文章——总是生成一篇长度固定且完全随机的文章。 +也就是说,生成的文章中每个字符都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章 +\(s\) 包含单词 \(t\),当且仅当单词 \(t\) 是文章 \(s\) +的子串)。但是,即使按照这样的标准,使用者现在使用的 GW 文本生成器 v6 +版所生成的文章也是几乎完全不可读的。ZYX 需要指出 GW 文本生成器 v6 +生成的所有文本中,可读文本的数量,以便能够成功获得 v7 +更新版。你能帮助他吗?

              +

              答案对 \(10^4 + 7\) 取模。

              对于全部的测试点,保证:

                -
              • $1 \leq n \leq 60$,$1 \leq m \leq 100$。
              • -
              • $1 \leq |s_i| \leq 100$,其中 $|s_i|$ 表示字符串 $s_i$ 的长度。
              • -
              • $s_i$ 中只含大写英文字母。
              • +
              • \(1 \leq n \leq 60\)\(1 \leq m \leq 100\)
              • +
              • \(1 \leq |s_i| \leq 100\),其中 +\(|s_i|\) 表示字符串 \(s_i\) 的长度。
              • +
              • \(s_i\) 中只含大写英文字母。
              -

              显然要建 $\tt{AC}$ 自动机。包含单词,比较麻烦。

              +

              显然要建 \(\tt{AC}\) +自动机。包含单词,比较麻烦。

              考虑容斥,计算不包含单词的数量(不可读文本数量)

              「可读文本数量」等于「所有文本数量」减去「不可读文本数量」

              -

              「所有文本数量」显然是 $26^m$

              -

              不难发现,一个不可读文本在 $\tt{AC}$ 自动机上跑的时候,是不会碰到「危险结点」的

              +

              「所有文本数量」显然是 \(26^m\)

              +

              不难发现,一个不可读文本在 \(\tt{AC}\) +自动机上跑的时候,是不会碰到「危险结点」的

              「危险结点」包括「是字符串结尾」的结点和「某个或某些后缀是单词」的结点

              危险结点的处理比较简单,只要在建自动机的时候处理一下就好了,具体看代码

              然后这个题目是计数题,考虑dp

              -

              设 $f_{i,j}$ 表示走到 $j$ 号结点,文本长度为 $i$ 时的方案数

              -

              显然 $f_{0,0} = 1$

              -

              这里的 $(u,v) \in E$ 表示在 $\tt{AC}$ 自动机上存在 $u$ 指向 $v$ 的边

              -

              「不可读文本数量」就等于 $\sum_{0 \le i \le \text{tot}} f_{m,i}$

              -

              时间复杂度 $O(26 \times m \sum |s_i|)$

              +

              \(f_{i,j}\) 表示走到 \(j\) 号结点,文本长度为 \(i\) 时的方案数

              +

              显然 \(f_{0,0} = 1\) \[ +f_{i+1,v} = \sum_{(u,v) \in E} f_{i,u} +\] 这里的 \((u,v) \in E\) 表示在 +\(\tt{AC}\) 自动机上存在 \(u\) 指向 \(v\) 的边

              +

              「不可读文本数量」就等于 \(\sum_{0 \le i +\le \text{tot}} f_{m,i}\)

              +

              时间复杂度 \(O(26 \times m \sum +|s_i|)\)

              完整代码:

              #include <iostream>
               #include <string>
              @@ -970,7 +998,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/15/luo-gu-p4113-heoi2012-cai-hua-ti-jie/index.html b/2022/07/15/luo-gu-p4113-heoi2012-cai-hua-ti-jie/index.html index a780e84dd2..e5c1a0a55b 100644 --- a/2022/07/15/luo-gu-p4113-heoi2012-cai-hua-ti-jie/index.html +++ b/2022/07/15/luo-gu-p4113-heoi2012-cai-hua-ti-jie/index.html @@ -472,33 +472,51 @@

              洛谷P4113 [HEOI2012]采花 题
              -

              洛谷P4113 [HEOI2012]采花 题解

              题目链接:P4113 [HEOI2012]采花

              +

              洛谷P4113 [HEOI2012]采花 题解

              +

              题目链接:P4113 +[HEOI2012]采花

              题意:萧薰儿是古国的公主,平时的一大爱好是采花。

              今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。

              -

              花园足够大,容纳了 $n$ 朵花,共有 $c$ 种颜色,用整数 $1 \sim c$ 表示。且花是排成一排的,以便于公主采花。公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴。同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。

              -

              由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了 $m$ 个行程,然后一一向你询问公主能采到的花共有几种不同的颜色。

              -

              $1 \leq n, c, m \leq 2 \times 10^6$。

              -

              对于全部的测试点,保证 $1 \leq x_i \leq c$,$1 \leq l \leq r \leq n$。

              +

              花园足够大,容纳了 \(n\) 朵花,共有 +\(c\) 种颜色,用整数 \(1 \sim c\) +表示。且花是排成一排的,以便于公主采花。公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴。同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。

              +

              由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了 +\(m\) +个行程,然后一一向你询问公主能采到的花共有几种不同的颜色。

              +

              \(1 \leq n, c, m \leq 2 \times +10^6\)

              +

              对于全部的测试点,保证 \(1 \leq x_i \leq +c\)\(1 \leq l \leq r \leq +n\)

              -

              其实就是 P1972 [SDOI2009] HH的项链 改了一改,

              +

              其实就是 P1972 +[SDOI2009] HH的项链 改了一改,

              本题具体的做法是预处理出所有第二次出现的数的贡献,然后离线处理询问。

              (作者语文比较烂,不知道这句话放哪里比较好,就放前面来了)

              下面是详细的解释。

              -
              -

              设数组 $\text{val} = \{1,2,2,3,1,1,3\}$

              -

              要询问一个区间 $[1,7]=\{1,2,2,3,1,1,3\}$

              -

              每个数的贡献是这样的 $\{0,0,1,0,1,0,1\}$

              +
              +

              设数组 \(\text{val} = +\{1,2,2,3,1,1,3\}\)

              +

              要询问一个区间 \([1,7]=\{1,2,2,3,1,1,3\}\)

              +

              每个数的贡献是这样的 \(\{0,0,1,0,1,0,1\}\)

              实际上我们只需要处理第二次出现的数的贡献即可

              因为出现两次是容易处理的,并且小于的不会产生贡献,大于的不会增加贡献

              不难发现区间询问可以用树状数组维护贡献

              -

              一个暴力的想法是,对于每个询问 $[l,r]$ ,我们扫一遍 $[1,l-1]$

              +

              一个暴力的想法是,对于每个询问 \([l,r]\) ,我们扫一遍 \([1,l-1]\)

              为什么要扫一遍呢?因为对于出现大于两次的,

              我们只把贡献记在了它第二次出现的位置上,

              -

              而这个数可能在 $[1,l-1]$ 和 $[l,r]$ 都出现了两次及以上

              +

              而这个数可能在 \([1,l-1]\)\([l,r]\) 都出现了两次及以上

              当然这样暴力扫肯定是不行的,考虑离线处理询问。

              -

              把询问按照 $l$ 从小到大排序,这样我们只要在询问的过程中扫就可以了

              -

              时间复杂度 $O(m \log n)$

              +

              把询问按照 \(l\) +从小到大排序,这样我们只要在询问的过程中扫就可以了

              +

              时间复杂度 \(O(m \log n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -600,7 +618,8 @@ 

              return 0; }

              参考文献

              -

              [1] https://www.luogu.com.cn/blog/zykykyk/solution-p4113

              +

              [1] https://www.luogu.com.cn/blog/zykykyk/solution-p4113

              @@ -974,7 +993,7 @@

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/16/uva11362-phone-list-ti-jie/index.html b/2022/07/16/uva11362-phone-list-ti-jie/index.html index f9088f4c3c..100c2f38fc 100644 --- a/2022/07/16/uva11362-phone-list-ti-jie/index.html +++ b/2022/07/16/uva11362-phone-list-ti-jie/index.html @@ -468,13 +468,23 @@

              UVA11362 Phone List 题解

              -

              UVA11362 Phone List 题解

              题目链接:UVA11362 Phone List

              +

              UVA11362 Phone List 题解

              +

              题目链接:UVA11362 Phone +List

              -

              题意:给定$n$个长度不超过$10$的数字串,判断是否有两个字符串$A$和$B$,满足$A$是$B$的前缀,若有,输出NO,若没有,输出YES

              +

              题意:给定\(n\)个长度不超过\(10\)的数字串,判断是否有两个字符串\(A\)\(B\),满足\(A\)\(B\)的前缀,若有,输出NO,若没有,输出YES

              -

              其实可以去建个 $\tt{trie}$ 树然后dfs的

              +

              其实可以去建个 \(\tt{trie}\) +树然后dfs的

              但是方便起见,直接sort就能过

              -

              时间复杂度 $O(Qn^2)$

              +

              时间复杂度 \(O(Qn^2)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -878,7 +888,7 @@ 

                站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/17/luo-gu-p2422-liang-hao-de-gan-jue-ti-jie/index.html b/2022/07/17/luo-gu-p2422-liang-hao-de-gan-jue-ti-jie/index.html index b926547dd9..883e24d14a 100644 --- a/2022/07/17/luo-gu-p2422-liang-hao-de-gan-jue-ti-jie/index.html +++ b/2022/07/17/luo-gu-p2422-liang-hao-de-gan-jue-ti-jie/index.html @@ -476,27 +476,56 @@

              洛谷P2422 良好的感觉 题
              -

              洛谷P2422 良好的感觉 题解

              题目链接:P2422 良好的感觉

              +

              洛谷P2422 良好的感觉 题解

              +

              题目链接:P2422 +良好的感觉

              题意

              -

              kkk 做了一个人体感觉分析器。每一天,人都有一个感受值 $A_i$,$A_i$ 越大,表示人感觉越舒适。在一段时间 $\left[i, j\right]$ 内,人的舒适程度定义为 $\left[i, j\right]$ 中最不舒服的那一天的感受值 $\times$ $\left[i, j\right]$中每一天感受值的和。现在给出 kkk 在连续 $N$ 天中的感受值,请问,在哪一段时间,kkk 感觉最舒适?

              -

              对于 $100\%$ 的数据,$1\le N\le 100000$,$1\le \texttt{感受值}\le 1000000$。

              +

              kkk 做了一个人体感觉分析器。每一天,人都有一个感受值 \(A_i\)\(A_i\) 越大,表示人感觉越舒适。在一段时间 +\(\left[i, j\right]\) +内,人的舒适程度定义为 \(\left[i, +j\right]\) 中最不舒服的那一天的感受值 \(\times\) \(\left[i, +j\right]\)中每一天感受值的和。现在给出 kkk 在连续 \(N\) 天中的感受值,请问,在哪一段时间,kkk +感觉最舒适?

              +

              对于 \(100\%\) 的数据,\(1\le N\le 100000\)\(1\le \texttt{感受值}\le 1000000\)

              题意简化一下就是

              找到一个区间,使得区间最小值乘以区间的和最大

              考虑每个最小值对区间的贡献

              -

              设当前的数为 $a_i$ ,

              -

              它能成为最小值的区间 $[l,r]$ 满足 $l\in [j+1,i],r\in [i,k-1]$

              -

              其中 $j$ 是所有 $a_j <a_i$ 中最大的 $j$ ,$k$ 是所有 $a_k < a_i$ 中最小的 $k$

              -

              设 $f_i$ 表示前 $i$ 个数中的最大答案

              -

              这里的 $j,k$ 和上面同义,$S_i$ 为前缀和,即 $S_i = \sum_{j=1}^{i}a_j$

              +

              设当前的数为 \(a_i\)

              +

              它能成为最小值的区间 \([l,r]\) 满足 +\(l\in [j+1,i],r\in [i,k-1]\)

              +

              其中 \(j\) 是所有 \(a_j <a_i\) 中最大的 \(j\)\(k\) 是所有 \(a_k +< a_i\) 中最小的 \(k\)

              +

              \(f_i\) 表示前 \(i\) 个数中的最大答案 \[ +f_i = (f_j + S_i-S_j)+(f_k+S_{k-1}-S_i) +\] 这里的 \(j,k\) +和上面同义,\(S_i\) 为前缀和,即 \(S_i = \sum_{j=1}^{i}a_j\)

              于是用单调栈维护最小值,从左往右扫,就能把左半部分搞定了

              那么右半部分怎么搞呢?

              -

              不难发现 $a_k$ 一定是第一个加入单调栈以后把 $a_i$ 踢出去的数

              -

              我们只要在踢出 $a_i$ 的时候把 $i$ 到 $k-1$ 的贡献给加到 $f_i$ 上就好了

              -

              注意最后一定要把 $a_{n+1}$ 设为一个极小值,使其能把前面所有的踢出去

              -

              时间复杂度 $O(n)$

              +

              不难发现 \(a_k\) +一定是第一个加入单调栈以后把 \(a_i\) +踢出去的数

              +

              我们只要在踢出 \(a_i\) 的时候把 +\(i\)\(k-1\) 的贡献给加到 \(f_i\) 上就好了

              +

              注意最后一定要把 \(a_{n+1}\) +设为一个极小值,使其能把前面所有的踢出去

              +

              时间复杂度 \(O(n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -906,7 +935,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/17/luo-gu-p3512-poi2010-pil-pilots-ti-jie/index.html b/2022/07/17/luo-gu-p3512-poi2010-pil-pilots-ti-jie/index.html index c7dfe69093..2bfc56ce57 100644 --- a/2022/07/17/luo-gu-p3512-poi2010-pil-pilots-ti-jie/index.html +++ b/2022/07/17/luo-gu-p3512-poi2010-pil-pilots-ti-jie/index.html @@ -468,14 +468,20 @@

              洛谷P3512 [POI2010]PIL-Pilots
              -

              洛谷P3512 [POI2010]PIL-Pilots 题解

              题目链接:P3512 [POI2010]PIL-Pilots

              +

              洛谷P3512 [POI2010]PIL-Pilots +题解

              +

              题目链接:P3512 +[POI2010]PIL-Pilots

              -

              题意:给定 $n$ 个数,找一个最长区间,使得区间中的最大值减最小值不超过 $k$

              +

              题意:给定 \(n\) +个数,找一个最长区间,使得区间中的最大值减最小值不超过 \(k\)

              题面写的太烂了

              -

              考虑用两个单调队列分别维护从 $1$ 到 $i$ 的最大值和最小值

              +

              考虑用两个单调队列分别维护从 \(1\) +到 \(i\) 的最大值和最小值

              注意pop()时候的判断,具体见代码

              -

              时间复杂度 $O(n)$

              +

              时间复杂度 \(O(n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -909,7 +915,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/17/luo-gu-p3522-poi2011-tem-temperature-ti-jie/index.html b/2022/07/17/luo-gu-p3522-poi2011-tem-temperature-ti-jie/index.html index 84706d104a..274f2cf1ae 100644 --- a/2022/07/17/luo-gu-p3522-poi2011-tem-temperature-ti-jie/index.html +++ b/2022/07/17/luo-gu-p3522-poi2011-tem-temperature-ti-jie/index.html @@ -472,19 +472,30 @@

              洛谷P3522 [POI2011]TEM-Tempera
              -

              洛谷P3522 [POI2011]TEM-Temperature 题解

              题目链接:P3522 [POI2011]TEM-Temperature

              +

              洛谷P3522 +[POI2011]TEM-Temperature 题解

              +

              题目链接:P3522 +[POI2011]TEM-Temperature

              题意

              -

              某国进行了连续 $n$ ( $1 \le n \le 1,000,000$)天的温度测量,测量存在误差,测量结果是第 $i$ 天温度在 $[l_i,r_i]$ 范围内。

              +

              某国进行了连续 \(n\) ( \(1 \le n \le +1,000,000\))天的温度测量,测量存在误差,测量结果是第 \(i\) 天温度在 \([l_i,r_i]\) 范围内。

              求最长的连续的一段,满足该段内可能温度不降。

              -

              不难发现合法区间为 $\max\{l_i\}>r_{i+1}$

              +

              不难发现合法区间为 \(\max\{l_i\}>r_{i+1}\)

              考虑单调队列维护最大值

              注意答案的更新应该用(i-1)-q[st]+1

              -

              因为如果有个 $l_i$ 特别大,把当前维护的合法的 $l$ 都给pop掉了

              -

              计数不应当是从现在的 $l_i$ 开始,而是最后一个被pop掉的

              +

              因为如果有个 \(l_i\) +特别大,把当前维护的合法的 \(l\) +都给pop掉了

              +

              计数不应当是从现在的 \(l_i\) +开始,而是最后一个被pop掉的

              这样用q[st]正好(我的代码是(l,r]的)

              -

              时间复杂度 $O(n)$

              +

              时间复杂度 \(O(n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -907,7 +918,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/18/luo-gu-p1016-noip1999-ti-gao-zu-lu-xing-jia-de-yu-suan-ti-jie/index.html b/2022/07/18/luo-gu-p1016-noip1999-ti-gao-zu-lu-xing-jia-de-yu-suan-ti-jie/index.html index 4683f0ef65..0ffef7786d 100644 --- a/2022/07/18/luo-gu-p1016-noip1999-ti-gao-zu-lu-xing-jia-de-yu-suan-ti-jie/index.html +++ b/2022/07/18/luo-gu-p1016-noip1999-ti-gao-zu-lu-xing-jia-de-yu-suan-ti-jie/index.html @@ -472,18 +472,32 @@

              洛谷P1016 [NOIP1999 提高组]
              -

              洛谷P1016 [NOIP1999 提高组] 旅行家的预算 题解

              题目链接:P1016 [NOIP1999 提高组] 旅行家的预算

              +

              洛谷P1016 [NOIP1999 +提高组] 旅行家的预算 题解

              +

              题目链接:P1016 +[NOIP1999 提高组] 旅行家的预算

              题意

              -

              一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离 $D_1$、汽车油箱的容量 $C$(以升为单位)、每升汽油能行驶的距离 $D_2$、出发点每升汽油价格$P$和沿途油站数 $N$($N$ 可以为零),油站 $i$ 离出发点的距离 $D_i$、每升汽油价格 $P_i$($i=1,2,…,N$)。计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出 No Solution

              -

              $N \le 6$,其余数字$ \le 500$。

              +

              一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离 +\(D_1\)、汽车油箱的容量 \(C\)(以升为单位)、每升汽油能行驶的距离 +\(D_2\)、出发点每升汽油价格\(P\)和沿途油站数 \(N\)\(N\) +可以为零),油站 \(i\) 离出发点的距离 +\(D_i\)、每升汽油价格 \(P_i\)\(i=1,2,…,N\))。计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出 +No Solution

              +

              \(N \le 6\),其余数字$ $。

              经典的反悔型贪心

              每次碰到一个加油站,烧掉最便宜的油(从上一个位置到现在位置)

              接着退掉比当前加油站贵的油(反悔),然后直接加满当前加油站的油

              -

              记得设一个终点站,显然它和起点的距离为 $d_1$ ,推掉所有多的油

              +

              记得设一个终点站,显然它和起点的距离为 \(d_1\) ,推掉所有多的油

              这个油价的维护直接用单调队列维护,这里用deque<node>来搞方便一些

              -

              时间复杂度 $O(n)$

              +

              时间复杂度 \(O(n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -559,7 +573,8 @@ 

              return 0; }

              参考文献

              -

              [1] https://www.luogu.com.cn/blog/hongzy/solution-p1016

              +

              [1] https://www.luogu.com.cn/blog/hongzy/solution-p1016

              @@ -929,7 +944,7 @@

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/18/luo-gu-p1631-xu-lie-he-bing-ti-jie/index.html b/2022/07/18/luo-gu-p1631-xu-lie-he-bing-ti-jie/index.html index 9166531c9d..62f9daa0d1 100644 --- a/2022/07/18/luo-gu-p1631-xu-lie-he-bing-ti-jie/index.html +++ b/2022/07/18/luo-gu-p1631-xu-lie-he-bing-ti-jie/index.html @@ -472,16 +472,22 @@

              洛谷P1631 序列合并 题解<
              -

              洛谷P1631 序列合并 题解

              题目链接:P1631 序列合并

              +

              洛谷P1631 序列合并 题解

              +

              题目链接:P1631 +序列合并

              题意

              -

              有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到$N^2$个和,求这$N^2$个和中最小的N个。

              +

              有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到\(N^2\)个和,求这\(N^2\)个和中最小的N个。

              对于100%的数据中,满足1<=N<=100000。

              -

              维护一个大根堆,初始有 $N$ 个极大值

              -

              贪心地选择较小的 $A_i$ 和较小的 $B_i$ 合并

              +

              维护一个大根堆,初始有 \(N\) +个极大值

              +

              贪心地选择较小的 \(A_i\) 和较小的 +\(B_i\) 合并

              然后有一个简单易懂的剪枝,具体见代码

              -

              时间复杂度 $O(n \log n)$

              +

              时间复杂度 \(O(n \log n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -894,7 +900,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/18/luo-gu-p1792-guo-jia-ji-xun-dui-chong-shu-ti-jie/index.html b/2022/07/18/luo-gu-p1792-guo-jia-ji-xun-dui-chong-shu-ti-jie/index.html index 010ffd2f64..6e42c336c3 100644 --- a/2022/07/18/luo-gu-p1792-guo-jia-ji-xun-dui-chong-shu-ti-jie/index.html +++ b/2022/07/18/luo-gu-p1792-guo-jia-ji-xun-dui-chong-shu-ti-jie/index.html @@ -476,20 +476,40 @@

              洛谷P1792 [国家集训队]种
              -

              洛谷P1792 [国家集训队]种树 题解

              题目链接:P1792 [国家集训队]种树

              +

              洛谷P1792 [国家集训队]种树 +题解

              +

              题目链接:P1792 +[国家集训队]种树

              题意

              A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。

              -

              园林部门得到指令后,初步规划出 $n$ 个种树的位置,顺时针编号 $1$ 到 $n$。并且每个位置都有一个美观度 $A_i$,如果在这里种树就可以得到这 $A_i$ 的美观度。但由于 $A$ 城市土壤肥力欠佳,两棵树决不能种在相邻的位置($i$ 号位置和 $i+1$ 号位置叫相邻位置。值得注意的是 $1$ 号和 $n$ 号也算相邻位置)。

              -

              最终市政府给园林部门提供了 $m$ 棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将 $m$ 棵树苗全部种上,给出无解信息。

              -

              $m\le n \le 2\times 10^5$,$-1000\le A_i\le1000$。

              +

              园林部门得到指令后,初步规划出 \(n\) +个种树的位置,顺时针编号 \(1\)\(n\)。并且每个位置都有一个美观度 \(A_i\),如果在这里种树就可以得到这 \(A_i\) 的美观度。但由于 \(A\) +城市土壤肥力欠佳,两棵树决不能种在相邻的位置(\(i\) 号位置和 \(i+1\) 号位置叫相邻位置。值得注意的是 \(1\) 号和 \(n\) 号也算相邻位置)。

              +

              最终市政府给园林部门提供了 \(m\) +棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将 +\(m\) +棵树苗全部种上,给出无解信息。

              +

              \(m\le n \le 2\times 10^5\)\(-1000\le A_i\le1000\)

              考虑反悔型贪心,维护一个大根堆,先把所有结点加进去

              然后每次取出权值最大的点,把它加入答案

              如何反悔呢?

              -

              我们再加入一个权值为 $a_{l_i} + a_{r_i}-a_i$ 的节点,表示反悔结点 $i$ 时的答案

              -

              不难发现这样我们再次选到 $a_i$ 的时候,虽然选的是它,但是其实对答案的贡献是它左右两个结点。

              -

              时间复杂度 $O(m \log n)$

              +

              我们再加入一个权值为 \(a_{l_i} + +a_{r_i}-a_i\) 的节点,表示反悔结点 \(i\) 时的答案

              +

              不难发现这样我们再次选到 \(a_i\) +的时候,虽然选的是它,但是其实对答案的贡献是它左右两个结点。

              +

              时间复杂度 \(O(m \log n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -941,7 +961,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/18/luo-gu-p2024-noi2001-shi-wu-lian-ti-jie/index.html b/2022/07/18/luo-gu-p2024-noi2001-shi-wu-lian-ti-jie/index.html index 149bdf890c..8b311f6d8b 100644 --- a/2022/07/18/luo-gu-p2024-noi2001-shi-wu-lian-ti-jie/index.html +++ b/2022/07/18/luo-gu-p2024-noi2001-shi-wu-lian-ti-jie/index.html @@ -476,35 +476,44 @@

              洛谷P2024 [NOI2001] 食物链
              -

              洛谷P2024 [NOI2001] 食物链 题解

              题目链接:P2024 [NOI2001] 食物链

              +

              洛谷P2024 [NOI2001] 食物链 +题解

              +

              题目链接:P2024 +[NOI2001] 食物链

              -

              题意:动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形。A 吃 B,B 吃 C,C 吃 A。

              -

              现有 N 个动物,以 1 - N 编号。每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。

              +

              题意:动物王国中有三类动物 +A,B,C,这三类动物的食物链构成了有趣的环形。A 吃 B,B 吃 C,C 吃 A。

              +

              现有 N 个动物,以 1 - N 编号。每个动物都是 A,B,C +中的一种,但是我们并不知道它到底是哪一种。

              有人用两种说法对这 N 个动物所构成的食物链关系进行描述:

              • 第一种说法是1 X Y,表示 X 和 Y 是同类。
              • 第二种说法是2 X Y,表示 X 吃 Y 。
              -

              此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

              +

              此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K +句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

              • 当前的话与前面的某些真的话冲突,就是假话
              • 当前的话中 X 或 Y 比 N 大,就是假话
              • 当前的话表示 X 吃 X,就是假话

              你的任务是根据给定的 N 和 K 句话,输出假话的总数。

              -

              $1 \le N \le 5\times10^4,~1 \le K \le 10^5$

              +

              \(1 \le N \le 5\times10^4,~1 \le K \le +10^5\)

              考虑种类并查集。

              我们维护三个并查集,分别存同类、食物和天敌

              -

              这里可以用1倍~3倍来划分,即 xx+nx+2*n

              -

              例如 xx+n 合并,表示 x 及其同类均能吃 x+n 及其同类

              +

              这里可以用1倍~3倍来划分,即 +xx+nx+2*n

              +

              例如 xx+n 合并,表示 x +及其同类均能吃 x+n 及其同类

              不用A,B,C作为划分依据是因为题目没说每个动物的种类

              并且在本题中,准确的种类并没有什么用处。

              对于「X和Y是同类」,看上去只要把同类的并查集合并

              但其实其他两个并查集也要合并,因为有可能X存在已知的天敌

              而X和Y是同类,所以Y也有这个(些)天敌

              同样的,对于「X吃Y」,也需要对两两关系进行处理

              -

              时间复杂度 $O(m)$

              +

              时间复杂度 \(O(m)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -551,7 +560,8 @@ 

              return 0; }

              参考文献

              -

              [1] https://www.luogu.com.cn/blog/DanKuroto/solution-p2024

              +

              [1] https://www.luogu.com.cn/blog/DanKuroto/solution-p2024

              @@ -921,7 +931,7 @@

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/18/luo-gu-p3871-tjoi2010-zhong-wei-shu-ti-jie/index.html b/2022/07/18/luo-gu-p3871-tjoi2010-zhong-wei-shu-ti-jie/index.html index 621306109b..834c6ad98e 100644 --- a/2022/07/18/luo-gu-p3871-tjoi2010-zhong-wei-shu-ti-jie/index.html +++ b/2022/07/18/luo-gu-p3871-tjoi2010-zhong-wei-shu-ti-jie/index.html @@ -468,13 +468,16 @@

              洛谷P3871 [TJOI2010]中位数
              -

              洛谷P3871 [TJOI2010]中位数 题解

              题目链接:P3871 [TJOI2010]中位数

              +

              洛谷P3871 [TJOI2010]中位数 +题解

              +

              题目链接:P3871 +[TJOI2010]中位数

              题意

              给定一个由N个元素组成的整数序列,现在有两种操作:

              1 add a

              在该序列的最后添加一个整数a,组成长度为N + 1的整数序列

              -

              2 mid
              输出当前序列的中位数

              +

              2 mid 输出当前序列的中位数

              中位数是指将一个序列按照从小到大排序后处在中间位置的数。(若序列长度为偶数,则指处在中间位置的两个数中较小的那个)

              例1:1 2 13 14 15 16 中位数为13

              例2:1 3 5 7 10 11 17 中位数为7

              @@ -482,9 +485,11 @@

              link

              +

              考虑维护一个对顶堆,详细见 link

              然后就是板子题,注意对询问分类讨论

              -

              时间复杂度 $O((n+m) \log (n+m))$

              +

              时间复杂度 \(O((n+m) \log +(n+m))\)

              代码:

              #include <iostream>
               #include <string>
              @@ -912,7 +917,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/19/luo-gu-p2391-bai-xue-ai-ai-ti-jie/index.html b/2022/07/19/luo-gu-p2391-bai-xue-ai-ai-ti-jie/index.html index 4dbf1909c9..b85dc88c9f 100644 --- a/2022/07/19/luo-gu-p2391-bai-xue-ai-ai-ti-jie/index.html +++ b/2022/07/19/luo-gu-p2391-bai-xue-ai-ai-ti-jie/index.html @@ -468,12 +468,26 @@

              洛谷P2391 白雪皑皑 题解<
              -

              洛谷P2391 白雪皑皑 题解

              题目链接:P2391 白雪皑皑

              +

              洛谷P2391 白雪皑皑 题解

              +

              题目链接:P2391 +白雪皑皑

              题意

              -

              现在有 $n$ 片雪花排成一列。 pty 要对雪花进行 $m$ 次染色操作,第 $i$ 次染色操作中,把第 $((i\times p+q)\bmod n)+1$ 片雪花和第 $((i\times q+p)\bmod n)+1$ 片雪花之间的雪花(包括端点)染成颜色 $i$。其中 $p,q$ 是给定的两个正整数。他想知道最后 $n$ 片雪花被染成了什么颜色。没有被染色输出 $0$。

              -

              $1\leq n\leq 10^6,~1\leq m\leq 10^7$。

              -

              保证 $1\leq m\times p+q,m\times q+p\leq 2\times 10^9$。

              +

              现在有 \(n\) 片雪花排成一列。 pty +要对雪花进行 \(m\) 次染色操作,第 \(i\) 次染色操作中,把第 \(((i\times p+q)\bmod n)+1\) 片雪花和第 \(((i\times q+p)\bmod n)+1\) +片雪花之间的雪花(包括端点)染成颜色 \(i\)。其中 \(p,q\) 是给定的两个正整数。他想知道最后 +\(n\) +片雪花被染成了什么颜色。没有被染色输出 \(0\)

              +

              \(1\leq n\leq 10^6,~1\leq m\leq +10^7\)

              +

              保证 \(1\leq m\times p+q,m\times q+p\leq +2\times 10^9\)

              一句话题意:线性区间推平(区间赋同一个值),全局询问。

              我们模拟赛t3,也是我想了几个月结果有原题的东西,不写题解亏大了

              @@ -481,7 +495,7 @@

              \(O(n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -912,7 +926,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/19/oi-mo-ban-ji-suan-ji-he/index.html b/2022/07/19/oi-mo-ban-ji-suan-ji-he/index.html index 1bb8964687..3e82750b07 100644 --- a/2022/07/19/oi-mo-ban-ji-suan-ji-he/index.html +++ b/2022/07/19/oi-mo-ban-ji-suan-ji-he/index.html @@ -468,7 +468,10 @@

              OI模板-计算几何

              -

              OI模板-计算几何

              二维凸包

              Andrew

              时间复杂度 $O(n \log n)$

              +

              OI模板-计算几何

              +

              二维凸包

              +

              Andrew

              +

              时间复杂度 \(O(n \log n)\)

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -532,7 +535,8 @@ 

              << res << endl; return 0; }

              -

              Graham

              时间复杂度 $O(n\log n)$

              +

              Graham

              +

              时间复杂度 \(O(n\log n)\)

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -586,9 +590,11 @@ 

              Graham<< ans << endl; return 0; }

              -
              -

              平面最近点对

              P7883 平面最近点对(加强加强版)

              -

              时间复杂度 $O(n\log n)$

              +
              +

              平面最近点对

              +

              P7883 +平面最近点对(加强加强版)

              +

              时间复杂度 \(O(n\log n)\)

              注意下面的代码输出的是平方

              改为求sqrt一定要改成fabs !!!!!!!!!!!

              #include <bits/stdc++.h>
              @@ -641,12 +647,15 @@ 

              << cdq(1,n) << endl; return 0; }

              -
              -

              半平面交

              P4196 [CQOI2006]凸多边形 /【模板】半平面交

              -
                +
                +

                半平面交

                +

                P4196 +[CQOI2006]凸多边形 /【模板】半平面交

                +
                1. 给出了凸多边形,逆时针给出各个顶点的坐标
                -

                时间复杂度 $O(n\log n)$ (不是题目里的 $n$ )

                +

                时间复杂度 \(O(n\log n)\) +(不是题目里的 \(n\)

                #include <bits/stdc++.h>
                 using namespace std;
                 #define int long long
                @@ -742,11 +751,12 @@ 

                return 0; }

                poj2451

                -
                  +
                  1. 给出了向量

                  时间复杂度同上

                  -

                  注意POJ上提交要用printf() + %f ,所以下面这个代码交上去的时候要改一下

                  +

                  注意POJ上提交要用printf() + %f +,所以下面这个代码交上去的时候要改一下

                  #include <cstdio>
                   #include <iostream>
                   #include <vector>
                  @@ -850,9 +860,11 @@ 

                  else printf("%.1lf\n",calc(p+st-1,en-st+1)); return 0; }

                  -
                  -

                  旋转卡壳

                  时间复杂度 $O(n\log n)$

                  -

                  其实是二维凸包的复杂度,查找只有 $O(n)$

                  +
                  +

                  旋转卡壳

                  +

                  时间复杂度 \(O(n\log n)\)

                  +

                  其实是二维凸包的复杂度,查找只有 \(O(n)\)

                  注意下面这份代码写的是距离的平方

                  #include <bits/stdc++.h>
                   using namespace std;
                  @@ -926,9 +938,12 @@ 

                  << getmx() << endl; return 0; }

                  -
                  -

                  扫描线

                  扫描线 面积并

                  P5490 【模板】扫描线

                  -

                  时间复杂度 $O(n\log n)$

                  +
                  +

                  扫描线

                  +

                  扫描线 面积并

                  +

                  P5490 +【模板】扫描线

                  +

                  时间复杂度 \(O(n\log n)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -1012,7 +1027,9 @@ 

                  << res << endl; return 0; }

                  -

                  扫描线 周长并(并的周长)

                  P1856 [IOI1998] [USACO5.5] 矩形周长Picture

                  +

                  扫描线 周长并(并的周长)

                  +

                  P1856 [IOI1998] +[USACO5.5] 矩形周长Picture

                  【旧代码,懒得维护】

                  跑了两边扫描线(纵、横)

                  注意不要忘记把最后一条线搞一下

                  @@ -1114,17 +1131,19 @@

                  << ans << endl; return 0; }

              -
              -

              随机增量法

              时间复杂度 $O(n)$

              +
              +

              随机增量法

              +

              时间复杂度 \(O(n)\)

              三点确定一个圆,故三层循环

              -

              证:

              -

              例题:P1742 最小圆覆盖

              +T_1(n) &= O(n) + \sum\limits_{i=1}^{n}\dfrac{3}{i}T_2(i) \\ +T_2(n) &= O(n) + \sum\limits_{i=1}^{n}\dfrac{3}{i}T_3(i) \\ +T_3(n) &= O(n)\\\\ +\therefore T_1(n)&=T_2(n)=T_3(n)=O(n) +\end{aligned} +\] 例题:P1742 +最小圆覆盖

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -1548,7 +1567,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/19/oi-mo-ban-qi-ta/index.html b/2022/07/19/oi-mo-ban-qi-ta/index.html index 3d721e715f..2519c57b72 100644 --- a/2022/07/19/oi-mo-ban-qi-ta/index.html +++ b/2022/07/19/oi-mo-ban-qi-ta/index.html @@ -468,7 +468,12 @@

              OI模板-其他

              -

              OI模板-其他

              光速幂

              仅适用于int 且 底数相同,即 $a^b$ , $a$ 不变,复杂度约为 $O(\sqrt{n})$

              +

              OI模板-其他

              +

              光速幂

              +

              仅适用于int 且 底数相同,即 \(a^b\)\(a\) 不变,复杂度约为 \(O(\sqrt{n})\)

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -496,8 +501,9 @@ 

              printf("%lld^%lld mod %lld=%lld\n",a,b,p,solve()); return 0; }

              -
              -

              O(1)快速乘

              // 以下是O(1)快速乘+快速幂,可以防 10^18 的情况
              +
              +

              O(1)快速乘

              +
              // 以下是O(1)快速乘+快速幂,可以防 10^18 的情况
               #define int long long
               int mul(int a,int b,int p)
               {
              @@ -515,11 +521,15 @@ 

              } return ans; }

              -
              -

              二维数点

              P2163 [SHOI2007]园丁的烦恼

              -

              二维数点

              -

              二维数点 分治

              二维CDQ

              -

              时间复杂度 $O(n\log n)$

              +
              +

              二维数点

              +

              P2163 +[SHOI2007]园丁的烦恼

              +

              二维数点

              +

              二维数点 分治

              +

              二维CDQ

              +

              时间复杂度 \(O(n\log n)\)

              #include <bits/stdc++.h>
               using namespace std;
               // #define int long long
              @@ -611,7 +621,8 @@ 

              write(ans[i]),pc('\n'); return 0; }

              -

              二维数点 树状数组

              时间复杂度 $O(n\log n)$

              +

              二维数点 树状数组

              +

              时间复杂度 \(O(n\log n)\)

              #include <bits/stdc++.h>
               using namespace std;
               // #define int long long
              @@ -717,7 +728,8 @@ 

              write(ans[i]),pc('\n'); return 0; }

              -

              乘法取模封装

              需要头文件 cstdarg

              +

              乘法取模封装

              +

              需要头文件 cstdarg

              int mul(int cnt, ...)
               {
                   va_list ptr; va_start(ptr,cnt);
              @@ -728,7 +740,8 @@ 

              return res; }

              调用方式如下:

              -

              计算 $(a \times b \times c \times d )\bmod M$

              +

              计算 \((a \times b \times c \times d )\bmod +M\)

              ans = mul(4, a, b, c, d);
              @@ -1083,7 +1096,7 @@

                站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/19/oi-mo-ban-shu-ju-jie-gou/index.html b/2022/07/19/oi-mo-ban-shu-ju-jie-gou/index.html index 9538498e0c..251b439278 100644 --- a/2022/07/19/oi-mo-ban-shu-ju-jie-gou/index.html +++ b/2022/07/19/oi-mo-ban-shu-ju-jie-gou/index.html @@ -468,9 +468,14 @@

              OI模板-数据结构

              -

              OI模板-数据结构

              并查集

              @Roundgod老师说

              -

              按秩合并+路径压缩才能保证并查集查询的复杂度为 $O(\alpha(n))$

              -

              只用一个就是 $O(\log n)$ 的,不过要特意构造数据卡才会到这个上界

              +

              OI模板-数据结构

              +

              并查集

              +

              @Roundgod老师说

              +

              按秩合并+路径压缩才能保证并查集查询的复杂度为 \(O(\alpha(n))\)

              +

              只用一个就是 \(O(\log n)\) +的,不过要特意构造数据卡才会到这个上界

              int f[N],r[N]; // r[] 为深度
               void init(int n){for(int i=1; i<=n; i++) f[i]=i,r[i]=0;}
               int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
              @@ -481,7 +486,8 @@ 

              if(r[x]<r[y]) f[x]=y; else f[y]=x,r[x]+=r[x]==r[y]; }

              -

              种类并查集

              P2024 食物链

              +

              种类并查集

              +

              P2024 食物链

              #include <iostream>
               #include <string>
               #include <vector>
              @@ -527,10 +533,12 @@ 

              return 0; }

              -

              带权并查集

              待写

              -
              -

              单调队列

              时间复杂度 $O(n)$

              -

              空间复杂度 $O(n)$

              +

              带权并查集

              +

              待写

              +
              +

              单调队列

              +

              时间复杂度 \(O(n)\)

              +

              空间复杂度 \(O(n)\)

              手写版本(20220514)

              队列用的 (l,r] 写法

              #include <bits/stdc++.h>
              @@ -634,9 +642,10 @@ 

              } return 0; }

              -
              -

              二叉堆

              时间复杂度 $O(n\log n)$

              -

              空间复杂度 $O(m^2)$

              +
              +

              二叉堆

              +

              时间复杂度 \(O(n\log n)\)

              +

              空间复杂度 \(O(m^2)\)

              #include <iostream>
               #include <string>
               #include <vector>
              @@ -702,8 +711,9 @@ 

              } return 0; }

              -
              -

              左偏树(可并堆)

              时间复杂度 $O(n\log n)$

              +
              +

              左偏树(可并堆)

              +

              时间复杂度 \(O(n\log n)\)

              写法1

              #include <bits/stdc++.h>
               using namespace std;
              @@ -865,8 +875,9 @@ 

              } return 0; }

              -
              -

              珂朵莉树

              #include <bits/stdc++.h>
              +
              +

              珂朵莉树

              +
              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
               #define R register
              @@ -982,9 +993,11 @@ 

              } return 0; }

              -
              -

              平衡树

              FHQ_Treap

              时间复杂度 $O(n\log n)$

              -

              空间复杂度 $O(n)$

              +
              +

              平衡树

              +

              FHQ_Treap

              +

              时间复杂度 \(O(n\log n)\)

              +

              空间复杂度 \(O(n)\)

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -1123,9 +1136,10 @@ 

              if(op==6){getnext(a);} } }

              -

              替罪羊树

              小常数还好写

              -

              时间复杂度 $O(n\log n)$

              -

              空间复杂度 $O(n)$

              +

              替罪羊树

              +

              小常数还好写

              +

              时间复杂度 \(O(n\log n)\)

              +

              空间复杂度 \(O(n)\)

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -1266,9 +1280,10 @@ 

              } return 0; }

              -

              Splay

              大常数

              -

              时间复杂度 $O(n\log n)$

              -

              空间复杂度 $O(n)$

              +

              Splay

              +

              大常数

              +

              时间复杂度 \(O(n\log n)\)

              +

              空间复杂度 \(O(n)\)

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -1413,10 +1428,11 @@ 

              Splay

              < } return 0; }
              -
              -

              可持久化平衡树

              采用FHQ_Treap

              -

              时间复杂度 $O(n\log n)$

              -

              空间复杂度 $O(n\log n)$

              +
              +

              可持久化平衡树

              +

              采用FHQ_Treap

              +

              时间复杂度 \(O(n\log n)\)

              +

              空间复杂度 \(O(n\log n)\)

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -1565,11 +1581,14 @@ 

              if(op==6){getnext(a,T[i]);} } }

              -
              -

              ST表

              一定要预处理 $\log_2 n$ !! 不然查询的复杂度是假的!

              -

              因为 log() 函数的复杂度不超过log级,但是常数大!

              -

              时间复杂度 $O(n\log n)$

              -

              空间复杂度 $O(n\log n)$

              +
              +

              ST表

              +

              一定要预处理 \(\log_2 n\) +!! 不然查询的复杂度是假的!

              +

              因为 log() +函数的复杂度不超过log级,但是常数大!

              +

              时间复杂度 \(O(n\log n)\)

              +

              空间复杂度 \(O(n\log n)\)

              #include <iostream>
               #include <string>
               #include <vector>
              @@ -1638,8 +1657,9 @@ 

              ST表

              < } return 0; }
              -
              -

              树链剖分

              #include <bits/stdc++.h>
              +
              +

              树链剖分

              +
              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
               #define INF 0x3f3f3f3f3f3f3f3f
              @@ -1824,8 +1844,12 @@ 

              } return 0; }

              -
              -

              树套树

              树套树 位置线段树套权值平衡树

              P3380 【模板】二逼平衡树(树套树)

              +
              +

              树套树

              +

              树套树 +位置线段树套权值平衡树

              +

              P3380 +【模板】二逼平衡树(树套树)

              注意二分的方向

              #include <bits/stdc++.h>
               using namespace std;
              @@ -2067,8 +2091,11 @@ 

              } return 0; }

              -
              -

              线段树

              线段树1 朴素写法

              P3372 【模板】线段树 1

              +
              +

              线段树

              +

              线段树1 朴素写法

              +

              P3372 【模板】线段树 +1

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -2182,7 +2209,7 @@ 

              } return 0; }

              -

              还有一种比较好的query写法,如下

              
              +

              还有一种比较好的query写法,如下

              
               int query(int nl,int nr,int l,int r,int at)
               {
                   if(nl<=l&&r<=nr)
              @@ -2192,7 +2219,9 @@ 

              return query(nl,nr,l,mid,ls(at))+query(nl,nr,mid+1,r,rs(at)); }

              -

              线段树1 标记永久化+动态开点

              P3372 【模板】线段树 1

              +

              线段树1 标记永久化+动态开点

              +

              P3372 【模板】线段树 +1

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -2281,8 +2310,10 @@ 

              } return 0; }

              -
              -

              李超线段树

              P4097 [HEOI2013]Segment

              +
              +

              李超线段树

              +

              P4097 +[HEOI2013]Segment

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -2415,8 +2446,10 @@ 

              } return 0; }

              -
              -

              线段树分裂

              P5494 【模板】线段树分裂

              +
              +

              线段树分裂

              +

              P5494 +【模板】线段树分裂

              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
              @@ -2548,8 +2581,9 @@ 

              } return 0; }

              -
              -

              静态仙人掌

              #include <bits/stdc++.h>
              +
              +

              静态仙人掌

              +
              #include <bits/stdc++.h>
               using namespace std;
               #define int long long
               #define INF 0x3f3f3f3f3f3f3f3f
              @@ -2703,11 +2737,12 @@ 

              } return 0; }

              -
              -

              可持久化数组

                +
                +

                可持久化数组

                +
                1. 主席树实现被卡了long long
                -

                时间复杂度 $O(n\log n)$

                +

                时间复杂度 \(O(n\log n)\)

                #include <bits/stdc++.h>
                 using namespace std;
                 // #define int long long
                @@ -2795,11 +2830,10 @@ 

                } return 0; }

                -
                  +
                  1. Elegia神仙的神仙解法(离线解法)

                    -

                    根据依赖关系,且可逆操作,建关系树,然后跑dfs

                    -

                    时间复杂度 $O(n)$

                    -
                  2. +

                    根据依赖关系,且可逆操作,建关系树,然后跑dfs

                    +

                    时间复杂度 \(O(n)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                  @@ -2866,9 +2900,10 @@ 

                  write(ans[i]),pc('\n'); return 0; }

                  -
                  -

                  可持久化线段树(主席树)

                  区间 $k$ 小值

                  -

                  时间复杂度 $O(n\log n)$

                  +
                  +

                  可持久化线段树(主席树)

                  +

                  区间 \(k\) 小值

                  +

                  时间复杂度 \(O(n\log n)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -3314,7 +3349,7 @@ 

                   站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/19/oi-mo-ban-suan-fa/index.html b/2022/07/19/oi-mo-ban-suan-fa/index.html index 84c62e25aa..7c20a594cb 100644 --- a/2022/07/19/oi-mo-ban-suan-fa/index.html +++ b/2022/07/19/oi-mo-ban-suan-fa/index.html @@ -468,10 +468,15 @@

                  OI模板-算法

                  -

                  OI模板-算法

                  排序算法

                  其他乱七八糟的毛用没有(归并、松氏基排除外,还没补上来,咕咕咕…)

                  +

                  OI模板-算法

                  +

                  排序算法

                  +

                  其他乱七八糟的毛用没有(归并、松氏基排除外,还没补上来,咕咕咕...)

                  基本上一个sort全部搞定

                  -

                  计数排序

                  时间复杂度 $O(n)$

                  -

                  空间复杂度 $O(\max\{n,\max\limits_{0<i\le n}a_i\})$

                  +

                  计数排序

                  +

                  时间复杂度 \(O(n)\)

                  +

                  空间复杂度 \(O(\max\{n,\max\limits_{0<i\le +n}a_i\})\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -497,8 +502,9 @@ 

                  << b[i] << " \n"[i==n]; return 0; }

                  -

                  基数排序

                  时间复杂度 $O(n)$

                  -

                  空间复杂度 $O(n)$

                  +

                  基数排序

                  +

                  时间复杂度 \(O(n)\)

                  +

                  空间复杂度 \(O(n)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -534,8 +540,10 @@ 

                  << a[i] << " \n"[i==n]; return 0; }

                  -
                  -

                  CDQ分治

                  P3810 【模板】三维偏序(陌上花开)

                  +
                  +

                  CDQ分治

                  +

                  P3810 +【模板】三维偏序(陌上花开)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -629,8 +637,10 @@ 

                  << cnt[i] << endl; return 0; }

                  -
                  -

                  LCA

                  LCA 倍增

                  时间复杂度 $O(\log n)$

                  +
                  +

                  LCA

                  +

                  LCA 倍增

                  +

                  时间复杂度 \(O(\log n)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -698,7 +708,8 @@ 

                  LCA

                  } return 0; }

                  -

                  LCA 树链剖分

                  时间复杂度 $O(\log n)$

                  +

                  LCA 树链剖分

                  +

                  时间复杂度 \(O(\log n)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -793,8 +804,9 @@ 

                  } return 0; }

                  -
                  -

                  高精度加减乘除

                  #include<bits/stdc++.h>
                  +
                  +

                  高精度加减乘除

                  +
                  #include<bits/stdc++.h>
                   using namespace std;
                   #define int long long
                   #define R register
                  @@ -922,8 +934,9 @@ 

                  // ... return 0; }

                  -
                  -

                  高精度封装版

                  网上搜的。有空搞吧。忘记出处了,很久前复制的

                  +
                  +

                  高精度封装版

                  +

                  网上搜的。有空搞吧。忘记出处了,很久前复制的

                  #include <bits/stdc++.h>
                   using namespace std;
                   const int maxn=5009;
                  @@ -1036,7 +1049,7 @@ 

                  print(ans); }

                  -
                  +
                  @@ -1390,7 +1403,7 @@

                    站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/19/oi-mo-ban-tu-lun/index.html b/2022/07/19/oi-mo-ban-tu-lun/index.html index a285d63dc3..e1ad0a6725 100644 --- a/2022/07/19/oi-mo-ban-tu-lun/index.html +++ b/2022/07/19/oi-mo-ban-tu-lun/index.html @@ -468,8 +468,12 @@

                  OI模板-图论

                  -

                  OI模板-图论

                  最短路算法

                  dijkstra

                  P4779 【模板】单源最短路径(标准版)

                  -

                  优先队列优化 $O((n+m)\log m)$

                  +

                  OI模板-图论

                  +

                  最短路算法

                  +

                  dijkstra

                  +

                  P4779 +【模板】单源最短路径(标准版)

                  +

                  优先队列优化 \(O((n+m)\log m)\)

                  #include <iostream>
                   #include <string>
                   #include <vector>
                  @@ -534,7 +538,8 @@ 

                  << (d[i]==INF?-1:d[i]) << " \n"[i==n]; return 0; }

                  -

                  SPFA

                  时间复杂度 $O(nm)$

                  +

                  SPFA

                  +

                  时间复杂度 \(O(nm)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -589,7 +594,8 @@ 

                  SPFA

                  cout << (d[i]==INF?-1:d[i]) << " \n"[i==n]; return 0; }

                  -

                  分层图最短路

                  #include <iostream>
                  +

                  分层图最短路

                  +
                  #include <iostream>
                   #include <string>
                   #include <vector>
                   #include <algorithm>
                  @@ -664,10 +670,13 @@ 

                  << d[k*n+t] << '\n'; return 0; }

                  -
                  -

                  最小生成树

                  都是无向图!!!!有向图的叫最小树形图!!!

                  -

                  P3366 【模板】最小生成树

                  -

                  kruskal

                  时间复杂度 $O(m\log m)$

                  +
                  +

                  最小生成树

                  +

                  都是无向图!!!!有向图的叫最小树形图!!!

                  +

                  P3366 +【模板】最小生成树

                  +

                  kruskal

                  +

                  时间复杂度 \(O(m\log m)\)

                  #include <iostream>
                   #include <string>
                   #include <vector>
                  @@ -718,8 +727,9 @@ 

                  krusk else cout << ans; return 0; }

                  -

                  Boruvka

                  时间复杂度 $O(m\log n)$

                  -

                  空间复杂度 $O(m)$

                  +

                  Boruvka

                  +

                  时间复杂度 \(O(m\log n)\)

                  +

                  空间复杂度 \(O(m)\)

                  #include <iostream>
                   #include <string>
                   #include <vector>
                  @@ -789,9 +799,15 @@ 

                  Boruv else cout << ans << '\n'; return 0; }

                  -

                  Prim

                  优先队列优化的时间复杂度 $O(m\log n)$

                  -

                  斐波那契堆优化的时间复杂度 $O(n\log n)$

                  -

                  下面的代码是无优化邻接矩阵版的,$O(n^2)$ ,仅适用于稠密图 $n\le 2000$ ,不然会挂(MLE或TLE)

                  +

                  Prim

                  +

                  优先队列优化的时间复杂度 \(O(m\log +n)\)

                  +

                  斐波那契堆优化的时间复杂度 \(O(n\log +n)\)

                  +

                  下面的代码是无优化邻接矩阵版的,\(O(n^2)\)仅适用于稠密图 +\(n\le 2000\) +,不然会挂(MLE或TLE)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -831,7 +847,8 @@ 

                  Prim

                  cout << ans << endl; return 0; }

                  -

                  LCT

                  比较离谱的解法之一

                  +

                  LCT

                  +

                  比较离谱的解法之一

                  大常数

                  #include <bits/stdc++.h>
                   using namespace std;
                  @@ -979,16 +996,30 @@ 

                  LCT

                  比较 else write(ans),pc('\n'); return 0; }

                  -
                  -

                  线段树优化建图

                  板子1

                  CF786B Legacy题解

                  -

                  $n$ 个结点, $q$ 次操作,问操作后从 $s$ 出发的单源最短路

                  +
                  +

                  线段树优化建图

                  +

                  板子1

                  +

                  CF786B +Legacy题解

                  +

                  \(n\) 个结点, \(q\) 次操作,问操作后从 \(s\) 出发的单源最短路

                  操作如下

                    -
                  • 输入 1 u v w 表示 $u$ 到 $v$ 连一条有向边
                  • -
                  • 输入 2 u l r w 表示 $u$ 到 $[l,r]$ 的每个结点连一条有向边
                  • -
                  • 输入 3 u l r w 表示 $[l,r]$ 的每个结点向 $u$ 连一条有向边
                  • +
                  • 输入 1 u v w 表示 \(u\)\(v\) 连一条有向边
                  • +
                  • 输入 2 u l r w 表示 \(u\)\([l,r]\) 的每个结点连一条有向边
                  • +
                  • 输入 3 u l r w 表示 \([l,r]\) 的每个结点向 \(u\) 连一条有向边
                  -

                  对于 $100\%$ 的数据,$1\le n,q \le 10^5$,$1\le w \le 10^9$

                  +

                  对于 \(100\%\) 的数据,\(1\le n,q \le 10^5\)\(1\le w \le 10^9\)

                  代码:

                  #include <iostream>
                   #include <string>
                  @@ -1123,10 +1154,14 @@ 

                  write(d[a[i]]!=INF ? d[a[i]] : -1),pc(" \n"[i==n]); return 0; }

                  -

                  板子2

                  P6348 [PA2011]Journeys

                  +

                  板子2

                  +

                  P6348 +[PA2011]Journeys

                  咕咕咕。。。

                  -
                  -

                  判负环

                  spfa,这份代码是从结点 $1$ 为起点的

                  +
                  +

                  判负环

                  +

                  spfa,这份代码是从结点 \(1\) +为起点的

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -1257,11 +1292,14 @@ 

                  } return 0; }

                  -
                  -

                  kosaraju算法

                  时间复杂度 $O(n+m)$

                  -

                  空间复杂度 $O(m)$

                  -

                  例题:P3387 【模板】缩点

                  -

                  有向图强连通分量缩点

                  #include <iostream>
                  +
                  +

                  kosaraju算法

                  +

                  时间复杂度 \(O(n+m)\)

                  +

                  空间复杂度 \(O(m)\)

                  +

                  例题:P3387 +【模板】缩点

                  +

                  有向图强连通分量缩点

                  +
                  #include <iostream>
                   #include <string>
                   #include <vector>
                   #include <algorithm>
                  @@ -1353,10 +1391,14 @@ 

                  << topo() << '\n'; return 0; }

                  -
                  -

                  Tarjan算法 [连通性问题]

                  无向图

                  割边(桥)

                  时间复杂度 $O(n)$

                  -

                  空间复杂度 $O(m)$

                  -

                  例题:T103481 【模板】割边

                  +
                  +

                  Tarjan算法 [连通性问题]

                  +

                  无向图

                  +

                  割边(桥)

                  +

                  时间复杂度 \(O(n)\)

                  +

                  空间复杂度 \(O(m)\)

                  +

                  例题:T103481 +【模板】割边

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -1406,10 +1448,12 @@ 

                  << ans << endl; return 0; }

                  -

                  割点(割顶)

                  区别于桥,这个不用管fa结点,但是注意rt要有两个子结点都是“割点性质“

                  -

                  时间复杂度 $O(n)$

                  -

                  空间复杂度 $O(m)$

                  -

                  例题:P3388 【模板】割点(割顶)

                  +

                  割点(割顶)

                  +

                  区别于桥,这个不用管fa结点,但是注意rt要有两个子结点都是“割点性质“

                  +

                  时间复杂度 \(O(n)\)

                  +

                  空间复杂度 \(O(m)\)

                  +

                  例题:P3388 +【模板】割点(割顶)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -1465,10 +1509,14 @@ 

                  if(cut[i])cout << i << " \n"[!--ans]; return 0; }

                  -

                  边双连通分量

                  时间复杂度 $O(n)$

                  -

                  空间复杂度 $O(m)$

                  -

                  upd20220806 有个板子题P8436 【模板】边双连通分量

                  -

                  例题:P2860 [USACO06JAN]Redundant Paths G

                  +

                  边双连通分量

                  +

                  时间复杂度 \(O(n)\)

                  +

                  空间复杂度 \(O(m)\)

                  +

                  upd20220806 有个板子题P8436 +【模板】边双连通分量

                  +

                  例题:P2860 +[USACO06JAN]Redundant Paths G

                  例题的代码:

                  #include <bits/stdc++.h>
                   using namespace std;
                  @@ -1547,11 +1595,14 @@ 

                  write((ans+1)>>1);pc('\n'); return 0; }

                  -

                  点双连通分量

                  时间复杂度 $O(n)$

                  -

                  空间复杂度 $O(m)$

                  -

                  例题:T103492 【模板】点双连通分量

                  +

                  点双连通分量

                  +

                  时间复杂度 \(O(n)\)

                  +

                  空间复杂度 \(O(m)\)

                  +

                  例题:T103492 +【模板】点双连通分量

                  注:下面的第二种写法才可通过例题!

                  -

                  因为第一种把割点存在了第一个,和例题的标程不一样,然后例题没写special judge.

                  +

                  因为第一种把割点存在了第一个,和例题的标程不一样,然后例题没写special +judge.

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -1638,9 +1689,12 @@ 

                  write(vcc[i][j]),pc(" \n"[j+1==vcc[i].size()]); return 0; }

                  -

                  有向图

                  强连通分量

                  时间复杂度 $O(n+m)$

                  -

                  空间复杂度 $O(m)$

                  -

                  例题:P2746 [USACO5.3]校园网Network of Schools

                  +

                  有向图

                  +

                  强连通分量

                  +

                  时间复杂度 \(O(n+m)\)

                  +

                  空间复杂度 \(O(m)\)

                  +

                  例题:P2746 +[USACO5.3]校园网Network of Schools

                  要注意强连通图统计缩点后的出、入度要特判!

                  #include <bits/stdc++.h>
                   using namespace std;
                  @@ -1702,9 +1756,11 @@ 

                  else cout << max(x,y) << endl; return 0; }

                  -

                  强连通分量缩点

                  时间复杂度 $O(n)$

                  -

                  空间复杂度 $O(m)$

                  -

                  例题:P3387 【模板】缩点

                  +

                  强连通分量缩点

                  +

                  时间复杂度 \(O(n)\)

                  +

                  空间复杂度 \(O(m)\)

                  +

                  例题:P3387 +【模板】缩点

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -1799,8 +1855,10 @@ 

                  << topo() << endl; return 0; }

                  -
                  -

                  欧拉路径

                  P7771 【模板】欧拉路径

                  +
                  +

                  欧拉路径

                  +

                  P7771 +【模板】欧拉路径

                  求的是字典序最小的欧拉路径

                  /*
                   g++ test_20220124.cpp -o test_20220124
                  @@ -1879,9 +1937,12 @@ 

                  } return 0; }

                  -

                  欧拉回路

                  hierholzer算法?待补

                  -
                  -

                  2-SAT

                  P4782 【模板】2-SAT 问题

                  +

                  欧拉回路

                  +

                  hierholzer算法?待补

                  +
                  +

                  2-SAT

                  +

                  P4782 【模板】2-SAT +问题

                  kosaraju实现,注意跑dfs和开空间都要两倍。

                  #include <iostream>
                   #include <string>
                  @@ -1975,10 +2036,13 @@ 

                  2-SAT

                  < write((int)(scc[i]>scc[i+n])),pc(" \n"[i==n]); return 0; }
                  -
                  -

                  网络流算法

                  有向图网络最大流

                  这里默认s与t连通,实际上代码里写了判断

                  -

                  Dinic

                  时间复杂度 $O(n^2m)$

                  -

                  空间复杂度 $O(m)$

                  +
                  +

                  网络流算法

                  +

                  有向图网络最大流

                  +

                  这里默认s与t连通,实际上代码里写了判断

                  +

                  Dinic

                  +

                  时间复杂度 \(O(n^2m)\)

                  +

                  空间复杂度 \(O(m)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -2068,8 +2132,9 @@ 

                  Dinic

                  < write(ans);pc('\n'); return 0; }
                  -

                  ISAP

                  时间复杂度 $O(n^2m)$

                  -

                  空间复杂度 $O(m)$

                  +

                  ISAP

                  +

                  时间复杂度 \(O(n^2m)\)

                  +

                  空间复杂度 \(O(m)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -2175,9 +2240,11 @@ 

                  ISAP

                  write(ans);pc('\n'); return 0; }

                  -

                  HLPP

                  n=5e3,m=5e4

                  -

                  时间复杂度 $O(n^2\sqrt{m}\log n)$

                  -

                  空间复杂度 $O(m)$

                  +

                  HLPP

                  +

                  n=5e3,m=5e4

                  +

                  时间复杂度 \(O(n^2\sqrt{m}\log +n)\)

                  +

                  空间复杂度 \(O(m)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -2313,9 +2380,10 @@ 

                  HLPP

                  n= write(HLPP());pc('\n'); return 0; }

                  -

                  有向图最小费用最大流

                  时间复杂度 $O(n^2m)$

                  -

                  空间复杂度 $O(m)$

                  -
                    +

                    有向图最小费用最大流

                    +

                    时间复杂度 \(O(n^2m)\)

                    +

                    空间复杂度 \(O(m)\)

                    +
                    1. vector写法,C++14 1.41s(O2 542ms)
                    #include <bits/stdc++.h>
                    @@ -2408,7 +2476,7 @@ 

                    write(flow);pc(' ');write(cost);pc('\n'); return 0; }

                    -
                      +
                      1. 链式前向星写法,C++14 1.95s(O2 1.45s)
                      #include <bits/stdc++.h>
                      @@ -2511,16 +2579,21 @@ 

                      write(flow);pc(' ');write(cost);pc('\n'); return 0; }

                      -

                      有向图有源汇上下界最大流

                      例题:P5192 Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流

                      +

                      有向图有源汇上下界最大流

                      +

                      例题:P5192 Zoj3229 +Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流

                      本例题建模:

                      -
                        +
                        1. 先建立一个源点
                        2. -
                        3. 从源点到每个少女,流量为 $\left[G_i,+\infty\right)$
                        4. -
                        5. 从每个少女到每一天,流量为 $[l_i,r_i]$
                        6. -
                        7. 从每一天到汇点,流量为 $[0,D_i]$
                        8. +
                        9. 从源点到每个少女,流量为 \(\left[G_i,+\infty\right)\)
                        10. +
                        11. 从每个少女到每一天,流量为 \([l_i,r_i]\)
                        12. +
                        13. 从每一天到汇点,流量为 \([0,D_i]\)

                        然后跑有源汇上下界最大流就好了

                        -
                          +
                          1. ISAP版 C++14 O2 189ms
                          #include <bits/stdc++.h>
                          @@ -2661,11 +2734,12 @@ 

                          } return 0; }

                          -

                          无向图无源汇最大流

                          Stoer-Wagner

                          斯托瓦格纳?

                          -
                            -
                          1. 朴素写法

                            -

                            时间复杂度 $O(nm+n^3)$

                            -
                          2. +

                            无向图无源汇最大流

                            +

                            Stoer-Wagner

                            +

                            斯托瓦格纳?

                            +
                              +
                            1. 朴素写法

                              +

                              时间复杂度 \(O(nm+n^3)\)

                            #include <bits/stdc++.h>
                             using namespace std;
                            @@ -2739,14 +2813,15 @@ 

                            write(sw());pc('\n'); return 0; }

                            -
                              +
                              1. 堆优化(意义不大)

                                -

                                时间复杂度 $O(nm+n^2\log n)$

                                -

                                还没写

                                -
                              2. +

                                时间复杂度 \(O(nm+n^2\log n)\)

                                +

                                还没写

                              -
                              -

                              最小树形图

                              最小树形图 朱刘算法

                              时间复杂度 $O(nm)$

                              +
                              +

                              最小树形图

                              +

                              最小树形图 朱刘算法

                              +

                              时间复杂度 \(O(nm)\)

                              #include <bits/stdc++.h>
                               using namespace std;
                               #define int long long
                              @@ -2850,7 +2925,8 @@ 

                              write(res==INF?-1:res);pc('\n'); return 0; }

                              -

                              最小树形图 Tarjan的DMST

                              时间复杂度 $O(m+n\log m)$

                              +

                              最小树形图 Tarjan的DMST

                              +

                              时间复杂度 \(O(m+n\log m)\)

                              代码如下

                              #include <bits/stdc++.h>
                               using namespace std;
                              @@ -3145,8 +3221,11 @@ 

                              write(ans);pc('\n'); return 0; }

                              -
                              -

                              二分图

                              二分图最大匹配

                              匈牙利算法

                              时间复杂度 $O(nm)$

                              +
                              +

                              二分图

                              +

                              二分图最大匹配

                              +

                              匈牙利算法

                              +

                              时间复杂度 \(O(nm)\)

                              #include <bits/stdc++.h>
                               using namespace std;
                               #define int long long
                              @@ -3203,7 +3282,8 @@ 

                              write(ans);pc('\n'); return 0; }

                              -

                              ISAP

                              时间复杂度 $O(m\sqrt{n})$

                              +

                              ISAP

                              +

                              时间复杂度 \(O(m\sqrt{n})\)

                              #include <bits/stdc++.h>
                               using namespace std;
                               #define int long long
                              @@ -3305,7 +3385,9 @@ 

                              ISAP

                              < write(ans);pc('\n'); return 0; }
                              -

                              二分图最大权完美匹配

                              KM算法

                              时间复杂度 $O(n^3)$

                              +

                              二分图最大权完美匹配

                              +

                              KM算法

                              +

                              时间复杂度 \(O(n^3)\)

                              #include <bits/stdc++.h>
                               using namespace std;
                               #define int long long
                              @@ -3405,7 +3487,9 @@ 

                              << py[i] << " \n"[i==n]; return 0; }

                              -

                              一般图最大匹配

                              带花树算法

                              时间复杂度 $O(n^3)$

                              +

                              一般图最大匹配

                              +

                              带花树算法

                              +

                              时间复杂度 \(O(n^3)\)

                              #include <bits/stdc++.h>
                               using namespace std;
                               #define int long long
                              @@ -3495,9 +3579,12 @@ 

                              << mch[i] << " \n"[i==n]; return 0; }

                              -

                              一般图最大权匹配

                              不会 qwq 感觉要等到我大学才会去学,咕咕咕…

                              -
                              -

                              无向图的最小环问题

                              下面这个代码求的是至少包含 $3$ 个点的环

                              +

                              一般图最大权匹配

                              +

                              不会 qwq 感觉要等到我大学才会去学,咕咕咕...

                              +
                              +

                              无向图的最小环问题

                              +

                              下面这个代码求的是至少包含 \(3\) +个点的环

                              要根据题意判断

                               #include <bits/stdc++.h>
                               using namespace std;
                              @@ -3533,7 +3620,7 @@ 

                              else cout << ans; return 0; }

                              -
                              +
                  @@ -3886,7 +3973,7 @@

                    站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/19/oi-mo-ban-zi-fu-chuan/index.html b/2022/07/19/oi-mo-ban-zi-fu-chuan/index.html index 63ef61f80b..7794407bc1 100644 --- a/2022/07/19/oi-mo-ban-zi-fu-chuan/index.html +++ b/2022/07/19/oi-mo-ban-zi-fu-chuan/index.html @@ -468,8 +468,16 @@

                  OI模板-字符串

                  -

                  OI模板-字符串

                  字符串哈希

                  单哈希

                  给定 $N$ 个字符串(第 $i$ 个字符串长度为 $M_i$,字符串内包含数字、大小写字母,大小写敏感),请求出 $N$ 个字符串中共有多少个不同的字符串。

                  -

                  P3370 【模板】字符串哈希

                  +

                  OI模板-字符串

                  +

                  字符串哈希

                  +

                  单哈希

                  +

                  给定 \(N\) 个字符串(第 \(i\) 个字符串长度为 \(M_i\),字符串内包含数字、大小写字母,大小写敏感),请求出 +\(N\) +个字符串中共有多少个不同的字符串。

                  +

                  P3370 +【模板】字符串哈希

                  #include <iostream>
                   #include <string>
                   #include <vector>
                  @@ -527,9 +535,13 @@ 

                  << cnt << '\n'; return 0; }

                  -

                  双哈希

                  详见CF1200E Compress Words 题解

                  +

                  双哈希

                  +

                  详见CF1200E +Compress Words 题解

                  -

                  给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀 与 待插入串的前缀的最大匹配串

                  +

                  给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀 与 +待插入串的前缀的最大匹配串

                  #include <bits/stdc++.h>
                   using namespace std;
                  @@ -611,9 +623,10 @@ 

                  << s.s+1 << endl; return 0; }

                  -
                  -

                  KMP

                  时间复杂度 $O(n+m)$

                  -

                  空间复杂度 $O(n+m)$

                  +
                  +

                  KMP

                  +

                  时间复杂度 \(O(n+m)\)

                  +

                  空间复杂度 \(O(n+m)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -640,9 +653,10 @@ 

                  KMP

                  时间 printf("%lld%c",fail[i]," \n"[i==m]); return 0; }

                  -
                  -

                  ExKMP

                  时间复杂度 $O(n+m)$

                  -

                  空间复杂度 $O(n+m)$

                  +
                  +

                  ExKMP

                  +

                  时间复杂度 \(O(n+m)\)

                  +

                  空间复杂度 \(O(n+m)\)

                  b 与 b的后缀 的最大前缀、b 与 a的后缀 的最大前缀

                  #include <bits/stdc++.h>
                   using namespace std;
                  @@ -683,9 +697,10 @@ 

                  ExKMP

                  < cout << res << endl; return 0; }
                  -
                  -

                  Manacher

                  时间复杂度 $O(n)$

                  -

                  空间复杂度 $O(n)$

                  +
                  +

                  Manacher

                  +

                  时间复杂度 \(O(n)\)

                  +

                  空间复杂度 \(O(n)\)

                  内存105.53MB(比较极限的数据)

                  aabaa
                   #a#a#b#a#a#
                  @@ -735,10 +750,14 @@ 

                  Ma Manacher(n*2+1); return 0; }

                  -
                  -

                  Trie树

                  这个是老版本的(The XOR Largest Pair),太简单了不写一遍了,详见AC自动机即可

                  -

                  时间复杂度 $O(\max{\{|s_i|\}})$

                  -

                  空间复杂度 $O(k\sum |s_i|),k$ 为字符集范围

                  +
                  +

                  Trie树

                  +

                  这个是老版本的(The XOR Largest +Pair),太简单了不写一遍了,详见AC自动机即可

                  +

                  时间复杂度 \(O(\max{\{|s_i|\}})\)

                  +

                  空间复杂度 \(O(k\sum |s_i|),k\) +为字符集范围

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -797,9 +816,13 @@ 

                  Trie return 0; }

                  -
                  -

                  AC自动机

                  AC自动机(简单版)

                  时间复杂度 $O(\sum |s_i| + |S|)$

                  -

                  空间复杂度 $O(k\sum |s_i| + |S|)$ , $k$ 为字符集大小

                  +
                  +

                  AC自动机

                  +

                  AC自动机(简单版)

                  +

                  时间复杂度 \(O(\sum |s_i| + +|S|)\)

                  +

                  空间复杂度 \(O(k\sum |s_i| + |S|)\) +, \(k\) 为字符集大小

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -860,8 +883,11 @@ 

                  printf("%lld\n",query(strlen(t+1),t)); return 0; }

                  -

                  AC自动机(加强版)

                  最坏时间复杂度 $O(T|S|\max\{|s_i|\})$

                  -

                  空间复杂度 $O(k\sum|s_i|+|S|)$ , $k$ 为字符集大小

                  +

                  AC自动机(加强版)

                  +

                  最坏时间复杂度 \(O(T|S|\max\{|s_i|\})\)

                  +

                  空间复杂度 \(O(k\sum|s_i|+|S|)\) , +\(k\) 为字符集大小

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -945,8 +971,11 @@ 

                  } return 0; }

                  -

                  AC自动机(二次加强版)

                  时间复杂度 $O\left(\sum|s_i|+|S|\right)$

                  -

                  空间复杂度 $O\left(\sum|s_i|+|S|\right)$

                  +

                  AC自动机(二次加强版)

                  +

                  时间复杂度 \(O\left(\sum|s_i|+|S|\right)\)

                  +

                  空间复杂度 \(O\left(\sum|s_i|+|S|\right)\)

                  #include <bits/stdc++.h>
                   using namespace std;
                   #define int long long
                  @@ -1022,9 +1051,11 @@ 

                  printf("%lld\n",ans[find(i)]); return 0; }

                  -
                  -

                  后缀数组

                    -
                  1. 朴素写法 时间复杂度 $O(n\log n)$
                  2. +
                    +

                    后缀数组

                    +
                      +
                    1. 朴素写法 时间复杂度 \(O(n\log +n)\)
                    #include <bits/stdc++.h>
                     using namespace std;
                    @@ -1068,9 +1099,10 @@ 

                    << sa[i] << " \n"[i==n]; return 0; }

                    -
                    -

                    后缀自动机 SAM

                    时间复杂度 $O(n)$

                    -

                    空间复杂度 $O(n)$

                    +
                    +

                    后缀自动机 SAM

                    +

                    时间复杂度 \(O(n)\)

                    +

                    空间复杂度 \(O(n)\)

                    #include <bits/stdc++.h>
                     using namespace std;
                     #define int long long
                    @@ -1137,10 +1169,12 @@ 

                    << ans << endl; return 0; }

                    -
                    -

                    广义后缀自动机 ExSAM

                    之前看到巨佬的一个hack,还不确定我这个对不对

                    -

                    这里是讨论 link

                    -

                    时间复杂度 $O(n|\Sigma|)$

                    +
                    +

                    广义后缀自动机 ExSAM

                    +

                    之前看到巨佬的一个hack,还不确定我这个对不对

                    +

                    这里是讨论 link

                    +

                    时间复杂度 \(O(n|\Sigma|)\)

                    #include <bits/stdc++.h>
                     using namespace std;
                     #define int long long
                    @@ -1224,8 +1258,9 @@ 

                    << res << endl; return 0; }

                    -
                    -

                    回文自动机 PAM

                    时间复杂度 $O(n)$

                    +
                    +

                    回文自动机 PAM

                    +

                    时间复杂度 \(O(n)\)

                    #include <bits/stdc++.h>
                     using namespace std;
                     #define int long long
                    @@ -1269,8 +1304,9 @@ 

                    } return 0; }

                    -
                    -

                    子序列自动机

                    时间复杂度 $O(n\log n)$

                    +
                    +

                    子序列自动机

                    +

                    时间复杂度 \(O(n\log n)\)

                    #include <bits/stdc++.h>
                     using namespace std;
                     #define int long long
                    @@ -1677,7 +1713,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/19/oi-mo-ban/index.html b/2022/07/19/oi-mo-ban/index.html index 70a618a084..3f2540c1f8 100644 --- a/2022/07/19/oi-mo-ban/index.html +++ b/2022/07/19/oi-mo-ban/index.html @@ -468,47 +468,60 @@

                    OI模板

                    -

                    OI模板

                    由于文件比较多,分为了多个部分。

                    -
                    +

                    OI模板

                    +

                    由于文件比较多,分为了多个部分。

                    ++++ - + - - - + + + - - - + + + - - + + - - - + + + - - + + - - + + - - + +
                    Parts 包含内容
                    OI模板-图论最短路算法、最小生成树、线段树优化建图、判负环、kosaraju算法、Tarjan算法 [连通性问题]、欧拉路径、欧拉回路、2-SAT、网络流算法、最小树形图、二分图、无向图的最小环问题
                    OI模板-图论最短路算法、最小生成树、线段树优化建图、判负环、kosaraju算法、Tarjan算法 +[连通性问题]、欧拉路径、欧拉回路、2-SAT、网络流算法、最小树形图、二分图、无向图的最小环问题
                    OI模板-字符串字符串哈希、KMP、ExKMP、Manacher、Trie树、AC自动机、后缀数组、后缀自动机 SAM、广义后缀自动机 ExSAM、回文自动机 PAM、子序列自动机
                    OI模板-字符串字符串哈希、KMP、ExKMP、Manacher、Trie树、AC自动机、后缀数组、后缀自动机 +SAM、广义后缀自动机 ExSAM、回文自动机 PAM、子序列自动机
                    OI模板-算法
                    OI模板-算法 排序算法、CDQ分治、LCA、高精度加减乘除、高精度封装版
                    OI模板-其他光速幂、$O(1)$ 快速乘、二维数点、乘法取模封装
                    OI模板-其他光速幂、\(O(1)\) +快速乘、二维数点、乘法取模封装
                    OI模板-计算几何
                    OI模板-计算几何 二维凸包、平面最近点对、半平面交、旋转卡壳、扫描线、随机增量法
                    OI模板-数据结构
                    OI模板-数据结构 并查集、单调队列、二叉堆、左偏树(可并堆)、珂朵莉树、平衡树、可持久化平衡树、ST表、树链剖分、树套树、线段树、李超线段树、线段树分裂、静态仙人掌、可持久化数组、可持久化线段树(主席树)
                    OI模板-数学
                    OI模板-数学 目前不全、快速幂、矩阵快速幂、判断素数、线性筛、二次剩余、康托展开、逆康托展开
                    -
                    @@ -870,7 +883,7 @@

                    OI
                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/20/cf877e-danil-and-a-part-time-job-ti-jie/index.html b/2022/07/20/cf877e-danil-and-a-part-time-job-ti-jie/index.html index 7d22555d3d..3fd82824db 100644 --- a/2022/07/20/cf877e-danil-and-a-part-time-job-ti-jie/index.html +++ b/2022/07/20/cf877e-danil-and-a-part-time-job-ti-jie/index.html @@ -476,7 +476,10 @@

                    CF877E Danil and a Part-time Job
                    -

                    CF877E Danil and a Part-time Job 题解

                    题目链接:CF877E Danil and a Part-time Job

                    +

                    CF877E Danil and a +Part-time Job 题解

                    +

                    题目链接:CF877E +Danil and a Part-time Job

                    题意

                    一棵树有n个点,根结点编号为1,每个点的权值都是1或0
                    @@ -499,8 +502,9 @@ 

                    \(1\) +就好了

                    +

                    时间复杂度 \(O(m \log n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -974,7 +978,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/20/luo-gu-p5142-qu-jian-fang-chai-ti-jie/index.html b/2022/07/20/luo-gu-p5142-qu-jian-fang-chai-ti-jie/index.html index 105890c93b..27927301f8 100644 --- a/2022/07/20/luo-gu-p5142-qu-jian-fang-chai-ti-jie/index.html +++ b/2022/07/20/luo-gu-p5142-qu-jian-fang-chai-ti-jie/index.html @@ -476,32 +476,55 @@

                    洛谷P5142 区间方差 题解<
                    -

                    洛谷P5142 区间方差 题解

                    题目链接:P5142 区间方差

                    +

                    洛谷P5142 区间方差 题解

                    +

                    题目链接:P5142 +区间方差

                    题意

                    -

                    对于一个长度为 $n$ 的序列 $a_1,a_2,a_3\cdots a_n$,我们定义它的平均数 $a$ 为:

                    -

                    并定义它的方差 $d$ 为:

                    -

                    现在给定一个长度为 $n$ 的序列 $a_1,a_2\cdots a_n$。你需要支持两种操作。每种操作的格式为 c x y

                    -

                    若 $c=1$,为修改操作,代表将 $a_x$ 赋值为 $y$。

                    -

                    若 $c=2$,为查询操作,代表查询 $a_x$ 到 $a_y$ 的方差。

                    -

                    为了避免浮点数误差,请以分数取模形式输出结果(对 1000000007($10^9+7$)取模)。

                    -

                    对于 $100\%$ 的数据,$1\leq n,m\leq 1\times 10^5$,$1\leq a_i\leq 1\times 10^9$,$1\leq x\leq n$。对于操作 1,$1\leq y\leq 1\times 10^9$。对于操作2,$x\leq y\leq n$。

                    +

                    对于一个长度为 \(n\) 的序列 \(a_1,a_2,a_3\cdots a_n\),我们定义它的平均数 +\(a\) 为:

                    +

                    \[ +a=\frac{1}{n}\sum_{i=1}^{n}a_i +\]

                    +

                    并定义它的方差 \(d\) 为:

                    +

                    \[ +d=\frac{1}{n}\sum_{i=1}^{n}(a_i-a)^2 +\] 现在给定一个长度为 \(n\) +的序列 \(a_1,a_2\cdots +a_n\)。你需要支持两种操作。每种操作的格式为 +c x y

                    +

                    \(c=1\),为修改操作,代表将 \(a_x\) 赋值为 \(y\)

                    +

                    \(c=2\),为查询操作,代表查询 +\(a_x\)\(a_y\) 的方差。

                    +

                    为了避免浮点数误差,请以分数取模形式输出结果(对 1000000007(\(10^9+7\))取模)。

                    +

                    对于 \(100\%\) 的数据,\(1\leq n,m\leq 1\times 10^5\)\(1\leq a_i\leq 1\times 10^9\)\(1\leq x\leq n\)。对于操作 1,\(1\leq y\leq 1\times +10^9\)。对于操作2,\(x\leq y\leq +n\)

                    -

                    方差有个更快的公式

                    -

                    推导过程很简单,只要把那个 $(a-a_i)^2$ 展开就好了

                    -

                    这样我们就只要维护两个数组

                    -

                    单点修改都不需要懒标记,很水吧

                    +S_1 &= \sum_{i=l}^{r}a_i +\\S_2&= \sum_{i=l}^{r}a_i^2 +\end{aligned} +\] 单点修改都不需要懒标记,很水吧

                    不过这个取模除法,所以要算个逆元

                    -

                    因为 $10^9+7$ 是个质数,而且 $a_i \le 10^9$

                    +

                    因为 \(10^9+7\) 是个质数,而且 \(a_i \le 10^9\)

                    所以直接用费马小定理那个东西求个逆元就好了

                    -

                    时间复杂度 $O(m \log (na_i))$

                    +

                    时间复杂度 \(O(m \log (na_i))\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -995,7 +1018,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/20/shi-jian-chuo-you-hua-shu-zhuang-shu-zu-de-pin-fan-qing-kong/index.html b/2022/07/20/shi-jian-chuo-you-hua-shu-zhuang-shu-zu-de-pin-fan-qing-kong/index.html index 3840258156..78062de2c6 100644 --- a/2022/07/20/shi-jian-chuo-you-hua-shu-zhuang-shu-zu-de-pin-fan-qing-kong/index.html +++ b/2022/07/20/shi-jian-chuo-you-hua-shu-zhuang-shu-zu-de-pin-fan-qing-kong/index.html @@ -468,18 +468,27 @@

                    时间戳优化树状数组的
                    -

                    时间戳优化树状数组的频繁清空

                    来自@zx2017老师的ppt Orz

                    +

                    时间戳优化树状数组的频繁清空

                    +

                    来自@zx2017老师的ppt Orz

                    适用场景:

                    -

                    给定 $T$ 组数据,每组数据有 $Q$ 个询问,询问给定 $n$ 个数的区间和

                    -

                    数据范围:$T\le 2\times 10^5,~\sum Q \le 2\times 10^5,~n\le 5\times 10^5$

                    -

                    注意,这里只限制了 $\sum Q$ ,并没有限制 $\sum n$

                    -

                    对于每组数据不能直接去清空数组,因为 $T \times n$ 肯定爆炸

                    +

                    给定 \(T\) 组数据,每组数据有 \(Q\) 个询问,询问给定 \(n\) 个数的区间和

                    +

                    数据范围\(T\le 2\times +10^5,~\sum Q \le 2\times 10^5,~n\le 5\times 10^5\)

                    +

                    注意,这里只限制了 \(\sum Q\) +,并没有限制 \(\sum n\)

                    +

                    对于每组数据不能直接去清空数组,因为 \(T +\times n\) 肯定爆炸

                    考虑维护一个时间戳now,也就是现在是第几组数据

                    对于是这组数据的,也就是t[i]==now的情况,直接用

                    如果不是这组数据的,我们要先清零,再加。

                    -

                    注意到 tree[i]=0,tree[i]+=v 其实等价于 tree[i]=v

                    +

                    注意到 tree[i]=0,tree[i]+=v 其实等价于 +tree[i]=v

                    由于我们只有查询到一个树上结点,才会去判断它的时间戳

                    -

                    因此这类题目的时间复杂度可以控制在 $O(\sum Q \log \sum Q)$

                    +

                    因此这类题目的时间复杂度可以控制在 \(O(\sum +Q \log \sum Q)\)

                    修改部分的代码:

                    int tree[N],t[N],now=0;
                     void add(int x,int v)
                    @@ -501,7 +510,9 @@ 

                    P1120 小木棍 ,清空临时标记数组vis,类似于这个优化

                    +

                    upd.20220722 P1120 小木棍 +,清空临时标记数组vis,类似于这个优化

                    这么一说,好像匈牙利算法也是可以这么优化的来着。。。

                    @@ -872,7 +883,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/21/cf475d-cgcdssq-ti-jie/index.html b/2022/07/21/cf475d-cgcdssq-ti-jie/index.html index 2d9b0fb89d..af7f75c915 100644 --- a/2022/07/21/cf475d-cgcdssq-ti-jie/index.html +++ b/2022/07/21/cf475d-cgcdssq-ti-jie/index.html @@ -476,27 +476,44 @@

                    CF475D CGCDSSQ 题解

                    -

                    CF475D CGCDSSQ 题解

                    题目链接:CF475D CGCDSSQ

                    +

                    CF475D CGCDSSQ 题解

                    +

                    题目链接:CF475D +CGCDSSQ

                    题意

                    -

                    给出一个长度为 $n$ 的序列和 $q$ 个询问,

                    -

                    每个询问输出一行,询问满足 $\gcd\{a_l,a_{l+1},\dots,a_r\}=x$ 的 $[l,r]$ 的对数

                    -

                    $ 1 \le n \le 10^5,~1 \le q \le 3 \times 10^5,~1 \le a_i,x_i \le 10^9$

                    +

                    给出一个长度为 \(n\) 的序列和 \(q\) 个询问,

                    +

                    每个询问输出一行,询问满足 \(\gcd\{a_l,a_{l+1},\dots,a_r\}=x\)\([l,r]\) 的对数

                    +

                    $ 1 n ^5,~1 q ^5,~1 a_i,x_i ^9$

                    这题有一个很有趣的结论

                    -

                    区间 $\gcd$ 最多有 $O(n\log \max\{a_i\})$ 种可能的值

                    +

                    区间 \(\gcd\) 最多有 \(O(n\log \max\{a_i\})\) 种可能的值

                    证明:

                    -

                    原命题等价于: $l$ 固定时,$[l,r]$ 的区间 $\gcd$ 至多有 $O(\log \max\{a_i\})$ 种取值。

                    -

                    假设区间 $\gcd$ 在 $i$ 处发生了变化,此时新的 $\gcd$ 一定是原 $\gcd$ 的因数

                    -

                    因此新的 $\gcd$ 至少缩小了一半,则至多有 $O(\log \max\{a_i\})$ 种取值。

                    +

                    原命题等价于: \(l\) 固定时,\([l,r]\) 的区间 \(\gcd\) 至多有 \(O(\log \max\{a_i\})\) 种取值。

                    +

                    假设区间 \(\gcd\)\(i\) 处发生了变化,此时新的 \(\gcd\) 一定是原 \(\gcd\) 的因数

                    +

                    因此新的 \(\gcd\) +至少缩小了一半,则至多有 \(O(\log +\max\{a_i\})\) 种取值。

                    这样我们就可以预处理所有答案,用个unordered_map记录了

                    注:这里其实可以优化一下,不用记录所有答案,只记录询问的答案

                    那么答案怎么统计呢?

                    -

                    区间 $\gcd$ 直接用st表维护,应该很容易想到。

                    -

                    当 $l$ 固定时,至多有 $O(\log \max\{a_i\})$ 种取值

                    +

                    区间 \(\gcd\) +直接用st表维护,应该很容易想到。

                    +

                    \(l\) 固定时,至多有 \(O(\log \max\{a_i\})\) 种取值

                    而这个值一定随着右端点的增大单调不升

                    考虑二分右端点,然后记录下答案啥的

                    -

                    时间复杂度 $O(n\log n \log \max \{a_i\})$

                    +

                    时间复杂度 \(O(n\log n \log \max +\{a_i\})\)

                    注意st表一定要预处理查询时候的lg[]数组,因为log()常数比较大

                    代码:

                    #include <iostream>
                    @@ -970,7 +987,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/21/cf914d-bash-and-a-tough-math-puzzle-ti-jie/index.html b/2022/07/21/cf914d-bash-and-a-tough-math-puzzle-ti-jie/index.html index 8fad9f3d43..5f35012200 100644 --- a/2022/07/21/cf914d-bash-and-a-tough-math-puzzle-ti-jie/index.html +++ b/2022/07/21/cf914d-bash-and-a-tough-math-puzzle-ti-jie/index.html @@ -476,28 +476,45 @@

                    CF914D Bash and a Tough Math Puz
                    -

                    CF914D Bash and a Tough Math Puzzle 题解

                    题目链接:CF914D Bash and a Tough Math Puzzle

                    +

                    CF914D Bash and a +Tough Math Puzzle 题解

                    +

                    题目链接:CF914D +Bash and a Tough Math Puzzle

                    题意

                      -
                    • 给定长度为 $n$ 的序列 $a$。$m$ 次操作。操作有两种:
                        -
                      1. 1 l r x:若能在 $a_l\sim a_r$ 中 至多 修改一个数,使得 $\gcd(a_l,a_{l+1},\cdots,a_r)=x$,输出 YES,否则输出 NO。注意:我们不需要进行实际的改动。
                      2. -
                      3. 2 i y:将 $a_i$ 修改为 $y$。
                      4. -
                      -
                    • -
                    • $1\le n\le 5\times 10^5$,$1\le m\le 4\times 10^5$,$1\le a_i,x,y\le 10^9$。
                    • +
                    • 给定长度为 \(n\) 的序列 \(a\)\(m\) +次操作。操作有两种: +
                        +
                      1. 1 l r x:若能在 \(a_l\sim +a_r\)至多 修改一个数,使得 \(\gcd(a_l,a_{l+1},\cdots,a_r)=x\),输出 +YES,否则输出 +NO。注意:我们不需要进行实际的改动。
                      2. +
                      3. 2 i y:将 \(a_i\) +修改为 \(y\)
                      4. +
                    • +
                    • \(1\le n\le 5\times 10^5\)\(1\le m\le 4\times 10^5\)\(1\le a_i,x,y\le 10^9\)
                    -

                    设区间内不能被 $x$ 整除的数的个数为 $\text{cnt}$

                    +

                    设区间内不能被 \(x\) +整除的数的个数为 \(\text{cnt}\)

                      -
                    • 若 $\text{cnt}=0$ ,则改不改无所谓
                    • -
                    • 若 $\text{cnt}=1$ ,则直接把那个数修改为 $x$ 即可
                    • -
                    • 若 $\text{cnt}>1$ ,则无解,输出NO
                    • +
                    • \(\text{cnt}=0\) +,则改不改无所谓
                    • +
                    • \(\text{cnt}=1\) +,则直接把那个数修改为 \(x\) 即可
                    • +
                    • \(\text{cnt}>1\) +,则无解,输出NO
                    -

                    考虑用线段树维护区间 $\gcd$

                    -

                    查询时,只进入区间 $\gcd$ 能被 $x$ 整除的子结点

                    +

                    考虑用线段树维护区间 \(\gcd\)

                    +

                    查询时,只进入区间 \(\gcd\) 能被 +\(x\) 整除的子结点

                    如果到达叶子就 ++cnt

                    -

                    时间复杂度 $O(n \log^2 n)$

                    +

                    时间复杂度 \(O(n \log^2 n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -980,7 +997,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/21/luo-gu-p1410-zi-xu-lie-ti-jie/index.html b/2022/07/21/luo-gu-p1410-zi-xu-lie-ti-jie/index.html index 0e0d984fc0..f8a4bdabd6 100644 --- a/2022/07/21/luo-gu-p1410-zi-xu-lie-ti-jie/index.html +++ b/2022/07/21/luo-gu-p1410-zi-xu-lie-ti-jie/index.html @@ -472,10 +472,15 @@

                    洛谷P1410 子序列 题解

                    -

                    洛谷P1410 子序列 题解

                    题目链接:P1410 子序列

                    +

                    洛谷P1410 子序列 题解

                    +

                    题目链接:P1410 +子序列

                    题意

                    -

                    给定一个长度为 $N$($N$ 为偶数)的序列,问能否将其划分为两个长度为 $N / 2$ 的严格递增子序列。

                    +

                    给定一个长度为 \(N\)\(N\) +为偶数)的序列,问能否将其划分为两个长度为 \(N +/ 2\) 的严格递增子序列。

                    【数据范围】

                    共三组数据,每组数据行数<=50,0 <= 输入的所有数 <= 10^9

                    第一组(30%):N <= 20

                    @@ -483,21 +488,41 @@

                    第三组(40%):N <= 2000

                    首先可以有个原始的思路

                    -

                    设 $f_{i,j,k}$ 表示前 $i$ 位,以 $i$ 结尾的上升子序列长度为 $j$ ,另一个上升子序列以 $k$ 结尾是否可行。

                    +

                    \(f_{i,j,k}\) 表示前 \(i\) 位,以 \(i\) 结尾的上升子序列长度为 \(j\) ,另一个上升子序列以 \(k\) 结尾是否可行。

                    如果仅仅用是否可行去做,比较浪费。

                    -

                    注意到我们一定希望 $a_k$ 尽可能小,这个很好用dp去算

                    -

                    考虑把 $k$ 这一维去掉,移到 $f$ 里让它算。

                    -

                    设 $f_{i,j}$ 表示以 $i$ 结尾的上升子序列长度为 $j$ ,不以 $i$ 结尾的另一条上升子序列的结尾的最小值。

                    -

                    如果 $a_i < a_{i+1}$ ,那么 $a_{i+1}$ 可以被加入以 $i$ 结尾的上升子序列

                    -

                    如果 $f_{i,j}<a_{i+1}$ ,那么 $a_{i+1}$ 可以被加入不以 $i$ 结尾的上升子序列

                    -

                    注意此时如果加入不以 $i$ 结尾的上升子序列更优的话,

                    +

                    注意到我们一定希望 \(a_k\) +尽可能小,这个很好用dp去算

                    +

                    考虑把 \(k\) 这一维去掉,移到 \(f\) 里让它算。

                    +

                    \(f_{i,j}\) 表示以 \(i\) 结尾的上升子序列长度为 \(j\) ,不以 \(i\) +结尾的另一条上升子序列的结尾的最小值。

                    +

                    如果 \(a_i < a_{i+1}\) ,那么 +\(a_{i+1}\) 可以被加入以 \(i\) 结尾的上升子序列 \[ +f_{i+1,j+1}=\min\{f_{i+1,j+1},f_{i,j}\} +\] 如果 \(f_{i,j}<a_{i+1}\) +,那么 \(a_{i+1}\) 可以被加入不以 \(i\) 结尾的上升子序列

                    +

                    注意此时如果加入不以 \(i\) +结尾的上升子序列更优的话,

                      -
                    • 不以 $i$ 结尾的那个子序列变成了以 $i+1$ 结尾的子序列
                    • -
                    • 以 $i$ 结尾的那个子序列变成了不以 $i+1$ 结尾的子序列
                    • +
                    • 不以 \(i\) 结尾的那个子序列变成了以 +\(i+1\) 结尾的子序列
                    • +
                    • \(i\) 结尾的那个子序列变成了不以 +\(i+1\) 结尾的子序列
                    -

                    时间复杂度 $O(Qn^2)$

                    +

                    \[ +f_{i+1,i-j+1}=\min\{f_{i+1,i-j+1},a_i\} +\]

                    +

                    时间复杂度 \(O(Qn^2)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -915,7 +940,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/21/luo-gu-p2657-scoi2009-windy-shu-ti-jie/index.html b/2022/07/21/luo-gu-p2657-scoi2009-windy-shu-ti-jie/index.html index b468d6743b..c6a55936b4 100644 --- a/2022/07/21/luo-gu-p2657-scoi2009-windy-shu-ti-jie/index.html +++ b/2022/07/21/luo-gu-p2657-scoi2009-windy-shu-ti-jie/index.html @@ -472,11 +472,20 @@

                    洛谷P2657 [SCOI2009] windy 数
                    -

                    洛谷P2657 [SCOI2009] windy 数 题解

                    题目链接:P2657 [SCOI2009] windy 数

                    +

                    洛谷P2657 [SCOI2009] windy 数 +题解

                    +

                    题目链接:P2657 +[SCOI2009] windy 数

                    题意

                    -

                    不含前导零且相邻两个数字之差至少为 $2$ 的正整数被称为 windy 数。windy 想知道,在 $a$ 和 $b$ 之间,包括 $a$ 和 $b$ ,总共有多少个 windy 数?

                    -

                    对于全部的测试点,保证 $1 \leq a \leq b \leq 2 \times 10^9$。

                    +

                    不含前导零且相邻两个数字之差至少为 \(2\) 的正整数被称为 windy 数。windy +想知道,在 \(a\)\(b\) 之间,包括 \(a\)\(b\) ,总共有多少个 windy 数?

                    +

                    对于全部的测试点,保证 \(1 \leq a \leq b +\leq 2 \times 10^9\)

                    常规的数位dp,只需要记录上一位的数字即可

                    但是注意,最高位是不需要限制的

                    @@ -484,18 +493,27 @@

                    \(4234\) +,假设我们从高位开始做

                    +

                    如果以 \(4\) +开始,可以发现接下来一位不能超过 \(3\)

                    +

                    如果用 \(0 \sim 3\) +,则接下来没有任何限制

                    +

                    但是如果我们从低位开始做,比如填了一个 \(8\)

                    +

                    那么最后我们有可能推出来个 \(4238\) +之类的

                    因为无法判断

                    回到刚刚的问题,我们从高位往低位做数位dp

                    那么第一位,也就是我们推的某个数的最高位,

                    -

                    是可以随便填而不受到大于 $2$ 那个限制的(即前导零不能限制实际的最高位选择)

                    -

                    因此我们还需要记录是否之前全是 $0$

                    -

                    复杂度不太清楚,不过应该和位数有关,这题的位数就十几,所以基本上是 $O(1)$ 的

                    +

                    是可以随便填而不受到大于 \(2\) +那个限制的(即前导零不能限制实际的最高位选择)

                    +

                    因此我们还需要记录是否之前全是 \(0\)

                    +

                    复杂度不太清楚,不过应该和位数有关,这题的位数就十几,所以基本上是 +\(O(1)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -914,7 +932,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/21/luo-gu-p6225-ejoi2019-yi-huo-cheng-zi-ti-jie/index.html b/2022/07/21/luo-gu-p6225-ejoi2019-yi-huo-cheng-zi-ti-jie/index.html index f5995d1c76..91302b9fd5 100644 --- a/2022/07/21/luo-gu-p6225-ejoi2019-yi-huo-cheng-zi-ti-jie/index.html +++ b/2022/07/21/luo-gu-p6225-ejoi2019-yi-huo-cheng-zi-ti-jie/index.html @@ -472,7 +472,10 @@

                    洛谷P6225 [eJOI2019] 异或橙
                    -

                    洛谷P6225 [eJOI2019] 异或橙子 题解

                    题目链接:P6225 [eJOI2019] 异或橙子

                    +

                    洛谷P6225 [eJOI2019] 异或橙子 +题解

                    +

                    题目链接:P6225 +[eJOI2019] 异或橙子

                    题意

                    序列上有n个值,第i个值为Ai
                    @@ -494,20 +497,31 @@ 

                    -

                    其实就是单点修改+查询下面这个柿子

                    -

                    考虑区间中一个数的出现次数

                    -

                    设 $l \le i \le r$ ,则 $i$ 在区间 $[l,r]$ 的异或和的异或和中出现的次数为

                    -

                    推导过程:

                    -

                    包含 $i$ 的区间 $[l^{\prime},r^{\prime}]$一定满足 $l^{\prime} \le i \le r^{\prime}$

                    -

                    显然 $l^{\prime}$ 有 $i-l+1$ 种取值,$r^{\prime}$ 有 $r-i+1$ 种取值,易得上式。

                    -

                    根据异或的自反性,则 $i$ 的贡献取决于它出现次数的奇偶性

                    -

                    显然当 $(i-l+1)$ 和 $(r-i+1)$ 都为奇数时 $i$ 的出现次数为奇数

                    -

                    不难发现,此时 $i,l,r$ 同奇偶

                    +

                    其实就是单点修改+查询下面这个柿子 \[ +\bigoplus_{i=l}^{r}\bigoplus_{j=i}^{r}a_j +\] 考虑区间中一个数的出现次数

                    +

                    \(l \le i \le r\) ,则 \(i\) 在区间 \([l,r]\) 的异或和的异或和中出现的次数为 +\[ +(i-l+1) \times (r-i+1) +\] 推导过程:

                    +

                    包含 \(i\) 的区间 \([l^{\prime},r^{\prime}]\)一定满足 \(l^{\prime} \le i \le r^{\prime}\)

                    +

                    显然 \(l^{\prime}\)\(i-l+1\) 种取值,\(r^{\prime}\)\(r-i+1\) 种取值,易得上式。

                    +

                    根据异或的自反性,则 \(i\) +的贡献取决于它出现次数的奇偶性

                    +

                    显然当 \((i-l+1)\)\((r-i+1)\) 都为奇数时 \(i\) 的出现次数为奇数

                    +

                    不难发现,此时 \(i,l,r\) 同奇偶

                    故实际的贡献形如10101

                    考虑用两个树状数组,分别维护在奇、偶位置上的数的异或和

                    -

                    时间复杂度 $O(m \log n)$

                    +

                    时间复杂度 \(O(m \log n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -936,7 +950,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/22/cf479e-riding-in-a-lift-ti-jie/index.html b/2022/07/22/cf479e-riding-in-a-lift-ti-jie/index.html index 95a6d9565b..55860a2489 100644 --- a/2022/07/22/cf479e-riding-in-a-lift-ti-jie/index.html +++ b/2022/07/22/cf479e-riding-in-a-lift-ti-jie/index.html @@ -472,7 +472,9 @@

                    CF479E Riding in a Lift 题解
                    -

                    CF479E Riding in a Lift 题解

                    题目链接:CF479E Riding in a Lift

                    +

                    CF479E Riding in a Lift 题解

                    +

                    题目链接:CF479E +Riding in a Lift

                    题意

                    现在有n个传送点呈序列排列,编号为1到n
                    @@ -492,11 +494,13 @@ 

                    -\left[\max(1,j-|j-b|+1),j) \cup (j,\min(n,j+|j-b|-1)\right]

                    直接暴力去更新肯定是不行的

                    +

                    每个点 \(j\) (除了 \(b\) ,它不更新)能更新的范围都是 \[ +\left[\max(1,j-|j-b|+1),j) \cup (j,\min(n,j+|j-b|-1)\right] +\] 直接暴力去更新肯定是不行的

                    注意到其实它的更新是区间加,考虑差分,然后去掉「跳到自己」的贡献

                    -

                    时间复杂度 $O(nk)$

                    +

                    时间复杂度 \(O(nk)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -915,7 +919,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/22/luo-gu-p1879-usaco06nov-corn-fields-g-ti-jie/index.html b/2022/07/22/luo-gu-p1879-usaco06nov-corn-fields-g-ti-jie/index.html index 66c4879389..6d678317d7 100644 --- a/2022/07/22/luo-gu-p1879-usaco06nov-corn-fields-g-ti-jie/index.html +++ b/2022/07/22/luo-gu-p1879-usaco06nov-corn-fields-g-ti-jie/index.html @@ -472,15 +472,28 @@

                    洛谷P1879 [USACO06NOV]Corn Fie
                    -

                    洛谷P1879 [USACO06NOV]Corn Fields G 题解

                    题目链接:P1879 [USACO06NOV]Corn Fields G

                    +

                    洛谷P1879 +[USACO06NOV]Corn Fields G 题解

                    +

                    题目链接:P1879 +[USACO06NOV]Corn Fields G

                    题意

                    -

                    农场主 $\rm John$ 新买了一块长方形的新牧场,这块牧场被划分成 $M$ 行 $N$ 列 $(1 \le M \le 12; 1 \le N \le 12)$,每一格都是一块正方形的土地。 $\rm John$ 打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。

                    -

                    遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是 $\rm John$ 不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。

                    -

                    $\rm John$ 想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

                    +

                    农场主 \(\rm John\) +新买了一块长方形的新牧场,这块牧场被划分成 \(M\)\(N\)\((1 \le M +\le 12; 1 \le N \le 12)\),每一格都是一块正方形的土地。 \(\rm John\) +打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。

                    +

                    遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是 +\(\rm John\) +不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。

                    +

                    \(\rm John\) +想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

                    状压经典题

                    -

                    首先把每一行的情况压到状态 $F_i$ 中

                    +

                    首先把每一行的情况压到状态 \(F_i\) +中

                    可以预处理所有状态(选择哪些种草)在行上是否合法

                    例如两块草不能相邻选,当然此时不用考虑可不可以种

                    判断依据如下

                    @@ -488,7 +501,7 @@

                    for(int i=0; i<mx; i++) st[i]=( ((i&(i<<1))==0) && ((i&(i>>1))==0) );

                    然后枚举状态转移

                    -

                    时间复杂度 $O(n2^{2n})$

                    +

                    时间复杂度 \(O(n2^{2n})\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -535,6 +548,7 @@ 

                    << res << '\n'; return 0; }

                    +


                    @@ -899,7 +913,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/22/luo-gu-p2900-usaco08mar-land-acquisition-g-ti-jie/index.html b/2022/07/22/luo-gu-p2900-usaco08mar-land-acquisition-g-ti-jie/index.html index 14bfc8c33e..75c58d0b24 100644 --- a/2022/07/22/luo-gu-p2900-usaco08mar-land-acquisition-g-ti-jie/index.html +++ b/2022/07/22/luo-gu-p2900-usaco08mar-land-acquisition-g-ti-jie/index.html @@ -476,39 +476,72 @@

                    洛谷P2900 [USACO08MAR]Land Acq
                    -

                    洛谷P2900 [USACO08MAR]Land Acquisition G 题解

                    题目链接:P2900 [USACO08MAR]Land Acquisition G

                    +

                    洛谷P2900 +[USACO08MAR]Land Acquisition G 题解

                    +

                    题目链接:P2900 +[USACO08MAR]Land Acquisition G

                    题意

                    -

                    Farmer John 准备扩大他的农场,眼前他正在考虑购买 $N$ 块长方形的土地。

                    -

                    如果 FJ 单买一块土地,价格就是土地的面积。但他可以选择并购一组土地,并购的价格为这些土地中最大的长乘以最大的宽。比如 FJ 并购一块 $3 \times 5$ 和一块 $5 \times 3$ 的土地,他只需要支付 $5 \times 5=25$ 元, 比单买合算。

                    -

                    FJ 希望买下所有的土地。他发现,将这些土地分成不同的小组来并购可以节省经费。 给定每份土地的尺寸,请你帮助他计算购买所有土地所需的最小费用。

                    -

                    第一行一个整数 $N$($1 \leq N \leq 5 \times 10^4$)。

                    -

                    接下来 $N$ 行,每行两个整数 $w_i$ 和 $l_i$,代表第 $i$ 块土地的长和宽。保证土地的长和宽不超过 $10^6$。

                    +

                    Farmer John 准备扩大他的农场,眼前他正在考虑购买 \(N\) 块长方形的土地。

                    +

                    如果 FJ +单买一块土地,价格就是土地的面积。但他可以选择并购一组土地,并购的价格为这些土地中最大的长乘以最大的宽。比如 +FJ 并购一块 \(3 \times 5\) 和一块 \(5 \times 3\) 的土地,他只需要支付 \(5 \times 5=25\) 元, 比单买合算。

                    +

                    FJ +希望买下所有的土地。他发现,将这些土地分成不同的小组来并购可以节省经费。 +给定每份土地的尺寸,请你帮助他计算购买所有土地所需的最小费用。

                    +

                    第一行一个整数 \(N\)\(1 \leq N \leq 5 \times 10^4\))。

                    +

                    接下来 \(N\) 行,每行两个整数 \(w_i\)\(l_i\),代表第 \(i\) 块土地的长和宽。保证土地的长和宽不超过 +\(10^6\)

                    输出买下所有土地的最小费用。

                    斜率优化板子题。

                    首先可以推出来一个原始的转移方程

                    -

                    设 $f_i$ 表示买前 $i$ 个土地的最小花费

                    -

                    但是这样子根本没法优化。

                    +

                    \(f_i\) 表示买前 \(i\) 个土地的最小花费 \[ +f_i=\min\{f_{j} + \text{mxw}\{j+1,i\} \times \text{mxl}\{j+1,i\}\} +\] 但是这样子根本没法优化。

                    考虑怎样的土地可以对答案计算产生贡献(也就是必须计算花费的)

                    或者说,怎样的土地不能产生贡献

                    -

                    设土地 $i,j$ 满足 $w_i \ge w_j,~l_i \ge l_j$

                    -

                    则购买 $i$ 的时候, $j$ 可以与其并购并且不增加额外的花费

                    -

                    因此我们将所有物品按 $w_i$ 从大到小排序后所有能产生贡献的一定 $l_i$ 比之前大

                    -

                    即 $\forall i<j,w_i\ge w_j,l_i\le l_j$

                    +

                    设土地 \(i,j\) 满足 \(w_i \ge w_j,~l_i \ge l_j\)

                    +

                    则购买 \(i\) 的时候, \(j\) 可以与其并购并且不增加额外的花费

                    +

                    因此我们将所有物品按 \(w_i\) +从大到小排序后所有能产生贡献的一定 \(l_i\) 比之前大

                    +

                    \(\forall i<j,w_i\ge w_j,l_i\le +l_j\)

                    至此,我们可以推出更好的转移方程

                    -

                    设 $f_i$ 表示买前 $i$ 个土地的最小花费

                    -

                    当 $i$ 固定时,考虑怎样的一个 $j\,(i>j>k)$ 使得 $j$ 比 $k$ 更优

                    -

                    由于 $w$ 是递减的,即 $w_{k+1} \ge w_{j+1}$ ,因此移项可得

                    -

                    把右边的看作斜率,然后就是斜率优化

                    -

                    因为要求最小化 $f_i$ ,因此我们要维护一个下凸壳

                    -

                    关于凸壳的问题,可以看这篇 P3195 [HNOI2008]玩具装箱

                    -

                    时间复杂度 $O(n \log n)$

                    +\\f_j-f_k \le l_i \times (w_{k+1}-w_{j+1}) +\] 由于 \(w\) 是递减的,即 \(w_{k+1} \ge w_{j+1}\) ,因此移项可得 \[ +\dfrac{f_j-f_k}{w_{k+1}-w_{j+1}} \le l_i +\] 把右边的看作斜率,然后就是斜率优化

                    +

                    因为要求最小化 \(f_i\) +,因此我们要维护一个下凸壳

                    +

                    关于凸壳的问题,可以看这篇 P3195 +[HNOI2008]玩具装箱

                    +

                    时间复杂度 \(O(n \log n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -562,7 +595,8 @@ 

                    return 0; }

                    参考文献

                    -

                    [1] https://www.luogu.com.cn/problem/solution/P2900

                    +

                    [1] https://www.luogu.com.cn/problem/solution/P2900

                    @@ -932,7 +966,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/23/cf808g-anthem-of-berland-ti-jie/index.html b/2022/07/23/cf808g-anthem-of-berland-ti-jie/index.html index c82d5614ea..c317af84bb 100644 --- a/2022/07/23/cf808g-anthem-of-berland-ti-jie/index.html +++ b/2022/07/23/cf808g-anthem-of-berland-ti-jie/index.html @@ -476,30 +476,59 @@

                    CF808G Anthem of Berland 题解<
                    -

                    CF808G Anthem of Berland 题解

                    题目链接:CF808G Anthem of Berland

                    +

                    CF808G Anthem of Berland +题解

                    +

                    题目链接:CF808G +Anthem of Berland

                    题意

                    -

                    给定 $s$ 串和 $t$ 串,其中 $s$ 串包含小写字母和问号,$t$ 串只包含小写字母。

                    -

                    假设共有 $k$ 个问号。

                    -

                    你需要给把每个问号变成一个小写字母,共有 $26^k$ 种可能。

                    -

                    对于每种可能,设 $t$ 匹配 $s$ 的次数为 $f_i$,请输出 $\max(f_i)$ 。

                    -

                    $1\leq |s|,|t|\leq 10^5,|s|\times |t|\leq 10^7$

                    +

                    给定 \(s\) 串和 \(t\) 串,其中 \(s\) 串包含小写字母和问号,\(t\) 串只包含小写字母。

                    +

                    假设共有 \(k\) 个问号。

                    +

                    你需要给把每个问号变成一个小写字母,共有 \(26^k\) 种可能。

                    +

                    对于每种可能,设 \(t\) 匹配 \(s\) 的次数为 \(f_i\),请输出 \(\max(f_i)\)

                    +

                    \(1\leq |s|,|t|\leq 10^5,|s|\times |t|\leq +10^7\)

                    样例三告诉你了,这题不是贪心。

                    -

                    设 $f_i$ 表示前 $i$ 个字符的最大答案

                    -

                    似乎不好转移,因为我们不知道上一个放的 $t$ 在哪里

                    -

                    显然 $f_i$ 可以从 $f_{i-m}$ 转移过来,因为这里正好可以放得下一个 $t$

                    -

                    但是 $t$ 是可以重叠放的,例如 $t=\tt{aabaa}$

                    -

                    于是我们直接跳 $m$ 的 $\tt{fail}$ 数组,然后一个个转移吗?

                    -

                    不,因为我们不知道 $f_{i-(m-j)}$ 是否放了 $t$

                    +

                    \(f_i\) 表示前 \(i\) 个字符的最大答案

                    +

                    似乎不好转移,因为我们不知道上一个放的 \(t\) 在哪里

                    +

                    显然 \(f_i\) 可以从 \(f_{i-m}\) +转移过来,因为这里正好可以放得下一个 \(t\)

                    +

                    但是 \(t\) 是可以重叠放的,例如 +\(t=\tt{aabaa}\)

                    +

                    于是我们直接跳 \(m\)\(\tt{fail}\) 数组,然后一个个转移吗?

                    +

                    不,因为我们不知道 \(f_{i-(m-j)}\) +是否放了 \(t\)

                    -

                    注:放 $t$ 的方法是把新的 $t$ 的前缀和上一个 $t$ 的后缀重合着放

                    -

                    不难发现这里要用到 $\tt{kmp}$ ,正确性显然。

                    +

                    注:放 \(t\) 的方法是把新的 \(t\) 的前缀和上一个 \(t\) 的后缀重合着放

                    +

                    不难发现这里要用到 \(\tt{kmp}\) +,正确性显然。

                    -

                    因此设 $g_i$ 表示最后一个放的 $t$ 以 $i$ 结尾,前 $i$ 个字符的最大答案

                    -

                    因为 $f_i$ 表示可以放或者不放 $t$ ,所以转移方程是 $f_i=\max\{f_{i-1},g_i\}$

                    -

                    时间复杂度 $O(|s| \times |t|)$

                    +

                    因此设 \(g_i\) 表示最后一个放的 +\(t\)\(i\) 结尾,前 \(i\) 个字符的最大答案 \[ +g_i=\max\{g_{i-(m-j)}+1,g_i\} +\] 因为 \(f_i\) +表示可以放或者不放 \(t\) +,所以转移方程是 \(f_i=\max\{f_{i-1},g_i\}\)

                    +

                    时间复杂度 \(O(|s| \times |t|)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -551,6 +580,7 @@ 

                    << f[n] << '\n'; return 0; }

                    +

                    @@ -919,7 +949,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/23/cf888e-maximum-subsequence-ti-jie/index.html b/2022/07/23/cf888e-maximum-subsequence-ti-jie/index.html index abe76e8aa3..bc5138706d 100644 --- a/2022/07/23/cf888e-maximum-subsequence-ti-jie/index.html +++ b/2022/07/23/cf888e-maximum-subsequence-ti-jie/index.html @@ -468,7 +468,10 @@

                    CF888E Maximum Subsequence 题
                    -

                    CF888E Maximum Subsequence 题解

                    题目链接:CF888E Maximum Subsequence

                    +

                    CF888E Maximum Subsequence +题解

                    +

                    题目链接:CF888E +Maximum Subsequence

                    题意

                    给定N个数,第i个数的值为Ai,你现在可以从中选择一些数字,问选出数字的和模P最大为多少。
                    @@ -478,17 +481,25 @@ 

                    模拟赛合并复杂度寄了,特写此篇题解

                    -

                    这个 $n\le 35$ 就很明显是折半搜索

                    -

                    我们用 $p,q$ 两个数组记录两部分搜索出来的所有可能的数

                    -

                    $2^{17}=131072$ ,因此数组完全够

                    +

                    这个 \(n\le 35\) +就很明显是折半搜索

                    +

                    我们用 \(p,q\) +两个数组记录两部分搜索出来的所有可能的数

                    +

                    \(2^{17}=131072\) +,因此数组完全够

                    关键在于如何合并两部分的答案

                    -

                    直接两层循环枚举那么复杂度依旧是 $O(2^n)$ (我就是这么寄的

                    -

                    注意到 $p,q$ 满足 $0\le p_i,q_j<m$

                    -

                    因此对于每个 $p_i$ 我们只要找到最大的一个 $q_j$ 满足

                    -

                    正确性显然。

                    +

                    直接两层循环枚举那么复杂度依旧是 \(O(2^n)\)我就是这么寄的

                    +

                    注意到 \(p,q\) 满足 \(0\le p_i,q_j<m\)

                    +

                    因此对于每个 \(p_i\) +我们只要找到最大的一个 \(q_j\) 满足 +\[ +p_i+q_j<m +\] 正确性显然。

                    这样只要排个序然后维护双指针就好了

                    -

                    时间复杂度 $O(2^{n/2}\log 2^{n/2})$

                    +

                    时间复杂度 \(O(2^{n/2}\log +2^{n/2})\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -902,7 +913,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/23/luo-gu-p3538-poi2012-okr-a-horrible-poem-ti-jie/index.html b/2022/07/23/luo-gu-p3538-poi2012-okr-a-horrible-poem-ti-jie/index.html index 09dd93bfef..6f19658a93 100644 --- a/2022/07/23/luo-gu-p3538-poi2012-okr-a-horrible-poem-ti-jie/index.html +++ b/2022/07/23/luo-gu-p3538-poi2012-okr-a-horrible-poem-ti-jie/index.html @@ -476,7 +476,10 @@

                    洛谷P3538 [POI2012]OKR-A Horri
                    -

                    洛谷P3538 [POI2012]OKR-A Horrible Poem 题解

                    题目链接:P3538 [POI2012]OKR-A Horrible Poem

                    +

                    洛谷P3538 +[POI2012]OKR-A Horrible Poem 题解

                    +

                    题目链接:P3538 +[POI2012]OKR-A Horrible Poem

                    题意

                    给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节。
                    @@ -493,20 +496,30 @@ 

                    \(O(\log n)\) +了

                    +

                    具体地,用欧拉筛预处理出 \(1\sim n\) +中所有数的最小因数 \(g_i\)

                    +

                    然后直接跳 \(g\) 数组即可

                    那么确定是否是原串的循环节呢?

                    -

                    设当前枚举的串为 $A$ (不是在说字符 $\tt{A}$ 哦)

                    -

                    如果它是循环节,那么原串一定长这样

                    -

                    方便起见,我们考虑这样的情况

                    -

                    仔细想一想,会发现其实没必要一个个比较

                    -

                    我们只要比较 $\color{red}{AAAA}\color{black}{A}$ 和 $\color{black}{A}\color{red}{AAAA}$ 就可以了(只比较红色部分)

                    -

                    字符串比较?考虑 $\tt{Hash}$ 。

                    -

                    更多关于字符串哈希,见 CF1200E Compress Words 题解OI模板-字符串

                    -

                    时间复杂度 $O(m \log n)$

                    +

                    设当前枚举的串为 \(A\) +(不是在说字符 \(\tt{A}\) 哦)

                    +

                    如果它是循环节,那么原串一定长这样 \[ +A\dots AA +\] 方便起见,我们考虑这样的情况 \[ +AAAAA +\] 仔细想一想,会发现其实没必要一个个比较

                    +

                    我们只要比较 \(\color{red}{AAAA}\color{black}{A}\) 和 +\(\color{black}{A}\color{red}{AAAA}\) +就可以了(只比较红色部分)

                    +

                    字符串比较?考虑 \(\tt{Hash}\) +。

                    +

                    更多关于字符串哈希,见 CF1200E +Compress Words 题解OI模板-字符串

                    +

                    时间复杂度 \(O(m \log n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -941,7 +954,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/23/mi-ma-sheng-cheng-qi/index.html b/2022/07/23/mi-ma-sheng-cheng-qi/index.html index a6b28d1247..280a0b016d 100644 --- a/2022/07/23/mi-ma-sheng-cheng-qi/index.html +++ b/2022/07/23/mi-ma-sheng-cheng-qi/index.html @@ -468,7 +468,8 @@

                    密码生成器

                    -

                    密码生成器

                    简单的小破密码生成器 qwq

                    +

                    密码生成器

                    +

                    简单的小破密码生成器 qwq

                    也不知道搞了这个有啥用处

                    目前还比较脆弱,不支持不合法输入

                    反正就是个瞎搞的东西 qwq

                    @@ -955,7 +956,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/23/san-zi-qi-mo-ni-qi-for-linux/index.html b/2022/07/23/san-zi-qi-mo-ni-qi-for-linux/index.html index 6a9fdc9645..32885e2467 100644 --- a/2022/07/23/san-zi-qi-mo-ni-qi-for-linux/index.html +++ b/2022/07/23/san-zi-qi-mo-ni-qi-for-linux/index.html @@ -468,10 +468,13 @@

                    三子棋模拟器 for linux

                    -

                    三子棋模拟器 for linux

                    中考前一周开摆写的

                    -

                    采用的是minimax算法和 $\alpha - \beta$ 剪枝

                    +

                    三子棋模拟器 for linux

                    +

                    中考前一周开摆写的

                    +

                    采用的是minimax算法和 \(\alpha - +\beta\) 剪枝

                    目前是基于规则的估价函数(因为q779不会AI)

                    -

                    目前在含C++环境的 ubuntu20.04 和 macOS Monterey 12.4下都是可以编译的

                    +

                    目前在含C++环境的 ubuntu20.04 和 macOS Monterey +12.4下都是可以编译的

                    代码:

                    //######
                     //
                    @@ -1084,7 +1087,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/24/cf898f-restoring-the-expression-ti-jie/index.html b/2022/07/24/cf898f-restoring-the-expression-ti-jie/index.html index dd0531feca..165d2db335 100644 --- a/2022/07/24/cf898f-restoring-the-expression-ti-jie/index.html +++ b/2022/07/24/cf898f-restoring-the-expression-ti-jie/index.html @@ -476,7 +476,10 @@

                    CF898F Restoring the Expression
                    -

                    CF898F Restoring the Expression 题解

                    题目链接:CF898F Restoring the Expression

                    +

                    CF898F Restoring the +Expression 题解

                    +

                    题目链接:CF898F +Restoring the Expression

                    题意

                    给你一个数字字符串s,现在你要这个字符串切割成三个部分a,b,c,
                    @@ -497,18 +500,21 @@ 

                    -f(s[1\dots l])=\sum_{i=1}^{l} s[i]\times b^{l-i} \mod M

                    有没有觉得这个形式很熟悉?

                    -

                    如果 $x=10$ ?我相信你一定明白了

                    +

                    一般的字符串哈希函数都是这么定义的 \[ +f(s[1\dots l])=\sum_{i=1}^{l} s[i]\times b^{l-i} \mod M +\] 有没有觉得这个形式很熟悉? \[ +ax^3+bx^2+cx+d +\] 如果 \(x=10\) +?我相信你一定明白了

                    这个字符串哈希的过程,

                    -

                    其实就是把这一段字符表示成了模 $M$ 意义下的 $x$ 进制数

                    +

                    其实就是把这一段字符表示成了模 \(M\) +意义下的 \(x\) 进制数

                    因此,哈希,就没了。边界比较麻烦,要稍微处理一下

                    我们模拟赛没卡单哈,据说CF卡了那些经典素数,比如9982443531e9+7

                    所以我就照着题解写了个双哈,模数是9982448531e9+33

                    -

                    至于为什么要卡,因为 $10$ 不是质数,冲突的概率会变大很多

                    -

                    时间复杂度 $O(n)$

                    +

                    至于为什么要卡,因为 \(10\) +不是质数,冲突的概率会变大很多

                    +

                    时间复杂度 \(O(n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -613,13 +619,17 @@ 

                    << '\n'; return 0; }

                    -

                    @zx2017:现在做没有意义的事就是为了以后能做有意义的事。

                    -

                    @zx2017:要提高你们的游戏品味。

                    -

                    @zx2017:你们有人打星际吗?

                    +

                    @zx2017:现在做没有意义的事就是为了以后能做有意义的事。

                    +

                    @zx2017:要提高你们的游戏品味。

                    +

                    @zx2017:你们有人打星际吗?

                    有一说一,挺向往这样的快乐机房生活的。

                    可惜没有这样的条件,终究还是孤身一人吧。

                    参考文献

                    -

                    [1] https://www.luogu.com.cn/blog/xixike/solution-cf898f

                    +

                    [1] https://www.luogu.com.cn/blog/xixike/solution-cf898f

                    @@ -993,7 +1003,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/24/luo-gu-p2704-noi2001-pao-bing-zhen-di-ti-jie/index.html b/2022/07/24/luo-gu-p2704-noi2001-pao-bing-zhen-di-ti-jie/index.html index 0fee02e5d5..7dd794496a 100644 --- a/2022/07/24/luo-gu-p2704-noi2001-pao-bing-zhen-di-ti-jie/index.html +++ b/2022/07/24/luo-gu-p2704-noi2001-pao-bing-zhen-di-ti-jie/index.html @@ -472,24 +472,36 @@

                    洛谷P2704 [NOI2001] 炮兵阵
                    -

                    洛谷P2704 [NOI2001] 炮兵阵地 题解

                    题目链接:P2704 [NOI2001] 炮兵阵地

                    +

                    洛谷P2704 [NOI2001] 炮兵阵地 +题解

                    +

                    题目链接:P2704 +[NOI2001] 炮兵阵地

                    题意

                    -

                    司令部的将军们打算在 $N\times M$ 的网格地图上部署他们的炮兵部队。

                    -

                    一个 $N\times M$ 的地图由 $N$ 行 $M$ 列组成,地图的每一格可能是山地(用 $\texttt{H}$ 表示),也可能是平原(用 $\texttt{P}$ 表示),如下图。

                    +

                    司令部的将军们打算在 \(N\times M\) +的网格地图上部署他们的炮兵部队。

                    +

                    一个 \(N\times M\) 的地图由 \(N\)\(M\) 列组成,地图的每一格可能是山地(用 +\(\texttt{H}\) 表示),也可能是平原(用 +\(\texttt{P}\) 表示),如下图。

                    在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

                    -

                    +

                    如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

                    图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。

                    现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

                    -

                    对于 $100\%$ 的数据,$N\le 100$,$M\le 10$,保证字符仅包含 ph

                    +

                    对于 \(100\%\) 的数据,\(N\le 100\)\(M\le 10\),保证字符仅包含 p 与 +h

                    状压神仙题

                    考虑压缩一行上每列的状态,然后再一行行的搞

                    具体地,我们记录每一行所有列是否可以放炮兵

                    然后预处理所有合法的放炮兵的方法(不考虑地图的状况)

                    枚举转移即可,注意要滚动数组,还要单独处理前两行

                    -

                    时间复杂度 $O(n2^{3m}) = O(n8^m)$

                    +

                    时间复杂度 \(O(n2^{3m}) = +O(n8^m)\)

                    代码:(f[i][j][k]表示前两行的状态是ijk是滚动数组)

                    #include <iostream>
                     #include <string>
                    @@ -550,6 +562,7 @@ 

                    << res << '\n'; return 0; }

                    +

                    @@ -914,7 +927,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/24/oi-yi-cuo-dian-c-yu-fa/index.html b/2022/07/24/oi-yi-cuo-dian-c-yu-fa/index.html index 4257198733..9a331abd85 100644 --- a/2022/07/24/oi-yi-cuo-dian-c-yu-fa/index.html +++ b/2022/07/24/oi-yi-cuo-dian-c-yu-fa/index.html @@ -468,18 +468,27 @@

                    OI易错点-C++语法

                    -

                    OI易错点-C++语法

                    不保证本文内容完全正确,仅仅是个人总结!!

                    -

                    islowerisalphaisdigit 这种函数,返回值不是bool!!

                    +

                    OI易错点-C++语法

                    +

                    不保证本文内容完全正确,仅仅是个人总结!!

                    +

                    islowerisalphaisdigit +这种函数,返回值不是bool!!

                    在不同的机器上跑出来是不一样的!!!!不要直接加上它!!!

                    -

                    lower_bound(begin,end,x,greater<int>()) 返回第一个小于等于 $x$ 的位置

                    +

                    lower_bound(begin,end,x,greater<int>()) +返回第一个小于等于 \(x\) 的位置

                    去重 m=unique(b+1,b+1+n)-b-1;

                    -

                    string==find() 是 $O(n)$ 的!

                    -

                    注意 char 数组后面有个 \0因此多测一般不用清空但是\0后面的不会被清空

                    -

                    next_permutation(a+1,a+1+n)下一个排列!到了5 4 3 2 1这种就结束了!!

                    +

                    string==find() 是 +\(O(n)\) 的!

                    +

                    注意 char 数组后面有个 \0 +,因此多测一般不用清空但是\0后面的不会被清空

                    +

                    next_permutation(a+1,a+1+n) +是下一个排列!到了5 4 3 2 1这种就结束了!!

                    同理有 prev_permutation(a+1,a+1+n)

                    -

                    凡是位运算都加个括号,比如 $\&$ 的优先级比 == 还要低!!!(待考证)

                    +

                    凡是位运算都加个括号,比如 \(\&\) 的优先级比 == +还要低!!!(待考证)

                    判断两数的奇偶性是否相同不可以用异或因为高位还有数

                    -

                    __gnu_pbds的头文件可以用 #include <bits/extc++.h>

                    +

                    __gnu_pbds的头文件可以用 +#include <bits/extc++.h>

                    memcpy(a,b,sizeof(b)) 把 b 复制到 a

                    自定义优先队列

                    struct cmp
                    @@ -488,76 +497,95 @@ 

                    {return h[a]<h[b];} }; priority_queue<int,vector<int>,cmp> q; // 每次返回h最大的那个

                    -

                    str.c_str() 可以把string转化为char[] ,然后printf("%s\n",res.c_str());

                    +

                    str.c_str() 可以把string转化为char[] +,然后printf("%s\n",res.c_str());

                    string str

                    -
                      +
                      1. .find(‘a’)返回第一次出现a的位置
                      2. .find(str_1)就是KMP
                      3. .find(str_1,pos) 找pos起str_1的位置
                      4. .substr(begin,length) begin开始length个字符
                      -

                      string::find(str,pos),没找到返回的是 18446744073709551615 ,即string::npos

                      -

                      char 数则较用 strcmp(<a>,<b>)不要直接用 ==会出问题

                      +

                      string::find(str,pos),没找到返回的是 +18446744073709551615 ,即string::npos

                      +

                      char 数则较用 strcmp(<a>,<b>) +,不要直接用 == +,会出问题

                      reverse 函数 [first,last).

                      fixed表示定点数

                        -
                      • 如果不用fixed直接用setprecision(x)就是最多有效位数为 $x$ ,而且是科学计数法+四舍五入
                      • -
                      • 如果用了fixed 那么setprecision(x) 就是小数点后最多位数 $x$ ,非科学计数法+四舍五入
                      • +
                      • 如果不用fixed直接用setprecision(x)就是最多有效位数为 +\(x\) ,而且是科学计数法+四舍五入
                      • +
                      • 如果用了fixed 那么setprecision(x) +就是小数点后最多位数 \(x\) +,非科学计数法+四舍五入

                      atan2(y,x) 返回的值是方位角(极角)的值

                      -

                      atan(y,x) 返回的值是反正切的值

                      -
                      +\end{aligned} +\]

                      +

                    其实就是说,atan2(y,x)

                    -

                    在一、二象限的时候返回的是 $[0,\pi]$ 的值

                    -

                    在三、四象限的时候,返回的是 $(-\pi,0]$ 的值

                    -

                    atan(y,x) 的返回值只有 $\left[-\dfrac{\pi}{2},\dfrac{\pi}{2}\right]$

                    +

                    在一、二象限的时候返回的是 \([0,\pi]\) 的值

                    +

                    在三、四象限的时候,返回的是 \((-\pi,0]\) 的值

                    +

                    atan(y,x) 的返回值只有 \(\left[-\dfrac{\pi}{2},\dfrac{\pi}{2}\right]\)

                    scanf(“%lf%lf”,&x,&y) 读double

                    -0x3f3f3f3f3f3f3f3f-1 = (long long)0xc0c0c0c0c0c0c0c0
                     注意后面一个数本质是 unsigned long long 的,要转成 long long !!!!
                    -

                    合并两端有序的序列(归并的中间步骤)inplace_merge(l,mid,r)

                    -

                    这里的 mid归并时右边一段的开头元素的位置!!

                    -

                    还有 r最右边的元素的右边一个位置!!

                    -

                    如果答案是 -1e-10 这种答案,要注意直接赋值为 0 ,不然会输出 -0.0000

                    -

                    POJ上提交不能用万能头,而且要用 printf + %f 来输出 double

                    +

                    合并两端有序的序列(归并的中间步骤)inplace_merge(l,mid,r)

                    +

                    这里的 mid +是归并时右边一段的开头元素的位置!!

                    +

                    还有 r 是 +最右边的元素的右边一个位置!!

                    +

                    如果答案是 -1e-10 这种答案,要注意直接赋值为 +0 ,不然会输出 -0.0000

                    +

                    POJ上提交不能用万能头,而且要用 printf + %f +来输出 double

                    shuffle(p+1,p+1+n,rd); 不用 rd()

                    -

                    #define sum(x) ((x)*(x+1)/2) 注意宏定义一定要所有的 $x$ 都套个括号

                    +

                    #define sum(x) ((x)*(x+1)/2) 注意宏定义一定要所有的 +\(x\) 都套个括号

                    %llu 输出unsigned long long

                    getline(cin,str[pos]);

                    -

                    set::count 同 map,都是 $O(\log n)$

                    -

                    scanf("%s",a);n=strlen(a+1)

                    -

                    注意左移运算符

                    +

                    set::count 同 map,都是 \(O(\log n)\)

                    +

                    scanf("%s",a);n=strlen(a+1)

                    +

                    注意左移运算符

                    如果写1<<31ll(long long)(1<<31)会爆int

                    要写成1ll<<31

                    -

                    判断第 $i$ 位是否为$1$

                    +

                    判断第 \(i\) 位是否为\(1\)

                    ((m&(1ll<<(i-1)))!=0) // 一定要写 !=0 而且要套括号!
                     ((m>>(i-1))&1) // 这个安全一点

                    终极快读不要和 scanf(“%s”,a+1) 这种混用!

                    -

                    assert(x),当 $x$ 为 $\text{true}$ 时什么都不做

                    +

                    assert(x),当 \(x\) 为 +\(\text{true}\) 时什么都不做

                    double占8字节,long double占16字节,

                    cmp最好不要写在结构体里

                    因为成员函数指针和普通函数指针不同,前者实际上是这样子的。

                    bool cmp(node*this, int a, int b) 

                    开三次根号的方法(不要用pow

                    -
                      -
                    1. cbrt() 答案为double,可以放long long进去

                      -
                    2. -
                    3. 牛顿迭代法

                      -
                    4. -
                    5. 二分。

                      -
                    6. +
                        +
                      1. cbrt() +答案为double,可以放long long进去

                      2. +
                      3. 牛顿迭代法 \[ +x_{i+1}=x_i-\dfrac{f(x_i)}{f^{\prime}(x_i)} +\]

                      4. +
                      5. 二分。

                      笛卡尔树是一种二叉树。

                      stoi()stoll()

                      @@ -926,7 +954,7 @@

                        站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/25/cf1304e-1-trees-and-queries-ti-jie/index.html b/2022/07/25/cf1304e-1-trees-and-queries-ti-jie/index.html index bd633f70e9..284d97eecc 100644 --- a/2022/07/25/cf1304e-1-trees-and-queries-ti-jie/index.html +++ b/2022/07/25/cf1304e-1-trees-and-queries-ti-jie/index.html @@ -472,7 +472,10 @@

                      CF1304E 1-Trees and Queries 题
                      -

                      CF1304E 1-Trees and Queries 题解

                      题目链接:CF1304E 1-Trees and Queries

                      +

                      CF1304E 1-Trees and Queries +题解

                      +

                      题目链接:CF1304E +1-Trees and Queries

                      题意

                      题面是从模拟赛搬过来的,因此掺杂了某些奇怪的成分(?

                      @@ -500,13 +503,14 @@

                    -

                    可以关注下钻虚哥,是为很鬼畜的巨佬(?

                    +

                    可以关注下钻虚哥,是为很鬼畜的巨佬(?

              不难发现,如果不加边,那能不能走到,与路径的奇偶性有关

              如果加了边,就有可能增加一条改变奇偶性的路径

              然后就很简单了,只要求个lca搞个树上路径就好了

              然后我模拟赛就翻车了,寄。

              -

              时间复杂度 $O(Q\log n)$

              +

              时间复杂度 \(O(Q\log n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -982,7 +986,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/25/cf1468j-road-reform-ti-jie/index.html b/2022/07/25/cf1468j-road-reform-ti-jie/index.html index 2d5452667a..5a08136cee 100644 --- a/2022/07/25/cf1468j-road-reform-ti-jie/index.html +++ b/2022/07/25/cf1468j-road-reform-ti-jie/index.html @@ -476,36 +476,55 @@

              CF1468J Road Reform 题解

              -

              CF1468J Road Reform 题解

              题目链接:CF1468J Road Reform

              +

              CF1468J Road Reform 题解

              +

              题目链接:CF1468J +Road Reform

              题意

              -

              给定一个有 $n$ 个节点,$m$ 条无向带权边的图,和一个参数 $k$,第 $i$ 条边权值为 $s_i$。

              -

              现在你要保留这个图中的 $n-1$ 条边使得这个图变成一棵树,然后你可以对这棵树上的任意边进行修改,每次修改可以使这个边的权值加上一或减去一。

              -

              现在你需要使所有边权的最大值正好等于 $k$,求所有保留方案的最小操作数。

              -

              $T$ 组询问。

              +

              给定一个有 \(n\) 个节点,\(m\) 条无向带权边的图,和一个参数 \(k\),第 \(i\) 条边权值为 \(s_i\)

              +

              现在你要保留这个图中的 \(n-1\) +条边使得这个图变成一棵树,然后你可以对这棵树上的任意边进行修改,每次修改可以使这个边的权值加上一或减去一。

              +

              现在你需要使所有边权的最大值正好等于 \(k\),求所有保留方案的最小操作数。

              +

              \(T\) 组询问。

              保证初始时给定的图满足任意两个点互相可达,没有重边或自环。

              -

              $1\leq T\leq 10^3$

              -

              $1\leq n\leq2\times10^5,n-1\leq m\leq \min(\frac{n(n+1)}{2},2\times10^5)$

              -

              $\sum n,\sum m\leq2\times10^5,~1\leq k,s_i\leq 10^9$

              +

              \(1\leq T\leq 10^3\)

              +

              \(1\leq n\leq2\times10^5,n-1\leq m\leq +\min(\frac{n(n+1)}{2},2\times10^5)\)

              +

              \(\sum n,\sum m\leq2\times10^5,~1\leq +k,s_i\leq 10^9\)

              模拟赛A题爆零,于是有了这篇题解

              -

              感谢 @Roundgod 老师的耐心解释 Orz

              +

              感谢 @Roundgod老师的耐心解释 Orz

              本题要根据最小生成树的情况分类讨论

                -
              • 如果最小生成树的最大边超过 $k$

                -

                则此时的最优方案即,将最小生成树上所有大于 $k$ 的边都减成 $k$

                -
              • -
              • 如果最小生成树的最大边不超过 $k$

                +
              • 如果最小生成树的最大边超过 \(k\)

                +

                则此时的最优方案即,将最小生成树上所有大于 \(k\) 的边都减成 \(k\)

              • +
              • 如果最小生成树的最大边不超过 \(k\)

                此时有两种选择

                  -
                • 第一种,把最小生成树中的最大边抬到 $k$
                • -
                • 第二种,把不在最小生成树中的一条比 $k$ 大的边(显然要尽可能小些)加入最小生成树,然后去掉任意一条环上的边
                • +
                • 第一种,把最小生成树中的最大边抬到 \(k\)
                • +
                • 第二种,把不在最小生成树中的一条比 \(k\) +大的边(显然要尽可能小些)加入最小生成树,然后去掉任意一条环上的边
                -

                实现的时候,用mnimxi记录这两个值,最后去个 $\min$ 就好了

                -

                注意,这是在最小生成树的最大边不超过 $k$ 的前提下!

                -
              • +

                实现的时候,用mnimxi记录这两个值,最后去个 +\(\min\) 就好了

                +

                注意,这是在最小生成树的最大边不超过 \(k\) 的前提下!

              -

              时间复杂度 $O(m \log m)$

              +

              时间复杂度 \(O(m \log m)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -973,7 +992,7 @@ 

                站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/25/cf645d-robot-rapping-results-report-ti-jie/index.html b/2022/07/25/cf645d-robot-rapping-results-report-ti-jie/index.html index bb56997004..7ecc24ae25 100644 --- a/2022/07/25/cf645d-robot-rapping-results-report-ti-jie/index.html +++ b/2022/07/25/cf645d-robot-rapping-results-report-ti-jie/index.html @@ -472,21 +472,41 @@

              CF645D Robot Rapping Results Rep
              -

              CF645D Robot Rapping Results Report 题解

              题目链接:CF645D Robot Rapping Results Report

              +

              CF645D Robot Rapping +Results Report 题解

              +

              题目链接:CF645D +Robot Rapping Results Report

              题意

              -

              $n$ 个机器人,每个机器人有一个不同的级别,级别介于 $1\sim n$,高级别的可以打败低级别的,现在给出 $n$ 个机器人的 $m$ 场比赛胜负情况,问最少需要前几场比赛就可以确定每个机器人的级别。

              +

              \(n\) +个机器人,每个机器人有一个不同的级别,级别介于 \(1\sim +n\),高级别的可以打败低级别的,现在给出 \(n\) 个机器人的 \(m\) +场比赛胜负情况,问最少需要前几场比赛就可以确定每个机器人的级别。

              输入格式

              -

              第一行两个整数 $n$ 和 $m$ 表示机器人个数和比赛场数,之后 $m$ 行每行两个整数 $u$ 和 $v$ 表示机器人 $u$ 打败机器人 $v$。

              +

              第一行两个整数 \(n\)\(m\) 表示机器人个数和比赛场数,之后 \(m\) 行每行两个整数 \(u\)\(v\) 表示机器人 \(u\) 打败机器人 \(v\)

              输出格式

              -

              如果这 $m$ 场比赛可以确定每个机器人的级别则输出最少需要前几场比赛就可以确定,否则输出 $-1$。

              +

              如果这 \(m\) +场比赛可以确定每个机器人的级别则输出最少需要前几场比赛就可以确定,否则输出 +\(-1\)

              数据范围

              -

              $2\leq n\leq 10^5$,$1\leq m\leq \min(\frac{n\times (n-1)}{2},10^5)$。

              +

              \(2\leq n\leq 10^5\)\(1\leq m\leq \min(\frac{n\times +(n-1)}{2},10^5)\)

              不难发现这里有个单调性

              即越多的边越有可能确定每个机器人的级别

              当然如果所有边都不能确定的话,就输出无解即可

              -

              所以解法一:二分可能的边数,然后topo,时间复杂度 $O(m \log m)$

              +

              所以解法一:二分可能的边数,然后topo,时间复杂度 +\(O(m \log m)\)

              假如在某一时间队列中的元素多于一个,则这个图的拓扑序不只一种即为错误

              代码没写,基本上就是topo板子加一个if(q.size()>1) return 0;

              解法二:考虑直接topo,然后记录每个结点最少需要几条边才能确定

              @@ -494,7 +514,7 @@

              \(O(n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -561,7 +581,8 @@ 

              return 0; }

              参考文献

              -

              [1] https://www.luogu.com.cn/blog/EnochWenzhou/solution-cf645d

              +

              [1] https://www.luogu.com.cn/blog/EnochWenzhou/solution-cf645d

              @@ -923,7 +944,7 @@

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/26/luo-gu-p8060-poi2003-sums-ti-jie/index.html b/2022/07/26/luo-gu-p8060-poi2003-sums-ti-jie/index.html index 4449e8f50f..69317721df 100644 --- a/2022/07/26/luo-gu-p8060-poi2003-sums-ti-jie/index.html +++ b/2022/07/26/luo-gu-p8060-poi2003-sums-ti-jie/index.html @@ -480,25 +480,64 @@

              洛谷P8060 [POI2003] Sums 题
              -

              洛谷P8060 [POI2003] Sums 题解

              题目链接:P8060 [POI2003] Sums

              +

              洛谷P8060 [POI2003] Sums 题解

              +

              题目链接:P8060 +[POI2003] Sums

              题目描述

              -

              我们给定一个整数集合 $A$。考虑一个非负整数集合 $A’$,所有属于 $A’$ 的集合的数 $x$ 满足当且仅当能被表示成一些属于 $A$ 的元素的和(数字可重复)。

              -

              比如,当 $A = \{2,5,7\}$,属于 $A’$ 的数为:$0$($0$ 个元素的和),$2$,$4$($2 + 2$)和 $12$($5 + 7$ or $7 + 5$ or $2 + 2 + 2 + 2 + 2 + 2$);但是元素 $1$ 和 $3$ 不属于 $A’$。

              +

              我们给定一个整数集合 \(A\)。考虑一个非负整数集合 \(A'\),所有属于 \(A'\) 的集合的数 \(x\) 满足当且仅当能被表示成一些属于 \(A\) 的元素的和(数字可重复)。

              +

              比如,当 \(A = \{2,5,7\}\),属于 +\(A'\) 的数为:\(0\)\(0\) +个元素的和),\(2\)\(4\)\(2 + +2\))和 \(12\)\(5 + 7\) or \(7 + +5\) or \(2 + 2 + 2 + 2 + 2 + +2\));但是元素 \(1\)\(3\) 不属于 \(A'\)

              输入格式

              -

              第一行有一个整数 $n$,代表集合 $A$ 的元素个数。接下来每行一个数 $a_i$ 描述一个元素。$A = \{a_1,a_2,…,a_n\}$。

              -

              接下来一个整数 $k$,然后每行一个整数,分别代表 $b_1,b_2,…,b_k$。

              +

              第一行有一个整数 \(n\),代表集合 +\(A\) 的元素个数。接下来每行一个数 +\(a_i\) 描述一个元素。\(A = \{a_1,a_2,...,a_n\}\)

              +

              接下来一个整数 \(k\),然后每行一个整数,分别代表 \(b_1,b_2,...,b_k\)

              输出格式

              -

              输出 $k$ 行。如果 $b_i$ 属于 $A’$,第 $i$ 行打印 TAK,否则打印 NIE

              +

              输出 \(k\) 行。如果 \(b_i\) 属于 \(A'\),第 \(i\) 行打印 TAK,否则打印 +NIE

              数据范围

              -

              对于所有数据,$1 \le n \le 5 \times 10^3$,$1 \le k \le 10^4$,$1 \le a_1 < a_2 < … < a_n \le 5 \times 10^4$,$0 \le b_i \le 10^9$。

              +

              对于所有数据,\(1 \le n \le 5 \times +10^3\)\(1 \le k \le +10^4\)\(1 \le a_1 < a_2 < ... +< a_n \le 5 \times 10^4\)\(0 \le +b_i \le 10^9\)

              -

              考虑随便取一个模数 $a_i$ ,建议从小到大排序后选 $a_1$

              -

              然后求出 凑出每个 $x \in [0,a_1)$ 的所需的最小代价

              -

              或者说,用最少的 $a_i$ 凑出一个模 $a_1$ 意义下的 $x$ ,记这个数为 $d_x$

              -

              而同余最短路干的事情就是:求模 $M$ 余 $x$ 的最小基数 $d_x$

              -

              这样对于每个询问,我们只要看看 $d_{k\,\bmod\, a_1}$ 是不是小于 $k$ 就可以了

              -

              时间复杂度 $O(n a_i \log a_i)$

              +

              考虑随便取一个模数 \(a_i\) +,建议从小到大排序后选 \(a_1\)

              +

              然后求出 凑出每个 \(x \in [0,a_1)\) +的所需的最小代价

              +

              或者说,用最少的 \(a_i\) 凑出一个模 +\(a_1\) 意义下的 \(x\) ,记这个数为 \(d_x\)

              +

              而同余最短路干的事情就是:求模 \(M\) +余 \(x\) 的最小基数 \(d_x\)

              +

              这样对于每个询问,我们只要看看 \(d_{k\,\bmod\, a_1}\) 是不是小于 \(k\) 就可以了

              +

              时间复杂度 \(O(n a_i \log a_i)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -927,7 +966,7 @@ 

               站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/26/mo-ni-sai-ti-jiang-jie-1/index.html b/2022/07/26/mo-ni-sai-ti-jiang-jie-1/index.html index 6b1c09b56b..9af826f02e 100644 --- a/2022/07/26/mo-ni-sai-ti-jiang-jie-1/index.html +++ b/2022/07/26/mo-ni-sai-ti-jiang-jie-1/index.html @@ -472,18 +472,49 @@

              模拟赛题讲解[1]

              -

              模拟赛题讲解[1]

              来自 Roundgod 2022-07-25 noi.ac #2676

              +

              模拟赛题讲解[1]

              +

              来自 Roundgod +2022-07-25 noi.ac #2676

              题目描述

              -

              给定一个带权连通无向图 $G=(V,E)$ ,其中顶点个数 $|V|=n$ ,边数$|E|=m$ 图中可能包含重边以及自环。顶点编号为 $1−n$ ,边的编号为 $1−m$ ,第 $i$ 条边连接顶点 $a_i$ 和 $b_i$,权值为 $c_i$. 输入保证图中所有边的权值各不相同,即对于所有$1\le i<j \le n$ ,都有 $c_i\ne c_j$ 。

              -

              你现在需要处理 $q$ 个询问,其中第 $k$ 个询问是一个三元组 $(u_i,v_i,w_i)$ ,表示:

              -

              如果在图 $G$ 中添加一条连接 $u_i$ 和 $v_i$ 权值为 $w_i$ 的边得到图 $G^{\prime}$ ,图 $G^{\prime}$ 的最小生成树是否一定包含新添加的边?

              -

              询问保证对于所有 $1 \le j \le m$ ,都有 $w_i\ne c_j$ 。

              +

              给定一个带权连通无向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数\(|E|=m\) +图中可能包含重边以及自环。顶点编号为 +\(1−n\) ,边的编号为 \(1−m\) ,第 \(i\) 条边连接顶点 \(a_i\)\(b_i\),权值为 \(c_i\). +输入保证图中所有边的权值各不相同,即对于所有\(1\le i<j \le n\) ,都有 \(c_i\ne c_j\)

              +

              你现在需要处理 \(q\) 个询问,其中第 +\(k\) 个询问是一个三元组 \((u_i,v_i,w_i)\) ,表示:

              +

              如果在图 \(G\) 中添加一条连接 \(u_i\)\(v_i\) 权值为 \(w_i\) 的边得到图 \(G^{\prime}\) ,图 \(G^{\prime}\) +的最小生成树是否一定包含新添加的边?

              +

              询问保证对于所有 \(1 \le j \le m\) +,都有 \(w_i\ne c_j\)

              输入格式

              -

              输入第一行包含 $3$ 个整数 $n,m,q$ ,分别表示图的顶点数,图的边数,以及询问的个数。

              -

              接下来 $m$ 行,每行包含三个整数 $a_i,b_i,c_i(1\le a_i,b_i\le n)$ ,表示第 $i$ 条边连接的顶点以及权值。

              -

              接下来 $q$ 行,每行 $u_i,v_i,w_i(1\le u_i,v_i \le n)$ , 表示第 $i$ 组询问。

              +

              输入第一行包含 \(3\) 个整数 \(n,m,q\) +,分别表示图的顶点数,图的边数,以及询问的个数。

              +

              接下来 \(m\) 行,每行包含三个整数 +\(a_i,b_i,c_i(1\le a_i,b_i\le n)\) +,表示第 \(i\) +条边连接的顶点以及权值。

              +

              接下来 \(q\) 行,每行 \(u_i,v_i,w_i(1\le u_i,v_i \le n)\) , 表示第 +\(i\) 组询问。

              输出格式

              -

              对于每组询问,根据答案你需要在一行中输出 Yes 或者 No

              +

              对于每组询问,根据答案你需要在一行中输出 Yes 或者 +No

              输入1

              5 6 3
               1 2 2
              @@ -500,21 +531,28 @@ 

                站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/26/mo-ni-sai-ti-jiang-jie-2/index.html b/2022/07/26/mo-ni-sai-ti-jiang-jie-2/index.html index 179edba86f..6699e0c2e1 100644 --- a/2022/07/26/mo-ni-sai-ti-jiang-jie-2/index.html +++ b/2022/07/26/mo-ni-sai-ti-jiang-jie-2/index.html @@ -472,13 +472,29 @@

              模拟赛题讲解[2]

              -

              模拟赛题讲解[2]

              来自 Roundgod 2022-07-25 noi.ac #2677

              +

              模拟赛题讲解[2]

              +

              来自 Roundgod +2022-07-25 noi.ac #2677

              题目描述

              -

              给定一个有向图 $G=(V,E)$ ,其中顶点个数 $|V|=n$ ,边数 $|E|=m$ 。顶点编号为 $1−N$ ,边的编号为 $1−M$ ,第 $i$ 条边连接顶点 $a_i$ 和 $b_i$ 。

              -

              Makima会从图中的某个顶点出发并重复沿着某条有向边行走。你需要计算:存在多少个顶点 $v$ ,使得Makima可以从点 $v$ 出发无限地走下去?

              +

              给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数 \(|E|=m\) 。顶点编号为 \(1−N\) ,边的编号为 \(1−M\) ,第 \(i\) 条边连接顶点 \(a_i\)\(b_i\)

              +

              Makima会从图中的某个顶点出发并重复沿着某条有向边行走。你需要计算:存在多少个顶点 +\(v\) ,使得Makima可以从点 \(v\) 出发无限地走下去?

              输入格式

              -

              输入第一行包含 $2$ 个整数 $n,m$ ,分别表示图的顶点数和图的边数。

              -

              接下来 $m$ 行,每行包含两个整数 $a_i,b_i(1\le a_i,b_i\le n,a_i\ne b_i)$ ,表示第 $i$ 条边从顶点 $a_i$ 指向顶点 $b_i$ 。

              +

              输入第一行包含 \(2\) 个整数 \(n,m\) ,分别表示图的顶点数和图的边数。

              +

              接下来 \(m\) 行,每行包含两个整数 +\(a_i,b_i(1\le a_i,b_i\le n,a_i\ne +b_i)\) ,表示第 \(i\) 条边从顶点 +\(a_i\) 指向顶点 \(b_i\)

              输出格式

              在一行中输出一个整数,表示满足条件的顶点个数。

              输入1

              @@ -497,17 +513,23 @@

              输出2

              2

              数据范围

              -

              对于 $50\%$ 的数据,$1\le n\le 2000,~1\le m\le 10000$

              -

              对于 $100\%$ 的数据,$1\le n\le 2\times 10^5,~1\le m\le 2\times 10^5$

              +

              对于 \(50\%\) 的数据,\(1\le n\le 2000,~1\le m\le 10000\)

              +

              对于 \(100\%\) 的数据,\(1\le n\le 2\times 10^5,~1\le m\le 2\times +10^5\)

              题解

              这题还是比较简单的,赛时秒了

              不难发现,如果一个结点出发可以走到一个环上

              -

              这个点、这个点到环的路径上的所有点以及所有能走到这个点的点 都是满足要求的点

              +

              这个点、这个点到环的路径上的所有点以及所有能走到这个点的点 +都是满足要求的点

              显然问题就变成了:找到哪些结点是死路(走不到环上)

              -

              考虑建反图然后跑 $\text{topo}$ 排序。

              +

              考虑建反图然后跑 \(\text{topo}\) +排序。

              可以认为这个过程是,倒着思考,从死路出发,看看它能祸害多少结点

              -

              好像这个题可以用 $\text{Tarjan}$ ,不过我这个不太熟,等熟练了再说吧。

              -

              时间复杂度 $O(n)$

              +

              好像这个题可以用 \(\text{Tarjan}\) +,不过我这个不太熟,等熟练了再说吧。

              +

              时间复杂度 \(O(n)\)

              代码:

              #include <iostream>
               #include <string>
              @@ -947,7 +969,7 @@ 

                站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/26/oi-tricks/index.html b/2022/07/26/oi-tricks/index.html index 3f4bff8cbe..7c45842ba2 100644 --- a/2022/07/26/oi-tricks/index.html +++ b/2022/07/26/oi-tricks/index.html @@ -468,7 +468,8 @@

              OI tricks

              -

              OI tricks

              平时做题的时候发现的一些技巧,还没有仔细整理

              +

              OI tricks

              +

              平时做题的时候发现的一些技巧,还没有仔细整理

              因此本文比较像草稿般的个人总结

              1.终极快读

              namespace FastIO
              @@ -497,12 +498,18 @@ 

              while(top){pc(stk[--top]+'0');} } }using namespace FastIO;

              -
              +

              2.区间的平均数问题

              -

              例:询问一个区间是否平均数大于 $m$

              -

              解法:把原区间每个数减去 $m$ ,这样修改后的区间和大于等于 $\boldsymbol{0}$ ,则平均数大于 $m$​

              -

              (这个很还是好想的,平均数大于 $m$ ,那么和就大于 $m\times$区间长度)

              -
              +

              例:询问一个区间是否平均数大于 \(m\)

              +

              解法:把原区间每个数减去 \(m\) +,这样修改后的区间和大于等于 \(\boldsymbol{0}\) ,则平均数大于 \(m\)

              +

              (这个很还是好想的,平均数大于 \(m\) +,那么和就大于 \(m\times\)区间长度)

              +

              3.秦九韶算法

              double cal(double x)
               {
              @@ -511,32 +518,40 @@ 

              =ans*x+a[i]; return ans; }

              -
              -

              4.全局k大值的维护

              -

              开一个小根堆,先插入 $k$ 个极小值,这样堆顶就是第 $k$ 大,(最大的在堆底)每次pop()+push()以保证始终是 $k$ 个,如果有重复计算或统计的值要把 $k$ 乘以那个值

              -
              +
              +

              4.全局k大值的维护

              +

              开一个小根堆,先插入 \(k\) +个极小值,这样堆顶就是第 \(k\) +大,(最大的在堆底)每次pop()+push()以保证始终是 \(k\) 个,如果有重复计算或统计的值要把 \(k\) 乘以那个值

              +

              5.lambda表达式

              -
                +
                1. 简洁版cmp

                  sort(a+1,a+n+1,[](int x,int y){
                       return rk[x] == rk[y]?rk[x+w]<rk[y+w]:rk[x]<rk[y];
                  -});
                  -
                2. +});

            7. 一般情况

              auto f=[=](int x)
               {
                   // do something...
               };
              -// 中括号里面=是传值,&是传地址
              -
            8. +// 中括号里面=是传值,&是传地址

            -
            +

            6.顺反则逆

            -

            如果发现 $\boldsymbol{+1}$ 标记的操作要反过来跑,可以考虑改成 $\boldsymbol{-1}$ ,然后正着跑

            -
            +

            如果发现 \(\boldsymbol{+1}\) +标记的操作要反过来跑,可以考虑改成 +\(\boldsymbol{-1}\) +,然后正着跑

            +

            7.枚举二元函数

            -

            例如 f(a,b) ,如果有单调性(比如单调递增)且 $a,b$ 的范围可以线性求解的话,

            -

            则可以 $a=1 \to n,b=n$ ,随着循环去减 $b$ ,这样就是线性的

            +

            例如 f(a,b) ,如果有单调性(比如单调递增)且 \(a,b\) 的范围可以线性求解的话,

            +

            则可以 \(a=1 \to n,b=n\) +,随着循环去减 \(b\) +,这样就是线性的

            例:abc246D

            #include<bits/stdc++.h>
             using namespace std;
            @@ -584,63 +599,81 @@ 

            return 0; }

            类似于双指针的写法

            -
            +

            8.最大值&最小值的转化

            -

            $\max z = -\min (-z)$

            -
            +

            \(\max z = -\min (-z)\)

            +

            9.枚举子矩阵

            -
              -
            1. 枚举左下和右上的点,$O(n^4)$
            2. -
            3. 枚举上边界和下边界,左右通过某些题的贪心性质搞成线性 例:洛谷P1369 $O(n^3)$
            4. +
                +
              1. 枚举左下和右上的点,\(O(n^4)\)
              2. +
              3. 枚举上边界和下边界,左右通过某些题的贪心性质搞成线性 例:洛谷P1369 +\(O(n^3)\)
              -
              -

              10.快速计算平方根

              -

              正整数 $a$ 满足 $a^2\le x < (a+1)^2$

              -
              -

              11.判断有多少个结点可以到达 $n$ 结点

              -

              建反图,然后从 $n$ 结点跑 bfs 即可

              -
              +\sqrt{x}&= a+\dfrac{x-a^2}{\sqrt{x}+a} \\&\approx +a+\dfrac{x-a^2}{2\times a} +\end{aligned} +\]

              +

              正整数 \(a\) 满足 \(a^2\le x < (a+1)^2\)

              +
              +

              11.判断有多少个结点可以到达 \(n\) +结点

              +

              建反图,然后从 \(n\) 结点跑 bfs +即可

              +

              12.找每个结点能到达的点权最小结点

              可以把所有的结点按点权升序排序,然后从最小点权的开始,去尝试影响别的结点的答案

              -

              这样做是 $O(n\log n)$ 的

              -
              +

              这样做是 \(O(n\log n)\)

              +

              13.直径两端到直径上一点的最近距离

              -

              从 $x$ 端跑个dis数组,答案就是min(dis[u],dis[y]-dis[u])

              -
              +

              \(x\) +端跑个dis数组,答案就是min(dis[u],dis[y]-dis[u])

              +

              14.有向图路径长度必须恰好t从1到达n的方案数

              -

              P4159 [SCOI2009] 迷路

              -
                +

                P4159 [SCOI2009] +迷路

                +
                1. 从样例一发现,并且不考虑题目问题,01邻接矩阵相乘就是通过2条边到达节点的方案数
                2. 对于非01邻接矩阵,使用拆点
                -

                -

                -
                +

                +

                +

                15.树上选一条路径,路径上每条边的边权异或x

                -

                AT3913 XOR Tree

                +

                AT3913 XOR +Tree

                可以把边权化点权

                -

                把每个结点的点权定义为 该结点所有相邻的边的边权异或和

                -

                这样就把路径改边权转化为了修改两个端点

                -
                +

                把每个结点的点权定义为 +该结点所有相邻的边的边权异或和

                +

                这样就把路径改边权转化为了修改两个端点

                +

                16.因子的一些等价表示

                -

                P3935 Calculating

                -
                  -
                1. +

                  P3935 +Calculating

                  +
                    +
                  +

                  \[ +\sum_{i=1}^{n}\sum_{d\mid i}1 = \sum_{i=1}^{n}\left\lfloor{\dfrac{n}{ +i}}\right\rfloor +\]

                  这个柿子就可以用数论分块来搞了

                  -
                    -
                  1. 若 $n = \prod_{i=1}^{s}p_i^{k_i}$
                  2. +
                      +
                    1. \(n = +\prod_{i=1}^{s}p_i^{k_i}\)
                    -

                    注意是因数个数!不是质因数个数!

                    -
                    +

                    \[ +\prod_{i=1}^{s}{(k_i+1)}=\sum_{d\mid n}1 = n \text{ 的因数个数} +\]

                    +

                    注意是因数个数!不是质因数个数!

                    +

                    17.循环改写->前缀和优化

                    -

                    常见于dp,例题:P4099 [HEOI2013]SAO

                    +

                    常见于dp,例题:P4099 +[HEOI2013]SAO

                    for(int i=1; i<=n; i++)
                     	for(int j=i; j<=n; j++)
                     		dp[i][j];
                    @@ -648,58 +681,71 @@ 

                    for(int j=1; j<=n; o++) for(int i=1; i<=j; i++) dp[i][j];

                    -
                    +

                    18.乘法转log 防止爆long long

                    类似于哈希

                    -

                    要记录这样的值,又不想用哈希

                    -

                    可以转化为

                    -
                    +

                    要记录这样的值,又不想用哈希 \[ +a\times b \times c +\] 可以转化为 \[ +\log a + \log b + \log c +\]

                    +

                    19.移项后二分

                    -

                    求下面柿子的最大值

                    -

                    可以假设

                    -

                    然后

                    -
                    while(r-l>1e-6)
                    +

                    求下面柿子的最大值 \[ +\dfrac{\sum a_i}{\sum b_i} +\] 可以假设 \[ +\dfrac{\sum a_i}{\sum b_i} \ge x +\] 然后 \[ +\sum a_i - x \sum b_i \ge 0 +\]

                    +
                    while(r-l>1e-6)
                     {
                         double mid=(l+r)/2;
                         if(ck(mid))l=mid;
                         else r=mid;
                     }
                    -
                    -

                    20.判正环 —改符号-> 判负环

                    -

                    RT.P2868 [USACO07DEC]Sightseeing Cows G

                    -
                    +
                    +

                    20.判正环 --改符号-> 判负环

                    +

                    RT.P2868 +[USACO07DEC]Sightseeing Cows G

                    +

                    21.数组中下一个相同的数

                    for(int i=n; i>=1; i--)
                     {
                         nx[i]=first[val[i]];
                         first[val[i]]=i;
                     } // 数组是val[]
                    -
                    +

                    22.经典n^3枚举矩阵

                    -

                    $O(n^2)$ 枚举上下界,$O(n)$ 处理中间一条

                    +

                    \(O(n^2)\) 枚举上下界,\(O(n)\) 处理中间一条

                    把上界到下界压到一个数上,这样就变成了一行

                    枚举上界或者下界也是有用的想法

                    -
                    +

                    23.环上选不相邻序列,权值最大

                    见P1484&P1792,反悔型贪心,即a-b-c,选b,反悔选a,c(看作一个几点)

                    -
                    +

                    24.反悔型贪心常用技巧

                    -
                      -
                    1. 比如什么原价 $w_i$ ,用优惠券 $p_i$ ,反悔的做法是新增一个物品价格为 $w_i-p_i$ 以腾出优惠券
                    2. +
                        +
                      1. 比如什么原价 \(w_i\) ,用优惠券 +\(p_i\) +,反悔的做法是新增一个物品价格为 \(w_i-p_i\) 以腾出优惠券
                      2. 延迟删除:比起去优先队列里找元素删,不如延迟删除,即用个vis记录是否被删除,然后每次取堆顶的时候判断
                      -
                      +

                      25.标记优化树状数组的频繁清空操作

                      -

                      详见 link ,其实也叫时间戳优化

                      +

                      详见 link +,其实也叫时间戳优化

                      适用场景:

                      -

                      给定 $T$ 组数据,每组数据有 $Q$ 个询问,询问给定 $n$ 个数的区间和

                      -

                      数据范围:$T\le 2\times 10^5,~\sum Q \le 2\times 10^5,~n\le 5\times 10^5$

                      -

                      对于每组数据不能直接去清空数组,因为 $T \times n$ 肯定爆炸

                      +

                      给定 \(T\) 组数据,每组数据有 \(Q\) 个询问,询问给定 \(n\) 个数的区间和

                      +

                      数据范围\(T\le 2\times +10^5,~\sum Q \le 2\times 10^5,~n\le 5\times 10^5\)

                      +

                      对于每组数据不能直接去清空数组,因为 \(T +\times n\) 肯定爆炸

                      考虑维护一个时间戳now,也就是现在是第几组数据

                      int tree[N],t[N],now=0;
                       void add(int x,int v)
                      @@ -715,17 +761,19 @@ 

                      if(t[i]==now) res+=tree[i]; else t[i]=now,tree[i]=0; // res+=0 }

                      -
                      +

                      26.枚举技巧

                      -

                      求柿子的最大值

                      -

                      假设

                      -

                      然后按 $d1-d2$ 排序,接下来?

                      -
                      -
                        -
                      1. nx[i][j] 表示 $i$ 后面出现的第一个字符 $j$ 的位置
                      2. +\\d1[u]-d2[u]\le d1[v]-d2[v] +\] 然后按 \(d1-d2\) +排序,接下来?

                        +
                        +
                          +
                        1. nx[i][j] 表示 \(i\) +后面出现的第一个字符 \(j\) 的位置
                        for(int i=0; i<26; i++)
                             for(int j=1; j<=n+1; j++)
                        @@ -733,13 +781,16 @@ 

                        for(int i=0; i<26; i++) for(int j=n; j>=1; j--) nx[i][j]=(s[j]=='a'+i)?j:nx[i][j+1];

                        -
                        -
                          -
                        1. $x \equiv x_\texttt{十进制下的每个数位的和} \bmod p$
                        2. +
                          +
                            +
                          1. \(x \equiv +x_\texttt{十进制下的每个数位的和} \bmod p\)
                          -
                          -

                          29.$a_i \leftarrow a_{i-1}+a_{i+1}-a_i$

                          -

                          等价于交换 $a_i-a_{i-1}$ 和 $a_{i+1}-a_i$

                          +
                          +

                          29.\(a_i \leftarrow +a_{i-1}+a_{i+1}-a_i\)

                          +

                          等价于交换 \(a_i-a_{i-1}\)\(a_{i+1}-a_i\)

                          例如

                          a[3]-a[2]=5
                           a[4]-a[3]=4
                          @@ -753,7 +804,9 @@

                          return a.x*b.y==a.y*b.x; }

                    31.异或=模2意义下的加法

                    -

                    32.

                    +
                      +
                    1. +
                    // 返回x符号正负(1或-1)
                     int sgn(int x){return (x>0)-(x<0);}
                    @@ -1125,7 +1178,7 @@

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/27/luo-gu-p2341-usaco03fall-haoi2006-shou-huan-ying-de-niu-g-ti-jie/index.html b/2022/07/27/luo-gu-p2341-usaco03fall-haoi2006-shou-huan-ying-de-niu-g-ti-jie/index.html index e5eb4f3812..51e932c5bb 100644 --- a/2022/07/27/luo-gu-p2341-usaco03fall-haoi2006-shou-huan-ying-de-niu-g-ti-jie/index.html +++ b/2022/07/27/luo-gu-p2341-usaco03fall-haoi2006-shou-huan-ying-de-niu-g-ti-jie/index.html @@ -472,21 +472,34 @@

                    洛谷P2341 [USACO03FALL / HAOI2
                    -

                    洛谷P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G 题解

                    题目链接:P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G

                    +

                    # 洛谷P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G 题解

                    +

                    题目链接:P2341 +[USACO03FALL / HAOI2006] 受欢迎的牛 G

                    题意

                    -

                    每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 $A$ 喜欢 $B$,$B$ 喜欢 $C$,那么 $A$ 也喜欢 $C$。牛栏里共有 $N$ 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

                    -

                    对于 $100\%$ 的数据,$1\le N\le10^4$,$1\le M\le5\times 10^4$。

                    +

                    每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 +\(A\) 喜欢 \(B\)\(B\) +喜欢 \(C\),那么 \(A\) 也喜欢 \(C\)。牛栏里共有 \(N\) +头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

                    +

                    对于 \(100\%\) 的数据,\(1\le N\le10^4\)\(1\le M\le5\times 10^4\)

                    首先考虑最简单的情况

                    如果给定的是一个DAG(有向无环图)

                    -

                    当且仅当存在两个及以上出度为 $0$ 的结点时,答案为 $0$ (无解)

                    +

                    当且仅当存在两个及以上出度为 \(0\) +的结点时,答案为 \(0\) (无解)

                    但是题目给的不一定是DAG

                    注意到每个强连通分量内的结点两两可达

                    用这题的语言就是,某个强连通分量内的所有结点可以同时为“明星”

                    于是考虑强连通分量缩点,接下来就是DAG的情况了

                    注意此时的答案,如果有解的话,就是那个点对应的强连通分量大小

                    -

                    时间复杂度 $O(n+m)$ ,采用 $\text{kosaraju}$ 算法

                    +

                    时间复杂度 \(O(n+m)\) ,采用 \(\text{kosaraju}\) 算法

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -934,7 +947,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/27/mo-ni-sai-ti-jiang-jie-3/index.html b/2022/07/27/mo-ni-sai-ti-jiang-jie-3/index.html index 25f2ff5a21..4b962acaf2 100644 --- a/2022/07/27/mo-ni-sai-ti-jiang-jie-3/index.html +++ b/2022/07/27/mo-ni-sai-ti-jiang-jie-3/index.html @@ -472,18 +472,37 @@

                    模拟赛题讲解[3]

                    -

                    模拟赛题讲解[3]

                    来自 Roundgod 2022-07-26 noi.ac #2679

                    +

                    模拟赛题讲解[3]

                    +

                    来自 Roundgod +2022-07-26 noi.ac #2679

                    题目描述

                    -

                    给定一个无向图 $G=(V,E)$ ,其中顶点个数 $|V|=n$ ,边数 $|E|=m$ 。顶点编号为 $1−N$, 边的编号为 $1−M$ 。其中 $i$ 号顶点有一个权值 $h_i$ .

                    -

                    你现在可以从 $1$ 号顶点开始沿着图上的边行走。你初始时的得分为 $0$ ,当你经过一条边 $(u,v)$ 时,你的得分会如下更新:

                    -

                    1.如果 $h_u>h_v$ ,你的得分会增加 $h_u−h_v$ 。

                    -

                    2.如果 $h_u<h_v$ ,你的得分会减少 $2(h_v−h_u)$ 。

                    -

                    3.如果 $h_u=h_v$,你的得分会不变.

                    +

                    给定一个无向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数 \(|E|=m\) 。顶点编号为 \(1−N\), 边的编号为 \(1−M\) 。其中 \(i\) 号顶点有一个权值 \(h_i\) .

                    +

                    你现在可以从 \(1\) +号顶点开始沿着图上的边行走。你初始时的得分为 \(0\) ,当你经过一条边 \((u,v)\) 时,你的得分会如下更新:

                    +

                    1.如果 \(h_u>h_v\) +,你的得分会增加 \(h_u−h_v\)

                    +

                    2.如果 \(h_u<h_v\) +,你的得分会减少 \(2(h_v−h_u)\)

                    +

                    3.如果 \(h_u=h_v\),你的得分会不变.

                    你想要知道,经过任意行走后(也可以不行走),你可以得到的最大得分是多少?

                    输入格式

                    -

                    输入第一行包含 $3$ 个整数 $n,m$ ,分别表示图的顶点数和图的边数。

                    -

                    接下来一行包含 $n$ 个整数 $h_1,h_2,\dots ,h_n$ ,表示每个顶点的初始权值。

                    -

                    接下来 $m$ 行,每行 $u_i,v_i(1\le u_i,v_i\le n,u_i\ne v_i)$ ,表示第 $i$ 条边。

                    +

                    输入第一行包含 \(3\) 个整数 \(n,m\) ,分别表示图的顶点数和图的边数。

                    +

                    接下来一行包含 \(n\) 个整数 \(h_1,h_2,\dots ,h_n\) +,表示每个顶点的初始权值。

                    +

                    接下来 \(m\) 行,每行 \(u_i,v_i(1\le u_i,v_i\le n,u_i\ne v_i)\) +,表示第 \(i\) 条边。

                    输入格式

                    在一行中输出一个数,表示你可以得到的最大得分。

                    输入1

                    @@ -502,39 +521,57 @@

                    输出2

                    0

                    数据范围

                    -

                    对于 $30\%$ 的数据,$1\le n\le 200,~1\le m\le 1000,~1\le h_i\le 10^9$

                    -

                    对于 $100\%$ 的数据,$1\le n\le 2\times 10^5,~1\le m\le 2\times 10^5,~1\le h_i\le 10^{18}$

                    +

                    对于 \(30\%\) 的数据,\(1\le n\le 200,~1\le m\le 1000,~1\le h_i\le +10^9\)

                    +

                    对于 \(100\%\) 的数据,\(1\le n\le 2\times 10^5,~1\le m\le 2\times +10^5,~1\le h_i\le 10^{18}\)

                    题解

                    不难发现这个图就是要找个最长路

                    常见的trick就是把边权取反后跑最短路

                    注:这里以及下文中的取反变成其相反数

                    -

                    而这道题的边权其实很好推

                    -

                    注意到会有负权边,且 $n \le 2 \times 10^5$

                    +2h_u-2h_v,&h_u<h_v +\\\\0,&h_u=h_v +\\\\h_u-h_v,&h_u>h_v +\end{cases} +\] 注意到会有负权边,且 \(n \le 2 +\times 10^5\)

                    直接用spfa或者dijkstra肯定是不行的(虽然数据没成功卡掉)

                    -

                    还记得什么“黑科技”可以让 $\text{Dijkstra}$ 跑负权图吗?

                    -

                    答案是势能函数,也叫权重函数,思想来源于 Johnson 全源最短路算法

                    -

                    说的简单一点,一般势能函数就是把负权改成非负权,然后跑 $\text{Dijkstra}$

                    -

                    在本题中,我们可以设

                    -

                    于是

                    -

                    不难发现,此时 $w^{\prime}(u,v) \le 0$ 恒成立

                    -

                    设 $d_i=f_i+h_i-h_1$ ,其中 $f_i$ 为我们要求的最大分数(最长路长度)

                    -

                    可以发现,如果我们要求出 $f_i$ ,就需要求出 $d_i$

                    -

                    而这个 $d_i$ 就是使用势能函数作边权后 $1$ 到 $i$ 的最长路

                    +h_u-h_v,&h_u<h_v +\\\\0,&h_u\ge h_v +\end{cases} +\] 不难发现,此时 \(w^{\prime}(u,v) \le +0\) 恒成立

                    +

                    \(d_i=f_i+h_i-h_1\) ,其中 \(f_i\) +为我们要求的最大分数(最长路长度)

                    +

                    可以发现,如果我们要求出 \(f_i\) +,就需要求出 \(d_i\)

                    +

                    而这个 \(d_i\) +就是使用势能函数作边权后 \(1\)\(i\) 的最长路

                    还记得刚刚的trick吗?我们要求的是边权取反后的最短路

                    -

                    这样我们把 $-w^{\prime}(u,v)$ 加入,跑个最短路,就能求出答案了

                    -

                    此时求出的其实是 $d^{\prime}_i=-f_i-h_i+h_1$

                    -

                    移项得 $f_i=h_1-h_i-d^{\prime}_i$

                    -

                    时间复杂度 $O(n \log m)$

                    +

                    这样我们把 \(-w^{\prime}(u,v)\) +加入,跑个最短路,就能求出答案了

                    +

                    此时求出的其实是 \(d^{\prime}_i=-f_i-h_i+h_1\)

                    +

                    移项得 \(f_i=h_1-h_i-d^{\prime}_i\)

                    +

                    时间复杂度 \(O(n \log m)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -979,7 +1016,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/27/mo-ni-sai-ti-jiang-jie-4/index.html b/2022/07/27/mo-ni-sai-ti-jiang-jie-4/index.html index 73c9236e29..e9c000a608 100644 --- a/2022/07/27/mo-ni-sai-ti-jiang-jie-4/index.html +++ b/2022/07/27/mo-ni-sai-ti-jiang-jie-4/index.html @@ -472,16 +472,38 @@

                    模拟赛题讲解[4]

                    -

                    模拟赛题讲解[4]

                    来自 Roundgod 2022-07-26 noi.ac #2680

                    +

                    模拟赛题讲解[4]

                    +

                    来自 Roundgod +2022-07-26 noi.ac #2680

                    问题描述

                    -

                    Berland由 $n$ 个城市和 $m$ 条双向道路构成,其中第 $i$ 条道路连接城市 $a_i$ 和 $b_i$ ,并且距离 $c_i$ 公里.

                    -

                    你想要开车在Berland进行自驾游。已知车子的油箱最多能装 $L$ 升油,并且车子每行进一公里都要耗费恰好一升油。当你到达某个城市的时候,你可以选择将油箱加满油(也可以不加)。你不能在道路中间加油。

                    -

                    现在给出 $q$ 组如下形式的询问: 给定起点 $s$ 和终点 $t$ ,问如果初始时油箱装满从城市 $s$ 出发,最少要多少次加满油才能到达城市 $t$ ,如果无法到达输出 $−1$.

                    +

                    Berland由 \(n\) 个城市和 \(m\) 条双向道路构成,其中第 \(i\) 条道路连接城市 \(a_i\)\(b_i\) ,并且距离 \(c_i\) 公里.

                    +

                    你想要开车在Berland进行自驾游。已知车子的油箱最多能装 \(L\) +升油,并且车子每行进一公里都要耗费恰好一升油。当你到达某个城市的时候,你可以选择将油箱加满油(也可以不加)。你不能在道路中间加油。

                    +

                    现在给出 \(q\) 组如下形式的询问: +给定起点 \(s\) 和终点 \(t\) ,问如果初始时油箱装满从城市 \(s\) +出发,最少要多少次加满油才能到达城市 \(t\) ,如果无法到达输出 \(−1\).

                    输入格式

                    -

                    输入第一行包含 $3$ 个整数 $n,m,L$分别表示城市的个数,道路的条数以及油箱的容量。

                    -

                    接下来 $m$ 行,每行包含三个整数 $a_i,b_i,c_i(1\le a_i,b_i\le n,a_i\ne b_i)$ ,表示第 $i$ 条道路连接的城市以及道路长度。

                    -

                    接下来一行输出一个整数 $q$ ,表示询问的组数。

                    -

                    接下来 $q$ 行,每行输入两个整数 $s_i,t_i(1\le s_i,t_i\le n,s_i \ne t_i)$ ,表示每组询问。

                    +

                    输入第一行包含 \(3\) 个整数 \(n,m,L\)分别表示城市的个数,道路的条数以及油箱的容量。

                    +

                    接下来 \(m\) 行,每行包含三个整数 +\(a_i,b_i,c_i(1\le a_i,b_i\le n,a_i\ne +b_i)\) ,表示第 \(i\) +条道路连接的城市以及道路长度。

                    +

                    接下来一行输出一个整数 \(q\) +,表示询问的组数。

                    +

                    接下来 \(q\) 行,每行输入两个整数 +\(s_i,t_i(1\le s_i,t_i\le n,s_i \ne +t_i)\) ,表示每组询问。

                    输出格式

                    对于每组询问,你需要在一行中输出一个整数表示答案。

                    输入1

                    @@ -501,20 +523,30 @@

                    输出2

                    -1

                    数据范围

                    -

                    对于 $40\%$ 的数据,$1\le n\le 300,~1\le m\le 2000,~1\le L\le 10^9,~1\le q\le n(n−1),~c_i=L$

                    -

                    对于 $100\%$ 的数据,$1\le n\le 300,~1\le m\le \frac{n(n−1)}{2},~1\le L\le 10^9,~1\le q\le n(n−1),~1\le c_i\le 10^9$

                    +

                    对于 \(40\%\) 的数据,\(1\le n\le 300,~1\le m\le 2000,~1\le L\le +10^9,~1\le q\le n(n−1),~c_i=L\)

                    +

                    对于 \(100\%\) 的数据,\(1\le n\le 300,~1\le m\le \frac{n(n−1)}{2},~1\le +L\le 10^9,~1\le q\le n(n−1),~1\le c_i\le 10^9\)

                    题解

                    关于老师数据挂了,改完我就rank1这件事

                    -

                    首先,如果一条路径的边权和小于等于 $L$ ,那我们只要加一次油就可以了

                    -

                    因此我们可以认为在路径上的这些结点两两有花费为 $1$ 的边权(加油次数)

                    -

                    然后两遍 $\text{Floyd}$ 就好了,具体可以看代码

                    +

                    首先,如果一条路径的边权和小于等于 \(L\) ,那我们只要加一次油就可以了

                    +

                    因此我们可以认为在路径上的这些结点两两有花费为 \(1\) 的边权(加油次数)

                    +

                    然后两遍 \(\text{Floyd}\) +就好了,具体可以看代码

                    有一说一,虽然思路是这样的,但是我的实现和讲解的不太一样

                    所以我把标程搬过来了,可以参考一下

                    有没有一种可能,我自己都没看懂自己的写法

                    -

                    其实我的实现是 $f_{i,j}$ 表示从 $i$ 出发到 $j$

                    -

                    因为只有一个起点,因此另一个起点加满的油在合并时是要加上的,故

                    -

                    时间复杂度 $O(n^3)$

                    +

                    其实我的实现是 \(f_{i,j}\) +表示\(i\) 出发到 +\(j\)

                    +

                    因为只有一个起点,因此另一个起点加满的油在合并时是要加上的,故 \[ +f_{i,j}=\min\{f_{i,j},~f_{i,k}+f_{k,j}+1\} +\] 时间复杂度 \(O(n^3)\)

                    我的考场代码:

                    #include <iostream>
                     #include <string>
                    @@ -990,7 +1022,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/27/mo-ni-sai-ti-jiang-jie-5/index.html b/2022/07/27/mo-ni-sai-ti-jiang-jie-5/index.html index 94f090b3f4..772576a147 100644 --- a/2022/07/27/mo-ni-sai-ti-jiang-jie-5/index.html +++ b/2022/07/27/mo-ni-sai-ti-jiang-jie-5/index.html @@ -476,11 +476,24 @@

                    模拟赛题讲解[5]

                    -

                    模拟赛题讲解[5]

                    来自 Roundgod 2022-07-26 noi.ac #2678

                    +

                    模拟赛题讲解[5]

                    +

                    来自 Roundgod +2022-07-26 noi.ac #2678

                    题目描述

                    -

                    对于任意非负整数 $x$ 和 $m(2\le m\le 10)$ ,定义 $f_m(x)$ 为 $x$ 在 $m$ 进制下的各位数字之和。给定两个整数 $k$ 和 $m$ 。你需要计算在所有满足是 $k$ 倍数正数 $x$ 中,最小的 $f_m(x)$ 值是多少。

                    +

                    对于任意非负整数 \(x\)\(m(2\le m\le 10)\) ,定义 \(f_m(x)\)\(x\)\(m\) 进制下的各位数字之和。给定两个整数 +\(k\)\(m\) 。你需要计算在所有满足是 \(k\) +倍数正数 \(x\) 中,最小的 \(f_m(x)\) 值是多少。

                    输入格式

                    -

                    输入第一行包含两个整数 $k,m(2\le m\le 10)$ 。

                    +

                    输入第一行包含两个整数 \(k,m(2\le m\le +10)\)

                    输出格式

                    在一行中输出一个整数,表示答案。

                    输入1

                    @@ -496,20 +509,32 @@

                    输出3

                    4

                    数据范围

                    -

                    对于 $30\%$ 的数据,$1\le k\le 100$

                    -

                    对于 $100\%$ 的数据,$1\le k\le 10^6$

                    +

                    对于 \(30\%\) 的数据,\(1\le k\le 100\)

                    +

                    对于 \(100\%\) 的数据,\(1\le k\le 10^6\)

                    题解

                    -

                    尝试以最小的花费构造一个数使得其是 $k$ 的倍数

                    -

                    如果我们把这个构造的过程放在模 $k$ 的意义下

                    -

                    问题就转化为了,如何以最小的花费构造一个 $0$

                    -

                    设 $d_x,~x \in [0,k)$ 表示构造一个模 $k$ 意义下为 $x$ 的数的最小花费

                    +

                    尝试以最小的花费构造一个数使得其是 \(k\) 的倍数

                    +

                    如果我们把这个构造的过程放在模 \(k\) +的意义下

                    +

                    问题就转化为了,如何以最小的花费构造一个 \(0\)

                    +

                    \(d_x,~x \in [0,k)\) +表示构造一个模 \(k\) 意义下为 \(x\) 的数的最小花费

                    然后尝试转移?

                    -

                    对于构造的数,我们可以乘 $m$ ,也可以加 $1$

                    -

                    前者对应的花费为 $0$ ,后者对应的花费为 $1$

                    -

                    为什么只加 $1$ 呢?因为加 $2$ 等价于两次加 $1$

                    +

                    对于构造的数,我们可以乘 \(m\) +,也可以加 \(1\)

                    +

                    前者对应的花费为 \(0\) +,后者对应的花费为 \(1\)

                    +

                    为什么只加 \(1\) 呢?因为加 \(2\) 等价于两次加 \(1\)

                    于是,这个题就转化为了同余最短路

                    -

                    然后就是同余最短路,甚至不用 $\text{Dijkstra}$ ,直接01bfs就好了

                    -

                    考场直接打暴力骗分,至今不是很理解同余最短路,有待补充

                    +

                    然后就是同余最短路,甚至不用 \(\text{Dijkstra}\) ,直接01bfs就好了

                    +

                    考场直接打暴力骗分,至今不是很理解同余最短路,有待补充

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -917,7 +942,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/27/mo-ni-sai-ti-jiang-jie-6/index.html b/2022/07/27/mo-ni-sai-ti-jiang-jie-6/index.html index 6c7ebfdd84..8f0a8156f1 100644 --- a/2022/07/27/mo-ni-sai-ti-jiang-jie-6/index.html +++ b/2022/07/27/mo-ni-sai-ti-jiang-jie-6/index.html @@ -472,14 +472,39 @@

                    模拟赛题讲解[6]

                    -

                    模拟赛题讲解[6]

                    来自 Roundgod 2022-07-27 noi.ac #2683

                    +

                    模拟赛题讲解[6]

                    +

                    来自 Roundgod +2022-07-27 noi.ac #2683

                    题目描述

                    -

                    给定一个连通无向图 $G=(V,E)$ ,其中顶点个数 $\vert V\vert=n$ , 边数 $\vert E\vert=m$ 。顶点编号为 $1-n$ , 边的编号为 $1-m$ ,第 $i$条边连接顶点 $a_i$ 和 $b_i$。输入保证图中不存在重边。

                    -

                    你现在需要对图中的每条边定向: 对于原图中的每条无向边 $(u,v)$,你需要选择将它变为有向边 $(u,v)$或者有向边 $(v,u)$.

                    -

                    对于图 $G$中的每个顶点 $v\in V$,定义 $r_v$ 为将图定向之后 $v$ 能够到达的顶点个数,你需要选择一种定向使得最大化 $\min\limits_{v \in V}r_v$,也就是最大化所有 $r_v$ 的 最小值。输出这个最大化的值。

                    +

                    给定一个连通无向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\) , +边数 \(\vert E\vert=m\) 。顶点编号为 +\(1-n\) , 边的编号为 \(1-m\) ,第 \(i\)条边连接顶点 \(a_i\)\(b_i\)。输入保证图中不存在重边。

                    +

                    你现在需要对图中的每条边定向: 对于原图中的每条无向边 \((u,v)\),你需要选择将它变为有向边 \((u,v)\)或者有向边 \((v,u)\).

                    +

                    对于图 \(G\)中的每个顶点 \(v\in V\),定义 \(r_v\) 为将图定向之后 \(v\) +能够到达的顶点个数,你需要选择一种定向使得最大化 \(\min\limits_{v \in +V}r_v\),也就是最大化所有 \(r_v\) 的 最小值。输出这个最大化的值。

                    输入格式

                    -

                    输入第一行包含 $2$个整数 $n,m$,分别表示图的顶点数和图的边数。

                    -

                    接下来 $m$行,每行包含两个整数 $a_i,b_i(1\leq a_i,b_i\leq n,a_i\neq b_i)$ ,表示第 $i$条边连接顶点 $a_i$ 和顶点 $b_i$ 。

                    +

                    输入第一行包含 \(2\)个整数 \(n,m\),分别表示图的顶点数和图的边数。

                    +

                    接下来 \(m\)行,每行包含两个整数 +\(a_i,b_i(1\leq a_i,b_i\leq n,a_i\neq +b_i)\) ,表示第 \(i\)条边连接顶点 \(a_i\) 和顶点 \(b_i\)

                    输出格式

                    在一行中输出一个整数,表示答案。

                    输入1

                    @@ -504,21 +529,30 @@

                    输出2

                    4

                    数据范围

                    -

                    对于 $20\%$ 的数据,$1\leq n\leq 20,~1\leq m\leq 50$

                    -

                    对于 $50\%$ 的数据,$1\leq n\leq 2000,~1\leq m\leq 5000$

                    -

                    对于 $100\%$ 的数据,$1\leq n\leq 10^5,~1\leq m\leq 2\times 10^5$

                    +

                    对于 \(20\%\) 的数据,\(1\leq n\leq 20,~1\leq m\leq 50\)

                    +

                    对于 \(50\%\) 的数据,\(1\leq n\leq 2000,~1\leq m\leq 5000\)

                    +

                    对于 \(100\%\) 的数据,\(1\leq n\leq 10^5,~1\leq m\leq 2\times +10^5\)

                    题解

                    不难发现,一个边双连通分量,一定可以通过给边标向变成强连通分量

                    而这道题的答案,其实就是所有边双连通分量中最大的那个所包含的结点数

                    证明

                    -

                    首先我们先证明答案至多为 $\max\left\{|V^{\prime}_i|\right\}$

                    +

                    首先我们先证明答案至多为 \(\max\left\{|V^{\prime}_i|\right\}\)

                    不难发现,我们对所有的桥(割边)标向后,一定会有一个边双无出边

                    -

                    而这个边双的大小 $|V_i^{\prime}|$ 会影响全局的答案,于是要贪心的使这个大小取最大。证毕。

                    -

                    然后我们证明答案至少为 $\max\left\{|V_i^{\prime}|\right\}$

                    +

                    而这个边双的大小 \(|V_i^{\prime}|\) +会影响全局的答案,于是要贪心的使这个大小取最大。证毕。

                    +

                    然后我们证明答案至少为 \(\max\left\{|V_i^{\prime}|\right\}\)

                    以这个最大的边双为根节点(边双缩点后一定是棵树),其他的结点全部往父亲指

                    -

                    这样每个边双至少可以走到这个满足 $|V^{\prime}|=\max\left\{|V_i^{\prime}|\right\}$ 的 $V^{\prime}$ 。证毕。

                    +

                    这样每个边双至少可以走到这个满足 \(|V^{\prime}|=\max\left\{|V_i^{\prime}|\right\}\) +的 \(V^{\prime}\) 。证毕。

                    你敢信这段东西我花了3min速记的,不然会忘记老师讲的证明的,2333

                    -

                    时间复杂度 $O(n+m)$

                    +

                    时间复杂度 \(O(n+m)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -951,7 +985,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/28/cf1295d-same-gcds-ti-jie/index.html b/2022/07/28/cf1295d-same-gcds-ti-jie/index.html index d14e836e8c..534d88b149 100644 --- a/2022/07/28/cf1295d-same-gcds-ti-jie/index.html +++ b/2022/07/28/cf1295d-same-gcds-ti-jie/index.html @@ -472,22 +472,29 @@

                    CF1295D Same GCDs 题解

                    -

                    CF1295D Same GCDs 题解

                    题目链接:CF1295D Same GCDs

                    +

                    CF1295D Same GCDs 题解

                    +

                    题目链接:CF1295D +Same GCDs

                    题意

                    -

                    -

                    多组测试数据, $T \leq 50,\ 1 \leq a < m \leq 10^{10}$

                    +

                    \[ +\sum_{x=0}^{m-1} [\gcd(a, m) = \gcd(a + x, m)] +\] 多组测试数据, \(T \leq 50,\ 1 \leq a +< m \leq 10^{10}\)

                    -

                    设 $p=\gcd(a,m)$ ,则原式可化为

                    -

                    即求

                    -

                    时间复杂度 $O(Q\sqrt{n})$

                    +\sum_{x=0}^{m-1} [\gcd(a+x,m) = p] &= +\sum_{x=0}^{m-1}\left[\gcd\left(\frac{a+x}{p},\frac{m}{p}\right)=1\right] +\\\\&=\sum_{x=0}^{m-1}\left[\gcd\left(\left(\frac{a+x}{p} \bmod +\frac{m}{p}\right),\frac{m}{p}\right)=1\right] +\\\\&= \varphi\left(\frac{m}{p}\right) +\end{aligned} +\] 即求 \[ +\varphi\left(\frac{m}{\gcd(a,m)}\right) +\] 时间复杂度 \(O(Q\sqrt{n})\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -896,7 +903,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/28/cf1485c-floor-and-mod-ti-jie/index.html b/2022/07/28/cf1485c-floor-and-mod-ti-jie/index.html index b55cc10fef..76c427c1c1 100644 --- a/2022/07/28/cf1485c-floor-and-mod-ti-jie/index.html +++ b/2022/07/28/cf1485c-floor-and-mod-ti-jie/index.html @@ -472,28 +472,42 @@

                    CF1485C Floor and Mod 题解

                    -

                    CF1485C Floor and Mod 题解

                    题目链接:CF1485C Floor and Mod

                    +

                    CF1485C Floor and Mod 题解

                    +

                    题目链接:CF1485C +Floor and Mod

                    -

                    题意:求 $1\le a\le x,1\le b\le y$ 且 $\lfloor\frac{a}{b}\rfloor =a\bmod b$ 的$(a,b)$ 个数

                    +

                    题意:求 \(1\le a\le +x,1\le b\le y\)\(\lfloor\frac{a}{b}\rfloor =a\bmod b\) +的\((a,b)\) 个数

                    -

                    这道题有点麻烦 QAQ

                    -

                    先推一波柿子

                    -

                    显然 $b +1 \mid a$ ,同时因为 $a \bmod b <b$

                    -

                    所以 $\dfrac{a}{b+1} < b \Rightarrow a<b^2 + b \Rightarrow a \in [0,b^2+b-1]$

                    -

                    则满足条件的 $a$ 的个数为 $\left\lfloor\dfrac{\min\{x,b^2+b-1\}}{b+1}\right\rfloor$

                    -

                    答案就是

                    -

                    但是这个东西好像不好用数论分块

                    -

                    仔细观察,当 $b \ge \sqrt{x}-\epsilon,\epsilon\in \mathbb{N}$ 时,柿子是这样的

                    -

                    其实写成 $\sum\limits_{b=2}^{y+1}\left\lfloor\dfrac{x} {b}\right\rfloor$ 可以更明显地看出这是个数论分块的板子

                    -

                    对于 $b < \sqrt{x}-\epsilon,\epsilon\in \mathbb{N}$ 的情况,直接暴力枚举即好了

                    -

                    时间复杂度 $O(Q \sqrt{x})$

                    +\left\lfloor{\frac{a}{b}}\right\rfloor &= a \bmod b +\\\\\left\lfloor{\frac{a}{b}}\right\rfloor &= +a-\left\lfloor{\frac{a}{b}}\right\rfloor\times b +\\\\\left\lfloor{\frac{a}{b}}\right\rfloor &=\frac{a}{b+1} +\end{aligned} +\] 显然 \(b +1 \mid a\) +,同时因为 \(a \bmod b <b\)

                    +

                    所以 \(\dfrac{a}{b+1} < b \Rightarrow +a<b^2 + b \Rightarrow a \in [0,b^2+b-1]\)

                    +

                    则满足条件的 \(a\) 的个数为 \(\left\lfloor\dfrac{\min\{x,b^2+b-1\}}{b+1}\right\rfloor\)

                    +

                    答案就是 \[ +\sum_{b=1}^{y}\left\lfloor\dfrac{\min\{x,b^2+b-1\}}{b+1}\right\rfloor +\] 但是这个东西好像不好用数论分块

                    +

                    仔细观察,当 \(b \ge +\sqrt{x}-\epsilon,\epsilon\in \mathbb{N}\) 时,柿子是这样的 \[ +\sum_{b=1}^{y}\left\lfloor\dfrac{x}{b+1}\right\rfloor +\] 其实写成 \(\sum\limits_{b=2}^{y+1}\left\lfloor\dfrac{x} +{b}\right\rfloor\) 可以更明显地看出这是个数论分块的板子

                    +

                    对于 \(b < \sqrt{x}-\epsilon,\epsilon\in +\mathbb{N}\) 的情况,直接暴力枚举即好了

                    +

                    时间复杂度 \(O(Q \sqrt{x})\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -894,7 +908,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/28/cf427c-checkposts-ti-jie/index.html b/2022/07/28/cf427c-checkposts-ti-jie/index.html index 0e6644693f..bb43c228f3 100644 --- a/2022/07/28/cf427c-checkposts-ti-jie/index.html +++ b/2022/07/28/cf427c-checkposts-ti-jie/index.html @@ -472,19 +472,32 @@

                    CF427C Checkposts 题解

                    -

                    CF427C Checkposts 题解

                    题目链接:CF427C Checkposts

                    +

                    CF427C Checkposts 题解

                    +

                    题目链接:CF427C +Checkposts

                    题意

                    懒得贴翻译,那个翻译太烂了

                    -

                    Your city has $ n $ junctions. There are $ m $ one-way roads between the junctions. As a mayor of the city, you have to ensure the security of all the junctions.

                    -

                    To ensure the security, you have to build some police checkposts. Checkposts can only be built in a junction. A checkpost at junction $ i $ can protect junction $ j $ if either $ i=j $ or the police patrol car can go to $ j $ from $ i $ and then come back to $ i $ .

                    -

                    Building checkposts costs some money. As some areas of the city are more expensive than others, building checkpost at some junctions might cost more money than other junctions.

                    -

                    You have to determine the minimum possible money needed to ensure the security of all the junctions. Also you have to find the number of ways to ensure the security in minimum price and in addition in minimum number of checkposts. Two ways are different if any of the junctions contains a checkpost in one of them and do not contain in the other.

                    +

                    Your city has $ n $ junctions. There are $ m $ one-way roads between +the junctions. As a mayor of the city, you have to ensure the security +of all the junctions.

                    +

                    To ensure the security, you have to build some police checkposts. +Checkposts can only be built in a junction. A checkpost at junction $ i +$ can protect junction $ j $ if either $ i=j $ or the police patrol car +can go to $ j $ from $ i $ and then come back to $ i $ .

                    +

                    Building checkposts costs some money. As some areas of the city are +more expensive than others, building checkpost at some junctions might +cost more money than other junctions.

                    +

                    You have to determine the minimum possible money needed to ensure the +security of all the junctions. Also you have to find the number of ways +to ensure the security in minimum price and in addition in minimum +number of checkposts. Two ways are different if any of the junctions +contains a checkpost in one of them and do not contain in the other.

                    不难发现,一个强连通分量内的答案就是最小的那个花费

                    而这个强连通分量里的方案数就是和最小花费相等的结点数

                    然后求个强连通分量,乘法原理搞一搞就好了

                    -

                    时间复杂度 $O(n + m)$

                    +

                    时间复杂度 \(O(n + m)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -925,7 +938,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/28/cf906d-power-tower-ti-jie/index.html b/2022/07/28/cf906d-power-tower-ti-jie/index.html index c28218980a..afc2828499 100644 --- a/2022/07/28/cf906d-power-tower-ti-jie/index.html +++ b/2022/07/28/cf906d-power-tower-ti-jie/index.html @@ -472,19 +472,32 @@

                    CF906D Power Tower 题解

                    -

                    CF906D Power Tower 题解

                    题目链接:CF906D Power Tower

                    +

                    CF906D Power Tower 题解

                    +

                    题目链接:CF906D +Power Tower

                    -

                    题意:给定长度为 $n$ 的序列 $a_i$ 和模数 $p$

                    -

                    $Q$ 次询问区间 $[l,r]$ 的

                    -

                    $1 \le n \le 10^5,~1 \le p,a_i \le 10^9,~1\le Q \le 10^5$

                    +

                    题意:给定长度为 \(n\) 的序列 \(a_i\) 和模数 \(p\)

                    +

                    \(Q\) 次询问区间 \([l,r]\)\[ +a_l^{ {a_{l+1}^{ {a_{l+2}^{ ^{ {.}^{ {.}^{ {.}^{ {a_{r} } } } } } } } } +} } +\] \(1 \le n \le 10^5,~1 \le p,a_i \le +10^9,~1\le Q \le 10^5\)

                    显然扩展欧拉定理的应用

                    -

                    关于 $O(\varphi^*(n)=1)\approx O(\log n)$ 的证明,可以看这里 link

                    -

                    注:这里的 $O(\varphi^*(n)=1)$ 指最小的 $x$ 使得 $\varphi^{x}(n)=1$

                    +

                    关于 \(O(\varphi^*(n)=1)\approx O(\log +n)\) 的证明,可以看这里 link

                    +

                    注:这里的 \(O(\varphi^*(n)=1)\) +指最小的 \(x\) 使得 \(\varphi^{x}(n)=1\)

                    因此对于每个询问,我们直接从左往右跑就好了,递归实现

                    注意快速幂要满足扩欧的性质哦,不要乱模

                    -

                    时间复杂度 $O(Q \log \max\{a_i\})$

                    +

                    时间复杂度 \(O(Q \log +\max\{a_i\})\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -939,7 +952,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/28/ju-ti-shu-xue-2.3-he-shi-de-chu-li/index.html b/2022/07/28/ju-ti-shu-xue-2.3-he-shi-de-chu-li/index.html index 0e49fafd4c..2ef1f271a0 100644 --- a/2022/07/28/ju-ti-shu-xue-2.3-he-shi-de-chu-li/index.html +++ b/2022/07/28/ju-ti-shu-xue-2.3-he-shi-de-chu-li/index.html @@ -468,43 +468,56 @@

                    《具体数学》 2.3和式的
                    -

                    2.3和式的处理

                    设 $K$ 为任意一个有限整数集合,则有

                    -

                    扰动法求解未知和式的封闭形式

                    -

                    然后通过将它的最后一项和第一项分离出来,用两种方法重新改写 $S_{n+1}$

                    -

                    -

                    然后尝试用 $S_n$ 将它表示出来

                    -

                    例子:求一般的几何级数的和

                    -

                    由 $(1)$ 中一般的扰动法格式可得

                    -

                    显然有

                    -

                    -

                    练习:求下列和式的封闭形式

                    -

                    答案:

                    -
                    +S_{n}+a_{n+1} = \sum_{0 \le k\le n+1}a_k &= a_0+\sum_{1\le k \le +n+1} a_k +\\\\&=a_0+\sum_{1 \le k+1 \le n+1}a_{k+1} +\\\\&=a_0+\sum_{0\le k\le n}a_{k+1} +\end{aligned} +\] 即 \[ +S_n+a_{n+1}=a_0+\sum_{0\le k\le n}a_{k+1}\tag{1} +\] 然后尝试用 \(S_n\) +将它表示出来

                    +

                    例子:求一般的几何级数的和 \[ +S_n = \sum_{0\le k\le n}ax^k +\]\((1)\) +中一般的扰动法格式可得 \[ +S_n + ax^{n+1} = ax^{0}+\sum_{0\le k\le n}ax^{k+1} +\] 显然有 \[ +S_n+ax^{n+1}=ax^0+xS_n +\]\[ +S_n=\dfrac{a-ax^{n+1}}{1-x} +\] 练习:求下列和式的封闭形式 \[ +S_n = \sum_{0 \le k \le n}k2^k +\] 答案: \[ +S_n=(n-1)2^{n+1}+2 +\]

                    +

                    过程

                    -
                    -
                    +\\\\S_n = (n-1)2^{n+1}+2 +\]

                    +
                    +
                    @@ -866,7 +879,7 @@

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/28/luo-gu-p2424-yue-shu-he-ti-jie/index.html b/2022/07/28/luo-gu-p2424-yue-shu-he-ti-jie/index.html index 8c384fa6df..163143e9bf 100644 --- a/2022/07/28/luo-gu-p2424-yue-shu-he-ti-jie/index.html +++ b/2022/07/28/luo-gu-p2424-yue-shu-he-ti-jie/index.html @@ -472,27 +472,47 @@

                    洛谷P2424 约数和 题解

                    -

                    洛谷P2424 约数和 题解

                    题目链接:P2424 约数和

                    +

                    洛谷P2424 约数和 题解

                    +

                    题目链接:P2424 +约数和

                    题意

                    -

                    对于一个数 $X$,函数 $f(X)$ 表示 $X$ 所有约数的和。例如:$f(6)=1+2+3+6=12$。对于一个 $X$,Smart 可以很快的算出 $f(X)$。现在的问题是,给定两个正整数 $X,Y(X<Y)$,Smart 希望尽快地算出 $f(X)+f(X+1)+\dots +f(Y)$的值,你能帮助 Smart 算出这个值吗?

                    -

                    对于 $100\%$ 的数据有 $1\leq X<Y\leq 2\times 10^9$。

                    +

                    对于一个数 \(X\),函数 \(f(X)\) 表示 \(X\) 所有约数的和。例如:\(f(6)=1+2+3+6=12\)。对于一个 \(X\),Smart 可以很快的算出 \(f(X)\)。现在的问题是,给定两个正整数 \(X,Y(X<Y)\),Smart 希望尽快地算出 \(f(X)+f(X+1)+\dots +f(Y)\)的值,你能帮助 +Smart 算出这个值吗?

                    +

                    对于 \(100\%\) 的数据有 \(1\leq X<Y\leq 2\times 10^9\)

                    -

                    设 $f(n) = \sum_{i=1}^{n} g(i)$

                    -

                    其中 $g(i)$ 表示 $i$ 的约数个数和

                    -

                    变换枚举顺序

                    -

                    不难发现,$\sum_{i=1}^{n} [j \mid i]$ 其实就是求 $1$ 到 $n$ 中包含约束 $j$ 的数的数量,则

                    -

                    也就是

                    -

                    然后就是数论分块板子了 awa,答案就是 $f(r)-f(l-1)$

                    -

                    这里前缀和是 $\sum_{i=l}^{r} = \dfrac{1}{2}(l+r)\times(r-l+1)$

                    +f(n) &= \sum_{i=1}^{n} \sum_{j \mid i}^{n} j +\end{aligned} +\] 变换枚举顺序 \[ +\sum_{j=1}^{n}j\sum_{i=1}^{n} [j \mid i] +\] 不难发现,\(\sum_{i=1}^{n} [j \mid +i]\) 其实就是求 \(1\)\(n\) 中包含约束 \(j\) 的数的数量,则 \[ +\sum_{j=1}^{n} j \left\lfloor\frac{n}{j}\right\rfloor +\] 也就是 \[ +f(n) = \sum_{i=1}^{n} i \left\lfloor\frac{n}{i}\right\rfloor +\] 然后就是数论分块板子了 awa,答案就是 \(f(r)-f(l-1)\)

                    +

                    这里前缀和是 \(\sum_{i=l}^{r} = +\dfrac{1}{2}(l+r)\times(r-l+1)\)

                    有没有一种可能,我居然差点忘了这个前缀和是怎么算的

                    -

                    时间复杂度 $O(\sqrt{n})$

                    +

                    时间复杂度 \(O(\sqrt{n})\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -895,7 +915,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/28/mo-ni-sai-ti-jiang-jie-7/index.html b/2022/07/28/mo-ni-sai-ti-jiang-jie-7/index.html index eeca2d4f3c..231e633f19 100644 --- a/2022/07/28/mo-ni-sai-ti-jiang-jie-7/index.html +++ b/2022/07/28/mo-ni-sai-ti-jiang-jie-7/index.html @@ -472,13 +472,28 @@

                    模拟赛题讲解[7]

                    -

                    模拟赛题讲解[7]

                    来自 Roundgod 2022-07-27 noi.ac #2682

                    +

                    模拟赛题讲解[7]

                    +

                    来自 Roundgod +2022-07-27 noi.ac #2682

                    题目描述

                    -

                    给定一个有向图 $G=(V,E)$ ,其中顶点个数 $\vert V\vert=n$ ,边数 $\vert E\vert=m$ 。顶点编号为 $1-n$ ,边的编号为 $1-m$ ,第 $i$ 条边连接顶点 $a_i$ 和 $b_i$。

                    -

                    你最少需要添加多少条有向边,才能使得能够从 $1$ 到达所有顶点?

                    +

                    给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\) +,边数 \(\vert E\vert=m\) 。顶点编号为 +\(1-n\) ,边的编号为 \(1-m\) ,第 \(i\) 条边连接顶点 \(a_i\)\(b_i\)

                    +

                    你最少需要添加多少条有向边,才能使得能够从 \(1\) 到达所有顶点?

                    输入格式

                    -

                    输入第一行包含 $2$个整数 $n,m$,分别表示图的顶点数和图的边数。

                    -

                    接下来 $m$行,每行包含两个整数 $a_i,b_i(1\leq a_i,b_i\leq n,a_i\neq b_i)$ ,表示第 $i$ 条边从顶点 $a_i$ 指向顶点 $b_i$。

                    +

                    输入第一行包含 \(2\)个整数 \(n,m\),分别表示图的顶点数和图的边数。

                    +

                    接下来 \(m\)行,每行包含两个整数 +\(a_i,b_i(1\leq a_i,b_i\leq n,a_i\neq +b_i)\) ,表示第 \(i\) 条边从顶点 +\(a_i\) 指向顶点 \(b_i\)

                    输入保证图中不存在重边和自环。

                    输出格式

                    在一行中输出一个整数,表示最少需要添加的有向边条数。

                    @@ -504,14 +519,20 @@

                    输出2

                    1

                    数据范围

                    -

                    对于 $50\%$ 的数据,$1\leq n\leq 5000,~1\leq m\leq 5000$

                    -

                    对于 $100\%$ 的数据,$1\leq n\leq 2\times 10^5,~1\leq m\leq 2\times 10^5$

                    +

                    对于 \(50\%\) 的数据,\(1\leq n\leq 5000,~1\leq m\leq 5000\)

                    +

                    对于 \(100\%\) 的数据,\(1\leq n\leq 2\times 10^5,~1\leq m\leq 2\times +10^5\)

                    题解

                    先强连通分量缩点一下,正确性显然。

                    -

                    然后对于 $1$ 本来就能到的结点,根本不用管

                    -

                    对于 $1$ 不能到的结点,我们只要连入度为 $0$ 的那些就行了

                    +

                    然后对于 \(1\) +本来就能到的结点,根本不用管

                    +

                    对于 \(1\) +不能到的结点,我们只要连入度为 \(0\) +的那些就行了

                    因为连了那些点,其他点都可以走到

                    -

                    时间复杂度 $O(n+m)$

                    +

                    时间复杂度 \(O(n+m)\)

                    我的考场代码:

                    #include <iostream>
                     #include <string>
                    @@ -956,7 +977,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/28/mo-ni-sai-ti-jiang-jie-8/index.html b/2022/07/28/mo-ni-sai-ti-jiang-jie-8/index.html index 4e0b2a3f0a..7158ce2fb3 100644 --- a/2022/07/28/mo-ni-sai-ti-jiang-jie-8/index.html +++ b/2022/07/28/mo-ni-sai-ti-jiang-jie-8/index.html @@ -472,20 +472,46 @@

                    模拟赛题讲解[8]

                    -

                    模拟赛题讲解[8]

                    来自 Roundgod 2022-07-27 noi.ac #2681

                    +

                    模拟赛题讲解[8]

                    +

                    来自 Roundgod +2022-07-27 noi.ac #2681

                    题目描述

                    -

                    给定一个有向图 $G=(V,E)$ ,其中顶点个数 $\vert V\vert=n$, 边数 $\vert E\vert=m$。顶点编号为 $1-n$ ,边的编号为 $1-m$ ,第 $i$ 条边连接顶点 $a_i$ 和 $b_i$ 。

                    -

                    对于图中的每个顶点 $v$ ,你需要判断它属于以下哪种:

                    -

                    如果不存在从 $1$ 到 $v$ 的路径,输出 $0$ 。

                    -

                    如果存在恰好一条从 $1$ 到 $v$ 的路径,输出 $1$ 。

                    -

                    如果存在多于一条且有限的从 $1$ 到 $v$ 的路径,输出 $2$ 。

                    -

                    如果存在无限条从 $1$ 到 $v$ 的路径,输出 $3$ 。

                    +

                    给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\), 边数 +\(\vert E\vert=m\)。顶点编号为 \(1-n\) ,边的编号为 \(1-m\) ,第 \(i\) 条边连接顶点 \(a_i\)\(b_i\)

                    +

                    对于图中的每个顶点 \(v\) +,你需要判断它属于以下哪种:

                    +

                    如果不存在从 \(1\)\(v\) 的路径,输出 \(0\)

                    +

                    如果存在恰好一条从 \(1\)\(v\) 的路径,输出 \(1\)

                    +

                    如果存在多于一条且有限的从 \(1\) 到 +\(v\) 的路径,输出 \(2\)

                    +

                    如果存在无限条从 \(1\)\(v\) 的路径,输出 \(3\)

                    输入格式

                    -

                    输入第一行包含 $2$ 个整数 $n,m$,分别表示图的顶点数和图的边数。

                    -

                    接下来 $m$行,每行包含两个整数 $a_i,b_i(1\leq a_i,b_i\leq n,a_i\neq b_i)$ ,表示第 $i$ 条边从顶点 $a_i$ 指向顶点 $b_i$ 。

                    +

                    输入第一行包含 \(2\) 个整数 \(n,m\),分别表示图的顶点数和图的边数。

                    +

                    接下来 \(m\)行,每行包含两个整数 +\(a_i,b_i(1\leq a_i,b_i\leq n,a_i\neq +b_i)\) ,表示第 \(i\) 条边从顶点 +\(a_i\) 指向顶点 \(b_i\)

                    题目保证图中不包含任何重边和自环。

                    输出格式

                    -

                    在一行中输出 $n$ 个在 $\{0,1,2,3\}$ 中的数字,第 $i$ 个表示顶点 $i$ 属于的种类。

                    +

                    在一行中输出 \(n\) 个在 \(\{0,1,2,3\}\) 中的数字,第 \(i\) 个表示顶点 \(i\) 属于的种类。

                    输入1

                    3 3
                     1 2
                    @@ -506,16 +532,24 @@ 

                    输出3

                    1 1 2 1

                    数据范围

                    -

                    对于 $50\%$ 的数据,$1\leq n\leq 10^5,~1\leq m\leq 5\times 10^5$ ,并且保证图中不存在环。

                    -

                    对于 $100\%$ 的数据,$1\leq n\leq 10^5,~1\leq m\leq 5\times 10^5$。

                    +

                    对于 \(50\%\) 的数据,\(1\leq n\leq 10^5,~1\leq m\leq 5\times +10^5\) ,并且保证图中不存在环。

                    +

                    对于 \(100\%\) 的数据,\(1\leq n\leq 10^5,~1\leq m\leq 5\times +10^5\)

                    题解

                    注意是路径,不是简单路径!

                    不难想到,先强连通分量缩点一下

                    -

                    如果存在一条 $1$ 到 $v$ 的路径包含了一个强连通分量大小超过 $1$ 的结点

                    +

                    如果存在一条 \(1\)\(v\) 的路径包含了一个强连通分量大小超过 +\(1\) 的结点

                    那么他们的路径就有无数条

                    -

                    其他么简单 $\text{topo}$ 一下就好了,或者 $\text{bfs}$ 估计也是可以的

                    +

                    其他么简单 \(\text{topo}\) +一下就好了,或者 \(\text{bfs}\) +估计也是可以的

                    然后q779又写挂了23333

                    -

                    时间复杂度 $O(n+m)$

                    +

                    时间复杂度 \(O(n+m)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -977,7 +1011,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/28/shu-xue-chang-shu-biao/index.html b/2022/07/28/shu-xue-chang-shu-biao/index.html index 38dc0c95c9..677793894b 100644 --- a/2022/07/28/shu-xue-chang-shu-biao/index.html +++ b/2022/07/28/shu-xue-chang-shu-biao/index.html @@ -468,122 +468,120 @@

                    数学常数表

                    -

                    数学常数表

                    常数表

                    -

                    $e = \tt{2.718281828}$

                    -

                    $\pi = \tt{3.141592654}$

                    -

                    $\ln \pi = \tt{1.1447}$

                    -

                    $\sqrt{\pi} = \tt{1.7725}$

                    -
                    +

                    数学常数表

                    +

                    常数表

                    +

                    \(e = \tt{2.718281828}\)

                    +

                    \(\pi = \tt{3.141592654}\)

                    +

                    \(\ln \pi = \tt{1.1447}\)

                    +

                    \(\sqrt{\pi} = \tt{1.7725}\)

                    - + - - + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + +
                    自然对数表$N$$\ln N$\(N\)\(\ln N\)
                    $\ln2 = \tt{0.69}$$\tt{2}$$\tt{0.6931}$
                    \(\ln2 = \tt{0.69}\)\(\tt{2}\)\(\tt{0.6931}\)
                    $\ln3 = \tt{1.10}$$\tt{3}$$\tt{1.0986}$
                    \(\ln3 = \tt{1.10}\)\(\tt{3}\)\(\tt{1.0986}\)
                    $\ln5 = \tt{1.61}$$\tt{5}$$\tt{1.6094}$
                    \(\ln5 = \tt{1.61}\)\(\tt{5}\)\(\tt{1.6094}\)
                    $\ln7 = \tt{1.95}$$\tt{7}$$\tt{1.9459}$
                    \(\ln7 = \tt{1.95}\)\(\tt{7}\)\(\tt{1.9459}\)
                    $\ln11 = \tt{2.40}$$\tt{11}$$\tt{2.3979}$
                    \(\ln11 = \tt{2.40}\)\(\tt{11}\)\(\tt{2.3979}\)
                    $\ln13 = \tt{2.56}$$\tt{13}$$\tt{2.5649}$
                    \(\ln13 = \tt{2.56}\)\(\tt{13}\)\(\tt{2.5649}\)
                    $\ln17 = \tt{2.83}$$\tt{17}$$\tt{2.8332}$
                    \(\ln17 = \tt{2.83}\)\(\tt{17}\)\(\tt{2.8332}\)
                    $\ln19 = \tt{2.94}$$\tt{19}$$\tt{2.9444}$
                    \(\ln19 = \tt{2.94}\)\(\tt{19}\)\(\tt{2.9444}\)
                    $\ln23 = \tt{3.14}$$\tt{23}$$\tt{3.1355}$
                    \(\ln23 = \tt{3.14}\)\(\tt{23}\)\(\tt{3.1355}\)
                    $\ln29 = \tt{3.37}$$\tt{29}$$\tt{3.3673}$
                    \(\ln29 = \tt{3.37}\)\(\tt{29}\)\(\tt{3.3673}\)
                    $\ln31 = \tt{3.43}$$\tt{31}$$\tt{3.4340}$
                    \(\ln31 = \tt{3.43}\)\(\tt{31}\)\(\tt{3.4340}\)
                    -
                    -
                    - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + +
                    $\exp$ 表$N$$\exp N$
                    \(\exp\)\(N\)\(\exp N\)
                    $\exp{2} = \tt{7.39}$$\tt{2}$$\tt{7.3891}$
                    \(\exp{2} = \tt{7.39}\)\(\tt{2}\)\(\tt{7.3891}\)
                    $\exp{3} = \tt{20.09}$$\tt{3}$$\tt{20.0855}$
                    \(\exp{3} = \tt{20.09}\)\(\tt{3}\)\(\tt{20.0855}\)
                    $\exp{5} = \tt{148.41}$$\tt{5}$$\tt{148.4132}$
                    \(\exp{5} = \tt{148.41}\)\(\tt{5}\)\(\tt{148.4132}\)
                    $\exp{7} = \tt{1096.63}$$\tt{7}$$\tt{}$$\tt{1096.6332}$
                    \(\exp{7} = \tt{1096.63}\)\(\tt{7}\)\(\tt{}\)\(\tt{1096.6332}\)
                    $\exp{11} = \tt{59874.14}$$\tt{11}$$\tt{59874.1417}$
                    \(\exp{11} = \tt{59874.14}\)\(\tt{11}\)\(\tt{59874.1417}\)
                    $\exp{13} = \tt{442413.39}$$\tt{13}$$\tt{442413.3920}$
                    \(\exp{13} = \tt{442413.39}\)\(\tt{13}\)\(\tt{442413.3920}\)
                    -
                    @@ -945,7 +943,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/29/abc167e-colorful-blocks-ti-jie/index.html b/2022/07/29/abc167e-colorful-blocks-ti-jie/index.html index 962d7e697b..976409094d 100644 --- a/2022/07/29/abc167e-colorful-blocks-ti-jie/index.html +++ b/2022/07/29/abc167e-colorful-blocks-ti-jie/index.html @@ -472,26 +472,44 @@

                    ABC167E Colorful Blocks 题解
                    -

                    ABC167E Colorful Blocks 题解

                    题目链接:ABC167E Colorful Blocks

                    +

                    ABC167E Colorful Blocks 题解

                    +

                    题目链接:ABC167E +Colorful Blocks

                    题意

                    翻译从我们模拟赛搬过来的,稍微有些不一样。

                    -

                    有 $n$ 个小球按照编号为 $1−n$ 从左至右放成一排,你现在要给每个小球染上 $1−m$ 中的颜色,并且满足至多存在 $k$ 对相邻位置上小球的颜色相同

                    -

                    你需要输出一共有多少种不同的给小球染色的方案,两种染色方案被认为是不同的当且仅当两种方案中存在一个小球被染上了不同的颜色。由于答案可能过大,你需要对 $998244353$ 取模后输出。

                    -

                    $1 \le n,m \le 2\times 10^5,~0\le 0 \le n-1$

                    +

                    \(n\) 个小球按照编号为 \(1−n\) +从左至右放成一排,你现在要给每个小球染上 \(1−m\) 中的颜色,并且满足至多存在 +\(k\) +对相邻位置上小球的颜色相同

                    +

                    你需要输出一共有多少种不同的给小球染色的方案,两种染色方案被认为是不同的当且仅当两种方案中存在一个小球被染上了不同的颜色。由于答案可能过大,你需要对 +\(998244353\) 取模后输出。

                    +

                    \(1 \le n,m \le 2\times 10^5,~0\le 0 \le +n-1\)

                    -

                    简单组合数学。考虑 $k$ 对相邻的情况,

                    -

                    其实就是在前 $n-1$ 个球中选 $k$ 个和它右边的合并

                    -

                    因此答案就是

                    -

                    前面的 $m \times(m-1)^{n-i-1}$ 可能比较难理解

                    +

                    简单组合数学。考虑 \(k\) +对相邻的情况,

                    +

                    其实就是在前 \(n-1\) 个球中选 \(k\) 个和它右边的合并

                    +

                    因此答案就是 \[ +\sum_{i=0}^{k} m \times(m-1)^{n-i-1}\times \dbinom{n-1}{i} +\] 前面的 \(m +\times(m-1)^{n-i-1}\) 可能比较难理解

                    其实就是一个经典模型:

                    -

                    $n$ 个方格,$m$ 种颜色,相邻不能涂同一种颜色,方案数?

                    -

                    则第一个可以涂 $m$ 种颜色中的任意一种颜色,

                    -

                    剩下的每个都只能涂 $m-1$ 种颜色

                    -

                    故这个模型的答案就是 $m \times (m-1)^{n-1}$

                    +

                    \(n\) 个方格,\(m\) +种颜色,相邻不能涂同一种颜色,方案数?

                    +

                    则第一个可以涂 \(m\) +种颜色中的任意一种颜色,

                    +

                    剩下的每个都只能涂 \(m-1\) +种颜色

                    +

                    故这个模型的答案就是 \(m \times +(m-1)^{n-1}\)

                    然后就可以理解这个柿子了吧

                    -

                    时间复杂度 $O(n \log N)$

                    +

                    时间复杂度 \(O(n \log N)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -907,7 +925,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/29/abc210e-ring-mst-ti-jie/index.html b/2022/07/29/abc210e-ring-mst-ti-jie/index.html index 844599dd78..6fc90b05db 100644 --- a/2022/07/29/abc210e-ring-mst-ti-jie/index.html +++ b/2022/07/29/abc210e-ring-mst-ti-jie/index.html @@ -476,38 +476,80 @@

                    ABC210E Ring MST 题解

                    -

                    ABC210E Ring MST 题解

                    题目链接:ABC210E Ring MST

                    +

                    ABC210E Ring MST 题解

                    +

                    题目链接:ABC210E Ring +MST

                    -

                    题意:给定一张 $n$ 个点的无向图,顶点的编号为 $0,1,\dots,n−1$ 。同时给出两个长度为 $m$ 的数组 $a_1,a_2,\dots,a_m$ 和 $b_1,b_2,\dots,b_m$ 。初始时图中并没有任何边,你可以按照以下操作加边:

                    -

                    选择一个 $1\le i\le m$ 和一个 $0<x<n$ ,并在顶点 $x$ 和顶点 $(x+a_i) \bmod n$ 中添加一条长度为 $b_i$ 的边。

                    -

                    你现在想要知道,你添加的边的长度总和至少为多少,才能使得整个图连通?如果无论如何都不能使整个图连通,输出 $−1$ 。

                    +

                    题意:给定一张 \(n\) 个点的无向图,顶点的编号为 \(0,1,\dots,n−1\) 。同时给出两个长度为 \(m\) 的数组 \(a_1,a_2,\dots,a_m\)\(b_1,b_2,\dots,b_m\) +。初始时图中并没有任何边,你可以按照以下操作加边:

                    +

                    选择一个 \(1\le i\le m\) 和一个 +\(0<x<n\) ,并在顶点 \(x\) 和顶点 \((x+a_i) \bmod n\) 中添加一条长度为 \(b_i\) 的边。

                    +

                    你现在想要知道,你添加的边的长度总和至少为多少,才能使得整个图连通?如果无论如何都不能使整个图连通,输出 +\(−1\)

                    数据范围:

                    -

                    $1 \le a_i < n \le 10^9,~1 \le m \le 10^5,1\le b_i \le 10^9$

                    +

                    \(1 \le a_i < n \le 10^9,~1 \le m \le +10^5,1\le b_i \le 10^9\)

                    -

                    首先我们可以想到把 $(x,(x+a_i)\bmod n)$ 看作一条边

                    -

                    然后答案就是最小生成树,跑个 $\text{kruskal}$

                    -

                    不过这个是 $O(nm)$ 的,这是不能接受的

                    -

                    于是我们模拟 $\text{kruskal}$ 的算法过程,不过更加快捷

                    -

                    把 $(a_i,b_i)$ 按 $b_i$ 排序升序排序,保证有 $\forall i < j,~b_i\le b_j$

                    -

                    然后考虑尽可能的用较前的 $(a_i,b_i)$ 来建边

                    -

                    有一个结论:令 $g=\gcd(a_i,n)$ ,

                    -

                    所有模 $g$ 相同的结点均可以连到同一个连通块,并且会有 $g$ 个这样的连通块

                    -

                    注:此时我们尽可能在用 $a_i$ 建边,这里连到同一个连通块就是指尽可能建边的结果

                    +

                    首先我们可以想到把 \((x,(x+a_i)\bmod +n)\) 看作一条边

                    +

                    然后答案就是最小生成树,跑个 \(\text{kruskal}\)

                    +

                    不过这个是 \(O(nm)\) +的,这是不能接受的

                    +

                    于是我们模拟 \(\text{kruskal}\) +的算法过程,不过更加快捷

                    +

                    \((a_i,b_i)\)\(b_i\) 排序升序排序,保证有 \(\forall i < j,~b_i\le b_j\)

                    +

                    然后考虑尽可能的用较前的 \((a_i,b_i)\) 来建边

                    +

                    有一个结论:令 \(g=\gcd(a_i,n)\)

                    +

                    所有模 \(g\) +相同的结点均可以连到同一个连通块,并且会有 \(g\) 个这样的连通块

                    +

                    注:此时我们尽可能在用 \(a_i\) +建边,这里连到同一个连通块就是指尽可能建边的结果

                    这个结论其实证起来很简单

                    -

                    方便起见,我们固定 $0$ 为起点,显然这不会影响答案

                    -

                    则只用 $a_i$ 从 $0$ 可以走到的点一定是

                    -

                    其中 $x,y \in \mathbb{Z}$ 。

                    -

                    令 $c=a_ix+ny$ ,根据裴蜀定理可知,如果有解,则 $g \mid c$

                    -

                    注意我们这里是 $c$ ,实际上我们还会从 $0,1,\dots,g-1$ 出发去连边

                    -

                    不难发现这样我们会连出 $g$ 个这样的连通块

                    +

                    方便起见,我们固定 \(0\) +为起点,显然这不会影响答案

                    +

                    则只用 \(a_i\)\(0\) 可以走到的点一定是 \[ +(a_ix+ny) \bmod n +\] 其中 \(x,y \in \mathbb{Z}\) +。

                    +

                    \(c=a_ix+ny\) +,根据裴蜀定理可知,如果有解,则 \(g \mid +c\)

                    +

                    注意我们这里是 \(c\) +,实际上我们还会从 \(0,1,\dots,g-1\) +出发去连边

                    +

                    不难发现这样我们会连出 \(g\) +个这样的连通块

                    然后我们试着去推广这个结论

                    -

                    如果我们用前 $i$ 个 $a_i$ ,则他们一共有 $g=\gcd(a_1,a_2,\dots,a_i,n)$ 个连通块

                    +

                    如果我们用前 \(i\)\(a_i\) ,则他们一共有 \(g=\gcd(a_1,a_2,\dots,a_i,n)\) 个连通块

                    详细证明我也不太会,但是这个就是个很显然的结论

                    -

                    不难发现,当 $g=1$ 时,图就联通了。

                    -

                    然后这题就没了。边权只要用 $\sum(g_{i-1}-g_{i}) \times b_{i}$ 来算就行啦

                    -

                    这题我在Roundgod老师的指导下想了一个小时左右才想明白 QAQ

                    -

                    时间复杂度 $O(m\log m)$

                    +

                    不难发现,当 \(g=1\) +时,图就联通了。

                    +

                    然后这题就没了。边权只要用 \(\sum(g_{i-1}-g_{i}) \times b_{i}\) +来算就行啦

                    +

                    这题我在Roundgod老师的指导下想了一个小时左右才想明白 +QAQ

                    +

                    时间复杂度 \(O(m\log m)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -927,7 +969,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/29/arc060b-digit-sum-ti-jie/index.html b/2022/07/29/arc060b-digit-sum-ti-jie/index.html index 40b56ae5f8..55166d4d9f 100644 --- a/2022/07/29/arc060b-digit-sum-ti-jie/index.html +++ b/2022/07/29/arc060b-digit-sum-ti-jie/index.html @@ -472,29 +472,62 @@

                    ARC060B Digit Sum 题解

                    -

                    ARC060B Digit Sum 题解

                    题目链接:ARC060B Digit Sum

                    +

                    ARC060B Digit Sum 题解

                    +

                    题目链接:ARC060B Digit +Sum

                    题意

                    -

                    对于任意非负整数 $x$ 和 $m(2\le m)$ ,定义 $f_m(x)$ 为 $x$ 在 $m$ 进制下的各位数字之和。

                    -

                    例如$f_{10}(87654)=8+7+6+5+4=30,~f_{100}(87654)=8+76+54=138$ 。给定两个整数 $x$ 和 $n$ 。

                    -

                    你需要计算是否存在某个 $m\ge 2$ 使得 $f_m(x)=n$ 。

                    -

                    如果存在,输出最小的满足条件的 $m$ ;如果不存在,输出 $−1$ 。

                    -

                    数据范围:

                    -

                    $ 1 \le x,n \le 10^{11}$

                    +

                    对于任意非负整数 \(x\)\(m(2\le m)\) ,定义 \(f_m(x)\)\(x\)\(m\) 进制下的各位数字之和。

                    +

                    例如\(f_{10}(87654)=8+7+6+5+4=30,~f_{100}(87654)=8+76+54=138\) +。给定两个整数 \(x\)\(n\)

                    +

                    你需要计算是否存在某个 \(m\ge 2\) +使得 \(f_m(x)=n\)

                    +

                    如果存在,输出最小的满足条件的 \(m\) +;如果不存在,输出 \(−1\)

                    +

                    数据范围:

                    +

                    $ 1 x,n ^{11}$

                    -

                    设 $x$ 化为 $m$ 进制后为 $\overline{x_1x_2\dots x_t}$

                    -

                    不难发现当 $t \ge 3$ 时, $m\le \sqrt{x}-\epsilon,\epsilon \in \mathbb{N}$

                    -

                    例如当 $t=3$ 时,设 $x$ 化为 $m$ 进制后为 $\overline{abc}$ ,则

                    -

                    显然有 $m\le \sqrt{x}-\epsilon,\epsilon \in \mathbb{N}$

                    -

                    因此我们暴力枚举所有 $m\le \sqrt{x}-\epsilon,\epsilon \in \mathbb{N}$ ,对应 $t\ge 3$ 的情况

                    -

                    接下来 $m > \sqrt{x}-\epsilon,\epsilon \in \mathbb{N}$ 的情况,直接枚举 $m$ 肯定是不行的

                    -

                    注意到此时 $x$ 满足

                    -

                    因为有 $m > \sqrt{x}-\epsilon,\epsilon \in \mathbb{N}$ ,因此 $a \le \sqrt{x}-\epsilon^{\prime},\epsilon^{\prime} \in \mathbb{N}$

                    -

                    于是我们只要枚举这个 $a$ ,然后判断此时的 $m=\frac{x-(n-a)}{a}$ 是否合法即可

                    -

                    这里的合法指 $x$ 在当前 $m$ 进制下是 $am+b$

                    -

                    时间复杂度 $O(\sqrt{x})$

                    +

                    \(x\) 化为 \(m\) 进制后为 \(\overline{x_1x_2\dots x_t}\)

                    +

                    不难发现当 \(t \ge 3\) 时, \(m\le \sqrt{x}-\epsilon,\epsilon \in +\mathbb{N}\)

                    +

                    例如当 \(t=3\) 时,设 \(x\) 化为 \(m\) 进制后为 \(\overline{abc}\) ,则 \[ +x=am^2+bm+c +\] 显然有 \(m\le +\sqrt{x}-\epsilon,\epsilon \in \mathbb{N}\)

                    +

                    因此我们暴力枚举所有 \(m\le +\sqrt{x}-\epsilon,\epsilon \in \mathbb{N}\) ,对应 \(t\ge 3\) 的情况

                    +

                    接下来 \(m > \sqrt{x}-\epsilon,\epsilon +\in \mathbb{N}\) 的情况,直接枚举 \(m\) 肯定是不行的

                    +

                    注意到此时 \(x\) 满足 \[ +x=am+b +\] 因为有 \(m > +\sqrt{x}-\epsilon,\epsilon \in \mathbb{N}\) ,因此 \(a \le \sqrt{x}-\epsilon^{\prime},\epsilon^{\prime} +\in \mathbb{N}\)

                    +

                    于是我们只要枚举这个 \(a\) +,然后判断此时的 \(m=\frac{x-(n-a)}{a}\) 是否合法即可

                    +

                    这里的合法指 \(x\) 在当前 \(m\) 进制下是 \(am+b\)

                    +

                    时间复杂度 \(O(\sqrt{x})\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -902,7 +935,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/29/cf1000d-yet-another-problem-on-a-subsequence-ti-jie/index.html b/2022/07/29/cf1000d-yet-another-problem-on-a-subsequence-ti-jie/index.html index 7e265cf238..cf6cd4095c 100644 --- a/2022/07/29/cf1000d-yet-another-problem-on-a-subsequence-ti-jie/index.html +++ b/2022/07/29/cf1000d-yet-another-problem-on-a-subsequence-ti-jie/index.html @@ -476,20 +476,43 @@

                    CF1000D Yet Another Problem On a
                    -

                    CF1000D Yet Another Problem On a Subsequence 题解

                    题目链接:CF1000D Yet Another Problem On a Subsequence

                    +

                    CF1000D Yet +Another Problem On a Subsequence 题解

                    +

                    题目链接:CF1000D +Yet Another Problem On a Subsequence

                    题意

                    -

                    如果一个数组 $[a_1,a_2,a_3,…,a_n]a_1=n-1$ 并且 $a_1>0$ ,这个数组就被叫为好数组,如果一个序列能正好分为多个好数组,ta就被叫为好序列,现在给定一个序列,求这个序列有多少好子序列,答案对 $998244353$ 取模。

                    -

                    The sequence of integers $ a_1, a_2, \dots, a_k $ is called a good array if $ a_1 = k - 1 $ and $ a_1 > 0 $ . For example, the sequences $ [3, -1, 44, 0], [1, -99] $ are good arrays, and the sequences $ [3, 7, 8], [2, 5, 4, 1], [0] $ — are not.

                    -

                    A sequence of integers is called good if it can be divided into a positive number of good arrays. Each good array should be a subsegment of sequence and each element of the sequence should belong to exactly one array. For example, the sequences $ [2, -3, 0, 1, 4] $ , $ [1, 2, 3, -3, -9, 4] $ are good, and the sequences $ [2, -3, 0, 1] $ , $ [1, 2, 3, -3 -9, 4, 1] $ — are not.

                    -

                    For a given sequence of numbers, count the number of its subsequences that are good sequences, and print the number of such subsequences modulo $998244353$.

                    +

                    如果一个数组 \([a_1,a_2,a_3,...,a_n]a_1=n-1\) 并且 \(a_1>0\) +,这个数组就被叫为好数组,如果一个序列能正好分为多个好数组,ta就被叫为好序列,现在给定一个序列,求这个序列有多少好子序列,答案对 +\(998244353\) 取模。

                    +

                    The sequence of integers $ a_1, a_2, , a_k $ is called a good array +if $ a_1 = k - 1 $ and $ a_1 > 0 $ . For example, the sequences $ [3, +-1, 44, 0], [1, -99] $ are good arrays, and the sequences $ [3, 7, 8], +[2, 5, 4, 1], [0] $ — are not.

                    +

                    A sequence of integers is called good if it can be divided into a +positive number of good arrays. Each good array should be a subsegment +of sequence and each element of the sequence should belong to exactly +one array. For example, the sequences $ [2, -3, 0, 1, 4] $ , $ [1, 2, 3, +-3, -9, 4] $ are good, and the sequences $ [2, -3, 0, 1] $ , $ [1, 2, 3, +-3 -9, 4, 1] $ — are not.

                    +

                    For a given sequence of numbers, count the number of its subsequences +that are good sequences, and print the number of such subsequences +modulo \(998244353\).

                    -

                    设 $f_i$ 表示以 $i$ 结尾的 good sequences 的数量

                    -

                    当 $a_i\le 0$ 时,显然 $f_i=0$

                    -

                    当 $a_i >0$ 时,我们可以在 $[i+a_i+1,n]$ 之间找一个good sequences拼接

                    -

                    然后 $[i,j]$ 之间可以随便选 $a_i+1$ 个数,因此有

                    -

                    时间复杂度 $O(n^2)$

                    +

                    \(f_i\) 表示以 \(i\) 结尾的 good sequences 的数量

                    +

                    \(a_i\le 0\) 时,显然 \(f_i=0\)

                    +

                    \(a_i >0\) 时,我们可以在 +\([i+a_i+1,n]\) 之间找一个good +sequences拼接

                    +

                    然后 \([i,j]\) 之间可以随便选 \(a_i+1\) 个数,因此有 \[ +f_i = \sum f_j \times \dbinom{i-j}{a_i+1} +\] 时间复杂度 \(O(n^2)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -910,7 +933,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/29/hdu4135-co-prime-ti-jie/index.html b/2022/07/29/hdu4135-co-prime-ti-jie/index.html index fe58af1aa2..55f2db3bfc 100644 --- a/2022/07/29/hdu4135-co-prime-ti-jie/index.html +++ b/2022/07/29/hdu4135-co-prime-ti-jie/index.html @@ -472,25 +472,54 @@

                    hdu4135 Co-prime 题解

                    -

                    hdu4135 Co-prime 题解

                    题目链接:hdu4135 Co-prime

                    +

                    hdu4135 Co-prime 题解

                    +

                    题目链接:hdu4135 +Co-prime

                    题意

                    -

                    $T$ 组数据,每组给出 $a,b,n$ ,求区间 $[a,b]$ 中有多少个数与 $n$ 互质。

                    -

                    Given a number $N$, you are asked to count the number of integers between $A$ and $B$ inclusive which are relatively prime to $N$.
                    Two integers are said to be co-prime or relatively prime if they have no common positive divisors other than $1$ or, equivalently, if their greatest common divisor is $1$. The number $1$ is relatively prime to every integer.

                    -

                    $1 \le a\le b\le 10^{15},~1 \le n \le 10^9$

                    +

                    \(T\) 组数据,每组给出 \(a,b,n\) ,求区间 \([a,b]\) 中有多少个数与 \(n\) 互质。

                    +

                    Given a number \(N\), you are asked +to count the number of integers between \(A\) and \(B\) inclusive which are relatively prime to +\(N\). Two integers are said to be +co-prime or relatively prime if they have no common positive divisors +other than \(1\) or, equivalently, if +their greatest common divisor is \(1\). +The number \(1\) is relatively prime to +every integer.

                    +

                    \(1 \le a\le b\le 10^{15},~1 \le n \le +10^9\)

                    -

                    首先一个常见转化 $\text{ans} = f(b)-f(a-1)$

                    -

                    其中 $f(k)$ 表示 $[1,k]$ 中与 $n$ 互质的数的个数

                    -

                    注意到如果 $x$ 与 $n=\prod\limits_{1\le i \le t} p_i^{k_i}$ 互质,

                    -

                    则对于任意 $p_i$ ,均有 $p_i \nmid x$

                    +

                    首先一个常见转化 \(\text{ans} = +f(b)-f(a-1)\)

                    +

                    其中 \(f(k)\) 表示 \([1,k]\) 中与 \(n\) 互质的数的个数

                    +

                    注意到如果 \(x\)\(n=\prod\limits_{1\le i \le t} p_i^{k_i}\) +互质,

                    +

                    则对于任意 \(p_i\) ,均有 \(p_i \nmid x\)

                    直接去算并不好算,考虑容斥

                    下面会涉及一些定义,我放在了文章末尾

                    -

                    在这题里,“坏性质”就是与 $p_i$ 不互质

                    -

                    我们 $O(2^t)$ 枚举 $S$ (显然 $t \le \log n$ )

                    -

                    举个例子,假设 $S_i = \{1,2\}$ ,

                    -

                    则表示能放到 $N(S)$ 中的一定要有 $P_1,P_2$ 的性质

                    -

                    在这题里,就是能放到 $N(S)$ 里的 $x$ 满足 $p_1 \mid x \,\land \, p_2 \mid x$

                    -

                    时间复杂度为 $O(Q\times 2^{t})$

                    +

                    在这题里,“坏性质”就是与 \(p_i\) +不互质

                    +

                    我们 \(O(2^t)\) 枚举 \(S\) (显然 \(t +\le \log n\)

                    +

                    举个例子,假设 \(S_i = \{1,2\}\) +,

                    +

                    则表示能放到 \(N(S)\) 中的一定要有 +\(P_1,P_2\) 的性质

                    +

                    在这题里,就是能放到 \(N(S)\) 里的 +\(x\) 满足 \(p_1 \mid x \,\land \, p_2 \mid x\)

                    +

                    时间复杂度为 \(O(Q\times +2^{t})\)

                    代码采用比较好理解的位运算写法

                    #include <iostream>
                     #include <string>
                    @@ -547,19 +576,37 @@ 

                    } return 0; }

                    -
                    +

                    其实这一段是我打算放到总结里面的(2022.7.29现在还在咕咕咕)

                    不过我刚学容斥,所以这篇题解就是用来理清一下思路的 awa

                    -

                    下面的全是抄的Roundgod老师的PPT qwq

                    -

                    容斥原理(principle of inclusion-exclusion): 令 $X$ 为一个有限集合,$P_1, P_2,\dots , P_m$ 是一些性质的集合,对于任意 $S \subseteq [m]$ ,定义 $N(S) = \{x \in X \mid \forall i \in S : x \text{ has } P_i\}$ 。那么 $X$ 中不满足任何一个性质 $P_i$ 的元素个数

                    -

                    对于任意的 $S \subseteq [m]$ ,如果 $N(S)$ 只和 $S$ 的大小(即 $|S|$ )有关,那么式子可以化简为

                    -

                    其中 $N(i)$ 表示对于任意满足 $S \subseteq [m],|S|=i$ 的 $N(S)$ 值

                    +

                    下面的全是抄的Roundgod老师的PPT +qwq

                    +

                    容斥原理(principle of inclusion-exclusion): 令 \(X\) 为一个有限集合\(P_1, P_2,\dots , P_m\) +是一些性质的集合,对于任意 \(S \subseteq [m]\) ,定义 \(N(S) = \{x \in X \mid \forall i \in S : x \text{ +has } P_i\}\) 。那么 \(X\) +中不满足任何一个性质 \(P_i\) +的元素个数\[ +\sum_{S \subseteq [m]} (-1)^{|S|} \left|N(S)\right| +\] 对于任意的 \(S \subseteq +[m]\)如果 \(N(S)\) +只和 \(S\) 的大小(即 \(|S|\) )有关,那么式子可以化简为 +\[ +\sum_{i=0}^{m} (-1)^i \binom{m}{i} N(i) +\] 其中 \(N(i)\) +表示对于任意满足 \(S \subseteq +[m],|S|=i\)\(N(S)\)

                    容斥原理的常用技巧

                    -
                      -
                    1. 定义一些“坏性质” $P_1,\dots,P_m$
                    2. -
                    3. 找到对于每个 $S\subseteq [m]$ 计算 $N(S)$ 的方法
                    4. +
                        +
                      1. 定义一些“坏性质” \(P_1,\dots,P_m\)
                      2. +
                      3. 找到对于每个 \(S\subseteq [m]\) +计算 \(N(S)\) 的方法
                      4. 套用容斥原理计算不满足任何一个“坏性质”的元素个数
                      @@ -927,7 +974,7 @@

                       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/29/mo-ni-sai-ti-jiang-jie-9/index.html b/2022/07/29/mo-ni-sai-ti-jiang-jie-9/index.html index ea51921a0c..c668b59694 100644 --- a/2022/07/29/mo-ni-sai-ti-jiang-jie-9/index.html +++ b/2022/07/29/mo-ni-sai-ti-jiang-jie-9/index.html @@ -472,18 +472,34 @@

                      模拟赛题讲解[9]

                      -

                      模拟赛题讲解[9]

                      来自 Roundgod 2022-07-29 noi.ac #2693

                      -

                      原题来自 ABC172E NEQ

                      +

                      模拟赛题讲解[9]

                      +

                      来自 Roundgod +2022-07-29 noi.ac #2693

                      +

                      原题来自 ABC172E +NEQ

                      问题描述

                      -

                      给定 $n,m$,你需要计算满足以下条件的数组对 $A=[a_1,a_2,\dots,a_n]$ 和 $B=[b_1,b_2,\dots,b_n]$ 的个数:

                      -
                        -
                      1. 对于所有 $1\leq i\leq n$ ,都有 $1\leq a_i\leq m$ 以及 $1\leq b_i\leq m$.
                      2. -
                      3. 对于所有 $1\leq i\leq n$ ,都有 $a_i\neq b_i$.
                      4. -
                      5. 对于所有的 $1\leq i\lt j\leq n$ ,都有 $a_i\neq a_j$ 且 $b_i\neq b_j$.
                      6. +

                        给定 \(n,m\),你需要计算满足以下条件的数组对 \(A=[a_1,a_2,\dots,a_n]\)\(B=[b_1,b_2,\dots,b_n]\) 的个数:

                        +
                          +
                        1. 对于所有 \(1\leq i\leq n\) ,都有 +\(1\leq a_i\leq m\) 以及 \(1\leq b_i\leq m\).
                        2. +
                        3. 对于所有 \(1\leq i\leq n\) ,都有 +\(a_i\neq b_i\).
                        4. +
                        5. 对于所有的 \(1\leq i\lt j\leq n\) +,都有 \(a_i\neq a_j\)\(b_i\neq b_j\).
                        -

                        由于答案可能过大,你需要输出答案对 $10^9+7$取模后的值。

                        +

                        由于答案可能过大,你需要输出答案对 \(10^9+7\)取模后的值。

                        输入格式

                        -

                        输入第一行包含一个整数 $t(1\leq t\leq 10)$ ,表示数据的组数。 对于每组测试数据,输入为一行,包含两个整数 $n,m$。

                        +

                        输入第一行包含一个整数 \(t(1\leq t\leq +10)\) ,表示数据的组数。 +对于每组测试数据,输入为一行,包含两个整数 \(n,m\)

                        输出格式

                        对于每组测试数据,在一行中输出一个整数,表示答案。

                        输入1

                        @@ -499,24 +515,44 @@

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/29/oi-shu-xue-zong-jie-zu-he-shu-xue/index.html b/2022/07/29/oi-shu-xue-zong-jie-zu-he-shu-xue/index.html index 83128c7168..e736d25443 100644 --- a/2022/07/29/oi-shu-xue-zong-jie-zu-he-shu-xue/index.html +++ b/2022/07/29/oi-shu-xue-zong-jie-zu-he-shu-xue/index.html @@ -472,30 +472,52 @@

                    OI数学总结-组合数学

                    -

                    OI数学总结-组合数学

                    施工中,咕咕咕….

                    -

                    排列组合

                    更多详见 小蓝书 16.1

                    +

                    OI数学总结-组合数学

                    +

                    施工中,咕咕咕....

                    +

                    排列组合

                    +

                    更多详见 小蓝书 +16.1

                    下面两个指的是无重复的排列与组合

                    -

                    排列数

                    从 $n$ 个不同元素中取 $k(k\le n)$ 个不同元素,并按一定顺序排成一列的方案数

                    -

                    也可写作 $\mathrm{P}_{n}^{k}$ 或直接用组合数写法 $k! \times \binom{n}{k}$

                    -

                    代码求解

                    $O(n)$ 计算

                    -

                    例如求解 $A_{n-m+1}^{m} \bmod p$

                    +

                    排列数

                    +

                    \(n\) 个不同元素中取 \(k(k\le n)\) +个不同元素,并按一定顺序排成一列的方案数 \[ +\mathrm{A}_{n}^{k} = \frac{n!}{(n-k)!} +\] 也可写作 \(\mathrm{P}_{n}^{k}\) 或直接用组合数写法 +\(k! \times \binom{n}{k}\)

                    +

                    代码求解

                    +

                    \(O(n)\) 计算

                    +

                    例如求解 \(A_{n-m+1}^{m} \bmod +p\)

                    int res=1;
                     for(int i=n-m+1; i>=n-2*m+2; i--)
                         res=res*i%p;
                     cout << res << '\n'
                    -

                    组合数

                    从 $n$ 个不同元素中取 $k(k \le n)$ 个不同元素的方案数

                    -

                    高中一般记作 $\mathrm{C}_n^{k}$ ,不是很推荐使用。

                    -

                    特别地,规定当 $k > n$ 时, $\mathrm{A}_n^{k} = \mathrm{C}_n^{k}=0$

                    -

                    代码求解

                    $O(n^2)$ 递推
                    for(int i=0; i<=1000; i++)
                    +

                    组合数

                    +

                    \(n\) 个不同元素中取 \(k(k \le n)\) 个不同元素的方案数 \[ +\binom{n}{k} = \dfrac{n!}{k!(n-k)!} +\] 高中一般记作 \(\mathrm{C}_n^{k}\) ,不是很推荐使用。

                    +

                    特别地,规定当 \(k > +n\) 时, \(\mathrm{A}_n^{k} = +\mathrm{C}_n^{k}=0\)

                    +

                    代码求解

                    +
                    \(O(n^2)\) 递推
                    +
                    for(int i=0; i<=1000; i++)
                     {
                         C[i][0]=1;
                         for(int j=1; j<=i; j++)
                             C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
                     }
                    -
                    $O(p)$ Lucas定理

                    详见 [OI数学总结-数论]

                    -

                    P3807 【模板】卢卡斯定理/Lucas 定理

                    +
                    \(O(p)\) +Lucas定理
                    +

                    详见 [OI数学总结-数论]

                    +

                    P3807 +【模板】卢卡斯定理/Lucas 定理

                    #include <iostream>
                     #include <string>
                     #include <vector>
                    @@ -547,40 +569,89 @@ 
                    } return 0; }
                    -
                    -

                    容斥原理

                    容斥原理(principle of inclusion-exclusion): 令 $X$ 为一个有限集合,$P_1, P_2,\dots , P_m$ 是一些性质的集合,对于任意 $S \subseteq [m]$ ,定义 $N(S) = \{x \in X \mid \forall i \in S : x \text{ has } P_i\}$ 。那么 $X$ 中不满足任何一个性质 $P_i$ 的元素个数

                    -

                    对于任意的 $S \subseteq [m]$ ,如果 $N(S)$ 只和 $S$ 的大小(即 $|S|$ )有关,那么式子可以化简为

                    -

                    其中 $N(i)$ 表示对于任意满足 $S \subseteq [m],|S|=i$ 的 $N(S)$ 值

                    +
                    +

                    容斥原理

                    +

                    容斥原理(principle of inclusion-exclusion): 令 \(X\) 为一个有限集合\(P_1, P_2,\dots , P_m\) +是一些性质的集合,对于任意 \(S \subseteq [m]\) ,定义 \(N(S) = \{x \in X \mid \forall i \in S : x \text{ +has } P_i\}\) 。那么 \(X\) +中不满足任何一个性质 \(P_i\) +的元素个数\[ +\sum_{S \subseteq [m]} (-1)^{|S|} \left|N(S)\right| +\] 对于任意的 \(S \subseteq +[m]\)如果 \(N(S)\) +只和 \(S\) 的大小(即 \(|S|\) )有关,那么式子可以化简为 +\[ +\sum_{i=0}^{m} (-1)^i \binom{m}{i} N(i) +\] 其中 \(N(i)\) +表示对于任意满足 \(S \subseteq +[m],|S|=i\)\(N(S)\)

                    容斥原理的常用技巧

                    -
                      -
                    1. 定义一些“坏性质” $P_1,\dots,P_m$
                    2. -
                    3. 找到对于每个 $S\subseteq [m]$ 计算 $N(S)$ 的方法
                    4. +
                        +
                      1. 定义一些“坏性质” \(P_1,\dots,P_m\)
                      2. +
                      3. 找到对于每个 \(S\subseteq [m]\) +计算 \(N(S)\) 的方法
                      4. 套用容斥原理计算不满足任何一个“坏性质”的元素个数
                      -
                      -

                      鸽巢原理(抽屉原理)

                      假如有 $n+1$个元素放到 $n$ 个集合中去,其中必定有一个集合里至少有两个元素。

                      -

                      应用

                        -
                      1. 任意 $n+1$ 个数中至少有 $2$ 个数与 $n$ 同余
                      2. +
                        +

                        鸽巢原理(抽屉原理)

                        +

                        假如有 \(n+1\)个元素放到 \(n\) +个集合中去,其中必定有一个集合里至少有两个元素。

                        +

                        应用

                        +
                          +
                        1. 任意 \(n+1\) 个数中至少有 \(2\) 个数与 \(n\) 同余
                        -
                        -

                        第二类斯特林数

                        $\begin{Bmatrix}n\\m\end{Bmatrix}$ 或 $S(n,m)$ ,表示将 $n$ 个两两不同的元素,划分为 $m$ 个互不区分的非空子集的方案数

                        -
                        -

                        伯努利数

                        常用于等幂求和( $m$ 次幂和的公式)

                        -

                        记 $S_m(n) = \sum\limits_{i=0}^{n-1} i^m$ ,则

                        -

                        其中 $B_k$ 为伯努利数,定义如下

                        -

                        例如 $\binom{2}{0} B_0 = \binom{2}{1}B_1 = 0$

                        -
                        - +\sum_{j=0}^{m}\dbinom{m+1}{j}B_j = 0,\quad m>0 +\] 例如 \(\binom{2}{0} B_0 = +\binom{2}{1}B_1 = 0\)

                        +
                        ++++++++++++ - - + + @@ -589,54 +660,66 @@

                        \(\dots\)

                        - - - - - - - - - - - + + + + + + + + + + +
                        $n$
                        \(n\) 0 1 2
                        $B_n$$1$$-\frac{1}{2}$$\frac{1}{6}$$0$$-\frac{1}{30}$$0$$\frac{1}{42}$$0$$\dots$
                        \(B_n\)\(1\)\(-\frac{1}{2}\)\(\frac{1}{6}\)\(0\)\(-\frac{1}{30}\)\(0\)\(\frac{1}{42}\)\(0\)\(\dots\)
                        -
                        -

                        代码求解

                        咕咕咕…

                        -
                        -

                        贝尔数(Bell数)

                        $B_n$ 是基数为 $n$ 的集合的划分方法的数目

                        -

                        集合 $S$ 的一个划分定义为 $S$ 的两两不相交的非空子集的族,它们的并为 $S$

                        -

                        例如 $B_3=5$ ,因为 $3$ 个元素的集合 $a,b,c$ 有 $5$ 种不同的划分方法

                        -

                        贝尔数适合递推公式

                        -

                        贝尔三角形(类似于杨辉三角)

                        -

                        则有 $B_n = A_{n,1}$

                        -

                        代码求解

                        咕咕咕….

                        + 1, &i=1\land j=1 \\\\ + A_{i-1,j-1}, &i\ne 1 \land j=1 \\\\ + A_{i,j-1}+A_{i-1,j-1}, &i\ne 1 \land j \ne 1 +\end{cases} +\]

                        +

                        则有 \(B_n = A_{n,1}\)

                        +

                        代码求解

                        +

                        咕咕咕....

                    @@ -1006,7 +1089,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/30/abc156e-roaming-ti-jie/index.html b/2022/07/30/abc156e-roaming-ti-jie/index.html index d15d435529..878b467188 100644 --- a/2022/07/30/abc156e-roaming-ti-jie/index.html +++ b/2022/07/30/abc156e-roaming-ti-jie/index.html @@ -472,37 +472,64 @@

                    ABC156E Roaming 题解

                    -

                    ABC156E Roaming 题解

                    题目链接:ABC156E Roaming

                    +

                    ABC156E Roaming 题解

                    +

                    题目链接:ABC156E +Roaming

                    题意

                    翻译来自我们模拟赛,可能和原题有区别

                    -

                    输入两个数字 $n,k$ ,初始时有 $n$ 个盒子,每个盒子中都装着一个小球,$n$ 个小球之间不可区分。现在你要进行下列操作恰好 $k$ 次

                    +

                    输入两个数字 \(n,k\) ,初始时有 +\(n\) +个盒子,每个盒子中都装着一个小球,\(n\) +个小球之间不可区分。现在你要进行下列操作恰好 \(k\)

                    • 选择任意一个盒子中的一个小球,将它放到一个不同的盒子中
                    -

                    你需要算出在 $k$ 次操作之后,小球的不同的摆放方式有多少种,答案对 $10^9+7$ 取模。两种摆放小球的方式被认为是不同的,当且仅当存在某一个盒子在两种摆放方式中放了不同个数的小球。

                    -

                    $3 \le n \le 10^5,~2 \le k \le 10^9$

                    +

                    你需要算出在 \(k\) +次操作之后,小球的不同的摆放方式有多少种,答案对 \(10^9+7\) +取模。两种摆放小球的方式被认为是不同的,当且仅当存在某一个盒子在两种摆放方式中放了不同个数的小球。

                    +

                    \(3 \le n \le 10^5,~2 \le k \le +10^9\)

                    -

                    Roundgod老师:这道题需要很好的观察能力

                    -

                    注意到 $k \ge n-1$ 时方案数等于 $n-1$ 的方案数

                    -

                    因为至多 $n-1$ 步就可以摆出所有的情况,例如 5,0,0,0,0 只要 $4$ 步

                    -

                    步数是可以浪费的,比如本来要把 $1$ 的一个球移到 $2$ ,

                    -

                    可以先移到 $3$ 再移到 $2$ ,这样就浪费了一步,这样我们就可以把 $k$ 步一直浪费到 $n-1$ 步。

                    -

                    还有一个性质,如果某种情况 $0$ 的个数小于等于 $k$ ,那么一定可以达到,很显然。

                    +

                    Roundgod老师:这道题需要很好的观察能力

                    +

                    注意到 \(k \ge n-1\) 时方案数等于 +\(n-1\) 的方案数

                    +

                    因为至多 \(n-1\) +步就可以摆出所有的情况,例如 5,0,0,0,0 只要 \(4\)

                    +

                    步数是可以浪费的,比如本来要把 \(1\) 的一个球移到 \(2\)

                    +

                    可以先移到 \(3\) 再移到 \(2\) ,这样就浪费了一步,这样我们就可以把 +\(k\) 步一直浪费到 \(n-1\) 步。

                    +

                    还有一个性质,如果某种情况 \(0\) 的个数小于等于 \(k\) +,那么一定可以达到,很显然。

                      -
                    • 当 $k \ge n-1$ 时,答案为
                    • -
                    -

                    其实它就是 $x_1+\dots + x_n=n$ ,$x$ 的非负解个数

                    -
                      -
                    • 当 $k<n-1$ 步时,答案为

                      -

                      这里我们做的就是枚举 $0$ 的个数。

                      -

                      第一个是枚举 $0$ 的位置,第二个是 $x_1+\dots + x_{n-i}=n$ 的正整数解的个数

                      -
                    • +
                    • \(k \ge n-1\) 时,答案为 +\[ +\dbinom{2n-1}{n-1} +\]

                      +

                      其实它就是 \(x_1+\dots + x_n=n\) +,\(x\) 的非负解个数

                    • +
                    • \(k<n-1\) 步时,答案为 +\[ +\sum_{i=0}^{k} \dbinom{n}{i}\dbinom{n-1}{n-i-1} +\] 这里我们做的就是枚举 \(0\) +的个数。

                      +

                      第一个是枚举 \(0\) 的位置,第二个是 +\(x_1+\dots + x_{n-i}=n\) +的正整数解的个数

                    这个题稍微有点乱,因为性质很多,但是仔细思考一下还是颇有乐趣的。

                    -

                    时间复杂度 $O(n)$

                    +

                    时间复杂度 \(O(n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -930,7 +957,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/30/atcoder-educational-dp-contest-r-walk-ti-jie/index.html b/2022/07/30/atcoder-educational-dp-contest-r-walk-ti-jie/index.html index 55528a69c8..43ee59d3eb 100644 --- a/2022/07/30/atcoder-educational-dp-contest-r-walk-ti-jie/index.html +++ b/2022/07/30/atcoder-educational-dp-contest-r-walk-ti-jie/index.html @@ -476,35 +476,50 @@

                    Atcoder Educational DP Contest R
                    -

                    Atcoder Educational DP Contest R Walk 题解

                    题目链接:Atcoder Educational DP Contest R Walk

                    +

                    Atcoder Educational +DP Contest R Walk 题解

                    +

                    题目链接:Atcoder +Educational DP Contest R Walk

                    题意

                    -

                    给一张 $N$ 个结点的有向简单图,给出邻接矩阵 $A$ ,求长度为 $K$ 的路径条数,答案对 $10^9+7$ 取模。

                    -

                    $1 \le N \le 50,1\le K \le 10^{18}$ , $a_{i,j}$ is 0 or 1 ,$a_{i,i}=0$

                    +

                    给一张 \(N\) +个结点的有向简单图,给出邻接矩阵 \(A\) +,求长度为 \(K\) 的路径条数,答案对 +\(10^9+7\) 取模。

                    +

                    \(1 \le N \le 50,1\le K \le +10^{18}\)\(a_{i,j}\) is 0 or +1 ,\(a_{i,i}=0\)

                    -

                    设 $f_{k,i,j}$ 表示 $i$ 到 $j$ 路径长为 $k$ 的方案数,则

                    -

                    之所以不从 $k-t$ 转移,而从 $k-1$ 转移,是因为可能存在重复计数

                    +

                    \(f_{k,i,j}\) 表示 \(i\)\(j\) 路径长为 \(k\) 的方案数,则 \[ +f_{k,i,j}=\sum_{(s,j) \in E} f_{k-1,i,s} +\] 之所以不从 \(k-t\) 转移,而从 +\(k-1\) +转移,是因为可能存在重复计数

                    比如这张图

                    就可能重复计数

                    -

                    上面的式子可以进一步化简

                    -

                    这个形式是不是很熟悉?

                    -

                    考虑矩阵快速幂优化dp

                    +\\f^{k}_{i,j}=\sum f^{k-1}_{i,t}\times A_{t,j} +\] 考虑矩阵快速幂优化dp

                    关于为什么可以用矩阵快速幂,这里解释一下

                    -

                    由于矩阵乘法的定义为

                    -

                    而这里的形式和矩阵乘法的定义完全一致

                    -

                    因此我们可以看作三个矩阵的关系,即

                    -

                    稍微化一化柿子,可得

                    -

                    至此可知,答案就是

                    -

                    时间复杂度 $O(n^3 \log k)$

                    +

                    由于矩阵乘法的定义为 \[ +(AB)_{i,j}=\sum_{k=1}^{m}A_{i,k}B_{k,j}\quad i\in[1,n],j\in[1,p] +\] 而这里的形式和矩阵乘法的定义完全一致

                    +

                    因此我们可以看作三个矩阵的关系,即 \[ +f^k=f^{k-1}\times A +\] 稍微化一化柿子,可得 \[ +f^k = f^{k-2} A^2 = \dots = A^k +\] 至此可知,答案就是 \[ +\sum_{1\le i,j \le n}f^{k}_{i,j} = \sum_{1\le i,j \le n}A^{k}_{i,j} +\] 时间复杂度 \(O(n^3 \log +k)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -933,7 +948,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/30/oi-shu-xue-zong-jie-ji-chu-gong-shi-jie-lun/index.html b/2022/07/30/oi-shu-xue-zong-jie-ji-chu-gong-shi-jie-lun/index.html index ea7f22a342..a0db7da413 100644 --- a/2022/07/30/oi-shu-xue-zong-jie-ji-chu-gong-shi-jie-lun/index.html +++ b/2022/07/30/oi-shu-xue-zong-jie-ji-chu-gong-shi-jie-lun/index.html @@ -468,32 +468,52 @@

                    OI数学总结-基础公式&
                    -

                    OI数学总结-基础公式&结论

                    施工中,咕咕咕…

                    +

                    OI数学总结-基础公式&结论

                    +

                    施工中,咕咕咕...

                      -
                    • $A(n) = 1 + \dfrac{1}{\sqrt{2}}+\dfrac{1}{\sqrt{3}}+\dots+\dfrac{1}{\sqrt{n}}$ ,积分逼近有 $A(n) = \Theta(\sqrt{n})$
                    • -
                    • $B(n) = 1+\sqrt{2}+\sqrt{3}+\dots\sqrt{n}$ ,积分逼近有 $B(n) = \Theta(n\sqrt{n})$
                    • -
                    • 调和级数求和 $C(n) = 1 + \dfrac{1}{2}+\dfrac{1}{3}+\dots+\dfrac{1}{n}$ ,积分逼近有 $C(n) = \Theta(\ln n)$
                    • +
                    • \(A(n) = 1 + +\dfrac{1}{\sqrt{2}}+\dfrac{1}{\sqrt{3}}+\dots+\dfrac{1}{\sqrt{n}}\) +,积分逼近有 \(A(n) = +\Theta(\sqrt{n})\)
                    • +
                    • \(B(n) = +1+\sqrt{2}+\sqrt{3}+\dots\sqrt{n}\) ,积分逼近有 \(B(n) = \Theta(n\sqrt{n})\)
                    • +
                    • 调和级数求和 \(C(n) = 1 + +\dfrac{1}{2}+\dfrac{1}{3}+\dots+\dfrac{1}{n}\) ,积分逼近有 \(C(n) = \Theta(\ln n)\)
                    -

                    等差数列前缀和:$S(n) = \dfrac{n(a_1+a_n)}{2} = na_1 + \dfrac{n(n-1)}{2}d$

                    -

                    等比数列前缀和:$S(n) = \dfrac{a_1(1-q^n)}{1-q},q\ne 1$

                    +

                    等差数列前缀和:\(S(n) = +\dfrac{n(a_1+a_n)}{2} = na_1 + \dfrac{n(n-1)}{2}d\)

                    +

                    等比数列前缀和:\(S(n) = +\dfrac{a_1(1-q^n)}{1-q},q\ne 1\)

                    一些常见的前缀和

                      -
                    • $\sum\limits_{i=1}^{n}i = \dfrac{n(n+1)}{2}$
                    • -
                    • $\sum\limits_{i=1}^{n}i^2 = \dfrac{n(n+1)(2n+1)}{6}$
                    • -
                    • $\sum\limits_{i=1}^{n}i^3 = \left(\dfrac{n(n+1)}{2}\right)^2$
                    • +
                    • \(\sum\limits_{i=1}^{n}i = +\dfrac{n(n+1)}{2}\)
                    • +
                    • \(\sum\limits_{i=1}^{n}i^2 = +\dfrac{n(n+1)(2n+1)}{6}\)
                    • +
                    • \(\sum\limits_{i=1}^{n}i^3 = +\left(\dfrac{n(n+1)}{2}\right)^2\)
                    -

                    快速开平方(用手开平方的方法)

                    -

                    正整数 $a$ 满足 $a^2\le x < (a+1)^2$

                    -

                    极化恒等式

                    -

                    向量旋转公式

                    +\sqrt{x}&= a+\dfrac{x-a^2}{\sqrt{x}+a} \\&\approx +a+\dfrac{x-a^2}{2\times a} +\end{aligned} +\]

                    +

                    正整数 \(a\) 满足 \(a^2\le x < (a+1)^2\)

                    +

                    极化恒等式 \[ +\boldsymbol{a} \cdot \boldsymbol{b} = \dfrac{1}{4} +\left(\left(\boldsymbol{a}+\boldsymbol{b}\right)^2-\left(\boldsymbol{a}-\boldsymbol{b}\right)^2\right) +\] 向量旋转公式

                    oi-wiki

                    -

                    公式:(逆时针旋转角度 $\vartheta$ )

                    -

                    简记为 $\tt{xC-yS,xS+yC}$

                    +

                    公式:(逆时针旋转角度 \(\vartheta\) +) \[ +\boldsymbol{a} = (x,y)\\\boldsymbol{b}=(x\cos +\vartheta-y\sin\vartheta,x\sin\vartheta+y\cos\vartheta) +\] 简记为 \(\tt{xC-yS,xS+yC}\)

                    @@ -855,7 +875,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/30/xian-xing-dai-shu-ju-zhen/index.html b/2022/07/30/xian-xing-dai-shu-ju-zhen/index.html index 5e80b1effd..69b9fc7b28 100644 --- a/2022/07/30/xian-xing-dai-shu-ju-zhen/index.html +++ b/2022/07/30/xian-xing-dai-shu-ju-zhen/index.html @@ -468,53 +468,76 @@

                    线性代数-矩阵

                    -

                    线性代数-矩阵

                    施工中,咕咕咕….

                    -

                    矩阵乘法

                    矩阵乘法的定义

                    矩阵乘法的定义

                    -

                    一个 $n \times m$ 的矩阵和 $m\times p$ 的矩阵相乘,答案为一个 $n \times p$ 的矩阵

                    -

                    快速手算

                    之前我是把右矩阵逆时针转90度然后和第一个算的

                    +

                    线性代数-矩阵

                    +

                    施工中,咕咕咕....

                    +

                    矩阵乘法

                    +

                    矩阵乘法的定义

                    +

                    矩阵乘法的定义 \[ +(AB)_{i,j}=\sum_{k=1}^{m}A_{i,k}B_{k,j}\quad i\in[1,n],j\in[1,p] +\] 一个 \(n \times m\) 的矩阵和 +\(m\times p\) +的矩阵相乘,答案为一个 \(n \times +p\) 的矩阵

                    +

                    快速手算

                    +

                    之前我是把右矩阵逆时针转90度然后和第一个算的

                    然后我算了几万年都没真正记住怎么算 QAQ

                    现在新的算法,超好记的有没有qwqqqwqqwqqqwqwq

                    -

                    例:

                    -

                    第一行:

                    -

                    第二行

                    -

                    最后的答案

                    -

                    特殊矩阵

                    单位矩阵

                    -

                    满足对于任意矩阵 $A$ 有

                    -

                    矩阵乘法的性质

                    矩阵乘法具有结合律

                    -

                    矩阵乘法具有左右分配律

                    -

                    矩阵乘法不一定具有交换律

                    -

                    矩阵快速幂

                    优化递推

                    例:

                    -

                    尝试构造矩阵使得

                    -

                    显然 $M$ 是一个 $3\times 3$ 的矩阵,不难发现

                    -

                    然后我们可以用 $M^{k-1}$ 快速计算 $f_{k}$

                    -

                    常见数据范围:$10^{18}$

                    -

                    板子题:P1962 斐波那契数列

                    +\\C(A+B)=CA+CB +\] 矩阵乘法不一定具有交换律 \[ +AB\ne BA +\]

                    +

                    矩阵快速幂

                    +

                    优化递推

                    +

                    例: \[ +f_i = 2f_{i-1}+4f_{i-2}+3f_{i-3} +\] 尝试构造矩阵使得 \[ +\begin{bmatrix}f_{i}\\f_{i-1}\\f_{i-2}\end{bmatrix}=M\times\begin{bmatrix}f_{i-1}\\f_{i-2}\\f_{i-3}\end{bmatrix} +\] 显然 \(M\) 是一个 \(3\times 3\) 的矩阵,不难发现 \[ +M=\begin{bmatrix}2&4&3\\1&0&0\\0&1&0\end{bmatrix} +\] 然后我们可以用 \(M^{k-1}\) +快速计算 \(f_{k}\)

                    +

                    常见数据范围:\(10^{18}\)

                    +

                    板子题:P1962 +斐波那契数列

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -938,7 +961,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/31/cf691e-xor-sequences-ti-jie/index.html b/2022/07/31/cf691e-xor-sequences-ti-jie/index.html index e75f44d6cd..a620a42427 100644 --- a/2022/07/31/cf691e-xor-sequences-ti-jie/index.html +++ b/2022/07/31/cf691e-xor-sequences-ti-jie/index.html @@ -476,19 +476,37 @@

                    CF691E Xor-sequences 题解

                    -

                    CF691E Xor-sequences 题解

                    题目链接:CF691E Xor-sequences

                    +

                    CF691E Xor-sequences 题解

                    +

                    题目链接:CF691E +Xor-sequences

                    -

                    题意:给定大小为 $n$ 的集合 $\{a_1,\dots,a_n\}$,从集合中选择 $k$ 个数组成一个序列 $x_1 , \dots , x_k$ (可以重复选择),使得序列满足 $x_i$ 与 $x_i +1$ 异或的二进制中 $1$ 的个数是 $3$ 的倍数

                    -

                    问长度为 $k$ 的满足条件的序列的种数,答案对 $10^9 + 7$取模。

                    -

                    $1\le n\le 100,~1\le k,a_i \le 10^{18}$

                    +

                    题意:给定大小为 \(n\) 的集合 \(\{a_1,\dots,a_n\}\),从集合中选择 \(k\) 个数组成一个序列 \(x_1 , \dots , x_k\) +(可以重复选择),使得序列满足 \(x_i\) 与 +\(x_i +1\) 异或的二进制中 \(1\) 的个数是 \(3\) 的倍数

                    +

                    问长度为 \(k\) +的满足条件的序列的种数,答案对 \(10^9 + +7\)取模。

                    +

                    \(1\le n\le 100,~1\le k,a_i \le +10^{18}\)

                    -

                    设 $f_{i,j}$ 表示序列长度为 $i$ 且最后一个数为 $a_j$ 时的方案数,则

                    -

                    其中 $g_{k,j}$ 表示 $\left[3 \mid (a_k \oplus a_j)_{\texttt{ 2进制下1的数量}}\right]$

                    +\\\\f_{i,j}=\sum_{1\le k \le n}f_{i-1,k}\times g_{k,j} +\] 其中 \(g_{k,j}\) 表示 \(\left[3 \mid (a_k \oplus a_j)_{\texttt{ +2进制下1的数量}}\right]\)

                    可以发现这个形式就是矩阵乘法的定义式

                    考虑矩阵快速幂优化dp

                    -

                    时间复杂度 $O(n^3 \log k)$

                    +

                    时间复杂度 \(O(n^3 \log k)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -931,7 +949,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/07/31/cf852b-neural-network-country-ti-jie/index.html b/2022/07/31/cf852b-neural-network-country-ti-jie/index.html index e06621c0f6..b890f8deb1 100644 --- a/2022/07/31/cf852b-neural-network-country-ti-jie/index.html +++ b/2022/07/31/cf852b-neural-network-country-ti-jie/index.html @@ -472,41 +472,87 @@

                    CF852B Neural Network country
                    -

                    CF852B Neural Network country 题解

                    题目链接:CF852B Neural Network country

                    +

                    CF852B Neural Network +country 题解

                    +

                    题目链接:CF852B +Neural Network country

                    题意

                    -

                    给定一个包含一个出发点 $s$ ,终止点 $t$ 以及 $L$ 层 $V_1,V_2,\dots,V_L$ ,每层都包含 $N$ 个顶点的带边权有向分层图。从出发点 $s$ 到第一层 $V_1$ 中每个顶点都有一条带权边,从最后一层 $V_L$ 到终止点 $t$ 中每个顶点也都有一条带权边。对于所有 $1\le i<L$ ,第 $i$ 层的每个顶点到第 $i+1$ 层中的每个顶点都会连有一条带权有向边,且第 $i$ 层的每个顶点连出的边权完全相同,并且任意相邻两层之间所连边权完全相同。可以参照样例解释中链接内图示结合样例帮助理解。

                    -

                    现给出该分层图,你需要计算有多少种从 $s$ 走到 $t$ 的路径,使得路径上经过边权值和能被 $M$ 整除?由于答案可能过大,你需要将答案对 $10^9+7$ 取模后输出。

                    -

                    对于 $100\%$ 的数据,$1\le N\le 10^5,~2\le L\le 10^5,~2\le M\le 50,~0\le a_i,b_i,c_i<M$

                    +

                    给定一个包含一个出发点 \(s\) +,终止点 \(t\) 以及 \(L\)\(V_1,V_2,\dots,V_L\) ,每层都包含 \(N\) 个顶点的带边权有向分层图。从出发点 +\(s\) 到第一层 \(V_1\) 中每个顶点都有一条带权边,从最后一层 +\(V_L\) 到终止点 \(t\) 中每个顶点也都有一条带权边。对于所有 +\(1\le i<L\) ,第 \(i\) 层的每个顶点到第 \(i+1\) +层中的每个顶点都会连有一条带权有向边,且第 \(i\) +层的每个顶点连出的边权完全相同,并且任意相邻两层之间所连边权完全相同。可以参照样例解释中链接内图示结合样例帮助理解。

                    +

                    现给出该分层图,你需要计算有多少种从 \(s\) 走到 \(t\) 的路径,使得路径上经过边权值和能被 +\(M\) +整除?由于答案可能过大,你需要将答案对 \(10^9+7\) 取模后输出。

                    +

                    对于 \(100\%\) 的数据,\(1\le N\le 10^5,~2\le L\le 10^5,~2\le M\le 50,~0\le +a_i,b_i,c_i<M\)

                    感谢Roundgod老师的耐心指导,我还是太蠢了QAQ

                    这道题其实就是给了一个有特殊性质的分层图

                    -

                    然后用了 $s$ 和 $t$ 连上这个分层图

                    +

                    然后用了 \(s\)\(t\) 连上这个分层图

                    这个分层图的特殊性质就是

                    -

                    每个上一层的结点 $u_i$ 到下一层的结点 $v_i$ ,边权只由 $v_i$ 决定

                    +

                    每个上一层的结点 \(u_i\) +到下一层的结点 \(v_i\) +,边权只由 \(v_i\) +决定

                    因此对于分层图里面同一层的点,我们可以看作同一个点

                    -

                    每个点和下一层的点都有 $n$ 中走法,也就对应了路径和的 $O(n)$ 种变化

                    +

                    每个点和下一层的点都有 \(n\) +中走法,也就对应了路径和的 \(O(n)\) +种变化

                    这种题的常见思路就是,算出不同路径和的答案

                    -

                    然后把能被 $M$ 整除的答案加上,就是最终答案

                    +

                    然后把能被 \(M\) +整除的答案加上,就是最终答案

                    仔细思考一下,我们有没有必要记录整个路径和?

                    -

                    注意到 $M \le 50$ ,这说明路径和模 $M$ 的剩余系很小

                    -

                    而一条路径的边权和能否被 $M$ 整除,就等价于模 $M$ 等于 $0$

                    +

                    注意到 \(M \le 50\) ,这说明路径和模 +\(M\) 的剩余系很小

                    +

                    而一条路径的边权和能否被 \(M\) +整除,就等价于模 \(M\) 等于 \(0\)

                    这样我们就可以很容易的设出dp状态了

                    -

                    这里我们只算分层图上的 $1\sim L-1$ 层,因为第 $L$ 层显然不满足特殊性质

                    -

                    因为第 $L$ 层每个点到 $t$ 的边权不同,我们要单独处理。

                    -

                    设 $f_{i,j}$ 表示走到分层图上的第 $i$ 层,从第一层出发,边权和模 $M$ 为 $j$ 的方案数

                    -

                    转移方程就是

                    -

                    其中 $A_{k,j}$ 表示存在 $k$ 向 $j$ 的转移,这个可以预处理出来

                    -

                    稍微整理一下,可得

                    -

                    可以发现这个柿子就是矩阵乘法的定义,只不过 $f$ 是个 $1 \times M$ 的矩阵

                    -

                    于是写成矩阵乘法的形式,就是

                    -

                    考虑矩阵快速幂计算

                    -

                    实际上的答案是 $A \times B^{L-2} \times C$(和上面写的稍微有些区别)

                    -

                    分别对应 $s \to V_1,V_1 \to V_{L-1},V_{L-1}\to t$

                    -

                    时间复杂度 $O(M^3 \log n)$

                    +

                    这里我们只算分层图上的 \(1\sim L-1\) +层,因为第 \(L\) +层显然不满足特殊性质

                    +

                    因为第 \(L\) 层每个点到 \(t\) 的边权不同,我们要单独处理。

                    +

                    \(f_{i,j}\) 表示走到分层图上的第 +\(i\) 层,从第一层出发,边权和模 \(M\)\(j\) 的方案数

                    +

                    转移方程就是 \[ +f_{i,j}=\sum_{0\le k < M} f_{i-1,k} \times A_{k,j} +\] 其中 \(A_{k,j}\) 表示存在 +\(k\)\(j\) 的转移,这个可以预处理出来

                    +

                    稍微整理一下,可得 \[ +f^{i}_j = \sum _{0 \le k < M} f^{i-1}_{k} \times A_{k,j} +\] 可以发现这个柿子就是矩阵乘法的定义,只不过 \(f\) 是个 \(1 +\times M\) 的矩阵

                    +

                    于是写成矩阵乘法的形式,就是 \[ +f^k = f^{k-1} \times A = f^{k-2} \times A^2 = A^k +\] 考虑矩阵快速幂计算

                    +

                    实际上的答案是 \(A \times B^{L-2} \times +C\)(和上面写的稍微有些区别)

                    +

                    分别对应 \(s \to V_1,V_1 \to +V_{L-1},V_{L-1}\to t\)

                    +

                    时间复杂度 \(O(M^3 \log n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -974,7 +1020,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/02/cf553a-kyoya-and-colored-balls-ti-jie/index.html b/2022/08/02/cf553a-kyoya-and-colored-balls-ti-jie/index.html index 20ca173f7c..9ff2722e7c 100644 --- a/2022/08/02/cf553a-kyoya-and-colored-balls-ti-jie/index.html +++ b/2022/08/02/cf553a-kyoya-and-colored-balls-ti-jie/index.html @@ -472,23 +472,49 @@

                    CF553A Kyoya and Colored Balls
                    -

                    CF553A Kyoya and Colored Balls 题解

                    题目链接:CF553A Kyoya and Colored Balls

                    +

                    CF553A Kyoya and Colored +Balls 题解

                    +

                    题目链接:CF553A +Kyoya and Colored Balls

                    题意

                    -

                    一个袋子中有 $n$ 个彩球,他们用 $k$ 种不同的颜色染色。颜色被从 $1$ 到 $k$ 编号。同一种颜色的球看成是一样的。现在从袋中一个一个的拿出球来,直到拿完所有的球。对于所有颜色为 $i(1 \le i \le k-1)$ 的球,他的最后一个球总是在编号比他大的球拿完之前拿完,问这样情况有多少种,答案对 $10^9+7$ 取模。

                    -

                    输入单组测试数据。 第一行给出一个整数 $k(1 \le k \le 1000)$,表示球的种类。 接下来 $k$ 行,每行一个整数 $c_i$,表示第 $i$ 种颜色的球有 $c_i$ 个 $(1 \le c_i \le 1000)$。 球的总数目不超过 $1000$。

                    +

                    一个袋子中有 \(n\) 个彩球,他们用 +\(k\) 种不同的颜色染色。颜色被从 \(1\)\(k\) +编号。同一种颜色的球看成是一样的。现在从袋中一个一个的拿出球来,直到拿完所有的球。对于所有颜色为 +\(i(1 \le i \le k-1)\) +的球,他的最后一个球总是在编号比他大的球拿完之前拿完,问这样情况有多少种,答案对 +\(10^9+7\) 取模。

                    +

                    输入单组测试数据。 第一行给出一个整数 \(k(1 +\le k \le 1000)\),表示球的种类。 接下来 \(k\) 行,每行一个整数 \(c_i\),表示第 \(i\) 种颜色的球有 \(c_i\)\((1 \le +c_i \le 1000)\)。 球的总数目不超过 \(1000\)

                    发烧真的无聊所以就写写题目啥的吧23333

                    -

                    考虑依次插入每种颜色的球,不妨设现在要插入的球颜色为 $i$ ,有 $x_i$ 个

                    -

                    设 $n_i$ 为之前已经插入的球的数量,即

                    -

                    对于 $i$ 的最后一个球,一定是要放在序列的最后面的

                    -

                    剩下的 $x_i-1$ 个球可以随便在 $n_i-1$ 个空隙里面放,每个空隙可以放多个球

                    -

                    或者也可以认为,把剩下的 $x_i-1$ 个球分成 $n_i-1$ 个组,每个组可以为空

                    -

                    显然根据插板法可知共有 $\dbinom{n_i+x_i-1}{n_i}$ 种方案

                    -

                    因此总的答案就是

                    -

                    时间复杂度 $O(n^2)$

                    +

                    考虑依次插入每种颜色的球,不妨设现在要插入的球颜色为 \(i\) ,有 \(x_i\)

                    +

                    \(n_i\) +为之前已经插入的球的数量,即 \[ +n_i = \sum _{j=1}^{i-1} x_i +\] 对于 \(i\) +的最后一个球,一定是要放在序列的最后面的

                    +

                    剩下的 \(x_i-1\) 个球可以随便在 +\(n_i-1\) +个空隙里面放,每个空隙可以放多个球

                    +

                    或者也可以认为,把剩下的 \(x_i-1\) +个球分成 \(n_i-1\) +个组,每个组可以为空

                    +

                    显然根据插板法可知共有 \(\dbinom{n_i+x_i-1}{n_i}\) 种方案

                    +

                    因此总的答案就是 \[ +\prod_{i=1}^{k}\dbinom{n_i+x_i-1}{n_i} +\] 时间复杂度 \(O(n^2)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -892,7 +918,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/02/chu-sai-fu-xi/index.html b/2022/08/02/chu-sai-fu-xi/index.html index f2ba26c559..8f6835e3f5 100644 --- a/2022/08/02/chu-sai-fu-xi/index.html +++ b/2022/08/02/chu-sai-fu-xi/index.html @@ -468,52 +468,70 @@

                    初赛复习

                    -

                    初赛复习

                    施工中……

                    +

                    初赛复习

                    +

                    施工中......

                    参考文献等等补上来。

                    -

                    IT发展历史

                    第一台计算机:ENIAC,1946年

                    +

                    IT发展历史

                    +

                    第一台计算机:ENIAC,1946年

                    应用:计算,数据储存处理,通信,辅助工作等

                    第一个程序员:Ada(女),有为此命名的程序语言

                    图灵奖(计算机),菲尔兹奖(数学),诺贝尔奖(物化生经济文学和平)

                    ACM(美国计算机学会)IEEE(电气电子工程师学会)CCF(懂得都懂)

                    图灵奖由ACM设立,获得图灵奖的华人学者只有姚期智一人,名称取自计算机科学的先驱、英国科学家艾伦·麦席森·图灵,是计算机界最负盛名、最崇高的一个奖项,有“计算机界的诺贝尔奖”之称。

                    -

                    计算机组成

                    冯诺依曼结构:

                    -

                    +

                    计算机组成

                    +

                    冯诺依曼结构:

                    +

                    输入设备:将信息输入进计算机的是输入设备

                    输出设备:将计算机的信息输出给人的是输出设备

                    特例:触摸屏既是输入设备又是输出设备

                    -

                    CPU字长:32位/64位 (CPU一次能并行处理的二进制位数,一定是8的倍数)

                    +

                    CPU字长:32位/64位 +(CPU一次能并行处理的二进制位数,一定是8的倍数)

                    位(Bit):一个01

                    字节(Byte):八个01

                    1PB=1024TB,1TB=1024GB,1GB=1024KB,1KB=1024Byte,1Byte=8Bit

                    小例子:

                    int a[10000005];
                    -

                    所占空间为 $(10^7+5)\times 4 /1024 /1024 \approx 38.15\texttt{ MB }$

                    +

                    所占空间为 \((10^7+5)\times 4 /1024 /1024 +\approx 38.15\texttt{ MB }\)

                    内存:RAM(随机存储器),断电会丢失数据

                    CPU、GPU等固件上装配的ROM(只读存储器)所存储的数据无法被改写或删除,也不会因电源关闭而丢失

                    摩尔定律:18个月翻倍(很快就要失效了)

                    -

                    软件系统

                    操作系统Windows,DOS,UNIX,Linux,MacOS,Android,IOS等

                    +

                    软件系统

                    +

                    操作系统Windows,DOS,UNIX,Linux,MacOS,Android,IOS等

                    应用软件:浏览器、办公软件、游戏等等

                    机器语言、汇编语言、高级语言

                    -

                    编码

                    ASCII编码(英文),BGK编码(早期中文,2字节),UTF-8(新&常用,目前3字节)

                    +

                    编码

                    +

                    ASCII编码(英文),BGK编码(早期中文,2字节),UTF-8(新&常用,目前3字节)

                    图片:黑白(01),24位彩色(RGB)

                    文件系统扩展名:

                    图片:jpg(jpeg),gif,png,tiff

                    文档:txt,docx,pdf

                    声音:mp3/视频:avi,rm,mp4

                    可执行文件:exe(Windows),无(Linux)

                    -

                    NOI系列

                    NOI:1984年

                    -

                    NOIP:1995~2019,2020~至今

                    +

                    NOI系列

                    +

                    NOI:1984年

                    +

                    NOIP:19952019,2020至今

                    可以带文具(不可草稿纸),水,衣服,证件

                    2022年之后只能用C++了

                    -

                    程序设计

                    编译:代码->可执行文件(机器码):C/C++,Pascal

                    +

                    程序设计

                    +

                    编译:代码->可执行文件(机器码):C/C++,Pascal

                    解释:一行一行运行:Python,JacaScript,PHP,BASIC

                    特例:java不太好定义是编译执行的还是解释执行

                    时间复杂度:数据规模增长和运行时间增长的趋势

                    -

                    基本算法

                    排序算法

                    排序稳定性:元素相等时的相对顺序不改变,则排序算法具有稳定性。

                    -
                    +

                    基本算法

                    +

                    排序算法

                    +

                    排序稳定性:元素相等时的相对顺序不改变,则排序算法具有稳定性。

                    ++++++++ - + @@ -523,109 +541,127 @@

                    - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + - - - - + + + +
                    排序算法 平均时间复杂度 最坏时间复杂度冒泡排序$O(n^2)$$O(n^2)$$O(n)$$O(1)$\(O(n^2)\)\(O(n^2)\)\(O(n)\)\(O(1)\) yes
                    直接选择排序$O(n^2)$$O(n^2)$$O(n)$$O(1)$\(O(n^2)\)\(O(n^2)\)\(O(n)\)\(O(1)\) no
                    直接插入排序$O(n^2)$$O(n^2)$$O(n)$$O(1)$\(O(n^2)\)\(O(n^2)\)\(O(n)\)\(O(1)\) yes
                    快速排序$O(n \log n)$$O(n^2)$$O(n \log n)$$O(n \log n)$\(O(n \log n)\)\(O(n^2)\)\(O(n \log n)\)\(O(n \log n)\) no
                    堆排序$O(n \log n)$$O(n \log n)$$O(n \log n)$$O(1)$\(O(n \log n)\)\(O(n \log n)\)\(O(n \log n)\)\(O(1)\) no
                    希尔排序$O(n \log n$)$O(ns)$$O(n)$$O(1)$\(O(n \log n\))\(O(ns)\)\(O(n)\)\(O(1)\) no
                    归并排序$O(n \log n)$$O(n \log n)$$O(n \log n)$$O(n)$\(O(n \log n)\)\(O(n \log n)\)\(O(n \log n)\)\(O(n)\) yes
                    计数排序$O(n + k)$$O(n + k)$$O(n+k)$$O(n+k)$\(O(n + k)\)\(O(n + k)\)\(O(n+k)\)\(O(n+k)\) yes
                    基数排序$O(n+k)$$O(n+k)$$O(n+k)$$O(n+k)$\(O(n+k)\)\(O(n+k)\)\(O(n+k)\)\(O(n+k)\) yes
                    -
                    -

                    贪心

                    正确性比较难处理,一般考场上选择打表找规律(或者凭直觉

                    -

                    二分

                    二分答案要求答案在一定范围内具有“单调性”。

                    -

                    时间复杂度 $O(\log N)$ ,$N$ 表示二分的范围大小

                    -

                    递归&分治

                    递归的思想在于把原问题拆解成若干性质相同的子问题

                    -

                    与递归分治有关的时间复杂度分析:主定理

                    -

                    简单数据结构

                    好的我们来讲一讲树套树

                    -

                    出栈序列方案数:卡特兰数 $C(n) = \dfrac{1}{n+1} \dbinom{2n}{n}$

                    -

                    队列

                    队列:先进先出

                    -

                    链表

                    o->o->o->o

                    -

                    一种说法是:任意两个结点连通(无向图)且有唯一一条路径

                    -

                    二叉树

                    $n$ 个结点 $n-1$ 条边的连通图

                    +

                    贪心

                    +

                    正确性比较难处理,一般考场上选择打表找规律(或者凭直觉

                    +

                    二分

                    +

                    二分答案要求答案在一定范围内具有“单调性”。

                    +

                    时间复杂度 \(O(\log N)\)\(N\) 表示二分的范围大小

                    +

                    递归&分治

                    +

                    递归的思想在于把原问题拆解成若干性质相同的子问题

                    +

                    与递归分治有关的时间复杂度分析:主定理

                    +

                    简单数据结构

                    +

                    好的我们来讲一讲树套树

                    +

                    +

                    出栈序列方案数:卡特兰数 \(C(n) = +\dfrac{1}{n+1} \dbinom{2n}{n}\)

                    +

                    队列

                    +

                    队列:先进先出

                    +

                    链表

                    +

                    o->o->o->o

                    +

                    +

                    一种说法是:任意两个结点连通(无向图)且有唯一一条路径

                    +

                    二叉树

                    +

                    \(n\) 个结点 \(n-1\) 条边的连通图

                    每个结点的至多有两个子结点

                    -

                    满二叉树第 $i(i\ge 1)$ 层的结点个数为 $2^{i-1}$

                    -

                    $k(k\ge 1)$ 层的二叉树有 $2^{k}-1$ 个结点(证明显然)

                    +

                    满二叉树第 \(i(i\ge 1)\) +层的结点个数为 \(2^{i-1}\)

                    +

                    \(k(k\ge 1)\) 层的二叉树有 \(2^{k}-1\) 个结点(证明显然)

                    前序遍历:根左右

                    中序遍历:左根右

                    后序遍历:左右根

                    -

                    表达式树

                    建表达式树的方法(这个是自己想的方法)

                    +

                    表达式树

                    +

                    建表达式树的方法(这个是自己想的方法)

                    给定中缀表达式(就是1+1=2这种)

                    对于同一运算等级的,看作一个块递归

                    如果有多个,随便选一个分界线递归(那我肯定选最前面的)

                    -

                    a+b*c+d ,看作 ab*cd 三个同一运算等级的块

                    +

                    a+b*c+d ,看作 +ab*cd +三个同一运算等级的块

                    这里有三个,所以随便选一个,比如选第一个+

                    然后递归子树 b*c+d ,以此类推。

                    最后的树长这样(不唯一)

                    不难发现它的中序遍历就是中缀表达式。

                    -

                    好像有不少概念,到时候整理一个比较全的

                    +

                    +

                    好像有不少概念,到时候整理一个比较全的

                    这里写几个重点的:

                    完全图:两两都有连边的无向图

                    结点的度数:

                    @@ -637,8 +673,9 @@

                    好像

                    自环:起点和终点相同的边与这个结点构成的环

                    简单图:无重边无自环

                    DAG(有向无环图):显然。

                    -
                    -

                    参考文献
                    [1] https://blog.csdn.net/pange1991/article/details/85460755

                    +
                    +

                    参考文献 [1] https://blog.csdn.net/pange1991/article/details/85460755

                    @@ -1000,7 +1037,7 @@

                    好像
                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/03/mo-ni-sai-ti-jiang-jie-10/index.html b/2022/08/03/mo-ni-sai-ti-jiang-jie-10/index.html index d188198c09..87500a1f53 100644 --- a/2022/08/03/mo-ni-sai-ti-jiang-jie-10/index.html +++ b/2022/08/03/mo-ni-sai-ti-jiang-jie-10/index.html @@ -472,14 +472,26 @@

                    模拟赛题讲解[10]

                    -

                    模拟赛题讲解[10]

                    来自 xpp 2022-08-03 noi.ac #2723

                    +

                    模拟赛题讲解[10]

                    +

                    来自 xpp 2022-08-03 +noi.ac #2723

                    题目描述

                    -

                    lzr给了xpp一个字符串 $s$ 和一个字符串 $t$ ,xpp想通过 $s$ 构造出 $t$ ,构造方式为xpp每次选择 $s$ 的一个子序列,放到当前串的后面,xpp想知道他需要构造多少次才能构造出 $t$ 呢?

                    +

                    lzr给了xpp一个字符串 \(s\) +和一个字符串 \(t\) ,xpp想通过 \(s\) 构造出 \(t\) ,构造方式为xpp每次选择 \(s\) +的一个子序列,放到当前串的后面,xpp想知道他需要构造多少次才能构造出 +\(t\) 呢?

                    输入格式

                    -

                    第一行一个数 $T$ 表示数据组数。

                    -

                    对于每组数据第一行一个字符串 $s$ ,第二行一个字符串 $t$ ,含义见题目描述

                    +

                    第一行一个数 \(T\) +表示数据组数。

                    +

                    对于每组数据第一行一个字符串 \(s\) +,第二行一个字符串 \(t\) +,含义见题目描述

                    输出格式

                    -

                    每组数据输出一行表示次数。如果不能输出 $-1$。

                    +

                    每组数据输出一行表示次数。如果不能输出 \(-1\)

                    输入1

                    3
                     aabce
                    @@ -493,13 +505,25 @@ 

                    数据规模

                    -

                    对于 $30\%$ 的数据, $\texttt{字符串长度} \le 100$

                    -

                    对于 $100\%$ 的数据,$\texttt{字符串长度} \le 10^5,~1\le T\le 10$

                    +

                    对于 \(30\%\) 的数据, \(\texttt{字符串长度} \le 100\)

                    +

                    对于 \(100\%\) 的数据,\(\texttt{字符串长度} \le 10^5,~1\le T\le +10\)

                    题解

                    -

                    暴力的解法就是遍历 $t$ ,然后维护一个指针 $p$ 在 $s$ 上跳

                    -

                    仔细观察可以发现,如果 $s=\tt{aaabbbbbbc}$ , $t=\tt{ac}$

                    -

                    那么 $p$ 就会慢悠悠的从 $s$ 的第一个 $\tt{a}$ 一直跑到最后的 $\tt{c}$

                    -

                    考虑维护一个 $\text{nx}_{i,j}$ 表示 $i$ 后面出现的第一个字符 $j$ 的位置

                    +

                    暴力的解法就是遍历 \(t\) +,然后维护一个指针 \(p\)\(s\) 上跳

                    +

                    仔细观察可以发现,如果 \(s=\tt{aaabbbbbbc}\)\(t=\tt{ac}\)

                    +

                    那么 \(p\) 就会慢悠悠的从 \(s\) 的第一个 \(\tt{a}\) 一直跑到最后的 \(\tt{c}\)

                    +

                    考虑维护一个 \(\text{nx}_{i,j}\) +表示 \(i\) 后面出现的第一个字符 \(j\) 的位置

                    这个东西的预处理还是很有趣的,我当时没想到怎么搞,然后就用二分草过了

                    for(int i=0; i<26; i++)
                         for(int j=1; j<=n+1; j++)
                    @@ -507,7 +531,7 @@ 

                    for(int i=0; i<26; i++) for(int j=n; j>=1; j--) nx[i][j]=(s[j]=='a'+i)?j:nx[i][j+1];

                    -

                    时间复杂度 $O(Qn)$

                    +

                    时间复杂度 \(O(Qn)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -970,7 +994,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/04/mo-ni-sai-ti-jiang-jie-11/index.html b/2022/08/04/mo-ni-sai-ti-jiang-jie-11/index.html index 9c331a32a9..49e8956373 100644 --- a/2022/08/04/mo-ni-sai-ti-jiang-jie-11/index.html +++ b/2022/08/04/mo-ni-sai-ti-jiang-jie-11/index.html @@ -472,13 +472,19 @@

                    模拟赛题讲解[11]

                    -

                    模拟赛题讲解[11]

                    来自 xpp 2022-08-04 noi.ac #2731

                    +

                    模拟赛题讲解[11]

                    +

                    来自 xpp 2022-08-04 +noi.ac #2731

                    题目描述

                    xpp在玩祖玛游戏,他心血来潮想根据祖玛游戏出一个题。

                    -

                    给一个序列 $a_1,a_2,\dots,a_n$ ,你每次可以选择相同且相邻的三个数删除他们,如果序列中存在这样相同且相邻的三个数,xpp将不断将他们删除,xpp想知道最后序列中还剩几个数呢?

                    +

                    给一个序列 \(a_1,a_2,\dots,a_n\) +,你每次可以选择相同且相邻的三个数删除他们,如果序列中存在这样相同且相邻的三个数,xpp将不断将他们删除,xpp想知道最后序列中还剩几个数呢?

                    输入格式

                    -

                    第一行一个正整数 $T(1\le T\le 10)$ 表示数据组数。

                    -

                    对于每组数据,第一行一个正整数 $n$ 表示序列长度,第二行 $n$ 个正整数表示 $a[1\dots n]$。

                    +

                    第一行一个正整数 \(T(1\le T\le 10)\) +表示数据组数。

                    +

                    对于每组数据,第一行一个正整数 \(n\) +表示序列长度,第二行 \(n\) 个正整数表示 +\(a[1\dots n]\)

                    输出格式

                    对于每组数据输出一行,表示答案。

                    输入1

                    @@ -491,12 +497,15 @@

                    1 2

                    数据规模

                    -

                    对于 $30\%$ 的数据,$3\le n\le 10,~1\le a_i\le 10$

                    -

                    对于 $60\%$ 的数据,$3\le n\le 10^3,~1\le a_i\le 10^3$

                    -

                    对于 $100\%$ 的数据,$1\le a_i\le 10^9,~\sum n≤10^6$

                    +

                    对于 \(30\%\) 的数据,\(3\le n\le 10,~1\le a_i\le 10\)

                    +

                    对于 \(60\%\) 的数据,\(3\le n\le 10^3,~1\le a_i\le 10^3\)

                    +

                    对于 \(100\%\) 的数据,\(1\le a_i\le 10^9,~\sum n≤10^6\)

                    题解

                    考虑用一个栈维护,满三个就不断的弹出

                    -

                    时间复杂度 $O(\sum n)$ 大水题

                    +

                    时间复杂度 \(O(\sum n)\) 大水题

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -923,7 +932,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/06/mo-ni-sai-ti-jiang-jie-12/index.html b/2022/08/06/mo-ni-sai-ti-jiang-jie-12/index.html index d68b5c6fb3..b2a2a9cb0e 100644 --- a/2022/08/06/mo-ni-sai-ti-jiang-jie-12/index.html +++ b/2022/08/06/mo-ni-sai-ti-jiang-jie-12/index.html @@ -468,20 +468,35 @@

                    模拟赛题讲解[12]

                    -

                    模拟赛题讲解[12]

                    来自 yukuai26 2022-08-06 noi.ac #2756

                    +

                    模拟赛题讲解[12]

                    +

                    来自 yukuai26 +2022-08-06 noi.ac #2756

                    题目描述

                    曾经有一个 oj 叫做 bzoj, 里面有一题 bzoj1002 叫狼抓兔子

                    -

                    由于 1002 过于有名且显眼,很多人 A 掉了他,每 A 一次兔子就会少一只,兔子死亡惨重,救救兔子!

                    +

                    由于 1002 过于有名且显眼,很多人 A 掉了他,每 A +一次兔子就会少一只,兔子死亡惨重,救救兔子!

                    兔子为了反击策划了一次行动。

                    -

                    刚开始狼在二维平面上的 $(0,0)$ ,他希望走到 $(10^7,0)$,而有 n 只兔子分为处于位置 $(x_{i},y_{i})$。

                    -

                    当狼与某只兔子的欧几里得距离$\leq p$ 时($p$可以视为一个无限接近于 $0$ 的正实数, 事实上可以理解为狼和兔子非常非常接近),兔子就会干扰狼,这样狼会被干扰 $t$ 秒,

                    +

                    刚开始狼在二维平面上的 \((0,0)\) +,他希望走到 \((10^7,0)\),而有 n +只兔子分为处于位置 \((x_{i},y_{i})\)

                    +

                    当狼与某只兔子的欧几里得距离\(\leq +p\) 时(\(p\)可以视为一个无限接近于 \(0\) 的正实数, +事实上可以理解为狼和兔子非常非常接近),兔子就会干扰狼,这样狼会被干扰 +\(t\) 秒,

                    随后这只兔子完全任务就会离开。

                    兔子的目标是使得狼受到尽可能被更多的兔子干扰。

                    -

                    已知狼和兔子运动速度都为 1m/s。另外狼和兔子都知道所有动物的实时位置和实时速度方向。

                    -

                    现在围观的 yukuai26 想知道,假如兔子和狼都绝对聪明,狼会受到多少次兔子的干扰

                    +

                    已知狼和兔子运动速度都为 +1m/s。另外狼和兔子都知道所有动物的实时位置和实时速度方向。

                    +

                    现在围观的 yukuai26 +想知道,假如兔子和狼都绝对聪明,狼会受到多少次兔子的干扰

                    输入格式

                    -

                    第一行两个非负整数 $n,t$ ,表示兔子的个数和每次眩晕的时间。

                    -

                    接下来 $n$ 行每行两个整数 $x_{i},y_{i}$ ,表示每只兔子的位置

                    +

                    第一行两个非负整数 \(n,t\) +,表示兔子的个数和每次眩晕的时间。

                    +

                    接下来 \(n\) 行每行两个整数 \(x_{i},y_{i}\) ,表示每只兔子的位置

                    输出格式

                    一个正整数表示答案

                    输入1

                    @@ -492,18 +507,27 @@

                    2

                    样例解释

                    -

                    第一只兔子选择去 $(1,0)$ 处等狼,假如狼往右走就会经过 $(1,0)$ , 假如往上下走,那么兔子可以跟着一起上下移动, 时刻保持在狼右边。

                    -

                    所以狼必定被第一只兔子干扰。第一只兔子干扰狼 $1$ 秒,这 $1$ 秒时间可以让第二只兔子跑过来干扰。

                    +

                    第一只兔子选择去 \((1,0)\) +处等狼,假如狼往右走就会经过 \((1,0)\) +, 假如往上下走,那么兔子可以跟着一起上下移动, 时刻保持在狼右边。

                    +

                    所以狼必定被第一只兔子干扰。第一只兔子干扰狼 \(1\) 秒,这 \(1\) 秒时间可以让第二只兔子跑过来干扰。

                    但第三只兔子太远了,所以不可能干扰到狼。

                    数据规模与约定

                    -

                    对于前 $20$% 的数据,$n=1$

                    -

                    对于前 $40$% 的数据,$t=0$

                    -

                    对于另 $30$% 的数据,$x_{i}\ge 10^7$

                    -

                    对于 $100$% 的数据,$1 \le n \le 5\times 10^5,~-10^9 \leq x_{i},y_{i} \leq 10^9,t \le 10^3$

                    +

                    对于前 \(20\)% 的数据,\(n=1\)

                    +

                    对于前 \(40\)% 的数据,\(t=0\)

                    +

                    对于另 \(30\)% 的数据,\(x_{i}\ge 10^7\)

                    +

                    对于 \(100\)% 的数据,\(1 \le n \le 5\times 10^5,~-10^9 \leq x_{i},y_{i} +\leq 10^9,t \le 10^3\)

                    题解

                    一个很神奇的结论是,这些🐰有的去拦截🐺,不如直接在终点等着

                    然后就直接把所有🐰按距终点的距离排个序算算就好了

                    -

                    时间复杂度 $O(n\log n)$

                    +

                    时间复杂度 \(O(n\log n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -925,7 +949,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/06/mo-ni-sai-ti-jiang-jie-13/index.html b/2022/08/06/mo-ni-sai-ti-jiang-jie-13/index.html index d7e1a59c1e..b33956e9d0 100644 --- a/2022/08/06/mo-ni-sai-ti-jiang-jie-13/index.html +++ b/2022/08/06/mo-ni-sai-ti-jiang-jie-13/index.html @@ -468,17 +468,31 @@

                    模拟赛题讲解[13]

                    -

                    模拟赛题讲解[13]

                    来自 yukuai26 2022-08-06 noi.ac #2757

                    +

                    模拟赛题讲解[13]

                    +

                    来自 yukuai26 +2022-08-06 noi.ac #2757

                    据说是从JOI搬过来的,赛时有位巨佬18min就A了 Orz

                    题目描述

                    -

                    橙 —— 作为幽幽子大人的朋友的使者, 非常贪玩, 也喜欢思考有趣 的问题。

                    +

                    橙 ---- 作为幽幽子大人的朋友的使者, 非常贪玩, 也喜欢思考有趣 +的问题。

                    她在冥界思考一个有趣的问题, 希望赶到的灵梦能够解答。

                    -

                    一个字符串由 $\tt{a,b,c}$三种字母组成, 它最多能分成多少个 $\tt{abc}$ 和 $\tt{cbc}$ 的子序列呢?

                    -

                    具体的, 一个字符串 $str$ 可以由选择 $3$ 个位置 $i < j < k$ 使得 $s_i,s_j,s_k$ 依次连接组成 $\tt{abc}$ 或 $\tt{cbc}$ ,

                    -

                    这样算分出一个字符串, 接着把 $3$ 个位置上的字母删掉, 剩下的字母重新组成新的字符串并接着提取。

                    +

                    一个字符串由 \(\tt{a,b,c}\)三种字母组成, +它最多能分成多少个 \(\tt{abc}\) 和 +\(\tt{cbc}\) 的子序列呢?

                    +

                    具体的, 一个字符串 \(str\) +可以由选择 \(3\) 个位置 \(i < j < k\) 使得 \(s_i,s_j,s_k\) 依次连接组成 \(\tt{abc}\)\(\tt{cbc}\)

                    +

                    这样算分出一个字符串, 接着把 \(3\) +个位置上的字母删掉, 剩下的字母重新组成新的字符串并接着提取。

                    输入格式

                    -

                    第一行一个正整数 $n$ 表示字符串的长度

                    -

                    接下来一个长度为 $n$ 的字符串, 保证由 $\tt{a,b,c}$ 组成

                    +

                    第一行一个正整数 \(n\) +表示字符串的长度

                    +

                    接下来一个长度为 \(n\) 的字符串, +保证由 \(\tt{a,b,c}\) 组成

                    输出格式

                    一行一个数,表示最大能分成的个数

                    输入1

                    @@ -493,36 +507,69 @@

                    1

                    数据范围

                    打包测试

                    -

                    对于测试包 $1$,分值 $40$ 分,$n \leq 15$

                    -

                    对于测试包 $2$,分值 $30$ 分,$n \leq 50$

                    -

                    对于测试包 $3$,分值 $20$ 分,$n \leq 3000$

                    -

                    对于测试包 $4$,分值 $10$ 分,$n \leq 10^6$

                    +

                    对于测试包 \(1\),分值 \(40\) 分,\(n \leq +15\)

                    +

                    对于测试包 \(2\),分值 \(30\) 分,\(n \leq +50\)

                    +

                    对于测试包 \(3\),分值 \(20\) 分,\(n \leq +3000\)

                    +

                    对于测试包 \(4\),分值 \(10\) 分,\(n \leq +10^6\)

                    题解

                    -

                    Subtask 1 40pts

                    注意到 $\tt{c}$ 可以作为结尾,也可以作为开头

                    -

                    考虑对于所有的 $\tt{c}$ 暴搜他作为第一个还是第三个

                    -

                    如果是第一个就变成 $\tt{a}$ ,计算即可。

                    -

                    Subtask 2 30pts

                    考虑从后往前做

                    -

                    设 $f_{i,j,k}$ 表示最后 $i$ 位,有 $j$ 个 $\tt{bc}$ ,$k$ 个 $\tt{c}$ ,最多能有多少 $\tt{abc}$ 或 $\tt{cbc}$ ,转移 $O(1)$。

                    -

                    Subtask 3 20pts

                    把所有作为 $\tt{abc}$ 和 $\tt{cbc}$ 末尾的 $\tt{c}$ 放在原串的最后面

                    -

                    具体地,如果串里面出现了

                    -

                    我们强制做如下匹配

                    -

                    这样的好处在于,对于合法的方案,一定存在一个分界线,满足

                    +\color{blue}{c}\, \color{black}{\dots}}\, +\] +这样的好处在于,对于合法的方案,一定存在一个分界线,满足

                      -
                    • 分界线左侧的所有 $\tt{c}$ 均可以看作 $\tt{a}$
                    • -
                    • 分界线右侧仅由 $\tt{bc}$ 构成,并且与分界线左侧每个 $\tt{a/c}$ 两两配对
                    • +
                    • 分界线左侧的所有 \(\tt{c}\) +均可以看作 \(\tt{a}\)
                    • +
                    • 分界线右侧仅由 \(\tt{bc}\) +构成,并且与分界线左侧每个 \(\tt{a/c}\) +两两配对
                    -

                    考虑枚举这个分界线,时间复杂度 $O(n^2)$

                    -

                    Subtask 4 10pts

                    注意到如果右侧的 $\tt{bc}$ 不够了,分界线理应往左移

                    -

                    如果左侧的 $\tt{c}$ 过多了,分界线理应往右移

                    +

                    考虑枚举这个分界线,时间复杂度 \(O(n^2)\)

                    +

                    Subtask 4 10pts

                    +

                    注意到如果右侧的 \(\tt{bc}\) +不够了,分界线理应往左移

                    +

                    如果左侧的 \(\tt{c}\) +过多了,分界线理应往右移

                    实际上是存在单调性的。考虑二分分界线。

                    -

                    时间复杂度 $O(n \log n)$ ,可以通过此题

                    +

                    时间复杂度 \(O(n \log n)\) +,可以通过此题

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -577,7 +624,9 @@ 

                    << l << '\n'; return 0; }

                    -

                    Extra 更快的解法

                    时间复杂度 $O(n)$ ,目前q779还不会,因此把老师的题解搬过来了

                    +

                    Extra 更快的解法

                    +

                    时间复杂度 \(O(n)\) +,目前q779还不会,因此把老师的题解搬过来了

                    O(n)
                     考虑从后往前做,记录bc和c的个数
                     对于已经有bc时出现c的情况,先匹配成cbc,并记录这个c的位置
                    @@ -980,7 +1029,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/06/mo-ni-sai-ti-jiang-jie-14/index.html b/2022/08/06/mo-ni-sai-ti-jiang-jie-14/index.html index 4f2bb3b900..a034236144 100644 --- a/2022/08/06/mo-ni-sai-ti-jiang-jie-14/index.html +++ b/2022/08/06/mo-ni-sai-ti-jiang-jie-14/index.html @@ -472,16 +472,24 @@

                    模拟赛题讲解[14]

                    -

                    模拟赛题讲解[14]

                    来自 yukuai26 2022-08-06 noi.ac #2755

                    +

                    模拟赛题讲解[14]

                    +

                    来自 yukuai26 +2022-08-06 noi.ac #2755

                    题目背景

                    -

                    $\text{yukuai26}$ 喜欢算术,但他又菜又爱玩,所以需要你的帮助

                    +

                    \(\text{yukuai26}\) +喜欢算术,但他又菜又爱玩,所以需要你的帮助

                    题目描述

                    -

                    小明给你 $n$ 个正整数,他想取一些数加起来(可以取 $0$ 个数),问能得到的最大的能被 $3$ 整除的和是多少

                    +

                    小明给你 \(n\) +个正整数,他想取一些数加起来(可以取 \(0\) 个数),问能得到的最大的能被 \(3\) 整除的和是多少

                    输入格式

                    -

                    第一行一个整数 $n$

                    -

                    第二行 $n$ 个正整数, 表示小明给你的 $n$ 个正整数

                    +

                    第一行一个整数 \(n\)

                    +

                    第二行 \(n\) 个正整数, +表示小明给你的 \(n\) 个正整数

                    输出格式

                    -

                    一个整数,表示最大的能被 $3$ 整除的和

                    +

                    一个整数,表示最大的能被 \(3\) +整除的和

                    输入1

                    4
                     1 2 3 4
                    @@ -493,28 +501,53 @@

                    1890

                    数据范围

                    -

                    总共 $10$ 个测试点

                    -

                    对于测试点 $1,2$,$n \leq 3$

                    -

                    对于测试点 $3$,$n \leq 10$

                    -

                    对于测试点 $4,5$,保证小明给你的数只有 $3$ 的倍数

                    -

                    对于测试点 $6$,保证小明给的数对 $3$取模为 $0$ 或 $1$

                    -

                    对于测试点 $7$,$a_i \leq 100$

                    -

                    对于所有测试点 $n \leq 100000 ,a_i \leq 1000$

                    +

                    总共 \(10\) 个测试点

                    +

                    对于测试点 \(1,2\)\(n \leq 3\)

                    +

                    对于测试点 \(3\)\(n \leq 10\)

                    +

                    对于测试点 \(4,5\),保证小明给你的数只有 \(3\) 的倍数

                    +

                    对于测试点 \(6\),保证小明给的数对 +\(3\)取模为 \(0\)\(1\)

                    +

                    对于测试点 \(7\)\(a_i \leq 100\)

                    +

                    对于所有测试点 \(n \leq 100000 ,a_i \leq +1000\)

                    题解

                    -

                    解法一 贪心

                    先把所有的数都加上

                    -

                    那么最后的和模 $3$ 只有 $3$ 种情况

                    +

                    解法一 贪心

                    +

                    先把所有的数都加上

                    +

                    那么最后的和模 \(3\) 只有 \(3\) 种情况

                      -
                    • $S\bmod 3=0$ 直接输出答案即可
                    • -
                    • $S\bmod 3=1$ 去掉一个最小的模 $3$ 余 $1$ 的数即可
                    • -
                    • $S\bmod 3=2$ 去掉「两个最小的模 $3$ 余 $1$ 的数」和「一个最小的模 $3$ 余 $1$ 的数」中的较小者即可
                    • +
                    • \(S\bmod 3=0\) +直接输出答案即可
                    • +
                    • \(S\bmod 3=1\) 去掉一个最小的模 +\(3\)\(1\) 的数即可
                    • +
                    • \(S\bmod 3=2\) 去掉「两个最小的模 +\(3\)\(1\) 的数」和「一个最小的模 \(3\)\(1\) 的数」中的较小者即可
                    -

                    时间复杂度 $O(n\log n)$

                    +

                    时间复杂度 \(O(n\log n)\)

                    代码没写,因为考场上dp过了

                    -

                    解法二 DP

                    对于每个数,我们可以看作重量为 $w_i = x \bmod 3,v_i=x$ 的物品

                    -

                    设 $f_{i,j}$ 表示只考虑前 $i$ 个物品,总和模 $3$ 为 $j$ 时的最大总和,则

                    -

                    然后就是简单的01背包啦

                    -

                    时间复杂度 $O(n)$

                    +

                    解法二 DP

                    +

                    对于每个数,我们可以看作重量为 \(w_i = x +\bmod 3,v_i=x\) 的物品

                    +

                    \(f_{i,j}\) 表示只考虑前 \(i\) 个物品,总和模 \(3\)\(j\) 时的最大总和,则 \[ +f_{i+1,(j+w_{i+1}) \,\bmod \,3} = \max\{f_{i,(j+w_{i+1}) \,\bmod +\,3},f_{i,j}+v_{i+1}\} +\] 然后就是简单的01背包啦

                    +

                    时间复杂度 \(O(n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -914,7 +947,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/07/cf1713a-traveling-salesman-problem-ti-jie/index.html b/2022/08/07/cf1713a-traveling-salesman-problem-ti-jie/index.html index 63604fb883..aaa8e7ba2c 100644 --- a/2022/08/07/cf1713a-traveling-salesman-problem-ti-jie/index.html +++ b/2022/08/07/cf1713a-traveling-salesman-problem-ti-jie/index.html @@ -468,22 +468,34 @@

                    CF1713A Traveling Salesman Probl
                    -

                    CF1713A Traveling Salesman Problem 题解

                    题目链接:CF1713 Traveling Salesman Problem

                    +

                    CF1713A Traveling +Salesman Problem 题解

                    +

                    题目链接:CF1713 Traveling +Salesman Problem

                    -

                    题意:$t$ 组数据,每组数据给定 $n$ 个在坐标轴上的点 $(x_i,y_i)$ ,求最小的路径满足

                    +

                    题意\(t\) +组数据,每组数据给定 \(n\) +个在坐标轴上的\((x_i,y_i)\) ,求最小的路径满足

                      -
                    • 起点和终点均为 $(0,0)$
                    • -
                    • 每一步只能走到 $(x+1,y),~(x-1,y),~(x,y+1),~(x,y-1)$四者中的一个
                    • +
                    • 起点和终点均为 \((0,0)\)
                    • +
                    • 每一步只能走到 \((x+1,y),~(x-1,y),~(x,y+1),~(x,y-1)\)四者中的一个
                    -

                    例如下图

                    -

                    -

                    $1 \le t \le 100,~1\le n \le 100,~-100 \le x_i,y_i \le 100$

                    +

                    例如下图

                    +

                    +

                    \(1 \le t \le 100,~1\le n \le 100,~-100 \le +x_i,y_i \le 100\)

                    -

                    考虑四个方向上距离 $(0,0)$ 最远的所构成的合法矩形(满足走法的矩形)

                    +

                    考虑四个方向上距离 \((0,0)\) +最远的所构成的合法矩形(满足走法的矩形)

                    对矩形进行一定的伸缩,一定能走完所有的点,并且一定是最优的,

                    -

                    例如题目里给出的这个图,如果 $(0,1)$ 有个点,也是可以走到的

                    -

                    显然, $(0,0)$ 也可以走到,然后就没了

                    -

                    时间复杂度 $O(tn)$

                    +

                    例如题目里给出的这个图,如果 \((0,1)\) 有个点,也是可以走到的

                    +

                    显然, \((0,0)\) +也可以走到,然后就没了

                    +

                    时间复杂度 \(O(tn)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -881,7 +893,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/07/mo-ni-sai-ti-jiang-jie-15/index.html b/2022/08/07/mo-ni-sai-ti-jiang-jie-15/index.html index 26475ce6c2..30393a72f2 100644 --- a/2022/08/07/mo-ni-sai-ti-jiang-jie-15/index.html +++ b/2022/08/07/mo-ni-sai-ti-jiang-jie-15/index.html @@ -472,17 +472,31 @@

                    模拟赛题讲解[15]

                    -

                    模拟赛题讲解[15]

                    来自 yukuai26 2022-08-07 noi.ac #2763

                    +

                    模拟赛题讲解[15]

                    +

                    来自 yukuai26 +2022-08-07 noi.ac +#2763

                    题目描述

                    -

                    幻想乡有 $n$ 个建筑,每个建筑里住了一些居民。

                    -

                    有一些双向道路连接居民的家,共有 $n-1$ 条道路,恰好让所有居民的家能够互相到达。

                    -

                    其中标号为 $i$ 的建筑居住了 $i$ 名幻想乡的居民。

                    -

                    现在居民想要互相拜访,但是一次拜访需要花费 $\text{dis}(u,v)$ 的代价,其中 $u,v$ 分别是两人所在的建筑,$\text{dis}$ 表示这两个建筑之间的最短路。

                    +

                    幻想乡有 \(n\) +个建筑,每个建筑里住了一些居民。

                    +

                    有一些双向道路连接居民的家,共有 \(n-1\) +条道路,恰好让所有居民的家能够互相到达。

                    +

                    其中标号为 \(i\) 的建筑居住了 \(i\) 名幻想乡的居民。

                    +

                    现在居民想要互相拜访,但是一次拜访需要花费 \(\text{dis}(u,v)\) 的代价,其中 \(u,v\) 分别是两人所在的建筑,\(\text{dis}\) +表示这两个建筑之间的最短路。

                    ⑨想要知道, 所有幻想乡人都拜访其他人所需要的代价之和。

                    -

                    答案对 $10^9+7$ 取模。

                    +

                    答案对 \(10^9+7\) 取模。

                    输入格式

                    -

                    第一行一个整数 $n$。

                    -

                    接下来 $n-1$ 行, 每行 $2$ 个整数 $n-1$ 个整数描述 $n-1$ 条道路。

                    +

                    第一行一个整数 \(n\)

                    +

                    接下来 \(n-1\) 行, 每行 \(2\) 个整数 \(n-1\) 个整数描述 \(n-1\) 条道路。

                    输出格式

                    一个整数,表示总花费之和。

                    输入1

                    @@ -494,18 +508,23 @@

                    184

                    数据范围

                    -

                    对于 $30\%$ 的数据,满足 $n≤200$

                    -

                    对于 $60\%$ 的数据,满足 $n≤3000$

                    -

                    对于 $100\%$ 的数据,满足 $n≤1000000$

                    +

                    对于 \(30\%\) 的数据,满足 \(n≤200\)

                    +

                    对于 \(60\%\) 的数据,满足 \(n≤3000\)

                    +

                    对于 \(100\%\) 的数据,满足 \(n≤1000000\)

                    题解

                    -

                    直接去算是 $O(n^2)$ 的,显然这是不可接受的

                    +

                    直接去算是 \(O(n^2)\) +的,显然这是不可接受的

                    注意到询问是问的总和,因此我们可以考虑每条边的贡献

                    考虑一条边会被经过几次,它就会贡献多少

                    -

                    显然会经过

                    -

                    其中 $S=\sum_{1\le i \le n} i,~~s_u = u+\sum_{v \in \text{son}(u)}v$

                    +

                    显然会经过 \[ +s_u \times (S-s_u) +\] 其中 \(S=\sum_{1\le i \le n} i,~~s_u += u+\sum_{v \in \text{son}(u)}v\)

                    然后把每条边都加上就好了

                    -

                    时间复杂度 $O(n)$

                    +

                    时间复杂度 \(O(n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -942,7 +961,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/07/mo-ni-sai-ti-jiang-jie-16/index.html b/2022/08/07/mo-ni-sai-ti-jiang-jie-16/index.html index f2f7ea94d0..800d4467b4 100644 --- a/2022/08/07/mo-ni-sai-ti-jiang-jie-16/index.html +++ b/2022/08/07/mo-ni-sai-ti-jiang-jie-16/index.html @@ -468,22 +468,32 @@

                    模拟赛题讲解[16]

                    -

                    模拟赛题讲解[16]

                    来自 yukuai26 2022-08-07 noi.ac #2764

                    +

                    模拟赛题讲解[16]

                    +

                    来自 yukuai26 +2022-08-07 noi.ac #2764

                    题目描述

                    紫开始研究特殊的括号序列

                    这些特殊括号序列的形式是若干个右括号(可以没有)在前,若干个左括号(可以没有)在后。

                    -

                    比如 (()))))( 都是特殊的括号序列

                    +

                    比如 (()))))( +都是特殊的括号序列

                    显然刚开始给出的特殊的括号序列都不合法。

                    紫决定把所有序列首尾相连(按照紫想要的顺序排列),然后她再删除若干个括号,她希望最终删完括号的序列是一个合法的括号序列。

                    以下合法的括号序列定义

                    1、空串是一个合法的括号序列

                    -

                    2、若 $A,B$ 分别是合法的括号序列,则 $AB$ 是合法的括号序列

                    -

                    3、若 $A$ 是合法的括号序列,则 $(A)$ 是合法的括号序列

                    +

                    2、若 \(A,B\) +分别是合法的括号序列,则 \(AB\) +是合法的括号序列

                    +

                    3、若 \(A\) 是合法的括号序列,则 +\((A)\) 是合法的括号序列

                    紫想要知道,她最少需要删除多少个括号。

                    输入格式

                    第一行一个正整数 n,表示有多少个括号序列

                    -

                    接下来 n 行,每行两个非负整数 $a_{i},b_{i}$

                    -

                    $a_{i}$ 表示第 $i$ 个括号序列的右括号数,$b_{i}$ 表示第 $i$ 个括号序列的左括号数

                    +

                    接下来 n 行,每行两个非负整数 \(a_{i},b_{i}\)

                    +

                    \(a_{i}\) 表示第 \(i\) 个括号序列的右括号数,\(b_{i}\) 表示第 \(i\) 个括号序列的左括号数

                    输出格式

                    一个整数表示最少需要删除多少个括号。

                    输入1

                    @@ -498,10 +508,15 @@

                    \(40\%\) 的数据,满足 \(n\le 10\)

                    +

                    对于 \(60\%\) 的数据,满足 \(n\le 20\)

                    +

                    另存在 \(20\%\) 数据,满足若 \(a_{i}\le a_{j}\)\(b_{i}\ge b_{j}\)

                    +

                    对于 \(100\%\) 的数据,满足 \(n\le 100000,0\le a_{i},b_{i}\le 10^9\)

                    题解

                    这里先放个巨佬在考场代码里写的(有删改) Orz

                    /*
                    @@ -515,24 +530,31 @@ 

                    好的,开始我们的讲解

                    -

                    首先,别搞错了,(是左括号,) 是右括号,给出的是这样的 ))((((

                    -

                    考虑贪心。假如把所有的括号序列按照 $a_i > b_i$ 或 $a_i \le b_i$ 进行分类

                    +

                    首先,别搞错了,(是左括号,) +是右括号,给出的是这样的 ))((((

                    +

                    考虑贪心。假如把所有的括号序列按照 \(a_i +> b_i\)\(a_i \le b_i\) +进行分类

                    则第二类一定会放在第一类的前面,否则一定不优。

                    -

                    因为 $a_i > b_i$ 的会让未匹配的左括号变少

                    +

                    因为 \(a_i > b_i\) +的会让未匹配的左括号变少

                    反之,在末尾加入一个第二类的,会使未匹配的左括号数增多

                    此时第一类中的右括号无法匹配这些增加的左括号,显然这不优。

                    然后我们就可以考虑同一类中的选哪个了

                    显然我们会想尽可能早匹配掉左括号

                    -

                    每次加入一个第二类的,一定会导致未被匹配的左括号数增加 $x (x\in \mathbb{N})$ 个

                    -

                    那么这个结论有什么用呢,别急,后面会用到。

                    +

                    每次加入一个第二类的,一定会导致未被匹配的左括号数增加 \(x (x\in \mathbb{N})\)

                    +

                    那么这个结论有什么用呢,别急,后面会用到。

                    比起寄希望于后面第一类的右括号来匹配他们,不如直接在第二类内匹配掉尽可能多的

                    或者说,我们要最小化第二类中的未匹配的左括号

                    这等价于要最大化第二类中的匹配的右括号

                    我们刚刚说了,左括号的数量是单调不降的

                    而右括号是有可能匹配失败的,因为它前面可能没有左括号跟它匹配

                    -

                    于是,我们对于第二类的,直接按右括号数量 $a_i$ 从小到大排序就好了

                    -

                    同理,我们把上面的推导颠倒过来,可以退出第一类的情况是按左括号的数量 $b_i$ 从大到小排序。

                    -

                    时间复杂度 $O(n \log n)$

                    +

                    于是,我们对于第二类的,直接按右括号数量 \(a_i\) 从小到大排序就好了

                    +

                    同理,我们把上面的推导颠倒过来,可以退出第一类的情况是按左括号的数量 +\(b_i\) 从大到小排序。

                    +

                    时间复杂度 \(O(n \log n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -944,7 +966,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/08/luo-gu-p3243-hnoi2015-cai-yao-zhi-zuo-ti-jie/index.html b/2022/08/08/luo-gu-p3243-hnoi2015-cai-yao-zhi-zuo-ti-jie/index.html index 86b27a0536..9bbcf98b4b 100644 --- a/2022/08/08/luo-gu-p3243-hnoi2015-cai-yao-zhi-zuo-ti-jie/index.html +++ b/2022/08/08/luo-gu-p3243-hnoi2015-cai-yao-zhi-zuo-ti-jie/index.html @@ -476,28 +476,41 @@

                    洛谷P3243 [HNOI2015]菜肴制
                    -

                    洛谷P3243 [HNOI2015]菜肴制作 题解

                    题目链接:洛谷P3243 [HNOI2015]菜肴制作 题解

                    +

                    洛谷P3243 [HNOI2015]菜肴制作 +题解

                    +

                    题目链接:洛谷P3243 +[HNOI2015]菜肴制作 题解

                    -

                    题意: $t$ 组数据,每组要做 $n$ 个菜,但是需要满足 $m$ 个限制。

                    -

                    每个限制形如 $(i,j)$ ,指菜肴 $i$ 要在菜肴 $j$ 之前做。(这里的 $i,j$ 都是编号)

                    +

                    题意\(t\) +组数据,每组要做 \(n\) +个菜,但是需要满足 \(m\) 个限制。

                    +

                    每个限制形如 \((i,j)\) ,指菜肴 +\(i\) 要在菜肴 \(j\) 之前做。(这里的 \(i,j\) 都是编号)

                    在满足所有限制的前提下,要保证编号越小的菜肴越早做

                    -

                    对于每组数据,求出最优的菜肴制作顺序,或者输出 Impossible! 表示无解

                    -

                    $m$ 条限制中可能存在完全相同的。

                    -

                    $1\le t \le 3,~ 1 \le n,m \le 10^5$

                    +

                    对于每组数据,求出最优的菜肴制作顺序,或者输出 +Impossible! 表示无解

                    +

                    \(m\) +条限制中可能存在完全相同的。

                    +

                    \(1\le t \le 3,~ 1 \le n,m \le +10^5\)

                    显然建图然后topo,有环就无解。

                    但是怎么拓扑呢?字典序最小?

                    显然,这并不显然是错的

                    -

                    例: $(2,4),~(3,1)$

                    -

                    字典序最小的算出来的是 2,3,1,4 ,而答案是 3,1,2,4

                    -

                    原因在于字典序最小,不一定能保证 $1$ 尽可能在前面

                    +

                    例: \((2,4),~(3,1)\)

                    +

                    字典序最小的算出来的是 2,3,1,4 ,而答案是 +3,1,2,4

                    +

                    原因在于字典序最小,不一定能保证 \(1\) 尽可能在前面

                    即字典序最小,不一定保证编号越小的菜肴越早做

                    重新考虑怎么做

                    题目要求编号越小的菜肴越早做

                    因此标号越大的菜肴就要越晚做

                    则每个菜肴在它的合法范围内一定是放到最后面时最优

                    于是考虑建反图求字典序最大的拓扑序

                    -

                    时间复杂度 $O(tn)$

                    +

                    时间复杂度 \(O(tn)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -966,7 +979,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/08/mo-ni-sai-ti-jiang-jie-17/index.html b/2022/08/08/mo-ni-sai-ti-jiang-jie-17/index.html index 89a39c31f4..21d6c02d5b 100644 --- a/2022/08/08/mo-ni-sai-ti-jiang-jie-17/index.html +++ b/2022/08/08/mo-ni-sai-ti-jiang-jie-17/index.html @@ -476,19 +476,28 @@

                    模拟赛题讲解[17]

                    -

                    模拟赛题讲解[17]

                    来自 yukuai26 2022-08-08 noi.ac #2772

                    +

                    模拟赛题讲解[17]

                    +

                    来自 yukuai26 +2022-08-08 noi.ac #2772

                    题目描述

                    -

                    $\text{ysgh}$ 有一个长度为 $m$ 的,元素两两不同的序列。

                    -

                    $\text{emoairx}$ 想要知道这个序列里的元素及其顺序,她用一个神奇的望远镜可以看到序列里的一部分元素及其相对顺序。

                    +

                    \(\text{ysgh}\) 有一个长度为 \(m\) 的,元素两两不同的序列。

                    +

                    \(\text{emoairx}\) +想要知道这个序列里的元素及其顺序,她用一个神奇的望远镜可以看到序列里的一部分元素及其相对顺序。

                    他得到了很多组观测数据。

                    他想要知道这个序列应该是什么样的。

                    假如存在多组解,输出长度最小的解。

                    假如还是存在多组解,输出字典序最小的解。

                    数据保证有解。

                    输入格式

                    -

                    第一行一个正整数 $n$,表示有多少组观测数据

                    -

                    接下来 $n$ 行,每行第一个正整数 $k_{i}$ 表示第 $i$ 次观测观测到了多少个数据。

                    -

                    这行接下来 $k_{i}$ 个数描述序列中的 $k_{i}$ 个元素,需要保证原序列中的这几个元素一定要按照该顺序出现

                    +

                    第一行一个正整数 \(n\),表示有多少组观测数据

                    +

                    接下来 \(n\) 行,每行第一个正整数 +\(k_{i}\) 表示第 \(i\) 次观测观测到了多少个数据。

                    +

                    这行接下来 \(k_{i}\) +个数描述序列中的 \(k_{i}\) +个元素,需要保证原序列中的这几个元素一定要按照该顺序出现

                    输出格式

                    一行若干个正整数,描述长度最小前提下,字典序最小的合法的序列。

                    输入1

                    @@ -499,29 +508,41 @@

                    1 2 3 4 6 5

                    样例解释

                    -

                    诸如 2 1 3 4 6 51 2 3 4 6 5 7 也是合法的序列,但是它们长度和字典序不及 1 2 3 4 6 5

                    +

                    诸如 2 1 3 4 6 51 2 3 4 6 5 7 +也是合法的序列,但是它们长度和字典序不及 1 2 3 4 6 5

                    数据范围

                    -

                    对于 $60\%$ 的数据,满足 $n\le 10$

                    -

                    另存在 $20\%$ 数据,满足 $k_{i}=2$

                    -

                    对于前 $90\%$ 数据,满足若$\sum k_{i}\le 3\times 10^3$

                    -

                    对于 $100\%$ 的数据,满足 $k_{i}\ge 1,\sum k_{i}\le 3\times 10^5$ 保证读入的元素大小 $\leq 10^6$ , 并且所有读入的数都是正整数

                    +

                    对于 \(60\%\) 的数据,满足 \(n\le 10\)

                    +

                    另存在 \(20\%\) 数据,满足 \(k_{i}=2\)

                    +

                    对于前 \(90\%\) 数据,满足若\(\sum k_{i}\le 3\times 10^3\)

                    +

                    对于 \(100\%\) 的数据,满足 \(k_{i}\ge 1,\sum k_{i}\le 3\times 10^5\) +保证读入的元素大小 \(\leq 10^6\) , +并且所有读入的数都是正整数

                    题解

                    -

                    考虑建图,边 $u \to v$ 表示 $v$ 需要在 $u$ 后面

                    +

                    考虑建图,边 \(u \to v\) 表示 \(v\) 需要在 \(u\) 后面

                    因为有解,所以图是个DAG

                    什么?DAG?直接topo!

                    因为要求一个字典序最小的拓扑序,因此用一个小根堆来维护

                    -

                    yukuai26: “有一位选手偏不这么干,他偏要倒过来,建反图求字典序最大的。”

                    +

                    yukuai26: +“有一位选手偏不这么干,他偏要倒过来,建反图求字典序最大的。”

                    yukuai26:“字典序是从前往后比的,不是从后往前比的。”

                    然后那位选手喜提10pts。不愧是我呐QAQ

                    然后老师在讲题时忘记了hack是什么

                    -

                    然后有一位叫 Gordonlu 的巨佬给出了如下hack Orz

                    +

                    然后有一位叫 Gordonlu +的巨佬给出了如下hack Orz

                    显然答案应该是1 3 4 5 9 2

                    但是反过来会变成 1 9 2 3 4 5

                    我只能说,唉,我tcl

                    -

                    时间复杂度 $O(\sum k_i)$

                    +

                    时间复杂度 \(O(\sum k_i)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -984,7 +1005,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/08/mo-ni-sai-ti-jiang-jie-18/index.html b/2022/08/08/mo-ni-sai-ti-jiang-jie-18/index.html index 791a2da509..13c786a2dc 100644 --- a/2022/08/08/mo-ni-sai-ti-jiang-jie-18/index.html +++ b/2022/08/08/mo-ni-sai-ti-jiang-jie-18/index.html @@ -472,16 +472,22 @@

                    模拟赛题讲解[18]

                    -

                    模拟赛题讲解[18]

                    来自 yukuai26 2022-08-08 noi.ac #2773

                    +

                    模拟赛题讲解[18]

                    +

                    来自 yukuai26 +2022-08-08 noi.ac #2773

                    题目背景

                    搞个大新闻. jpg

                    题目描述

                    你来到了幻想乡。首先你准备搞一个大新闻,获得文文的报道并变得出名,你决定对幻想乡的股票系统下手。

                    -

                    你通过猪脚光环了解到了接下来 $n$ 天的股票价格 $a_i$,你每天可以买一个股票或卖出一个股票或者什么都不做,一天只能干一件事。

                    -

                    那么为了搞个大事情,你希望赚的钱最多,那么你最多能获得多少钱呢? (注意到最终手上的股票不能算钱, 并且你可以暂时亏损)

                    +

                    你通过猪脚光环了解到了接下来 \(n\) +天的股票价格 \(a_i\),你每天可以买一个股票或卖出一个股票或者什么都不做,一天只能干一件事。

                    +

                    那么为了搞个大事情,你希望赚的钱最多,那么你最多能获得多少钱呢? +(注意到最终手上的股票不能算钱, 并且你可以暂时亏损)

                    输入格式

                    -

                    第一行一个整数 $n$,表示天数

                    -

                    接下来一行 $n$ 个数,表示每天股票的价格

                    +

                    第一行一个整数 \(n\),表示天数

                    +

                    接下来一行 \(n\) +个数,表示每天股票的价格

                    输出格式

                    一行一个数,表示最大能挣的钱

                    输入1

                    @@ -496,26 +502,42 @@

                    41

                    数据范围

                    打包测试

                    -

                    测试包 1: $n \leq 15,~55$ 分

                    -

                    测试包 2: $n \leq 100,~a_i \leq 100 ,~10$分

                    -

                    测试包 3: $n \leq 5000 ,~20$分

                    -

                    测试包 4: $a_i \leq a_{i+1} ,~10$分

                    -

                    测试包 5: 无特殊限制 $5$ 分

                    -

                    对于 $100\%$ 的数据 $1 \leq a_i \leq 10^6,1 \leq n \leq 3\times 10^5$

                    +

                    测试包 1: \(n \leq 15,~55\)

                    +

                    测试包 2: \(n \leq 100,~a_i \leq 100 +,~10\)

                    +

                    测试包 3: \(n \leq 5000 ,~20\)

                    +

                    测试包 4: \(a_i \leq a_{i+1} +,~10\)

                    +

                    测试包 5: 无特殊限制 \(5\)

                    +

                    对于 \(100\%\) 的数据 \(1 \leq a_i \leq 10^6,1 \leq n \leq 3\times +10^5\)

                    题解

                    好可怕的贪心!QAQ

                    样例一某种程度上提示了我们,如何选择更好的一天卖出很重要

                    而很多时候我们只是不知道之后还有更好的机会卖出,显然反悔型贪心。

                    倒着考虑每天,维护一个可以卖的时候的大根堆

                    -

                    对于每个数 $a_i$ ,如果它比堆顶小,则把堆顶 $a_k$ 踢掉,代表在这一天买了股票,在堆顶卖出

                    -

                    并且, $a_i$ 也要入堆,因为不一定这一天买是最优的

                    -

                    当 $a_i$ 入堆以后,在未来某次决策中

                    -

                    如果有一个数 $a_j$ 加入,并踢掉了 $a_i$

                    -

                    这就等价于在 $a_j$ 买入, $a_i$ 卖出,再在 $a_i$ 买入,再在之前那个 $a_k$ 卖出

                    -

                    加粗的部分在实际决策时是不会做的,这个决策最后就等价于 $a_j$ 买 $a_k$ 卖

                    -

                    值得注意的时,此时我们还要把 $a_i$ 入堆,因为它也可以卖东西了(这个在代码里用 $\text{vis}$ 数组标记)

                    +

                    对于每个数 \(a_i\) +,如果它比堆顶小,则把堆顶 \(a_k\) +踢掉,代表在这一天买了股票,在堆顶卖出

                    +

                    并且, \(a_i\) +也要入堆,因为不一定这一天买是最优的

                    +

                    \(a_i\) +入堆以后,在未来某次决策中

                    +

                    如果有一个数 \(a_j\) 加入,并踢掉了 +\(a_i\)

                    +

                    这就等价于在 \(a_j\) 买入, +\(a_i\) 卖出,再在 \(a_i\) 买入,再在之前那个 \(a_k\) 卖出

                    +

                    加粗的部分在实际决策时是不会做的,这个决策最后就等价于 \(a_j\)\(a_k\)

                    +

                    值得注意的时,此时我们还要把 \(a_i\) +入堆,因为它也可以卖东西了(这个在代码里用 \(\text{vis}\) 数组标记)

                    然后这题就搞定了,好难啊

                    -

                    时间复杂度 $O(n\log n)$

                    +

                    时间复杂度 \(O(n\log n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -932,7 +954,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/08/mo-ni-sai-ti-jiang-jie-19/index.html b/2022/08/08/mo-ni-sai-ti-jiang-jie-19/index.html index dc4006fd69..67399217a7 100644 --- a/2022/08/08/mo-ni-sai-ti-jiang-jie-19/index.html +++ b/2022/08/08/mo-ni-sai-ti-jiang-jie-19/index.html @@ -472,18 +472,34 @@

                    模拟赛题讲解[19]

                    -

                    模拟赛题讲解[19]

                    来自 yukuai26 2022-08-08 noi.ac #2771

                    +

                    模拟赛题讲解[19]

                    +

                    来自 yukuai26 +2022-08-08 noi.ac #2771

                    题目背景

                    -

                    $\text{yukuai26}$ 喜欢爬山,所以他选择绕着山跑圈

                    +

                    \(\text{yukuai26}\) +喜欢爬山,所以他选择绕着山跑圈

                    题目描述

                    -

                    $\text{yukuai26}$ 会跑一个环形路径,每个路径有一个高度 $h$,他会顺时针或者逆时针沿着路径跑,一旦确定方向就不会换方向。

                    +

                    \(\text{yukuai26}\) +会跑一个环形路径,每个路径有一个高度 \(h\),他会顺时针或者逆时针沿着路径跑,一旦确定方向就不会换方向。

                    由于他喜欢爬山,所以如果前一次跑步时上坡,他希望下一次是下坡,同样的,如果前一次跑步时下坡,下一次他希望是上坡,并且他不希望重复经过同一地点,所以他跑过所有点时就会停下来。

                    -

                    现在 $\text{yukuai26}$ 想知道,他最多可以跑过多少点呢?

                    -

                    一句话题意,给你一个环,环上每个点有一个高度,你要找到其中最长的一条路径 $d_1,d_2….d_l$ , 满足 $d_i$和 $d_{i+1}$ 相邻且所有 $d_i$ 互不相同,且 $h_{d_1} \leq h_{d_2} \geq h_{d_3} \leq h_{d_4}…$或者 $h_{d_1} \geq h_{d_2} \leq h_{d_3} \geq h_{d_4}….$

                    -

                    问最长的 $l$ 是多少。

                    +

                    现在 \(\text{yukuai26}\) +想知道,他最多可以跑过多少点呢?

                    +

                    一句话题意,给你一个环,环上每个点有一个高度,你要找到其中最长的一条路径 +\(d_1,d_2....d_l\) , 满足 \(d_i\)\(d_{i+1}\) 相邻且所有 \(d_i\) 互不相同,且 \(h_{d_1} \leq h_{d_2} \geq h_{d_3} \leq +h_{d_4}...\)或者 \(h_{d_1} \geq h_{d_2} +\leq h_{d_3} \geq h_{d_4}....\)

                    +

                    问最长的 \(l\) 是多少。

                    输入格式

                    -

                    第一行一个数 $n$ ,表示环形路径的长度

                    -

                    第二行 $n$ 个数 $h_i$ ,表示 $i$ 号点的高度

                    +

                    第一行一个数 \(n\) +,表示环形路径的长度

                    +

                    第二行 \(n\) 个数 \(h_i\) ,表示 \(i\) 号点的高度

                    输出格式

                    一行一个数,表示最长的路径长度是多少

                    输入1

                    @@ -497,18 +513,31 @@

                    7

                    数据范围

                    -

                    对于 $50$% 的数据 $n \leq 100$

                    -

                    对于 $70$% 的数据 $n \leq 1000$

                    -

                    对于 $100$% 的数据 $n \leq 10^5$ , $h_i \leq 10^4$

                    +

                    对于 \(50\)% 的数据 \(n \leq 100\)

                    +

                    对于 \(70\)% 的数据 \(n \leq 1000\)

                    +

                    对于 \(100\)% 的数据 \(n \leq 10^5\)\(h_i \leq 10^4\)

                    题解

                    首先断环为链。

                    -

                    注意到以 $i$ 为起点的极大合法段,设其为 $[i,i+\text{sz}_i-1]$

                    -

                    不难发现 $\forall x \in [i,i+\text{sz}_i-1]$ ,$x$ 与 $i$ 重合的那种合法段是一样的。

                    -

                    这里的重合指的是以 $i$ 为起点的那个子段在 $x$ 处切一刀,分割线到 $i+\text{sz}_i-1$ 的那一段。

                    -

                    设 $f_{i,0/1}$ 表示以 $i$ 结尾的极大路径,下一个要朝上走还是朝下走

                    -

                    时间复杂度 $O(n)$

                    +\\f_{i,1} = \max\{1,(f_{i-1,0}+1)\times [a_{i-1}\ge a_i]\} +\] 时间复杂度 \(O(n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -913,7 +942,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/09/luo-gu-p1229-bian-li-wen-ti-ti-jie/index.html b/2022/08/09/luo-gu-p1229-bian-li-wen-ti-ti-jie/index.html index 81e6bb2d33..95aa032637 100644 --- a/2022/08/09/luo-gu-p1229-bian-li-wen-ti-ti-jie/index.html +++ b/2022/08/09/luo-gu-p1229-bian-li-wen-ti-ti-jie/index.html @@ -468,11 +468,13 @@

                    洛谷P1229 遍历问题 题解<
                    -

                    洛谷P1229 遍历问题 题解

                    题目链接:P1229 遍历问题

                    +

                    洛谷P1229 遍历问题 题解

                    +

                    题目链接:P1229 +遍历问题

                    题意

                    我们都很熟悉二叉树的前序、中序、后序遍历,在数据结构中常提出这样的问题:已知一棵二叉树的前序和中序遍历,求它的后序遍历,相应的,已知一棵二叉树的后序遍历和中序遍历序列你也能求出它的前序遍历。然而给定一棵二叉树的前序和后序遍历,你却不能确定其中序遍历序列,考虑如下图中的几棵二叉树:

                    -

                    +

                    所有这些二叉树都有着相同的前序遍历和后序遍历,但中序遍历却不相同。

                    输出可能的中序遍历序列的总数,结果不超过长整型数。

                    @@ -483,8 +485,12 @@

                    \(k\) 个结点 +\(u_i\) 满足 \(\left|\left\{v \mid v \in +\text{son}(u)\right\}\right|=1\)

                    +

                    则答案为 \(2^k\) 。直接 \(O(n^2)\) 跑一下就好啦!

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -873,7 +879,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/09/luo-gu-p1305-xin-er-cha-shu-ti-jie/index.html b/2022/08/09/luo-gu-p1305-xin-er-cha-shu-ti-jie/index.html index 3eb37cab2f..53aa97c1ee 100644 --- a/2022/08/09/luo-gu-p1305-xin-er-cha-shu-ti-jie/index.html +++ b/2022/08/09/luo-gu-p1305-xin-er-cha-shu-ti-jie/index.html @@ -468,7 +468,9 @@

                    洛谷P1305 新二叉树 题解<
                    -

                    洛谷P1305 新二叉树 题解

                    题目链接:P1305 新二叉树

                    +

                    洛谷P1305 新二叉树 题解

                    +

                    题目链接:P1305 +新二叉树

                    题意

                    输入一串二叉树,输出其前序遍历。

                    @@ -881,7 +883,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/09/luo-gu-p1983-noip2013-pu-ji-zu-che-zhan-fen-ji-ti-jie/index.html b/2022/08/09/luo-gu-p1983-noip2013-pu-ji-zu-che-zhan-fen-ji-ti-jie/index.html index 2a0804ef25..9f8f0f8287 100644 --- a/2022/08/09/luo-gu-p1983-noip2013-pu-ji-zu-che-zhan-fen-ji-ti-jie/index.html +++ b/2022/08/09/luo-gu-p1983-noip2013-pu-ji-zu-che-zhan-fen-ji-ti-jie/index.html @@ -472,18 +472,42 @@

                    洛谷P1983 [NOIP2013 普及组]
                    -

                    洛谷P1983 [NOIP2013 普及组] 车站分级 题解

                    题目链接:P1983 [NOIP2013 普及组] 车站分级

                    +

                    洛谷P1983 [NOIP2013 +普及组] 车站分级 题解

                    +

                    题目链接:P1983 +[NOIP2013 普及组] 车站分级

                    题意

                    -

                    一条单向的铁路线上,依次有编号为 $1, 2,\dots, n $ 的 $n$ 个火车站。每个火车站都有一个级别,最低为 $1$ 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 $x$,则始发站、终点站之间所有级别大于等于火车站 $x$ 的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)

                    -

                    例如,下表是 $5$ 趟车次的运行情况。其中,前$ 4$ 趟车次均满足要求,而第 $5$ 趟车次由于停靠了 $3$ 号火车站( $2$ 级)却未停靠途经的 $6$ 号火车站(亦为 $2$ 级)而不满足要求。

                    -

                    现有 $m$ 趟车次的运行情况(全部满足要求),试推算这 $n$ 个火车站至少分为几个不同的级别。

                    -

                    对于 $100\%$ 的数据,$1 \le n, m \le 1000$。

                    +

                    一条单向的铁路线上,依次有编号为 $1, 2,, n $ 的 \(n\) +个火车站。每个火车站都有一个级别,最低为 \(1\) +级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 +\(x\),则始发站、终点站之间所有级别大于等于火车站 +\(x\) +的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)

                    +

                    例如,下表是 \(5\) +趟车次的运行情况。其中,前$ 4$ 趟车次均满足要求,而第 \(5\) 趟车次由于停靠了 \(3\) 号火车站( \(2\) 级)却未停靠途经的 \(6\) 号火车站(亦为 \(2\) 级)而不满足要求。

                    +

                    现有 \(m\) +趟车次的运行情况(全部满足要求),试推算这 \(n\) 个火车站至少分为几个不同的级别。

                    +

                    对于 \(100\%\) 的数据,\(1 \le n, m \le 1000\)

                    -

                    考虑建图。

                    +

                    考虑建图。

                    对于一趟车,我们把所有「不停靠的站」向「停靠的站」连一条有向边

                    -

                    即边 $u \to v$ 表示 $u$ 的级别在 $v$ 之前。然后跑一遍 toposort 就好了

                    -

                    时间复杂度 $O(n^2)$ 感觉有点水,应该还可以加强的。

                    +

                    即边 \(u \to v\) 表示 \(u\) 的级别在 \(v\) 之前。然后跑一遍 toposort 就好了

                    +

                    时间复杂度 \(O(n^2)\) +感觉有点水,应该还可以加强的。

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -903,7 +927,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/09/mo-ni-sai-ti-jiang-jie-20/index.html b/2022/08/09/mo-ni-sai-ti-jiang-jie-20/index.html index 7b40110fc5..9f9fec63e9 100644 --- a/2022/08/09/mo-ni-sai-ti-jiang-jie-20/index.html +++ b/2022/08/09/mo-ni-sai-ti-jiang-jie-20/index.html @@ -472,13 +472,29 @@

                    模拟赛题讲解[20]

                    -

                    模拟赛题讲解[20]

                    来自 AprilGrimoire 2022-08-09 noi.ac #2775

                    +

                    模拟赛题讲解[20]

                    +

                    来自 AprilGrimoire +2022-08-09 noi.ac #2775

                    题目描述

                    小 Z 想出几道送分题。

                    -

                    小 Z 有一列脑洞(共 $m(m \leq 10^9)$ 个)。脑洞分为白色与黑色,白色脑洞会让题目难度降低,黑色脑洞会让题目难度提高。因为小 Z 很懒,她决定将脑洞分为连续的若干段,将每段缝合成一道送分题。为了达到雨露均沾的送分效果,小 Z 希望每段中黑色脑洞所占的比例相同。小 Z 想知道她最多能缝合出几道题?

                    +

                    小 Z 有一列脑洞(共 \(m(m \leq +10^9)\) +个)。脑洞分为白色与黑色,白色脑洞会让题目难度降低,黑色脑洞会让题目难度提高。因为小 +Z +很懒,她决定将脑洞分为连续的若干段,将每段缝合成一道送分题。为了达到雨露均沾的送分效果,小 +Z 希望每段中黑色脑洞所占的比例相同。小 Z +想知道她最多能缝合出几道题?

                    输入格式

                    -

                    第一行一个整数 $n(n \leq 10^6)$

                    -

                    接下来 $n$ 行按顺序描述了小 Z 的脑洞。其中第 $i$ 行一个正整数 $r_i$ 和字符 $w_i$。这表示接下来有连续 $r_i$个颜色为 $w_i$ 的脑洞( W 代表白色,B 代表黑色)。

                    +

                    第一行一个整数 \(n(n \leq +10^6)\)

                    +

                    接下来 \(n\) 行按顺序描述了小 Z +的脑洞。其中第 \(i\) 行一个正整数 \(r_i\) 和字符 \(w_i\)。这表示接下来有连续 \(r_i\)个颜色为 \(w_i\) 的脑洞( W +代表白色,B 代表黑色)。

                    输出格式

                    一行一个整数,表示小 Z 最多能缝合出的题目数。

                    输入1

                    @@ -528,19 +544,26 @@

                    3

                    数据规模与约定

                    时空限制:1s,512MiB

                    -

                    对于所有数据,保证 $n \leq 10^6$

                    -

                    子任务一(25pts):满足 $n \leq 20, r_i=1$

                    -

                    子任务二(25pts):满足 $n \leq 1000, r_i=1$

                    -

                    子任务三(20pts):满足 $n \leq 1000$

                    +

                    对于所有数据,保证 \(n \leq +10^6\)

                    +

                    子任务一(25pts):满足 \(n \leq 20, +r_i=1\)

                    +

                    子任务二(25pts):满足 \(n \leq 1000, +r_i=1\)

                    +

                    子任务三(20pts):满足 \(n \leq +1000\)

                    子任务四(30pts):无特殊约定

                    题解

                    注意到其实这个比例是固定的,我们直接暴力扫即可

                    也就是说,每一段的比例等于整个序列的比例

                    一个比较有趣的理解是,

                    -

                    DNA双链上,每一条链都有 $20\%$ 的 $A$ ,那么整个双链就有 $20\%$ 的 $A$

                    +

                    DNA双链上,每一条链都有 \(20\%\) 的 +\(A\) ,那么整个双链就有 \(20\%\)\(A\)

                    那就直接从左往右扫,然后每ok就立刻分为一段

                    这里用gcd什么的可以更精确,但是我选择double

                    -

                    时间复杂度 $O(n)$

                    +

                    时间复杂度 \(O(n)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -983,7 +1006,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/11/luo-gu-p2292-hnoi2004-l-yu-yan-ti-jie/index.html b/2022/08/11/luo-gu-p2292-hnoi2004-l-yu-yan-ti-jie/index.html index 359de9baf3..f5c236f84e 100644 --- a/2022/08/11/luo-gu-p2292-hnoi2004-l-yu-yan-ti-jie/index.html +++ b/2022/08/11/luo-gu-p2292-hnoi2004-l-yu-yan-ti-jie/index.html @@ -476,28 +476,84 @@

                    洛谷P2292 [HNOI2004] L 语言
                    -

                    洛谷P2292 [HNOI2004] L 语言 题解

                    题目链接:P2292 [HNOI2004] L 语言

                    +

                    洛谷P2292 [HNOI2004] L 语言 +题解

                    +

                    题目链接:P2292 +[HNOI2004] L 语言

                    题意

                    -

                    标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的。现在你要处理的就是一段没有标点的文章。

                    -

                    一段文章 $T$ 是由若干小写字母构成。一个单词 $W$ 也是由若干小写字母构成。一个字典 $D$ 是若干个单词的集合。我们称一段文章 $T$ 在某个字典 $D$ 下是可以被理解的,是指如果文章 $T$ 可以被分成若干部分,且每一个部分都是字典 $D$ 中的单词。

                    -

                    例如字典 $D$ 中包括单词 $\texttt{is},\texttt{name},\texttt{what},\texttt{your}$,则文章 $\texttt{whatisyourname}$ 是在字典 $D$ 下可以被理解的,因为它可以分成 $4$ 个单词:$\texttt{what},\texttt{is},\texttt{your},\texttt{name}$,且每个单词都属于字典 $D$,而文章 $\texttt{whatisyouname}$ 在字典 $D$ 下不能被理解,但可以在字典 $D’=D\cup\{\texttt{you}\}$ 下被理解。这段文章的一个前缀 $\texttt{whatis}$,也可以在字典 $D$ 下被理解,而且是在字典 $D$ 下能够被理解的最长的前缀。

                    -

                    给定一个字典 $D$,你的程序需要判断若干段文章在字典 $D$ 下是否能够被理解。并给出其在字典 $D$ 下能够被理解的最长前缀的位置。

                    +

                    标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的。现在你要处理的就是一段没有标点的文章。

                    +

                    一段文章 \(T\) +是由若干小写字母构成。一个单词 \(W\) +也是由若干小写字母构成。一个字典 \(D\) +是若干个单词的集合。我们称一段文章 \(T\) 在某个字典 \(D\) 下是可以被理解的,是指如果文章 \(T\) +可以被分成若干部分,且每一个部分都是字典 \(D\) 中的单词。

                    +

                    例如字典 \(D\) 中包括单词 \(\texttt{is},\texttt{name},\texttt{what},\texttt{your}\),则文章 +\(\texttt{whatisyourname}\) 是在字典 +\(D\) 下可以被理解的,因为它可以分成 +\(4\) 个单词:\(\texttt{what},\texttt{is},\texttt{your},\texttt{name}\),且每个单词都属于字典 +\(D\),而文章 \(\texttt{whatisyouname}\) 在字典 \(D\) 下不能被理解,但可以在字典 \(D'=D\cup\{\texttt{you}\}\) +下被理解。这段文章的一个前缀 \(\texttt{whatis}\),也可以在字典 \(D\) 下被理解,而且是在字典 \(D\) 下能够被理解的最长的前缀。

                    +

                    给定一个字典 \(D\),你的程序需要判断若干段文章在字典 \(D\) 下是否能够被理解。并给出其在字典 \(D\) 下能够被理解的最长前缀的位置。

                      -
                    • 对于 $100\%$ 的数据,保证 $1 \leq n \leq 20$,$1 \leq m \leq 50$,$1 \leq |s| \leq 20$,$1 \leq |t| \leq 2 \times 10^6$,$s$ 与 $t$ 中均只含小写英文字母。
                    • +
                    • 对于 \(100\%\) 的数据,保证 \(1 \leq n \leq 20\)\(1 \leq m \leq 50\)\(1 \leq |s| \leq 20\)\(1 \leq |t| \leq 2 \times 10^6\)\(s\)\(t\) 中均只含小写英文字母。
                    -

                    设 $f_i$ 表示前 $i$ 个字符是否可行,则有

                    -

                    其中 $t[j+1,i]$ 表示文本串的 $[j+1,i]$ ,$S$ 表示所有 $s_i$ 构成的不可重集

                    -

                    注意到 $1\le |s_i| \le 20$ ,因此我们可以建 $\tt{AC}$ 自动机然后暴力转移。

                    -

                    具体地,我们把所有 $s_i$ 都插入 $\tt{AC}$ 自动机,

                    -

                    然后预处理出每个结点在 $\text{fail}$ 树上的 $i (i \le 20)$ 级祖先(认为儿子存在指向父亲的边)

                    -

                    这个信息可以压缩为一个二进制数,不妨设为 $g_u$ 。

                    -

                    转移时,设当前所在的结点为 $u$ ,同时维护 $f_j(i-20 < j \le i)$ 的状态。

                    -

                    后者又可以压到一个二进制数 $x$ 里,即 $x$ 二进制下第 $k(0\le k \le 20)$ 位为 $1$ 表示 $f_{i-k-1}=\text{True}$ 。

                    -

                    转移时,若 $g_u \text{ and } x = \text{True}$ ,则 $f_i=\text{True}$ 。这里的 $\text{and}$ 表示二进制下按位与。

                    -

                    时间复杂度 $O(20 \times \sum s_i + \sum t_i)$

                    +

                    \(f_i\) 表示前 \(i\) 个字符是否可行,则有 \[ +f_i = \bigvee\limits_{j-\max\{|s_i|\}}^{i-1} \left\{f_j +\,\land\,t{[j+1,i]} \in S \right\} +\] 其中 \(t[j+1,i]\) +表示文本串的 \([j+1,i]\)\(S\) 表示所有 \(s_i\) 构成的不可重集

                    +

                    注意到 \(1\le |s_i| \le 20\) +,因此我们可以建 \(\tt{AC}\) +自动机然后暴力转移。

                    +

                    具体地,我们把所有 \(s_i\) 都插入 +\(\tt{AC}\) 自动机,

                    +

                    然后预处理出每个结点在 \(\text{fail}\) 树上的 \(i (i \le 20)\) +级祖先(认为儿子存在指向父亲的边)

                    +

                    这个信息可以压缩为一个二进制数,不妨设为 \(g_u\)

                    +

                    转移时,设当前所在的结点为 \(u\) +,同时维护 \(f_j(i-20 < j \le i)\) +的状态。

                    +

                    后者又可以压到一个二进制数 \(x\) +里,即 \(x\) 二进制下第 \(k(0\le k \le 20)\) 位为 \(1\) 表示 \(f_{i-k-1}=\text{True}\)

                    +

                    转移时,若 \(g_u \text{ and } x = +\text{True}\) ,则 \(f_i=\text{True}\) 。这里的 \(\text{and}\) 表示二进制下按位与。

                    +

                    时间复杂度 \(O(20 \times \sum s_i + \sum +t_i)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -952,7 +1008,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/12/uoj66-xin-nian-de-qiao-ke-li-bang-ti-jie/index.html b/2022/08/12/uoj66-xin-nian-de-qiao-ke-li-bang-ti-jie/index.html index 6807195da4..0049c12a59 100644 --- a/2022/08/12/uoj66-xin-nian-de-qiao-ke-li-bang-ti-jie/index.html +++ b/2022/08/12/uoj66-xin-nian-de-qiao-ke-li-bang-ti-jie/index.html @@ -472,18 +472,33 @@

                    UOJ66 新年的巧克力棒 题
                    -

                    UOJ66 新年的巧克力棒 题解

                    题目链接:#66. 新年的巧克力棒

                    +

                    UOJ66 新年的巧克力棒 题解

                    +

                    题目链接:#66. +新年的巧克力棒

                    题意:马上就要到羊年了,羊村一片欢腾,懒羊羊则懒洋洋地躺在草坪上吃新年的巧克力棒。

                    -

                    他手上的巧克力棒是个由 $n$ 个巧克力单元格组成的长度为 $n$ 的长条,现在懒羊羊想把巧克力棒掰开成一个个小单元格。

                    -

                    初始时懒羊羊会把这根巧克力棒丢在草坪上,然后每次懒羊羊会从草坪上拿起一根长度大于 $1$ 的巧克力棒,然后从某两个相邻的单元格的间隙处掰开变成两根巧克力棒,然后把这两根巧克力棒丢在草坪上。懒羊羊初始愉悦值为 $0$,每次掰开巧克力棒后如果这两根巧克力棒长度相等,那么懒羊羊将提升 $1$ 点愉悦值。

                    -

                    当然,草坪上全是长度为 $1$ 的巧克力棒时懒羊羊就会停止操作。现在懒羊羊想知道,他能获得的愉悦值最多是多少?

                    +

                    他手上的巧克力棒是个由 \(n\) +个巧克力单元格组成的长度为 \(n\) +的长条,现在懒羊羊想把巧克力棒掰开成一个个小单元格。

                    +

                    初始时懒羊羊会把这根巧克力棒丢在草坪上,然后每次懒羊羊会从草坪上拿起一根长度大于 +\(1\) +的巧克力棒,然后从某两个相邻的单元格的间隙处掰开变成两根巧克力棒,然后把这两根巧克力棒丢在草坪上。懒羊羊初始愉悦值为 +\(0\),每次掰开巧克力棒后如果这两根巧克力棒长度相等,那么懒羊羊将提升 +\(1\) 点愉悦值。

                    +

                    当然,草坪上全是长度为 \(1\) +的巧克力棒时懒羊羊就会停止操作。现在懒羊羊想知道,他能获得的愉悦值最多是多少?

                    -

                    设 $f_i$ 表示长度为 $i$ 的巧(áo)克(lì)力(gèi)能获得的最大价值,则

                    -

                    然后这东西是 $O(n^2)$ 的,看上去也没什么能优化的

                    -

                    注意到有 $n \le 10^3$ 的部分分,说明我们推的柿子是大概率正确的

                    -

                    好像没什么思路,但是这个 $n\le 10^9$ 印发了我们的好奇心(确信

                    +

                    \(f_i\) 表示长度为 \(i\) +的巧(áo)克(lì)力(gèi)能获得的最大价值,则 \[ +f_i = \max\limits_{1 \le j < i}\left\{f_j + f_{i-j} + [j=i-j]\right\} +\] 然后这东西是 \(O(n^2)\) +的,看上去也没什么能优化的

                    +

                    注意到有 \(n \le 10^3\) +的部分分,说明我们推的柿子是大概率正确的

                    +

                    好像没什么思路,但是这个 \(n\le +10^9\) 印发了我们的好奇心(确信

                    然后打打表试试,打表代码如下:

                    #include <iostream>
                     #include <string>
                    @@ -535,9 +550,17 @@ 

                    -

                    观察到 $1,2,4,8$ 都减了 $1$ ,而 $6,10$ 减了 $2$

                    -

                    前面这个又是 $2$ 的次幂,难道是??

                    -

                    答案确实是 $n - \text{popc}(n)$ ,这里的 $\text{popc}(n)$ 表示 $n$ 在二进制下的 $1$ 的个数

                    +

                    观察到 \(1,2,4,8\) 都减了 \(1\) ,而 \(6,10\) 减了 \(2\)

                    +

                    前面这个又是 \(2\) +的次幂,难道是??

                    +

                    答案确实是 \(n - +\text{popc}(n)\) ,这里的 \(\text{popc}(n)\) 表示 \(n\) 在二进制下的 \(1\) 的个数

                    于是超短码就出现啦:

                    #include <cstdio>
                     signed main()
                    @@ -547,7 +570,8 @@ 

                    scanf("%d",&y),printf("%d\n",y-__builtin_popcount(y)); return 0; }

                    -

                    至于为什么答案是这个,可以看看这里的证明 link

                    +

                    至于为什么答案是这个,可以看看这里的证明 link

                    @@ -917,7 +941,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/14/cf1073e-segment-sum-ti-jie/index.html b/2022/08/14/cf1073e-segment-sum-ti-jie/index.html index 363803414c..938d8e042b 100644 --- a/2022/08/14/cf1073e-segment-sum-ti-jie/index.html +++ b/2022/08/14/cf1073e-segment-sum-ti-jie/index.html @@ -472,20 +472,34 @@

                    CF1073E Segment Sum 题解

                    -

                    CF1073E Segment Sum 题解

                    题目链接:CF1073E Segment Sum

                    +

                    CF1073E Segment Sum 题解

                    +

                    题目链接:CF1073E +Segment Sum

                    题意

                    -

                    给定 $l,r,k$ ,求 $[l,r]$ 有多少个数满足「不包含超过 $k$ 个数码」,输出他们的和 $\bmod {998244353}$

                    -

                    $0 \le k\le 10,~1\le l ,r \le 10^{18}$

                    +

                    给定 \(l,r,k\) ,求 \([l,r]\) 有多少个数满足「不包含超过 \(k\) 个数码」,输出他们的和 \(\bmod {998244353}\)

                    +

                    \(0 \le k\le 10,~1\le l ,r \le +10^{18}\)

                    做了这题才发现自己对数位dp的掌握很烂

                    其实很水,用个状态 st 记录每个数码是否出现过

                    -

                    设 $f_{i,j}$ 表示只考虑前 $i$ 位(从低位向高位计数),数码状态为 $j$ 的数字的个数

                    -

                    设 $g_{i,j}$ 表示只考虑前 $i$ 位(从低位向高位计数),数码状态为 $j$ 的数字的和

                    -

                    -

                    其中 $d$ 表示第 $i-1$ 位要填啥, $j^{\prime}$ 表示转移后的 $j$ ,懒得讲,直接看代码

                    +

                    \(f_{i,j}\) 表示只考虑前 \(i\) 位(从低位向高位计数),数码状态为 +\(j\) 的数字的个数

                    +

                    \(g_{i,j}\) 表示只考虑前 \(i\) 位(从低位向高位计数),数码状态为 +\(j\) 的数字的和

                    +

                    \[ +f_{i,j} = \sum f_{i-1,j^{\prime}}\\ +\\g_{i,j} = \sum 10^{i-1} \times d \times f_{i-1,j^{\prime}} + +g_{i,j^{\prime}} +\] 其中 \(d\) 表示第 \(i-1\) 位要填啥, \(j^{\prime}\) 表示转移后的 \(j\)懒得讲,直接看代码

                    注意考虑一下前导零什么的就好了(因为这里前导零会影响数码出现次数

                    代码:

                    #include <iostream>
                    @@ -909,7 +923,7 @@ 

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/14/sp10606-balnum-balanced-numbers-ti-jie/index.html b/2022/08/14/sp10606-balnum-balanced-numbers-ti-jie/index.html index c65999f4cf..bad9fccf50 100644 --- a/2022/08/14/sp10606-balnum-balanced-numbers-ti-jie/index.html +++ b/2022/08/14/sp10606-balnum-balanced-numbers-ti-jie/index.html @@ -472,20 +472,29 @@

                    SP10606 BALNUM - Balanced Number
                    -

                    SP10606 BALNUM - Balanced Numbers 题解

                    题目链接:SP10606 BALNUM - Balanced Numbers

                    +

                    SP10606 BALNUM - +Balanced Numbers 题解

                    +

                    题目链接:SP10606 +BALNUM - Balanced Numbers

                    题意

                    一个数被称为是平衡的数

                    -

                    当且仅当对于所有出现过的数位(即 $0$ 到 $9$ )

                    +

                    当且仅当对于所有出现过的数位(即 \(0\)\(9\)

                    每个偶数出现奇数次,每个奇数出现偶数次。

                    -

                    给定 $A,B$,请统计出 $[A,B]$ 内所有平衡数的个数。

                    -

                    $1\leq A\leq B\leq 10^{19}$

                    +

                    给定 \(A,B\),请统计出 \([A,B]\) 内所有平衡数的个数。

                    +

                    \(1\leq A\leq B\leq 10^{19}\)

                    数位dp就是套板子,然后我现在还没记住板子(

                    考虑记录每个数的奇偶性,用状态 st 表示

                    -

                    第 $i(1\le i \le 9)$ 位为 $0$ 表示出现次数为偶,否则为 $1$

                    +

                    \(i(1\le i \le 9)\) 位为 \(0\) 表示出现次数为偶,否则为 \(1\)

                    再记录一个 vis ,表示这个数是否出现了

                    -

                    这两个东西可以压到一个 pair 里,这样方便开数组(和其他题解学的

                    +

                    这两个东西可以压到一个 pair +里,这样方便开数组(和其他题解学的

                    因为这道题是统计出现次数的,所以前导零会影响计数

                    所以还要记录一下有没有前导零,代码里用 qd0 表示了(

                    最后 check 一下状态啥的,就好了(

                    @@ -923,7 +932,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/15/cf1491f-magnets-ti-jie/index.html b/2022/08/15/cf1491f-magnets-ti-jie/index.html index fb2d02731c..b73fb345c7 100644 --- a/2022/08/15/cf1491f-magnets-ti-jie/index.html +++ b/2022/08/15/cf1491f-magnets-ti-jie/index.html @@ -472,47 +472,91 @@

                    CF1491F Magnets 题解

                    -

                    CF1491F Magnets 题解

                    题目链接:CF1491F Magnets

                    +

                    CF1491F Magnets 题解

                    +

                    题目链接:CF1491F +Magnets

                    题意

                    -

                    _这是一个交互题。_

                    -

                    早苗有 $n$ 块磁石,编号为 $1,2,\cdots,n$。每块磁石的磁极可能是正极,负极,也可能没有磁性。她希望你能帮她找出所有没有磁性的磁石。

                    +

                    这是一个交互题。

                    +

                    早苗有 \(n\) 块磁石,编号为 \(1,2,\cdots,n\)。每块磁石的磁极可能是正极,负极,也可能没有磁性。她希望你能帮她找出所有没有磁性的磁石。

                    万幸的是,你有一台磁力检测仪。你每次可以将每个磁石放在这台机器的左托盘,右托盘或者不放。

                    -

                    机器将会返回此时的磁力强度。设托盘左边有 $n_1$ 个磁石为正极,$s_1$ 个磁石为负极,托盘右边中有 $n_2$ 磁石为正极,$s_2$ 个磁石为负极,则返回的磁力强度为 $ n_1n_2+s_1s_2-n_1s_2-n_2s_1 $。

                    -

                    如果一次测试中磁力强度的绝对值大于 $n$,这台机器就会坏掉。

                    -

                    你需要在 $n+\lfloor\log_2n\rfloor$ 次测试内找到所有没有磁性的磁石的编号,同时不能弄坏机器。

                    -

                    保证存在至少 $2$ 块磁石有磁性且至少 $1$ 块磁石没有磁性。

                    -
                    +

                    机器将会返回此时的磁力强度。设托盘左边有 \(n_1\) 个磁石为正极,\(s_1\) 个磁石为负极,托盘右边中有 \(n_2\) 磁石为正极,\(s_2\) 个磁石为负极,则返回的磁力强度为 $ +n_1n_2+s_1s_2-n_1s_2-n_2s_1 $。

                    +

                    如果一次测试中磁力强度的绝对值大于 \(n\),这台机器就会坏掉。

                    +

                    你需要在 \(n+\lfloor\log_2n\rfloor\) +次测试内找到所有没有磁性的磁石的编号,同时不能弄坏机器。

                    +

                    保证存在至少 \(2\) +块磁石有磁性且至少 \(1\) +块磁石没有磁性。

                    +

                    输入格式

                    -

                    仅一行,包含一个正整数 $T$($1\leq T\leq 100$),表示数据的组数。

                    -
                    -

                    对于每组数据,你需要先读入一个正整数 $n$($3\leq n\leq 2000$),代表磁石的数量,保证每组数据的 $n$ 之和不超过 $2000$。

                    +

                    仅一行,包含一个正整数 \(T\)\(1\leq T\leq 100\)),表示数据的组数。

                    +
                    +

                    对于每组数据,你需要先读入一个正整数 \(n\)\(3\leq +n\leq 2000\)),代表磁石的数量,保证每组数据的 \(n\) 之和不超过 \(2000\)

                    接下来,你可以向交互库提出若干次询问。对于每个询问,你需要输出三行:

                      -
                    • 第一行输出 ? L R,其中 $L$ 代表放在左托盘的磁石个数,$R$ 代表放在右托盘的磁石个数。
                    • -
                    • 第二行输出 $L$ 个数 $a_1,a_2,\cdots,a_L$,代表放在左托盘的磁石编号。
                    • -
                    • 第三行输出 $R$ 个数 $b_1,b_2,\cdots,b_R$,代表放在右托盘的磁石编号。
                    • +
                    • 第一行输出 ? L R,其中 \(L\) 代表放在左托盘的磁石个数,\(R\) 代表放在右托盘的磁石个数。
                    • +
                    • 第二行输出 \(L\) 个数 \(a_1,a_2,\cdots,a_L\),代表放在左托盘的磁石编号。
                    • +
                    • 第三行输出 \(R\) 个数 \(b_1,b_2,\cdots,b_R\),代表放在右托盘的磁石编号。
                    -

                    显然,你需要保证 $a_i\neq b_j$。

                    +

                    显然,你需要保证 \(a_i\neq +b_j\)

                    接下来,你需要刷新缓冲区。

                    (刷新缓冲区教程)

                    -

                    最后,你需要读入一个整数 $F$,代表这次测试的磁性大小。

                    -

                    如果你已经确定了答案,输出一行 ! k A 表示答案,其中 $k$ 代表没有磁性的磁石数量,$A$ 为一个长度为 $k$ 的序列,代表所有没有磁性的磁石编号。如果这不是最后一组数据,你应该继续处理下一组数据。如果这是最后一组数据,你应该立刻结束你的程序。

                    -

                    此题中,交互库是固定的。 每个磁石的状态不随着你的测试而改变。

                    +

                    最后,你需要读入一个整数 \(F\),代表这次测试的磁性大小。

                    +

                    如果你已经确定了答案,输出一行 ! k A 表示答案,其中 +\(k\) 代表没有磁性的磁石数量,\(A\) 为一个长度为 \(k\) +的序列,代表所有没有磁性的磁石编号。如果这不是最后一组数据,你应该继续处理下一组数据。如果这是最后一组数据,你应该立刻结束你的程序。

                    +

                    此题中,交互库是固定的。 +每个磁石的状态不随着你的测试而改变。

                    -

                    先化简一下柿子,可得 $(n_1 - s_1) (n_2 - s_2)$

                    -

                    不难发现,只要天平一侧没有带磁极的,返回值一定是 $0$

                    -

                    但是直接求解依旧相当困难,因为返回值为 $0$ 的情况太多

                    -

                    考虑返回值不为 $0$ 时的情况,此时左右必定各有不少于 $1$ 个带磁极的

                    +

                    先化简一下柿子,可得 \((n_1 - s_1) (n_2 - +s_2)\)

                    +

                    不难发现,只要天平一侧没有带磁极的,返回值一定是 \(0\)

                    +

                    但是直接求解依旧相当困难,因为返回值为 \(0\) 的情况太多

                    +

                    考虑返回值不为 \(0\) +时的情况,此时左右必定各有不少于 \(1\) +个带磁极的

                    这意味着如果我们找到了一个带磁极的,问题求解将变得相当容易

                    -

                    于是我们枚举一个 $i$ ,每次询问 $A=\{i\},B=\{1,\cdots,i-1\}$ ,直到返回值不为 $0$ 。

                    -

                    这样的好处在于:如果返回值不为 $0$ ,则 $i$ 一定带磁极

                    -

                    知道 $i$ 带磁极,那 $i+1$ 到 $n$ 的就可以一个个查了

                    -

                    同时可知 $\{1,\dots,i-1\}$ 中必定有且仅有一个带磁极的

                    -

                    这个带磁极的可以通过询问 $A=\{i\}$ 和 $B = \{1,\cdots,j\}$ ,这里的 $j$ 是枚举的

                    -

                    仔细观察可以发现,这个 $j$ 没必要一个个询问,它是有单调性的。

                    -

                    时间复杂度 $O(Qn)$ ,然后我们就可以愉快地通过此题啦(

                    +

                    于是我们枚举一个 \(i\) ,每次询问 +\(A=\{i\},B=\{1,\cdots,i-1\}\) +,直到返回值不为 \(0\)

                    +

                    这样的好处在于:如果返回值不为 \(0\) +,则 \(i\) 一定带磁极

                    +

                    知道 \(i\) 带磁极,那 \(i+1\)\(n\) 的就可以一个个查了

                    +

                    同时可知 \(\{1,\dots,i-1\}\) +中必定有且仅有一个带磁极的

                    +

                    这个带磁极的可以通过询问 \(A=\{i\}\) +和 \(B = \{1,\cdots,j\}\) ,这里的 +\(j\) 是枚举的

                    +

                    仔细观察可以发现,这个 \(j\) +没必要一个个询问,它是有单调性的。 \[ +\texttt{总查询数} =n-1 + \left\lceil{\log_2 n}\right\rceil \le n + +\left\lfloor{\log_2 n}\right\rfloor +\] 时间复杂度 \(O(Qn)\) +,然后我们就可以愉快地通过此题啦(

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -930,7 +974,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/15/luo-gu-p2396-yyy-loves-maths-vii-ti-jie/index.html b/2022/08/15/luo-gu-p2396-yyy-loves-maths-vii-ti-jie/index.html index ea545f53ef..bc2ad480d7 100644 --- a/2022/08/15/luo-gu-p2396-yyy-loves-maths-vii-ti-jie/index.html +++ b/2022/08/15/luo-gu-p2396-yyy-loves-maths-vii-ti-jie/index.html @@ -472,30 +472,58 @@

                    洛谷P2396 yyy loves Maths VII
                    -

                    洛谷P2396 yyy loves Maths VII 题解

                    题目链接:P2396 yyy loves Maths VII

                    +

                    洛谷P2396 yyy loves Maths +VII 题解

                    +

                    题目链接:P2396 yyy +loves Maths VII

                    题意

                    一群同学在和 yyy 玩一个游戏。

                    -

                    每次,他们会给 yyy $n$ 张卡片,卡片上有数字,所有的数字都是“幸运数字”,我们认为第 $i$ 张卡片上数字是 $a_{i}$。

                    -

                    每次 yyy 可以选择向前走 $a_{i}$ 步并且丢掉第 $i$ 张卡片。当他手上没有卡片的时候他就赢了。

                    -

                    但是呢,大家对“厄运数字”的位置布置下了陷阱,如果 yyy 停在这个格子上,那么他就输了。注意:即使到了终点,但是这个位置是厄运数字,那么也输了。

                    +

                    每次,他们会给 yyy \(n\) +张卡片,卡片上有数字,所有的数字都是“幸运数字”,我们认为第 \(i\) 张卡片上数字是 \(a_{i}\)

                    +

                    每次 yyy 可以选择向前走 \(a_{i}\) +步并且丢掉第 \(i\) +张卡片。当他手上没有卡片的时候他就赢了。

                    +

                    但是呢,大家对“厄运数字”的位置布置下了陷阱,如果 yyy +停在这个格子上,那么他就输了。注意:即使到了终点,但是这个位置是厄运数字,那么也输了。

                    现在,有些同学开始问:yyy 有多大的概率会赢呢?

                    -

                    大家觉得这是个好问题,有人立即让 yyy 写个程序:“电脑运行速度很快!$24$ 的阶乘也不过就 $620\,448\,401\,733\,239\,439\,360\,000$,yyy 你快写个程序来算一算。”

                    -

                    yyy 表示很无语,他表示他不想算概率,最多算算赢的方案数,而且是对 $10^9+7$ 取模后的值。

                    +

                    大家觉得这是个好问题,有人立即让 yyy +写个程序:“电脑运行速度很快!\(24\) +的阶乘也不过就 \(620\,448\,401\,733\,239\,439\,360\,000\),yyy +你快写个程序来算一算。”

                    +

                    yyy 表示很无语,他表示他不想算概率,最多算算赢的方案数,而且是对 +\(10^9+7\) 取模后的值。

                    大家都不会写程序,只好妥协。

                    -

                    但是这时候 yyy 为难了,$24!$ 太大了,要跑好长时间。

                    +

                    但是这时候 yyy 为难了,\(24!\) +太大了,要跑好长时间。

                    他时间严重不够!需要你的帮助!

                    由于 yyy 人格分裂,某个数字可能既属于幸运数字又属于厄运数字。

                    -

                    $50\%$ 的数据 $n \leq 23$ , $100\%$ 的数据 $n \leq 24$。

                    +

                    \(50\%\) 的数据 \(n \leq 23\)\(100\%\) 的数据 \(n \leq 24\)

                    -

                    注意到 $n \le 24$ ,并且每张牌只能用一次,考虑状压dp。

                    -

                    对于每个状态 $i$ ,枚举它是从哪个地方转移来的

                    -

                    具体地,设 $f_i$ 表示状态为 $i$ 的方案数,则

                    -

                    显然答案为 $f_{\text{mx}} , \text{mx}= 2^n -1$。

                    -

                    同时我们还要记录一个 $\text{dis}_i$ 表示 $i$ 状态对应的路径长

                    -

                    当 $\text{dis}_i$ 为“厄运数字”时,不转移 $f_i$ ,即 $f_i = 0$ 。

                    -

                    时间复杂度 $O(n 2^n)$ ,卡常题。

                    +

                    注意到 \(n \le 24\) +,并且每张牌只能用一次,考虑状压dp。

                    +

                    对于每个状态 \(i\) +,枚举它是从哪个地方转移来的

                    +

                    具体地,设 \(f_i\) 表示状态为 \(i\) 的方案数,则 \[ +f_i = \sum f_{i \setminus \{j\}} \times [j \in i] +\] 显然答案为 \(f_{\text{mx}} , +\text{mx}= 2^n -1\)

                    +

                    同时我们还要记录一个 \(\text{dis}_i\) 表示 \(i\) 状态对应的路径长

                    +

                    \(\text{dis}_i\) +为“厄运数字”时,不转移 \(f_i\) ,即 +\(f_i = 0\)

                    +

                    时间复杂度 \(O(n 2^n)\) +,卡常题。

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -905,7 +933,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/16/xiao-lan-shu-16.1/index.html b/2022/08/16/xiao-lan-shu-16.1/index.html index d1d5d60c2c..be19069bfd 100644 --- a/2022/08/16/xiao-lan-shu-16.1/index.html +++ b/2022/08/16/xiao-lan-shu-16.1/index.html @@ -468,131 +468,255 @@

                    小蓝书 16.1

                    -

                    16.1

                    施工中,咕咕咕….

                    -

                    计数原理

                    加法原理

                    设完成某件事有 $m$ 类不同的方法,

                    -

                    第 $i(1\le i \le m)$ 类方法中有 $n_i$ 种不同的方法,则完成这件事共有

                    -

                    种方法。

                    +

                    16.1

                    +

                    施工中,咕咕咕....

                    +

                    计数原理

                    +

                    加法原理

                    +

                    设完成某件事有 \(m\) +类不同的方法,

                    +

                    \(i(1\le i \le m)\) 类方法中有 +\(n_i\) 种不同的方法,则完成这件事共有 +\[ +n_1 + n_2 + \cdots + n_m +\] 种方法。

                    -

                    在严格化的数学中,加法原理是有关集合大小的事实。1

                    +

                    在严格化的数学中,加法原理是有关集合大小的事实。1

                    断言任意有限多个两两互斥的集合大小之和,等于其并集的大小

                    -

                    以符号表示为,若集合 $S_1,S_2,\cdots,S_n$ 两两互斥,则有

                    -

                    容斥原理可以视为加法原理的推广。

                    +

                    以符号表示为,若集合 \(S_1,S_2,\cdots,S_n\) 两两互斥,则有 \[ +|S_1|+|S_2| + \cdots + |S_n| = |S_1 \cup S_2 \cup \cdots S_n| +\] 容斥原理可以视为加法原理的推广。

                    -

                    乘法原理

                    设完成某件事需要 $m$ 个步骤,

                    -

                    做第 $i$ 步有 $n_i$ 种不同的方法,则完成这件事共有

                    -
                    -

                    在集合论中,乘法原理可以设为基数乘积的定义。2

                    -

                    对于集合 $S_1,S_2,\cdots,S_n$ ,以 $|S_i|$ 表示 $S_i$ 的元素个数(基数),则有

                    -

                    其中 $S_i \times S_j$ 表示笛卡尔积。 $S_i$ 可以是无穷集。参见基数和选择公理。

                    +

                    乘法原理

                    +

                    设完成某件事需要 \(m\) 个步骤,

                    +

                    做第 \(i\) 步有 \(n_i\) 种不同的方法,则完成这件事共有 \[ +n_1 \times n_2 \times \cdots \times n_m +\]

                    +
                    +

                    在集合论中,乘法原理可以设为基数乘积的定义。2

                    +

                    对于集合 \(S_1,S_2,\cdots,S_n\) ,以 +\(|S_i|\) 表示 \(S_i\) 的元素个数(基数),则有 \[ +|S_1| \times |S_2| \times \cdots \times |S_n| = |S_1 \times S_2 \times +\cdots \times S_n| +\] 其中 \(S_i \times S_j\) +表示笛卡尔积。 \(S_i\) +可以是无穷集。参见基数和选择公理。

                    -
                    -

                    排列组合

                    下面两个指的是无重复的排列与组合

                    -

                    排列数

                    从 $n$ 个不同元素中取 $k(k\le n)$ 个不同元素,并按一定顺序排成一列的方案数

                    -

                    也可写作 $\mathrm{P}_{n}^{k}$ 或直接用组合数写法 $k! \times \binom{n}{k}$

                    -

                    组合数

                    从 $n$ 个不同元素中取 $k(k \le n)$ 个不同元素的方案数

                    -

                    高中一般记作 $\mathrm{C}_n^{k}$ ,不是很推荐使用。

                    -

                    特别地,规定当 $k > n$ 时, $\mathrm{A}_n^{k} = \mathrm{C}_n^{k}=0$

                    -

                    插板法

                    $n$ 个相同的球分 $m$ 个组,不能有组为空

                    -

                    方案数

                    -

                    $n$ 个相同球分 $m$ 个组,可以有组为空

                    -

                    方案数

                    -

                    可重复的排列

                    从 $n$ 个不同元素中取 $k(k\le n)$ 个元素(同一元素允许重复取出),并按一定顺序排成一列的方案数为

                    -

                    可重复的组合

                    从 $n$ 个不同元素中取 $k(k \le n)$ 个元素(同一元素允许重复取出)的方案数为

                    -
                    +
                    +

                    排列组合

                    +

                    下面两个指的是无重复的排列与组合

                    +

                    排列数

                    +

                    \(n\) 个不同元素中取 \(k(k\le n)\) +个不同元素,并按一定顺序排成一列的方案数 \[ +\mathrm{A}_{n}^{k} = \frac{n!}{(n-k)!} +\] 也可写作 \(\mathrm{P}_{n}^{k}\) 或直接用组合数写法 +\(k! \times \binom{n}{k}\)

                    +

                    组合数

                    +

                    \(n\) 个不同元素中取 \(k(k \le n)\) 个不同元素的方案数 \[ +\binom{n}{k} = \dfrac{n!}{k!(n-k)!} +\] 高中一般记作 \(\mathrm{C}_n^{k}\) ,不是很推荐使用。

                    +

                    特别地,规定当 \(k > +n\) 时, \(\mathrm{A}_n^{k} = +\mathrm{C}_n^{k}=0\)

                    +

                    插板法

                    +

                    \(n\) 个相同的球分 \(m\) 个组,不能有组为空

                    +

                    方案数 \[ +\dbinom{n-1}{m} +\] \(n\) 个相同球分 \(m\) 个组,可以有组为空

                    +

                    方案数 \[ +\dbinom{n-1+m}{m} +\]

                    +

                    可重复的排列

                    +

                    \(n\) 个不同元素中取 \(k(k\le n)\) +个元素(同一元素允许重复取出),并按一定顺序排成一列的方案数为 \[ +n^k +\]

                    +

                    可重复的组合

                    +

                    \(n\) 个不同元素中取 \(k(k \le n)\) +个元素(同一元素允许重复取出)的方案数为 \[ +\binom{n+m-1}{m} +\]

                    +

                    简单证明

                    -

                    把每次选的看作

                    -

                    然后令 $y_i = x_i + i-1$

                    -

                    得到

                    -

                    不难发现这就是 $\binom{n+m-1}{m}$。证毕。

                    +

                    把每次选的看作 \[ +\{x_1,x_2,\cdots,x_m\} \quad(1\le x_1 \le x_2 \le \dots \le x_m \le n) +\] 然后令 \(y_i = x_i + +i-1\)

                    +

                    得到 \[ +\{y_1,y_2,\cdots,y_m\} \quad (1 \le y_1 < y_2 < \cdots \le n+m-1) +\] 不难发现这就是 \(\binom{n+m-1}{m}\)。证毕。

                    -
                    -

                    排列组合的扩展

                    请注意区分多重组合数多重集的组合数

                    +
                    +

                    排列组合的扩展

                    +

                    请注意区分多重组合数多重集的组合数

                    小蓝书上写的「不全相异元素的全排列」就是多重组合数(多重集的排列数)

                    -

                    多重集&多重组合数

                    多重集是指包含重复元素的广义集合。3

                    -

                    设 $S=\{n_1 \cdot a_1,~n_2 \cdot a_2,\cdots,n_k\cdot a_k\}$ 表示由 $n_1$ 个 $a_1$ ,$n_2$ 个 $a_2$ ,$\cdots$ ,$n_k$ 个 $a_k$ 组成的多重集, $S$ 的全排列个数为

                    -

                    相当于把相同元素的排列数除掉了。

                    -

                    具体地,如果 $n$ 个元素中,分别有 $n_1,n_2,\cdots,n_k$ 个元素相同,且 $n_1 + n_2 + \cdots + n_k = n$ ,则这 $n$ 个元素的全排列称为多重集的排列数,或称作多重组合数

                    -

                    其不同的排列个数记为 $\binom{n}{n_1,n_2,\cdots,n_k}$,则

                    -

                    (小蓝书上写的是空格而不是逗号)

                    -

                    不难发现, $\dbinom{n}{m} = \dbinom{n}{m,n-m}$ ,只不过后者比较繁琐,一般不写。

                    -

                    多组组合数

                    把 $n$ 个相异元素分为 $k (k \le n)$ 个按照一定顺序排列成的组,其中第 $i$ 组有 $n_i$ 个元素( $\sum n_i = n$ ),则不同的分组方法的种数为

                    -
                    +

                    多重集&多重组合数

                    +

                    多重集是指包含重复元素的广义集合。3

                    +

                    \(S=\{n_1 \cdot a_1,~n_2 \cdot +a_2,\cdots,n_k\cdot a_k\}\) 表示由 \(n_1\)\(a_1\)\(n_2\)\(a_2\)\(\cdots\)\(n_k\)\(a_k\) 组成的多重集, \(S\) 的全排列个数为 \[ +\dbinom{n}{n_1,n_2,\cdots,n_k} = \dfrac{n!}{n_1!n_2!\cdots n_k!} +\] 相当于把相同元素的排列数除掉了。

                    +

                    具体地,如果 \(n\) 个元素中,分别有 +\(n_1,n_2,\cdots,n_k\) 个元素相同,且 +\(n_1 + n_2 + \cdots + n_k = n\) ,则这 +\(n\) +个元素的全排列称为多重集的排列数,或称作多重组合数

                    +

                    其不同的排列个数记为 \(\binom{n}{n_1,n_2,\cdots,n_k}\),则 \[ +\dbinom{n}{n_1,n_2,\cdots,n_k} = \dfrac{n!}{n_1!n_2!\cdots n_k!} +\] (小蓝书上写的是空格而不是逗号)

                    +

                    不难发现, \(\dbinom{n}{m} = +\dbinom{n}{m,n-m}\) ,只不过后者比较繁琐,一般不写。

                    +

                    多组组合数

                    +

                    \(n\) 个相异元素分为 \(k (k \le n)\) +个按照一定顺序排列成的组,其中第 \(i\) +组有 \(n_i\) 个元素( \(\sum n_i = n\) ),则不同的分组方法的种数为 +\[ +\dbinom{n}{n_1,n_2,\cdots,n_k} = \dfrac{n!}{n_1!n_2!\cdots n_k!} +\]

                    +

                    简单证明

                    -

                    根据组合数的性质,不难推出方案数为

                    -

                    通过展开上面的式子,可以得到

                    -

                    证毕。

                    +

                    根据组合数的性质,不难推出方案数为 \[ +\binom{n}{n_1} \times \binom{n-n_1}{n_2}\times +\binom{n-n_1-n_2}{n_3}\times \cdots \times \binom{n_k-\sum_{1\le i +<k}n_i}{n_k} +\] 通过展开上面的式子,可以得到 \[ +\dfrac{n!}{n_1!n_2!\cdots n_k!} +\] 证毕。

                    -

                    多重集的组合数1

                    咕咕咕。

                    -

                    多重集的组合数2

                    咕咕咕。

                    -

                    不相邻的排列

                    $1\sim n$ 这 $n$ 个自然数中选 $k$ 个,这 $k$ 个数任何两个都不相邻的方案数

                    -
                    +

                    多重集的组合数1

                    +

                    咕咕咕。

                    +

                    多重集的组合数2

                    +

                    咕咕咕。

                    +

                    不相邻的排列

                    +

                    \(1\sim n\)\(n\) 个自然数中选 \(k\) 个,这 \(k\) 个数任何两个都不相邻的方案数 \[ +\dbinom{n-k+1}{k} +\]

                    +

                    证明

                    -

                    感谢 AprilGrimoire 老师的耐心解释 我太蠢了

                    -

                    因为选出来的 $k$ 个数按照一定顺序排列(组合嘛)

                    -

                    不妨假设这个顺序满足

                    -

                    $a_i$ 表示选的第 $i$ 个数

                    -

                    设合法的答案序列为 $a_1,a_2,\cdots,a_k$ ,并让原序列按 $1\sim n$ 排好

                    -

                    我们将「前 $k-1$ 个被选中的数」在原序列中分别与其右侧相邻的数合并

                    -

                    因为 $a_i$ 和 $a_{i+1}$ 一定不相邻,因此合并不会改变选出 $k$ 个的事实

                    -

                    我们不需要知道这 $k-1$ 个数具体是什么

                    +

                    感谢 AprilGrimoire +老师的耐心解释 我太蠢了

                    +

                    因为选出来的 \(k\) +个数按照一定顺序排列(组合嘛)

                    +

                    不妨假设这个顺序满足 \[ +a_1 < a_2 < \cdots < a_k +\] \(a_i\) 表示选的第 \(i\) 个数

                    +

                    设合法的答案序列为 \(a_1,a_2,\cdots,a_k\) ,并让原序列按 \(1\sim n\) 排好

                    +

                    我们将「前 \(k-1\) +个被选中的数」在原序列中分别与其右侧相邻的数合并

                    +

                    因为 \(a_i\)\(a_{i+1}\) 一定不相邻,因此合并不会改变选出 +\(k\) 个的事实

                    +

                    我们不需要知道这 \(k-1\) +个数具体是什么

                    因为对于每一个合法的答案序列,都存在唯一的合并方法

                    -

                    这样就形成了 $n-k+1$ 个新的块,并且这些块不再受到“相邻”的限制

                    -

                    然后就在这 $n-k+1$ 个块中选 $k$ 个即可。

                    -

                    答案就是 $\dbinom{n-k+1}{k}$

                    +

                    这样就形成了 \(n-k+1\) +个新的块,并且这些块不再受到“相邻”的限制

                    +

                    然后就在这 \(n-k+1\) 个块中选 \(k\) 个即可。

                    +

                    答案就是 \(\dbinom{n-k+1}{k}\)

                    -
                    -

                    练习题

                    例1

                    -

                    从 $1,2,3,\cdots,100$ 中取 $3$ 个数,使这 $3$ 个数恰好成等差数列的不同取法有___种。

                    +
                    +

                    练习题

                    +

                    例1

                    +

                    \(1,2,3,\cdots,100\) 中取 \(3\) 个数,使这 \(3\) +个数恰好成等差数列的不同取法有___种。

                    解法1

                    -

                    设取出的 $3$ 个数构成首项为 $a$ ,公差为 $d$ 的等差数列:

                    -

                    则 $a+2d \le 100$ ,则

                    -

                    整理一下就是

                    -

                    对于每个确定的 $d$ ,$a$ 都有 $100-2d$ 种取法,则答案为

                    -

                    解法2

                    -

                    设取出的 $3$ 个数分别为 $a_1,a_2,a_3$ ,它们构成公差为 $d$ 的等差数列

                    -

                    于是 $a_2=\dfrac{a_1+a_3}{2}$ 可被 $a_1,a_3$ 唯一确定,且 $a_1,a_3$ 的奇偶性相同

                    +\\ a\le 100-2d +\] 对于每个确定的 \(d\)\(a\) 都有 \(100-2d\) 种取法,则答案为 \[ +\sum_{d=1}^{49}(100-2d) = 49\times 100 -2 \times \frac{49(49+1)}{2} = +2450 +\] 解法2

                    +

                    设取出的 \(3\) 个数分别为 \(a_1,a_2,a_3\) ,它们构成公差为 \(d\) 的等差数列

                    +

                    于是 \(a_2=\dfrac{a_1+a_3}{2}\) 可被 +\(a_1,a_3\) 唯一确定,且 \(a_1,a_3\) 的奇偶性相同

                      -
                    • 若 $a_1$ 与 $a_3$ 同为偶数,则有 $\mathrm{C}_{50}^{2}$ 种取法
                    • -
                    • 若 $a_1$ 与 $a_3$ 同为计数,则有 $\mathrm{C}_{50}^{2}$ 种取法
                    • +
                    • \(a_1\)\(a_3\) 同为偶数,则有 \(\mathrm{C}_{50}^{2}\) 种取法
                    • +
                    • \(a_1\)\(a_3\) 同为计数,则有 \(\mathrm{C}_{50}^{2}\) 种取法
                    -

                    则共有 $2 \times \mathrm{C}_{50}^2$ 种取法。

                    -
                    +

                    则共有 \(2 \times +\mathrm{C}_{50}^2\) 种取法。

                    +

                    其他题还在咕咕咕。

                    -
                    +

                    参考文献

                    -
                    -1. https://zh.wikipedia.org/wiki/%E5%8A%A0%E6%B3%95%E5%8E%9F%E7%90%86 -
                    -
                    -2. https://zh.wikipedia.org/wiki/%E4%B9%98%E6%B3%95%E5%8E%9F%E7%90%86 -
                    -
                    -3. https://oi-wiki.org/math/combinatorics/combination/ -
                    +
                    +
                    +
                      +
                    1. https://zh.wikipedia.org/wiki/%E5%8A%A0%E6%B3%95%E5%8E%9F%E7%90%86↩︎

                    2. +
                    3. https://zh.wikipedia.org/wiki/%E4%B9%98%E6%B3%95%E5%8E%9F%E7%90%86↩︎

                    4. +
                    5. https://oi-wiki.org/math/combinatorics/combination/↩︎

                    6. +
                    +
                    @@ -950,7 +1074,7 @@

                      站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/17/bzoj2141-pai-dui-ti-jie/index.html b/2022/08/17/bzoj2141-pai-dui-ti-jie/index.html index a92eeda70c..ab7168d7f0 100644 --- a/2022/08/17/bzoj2141-pai-dui-ti-jie/index.html +++ b/2022/08/17/bzoj2141-pai-dui-ti-jie/index.html @@ -468,25 +468,44 @@

                    BZOJ2141 排队 题解

                    -

                    BZOJ2141 排队 题解

                    题目链接:#2141. 排队

                    +

                    BZOJ2141 排队 题解

                    +

                    题目链接:#2141. +排队

                    题意

                    一句话题意:

                    -

                    给定 $n$ 个数,$Q$ 次询问,每次交换两个不同位置的数,然后输出逆序对数。

                    -

                    $1 \le n \le 2\times 10^4,~1\le Q \le 2 \times 10^3$

                    +

                    给定 \(n\) 个数,\(Q\) +次询问,每次交换两个不同位置的数,然后输出逆序对数。

                    +

                    \(1 \le n \le 2\times 10^4,~1\le Q \le 2 +\times 10^3\)

                    排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家乐和和。红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。

                    -

                    不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第i个小朋友的身高为 $h_i (1\le h_i \le 10^9)$ ,我们定义一个序列的杂乱程度为:满足 $h_i>h_j$ 的 $(i, j)$ 数量。幼儿园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的杂乱程度。为方便幼儿园阿姨统计,在未进行任何交换操作时,你也应该输出该序列的杂乱程度。

                    +

                    不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第i个小朋友的身高为 +\(h_i (1\le h_i \le 10^9)\) +,我们定义一个序列的杂乱程度为:满足 \(h_i>h_j\)\((i, j)\) +数量。幼儿园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的杂乱程度。为方便幼儿园阿姨统计,在未进行任何交换操作时,你也应该输出该序列的杂乱程度。

                    这题还是比较水的。

                    -

                    首先朴素的逆序对不会有人不会吧(不会吧不会吧 ,不会的戳这里

                    +

                    首先朴素的逆序对不会有人不会吧(不会吧不会吧 ,不会的戳这里

                    考虑一次修改会对答案产生什么影响

                    -

                    设交换 $a_l$ 和 $a_r$ ,则显然只有 $(l,r)$ 的数会影响答案

                    -

                    于是我们依次考虑每个 $a_i (l < i < r)$

                    -

                    经过观察可以发现, $a_i$ 对答案的影响和它与 $a_l,a_r$ 的大小有关

                    -

                    则 $a_i$ 对答案的贡献为

                    -

                    其中 $\mathrm{sgn}(x)=\begin{cases}1,&x>0\\0,&x=0\\ -1,&x<0\end{cases}$

                    -

                    时间复杂度 $O(n \log n + nm)$

                    +

                    设交换 \(a_l\)\(a_r\) ,则显然只有 \((l,r)\) 的数会影响答案

                    +

                    于是我们依次考虑每个 \(a_i (l < i < +r)\)

                    +

                    经过观察可以发现, \(a_i\) +对答案的影响和它与 \(a_l,a_r\) +的大小有关

                    +

                    \(a_i\) 对答案的贡献为 \[ +\mathrm{sgn}(a_i-a_l) + \mathrm{sgn}(a_r-a_i) +\] 其中 \(\mathrm{sgn}(x)=\begin{cases}1,&x>0\\0,&x=0\\ +-1,&x<0\end{cases}\)

                    +

                    时间复杂度 \(O(n \log n + nm)\)

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -912,7 +931,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/19/cf178f3-representative-sampling-ti-jie/index.html b/2022/08/19/cf178f3-representative-sampling-ti-jie/index.html index 175f1e224a..4ae95bd709 100644 --- a/2022/08/19/cf178f3-representative-sampling-ti-jie/index.html +++ b/2022/08/19/cf178f3-representative-sampling-ti-jie/index.html @@ -480,25 +480,50 @@

                    CF178F3 Representative Sampling
                    -

                    CF178F3 Representative Sampling 题解

                    题目链接:CF178F3 Representative Sampling

                    +

                    CF178F3 Representative +Sampling 题解

                    +

                    题目链接:CF178F3 +Representative Sampling

                    题意

                    -

                    有 $n$ 个字符串 $a_i$ ,从中选取 $k$ 个,使它们两两之间的 $\text{LCP}$(最长公共前缀)之和最大。输出这个最大值。

                    -

                    $k\le n\le 2000,|a_i|\le 500$

                    +

                    \(n\) 个字符串 \(a_i\) ,从中选取 \(k\) 个,使它们两两之间的 \(\text{LCP}\)(最长公共前缀)之和最大。输出这个最大值。

                    +

                    \(k\le n\le 2000,|a_i|\le 500\)

                    -

                    看到最长公共前缀我还愣了一下,这不裸的 $\tt{trie}$ 树吗,然后想了半天不会。。

                    -

                    考虑建 $\tt{trie}$ 树然后在上面做树上背包

                    -

                    注意这里背包能选的“物品”只有 $\tt{trie}$ 树上「是字符串结尾的结点」

                    +

                    看到最长公共前缀我还愣了一下,这不裸的 \(\tt{trie}\) +树吗,然后想了半天不会。。

                    +

                    考虑建 \(\tt{trie}\) +树然后在上面做树上背包

                    +

                    注意这里背包能选的“物品”只有 \(\tt{trie}\) 树上「是字符串结尾的结点」

                    方便起见,称「是字符串结尾的结点」为「串结点」

                    两两串结点的贡献,也就是他们的最长公共前缀(LCP)长度,其实就是他们的LCA深度。

                    -

                    记 $i$ 的子结点 $c_1,c_2,\cdots,c_{\lambda_i}$ ,设 $f_{c_k,j}$ 表示以 $i$ 为根的子树中,只考虑 $c_1,c_2,\cdots,c_k$ ,选 $j$ 个串结点,「两两串结点的LCA」的深度之和($i$ 深度为 $0$ ),

                    -

                    设 $g_{i,j}$ 表示以 $i$ 为根的子树中,选 $j$ 个串结点,「两两串结点的LCA」的深度之和 ($i$ 深度为 $0$ ),则

                    -

                    注意到两个串只会在他们的LCA处更新dp值,因此有很多结点都是没用的

                    +\left\{f_{c_{k-1},j-l}+g_{c_k,l}+\dbinom{l}{2}\right\} +\] +注意到两个串只会在他们的LCA处更新dp值,因此有很多结点都是没用的

                    我们直接把这棵树缩成一个带权树,保留可以成为LCA的结点

                    -

                    于是我们就可以 $O(n^2)$ 通过这题了

                    -

                    u1s1,代码是学的yhx巨佬的1,懒得改了(

                    +

                    于是我们就可以 \(O(n^2)\) +通过这题了

                    +

                    u1s1,代码是学的yhx巨佬的1,懒得改了(

                    代码:

                    #include <iostream>
                     #include <string>
                    @@ -582,9 +607,15 @@ 

                    return 0; }

                    参考文献

                    -
                    -1. https://yhx-12243.github.io/OI-transit/records/cf178F3.html -
                    +
                    +
                    +
                      +
                    1. https://yhx-12243.github.io/OI-transit/records/cf178F3.html↩︎

                    2. +
                    +
                    @@ -958,7 +989,7 @@

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/20/cf708e-student-s-camp-ti-jie/index.html b/2022/08/20/cf708e-student-s-camp-ti-jie/index.html index 52076ba523..6533dc74c3 100644 --- a/2022/08/20/cf708e-student-s-camp-ti-jie/index.html +++ b/2022/08/20/cf708e-student-s-camp-ti-jie/index.html @@ -476,60 +476,115 @@

                    CF708E Student's Camp 题解
                    -

                    CF708E Student’s Camp 题解

                    题目链接:CF708E Student’s Camp

                    +

                    CF708E Student's Camp 题解

                    +

                    题目链接:CF708E +Student's Camp

                    题意

                    -

                    有一个 $(n+2) \times m$ 的网格。

                    -

                    给定 $n,m,a,b,k$ ,记 $p=\frac{a}{b}$

                    -

                    除了第 $0$ 行和第 $n+1$ 行,其他每一行

                    +

                    有一个 \((n+2) \times m\) +的网格。

                    +

                    给定 \(n,m,a,b,k\) ,记 \(p=\frac{a}{b}\)

                    +

                    除了第 \(0\) 行和第 \(n+1\) 行,其他每一行

                      -
                    • 白天最左边的格子有 $p$ 的概率被吹走。
                    • -
                    • 夜晚最右边的格子有 $p$ 的概率被吹走。
                    • +
                    • 白天最左边的格子有 \(p\) +的概率被吹走。
                    • +
                    • 夜晚最右边的格子有 \(p\) +的概率被吹走。
                    -

                    如果网格(包括第 $0$ 行和第 $n+1$ 行)被分成了两个及以上连通分量,则称网格不连通。

                    -

                    求 $k$ 天后,网格始终保持连通的概率

                    -

                    $n,m \le 1.5 \times 10^3,~k \le 10^5,~a < b \le 10^9$,答案对 $10^9+7$ 取模。

                    +

                    如果网格(包括第 \(0\) 行和第 \(n+1\) +行)被分成了两个及以上连通分量,则称网格不连通。

                    +

                    \(k\) +天后,网格始终保持连通的概率

                    +

                    \(n,m \le 1.5 \times 10^3,~k \le 10^5,~a +< b \le 10^9\),答案对 \(10^9+7\) 取模。

                    特别🐮🍺的一道题,考虑概率dp。

                    -

                    下文中出现的奇怪变量名(因为参考自 link),先给出对应的代码形式

                    -

                    $\mathtt{pl}_i\to $ pl[i] ,$\mathtt{pr}_i \to$ pr[i]

                    -

                    $F_{i,j} \to$ F[i][j] , $\mathtt{sl}_{i,j} \to$ sl[i][j] , $\mathtt{sr}_{i,j} \to$ sr[i][j]

                    +

                    下文中出现的奇怪变量名(因为参考自 link),先给出对应的代码形式

                    +

                    $_i$ pl[i]\(\mathtt{pr}_i +\to\) pr[i]

                    +

                    \(F_{i,j} \to\) F[i][j] +, \(\mathtt{sl}_{i,j} \to\) +sl[i][j]\(\mathtt{sr}_{i,j} +\to\) sr[i][j]

                    注意到每一行是相互独立的,故

                    -

                    设 $\mathtt{pl}_i$ 表示某一行在 $k$ 个白天后,最左边的砖块为第 $i$ 块的概率

                    -

                    设 $\mathtt{pr}_i$ 表示某一行在 $k$ 个夜晚后,最右边的砖块为第 $i$ 块的概率

                    -

                    根据小学数学,如果把这里这个 $i$ 看作一个离散随机变量,则它一定服从二项分布

                    -

                    记 $q=1-p$ ,即

                    -

                    根据二项分布的对称性可得 $\mathtt{pr}_{m-i} = \mathtt{pl}_{i+1}$

                    +

                    \(\mathtt{pl}_i\) 表示某一行在 +\(k\) 个白天后,最左边的砖块为第 \(i\) 块的概率

                    +

                    \(\mathtt{pr}_i\) 表示某一行在 +\(k\) 个夜晚后,最右边的砖块为第 \(i\) 块的概率

                    +

                    根据小学数学,如果把这里这个 \(i\) +看作一个离散随机变量,则它一定服从二项分布

                    +

                    \(q=1-p\) ,即 \[ +\mathtt{pl}_{i+1} = \dbinom{k}{i} q^{i} p^{k-i} +\] 根据二项分布的对称性可得 \(\mathtt{pr}_{m-i} = \mathtt{pl}_{i+1}\)

                    根据砖块吹走的性质,最后每一行一定是连续的一段,

                    -

                    故设 $f_{i,l,r}$ 表示 $k$ 天后,从下往上数第 $i$ 行仅剩下砖块 $l,l+1,\cdots,r$ 的概率

                    +

                    故设 \(f_{i,l,r}\) 表示 \(k\) 天后,从下往上数第 \(i\) 行仅剩下砖块 \(l,l+1,\cdots,r\) 的概率

                    如果建筑没有倒塌,那么上下两行一定是有交集的。

                    这个交集并不是很好枚举,但是没交集的部分还是比较好枚举的

                    -

                    设 $F_{i,r}$ 表示 $k$ 天后,从下往上数第 $i$ 行最右边的砖块为第 $r$ 块的概率,则

                    -

                    设 $\mathtt{sr}_{i,j}$ 表示 $k$ 天后,从下往上数第 $i$ 行最右边的砖块为第 $j$ 块或更靠左的概率,则

                    -

                    设 $\mathtt{sl}_{i,j}$ 表示 $k$ 天后,从下往上数第 $i$ 行最左边的砖块为第 $j$ 块或更靠右的概率,则

                    -

                    这个可以由对称性 $\mathtt{sr}_{i,m-j+1}$ 的值推得。

                    -

                    故转移方程为

                    -

                    于是

                    -

                    设 $\mathtt{s1}_i = \sum_{k=1}^{i} \mathtt{pl}_k,~\mathtt{s2}_i = \sum_{k=1}^{i} \mathtt{pl}_k \times \mathtt{sr}_{i-1,k-1}$ ,则

                    -

                    然后我们就能 $O(nm)$ 求解啦!

                    -

                    最终的答案就是 $\mathtt{sr}_{n,m}$

                    +F_{i,r} &= \sum_{l=1}^{r} f_{i,l,r} +\\&=\sum_{l=1}^{r}{\left(\mathtt{pl}_l\times \mathtt{pr}_r \times +(\mathtt{sr}_{i-1,m} - \mathtt{sr}_{i-1,l-1} +-\mathtt{sr}_{i-1,r+1})\right)} +\\&= \mathtt{pr}_r \times \left(\left(\sum_{l=1}^{r} +\mathtt{pl}_l\right) \times (\mathtt{sr}_{i-1,m}-\mathtt{sl}_{i-1,r+1}) +- \sum_{l=1}^{r} (\mathtt{pl}_l \times \mathtt{sr}_{i-1,l-1})\right) +\end{aligned} +\] 设 \(\mathtt{s1}_i = \sum_{k=1}^{i} +\mathtt{pl}_k,~\mathtt{s2}_i = \sum_{k=1}^{i} \mathtt{pl}_k \times +\mathtt{sr}_{i-1,k-1}\) ,则 \[ +F_{i,r} = \mathtt{pr}_r \times (\mathtt{s1}_r \times +(\mathtt{sr}_{i-1,m} - \mathtt{sl}_{i-1,r+1})-\mathtt{s2}_r) +\] 然后我们就能 \(O(nm)\) +求解啦!

                    +

                    最终的答案就是 \(\mathtt{sr}_{n,m}\)

                    呼,总算写完了,累死了(

                    代码:

                    #include <iostream>
                    @@ -987,7 +1042,7 @@ 

                     站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/20/oi-shu-xue-zong-jie-bo-yi-lun/index.html b/2022/08/20/oi-shu-xue-zong-jie-bo-yi-lun/index.html index a252d5ae16..76b02c5a88 100644 --- a/2022/08/20/oi-shu-xue-zong-jie-bo-yi-lun/index.html +++ b/2022/08/20/oi-shu-xue-zong-jie-bo-yi-lun/index.html @@ -472,8 +472,10 @@

                    OI数学总结-博弈论

                    -

                    OI数学总结-博弈论

                    施工中,咕咕咕…

                    -

                    博弈论基础

                    对于博弈论的题目,重点考虑以下两点

                    +

                    OI数学总结-博弈论

                    +

                    施工中,咕咕咕...

                    +

                    博弈论基础

                    +

                    对于博弈论的题目,重点考虑以下两点

                    • 什么时候可以判定胜利者
                    • 先手的影响
                    • @@ -851,7 +853,7 @@

                       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/20/oi-shu-xue-zong-jie-qi-ta/index.html b/2022/08/20/oi-shu-xue-zong-jie-qi-ta/index.html index 98a7085a57..a32f199b11 100644 --- a/2022/08/20/oi-shu-xue-zong-jie-qi-ta/index.html +++ b/2022/08/20/oi-shu-xue-zong-jie-qi-ta/index.html @@ -472,87 +472,140 @@

                      OI数学总结-其他

                      -

                      OI数学总结-其他

                      本文充满了从各种地方偷学来的东西以及没什么分类的东西

                      -

                      数值积分

                      定积分

                      简单来说,函数 $f(x)$ 在区间 $[l,r]$ 上的定积分 $\int_{l}^{r}f(x)\mathrm{d}x$ 指的是 $f(x)$ 在区间 $[l,r]$ 中于 $x$ 轴围成的区域的面积(其中 $x$ 轴上方的部分为正值, $x$ 轴下方的部分为负值)

                      -

                      辛普森法

                      辛普森公式

                      -

                      对于一个二次函数 $f(x) = Ax^2 + Bx + C$ ,有

                      -
                      -

                      推导过程: 对于一个二次函数 $f(x)=Ax^2+Bx+C$ ; 求积分可得 $F(x)=\int_0^x f(x) {\mathrm d}x = \frac{a}{3}x^3+\frac{b}{2}x^2+cx+D$ 在这里 D 是一个常数,那么

                      -
                      +

                      OI数学总结-其他

                      +

                      本文充满了从各种地方偷学来的东西以及没什么分类的东西

                      +

                      数值积分

                      +

                      定积分

                      +

                      简单来说,函数 \(f(x)\) 在区间 \([l,r]\) 上的定积分 \(\int_{l}^{r}f(x)\mathrm{d}x\) 指的是 \(f(x)\) 在区间 \([l,r]\) 中于 \(x\) 轴围成的区域的面积(其中 \(x\) 轴上方的部分为正值, \(x\) 轴下方的部分为负值)

                      +

                      辛普森法

                      +

                      辛普森公式

                      +

                      对于一个二次函数 \(f(x) = Ax^2 + Bx + +C\) ,有 \[ +\int_l^rf(x)\mathrm{d}x = +\dfrac{(r-l)\left(f(l)+f(r)+4f\left(\frac{l+r}{2}\right)\right)}{6} +\]

                      +
                      +

                      推导过程: 对于一个二次函数 \(f(x)=Ax^2+Bx+C\) ; 求积分可得 \(F(x)=\int_0^x f(x) {\mathrm d}x = +\frac{a}{3}x^3+\frac{b}{2}x^2+cx+D\) 在这里 D +是一个常数,那么

                      +

                      \[ +\begin{aligned} \int_l^r f(x) {\mathrm d}x &= F(r)-F(l) \\ &= +\frac{a}{3}(r^3-l^3)+\frac{b}{2}(r^2-l^2)+c(r-l) \\ +&=(r-l)\left(\frac{a}{3}(l^2+r^2+lr)+\frac{b}{2}(l+r)+c\right) \\ +&=\frac{r-l}{6}(2al^2+2ar^2+2alr+3bl+3br+6c)\\ +&=\frac{r-l}{6}((al^2+bl+c)+(ar^2+br+c)+4\left(a\left(\frac{l+r}{2}\right)^2+b\left(\frac{l+r}{2}\right)+c)\right) +\\ +&=\frac{r-l}{6}\left(f(l)+f(r)+4f\left(\frac{l+r}{2}\right)\right) +\end{aligned} +\]

                      +

                      普通辛普森法

                      -

                      咕咕咕…

                      -
                      -

                      洛必达法则

                      设 $c \in \overline{\mathbb{R}}$(扩展实数) ,两函数 $f(x),g(x)$ 在 $x=c$ 为端点的开区间可微,

                      -

                      $\lim\limits_{x \to c}\dfrac{f^{\prime}(x)}{g^{\prime}(x)} \in \overline{\mathbb{R}}$ ,并且 $g^{\prime}(x) \ne 0$ 。

                      -

                      如果 $\lim\limits_{x \to c}f(x) = \lim\limits_{x \to c} g(x) = 0$ 或 $\lim\limits_{x \to c}|f(x)| = \lim\limits_{x \to c} |g(x)| = \infty$ 其中一者成立,

                      -

                      则称欲求的极限 $\lim\limits_{x \to c}\dfrac{f(x)}{g(x)}$ 为未定式。

                      -

                      此时洛必达法则表明

                      -
                      -

                      取整函数

                      向下取整floor(x) $ = [x] = \lfloor x \rfloor $

                      -

                      向上取整 ceil(x) $=\lceil x \rceil$

                      -

                      小数部分 $\left\{x\right\} = x-\left\lfloor{x}\right\rfloor$

                      -

                      部分性质

                        -
                      1. $\left\lfloor{x}\right\rfloor \le x < \left\lfloor{x}\right\rfloor+1$ 当且仅当 $x$ 为正整数时取等号

                        -
                      2. -
                      3. $\left\lfloor{k+x}\right\rfloor = k+\left\lfloor{x}\right\rfloor,k\in \mathbb{Z},x\in \mathbb{R}$

                        -
                      4. -
                      5. $\left\lfloor{x}\right\rfloor \le \left\lceil{x}\right\rceil$

                        -
                      6. -
                      7. 四舍五入 = floor(x+0.5)

                        -
                      8. -
                      9. -
                      10. $\left\lceil{x}\right\rceil = - \left\lfloor{-x}\right\rfloor$

                        -
                      11. -
                      12. -
                      13. 多种嵌套相当于只看最内层的

                        -
                      14. +&\left\lceil\left\lceil{x}\right\rceil\right\rceil=\left\lceil{x}\right\rceil\\\\ +&\left\lfloor\left\lfloor{x}\right\rfloor\right\rfloor=\left\lfloor{x}\right\rfloor +\\\\ +&\left\{\left\{x\right\}\right\}=\left\{x\right\} +\end{aligned} +\]

                        +
                      15. 多种嵌套相当于只看最内层的

                      -
                        -
                      1. 若 $n$ 为正整数
                      2. + &\left\lceil{\left\lfloor{x}\right\rfloor}\right\rceil=\left\lfloor{x}\right\rfloor + \end{aligned} +\]

                        +
                          +
                        1. \(n\) 为正整数
                        -

                        ​ 将 $m=2$ 代入可得

                        - +n&=\left\lfloor{\dfrac{n}{m}}\right\rfloor+\left\lfloor{\dfrac{n+1}{m}}\right\rfloor+\cdots+\left\lfloor{\dfrac{n+m-1}{m}}\right\rfloor +\end{aligned} +\]

                        +

                        ​ 将 \(m=2\) 代入可得 \[ +n=\left\lfloor{\dfrac{n}{2}}\right\rfloor+\left\lceil{\dfrac{n}{2}}\right\rceil +\]

                        +

                      @@ -917,7 +970,7 @@

                       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/20/oi-shu-xue-zong-jie-qun-lun/index.html b/2022/08/20/oi-shu-xue-zong-jie-qun-lun/index.html index c571a62d90..bf6ac5ee95 100644 --- a/2022/08/20/oi-shu-xue-zong-jie-qun-lun/index.html +++ b/2022/08/20/oi-shu-xue-zong-jie-qun-lun/index.html @@ -472,70 +472,172 @@

                      OI数学总结-群论

                      -

                      OI数学总结-群论

                      施工中,咕咕咕….

                      -

                      群的基本概念

                      群的定义

                      群公理包含下述四个性质(有时略去封闭性,只有三个性质)。若集合 $G\neq\varnothing$ 和 $G$ 上的运算 $\cdot$ 构成的代数结构 $(G,\cdot)$ 满足以下性质:

                      -
                        -
                      1. 封闭性:对于所有 $G$ 中 $a, b$ ,运算 $a·b$ 的结果也在 $G$ 中。
                      2. -
                      3. 结合律:对于 $G$ 中所有的 $a, b, c$ ,等式 $(a \cdot b)\cdot c = a \cdot (b \cdot c)$ 成立。
                      4. -
                      5. 标识元(单位元): $G$ 中存在一个元素 $e$ ,使得对于 $G$ 中的每一个 $a$ ,都有一个 $e \cdot a=a\cdot e=a$ 成立。这样的元素是独一无二的。它被称为群的标识元素。
                      6. -
                      7. 逆元:对于每个 $G$ 中的 $a$ ,总存在 $G$ 中的一个元素 $b$ 使 $a \cdot b = b \cdot a = e$ ,此处 $e$ 为单位元,称 $b$ 为 $a$ 的逆元,记为 $a^{-1}$ 。
                      8. +

                        OI数学总结-群论

                        +

                        施工中,咕咕咕....

                        +

                        群的基本概念

                        +

                        群的定义

                        +

                        群公理包含下述四个性质(有时略去封闭性,只有三个性质)。若集合 \(G\neq\varnothing\)\(G\) 上的运算 \(\cdot\) 构成的代数结构 \((G,\cdot)\) 满足以下性质:

                        +
                          +
                        1. 封闭性:对于所有 \(G\)\(a, +b\) ,运算 \(a·b\) 的结果也在 +\(G\) 中。
                        2. +
                        3. 结合律:对于 \(G\) +中所有的 \(a, b, c\) ,等式 \((a \cdot b)\cdot c = a \cdot (b \cdot c)\) +成立。
                        4. +
                        5. 标识元(单位元): \(G\) 中存在一个元素 \(e\) ,使得对于 \(G\) 中的每一个 \(a\) ,都有一个 \(e \cdot a=a\cdot e=a\) +成立。这样的元素是独一无二的。它被称为群的标识元素。
                        6. +
                        7. 逆元:对于每个 \(G\) 中的 \(a\) ,总存在 \(G\) 中的一个元素 \(b\) 使 \(a \cdot +b = b \cdot a = e\) ,此处 \(e\) +为单位元,称 \(b\)\(a\) 的逆元,记为 \(a^{-1}\)
                        -

                        则称 $(G,\cdot)$ 为一个

                        -

                        例如,整数集和整数间的加法 $(\mathbb{Z},+)$ 构成一个群,单位元是 $0$,一个整数的逆元是它的相反数。

                        -

                        阶的定义

                          -
                        • 一个群的阶是指其势,即其元素的个数;

                          -
                        • -
                        • 一个群内的一个元素 $a$ 的阶(有时称为周期)

                          -

                          是指会使得 $a^m = e$ 的最小正整数 $m$ (其中的e为这个群的单位元素,且$a^m$为 $a$ 的 $m$ 次幂)

                          -

                          若没有此数存在,则称 $a$ 有无限阶。有限群的所有元素都有有限阶。

                          -
                        • +

                          则称 \((G,\cdot)\) 为一个 +

                          +

                          例如,整数集和整数间的加法 \((\mathbb{Z},+)\) 构成一个群,单位元是 \(0\),一个整数的逆元是它的相反数。

                          +

                          阶的定义

                          +
                            +
                          • 一个群的阶是指其势,即其元素的个数;

                          • +
                          • 一个群内的一个元素 \(a\) +的阶(有时称为周期)

                            +

                            是指会使得 \(a^m = e\) 的最小正整数 +\(m\) +(其中的e为这个群的单位元素,且\(a^m\)\(a\)\(m\) 次幂)

                            +

                            若没有此数存在,则称 \(a\) +有无限阶。有限群的所有元素都有有限阶。

                          -

                          群的主要类别

                          置换群

                          -

                          对于给定的集合 $X$ ,$X$ 到自身的一些置换集合 $G$ 如果在复合运算和求逆运算下封闭,那么称 $G$ 是一个作用于 $X$ 上的群。

                          +

                          群的主要类别

                          +

                          置换群

                          +
                          +

                          对于给定的集合 \(X\)\(X\) 到自身的一些置换集合 \(G\) 如果在复合运算和求逆运算下封闭,那么称 +\(G\) 是一个作用于 \(X\) 上的群。

                          -

                          易证,集合 $S$ 上的所有置换关于置换的乘法满足封闭性、结合律、有单位元(恒等置换,即每个元素映射成它自己)、有逆元(交换置换表示中的上下两行),因此构成一个群。这个群的任意一个 子群 即称为 置换群

                          -

                          置换的定义

                          有限集合到自身的双射成为置换。集合 $S = \{a_1,a_2,\cdots ,a_n\}$ 上的置换可以表示为

                          -

                          意为将 $a_i$ 映射为 $a_{p_i}$ ,其中 $p_1,p_2,\cdots,p_n$ 是 $1,2,\cdots,n$ 的一个排列

                          -

                          显然 $S$ 上所有置换的数量为 $n!$

                          -

                          置换的乘法

                          对于两个置换 $f= \begin{pmatrix}a_1,a_2,\cdots,a_n\\a_{p_1},a_{p_2},\cdots,a_{p_n}\end{pmatrix}$和 $g= \begin{pmatrix}a_{p_1},a_{p_2},\cdots,a_{p_n}\\a_{q_1},a_{q_2},\cdots,a_{q_n}\end{pmatrix}$ ,$f$ 和 $g$ 的乘积记为 $f \circ g$ ,其值为

                          -

                          简单来说就是先后经过 $f$ 的映射,再经过 $g$ 的映射。

                          -

                          循环置换

                          循环置换是一类特殊的置换,可表示为

                          -

                          若两个循环置换不含有相同的元素,则称它们是不相交的,有如下定理:

                          -

                          任意一个置换都可以分解为若干不相交的循环置换的乘积,例如

                          -

                          该定理的证明非常简单。如果把元素视为图的结点,映射关系视为有向边,则每个结点的入度和出度都为 $1$ ,因此形成的图形必定是若干个环的集合,而一个环即可用一个循环置换表示。

                          -

                          循环群

                          循环群是最简单的群

                          -

                          群 $G$ 中任意一个元素 $a$ 都可以表示为 $a=g^k$ ,其 $k$ 为整数。称 $g$ 为群 $G$ 的生成元。

                          -

                          有定理:生成元 $g$ 的阶就是群 $G$ 的阶。

                          -

                          阶为 $m$ 的有限循环群 $G$ 同构于模 $m$ 剩余类对于加法构成的群 $Z_m$。

                          -

                          Burnside 引理

                          设 $A,B$ 为有限集合,$X$ 为一些从 $A$ 到 $B$ 的映射组成的集合。

                          -

                          $G$ 是 $A$ 上的置换群,且 $X$ 中的映射在 $G$ 中的置换作用下封闭。

                          -

                          $X/G$ 表示 $G$ 作用在 $X$ 产生的所有等价类的集合

                          -

                          (若 $X$ 中的两个映射经过 $G$ 中的置换作用后相等,则它们在同一个等价类中),则

                          -

                          其中 $|S|$ 表示集合 $S$ 的基数,即元素个数,且

                          -

                          举例:

                          -

                          用 $3$ 种颜色给一个立方体染色,求本质不同的方案数

                          +\end{pmatrix} +\] 意为将 \(a_i\) 映射为 \(a_{p_i}\) ,其中 \(p_1,p_2,\cdots,p_n\)\(1,2,\cdots,n\) 的一个排列

                          +

                          显然 \(S\) 上所有置换的数量为 \(n!\)

                          +

                          置换的乘法

                          +

                          对于两个置换 \(f= +\begin{pmatrix}a_1,a_2,\cdots,a_n\\a_{p_1},a_{p_2},\cdots,a_{p_n}\end{pmatrix}\)和 +\(g= +\begin{pmatrix}a_{p_1},a_{p_2},\cdots,a_{p_n}\\a_{q_1},a_{q_2},\cdots,a_{q_n}\end{pmatrix}\) +,\(f\)\(g\) 的乘积记为 \(f \circ g\) ,其值为 \[ +f \circ g= +\begin{pmatrix}a_1,a_2,\cdots,a_n\\a_{q_1},a_{q_2},\cdots,a_{q_n}\end{pmatrix} +\] 简单来说就是先后经过 \(f\) +的映射,再经过 \(g\) 的映射。

                          +

                          循环置换

                          +

                          循环置换是一类特殊的置换,可表示为 \[ +(a_1,a_2,\cdots,a_m) +=\begin{pmatrix}a_1,a_2,\cdots,a_{m-1},a_m\\a_{2},a_{3},\cdots,a_{m},a_1\end{pmatrix} +\] +若两个循环置换不含有相同的元素,则称它们是不相交的,有如下定理:

                          +

                          任意一个置换都可以分解为若干不相交的循环置换的乘积,例如 \[ +\begin{pmatrix}a_1,a_2,a_3,a_4,a_5\\a_3,a_1,a_2,a_5,a_4\end{pmatrix} = +(a_1,a_3,a_2) \circ (a_4,a_5) +\] +该定理的证明非常简单。如果把元素视为图的结点,映射关系视为有向边,则每个结点的入度和出度都为 +\(1\) +,因此形成的图形必定是若干个环的集合,而一个环即可用一个循环置换表示。

                          +

                          循环群

                          +

                          循环群是最简单的群

                          +

                          \(G\) 中任意一个元素 \(a\) 都可以表示为 \(a=g^k\) ,其 \(k\) 为整数。称 \(g\) 为群 \(G\) 的生成元。

                          +

                          有定理:生成元 \(g\) 的阶就是群 +\(G\) 的阶。

                          +

                          阶为 \(m\) 的有限循环群 \(G\) 同构于模 \(m\) 剩余类对于加法构成的群 \(Z_m\)

                          +

                          Burnside 引理

                          +

                          \(A,B\) 为有限集合,\(X\) 为一些从 \(A\)\(B\) 的映射组成的集合。

                          +

                          \(G\)\(A\) 上的置换群,且 \(X\) 中的映射在 \(G\) 中的置换作用下封闭。

                          +

                          \(X/G\) 表示 \(G\) 作用在 \(X\) 产生的所有等价类的集合

                          +

                          (若 \(X\) 中的两个映射经过 \(G\) +中的置换作用后相等,则它们在同一个等价类中),则 \[ +|X/G| = \dfrac{1}{|G|} \sum_{g \in G} |X^g| +\] 其中 \(|S|\) 表示集合 \(S\) 的基数,即元素个数,且 \[ +X^g = \{x|x\in X,g(x) =x\} +\] 举例:

                          +

                          \(3\) +种颜色给一个立方体染色,求本质不同的方案数

                          本质不同指:经过任意翻转后,两种染色方案均不同

                          -

                          +

                          则根据上面的模板,有

                            -
                          • $A$ :立方体 $6$ 个面的集合
                          • -
                          • $B$ : $3$ 种颜色的集合
                          • -
                          • $X$ :直接给每个面染色,不考虑本质不同的方案的集合,共有 $3^6$ 种
                          • -
                          • $G$ :各种翻转操作构成的置换群
                          • -
                          • $X/G$ :本质不同的染色方案的集合
                          • -
                          • $X^g$ :对于某一种反转操作 $g$ ,所有直接染色方案中,经过 $g$ 这种翻转后保持不变的染色方案的集合
                          • +
                          • \(A\) :立方体 \(6\) 个面的集合
                          • +
                          • \(B\)\(3\) 种颜色的集合
                          • +
                          • \(X\) +:直接给每个面染色,不考虑本质不同的方案的集合,共有 \(3^6\)
                          • +
                          • \(G\) +:各种翻转操作构成的置换群
                          • +
                          • \(X/G\) +:本质不同的染色方案的集合
                          • +
                          • \(X^g\) :对于某一种反转操作 \(g\) ,所有直接染色方案中,经过 \(g\) 这种翻转后保持不变的染色方案的集合

                          下面懒得写了,直接看图片吧。

                          -

                          +

                      @@ -901,7 +1003,7 @@

                       站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/20/oi-shu-xue-zong-jie-shu-lun/index.html b/2022/08/20/oi-shu-xue-zong-jie-shu-lun/index.html index 5334e375a3..af492f75bd 100644 --- a/2022/08/20/oi-shu-xue-zong-jie-shu-lun/index.html +++ b/2022/08/20/oi-shu-xue-zong-jie-shu-lun/index.html @@ -472,25 +472,43 @@

                      OI数学总结-数论

                      -

                      OI数学总结-数论

                      施工中,咕咕咕….

                      -

                      积性函数

                      数论中定义:定义域为正整数域的函数 $f(n)$ ,

                      -

                      且满足 $\forall a,b \in \mathbb{Z} _+,f(ab)=f(a)f(b) \land \gcd(a,b)=1$

                      -
                      -

                      完全积性函数

                      数论中定义:定义域为正整数域的函数 $f(n)$ ,

                      -

                      且满足 $\forall a,b \in \mathbb{Z} _+,f(ab)=f(a)f(b)$

                      -
                      -

                      最大公约数(gcd)

                      最大公约数指能够整除多个不全为零的整数的最大正整数

                      -

                      小性质: $a\times b = \gcd(a,b) \times \mathrm{lcm}(a,b)$

                      -

                      代码求解

                      int gcd(int a,int b)
                      +                

                      OI数学总结-数论

                      +

                      施工中,咕咕咕....

                      +

                      积性函数

                      +

                      数论中定义:定义域为正整数域的函数 \(f(n)\)

                      +

                      且满足 \(\forall a,b \in \mathbb{Z} +_+,f(ab)=f(a)f(b) \land \gcd(a,b)=1\)

                      +
                      +

                      完全积性函数

                      +

                      数论中定义:定义域为正整数域的函数 \(f(n)\)

                      +

                      且满足 \(\forall a,b \in \mathbb{Z} +_+,f(ab)=f(a)f(b)\)

                      +
                      +

                      最大公约数(gcd)

                      +

                      最大公约数指能够整除多个不全为零的整数的最大正整数

                      +

                      小性质: \(a\times b = \gcd(a,b) \times +\mathrm{lcm}(a,b)\)

                      +

                      代码求解

                      +
                      int gcd(int a,int b)
                       {
                           return b==0?a:gcd(b,a%b);
                       } // 注意这份代码对于(+,-)的情况会求出负数
                      -
                      -

                      裴蜀定理

                      设 $a,b$ 是不全为零的整数,则存在整数,使得 $ax+by=\gcd(a,b)$

                      -

                      证明详见 裴蜀定理及其证明

                      -
                      -

                      扩展欧几里德算法(Exgcd)

                      板子题: P5656 【模板】二元一次不定方程 (exgcd)

                      -

                      若 $b \ne 0$ ,扩展欧几里得算法求出的可行解必有 $|x| \le b ,|y| \le a$ 。

                      +
                      +

                      裴蜀定理

                      +

                      \(a,b\) +是不全为零的整数,则存在整数,使得 \(ax+by=\gcd(a,b)\)

                      +

                      证明详见 裴蜀定理及其证明

                      +
                      +

                      扩展欧几里德算法(Exgcd)

                      +

                      板子题: P5656 +【模板】二元一次不定方程 (exgcd)

                      +

                      \(b \ne 0\) +,扩展欧几里得算法求出的可行解必有 \(|x| \le b +,|y| \le a\)

                      int exgcd(int a,int b,int &x,int &y)
                       {
                           int d=a;
                      @@ -522,36 +540,59 @@ 

                      } pc('\n'); }

                      -
                      -

                      费马小定理

                        -
                      1. 若 $p$ 为素数 , $\gcd(a,p)=1$ 则 $a^{p-1}\equiv 1 \pmod{p}$

                        -
                      2. -
                      3. 若 $p$ 为素数 ,则对于任意整数 $a$ ,有 $a^p \equiv a \pmod{p}$

                        -
                      4. +
                        +

                        费马小定理

                        +
                          +
                        1. \(p\) 为素数 , \(\gcd(a,p)=1\)\(a^{p-1}\equiv 1 \pmod{p}\)

                        2. +
                        3. \(p\) 为素数 ,则对于任意整数 +\(a\) ,有 \(a^p \equiv a \pmod{p}\)

                        -

                        常用于 $O(\log n)$ 求解 $n$ 模素数 $p$ 意义下的逆元,详见逆元部分。

                        -
                        -

                        逆元

                        如果一个线性同余方程组 $ax \equiv 1 \pmod{b}$ ,则 $x$ 称为 $a \bmod b$ 的逆元,记作 $a^{-1}$

                        +

                        常用于 \(O(\log n)\) 求解 \(n\) 模素数 \(p\) 意义下的逆元,详见逆元部分。

                        +
                        +

                        逆元

                        +

                        如果一个线性同余方程组 \(ax \equiv 1 +\pmod{b}\) ,则 \(x\) 称为 \(a \bmod b\) 的逆元,记作 \(a^{-1}\)

                        模意义下的乘法逆元不唯一。

                        -

                        如果 $b$ 不为素数,可能不存在逆元。

                        -

                        代码求解

                        P3811 【模板】乘法逆元

                        -

                        $O(n)$ 预处理 ,$O(1)$ 查询

                        原理:

                        -

                        注意空间复杂度为 $O(n)$

                        +\end{aligned} +\]

                        +

                        注意空间复杂度为 \(O(n)\)

                        inv[1]=1;
                         for(int i=2; i<=n; i++)
                             inv[i]=(p-p/i)*inv[p%i]%p;
                        -

                        费马小定理 $O(\log p)$ 查询 $x^{-1}$

                        原理:$x \equiv a^{p-2}\pmod{p}$ (根据费马小定理)

                        -

                        $p$ 需要是质数

                        +

                        费马小定理 \(O(\log p)\) 查询 \(x^{-1}\)

                        +

                        原理:\(x \equiv a^{p-2}\pmod{p}\) +(根据费马小定理)

                        +

                        \(p\) 需要是质数

                        cout << qpow(a,p-2) << endl; // 快速幂
                        -

                        $\text{Exgcd}$ $O(\log p)$ 查询 $x^{-1}$

                        int exgcd(int a,int b,int &x,int &y)
                        +

                        \(\text{Exgcd}\) \(O(\log p)\) 查询 \(x^{-1}\)

                        +
                        int exgcd(int a,int b,int &x,int &y)
                         {
                             int d=a;
                             if(!b)x=1,y=0;
                        @@ -565,12 +606,26 @@ 

                        =(x%p+p)%p; return x; }

                        -

                        $O(n)$ 预处理阶乘的逆元

                        因为 $n!=(n-1)! \cdot n$ ,所以 $((n-1)!)^{-1}=(n!)^{-1} \times n$

                        -
                        -

                        欧拉函数

                        欧拉函数 $\varphi(n)$ 表示小于等于 $n$ 和 $n$ 互质的数的个数。

                        -

                        设 $n=\prod_{i=1}^{s}p_i^{k_i}$

                        -

                        代码求解

                        $O(\sqrt{n})$ 求 $\varphi(n)$

                        板子题:poj2407

                        +

                        \(O(n)\) +预处理阶乘的逆元

                        +

                        因为 \(n!=(n-1)! \cdot n\) ,所以 +\(((n-1)!)^{-1}=(n!)^{-1} \times +n\)

                        +
                        +

                        欧拉函数

                        +

                        欧拉函数 \(\varphi(n)\) 表示小于等于 +\(n\)\(n\) 互质的数的个数。

                        +

                        \(n=\prod_{i=1}^{s}p_i^{k_i}\) +\[ +\varphi(n) = n \prod\limits_{i=1}^s\left(\dfrac{p_i-1}{p_i}\right) = +\prod(p_i-1)\times p_i^{k_i-1} +\]

                        +

                        代码求解

                        +

                        \(O(\sqrt{n})\)\(\varphi(n)\)

                        +

                        板子题:poj2407

                        int Euler_phi(int n)
                         {
                             int ans=n;
                        @@ -595,7 +650,11 @@ 

                        return 0; }

                        -

                        线性筛 $O(n)$ 求解

                        poj2478 (这个题要改成 $\varphi$ 的前缀和)

                        +

                        线性筛 \(O(n)\) +求解

                        +

                        poj2478 +(这个题要改成 \(\varphi\) +的前缀和)

                        int phi[N],prime[N],pcnt;
                         bool ck[N];
                         void Euler()
                        @@ -625,21 +684,31 @@ 

                        } } }

                        -
                        -

                        欧拉定理

                        若 $\gcd(a,m) = 1$ ,则 $a^{\varphi(m)} \equiv 1\pmod{m}$

                        -

                        当 $m$ 为素数时,根据欧拉函数的性质有 $a^{m-1}\equiv 1 \pmod{m}$ ,即费马小定理

                        -
                        -

                        扩展欧拉定理

                        应用

                        例题:

                        -

                        -

                        $1\le a,m\le 10^{12},1\le b\le 10^{20000000}$(这个是加强版的,朴素版只要int就行)

                        +\end{aligned} +\]

                        +

                        应用

                        +

                        例题:

                        +

                        \[ +a^b\bmod m +\] \(1\le a,m\le 10^{12},1\le b\le +10^{20000000}\)(这个是加强版的,朴素版只要int就行)

                        #include <bits/stdc++.h>
                         using namespace std;
                         #define int long long
                        @@ -688,11 +757,19 @@ 

                        printf("%lld\n",qpow(a,b,m)); return 0; }

                        -
                        -

                        Lucas定理

                        对于质数 $p$ ,有

                        -

                        $\binom{\left\lfloor{n/p}\right\rfloor}{\left\lfloor{m/p}\right\rfloor}$ 采用递归处理,后面的直接 $O(p)$ 预处理组合数

                        -

                        板子题:P3807 【模板】卢卡斯定理/Lucas 定理

                        +
                        +

                        Lucas定理

                        +

                        对于质数 \(p\) ,有 \[ +\dbinom{n}{m} \bmod p = +\dbinom{\left\lfloor{n/p}\right\rfloor}{\left\lfloor{m/p}\right\rfloor} +\times \dbinom{n \bmod p}{m \bmod p} \bmod p +\] \(\binom{\left\lfloor{n/p}\right\rfloor}{\left\lfloor{m/p}\right\rfloor}\) +采用递归处理,后面的直接 \(O(p)\) +预处理组合数

                        +

                        板子题:P3807 +【模板】卢卡斯定理/Lucas 定理

                        #include <iostream>
                         #include <string>
                         #include <vector>
                        @@ -744,14 +821,19 @@ 

                        } return 0; }

                        -
                        -

                        中国剩余定理

                        中国剩余定理(CRT)可求解如下形式的一元线性同余方程组

                        -

                        其中 $n_1,n_2,\cdots,n_k$ 两两互质 ,扩展中国剩余定理可以解决这个问题

                        -

                        板子题:P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪

                        +
                        +

                        中国剩余定理

                        +

                        中国剩余定理(CRT)可求解如下形式的一元线性同余方程组 \[ +\begin{cases} x \equiv b_1\ ({\rm mod}\ a_1) +\\\\ x\equiv b_2\ ({\rm mod}\ a_2) +\\ \\\dots +\\\\ x \equiv b_n\ ({\rm mod}\ a_n)\end{cases} +\] 其中 \(n_1,n_2,\cdots,n_k\) +两两互质 ,扩展中国剩余定理可以解决这个问题

                        +

                        板子题:P1495 +【模板】中国剩余定理(CRT)/ 曹冲养猪

                        懒得背这个,直接去背扩展中国剩余定理。

                        #include <iostream>
                         #include <string>
                        @@ -800,9 +882,11 @@ 

                        << solve() << '\n'; return 0; }

                        -
                        -

                        扩展中国剩余定理

                        只会个板子,没理解,待补充。

                        -

                        板子题:P4777 【模板】扩展中国剩余定理(EXCRT)

                        +
                        +

                        扩展中国剩余定理

                        +

                        只会个板子,没理解,待补充。

                        +

                        板子题:P4777 +【模板】扩展中国剩余定理(EXCRT)

                        #include <iostream>
                         #include <string>
                         #include <vector>
                        @@ -851,16 +935,21 @@ 

                        << excrt() << '\n'; return 0; }

                        -
                        -

                        莫比乌斯函数

                        $\mu$ 为莫比乌斯函数,定义为

                        -

                        代码求解

                        线性筛 $O(n)$ 求 $\mu$

                        +\end{aligned} +\]

                        +

                        代码求解

                        +

                        线性筛 \(O(n)\)\(\mu\)

                        void Mobius()
                         {
                             mu[1]=1;
                        @@ -886,86 +975,172 @@ 

                        } } }

                        -
                        -

                        莫比乌斯反演

                        应用

                        一个小转化(仅改变这两个相关和式,左侧其他的 $\Sigma$ 不变)

                        -

                        设 $d(x)$为 $x$ 的约数个数,则有

                        -
                        -

                        拉格朗日定理

                        设 $p$ 为素数,对于模 $p$ 意义下的整系数多项式

                        -

                        的同余方程 $f(x) \equiv 0 \pmod{p}$ 在模 $p$ 意义下至多有 $n$ 个不同解

                        -

                        应用

                        关于同余方程的引理

                        -

                        对于任意 $a$ ,至多有 $n$ 个不同的 $x$ 满足同余方程 $nx \equiv a \pmod{m}$

                        +=\sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[\gcd(i,j)=1] +\] 设 \(d(x)\)\(x\) 的约数个数,则有 \[ +d(ij)=\sum_{x\mid i}\sum_{y \mid j}[\gcd(x,y)=1] +\]

                        +
                        +

                        拉格朗日定理

                        +

                        \(p\) 为素数,对于模 \(p\) 意义下的整系数多项式 \[ +f(x) = \sum_{n\ge 0}a_nx^n,p\nmid a_n +\] 的同余方程 \(f(x) \equiv 0 +\pmod{p}\) 在模 \(p\) +意义下至多有 \(n\) 个不同解

                        +

                        应用

                        +

                        关于同余方程的引理

                        +

                        对于任意 \(a\) ,至多有 \(n\) 个不同的 \(x\) 满足同余方程 \(nx \equiv a \pmod{m}\)

                        应用于抽象代数的定理中

                        -

                        在有限可交换群 $G$ 中,以下两个条件等价:

                        -

                        $G$ 是循环群。

                        -

                        对于任意一个元素 $a$ ,至多有 $n$ 个不同的元素 $x$ 满足条件 $x^n=a$ 。

                        +

                        在有限可交换群 \(G\) +中,以下两个条件等价:

                        +

                        \(G\) 是循环群。

                        +

                        对于任意一个元素 \(a\) ,至多有 +\(n\) 个不同的元素 \(x\) 满足条件 \(x^n=a\)

                        -

                        结论:对于素数 $p$ ,模 $p$ 的简化剩余系 $Z_p^*$ 对于乘法构成的群是循环群

                        -
                        -

                        原根

                        :满足同余式 $a^n \equiv 1 \pmod{m}$ 的最小正整数 $n$ 存在,这个 $n$ 称作 $a$ 模 $m$ 的阶,记作 $\delta_m(a)$

                        +

                        结论:对于素数 \(p\) ,模 \(p\) 的简化剩余系 \(Z_p^*\) 对于乘法构成的群是循环群

                        +
                        +

                        原根

                        +

                        :满足同余式 \(a^n +\equiv 1 \pmod{m}\) 的最小正整数 \(n\) 存在,这个 \(n\) 称作 \(a\)\(m\) 的阶,记作 \(\delta_m(a)\)

                        -

                        在抽象代数中,这里的“阶”就是模 $m$ 缩剩余系关于乘法形成的群中,元素 $a$ 的阶。记号 $\delta$ 表示阶也只用于这个特殊的群。

                        +

                        在抽象代数中,这里的“阶”就是模 \(m\) +缩剩余系关于乘法形成的群中,元素 \(a\) +的阶。记号 \(\delta\) +表示阶也只用于这个特殊的群。

                        下面的诸多性质可以直接扩展到抽象代数中阶的性质。

                        阶的性质

                        -
                          -
                        1. $a,a^2,\dots,a^{\delta_m(a)}$ 模 $m$ 两两不同余

                          -
                        2. -
                        3. 若 $a^n \equiv 1 \pmod{m}$ ,则 $\delta_m(a) \mid n$

                          -
                        4. -
                        5. 若 $a^p \equiv a^q \pmod{m}$ ,则有 $p\equiv q \pmod{\delta_m(a)}$

                          -
                        6. -
                        7. 设 $m \in \mathbb{N}^*,a,b \in \mathbb{Z} ,\gcd(a,m)=\gcd(b,m)=1$ ,则

                          -

                          的充分必要条件是

                          -
                        8. -
                        9. 设 $k\in \mathbb{N},m\in \mathbb{N}^*,a\in\mathbb{Z} ,\gcd(a,m)=1$ ,则

                          -
                        10. +
                            +
                          1. \(a,a^2,\dots,a^{\delta_m(a)}\) +模 \(m\) 两两不同余

                          2. +
                          3. \(a^n \equiv 1 \pmod{m}\) +,则 \(\delta_m(a) \mid n\)

                          4. +
                          5. \(a^p \equiv a^q \pmod{m}\) +,则有 \(p\equiv q +\pmod{\delta_m(a)}\)

                          6. +
                          7. \(m \in \mathbb{N}^*,a,b \in +\mathbb{Z} ,\gcd(a,m)=\gcd(b,m)=1\) ,则 \[ +\delta_m(ab)=\delta_m(a)\delta_m(b) +\] 的充分必要条件是 \[ +\gcd(\delta_m(a),\delta_m(b))=1 +\]

                          8. +
                          9. \(k\in \mathbb{N},m\in +\mathbb{N}^*,a\in\mathbb{Z} ,\gcd(a,m)=1\) ,则 \[ +\delta_m(a^k)=\dfrac{\delta_m(a)}{\gcd(\delta_m(a),k)} +\]

                          -

                          原根:设 $m\in \mathbb{N}^*,a \in \mathbb{Z} $ 若 $\gcd(a,m)=1$ ,且 $\delta_m(a) = \varphi(m)$ ,则称 $a$ 为模 $m$ 的原根

                          +

                          原根:设 $m^*,a $ 若 \(\gcd(a,m)=1\) ,且 \(\delta_m(a) = \varphi(m)\) ,则称 \(a\) 为模 \(m\) 的原根

                          -

                          在抽象代数中,原根就是循环群的生成元。这个概念只在模 $m$ 缩剩余系关于乘法形成的群中有“原根”这个名字,在一般的循环群中都称作“生成元”。

                          -

                          并非每个模 $m$ 缩剩余系关于乘法形成的群都是循环群,存在原根就表明它同构于循环群,如果不存在原根就表明不同构。

                          +

                          在抽象代数中,原根就是循环群的生成元。这个概念只在模 \(m\) +缩剩余系关于乘法形成的群中有“原根”这个名字,在一般的循环群中都称作“生成元”。

                          +

                          并非每个模 \(m\) +缩剩余系关于乘法形成的群都是循环群,存在原根就表明它同构于循环群,如果不存在原根就表明不同构。

                          -

                          原根判定定理:设 $m\ge 3,\gcd(a,m)=1$ ,则 $a$ 是模 $m$ 的原根的充要条件是,对于 $\varphi(m)$ 的每个素因数 $p$ ,都有 $a^{\frac{\varphi(m)}{p}}\not\equiv 1 \pmod{m}$

                          -

                          原根个数:若一个数 $m$ 有原根,则它的原根个数为 $\varphi(\varphi(m))$

                          -

                          原根存在定理:一个数 $m$ 存在原根当且仅当 $m=2,4,p^\alpha,2p^\alpha$ ,其中 $p$ 为奇素数,$\alpha \in \mathbb{N}^*$

                          +

                          原根判定定理:设 \(m\ge +3,\gcd(a,m)=1\) ,则 \(a\) 是模 +\(m\) 的原根的充要条件是,对于 \(\varphi(m)\) 的每个素因数 \(p\) ,都有 \(a^{\frac{\varphi(m)}{p}}\not\equiv 1 +\pmod{m}\)

                          +

                          原根个数:若一个数 \(m\) 有原根,则它的原根个数为 \(\varphi(\varphi(m))\)

                          +

                          原根存在定理:一个数 \(m\) 存在原根当且仅当 \(m=2,4,p^\alpha,2p^\alpha\) ,其中 \(p\) 为奇素数,\(\alpha \in \mathbb{N}^*\)

                            -
                          • 定理1:对于奇素数 $p$ ,$p$ 有原根
                          • -
                          • 定理2:对于奇素数 $p$ ,$\alpha \in \mathbb{N}^*$ ,$p^\alpha$ 有原根
                          • -
                          • 定理3:对于奇素数 $p$ ,$\alpha\in\mathbb{N}^*$ ,$2p^\alpha$ 的原根存在
                          • -
                          • 定理4:对于 $m\ne2,4$ ,且不存在奇素数 $p$ 及 $\alpha \in \mathbb{N}^*$ 使得 $m=p^\alpha,2p^\alpha$ ,模 $m$ 的原根不存在
                          • +
                          • 定理1:对于奇素数 \(p\)\(p\) 有原根
                          • +
                          • 定理2:对于奇素数 \(p\)\(\alpha \in \mathbb{N}^*\)\(p^\alpha\) 有原根
                          • +
                          • 定理3:对于奇素数 \(p\)\(\alpha\in\mathbb{N}^*\)\(2p^\alpha\) 的原根存在
                          • +
                          • 定理4:对于 \(m\ne2,4\) +,且不存在奇素数 \(p\)\(\alpha \in \mathbb{N}^*\) 使得 \(m=p^\alpha,2p^\alpha\) ,模 \(m\) 的原根不存在
                          -
                          -

                          二次剩余

                          对于二次同余方程 $x^2\equiv n \pmod{p}$ 有解,则称 $n$ 为 $p$ 的二次剩余,$x$ 为该二次同余方程的解

                          -

                          二次剩余 $n$ 就是一个二次项 $\bmod p$ 后的剩余,$n$ 不可以是 $p$ 的倍数

                          -

                          Legendre 符号(中间那个发音是g不是j)

                          -

                          Legendre 符号为完全积性函数

                          +1,&p\nmid a \text{ 且 }a \text{ 是模 }p \text{ 的二次剩余} , +\\-1,&p\nmid a \text{ 且 }a \text{ 不是模 }p \text{ 的二次剩余} , +\\0,&p\mid a. +\end{cases} +\] Legendre 符号为完全积性函数

                          Euler 判别准则

                          -

                          对于奇素数

                          -
                          -

                          引理:令 $p$ 为素数和模 $p$ 意义下原根 $g$ 并令 $a\equiv g^k \pmod{p}$ ,那么 $x^2 \equiv a \pmod{p}$ 有解当且仅当 $k$ 为偶数

                          +\end{cases} +\]

                          +
                          +

                          引理:令 \(p\) 为素数和模 \(p\) 意义下原根 \(g\) 并令 \(a\equiv g^k \pmod{p}\) ,那么 \(x^2 \equiv a \pmod{p}\) 有解当且仅当 \(k\) 为偶数

                          二次剩余和二次非剩余的数量

                          -

                          对于奇素数 $p$ 和集合 $\{1,2,\dots,p-1\}$ ,在模 $p$ 意义下二次剩余的数量等于二次非剩余的数量

                          +

                          对于奇素数 \(p\) 和集合 \(\{1,2,\dots,p-1\}\) ,在模 \(p\) +意义下二次剩余的数量等于二次非剩余的数量

                          -

                          引理:对于 $d \mid (p-1)$ 和奇素数 $p\in \mathbb{Z} $ ,$x^d \equiv 1 \pmod{p}$ 恰有 $d$ 个解

                          +

                          引理:对于 \(d \mid (p-1)\) 和奇素数 +$p $ ,\(x^d \equiv 1 \pmod{p}\) 恰有 +\(d\) 个解

                          @@ -1336,7 +1511,7 @@

                           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/21/cf786b-legacy-ti-jie/index.html b/2022/08/21/cf786b-legacy-ti-jie/index.html index 4cc742b450..406100067f 100644 --- a/2022/08/21/cf786b-legacy-ti-jie/index.html +++ b/2022/08/21/cf786b-legacy-ti-jie/index.html @@ -476,39 +476,60 @@

                          CF786B Legacy 题解

                          -

                          CF786B Legacy 题解

                          题目链接:CF786B Legacy

                          +

                          CF786B Legacy 题解

                          +

                          题目链接:CF786B +Legacy

                          题意

                          -

                          $n$ 个结点, $q$ 次操作,问操作后从 $s$ 出发的单源最短路

                          +

                          \(n\) 个结点, \(q\) 次操作,问操作后从 \(s\) 出发的单源最短路

                          操作如下

                            -
                          • 输入 1 u v w 表示 $u$ 到 $v$ 连一条有向边
                          • -
                          • 输入 2 u l r w 表示 $u$ 到 $[l,r]$ 的每个结点连一条有向边
                          • -
                          • 输入 3 u l r w 表示 $[l,r]$ 的每个结点向 $u$ 连一条有向边
                          • +
                          • 输入 1 u v w 表示 \(u\)\(v\) 连一条有向边
                          • +
                          • 输入 2 u l r w 表示 \(u\)\([l,r]\) 的每个结点连一条有向边
                          • +
                          • 输入 3 u l r w 表示 \([l,r]\) 的每个结点向 \(u\) 连一条有向边
                          -

                          对于 $100\%$ 的数据,$1\le n,q \le 10^5$,$1\le w \le 10^9$

                          +

                          对于 \(100\%\) 的数据,\(1\le n,q \le 10^5\)\(1\le w \le 10^9\)

                          线段树优化建图的板子

                          考虑建两棵线段树,分别称为「入树」和「出树」

                          -

                          入树的父节点向子结点连一条边权为 $0$ 的有向边

                          -

                          出树的子节点向父结点连一条边权为 $0$ 的有向边

                          +

                          入树的父节点向子结点连一条边权为 \(0\) 的有向边

                          +

                          出树的子节点向父结点连一条边权为 \(0\) 的有向边

                          两棵树的叶子结点均表示原图中的点

                          -

                          这样每次区间的连边,我们直接在线段树上把管理对应区间的结点当作 $v$ 即可

                          +

                          这样每次区间的连边,我们直接在线段树上把管理对应区间的结点当作 \(v\) 即可

                          具体地,

                            -
                          • 把 $u$ 到 $[l,r]$ 的每个结点连一条有向边

                            -

                            我们只要找出在入树上对应的结点,把出树中的 $u$ 向这些结点都连一条有向边就行了

                            -
                          • -
                          • 把 $[l,r]$ 的每个结点向 $u$ 连一条有向边

                            -

                            我们只要找出在出树上对应的结点,把这些结点都向入树中的 $u$ 连一条有向边就行了

                            -
                          • +
                          • \(u\)\([l,r]\) 的每个结点连一条有向边

                            +

                            我们只要找出在入树上对应的结点,把出树中的 \(u\) 向这些结点都连一条有向边就行了

                          • +
                          • \([l,r]\) 的每个结点向 \(u\) 连一条有向边

                            +

                            我们只要找出在出树上对应的结点,把这些结点都向入树中的 \(u\) 连一条有向边就行了

                          -

                          这么说很抽象,给张图大家就懂了 made by diagrams

                          -

                          -

                          -

                          可以证明,这样修改的复杂度为 $O(\log n)$

                          -

                          总时间复杂度 $O(n\log n + q \log n)$

                          -

                          注意要记录结点 $i$ 对应入树的叶子结点的编号,这两个是不一样的!

                          +

                          这么说很抽象,给张图大家就懂了 made by diagrams

                          +

                          +

                          +

                          可以证明,这样修改的复杂度为 \(O(\log +n)\)

                          +

                          总时间复杂度 \(O(n\log n + q \log +n)\)

                          +

                          注意要记录结点 \(i\) +对应入树的叶子结点的编号,这两个是不一样的!

                          代码:

                          #include <iostream>
                           #include <string>
                          @@ -645,11 +666,17 @@ 

                          }

                          题外话

                          -

                          Rick and his co-workers have made a new radioactive formula and a lot of bad guys are after them. So Rick wants to give his legacy to Morty before bad guys catch them.

                          -

                          Rick 和他的同事们做出了一种新的带放射性的婴儿食品(???根据图片和原文的确如此…),与此同时很多坏人正追赶着他们。因此 Rick 想在坏人们捉到他之前把他的遗产留给 Morty。

                          +

                          Rick and his co-workers have made a new radioactive formula and a lot +of bad guys are after them. So Rick wants to give his legacy to Morty +before bad guys catch them.

                          +

                          Rick +和他的同事们做出了一种新的带放射性的婴儿食品(???根据图片和原文的确如此...),与此同时很多坏人正追赶着他们。因此 +Rick 想在坏人们捉到他之前把他的遗产留给 Morty。

                          -

                          带放射性的婴儿食品。给我来一碗,美汁汁

                          -

                          +

                          带放射性的婴儿食品。给我来一碗,美汁汁

                          +

                          @@ -1027,7 +1054,7 @@

                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/21/luo-gu-p3193-hnoi2008-gt-kao-shi-ti-jie/index.html b/2022/08/21/luo-gu-p3193-hnoi2008-gt-kao-shi-ti-jie/index.html index ea3681bac4..cc5b08db19 100644 --- a/2022/08/21/luo-gu-p3193-hnoi2008-gt-kao-shi-ti-jie/index.html +++ b/2022/08/21/luo-gu-p3193-hnoi2008-gt-kao-shi-ti-jie/index.html @@ -480,31 +480,67 @@

                          洛谷P3193 [HNOI2008]GT考试
                          -

                          洛谷P3193 [HNOI2008]GT考试 题解

                          题目链接:P3193 [HNOI2008]GT考试

                          +

                          洛谷P3193 [HNOI2008]GT考试 +题解

                          +

                          题目链接:P3193 +[HNOI2008]GT考试

                          题意

                          -

                          阿申准备报名参加 GT 考试,准考证号为 $N$ 位数$X_1,X_2…X_n(0\le X_i\le9)$,他不希望准考证号上出现不吉利的数字。
                          他的不吉利数字$A_1,A_2…A_m(0\le A_i\le 9)$ 有 $M$ 位,不出现是指 $X_1,X_2…X_n$ 中没有恰好一段等于 $A_1,A_2…A_m$,$A_1$ 和$X_1$ 可以为 $0$

                          -

                          $N\leq10^9,M\leq20,K\leq1000$

                          +

                          阿申准备报名参加 GT 考试,准考证号为 \(N\) 位数\(X_1,X_2…X_n(0\le +X_i\le9)\),他不希望准考证号上出现不吉利的数字。 +他的不吉利数字\(A_1,A_2…A_m(0\le A_i\le +9)\)\(M\) 位,不出现是指 +\(X_1,X_2…X_n\) 中没有恰好一段等于 +\(A_1,A_2…A_m\)\(A_1\)\(X_1\) 可以为 \(0\)

                          +

                          \(N\leq10^9,M\leq20,K\leq1000\)

                          显然是矩阵快速幂优化dp(看数据范围)。

                          -

                          设 $f_{i,j}$ 表示考虑到第 $i$ 个数,匹配到 $X$ 的第 $j$ 位时的方案数,则

                          -

                          注意这里的 $0\le j < m$ ,因为我们不能让它包含完整的 $X$ 。

                          -

                          这个 $p$ 不一定是 $0$ 或 $j-1$ ,根据KMP的性质可以很容易发现。

                          +

                          \(f_{i,j}\) 表示考虑到第 \(i\) 个数,匹配到 \(X\) 的第 \(j\) 位时的方案数,则 \[ +f_{i,j} = \sum_{k=0}^{9}f_{i-1,p} +\] 注意这里的 \(0\le j < m\) +,因为我们不能让它包含完整的 \(X\) +。

                          +

                          这个 \(p\) 不一定是 \(0\)\(j-1\) ,根据KMP的性质可以很容易发现。

                          看上去这个可以用KMP来搞搞,又长的很像矩阵快速幂的题目

                          -

                          于是尝试套路地构造出预处理数组 $g_{i,j}$ ,以产生矩阵快速幂的形式

                          -

                          设 $g_{i,j}$ 表示匹配到 $X$ 的第 $i$ 位,有多少种加字符 $0\sim9$ 的方法可以使其匹配到 $j$

                          -

                          注意到题目中 $X_1$ 是可以为 $0$ 的,因此不用单独考虑前导零的问题,则

                          -

                          注意这里 $k$ 的意义改变为了:枚举已经匹配的长度,尝试加一个字符转移至 $j$ 。

                          -

                          然后把式子看成这样的形式 $f^{i}_{j} =\sum f^{i-1}_{k} \times g_{k,j}$ ,显然与矩阵乘法的定义相同

                          -

                          我们把 $f$ 和 $g$ 看作两个矩阵(注意 $f$ 是行矩阵),则有

                          -

                          -

                          然后我们矩阵快速幂求一下就好啦

                          -

                          最后答案就是 $\sum\limits_{i=0}^{m-1} f^{n}_{i}$ ,没有 $m-1$ 的原因是,不能包含 $m$

                          -

                          时间复杂度 $O(m^3 \log n)$

                          +

                          于是尝试套路地构造出预处理数组 \(g_{i,j}\) ,以产生矩阵快速幂的形式

                          +

                          \(g_{i,j}\) 表示匹配到 \(X\) 的第 \(i\) 位,有多少种加字符 +\(0\sim9\) 的方法可以使其匹配到 \(j\)

                          +

                          注意到题目中 \(X_1\) 是可以为 \(0\) 的,因此不用单独考虑前导零的问题,则 +\[ +f_{i,j} = \sum_{k=0}^{m-1} f_{i-1,k}\times g_{k,j} +\] 注意这里 \(k\) +的意义改变为了:枚举已经匹配的长度,尝试加一个字符转移至 \(j\)

                          +

                          然后把式子看成这样的形式 \(f^{i}_{j} =\sum +f^{i-1}_{k} \times g_{k,j}\) ,显然与矩阵乘法的定义相同

                          +

                          我们把 \(f\)\(g\) 看作两个矩阵(注意 \(f\) 是行矩阵),则有 \[ +f^{k} = f^{k-1} \times g +\]\[ +f^k=g^k +\] 然后我们矩阵快速幂求一下就好啦

                          +

                          最后答案就是 \(\sum\limits_{i=0}^{m-1} +f^{n}_{i}\) ,没有 \(m-1\) +的原因是,不能包含 \(m\)

                          +

                          时间复杂度 \(O(m^3 \log n)\)

                          代码:

                          #include <iostream>
                           #include <string>
                          @@ -956,7 +992,7 @@ 

                           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/21/luo-gu-p3311-sdoi2014-shu-shu-ti-jie/index.html b/2022/08/21/luo-gu-p3311-sdoi2014-shu-shu-ti-jie/index.html index 797b48e6f8..499a9bb532 100644 --- a/2022/08/21/luo-gu-p3311-sdoi2014-shu-shu-ti-jie/index.html +++ b/2022/08/21/luo-gu-p3311-sdoi2014-shu-shu-ti-jie/index.html @@ -476,22 +476,57 @@

                          洛谷P3311 [SDOI2014] 数数
                          -

                          洛谷P3311 [SDOI2014] 数数 题解

                          题目链接:P3311 [SDOI2014] 数数

                          +

                          洛谷P3311 [SDOI2014] 数数 +题解

                          +

                          题目链接:P3311 +[SDOI2014] 数数

                          题意

                          -

                          我们称一个正整数 $x$ 是幸运数,当且仅当它的十进制表示中不包含数字串集合 $s$ 中任意一个元素作为其子串。例如当 $s = \{22, 333, 0233\}$ 时,$233$ 是幸运数,$2333$、$20233$、$3223$ 不是幸运数。给定 $n$ 和 $s$,计算不大于 $n$ 的幸运数个数。

                          -

                          答案对 $10^9 + 7$ 取模。

                          -

                          $1 \leq n < 10^{1201}$,$1 \leq m \leq 100$,$1 \leq \sum_{i = 1}^m |s_i| \leq 1500$,$\min_{i = 1}^m |s_i| \geq 1$,其中 $|s_i|$ 表示字符串 $s_i$ 的长度。$n$ 没有前导 $0$,但是 $s_i$ 可能有前导 $0$。

                          +

                          我们称一个正整数 \(x\) +是幸运数,当且仅当它的十进制表示中不包含数字串集合 \(s\) 中任意一个元素作为其子串。例如当 \(s = \{22, 333, 0233\}\) 时,\(233\) 是幸运数,\(2333\)\(20233\)\(3223\) 不是幸运数。给定 \(n\)\(s\),计算不大于 \(n\) 的幸运数个数。

                          +

                          答案对 \(10^9 + 7\) 取模。

                          +

                          \(1 \leq n < 10^{1201}\)\(1 \leq m \leq 100\)\(1 \leq \sum_{i = 1}^m |s_i| \leq +1500\)\(\min_{i = 1}^m |s_i| \geq +1\),其中 \(|s_i|\) 表示字符串 +\(s_i\) 的长度。\(n\) 没有前导 \(0\),但是 \(s_i\) 可能有前导 \(0\)

                          关键词:数位dp、AC自动机

                          -

                          可能的前置芝士:P4052 [JSOI2007]文本生成器 (涉及“危险结点”的定义)

                          -

                          和P4052一样,我们先建出 $\tt{AC}$ 自动机,然后找到所有的危险结点

                          -

                          因为这道题要求答案不超过 $n$ ,并且 $n < 10^{1201}$ ,考虑数位dp

                          -

                          设 $f_{i,j}$ 表示考虑 $n$ 的前 $i$ 位(从高位向低位数),走到 $\tt{AC}$ 自动机的 $j$ 结点的答案。

                          +

                          可能的前置芝士:P4052 +[JSOI2007]文本生成器 (涉及“危险结点”的定义)

                          +

                          和P4052一样,我们先建出 \(\tt{AC}\) +自动机,然后找到所有的危险结点

                          +

                          因为这道题要求答案不超过 \(n\) +,并且 \(n < 10^{1201}\) +,考虑数位dp

                          +

                          \(f_{i,j}\) 表示考虑 \(n\) 的前 \(i\) 位(从高位向低位数),走到 \(\tt{AC}\) 自动机的 \(j\) 结点的答案。

                          然后用记搜搞搞就好了,懒得写转移方程,直接看代码

                          -

                          值得注意的是,如果数位dp的过程中 $j$ 走到了危险结点,

                          -

                          则要立刻返回 $0$ ,因为不能包含任何的非法子串 $s_i$

                          -

                          时间复杂度 $O(n \times \sum |s_i|)$

                          +

                          值得注意的是,如果数位dp的过程中 \(j\) 走到了危险结点,

                          +

                          则要立刻返回 \(0\) +,因为不能包含任何的非法子串 \(s_i\)

                          +

                          时间复杂度 \(O(n \times \sum +|s_i|)\)

                          代码:

                          #include <iostream>
                           #include <string>
                          @@ -960,7 +995,7 @@ 

                           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/21/luo-gu-p6348-pa2011-journeys-ti-jie/index.html b/2022/08/21/luo-gu-p6348-pa2011-journeys-ti-jie/index.html index 1d94d191d6..824b44e4e7 100644 --- a/2022/08/21/luo-gu-p6348-pa2011-journeys-ti-jie/index.html +++ b/2022/08/21/luo-gu-p6348-pa2011-journeys-ti-jie/index.html @@ -476,30 +476,51 @@

                          洛谷P6348 [PA2011]Journeys 题
                          -

                          洛谷P6348 [PA2011]Journeys 题解

                          题目链接:P6348 [PA2011]Journeys

                          +

                          洛谷P6348 [PA2011]Journeys +题解

                          +

                          题目链接:P6348 +[PA2011]Journeys

                          题意

                          -

                          给定 $n$ 个结点, $m$ 次操作

                          -

                          每次操作给出 $a,b,c,d$ ,表示

                          -

                          也就是编号在 $[a,b]$ 内的所有结点与编号在 $[c,d]$ 内的结点存在边权为 $1$ 的无向边

                          -

                          求 $s$ 到每个点的最短路。

                          -

                          $1\le n\le 5\times 10^5$,$1\le m\le 10^5$,$1\le a\le b\le n$,$1\le c\le d\le n$。

                          +

                          给定 \(n\) 个结点, \(m\) 次操作

                          +

                          每次操作给出 \(a,b,c,d\) ,表示 +\[ +\forall x\in [a,b],\forall y\in [c,d], \exist (x,y) \in E\land (y,x) \in +E +\] 也就是编号在 \([a,b]\) +内的所有结点与编号在 \([c,d]\) +内的结点存在边权为 \(1\) +的无向边

                          +

                          \(s\) 到每个点的最短路。

                          +

                          \(1\le n\le 5\times 10^5\)\(1\le m\le 10^5\)\(1\le a\le b\le n\)\(1\le c\le d\le n\)

                          -

                          线段树优化建图板子(区间连区间),不会单点连区间的戳这里

                          +

                          线段树优化建图板子(区间连区间),不会单点连区间的戳这里

                          同样的,我们还是建两棵树,称为「入树」和「出树」

                          先考虑连有向边的情况。

                          对于区间的修改,我们只需要把入树和出树中的结点提出来

                          -

                          出树中提取的结点构成 $S$ ,入树中提取的结点构成 $T$

                          -

                          我们新建一个虚点 $u$ ,把 $S$ 中的每个结点向 $u$ 连一条边权为 $0$ 的有向边

                          -

                          然后再把 $u$ 向 $T$ 中的每个结点分别连一条边权为 $1$ 的有向边

                          +

                          出树中提取的结点构成 \(S\)入树中提取的结点构成 +\(T\)

                          +

                          我们新建一个虚点 \(u\) ,把 \(S\) 中的每个结点向 \(u\) 连一条边权为 \(0\) 的有向边

                          +

                          然后再把 \(u\)\(T\) 中的每个结点分别连一条边权为 \(1\) 的有向边

                          然后这道题要连无向边,所以我们反过来再做一次就行了

                          注意!反过来的时候一定要再建一个虚点,不然会出错!(显然)

                          纯文本可能不是很好懂,所以放张图。

                          -

                          -

                          这道题因为边权只有 $0,1$ ,考虑 $\mathtt{01bfs}$

                          -

                          时间复杂度 $O((n+m)\log n)$

                          -

                          空间复杂度 $O(2n\log n + 2m)$

                          +

                          +

                          这道题因为边权只有 \(0,1\) ,考虑 +\(\mathtt{01bfs}\)

                          +

                          时间复杂度 \(O((n+m)\log n)\)

                          +

                          空间复杂度 \(O(2n\log n + 2m)\)

                          代码:(常数比较大

                          #include <iostream>
                           #include <string>
                          @@ -1012,7 +1033,7 @@ 

                           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/22/cf526g-spiders-evil-plan-ti-jie/index.html b/2022/08/22/cf526g-spiders-evil-plan-ti-jie/index.html index 34295fee62..b675dd6ec5 100644 --- a/2022/08/22/cf526g-spiders-evil-plan-ti-jie/index.html +++ b/2022/08/22/cf526g-spiders-evil-plan-ti-jie/index.html @@ -476,60 +476,111 @@

                          CF526G Spiders Evil Plan 题解<
                          -

                          CF526G Spiders Evil Plan 题解

                          题目链接:CF526G Spiders Evil Plan

                          +

                          CF526G Spiders Evil Plan +题解

                          +

                          题目链接:CF526G +Spiders Evil Plan

                          题意

                          -

                          二次魔改自 link

                          +

                          二次魔改自 link

                          最近 q779 迷上了一个叫做割绳子 3 的yúsì(游戏)。

                          这个游戏的要求是你要扮演蜘蛛去吃掉一个糖果。

                          -

                          那是一个像割绳子游戏一样的界面,共有 $n$ 个节点,和 $n-1$ 条有长度的边,组成了一个树状结构。一共有 $q$ 关,第 $i$ 关,糖果在第 $x_i$ 个节点上,共有 $y_i$ 只蜘蛛。

                          -

                          每只蜘蛛可以用它的网去覆盖任意不同两点 $a$ 和 $b$ 之间的路径,因此,$y$ 只蜘蛛可以覆盖 $y$ 条树上路径的并。当然,不同的蜘蛛选择的路径可以相交。并且,具有如下的要求:

                          -
                            -
                          1. $y$ 条路径必须包含糖果所在的节点 $x$。
                          2. -
                          3. $y$ 条路径的并是连通的。
                          4. -
                          5. 使 $y$ 条路径所经过的边的长度总和最大。
                          6. +

                            那是一个像割绳子游戏一样的界面,共有 \(n\) 个节点,和 \(n-1\) +条有长度的边,组成了一个树状结构。一共有 \(q\) 关,第 \(i\) 关,糖果在第 \(x_i\) 个节点上,共有 \(y_i\) 只蜘蛛。

                            +

                            每只蜘蛛可以用它的网去覆盖任意不同两点 \(a\)\(b\) 之间的路径,因此,\(y\) 只蜘蛛可以覆盖 \(y\) +条树上路径的并。当然,不同的蜘蛛选择的路径可以相交。并且,具有如下的要求:

                            +
                              +
                            1. \(y\) 条路径必须包含糖果所在的节点 +\(x\)
                            2. +
                            3. \(y\) 条路径的并是连通的。
                            4. +
                            5. 使 \(y\) +条路径所经过的边的长度总和最大。

                            可是蜘蛛们好像都非常智障,所以它们需要你帮忙求出最大值。

                            -

                            本题强制在线 ,数据范围: $1 \le x_i,y_i \le n,~1\le n,q \le 10^5$

                            +

                            本题强制在线 ,数据范围: \(1 \le x_i,y_i \le n,~1\le n,q \le +10^5\)

                          关键词:贪心、树剖、树的直径

                          -

                          先不考虑 $x$ 的存在,直接去选这样的路径并

                          +

                          先不考虑 \(x\) +的存在,直接去选这样的路径并

                          肯定是这样子的

                            -
                          • $y=1$ ,选直径
                          • -
                          • $y=2$ ,选 $y=1$ 的再加一个最大的分支
                          • -
                          • $y=3$ ,选 $y=2$ 的再加一个剩下的最大分支
                          • -
                          • $\cdots$
                          • +
                          • \(y=1\) ,选直径
                          • +
                          • \(y=2\) ,选 \(y=1\) 的再加一个最大的分支
                          • +
                          • \(y=3\) ,选 \(y=2\) 的再加一个剩下的最大分支
                          • +
                          • \(\cdots\)
                          -

                          因此可以把直径的一个端点当作根 $\mathtt{rt}$ ,然后去跑个重链剖分(树剖)

                          -

                          然后在所有切出的链中找最优的 $2y-1$ 条链

                          +

                          因此可以把直径的一个端点当作根 \(\mathtt{rt}\) +,然后去跑个重链剖分(树剖)

                          +

                          然后在所有切出的链中找最优的 \(2y-1\) 条链

                          注:下文中的链均指树剖后的链

                          -

                          为什么是 $2y-1$ 条链?怎么最优?

                          +

                          为什么是 \(2y-1\) +条链?怎么最优?

                          首先每条切出来的链一定包含叶子结点,从重链剖分的性质可以得出

                          -

                          那么就是选 $2y-1$ 个叶子,$-1$ 是因为根节点必选,但它不是叶子。

                          +

                          那么就是选 \(2y-1\) 个叶子,\(-1\) 是因为根节点必选,但它不是叶子。

                          而两两叶子对应的链可以通过根节点并成一条链,显然这是优的。

                          -

                          考虑两两叶子的全序关系。我们令 $\mathtt{len}_i$ 表示叶子 $i$ 对应的链的长度

                          -

                          显然我们选的一定是 $\mathtt{len}_i$ 最大的 $2y-1$ 个叶子。

                          -

                          现在考虑 $x$ 的情况

                          -

                          记 $\mathtt{rnk}_i$ 表示按 $\mathtt{len}_i$ 降序排序后的叶子 $i$ 的排名

                          -

                          如果 $x$ 在最优方案中已经被选了,即 $\mathtt{rnk}_x \le 2y-1$ ,那么直接输出即可

                          -

                          如果 $x$ 不在最优方案中,考虑两种方案的最优者

                          +

                          考虑两两叶子的全序关系。我们令 \(\mathtt{len}_i\) 表示叶子 \(i\) 对应的链的长度

                          +

                          显然我们选的一定是 \(\mathtt{len}_i\) 最大的 \(2y-1\) 个叶子。

                          +

                          现在考虑 \(x\) 的情况

                          +

                          \(\mathtt{rnk}_i\) 表示按 \(\mathtt{len}_i\) 降序排序后的叶子 \(i\) 的排名

                          +

                          如果 \(x\) +在最优方案中已经被选了,即 \(\mathtt{rnk}_x +\le 2y-1\) ,那么直接输出即可

                          +

                          如果 \(x\) +不在最优方案中,考虑两种方案的最优者

                            -
                          • 把 $\mathtt{rnk}_{2y-1}$ 扔了,把 $i$ 加进去

                            -
                          • -
                          • 把 $i$ 到 $\mathtt{rt}$ 的路径找出来,取其中深度最大的一个结点 $\delta$

                            -

                            使得 $\delta$ 在原最优方案中被选了,然后把 $\delta$ 下面原来连的砍了(害怕),把它与 $x$ 接上。

                            -

                            这个可以用倍增数组来 $O(\log n)$ 查。

                            -
                          • +
                          • \(\mathtt{rnk}_{2y-1}\) +扔了,把 \(i\) 加进去

                          • +
                          • \(i\)\(\mathtt{rt}\) +的路径找出来,取其中深度最大的一个结点 \(\delta\)

                            +

                            使得 \(\delta\) +在原最优方案中被选了,然后把 \(\delta\) +下面原来连的砍了(害怕),把它与 \(x\) 接上。

                            +

                            这个可以用倍增数组来 \(O(\log n)\) +查。

                          -

                          然后会发现第二行在 $y=1$ 的情况就挂了

                          -

                          事实上只有 $y=1$ 会挂。特判即可。

                          -

                          为什么呢?因为 $y=1$ 时的最优方案不一定会把 $\mathtt{rt}$ 选进去

                          -

                          稍微因此有点区别。我们还是找到这样的 $\delta$

                          +

                          然后会发现第二行在 \(y=1\) +的情况就挂了

                          +

                          事实上只有 \(y=1\) +会挂。特判即可。

                          +

                          为什么呢?因为 \(y=1\) +时的最优方案不一定会把 \(\mathtt{rt}\) +选进去

                          +

                          稍微因此有点区别。我们还是找到这样的 \(\delta\)

                          然后尝试把它与直径另一端连一下

                          可以证明,这样一定是最优的

                          -

                          然后这题就没了,恭喜您做出了Codeforces评分3300的题目 Orz

                          -

                          时间复杂度 $O(Q \log n)$ ,因为链数与 $\Theta(\log n)$ 同阶,对复杂度影响不大

                          +

                          然后这题就没了,恭喜您做出了Codeforces评分3300的题目 +Orz

                          +

                          时间复杂度 \(O(Q \log n)\) +,因为链数与 \(\Theta(\log n)\) +同阶,对复杂度影响不大

                          代码:

                          #include <iostream>
                           #include <string>
                          @@ -1005,7 +1056,7 @@ 

                           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/22/loj6269-wan-ji-ji-shu-jia-qiang-ban-ti-jie/index.html b/2022/08/22/loj6269-wan-ji-ji-shu-jia-qiang-ban-ti-jie/index.html index 9d3bf0e17e..f9255b0827 100644 --- a/2022/08/22/loj6269-wan-ji-ji-shu-jia-qiang-ban-ti-jie/index.html +++ b/2022/08/22/loj6269-wan-ji-ji-shu-jia-qiang-ban-ti-jie/index.html @@ -476,18 +476,23 @@

                          LOJ6269 烷基计数 加强版
                          -

                          LOJ6269 烷基计数 加强版 题解

                          题目链接:#6269. 烷基计数 加强版

                          +

                          LOJ6269 烷基计数 加强版 题解

                          +

                          题目链接:#6269. 烷基计数 +加强版

                          -

                          题意:众所周知,大连 24 中是一所神奇的学校,在那里,化竞的同学很多都擅长写代码。

                          +

                          题意:众所周知,大连 24 +中是一所神奇的学校,在那里,化竞的同学很多都擅长写代码。

                          有一天,化学不及格的胡小兔向化竞巨佬晴岚请教化学题:

                          -

                          “$n$ 个碳原子的烷基共有多少种同分异构体?”

                          -

                          刚刚得了化竞全市第一的晴岚听了,认为这道题十分简单,建议胡小兔写个程序解决这个问题。但胡小兔弱得连什么是同分异构体都不知道,于是晴岚给胡小兔画了个图——例如 $n=4$ 时 (即丁基),有 $4$ 种同分异构体:

                          -

                          +

                          \(n\) +个碳原子的烷基共有多少种同分异构体?”

                          +

                          刚刚得了化竞全市第一的晴岚听了,认为这道题十分简单,建议胡小兔写个程序解决这个问题。但胡小兔弱得连什么是同分异构体都不知道,于是晴岚给胡小兔画了个图——例如 +\(n=4\) 时 (即丁基),有 \(4\) 种同分异构体:

                          +

                          同理,其他常见烷基同分异构体数目如下表:

                          -
                          - + @@ -498,7 +503,7 @@

                          @@ -509,38 +514,74 @@

                          \(n\),求对应的烷基有多少种同分异构体。

                          +

                          答案对 \(10^9 + 7\) 取模。

                          +

                          数据范围: \(n \le 5\times +10^3\)

                          +

                          注意:这里的烷基计数不用考虑空间异构,能否稳定存在等各种特殊情况。也就是说,你要求的是 +\(n\) 个点的每个点度数不超过 \(4\) 且根的度数不超过 \(3\) 的有根树的数目。

                          据说这种叫:无标号树计数题

                          -

                          最优的解法是 $O(n \log^2 n)$ 的,涉及Pólya原理和生成函数,这里暂且不提。

                          -

                          首先考虑弱化版 #6185. 烷基计数 的dp ( $n \le 400$ )

                          -

                          设 $f_i$ 表示 $i$ -烷基的个数,然后枚举三个子树的大小,乘个组合数就好了

                          -

                          实际上因为 $s_1+s_2+s_3=i-1$ ,因此只用枚举两个子树的大小

                          -

                          时间复杂度 $O(n^3)$ ,可以过弱化版。

                          +

                          最优的解法是 \(O(n \log^2 n)\) +的,涉及Pólya原理和生成函数,这里暂且不提。

                          +

                          首先考虑弱化版 #6185. 烷基计数 +的dp ( \(n \le 400\)

                          +

                          \(f_i\) 表示 \(i\) +-烷基的个数,然后枚举三个子树的大小,乘个组合数就好了

                          +

                          实际上因为 \(s_1+s_2+s_3=i-1\) +,因此只用枚举两个子树的大小

                          +

                          时间复杂度 \(O(n^3)\) +,可以过弱化版。

                          考虑这道题怎么做

                          刚刚的dp瓶颈在于枚举了所有的子树大小

                          还记得树上背包吗?我们也有很多子树,但是我们是一个个子树添加的,而不是枚举每个子树的大小。

                          因此可以尝试像树上背包一样,一个个子树的添加

                          可是这道题的子结点没有区别,因此我们按子树大小顺序一一添加

                          -

                          枚举一个 $\mathtt{sz}$ (从 $1$ 到 $n$ ) ,然后去对所有的dp值产生贡献

                          -

                          或者等价地,在dp中加上一维 $s$ 表示当前允许的子树大小

                          +

                          枚举一个 \(\mathtt{sz}\) (从 \(1\)\(n\) ) ,然后去对所有的dp值产生贡献

                          +

                          或者等价地,在dp中加上一维 \(s\) +表示当前允许的子树大小

                          同时我们还要记录当前根节点的度数

                          -

                          设 $f_{s,i,j}$ 表示 $i$ -烷基,根节点度数为 $j$ ,所有子树的大小不超过 $s$ 的方案数

                          -

                          考虑转移。首先,如果所有子树的大小都小于 $s$ ,则答案为 $f_{s-1,i,j}$

                          -

                          否则设存在 $k$ 个子树的大小为 $s$ ,显然 $1\le k \le \min\left\{j,\left\lfloor{\frac{i-1}{s}}\right\rfloor\right\}$ ,

                          -

                          把这些子树去掉以后,剩下 $i-sk$ 个点,根节点的度数为 $j-k$ ,且剩余子树大小均不超过 $s-1$

                          -

                          对应到状态就是 $f_{s-1,i-sk,j-k}$

                          -

                          然后就是考虑这 $k$ 个子树的分布,或者说把这 $k$ 个子树插进去

                          -

                          设大小为 $s$ 的子树有 $c(c=\sum f_{s-1,s,j})$ 个(显然这 $c$ 个本质不同)

                          -

                          那么这就是一个可重组合,方案数为 $\dbinom{c+k-1}{k}$ ( $c$ 个选 $k$ 个,可重复)

                          -

                          则完整转移方程为

                          -

                          时间复杂度 $O(n^2m \log m)$ ,其中 $m$ 为度数的限制,即 $4$ 。

                          -

                          然后这个第一维显然可以滚动数组。故空间复杂度为 $O(nm)$

                          +

                          \(f_{s,i,j}\) 表示 \(i\) -烷基,根节点度数为 \(j\) ,所有子树的大小不超过 \(s\) 的方案数

                          +

                          考虑转移。首先,如果所有子树的大小都小于 \(s\) ,则答案为 \(f_{s-1,i,j}\)

                          +

                          否则设存在 \(k\) 个子树的大小为 +\(s\) ,显然 \(1\le k \le +\min\left\{j,\left\lfloor{\frac{i-1}{s}}\right\rfloor\right\}\) +,

                          +

                          把这些子树去掉以后,剩下 \(i-sk\) +个点,根节点的度数为 \(j-k\) +,且剩余子树大小均不超过 \(s-1\)

                          +

                          对应到状态就是 \(f_{s-1,i-sk,j-k}\)

                          +

                          然后就是考虑这 \(k\) +个子树的分布,或者说把这 \(k\) +个子树插进去

                          +

                          设大小为 \(s\) 的子树有 \(c(c=\sum f_{s-1,s,j})\) 个(显然这 \(c\) 个本质不同)

                          +

                          那么这就是一个可重组合,方案数为 \(\dbinom{c+k-1}{k}\)\(c\) 个选 \(k\) 个,可重复)

                          +

                          则完整转移方程为 \[ +f_{s,i,j} = \sum_{0 \le k \le j \land sk < i} f_{s-1,i-sk,j-k} +\times\dbinom{c+k-1}{k} +\] 时间复杂度 \(O(n^2m \log m)\) +,其中 \(m\) 为度数的限制,即 \(4\)

                          +

                          然后这个第一维显然可以滚动数组。故空间复杂度为 \(O(nm)\)

                          代码:

                          #include <iostream>
                           #include <string>
                          @@ -593,7 +634,8 @@ 

                          return 0; }

                          参考文献

                          -

                          [1] https://yhx-12243.github.io/OI-transit/records/loj6185%3Bloj6269.html

                          +

                          [1] https://yhx-12243.github.io/OI-transit/records/loj6185%3Bloj6269.html

                          @@ -975,7 +1017,7 @@

                           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/2022/08/22/luo-gu-p3825-noi2017-you-xi-ti-jie/index.html b/2022/08/22/luo-gu-p3825-noi2017-you-xi-ti-jie/index.html index f05301964f..2205abe477 100644 --- a/2022/08/22/luo-gu-p3825-noi2017-you-xi-ti-jie/index.html +++ b/2022/08/22/luo-gu-p3825-noi2017-you-xi-ti-jie/index.html @@ -480,36 +480,91 @@

                          洛谷P3825 [NOI2017] 游戏 题
                          -

                          洛谷P3825 [NOI2017] 游戏 题解

                          题目链接:P3825 [NOI2017] 游戏

                          +

                          洛谷P3825 [NOI2017] 游戏 题解

                          +

                          题目链接:P3825 +[NOI2017] 游戏

                          题意

                          -

                          小 L 计划进行 $n$ 场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。

                          -

                          小 L 的赛车有三辆,分别用大写字母 $A$、$B$、$C$ 表示。地图一共有四种,分别用小写字母 $x$、$a$、$b$、$c$ 表示。

                          -

                          其中,赛车 $A$ 不适合在地图 $a$ 上使用,赛车 $B$ 不适合在地图 $b$ 上使用,赛车 $C$ 不适合在地图 $c$ 上使用,而地图 $x$ 则适合所有赛车参加。

                          -

                          适合所有赛车参加的地图并不多见,最多只会有 $d$ 张。

                          -

                          $n$ 场游戏的地图可以用一个小写字母组成的字符串描述。例如:$S=\texttt{xaabxcbc}$ 表示小L计划进行 $8$ 场游戏,其中第 $1$ 场和第 $5$ 场的地图类型是 $x$,适合所有赛车,第 $2$ 场和第 $3$ 场的地图是 $a$,不适合赛车 $A$,第 $4$ 场和第 $7$ 场的地图是 $b$,不适合赛车 $B$,第 $6$ 场和第 $8$ 场的地图是 $c$,不适合赛车 $C$。

                          -

                          小 L 对游戏有一些特殊的要求,这些要求可以用四元组 $ (i, h_i, j, h_j) $ 来描述,表示若在第 $i$ 场使用型号为 $h_i$ 的车子,则第 $j$ 场游戏要使用型号为 $h_j$ 的车子。

                          -

                          你能帮小 L 选择每场游戏使用的赛车吗?如果有多种方案,输出任意一种方案。

                          +

                          小 L 计划进行 \(n\) +场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。

                          +

                          小 L 的赛车有三辆,分别用大写字母 \(A\)\(B\)\(C\) +表示。地图一共有四种,分别用小写字母 \(x\)\(a\)\(b\)\(c\) +表示。

                          +

                          其中,赛车 \(A\) 不适合在地图 \(a\) 上使用,赛车 \(B\) 不适合在地图 \(b\) 上使用,赛车 \(C\) 不适合在地图 \(c\) 上使用,而地图 \(x\) 则适合所有赛车参加。

                          +

                          适合所有赛车参加的地图并不多见,最多只会有 \(d\) 张。

                          +

                          \(n\) +场游戏的地图可以用一个小写字母组成的字符串描述。例如:\(S=\texttt{xaabxcbc}\) 表示小L计划进行 \(8\) 场游戏,其中第 \(1\) 场和第 \(5\) 场的地图类型是 \(x\),适合所有赛车,第 \(2\) 场和第 \(3\) 场的地图是 \(a\),不适合赛车 \(A\),第 \(4\) 场和第 \(7\) 场的地图是 \(b\),不适合赛车 \(B\),第 \(6\) 场和第 \(8\) 场的地图是 \(c\),不适合赛车 \(C\)

                          +

                          小 L 对游戏有一些特殊的要求,这些要求可以用四元组 $ (i, h_i, j, h_j) +$ 来描述,表示若在第 \(i\) 场使用型号为 +\(h_i\) 的车子,则第 \(j\) 场游戏要使用型号为 \(h_j\) 的车子。

                          +

                          你能帮小 L +选择每场游戏使用的赛车吗?如果有多种方案,输出任意一种方案。

                          如果无解,输出 -1

                          -

                          什么恶心卡常题,心态要崩了。

                          +

                          什么恶心卡常题,心态要崩了。

                          这篇题解在uoj是过不了Extra Test 12的,但是洛谷可以过hack数据。

                          -

                          显然 $\mathtt{2-SAT}$ ,但是 x 的出现意味这道题还要加一个 $\mathtt{3-SAT}$

                          -

                          众所周知, $\mathtt{k-SAT}$ 在 $k>2$ 时被证明是NP完全的

                          +

                          显然 \(\mathtt{2-SAT}\) ,但是 +x 的出现意味这道题还要加一个 \(\mathtt{3-SAT}\)

                          +

                          众所周知, \(\mathtt{k-SAT}\) 在 +\(k>2\) 时被证明是NP完全的

                          因此只能暴力枚举所有 x 的选法

                          -

                          这里的暴力可以优化为枚举每个 x 地图为 a 地图还是 b 地图

                          +

                          这里的暴力可以优化为枚举每个 x 地图为 a +地图还是 b 地图

                          这样为什么对呢?

                          -

                          因为 a 适合 BCb 适合 AC ,然后我们就把填 ABC 的三种情况都试过了

                          +

                          因为 a 适合 BCb 适合 +AC ,然后我们就把填 ABC 的三种情况都试过了

                          考虑没有 x 的部分怎么做

                            -
                          • 如果 $i$ 的位置适合 $h_i$ 赛车,但 $j$ 不适合 $h_j$ 赛车

                            -

                            则把 $u_i$ 向 $u_{i}^{\prime}$ 连有向边,表示如果选了 $h_i$ 一定无解

                            -
                          • -
                          • 如果 $i$ 的位置适合 $h_i$ 赛车,$j$ 的位置适合 $h_j$ 赛车

                            -

                            那就把 $u_i$ 向 $v_i$ 连有向边,$v_{i}^{\prime}$ 向 $u_{i}^{\prime}$ 连有向边,显然。

                            -
                          • +
                          • 如果 \(i\) 的位置适合 \(h_i\) 赛车,但 \(j\) 不适合 \(h_j\) 赛车

                            +

                            则把 \(u_i\)\(u_{i}^{\prime}\) 连有向边,表示如果选了 +\(h_i\) 一定无解

                          • +
                          • 如果 \(i\) 的位置适合 \(h_i\) 赛车,\(j\) 的位置适合 \(h_j\) 赛车

                          -

                          时间复杂度 $O(2^d \times (n+m))$

                          +

                          那就把 \(u_i\)\(v_i\) 连有向边,\(v_{i}^{\prime}\)\(u_{i}^{\prime}\) 连有向边,显然。

                          +

                          时间复杂度 \(O(2^d \times +(n+m))\)

                          代码:

                          #include <iostream>
                           #include <string>
                          @@ -686,6 +741,7 @@ 

                          // printf("times used %.3lf s",(1.0*clock()-st)/CLOCKS_PER_SEC); return 0; }

                          +

                          @@ -1066,7 +1122,7 @@

                           站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/404.html b/404.html index 880365ce52..e447fc1835 100644 --- a/404.html +++ b/404.html @@ -364,8 +364,8 @@

                          404

                          这里什么都没有诶!QAQ

                          所以你是怎么点进来的呀~ qwq

                          建议使用右上角↗的搜索功能来查找文章哦! awa

                          - - +

                          @@ -591,7 +591,7 @@

                          404


                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/about/index.html b/about/index.html index a288949837..cb95b66c2a 100644 --- a/about/index.html +++ b/about/index.html @@ -742,14 +742,14 @@ -
                          - DP + + 图论 - - 图论 + + DP @@ -778,14 +778,14 @@ - - 瞎搞 + + 01分数规划 - - 01分数规划 + + 瞎搞 @@ -796,14 +796,14 @@ - - 摆烂 + + 计算几何 - - 计算几何 + + 摆烂 @@ -862,7 +862,7 @@
                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/archives/2021/02/index.html b/archives/2021/02/index.html index e69de29bb2..49ca324f48 100644 --- a/archives/2021/02/index.html +++ b/archives/2021/02/index.html @@ -0,0 +1,1124 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021/2 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 02 +
                          + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 整数的划分 动态规划 + + 整数的划分 动态规划 +
                          +
                          +
                          +
                          + + 整数的划分 动态规划 +题目描述 +每个非负整数都可以被拆分,比如说 +2 = 2 +2 = 1+1 +3 = 3 +3 = 2+1 +3 = 1+1+1 +输入格式 一个非负整数\(n(0 +\leq n + +
                          +
                          + + 2021-02-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu20.04 桌面图标显示异常及解决方法 + + ubuntu20.04 桌面图标显示异常及解决方法 +
                          +
                          +
                          +
                          + + ubuntu20.04 +桌面图标显示异常及解决方法 +前言 +更新至ubuntu20.04后,出现了一些以前没有的问题 +桌面上有些图标不显示 + +一、具体表现 +例如有一次我在做备忘录时 +我习惯地打开终端 +cd 桌面 +gedit 账号.txt +桌面 + +
                          +
                          + + 2021-02-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + CF652B z-sort 题解 + + CF652B z-sort 题解 +
                          +
                          +
                          +
                          + + CF652B z-sort 题解 +题目链接:CF652B +z-sort + +题意:一种叫Z排序的方法,奇数位递增,偶数位递减,给定数组请用此方法排序 + +题意要求奇数位递增,偶数位递减 +那每次只要输出最小值和最大值就可以了 +这里给出了优先队列的 + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + CF708A Letters Cyclic Shift 题解 + + CF708A Letters Cyclic Shift 题解 +
                          +
                          +
                          +
                          + + CF708A Letters Cyclic Shift +题解 +题目链接:CF708A +Letters Cyclic Shift + +题意:一次变换指将字母变为它前面一个字母,例如a变成z,b变成a,给定字符串,找出一个非空子串进行变换使得改变 + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + CF676A Nicholas and Permutation 题解 + + CF676A Nicholas and Permutation 题解 +
                          +
                          +
                          +
                          + + CF676A Nicholas and +Permutation 题解 +题目链接:CF676A +Nicholas and Permutation + +题意:给定数组,可以让两个数的位置交换,让最大值和最小值的位置的差的绝对值最大 + +先用\(c\ + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + AT1899 画像処理高橋君 题解 + + AT1899 画像処理高橋君 题解 +
                          +
                          +
                          +
                          + + AT1899 画像処理高橋君 题解 +题目链接:AT1899 +画像処理高橋君 +原题是日文的,我就不翻译了( + +题意:给出压缩后的图像,求压缩前的图像 +压缩是指对于各个像素,在其周围8个方向的像素中,只要有一个黑色像素,其像素就会变黑的处理 + + + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/03/index.html b/archives/2021/03/index.html index e69de29bb2..6902f91ebe 100644 --- a/archives/2021/03/index.html +++ b/archives/2021/03/index.html @@ -0,0 +1,995 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021/3 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 03 +
                          + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 追逐鼠标指针的小猫~Oneko + + ubuntu 追逐鼠标指针的小猫~Oneko +
                          +
                          +
                          +
                          + + ubuntu +追逐鼠标指针的小猫~Oneko +前言 +最近发现了一个有趣的软件 Oneko +可以让一只小猫追着鼠标指针跑 +是不是很有趣? +一、下载Oneko +打开终端 sudo apt install oneko 安装即可 +然后它就会在应用程 + +
                          +
                          + + 2021-03-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 显示键盘按键 + + ubuntu 显示键盘按键 +
                          +
                          +
                          +
                          + + ubuntu 显示键盘按键 +前言 +在看一些主播玩游戏时,他们屏幕上会有一个虚拟键盘,可以显示按键 +当时觉得很神奇,就想着给ubuntu也弄一个 +庆幸的是,ubuntu的确有这种软件 + +一、KeyMon简介 +这个软件叫key-mon,全称Ke + +
                          +
                          + + 2021-03-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + linux及windows对拍程序 C++ + + linux及windows对拍程序 C++ +
                          +
                          +
                          +
                          + + linux及windows对拍程序 C++ +前言 +OI赛制的比赛中,选手不能看到自己的成绩,那么如何保证代码正确呢? +1.水品高 秒切 2.暴力+对拍 尝试调正解 +本文给出了linux和windows的对拍程序 + +一、什么是对拍? +在比赛中 + +
                          +
                          + + 2021-03-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 内存占用过高导致卡死 解决办法 + + ubuntu 内存占用过高导致卡死 解决办法 +
                          +
                          +
                          +
                          + + ubuntu +内存占用过高导致卡死 解决办法 +一、具体表现 +例如下图 + + +在这里插入图片描述 + +注:图示版本为ubuntu18.04,现在我用的是ubuntu20.04 + +二、原因 +查阅到了一些资料 + +在Linux中经常发现空闲内存很少,似 + +
                          +
                          + + 2021-03-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/05/index.html b/archives/2021/05/index.html index e69de29bb2..87fa14c75f 100644 --- a/archives/2021/05/index.html +++ b/archives/2021/05/index.html @@ -0,0 +1,863 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021/5 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 05 +
                          + + +
                          + 15 +
                          + +
                          + +
                          + + + + + +
                          + 07 +
                          + +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/07/index.html b/archives/2021/07/index.html index e69de29bb2..3836df61c7 100644 --- a/archives/2021/07/index.html +++ b/archives/2021/07/index.html @@ -0,0 +1,1053 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021/7 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 07 +
                          + + +
                          + 22 +
                          +
                          +
                          +
                          + +
                          + + 导数的基本公式推导 + + 导数的基本公式推导 +
                          +
                          +
                          +
                          + + 导数的基本公式推导 +主要推导了人教版A版数学选择性必修二上直接给出的基本的导数公式 +本文写于作者初三暑假,更新于高一暑假 +可能含有很多不足,如果您方便的话可以联系我修改 awa +大概率会在高二暑假再更新一次吧 + +一、导数的四则运算法则 +设 + +
                          +
                          + + 2021-07-22 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 蒙提霍尔问题及其推广 + + 蒙提霍尔问题及其推广 +
                          +
                          +
                          +
                          + + 蒙提霍尔问题及其推广 +前言 +蒙提霍尔问题在《人教版A版数学选择性必修三》上作为阅读与思考的材料出现 +本文会提供一种简单的解法并推广这个著名的问题 + +蒙提霍尔问题 +一、背景 +三门问题(Monty Hall +problem)亦称为蒙提霍尔问题、 + +
                          +
                          + + 2021-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 全源最短路 Johnson算法 + + 全源最短路 Johnson算法 +
                          +
                          +
                          +
                          + + 全源最短路 Johnson算法 +本文写于较早时期,之前对Dijkstra的理解不是很透彻 +已经修改了部分显然错误的内容,有空会再仔细检查的 +模板题:P5905 【模板】Johnson +全源最短路 +题意简述:给定一个包含 \(n\) 个结点和 + +
                          +
                          + + 2021-07-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 无序数组交换任意两个元素 最少交换次数 + + 无序数组交换任意两个元素 最少交换次数 +
                          +
                          +
                          +
                          + + 无序数组交换任意两个元素 +最少交换次数 +题目描述 +给定长度为 \(n\) +的无序数组,将数组中的元素按从小到大的顺序排列,每次可以交换任意两个元素,最少要交换几次? + +解题方法 +解法一(较繁琐) +我们可以遍历一遍原数组,如果当前元素不在正确 + +
                          +
                          + + 2021-07-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈补码的原理和正确性 + + 浅谈补码的原理和正确性 +
                          +
                          +
                          +
                          + + 浅谈补码的原理和正确性 +前言 +upd 2022.2.14 +我就该早点看《计算机组成原理》,补码的定义就是 + +一个 \(n\) 位二进制数 \(N\) 的二进制补码定义为 \(2^n-N\) + +不过本文还是严谨证明了它的正确性,以下为原文 +补 + +
                          +
                          + + 2021-07-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/08/index.html b/archives/2021/08/index.html index e69de29bb2..4af1a33a2c 100644 --- a/archives/2021/08/index.html +++ b/archives/2021/08/index.html @@ -0,0 +1,1524 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021/8 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 08 +
                          + + +
                          + 31 +
                          + +
                          + +
                          + + + + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + Vijos1659 河蟹王国 题解 + + Vijos1659 河蟹王国 题解 +
                          +
                          +
                          +
                          + + Vijos1659 河蟹王国 题解 +题目链接:Vijos1659 +河蟹王国 + +题意:维护一个数据结构,支持区间最大值查询、区间加操作 + +一看就线段树水题 +我们在建树时将最大值搞好查询就好了 +那么区间加怎么办? +显然区间加操作会将影响到的最大 + +
                          +
                          + + 2021-08-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + UVA1121 Subsequence 题解 + + UVA1121 Subsequence 题解 +
                          +
                          +
                          +
                          + + UVA1121 Subsequence 题解 +题目链接:UVA1121 +Subsequence + +题意:给定数组,找最短连续子序列使其和大于 \(S\) ,多组数据 + +解法一 +对于区间 \([l,r]\) ,若 \(\sum_{i=l}^{ + +
                          +
                          + + 2021-08-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1006 [NOIP2008 提高组] 传纸条 + + 洛谷P1006 [NOIP2008 提高组] 传纸条 +
                          +
                          +
                          +
                          + + 洛谷P1006 [NOIP2008 提高组] +传纸条 +题目链接:P1006 +[NOIP2008 提高组] 传纸条 + +题意:网格图, \((1,1)\) 到 \((n,m)\) 找两条不重合的路径,最大价值 +注:原题是 \((m,n)\) +, + +
                          +
                          + + 2021-08-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 数论题瞎做[1] + + 数论题瞎做[1] +
                          +
                          +
                          +
                          + + 数论题瞎做[1] +某个学MO的朋友给我看的题 +这题似乎是1994年国家数学集训队选拔考试D1T1 +怪不得我做了好久( +upd.20220513 害,他半途而废退役了 + +题面: +求四个所有的由四个自然数 \(a,b,c,d\) +组成的数组,使 + +
                          +
                          + + 2021-08-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4878 [USACO05DEC]Layout G 题解 + + 洛谷P4878 [USACO05DEC]Layout G 题解 +
                          +
                          +
                          +
                          + + 洛谷P4878 [USACO05DEC]Layout +G 题解 +题目链接:P4878 +[USACO05DEC]Layout G + +题意:按编号排了 \(n\) +只奶牛,有的奶牛间必须相距小于等于一个距离,有的奶牛间必须相距大于等于一个距离, + +
                          +
                          + + 2021-08-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + POJ3723 Conscription 题解 + + POJ3723 Conscription 题解 +
                          +
                          +
                          +
                          + + POJ3723 Conscription 题解 +题目链接:POJ3723 +Conscription + +题意:要招 \(n\) +个女的, \(m\) 个男的,原价 \(10000\),如果招了关系亲密的(男女)人可以降价,求最小花费 + +首先, + +
                          +
                          + + 2021-08-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          1 / 2
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/08/page/2/index.html b/archives/2021/08/page/2/index.html index e69de29bb2..6e2f46ce0b 100644 --- a/archives/2021/08/page/2/index.html +++ b/archives/2021/08/page/2/index.html @@ -0,0 +1,1210 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021/8 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 08 +
                          + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1047 [NOIP2005 普及组] 校门外的树 题解 + + 洛谷P1047 [NOIP2005 普及组] 校门外的树 题解 +
                          +
                          +
                          +
                          + + 洛谷P1047 [NOIP2005 +普及组] 校门外的树 题解 +前言 +如何把一道入门题写成省选题?(手动滑稽) +本题解是我在练习分块时突发奇想写的,真就把入门题写成省选题的感觉( +才发现原来这些简单题这么有趣( + +题目链接: P1047 +[ + +
                          +
                          + + 2021-08-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + 浅谈珂朵莉树(ODT) + + 浅谈珂朵莉树(ODT) +
                          +
                          +
                          +
                          + + 浅谈珂朵莉树(ODT) +前言 +珂学家狂喜( + +一、珂朵莉树来源 +珂朵莉树,原名老司机树(Old Driver +Tree),在某场CF比赛中提出 +因为题目背景是《末日时在做什么?有没有空?可以来拯救吗?》中的珂朵莉,所以就叫珂朵莉树了 + +二、 + +
                          +
                          + + 2021-08-15 + + + + + q779 + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 13 +
                          +
                          +
                          +
                          + +
                          + + + 裴蜀定理及其证明 + + 裴蜀定理及其证明 +
                          +
                          +
                          +
                          + + 裴蜀定理及其证明 +前言 +原来裴蜀是法国数学家QwQ + +一、裴蜀定理 +对于 \(x,y\) +的二元一次不定方程 \(ax+by=c\) ,其有解的充要条件为 \(\gcd(a,b)\mid c\) +1.充分性证明 +充分性:若 \(\gcd(a + +
                          +
                          + + 2021-08-13 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 强制卸载vmware player + + ubuntu 强制卸载vmware player +
                          +
                          +
                          +
                          + + ubuntu 强制卸载vmware player +不知道什么时候下了vmware-player,然后怎么都删不掉 +解决方法 +打开终端,输入以下指令 locate vmware-player 然后会出现一大堆vmware +player的文件 + + +
                          +
                          + + 2021-08-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈舞蹈链(DLX) + + 浅谈舞蹈链(DLX) +
                          +
                          +
                          +
                          + + 浅谈舞蹈链(DLX) +前言 +舞蹈链的名字真好玩... + +一、舞蹈链概述 +舞蹈链 (Dancing links),也叫 DLX +,是由 Donald Knuth +提出的数据结构,目的是快速实现他提出的X算法。X算法是一种递归算法,时间复杂度不 + +
                          +
                          + + 2021-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          2 / 2
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/09/index.html b/archives/2021/09/index.html index e69de29bb2..2cac8ba210 100644 --- a/archives/2021/09/index.html +++ b/archives/2021/09/index.html @@ -0,0 +1,1130 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021/9 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 09 +
                          + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 均值不等式及其证明 + + 均值不等式及其证明 +
                          +
                          +
                          +
                          + + 均值不等式及其证明 +前言 +还有很多证明方法,等我学了再写 qwq + +均值不等式 +引理1:若 \(a\ge 0,b\ge +0\) ,则 \((a+b)^n\ge +a^n+na^{n-1}b,n\in \Z_+\) +直接二项式展开,证明略 + +命 + +
                          +
                          + + 2021-09-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈分块 区间众数 + + 浅谈分块 区间众数 +
                          +
                          +
                          +
                          + + 浅谈分块 区间众数 +前言 +分块大法好( +本文直接讲例题了 qwq + +P4168 [Violet]蒲公英 +题目链接:P4168 +[Violet]蒲公英 + +题意: 找到区间内编号最小的众数,强制在线 + +解法一 +直接分块 +设块长为 \(len\) + +
                          +
                          + + 2021-09-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          + +
                          + +
                          + + + + + +
                          + 05 +
                          + +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 逆序对的三种求法 + + 逆序对的三种求法 +
                          +
                          +
                          +
                          + + 逆序对的三种求法 +一、什么是逆序对? +对于给定的一段正整数序列,逆序对就是序列中 \(a_i>a_j\) 且 \(i<j\) 的有序对 + +二、怎么求逆序对 +1.归并排序解法 +归并排序可以很好的解决逆序对问题 +我们只需要计算跨越分 + +
                          +
                          + + 2021-09-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈树状数组 区间修改&区间查询 + + 浅谈树状数组 区间修改&区间查询 +
                          +
                          +
                          +
                          + + 浅谈树状数组 +区间修改&区间查询 +一、区间修改,单点查询 +首先我们可以先来想一下,树状数组的区间修改,单点查询怎么弄 +我们可以维护一个关于原数组的差分数组 +很容易知道 \(a_i=\sum\limits_{j=1}^{i}b_j\) + +
                          +
                          + + 2021-09-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/10/index.html b/archives/2021/10/index.html index e69de29bb2..0221ed2198 100644 --- a/archives/2021/10/index.html +++ b/archives/2021/10/index.html @@ -0,0 +1,864 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021/10 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 10 +
                          + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1486 [NOI2004] 郁闷的出纳员 题解 + + 洛谷P1486 [NOI2004] 郁闷的出纳员 题解 +
                          +
                          +
                          +
                          + + 洛谷P1486 [NOI2004] +郁闷的出纳员 题解 +题目链接:P1486 +[NOI2004] 郁闷的出纳员 + +题意:维护一个数据结构,支持 + +插入一个大小为 \(k\) +的值,小于下界时不插入 +所有元素加上 \(k\) +所有元素减去 \ + +
                          +
                          + + 2021-10-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          + +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/11/index.html b/archives/2021/11/index.html index e69de29bb2..8ff709d453 100644 --- a/archives/2021/11/index.html +++ b/archives/2021/11/index.html @@ -0,0 +1,1114 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021/11 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 11 +
                          + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2633 Count on a tree 题解 + + 洛谷P2633 Count on a tree 题解 +
                          +
                          +
                          +
                          + + 洛谷P2633 Count on a tree +题解 +题目链接:P2633 +Count on a tree + +题意:给定一棵树和 \(u,v,k\) ,求 \(u,v\) 结点间的第 \(k\) 小点权 + +本来以为是个树链剖分+主席树,结果 + +
                          +
                          + + 2021-11-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + windows Matlab R2020b 安装教程 + + windows Matlab R2020b 安装教程 +
                          +
                          +
                          +
                          + + windows Matlab R2020b +安装教程 +前言 +本文其实是我自己在安装过程中截图的,现在搞定了整理出来给大家参考一下 +使用的是matlab R2020b +如果本文有什么错误或对本文有什么问题,欢迎留言 qwq +要是图挂了可以直接 + +
                          +
                          + + 2021-11-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          + +
                          + +
                          + + + + + +
                          + 13 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu WPS字体缺失 解决方法 + + ubuntu WPS字体缺失 解决方法 +
                          +
                          +
                          +
                          + + ubuntu WPS字体缺失 解决方法 +前言 +请保证您还有一台windows + +一、在windows复制字体 +首先在windows下载好WPS,然后找到字体,复制 + + +在这里插入图片描述 + +二、复制到ubuntu +我的ubuntu上装了WP + +
                          +
                          + + 2021-11-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/12/index.html b/archives/2021/12/index.html index e69de29bb2..609a5a5c8f 100644 --- a/archives/2021/12/index.html +++ b/archives/2021/12/index.html @@ -0,0 +1,990 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021/12 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 12 +
                          + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 释放缓存脚本 + + ubuntu 释放缓存脚本 +
                          +
                          +
                          +
                          + + ubuntu 释放缓存脚本 +注意:缓存\(\ne\)内存 +内存占用过高解决办法 +首先在主目录新建一个free.sh,输入 +echo 3 > /proc/sys/vm/drop_caches +保存后打开终端 +sudo su +crontab - + +
                          +
                          + + 2021-12-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 04 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4315 月下“毛景树” 题解 + + 洛谷P4315 月下“毛景树” 题解 +
                          +
                          +
                          +
                          + + 洛谷P4315 月下“毛景树” 题解 +题目链接:P4315 +月下“毛景树” + +题意:请维护一个数据结构,支持 + +改第 \(k\) 条边的边权 +结点 \(u\) 到 \(v\) 路径上的边权改为 \(k\) +结点 \(u\) 到 \(v\) + +
                          +
                          + + 2021-12-04 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈拉格朗日插值法 + + 浅谈拉格朗日插值法 +
                          +
                          +
                          +
                          + + 浅谈拉格朗日插值法 +模板题链接:P4781 +【模板】拉格朗日插值 + +题意:给定 \(n\) +个点 \(P_i(x_i,y_i)\) ,将过该 \(n\) 个点的最多 \(n-1\) 次多项式记为 \(f(x)\) +给出 \(k\) ,求 \ + +
                          +
                          + + 2021-12-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/index.html b/archives/2021/index.html index e69de29bb2..f56d277711 100644 --- a/archives/2021/index.html +++ b/archives/2021/index.html @@ -0,0 +1,1530 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 12 +
                          + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 释放缓存脚本 + + ubuntu 释放缓存脚本 +
                          +
                          +
                          +
                          + + ubuntu 释放缓存脚本 +注意:缓存\(\ne\)内存 +内存占用过高解决办法 +首先在主目录新建一个free.sh,输入 +echo 3 > /proc/sys/vm/drop_caches +保存后打开终端 +sudo su +crontab - + +
                          +
                          + + 2021-12-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 04 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4315 月下“毛景树” 题解 + + 洛谷P4315 月下“毛景树” 题解 +
                          +
                          +
                          +
                          + + 洛谷P4315 月下“毛景树” 题解 +题目链接:P4315 +月下“毛景树” + +题意:请维护一个数据结构,支持 + +改第 \(k\) 条边的边权 +结点 \(u\) 到 \(v\) 路径上的边权改为 \(k\) +结点 \(u\) 到 \(v\) + +
                          +
                          + + 2021-12-04 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈拉格朗日插值法 + + 浅谈拉格朗日插值法 +
                          +
                          +
                          +
                          + + 浅谈拉格朗日插值法 +模板题链接:P4781 +【模板】拉格朗日插值 + +题意:给定 \(n\) +个点 \(P_i(x_i,y_i)\) ,将过该 \(n\) 个点的最多 \(n-1\) 次多项式记为 \(f(x)\) +给出 \(k\) ,求 \ + +
                          +
                          + + 2021-12-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2633 Count on a tree 题解 + + 洛谷P2633 Count on a tree 题解 +
                          +
                          +
                          +
                          + + 洛谷P2633 Count on a tree +题解 +题目链接:P2633 +Count on a tree + +题意:给定一棵树和 \(u,v,k\) ,求 \(u,v\) 结点间的第 \(k\) 小点权 + +本来以为是个树链剖分+主席树,结果 + +
                          +
                          + + 2021-11-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + windows Matlab R2020b 安装教程 + + windows Matlab R2020b 安装教程 +
                          +
                          +
                          +
                          + + windows Matlab R2020b +安装教程 +前言 +本文其实是我自己在安装过程中截图的,现在搞定了整理出来给大家参考一下 +使用的是matlab R2020b +如果本文有什么错误或对本文有什么问题,欢迎留言 qwq +要是图挂了可以直接 + +
                          +
                          + + 2021-11-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          + +
                          + +
                          + + + + + +
                          + 13 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu WPS字体缺失 解决方法 + + ubuntu WPS字体缺失 解决方法 +
                          +
                          +
                          +
                          + + ubuntu WPS字体缺失 解决方法 +前言 +请保证您还有一台windows + +一、在windows复制字体 +首先在windows下载好WPS,然后找到字体,复制 + + +在这里插入图片描述 + +二、复制到ubuntu +我的ubuntu上装了WP + +
                          +
                          + + 2021-11-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1486 [NOI2004] 郁闷的出纳员 题解 + + 洛谷P1486 [NOI2004] 郁闷的出纳员 题解 +
                          +
                          +
                          +
                          + + 洛谷P1486 [NOI2004] +郁闷的出纳员 题解 +题目链接:P1486 +[NOI2004] 郁闷的出纳员 + +题意:维护一个数据结构,支持 + +插入一个大小为 \(k\) +的值,小于下界时不插入 +所有元素加上 \(k\) +所有元素减去 \ + +
                          +
                          + + 2021-10-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          1 / 5
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/page/2/index.html b/archives/2021/page/2/index.html index e69de29bb2..ce52d61851 100644 --- a/archives/2021/page/2/index.html +++ b/archives/2021/page/2/index.html @@ -0,0 +1,1540 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 09 +
                          + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 均值不等式及其证明 + + 均值不等式及其证明 +
                          +
                          +
                          +
                          + + 均值不等式及其证明 +前言 +还有很多证明方法,等我学了再写 qwq + +均值不等式 +引理1:若 \(a\ge 0,b\ge +0\) ,则 \((a+b)^n\ge +a^n+na^{n-1}b,n\in \Z_+\) +直接二项式展开,证明略 + +命 + +
                          +
                          + + 2021-09-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈分块 区间众数 + + 浅谈分块 区间众数 +
                          +
                          +
                          +
                          + + 浅谈分块 区间众数 +前言 +分块大法好( +本文直接讲例题了 qwq + +P4168 [Violet]蒲公英 +题目链接:P4168 +[Violet]蒲公英 + +题意: 找到区间内编号最小的众数,强制在线 + +解法一 +直接分块 +设块长为 \(len\) + +
                          +
                          + + 2021-09-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          + +
                          + +
                          + + + + + +
                          + 05 +
                          + +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 逆序对的三种求法 + + 逆序对的三种求法 +
                          +
                          +
                          +
                          + + 逆序对的三种求法 +一、什么是逆序对? +对于给定的一段正整数序列,逆序对就是序列中 \(a_i>a_j\) 且 \(i<j\) 的有序对 + +二、怎么求逆序对 +1.归并排序解法 +归并排序可以很好的解决逆序对问题 +我们只需要计算跨越分 + +
                          +
                          + + 2021-09-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈树状数组 区间修改&区间查询 + + 浅谈树状数组 区间修改&区间查询 +
                          +
                          +
                          +
                          + + 浅谈树状数组 +区间修改&区间查询 +一、区间修改,单点查询 +首先我们可以先来想一下,树状数组的区间修改,单点查询怎么弄 +我们可以维护一个关于原数组的差分数组 +很容易知道 \(a_i=\sum\limits_{j=1}^{i}b_j\) + +
                          +
                          + + 2021-09-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          + + +
                          + 31 +
                          + +
                          + +
                          + + + + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + Vijos1659 河蟹王国 题解 + + Vijos1659 河蟹王国 题解 +
                          +
                          +
                          +
                          + + Vijos1659 河蟹王国 题解 +题目链接:Vijos1659 +河蟹王国 + +题意:维护一个数据结构,支持区间最大值查询、区间加操作 + +一看就线段树水题 +我们在建树时将最大值搞好查询就好了 +那么区间加怎么办? +显然区间加操作会将影响到的最大 + +
                          +
                          + + 2021-08-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + UVA1121 Subsequence 题解 + + UVA1121 Subsequence 题解 +
                          +
                          +
                          +
                          + + UVA1121 Subsequence 题解 +题目链接:UVA1121 +Subsequence + +题意:给定数组,找最短连续子序列使其和大于 \(S\) ,多组数据 + +解法一 +对于区间 \([l,r]\) ,若 \(\sum_{i=l}^{ + +
                          +
                          + + 2021-08-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          2 / 5
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/page/3/index.html b/archives/2021/page/3/index.html index e69de29bb2..f9efaba85b 100644 --- a/archives/2021/page/3/index.html +++ b/archives/2021/page/3/index.html @@ -0,0 +1,1530 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 08 +
                          + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1006 [NOIP2008 提高组] 传纸条 + + 洛谷P1006 [NOIP2008 提高组] 传纸条 +
                          +
                          +
                          +
                          + + 洛谷P1006 [NOIP2008 提高组] +传纸条 +题目链接:P1006 +[NOIP2008 提高组] 传纸条 + +题意:网格图, \((1,1)\) 到 \((n,m)\) 找两条不重合的路径,最大价值 +注:原题是 \((m,n)\) +, + +
                          +
                          + + 2021-08-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 数论题瞎做[1] + + 数论题瞎做[1] +
                          +
                          +
                          +
                          + + 数论题瞎做[1] +某个学MO的朋友给我看的题 +这题似乎是1994年国家数学集训队选拔考试D1T1 +怪不得我做了好久( +upd.20220513 害,他半途而废退役了 + +题面: +求四个所有的由四个自然数 \(a,b,c,d\) +组成的数组,使 + +
                          +
                          + + 2021-08-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4878 [USACO05DEC]Layout G 题解 + + 洛谷P4878 [USACO05DEC]Layout G 题解 +
                          +
                          +
                          +
                          + + 洛谷P4878 [USACO05DEC]Layout +G 题解 +题目链接:P4878 +[USACO05DEC]Layout G + +题意:按编号排了 \(n\) +只奶牛,有的奶牛间必须相距小于等于一个距离,有的奶牛间必须相距大于等于一个距离, + +
                          +
                          + + 2021-08-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + POJ3723 Conscription 题解 + + POJ3723 Conscription 题解 +
                          +
                          +
                          +
                          + + POJ3723 Conscription 题解 +题目链接:POJ3723 +Conscription + +题意:要招 \(n\) +个女的, \(m\) 个男的,原价 \(10000\),如果招了关系亲密的(男女)人可以降价,求最小花费 + +首先, + +
                          +
                          + + 2021-08-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1047 [NOIP2005 普及组] 校门外的树 题解 + + 洛谷P1047 [NOIP2005 普及组] 校门外的树 题解 +
                          +
                          +
                          +
                          + + 洛谷P1047 [NOIP2005 +普及组] 校门外的树 题解 +前言 +如何把一道入门题写成省选题?(手动滑稽) +本题解是我在练习分块时突发奇想写的,真就把入门题写成省选题的感觉( +才发现原来这些简单题这么有趣( + +题目链接: P1047 +[ + +
                          +
                          + + 2021-08-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + 浅谈珂朵莉树(ODT) + + 浅谈珂朵莉树(ODT) +
                          +
                          +
                          +
                          + + 浅谈珂朵莉树(ODT) +前言 +珂学家狂喜( + +一、珂朵莉树来源 +珂朵莉树,原名老司机树(Old Driver +Tree),在某场CF比赛中提出 +因为题目背景是《末日时在做什么?有没有空?可以来拯救吗?》中的珂朵莉,所以就叫珂朵莉树了 + +二、 + +
                          +
                          + + 2021-08-15 + + + + + q779 + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 13 +
                          +
                          +
                          +
                          + +
                          + + + 裴蜀定理及其证明 + + 裴蜀定理及其证明 +
                          +
                          +
                          +
                          + + 裴蜀定理及其证明 +前言 +原来裴蜀是法国数学家QwQ + +一、裴蜀定理 +对于 \(x,y\) +的二元一次不定方程 \(ax+by=c\) ,其有解的充要条件为 \(\gcd(a,b)\mid c\) +1.充分性证明 +充分性:若 \(\gcd(a + +
                          +
                          + + 2021-08-13 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 强制卸载vmware player + + ubuntu 强制卸载vmware player +
                          +
                          +
                          +
                          + + ubuntu 强制卸载vmware player +不知道什么时候下了vmware-player,然后怎么都删不掉 +解决方法 +打开终端,输入以下指令 locate vmware-player 然后会出现一大堆vmware +player的文件 + + +
                          +
                          + + 2021-08-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          3 / 5
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/page/4/index.html b/archives/2021/page/4/index.html index e69de29bb2..15bbf6a906 100644 --- a/archives/2021/page/4/index.html +++ b/archives/2021/page/4/index.html @@ -0,0 +1,1544 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 08 +
                          + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈舞蹈链(DLX) + + 浅谈舞蹈链(DLX) +
                          +
                          +
                          +
                          + + 浅谈舞蹈链(DLX) +前言 +舞蹈链的名字真好玩... + +一、舞蹈链概述 +舞蹈链 (Dancing links),也叫 DLX +,是由 Donald Knuth +提出的数据结构,目的是快速实现他提出的X算法。X算法是一种递归算法,时间复杂度不 + +
                          +
                          + + 2021-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          + + +
                          + 22 +
                          +
                          +
                          +
                          + +
                          + + 导数的基本公式推导 + + 导数的基本公式推导 +
                          +
                          +
                          +
                          + + 导数的基本公式推导 +主要推导了人教版A版数学选择性必修二上直接给出的基本的导数公式 +本文写于作者初三暑假,更新于高一暑假 +可能含有很多不足,如果您方便的话可以联系我修改 awa +大概率会在高二暑假再更新一次吧 + +一、导数的四则运算法则 +设 + +
                          +
                          + + 2021-07-22 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 蒙提霍尔问题及其推广 + + 蒙提霍尔问题及其推广 +
                          +
                          +
                          +
                          + + 蒙提霍尔问题及其推广 +前言 +蒙提霍尔问题在《人教版A版数学选择性必修三》上作为阅读与思考的材料出现 +本文会提供一种简单的解法并推广这个著名的问题 + +蒙提霍尔问题 +一、背景 +三门问题(Monty Hall +problem)亦称为蒙提霍尔问题、 + +
                          +
                          + + 2021-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 全源最短路 Johnson算法 + + 全源最短路 Johnson算法 +
                          +
                          +
                          +
                          + + 全源最短路 Johnson算法 +本文写于较早时期,之前对Dijkstra的理解不是很透彻 +已经修改了部分显然错误的内容,有空会再仔细检查的 +模板题:P5905 【模板】Johnson +全源最短路 +题意简述:给定一个包含 \(n\) 个结点和 + +
                          +
                          + + 2021-07-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 无序数组交换任意两个元素 最少交换次数 + + 无序数组交换任意两个元素 最少交换次数 +
                          +
                          +
                          +
                          + + 无序数组交换任意两个元素 +最少交换次数 +题目描述 +给定长度为 \(n\) +的无序数组,将数组中的元素按从小到大的顺序排列,每次可以交换任意两个元素,最少要交换几次? + +解题方法 +解法一(较繁琐) +我们可以遍历一遍原数组,如果当前元素不在正确 + +
                          +
                          + + 2021-07-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈补码的原理和正确性 + + 浅谈补码的原理和正确性 +
                          +
                          +
                          +
                          + + 浅谈补码的原理和正确性 +前言 +upd 2022.2.14 +我就该早点看《计算机组成原理》,补码的定义就是 + +一个 \(n\) 位二进制数 \(N\) 的二进制补码定义为 \(2^n-N\) + +不过本文还是严谨证明了它的正确性,以下为原文 +补 + +
                          +
                          + + 2021-07-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          + + +
                          + 15 +
                          + +
                          + +
                          + + + + + +
                          + 07 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 追逐鼠标指针的小猫~Oneko + + ubuntu 追逐鼠标指针的小猫~Oneko +
                          +
                          +
                          +
                          + + ubuntu +追逐鼠标指针的小猫~Oneko +前言 +最近发现了一个有趣的软件 Oneko +可以让一只小猫追着鼠标指针跑 +是不是很有趣? +一、下载Oneko +打开终端 sudo apt install oneko 安装即可 +然后它就会在应用程 + +
                          +
                          + + 2021-03-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 显示键盘按键 + + ubuntu 显示键盘按键 +
                          +
                          +
                          +
                          + + ubuntu 显示键盘按键 +前言 +在看一些主播玩游戏时,他们屏幕上会有一个虚拟键盘,可以显示按键 +当时觉得很神奇,就想着给ubuntu也弄一个 +庆幸的是,ubuntu的确有这种软件 + +一、KeyMon简介 +这个软件叫key-mon,全称Ke + +
                          +
                          + + 2021-03-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + linux及windows对拍程序 C++ + + linux及windows对拍程序 C++ +
                          +
                          +
                          +
                          + + linux及windows对拍程序 C++ +前言 +OI赛制的比赛中,选手不能看到自己的成绩,那么如何保证代码正确呢? +1.水品高 秒切 2.暴力+对拍 尝试调正解 +本文给出了linux和windows的对拍程序 + +一、什么是对拍? +在比赛中 + +
                          +
                          + + 2021-03-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 内存占用过高导致卡死 解决办法 + + ubuntu 内存占用过高导致卡死 解决办法 +
                          +
                          +
                          +
                          + + ubuntu +内存占用过高导致卡死 解决办法 +一、具体表现 +例如下图 + + +在这里插入图片描述 + +注:图示版本为ubuntu18.04,现在我用的是ubuntu20.04 + +二、原因 +查阅到了一些资料 + +在Linux中经常发现空闲内存很少,似 + +
                          +
                          + + 2021-03-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          4 / 5
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2021/page/5/index.html b/archives/2021/page/5/index.html index e69de29bb2..349e5f2595 100644 --- a/archives/2021/page/5/index.html +++ b/archives/2021/page/5/index.html @@ -0,0 +1,1148 @@ + + + + + + + + + + + + + + + + + + + 归档: 2021 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 02 +
                          + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 整数的划分 动态规划 + + 整数的划分 动态规划 +
                          +
                          +
                          +
                          + + 整数的划分 动态规划 +题目描述 +每个非负整数都可以被拆分,比如说 +2 = 2 +2 = 1+1 +3 = 3 +3 = 2+1 +3 = 1+1+1 +输入格式 一个非负整数\(n(0 +\leq n + +
                          +
                          + + 2021-02-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu20.04 桌面图标显示异常及解决方法 + + ubuntu20.04 桌面图标显示异常及解决方法 +
                          +
                          +
                          +
                          + + ubuntu20.04 +桌面图标显示异常及解决方法 +前言 +更新至ubuntu20.04后,出现了一些以前没有的问题 +桌面上有些图标不显示 + +一、具体表现 +例如有一次我在做备忘录时 +我习惯地打开终端 +cd 桌面 +gedit 账号.txt +桌面 + +
                          +
                          + + 2021-02-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + CF652B z-sort 题解 + + CF652B z-sort 题解 +
                          +
                          +
                          +
                          + + CF652B z-sort 题解 +题目链接:CF652B +z-sort + +题意:一种叫Z排序的方法,奇数位递增,偶数位递减,给定数组请用此方法排序 + +题意要求奇数位递增,偶数位递减 +那每次只要输出最小值和最大值就可以了 +这里给出了优先队列的 + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + CF708A Letters Cyclic Shift 题解 + + CF708A Letters Cyclic Shift 题解 +
                          +
                          +
                          +
                          + + CF708A Letters Cyclic Shift +题解 +题目链接:CF708A +Letters Cyclic Shift + +题意:一次变换指将字母变为它前面一个字母,例如a变成z,b变成a,给定字符串,找出一个非空子串进行变换使得改变 + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + CF676A Nicholas and Permutation 题解 + + CF676A Nicholas and Permutation 题解 +
                          +
                          +
                          +
                          + + CF676A Nicholas and +Permutation 题解 +题目链接:CF676A +Nicholas and Permutation + +题意:给定数组,可以让两个数的位置交换,让最大值和最小值的位置的差的绝对值最大 + +先用\(c\ + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + AT1899 画像処理高橋君 题解 + + AT1899 画像処理高橋君 题解 +
                          +
                          +
                          +
                          + + AT1899 画像処理高橋君 题解 +题目链接:AT1899 +画像処理高橋君 +原题是日文的,我就不翻译了( + +题意:给出压缩后的图像,求压缩前的图像 +压缩是指对于各个像素,在其周围8个方向的像素中,只要有一个黑色像素,其像素就会变黑的处理 + + + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          5 / 5
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/01/index.html b/archives/2022/01/index.html index e69de29bb2..d1ce259441 100644 --- a/archives/2022/01/index.html +++ b/archives/2022/01/index.html @@ -0,0 +1,1325 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/1 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 01 +
                          + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + RMB找零问题 + + RMB找零问题 +
                          +
                          +
                          +
                          + + RMB找零问题 +来自某次研究性学习的作业 +前言 +可以先考虑这样的问题 + +给定 \(n\) +种足量多的纸币,每种面额为 \(a_i\) 元 +\((0<a[i]≤10000,a[i]\in +\Z,1≤i≤n≤100)\) +给出需要找零的金 + +
                          +
                          + + 2022-01-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/02/index.html b/archives/2022/02/index.html index e69de29bb2..e422b0868b 100644 --- a/archives/2022/02/index.html +++ b/archives/2022/02/index.html @@ -0,0 +1,1525 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/2 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 02 +
                          + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4234 最小差值生成树 题解 + + 洛谷P4234 最小差值生成树 题解 +
                          +
                          +
                          +
                          + + 洛谷P4234 最小差值生成树 +题解 +题目链接:P4234 +最小差值生成树 + +题意:给定一个点标号从 \(1\) 到 \(n\) 的、有 \(m\) +条边的无向图,求边权最大值与最小值的差值最小的生成树,图可能存在自环 + +这个题不太好利用k + +
                          +
                          + + 2022-02-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + LCT求解最小生成树 + + LCT求解最小生成树 +
                          +
                          +
                          +
                          + + LCT求解最小生成树 +前言 +最小生成树模板: P3366 +【模板】最小生成树 +朴素的kruskal为主流最小生成树算法 +而LCT(link cut tree)也是可以维护最小生成树的 +由于LCT动态维护最小生成树,加上常数较大 +在实际测试中 + +
                          +
                          + + 2022-02-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2147 [SDOI2008] 洞穴勘测 题解 + + 洛谷P2147 [SDOI2008] 洞穴勘测 题解 +
                          +
                          +
                          +
                          + + 洛谷P2147 [SDOI2008] 洞穴勘测 +题解 +题目链接:P2147 +[SDOI2008] 洞穴勘测 + +题意:给定若干个点,动态连接(无向边),询问连通性 + +由于它有删边的操作,因此用并查集并不可行 +于是想到LCT(? +由于LCT有 + +
                          +
                          + + 2022-02-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 24 +
                          + +
                          + +
                          + + + + + +
                          + 13 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈快速乘 + + 浅谈快速乘 +
                          +
                          +
                          +
                          + + 浅谈快速乘 +前言 +想必大家都听说过快速幂 +那快速乘是个什么东西呢? +考虑取模操作a*b%p,1^10 ≤ a,b,p ≤ 2^10 +可以发现在 long long情况下,我们直接取模会溢出 +那么怎么办呢? +题目链接:https://www. + +
                          +
                          + + 2022-02-13 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          +
                          +
                          +
                          + +
                          + + + AT4284 & 洛谷 P1969 P3078 P5019 题解 + + AT4284 & 洛谷 P1969 P3078 P5019 题解 +
                          +
                          +
                          +
                          + + AT4284 & 洛谷 P1969 +P3078 P5019 题解 +题目链接:AT4284 P1969 P3078 P5019 + +题意:若干次区间减一,使所有数相等,求最小次数 + +这几道题就是一个std编出来的吧 \(😅\) +对于相 + +
                          +
                          + + 2022-02-07 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1234 小A的口头禅 题解 + + 洛谷P1234 小A的口头禅 题解 +
                          +
                          +
                          +
                          + + 洛谷P1234 小A的口头禅 题解 +题目链接:P1234 +小A的口头禅 + +给出了一个矩形,让你求出里面有几个hehe(方向无所谓,斜着不算) + +数据范围很良心,嗯~ +所以暴力枚举即可 +值得注意的是 \(\tt{eheh}\) +这种也算 +顺便 + +
                          +
                          + + 2022-02-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2804 神秘数字 题解 + + 洛谷P2804 神秘数字 题解 +
                          +
                          +
                          +
                          + + 洛谷P2804 神秘数字 题解 +题目链接:P2804 +神秘数字 + +题意:询问有多少段连续区间的平均值大于 \(m\) + +可以发现将每个数都减去 \(m\) +后任意和大于 \(0\) +的连续区间都满足题意 +区间和可以用前缀和优化,记为 \(s + +
                          +
                          + + 2022-02-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5764 [CQOI2005]新年好 题解 + + 洛谷P5764 [CQOI2005]新年好 题解 +
                          +
                          +
                          +
                          + + 洛谷P5764 [CQOI2005]新年好 +题解 +题目链接:P5764 +[CQOI2005]新年好 + +题意:从 \(1\) +号结点出发,要访问其他 \(5\) +个结点,顺序随意,访问一个结点后不用返回 + +注意到 \(5! = O(1)\) + + +
                          +
                          + + 2022-02-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1462 通往奥格瑞玛的道路 题解 + + 洛谷P1462 通往奥格瑞玛的道路 题解 +
                          +
                          +
                          +
                          + + 洛谷P1462 通往奥格瑞玛的道路 +题解 +题目链接:P1462 +通往奥格瑞玛的道路 + +题意:在艾泽拉斯,有 \(n\) 个城市。编号为 \(1,2,3,\ldots,n\) 。 +城市之间有 \(m\) +条双向的公路,连接着两个城市,从某个城 + +
                          +
                          + + 2022-02-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/03/index.html b/archives/2022/03/index.html index e69de29bb2..143102fbf5 100644 --- a/archives/2022/03/index.html +++ b/archives/2022/03/index.html @@ -0,0 +1,1254 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/3 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 03 +
                          + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + Dijkstra及其复杂度证明 + + Dijkstra及其复杂度证明 +
                          +
                          +
                          +
                          + + Dijkstra及其复杂度证明 +前言 +本文主要围绕易混淆的复杂度分析进行讨论 + +Dijkstra +其实这个不叫迪杰斯特拉,这个叫/ˈdɛɪkstra/ qwq +一、小概念 +先放几个简单概念 +无向图:图中所有的边都是两端可达的,也就是可以从任 + +
                          +
                          + + 2022-03-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 最小树形图 Tarjan的DMST算法 + + 最小树形图 Tarjan的DMST算法 +
                          +
                          +
                          +
                          + + 最小树形图 Tarjan的DMST算法 +前言 +网上怎么都是朱刘算法啊? +那我来写一篇 Tarjan 的 DMST 算法吧 +qwq +注:本文的DMST采用左偏树+并查集实现 +时间复杂度为 \(O(E+V\log E)\) +如果采用斐波那契堆则 + +
                          +
                          + + 2022-03-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5826 【模板】子序列自动机 + + 洛谷P5826 【模板】子序列自动机 +
                          +
                          +
                          +
                          + + 洛谷P5826 【模板】子序列自动机 +题目链接:P5826 +【模板】子序列自动机 + +题意:给定一个主序列,每次给出一个序列,判断是否为其子序列 + +我们可以把每个字符的出现位置用vector维护 +然后对于每个询问,直接二分离当前位置最近的那个 + +
                          +
                          + + 2022-03-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3919 【模板】可持久化线段树 1(可持久化数组) 题解 + + 洛谷P3919 【模板】可持久化线段树 1(可持久化数组) 题解 +
                          +
                          +
                          +
                          + + 洛谷P3919 +【模板】可持久化线段树 1(可持久化数组) 题解 +题目链接:P3919 +【模板】可持久化线段树 1(可持久化数组) + +题意:如题,你需要维护这样的一个长度为 NN +的数组,支持如下几种操作 + +在某个历史版本上修改某一个位置上 + +
                          +
                          + + 2022-03-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1129 [ZJOI2007] 矩阵游戏 题解 + + 洛谷P1129 [ZJOI2007] 矩阵游戏 题解 +
                          +
                          +
                          +
                          + + 洛谷P1129 [ZJOI2007] 矩阵游戏 +题解 +题目链接:P1129 +[ZJOI2007] 矩阵游戏 + +题意:给定一张有黑白棋子的正方形棋盘,问存不存在解法使得经过若干次交换行或列的操作后,左上角至右下角的对角线上所有的点放着黑色棋子 + +
                          +
                          + + 2022-03-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + kd-tree(KDT) 时间复杂度证明 + + kd-tree(KDT) 时间复杂度证明 +
                          +
                          +
                          +
                          + + kd-tree(KDT) 时间复杂度证明 +kd-tree 是一种可以高效处理 \(k\) +维空间的数据结构 +在算法竞赛类的题目中一般有 \(k=2\) +还有个比较有趣的结论,当 \(k=1\) +时其实它就是一棵线段树 +下文中的 \(n\) + + +
                          +
                          + + 2022-03-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + + + + + +
                          + 01 +
                          +
                          +
                          +
                          + +
                          + + + CF1200E Compress Words 题解 + + CF1200E Compress Words 题解 +
                          +
                          +
                          +
                          + + CF1200E Compress Words 题解 +题目链接:CF1200E +Compress Words + +题意:给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀 +与 待插入串的前缀的最大匹配串 + +解法一 KMP +这个解法常数比较 + +
                          +
                          + + 2022-03-01 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/04/index.html b/archives/2022/04/index.html index e69de29bb2..2ce7559315 100644 --- a/archives/2022/04/index.html +++ b/archives/2022/04/index.html @@ -0,0 +1,1395 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/4 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 04 +
                          + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4159 [SCOI2009] 迷路 题解 + + 洛谷P4159 [SCOI2009] 迷路 题解 +
                          +
                          +
                          +
                          + + 洛谷P4159 [SCOI2009] 迷路 +题解 +题目链接:P4159 +[SCOI2009] 迷路 + +题意:该有向图有 \(n\) 个节点,节点从 \(1\) 至 \(n\) 编号,windy 从节点 \(1\) 出发,他必须恰好在 \(t + +
                          +
                          + + 2022-04-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3829 [SHOI2012]信用卡凸包 题解 + + 洛谷P3829 [SHOI2012]信用卡凸包 题解 +
                          +
                          +
                          +
                          + + 洛谷P3829 +[SHOI2012]信用卡凸包 题解 +题目链接:P3829 +[SHOI2012]信用卡凸包 + +题意: 给定若干个“信用卡”,求其“凸包”周长 + + +这个题其实看上去很不可做,其实很简单 +注意到(搬了一张图,来自link,不过这 + +
                          +
                          + + 2022-04-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 23 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 19 +
                          + +
                          + +
                          + + + + + +
                          + 17 +
                          + +
                          + +
                          + +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/05/index.html b/archives/2022/05/index.html index e69de29bb2..f85246bb9b 100644 --- a/archives/2022/05/index.html +++ b/archives/2022/05/index.html @@ -0,0 +1,1566 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/5 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 31 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1273 有线电视网 题解 + + 洛谷P1273 有线电视网 题解 +
                          +
                          +
                          +
                          + + 洛谷P1273 有线电视网 题解 +题目链接:P1273 +有线电视网 + +题意: +某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节 + +
                          +
                          + + 2022-05-31 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2279 [HNOI2003]消防局的设立 题解 + + 洛谷P2279 [HNOI2003]消防局的设立 题解 +
                          +
                          +
                          +
                          + + 洛谷P2279 +[HNOI2003]消防局的设立 题解 +题目链接:P2279 +[HNOI2003]消防局的设立 + +题意: +2020 年,人类在火星上建立了一个庞大的基地群,总共有 \(n\) 个基地。起初为了节约材料,人类只修建了 +\(n- + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2015 二叉苹果树 题解 + + 洛谷P2015 二叉苹果树 题解 +
                          +
                          +
                          +
                          + + 洛谷P2015 二叉苹果树 题解 +题目链接:P2015 +二叉苹果树 + +题意: +有一棵苹果树,如果树枝有分叉,一定是分二叉(就是说没有只有一个儿子的结点) +这棵树共有 \(N\) +个结点(叶子点或者树枝分叉点),编号为 \(1 +\sim N\ + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3174 [HAOI2009] 毛毛虫 题解 + + 洛谷P3174 [HAOI2009] 毛毛虫 题解 +
                          +
                          +
                          +
                          + + 洛谷P3174 [HAOI2009] 毛毛虫 +题解 +题目链接:P3174 +[HAOI2009] 毛毛虫 + +题意: +对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大。例如下图左边的树(图 +\( + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3621 [APIO2007] 风铃 题解 + + 洛谷P3621 [APIO2007] 风铃 题解 +
                          +
                          +
                          +
                          + + 洛谷P3621 [APIO2007] 风铃 +题解 +题目链接:P3621 +[APIO2007] 风铃 + +题意: +你准备给弟弟 Ike 买一件礼物,但是,Ike +挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西。 +你准备给 Ike +买 + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2458 [SDOI2006]保安站岗 题解 + + 洛谷P2458 [SDOI2006]保安站岗 题解 +
                          +
                          +
                          +
                          + + 洛谷P2458 [SDOI2006]保安站岗 +题解 +题目链接:P2458 +[SDOI2006]保安站岗 + +题意: +五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序 + +
                          +
                          + + 2022-05-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2016 战略游戏 题解 + + 洛谷P2016 战略游戏 题解 +
                          +
                          +
                          +
                          + + 洛谷P2016 战略游戏 题解 +题目链接:P2016 +战略游戏 + +题意: Bob +要建立一个古城堡,城堡中的路形成一棵无根树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。 +注意,某个士兵在一个结点上时,与该结点相连 + +
                          +
                          + + 2022-05-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1122 最大子树和 题解 + + 洛谷P1122 最大子树和 题解 +
                          +
                          +
                          +
                          + + 洛谷P1122 最大子树和 题解 +题目链接:P1122 +最大子树和 + +题意: +小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花 + +
                          +
                          + + 2022-05-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3205 [HNOI2010]合唱队 题解 + + 洛谷P3205 [HNOI2010]合唱队 题解 +
                          +
                          +
                          +
                          + + 洛谷P3205 [HNOI2010]合唱队 +题解 +题目链接:P3205 +[HNOI2010]合唱队 + +题意: +为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A +需要将合唱队的人根据他们的身高排出一个队形。假定合唱队 + +
                          +
                          + + 2022-05-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          1 / 7
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/05/page/2/index.html b/archives/2022/05/page/2/index.html index e69de29bb2..bc6ecef5e9 100644 --- a/archives/2022/05/page/2/index.html +++ b/archives/2022/05/page/2/index.html @@ -0,0 +1,1524 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/5 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + UVA1629 切蛋糕 Cake slicing 题解 + + UVA1629 切蛋糕 Cake slicing 题解 +
                          +
                          +
                          +
                          + + UVA1629 切蛋糕 Cake slicing +题解 +题目链接:UVA1629 +切蛋糕 Cake slicing + +题意:这个翻译够烂的,直接看pdf +翻译:有一个n行m列(1<=n,m<=20)的网络蛋糕上有k个樱桃。每次可 + +
                          +
                          + + 2022-05-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4170 [CQOI2007]涂色 题解 + + 洛谷P4170 [CQOI2007]涂色 题解 +
                          +
                          +
                          +
                          + + 洛谷P4170 [CQOI2007]涂色 题解 +题目链接:P4170 +[CQOI2007]涂色 + +题意: +假设你有一条长度为 \(5\) +的木板,初始时没有涂过任何颜色。你希望把它的 \(5\) +个单位长度分别涂上红、绿、蓝、绿、红色,用一 + +
                          +
                          + + 2022-05-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + AT3913 XOR Tree 题解 + + AT3913 XOR Tree 题解 +
                          +
                          +
                          +
                          + + AT3913 XOR Tree 题解 +题目链接:AT3913 +XOR Tree + +题意:给你一棵有 \(N\) 个节点的树,节点编号从 \(0\) 到 \(N-1\) , 树边编号从 \(1\) 到 \(N-1\) 。第 \(i\) 条边连 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF149D Coloring Brackets 题解 + + CF149D Coloring Brackets 题解 +
                          +
                          +
                          +
                          + + CF149D Coloring Brackets +题解 +题目链接:CF149D +Coloring Brackets + +题意:给出一个配对的括号序列(如 “\(\texttt{(())()}\)”、“\(\texttt{()}\)” 等,“\ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF19B Checkout Assistant 题解 + + CF19B Checkout Assistant 题解 +
                          +
                          +
                          +
                          + + CF19B Checkout Assistant +题解 +题目链接:CF19B +Checkout Assistant + +题意:Bob 来到一家现购自运商店,将 \(n\) +件商品放入了他的手推车,然后到收银台付款。每件商品由它的价格 \(c_ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF346B Lucky Common Subsequence 题解 + + CF346B Lucky Common Subsequence 题解 +
                          +
                          +
                          +
                          + + CF346B Lucky Common +Subsequence 题解 +题目链接:CF346B +Lucky Common Subsequence + +题意:通过删除一个字符串中的某些元素而不改变其余元素的顺序,可以派生出该字符串的一个子序列。 + + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF374C Inna and Dima 题解 + + CF374C Inna and Dima 题解 +
                          +
                          +
                          +
                          + + CF374C Inna and Dima 题解 +题目链接:CF374C +Inna and Dima + +题意: +Inna和Dima在商店买了一张 n * m +的桌子,桌子的每一个单元格上都有一个字符,字符集为("D","I","M","A") + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          2 / 7
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/05/page/3/index.html b/archives/2022/05/page/3/index.html index e69de29bb2..20b9f1d0f6 100644 --- a/archives/2022/05/page/3/index.html +++ b/archives/2022/05/page/3/index.html @@ -0,0 +1,1535 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/5 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF41D Pawn 题解 + + CF41D Pawn 题解 +
                          +
                          +
                          +
                          + + CF41D Pawn 题解 +题目链接:CF41D +Pawn + +题意:国际象棋棋盘最底行站了一个兵。 +它只有两种行动方式: 向上左或向上右走。 +它可以选择从最低行哪个节点开始他的旅程。 +每个格子上有0-9颗豌豆,而士兵想移动到最上一行并且积累 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF526B Om Nom and Dark Park 题解 + + CF526B Om Nom and Dark Park 题解 +
                          +
                          +
                          +
                          + + CF526B Om Nom and Dark Park +题解 +题目链接:CF526B Om +Nom and Dark Park + +题意:给定 \(2^{n+1}-1\) +个结点的满二叉树,求保证根到每一个叶子节点的路径权值和相等的情况下,增 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1108 低价购买 题解 + + 洛谷P1108 低价购买 题解 +
                          +
                          +
                          +
                          + + 洛谷P1108 低价购买 题解 +题目链接:P1108 +低价购买 + +题意:“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 +
                          +
                          +
                          +
                          + + 洛谷P1156 垃圾陷阱 +题解&浅谈刷表法与填表法 +填表法 +:就是一般的动态规划,当前点的状态,可以直接用状态方程,根据之前点的状态推导出来。 +刷表法:由当前点的状态,更新其他点的状态。需要注意:只用当每个状态所依赖的状态对它的影响 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1171 售货员的难题 题解 + + 洛谷P1171 售货员的难题 题解 +
                          +
                          +
                          +
                          + + 洛谷P1171 售货员的难题 题解 +题目链接:P1171 +售货员的难题 + +题意:TSP问题。 +某乡有\(n\)个村庄(\(1<n \le +20\)),有一个售货员,他要到各个村庄去售货,各村庄之间的路程\(s(0<s<10 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1282 多米诺骨牌 题解 + + 洛谷P1282 多米诺骨牌 题解 +
                          +
                          +
                          +
                          + + 洛谷P1282 多米诺骨牌 题解 +题目链接:P1282 +多米诺骨牌 + +题意: +多米诺骨牌由上下 \(2\) +个方块组成,每个方块中有 \(1\sim6\) +个点。现有排成行的上方块中点数之和记为 \(S_1\),下方块中点数之和记为 \(S + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1284 三角形牧场 题解 + + 洛谷P1284 三角形牧场 题解 +
                          +
                          +
                          +
                          + + 洛谷P1284 三角形牧场 题解 +题目链接:P1284 +三角形牧场 + +题意:和所有人一样,奶牛喜欢变化。它们正在设想新造型的牧场。奶牛建筑师 +Hei 想建造围有漂亮白色栅栏的三角形牧场。她拥有 \(n\) 块木板,每块的长度 \(l_i\) + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1772 [ZJOI2006]物流运输 题解 + + 洛谷P1772 [ZJOI2006]物流运输 题解 +
                          +
                          +
                          +
                          + + 洛谷P1772 [ZJOI2006]物流运输 +题解 +题目链接:P1772 +[ZJOI2006]物流运输 + +题意:物流公司要把一批货物从码头 A 运到码头 +B。由于货物量比较大,需要 \(n\) +天才能运完。货物运输过程中一般要转停好几个码头 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1858 多人背包 题解 + + 洛谷P1858 多人背包 题解 +
                          +
                          +
                          +
                          + + 洛谷P1858 多人背包 题解 +题目链接:P1858 +多人背包 + +题意:求01背包前k优解的价值和 + +建议先去读一读《背包九讲》再来看 +注意到朴素的 \(01\) 背包是这样转移的 +\[ +dp[j]=\max(dp[j],dp[j-w[i] + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1944 最长括号匹配 题解 + + 洛谷P1944 最长括号匹配 题解 +
                          +
                          +
                          +
                          + + 洛谷P1944 最长括号匹配 题解 + +题意:对一个由(,),[,]括号组成的字符串,求出其中最长的括号匹配子串。具体来说,满足如下条件的字符串成为括号匹配的字符串: +1.(),[]是括号匹配的字符串。 +2.若A是括号匹配的串,则(A),[A + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2158 [SDOI2008] 仪仗队 题解 + + 洛谷P2158 [SDOI2008] 仪仗队 题解 +
                          +
                          +
                          +
                          + + 洛谷P2158 [SDOI2008] 仪仗队 +题解 +题目链接:P2158 +[SDOI2008] 仪仗队 + +题意:作为体育委员,C +君负责这次运动会仪仗队的训练。仪仗队是由学生组成的 \(N \times N\) +的方阵,为了保证队伍在行进中 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          3 / 7
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/05/page/4/index.html b/archives/2022/05/page/4/index.html index e69de29bb2..70ece5de65 100644 --- a/archives/2022/05/page/4/index.html +++ b/archives/2022/05/page/4/index.html @@ -0,0 +1,1535 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/5 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2170 选学霸 题解 + + 洛谷P2170 选学霸 题解 +
                          +
                          +
                          +
                          + + 洛谷P2170 选学霸 题解 +题目链接:P2170 +选学霸 + +题意:老师想从 \(n\) 名学生中选 \(m\) 人当学霸,但有 \(k\) +人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2224 [HNOI2001]产品加工 题解 + + 洛谷P2224 [HNOI2001]产品加工 题解 +
                          +
                          +
                          +
                          + + 洛谷P2224 [HNOI2001]产品加工 +题解 +题目链接:P2224 +[HNOI2001]产品加工 + +题意: +某加工厂有 A、B +两台机器,来加工的产品可以由其中任何一台机器完成,或者两台机器共同完成。由于受到机器性能和产品特性的限制, + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2257 YY的GCD 题解 + + 洛谷P2257 YY的GCD 题解 +
                          +
                          +
                          +
                          + + 洛谷P2257 YY的GCD 题解 + +题意: +多组询问。 +给定 \(N, M\),求 \(1 \leq x \leq N\),\(1 \leq y \leq M\) 且 \(\gcd(x,y)\) 为质数的 \((x,y)\) 有多少对。 + + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2568 GCD 题解 + + 洛谷P2568 GCD 题解 +
                          +
                          +
                          +
                          + + 洛谷P2568 GCD 题解 +题目链接:P2568 +GCD + +题意: +给定正整数 \(n\),求 \(1\le x,y\le n\) 且 \(\gcd(x,y)\)为素数的数对 \((x,y)\) 有多少对。 +\(1 \le n\le 10 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          4 / 7
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/05/page/5/index.html b/archives/2022/05/page/5/index.html index e69de29bb2..7c47df1022 100644 --- a/archives/2022/05/page/5/index.html +++ b/archives/2022/05/page/5/index.html @@ -0,0 +1,1548 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/5 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3188 [HNOI2007]梦幻岛宝珠 题解 + + 洛谷P3188 [HNOI2007]梦幻岛宝珠 题解 +
                          +
                          +
                          +
                          + + 洛谷P3188 +[HNOI2007]梦幻岛宝珠 题解 +题目链接:P3188 +[HNOI2007]梦幻岛宝珠 + +题意: +给你 \(n\) +颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过 +\(W\),且总价值最大 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3297 [SDOI2013] 逃考 题解 + + 洛谷P3297 [SDOI2013] 逃考 题解 +
                          +
                          +
                          +
                          + + 洛谷P3297 [SDOI2013] 逃考 +题解 +题目链接:P3297 +[SDOI2013] 逃考 + +题意:髙考又来了,对于不认真读书的小杨来讲,真不是个好消息。为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶、外 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3336 [ZJOI2013]话旧 题解 + + 洛谷P3336 [ZJOI2013]话旧 题解 +
                          +
                          +
                          +
                          + + 洛谷P3336 [ZJOI2013]话旧 题解 +题目链接:P3336 +[ZJOI2013]话旧 + +题意:小林跟着银河队选手去了一趟宇宙比赛,耳濡目染,变得学术起来。回来后,他发现世界大变样了。比丘兽究级进化,成了凤凰兽;金先生因为发了一篇 + + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3985 不开心的金明 题解 + + 洛谷P3985 不开心的金明 题解 +
                          +
                          +
                          +
                          + + 洛谷P3985 不开心的金明 题解 +题目链接:P3985 +不开心的金明 + +题意: +金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4141 消失之物 题解 + + 洛谷P4141 消失之物 题解 +
                          +
                          +
                          +
                          + + 洛谷P4141 消失之物 题解 +题目链接:P4141 +消失之物 + +题意:ftiasch 有 \(n\) 个物品, 体积分别是 \(w_1,w_2,\dots,w_n\) 。由于她的疏忽,第 +\(i\) 个物品丢失了。 +“要使用剩下的 \(n + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          5 / 7
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/05/page/6/index.html b/archives/2022/05/page/6/index.html index e69de29bb2..7e288a7e18 100644 --- a/archives/2022/05/page/6/index.html +++ b/archives/2022/05/page/6/index.html @@ -0,0 +1,1537 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/5 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4158 [SCOI2009]粉刷匠 题解 + + 洛谷P4158 [SCOI2009]粉刷匠 题解 +
                          +
                          +
                          +
                          + + 洛谷P4158 [SCOI2009]粉刷匠 +题解 +题目链接:P4158 +[SCOI2009]粉刷匠 + +题意:windy有 N 条木板需要被粉刷。 每条木板被分为 M +个格子。 每个格子要被刷成红色或蓝色。 +windy每次粉刷,只能选择一条木 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4310 绝世好题 题解 + + 洛谷P4310 绝世好题 题解 +
                          +
                          +
                          +
                          + + 洛谷P4310 绝世好题 题解 +题目链接:P4310 +绝世好题 + +题意:给定一个长度为 \(n\) 的数列 \(a_i\) ,求 \(a_i\) 的子序列 \(b_i\) 的最长长度 \(k\),满足 \(b_i +\& b_{i-1 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4342 [IOI1998]Polygon 题解 + + 洛谷P4342 [IOI1998]Polygon 题解 +
                          +
                          +
                          +
                          + + 洛谷P4342 [IOI1998]Polygon +题解 +题目链接:P4342 +[IOI1998]Polygon + +题意:多边形是一个玩家在一个有 \(n\) 个顶点的多边形上的游戏,如图所示,其中 +\(n=4\) +。每个顶点用整数标记,每个 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 + + 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 +
                          +
                          +
                          +
                          + + 洛谷P4390 [BOI2007]Mokia +摩基亚 题解 +题目链接:P4390 +[BOI2007]Mokia 摩基亚 + +题意:摩尔瓦多的移动电话公司摩基亚(Mokia)设计出了一种新的用户定位系统。和其他的定位系统一样,它能够迅速回答任何 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4395 [BOI2003]Gem 气垫车 题解 + + 洛谷P4395 [BOI2003]Gem 气垫车 题解 +
                          +
                          +
                          +
                          + + 洛谷P4395 [BOI2003]Gem 气垫车 +题解 +题目链接:P4395 +[BOI2003]Gem 气垫车 + +题意:给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数 +唯一的限制条件是相临的两个结点不能标上相同的权值,要求一 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5110 块速递推 题解 + + 洛谷P5110 块速递推 题解 +
                          +
                          +
                          +
                          + + 洛谷P5110 块速递推 题解 +题目链接:P5110 +块速递推 + +题意:给定一个数列 \(a\) 满足递推式 \[ +a_0=0,a_1=1 +\\a_n = 233a_{n-1}+666a_{n-2} +\] 求 \(a_n \bmod (10 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5322 [BJOI2019] 排兵布阵 题解 + + 洛谷P5322 [BJOI2019] 排兵布阵 题解 +
                          +
                          +
                          +
                          + + 洛谷P5322 [BJOI2019] 排兵布阵 +题解 + +题意:小 C 正在玩一款排兵布阵的游戏。在游戏中有 +\(n\) +座城堡,每局对战由两名玩家来争夺这些城堡。每名玩家有 \(m\) 名士兵,可以向第 \(i\) 座城堡派遣 \(a_i\ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5365 [SNOI2017] 英雄联盟 题解 + + 洛谷P5365 [SNOI2017] 英雄联盟 题解 +
                          +
                          +
                          +
                          + + 洛谷P5365 [SNOI2017] 英雄联盟 +题解 +题目链接:P5365 +[SNOI2017] 英雄联盟 + +题意:正在上大学的小皮球热爱英雄联盟这款游戏,而且打的很菜,被网友们戏称为「小学生」。 +现在,小皮球终于受不了网友们的嘲讽,决定变 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          6 / 7
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/05/page/7/index.html b/archives/2022/05/page/7/index.html index e69de29bb2..7247c90652 100644 --- a/archives/2022/05/page/7/index.html +++ b/archives/2022/05/page/7/index.html @@ -0,0 +1,1148 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/5 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P6216 回文匹配 题解 + + 洛谷P6216 回文匹配 题解 +
                          +
                          +
                          +
                          + + 洛谷P6216 回文匹配 题解 +题目链接:P6216 +回文匹配 + +题意:对于一对字符串 \((s_1,s_2)\),若 \(s_1\) 的长度为奇数的子串 +\((l,r)\) 满足 \((l,r)\) 是回文的,那么 \(s_1\) 的“分 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 等差数列&等比数列小结 + + 等差数列&等比数列小结 +
                          +
                          +
                          +
                          + + 等差数列&等比数列小结 +高一自学的时候瞎总结写的(好吧我现在还是高一 +2022.5.7) +感觉丢在文件夹里吃灰没啥用,就放上来了 +欢迎各位指出我的错误(我数学真的烂 \(😓\) + +等差数列 +等差数列通项公式 +\[ +a_n = a_ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 线段树空间开4倍的原因 + + 线段树空间开4倍的原因 +
                          +
                          +
                          +
                          + + 线段树空间开4倍的原因 +如果证明有错欢迎指出。 +对于长为 \(n\) +的序列,显然以其构建的线段树有 \(n\) +个叶子节点 +此时线段树的高度为 \(k=\left\lceil{\log_2 +n}\right\rceil+1\) (第一层的 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 主定理 + + 主定理 +
                          +
                          +
                          +
                          + + 主定理 +证明先不写 +将一个规模为 \(n\) +的问题,通过分治得到 \(a\) 个规模为 +\(n/b\) +的子问题,每个递归带来的额外计算为 \(f(n)\) ,则有 +\(T(n)=aT(n/b)+f(n)\) +其中 \(a,b\) 为常数 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          7 / 7
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/06/index.html b/archives/2022/06/index.html index e69de29bb2..f2729b7211 100644 --- a/archives/2022/06/index.html +++ b/archives/2022/06/index.html @@ -0,0 +1,1543 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/6 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 06 +
                          + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3426 [POI2005]SZA-Template 题解 + + 洛谷P3426 [POI2005]SZA-Template 题解 +
                          +
                          +
                          +
                          + + 洛谷P3426 +[POI2005]SZA-Template 题解 +题目链接:P3426 +[POI2005]SZA-Template + +题意:你打算在纸上印一串字母。 +为了完成这项工作,你决定刻一个印章。印章每使用一次,就会将印章上的所有字母 + +
                          +
                          + + 2022-06-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2466 [SDOI2008] Sue 的小球 题解 + + 洛谷P2466 [SDOI2008] Sue 的小球 题解 +
                          +
                          +
                          +
                          + + 洛谷P2466 [SDOI2008] Sue +的小球 题解 + 题目链接:P2466 +[SDOI2008] Sue 的小球 + +题意: +Sue 和 Sandy +最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue +有一 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1608 路径统计 题解 + + 洛谷P1608 路径统计 题解 +
                          +
                          +
                          +
                          + + 洛谷P1608 路径统计 题解 +题目链接:P1608 +路径统计 + +题意: +一句话题意:最短路计数。 +“RP 餐厅” +的员工素质就是不一般,在齐刷刷的算出同一个电话号码之后,就准备让 HZH,TZY +去送快餐了,他们将自己居住的城市画了一张地 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5440 【XR-2】奇迹 题解 + + 洛谷P5440 【XR-2】奇迹 题解 +
                          +
                          +
                          +
                          + + 洛谷P5440 【XR-2】奇迹 题解 +题目链接:P5440 +【XR-2】奇迹 + +题意: +我们称一个日期为一个八位数,第 1~4 位构成年,第 5~6 +位构成月,第 7~8 位构成日,不足位数用 0 +补足。同时,要求日期所代表的这一天真实存 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1514 [NOIP2010 提高组] 引水入城 题解 + + 洛谷P1514 [NOIP2010 提高组] 引水入城 题解 +
                          +
                          +
                          +
                          + + 洛谷P1514 [NOIP2010 +提高组] 引水入城 题解 +题目链接:P1514 +[NOIP2010 提高组] 引水入城 + +题意: +在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个\( + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1378 油滴扩展 题解 + + 洛谷P1378 油滴扩展 题解 +
                          +
                          +
                          +
                          + + 洛谷P1378 油滴扩展 题解 +题目链接:P1378 +油滴扩展 + +题意: +在一个长方形框子里,最多有 \(N\) +个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界。必须等一个油滴扩展完 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4983 忘情 题解 + + 洛谷P4983 忘情 题解 +
                          +
                          +
                          +
                          + + 洛谷P4983 忘情 题解 +题目链接:P4983 +忘情 + +题意: +“为什么要离开我!” +“因为你没玩儿转!” +“我玩儿转了!” +“那好,你现在就给我维护这么一个式子!” +“为什么要出这么毒瘤的东西。” +“为了恶心你。” +“......” +\ + +
                          +
                          + + 2022-06-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          + +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + CF125E MST Company 题解 + + CF125E MST Company 题解 +
                          +
                          +
                          +
                          + + CF125E MST Company 题解 +题目链接:CF125E +MST Company + +题意: +给你一个有 \(n\) 个节点,\(m\) +条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足节点 \(1\) 正好连了 \( + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          1 / 3
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/06/page/2/index.html b/archives/2022/06/page/2/index.html index e69de29bb2..add09622fa 100644 --- a/archives/2022/06/page/2/index.html +++ b/archives/2022/06/page/2/index.html @@ -0,0 +1,1542 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/6 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 06 +
                          + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5633 最小度限制生成树 题解 + + 洛谷P5633 最小度限制生成树 题解 +
                          +
                          +
                          +
                          + + 洛谷P5633 最小度限制生成树 +题解 +题目链接:P5633 +最小度限制生成树 + +题意: +给你一个有 \(n\) 个节点,\(m\) +条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足编号为 +\(s\) 的节点正好连了 \(k\ + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2619 [国家集训队]Tree I 题解 + + 洛谷P2619 [国家集训队]Tree I 题解 +
                          +
                          +
                          +
                          + + 洛谷P2619 [国家集训队]Tree I +题解 +题目链接:P2619 +[国家集训队]Tree I + +题意: +给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 +\(\text{need}\) 条白色边的生成树。 +题目保证 + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2607 [ZJOI2008] 骑士 题解 + + 洛谷P2607 [ZJOI2008] 骑士 题解 +
                          +
                          +
                          +
                          + + 洛谷P2607 [ZJOI2008] 骑士 +题解 +题目链接:P2607 +[ZJOI2008] 骑士 + +题意: +Z +国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。 +最近发生了一件可怕的 + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1453 城市环路 题解 + + 洛谷P1453 城市环路 题解 +
                          +
                          +
                          +
                          + + 洛谷P1453 城市环路 题解 +题目链接:P1453 +城市环路 + +题意: +整个城市可以看做一个 \(n\) +个点,\(n\) +条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有 +\(2\) +条简单路径互通。图中的其 + +
                          +
                          + + 2022-06-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1083 [NOIP2012 提高组] 借教室 题解 + + 洛谷P1083 [NOIP2012 提高组] 借教室 题解 +
                          +
                          +
                          +
                          + + 洛谷P1083 [NOIP2012 +提高组] 借教室 题解 +题目链接:P1083 +[NOIP2012 提高组] 借教室 + +题意: +在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不 + +
                          +
                          + + 2022-06-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2678 [NOIP2015 提高组] 跳石头 题解 + + 洛谷P2678 [NOIP2015 提高组] 跳石头 题解 +
                          +
                          +
                          +
                          + + 洛谷P2678 [NOIP2015 +提高组] 跳石头 题解 +题目链接:P2678 +[NOIP2015 提高组] 跳石头 + +题意: +这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起 + +
                          +
                          + + 2022-06-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3647 [APIO2014] 连珠线 题解 + + 洛谷P3647 [APIO2014] 连珠线 题解 +
                          +
                          +
                          +
                          + + 洛谷P3647 [APIO2014] 连珠线 +题解 +题目链接:P3647 +[APIO2014] 连珠线 + +题意: +在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 +\(1\) 到 + +
                          +
                          + + 2022-06-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          2 / 3
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/06/page/3/index.html b/archives/2022/06/page/3/index.html index e69de29bb2..f9e74ac0b3 100644 --- a/archives/2022/06/page/3/index.html +++ b/archives/2022/06/page/3/index.html @@ -0,0 +1,1492 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/6 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 06 +
                          + + +
                          + 08 +
                          + +
                          + +
                          + + + + + +
                          + 07 +
                          + +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-数学 + + OI模板-数学 +
                          +
                          +
                          +
                          + + OI模板-数学 +待补全 +排列的计算 +排列 \(A_n^m\) 直接算可以 \(O(n)\) +例如求解 \(A_{n-m+1}^{m} \bmod +p\) +int res=1; +for(int i=n-m+1; i>=n-2*m+2; i-- + +
                          +
                          + + 2022-06-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1272 重建道路 题解 + + 洛谷P1272 重建道路 题解 +
                          +
                          +
                          +
                          + + 洛谷P1272 重建道路 题解 +题目链接:P1272 +重建道路 + +题意: +一场可怕的地震后,人们用 \(N\) +个牲口棚(编号 \(1\sim N\))重建了农夫 +John +的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一 + +
                          +
                          + + 2022-06-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1734 最大约数和 题解 + + 洛谷P1734 最大约数和 题解 +
                          +
                          +
                          +
                          + + 洛谷P1734 最大约数和 题解 +题目链接:P1734 +最大约数和 + +题意:选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。 + +设 \(dp[i][j]\) 表示只考虑前 \(i\) 个数总和不超过 \(j\) + +
                          +
                          + + 2022-06-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          + +
                          + +
                          + + + + + +
                          + 06 +
                          + +
                          + +
                          + + + + + +
                          + 06 +
                          + +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1270 “访问”美术馆 题解 + + 洛谷P1270 “访问”美术馆 题解 +
                          +
                          +
                          +
                          + + 洛谷P1270 “访问”美术馆 题解 +题目链接:P1270 +“访问”美术馆 + +题意: +经过数月的精心准备,Peer +Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。 + +
                          +
                          + + 2022-06-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3354 [IOI2005]Riv 河流 题解 + + 洛谷P3354 [IOI2005]Riv 河流 题解 +
                          +
                          +
                          +
                          + + 洛谷P3354 [IOI2005]Riv 河流 +题解 +题目链接:P3354 +[IOI2005]Riv 河流 + +题意: +几乎整个 Byteland +王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进 + +
                          +
                          + + 2022-06-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3237 [HNOI2014]米特运输 题解 + + 洛谷P3237 [HNOI2014]米特运输 题解 +
                          +
                          +
                          +
                          + + 洛谷P3237 [HNOI2014]米特运输 +题解 +题目链接:P3237 +[HNOI2014]米特运输 + +题意: +米特是D星球上一种非常神秘的物质,蕴含着巨大的能量。在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题。 +D + +
                          +
                          + + 2022-06-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          3 / 3
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/07/index.html b/archives/2022/07/index.html index e69de29bb2..d7676e5428 100644 --- a/archives/2022/07/index.html +++ b/archives/2022/07/index.html @@ -0,0 +1,1536 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/7 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 31 +
                          + +
                          + +
                          + + + + + +
                          + 31 +
                          +
                          +
                          +
                          + +
                          + + + CF691E Xor-sequences 题解 + + CF691E Xor-sequences 题解 +
                          +
                          +
                          +
                          + + CF691E Xor-sequences 题解 +题目链接:CF691E +Xor-sequences + +题意:给定大小为 \(n\) 的集合 \(\{a_1,\dots,a_n\}\),从集合中选择 \(k\) 个数组成一个序列 \(x_1 + +
                          +
                          + + 2022-07-31 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 线性代数-矩阵 + + 线性代数-矩阵 +
                          +
                          +
                          +
                          + + 线性代数-矩阵 +施工中,咕咕咕.... +矩阵乘法 +矩阵乘法的定义 +矩阵乘法的定义 \[ +(AB)_{i,j}=\sum_{k=1}^{m}A_{i,k}B_{k,j}\quad i\in[1,n],j\in[1,p] +\] 一个 \(n \ + +
                          +
                          + + 2022-07-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + ABC156E Roaming 题解 + + ABC156E Roaming 题解 +
                          +
                          +
                          +
                          + + ABC156E Roaming 题解 +题目链接:ABC156E +Roaming + +题意: +翻译来自我们模拟赛,可能和原题有区别 +输入两个数字 \(n,k\) ,初始时有 +\(n\) +个盒子,每个盒子中都装着一个小球,\(n\) +个小球之间不 + +
                          +
                          + + 2022-07-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + ABC167E Colorful Blocks 题解 + + ABC167E Colorful Blocks 题解 +
                          +
                          +
                          +
                          + + ABC167E Colorful Blocks 题解 +题目链接:ABC167E +Colorful Blocks + +题意: +翻译从我们模拟赛搬过来的,稍微有些不一样。 +有 \(n\) 个小球按照编号为 \(1−n\) +从左至右放成一排,你现在 + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[9] + + 模拟赛题讲解[9] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[9] +来自 Roundgod +2022-07-29 noi.ac #2693 +原题来自 ABC172E +NEQ +问题描述: +给定 \(n,m\),你需要计算满足以下条件的数组对 \(A=[a_1,a_2,\dots,a_n]\ + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + hdu4135 Co-prime 题解 + + hdu4135 Co-prime 题解 +
                          +
                          +
                          +
                          + + hdu4135 Co-prime 题解 +题目链接:hdu4135 +Co-prime + +题意: +\(T\) 组数据,每组给出 \(a,b,n\) ,求区间 \([a,b]\) 中有多少个数与 \(n\) 互质。 +Given a number + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-组合数学 + + OI数学总结-组合数学 +
                          +
                          +
                          +
                          + + OI数学总结-组合数学 +施工中,咕咕咕.... +排列组合 +更多详见 小蓝书 +16.1 +下面两个指的是无重复的排列与组合 +排列数 +从 \(n\) 个不同元素中取 \(k(k\le n)\) +个不同元素,并按一定顺序排成一列的方案数 \[ +\m + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + ABC210E Ring MST 题解 + + ABC210E Ring MST 题解 +
                          +
                          +
                          +
                          + + ABC210E Ring MST 题解 +题目链接:ABC210E Ring +MST + +题意:给定一张 \(n\) 个点的无向图,顶点的编号为 \(0,1,\dots,n−1\) 。同时给出两个长度为 \(m\) 的数组 \(a_1,a_2, + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          1 / 9
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/07/page/2/index.html b/archives/2022/07/page/2/index.html index e69de29bb2..6a49c36e77 100644 --- a/archives/2022/07/page/2/index.html +++ b/archives/2022/07/page/2/index.html @@ -0,0 +1,1515 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/7 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + ARC060B Digit Sum 题解 + + ARC060B Digit Sum 题解 +
                          +
                          +
                          +
                          + + ARC060B Digit Sum 题解 +题目链接:ARC060B Digit +Sum + +题意: +对于任意非负整数 \(x\) 和 \(m(2\le m)\) ,定义 \(f_m(x)\) 为 \(x\) 在 \(m\) 进制下的各位数字之 + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 数学常数表 + + 数学常数表 +
                          +
                          +
                          +
                          + + 一些闲着无聊可以瞎背背的常数,比如$e = \tt{2.718281828}$,$\pi = \tt{3.141592654}$,$\ln \pi = \tt{1.1447}$,$\sqrt{\pi} = \tt{1.7725}$等等... + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + CF906D Power Tower 题解 + + CF906D Power Tower 题解 +
                          +
                          +
                          +
                          + + CF906D Power Tower 题解 +题目链接:CF906D +Power Tower + +题意:给定长度为 \(n\) 的序列 \(a_i\) 和模数 \(p\) +\(Q\) 次询问区间 \([l,r]\) 的 \[ +a_l^{ {a_ + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + CF427C Checkposts 题解 + + CF427C Checkposts 题解 +
                          +
                          +
                          +
                          + + CF427C Checkposts 题解 +题目链接:CF427C +Checkposts + +题意: +懒得贴翻译,那个翻译太烂了 +Your city has $ n $ junctions. There are $ m $ one-way ro + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2424 约数和 题解 + + 洛谷P2424 约数和 题解 +
                          +
                          +
                          +
                          + + 洛谷P2424 约数和 题解 +题目链接:P2424 +约数和 + +题意: +对于一个数 \(X\),函数 \(f(X)\) 表示 \(X\) 所有约数的和。例如:\(f(6)=1+2+3+6=12\)。对于一个 \(X\),Smart 可以很快的 + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[8] + + 模拟赛题讲解[8] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[8] +来自 Roundgod +2022-07-27 noi.ac #2681 +题目描述: +给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\), 边数 +\(\vert E\vert=m\ + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[7] + + 模拟赛题讲解[7] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[7] +来自 Roundgod +2022-07-27 noi.ac #2682 +题目描述: +给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\) +,边数 \(\vert E\vert=m\ + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[6] + + 模拟赛题讲解[6] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[6] +来自 Roundgod +2022-07-27 noi.ac #2683 +题目描述: +给定一个连通无向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\) , +边数 \(\vert E\vert + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          2 / 9
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/07/page/3/index.html b/archives/2022/07/page/3/index.html index e69de29bb2..66d8059829 100644 --- a/archives/2022/07/page/3/index.html +++ b/archives/2022/07/page/3/index.html @@ -0,0 +1,1530 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/7 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[5] + + 模拟赛题讲解[5] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[5] +来自 Roundgod +2022-07-26 noi.ac #2678 +题目描述: +对于任意非负整数 \(x\) 和 \(m(2\le m\le 10)\) ,定义 \(f_m(x)\) 为 \(x\) 在 \(m\) + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[4] + + 模拟赛题讲解[4] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[4] +来自 Roundgod +2022-07-26 noi.ac #2680 +问题描述: +Berland由 \(n\) 个城市和 \(m\) 条双向道路构成,其中第 \(i\) 条道路连接城市 \(a_i\) 和 \(b_i\ + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[3] + + 模拟赛题讲解[3] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[3] +来自 Roundgod +2022-07-26 noi.ac #2679 +题目描述: +给定一个无向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数 \(|E|=m\) 。顶点编号为 \(1−N\), + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + OI tricks + + OI tricks +
                          +
                          +
                          +
                          + + OI tricks +平时做题的时候发现的一些技巧,还没有仔细整理 +因此本文比较像草稿般的个人总结 +1.终极快读 +namespace FastIO +{ + #define gc() readchar() + #define pc(a) + +
                          +
                          + + 2022-07-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[2] + + 模拟赛题讲解[2] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[2] +来自 Roundgod +2022-07-25 noi.ac #2677 +题目描述: +给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数 \(|E|=m\) 。顶点编号为 \(1−N\) , + +
                          +
                          + + 2022-07-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[1] + + 模拟赛题讲解[1] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[1] +来自 Roundgod +2022-07-25 noi.ac #2676 +题目描述: +给定一个带权连通无向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数\(|E|=m\) +图中可能包含重边以及自环。 + +
                          +
                          + + 2022-07-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + OI易错点-C++语法 + + OI易错点-C++语法 +
                          +
                          +
                          +
                          + + OI易错点-C++语法 +不保证本文内容完全正确,仅仅是个人总结!! +islower,isalpha,isdigit +这种函数,返回值不是bool!! +在不同的机器上跑出来是不一样的!!!!不要直接加上它!!! +lower_bound(beg + +
                          +
                          + + 2022-07-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2704 [NOI2001] 炮兵阵地 题解 + + 洛谷P2704 [NOI2001] 炮兵阵地 题解 +
                          +
                          +
                          +
                          + + 洛谷P2704 [NOI2001] 炮兵阵地 +题解 +题目链接:P2704 +[NOI2001] 炮兵阵地 + +题意: +司令部的将军们打算在 \(N\times M\) +的网格地图上部署他们的炮兵部队。 +一个 \(N\times M\) 的地图由 + +
                          +
                          + + 2022-07-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          3 / 9
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/07/page/4/index.html b/archives/2022/07/page/4/index.html index e69de29bb2..06e34df407 100644 --- a/archives/2022/07/page/4/index.html +++ b/archives/2022/07/page/4/index.html @@ -0,0 +1,1547 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/7 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 24 +
                          + +
                          + +
                          + + + + + +
                          + 23 +
                          + +
                          + +
                          + + + + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 密码生成器 + + 密码生成器 +
                          +
                          +
                          +
                          + + 密码生成器 +简单的小破密码生成器 qwq +也不知道搞了这个有啥用处 +目前还比较脆弱,不支持不合法输入 +反正就是个瞎搞的东西 qwq +代码: +/* + Name: 密码生成器1.0 + Author: q779 + Date: 2021.5.20 + + +
                          +
                          + + 2022-07-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 三子棋模拟器 for linux + + 三子棋模拟器 for linux +
                          +
                          +
                          +
                          + + 三子棋模拟器 for linux +中考前一周开摆写的 +采用的是minimax算法和 \(\alpha - +\beta\) 剪枝 +目前是基于规则的估价函数(因为q779不会AI) +目前在含C++环境的 ubuntu20.04 和 macOS + +
                          +
                          + + 2022-07-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 23 +
                          + +
                          + +
                          + + + + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + CF888E Maximum Subsequence 题解 + + CF888E Maximum Subsequence 题解 +
                          +
                          +
                          +
                          + + CF888E Maximum Subsequence +题解 +题目链接:CF888E +Maximum Subsequence + +题意: +给定N个数,第i个数的值为Ai,你现在可以从中选择一些数字,问选出数字的和模P最大为多少。 +输入第一行为N + +
                          +
                          + + 2022-07-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          +
                          +
                          +
                          + +
                          + + + CF479E Riding in a Lift 题解 + + CF479E Riding in a Lift 题解 +
                          +
                          +
                          +
                          + + CF479E Riding in a Lift 题解 +题目链接:CF479E +Riding in a Lift + +题意: +现在有n个传送点呈序列排列,编号为1到n +每一次可以通过折跃从一个传送点传送到另一处传送点, +由于折跃需要消耗巨大的能 + +
                          +
                          + + 2022-07-22 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2657 [SCOI2009] windy 数 题解 + + 洛谷P2657 [SCOI2009] windy 数 题解 +
                          +
                          +
                          +
                          + + 洛谷P2657 [SCOI2009] windy 数 +题解 +题目链接:P2657 +[SCOI2009] windy 数 + +题意: +不含前导零且相邻两个数字之差至少为 \(2\) 的正整数被称为 windy 数。windy +想知道,在 \(a + +
                          +
                          + + 2022-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1410 子序列 题解 + + 洛谷P1410 子序列 题解 +
                          +
                          +
                          +
                          + + 洛谷P1410 子序列 题解 +题目链接:P1410 +子序列 + +题意: +给定一个长度为 \(N\)(\(N\) +为偶数)的序列,问能否将其划分为两个长度为 \(N +/ 2\) 的严格递增子序列。 +【数据范围】 +共三组数据,每组数据行数< + +
                          +
                          + + 2022-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          4 / 9
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/07/page/5/index.html b/archives/2022/07/page/5/index.html index e69de29bb2..3fdbbe9e88 100644 --- a/archives/2022/07/page/5/index.html +++ b/archives/2022/07/page/5/index.html @@ -0,0 +1,1537 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/7 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + CF475D CGCDSSQ 题解 + + CF475D CGCDSSQ 题解 +
                          +
                          +
                          +
                          + + CF475D CGCDSSQ 题解 +题目链接:CF475D +CGCDSSQ + +题意: +给出一个长度为 \(n\) 的序列和 \(q\) 个询问, +每个询问输出一行,询问满足 \(\gcd\{a_l,a_{l+1},\dots,a_r\}=x + +
                          +
                          + + 2022-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + OI模板 + + OI模板 +
                          +
                          +
                          +
                          + + OI模板 +由于文件比较多,分为了多个部分。 + + + + + + + +Parts +包含内容 + + + + +OI模板-图论 +最短路算法、最小生成树、线段树优化建图、判负环、kosaraju算法、Tarjan算法 +[连通性问题]、欧拉路径、欧拉回路、2-SAT、 + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-图论 + + OI模板-图论 +
                          +
                          +
                          +
                          + + OI模板-图论 +最短路算法 +dijkstra +P4779 +【模板】单源最短路径(标准版) +优先队列优化 \(O((n+m)\log m)\) +#include <iostream> +#include <string> +#incl + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-字符串 + + OI模板-字符串 +
                          +
                          +
                          +
                          + + OI模板-字符串 +字符串哈希 +单哈希 +给定 \(N\) 个字符串(第 \(i\) 个字符串长度为 \(M_i\),字符串内包含数字、大小写字母,大小写敏感),请求出 +\(N\) +个字符串中共有多少个不同的字符串。 +P3370 +【模板】字符串 + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-算法 + + OI模板-算法 +
                          +
                          +
                          +
                          + + OI模板-算法 +排序算法 +其他乱七八糟的毛用没有(归并、松氏基排除外,还没补上来,咕咕咕...) +基本上一个sort全部搞定 +计数排序 +时间复杂度 \(O(n)\) +空间复杂度 \(O(\max\{n,\max\limits_{0<i + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-其他 + + OI模板-其他 +
                          +
                          +
                          +
                          + + OI模板-其他 +光速幂 +仅适用于int 且 底数相同,即 \(a^b\) , \(a\) 不变,复杂度约为 \(O(\sqrt{n})\) +#include <bits/stdc++.h> +using namespace std; +# + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-计算几何 + + OI模板-计算几何 +
                          +
                          +
                          +
                          + + OI模板-计算几何 +二维凸包 +Andrew +时间复杂度 \(O(n \log n)\) +#include <bits/stdc++.h> +using namespace std; +#define int long long +#defi + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-数据结构 + + OI模板-数据结构 +
                          +
                          +
                          +
                          + + OI模板-数据结构 +并查集 +具@Roundgod老师说 +按秩合并+路径压缩才能保证并查集查询的复杂度为 \(O(\alpha(n))\) +只用一个就是 \(O(\log n)\) +的,不过要特意构造数据卡才会到这个上界 +int f[N],r + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          5 / 9
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/07/page/6/index.html b/archives/2022/07/page/6/index.html index e69de29bb2..04d9e00956 100644 --- a/archives/2022/07/page/6/index.html +++ b/archives/2022/07/page/6/index.html @@ -0,0 +1,1540 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/7 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2391 白雪皑皑 题解 + + 洛谷P2391 白雪皑皑 题解 +
                          +
                          +
                          +
                          + + 洛谷P2391 白雪皑皑 题解 +题目链接:P2391 +白雪皑皑 + +题意: +现在有 \(n\) 片雪花排成一列。 pty +要对雪花进行 \(m\) 次染色操作,第 \(i\) 次染色操作中,把第 \(((i\times p+q)\bmod n + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          + +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1792 [国家集训队]种树 题解 + + 洛谷P1792 [国家集训队]种树 题解 +
                          +
                          +
                          +
                          + + 洛谷P1792 [国家集训队]种树 +题解 +题目链接:P1792 +[国家集训队]种树 + +题意: +A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。 +园林部门得到指令后,初步规划出 \(n\) +个种树的位置, + +
                          +
                          + + 2022-07-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          + +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1631 序列合并 题解 + + 洛谷P1631 序列合并 题解 +
                          +
                          +
                          +
                          + + 洛谷P1631 序列合并 题解 +题目链接:P1631 +序列合并 + +题意: +有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到\(N^2\)个和,求这\(N^2\)个和中最小的N个。 +对于100%的数据中,满足1<=N< + +
                          +
                          + + 2022-07-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3871 [TJOI2010]中位数 题解 + + 洛谷P3871 [TJOI2010]中位数 题解 +
                          +
                          +
                          +
                          + + 洛谷P3871 [TJOI2010]中位数 +题解 +题目链接:P3871 +[TJOI2010]中位数 + +题意: +给定一个由N个元素组成的整数序列,现在有两种操作: +1 add a +在该序列的最后添加一个整数a,组成长度为N + 1的整数序列 + + +
                          +
                          + + 2022-07-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          + +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2422 良好的感觉 题解 + + 洛谷P2422 良好的感觉 题解 +
                          +
                          +
                          +
                          + + 洛谷P2422 良好的感觉 题解 +题目链接:P2422 +良好的感觉 + +题意: +kkk 做了一个人体感觉分析器。每一天,人都有一个感受值 \(A_i\),\(A_i\) 越大,表示人感觉越舒适。在一段时间 +\(\left[i, j\right + +
                          +
                          + + 2022-07-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3512 [POI2010]PIL-Pilots 题解 + + 洛谷P3512 [POI2010]PIL-Pilots 题解 +
                          +
                          +
                          +
                          + + 洛谷P3512 [POI2010]PIL-Pilots +题解 +题目链接:P3512 +[POI2010]PIL-Pilots + +题意:给定 \(n\) +个数,找一个最长区间,使得区间中的最大值减最小值不超过 \(k\) + +题面写的太烂了 +考虑 + +
                          +
                          + + 2022-07-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + UVA11362 Phone List 题解 + + UVA11362 Phone List 题解 +
                          +
                          +
                          +
                          + + UVA11362 Phone List 题解 +题目链接:UVA11362 Phone +List + +题意:给定\(n\)个长度不超过\(10\)的数字串,判断是否有两个字符串\(A\)和\(B\),满足\(A\)是\(B\)的前缀,若有,输出 + +
                          +
                          + + 2022-07-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          + +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4113 [HEOI2012]采花 题解 + + 洛谷P4113 [HEOI2012]采花 题解 +
                          +
                          +
                          +
                          + + 洛谷P4113 [HEOI2012]采花 题解 +题目链接:P4113 +[HEOI2012]采花 + +题意:萧薰儿是古国的公主,平时的一大爱好是采花。 +今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。 +花园足够大,容纳了 \(n\) + +
                          +
                          + + 2022-07-15 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          6 / 9
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/07/page/7/index.html b/archives/2022/07/page/7/index.html index e69de29bb2..b58aa6953e 100644 --- a/archives/2022/07/page/7/index.html +++ b/archives/2022/07/page/7/index.html @@ -0,0 +1,1542 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/7 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3294 [SCOI2016]背单词 题解 + + 洛谷P3294 [SCOI2016]背单词 题解 +
                          +
                          +
                          +
                          + + 洛谷P3294 [SCOI2016]背单词 +题解 +题目链接:P3294 +[SCOI2016]背单词 + +题意:给定 \(n\) +个不同的字符串,求一个最优顺序使得花费最小 +设当前字符串为 \(a\) ,位于排列中的 +\(x\) 位置 + +如果 + +
                          +
                          + + 2022-07-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2444 [POI2000]病毒 题解 + + 洛谷P2444 [POI2000]病毒 题解 +
                          +
                          +
                          +
                          + + 洛谷P2444 [POI2000]病毒 题解 +题目链接:P2444 +[POI2000]病毒 + +题意: +二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全 + +
                          +
                          + + 2022-07-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 13 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3966 [TJOI2013]单词 题解 + + 洛谷P3966 [TJOI2013]单词 题解 +
                          +
                          +
                          +
                          + + 洛谷P3966 [TJOI2013]单词 题解 +题目链接:P3966 +[TJOI2013]单词 + +题意: +小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了 + +
                          +
                          + + 2022-07-13 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 13 +
                          + +
                          + +
                          + + + + + +
                          + 13 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3586 [POI2015] LOG 题解 + + 洛谷P3586 [POI2015] LOG 题解 +
                          +
                          +
                          +
                          + + 洛谷P3586 [POI2015] LOG 题解 +题目链接:P3586 +[POI2015] LOG + +题意: +维护一个长度为 \(n\) +的序列,一开始都是 \(0\),支持以下两种操作: + +U k a 将序列中第 \(k\) +个数修改为 + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3879 [TJOI2010] 阅读理解 题解 + + 洛谷P3879 [TJOI2010] 阅读理解 题解 +
                          +
                          +
                          +
                          + + 洛谷P3879 [TJOI2010] 阅读理解 +题解 +题目链接:P3879 +[TJOI2010] 阅读理解 + +题意: +英语老师留了 \(N\) +篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些 + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1168 中位数 题解 + + 洛谷P1168 中位数 题解 +
                          +
                          +
                          +
                          + + 洛谷P1168 中位数 题解 +题目链接:P1168 +中位数 + +题意: +给出一个长度为\(N\)的非负整数序列\(A_i\),对于所有\(1 ≤ k ≤ (N + 1) / 2\),输出\(A_1, A_1 \sim A_3, …,A_1 \ + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1816 忠诚 题解 + + 洛谷P1816 忠诚 题解 +
                          +
                          +
                          +
                          + + 洛谷P1816 忠诚 题解 +题目链接:P1816 +忠诚 + +题意:区间min。 + +BIT写法也就图一乐(其实不建议) +主要在询问上有些区别, +例如如果 x-lowbit(x) 小于 \(l\) ,则此时不能直接 +x-=lowbit(x) +因为 + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          7 / 9
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/07/page/8/index.html b/archives/2022/07/page/8/index.html index e69de29bb2..2428226e63 100644 --- a/archives/2022/07/page/8/index.html +++ b/archives/2022/07/page/8/index.html @@ -0,0 +1,1551 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/7 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 11 +
                          + +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1868 饥饿的奶牛 题解 + + 洛谷P1868 饥饿的奶牛 题解 +
                          +
                          +
                          +
                          + + 洛谷P1868 饥饿的奶牛 题解 +题目链接:P1868 +饥饿的奶牛 + +题意: +有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字。 +现用汉语翻译为: +有 \(N\) 个区间,每个区间 \(x,y\) 表示提供的 \ + +
                          +
                          + + 2022-07-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          + +
                          + +
                          + + + + + +
                          + 10 +
                          + +
                          + +
                          + + + + + +
                          + 10 +
                          + +
                          + +
                          + + + + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2375 [NOI2014] 动物园 题解 + + 洛谷P2375 [NOI2014] 动物园 题解 +
                          +
                          +
                          +
                          + + 洛谷P2375 [NOI2014] 动物园 +题解 +题目链接:P2375 +[NOI2014] 动物园 + +题意: +近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学 + +
                          +
                          + + 2022-07-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4568 [JLOI2011] 飞行路线 题解 + + 洛谷P4568 [JLOI2011] 飞行路线 题解 +
                          +
                          +
                          +
                          + + 洛谷P4568 [JLOI2011] 飞行路线 +题解 +题目链接:P4568 +[JLOI2011] 飞行路线 + +题意: +Alice 和 Bob +现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 +\(n\) +个城市设有业务, + +
                          +
                          + + 2022-07-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4551 最长异或路径 题解 + + 洛谷P4551 最长异或路径 题解 +
                          +
                          +
                          +
                          + + 洛谷P4551 最长异或路径 题解 +题目链接:P4551 +最长异或路径 + +题意:给定一棵 \(n\) 个点的带权树,结点下标从 \(1\) 开始到 \(n\)。寻找树中找两个结点,求最长的异或路径。 +异或路径指的是指两个结点之间唯一路径上的 + +
                          +
                          + + 2022-07-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3698 [CQOI2017]小Q的棋盘 题解 + + 洛谷P3698 [CQOI2017]小Q的棋盘 题解 +
                          +
                          +
                          +
                          + + 洛谷P3698 [CQOI2017]小Q的棋盘 +题解 +题目链接:P3698 +[CQOI2017]小Q的棋盘 + +题意: +小 Q 正在设计一种棋类游戏。 +在小 Q +设计的游戏中,棋子可以放在棋盘上的格点中。某些格点之间有连线,棋子只能在有连线的 + +
                          +
                          + + 2022-07-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          + +
                          + +
                          + + + + + +
                          + 09 +
                          + +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5520 [yLOI2019] 青原樱 题解 + + 洛谷P5520 [yLOI2019] 青原樱 题解 +
                          +
                          +
                          +
                          + + 洛谷P5520 [yLOI2019] 青原樱 +题解 +题目链接:P5520 +[yLOI2019] 青原樱 + +题意: +\(n\) 个空放 \(m\) +个物品,两两物品不能直接相邻,至少空一格 + +纯数学题。 +看看几个空不能放,啊 \(m-1\) + + +
                          +
                          + + 2022-07-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          8 / 9
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/07/page/9/index.html b/archives/2022/07/page/9/index.html index e69de29bb2..991fae2c90 100644 --- a/archives/2022/07/page/9/index.html +++ b/archives/2022/07/page/9/index.html @@ -0,0 +1,1407 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/7 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 08 +
                          + +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4127 [AHOI2009]同类分布 题解 + + 洛谷P4127 [AHOI2009]同类分布 题解 +
                          +
                          +
                          +
                          + + 洛谷P4127 [AHOI2009]同类分布 +题解 +题目链接:P4127 +[AHOI2009]同类分布 + +题意: +给出两个数 \(a,b\) ,求出 \([a,b]\) +中各位数字之和能整除原数的数的个数。 +对于所有的数据, \(1 ≤ a + +
                          +
                          + + 2022-07-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + CF1036C Classy Numbers 题解 + + CF1036C Classy Numbers 题解 +
                          +
                          +
                          +
                          + + CF1036C Classy Numbers 题解 +题目链接:CF1036C +Classy Numbers + +题意:定义一个数字是“好数”,当且仅当它的十进制表示下有不超过\(3\)个数字\(1 \sim +9\) +举个例子:\(4,2000 + +
                          +
                          + + 2022-07-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1836 数页码 题解 + + 洛谷P1836 数页码 题解 +
                          +
                          +
                          +
                          + + 洛谷P1836 数页码 题解 +题目链接:P1836 +数页码 + +题意: +一本书的页码是从 \(1\sim n\) +编号的连续整数:\(1,2,3,\cdots,n\)。请你求出全部页码中所有单个数字的和,例如第 +\(123\) 页,它的和就是 + +
                          +
                          + + 2022-07-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4047 [JSOI2010]部落划分 题解 + + 洛谷P4047 [JSOI2010]部落划分 题解 +
                          +
                          +
                          +
                          + + 洛谷P4047 [JSOI2010]部落划分 +题解 +题目链接:P4047 +[JSOI2010]部落划分 + +题意: +聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部 + +
                          +
                          + + 2022-07-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5536 【XR-3】核心城市 题解 + + 洛谷P5536 【XR-3】核心城市 题解 +
                          +
                          +
                          +
                          + + 洛谷P5536 【XR-3】核心城市 +题解 +题目链接:P5536 +【XR-3】核心城市 + +题意: +X 国有 \(n\) 座城市,\(n - 1\) 条长度为 \(1\) +的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达 + +
                          +
                          + + 2022-07-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          + +
                          + +
                          + + + + + +
                          + 02 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          9 / 9
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/08/index.html b/archives/2022/08/index.html index e69de29bb2..62339b532b 100644 --- a/archives/2022/08/index.html +++ b/archives/2022/08/index.html @@ -0,0 +1,1558 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/8 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          +
                          +
                          +
                          + +
                          + + + LOJ6269 烷基计数 加强版 题解 + + LOJ6269 烷基计数 加强版 题解 +
                          +
                          +
                          +
                          + + LOJ6269 烷基计数 加强版 题解 +题目链接:#6269. 烷基计数 +加强版 + +题意:众所周知,大连 24 +中是一所神奇的学校,在那里,化竞的同学很多都擅长写代码。 +有一天,化学不及格的胡小兔向化竞巨佬晴岚请教化学题: +“\(n\) +个 + +
                          +
                          + + 2022-08-22 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + CF786B Legacy 题解 + + CF786B Legacy 题解 +
                          +
                          +
                          +
                          + + CF786B Legacy 题解 +题目链接:CF786B +Legacy + +题意: +\(n\) 个结点, \(q\) 次操作,问操作后从 \(s\) 出发的单源最短路 +操作如下 + +输入 1 u v w 表示 \(u\) 到 \(v\) 连一条 + +
                          +
                          + + 2022-08-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3311 [SDOI2014] 数数 题解 + + 洛谷P3311 [SDOI2014] 数数 题解 +
                          +
                          +
                          +
                          + + 洛谷P3311 [SDOI2014] 数数 +题解 +题目链接:P3311 +[SDOI2014] 数数 + +题意: +我们称一个正整数 \(x\) +是幸运数,当且仅当它的十进制表示中不包含数字串集合 \(s\) 中任意一个元素作为其子串。例如当 \ + +
                          +
                          + + 2022-08-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-博弈论 + + OI数学总结-博弈论 +
                          +
                          +
                          +
                          + + OI数学总结-博弈论 +施工中,咕咕咕... +博弈论基础 +对于博弈论的题目,重点考虑以下两点 + +什么时候可以判定胜利者 +先手的影响 + + + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-其他 + + OI数学总结-其他 +
                          +
                          +
                          +
                          + + OI数学总结-其他 +本文充满了从各种地方偷学来的东西以及没什么分类的东西 +数值积分 +定积分 +简单来说,函数 \(f(x)\) 在区间 \([l,r]\) 上的定积分 \(\int_{l}^{r}f(x)\mathrm{d}x\) 指的是 \ + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-群论 + + OI数学总结-群论 +
                          +
                          +
                          +
                          + + OI数学总结-群论 +施工中,咕咕咕.... +群的基本概念 +群的定义 +群公理包含下述四个性质(有时略去封闭性,只有三个性质)。若集合 \(G\neq\varnothing\) 和 \(G\) 上的运算 \(\cdot\) 构成的代数结构 \( + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-数论 + + OI数学总结-数论 +
                          +
                          +
                          +
                          + + OI数学总结-数论 +施工中,咕咕咕.... +积性函数 +数论中定义:定义域为正整数域的函数 \(f(n)\) , +且满足 \(\forall a,b \in \mathbb{Z} +_+,f(ab)=f(a)f(b) \land \gcd(a, + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          1 / 4
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/08/page/2/index.html b/archives/2022/08/page/2/index.html index e69de29bb2..6529a47bfb 100644 --- a/archives/2022/08/page/2/index.html +++ b/archives/2022/08/page/2/index.html @@ -0,0 +1,1549 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/8 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 19 +
                          + +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + BZOJ2141 排队 题解 + + BZOJ2141 排队 题解 +
                          +
                          +
                          +
                          + + BZOJ2141 排队 题解 +题目链接:#2141. +排队 + +题意: +一句话题意: +给定 \(n\) 个数,\(Q\) +次询问,每次交换两个不同位置的数,然后输出逆序对数。 +\(1 \le n \le 2\times 10^4,~1\le + +
                          +
                          + + 2022-08-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 小蓝书 16.1 + + 小蓝书 16.1 +
                          +
                          +
                          +
                          + + 16.1 +施工中,咕咕咕.... +计数原理 +加法原理 +设完成某件事有 \(m\) +类不同的方法, +第 \(i(1\le i \le m)\) 类方法中有 +\(n_i\) 种不同的方法,则完成这件事共有 +\[ +n_1 + n_2 + \cdo + +
                          +
                          + + 2022-08-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + + CF1491F Magnets 题解 + + CF1491F Magnets 题解 +
                          +
                          +
                          +
                          + + CF1491F Magnets 题解 +题目链接:CF1491F +Magnets + +题意: +这是一个交互题。 +早苗有 \(n\) 块磁石,编号为 \(1,2,\cdots,n\)。每块磁石的磁极可能是正极,负极,也可能没有磁性。她希望你能帮她 + +
                          +
                          + + 2022-08-15 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2396 yyy loves Maths VII 题解 + + 洛谷P2396 yyy loves Maths VII 题解 +
                          +
                          +
                          +
                          + + 洛谷P2396 yyy loves Maths +VII 题解 +题目链接:P2396 yyy +loves Maths VII + +题意: +一群同学在和 yyy 玩一个游戏。 +每次,他们会给 yyy \(n\) +张卡片,卡片上有数字,所有的数字都 + +
                          +
                          + + 2022-08-15 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + CF1073E Segment Sum 题解 + + CF1073E Segment Sum 题解 +
                          +
                          +
                          +
                          + + CF1073E Segment Sum 题解 +题目链接:CF1073E +Segment Sum + +题意: +给定 \(l,r,k\) ,求 \([l,r]\) 有多少个数满足「不包含超过 \(k\) 个数码」,输出他们的和 \(\bmod { + +
                          +
                          + + 2022-08-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + UOJ66 新年的巧克力棒 题解 + + UOJ66 新年的巧克力棒 题解 +
                          +
                          +
                          +
                          + + UOJ66 新年的巧克力棒 题解 +题目链接:#66. +新年的巧克力棒 + +题意:马上就要到羊年了,羊村一片欢腾,懒羊羊则懒洋洋地躺在草坪上吃新年的巧克力棒。 +他手上的巧克力棒是个由 \(n\) +个巧克力单元格组成的长度为 \(n\) +的长条, + +
                          +
                          + + 2022-08-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2292 [HNOI2004] L 语言 题解 + + 洛谷P2292 [HNOI2004] L 语言 题解 +
                          +
                          +
                          +
                          + + 洛谷P2292 [HNOI2004] L 语言 +题解 +题目链接:P2292 +[HNOI2004] L 语言 + +题意: +标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的。现在你要处理的就是一段没有标点的文章。 +一段文章 \(T\) + + +
                          +
                          + + 2022-08-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1305 新二叉树 题解 + + 洛谷P1305 新二叉树 题解 +
                          +
                          +
                          +
                          + + 洛谷P1305 新二叉树 题解 +题目链接:P1305 +新二叉树 + +题意: +输入一串二叉树,输出其前序遍历。 +输入1: +6 +abc +bdi +cj* +d** +i** +j** +输出1: +abdicj + +虽然是大水题,但是有个解法还是很妙的 +利用前 + +
                          +
                          + + 2022-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1229 遍历问题 题解 + + 洛谷P1229 遍历问题 题解 +
                          +
                          +
                          +
                          + + 洛谷P1229 遍历问题 题解 +题目链接:P1229 +遍历问题 + +题意: +我们都很熟悉二叉树的前序、中序、后序遍历,在数据结构中常提出这样的问题:已知一棵二叉树的前序和中序遍历,求它的后序遍历,相应的,已知一棵二叉树的后序遍历和中序遍历序列 + +
                          +
                          + + 2022-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          2 / 4
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/08/page/3/index.html b/archives/2022/08/page/3/index.html index e69de29bb2..773020fb33 100644 --- a/archives/2022/08/page/3/index.html +++ b/archives/2022/08/page/3/index.html @@ -0,0 +1,1535 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/8 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[20] + + 模拟赛题讲解[20] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[20] +来自 AprilGrimoire +2022-08-09 noi.ac #2775 +题目描述: +小 Z 想出几道送分题。 +小 Z 有一列脑洞(共 \(m(m \leq +10^9)\) +个)。脑洞分为白色与黑色,白色脑洞会 + +
                          +
                          + + 2022-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[19] + + 模拟赛题讲解[19] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[19] +来自 yukuai26 +2022-08-08 noi.ac #2771 +题目背景: +\(\text{yukuai26}\) +喜欢爬山,所以他选择绕着山跑圈 +题目描述: +\(\text{yukuai26}\) +会跑一个环 + +
                          +
                          + + 2022-08-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[18] + + 模拟赛题讲解[18] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[18] +来自 yukuai26 +2022-08-08 noi.ac #2773 +题目背景: +搞个大新闻. jpg +题目描述: +你来到了幻想乡。首先你准备搞一个大新闻,获得文文的报道并变得出名,你决定对幻想乡的股票系统下手。 +你 + +
                          +
                          + + 2022-08-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          + +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[17] + + 模拟赛题讲解[17] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[17] +来自 yukuai26 +2022-08-08 noi.ac #2772 +题目描述: +\(\text{ysgh}\) 有一个长度为 \(m\) 的,元素两两不同的序列。 +\(\text{emoairx}\) +想要知道这个 + +
                          +
                          + + 2022-08-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[16] + + 模拟赛题讲解[16] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[16] +来自 yukuai26 +2022-08-07 noi.ac #2764 +题目描述: +紫开始研究特殊的括号序列 +这些特殊括号序列的形式是若干个右括号(可以没有)在前,若干个左括号(可以没有)在后。 +比如 ((,))),) + +
                          +
                          + + 2022-08-07 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          + +
                          + +
                          + + + + + +
                          + 07 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[15] + + 模拟赛题讲解[15] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[15] +来自 yukuai26 +2022-08-07 noi.ac +#2763 +题目描述: +幻想乡有 \(n\) +个建筑,每个建筑里住了一些居民。 +有一些双向道路连接居民的家,共有 \(n-1\) +条道路,恰好让所有居民的家能 + +
                          +
                          + + 2022-08-07 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[14] + + 模拟赛题讲解[14] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[14] +来自 yukuai26 +2022-08-06 noi.ac #2755 +题目背景 +\(\text{yukuai26}\) +喜欢算术,但他又菜又爱玩,所以需要你的帮助 +题目描述: +小明给你 \(n\) +个正整数,他想取一 + +
                          +
                          + + 2022-08-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[13] + + 模拟赛题讲解[13] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[13] +来自 yukuai26 +2022-08-06 noi.ac #2757 +据说是从JOI搬过来的,赛时有位巨佬18min就A了 Orz +题目描述: +橙 ---- 作为幽幽子大人的朋友的使者, 非常贪玩, 也喜欢思考有趣 + + +
                          +
                          + + 2022-08-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[12] + + 模拟赛题讲解[12] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[12] +来自 yukuai26 +2022-08-06 noi.ac #2756 +题目描述: +曾经有一个 oj 叫做 bzoj, 里面有一题 bzoj1002 叫狼抓兔子 +由于 1002 过于有名且显眼,很多人 A 掉了他,每 + +
                          +
                          + + 2022-08-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 04 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[11] + + 模拟赛题讲解[11] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[11] +来自 xpp 2022-08-04 +noi.ac #2731 +题目描述: +xpp在玩祖玛游戏,他心血来潮想根据祖玛游戏出一个题。 +给一个序列 \(a_1,a_2,\dots,a_n\) +,你每次可以选择相同且相邻的三个 + +
                          +
                          + + 2022-08-04 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          3 / 4
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/08/page/4/index.html b/archives/2022/08/page/4/index.html index e69de29bb2..2fc30681b1 100644 --- a/archives/2022/08/page/4/index.html +++ b/archives/2022/08/page/4/index.html @@ -0,0 +1,951 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022/8 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[10] + + 模拟赛题讲解[10] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[10] +来自 xpp 2022-08-03 +noi.ac #2723 +题目描述 +lzr给了xpp一个字符串 \(s\) +和一个字符串 \(t\) ,xpp想通过 \(s\) 构造出 \(t\) ,构造方式为xpp每次选择 \( + +
                          +
                          + + 2022-08-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          + +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + 初赛复习 + + 初赛复习 +
                          +
                          +
                          +
                          + + 初赛复习 +施工中...... +参考文献等等补上来。 +IT发展历史 +第一台计算机:ENIAC,1946年 +应用:计算,数据储存处理,通信,辅助工作等 +第一个程序员:Ada(女),有为此命名的程序语言 +图灵奖(计算机),菲尔兹奖(数学),诺贝尔 + +
                          +
                          + + 2022-08-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          4 / 4
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/index.html b/archives/2022/index.html index e69de29bb2..e096423e64 100644 --- a/archives/2022/index.html +++ b/archives/2022/index.html @@ -0,0 +1,1558 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          +
                          +
                          +
                          + +
                          + + + LOJ6269 烷基计数 加强版 题解 + + LOJ6269 烷基计数 加强版 题解 +
                          +
                          +
                          +
                          + + LOJ6269 烷基计数 加强版 题解 +题目链接:#6269. 烷基计数 +加强版 + +题意:众所周知,大连 24 +中是一所神奇的学校,在那里,化竞的同学很多都擅长写代码。 +有一天,化学不及格的胡小兔向化竞巨佬晴岚请教化学题: +“\(n\) +个 + +
                          +
                          + + 2022-08-22 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + CF786B Legacy 题解 + + CF786B Legacy 题解 +
                          +
                          +
                          +
                          + + CF786B Legacy 题解 +题目链接:CF786B +Legacy + +题意: +\(n\) 个结点, \(q\) 次操作,问操作后从 \(s\) 出发的单源最短路 +操作如下 + +输入 1 u v w 表示 \(u\) 到 \(v\) 连一条 + +
                          +
                          + + 2022-08-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3311 [SDOI2014] 数数 题解 + + 洛谷P3311 [SDOI2014] 数数 题解 +
                          +
                          +
                          +
                          + + 洛谷P3311 [SDOI2014] 数数 +题解 +题目链接:P3311 +[SDOI2014] 数数 + +题意: +我们称一个正整数 \(x\) +是幸运数,当且仅当它的十进制表示中不包含数字串集合 \(s\) 中任意一个元素作为其子串。例如当 \ + +
                          +
                          + + 2022-08-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-博弈论 + + OI数学总结-博弈论 +
                          +
                          +
                          +
                          + + OI数学总结-博弈论 +施工中,咕咕咕... +博弈论基础 +对于博弈论的题目,重点考虑以下两点 + +什么时候可以判定胜利者 +先手的影响 + + + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-其他 + + OI数学总结-其他 +
                          +
                          +
                          +
                          + + OI数学总结-其他 +本文充满了从各种地方偷学来的东西以及没什么分类的东西 +数值积分 +定积分 +简单来说,函数 \(f(x)\) 在区间 \([l,r]\) 上的定积分 \(\int_{l}^{r}f(x)\mathrm{d}x\) 指的是 \ + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-群论 + + OI数学总结-群论 +
                          +
                          +
                          +
                          + + OI数学总结-群论 +施工中,咕咕咕.... +群的基本概念 +群的定义 +群公理包含下述四个性质(有时略去封闭性,只有三个性质)。若集合 \(G\neq\varnothing\) 和 \(G\) 上的运算 \(\cdot\) 构成的代数结构 \( + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-数论 + + OI数学总结-数论 +
                          +
                          +
                          +
                          + + OI数学总结-数论 +施工中,咕咕咕.... +积性函数 +数论中定义:定义域为正整数域的函数 \(f(n)\) , +且满足 \(\forall a,b \in \mathbb{Z} +_+,f(ab)=f(a)f(b) \land \gcd(a, + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          1 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/10/index.html b/archives/2022/page/10/index.html index e69de29bb2..9fe63cc6c1 100644 --- a/archives/2022/page/10/index.html +++ b/archives/2022/page/10/index.html @@ -0,0 +1,1537 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + UVA11362 Phone List 题解 + + UVA11362 Phone List 题解 +
                          +
                          +
                          +
                          + + UVA11362 Phone List 题解 +题目链接:UVA11362 Phone +List + +题意:给定\(n\)个长度不超过\(10\)的数字串,判断是否有两个字符串\(A\)和\(B\),满足\(A\)是\(B\)的前缀,若有,输出 + +
                          +
                          + + 2022-07-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          + +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4113 [HEOI2012]采花 题解 + + 洛谷P4113 [HEOI2012]采花 题解 +
                          +
                          +
                          +
                          + + 洛谷P4113 [HEOI2012]采花 题解 +题目链接:P4113 +[HEOI2012]采花 + +题意:萧薰儿是古国的公主,平时的一大爱好是采花。 +今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。 +花园足够大,容纳了 \(n\) + +
                          +
                          + + 2022-07-15 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3294 [SCOI2016]背单词 题解 + + 洛谷P3294 [SCOI2016]背单词 题解 +
                          +
                          +
                          +
                          + + 洛谷P3294 [SCOI2016]背单词 +题解 +题目链接:P3294 +[SCOI2016]背单词 + +题意:给定 \(n\) +个不同的字符串,求一个最优顺序使得花费最小 +设当前字符串为 \(a\) ,位于排列中的 +\(x\) 位置 + +如果 + +
                          +
                          + + 2022-07-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2444 [POI2000]病毒 题解 + + 洛谷P2444 [POI2000]病毒 题解 +
                          +
                          +
                          +
                          + + 洛谷P2444 [POI2000]病毒 题解 +题目链接:P2444 +[POI2000]病毒 + +题意: +二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全 + +
                          +
                          + + 2022-07-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 13 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3966 [TJOI2013]单词 题解 + + 洛谷P3966 [TJOI2013]单词 题解 +
                          +
                          +
                          +
                          + + 洛谷P3966 [TJOI2013]单词 题解 +题目链接:P3966 +[TJOI2013]单词 + +题意: +小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了 + +
                          +
                          + + 2022-07-13 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 13 +
                          + +
                          + +
                          + + + + + +
                          + 13 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3586 [POI2015] LOG 题解 + + 洛谷P3586 [POI2015] LOG 题解 +
                          +
                          +
                          +
                          + + 洛谷P3586 [POI2015] LOG 题解 +题目链接:P3586 +[POI2015] LOG + +题意: +维护一个长度为 \(n\) +的序列,一开始都是 \(0\),支持以下两种操作: + +U k a 将序列中第 \(k\) +个数修改为 + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3879 [TJOI2010] 阅读理解 题解 + + 洛谷P3879 [TJOI2010] 阅读理解 题解 +
                          +
                          +
                          +
                          + + 洛谷P3879 [TJOI2010] 阅读理解 +题解 +题目链接:P3879 +[TJOI2010] 阅读理解 + +题意: +英语老师留了 \(N\) +篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些 + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1168 中位数 题解 + + 洛谷P1168 中位数 题解 +
                          +
                          +
                          +
                          + + 洛谷P1168 中位数 题解 +题目链接:P1168 +中位数 + +题意: +给出一个长度为\(N\)的非负整数序列\(A_i\),对于所有\(1 ≤ k ≤ (N + 1) / 2\),输出\(A_1, A_1 \sim A_3, …,A_1 \ + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          10 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/11/index.html b/archives/2022/page/11/index.html index e69de29bb2..28e3d4681a 100644 --- a/archives/2022/page/11/index.html +++ b/archives/2022/page/11/index.html @@ -0,0 +1,1547 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1816 忠诚 题解 + + 洛谷P1816 忠诚 题解 +
                          +
                          +
                          +
                          + + 洛谷P1816 忠诚 题解 +题目链接:P1816 +忠诚 + +题意:区间min。 + +BIT写法也就图一乐(其实不建议) +主要在询问上有些区别, +例如如果 x-lowbit(x) 小于 \(l\) ,则此时不能直接 +x-=lowbit(x) +因为 + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          + +
                          + +
                          + + + + + +
                          + 11 +
                          + +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1868 饥饿的奶牛 题解 + + 洛谷P1868 饥饿的奶牛 题解 +
                          +
                          +
                          +
                          + + 洛谷P1868 饥饿的奶牛 题解 +题目链接:P1868 +饥饿的奶牛 + +题意: +有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字。 +现用汉语翻译为: +有 \(N\) 个区间,每个区间 \(x,y\) 表示提供的 \ + +
                          +
                          + + 2022-07-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          + +
                          + +
                          + + + + + +
                          + 10 +
                          + +
                          + +
                          + + + + + +
                          + 10 +
                          + +
                          + +
                          + + + + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2375 [NOI2014] 动物园 题解 + + 洛谷P2375 [NOI2014] 动物园 题解 +
                          +
                          +
                          +
                          + + 洛谷P2375 [NOI2014] 动物园 +题解 +题目链接:P2375 +[NOI2014] 动物园 + +题意: +近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学 + +
                          +
                          + + 2022-07-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4568 [JLOI2011] 飞行路线 题解 + + 洛谷P4568 [JLOI2011] 飞行路线 题解 +
                          +
                          +
                          +
                          + + 洛谷P4568 [JLOI2011] 飞行路线 +题解 +题目链接:P4568 +[JLOI2011] 飞行路线 + +题意: +Alice 和 Bob +现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 +\(n\) +个城市设有业务, + +
                          +
                          + + 2022-07-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4551 最长异或路径 题解 + + 洛谷P4551 最长异或路径 题解 +
                          +
                          +
                          +
                          + + 洛谷P4551 最长异或路径 题解 +题目链接:P4551 +最长异或路径 + +题意:给定一棵 \(n\) 个点的带权树,结点下标从 \(1\) 开始到 \(n\)。寻找树中找两个结点,求最长的异或路径。 +异或路径指的是指两个结点之间唯一路径上的 + +
                          +
                          + + 2022-07-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3698 [CQOI2017]小Q的棋盘 题解 + + 洛谷P3698 [CQOI2017]小Q的棋盘 题解 +
                          +
                          +
                          +
                          + + 洛谷P3698 [CQOI2017]小Q的棋盘 +题解 +题目链接:P3698 +[CQOI2017]小Q的棋盘 + +题意: +小 Q 正在设计一种棋类游戏。 +在小 Q +设计的游戏中,棋子可以放在棋盘上的格点中。某些格点之间有连线,棋子只能在有连线的 + +
                          +
                          + + 2022-07-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          11 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/12/index.html b/archives/2022/page/12/index.html index e69de29bb2..278c87c3e4 100644 --- a/archives/2022/page/12/index.html +++ b/archives/2022/page/12/index.html @@ -0,0 +1,1544 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 09 +
                          + +
                          + +
                          + + + + + +
                          + 09 +
                          + +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5520 [yLOI2019] 青原樱 题解 + + 洛谷P5520 [yLOI2019] 青原樱 题解 +
                          +
                          +
                          +
                          + + 洛谷P5520 [yLOI2019] 青原樱 +题解 +题目链接:P5520 +[yLOI2019] 青原樱 + +题意: +\(n\) 个空放 \(m\) +个物品,两两物品不能直接相邻,至少空一格 + +纯数学题。 +看看几个空不能放,啊 \(m-1\) + + +
                          +
                          + + 2022-07-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          + +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4127 [AHOI2009]同类分布 题解 + + 洛谷P4127 [AHOI2009]同类分布 题解 +
                          +
                          +
                          +
                          + + 洛谷P4127 [AHOI2009]同类分布 +题解 +题目链接:P4127 +[AHOI2009]同类分布 + +题意: +给出两个数 \(a,b\) ,求出 \([a,b]\) +中各位数字之和能整除原数的数的个数。 +对于所有的数据, \(1 ≤ a + +
                          +
                          + + 2022-07-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + CF1036C Classy Numbers 题解 + + CF1036C Classy Numbers 题解 +
                          +
                          +
                          +
                          + + CF1036C Classy Numbers 题解 +题目链接:CF1036C +Classy Numbers + +题意:定义一个数字是“好数”,当且仅当它的十进制表示下有不超过\(3\)个数字\(1 \sim +9\) +举个例子:\(4,2000 + +
                          +
                          + + 2022-07-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1836 数页码 题解 + + 洛谷P1836 数页码 题解 +
                          +
                          +
                          +
                          + + 洛谷P1836 数页码 题解 +题目链接:P1836 +数页码 + +题意: +一本书的页码是从 \(1\sim n\) +编号的连续整数:\(1,2,3,\cdots,n\)。请你求出全部页码中所有单个数字的和,例如第 +\(123\) 页,它的和就是 + +
                          +
                          + + 2022-07-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4047 [JSOI2010]部落划分 题解 + + 洛谷P4047 [JSOI2010]部落划分 题解 +
                          +
                          +
                          +
                          + + 洛谷P4047 [JSOI2010]部落划分 +题解 +题目链接:P4047 +[JSOI2010]部落划分 + +题意: +聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部 + +
                          +
                          + + 2022-07-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5536 【XR-3】核心城市 题解 + + 洛谷P5536 【XR-3】核心城市 题解 +
                          +
                          +
                          +
                          + + 洛谷P5536 【XR-3】核心城市 +题解 +题目链接:P5536 +【XR-3】核心城市 + +题意: +X 国有 \(n\) 座城市,\(n - 1\) 条长度为 \(1\) +的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达 + +
                          +
                          + + 2022-07-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          12 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/13/index.html b/archives/2022/page/13/index.html index e69de29bb2..ce39933c41 100644 --- a/archives/2022/page/13/index.html +++ b/archives/2022/page/13/index.html @@ -0,0 +1,1549 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 02 +
                          + +
                          + +
                          + + + + + +
                          + 06 +
                          + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3426 [POI2005]SZA-Template 题解 + + 洛谷P3426 [POI2005]SZA-Template 题解 +
                          +
                          +
                          +
                          + + 洛谷P3426 +[POI2005]SZA-Template 题解 +题目链接:P3426 +[POI2005]SZA-Template + +题意:你打算在纸上印一串字母。 +为了完成这项工作,你决定刻一个印章。印章每使用一次,就会将印章上的所有字母 + +
                          +
                          + + 2022-06-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2466 [SDOI2008] Sue 的小球 题解 + + 洛谷P2466 [SDOI2008] Sue 的小球 题解 +
                          +
                          +
                          +
                          + + 洛谷P2466 [SDOI2008] Sue +的小球 题解 + 题目链接:P2466 +[SDOI2008] Sue 的小球 + +题意: +Sue 和 Sandy +最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue +有一 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1608 路径统计 题解 + + 洛谷P1608 路径统计 题解 +
                          +
                          +
                          +
                          + + 洛谷P1608 路径统计 题解 +题目链接:P1608 +路径统计 + +题意: +一句话题意:最短路计数。 +“RP 餐厅” +的员工素质就是不一般,在齐刷刷的算出同一个电话号码之后,就准备让 HZH,TZY +去送快餐了,他们将自己居住的城市画了一张地 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5440 【XR-2】奇迹 题解 + + 洛谷P5440 【XR-2】奇迹 题解 +
                          +
                          +
                          +
                          + + 洛谷P5440 【XR-2】奇迹 题解 +题目链接:P5440 +【XR-2】奇迹 + +题意: +我们称一个日期为一个八位数,第 1~4 位构成年,第 5~6 +位构成月,第 7~8 位构成日,不足位数用 0 +补足。同时,要求日期所代表的这一天真实存 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1514 [NOIP2010 提高组] 引水入城 题解 + + 洛谷P1514 [NOIP2010 提高组] 引水入城 题解 +
                          +
                          +
                          +
                          + + 洛谷P1514 [NOIP2010 +提高组] 引水入城 题解 +题目链接:P1514 +[NOIP2010 提高组] 引水入城 + +题意: +在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个\( + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1378 油滴扩展 题解 + + 洛谷P1378 油滴扩展 题解 +
                          +
                          +
                          +
                          + + 洛谷P1378 油滴扩展 题解 +题目链接:P1378 +油滴扩展 + +题意: +在一个长方形框子里,最多有 \(N\) +个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界。必须等一个油滴扩展完 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4983 忘情 题解 + + 洛谷P4983 忘情 题解 +
                          +
                          +
                          +
                          + + 洛谷P4983 忘情 题解 +题目链接:P4983 +忘情 + +题意: +“为什么要离开我!” +“因为你没玩儿转!” +“我玩儿转了!” +“那好,你现在就给我维护这么一个式子!” +“为什么要出这么毒瘤的东西。” +“为了恶心你。” +“......” +\ + +
                          +
                          + + 2022-06-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          13 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/14/index.html b/archives/2022/page/14/index.html index e69de29bb2..2cccb3570c 100644 --- a/archives/2022/page/14/index.html +++ b/archives/2022/page/14/index.html @@ -0,0 +1,1541 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 06 +
                          + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + CF125E MST Company 题解 + + CF125E MST Company 题解 +
                          +
                          +
                          +
                          + + CF125E MST Company 题解 +题目链接:CF125E +MST Company + +题意: +给你一个有 \(n\) 个节点,\(m\) +条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足节点 \(1\) 正好连了 \( + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5633 最小度限制生成树 题解 + + 洛谷P5633 最小度限制生成树 题解 +
                          +
                          +
                          +
                          + + 洛谷P5633 最小度限制生成树 +题解 +题目链接:P5633 +最小度限制生成树 + +题意: +给你一个有 \(n\) 个节点,\(m\) +条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足编号为 +\(s\) 的节点正好连了 \(k\ + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2619 [国家集训队]Tree I 题解 + + 洛谷P2619 [国家集训队]Tree I 题解 +
                          +
                          +
                          +
                          + + 洛谷P2619 [国家集训队]Tree I +题解 +题目链接:P2619 +[国家集训队]Tree I + +题意: +给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 +\(\text{need}\) 条白色边的生成树。 +题目保证 + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2607 [ZJOI2008] 骑士 题解 + + 洛谷P2607 [ZJOI2008] 骑士 题解 +
                          +
                          +
                          +
                          + + 洛谷P2607 [ZJOI2008] 骑士 +题解 +题目链接:P2607 +[ZJOI2008] 骑士 + +题意: +Z +国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。 +最近发生了一件可怕的 + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1453 城市环路 题解 + + 洛谷P1453 城市环路 题解 +
                          +
                          +
                          +
                          + + 洛谷P1453 城市环路 题解 +题目链接:P1453 +城市环路 + +题意: +整个城市可以看做一个 \(n\) +个点,\(n\) +条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有 +\(2\) +条简单路径互通。图中的其 + +
                          +
                          + + 2022-06-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1083 [NOIP2012 提高组] 借教室 题解 + + 洛谷P1083 [NOIP2012 提高组] 借教室 题解 +
                          +
                          +
                          +
                          + + 洛谷P1083 [NOIP2012 +提高组] 借教室 题解 +题目链接:P1083 +[NOIP2012 提高组] 借教室 + +题意: +在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不 + +
                          +
                          + + 2022-06-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2678 [NOIP2015 提高组] 跳石头 题解 + + 洛谷P2678 [NOIP2015 提高组] 跳石头 题解 +
                          +
                          +
                          +
                          + + 洛谷P2678 [NOIP2015 +提高组] 跳石头 题解 +题目链接:P2678 +[NOIP2015 提高组] 跳石头 + +题意: +这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起 + +
                          +
                          + + 2022-06-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          14 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/15/index.html b/archives/2022/page/15/index.html index e69de29bb2..26b2f85d45 100644 --- a/archives/2022/page/15/index.html +++ b/archives/2022/page/15/index.html @@ -0,0 +1,1560 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 06 +
                          + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3647 [APIO2014] 连珠线 题解 + + 洛谷P3647 [APIO2014] 连珠线 题解 +
                          +
                          +
                          +
                          + + 洛谷P3647 [APIO2014] 连珠线 +题解 +题目链接:P3647 +[APIO2014] 连珠线 + +题意: +在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 +\(1\) 到 + +
                          +
                          + + 2022-06-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          + +
                          + +
                          + + + + + +
                          + 07 +
                          + +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-数学 + + OI模板-数学 +
                          +
                          +
                          +
                          + + OI模板-数学 +待补全 +排列的计算 +排列 \(A_n^m\) 直接算可以 \(O(n)\) +例如求解 \(A_{n-m+1}^{m} \bmod +p\) +int res=1; +for(int i=n-m+1; i>=n-2*m+2; i-- + +
                          +
                          + + 2022-06-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1272 重建道路 题解 + + 洛谷P1272 重建道路 题解 +
                          +
                          +
                          +
                          + + 洛谷P1272 重建道路 题解 +题目链接:P1272 +重建道路 + +题意: +一场可怕的地震后,人们用 \(N\) +个牲口棚(编号 \(1\sim N\))重建了农夫 +John +的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一 + +
                          +
                          + + 2022-06-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1734 最大约数和 题解 + + 洛谷P1734 最大约数和 题解 +
                          +
                          +
                          +
                          + + 洛谷P1734 最大约数和 题解 +题目链接:P1734 +最大约数和 + +题意:选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。 + +设 \(dp[i][j]\) 表示只考虑前 \(i\) 个数总和不超过 \(j\) + +
                          +
                          + + 2022-06-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          + +
                          + +
                          + + + + + +
                          + 06 +
                          + +
                          + +
                          + + + + + +
                          + 06 +
                          + +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1270 “访问”美术馆 题解 + + 洛谷P1270 “访问”美术馆 题解 +
                          +
                          +
                          +
                          + + 洛谷P1270 “访问”美术馆 题解 +题目链接:P1270 +“访问”美术馆 + +题意: +经过数月的精心准备,Peer +Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。 + +
                          +
                          + + 2022-06-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3354 [IOI2005]Riv 河流 题解 + + 洛谷P3354 [IOI2005]Riv 河流 题解 +
                          +
                          +
                          +
                          + + 洛谷P3354 [IOI2005]Riv 河流 +题解 +题目链接:P3354 +[IOI2005]Riv 河流 + +题意: +几乎整个 Byteland +王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进 + +
                          +
                          + + 2022-06-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3237 [HNOI2014]米特运输 题解 + + 洛谷P3237 [HNOI2014]米特运输 题解 +
                          +
                          +
                          +
                          + + 洛谷P3237 [HNOI2014]米特运输 +题解 +题目链接:P3237 +[HNOI2014]米特运输 + +题意: +米特是D星球上一种非常神秘的物质,蕴含着巨大的能量。在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题。 +D + +
                          +
                          + + 2022-06-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          15 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/16/index.html b/archives/2022/page/16/index.html index e69de29bb2..da22ff5611 100644 --- a/archives/2022/page/16/index.html +++ b/archives/2022/page/16/index.html @@ -0,0 +1,1567 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 31 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1273 有线电视网 题解 + + 洛谷P1273 有线电视网 题解 +
                          +
                          +
                          +
                          + + 洛谷P1273 有线电视网 题解 +题目链接:P1273 +有线电视网 + +题意: +某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节 + +
                          +
                          + + 2022-05-31 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2279 [HNOI2003]消防局的设立 题解 + + 洛谷P2279 [HNOI2003]消防局的设立 题解 +
                          +
                          +
                          +
                          + + 洛谷P2279 +[HNOI2003]消防局的设立 题解 +题目链接:P2279 +[HNOI2003]消防局的设立 + +题意: +2020 年,人类在火星上建立了一个庞大的基地群,总共有 \(n\) 个基地。起初为了节约材料,人类只修建了 +\(n- + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2015 二叉苹果树 题解 + + 洛谷P2015 二叉苹果树 题解 +
                          +
                          +
                          +
                          + + 洛谷P2015 二叉苹果树 题解 +题目链接:P2015 +二叉苹果树 + +题意: +有一棵苹果树,如果树枝有分叉,一定是分二叉(就是说没有只有一个儿子的结点) +这棵树共有 \(N\) +个结点(叶子点或者树枝分叉点),编号为 \(1 +\sim N\ + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3174 [HAOI2009] 毛毛虫 题解 + + 洛谷P3174 [HAOI2009] 毛毛虫 题解 +
                          +
                          +
                          +
                          + + 洛谷P3174 [HAOI2009] 毛毛虫 +题解 +题目链接:P3174 +[HAOI2009] 毛毛虫 + +题意: +对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大。例如下图左边的树(图 +\( + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3621 [APIO2007] 风铃 题解 + + 洛谷P3621 [APIO2007] 风铃 题解 +
                          +
                          +
                          +
                          + + 洛谷P3621 [APIO2007] 风铃 +题解 +题目链接:P3621 +[APIO2007] 风铃 + +题意: +你准备给弟弟 Ike 买一件礼物,但是,Ike +挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西。 +你准备给 Ike +买 + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2458 [SDOI2006]保安站岗 题解 + + 洛谷P2458 [SDOI2006]保安站岗 题解 +
                          +
                          +
                          +
                          + + 洛谷P2458 [SDOI2006]保安站岗 +题解 +题目链接:P2458 +[SDOI2006]保安站岗 + +题意: +五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序 + +
                          +
                          + + 2022-05-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2016 战略游戏 题解 + + 洛谷P2016 战略游戏 题解 +
                          +
                          +
                          +
                          + + 洛谷P2016 战略游戏 题解 +题目链接:P2016 +战略游戏 + +题意: Bob +要建立一个古城堡,城堡中的路形成一棵无根树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。 +注意,某个士兵在一个结点上时,与该结点相连 + +
                          +
                          + + 2022-05-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1122 最大子树和 题解 + + 洛谷P1122 最大子树和 题解 +
                          +
                          +
                          +
                          + + 洛谷P1122 最大子树和 题解 +题目链接:P1122 +最大子树和 + +题意: +小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花 + +
                          +
                          + + 2022-05-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3205 [HNOI2010]合唱队 题解 + + 洛谷P3205 [HNOI2010]合唱队 题解 +
                          +
                          +
                          +
                          + + 洛谷P3205 [HNOI2010]合唱队 +题解 +题目链接:P3205 +[HNOI2010]合唱队 + +题意: +为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A +需要将合唱队的人根据他们的身高排出一个队形。假定合唱队 + +
                          +
                          + + 2022-05-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          16 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/17/index.html b/archives/2022/page/17/index.html index e69de29bb2..cd20783bb5 100644 --- a/archives/2022/page/17/index.html +++ b/archives/2022/page/17/index.html @@ -0,0 +1,1524 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + UVA1629 切蛋糕 Cake slicing 题解 + + UVA1629 切蛋糕 Cake slicing 题解 +
                          +
                          +
                          +
                          + + UVA1629 切蛋糕 Cake slicing +题解 +题目链接:UVA1629 +切蛋糕 Cake slicing + +题意:这个翻译够烂的,直接看pdf +翻译:有一个n行m列(1<=n,m<=20)的网络蛋糕上有k个樱桃。每次可 + +
                          +
                          + + 2022-05-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4170 [CQOI2007]涂色 题解 + + 洛谷P4170 [CQOI2007]涂色 题解 +
                          +
                          +
                          +
                          + + 洛谷P4170 [CQOI2007]涂色 题解 +题目链接:P4170 +[CQOI2007]涂色 + +题意: +假设你有一条长度为 \(5\) +的木板,初始时没有涂过任何颜色。你希望把它的 \(5\) +个单位长度分别涂上红、绿、蓝、绿、红色,用一 + +
                          +
                          + + 2022-05-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + AT3913 XOR Tree 题解 + + AT3913 XOR Tree 题解 +
                          +
                          +
                          +
                          + + AT3913 XOR Tree 题解 +题目链接:AT3913 +XOR Tree + +题意:给你一棵有 \(N\) 个节点的树,节点编号从 \(0\) 到 \(N-1\) , 树边编号从 \(1\) 到 \(N-1\) 。第 \(i\) 条边连 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF149D Coloring Brackets 题解 + + CF149D Coloring Brackets 题解 +
                          +
                          +
                          +
                          + + CF149D Coloring Brackets +题解 +题目链接:CF149D +Coloring Brackets + +题意:给出一个配对的括号序列(如 “\(\texttt{(())()}\)”、“\(\texttt{()}\)” 等,“\ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF19B Checkout Assistant 题解 + + CF19B Checkout Assistant 题解 +
                          +
                          +
                          +
                          + + CF19B Checkout Assistant +题解 +题目链接:CF19B +Checkout Assistant + +题意:Bob 来到一家现购自运商店,将 \(n\) +件商品放入了他的手推车,然后到收银台付款。每件商品由它的价格 \(c_ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF346B Lucky Common Subsequence 题解 + + CF346B Lucky Common Subsequence 题解 +
                          +
                          +
                          +
                          + + CF346B Lucky Common +Subsequence 题解 +题目链接:CF346B +Lucky Common Subsequence + +题意:通过删除一个字符串中的某些元素而不改变其余元素的顺序,可以派生出该字符串的一个子序列。 + + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF374C Inna and Dima 题解 + + CF374C Inna and Dima 题解 +
                          +
                          +
                          +
                          + + CF374C Inna and Dima 题解 +题目链接:CF374C +Inna and Dima + +题意: +Inna和Dima在商店买了一张 n * m +的桌子,桌子的每一个单元格上都有一个字符,字符集为("D","I","M","A") + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          17 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/18/index.html b/archives/2022/page/18/index.html index e69de29bb2..f7a7c61794 100644 --- a/archives/2022/page/18/index.html +++ b/archives/2022/page/18/index.html @@ -0,0 +1,1535 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF41D Pawn 题解 + + CF41D Pawn 题解 +
                          +
                          +
                          +
                          + + CF41D Pawn 题解 +题目链接:CF41D +Pawn + +题意:国际象棋棋盘最底行站了一个兵。 +它只有两种行动方式: 向上左或向上右走。 +它可以选择从最低行哪个节点开始他的旅程。 +每个格子上有0-9颗豌豆,而士兵想移动到最上一行并且积累 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF526B Om Nom and Dark Park 题解 + + CF526B Om Nom and Dark Park 题解 +
                          +
                          +
                          +
                          + + CF526B Om Nom and Dark Park +题解 +题目链接:CF526B Om +Nom and Dark Park + +题意:给定 \(2^{n+1}-1\) +个结点的满二叉树,求保证根到每一个叶子节点的路径权值和相等的情况下,增 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1108 低价购买 题解 + + 洛谷P1108 低价购买 题解 +
                          +
                          +
                          +
                          + + 洛谷P1108 低价购买 题解 +题目链接:P1108 +低价购买 + +题意:“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 +
                          +
                          +
                          +
                          + + 洛谷P1156 垃圾陷阱 +题解&浅谈刷表法与填表法 +填表法 +:就是一般的动态规划,当前点的状态,可以直接用状态方程,根据之前点的状态推导出来。 +刷表法:由当前点的状态,更新其他点的状态。需要注意:只用当每个状态所依赖的状态对它的影响 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1171 售货员的难题 题解 + + 洛谷P1171 售货员的难题 题解 +
                          +
                          +
                          +
                          + + 洛谷P1171 售货员的难题 题解 +题目链接:P1171 +售货员的难题 + +题意:TSP问题。 +某乡有\(n\)个村庄(\(1<n \le +20\)),有一个售货员,他要到各个村庄去售货,各村庄之间的路程\(s(0<s<10 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1282 多米诺骨牌 题解 + + 洛谷P1282 多米诺骨牌 题解 +
                          +
                          +
                          +
                          + + 洛谷P1282 多米诺骨牌 题解 +题目链接:P1282 +多米诺骨牌 + +题意: +多米诺骨牌由上下 \(2\) +个方块组成,每个方块中有 \(1\sim6\) +个点。现有排成行的上方块中点数之和记为 \(S_1\),下方块中点数之和记为 \(S + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1284 三角形牧场 题解 + + 洛谷P1284 三角形牧场 题解 +
                          +
                          +
                          +
                          + + 洛谷P1284 三角形牧场 题解 +题目链接:P1284 +三角形牧场 + +题意:和所有人一样,奶牛喜欢变化。它们正在设想新造型的牧场。奶牛建筑师 +Hei 想建造围有漂亮白色栅栏的三角形牧场。她拥有 \(n\) 块木板,每块的长度 \(l_i\) + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1772 [ZJOI2006]物流运输 题解 + + 洛谷P1772 [ZJOI2006]物流运输 题解 +
                          +
                          +
                          +
                          + + 洛谷P1772 [ZJOI2006]物流运输 +题解 +题目链接:P1772 +[ZJOI2006]物流运输 + +题意:物流公司要把一批货物从码头 A 运到码头 +B。由于货物量比较大,需要 \(n\) +天才能运完。货物运输过程中一般要转停好几个码头 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1858 多人背包 题解 + + 洛谷P1858 多人背包 题解 +
                          +
                          +
                          +
                          + + 洛谷P1858 多人背包 题解 +题目链接:P1858 +多人背包 + +题意:求01背包前k优解的价值和 + +建议先去读一读《背包九讲》再来看 +注意到朴素的 \(01\) 背包是这样转移的 +\[ +dp[j]=\max(dp[j],dp[j-w[i] + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1944 最长括号匹配 题解 + + 洛谷P1944 最长括号匹配 题解 +
                          +
                          +
                          +
                          + + 洛谷P1944 最长括号匹配 题解 + +题意:对一个由(,),[,]括号组成的字符串,求出其中最长的括号匹配子串。具体来说,满足如下条件的字符串成为括号匹配的字符串: +1.(),[]是括号匹配的字符串。 +2.若A是括号匹配的串,则(A),[A + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2158 [SDOI2008] 仪仗队 题解 + + 洛谷P2158 [SDOI2008] 仪仗队 题解 +
                          +
                          +
                          +
                          + + 洛谷P2158 [SDOI2008] 仪仗队 +题解 +题目链接:P2158 +[SDOI2008] 仪仗队 + +题意:作为体育委员,C +君负责这次运动会仪仗队的训练。仪仗队是由学生组成的 \(N \times N\) +的方阵,为了保证队伍在行进中 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          18 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/19/index.html b/archives/2022/page/19/index.html index e69de29bb2..f2b6fe7aab 100644 --- a/archives/2022/page/19/index.html +++ b/archives/2022/page/19/index.html @@ -0,0 +1,1535 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2170 选学霸 题解 + + 洛谷P2170 选学霸 题解 +
                          +
                          +
                          +
                          + + 洛谷P2170 选学霸 题解 +题目链接:P2170 +选学霸 + +题意:老师想从 \(n\) 名学生中选 \(m\) 人当学霸,但有 \(k\) +人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2224 [HNOI2001]产品加工 题解 + + 洛谷P2224 [HNOI2001]产品加工 题解 +
                          +
                          +
                          +
                          + + 洛谷P2224 [HNOI2001]产品加工 +题解 +题目链接:P2224 +[HNOI2001]产品加工 + +题意: +某加工厂有 A、B +两台机器,来加工的产品可以由其中任何一台机器完成,或者两台机器共同完成。由于受到机器性能和产品特性的限制, + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2257 YY的GCD 题解 + + 洛谷P2257 YY的GCD 题解 +
                          +
                          +
                          +
                          + + 洛谷P2257 YY的GCD 题解 + +题意: +多组询问。 +给定 \(N, M\),求 \(1 \leq x \leq N\),\(1 \leq y \leq M\) 且 \(\gcd(x,y)\) 为质数的 \((x,y)\) 有多少对。 + + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2568 GCD 题解 + + 洛谷P2568 GCD 题解 +
                          +
                          +
                          +
                          + + 洛谷P2568 GCD 题解 +题目链接:P2568 +GCD + +题意: +给定正整数 \(n\),求 \(1\le x,y\le n\) 且 \(\gcd(x,y)\)为素数的数对 \((x,y)\) 有多少对。 +\(1 \le n\le 10 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          19 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/2/index.html b/archives/2022/page/2/index.html index e69de29bb2..6ccc37f856 100644 --- a/archives/2022/page/2/index.html +++ b/archives/2022/page/2/index.html @@ -0,0 +1,1549 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 19 +
                          + +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + BZOJ2141 排队 题解 + + BZOJ2141 排队 题解 +
                          +
                          +
                          +
                          + + BZOJ2141 排队 题解 +题目链接:#2141. +排队 + +题意: +一句话题意: +给定 \(n\) 个数,\(Q\) +次询问,每次交换两个不同位置的数,然后输出逆序对数。 +\(1 \le n \le 2\times 10^4,~1\le + +
                          +
                          + + 2022-08-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 小蓝书 16.1 + + 小蓝书 16.1 +
                          +
                          +
                          +
                          + + 16.1 +施工中,咕咕咕.... +计数原理 +加法原理 +设完成某件事有 \(m\) +类不同的方法, +第 \(i(1\le i \le m)\) 类方法中有 +\(n_i\) 种不同的方法,则完成这件事共有 +\[ +n_1 + n_2 + \cdo + +
                          +
                          + + 2022-08-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + + CF1491F Magnets 题解 + + CF1491F Magnets 题解 +
                          +
                          +
                          +
                          + + CF1491F Magnets 题解 +题目链接:CF1491F +Magnets + +题意: +这是一个交互题。 +早苗有 \(n\) 块磁石,编号为 \(1,2,\cdots,n\)。每块磁石的磁极可能是正极,负极,也可能没有磁性。她希望你能帮她 + +
                          +
                          + + 2022-08-15 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2396 yyy loves Maths VII 题解 + + 洛谷P2396 yyy loves Maths VII 题解 +
                          +
                          +
                          +
                          + + 洛谷P2396 yyy loves Maths +VII 题解 +题目链接:P2396 yyy +loves Maths VII + +题意: +一群同学在和 yyy 玩一个游戏。 +每次,他们会给 yyy \(n\) +张卡片,卡片上有数字,所有的数字都 + +
                          +
                          + + 2022-08-15 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + CF1073E Segment Sum 题解 + + CF1073E Segment Sum 题解 +
                          +
                          +
                          +
                          + + CF1073E Segment Sum 题解 +题目链接:CF1073E +Segment Sum + +题意: +给定 \(l,r,k\) ,求 \([l,r]\) 有多少个数满足「不包含超过 \(k\) 个数码」,输出他们的和 \(\bmod { + +
                          +
                          + + 2022-08-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + UOJ66 新年的巧克力棒 题解 + + UOJ66 新年的巧克力棒 题解 +
                          +
                          +
                          +
                          + + UOJ66 新年的巧克力棒 题解 +题目链接:#66. +新年的巧克力棒 + +题意:马上就要到羊年了,羊村一片欢腾,懒羊羊则懒洋洋地躺在草坪上吃新年的巧克力棒。 +他手上的巧克力棒是个由 \(n\) +个巧克力单元格组成的长度为 \(n\) +的长条, + +
                          +
                          + + 2022-08-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2292 [HNOI2004] L 语言 题解 + + 洛谷P2292 [HNOI2004] L 语言 题解 +
                          +
                          +
                          +
                          + + 洛谷P2292 [HNOI2004] L 语言 +题解 +题目链接:P2292 +[HNOI2004] L 语言 + +题意: +标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的。现在你要处理的就是一段没有标点的文章。 +一段文章 \(T\) + + +
                          +
                          + + 2022-08-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1305 新二叉树 题解 + + 洛谷P1305 新二叉树 题解 +
                          +
                          +
                          +
                          + + 洛谷P1305 新二叉树 题解 +题目链接:P1305 +新二叉树 + +题意: +输入一串二叉树,输出其前序遍历。 +输入1: +6 +abc +bdi +cj* +d** +i** +j** +输出1: +abdicj + +虽然是大水题,但是有个解法还是很妙的 +利用前 + +
                          +
                          + + 2022-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1229 遍历问题 题解 + + 洛谷P1229 遍历问题 题解 +
                          +
                          +
                          +
                          + + 洛谷P1229 遍历问题 题解 +题目链接:P1229 +遍历问题 + +题意: +我们都很熟悉二叉树的前序、中序、后序遍历,在数据结构中常提出这样的问题:已知一棵二叉树的前序和中序遍历,求它的后序遍历,相应的,已知一棵二叉树的后序遍历和中序遍历序列 + +
                          +
                          + + 2022-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          2 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/20/index.html b/archives/2022/page/20/index.html index e69de29bb2..357d6d2e49 100644 --- a/archives/2022/page/20/index.html +++ b/archives/2022/page/20/index.html @@ -0,0 +1,1548 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3188 [HNOI2007]梦幻岛宝珠 题解 + + 洛谷P3188 [HNOI2007]梦幻岛宝珠 题解 +
                          +
                          +
                          +
                          + + 洛谷P3188 +[HNOI2007]梦幻岛宝珠 题解 +题目链接:P3188 +[HNOI2007]梦幻岛宝珠 + +题意: +给你 \(n\) +颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过 +\(W\),且总价值最大 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3297 [SDOI2013] 逃考 题解 + + 洛谷P3297 [SDOI2013] 逃考 题解 +
                          +
                          +
                          +
                          + + 洛谷P3297 [SDOI2013] 逃考 +题解 +题目链接:P3297 +[SDOI2013] 逃考 + +题意:髙考又来了,对于不认真读书的小杨来讲,真不是个好消息。为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶、外 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3336 [ZJOI2013]话旧 题解 + + 洛谷P3336 [ZJOI2013]话旧 题解 +
                          +
                          +
                          +
                          + + 洛谷P3336 [ZJOI2013]话旧 题解 +题目链接:P3336 +[ZJOI2013]话旧 + +题意:小林跟着银河队选手去了一趟宇宙比赛,耳濡目染,变得学术起来。回来后,他发现世界大变样了。比丘兽究级进化,成了凤凰兽;金先生因为发了一篇 + + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3985 不开心的金明 题解 + + 洛谷P3985 不开心的金明 题解 +
                          +
                          +
                          +
                          + + 洛谷P3985 不开心的金明 题解 +题目链接:P3985 +不开心的金明 + +题意: +金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4141 消失之物 题解 + + 洛谷P4141 消失之物 题解 +
                          +
                          +
                          +
                          + + 洛谷P4141 消失之物 题解 +题目链接:P4141 +消失之物 + +题意:ftiasch 有 \(n\) 个物品, 体积分别是 \(w_1,w_2,\dots,w_n\) 。由于她的疏忽,第 +\(i\) 个物品丢失了。 +“要使用剩下的 \(n + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          20 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/21/index.html b/archives/2022/page/21/index.html index e69de29bb2..893c5bef21 100644 --- a/archives/2022/page/21/index.html +++ b/archives/2022/page/21/index.html @@ -0,0 +1,1537 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4158 [SCOI2009]粉刷匠 题解 + + 洛谷P4158 [SCOI2009]粉刷匠 题解 +
                          +
                          +
                          +
                          + + 洛谷P4158 [SCOI2009]粉刷匠 +题解 +题目链接:P4158 +[SCOI2009]粉刷匠 + +题意:windy有 N 条木板需要被粉刷。 每条木板被分为 M +个格子。 每个格子要被刷成红色或蓝色。 +windy每次粉刷,只能选择一条木 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4310 绝世好题 题解 + + 洛谷P4310 绝世好题 题解 +
                          +
                          +
                          +
                          + + 洛谷P4310 绝世好题 题解 +题目链接:P4310 +绝世好题 + +题意:给定一个长度为 \(n\) 的数列 \(a_i\) ,求 \(a_i\) 的子序列 \(b_i\) 的最长长度 \(k\),满足 \(b_i +\& b_{i-1 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4342 [IOI1998]Polygon 题解 + + 洛谷P4342 [IOI1998]Polygon 题解 +
                          +
                          +
                          +
                          + + 洛谷P4342 [IOI1998]Polygon +题解 +题目链接:P4342 +[IOI1998]Polygon + +题意:多边形是一个玩家在一个有 \(n\) 个顶点的多边形上的游戏,如图所示,其中 +\(n=4\) +。每个顶点用整数标记,每个 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 + + 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 +
                          +
                          +
                          +
                          + + 洛谷P4390 [BOI2007]Mokia +摩基亚 题解 +题目链接:P4390 +[BOI2007]Mokia 摩基亚 + +题意:摩尔瓦多的移动电话公司摩基亚(Mokia)设计出了一种新的用户定位系统。和其他的定位系统一样,它能够迅速回答任何 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4395 [BOI2003]Gem 气垫车 题解 + + 洛谷P4395 [BOI2003]Gem 气垫车 题解 +
                          +
                          +
                          +
                          + + 洛谷P4395 [BOI2003]Gem 气垫车 +题解 +题目链接:P4395 +[BOI2003]Gem 气垫车 + +题意:给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数 +唯一的限制条件是相临的两个结点不能标上相同的权值,要求一 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5110 块速递推 题解 + + 洛谷P5110 块速递推 题解 +
                          +
                          +
                          +
                          + + 洛谷P5110 块速递推 题解 +题目链接:P5110 +块速递推 + +题意:给定一个数列 \(a\) 满足递推式 \[ +a_0=0,a_1=1 +\\a_n = 233a_{n-1}+666a_{n-2} +\] 求 \(a_n \bmod (10 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5322 [BJOI2019] 排兵布阵 题解 + + 洛谷P5322 [BJOI2019] 排兵布阵 题解 +
                          +
                          +
                          +
                          + + 洛谷P5322 [BJOI2019] 排兵布阵 +题解 + +题意:小 C 正在玩一款排兵布阵的游戏。在游戏中有 +\(n\) +座城堡,每局对战由两名玩家来争夺这些城堡。每名玩家有 \(m\) 名士兵,可以向第 \(i\) 座城堡派遣 \(a_i\ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5365 [SNOI2017] 英雄联盟 题解 + + 洛谷P5365 [SNOI2017] 英雄联盟 题解 +
                          +
                          +
                          +
                          + + 洛谷P5365 [SNOI2017] 英雄联盟 +题解 +题目链接:P5365 +[SNOI2017] 英雄联盟 + +题意:正在上大学的小皮球热爱英雄联盟这款游戏,而且打的很菜,被网友们戏称为「小学生」。 +现在,小皮球终于受不了网友们的嘲讽,决定变 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          21 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/22/index.html b/archives/2022/page/22/index.html index e69de29bb2..3d0298c958 100644 --- a/archives/2022/page/22/index.html +++ b/archives/2022/page/22/index.html @@ -0,0 +1,1548 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P6216 回文匹配 题解 + + 洛谷P6216 回文匹配 题解 +
                          +
                          +
                          +
                          + + 洛谷P6216 回文匹配 题解 +题目链接:P6216 +回文匹配 + +题意:对于一对字符串 \((s_1,s_2)\),若 \(s_1\) 的长度为奇数的子串 +\((l,r)\) 满足 \((l,r)\) 是回文的,那么 \(s_1\) 的“分 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 等差数列&等比数列小结 + + 等差数列&等比数列小结 +
                          +
                          +
                          +
                          + + 等差数列&等比数列小结 +高一自学的时候瞎总结写的(好吧我现在还是高一 +2022.5.7) +感觉丢在文件夹里吃灰没啥用,就放上来了 +欢迎各位指出我的错误(我数学真的烂 \(😓\) + +等差数列 +等差数列通项公式 +\[ +a_n = a_ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 线段树空间开4倍的原因 + + 线段树空间开4倍的原因 +
                          +
                          +
                          +
                          + + 线段树空间开4倍的原因 +如果证明有错欢迎指出。 +对于长为 \(n\) +的序列,显然以其构建的线段树有 \(n\) +个叶子节点 +此时线段树的高度为 \(k=\left\lceil{\log_2 +n}\right\rceil+1\) (第一层的 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 主定理 + + 主定理 +
                          +
                          +
                          +
                          + + 主定理 +证明先不写 +将一个规模为 \(n\) +的问题,通过分治得到 \(a\) 个规模为 +\(n/b\) +的子问题,每个递归带来的额外计算为 \(f(n)\) ,则有 +\(T(n)=aT(n/b)+f(n)\) +其中 \(a,b\) 为常数 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 04 +
                          + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4159 [SCOI2009] 迷路 题解 + + 洛谷P4159 [SCOI2009] 迷路 题解 +
                          +
                          +
                          +
                          + + 洛谷P4159 [SCOI2009] 迷路 +题解 +题目链接:P4159 +[SCOI2009] 迷路 + +题意:该有向图有 \(n\) 个节点,节点从 \(1\) 至 \(n\) 编号,windy 从节点 \(1\) 出发,他必须恰好在 \(t + +
                          +
                          + + 2022-04-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3829 [SHOI2012]信用卡凸包 题解 + + 洛谷P3829 [SHOI2012]信用卡凸包 题解 +
                          +
                          +
                          +
                          + + 洛谷P3829 +[SHOI2012]信用卡凸包 题解 +题目链接:P3829 +[SHOI2012]信用卡凸包 + +题意: 给定若干个“信用卡”,求其“凸包”周长 + + +这个题其实看上去很不可做,其实很简单 +注意到(搬了一张图,来自link,不过这 + +
                          +
                          + + 2022-04-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 23 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          22 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/23/index.html b/archives/2022/page/23/index.html index e69de29bb2..5caa899a21 100644 --- a/archives/2022/page/23/index.html +++ b/archives/2022/page/23/index.html @@ -0,0 +1,1549 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 04 +
                          + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 19 +
                          + +
                          + +
                          + + + + + +
                          + 17 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + Dijkstra及其复杂度证明 + + Dijkstra及其复杂度证明 +
                          +
                          +
                          +
                          + + Dijkstra及其复杂度证明 +前言 +本文主要围绕易混淆的复杂度分析进行讨论 + +Dijkstra +其实这个不叫迪杰斯特拉,这个叫/ˈdɛɪkstra/ qwq +一、小概念 +先放几个简单概念 +无向图:图中所有的边都是两端可达的,也就是可以从任 + +
                          +
                          + + 2022-03-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 最小树形图 Tarjan的DMST算法 + + 最小树形图 Tarjan的DMST算法 +
                          +
                          +
                          +
                          + + 最小树形图 Tarjan的DMST算法 +前言 +网上怎么都是朱刘算法啊? +那我来写一篇 Tarjan 的 DMST 算法吧 +qwq +注:本文的DMST采用左偏树+并查集实现 +时间复杂度为 \(O(E+V\log E)\) +如果采用斐波那契堆则 + +
                          +
                          + + 2022-03-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5826 【模板】子序列自动机 + + 洛谷P5826 【模板】子序列自动机 +
                          +
                          +
                          +
                          + + 洛谷P5826 【模板】子序列自动机 +题目链接:P5826 +【模板】子序列自动机 + +题意:给定一个主序列,每次给出一个序列,判断是否为其子序列 + +我们可以把每个字符的出现位置用vector维护 +然后对于每个询问,直接二分离当前位置最近的那个 + +
                          +
                          + + 2022-03-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3919 【模板】可持久化线段树 1(可持久化数组) 题解 + + 洛谷P3919 【模板】可持久化线段树 1(可持久化数组) 题解 +
                          +
                          +
                          +
                          + + 洛谷P3919 +【模板】可持久化线段树 1(可持久化数组) 题解 +题目链接:P3919 +【模板】可持久化线段树 1(可持久化数组) + +题意:如题,你需要维护这样的一个长度为 NN +的数组,支持如下几种操作 + +在某个历史版本上修改某一个位置上 + +
                          +
                          + + 2022-03-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1129 [ZJOI2007] 矩阵游戏 题解 + + 洛谷P1129 [ZJOI2007] 矩阵游戏 题解 +
                          +
                          +
                          +
                          + + 洛谷P1129 [ZJOI2007] 矩阵游戏 +题解 +题目链接:P1129 +[ZJOI2007] 矩阵游戏 + +题意:给定一张有黑白棋子的正方形棋盘,问存不存在解法使得经过若干次交换行或列的操作后,左上角至右下角的对角线上所有的点放着黑色棋子 + +
                          +
                          + + 2022-03-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + kd-tree(KDT) 时间复杂度证明 + + kd-tree(KDT) 时间复杂度证明 +
                          +
                          +
                          +
                          + + kd-tree(KDT) 时间复杂度证明 +kd-tree 是一种可以高效处理 \(k\) +维空间的数据结构 +在算法竞赛类的题目中一般有 \(k=2\) +还有个比较有趣的结论,当 \(k=1\) +时其实它就是一棵线段树 +下文中的 \(n\) + + +
                          +
                          + + 2022-03-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + + + + + +
                          + 01 +
                          +
                          +
                          +
                          + +
                          + + + CF1200E Compress Words 题解 + + CF1200E Compress Words 题解 +
                          +
                          +
                          +
                          + + CF1200E Compress Words 题解 +题目链接:CF1200E +Compress Words + +题意:给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀 +与 待插入串的前缀的最大匹配串 + +解法一 KMP +这个解法常数比较 + +
                          +
                          + + 2022-03-01 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          23 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/24/index.html b/archives/2022/page/24/index.html index e69de29bb2..d561764baf 100644 --- a/archives/2022/page/24/index.html +++ b/archives/2022/page/24/index.html @@ -0,0 +1,1550 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 02 +
                          + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4234 最小差值生成树 题解 + + 洛谷P4234 最小差值生成树 题解 +
                          +
                          +
                          +
                          + + 洛谷P4234 最小差值生成树 +题解 +题目链接:P4234 +最小差值生成树 + +题意:给定一个点标号从 \(1\) 到 \(n\) 的、有 \(m\) +条边的无向图,求边权最大值与最小值的差值最小的生成树,图可能存在自环 + +这个题不太好利用k + +
                          +
                          + + 2022-02-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + LCT求解最小生成树 + + LCT求解最小生成树 +
                          +
                          +
                          +
                          + + LCT求解最小生成树 +前言 +最小生成树模板: P3366 +【模板】最小生成树 +朴素的kruskal为主流最小生成树算法 +而LCT(link cut tree)也是可以维护最小生成树的 +由于LCT动态维护最小生成树,加上常数较大 +在实际测试中 + +
                          +
                          + + 2022-02-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2147 [SDOI2008] 洞穴勘测 题解 + + 洛谷P2147 [SDOI2008] 洞穴勘测 题解 +
                          +
                          +
                          +
                          + + 洛谷P2147 [SDOI2008] 洞穴勘测 +题解 +题目链接:P2147 +[SDOI2008] 洞穴勘测 + +题意:给定若干个点,动态连接(无向边),询问连通性 + +由于它有删边的操作,因此用并查集并不可行 +于是想到LCT(? +由于LCT有 + +
                          +
                          + + 2022-02-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 24 +
                          + +
                          + +
                          + + + + + +
                          + 13 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈快速乘 + + 浅谈快速乘 +
                          +
                          +
                          +
                          + + 浅谈快速乘 +前言 +想必大家都听说过快速幂 +那快速乘是个什么东西呢? +考虑取模操作a*b%p,1^10 ≤ a,b,p ≤ 2^10 +可以发现在 long long情况下,我们直接取模会溢出 +那么怎么办呢? +题目链接:https://www. + +
                          +
                          + + 2022-02-13 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          +
                          +
                          +
                          + +
                          + + + AT4284 & 洛谷 P1969 P3078 P5019 题解 + + AT4284 & 洛谷 P1969 P3078 P5019 题解 +
                          +
                          +
                          +
                          + + AT4284 & 洛谷 P1969 +P3078 P5019 题解 +题目链接:AT4284 P1969 P3078 P5019 + +题意:若干次区间减一,使所有数相等,求最小次数 + +这几道题就是一个std编出来的吧 \(😅\) +对于相 + +
                          +
                          + + 2022-02-07 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1234 小A的口头禅 题解 + + 洛谷P1234 小A的口头禅 题解 +
                          +
                          +
                          +
                          + + 洛谷P1234 小A的口头禅 题解 +题目链接:P1234 +小A的口头禅 + +给出了一个矩形,让你求出里面有几个hehe(方向无所谓,斜着不算) + +数据范围很良心,嗯~ +所以暴力枚举即可 +值得注意的是 \(\tt{eheh}\) +这种也算 +顺便 + +
                          +
                          + + 2022-02-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2804 神秘数字 题解 + + 洛谷P2804 神秘数字 题解 +
                          +
                          +
                          +
                          + + 洛谷P2804 神秘数字 题解 +题目链接:P2804 +神秘数字 + +题意:询问有多少段连续区间的平均值大于 \(m\) + +可以发现将每个数都减去 \(m\) +后任意和大于 \(0\) +的连续区间都满足题意 +区间和可以用前缀和优化,记为 \(s + +
                          +
                          + + 2022-02-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5764 [CQOI2005]新年好 题解 + + 洛谷P5764 [CQOI2005]新年好 题解 +
                          +
                          +
                          +
                          + + 洛谷P5764 [CQOI2005]新年好 +题解 +题目链接:P5764 +[CQOI2005]新年好 + +题意:从 \(1\) +号结点出发,要访问其他 \(5\) +个结点,顺序随意,访问一个结点后不用返回 + +注意到 \(5! = O(1)\) + + +
                          +
                          + + 2022-02-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1462 通往奥格瑞玛的道路 题解 + + 洛谷P1462 通往奥格瑞玛的道路 题解 +
                          +
                          +
                          +
                          + + 洛谷P1462 通往奥格瑞玛的道路 +题解 +题目链接:P1462 +通往奥格瑞玛的道路 + +题意:在艾泽拉斯,有 \(n\) 个城市。编号为 \(1,2,3,\ldots,n\) 。 +城市之间有 \(m\) +条双向的公路,连接着两个城市,从某个城 + +
                          +
                          + + 2022-02-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          24 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/25/index.html b/archives/2022/page/25/index.html index e69de29bb2..8bf6959064 100644 --- a/archives/2022/page/25/index.html +++ b/archives/2022/page/25/index.html @@ -0,0 +1,1349 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 01 +
                          + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + RMB找零问题 + + RMB找零问题 +
                          +
                          +
                          +
                          + + RMB找零问题 +来自某次研究性学习的作业 +前言 +可以先考虑这样的问题 + +给定 \(n\) +种足量多的纸币,每种面额为 \(a_i\) 元 +\((0<a[i]≤10000,a[i]\in +\Z,1≤i≤n≤100)\) +给出需要找零的金 + +
                          +
                          + + 2022-01-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          25 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/3/index.html b/archives/2022/page/3/index.html index e69de29bb2..559bd377ee 100644 --- a/archives/2022/page/3/index.html +++ b/archives/2022/page/3/index.html @@ -0,0 +1,1535 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[20] + + 模拟赛题讲解[20] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[20] +来自 AprilGrimoire +2022-08-09 noi.ac #2775 +题目描述: +小 Z 想出几道送分题。 +小 Z 有一列脑洞(共 \(m(m \leq +10^9)\) +个)。脑洞分为白色与黑色,白色脑洞会 + +
                          +
                          + + 2022-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[19] + + 模拟赛题讲解[19] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[19] +来自 yukuai26 +2022-08-08 noi.ac #2771 +题目背景: +\(\text{yukuai26}\) +喜欢爬山,所以他选择绕着山跑圈 +题目描述: +\(\text{yukuai26}\) +会跑一个环 + +
                          +
                          + + 2022-08-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[18] + + 模拟赛题讲解[18] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[18] +来自 yukuai26 +2022-08-08 noi.ac #2773 +题目背景: +搞个大新闻. jpg +题目描述: +你来到了幻想乡。首先你准备搞一个大新闻,获得文文的报道并变得出名,你决定对幻想乡的股票系统下手。 +你 + +
                          +
                          + + 2022-08-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          + +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[17] + + 模拟赛题讲解[17] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[17] +来自 yukuai26 +2022-08-08 noi.ac #2772 +题目描述: +\(\text{ysgh}\) 有一个长度为 \(m\) 的,元素两两不同的序列。 +\(\text{emoairx}\) +想要知道这个 + +
                          +
                          + + 2022-08-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[16] + + 模拟赛题讲解[16] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[16] +来自 yukuai26 +2022-08-07 noi.ac #2764 +题目描述: +紫开始研究特殊的括号序列 +这些特殊括号序列的形式是若干个右括号(可以没有)在前,若干个左括号(可以没有)在后。 +比如 ((,))),) + +
                          +
                          + + 2022-08-07 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          + +
                          + +
                          + + + + + +
                          + 07 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[15] + + 模拟赛题讲解[15] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[15] +来自 yukuai26 +2022-08-07 noi.ac +#2763 +题目描述: +幻想乡有 \(n\) +个建筑,每个建筑里住了一些居民。 +有一些双向道路连接居民的家,共有 \(n-1\) +条道路,恰好让所有居民的家能 + +
                          +
                          + + 2022-08-07 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[14] + + 模拟赛题讲解[14] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[14] +来自 yukuai26 +2022-08-06 noi.ac #2755 +题目背景 +\(\text{yukuai26}\) +喜欢算术,但他又菜又爱玩,所以需要你的帮助 +题目描述: +小明给你 \(n\) +个正整数,他想取一 + +
                          +
                          + + 2022-08-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[13] + + 模拟赛题讲解[13] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[13] +来自 yukuai26 +2022-08-06 noi.ac #2757 +据说是从JOI搬过来的,赛时有位巨佬18min就A了 Orz +题目描述: +橙 ---- 作为幽幽子大人的朋友的使者, 非常贪玩, 也喜欢思考有趣 + + +
                          +
                          + + 2022-08-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[12] + + 模拟赛题讲解[12] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[12] +来自 yukuai26 +2022-08-06 noi.ac #2756 +题目描述: +曾经有一个 oj 叫做 bzoj, 里面有一题 bzoj1002 叫狼抓兔子 +由于 1002 过于有名且显眼,很多人 A 掉了他,每 + +
                          +
                          + + 2022-08-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 04 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[11] + + 模拟赛题讲解[11] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[11] +来自 xpp 2022-08-04 +noi.ac #2731 +题目描述: +xpp在玩祖玛游戏,他心血来潮想根据祖玛游戏出一个题。 +给一个序列 \(a_1,a_2,\dots,a_n\) +,你每次可以选择相同且相邻的三个 + +
                          +
                          + + 2022-08-04 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          3 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/4/index.html b/archives/2022/page/4/index.html index e69de29bb2..a1723647b0 100644 --- a/archives/2022/page/4/index.html +++ b/archives/2022/page/4/index.html @@ -0,0 +1,1536 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[10] + + 模拟赛题讲解[10] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[10] +来自 xpp 2022-08-03 +noi.ac #2723 +题目描述 +lzr给了xpp一个字符串 \(s\) +和一个字符串 \(t\) ,xpp想通过 \(s\) 构造出 \(t\) ,构造方式为xpp每次选择 \( + +
                          +
                          + + 2022-08-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          + +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + 初赛复习 + + 初赛复习 +
                          +
                          +
                          +
                          + + 初赛复习 +施工中...... +参考文献等等补上来。 +IT发展历史 +第一台计算机:ENIAC,1946年 +应用:计算,数据储存处理,通信,辅助工作等 +第一个程序员:Ada(女),有为此命名的程序语言 +图灵奖(计算机),菲尔兹奖(数学),诺贝尔 + +
                          +
                          + + 2022-08-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          + + +
                          + 31 +
                          + +
                          + +
                          + + + + + +
                          + 31 +
                          +
                          +
                          +
                          + +
                          + + + CF691E Xor-sequences 题解 + + CF691E Xor-sequences 题解 +
                          +
                          +
                          +
                          + + CF691E Xor-sequences 题解 +题目链接:CF691E +Xor-sequences + +题意:给定大小为 \(n\) 的集合 \(\{a_1,\dots,a_n\}\),从集合中选择 \(k\) 个数组成一个序列 \(x_1 + +
                          +
                          + + 2022-07-31 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 线性代数-矩阵 + + 线性代数-矩阵 +
                          +
                          +
                          +
                          + + 线性代数-矩阵 +施工中,咕咕咕.... +矩阵乘法 +矩阵乘法的定义 +矩阵乘法的定义 \[ +(AB)_{i,j}=\sum_{k=1}^{m}A_{i,k}B_{k,j}\quad i\in[1,n],j\in[1,p] +\] 一个 \(n \ + +
                          +
                          + + 2022-07-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + ABC156E Roaming 题解 + + ABC156E Roaming 题解 +
                          +
                          +
                          +
                          + + ABC156E Roaming 题解 +题目链接:ABC156E +Roaming + +题意: +翻译来自我们模拟赛,可能和原题有区别 +输入两个数字 \(n,k\) ,初始时有 +\(n\) +个盒子,每个盒子中都装着一个小球,\(n\) +个小球之间不 + +
                          +
                          + + 2022-07-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + ABC167E Colorful Blocks 题解 + + ABC167E Colorful Blocks 题解 +
                          +
                          +
                          +
                          + + ABC167E Colorful Blocks 题解 +题目链接:ABC167E +Colorful Blocks + +题意: +翻译从我们模拟赛搬过来的,稍微有些不一样。 +有 \(n\) 个小球按照编号为 \(1−n\) +从左至右放成一排,你现在 + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[9] + + 模拟赛题讲解[9] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[9] +来自 Roundgod +2022-07-29 noi.ac #2693 +原题来自 ABC172E +NEQ +问题描述: +给定 \(n,m\),你需要计算满足以下条件的数组对 \(A=[a_1,a_2,\dots,a_n]\ + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + hdu4135 Co-prime 题解 + + hdu4135 Co-prime 题解 +
                          +
                          +
                          +
                          + + hdu4135 Co-prime 题解 +题目链接:hdu4135 +Co-prime + +题意: +\(T\) 组数据,每组给出 \(a,b,n\) ,求区间 \([a,b]\) 中有多少个数与 \(n\) 互质。 +Given a number + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          4 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/5/index.html b/archives/2022/page/5/index.html index e69de29bb2..8f97364a4c 100644 --- a/archives/2022/page/5/index.html +++ b/archives/2022/page/5/index.html @@ -0,0 +1,1521 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-组合数学 + + OI数学总结-组合数学 +
                          +
                          +
                          +
                          + + OI数学总结-组合数学 +施工中,咕咕咕.... +排列组合 +更多详见 小蓝书 +16.1 +下面两个指的是无重复的排列与组合 +排列数 +从 \(n\) 个不同元素中取 \(k(k\le n)\) +个不同元素,并按一定顺序排成一列的方案数 \[ +\m + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + ABC210E Ring MST 题解 + + ABC210E Ring MST 题解 +
                          +
                          +
                          +
                          + + ABC210E Ring MST 题解 +题目链接:ABC210E Ring +MST + +题意:给定一张 \(n\) 个点的无向图,顶点的编号为 \(0,1,\dots,n−1\) 。同时给出两个长度为 \(m\) 的数组 \(a_1,a_2, + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + ARC060B Digit Sum 题解 + + ARC060B Digit Sum 题解 +
                          +
                          +
                          +
                          + + ARC060B Digit Sum 题解 +题目链接:ARC060B Digit +Sum + +题意: +对于任意非负整数 \(x\) 和 \(m(2\le m)\) ,定义 \(f_m(x)\) 为 \(x\) 在 \(m\) 进制下的各位数字之 + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 数学常数表 + + 数学常数表 +
                          +
                          +
                          +
                          + + 一些闲着无聊可以瞎背背的常数,比如$e = \tt{2.718281828}$,$\pi = \tt{3.141592654}$,$\ln \pi = \tt{1.1447}$,$\sqrt{\pi} = \tt{1.7725}$等等... + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + CF906D Power Tower 题解 + + CF906D Power Tower 题解 +
                          +
                          +
                          +
                          + + CF906D Power Tower 题解 +题目链接:CF906D +Power Tower + +题意:给定长度为 \(n\) 的序列 \(a_i\) 和模数 \(p\) +\(Q\) 次询问区间 \([l,r]\) 的 \[ +a_l^{ {a_ + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + CF427C Checkposts 题解 + + CF427C Checkposts 题解 +
                          +
                          +
                          +
                          + + CF427C Checkposts 题解 +题目链接:CF427C +Checkposts + +题意: +懒得贴翻译,那个翻译太烂了 +Your city has $ n $ junctions. There are $ m $ one-way ro + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2424 约数和 题解 + + 洛谷P2424 约数和 题解 +
                          +
                          +
                          +
                          + + 洛谷P2424 约数和 题解 +题目链接:P2424 +约数和 + +题意: +对于一个数 \(X\),函数 \(f(X)\) 表示 \(X\) 所有约数的和。例如:\(f(6)=1+2+3+6=12\)。对于一个 \(X\),Smart 可以很快的 + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[8] + + 模拟赛题讲解[8] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[8] +来自 Roundgod +2022-07-27 noi.ac #2681 +题目描述: +给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\), 边数 +\(\vert E\vert=m\ + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          5 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/6/index.html b/archives/2022/page/6/index.html index e69de29bb2..ed97d09bb0 100644 --- a/archives/2022/page/6/index.html +++ b/archives/2022/page/6/index.html @@ -0,0 +1,1529 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[7] + + 模拟赛题讲解[7] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[7] +来自 Roundgod +2022-07-27 noi.ac #2682 +题目描述: +给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\) +,边数 \(\vert E\vert=m\ + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[6] + + 模拟赛题讲解[6] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[6] +来自 Roundgod +2022-07-27 noi.ac #2683 +题目描述: +给定一个连通无向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\) , +边数 \(\vert E\vert + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[5] + + 模拟赛题讲解[5] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[5] +来自 Roundgod +2022-07-26 noi.ac #2678 +题目描述: +对于任意非负整数 \(x\) 和 \(m(2\le m\le 10)\) ,定义 \(f_m(x)\) 为 \(x\) 在 \(m\) + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[4] + + 模拟赛题讲解[4] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[4] +来自 Roundgod +2022-07-26 noi.ac #2680 +问题描述: +Berland由 \(n\) 个城市和 \(m\) 条双向道路构成,其中第 \(i\) 条道路连接城市 \(a_i\) 和 \(b_i\ + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[3] + + 模拟赛题讲解[3] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[3] +来自 Roundgod +2022-07-26 noi.ac #2679 +题目描述: +给定一个无向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数 \(|E|=m\) 。顶点编号为 \(1−N\), + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + OI tricks + + OI tricks +
                          +
                          +
                          +
                          + + OI tricks +平时做题的时候发现的一些技巧,还没有仔细整理 +因此本文比较像草稿般的个人总结 +1.终极快读 +namespace FastIO +{ + #define gc() readchar() + #define pc(a) + +
                          +
                          + + 2022-07-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[2] + + 模拟赛题讲解[2] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[2] +来自 Roundgod +2022-07-25 noi.ac #2677 +题目描述: +给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数 \(|E|=m\) 。顶点编号为 \(1−N\) , + +
                          +
                          + + 2022-07-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[1] + + 模拟赛题讲解[1] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[1] +来自 Roundgod +2022-07-25 noi.ac #2676 +题目描述: +给定一个带权连通无向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数\(|E|=m\) +图中可能包含重边以及自环。 + +
                          +
                          + + 2022-07-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          6 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/7/index.html b/archives/2022/page/7/index.html index e69de29bb2..867f6c7271 100644 --- a/archives/2022/page/7/index.html +++ b/archives/2022/page/7/index.html @@ -0,0 +1,1539 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + OI易错点-C++语法 + + OI易错点-C++语法 +
                          +
                          +
                          +
                          + + OI易错点-C++语法 +不保证本文内容完全正确,仅仅是个人总结!! +islower,isalpha,isdigit +这种函数,返回值不是bool!! +在不同的机器上跑出来是不一样的!!!!不要直接加上它!!! +lower_bound(beg + +
                          +
                          + + 2022-07-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2704 [NOI2001] 炮兵阵地 题解 + + 洛谷P2704 [NOI2001] 炮兵阵地 题解 +
                          +
                          +
                          +
                          + + 洛谷P2704 [NOI2001] 炮兵阵地 +题解 +题目链接:P2704 +[NOI2001] 炮兵阵地 + +题意: +司令部的将军们打算在 \(N\times M\) +的网格地图上部署他们的炮兵部队。 +一个 \(N\times M\) 的地图由 + +
                          +
                          + + 2022-07-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 24 +
                          + +
                          + +
                          + + + + + +
                          + 23 +
                          + +
                          + +
                          + + + + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 密码生成器 + + 密码生成器 +
                          +
                          +
                          +
                          + + 密码生成器 +简单的小破密码生成器 qwq +也不知道搞了这个有啥用处 +目前还比较脆弱,不支持不合法输入 +反正就是个瞎搞的东西 qwq +代码: +/* + Name: 密码生成器1.0 + Author: q779 + Date: 2021.5.20 + + +
                          +
                          + + 2022-07-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 三子棋模拟器 for linux + + 三子棋模拟器 for linux +
                          +
                          +
                          +
                          + + 三子棋模拟器 for linux +中考前一周开摆写的 +采用的是minimax算法和 \(\alpha - +\beta\) 剪枝 +目前是基于规则的估价函数(因为q779不会AI) +目前在含C++环境的 ubuntu20.04 和 macOS + +
                          +
                          + + 2022-07-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 23 +
                          + +
                          + +
                          + + + + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + CF888E Maximum Subsequence 题解 + + CF888E Maximum Subsequence 题解 +
                          +
                          +
                          +
                          + + CF888E Maximum Subsequence +题解 +题目链接:CF888E +Maximum Subsequence + +题意: +给定N个数,第i个数的值为Ai,你现在可以从中选择一些数字,问选出数字的和模P最大为多少。 +输入第一行为N + +
                          +
                          + + 2022-07-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          +
                          +
                          +
                          + +
                          + + + CF479E Riding in a Lift 题解 + + CF479E Riding in a Lift 题解 +
                          +
                          +
                          +
                          + + CF479E Riding in a Lift 题解 +题目链接:CF479E +Riding in a Lift + +题意: +现在有n个传送点呈序列排列,编号为1到n +每一次可以通过折跃从一个传送点传送到另一处传送点, +由于折跃需要消耗巨大的能 + +
                          +
                          + + 2022-07-22 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          7 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/8/index.html b/archives/2022/page/8/index.html index e69de29bb2..e24ea0df09 100644 --- a/archives/2022/page/8/index.html +++ b/archives/2022/page/8/index.html @@ -0,0 +1,1551 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2657 [SCOI2009] windy 数 题解 + + 洛谷P2657 [SCOI2009] windy 数 题解 +
                          +
                          +
                          +
                          + + 洛谷P2657 [SCOI2009] windy 数 +题解 +题目链接:P2657 +[SCOI2009] windy 数 + +题意: +不含前导零且相邻两个数字之差至少为 \(2\) 的正整数被称为 windy 数。windy +想知道,在 \(a + +
                          +
                          + + 2022-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1410 子序列 题解 + + 洛谷P1410 子序列 题解 +
                          +
                          +
                          +
                          + + 洛谷P1410 子序列 题解 +题目链接:P1410 +子序列 + +题意: +给定一个长度为 \(N\)(\(N\) +为偶数)的序列,问能否将其划分为两个长度为 \(N +/ 2\) 的严格递增子序列。 +【数据范围】 +共三组数据,每组数据行数< + +
                          +
                          + + 2022-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + CF475D CGCDSSQ 题解 + + CF475D CGCDSSQ 题解 +
                          +
                          +
                          +
                          + + CF475D CGCDSSQ 题解 +题目链接:CF475D +CGCDSSQ + +题意: +给出一个长度为 \(n\) 的序列和 \(q\) 个询问, +每个询问输出一行,询问满足 \(\gcd\{a_l,a_{l+1},\dots,a_r\}=x + +
                          +
                          + + 2022-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + OI模板 + + OI模板 +
                          +
                          +
                          +
                          + + OI模板 +由于文件比较多,分为了多个部分。 + + + + + + + +Parts +包含内容 + + + + +OI模板-图论 +最短路算法、最小生成树、线段树优化建图、判负环、kosaraju算法、Tarjan算法 +[连通性问题]、欧拉路径、欧拉回路、2-SAT、 + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-图论 + + OI模板-图论 +
                          +
                          +
                          +
                          + + OI模板-图论 +最短路算法 +dijkstra +P4779 +【模板】单源最短路径(标准版) +优先队列优化 \(O((n+m)\log m)\) +#include <iostream> +#include <string> +#incl + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-字符串 + + OI模板-字符串 +
                          +
                          +
                          +
                          + + OI模板-字符串 +字符串哈希 +单哈希 +给定 \(N\) 个字符串(第 \(i\) 个字符串长度为 \(M_i\),字符串内包含数字、大小写字母,大小写敏感),请求出 +\(N\) +个字符串中共有多少个不同的字符串。 +P3370 +【模板】字符串 + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-算法 + + OI模板-算法 +
                          +
                          +
                          +
                          + + OI模板-算法 +排序算法 +其他乱七八糟的毛用没有(归并、松氏基排除外,还没补上来,咕咕咕...) +基本上一个sort全部搞定 +计数排序 +时间复杂度 \(O(n)\) +空间复杂度 \(O(\max\{n,\max\limits_{0<i + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          8 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/2022/page/9/index.html b/archives/2022/page/9/index.html index e69de29bb2..0265f0fdcb 100644 --- a/archives/2022/page/9/index.html +++ b/archives/2022/page/9/index.html @@ -0,0 +1,1533 @@ + + + + + + + + + + + + + + + + + + + 归档: 2022 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-其他 + + OI模板-其他 +
                          +
                          +
                          +
                          + + OI模板-其他 +光速幂 +仅适用于int 且 底数相同,即 \(a^b\) , \(a\) 不变,复杂度约为 \(O(\sqrt{n})\) +#include <bits/stdc++.h> +using namespace std; +# + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-计算几何 + + OI模板-计算几何 +
                          +
                          +
                          +
                          + + OI模板-计算几何 +二维凸包 +Andrew +时间复杂度 \(O(n \log n)\) +#include <bits/stdc++.h> +using namespace std; +#define int long long +#defi + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-数据结构 + + OI模板-数据结构 +
                          +
                          +
                          +
                          + + OI模板-数据结构 +并查集 +具@Roundgod老师说 +按秩合并+路径压缩才能保证并查集查询的复杂度为 \(O(\alpha(n))\) +只用一个就是 \(O(\log n)\) +的,不过要特意构造数据卡才会到这个上界 +int f[N],r + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2391 白雪皑皑 题解 + + 洛谷P2391 白雪皑皑 题解 +
                          +
                          +
                          +
                          + + 洛谷P2391 白雪皑皑 题解 +题目链接:P2391 +白雪皑皑 + +题意: +现在有 \(n\) 片雪花排成一列。 pty +要对雪花进行 \(m\) 次染色操作,第 \(i\) 次染色操作中,把第 \(((i\times p+q)\bmod n + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          + +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1792 [国家集训队]种树 题解 + + 洛谷P1792 [国家集训队]种树 题解 +
                          +
                          +
                          +
                          + + 洛谷P1792 [国家集训队]种树 +题解 +题目链接:P1792 +[国家集训队]种树 + +题意: +A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。 +园林部门得到指令后,初步规划出 \(n\) +个种树的位置, + +
                          +
                          + + 2022-07-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          + +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1631 序列合并 题解 + + 洛谷P1631 序列合并 题解 +
                          +
                          +
                          +
                          + + 洛谷P1631 序列合并 题解 +题目链接:P1631 +序列合并 + +题意: +有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到\(N^2\)个和,求这\(N^2\)个和中最小的N个。 +对于100%的数据中,满足1<=N< + +
                          +
                          + + 2022-07-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3871 [TJOI2010]中位数 题解 + + 洛谷P3871 [TJOI2010]中位数 题解 +
                          +
                          +
                          +
                          + + 洛谷P3871 [TJOI2010]中位数 +题解 +题目链接:P3871 +[TJOI2010]中位数 + +题意: +给定一个由N个元素组成的整数序列,现在有两种操作: +1 add a +在该序列的最后添加一个整数a,组成长度为N + 1的整数序列 + + +
                          +
                          + + 2022-07-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          + +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2422 良好的感觉 题解 + + 洛谷P2422 良好的感觉 题解 +
                          +
                          +
                          +
                          + + 洛谷P2422 良好的感觉 题解 +题目链接:P2422 +良好的感觉 + +题意: +kkk 做了一个人体感觉分析器。每一天,人都有一个感受值 \(A_i\),\(A_i\) 越大,表示人感觉越舒适。在一段时间 +\(\left[i, j\right + +
                          +
                          + + 2022-07-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3512 [POI2010]PIL-Pilots 题解 + + 洛谷P3512 [POI2010]PIL-Pilots 题解 +
                          +
                          +
                          +
                          + + 洛谷P3512 [POI2010]PIL-Pilots +题解 +题目链接:P3512 +[POI2010]PIL-Pilots + +题意:给定 \(n\) +个数,找一个最长区间,使得区间中的最大值减最小值不超过 \(k\) + +题面写的太烂了 +考虑 + +
                          +
                          + + 2022-07-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          9 / 25
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/index.html b/archives/index.html index e69de29bb2..49409f6b67 100644 --- a/archives/index.html +++ b/archives/index.html @@ -0,0 +1,1558 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          +
                          +
                          +
                          + +
                          + + + LOJ6269 烷基计数 加强版 题解 + + LOJ6269 烷基计数 加强版 题解 +
                          +
                          +
                          +
                          + + LOJ6269 烷基计数 加强版 题解 +题目链接:#6269. 烷基计数 +加强版 + +题意:众所周知,大连 24 +中是一所神奇的学校,在那里,化竞的同学很多都擅长写代码。 +有一天,化学不及格的胡小兔向化竞巨佬晴岚请教化学题: +“\(n\) +个 + +
                          +
                          + + 2022-08-22 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + CF786B Legacy 题解 + + CF786B Legacy 题解 +
                          +
                          +
                          +
                          + + CF786B Legacy 题解 +题目链接:CF786B +Legacy + +题意: +\(n\) 个结点, \(q\) 次操作,问操作后从 \(s\) 出发的单源最短路 +操作如下 + +输入 1 u v w 表示 \(u\) 到 \(v\) 连一条 + +
                          +
                          + + 2022-08-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3311 [SDOI2014] 数数 题解 + + 洛谷P3311 [SDOI2014] 数数 题解 +
                          +
                          +
                          +
                          + + 洛谷P3311 [SDOI2014] 数数 +题解 +题目链接:P3311 +[SDOI2014] 数数 + +题意: +我们称一个正整数 \(x\) +是幸运数,当且仅当它的十进制表示中不包含数字串集合 \(s\) 中任意一个元素作为其子串。例如当 \ + +
                          +
                          + + 2022-08-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-博弈论 + + OI数学总结-博弈论 +
                          +
                          +
                          +
                          + + OI数学总结-博弈论 +施工中,咕咕咕... +博弈论基础 +对于博弈论的题目,重点考虑以下两点 + +什么时候可以判定胜利者 +先手的影响 + + + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-其他 + + OI数学总结-其他 +
                          +
                          +
                          +
                          + + OI数学总结-其他 +本文充满了从各种地方偷学来的东西以及没什么分类的东西 +数值积分 +定积分 +简单来说,函数 \(f(x)\) 在区间 \([l,r]\) 上的定积分 \(\int_{l}^{r}f(x)\mathrm{d}x\) 指的是 \ + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-群论 + + OI数学总结-群论 +
                          +
                          +
                          +
                          + + OI数学总结-群论 +施工中,咕咕咕.... +群的基本概念 +群的定义 +群公理包含下述四个性质(有时略去封闭性,只有三个性质)。若集合 \(G\neq\varnothing\) 和 \(G\) 上的运算 \(\cdot\) 构成的代数结构 \( + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-数论 + + OI数学总结-数论 +
                          +
                          +
                          +
                          + + OI数学总结-数论 +施工中,咕咕咕.... +积性函数 +数论中定义:定义域为正整数域的函数 \(f(n)\) , +且满足 \(\forall a,b \in \mathbb{Z} +_+,f(ab)=f(a)f(b) \land \gcd(a, + +
                          +
                          + + 2022-08-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          1 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/10/index.html b/archives/page/10/index.html index e69de29bb2..762d44113c 100644 --- a/archives/page/10/index.html +++ b/archives/page/10/index.html @@ -0,0 +1,1537 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + UVA11362 Phone List 题解 + + UVA11362 Phone List 题解 +
                          +
                          +
                          +
                          + + UVA11362 Phone List 题解 +题目链接:UVA11362 Phone +List + +题意:给定\(n\)个长度不超过\(10\)的数字串,判断是否有两个字符串\(A\)和\(B\),满足\(A\)是\(B\)的前缀,若有,输出 + +
                          +
                          + + 2022-07-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          + +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4113 [HEOI2012]采花 题解 + + 洛谷P4113 [HEOI2012]采花 题解 +
                          +
                          +
                          +
                          + + 洛谷P4113 [HEOI2012]采花 题解 +题目链接:P4113 +[HEOI2012]采花 + +题意:萧薰儿是古国的公主,平时的一大爱好是采花。 +今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。 +花园足够大,容纳了 \(n\) + +
                          +
                          + + 2022-07-15 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3294 [SCOI2016]背单词 题解 + + 洛谷P3294 [SCOI2016]背单词 题解 +
                          +
                          +
                          +
                          + + 洛谷P3294 [SCOI2016]背单词 +题解 +题目链接:P3294 +[SCOI2016]背单词 + +题意:给定 \(n\) +个不同的字符串,求一个最优顺序使得花费最小 +设当前字符串为 \(a\) ,位于排列中的 +\(x\) 位置 + +如果 + +
                          +
                          + + 2022-07-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2444 [POI2000]病毒 题解 + + 洛谷P2444 [POI2000]病毒 题解 +
                          +
                          +
                          +
                          + + 洛谷P2444 [POI2000]病毒 题解 +题目链接:P2444 +[POI2000]病毒 + +题意: +二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全 + +
                          +
                          + + 2022-07-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 13 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3966 [TJOI2013]单词 题解 + + 洛谷P3966 [TJOI2013]单词 题解 +
                          +
                          +
                          +
                          + + 洛谷P3966 [TJOI2013]单词 题解 +题目链接:P3966 +[TJOI2013]单词 + +题意: +小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了 + +
                          +
                          + + 2022-07-13 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 13 +
                          + +
                          + +
                          + + + + + +
                          + 13 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3586 [POI2015] LOG 题解 + + 洛谷P3586 [POI2015] LOG 题解 +
                          +
                          +
                          +
                          + + 洛谷P3586 [POI2015] LOG 题解 +题目链接:P3586 +[POI2015] LOG + +题意: +维护一个长度为 \(n\) +的序列,一开始都是 \(0\),支持以下两种操作: + +U k a 将序列中第 \(k\) +个数修改为 + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3879 [TJOI2010] 阅读理解 题解 + + 洛谷P3879 [TJOI2010] 阅读理解 题解 +
                          +
                          +
                          +
                          + + 洛谷P3879 [TJOI2010] 阅读理解 +题解 +题目链接:P3879 +[TJOI2010] 阅读理解 + +题意: +英语老师留了 \(N\) +篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些 + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1168 中位数 题解 + + 洛谷P1168 中位数 题解 +
                          +
                          +
                          +
                          + + 洛谷P1168 中位数 题解 +题目链接:P1168 +中位数 + +题意: +给出一个长度为\(N\)的非负整数序列\(A_i\),对于所有\(1 ≤ k ≤ (N + 1) / 2\),输出\(A_1, A_1 \sim A_3, …,A_1 \ + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          10 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/11/index.html b/archives/page/11/index.html index e69de29bb2..ec2638feef 100644 --- a/archives/page/11/index.html +++ b/archives/page/11/index.html @@ -0,0 +1,1547 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1816 忠诚 题解 + + 洛谷P1816 忠诚 题解 +
                          +
                          +
                          +
                          + + 洛谷P1816 忠诚 题解 +题目链接:P1816 +忠诚 + +题意:区间min。 + +BIT写法也就图一乐(其实不建议) +主要在询问上有些区别, +例如如果 x-lowbit(x) 小于 \(l\) ,则此时不能直接 +x-=lowbit(x) +因为 + +
                          +
                          + + 2022-07-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          + +
                          + +
                          + + + + + +
                          + 11 +
                          + +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1868 饥饿的奶牛 题解 + + 洛谷P1868 饥饿的奶牛 题解 +
                          +
                          +
                          +
                          + + 洛谷P1868 饥饿的奶牛 题解 +题目链接:P1868 +饥饿的奶牛 + +题意: +有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字。 +现用汉语翻译为: +有 \(N\) 个区间,每个区间 \(x,y\) 表示提供的 \ + +
                          +
                          + + 2022-07-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          + +
                          + +
                          + + + + + +
                          + 10 +
                          + +
                          + +
                          + + + + + +
                          + 10 +
                          + +
                          + +
                          + + + + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2375 [NOI2014] 动物园 题解 + + 洛谷P2375 [NOI2014] 动物园 题解 +
                          +
                          +
                          +
                          + + 洛谷P2375 [NOI2014] 动物园 +题解 +题目链接:P2375 +[NOI2014] 动物园 + +题意: +近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学 + +
                          +
                          + + 2022-07-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4568 [JLOI2011] 飞行路线 题解 + + 洛谷P4568 [JLOI2011] 飞行路线 题解 +
                          +
                          +
                          +
                          + + 洛谷P4568 [JLOI2011] 飞行路线 +题解 +题目链接:P4568 +[JLOI2011] 飞行路线 + +题意: +Alice 和 Bob +现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 +\(n\) +个城市设有业务, + +
                          +
                          + + 2022-07-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4551 最长异或路径 题解 + + 洛谷P4551 最长异或路径 题解 +
                          +
                          +
                          +
                          + + 洛谷P4551 最长异或路径 题解 +题目链接:P4551 +最长异或路径 + +题意:给定一棵 \(n\) 个点的带权树,结点下标从 \(1\) 开始到 \(n\)。寻找树中找两个结点,求最长的异或路径。 +异或路径指的是指两个结点之间唯一路径上的 + +
                          +
                          + + 2022-07-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3698 [CQOI2017]小Q的棋盘 题解 + + 洛谷P3698 [CQOI2017]小Q的棋盘 题解 +
                          +
                          +
                          +
                          + + 洛谷P3698 [CQOI2017]小Q的棋盘 +题解 +题目链接:P3698 +[CQOI2017]小Q的棋盘 + +题意: +小 Q 正在设计一种棋类游戏。 +在小 Q +设计的游戏中,棋子可以放在棋盘上的格点中。某些格点之间有连线,棋子只能在有连线的 + +
                          +
                          + + 2022-07-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          11 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/12/index.html b/archives/page/12/index.html index e69de29bb2..daf4369179 100644 --- a/archives/page/12/index.html +++ b/archives/page/12/index.html @@ -0,0 +1,1544 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 09 +
                          + +
                          + +
                          + + + + + +
                          + 09 +
                          + +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5520 [yLOI2019] 青原樱 题解 + + 洛谷P5520 [yLOI2019] 青原樱 题解 +
                          +
                          +
                          +
                          + + 洛谷P5520 [yLOI2019] 青原樱 +题解 +题目链接:P5520 +[yLOI2019] 青原樱 + +题意: +\(n\) 个空放 \(m\) +个物品,两两物品不能直接相邻,至少空一格 + +纯数学题。 +看看几个空不能放,啊 \(m-1\) + + +
                          +
                          + + 2022-07-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          + +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4127 [AHOI2009]同类分布 题解 + + 洛谷P4127 [AHOI2009]同类分布 题解 +
                          +
                          +
                          +
                          + + 洛谷P4127 [AHOI2009]同类分布 +题解 +题目链接:P4127 +[AHOI2009]同类分布 + +题意: +给出两个数 \(a,b\) ,求出 \([a,b]\) +中各位数字之和能整除原数的数的个数。 +对于所有的数据, \(1 ≤ a + +
                          +
                          + + 2022-07-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + CF1036C Classy Numbers 题解 + + CF1036C Classy Numbers 题解 +
                          +
                          +
                          +
                          + + CF1036C Classy Numbers 题解 +题目链接:CF1036C +Classy Numbers + +题意:定义一个数字是“好数”,当且仅当它的十进制表示下有不超过\(3\)个数字\(1 \sim +9\) +举个例子:\(4,2000 + +
                          +
                          + + 2022-07-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1836 数页码 题解 + + 洛谷P1836 数页码 题解 +
                          +
                          +
                          +
                          + + 洛谷P1836 数页码 题解 +题目链接:P1836 +数页码 + +题意: +一本书的页码是从 \(1\sim n\) +编号的连续整数:\(1,2,3,\cdots,n\)。请你求出全部页码中所有单个数字的和,例如第 +\(123\) 页,它的和就是 + +
                          +
                          + + 2022-07-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4047 [JSOI2010]部落划分 题解 + + 洛谷P4047 [JSOI2010]部落划分 题解 +
                          +
                          +
                          +
                          + + 洛谷P4047 [JSOI2010]部落划分 +题解 +题目链接:P4047 +[JSOI2010]部落划分 + +题意: +聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部 + +
                          +
                          + + 2022-07-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5536 【XR-3】核心城市 题解 + + 洛谷P5536 【XR-3】核心城市 题解 +
                          +
                          +
                          +
                          + + 洛谷P5536 【XR-3】核心城市 +题解 +题目链接:P5536 +【XR-3】核心城市 + +题意: +X 国有 \(n\) 座城市,\(n - 1\) 条长度为 \(1\) +的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达 + +
                          +
                          + + 2022-07-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          12 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/13/index.html b/archives/page/13/index.html index e69de29bb2..022a3ff150 100644 --- a/archives/page/13/index.html +++ b/archives/page/13/index.html @@ -0,0 +1,1549 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 02 +
                          + +
                          + +
                          + + + + + +
                          + 06 +
                          + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3426 [POI2005]SZA-Template 题解 + + 洛谷P3426 [POI2005]SZA-Template 题解 +
                          +
                          +
                          +
                          + + 洛谷P3426 +[POI2005]SZA-Template 题解 +题目链接:P3426 +[POI2005]SZA-Template + +题意:你打算在纸上印一串字母。 +为了完成这项工作,你决定刻一个印章。印章每使用一次,就会将印章上的所有字母 + +
                          +
                          + + 2022-06-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2466 [SDOI2008] Sue 的小球 题解 + + 洛谷P2466 [SDOI2008] Sue 的小球 题解 +
                          +
                          +
                          +
                          + + 洛谷P2466 [SDOI2008] Sue +的小球 题解 + 题目链接:P2466 +[SDOI2008] Sue 的小球 + +题意: +Sue 和 Sandy +最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue +有一 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1608 路径统计 题解 + + 洛谷P1608 路径统计 题解 +
                          +
                          +
                          +
                          + + 洛谷P1608 路径统计 题解 +题目链接:P1608 +路径统计 + +题意: +一句话题意:最短路计数。 +“RP 餐厅” +的员工素质就是不一般,在齐刷刷的算出同一个电话号码之后,就准备让 HZH,TZY +去送快餐了,他们将自己居住的城市画了一张地 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5440 【XR-2】奇迹 题解 + + 洛谷P5440 【XR-2】奇迹 题解 +
                          +
                          +
                          +
                          + + 洛谷P5440 【XR-2】奇迹 题解 +题目链接:P5440 +【XR-2】奇迹 + +题意: +我们称一个日期为一个八位数,第 1~4 位构成年,第 5~6 +位构成月,第 7~8 位构成日,不足位数用 0 +补足。同时,要求日期所代表的这一天真实存 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1514 [NOIP2010 提高组] 引水入城 题解 + + 洛谷P1514 [NOIP2010 提高组] 引水入城 题解 +
                          +
                          +
                          +
                          + + 洛谷P1514 [NOIP2010 +提高组] 引水入城 题解 +题目链接:P1514 +[NOIP2010 提高组] 引水入城 + +题意: +在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个\( + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1378 油滴扩展 题解 + + 洛谷P1378 油滴扩展 题解 +
                          +
                          +
                          +
                          + + 洛谷P1378 油滴扩展 题解 +题目链接:P1378 +油滴扩展 + +题意: +在一个长方形框子里,最多有 \(N\) +个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界。必须等一个油滴扩展完 + +
                          +
                          + + 2022-06-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4983 忘情 题解 + + 洛谷P4983 忘情 题解 +
                          +
                          +
                          +
                          + + 洛谷P4983 忘情 题解 +题目链接:P4983 +忘情 + +题意: +“为什么要离开我!” +“因为你没玩儿转!” +“我玩儿转了!” +“那好,你现在就给我维护这么一个式子!” +“为什么要出这么毒瘤的东西。” +“为了恶心你。” +“......” +\ + +
                          +
                          + + 2022-06-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          13 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/14/index.html b/archives/page/14/index.html index e69de29bb2..778ee11639 100644 --- a/archives/page/14/index.html +++ b/archives/page/14/index.html @@ -0,0 +1,1541 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 06 +
                          + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + CF125E MST Company 题解 + + CF125E MST Company 题解 +
                          +
                          +
                          +
                          + + CF125E MST Company 题解 +题目链接:CF125E +MST Company + +题意: +给你一个有 \(n\) 个节点,\(m\) +条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足节点 \(1\) 正好连了 \( + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5633 最小度限制生成树 题解 + + 洛谷P5633 最小度限制生成树 题解 +
                          +
                          +
                          +
                          + + 洛谷P5633 最小度限制生成树 +题解 +题目链接:P5633 +最小度限制生成树 + +题意: +给你一个有 \(n\) 个节点,\(m\) +条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足编号为 +\(s\) 的节点正好连了 \(k\ + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2619 [国家集训队]Tree I 题解 + + 洛谷P2619 [国家集训队]Tree I 题解 +
                          +
                          +
                          +
                          + + 洛谷P2619 [国家集训队]Tree I +题解 +题目链接:P2619 +[国家集训队]Tree I + +题意: +给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 +\(\text{need}\) 条白色边的生成树。 +题目保证 + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2607 [ZJOI2008] 骑士 题解 + + 洛谷P2607 [ZJOI2008] 骑士 题解 +
                          +
                          +
                          +
                          + + 洛谷P2607 [ZJOI2008] 骑士 +题解 +题目链接:P2607 +[ZJOI2008] 骑士 + +题意: +Z +国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。 +最近发生了一件可怕的 + +
                          +
                          + + 2022-06-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1453 城市环路 题解 + + 洛谷P1453 城市环路 题解 +
                          +
                          +
                          +
                          + + 洛谷P1453 城市环路 题解 +题目链接:P1453 +城市环路 + +题意: +整个城市可以看做一个 \(n\) +个点,\(n\) +条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有 +\(2\) +条简单路径互通。图中的其 + +
                          +
                          + + 2022-06-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1083 [NOIP2012 提高组] 借教室 题解 + + 洛谷P1083 [NOIP2012 提高组] 借教室 题解 +
                          +
                          +
                          +
                          + + 洛谷P1083 [NOIP2012 +提高组] 借教室 题解 +题目链接:P1083 +[NOIP2012 提高组] 借教室 + +题意: +在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不 + +
                          +
                          + + 2022-06-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2678 [NOIP2015 提高组] 跳石头 题解 + + 洛谷P2678 [NOIP2015 提高组] 跳石头 题解 +
                          +
                          +
                          +
                          + + 洛谷P2678 [NOIP2015 +提高组] 跳石头 题解 +题目链接:P2678 +[NOIP2015 提高组] 跳石头 + +题意: +这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起 + +
                          +
                          + + 2022-06-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          14 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/15/index.html b/archives/page/15/index.html index e69de29bb2..5e5ba01e75 100644 --- a/archives/page/15/index.html +++ b/archives/page/15/index.html @@ -0,0 +1,1560 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 06 +
                          + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3647 [APIO2014] 连珠线 题解 + + 洛谷P3647 [APIO2014] 连珠线 题解 +
                          +
                          +
                          +
                          + + 洛谷P3647 [APIO2014] 连珠线 +题解 +题目链接:P3647 +[APIO2014] 连珠线 + +题意: +在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 +\(1\) 到 + +
                          +
                          + + 2022-06-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          + +
                          + +
                          + + + + + +
                          + 07 +
                          + +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-数学 + + OI模板-数学 +
                          +
                          +
                          +
                          + + OI模板-数学 +待补全 +排列的计算 +排列 \(A_n^m\) 直接算可以 \(O(n)\) +例如求解 \(A_{n-m+1}^{m} \bmod +p\) +int res=1; +for(int i=n-m+1; i>=n-2*m+2; i-- + +
                          +
                          + + 2022-06-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1272 重建道路 题解 + + 洛谷P1272 重建道路 题解 +
                          +
                          +
                          +
                          + + 洛谷P1272 重建道路 题解 +题目链接:P1272 +重建道路 + +题意: +一场可怕的地震后,人们用 \(N\) +个牲口棚(编号 \(1\sim N\))重建了农夫 +John +的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一 + +
                          +
                          + + 2022-06-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1734 最大约数和 题解 + + 洛谷P1734 最大约数和 题解 +
                          +
                          +
                          +
                          + + 洛谷P1734 最大约数和 题解 +题目链接:P1734 +最大约数和 + +题意:选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。 + +设 \(dp[i][j]\) 表示只考虑前 \(i\) 个数总和不超过 \(j\) + +
                          +
                          + + 2022-06-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          + +
                          + +
                          + + + + + +
                          + 06 +
                          + +
                          + +
                          + + + + + +
                          + 06 +
                          + +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1270 “访问”美术馆 题解 + + 洛谷P1270 “访问”美术馆 题解 +
                          +
                          +
                          +
                          + + 洛谷P1270 “访问”美术馆 题解 +题目链接:P1270 +“访问”美术馆 + +题意: +经过数月的精心准备,Peer +Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。 + +
                          +
                          + + 2022-06-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3354 [IOI2005]Riv 河流 题解 + + 洛谷P3354 [IOI2005]Riv 河流 题解 +
                          +
                          +
                          +
                          + + 洛谷P3354 [IOI2005]Riv 河流 +题解 +题目链接:P3354 +[IOI2005]Riv 河流 + +题意: +几乎整个 Byteland +王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进 + +
                          +
                          + + 2022-06-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3237 [HNOI2014]米特运输 题解 + + 洛谷P3237 [HNOI2014]米特运输 题解 +
                          +
                          +
                          +
                          + + 洛谷P3237 [HNOI2014]米特运输 +题解 +题目链接:P3237 +[HNOI2014]米特运输 + +题意: +米特是D星球上一种非常神秘的物质,蕴含着巨大的能量。在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题。 +D + +
                          +
                          + + 2022-06-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          15 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/16/index.html b/archives/page/16/index.html index e69de29bb2..94d1517293 100644 --- a/archives/page/16/index.html +++ b/archives/page/16/index.html @@ -0,0 +1,1567 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 31 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1273 有线电视网 题解 + + 洛谷P1273 有线电视网 题解 +
                          +
                          +
                          +
                          + + 洛谷P1273 有线电视网 题解 +题目链接:P1273 +有线电视网 + +题意: +某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节 + +
                          +
                          + + 2022-05-31 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2279 [HNOI2003]消防局的设立 题解 + + 洛谷P2279 [HNOI2003]消防局的设立 题解 +
                          +
                          +
                          +
                          + + 洛谷P2279 +[HNOI2003]消防局的设立 题解 +题目链接:P2279 +[HNOI2003]消防局的设立 + +题意: +2020 年,人类在火星上建立了一个庞大的基地群,总共有 \(n\) 个基地。起初为了节约材料,人类只修建了 +\(n- + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2015 二叉苹果树 题解 + + 洛谷P2015 二叉苹果树 题解 +
                          +
                          +
                          +
                          + + 洛谷P2015 二叉苹果树 题解 +题目链接:P2015 +二叉苹果树 + +题意: +有一棵苹果树,如果树枝有分叉,一定是分二叉(就是说没有只有一个儿子的结点) +这棵树共有 \(N\) +个结点(叶子点或者树枝分叉点),编号为 \(1 +\sim N\ + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3174 [HAOI2009] 毛毛虫 题解 + + 洛谷P3174 [HAOI2009] 毛毛虫 题解 +
                          +
                          +
                          +
                          + + 洛谷P3174 [HAOI2009] 毛毛虫 +题解 +题目链接:P3174 +[HAOI2009] 毛毛虫 + +题意: +对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大。例如下图左边的树(图 +\( + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3621 [APIO2007] 风铃 题解 + + 洛谷P3621 [APIO2007] 风铃 题解 +
                          +
                          +
                          +
                          + + 洛谷P3621 [APIO2007] 风铃 +题解 +题目链接:P3621 +[APIO2007] 风铃 + +题意: +你准备给弟弟 Ike 买一件礼物,但是,Ike +挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西。 +你准备给 Ike +买 + +
                          +
                          + + 2022-05-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2458 [SDOI2006]保安站岗 题解 + + 洛谷P2458 [SDOI2006]保安站岗 题解 +
                          +
                          +
                          +
                          + + 洛谷P2458 [SDOI2006]保安站岗 +题解 +题目链接:P2458 +[SDOI2006]保安站岗 + +题意: +五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序 + +
                          +
                          + + 2022-05-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2016 战略游戏 题解 + + 洛谷P2016 战略游戏 题解 +
                          +
                          +
                          +
                          + + 洛谷P2016 战略游戏 题解 +题目链接:P2016 +战略游戏 + +题意: Bob +要建立一个古城堡,城堡中的路形成一棵无根树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。 +注意,某个士兵在一个结点上时,与该结点相连 + +
                          +
                          + + 2022-05-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1122 最大子树和 题解 + + 洛谷P1122 最大子树和 题解 +
                          +
                          +
                          +
                          + + 洛谷P1122 最大子树和 题解 +题目链接:P1122 +最大子树和 + +题意: +小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花 + +
                          +
                          + + 2022-05-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3205 [HNOI2010]合唱队 题解 + + 洛谷P3205 [HNOI2010]合唱队 题解 +
                          +
                          +
                          +
                          + + 洛谷P3205 [HNOI2010]合唱队 +题解 +题目链接:P3205 +[HNOI2010]合唱队 + +题意: +为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A +需要将合唱队的人根据他们的身高排出一个队形。假定合唱队 + +
                          +
                          + + 2022-05-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          16 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/17/index.html b/archives/page/17/index.html index e69de29bb2..d9d9527a6a 100644 --- a/archives/page/17/index.html +++ b/archives/page/17/index.html @@ -0,0 +1,1524 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + UVA1629 切蛋糕 Cake slicing 题解 + + UVA1629 切蛋糕 Cake slicing 题解 +
                          +
                          +
                          +
                          + + UVA1629 切蛋糕 Cake slicing +题解 +题目链接:UVA1629 +切蛋糕 Cake slicing + +题意:这个翻译够烂的,直接看pdf +翻译:有一个n行m列(1<=n,m<=20)的网络蛋糕上有k个樱桃。每次可 + +
                          +
                          + + 2022-05-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4170 [CQOI2007]涂色 题解 + + 洛谷P4170 [CQOI2007]涂色 题解 +
                          +
                          +
                          +
                          + + 洛谷P4170 [CQOI2007]涂色 题解 +题目链接:P4170 +[CQOI2007]涂色 + +题意: +假设你有一条长度为 \(5\) +的木板,初始时没有涂过任何颜色。你希望把它的 \(5\) +个单位长度分别涂上红、绿、蓝、绿、红色,用一 + +
                          +
                          + + 2022-05-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + AT3913 XOR Tree 题解 + + AT3913 XOR Tree 题解 +
                          +
                          +
                          +
                          + + AT3913 XOR Tree 题解 +题目链接:AT3913 +XOR Tree + +题意:给你一棵有 \(N\) 个节点的树,节点编号从 \(0\) 到 \(N-1\) , 树边编号从 \(1\) 到 \(N-1\) 。第 \(i\) 条边连 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF149D Coloring Brackets 题解 + + CF149D Coloring Brackets 题解 +
                          +
                          +
                          +
                          + + CF149D Coloring Brackets +题解 +题目链接:CF149D +Coloring Brackets + +题意:给出一个配对的括号序列(如 “\(\texttt{(())()}\)”、“\(\texttt{()}\)” 等,“\ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF19B Checkout Assistant 题解 + + CF19B Checkout Assistant 题解 +
                          +
                          +
                          +
                          + + CF19B Checkout Assistant +题解 +题目链接:CF19B +Checkout Assistant + +题意:Bob 来到一家现购自运商店,将 \(n\) +件商品放入了他的手推车,然后到收银台付款。每件商品由它的价格 \(c_ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF346B Lucky Common Subsequence 题解 + + CF346B Lucky Common Subsequence 题解 +
                          +
                          +
                          +
                          + + CF346B Lucky Common +Subsequence 题解 +题目链接:CF346B +Lucky Common Subsequence + +题意:通过删除一个字符串中的某些元素而不改变其余元素的顺序,可以派生出该字符串的一个子序列。 + + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF374C Inna and Dima 题解 + + CF374C Inna and Dima 题解 +
                          +
                          +
                          +
                          + + CF374C Inna and Dima 题解 +题目链接:CF374C +Inna and Dima + +题意: +Inna和Dima在商店买了一张 n * m +的桌子,桌子的每一个单元格上都有一个字符,字符集为("D","I","M","A") + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          17 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/18/index.html b/archives/page/18/index.html index e69de29bb2..c89bbd461f 100644 --- a/archives/page/18/index.html +++ b/archives/page/18/index.html @@ -0,0 +1,1535 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF41D Pawn 题解 + + CF41D Pawn 题解 +
                          +
                          +
                          +
                          + + CF41D Pawn 题解 +题目链接:CF41D +Pawn + +题意:国际象棋棋盘最底行站了一个兵。 +它只有两种行动方式: 向上左或向上右走。 +它可以选择从最低行哪个节点开始他的旅程。 +每个格子上有0-9颗豌豆,而士兵想移动到最上一行并且积累 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + CF526B Om Nom and Dark Park 题解 + + CF526B Om Nom and Dark Park 题解 +
                          +
                          +
                          +
                          + + CF526B Om Nom and Dark Park +题解 +题目链接:CF526B Om +Nom and Dark Park + +题意:给定 \(2^{n+1}-1\) +个结点的满二叉树,求保证根到每一个叶子节点的路径权值和相等的情况下,增 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1108 低价购买 题解 + + 洛谷P1108 低价购买 题解 +
                          +
                          +
                          +
                          + + 洛谷P1108 低价购买 题解 +题目链接:P1108 +低价购买 + +题意:“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 +
                          +
                          +
                          +
                          + + 洛谷P1156 垃圾陷阱 +题解&浅谈刷表法与填表法 +填表法 +:就是一般的动态规划,当前点的状态,可以直接用状态方程,根据之前点的状态推导出来。 +刷表法:由当前点的状态,更新其他点的状态。需要注意:只用当每个状态所依赖的状态对它的影响 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1171 售货员的难题 题解 + + 洛谷P1171 售货员的难题 题解 +
                          +
                          +
                          +
                          + + 洛谷P1171 售货员的难题 题解 +题目链接:P1171 +售货员的难题 + +题意:TSP问题。 +某乡有\(n\)个村庄(\(1<n \le +20\)),有一个售货员,他要到各个村庄去售货,各村庄之间的路程\(s(0<s<10 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1282 多米诺骨牌 题解 + + 洛谷P1282 多米诺骨牌 题解 +
                          +
                          +
                          +
                          + + 洛谷P1282 多米诺骨牌 题解 +题目链接:P1282 +多米诺骨牌 + +题意: +多米诺骨牌由上下 \(2\) +个方块组成,每个方块中有 \(1\sim6\) +个点。现有排成行的上方块中点数之和记为 \(S_1\),下方块中点数之和记为 \(S + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1284 三角形牧场 题解 + + 洛谷P1284 三角形牧场 题解 +
                          +
                          +
                          +
                          + + 洛谷P1284 三角形牧场 题解 +题目链接:P1284 +三角形牧场 + +题意:和所有人一样,奶牛喜欢变化。它们正在设想新造型的牧场。奶牛建筑师 +Hei 想建造围有漂亮白色栅栏的三角形牧场。她拥有 \(n\) 块木板,每块的长度 \(l_i\) + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1772 [ZJOI2006]物流运输 题解 + + 洛谷P1772 [ZJOI2006]物流运输 题解 +
                          +
                          +
                          +
                          + + 洛谷P1772 [ZJOI2006]物流运输 +题解 +题目链接:P1772 +[ZJOI2006]物流运输 + +题意:物流公司要把一批货物从码头 A 运到码头 +B。由于货物量比较大,需要 \(n\) +天才能运完。货物运输过程中一般要转停好几个码头 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1858 多人背包 题解 + + 洛谷P1858 多人背包 题解 +
                          +
                          +
                          +
                          + + 洛谷P1858 多人背包 题解 +题目链接:P1858 +多人背包 + +题意:求01背包前k优解的价值和 + +建议先去读一读《背包九讲》再来看 +注意到朴素的 \(01\) 背包是这样转移的 +\[ +dp[j]=\max(dp[j],dp[j-w[i] + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1944 最长括号匹配 题解 + + 洛谷P1944 最长括号匹配 题解 +
                          +
                          +
                          +
                          + + 洛谷P1944 最长括号匹配 题解 + +题意:对一个由(,),[,]括号组成的字符串,求出其中最长的括号匹配子串。具体来说,满足如下条件的字符串成为括号匹配的字符串: +1.(),[]是括号匹配的字符串。 +2.若A是括号匹配的串,则(A),[A + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2158 [SDOI2008] 仪仗队 题解 + + 洛谷P2158 [SDOI2008] 仪仗队 题解 +
                          +
                          +
                          +
                          + + 洛谷P2158 [SDOI2008] 仪仗队 +题解 +题目链接:P2158 +[SDOI2008] 仪仗队 + +题意:作为体育委员,C +君负责这次运动会仪仗队的训练。仪仗队是由学生组成的 \(N \times N\) +的方阵,为了保证队伍在行进中 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          18 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/19/index.html b/archives/page/19/index.html index e69de29bb2..f8a1500b5e 100644 --- a/archives/page/19/index.html +++ b/archives/page/19/index.html @@ -0,0 +1,1535 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2170 选学霸 题解 + + 洛谷P2170 选学霸 题解 +
                          +
                          +
                          +
                          + + 洛谷P2170 选学霸 题解 +题目链接:P2170 +选学霸 + +题意:老师想从 \(n\) 名学生中选 \(m\) 人当学霸,但有 \(k\) +人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2224 [HNOI2001]产品加工 题解 + + 洛谷P2224 [HNOI2001]产品加工 题解 +
                          +
                          +
                          +
                          + + 洛谷P2224 [HNOI2001]产品加工 +题解 +题目链接:P2224 +[HNOI2001]产品加工 + +题意: +某加工厂有 A、B +两台机器,来加工的产品可以由其中任何一台机器完成,或者两台机器共同完成。由于受到机器性能和产品特性的限制, + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2257 YY的GCD 题解 + + 洛谷P2257 YY的GCD 题解 +
                          +
                          +
                          +
                          + + 洛谷P2257 YY的GCD 题解 + +题意: +多组询问。 +给定 \(N, M\),求 \(1 \leq x \leq N\),\(1 \leq y \leq M\) 且 \(\gcd(x,y)\) 为质数的 \((x,y)\) 有多少对。 + + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2568 GCD 题解 + + 洛谷P2568 GCD 题解 +
                          +
                          +
                          +
                          + + 洛谷P2568 GCD 题解 +题目链接:P2568 +GCD + +题意: +给定正整数 \(n\),求 \(1\le x,y\le n\) 且 \(\gcd(x,y)\)为素数的数对 \((x,y)\) 有多少对。 +\(1 \le n\le 10 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          19 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/2/index.html b/archives/page/2/index.html index e69de29bb2..a6570b59fd 100644 --- a/archives/page/2/index.html +++ b/archives/page/2/index.html @@ -0,0 +1,1549 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 19 +
                          + +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + BZOJ2141 排队 题解 + + BZOJ2141 排队 题解 +
                          +
                          +
                          +
                          + + BZOJ2141 排队 题解 +题目链接:#2141. +排队 + +题意: +一句话题意: +给定 \(n\) 个数,\(Q\) +次询问,每次交换两个不同位置的数,然后输出逆序对数。 +\(1 \le n \le 2\times 10^4,~1\le + +
                          +
                          + + 2022-08-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + 小蓝书 16.1 + + 小蓝书 16.1 +
                          +
                          +
                          +
                          + + 16.1 +施工中,咕咕咕.... +计数原理 +加法原理 +设完成某件事有 \(m\) +类不同的方法, +第 \(i(1\le i \le m)\) 类方法中有 +\(n_i\) 种不同的方法,则完成这件事共有 +\[ +n_1 + n_2 + \cdo + +
                          +
                          + + 2022-08-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + + CF1491F Magnets 题解 + + CF1491F Magnets 题解 +
                          +
                          +
                          +
                          + + CF1491F Magnets 题解 +题目链接:CF1491F +Magnets + +题意: +这是一个交互题。 +早苗有 \(n\) 块磁石,编号为 \(1,2,\cdots,n\)。每块磁石的磁极可能是正极,负极,也可能没有磁性。她希望你能帮她 + +
                          +
                          + + 2022-08-15 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2396 yyy loves Maths VII 题解 + + 洛谷P2396 yyy loves Maths VII 题解 +
                          +
                          +
                          +
                          + + 洛谷P2396 yyy loves Maths +VII 题解 +题目链接:P2396 yyy +loves Maths VII + +题意: +一群同学在和 yyy 玩一个游戏。 +每次,他们会给 yyy \(n\) +张卡片,卡片上有数字,所有的数字都 + +
                          +
                          + + 2022-08-15 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + CF1073E Segment Sum 题解 + + CF1073E Segment Sum 题解 +
                          +
                          +
                          +
                          + + CF1073E Segment Sum 题解 +题目链接:CF1073E +Segment Sum + +题意: +给定 \(l,r,k\) ,求 \([l,r]\) 有多少个数满足「不包含超过 \(k\) 个数码」,输出他们的和 \(\bmod { + +
                          +
                          + + 2022-08-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + UOJ66 新年的巧克力棒 题解 + + UOJ66 新年的巧克力棒 题解 +
                          +
                          +
                          +
                          + + UOJ66 新年的巧克力棒 题解 +题目链接:#66. +新年的巧克力棒 + +题意:马上就要到羊年了,羊村一片欢腾,懒羊羊则懒洋洋地躺在草坪上吃新年的巧克力棒。 +他手上的巧克力棒是个由 \(n\) +个巧克力单元格组成的长度为 \(n\) +的长条, + +
                          +
                          + + 2022-08-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2292 [HNOI2004] L 语言 题解 + + 洛谷P2292 [HNOI2004] L 语言 题解 +
                          +
                          +
                          +
                          + + 洛谷P2292 [HNOI2004] L 语言 +题解 +题目链接:P2292 +[HNOI2004] L 语言 + +题意: +标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的。现在你要处理的就是一段没有标点的文章。 +一段文章 \(T\) + + +
                          +
                          + + 2022-08-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1305 新二叉树 题解 + + 洛谷P1305 新二叉树 题解 +
                          +
                          +
                          +
                          + + 洛谷P1305 新二叉树 题解 +题目链接:P1305 +新二叉树 + +题意: +输入一串二叉树,输出其前序遍历。 +输入1: +6 +abc +bdi +cj* +d** +i** +j** +输出1: +abdicj + +虽然是大水题,但是有个解法还是很妙的 +利用前 + +
                          +
                          + + 2022-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1229 遍历问题 题解 + + 洛谷P1229 遍历问题 题解 +
                          +
                          +
                          +
                          + + 洛谷P1229 遍历问题 题解 +题目链接:P1229 +遍历问题 + +题意: +我们都很熟悉二叉树的前序、中序、后序遍历,在数据结构中常提出这样的问题:已知一棵二叉树的前序和中序遍历,求它的后序遍历,相应的,已知一棵二叉树的后序遍历和中序遍历序列 + +
                          +
                          + + 2022-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          2 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/20/index.html b/archives/page/20/index.html index e69de29bb2..8d69caad54 100644 --- a/archives/page/20/index.html +++ b/archives/page/20/index.html @@ -0,0 +1,1548 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3188 [HNOI2007]梦幻岛宝珠 题解 + + 洛谷P3188 [HNOI2007]梦幻岛宝珠 题解 +
                          +
                          +
                          +
                          + + 洛谷P3188 +[HNOI2007]梦幻岛宝珠 题解 +题目链接:P3188 +[HNOI2007]梦幻岛宝珠 + +题意: +给你 \(n\) +颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过 +\(W\),且总价值最大 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3297 [SDOI2013] 逃考 题解 + + 洛谷P3297 [SDOI2013] 逃考 题解 +
                          +
                          +
                          +
                          + + 洛谷P3297 [SDOI2013] 逃考 +题解 +题目链接:P3297 +[SDOI2013] 逃考 + +题意:髙考又来了,对于不认真读书的小杨来讲,真不是个好消息。为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶、外 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3336 [ZJOI2013]话旧 题解 + + 洛谷P3336 [ZJOI2013]话旧 题解 +
                          +
                          +
                          +
                          + + 洛谷P3336 [ZJOI2013]话旧 题解 +题目链接:P3336 +[ZJOI2013]话旧 + +题意:小林跟着银河队选手去了一趟宇宙比赛,耳濡目染,变得学术起来。回来后,他发现世界大变样了。比丘兽究级进化,成了凤凰兽;金先生因为发了一篇 + + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3985 不开心的金明 题解 + + 洛谷P3985 不开心的金明 题解 +
                          +
                          +
                          +
                          + + 洛谷P3985 不开心的金明 题解 +题目链接:P3985 +不开心的金明 + +题意: +金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4141 消失之物 题解 + + 洛谷P4141 消失之物 题解 +
                          +
                          +
                          +
                          + + 洛谷P4141 消失之物 题解 +题目链接:P4141 +消失之物 + +题意:ftiasch 有 \(n\) 个物品, 体积分别是 \(w_1,w_2,\dots,w_n\) 。由于她的疏忽,第 +\(i\) 个物品丢失了。 +“要使用剩下的 \(n + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          20 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/21/index.html b/archives/page/21/index.html index e69de29bb2..0d6b38a682 100644 --- a/archives/page/21/index.html +++ b/archives/page/21/index.html @@ -0,0 +1,1537 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4158 [SCOI2009]粉刷匠 题解 + + 洛谷P4158 [SCOI2009]粉刷匠 题解 +
                          +
                          +
                          +
                          + + 洛谷P4158 [SCOI2009]粉刷匠 +题解 +题目链接:P4158 +[SCOI2009]粉刷匠 + +题意:windy有 N 条木板需要被粉刷。 每条木板被分为 M +个格子。 每个格子要被刷成红色或蓝色。 +windy每次粉刷,只能选择一条木 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4310 绝世好题 题解 + + 洛谷P4310 绝世好题 题解 +
                          +
                          +
                          +
                          + + 洛谷P4310 绝世好题 题解 +题目链接:P4310 +绝世好题 + +题意:给定一个长度为 \(n\) 的数列 \(a_i\) ,求 \(a_i\) 的子序列 \(b_i\) 的最长长度 \(k\),满足 \(b_i +\& b_{i-1 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4342 [IOI1998]Polygon 题解 + + 洛谷P4342 [IOI1998]Polygon 题解 +
                          +
                          +
                          +
                          + + 洛谷P4342 [IOI1998]Polygon +题解 +题目链接:P4342 +[IOI1998]Polygon + +题意:多边形是一个玩家在一个有 \(n\) 个顶点的多边形上的游戏,如图所示,其中 +\(n=4\) +。每个顶点用整数标记,每个 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 + + 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 +
                          +
                          +
                          +
                          + + 洛谷P4390 [BOI2007]Mokia +摩基亚 题解 +题目链接:P4390 +[BOI2007]Mokia 摩基亚 + +题意:摩尔瓦多的移动电话公司摩基亚(Mokia)设计出了一种新的用户定位系统。和其他的定位系统一样,它能够迅速回答任何 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4395 [BOI2003]Gem 气垫车 题解 + + 洛谷P4395 [BOI2003]Gem 气垫车 题解 +
                          +
                          +
                          +
                          + + 洛谷P4395 [BOI2003]Gem 气垫车 +题解 +题目链接:P4395 +[BOI2003]Gem 气垫车 + +题意:给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数 +唯一的限制条件是相临的两个结点不能标上相同的权值,要求一 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5110 块速递推 题解 + + 洛谷P5110 块速递推 题解 +
                          +
                          +
                          +
                          + + 洛谷P5110 块速递推 题解 +题目链接:P5110 +块速递推 + +题意:给定一个数列 \(a\) 满足递推式 \[ +a_0=0,a_1=1 +\\a_n = 233a_{n-1}+666a_{n-2} +\] 求 \(a_n \bmod (10 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5322 [BJOI2019] 排兵布阵 题解 + + 洛谷P5322 [BJOI2019] 排兵布阵 题解 +
                          +
                          +
                          +
                          + + 洛谷P5322 [BJOI2019] 排兵布阵 +题解 + +题意:小 C 正在玩一款排兵布阵的游戏。在游戏中有 +\(n\) +座城堡,每局对战由两名玩家来争夺这些城堡。每名玩家有 \(m\) 名士兵,可以向第 \(i\) 座城堡派遣 \(a_i\ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5365 [SNOI2017] 英雄联盟 题解 + + 洛谷P5365 [SNOI2017] 英雄联盟 题解 +
                          +
                          +
                          +
                          + + 洛谷P5365 [SNOI2017] 英雄联盟 +题解 +题目链接:P5365 +[SNOI2017] 英雄联盟 + +题意:正在上大学的小皮球热爱英雄联盟这款游戏,而且打的很菜,被网友们戏称为「小学生」。 +现在,小皮球终于受不了网友们的嘲讽,决定变 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          21 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/22/index.html b/archives/page/22/index.html index e69de29bb2..0cbde6bc0a 100644 --- a/archives/page/22/index.html +++ b/archives/page/22/index.html @@ -0,0 +1,1548 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 05 +
                          + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P6216 回文匹配 题解 + + 洛谷P6216 回文匹配 题解 +
                          +
                          +
                          +
                          + + 洛谷P6216 回文匹配 题解 +题目链接:P6216 +回文匹配 + +题意:对于一对字符串 \((s_1,s_2)\),若 \(s_1\) 的长度为奇数的子串 +\((l,r)\) 满足 \((l,r)\) 是回文的,那么 \(s_1\) 的“分 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 等差数列&等比数列小结 + + 等差数列&等比数列小结 +
                          +
                          +
                          +
                          + + 等差数列&等比数列小结 +高一自学的时候瞎总结写的(好吧我现在还是高一 +2022.5.7) +感觉丢在文件夹里吃灰没啥用,就放上来了 +欢迎各位指出我的错误(我数学真的烂 \(😓\) + +等差数列 +等差数列通项公式 +\[ +a_n = a_ + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 线段树空间开4倍的原因 + + 线段树空间开4倍的原因 +
                          +
                          +
                          +
                          + + 线段树空间开4倍的原因 +如果证明有错欢迎指出。 +对于长为 \(n\) +的序列,显然以其构建的线段树有 \(n\) +个叶子节点 +此时线段树的高度为 \(k=\left\lceil{\log_2 +n}\right\rceil+1\) (第一层的 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 主定理 + + 主定理 +
                          +
                          +
                          +
                          + + 主定理 +证明先不写 +将一个规模为 \(n\) +的问题,通过分治得到 \(a\) 个规模为 +\(n/b\) +的子问题,每个递归带来的额外计算为 \(f(n)\) ,则有 +\(T(n)=aT(n/b)+f(n)\) +其中 \(a,b\) 为常数 + +
                          +
                          + + 2022-05-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 04 +
                          + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4159 [SCOI2009] 迷路 题解 + + 洛谷P4159 [SCOI2009] 迷路 题解 +
                          +
                          +
                          +
                          + + 洛谷P4159 [SCOI2009] 迷路 +题解 +题目链接:P4159 +[SCOI2009] 迷路 + +题意:该有向图有 \(n\) 个节点,节点从 \(1\) 至 \(n\) 编号,windy 从节点 \(1\) 出发,他必须恰好在 \(t + +
                          +
                          + + 2022-04-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3829 [SHOI2012]信用卡凸包 题解 + + 洛谷P3829 [SHOI2012]信用卡凸包 题解 +
                          +
                          +
                          +
                          + + 洛谷P3829 +[SHOI2012]信用卡凸包 题解 +题目链接:P3829 +[SHOI2012]信用卡凸包 + +题意: 给定若干个“信用卡”,求其“凸包”周长 + + +这个题其实看上去很不可做,其实很简单 +注意到(搬了一张图,来自link,不过这 + +
                          +
                          + + 2022-04-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 23 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          22 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/23/index.html b/archives/page/23/index.html index e69de29bb2..a080ba5a92 100644 --- a/archives/page/23/index.html +++ b/archives/page/23/index.html @@ -0,0 +1,1549 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 04 +
                          + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 19 +
                          + +
                          + +
                          + + + + + +
                          + 17 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + Dijkstra及其复杂度证明 + + Dijkstra及其复杂度证明 +
                          +
                          +
                          +
                          + + Dijkstra及其复杂度证明 +前言 +本文主要围绕易混淆的复杂度分析进行讨论 + +Dijkstra +其实这个不叫迪杰斯特拉,这个叫/ˈdɛɪkstra/ qwq +一、小概念 +先放几个简单概念 +无向图:图中所有的边都是两端可达的,也就是可以从任 + +
                          +
                          + + 2022-03-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 最小树形图 Tarjan的DMST算法 + + 最小树形图 Tarjan的DMST算法 +
                          +
                          +
                          +
                          + + 最小树形图 Tarjan的DMST算法 +前言 +网上怎么都是朱刘算法啊? +那我来写一篇 Tarjan 的 DMST 算法吧 +qwq +注:本文的DMST采用左偏树+并查集实现 +时间复杂度为 \(O(E+V\log E)\) +如果采用斐波那契堆则 + +
                          +
                          + + 2022-03-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5826 【模板】子序列自动机 + + 洛谷P5826 【模板】子序列自动机 +
                          +
                          +
                          +
                          + + 洛谷P5826 【模板】子序列自动机 +题目链接:P5826 +【模板】子序列自动机 + +题意:给定一个主序列,每次给出一个序列,判断是否为其子序列 + +我们可以把每个字符的出现位置用vector维护 +然后对于每个询问,直接二分离当前位置最近的那个 + +
                          +
                          + + 2022-03-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 14 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3919 【模板】可持久化线段树 1(可持久化数组) 题解 + + 洛谷P3919 【模板】可持久化线段树 1(可持久化数组) 题解 +
                          +
                          +
                          +
                          + + 洛谷P3919 +【模板】可持久化线段树 1(可持久化数组) 题解 +题目链接:P3919 +【模板】可持久化线段树 1(可持久化数组) + +题意:如题,你需要维护这样的一个长度为 NN +的数组,支持如下几种操作 + +在某个历史版本上修改某一个位置上 + +
                          +
                          + + 2022-03-14 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1129 [ZJOI2007] 矩阵游戏 题解 + + 洛谷P1129 [ZJOI2007] 矩阵游戏 题解 +
                          +
                          +
                          +
                          + + 洛谷P1129 [ZJOI2007] 矩阵游戏 +题解 +题目链接:P1129 +[ZJOI2007] 矩阵游戏 + +题意:给定一张有黑白棋子的正方形棋盘,问存不存在解法使得经过若干次交换行或列的操作后,左上角至右下角的对角线上所有的点放着黑色棋子 + +
                          +
                          + + 2022-03-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + kd-tree(KDT) 时间复杂度证明 + + kd-tree(KDT) 时间复杂度证明 +
                          +
                          +
                          +
                          + + kd-tree(KDT) 时间复杂度证明 +kd-tree 是一种可以高效处理 \(k\) +维空间的数据结构 +在算法竞赛类的题目中一般有 \(k=2\) +还有个比较有趣的结论,当 \(k=1\) +时其实它就是一棵线段树 +下文中的 \(n\) + + +
                          +
                          + + 2022-03-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + + + + + +
                          + 01 +
                          +
                          +
                          +
                          + +
                          + + + CF1200E Compress Words 题解 + + CF1200E Compress Words 题解 +
                          +
                          +
                          +
                          + + CF1200E Compress Words 题解 +题目链接:CF1200E +Compress Words + +题意:给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀 +与 待插入串的前缀的最大匹配串 + +解法一 KMP +这个解法常数比较 + +
                          +
                          + + 2022-03-01 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          23 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/24/index.html b/archives/page/24/index.html index e69de29bb2..73041df460 100644 --- a/archives/page/24/index.html +++ b/archives/page/24/index.html @@ -0,0 +1,1550 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 02 +
                          + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4234 最小差值生成树 题解 + + 洛谷P4234 最小差值生成树 题解 +
                          +
                          +
                          +
                          + + 洛谷P4234 最小差值生成树 +题解 +题目链接:P4234 +最小差值生成树 + +题意:给定一个点标号从 \(1\) 到 \(n\) 的、有 \(m\) +条边的无向图,求边权最大值与最小值的差值最小的生成树,图可能存在自环 + +这个题不太好利用k + +
                          +
                          + + 2022-02-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          +
                          +
                          +
                          + +
                          + + + LCT求解最小生成树 + + LCT求解最小生成树 +
                          +
                          +
                          +
                          + + LCT求解最小生成树 +前言 +最小生成树模板: P3366 +【模板】最小生成树 +朴素的kruskal为主流最小生成树算法 +而LCT(link cut tree)也是可以维护最小生成树的 +由于LCT动态维护最小生成树,加上常数较大 +在实际测试中 + +
                          +
                          + + 2022-02-25 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2147 [SDOI2008] 洞穴勘测 题解 + + 洛谷P2147 [SDOI2008] 洞穴勘测 题解 +
                          +
                          +
                          +
                          + + 洛谷P2147 [SDOI2008] 洞穴勘测 +题解 +题目链接:P2147 +[SDOI2008] 洞穴勘测 + +题意:给定若干个点,动态连接(无向边),询问连通性 + +由于它有删边的操作,因此用并查集并不可行 +于是想到LCT(? +由于LCT有 + +
                          +
                          + + 2022-02-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 24 +
                          + +
                          + +
                          + + + + + +
                          + 13 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈快速乘 + + 浅谈快速乘 +
                          +
                          +
                          +
                          + + 浅谈快速乘 +前言 +想必大家都听说过快速幂 +那快速乘是个什么东西呢? +考虑取模操作a*b%p,1^10 ≤ a,b,p ≤ 2^10 +可以发现在 long long情况下,我们直接取模会溢出 +那么怎么办呢? +题目链接:https://www. + +
                          +
                          + + 2022-02-13 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          +
                          +
                          +
                          + +
                          + + + AT4284 & 洛谷 P1969 P3078 P5019 题解 + + AT4284 & 洛谷 P1969 P3078 P5019 题解 +
                          +
                          +
                          +
                          + + AT4284 & 洛谷 P1969 +P3078 P5019 题解 +题目链接:AT4284 P1969 P3078 P5019 + +题意:若干次区间减一,使所有数相等,求最小次数 + +这几道题就是一个std编出来的吧 \(😅\) +对于相 + +
                          +
                          + + 2022-02-07 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1234 小A的口头禅 题解 + + 洛谷P1234 小A的口头禅 题解 +
                          +
                          +
                          +
                          + + 洛谷P1234 小A的口头禅 题解 +题目链接:P1234 +小A的口头禅 + +给出了一个矩形,让你求出里面有几个hehe(方向无所谓,斜着不算) + +数据范围很良心,嗯~ +所以暴力枚举即可 +值得注意的是 \(\tt{eheh}\) +这种也算 +顺便 + +
                          +
                          + + 2022-02-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2804 神秘数字 题解 + + 洛谷P2804 神秘数字 题解 +
                          +
                          +
                          +
                          + + 洛谷P2804 神秘数字 题解 +题目链接:P2804 +神秘数字 + +题意:询问有多少段连续区间的平均值大于 \(m\) + +可以发现将每个数都减去 \(m\) +后任意和大于 \(0\) +的连续区间都满足题意 +区间和可以用前缀和优化,记为 \(s + +
                          +
                          + + 2022-02-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P5764 [CQOI2005]新年好 题解 + + 洛谷P5764 [CQOI2005]新年好 题解 +
                          +
                          +
                          +
                          + + 洛谷P5764 [CQOI2005]新年好 +题解 +题目链接:P5764 +[CQOI2005]新年好 + +题意:从 \(1\) +号结点出发,要访问其他 \(5\) +个结点,顺序随意,访问一个结点后不用返回 + +注意到 \(5! = O(1)\) + + +
                          +
                          + + 2022-02-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1462 通往奥格瑞玛的道路 题解 + + 洛谷P1462 通往奥格瑞玛的道路 题解 +
                          +
                          +
                          +
                          + + 洛谷P1462 通往奥格瑞玛的道路 +题解 +题目链接:P1462 +通往奥格瑞玛的道路 + +题意:在艾泽拉斯,有 \(n\) 个城市。编号为 \(1,2,3,\ldots,n\) 。 +城市之间有 \(m\) +条双向的公路,连接着两个城市,从某个城 + +
                          +
                          + + 2022-02-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          24 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/25/index.html b/archives/page/25/index.html index e69de29bb2..c364b241da 100644 --- a/archives/page/25/index.html +++ b/archives/page/25/index.html @@ -0,0 +1,1550 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 01 +
                          + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 16 +
                          +
                          +
                          +
                          + +
                          + + + RMB找零问题 + + RMB找零问题 +
                          +
                          +
                          +
                          + + RMB找零问题 +来自某次研究性学习的作业 +前言 +可以先考虑这样的问题 + +给定 \(n\) +种足量多的纸币,每种面额为 \(a_i\) 元 +\((0<a[i]≤10000,a[i]\in +\Z,1≤i≤n≤100)\) +给出需要找零的金 + +
                          +
                          + + 2022-01-16 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + + + + + +
                          + 01 +
                          + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 12 +
                          + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 释放缓存脚本 + + ubuntu 释放缓存脚本 +
                          +
                          +
                          +
                          + + ubuntu 释放缓存脚本 +注意:缓存\(\ne\)内存 +内存占用过高解决办法 +首先在主目录新建一个free.sh,输入 +echo 3 > /proc/sys/vm/drop_caches +保存后打开终端 +sudo su +crontab - + +
                          +
                          + + 2021-12-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 04 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4315 月下“毛景树” 题解 + + 洛谷P4315 月下“毛景树” 题解 +
                          +
                          +
                          +
                          + + 洛谷P4315 月下“毛景树” 题解 +题目链接:P4315 +月下“毛景树” + +题意:请维护一个数据结构,支持 + +改第 \(k\) 条边的边权 +结点 \(u\) 到 \(v\) 路径上的边权改为 \(k\) +结点 \(u\) 到 \(v\) + +
                          +
                          + + 2021-12-04 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          25 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/26/index.html b/archives/page/26/index.html index e69de29bb2..bef872d543 100644 --- a/archives/page/26/index.html +++ b/archives/page/26/index.html @@ -0,0 +1,1551 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 12 +
                          + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈拉格朗日插值法 + + 浅谈拉格朗日插值法 +
                          +
                          +
                          +
                          + + 浅谈拉格朗日插值法 +模板题链接:P4781 +【模板】拉格朗日插值 + +题意:给定 \(n\) +个点 \(P_i(x_i,y_i)\) ,将过该 \(n\) 个点的最多 \(n-1\) 次多项式记为 \(f(x)\) +给出 \(k\) ,求 \ + +
                          +
                          + + 2021-12-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2633 Count on a tree 题解 + + 洛谷P2633 Count on a tree 题解 +
                          +
                          +
                          +
                          + + 洛谷P2633 Count on a tree +题解 +题目链接:P2633 +Count on a tree + +题意:给定一棵树和 \(u,v,k\) ,求 \(u,v\) 结点间的第 \(k\) 小点权 + +本来以为是个树链剖分+主席树,结果 + +
                          +
                          + + 2021-11-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + windows Matlab R2020b 安装教程 + + windows Matlab R2020b 安装教程 +
                          +
                          +
                          +
                          + + windows Matlab R2020b +安装教程 +前言 +本文其实是我自己在安装过程中截图的,现在搞定了整理出来给大家参考一下 +使用的是matlab R2020b +如果本文有什么错误或对本文有什么问题,欢迎留言 qwq +要是图挂了可以直接 + +
                          +
                          + + 2021-11-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          + +
                          + +
                          + + + + + +
                          + 13 +
                          + +
                          + +
                          + + + + + +
                          + 12 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu WPS字体缺失 解决方法 + + ubuntu WPS字体缺失 解决方法 +
                          +
                          +
                          +
                          + + ubuntu WPS字体缺失 解决方法 +前言 +请保证您还有一台windows + +一、在windows复制字体 +首先在windows下载好WPS,然后找到字体,复制 + + +在这里插入图片描述 + +二、复制到ubuntu +我的ubuntu上装了WP + +
                          +
                          + + 2021-11-12 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1486 [NOI2004] 郁闷的出纳员 题解 + + 洛谷P1486 [NOI2004] 郁闷的出纳员 题解 +
                          +
                          +
                          +
                          + + 洛谷P1486 [NOI2004] +郁闷的出纳员 题解 +题目链接:P1486 +[NOI2004] 郁闷的出纳员 + +题意:维护一个数据结构,支持 + +插入一个大小为 \(k\) +的值,小于下界时不插入 +所有元素加上 \(k\) +所有元素减去 \ + +
                          +
                          + + 2021-10-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 03 +
                          + +
                          + +
                          + + + + + +
                          + 09 +
                          + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 均值不等式及其证明 + + 均值不等式及其证明 +
                          +
                          +
                          +
                          + + 均值不等式及其证明 +前言 +还有很多证明方法,等我学了再写 qwq + +均值不等式 +引理1:若 \(a\ge 0,b\ge +0\) ,则 \((a+b)^n\ge +a^n+na^{n-1}b,n\in \Z_+\) +直接二项式展开,证明略 + +命 + +
                          +
                          + + 2021-09-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈分块 区间众数 + + 浅谈分块 区间众数 +
                          +
                          +
                          +
                          + + 浅谈分块 区间众数 +前言 +分块大法好( +本文直接讲例题了 qwq + +P4168 [Violet]蒲公英 +题目链接:P4168 +[Violet]蒲公英 + +题意: 找到区间内编号最小的众数,强制在线 + +解法一 +直接分块 +设块长为 \(len\) + +
                          +
                          + + 2021-09-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          26 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/27/index.html b/archives/page/27/index.html index e69de29bb2..30c9cf632e 100644 --- a/archives/page/27/index.html +++ b/archives/page/27/index.html @@ -0,0 +1,1526 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 09 +
                          + + +
                          + 05 +
                          + +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 逆序对的三种求法 + + 逆序对的三种求法 +
                          +
                          +
                          +
                          + + 逆序对的三种求法 +一、什么是逆序对? +对于给定的一段正整数序列,逆序对就是序列中 \(a_i>a_j\) 且 \(i<j\) 的有序对 + +二、怎么求逆序对 +1.归并排序解法 +归并排序可以很好的解决逆序对问题 +我们只需要计算跨越分 + +
                          +
                          + + 2021-09-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈树状数组 区间修改&区间查询 + + 浅谈树状数组 区间修改&区间查询 +
                          +
                          +
                          +
                          + + 浅谈树状数组 +区间修改&区间查询 +一、区间修改,单点查询 +首先我们可以先来想一下,树状数组的区间修改,单点查询怎么弄 +我们可以维护一个关于原数组的差分数组 +很容易知道 \(a_i=\sum\limits_{j=1}^{i}b_j\) + +
                          +
                          + + 2021-09-05 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          + + +
                          + 31 +
                          + +
                          + +
                          + + + + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + Vijos1659 河蟹王国 题解 + + Vijos1659 河蟹王国 题解 +
                          +
                          +
                          +
                          + + Vijos1659 河蟹王国 题解 +题目链接:Vijos1659 +河蟹王国 + +题意:维护一个数据结构,支持区间最大值查询、区间加操作 + +一看就线段树水题 +我们在建树时将最大值搞好查询就好了 +那么区间加怎么办? +显然区间加操作会将影响到的最大 + +
                          +
                          + + 2021-08-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + UVA1121 Subsequence 题解 + + UVA1121 Subsequence 题解 +
                          +
                          +
                          +
                          + + UVA1121 Subsequence 题解 +题目链接:UVA1121 +Subsequence + +题意:给定数组,找最短连续子序列使其和大于 \(S\) ,多组数据 + +解法一 +对于区间 \([l,r]\) ,若 \(\sum_{i=l}^{ + +
                          +
                          + + 2021-08-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1006 [NOIP2008 提高组] 传纸条 + + 洛谷P1006 [NOIP2008 提高组] 传纸条 +
                          +
                          +
                          +
                          + + 洛谷P1006 [NOIP2008 提高组] +传纸条 +题目链接:P1006 +[NOIP2008 提高组] 传纸条 + +题意:网格图, \((1,1)\) 到 \((n,m)\) 找两条不重合的路径,最大价值 +注:原题是 \((m,n)\) +, + +
                          +
                          + + 2021-08-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          27 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/28/index.html b/archives/page/28/index.html index e69de29bb2..bb4ed2112a 100644 --- a/archives/page/28/index.html +++ b/archives/page/28/index.html @@ -0,0 +1,1535 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 08 +
                          + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 数论题瞎做[1] + + 数论题瞎做[1] +
                          +
                          +
                          +
                          + + 数论题瞎做[1] +某个学MO的朋友给我看的题 +这题似乎是1994年国家数学集训队选拔考试D1T1 +怪不得我做了好久( +upd.20220513 害,他半途而废退役了 + +题面: +求四个所有的由四个自然数 \(a,b,c,d\) +组成的数组,使 + +
                          +
                          + + 2021-08-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P4878 [USACO05DEC]Layout G 题解 + + 洛谷P4878 [USACO05DEC]Layout G 题解 +
                          +
                          +
                          +
                          + + 洛谷P4878 [USACO05DEC]Layout +G 题解 +题目链接:P4878 +[USACO05DEC]Layout G + +题意:按编号排了 \(n\) +只奶牛,有的奶牛间必须相距小于等于一个距离,有的奶牛间必须相距大于等于一个距离, + +
                          +
                          + + 2021-08-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + POJ3723 Conscription 题解 + + POJ3723 Conscription 题解 +
                          +
                          +
                          +
                          + + POJ3723 Conscription 题解 +题目链接:POJ3723 +Conscription + +题意:要招 \(n\) +个女的, \(m\) 个男的,原价 \(10000\),如果招了关系亲密的(男女)人可以降价,求最小花费 + +首先, + +
                          +
                          + + 2021-08-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1047 [NOIP2005 普及组] 校门外的树 题解 + + 洛谷P1047 [NOIP2005 普及组] 校门外的树 题解 +
                          +
                          +
                          +
                          + + 洛谷P1047 [NOIP2005 +普及组] 校门外的树 题解 +前言 +如何把一道入门题写成省选题?(手动滑稽) +本题解是我在练习分块时突发奇想写的,真就把入门题写成省选题的感觉( +才发现原来这些简单题这么有趣( + +题目链接: P1047 +[ + +
                          +
                          + + 2021-08-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 15 +
                          +
                          +
                          +
                          + +
                          + + 浅谈珂朵莉树(ODT) + + 浅谈珂朵莉树(ODT) +
                          +
                          +
                          +
                          + + 浅谈珂朵莉树(ODT) +前言 +珂学家狂喜( + +一、珂朵莉树来源 +珂朵莉树,原名老司机树(Old Driver +Tree),在某场CF比赛中提出 +因为题目背景是《末日时在做什么?有没有空?可以来拯救吗?》中的珂朵莉,所以就叫珂朵莉树了 + +二、 + +
                          +
                          + + 2021-08-15 + + + + + q779 + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 13 +
                          +
                          +
                          +
                          + +
                          + + + 裴蜀定理及其证明 + + 裴蜀定理及其证明 +
                          +
                          +
                          +
                          + + 裴蜀定理及其证明 +前言 +原来裴蜀是法国数学家QwQ + +一、裴蜀定理 +对于 \(x,y\) +的二元一次不定方程 \(ax+by=c\) ,其有解的充要条件为 \(\gcd(a,b)\mid c\) +1.充分性证明 +充分性:若 \(\gcd(a + +
                          +
                          + + 2021-08-13 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 强制卸载vmware player + + ubuntu 强制卸载vmware player +
                          +
                          +
                          +
                          + + ubuntu 强制卸载vmware player +不知道什么时候下了vmware-player,然后怎么都删不掉 +解决方法 +打开终端,输入以下指令 locate vmware-player 然后会出现一大堆vmware +player的文件 + + +
                          +
                          + + 2021-08-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈舞蹈链(DLX) + + 浅谈舞蹈链(DLX) +
                          +
                          +
                          +
                          + + 浅谈舞蹈链(DLX) +前言 +舞蹈链的名字真好玩... + +一、舞蹈链概述 +舞蹈链 (Dancing links),也叫 DLX +,是由 Donald Knuth +提出的数据结构,目的是快速实现他提出的X算法。X算法是一种递归算法,时间复杂度不 + +
                          +
                          + + 2021-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          + + +
                          + 22 +
                          +
                          +
                          +
                          + +
                          + + 导数的基本公式推导 + + 导数的基本公式推导 +
                          +
                          +
                          +
                          + + 导数的基本公式推导 +主要推导了人教版A版数学选择性必修二上直接给出的基本的导数公式 +本文写于作者初三暑假,更新于高一暑假 +可能含有很多不足,如果您方便的话可以联系我修改 awa +大概率会在高二暑假再更新一次吧 + +一、导数的四则运算法则 +设 + +
                          +
                          + + 2021-07-22 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 蒙提霍尔问题及其推广 + + 蒙提霍尔问题及其推广 +
                          +
                          +
                          +
                          + + 蒙提霍尔问题及其推广 +前言 +蒙提霍尔问题在《人教版A版数学选择性必修三》上作为阅读与思考的材料出现 +本文会提供一种简单的解法并推广这个著名的问题 + +蒙提霍尔问题 +一、背景 +三门问题(Monty Hall +problem)亦称为蒙提霍尔问题、 + +
                          +
                          + + 2021-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          28 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/29/index.html b/archives/page/29/index.html index e69de29bb2..dd96e9da0a 100644 --- a/archives/page/29/index.html +++ b/archives/page/29/index.html @@ -0,0 +1,1551 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 07 +
                          + + +
                          + 10 +
                          +
                          +
                          +
                          + +
                          + + + 全源最短路 Johnson算法 + + 全源最短路 Johnson算法 +
                          +
                          +
                          +
                          + + 全源最短路 Johnson算法 +本文写于较早时期,之前对Dijkstra的理解不是很透彻 +已经修改了部分显然错误的内容,有空会再仔细检查的 +模板题:P5905 【模板】Johnson +全源最短路 +题意简述:给定一个包含 \(n\) 个结点和 + +
                          +
                          + + 2021-07-10 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 无序数组交换任意两个元素 最少交换次数 + + 无序数组交换任意两个元素 最少交换次数 +
                          +
                          +
                          +
                          + + 无序数组交换任意两个元素 +最少交换次数 +题目描述 +给定长度为 \(n\) +的无序数组,将数组中的元素按从小到大的顺序排列,每次可以交换任意两个元素,最少要交换几次? + +解题方法 +解法一(较繁琐) +我们可以遍历一遍原数组,如果当前元素不在正确 + +
                          +
                          + + 2021-07-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 浅谈补码的原理和正确性 + + 浅谈补码的原理和正确性 +
                          +
                          +
                          +
                          + + 浅谈补码的原理和正确性 +前言 +upd 2022.2.14 +我就该早点看《计算机组成原理》,补码的定义就是 + +一个 \(n\) 位二进制数 \(N\) 的二进制补码定义为 \(2^n-N\) + +不过本文还是严谨证明了它的正确性,以下为原文 +补 + +
                          +
                          + + 2021-07-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 05 +
                          + + +
                          + 15 +
                          + +
                          + +
                          + + + + + +
                          + 07 +
                          + +
                          + +
                          + + + + + +
                          + 03 +
                          + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 追逐鼠标指针的小猫~Oneko + + ubuntu 追逐鼠标指针的小猫~Oneko +
                          +
                          +
                          +
                          + + ubuntu +追逐鼠标指针的小猫~Oneko +前言 +最近发现了一个有趣的软件 Oneko +可以让一只小猫追着鼠标指针跑 +是不是很有趣? +一、下载Oneko +打开终端 sudo apt install oneko 安装即可 +然后它就会在应用程 + +
                          +
                          + + 2021-03-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 20 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 显示键盘按键 + + ubuntu 显示键盘按键 +
                          +
                          +
                          +
                          + + ubuntu 显示键盘按键 +前言 +在看一些主播玩游戏时,他们屏幕上会有一个虚拟键盘,可以显示按键 +当时觉得很神奇,就想着给ubuntu也弄一个 +庆幸的是,ubuntu的确有这种软件 + +一、KeyMon简介 +这个软件叫key-mon,全称Ke + +
                          +
                          + + 2021-03-20 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + linux及windows对拍程序 C++ + + linux及windows对拍程序 C++ +
                          +
                          +
                          +
                          + + linux及windows对拍程序 C++ +前言 +OI赛制的比赛中,选手不能看到自己的成绩,那么如何保证代码正确呢? +1.水品高 秒切 2.暴力+对拍 尝试调正解 +本文给出了linux和windows的对拍程序 + +一、什么是对拍? +在比赛中 + +
                          +
                          + + 2021-03-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu 内存占用过高导致卡死 解决办法 + + ubuntu 内存占用过高导致卡死 解决办法 +
                          +
                          +
                          +
                          + + ubuntu +内存占用过高导致卡死 解决办法 +一、具体表现 +例如下图 + + +在这里插入图片描述 + +注:图示版本为ubuntu18.04,现在我用的是ubuntu20.04 + +二、原因 +查阅到了一些资料 + +在Linux中经常发现空闲内存很少,似 + +
                          +
                          + + 2021-03-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 整数的划分 动态规划 + + 整数的划分 动态规划 +
                          +
                          +
                          +
                          + + 整数的划分 动态规划 +题目描述 +每个非负整数都可以被拆分,比如说 +2 = 2 +2 = 1+1 +3 = 3 +3 = 2+1 +3 = 1+1+1 +输入格式 一个非负整数\(n(0 +\leq n + +
                          +
                          + + 2021-02-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + ubuntu20.04 桌面图标显示异常及解决方法 + + ubuntu20.04 桌面图标显示异常及解决方法 +
                          +
                          +
                          +
                          + + ubuntu20.04 +桌面图标显示异常及解决方法 +前言 +更新至ubuntu20.04后,出现了一些以前没有的问题 +桌面上有些图标不显示 + +一、具体表现 +例如有一次我在做备忘录时 +我习惯地打开终端 +cd 桌面 +gedit 账号.txt +桌面 + +
                          +
                          + + 2021-02-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + CF652B z-sort 题解 + + CF652B z-sort 题解 +
                          +
                          +
                          +
                          + + CF652B z-sort 题解 +题目链接:CF652B +z-sort + +题意:一种叫Z排序的方法,奇数位递增,偶数位递减,给定数组请用此方法排序 + +题意要求奇数位递增,偶数位递减 +那每次只要输出最小值和最大值就可以了 +这里给出了优先队列的 + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          29 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/3/index.html b/archives/page/3/index.html index e69de29bb2..c191778df4 100644 --- a/archives/page/3/index.html +++ b/archives/page/3/index.html @@ -0,0 +1,1535 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 09 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[20] + + 模拟赛题讲解[20] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[20] +来自 AprilGrimoire +2022-08-09 noi.ac #2775 +题目描述: +小 Z 想出几道送分题。 +小 Z 有一列脑洞(共 \(m(m \leq +10^9)\) +个)。脑洞分为白色与黑色,白色脑洞会 + +
                          +
                          + + 2022-08-09 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[19] + + 模拟赛题讲解[19] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[19] +来自 yukuai26 +2022-08-08 noi.ac #2771 +题目背景: +\(\text{yukuai26}\) +喜欢爬山,所以他选择绕着山跑圈 +题目描述: +\(\text{yukuai26}\) +会跑一个环 + +
                          +
                          + + 2022-08-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[18] + + 模拟赛题讲解[18] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[18] +来自 yukuai26 +2022-08-08 noi.ac #2773 +题目背景: +搞个大新闻. jpg +题目描述: +你来到了幻想乡。首先你准备搞一个大新闻,获得文文的报道并变得出名,你决定对幻想乡的股票系统下手。 +你 + +
                          +
                          + + 2022-08-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 08 +
                          + +
                          + +
                          + + + + + +
                          + 08 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[17] + + 模拟赛题讲解[17] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[17] +来自 yukuai26 +2022-08-08 noi.ac #2772 +题目描述: +\(\text{ysgh}\) 有一个长度为 \(m\) 的,元素两两不同的序列。 +\(\text{emoairx}\) +想要知道这个 + +
                          +
                          + + 2022-08-08 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[16] + + 模拟赛题讲解[16] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[16] +来自 yukuai26 +2022-08-07 noi.ac #2764 +题目描述: +紫开始研究特殊的括号序列 +这些特殊括号序列的形式是若干个右括号(可以没有)在前,若干个左括号(可以没有)在后。 +比如 ((,))),) + +
                          +
                          + + 2022-08-07 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          + +
                          + +
                          + + + + + +
                          + 07 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[15] + + 模拟赛题讲解[15] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[15] +来自 yukuai26 +2022-08-07 noi.ac +#2763 +题目描述: +幻想乡有 \(n\) +个建筑,每个建筑里住了一些居民。 +有一些双向道路连接居民的家,共有 \(n-1\) +条道路,恰好让所有居民的家能 + +
                          +
                          + + 2022-08-07 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[14] + + 模拟赛题讲解[14] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[14] +来自 yukuai26 +2022-08-06 noi.ac #2755 +题目背景 +\(\text{yukuai26}\) +喜欢算术,但他又菜又爱玩,所以需要你的帮助 +题目描述: +小明给你 \(n\) +个正整数,他想取一 + +
                          +
                          + + 2022-08-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[13] + + 模拟赛题讲解[13] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[13] +来自 yukuai26 +2022-08-06 noi.ac #2757 +据说是从JOI搬过来的,赛时有位巨佬18min就A了 Orz +题目描述: +橙 ---- 作为幽幽子大人的朋友的使者, 非常贪玩, 也喜欢思考有趣 + + +
                          +
                          + + 2022-08-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 06 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[12] + + 模拟赛题讲解[12] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[12] +来自 yukuai26 +2022-08-06 noi.ac #2756 +题目描述: +曾经有一个 oj 叫做 bzoj, 里面有一题 bzoj1002 叫狼抓兔子 +由于 1002 过于有名且显眼,很多人 A 掉了他,每 + +
                          +
                          + + 2022-08-06 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 04 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[11] + + 模拟赛题讲解[11] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[11] +来自 xpp 2022-08-04 +noi.ac #2731 +题目描述: +xpp在玩祖玛游戏,他心血来潮想根据祖玛游戏出一个题。 +给一个序列 \(a_1,a_2,\dots,a_n\) +,你每次可以选择相同且相邻的三个 + +
                          +
                          + + 2022-08-04 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          3 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/30/index.html b/archives/page/30/index.html index e69de29bb2..32ccf5b45d 100644 --- a/archives/page/30/index.html +++ b/archives/page/30/index.html @@ -0,0 +1,950 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2021 +
                          + + + + +
                          + 02 +
                          + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + CF708A Letters Cyclic Shift 题解 + + CF708A Letters Cyclic Shift 题解 +
                          +
                          +
                          +
                          + + CF708A Letters Cyclic Shift +题解 +题目链接:CF708A +Letters Cyclic Shift + +题意:一次变换指将字母变为它前面一个字母,例如a变成z,b变成a,给定字符串,找出一个非空子串进行变换使得改变 + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + CF676A Nicholas and Permutation 题解 + + CF676A Nicholas and Permutation 题解 +
                          +
                          +
                          +
                          + + CF676A Nicholas and +Permutation 题解 +题目链接:CF676A +Nicholas and Permutation + +题意:给定数组,可以让两个数的位置交换,让最大值和最小值的位置的差的绝对值最大 + +先用\(c\ + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 11 +
                          +
                          +
                          +
                          + +
                          + + + AT1899 画像処理高橋君 题解 + + AT1899 画像処理高橋君 题解 +
                          +
                          +
                          +
                          + + AT1899 画像処理高橋君 题解 +题目链接:AT1899 +画像処理高橋君 +原题是日文的,我就不翻译了( + +题意:给出压缩后的图像,求压缩前的图像 +压缩是指对于各个像素,在其周围8个方向的像素中,只要有一个黑色像素,其像素就会变黑的处理 + + + +
                          +
                          + + 2021-02-11 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          30 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/4/index.html b/archives/page/4/index.html index e69de29bb2..085afc34e9 100644 --- a/archives/page/4/index.html +++ b/archives/page/4/index.html @@ -0,0 +1,1536 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 08 +
                          + + +
                          + 03 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[10] + + 模拟赛题讲解[10] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[10] +来自 xpp 2022-08-03 +noi.ac #2723 +题目描述 +lzr给了xpp一个字符串 \(s\) +和一个字符串 \(t\) ,xpp想通过 \(s\) 构造出 \(t\) ,构造方式为xpp每次选择 \( + +
                          +
                          + + 2022-08-03 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 02 +
                          + +
                          + +
                          + + + + + +
                          + 02 +
                          +
                          +
                          +
                          + +
                          + + + 初赛复习 + + 初赛复习 +
                          +
                          +
                          +
                          + + 初赛复习 +施工中...... +参考文献等等补上来。 +IT发展历史 +第一台计算机:ENIAC,1946年 +应用:计算,数据储存处理,通信,辅助工作等 +第一个程序员:Ada(女),有为此命名的程序语言 +图灵奖(计算机),菲尔兹奖(数学),诺贝尔 + +
                          +
                          + + 2022-08-02 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 07 +
                          + + +
                          + 31 +
                          + +
                          + +
                          + + + + + +
                          + 31 +
                          +
                          +
                          +
                          + +
                          + + + CF691E Xor-sequences 题解 + + CF691E Xor-sequences 题解 +
                          +
                          +
                          +
                          + + CF691E Xor-sequences 题解 +题目链接:CF691E +Xor-sequences + +题意:给定大小为 \(n\) 的集合 \(\{a_1,\dots,a_n\}\),从集合中选择 \(k\) 个数组成一个序列 \(x_1 + +
                          +
                          + + 2022-07-31 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          + +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + 线性代数-矩阵 + + 线性代数-矩阵 +
                          +
                          +
                          +
                          + + 线性代数-矩阵 +施工中,咕咕咕.... +矩阵乘法 +矩阵乘法的定义 +矩阵乘法的定义 \[ +(AB)_{i,j}=\sum_{k=1}^{m}A_{i,k}B_{k,j}\quad i\in[1,n],j\in[1,p] +\] 一个 \(n \ + +
                          +
                          + + 2022-07-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 30 +
                          +
                          +
                          +
                          + +
                          + + + ABC156E Roaming 题解 + + ABC156E Roaming 题解 +
                          +
                          +
                          +
                          + + ABC156E Roaming 题解 +题目链接:ABC156E +Roaming + +题意: +翻译来自我们模拟赛,可能和原题有区别 +输入两个数字 \(n,k\) ,初始时有 +\(n\) +个盒子,每个盒子中都装着一个小球,\(n\) +个小球之间不 + +
                          +
                          + + 2022-07-30 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + ABC167E Colorful Blocks 题解 + + ABC167E Colorful Blocks 题解 +
                          +
                          +
                          +
                          + + ABC167E Colorful Blocks 题解 +题目链接:ABC167E +Colorful Blocks + +题意: +翻译从我们模拟赛搬过来的,稍微有些不一样。 +有 \(n\) 个小球按照编号为 \(1−n\) +从左至右放成一排,你现在 + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[9] + + 模拟赛题讲解[9] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[9] +来自 Roundgod +2022-07-29 noi.ac #2693 +原题来自 ABC172E +NEQ +问题描述: +给定 \(n,m\),你需要计算满足以下条件的数组对 \(A=[a_1,a_2,\dots,a_n]\ + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + hdu4135 Co-prime 题解 + + hdu4135 Co-prime 题解 +
                          +
                          +
                          +
                          + + hdu4135 Co-prime 题解 +题目链接:hdu4135 +Co-prime + +题意: +\(T\) 组数据,每组给出 \(a,b,n\) ,求区间 \([a,b]\) 中有多少个数与 \(n\) 互质。 +Given a number + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          4 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/5/index.html b/archives/page/5/index.html index e69de29bb2..75fa687705 100644 --- a/archives/page/5/index.html +++ b/archives/page/5/index.html @@ -0,0 +1,1521 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + OI数学总结-组合数学 + + OI数学总结-组合数学 +
                          +
                          +
                          +
                          + + OI数学总结-组合数学 +施工中,咕咕咕.... +排列组合 +更多详见 小蓝书 +16.1 +下面两个指的是无重复的排列与组合 +排列数 +从 \(n\) 个不同元素中取 \(k(k\le n)\) +个不同元素,并按一定顺序排成一列的方案数 \[ +\m + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          + +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + ABC210E Ring MST 题解 + + ABC210E Ring MST 题解 +
                          +
                          +
                          +
                          + + ABC210E Ring MST 题解 +题目链接:ABC210E Ring +MST + +题意:给定一张 \(n\) 个点的无向图,顶点的编号为 \(0,1,\dots,n−1\) 。同时给出两个长度为 \(m\) 的数组 \(a_1,a_2, + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 29 +
                          +
                          +
                          +
                          + +
                          + + + ARC060B Digit Sum 题解 + + ARC060B Digit Sum 题解 +
                          +
                          +
                          +
                          + + ARC060B Digit Sum 题解 +题目链接:ARC060B Digit +Sum + +题意: +对于任意非负整数 \(x\) 和 \(m(2\le m)\) ,定义 \(f_m(x)\) 为 \(x\) 在 \(m\) 进制下的各位数字之 + +
                          +
                          + + 2022-07-29 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 数学常数表 + + 数学常数表 +
                          +
                          +
                          +
                          + + 一些闲着无聊可以瞎背背的常数,比如$e = \tt{2.718281828}$,$\pi = \tt{3.141592654}$,$\ln \pi = \tt{1.1447}$,$\sqrt{\pi} = \tt{1.7725}$等等... + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + CF906D Power Tower 题解 + + CF906D Power Tower 题解 +
                          +
                          +
                          +
                          + + CF906D Power Tower 题解 +题目链接:CF906D +Power Tower + +题意:给定长度为 \(n\) 的序列 \(a_i\) 和模数 \(p\) +\(Q\) 次询问区间 \([l,r]\) 的 \[ +a_l^{ {a_ + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + CF427C Checkposts 题解 + + CF427C Checkposts 题解 +
                          +
                          +
                          +
                          + + CF427C Checkposts 题解 +题目链接:CF427C +Checkposts + +题意: +懒得贴翻译,那个翻译太烂了 +Your city has $ n $ junctions. There are $ m $ one-way ro + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2424 约数和 题解 + + 洛谷P2424 约数和 题解 +
                          +
                          +
                          +
                          + + 洛谷P2424 约数和 题解 +题目链接:P2424 +约数和 + +题意: +对于一个数 \(X\),函数 \(f(X)\) 表示 \(X\) 所有约数的和。例如:\(f(6)=1+2+3+6=12\)。对于一个 \(X\),Smart 可以很快的 + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 28 +
                          + +
                          + +
                          + + + + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[8] + + 模拟赛题讲解[8] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[8] +来自 Roundgod +2022-07-27 noi.ac #2681 +题目描述: +给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\), 边数 +\(\vert E\vert=m\ + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          5 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/6/index.html b/archives/page/6/index.html index e69de29bb2..0b56f7a6dd 100644 --- a/archives/page/6/index.html +++ b/archives/page/6/index.html @@ -0,0 +1,1529 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 28 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[7] + + 模拟赛题讲解[7] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[7] +来自 Roundgod +2022-07-27 noi.ac #2682 +题目描述: +给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\) +,边数 \(\vert E\vert=m\ + +
                          +
                          + + 2022-07-28 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[6] + + 模拟赛题讲解[6] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[6] +来自 Roundgod +2022-07-27 noi.ac #2683 +题目描述: +给定一个连通无向图 \(G=(V,E)\) +,其中顶点个数 \(\vert V\vert=n\) , +边数 \(\vert E\vert + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          + +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[5] + + 模拟赛题讲解[5] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[5] +来自 Roundgod +2022-07-26 noi.ac #2678 +题目描述: +对于任意非负整数 \(x\) 和 \(m(2\le m\le 10)\) ,定义 \(f_m(x)\) 为 \(x\) 在 \(m\) + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[4] + + 模拟赛题讲解[4] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[4] +来自 Roundgod +2022-07-26 noi.ac #2680 +问题描述: +Berland由 \(n\) 个城市和 \(m\) 条双向道路构成,其中第 \(i\) 条道路连接城市 \(a_i\) 和 \(b_i\ + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 27 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[3] + + 模拟赛题讲解[3] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[3] +来自 Roundgod +2022-07-26 noi.ac #2679 +题目描述: +给定一个无向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数 \(|E|=m\) 。顶点编号为 \(1−N\), + +
                          +
                          + + 2022-07-27 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          + +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + OI tricks + + OI tricks +
                          +
                          +
                          +
                          + + OI tricks +平时做题的时候发现的一些技巧,还没有仔细整理 +因此本文比较像草稿般的个人总结 +1.终极快读 +namespace FastIO +{ + #define gc() readchar() + #define pc(a) + +
                          +
                          + + 2022-07-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[2] + + 模拟赛题讲解[2] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[2] +来自 Roundgod +2022-07-25 noi.ac #2677 +题目描述: +给定一个有向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数 \(|E|=m\) 。顶点编号为 \(1−N\) , + +
                          +
                          + + 2022-07-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 26 +
                          +
                          +
                          +
                          + +
                          + + + 模拟赛题讲解[1] + + 模拟赛题讲解[1] +
                          +
                          +
                          +
                          + + 模拟赛题讲解[1] +来自 Roundgod +2022-07-25 noi.ac #2676 +题目描述: +给定一个带权连通无向图 \(G=(V,E)\) +,其中顶点个数 \(|V|=n\) ,边数\(|E|=m\) +图中可能包含重边以及自环。 + +
                          +
                          + + 2022-07-26 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 25 +
                          + +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          6 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/7/index.html b/archives/page/7/index.html index e69de29bb2..423df777b0 100644 --- a/archives/page/7/index.html +++ b/archives/page/7/index.html @@ -0,0 +1,1539 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 25 +
                          + +
                          + +
                          + + + + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + OI易错点-C++语法 + + OI易错点-C++语法 +
                          +
                          +
                          +
                          + + OI易错点-C++语法 +不保证本文内容完全正确,仅仅是个人总结!! +islower,isalpha,isdigit +这种函数,返回值不是bool!! +在不同的机器上跑出来是不一样的!!!!不要直接加上它!!! +lower_bound(beg + +
                          +
                          + + 2022-07-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 24 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2704 [NOI2001] 炮兵阵地 题解 + + 洛谷P2704 [NOI2001] 炮兵阵地 题解 +
                          +
                          +
                          +
                          + + 洛谷P2704 [NOI2001] 炮兵阵地 +题解 +题目链接:P2704 +[NOI2001] 炮兵阵地 + +题意: +司令部的将军们打算在 \(N\times M\) +的网格地图上部署他们的炮兵部队。 +一个 \(N\times M\) 的地图由 + +
                          +
                          + + 2022-07-24 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 24 +
                          + +
                          + +
                          + + + + + +
                          + 23 +
                          + +
                          + +
                          + + + + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 密码生成器 + + 密码生成器 +
                          +
                          +
                          +
                          + + 密码生成器 +简单的小破密码生成器 qwq +也不知道搞了这个有啥用处 +目前还比较脆弱,不支持不合法输入 +反正就是个瞎搞的东西 qwq +代码: +/* + Name: 密码生成器1.0 + Author: q779 + Date: 2021.5.20 + + +
                          +
                          + + 2022-07-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + 三子棋模拟器 for linux + + 三子棋模拟器 for linux +
                          +
                          +
                          +
                          + + 三子棋模拟器 for linux +中考前一周开摆写的 +采用的是minimax算法和 \(\alpha - +\beta\) 剪枝 +目前是基于规则的估价函数(因为q779不会AI) +目前在含C++环境的 ubuntu20.04 和 macOS + +
                          +
                          + + 2022-07-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 23 +
                          + +
                          + +
                          + + + + + +
                          + 23 +
                          +
                          +
                          +
                          + +
                          + + + CF888E Maximum Subsequence 题解 + + CF888E Maximum Subsequence 题解 +
                          +
                          +
                          +
                          + + CF888E Maximum Subsequence +题解 +题目链接:CF888E +Maximum Subsequence + +题意: +给定N个数,第i个数的值为Ai,你现在可以从中选择一些数字,问选出数字的和模P最大为多少。 +输入第一行为N + +
                          +
                          + + 2022-07-23 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          + +
                          + +
                          + + + + + +
                          + 22 +
                          +
                          +
                          +
                          + +
                          + + + CF479E Riding in a Lift 题解 + + CF479E Riding in a Lift 题解 +
                          +
                          +
                          +
                          + + CF479E Riding in a Lift 题解 +题目链接:CF479E +Riding in a Lift + +题意: +现在有n个传送点呈序列排列,编号为1到n +每一次可以通过折跃从一个传送点传送到另一处传送点, +由于折跃需要消耗巨大的能 + +
                          +
                          + + 2022-07-22 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          7 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/8/index.html b/archives/page/8/index.html index e69de29bb2..45013a4d59 100644 --- a/archives/page/8/index.html +++ b/archives/page/8/index.html @@ -0,0 +1,1551 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2657 [SCOI2009] windy 数 题解 + + 洛谷P2657 [SCOI2009] windy 数 题解 +
                          +
                          +
                          +
                          + + 洛谷P2657 [SCOI2009] windy 数 +题解 +题目链接:P2657 +[SCOI2009] windy 数 + +题意: +不含前导零且相邻两个数字之差至少为 \(2\) 的正整数被称为 windy 数。windy +想知道,在 \(a + +
                          +
                          + + 2022-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1410 子序列 题解 + + 洛谷P1410 子序列 题解 +
                          +
                          +
                          +
                          + + 洛谷P1410 子序列 题解 +题目链接:P1410 +子序列 + +题意: +给定一个长度为 \(N\)(\(N\) +为偶数)的序列,问能否将其划分为两个长度为 \(N +/ 2\) 的严格递增子序列。 +【数据范围】 +共三组数据,每组数据行数< + +
                          +
                          + + 2022-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 21 +
                          +
                          +
                          +
                          + +
                          + + + CF475D CGCDSSQ 题解 + + CF475D CGCDSSQ 题解 +
                          +
                          +
                          +
                          + + CF475D CGCDSSQ 题解 +题目链接:CF475D +CGCDSSQ + +题意: +给出一个长度为 \(n\) 的序列和 \(q\) 个询问, +每个询问输出一行,询问满足 \(\gcd\{a_l,a_{l+1},\dots,a_r\}=x + +
                          +
                          + + 2022-07-21 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 21 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + + + + + +
                          + 20 +
                          + +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + OI模板 + + OI模板 +
                          +
                          +
                          +
                          + + OI模板 +由于文件比较多,分为了多个部分。 + + + + + + + +Parts +包含内容 + + + + +OI模板-图论 +最短路算法、最小生成树、线段树优化建图、判负环、kosaraju算法、Tarjan算法 +[连通性问题]、欧拉路径、欧拉回路、2-SAT、 + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-图论 + + OI模板-图论 +
                          +
                          +
                          +
                          + + OI模板-图论 +最短路算法 +dijkstra +P4779 +【模板】单源最短路径(标准版) +优先队列优化 \(O((n+m)\log m)\) +#include <iostream> +#include <string> +#incl + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-字符串 + + OI模板-字符串 +
                          +
                          +
                          +
                          + + OI模板-字符串 +字符串哈希 +单哈希 +给定 \(N\) 个字符串(第 \(i\) 个字符串长度为 \(M_i\),字符串内包含数字、大小写字母,大小写敏感),请求出 +\(N\) +个字符串中共有多少个不同的字符串。 +P3370 +【模板】字符串 + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-算法 + + OI模板-算法 +
                          +
                          +
                          +
                          + + OI模板-算法 +排序算法 +其他乱七八糟的毛用没有(归并、松氏基排除外,还没补上来,咕咕咕...) +基本上一个sort全部搞定 +计数排序 +时间复杂度 \(O(n)\) +空间复杂度 \(O(\max\{n,\max\limits_{0<i + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          8 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/archives/page/9/index.html b/archives/page/9/index.html index e69de29bb2..35c1fb3b88 100644 --- a/archives/page/9/index.html +++ b/archives/page/9/index.html @@ -0,0 +1,1533 @@ + + + + + + + + + + + + + + + + + + + 归档 | Q779的博客 + + + + + + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          +
                          +
                          +
                          + + Welcome + +
                          + +
                          + + + + + +
                          +
                          +
                          +
                          + + + + + +
                          +
                          + +
                          + + +
                          +
                          +
                          +
                          +
                          + + + + + + + + +
                          + +
                          + + + +
                          + 2022 +
                          + + + + +
                          + 07 +
                          + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-其他 + + OI模板-其他 +
                          +
                          +
                          +
                          + + OI模板-其他 +光速幂 +仅适用于int 且 底数相同,即 \(a^b\) , \(a\) 不变,复杂度约为 \(O(\sqrt{n})\) +#include <bits/stdc++.h> +using namespace std; +# + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-计算几何 + + OI模板-计算几何 +
                          +
                          +
                          +
                          + + OI模板-计算几何 +二维凸包 +Andrew +时间复杂度 \(O(n \log n)\) +#include <bits/stdc++.h> +using namespace std; +#define int long long +#defi + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + OI模板-数据结构 + + OI模板-数据结构 +
                          +
                          +
                          +
                          + + OI模板-数据结构 +并查集 +具@Roundgod老师说 +按秩合并+路径压缩才能保证并查集查询的复杂度为 \(O(\alpha(n))\) +只用一个就是 \(O(\log n)\) +的,不过要特意构造数据卡才会到这个上界 +int f[N],r + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 19 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2391 白雪皑皑 题解 + + 洛谷P2391 白雪皑皑 题解 +
                          +
                          +
                          +
                          + + 洛谷P2391 白雪皑皑 题解 +题目链接:P2391 +白雪皑皑 + +题意: +现在有 \(n\) 片雪花排成一列。 pty +要对雪花进行 \(m\) 次染色操作,第 \(i\) 次染色操作中,把第 \(((i\times p+q)\bmod n + +
                          +
                          + + 2022-07-19 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          + +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1792 [国家集训队]种树 题解 + + 洛谷P1792 [国家集训队]种树 题解 +
                          +
                          +
                          +
                          + + 洛谷P1792 [国家集训队]种树 +题解 +题目链接:P1792 +[国家集训队]种树 + +题意: +A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。 +园林部门得到指令后,初步规划出 \(n\) +个种树的位置, + +
                          +
                          + + 2022-07-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          + +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P1631 序列合并 题解 + + 洛谷P1631 序列合并 题解 +
                          +
                          +
                          +
                          + + 洛谷P1631 序列合并 题解 +题目链接:P1631 +序列合并 + +题意: +有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到\(N^2\)个和,求这\(N^2\)个和中最小的N个。 +对于100%的数据中,满足1<=N< + +
                          +
                          + + 2022-07-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 18 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3871 [TJOI2010]中位数 题解 + + 洛谷P3871 [TJOI2010]中位数 题解 +
                          +
                          +
                          +
                          + + 洛谷P3871 [TJOI2010]中位数 +题解 +题目链接:P3871 +[TJOI2010]中位数 + +题意: +给定一个由N个元素组成的整数序列,现在有两种操作: +1 add a +在该序列的最后添加一个整数a,组成长度为N + 1的整数序列 + + +
                          +
                          + + 2022-07-18 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          + +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P2422 良好的感觉 题解 + + 洛谷P2422 良好的感觉 题解 +
                          +
                          +
                          +
                          + + 洛谷P2422 良好的感觉 题解 +题目链接:P2422 +良好的感觉 + +题意: +kkk 做了一个人体感觉分析器。每一天,人都有一个感受值 \(A_i\),\(A_i\) 越大,表示人感觉越舒适。在一段时间 +\(\left[i, j\right + +
                          +
                          + + 2022-07-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + + + + + +
                          + 17 +
                          +
                          +
                          +
                          + +
                          + + + 洛谷P3512 [POI2010]PIL-Pilots 题解 + + 洛谷P3512 [POI2010]PIL-Pilots 题解 +
                          +
                          +
                          +
                          + + 洛谷P3512 [POI2010]PIL-Pilots +题解 +题目链接:P3512 +[POI2010]PIL-Pilots + +题意:给定 \(n\) +个数,找一个最长区间,使得区间中的最大值减最小值不超过 \(k\) + +题面写的太烂了 +考虑 + +
                          +
                          + + 2022-07-17 + + + + + + + + + +
                          +
                          + + + + +
                          +
                          +
                          +
                          + +
                          + +
                          + + +
                          +
                          +
                          + + + + + +
                          +
                          +
                          9 / 30
                          +
                          +
                          + + + + + +
                          +
                          +
                          + + + +
                          + +
                          +
                          + Copyright © + + 2022 + + 2022 + q779 + | Powered by Hexo + | Theme Matery +
                          + +   站点总字数: 322.9k 字 + + + + + + + + |  总访问量:  次 + + + + + |  总访问人数:  人 + + +
                          + + 载入运行时间... + + +
                          + +
                          + +
                          +
                          + +
                          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/atom.xml b/atom.xml index b9b9b5ae26..ff3971edae 100644 --- a/atom.xml +++ b/atom.xml @@ -28,7 +28,9 @@ - <h1 id="CF526G-Spiders-Evil-Plan-题解"><a href="#CF526G-Spiders-Evil-Plan-题解" class="headerlink" title="CF526G Spiders Evil Plan + <h1 id="cf526g-spiders-evil-plan-题解">CF526G Spiders Evil Plan +题解</h1> +<p>题目链接:<a @@ -57,7 +59,8 @@ - <h1 id="LOJ6269-烷基计数-加强版-题解"><a href="#LOJ6269-烷基计数-加强版-题解" class="headerlink" title="LOJ6269 烷基计数 加强版 题解"></a>LOJ6269 烷基计数 加强版 + <h1 id="loj6269-烷基计数-加强版-题解">LOJ6269 烷基计数 加强版 题解</h1> +<p>题目链接:<a href="https://loj.ac/p/6269">#6269. @@ -86,7 +89,9 @@ - <h1 id="洛谷P3825-NOI2017-游戏-题解"><a href="#洛谷P3825-NOI2017-游戏-题解" class="headerlink" title="洛谷P3825 [NOI2017] 游戏 题解"></a>洛谷P3825 [NOI2017] 游戏 + <h1 id="洛谷p3825-noi2017-游戏-题解">洛谷P3825 [NOI2017] 游戏 题解</h1> +<p>题目链接:<a href="https://www.luogu.com.cn/problem/P3825">P3825 +[NOI2017] @@ -117,7 +122,9 @@ - <h1 id="洛谷P6348-PA2011-Journeys-题解"><a href="#洛谷P6348-PA2011-Journeys-题解" class="headerlink" title="洛谷P6348 [PA2011]Journeys + <h1 id="洛谷p6348-pa2011journeys-题解">洛谷P6348 [PA2011]Journeys +题解</h1> +<p>题目链接:<a @@ -146,7 +153,8 @@ - <h1 id="CF786B-Legacy-题解"><a href="#CF786B-Legacy-题解" class="headerlink" title="CF786B Legacy 题解"></a>CF786B Legacy 题解</h1><p>题目链接:<a + <h1 id="cf786b-legacy-题解">CF786B Legacy 题解</h1> +<p>题目链接:<a @@ -175,7 +183,10 @@ - <h1 id="洛谷P3311-SDOI2014-数数-题解"><a href="#洛谷P3311-SDOI2014-数数-题解" class="headerlink" title="洛谷P3311 [SDOI2014] 数数 题解"></a>洛谷P3311 + <h1 id="洛谷p3311-sdoi2014-数数-题解">洛谷P3311 [SDOI2014] 数数 +题解</h1> +<p>题目链接:<a href="https://www.luogu.com.cn/problem/P3311">P3311 +[SDOI2014] @@ -204,7 +215,9 @@ - <h1 id="洛谷P3193-HNOI2008-GT考试-题解"><a href="#洛谷P3193-HNOI2008-GT考试-题解" class="headerlink" title="洛谷P3193 [HNOI2008]GT考试 题解"></a>洛谷P3193 + <h1 id="洛谷p3193-hnoi2008gt考试-题解">洛谷P3193 [HNOI2008]GT考试 +题解</h1> +<p>题目链接:<a @@ -235,8 +248,9 @@ - <h1 id="OI数学总结-博弈论"><a href="#OI数学总结-博弈论" class="headerlink" title="OI数学总结-博弈论"></a>OI数学总结-博弈论</h1><p>施工中,咕咕咕…</p> -<h2 id="博弈论基础"><a + <h1 id="oi数学总结-博弈论">OI数学总结-博弈论</h1> +<p>施工中,咕咕咕...</p> +<h2 @@ -263,7 +277,11 @@ - <h1 id="OI数学总结-其他"><a href="#OI数学总结-其他" class="headerlink" + <h1 id="oi数学总结-其他">OI数学总结-其他</h1> +<p>本文充满了从各种地方<del>偷学来</del>的东西以及没什么分类的东西</p> +<h2 id="数值积分">数值积分</h2> +<h3 id="定积分">定积分</h3> +<p>简单来说,函数 @@ -290,8 +308,11 @@ - <h1 id="OI数学总结-群论"><a href="#OI数学总结-群论" class="headerlink" title="OI数学总结-群论"></a>OI数学总结-群论</h1><p>施工中,咕咕咕….</p> -<h2 id="群的基本概念"><a + <h1 id="oi数学总结-群论">OI数学总结-群论</h1> +<p>施工中,咕咕咕....</p> +<h2 id="群的基本概念">群的基本概念</h2> +<h3 id="群的定义">群的定义</h3> +<p>群公理包含下述四个性质(有时略去封闭性,只有三个性质)。若集合 @@ -318,8 +339,11 @@ - <h1 id="OI数学总结-数论"><a href="#OI数学总结-数论" class="headerlink" title="OI数学总结-数论"></a>OI数学总结-数论</h1><p>施工中,咕咕咕….</p> -<h2 id="积性函数"><a + <h1 id="oi数学总结-数论">OI数学总结-数论</h1> +<p>施工中,咕咕咕....</p> +<h2 id="积性函数">积性函数</h2> +<p>数论中定义:定义域为正整数域的函数 <span +class="math inline">\(f(n)\)</span> @@ -346,7 +370,9 @@ - <h1 id="CF708E-Student’s-Camp-题解"><a href="#CF708E-Student’s-Camp-题解" class="headerlink" title="CF708E Student’s Camp 题解"></a>CF708E + <h1 id="cf708e-students-camp-题解">CF708E Student's Camp 题解</h1> +<p>题目链接:<a href="https://www.luogu.com.cn/problem/CF708E">CF708E +Student's @@ -375,7 +401,9 @@ - <h1 id="CF178F3-Representative-Sampling-题解"><a href="#CF178F3-Representative-Sampling-题解" class="headerlink" title="CF178F3 Representative + <h1 id="cf178f3-representative-sampling-题解">CF178F3 Representative +Sampling 题解</h1> +<p>题目链接:<a @@ -406,7 +434,8 @@ - <h1 id="BZOJ2141-排队-题解"><a href="#BZOJ2141-排队-题解" class="headerlink" title="BZOJ2141 排队 题解"></a>BZOJ2141 排队 题解</h1><p>题目链接:<a + <h1 id="bzoj2141-排队-题解">BZOJ2141 排队 题解</h1> +<p>题目链接:<a @@ -431,8 +460,11 @@ - <h1 id="16-1"><a href="#16-1" class="headerlink" title="16.1"></a>16.1</h1><p>施工中,咕咕咕….</p> -<h2 id="计数原理"><a href="#计数原理" + <h1 id="section">16.1</h1> +<p>施工中,咕咕咕....</p> +<h2 id="计数原理">计数原理</h2> +<h3 id="加法原理">加法原理</h3> +<p>设完成某件事有 <span class="math @@ -457,7 +489,8 @@ - <h1 id="CF1491F-Magnets-题解"><a href="#CF1491F-Magnets-题解" class="headerlink" title="CF1491F Magnets 题解"></a>CF1491F Magnets + <h1 id="cf1491f-magnets-题解">CF1491F Magnets 题解</h1> +<p>题目链接:<a @@ -484,7 +517,9 @@ - <h1 id="洛谷P2396-yyy-loves-Maths-VII-题解"><a href="#洛谷P2396-yyy-loves-Maths-VII-题解" class="headerlink" title="洛谷P2396 yyy loves Maths VII + <h1 id="洛谷p2396-yyy-loves-maths-vii-题解">洛谷P2396 yyy loves Maths +VII 题解</h1> +<p>题目链接:<a href="https://www.luogu.com.cn/problem/P2396">P2396 @@ -511,7 +546,9 @@ - <h1 id="CF1073E-Segment-Sum-题解"><a href="#CF1073E-Segment-Sum-题解" class="headerlink" title="CF1073E Segment Sum 题解"></a>CF1073E Segment Sum + <h1 id="cf1073e-segment-sum-题解">CF1073E Segment Sum 题解</h1> +<p>题目链接:<a href="https://www.luogu.com.cn/problem/CF1073E">CF1073E +Segment @@ -538,7 +575,9 @@ - <h1 id="SP10606-BALNUM-Balanced-Numbers-题解"><a href="#SP10606-BALNUM-Balanced-Numbers-题解" class="headerlink" title="SP10606 BALNUM - + <h1 id="sp10606-balnum---balanced-numbers-题解">SP10606 BALNUM - +Balanced Numbers 题解</h1> +<p>题目链接:<a @@ -565,7 +604,8 @@ - <h1 id="UOJ66-新年的巧克力棒-题解"><a href="#UOJ66-新年的巧克力棒-题解" class="headerlink" title="UOJ66 新年的巧克力棒 题解"></a>UOJ66 新年的巧克力棒 题解</h1><p>题目链接:<a + <h1 id="uoj66-新年的巧克力棒-题解">UOJ66 新年的巧克力棒 题解</h1> +<p>题目链接:<a diff --git a/categories/OI/index.html b/categories/OI/index.html index b6b3b3520e..986f1214f2 100644 --- a/categories/OI/index.html +++ b/categories/OI/index.html @@ -404,12 +404,15 @@
                          - CF526G Spiders Evil Plan 题解题目链接:CF526G Spiders Evil Plan + CF526G Spiders Evil Plan +题解 +题目链接:CF526G +Spiders Evil Plan 题意: 二次魔改自 link 最近 q779 迷上了一个叫做割绳子 3 的yúsì(游戏)。 -这个游戏的要求是你要扮演蜘蛛去 +这个游戏的要求是你要扮演蜘蛛
                          @@ -463,11 +466,15 @@
                          - LOJ6269 烷基计数 加强版 题解题目链接:#6269. 烷基计数 加强版 + LOJ6269 烷基计数 加强版 题解 +题目链接:#6269. 烷基计数 +加强版 -题意:众所周知,大连 24 中是一所神奇的学校,在那里,化竞的同学很多都擅长写代码。 +题意:众所周知,大连 24 +中是一所神奇的学校,在那里,化竞的同学很多都擅长写代码。 有一天,化学不及格的胡小兔向化竞巨佬晴岚请教化学题: -“$n$ 个碳原子 +“\(n\) +个
                          @@ -521,11 +528,14 @@
                          - 洛谷P3825 [NOI2017] 游戏 题解题目链接:P3825 [NOI2017] 游戏 + 洛谷P3825 [NOI2017] 游戏 题解 +题目链接:P3825 +[NOI2017] 游戏 题意: -小 L 计划进行 $n$ 场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。 -小 L 的赛车有三辆,分别用大写字母 +小 L 计划进行 \(n\) +场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。 +小 L 的赛车有三辆,分别用大写
                          @@ -583,13 +593,16 @@
                          - 洛谷P6348 [PA2011]Journeys 题解题目链接:P6348 [PA2011]Journeys + 洛谷P6348 [PA2011]Journeys +题解 +题目链接:P6348 +[PA2011]Journeys 题意: -给定 $n$ 个结点, $m$ 次操作 -每次操作给出 $a,b,c,d$ ,表示 - -\forall x\in [a,b] +给定 \(n\) 个结点, \(m\) 次操作 +每次操作给出 \(a,b,c,d\) ,表示 +\[ +\forall x
                          @@ -643,14 +656,15 @@
                          - CF786B Legacy 题解题目链接:CF786B Legacy + CF786B Legacy 题解 +题目链接:CF786B +Legacy 题意: -$n$ 个结点, $q$ 次操作,问操作后从 $s$ 出发的单源最短路 +\(n\) 个结点, \(q\) 次操作,问操作后从 \(s\) 出发的单源最短路 操作如下 -输入 1 u v w 表示 $u$ 到 $v$ 连一条有向边 -输入 2 u +输入 1 u v w 表示 \(u\) 到 \(v\) 连一条
                          @@ -704,10 +718,14 @@
                          - 洛谷P3311 [SDOI2014] 数数 题解题目链接:P3311 [SDOI2014] 数数 + 洛谷P3311 [SDOI2014] 数数 +题解 +题目链接:P3311 +[SDOI2014] 数数 题意: -我们称一个正整数 $x$ 是幸运数,当且仅当它的十进制表示中不包含数字串集合 $s$ 中任意一个元素作为其子串。例如当 $s = \ +我们称一个正整数 \(x\) +是幸运数,当且仅当它的十进制表示中不包含数字串集合 \(s\) 中任意一个元素作为其子串。例如当 \
                          @@ -761,10 +779,14 @@
                          - 洛谷P3193 [HNOI2008]GT考试 题解题目链接:P3193 [HNOI2008]GT考试 + 洛谷P3193 [HNOI2008]GT考试 +题解 +题目链接:P3193 +[HNOI2008]GT考试 题意: -阿申准备报名参加 GT 考试,准考证号为 $N$ 位数$X_1,X_2…X_n(0\le X_i\le9)$,他不希望准考证号上 +阿申准备报名参加 GT 考试,准考证号为 \(N\) 位数\(X_1,X_2…X_n(0\le +X_i\le9)\),他不希望
                          @@ -822,8 +844,10 @@
                          - OI数学总结-博弈论施工中,咕咕咕… -博弈论基础对于博弈论的题目,重点考虑以下两点 + OI数学总结-博弈论 +施工中,咕咕咕... +博弈论基础 +对于博弈论的题目,重点考虑以下两点 什么时候可以判定胜利者 先手的影响 @@ -878,8 +902,11 @@
                          - OI数学总结-其他本文充满了从各种地方偷学来的东西以及没什么分类的东西 -数值积分定积分简单来说,函数 $f(x)$ 在区间 $[l,r]$ 上的定积分 $\int_{l}^{r}f(x)\mathrm{d}x$ 指的是 $f(x)$ 在区间 + OI数学总结-其他 +本文充满了从各种地方偷学来的东西以及没什么分类的东西 +数值积分 +定积分 +简单来说,函数 \(f(x)\) 在区间 \([l,r]\) 上的定积分 \(\int_{l}^{r}f(x)\mathrm{d}x\) 指的是 \
                          @@ -929,8 +956,11 @@
                          - OI数学总结-群论施工中,咕咕咕…. -群的基本概念群的定义群公理包含下述四个性质(有时略去封闭性,只有三个性质)。若集合 $G\neq\varnothing$ 和 $G$ 上的运算 $\cdot$ 构成的代数结构 $(G,\cdot)$ 满 + OI数学总结-群论 +施工中,咕咕咕.... +群的基本概念 +群的定义 +群公理包含下述四个性质(有时略去封闭性,只有三个性质)。若集合 \(G\neq\varnothing\) 和 \(G\) 上的运算 \(\cdot\) 构成的代数结构 \(
                          @@ -980,11 +1010,12 @@
                          - OI数学总结-数论施工中,咕咕咕…. -积性函数数论中定义:定义域为正整数域的函数 $f(n)$ , -且满足 $\forall a,b \in \mathbb{Z} _+,f(ab)=f(a)f(b) \land \gcd(a,b)=1$ - - + OI数学总结-数论 +施工中,咕咕咕.... +积性函数 +数论中定义:定义域为正整数域的函数 \(f(n)\) , +且满足 \(\forall a,b \in \mathbb{Z} +_+,f(ab)=f(a)f(b) \land \gcd(a,
                          @@ -1034,12 +1065,14 @@
                          - CF708E Student’s Camp 题解题目链接:CF708E Student’s Camp + CF708E Student's Camp 题解 +题目链接:CF708E +Student's Camp 题意: -有一个 $(n+2) \times m$ 的网格。 -给定 $n,m,a,b,k$ ,记 $p=\frac{a}{b}$ -除了 +有一个 \((n+2) \times m\) +的网格。 +给定 \(n,m,a,b,k\) ,记 \(p=\frac{a}{b}
                          @@ -1125,7 +1158,7 @@
                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/categories/OI/page/10/index.html b/categories/OI/page/10/index.html index 39bf41f2d8..37490f6a59 100644 --- a/categories/OI/page/10/index.html +++ b/categories/OI/page/10/index.html @@ -404,10 +404,12 @@
                          - 洛谷P3966 [TJOI2013]单词 题解题目链接:P3966 [TJOI2013]单词 + 洛谷P3966 [TJOI2013]单词 题解 +题目链接:P3966 +[TJOI2013]单词 题意: -小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多 +小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了
                          @@ -457,9 +459,12 @@
                          - 洛谷P4407 [JSOI2009] 电子字典 题解题目链接:P4407 [JSOI2009] 电子字典 + 洛谷P4407 [JSOI2009] 电子字典 +题解 +题目链接:P4407 +[JSOI2009] 电子字典 -题意:人们在英文字典中查找某个单词的时候可能不知道该单词的完整拼法,而只知道该单词的一个错误的近似拼法,这时人们可能陷入困境,为了查 +题意:人们在英文字典中查找某个单词的时候可能不知道该单词的完整拼法,而只知道该单词的一个错误的近似拼法,这时人们可能陷入困境,为了
                          @@ -513,10 +518,14 @@
                          - 洛谷P2922 [USACO08DEC]Secret Message G 题解题目链接:P2922 [USACO08DEC]Secret Message G + 洛谷P2922 +[USACO08DEC]Secret Message G 题解 +题目链接:P2922 +[USACO08DEC]Secret Message G 题意: -一句话题意:$m$ 次询问,查询已有的 $n$ 个 $\tt{01}$ +一句话题意:\(m\) 次询问,查询已有的 +\(n\) 个 \(\tt
                          @@ -566,13 +575,16 @@
                          - 洛谷P3586 [POI2015] LOG 题解题目链接:P3586 [POI2015] LOG + 洛谷P3586 [POI2015] LOG 题解 +题目链接:P3586 +[POI2015] LOG 题意: -维护一个长度为 $n$ 的序列,一开始都是 $0$,支持以下两种操作: +维护一个长度为 \(n\) +的序列,一开始都是 \(0\),支持以下两种操作: -U k a 将序列中第 $k$ 个数修改为 $a$。 -Z +U k a 将序列中第 \(k\) +个数修改为
                          @@ -622,10 +634,14 @@
                          - 洛谷P3879 [TJOI2010] 阅读理解 题解题目链接:P3879 [TJOI2010] 阅读理解 + 洛谷P3879 [TJOI2010] 阅读理解 +题解 +题目链接:P3879 +[TJOI2010] 阅读理解 题意: -英语老师留了 $N$ 篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些生词都 +英语老师留了 \(N\) +篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些
                          @@ -675,10 +691,12 @@
                          - 洛谷P1168 中位数 题解题目链接:P1168 中位数 + 洛谷P1168 中位数 题解 +题目链接:P1168 +中位数 题意: -给出一个长度为$N$的非负整数序列$A_i$,对于所有$1 ≤ k ≤ (N + 1) / 2$,输出$A_1, A_1 \sim A_3, …,A_1 \sim A_{2 +给出一个长度为\(N\)的非负整数序列\(A_i\),对于所有\(1 ≤ k ≤ (N + 1) / 2\),输出\(A_1, A_1 \sim A_3, …,A_1 \
                          @@ -728,10 +746,13 @@
                          - 洛谷P3958 [NOIP2017 提高组] 奶酪 题解题目链接:P3958 [NOIP2017 提高组] 奶酪 + 洛谷P3958 [NOIP2017 提高组] +奶酪 题解 +题目链接:P3958 +[NOIP2017 提高组] 奶酪 题意: -现有一块大奶酪,它的高度为 $h$,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以 +现有一块大奶酪,它的高度为 \(h\),它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我
                          @@ -781,14 +802,17 @@
                          - 洛谷P1816 忠诚 题解题目链接:P1816 忠诚 + 洛谷P1816 忠诚 题解 +题目链接:P1816 +忠诚 题意:区间min。 BIT写法也就图一乐(其实不建议) 主要在询问上有些区别, -例如如果 x-lowbit(x) 小于 $l$ ,则此时不能直接 x-=lowbit(x) -因为这样 +例如如果 x-lowbit(x) 小于 \(l\) ,则此时不能直接 +x-=lowbit(x) +因为
                          @@ -834,10 +858,15 @@
                          - 洛谷P4185 [USACO18JAN]MooTube G 题解题目链接:P4185 [USACO18JAN]MooTube G + 洛谷P4185 +[USACO18JAN]MooTube G 题解 +题目链接:P4185 +[USACO18JAN]MooTube G 题意: -给出一棵无根树, $m \le 10^5$ 次询问,给出 $k,v$ ,询问共有多少结点 $u$ 满 +给出一棵无根树, \(m \le 10^5\) +次询问,给出 \(k,v\) ,询问共有多少结点 +
                          @@ -891,10 +920,13 @@
                          - 洛谷P2085 最小函数值 题解题目链接:P2085 最小函数值 + 洛谷P2085 最小函数值 题解 +题目链接:P2085 +最小函数值 题意: -有 $n$ 个函数,分别为 $F_1,F_2,\dots,F_n$。定义 $F_i(x)=A_ix^2+B_ix+C_i(x\in\mathbb N^*)$。给定这 +有 \(n\) 个函数,分别为 \(F_1,F_2,\dots,F_n\)。定义 \(F_i(x)=A_ix^2+B_ix+C_i(x\in\mathbb +N^*
                          @@ -944,11 +976,14 @@
                          - 洛谷P2679 [NOIP2015 提高组] 子串 题解题目链接:P2679 [NOIP2015 提高组] 子串 + 洛谷P2679 [NOIP2015 提高组] +子串 题解 +题目链接:P2679 +[NOIP2015 提高组] 子串 题意: -有两个仅包含小写英文字母的字符串 $A$ 和 $B$。 -现在要从字符串 $A$ 中取出 $d$ 个互不重叠的非空子串 +有两个仅包含小写英文字母的字符串 \(A\) 和 \(B\)。 +现在要从字符串 \(A\) 中取出 \(d\) 个
                          @@ -1002,12 +1037,14 @@
                          - 洛谷P1868 饥饿的奶牛 题解题目链接:P1868 饥饿的奶牛 + 洛谷P1868 饥饿的奶牛 题解 +题目链接:P1868 +饥饿的奶牛 题意: 有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字。 现用汉语翻译为: -有 $N$ 个区间,每个区间 $x,y$ 表示提供的 $x\sim +有 \(N\) 个区间,每个区间 \(x,y\) 表示提供的 \
                          @@ -1090,7 +1127,7 @@
                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/categories/OI/page/11/index.html b/categories/OI/page/11/index.html index 41c5d2e468..9b3ca18e62 100644 --- a/categories/OI/page/11/index.html +++ b/categories/OI/page/11/index.html @@ -404,10 +404,14 @@
                          - 洛谷P4799 [CEOI2015 Day2] 世界冰球锦标赛 题解题目链接:P4799 [CEOI2015 Day2] 世界冰球锦标赛 + 洛谷P4799 [CEOI2015 +Day2] 世界冰球锦标赛 题解 +题目链接:P4799 +[CEOI2015 Day2] 世界冰球锦标赛 题意: -译自 CEOI2015 Day2 T1「Ice Hockey World Champions +译自 CEOI2015 Day2 T1「Ice Hockey +World Champion
                          @@ -453,10 +457,14 @@
                          - 洛谷P3435 [POI2006] OKR-Periods of Words 题解题目链接:P3435 [POI2006] OKR-Periods of Words + 洛谷P3435 [POI2006] +OKR-Periods of Words 题解 +题目链接:P3435 +[POI2006] OKR-Periods of Words 题意: -对于一个仅含小写字母的字符串 $a$,$p$ 为 $a$ 的前缀 +对于一个仅含小写字母的字符串 \(a\),\(p\) +为 \(
                          @@ -506,10 +514,13 @@
                          - 洛谷P4287 [SHOI2011]双倍回文 题解题目链接:P4287 [SHOI2011]双倍回文 + 洛谷P4287 [SHOI2011]双倍回文 +题解 +题目链接:P4287 +[SHOI2011]双倍回文 题意: -记字符串 $w$ 的倒置为 $w^R$ 。例如 $(\tt{abcd})^R=\tt{dcba}$ , $(\tt{abba}) +记字符串 \(w\) 的倒置为 \(w^R\) 。例如 \((\tt{abcd})^R=\tt{dcba}\) , \((\t
                          @@ -559,10 +570,13 @@
                          - 洛谷P2375 [NOI2014] 动物园 题解题目链接:P2375 [NOI2014] 动物园 + 洛谷P2375 [NOI2014] 动物园 +题解 +题目链接:P2375 +[NOI2014] 动物园 题意: -近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学向 +近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学
                          @@ -612,10 +626,16 @@
                          - 洛谷P4568 [JLOI2011] 飞行路线 题解题目链接:P4568 [JLOI2011] 飞行路线 + 洛谷P4568 [JLOI2011] 飞行路线 +题解 +题目链接:P4568 +[JLOI2011] 飞行路线 题意: -Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 $n$ 个城市设有业务,设这些 +Alice 和 Bob +现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 +\(n\) +个城市设有业务,
                          @@ -665,10 +685,12 @@
                          - 洛谷P4551 最长异或路径 题解题目链接:P4551 最长异或路径 + 洛谷P4551 最长异或路径 题解 +题目链接:P4551 +最长异或路径 -题意:给定一棵 $n$ 个点的带权树,结点下标从 $1$ 开始到 $n$。寻找树中找两个结点,求最长的异或路径。 -异或路径指的是指两个结点之间唯一路径上的所有边权的异或 +题意:给定一棵 \(n\) 个点的带权树,结点下标从 \(1\) 开始到 \(n\)。寻找树中找两个结点,求最长的异或路径。 +异或路径指的是指两个结点之间唯一路径上的
                          @@ -722,11 +744,15 @@
                          - 洛谷P3698 [CQOI2017]小Q的棋盘 题解题目链接:P3698 [CQOI2017]小Q的棋盘 + 洛谷P3698 [CQOI2017]小Q的棋盘 +题解 +题目链接:P3698 +[CQOI2017]小Q的棋盘 题意: 小 Q 正在设计一种棋类游戏。 -在小 Q 设计的游戏中,棋子可以放在棋盘上的格点中。某些格点之间有连线,棋子只能在有连线的格 +在小 Q +设计的游戏中,棋子可以放在棋盘上的格点中。某些格点之间有连线,棋子只能在有连线的
                          @@ -780,10 +806,13 @@
                          - 洛谷P1005 [NOIP2007 提高组] 矩阵取数游戏 题解题目链接:P1005 [NOIP2007 提高组] 矩阵取数游戏 + 洛谷P1005 [NOIP2007 +提高组] 矩阵取数游戏 题解 +题目链接:P1005 +[NOIP2007 提高组] 矩阵取数游戏 题意: -帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 $n \times m$ 的矩阵,矩阵中的每个元素 +帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 \(n \times m\) 的矩阵,矩阵中的每
                          @@ -833,12 +862,14 @@
                          - 洛谷P2585 [ZJOI2006]三色二叉树 题解题目链接:P2585 [ZJOI2006]三色二叉树 + 洛谷P2585 +[ZJOI2006]三色二叉树 题解 +题目链接:P2585 +[ZJOI2006]三色二叉树 题意: -一棵二叉树可以按照如下规则表示成一个由 $0$、$1$、$2$ 组成的字符序列,我们称之为“二叉树序列 $S$”: -S= -\ +一棵二叉树可以按照如下规则表示成一个由 \(0\)、\(1\)、\(2\) +组成的字符序列,我们称之为“二叉树序列 \(S
                          @@ -865,14 +896,14 @@ 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -896,14 +927,18 @@
                          - 洛谷P5520 [yLOI2019] 青原樱 题解题目链接:P5520 [yLOI2019] 青原樱 + 洛谷P5520 [yLOI2019] 青原樱 +题解 +题目链接:P5520 +[yLOI2019] 青原樱 题意: - $n$ 个空放 $m$ 个物品,两两物品不能直接相邻,至少空一格 +\(n\) 个空放 \(m\) +个物品,两两物品不能直接相邻,至少空一格 纯数学题。 -看看几个空不能放,啊 $m-1$ -那能放的就 +看看几个空不能放,啊 \(m-1\) +
                          @@ -953,10 +988,14 @@
                          - 洛谷P2822 [NOIP2016 提高组] 组合数问题 题解题目链接:P2822 [NOIP2016 提高组] 组合数问题 + 洛谷P2822 [NOIP2016 +提高组] 组合数问题 题解 +题目链接:P2822 +[NOIP2016 提高组] 组合数问题 题意: -组合数 $\binom{n}{m}$ 表示的是从 $n$ 个物品中选出 $m$ 个物品的方案数。举个例子 +组合数 \(\binom{n}{m}\) 表示的是从 +\(n\) 个物品中选出 \(m\) 个物品的方
                          @@ -1002,11 +1041,15 @@
                          - 洛谷P4127 [AHOI2009]同类分布 题解题目链接:P4127 [AHOI2009]同类分布 + 洛谷P4127 [AHOI2009]同类分布 +题解 +题目链接:P4127 +[AHOI2009]同类分布 题意: -给出两个数 $a,b$ ,求出 $[a,b]$ 中各位数字之和能整除原数的数的个数。 -对于所有的数据, $1 ≤ a ≤ b ≤ +给出两个数 \(a,b\) ,求出 \([a,b]\) +中各位数字之和能整除原数的数的个数。 +对于所有的数据, \(1 ≤ a
                          @@ -1089,7 +1132,7 @@
                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/categories/OI/page/12/index.html b/categories/OI/page/12/index.html index ca07e101c1..435f588330 100644 --- a/categories/OI/page/12/index.html +++ b/categories/OI/page/12/index.html @@ -404,10 +404,13 @@
                          - CF1036C Classy Numbers 题解题目链接:CF1036C Classy Numbers + CF1036C Classy Numbers 题解 +题目链接:CF1036C +Classy Numbers -题意:定义一个数字是“好数”,当且仅当它的十进制表示下有不超过$3$个数字$1 \sim 9$ -举个例子:$4,200000,102 +题意:定义一个数字是“好数”,当且仅当它的十进制表示下有不超过\(3\)个数字\(1 \sim +9\) +举个例子:\(4,2000
                          @@ -457,10 +460,14 @@
                          - 洛谷P1836 数页码 题解题目链接:P1836 数页码 + 洛谷P1836 数页码 题解 +题目链接:P1836 +数页码 题意: -一本书的页码是从 $1\sim n$ 编号的连续整数:$1,2,3,\cdots,n$。请你求出全部页码中所有单个数字的和,例如第 $123$ 页,它的和就是 $1+2+3 +一本书的页码是从 \(1\sim n\) +编号的连续整数:\(1,2,3,\cdots,n\)。请你求出全部页码中所有单个数字的和,例如第 +\(123\) 页,它的和就是
                          @@ -510,10 +517,13 @@
                          - SP3928 MDIGITS - Counting Digits 题解题目链接:SP3928 MDIGITS - Counting Digits + SP3928 MDIGITS - Counting +Digits 题解 +题目链接:SP3928 +MDIGITS - Counting Digits 题意: -给定两个整数 $a$ 和 $b$,求 $a$ 和 $b$ 之间的所有数字中 $0$ +给定两个整数 \(a\) 和 \(b\),求 \(a\) 和 \(b\) 之间的所
                          @@ -563,10 +573,13 @@
                          - 洛谷P4047 [JSOI2010]部落划分 题解题目链接:P4047 [JSOI2010]部落划分 + 洛谷P4047 [JSOI2010]部落划分 +题解 +题目链接:P4047 +[JSOI2010]部落划分 题意: -聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落 +聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部
                          @@ -616,13 +629,16 @@
                          - 洛谷P5018 [NOIP2018 普及组] 对称二叉树 题解题目链接:P5018 [NOIP2018 普及组] 对称二叉树 + 洛谷P5018 [NOIP2018 +普及组] 对称二叉树 题解 +题目链接:P5018 +[NOIP2018 普及组] 对称二叉树 题意: -一棵有点权的有根树如果满足以下条件,则被轩轩称为对称二叉树: +一棵有点权的有根树如果满足以下条件,则被轩轩称为对称二叉树: -二叉树; -将这棵树所有节点的左右子树 +二叉树; +将这棵树所有节点的左右子树交
                          @@ -672,10 +688,14 @@
                          - 洛谷P5536 【XR-3】核心城市 题解题目链接:P5536 【XR-3】核心城市 + 洛谷P5536 【XR-3】核心城市 +题解 +题目链接:P5536 +【XR-3】核心城市 题意: -X 国有 $n$ 座城市,$n - 1$ 条长度为 $1$ 的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和 +X 国有 \(n\) 座城市,\(n - 1\) 条长度为 \(1\) +的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达
                          @@ -725,10 +745,14 @@
                          - 洛谷P5194 [USACO05DEC]Scales S 题解题目链接:P5194 [USACO05DEC]Scales S + 洛谷P5194 [USACO05DEC]Scales +S 题解 +题目链接:P5194 +[USACO05DEC]Scales S 题意: -约翰有一架用来称牛的体重的天平。与之配套的是 $N$ ( $1 \leq N \leq 1000$ )个 +约翰有一架用来称牛的体重的天平。与之配套的是 \(N\) ( \(1 \leq N +\leq 1000
                          @@ -774,10 +798,13 @@
                          - 洛谷P3065 [USACO12DEC]First! G 题解题目链接:P3065 [USACO12DEC]First! G + 洛谷P3065 [USACO12DEC]First! G +题解 +题目链接:P3065 +[USACO12DEC]First! G 题意: -Bessie一直在研究字符串。她发现,通过改变字母表的顺序,她可以按改变后的字母表来排列字符串(字典序大 +Bessie一直在研究字符串。她发现,通过改变字母表的顺序,她可以按改变后的字母表来排列字符串(字典序
                          @@ -831,10 +858,14 @@
                          - 洛谷P2939 [USACO09FEB]Revamping Trails G 题解题目链接:P2939 [USACO09FEB]Revamping Trails G + 洛谷P2939 +[USACO09FEB]Revamping Trails G 题解 +题目链接:P2939 +[USACO09FEB]Revamping Trails G 题意: -约翰一共有 $N$ 个牧场.由 $M$ 条布满尘埃的小径连接。小 +约翰一共有 \(N\) 个牧场.由 \(M\) +条布满尘埃的小
                          @@ -888,10 +919,13 @@
                          - 洛谷P4683 [IOI2008] Type Printer 题解题目链接:P4683 [IOI2008] Type Printer + 洛谷P4683 [IOI2008] Type +Printer 题解 +题目链接:P4683 +[IOI2008] Type Printer 题意: -你需要利用一台可移动的打印机打印出$N$个单词。这种可移动式打印机是一种老式打印机,它需要你将一 +你需要利用一台可移动的打印机打印出\(N\)个单词。这种可移动式打印机是一种老式打印机,它需要
                          @@ -941,10 +975,13 @@
                          - 洛谷P3426 [POI2005]SZA-Template 题解题目链接:P3426 [POI2005]SZA-Template + 洛谷P3426 +[POI2005]SZA-Template 题解 +题目链接:P3426 +[POI2005]SZA-Template 题意:你打算在纸上印一串字母。 -为了完成这项工作,你决定刻一个印章。印章每使用一次,就会将印章上的所有字母印 +为了完成这项工作,你决定刻一个印章。印章每使用一次,就会将印章上的所有字母
                          @@ -998,10 +1035,15 @@
                          - 洛谷P2466 [SDOI2008] Sue 的小球 题解 题目链接:P2466 [SDOI2008] Sue 的小球 + 洛谷P2466 [SDOI2008] Sue +的小球 题解 + 题目链接:P2466 +[SDOI2008] Sue 的小球 题意: -Sue 和 Sandy 最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue 有一支 +Sue 和 Sandy +最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue +有一
                          @@ -1084,7 +1126,7 @@
                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/categories/OI/page/13/index.html b/categories/OI/page/13/index.html index a60290bb58..7a10874e41 100644 --- a/categories/OI/page/13/index.html +++ b/categories/OI/page/13/index.html @@ -404,11 +404,15 @@
                          - 洛谷P1608 路径统计 题解题目链接:P1608 路径统计 + 洛谷P1608 路径统计 题解 +题目链接:P1608 +路径统计 题意: 一句话题意:最短路计数。 -“RP 餐厅” 的员工素质就是不一般,在齐刷刷的算出同一个电话号码之后,就准备让 HZH,TZY 去送快餐了,他们将自己居住的城市画了一张地图 +“RP 餐厅” +的员工素质就是不一般,在齐刷刷的算出同一个电话号码之后,就准备让 HZH,TZY +去送快餐了,他们将自己居住的城市画了一张地
                          @@ -454,10 +458,14 @@
                          - 洛谷P5440 【XR-2】奇迹 题解题目链接:P5440 【XR-2】奇迹 + 洛谷P5440 【XR-2】奇迹 题解 +题目链接:P5440 +【XR-2】奇迹 题意: -我们称一个日期为一个八位数,第 1~4 位构成年,第 5~6 位构成月,第 7~8 位构成日,不足位数用 0 补足。同时,要求日期所代表的这一天真实存在 +我们称一个日期为一个八位数,第 1~4 位构成年,第 5~6 +位构成月,第 7~8 位构成日,不足位数用 0 +补足。同时,要求日期所代表的这一天真实存
                          @@ -503,10 +511,13 @@
                          - 洛谷P1514 [NOIP2010 提高组] 引水入城 题解题目链接:P1514 [NOIP2010 提高组] 引水入城 + 洛谷P1514 [NOIP2010 +提高组] 引水入城 题解 +题目链接:P1514 +[NOIP2010 提高组] 引水入城 题意: -在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个$N$ +在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个\(
                          @@ -552,10 +563,14 @@
                          - 洛谷P1535 [USACO08MAR]Cow Travelling S 题解题目链接:P1535 [USACO08MAR]Cow Travelling S + 洛谷P1535 +[USACO08MAR]Cow Travelling S 题解 +题目链接:P1535 +[USACO08MAR]Cow Travelling S 题意: -奶牛们在被划分成 $N$ 行 $M$ 列($2 \leq N,M \le +奶牛们在被划分成 \(N\) 行 \(M\) 列(\(2 \leq +N
                          @@ -605,10 +620,13 @@
                          - 洛谷P1378 油滴扩展 题解题目链接:P1378 油滴扩展 + 洛谷P1378 油滴扩展 题解 +题目链接:P1378 +油滴扩展 题意: -在一个长方形框子里,最多有 $N$ 个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界。必须等一个油滴扩展完毕才能 +在一个长方形框子里,最多有 \(N\) +个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界。必须等一个油滴扩展完
                          @@ -658,17 +676,19 @@
                          - 洛谷P4983 忘情 题解题目链接:P4983 忘情 + 洛谷P4983 忘情 题解 +题目链接:P4983 +忘情 题意: “为什么要离开我!” - “因为你没玩儿转!” - “我玩儿转了!” - “那好,你现在就给我维护这么一个式子!” - “为什么要出这么毒瘤的东西。” - “为了恶心你。” - “……” - +“因为你没玩儿转!” +“我玩儿转了!” +“那好,你现在就给我维护这么一个式子!” +“为什么要出这么毒瘤的东西。” +“为了恶心你。” +“......” +\
                          @@ -718,10 +738,13 @@
                          - 洛谷P3195 [HNOI2008]玩具装箱 题解题目链接:P3195 [HNOI2008]玩具装箱 + 洛谷P3195 [HNOI2008]玩具装箱 +题解 +题目链接:P3195 +[HNOI2008]玩具装箱 题意: -有 $n$ 个玩具,第 $i$ 个玩具的价值为 $c_i$ 。要求将这 $n$ 个玩具排成一排,分成若干段。对于一段 $[l,r +有 \(n\) 个玩具,第 \(i\) 个玩具的价值为 \(c_i\) 。要求将这 \(n\) 个玩具排成一排,分成若干段。对
                          @@ -775,11 +798,13 @@
                          - CF125E MST Company 题解题目链接:CF125E MST Company + CF125E MST Company 题解 +题目链接:CF125E +MST Company 题意: -给你一个有 $n$ 个节点,$m$ 条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足节点 $1$ 正好连了 $k$ 条边。 -输 +给你一个有 \(n\) 个节点,\(m\) +条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足节点 \(1\) 正好连了 \(
                          @@ -833,11 +858,15 @@
                          - 洛谷P5633 最小度限制生成树 题解题目链接:P5633 最小度限制生成树 + 洛谷P5633 最小度限制生成树 +题解 +题目链接:P5633 +最小度限制生成树 题意: -给你一个有 $n$ 个节点,$m$ 条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足编号为 $s$ 的节点正好连了 $k$ 条边。 -可能会 +给你一个有 \(n\) 个节点,\(m\) +条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足编号为 +\(s\) 的节点正好连了 \(k\
                          @@ -887,11 +916,15 @@
                          - 洛谷P2619 [国家集训队]Tree I 题解题目链接:P2619 [国家集训队]Tree I + 洛谷P2619 [国家集训队]Tree I +题解 +题目链接:P2619 +[国家集训队]Tree I 题意: -给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 $\text{need}$ 条白色边的生成树。 -题目保证有解。 +给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 +\(\text{need}\) 条白色边的生成树。 +题目保证
                          @@ -941,11 +974,15 @@
                          - 洛谷P2607 [ZJOI2008] 骑士 题解题目链接:P2607 [ZJOI2008] 骑士 + 洛谷P2607 [ZJOI2008] 骑士 +题解 +题目链接:P2607 +[ZJOI2008] 骑士 题意: -Z 国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。 -最近发生了一件可怕的事 +Z +国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。 +最近发生了一件可怕的
                          @@ -972,14 +1009,14 @@ 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -1003,10 +1040,16 @@
                          - 洛谷P1453 城市环路 题解题目链接:P1453 城市环路 + 洛谷P1453 城市环路 题解 +题目链接:P1453 +城市环路 题意: -整个城市可以看做一个 $n$ 个点,$n$ 条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有 $2$ 条简单路径互通。图中的其它部分皆隶属城 +整个城市可以看做一个 \(n\) +个点,\(n\) +条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有 +\(2\) +条简单路径互通。图中的其
                          @@ -1089,7 +1132,7 @@
                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/categories/OI/page/14/index.html b/categories/OI/page/14/index.html index 218200fc48..6e720bb50a 100644 --- a/categories/OI/page/14/index.html +++ b/categories/OI/page/14/index.html @@ -404,10 +404,13 @@
                          - SP1716 GSS3 - Can you answer these queries III 题解题目链接:SP1716 GSS3 - Can you answer these queries III + SP1716 GSS3 +- Can you answer these queries III 题解 +题目链接:SP1716 +GSS3 - Can you answer these queries III 题意: - $n$ 个数, $q$ 次 +\(n\) 个数, \(q
                          @@ -453,10 +456,13 @@
                          - 洛谷P1083 [NOIP2012 提高组] 借教室 题解题目链接:P1083 [NOIP2012 提高组] 借教室 + 洛谷P1083 [NOIP2012 +提高组] 借教室 题解 +题目链接:P1083 +[NOIP2012 提高组] 借教室 题意: -在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同 +在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不
                          @@ -502,10 +508,13 @@
                          - 洛谷P4552 [Poetize6] IncDec Sequence 题解题目链接:P4552 [Poetize6] IncDec Sequence + 洛谷P4552 [Poetize6] +IncDec Sequence 题解 +题目链接:P4552 +[Poetize6] IncDec Sequence 题意: -给定一个长度为 $n$ 的数列 ${a_1,a_2,\cdots,a_n}$,每 +给定一个长度为 \(n\) 的数列 \({a_1,a_2,\cdots,a_n
                          @@ -551,11 +560,16 @@
                          - 洛谷P5521 [yLOI2019] 梅深不见冬 题解题目链接:P5521 [yLOI2019] 梅深不见冬 + 洛谷P5521 [yLOI2019] +梅深不见冬 题解 +题目链接:P5521 +[yLOI2019] 梅深不见冬 + +题意: +给定一棵树,根节点为 \(1\) ,有点权 +\(w_i\) ,在结点上放满梅花 +放的要求是,只有一个结点 \(u\) - 题意: -给定一棵树,根节点为 $1$ ,有点权 $w_i$ ,在结点上放满梅花 -放的要求是,只有一个结点 $u$ 的所有儿子
                          @@ -609,9 +623,12 @@
                          - 洛谷P3131 [USACO16JAN]Subsequences Summing to Sevens S 题解题目链接:P3131 [USACO16JAN]Subsequences Summing to Sevens S + 洛谷P3131 +[USACO16JAN]Subsequences Summing to Sevens S 题解 +题目链接:P3131 +[USACO16JAN]Subsequences Summing to Sevens S - 题意:给你n个 +题意:给你n个
                          @@ -661,10 +678,13 @@
                          - 洛谷P2678 [NOIP2015 提高组] 跳石头 题解题目链接:P2678 [NOIP2015 提高组] 跳石头 + 洛谷P2678 [NOIP2015 +提高组] 跳石头 题解 +题目链接:P2678 +[NOIP2015 提高组] 跳石头 题意: -这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点 +这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起
                          @@ -710,10 +730,14 @@
                          - 洛谷P1314 [NOIP2011 提高组] 聪明的质监员 题解题目链接:P1314 [NOIP2011 提高组] 聪明的质监员 + 洛谷P1314 [NOIP2011 +提高组] 聪明的质监员 题解 +题目链接:P1314 +[NOIP2011 提高组] 聪明的质监员 - 题意: - 小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 $n$ 个矿石,从 $1$ 到 +题意: +小T +是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 \(n\) 个矿石,从 \(1\
                          @@ -763,10 +787,14 @@
                          - 洛谷P3647 [APIO2014] 连珠线 题解题目链接:P3647 [APIO2014] 连珠线 + 洛谷P3647 [APIO2014] 连珠线 +题解 +题目链接:P3647 +[APIO2014] 连珠线 题意: -在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 $1$ 到 $n$ +在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 +\(1\) 到
                          @@ -820,10 +848,14 @@
                          - 洛谷P3047 [USACO12FEB]Nearby Cows G 题解题目链接:P3047 [USACO12FEB]Nearby Cows G + 洛谷P3047 +[USACO12FEB]Nearby Cows G 题解 +题目链接:P3047 +[USACO12FEB]Nearby Cows G 题意: -给你一棵 $n$ 个点的树,点带权,对于每个节点求出距离它不超过 $k$ 的所有节点 +给你一棵 \(n\) +个点的树,点带权,对于每个节点求出距离它不超过 \(k\)
                          @@ -850,14 +882,14 @@ 算法 - - DP - - 图论 + + DP + +
                          @@ -877,10 +909,14 @@
                          - 洛谷P4037 [JSOI2008]魔兽地图 题解题目链接:P4037 [JSOI2008]魔兽地图 + 洛谷P4037 [JSOI2008]魔兽地图 +题解 +题目链接:P4037 +[JSOI2008]魔兽地图 题意: -DotR (Defense of the Robots) Allstars是一个风靡全球的魔兽地图,他的规则简单与同样流行的地图 +DotR (Defense of the Robots) +Allstars是一个风靡全球的魔兽地图,他的规则简单与同样流行的地
                          @@ -934,12 +970,14 @@
                          - OI模板-数学待补全 -排列的计算排列 $A_n^m$ 直接算可以 $O(n)$ -例如求解 $A_{n-m+1}^{m} \bmod p$ + OI模板-数学 +待补全 +排列的计算 +排列 \(A_n^m\) 直接算可以 \(O(n)\) +例如求解 \(A_{n-m+1}^{m} \bmod +p\) int res=1; -for(int i=n-m+1; i>=n-2*m+2; i--) - +for(int i=n-m+1; i>=n-2*m+2; i--
                          @@ -985,10 +1023,15 @@
                          - 洛谷P1272 重建道路 题解题目链接:P1272 重建道路 + 洛谷P1272 重建道路 题解 +题目链接:P1272 +重建道路 题意: -一场可怕的地震后,人们用 $N$ 个牲口棚(编号 $1\sim N$)重建了农夫 John 的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的 +一场可怕的地震后,人们用 \(N\) +个牲口棚(编号 \(1\sim N\))重建了农夫 +John +的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一
                          @@ -1015,14 +1058,14 @@ 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -1079,7 +1122,7 @@
                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/categories/OI/page/15/index.html b/categories/OI/page/15/index.html index 91661b5f97..6129064778 100644 --- a/categories/OI/page/15/index.html +++ b/categories/OI/page/15/index.html @@ -404,11 +404,13 @@
                          - 洛谷P1734 最大约数和 题解题目链接:P1734 最大约数和 + 洛谷P1734 最大约数和 题解 +题目链接:P1734 +最大约数和 题意:选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。 -设 $dp[i][j]$ 表示只考虑前 $i$ 个数总和不超过 $j$ 的最大价值,则 +设 \(dp[i][j]\) 表示只考虑前 \(i\) 个数总和不超过 \(j\)
                          @@ -458,10 +460,14 @@
                          - 洛谷P4322 [JSOI2016]最佳团体 题解题目链接:P4322 [JSOI2016]最佳团体 + 洛谷P4322 [JSOI2016]最佳团体 +题解 +题目链接:P4322 +[JSOI2016]最佳团体 题意: -JSOI 信息学代表队一共有 $N$ 名候选人,这些候选人从 $1$ 到 $N$ 编号。方便起见,JYY 的编号是 $0$ 号。 +JSOI 信息学代表队一共有 \(N\) +名候选人,这些候选人从 \(1\) 到 \(N\) 编号。方便起见,JYY 的编号是
                          @@ -488,14 +494,14 @@ 算法 - - DP - - 图论 + + DP + + 01分数规划 @@ -519,10 +525,15 @@
                          - 洛谷P2868 [USACO07DEC]Sightseeing Cows G 题解题目链接:P2868 [USACO07DEC]Sightseeing Cows G + 洛谷P2868 +[USACO07DEC]Sightseeing Cows G 题解 +题目链接:P2868 +[USACO07DEC]Sightseeing Cows G 题意: -给定有向图,有点权 $F_i$ 和边权 $T_i$ ,找一条回路 +给定有向图,有点权 \(F_i\) 和边权 +\(T_i\) +,
                          @@ -580,11 +591,13 @@
                          - POJ2976 Dropping tests 题解题目链接:POJ2976 Dropping tests + POJ2976 Dropping tests 题解 +题目链接:POJ2976 Dropping +tests 题意: Description -In a certain course, you take n tests. If you get +In a certain course, you take n tests. If you get
                          @@ -634,10 +647,13 @@
                          - 洛谷P1270 “访问”美术馆 题解题目链接:P1270 “访问”美术馆 + 洛谷P1270 “访问”美术馆 题解 +题目链接:P1270 +“访问”美术馆 题意: -经过数月的精心准备,Peer Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。P +经过数月的精心准备,Peer +Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。
                          @@ -664,14 +680,14 @@ 算法 - - DP - - 图论 + + DP + +
                          @@ -691,10 +707,14 @@
                          - 洛谷P3354 [IOI2005]Riv 河流 题解题目链接:P3354 [IOI2005]Riv 河流 + 洛谷P3354 [IOI2005]Riv 河流 +题解 +题目链接:P3354 +[IOI2005]Riv 河流 题意: -几乎整个 Byteland 王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进了 +几乎整个 Byteland +王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进
                          @@ -721,14 +741,14 @@ 算法 - - DP - - 图论 + + DP + +
                          @@ -748,11 +768,14 @@
                          - 洛谷P3237 [HNOI2014]米特运输 题解题目链接:P3237 [HNOI2014]米特运输 + 洛谷P3237 [HNOI2014]米特运输 +题解 +题目链接:P3237 +[HNOI2014]米特运输 题意: 米特是D星球上一种非常神秘的物质,蕴含着巨大的能量。在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题。 -D星 +D
                          @@ -802,10 +825,12 @@
                          - 洛谷P1273 有线电视网 题解题目链接:P1273 有线电视网 + 洛谷P1273 有线电视网 题解 +题目链接:P1273 +有线电视网 题意: -某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点 +某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节
                          @@ -832,14 +857,14 @@ 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -863,10 +888,13 @@
                          - 洛谷P4099 [HEOI2013]SAO 题解题目链接:P4099 [HEOI2013]SAO + 洛谷P4099 [HEOI2013]SAO 题解 +题目链接:P4099 +[HEOI2013]SAO 题意: -Welcome to SAO ( Strange and Abnormal Online)。这是一个 VR MMORPG, 含有 $ +Welcome to SAO ( Strange and Abnormal Online)。这是一个 VR MMORPG, +含有
                          @@ -920,10 +948,14 @@
                          - 洛谷P2279 [HNOI2003]消防局的设立 题解题目链接:P2279 [HNOI2003]消防局的设立 + 洛谷P2279 +[HNOI2003]消防局的设立 题解 +题目链接:P2279 +[HNOI2003]消防局的设立 题意: -2020 年,人类在火星上建立了一个庞大的基地群,总共有 $n$ 个基地。起初为了节约材料,人类只修建了 $n-1$ 条 +2020 年,人类在火星上建立了一个庞大的基地群,总共有 \(n\) 个基地。起初为了节约材料,人类只修建了 +\(n-
                          @@ -950,14 +982,14 @@ 算法 - - DP - - 图论 + + DP + +
                          @@ -977,10 +1009,16 @@
                          - 洛谷P3177 [HAOI2015] 树上染色 题解题目链接:P3177 [HAOI2015] 树上染色 + 洛谷P3177 [HAOI2015] 树上染色 +题解 +题目链接:P3177 +[HAOI2015] 树上染色 题意: -有一棵点数为 $n$ 的树,树边有边权。给你一个在 $0 \sim n$ 之内的正整数 $m$ ,你要在这棵树中选择 $m$ +有一棵点数为 \(n\) +的树,树边有边权。给你一个在 \(0 \sim +n\) 之内的正整数 \(m\) +,你要在这棵树
                          @@ -1007,14 +1045,14 @@ 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -1038,11 +1076,15 @@
                          - 洛谷P2015 二叉苹果树 题解题目链接:P2015 二叉苹果树 + 洛谷P2015 二叉苹果树 题解 +题目链接:P2015 +二叉苹果树 题意: 有一棵苹果树,如果树枝有分叉,一定是分二叉(就是说没有只有一个儿子的结点) -这棵树共有 $N$ 个结点(叶子点或者树枝分叉点),编号为 $1 \sim N$,树根编 +这棵树共有 \(N\) +个结点(叶子点或者树枝分叉点),编号为 \(1 +\sim N\
                          @@ -1069,14 +1111,14 @@ 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -1133,7 +1175,7 @@
                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/categories/OI/page/16/index.html b/categories/OI/page/16/index.html index 32891b718d..2d49980e9a 100644 --- a/categories/OI/page/16/index.html +++ b/categories/OI/page/16/index.html @@ -404,10 +404,14 @@
                          - 洛谷P3174 [HAOI2009] 毛毛虫 题解题目链接:P3174 [HAOI2009] 毛毛虫 + 洛谷P3174 [HAOI2009] 毛毛虫 +题解 +题目链接:P3174 +[HAOI2009] 毛毛虫 题意: -对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大。例如下图左边的树(图 $1$ +对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大。例如下图左边的树(图 +\(
                          @@ -434,14 +438,14 @@ 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -465,11 +469,16 @@
                          - 洛谷P3621 [APIO2007] 风铃 题解题目链接:P3621 [APIO2007] 风铃 + 洛谷P3621 [APIO2007] 风铃 +题解 +题目链接:P3621 +[APIO2007] 风铃 题意: -你准备给弟弟 Ike 买一件礼物,但是,Ike 挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西。 -你准备给 Ike 买一 +你准备给弟弟 Ike 买一件礼物,但是,Ike +挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西。 +你准备给 Ike +买
                          @@ -523,10 +532,13 @@
                          - 洛谷P2458 [SDOI2006]保安站岗 题解题目链接:P2458 [SDOI2006]保安站岗 + 洛谷P2458 [SDOI2006]保安站岗 +题解 +题目链接:P2458 +[SDOI2006]保安站岗 题意: -五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序。 +五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序
                          @@ -553,14 +565,14 @@ 算法 - - DP - - 图论 + + DP + +
                          @@ -580,10 +592,13 @@
                          - 洛谷P2016 战略游戏 题解题目链接:P2016 战略游戏 + 洛谷P2016 战略游戏 题解 +题目链接:P2016 +战略游戏 -题意: Bob 要建立一个古城堡,城堡中的路形成一棵无根树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。 -注意,某个士兵在一个结点上时,与该结点相连的 +题意: Bob +要建立一个古城堡,城堡中的路形成一棵无根树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。 +注意,某个士兵在一个结点上时,与该结点相连
                          @@ -633,9 +648,12 @@
                          - 洛谷P4084 [USACO17DEC]Barn Painting G 题解题目链接:P4084 [USACO17DEC]Barn Painting G + 洛谷P4084 +[USACO17DEC]Barn Painting G 题解 +题目链接:P4084 +[USACO17DEC]Barn Painting G -题意:题意:给定一颗N个节点组成的树,3种颜色,其中K个节点已染色,要求任意两相邻节 +题意:题意:给定一颗N个节点组成的树,3种颜色,其中K个节点已染色,要求任意两相邻
                          @@ -662,14 +680,14 @@ 算法 - - DP - - 图论 + + DP + + 数据结构 @@ -693,10 +711,12 @@
                          - 洛谷P1122 最大子树和 题解题目链接:P1122 最大子树和 + 洛谷P1122 最大子树和 题解 +题目链接:P1122 +最大子树和 题意: -小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花卉 +小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花
                          @@ -723,14 +743,14 @@ 算法 - - DP - - 图论 + + DP + +
                          @@ -750,10 +770,14 @@
                          - 洛谷P3205 [HNOI2010]合唱队 题解题目链接:P3205 [HNOI2010]合唱队 + 洛谷P3205 [HNOI2010]合唱队 +题解 +题目链接:P3205 +[HNOI2010]合唱队 题意: -为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A 需要将合唱队的人根据他们的身高排出一个队形。假定合唱队一 +为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A +需要将合唱队的人根据他们的身高排出一个队形。假定合唱队
                          @@ -803,10 +827,13 @@
                          - UVA1629 切蛋糕 Cake slicing 题解题目链接:UVA1629 切蛋糕 Cake slicing + UVA1629 切蛋糕 Cake slicing +题解 +题目链接:UVA1629 +切蛋糕 Cake slicing 题意:这个翻译够烂的,直接看pdf -翻译:有一个n行m列(1<=n,m<=20)的网络蛋糕上有k个樱桃。每次可以 +翻译:有一个n行m列(1<=n,m<=20)的网络蛋糕上有k个樱桃。每次可
                          @@ -856,9 +883,12 @@
                          - UVA1437 String painter 题解题目链接:UVA1437 String painter + UVA1437 String painter 题解 +题目链接:UVA1437 +String painter -题意:There are two strings A and B with equal length. Both strings a +题意:There are two strings A and B with equal +length. Both strings
                          @@ -908,10 +938,13 @@
                          - 洛谷P2890 [USACO07OPEN]Cheapest Palindrome G 题解题目链接:P2890 [USACO07OPEN]Cheapest Palindrome G + 洛谷P2890 +[USACO07OPEN]Cheapest Palindrome G 题解 +题目链接:P2890 +[USACO07OPEN]Cheapest Palindrome G 题意: -Keeping track of all the +Keeping track of all th
                          @@ -961,10 +994,13 @@
                          - 洛谷P2851 [USACO06DEC]The Fewest Coins G 题解题目链接:P2851 [USACO06DEC]The Fewest Coins G + 洛谷P2851 +[USACO06DEC]The Fewest Coins G 题解 +题目链接:P2851 [USACO06DEC]The +Fewest Coins G 题意: -Farmer John has gone to town to +Farmer John has gone to town to
                          @@ -1014,10 +1050,14 @@
                          - 洛谷P4170 [CQOI2007]涂色 题解题目链接:P4170 [CQOI2007]涂色 + 洛谷P4170 [CQOI2007]涂色 题解 +题目链接:P4170 +[CQOI2007]涂色 题意: -假设你有一条长度为 $5$ 的木板,初始时没有涂过任何颜色。你希望把它的 $5$ 个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 +假设你有一条长度为 \(5\) +的木板,初始时没有涂过任何颜色。你希望把它的 \(5\) +个单位长度分别涂上红、绿、蓝、绿、红色,用一
                          @@ -1100,7 +1140,7 @@
                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/categories/OI/page/17/index.html b/categories/OI/page/17/index.html index 2341b57726..5d6afcae95 100644 --- a/categories/OI/page/17/index.html +++ b/categories/OI/page/17/index.html @@ -392,22 +392,25 @@
                          - +
                          - 洛谷P1282 多米诺骨牌 题解 + 洛谷P1171 售货员的难题 题解 - 洛谷P1282 多米诺骨牌 题解 + 洛谷P1171 售货员的难题 题解
                          - 洛谷P1282 多米诺骨牌 题解题目链接:P1282 多米诺骨牌 + 洛谷P1171 售货员的难题 题解 +题目链接:P1171 +售货员的难题 -题意: -多米诺骨牌由上下 $2$ 个方块组成,每个方块中有 $1\sim6$ 个点。现有排成行的上方块中点数之和记为 $S_1$,下方块中点数之和记为 $S_2$,它们的差 +题意:TSP问题。 +某乡有\(n\)个村庄(\(1<n \le +20\)),有一个售货员,他要到各个村庄去售货,各村庄之间的路程\(s(0<s<10
                          @@ -445,22 +448,23 @@
                          - +
                          - 洛谷P1171 售货员的难题 题解 + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 - 洛谷P1171 售货员的难题 题解 + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法
                          - 洛谷P1171 售货员的难题 题解题目链接:P1171 售货员的难题 - -题意:TSP问题。 -某乡有$n$个村庄($1<n \le 20$),有一个售货员,他要到各个村庄去售货,各村庄之间的路程$s(0<s<1000)$是已 + 洛谷P1156 垃圾陷阱 +题解&浅谈刷表法与填表法 +填表法 +:就是一般的动态规划,当前点的状态,可以直接用状态方程,根据之前点的状态推导出来。 +刷表法:由当前点的状态,更新其他点的状态。需要注意:只用当每个状态所依赖的状态对它的影响
                          @@ -510,9 +514,11 @@
                          - 洛谷P1108 低价购买 题解题目链接:P1108 低价购买 + 洛谷P1108 低价购买 题解 +题目链接:P1108 +低价购买 -题意:“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上 +题意:“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你
                          @@ -562,9 +568,13 @@
                          - CF526B Om Nom and Dark Park 题解题目链接:CF526B Om Nom and Dark Park + CF526B Om Nom and Dark Park +题解 +题目链接:CF526B Om +Nom and Dark Park -题意:给定 $2^{n+1}-1$ 个结点的满二叉树,求保证根到每一个叶子节点的路径权值和相等的情况下,增加权值 +题意:给定 \(2^{n+1}-1\) +个结点的满二叉树,求保证根到每一个叶子节点的路径权值和相等的情况下,增
                          @@ -602,21 +612,26 @@
                          - +
                          - CF346B Lucky Common Subsequence 题解 + CF41D Pawn 题解 - CF346B Lucky Common Subsequence 题解 + CF41D Pawn 题解
                          - CF346B Lucky Common Subsequence 题解题目链接:CF346B Lucky Common Subsequence - -题意:通过删除一个字符串中的某些元素而不改变其余元素的顺序,可以派生出该字符串的一个子序列。 例 + CF41D Pawn 题解 +题目链接:CF41D +Pawn + +题意:国际象棋棋盘最底行站了一个兵。 +它只有两种行动方式: 向上左或向上右走。 +它可以选择从最低行哪个节点开始他的旅程。 +每个格子上有0-9颗豌豆,而士兵想移动到最上一行并且积累
                          @@ -654,21 +669,24 @@
                          - +
                          - CF41D Pawn 题解 + CF294B Shaass and Bookshelf 题解 - CF41D Pawn 题解 + CF294B Shaass and Bookshelf 题解
                          - CF41D Pawn 题解题目链接:CF41D Pawn + CF294B Shaass and Bookshelf +题解 +题目链接:CF294B +Shaass and Bookshelf -题意:国际象棋棋盘最底行站了一个兵。 它只有两种行动方式: 向上左或向上右走。 它可以选择从最低行哪个节点开始他的旅程。 每个格子上有0-9颗豌豆,而士兵想移动到最上一行并且积累到 +题意:Shaass has \(n\) books. He wants to make a bookshelf
                          @@ -695,8 +713,8 @@ 算法 - - DP + + 数学
                          @@ -706,21 +724,25 @@
                          - +
                          - CF219D Choosing Capital for Treeland 题解 + CF374C Inna and Dima 题解 - CF219D Choosing Capital for Treeland 题解 + CF374C Inna and Dima 题解
                          - CF219D Choosing Capital for Treeland 题解题目链接:CF219D Choosing Capital for Treeland + CF374C Inna and Dima 题解 +题目链接:CF374C +Inna and Dima -题意:Treeland国有n个城市,这n个城市连成了一颗树,有n-1条道路连 +题意: +Inna和Dima在商店买了一张 n * m +的桌子,桌子的每一个单元格上都有一个字符,字符集为("D","I","M","A")
                          @@ -758,22 +780,25 @@
                          - +
                          - CF374C Inna and Dima 题解 + CF346B Lucky Common Subsequence 题解 - CF374C Inna and Dima 题解 + CF346B Lucky Common Subsequence 题解
                          - CF374C Inna and Dima 题解题目链接:CF374C Inna and Dima + CF346B Lucky Common +Subsequence 题解 +题目链接:CF346B +Lucky Common Subsequence + +题意:通过删除一个字符串中的某些元素而不改变其余元素的顺序,可以派生出该字符串的一个子序列。 -题意: -Inna和Dima在商店买了一张 n * m 的桌子,桌子的每一个单元格上都有一个字符,字符集为(“D”,”I”,”M”,”A”)。
                          @@ -811,21 +836,24 @@
                          - +
                          - CF294B Shaass and Bookshelf 题解 + CF219D Choosing Capital for Treeland 题解 - CF294B Shaass and Bookshelf 题解 + CF219D Choosing Capital for Treeland 题解
                          - CF294B Shaass and Bookshelf 题解题目链接:CF294B Shaass and Bookshelf + CF219D Choosing +Capital for Treeland 题解 +题目链接:CF219D +Choosing Capital for Treeland -题意:Shaass has $n$ books. He wants to make a bookshelf fo +题意:Treeland国有n个城市,这n个城市连成了一颗树,有n-1条道路
                          @@ -852,8 +880,8 @@ 算法 - - 数学 + + DP
                          @@ -875,9 +903,13 @@
                          - CF19B Checkout Assistant 题解题目链接:CF19B Checkout Assistant + CF19B Checkout Assistant +题解 +题目链接:CF19B +Checkout Assistant -题意:Bob 来到一家现购自运商店,将 $n$ 件商品放入了他的手推车,然后到收银台付款。每件商品由它的价格 $c_i$ 和 +题意:Bob 来到一家现购自运商店,将 \(n\) +件商品放入了他的手推车,然后到收银台付款。每件商品由它的价格 \(c_
                          @@ -927,9 +959,12 @@
                          - CF149D Coloring Brackets 题解题目链接:CF149D Coloring Brackets + CF149D Coloring Brackets +题解 +题目链接:CF149D +Coloring Brackets -题意:给出一个配对的括号序列(如 “$\texttt{(())()}$”、“$\texttt{()}$” 等,“$\text +题意:给出一个配对的括号序列(如 “\(\texttt{(())()}\)”、“\(\texttt{()}\)” 等,“\
                          @@ -979,9 +1014,11 @@
                          - AT3913 XOR Tree 题解题目链接:AT3913 XOR Tree + AT3913 XOR Tree 题解 +题目链接:AT3913 +XOR Tree -题意:给你一棵有 $N$ 个节点的树,节点编号从 $0$ 到 $N-1$ , 树边编号从 $1$ 到 $N-1$ 。第 $i$ 条边连接节点 $x_i$ 和 $ +题意:给你一棵有 \(N\) 个节点的树,节点编号从 \(0\) 到 \(N-1\) , 树边编号从 \(1\) 到 \(N-1\) 。第 \(i\) 条边连
                          @@ -1064,7 +1101,7 @@
                            站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/categories/OI/page/18/index.html b/categories/OI/page/18/index.html index b6dc27e2ac..cf7a717d67 100644 --- a/categories/OI/page/18/index.html +++ b/categories/OI/page/18/index.html @@ -392,21 +392,26 @@
                          - +
                          - 洛谷P2216 [HAOI2007]理想的正方形 题解 + 洛谷P2260 [清华集训2012]模积和 题解 - 洛谷P2216 [HAOI2007]理想的正方形 题解 + 洛谷P2260 [清华集训2012]模积和 题解
                          - 洛谷P2216 [HAOI2007]理想的正方形 题解题目链接:P2216 [HAOI2007]理想的正方形 + 洛谷P2260 +[清华集训2012]模积和 题解 +题目链接:P2260 +[清华集训2012]模积和 -题意: 有一个 $a \times b$ 的整数组成的矩阵,现请你从中找出一个 $n \times n$的正方形区域,使得该区域 +题意: +求 \[ +\left(\sum_{i=1}^{n}\sum_{j=1}^{m}(n\bmod i)\times(m\bmod j)
                          @@ -429,12 +434,8 @@ @@ -444,24 +445,24 @@
                          - +
                          - 洛谷P2260 [清华集训2012]模积和 题解 + 洛谷P2257 YY的GCD 题解 - 洛谷P2260 [清华集训2012]模积和 题解 + 洛谷P2257 YY的GCD 题解
                          - 洛谷P2260 [清华集训2012]模积和 题解题目链接:P2260 [清华集训2012]模积和 + 洛谷P2257 YY的GCD 题解 题意: -求 +多组询问。 +给定 \(N, M\),求 \(1 \leq x \leq N\),\(1 \leq y \leq M\) 且 \(\gcd(x,y)\) 为质数的 \((x,y)\) 有多少对。 -\left(\sum_{i=1}^{n}\sum_{j=1}^{m}(n\bmod i)\times(m\bmod j),i\
                          @@ -484,6 +485,10 @@

                          题目链接:CF526G Spiders Evil Plan

                          题意

                          二次魔改自 link

                          最近 q779 迷上了一个叫做割绳子 3 的yúsì(游戏)。

                          这个游戏的要求是你要扮演蜘蛛去吃掉一个糖果。

                          那是一个像割绳子游戏一样的界面,共有 $n$ 个节点,和 $n-1$ 条有长度的边,组成了一个树状结构。一共有 $q$ 关,第 $i$ 关,糖果在第 $x_i$ 个节点上,共有 $y_i$ 只蜘蛛。

                          每只蜘蛛可以用它的网去覆盖任意不同两点 $a$ 和 $b$ 之间的路径,因此,$y$ 只蜘蛛可以覆盖 $y$ 条树上路径的并。当然,不同的蜘蛛选择的路径可以相交。并且,具有如下的要求:

                          1. $y$ 条路径必须包含糖果所在的节点 $x$。
                          2. $y$ 条路径的并是连通的。
                          3. 使 $y$ 条路径所经过的边的长度总和最大。

                          可是蜘蛛们好像都非常智障,所以它们需要你帮忙求出最大值。

                          本题强制在线 ,数据范围: $1 \le x_i,y_i \le n,~1\le n,q \le 10^5$

                          关键词:贪心、树剖、树的直径

                          先不考虑 $x$ 的存在,直接去选这样的路径并

                          肯定是这样子的

                          • $y=1$ ,选直径
                          • $y=2$ ,选 $y=1$ 的再加一个最大的分支
                          • $y=3$ ,选 $y=2$ 的再加一个剩下的最大分支
                          • $\cdots$

                          因此可以把直径的一个端点当作根 $\mathtt{rt}$ ,然后去跑个重链剖分(树剖)

                          然后在所有切出的链中找最优的 $2y-1$ 条链

                          注:下文中的链均指树剖后的链

                          为什么是 $2y-1$ 条链?怎么最优?

                          首先每条切出来的链一定包含叶子结点,从重链剖分的性质可以得出

                          那么就是选 $2y-1$ 个叶子,$-1$ 是因为根节点必选,但它不是叶子。

                          而两两叶子对应的链可以通过根节点并成一条链,显然这是优的。

                          考虑两两叶子的全序关系。我们令 $\mathtt{len}_i$ 表示叶子 $i$ 对应的链的长度

                          显然我们选的一定是 $\mathtt{len}_i$ 最大的 $2y-1$ 个叶子。

                          现在考虑 $x$ 的情况

                          记 $\mathtt{rnk}_i$ 表示按 $\mathtt{len}_i$ 降序排序后的叶子 $i$ 的排名

                          如果 $x$ 在最优方案中已经被选了,即 $\mathtt{rnk}_x \le 2y-1$ ,那么直接输出即可

                          如果 $x$ 不在最优方案中,考虑两种方案的最优者

                          • 把 $\mathtt{rnk}_{2y-1}$ 扔了,把 $i$ 加进去

                          • 把 $i$ 到 $\mathtt{rt}$ 的路径找出来,取其中深度最大的一个结点 $\delta$

                            使得 $\delta$ 在原最优方案中被选了,然后把 $\delta$ 下面原来连的砍了(害怕),把它与 $x$ 接上。

                            这个可以用倍增数组来 $O(\log n)$ 查。

                          然后会发现第二行在 $y=1$ 的情况就挂了

                          事实上只有 $y=1$ 会挂。特判即可。

                          为什么呢?因为 $y=1$ 时的最优方案不一定会把 $\mathtt{rt}$ 选进去

                          稍微因此有点区别。我们还是找到这样的 $\delta$

                          然后尝试把它与直径另一端连一下

                          可以证明,这样一定是最优的

                          然后这题就没了,恭喜您做出了Codeforces评分3300的题目 Orz

                          时间复杂度 $O(Q \log n)$ ,因为链数与 $\Theta(\log n)$ 同阶,对复杂度影响不大

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N ((int)(1e5+15))#define M ((int)(2e5+15))#define LN 18struct Edge{int u,v,w,next;} e[M];int n,rt,pos=1,ccnt,head[N],dep[N],fa[N],sum[N];int grd[N],len[N],rnk[N],sz[N],chain[N],son[N],top[N],q[N][LN];void up(int &x,int y){ x = ( x > y ) ? x : y;}void down(int &x,int y) { x =  ( x > y ) ? y : x;}void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dfs1(int u,int Fa){    if(dep[u] > dep[rt]) rt=u;    for(int i=head[u],v; i; i=e[i].next)    {        if((v=e[i].v) != Fa)            dep[v] = dep[u] + e[i].w, dfs1(v,u);    }}void dfs2(int u){    int mx=-1;    for(int i=head[u],v; i; i=e[i].next)    {        if((v=e[i].v) != fa[u])        {            fa[v] = u; dep[v] = dep[u] + e[i].w;            dfs2(v); up(sz[u], sz[v] + e[i].w);            sz[v] + e[i].w > mx ? (son[u] = v, mx = sz[v] + e[i].w ): 0;        }    }}void dfs3(int u,int ftop){    top[u] = ftop;    if(u == ftop)        {q[u][0] = top[fa[u]]; for(int i=0; q[u][i]; i++) q[u][i+1]=q[q[u][i]][i];}    grd[u] = (ftop == rt ? u : grd[fa[u]]);    if(!son[u]) return len[ftop] += dep[u] - dep[ftop], void(0);    dfs3(son[u], ftop);    for(int i=head[u],v; i; i=e[i].next)        if(!top[v=e[i].v]) len[v] = e[i].w, dfs3(v,v);}int qry(int u,int r){    for(int i=LN-1; i>=0; i--)        if(q[u][i] && rnk[q[u][i]] >= r) u = q[u][i];    return u;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; cin >> n >> Q;    for(int i=1,u,v,w; i<n; i++)        cin >> u >> v >> w, addEdge(u,v,w), addEdge(v,u,w);    dfs1(1,0); dep[rt]=0; dfs2(rt); dfs3(rt,rt);    for(int i=1; i<=n; i++) if(top[i] == i) chain[ccnt++]=i;    sort(chain, chain + ccnt, [] (int x,int y) {return len[x]>len[y];});    for(int i=0; i<ccnt; i++) rnk[chain[i]]=i, sum[i+1] = sum[i] + len[chain[i]];    for(int u,v,m,ans=0; Q--; )    {        cin >> v >> m; ans%=n;        v = (v + ans - 1) % n + 1; m = (m + ans - 1) % n * 2 + 1;        if(rnk[top[v]] < m) ans=sum[min(m,ccnt)];        else        {            u=fa[qry(top[v],m)];            up(ans=sum[m-1], sum[m]-min(sz[u], dep[grd[u]]));            ans += dep[v] - dep[u] + sz[v];        }        cout << ans << '\n';    }    return 0;}
                          ]]> + CF526G Spiders Evil Plan题解

                          题目链接:CF526GSpiders Evil Plan

                          题意

                          二次魔改自 link

                          最近 q779 迷上了一个叫做割绳子 3 的yúsì(游戏)。

                          这个游戏的要求是你要扮演蜘蛛去吃掉一个糖果。

                          那是一个像割绳子游戏一样的界面,共有 \(n\) 个节点,和 \(n-1\)条有长度的边,组成了一个树状结构。一共有 \(q\) 关,第 \(i\) 关,糖果在第 \(x_i\) 个节点上,共有 \(y_i\) 只蜘蛛。

                          每只蜘蛛可以用它的网去覆盖任意不同两点 \(a\) 和 \(b\) 之间的路径,因此,\(y\) 只蜘蛛可以覆盖 \(y\)条树上路径的并。当然,不同的蜘蛛选择的路径可以相交。并且,具有如下的要求:

                          1. \(y\) 条路径必须包含糖果所在的节点\(x\)
                          2. \(y\) 条路径的并是连通的。
                          3. 使 \(y\)条路径所经过的边的长度总和最大。

                          可是蜘蛛们好像都非常智障,所以它们需要你帮忙求出最大值。

                          本题强制在线 ,数据范围: \(1 \le x_i,y_i \le n,~1\le n,q \le10^5\)

                          关键词:贪心、树剖、树的直径

                          先不考虑 \(x\)的存在,直接去选这样的路径并

                          肯定是这样子的

                          • \(y=1\) ,选直径
                          • \(y=2\) ,选 \(y=1\) 的再加一个最大的分支
                          • \(y=3\) ,选 \(y=2\) 的再加一个剩下的最大分支
                          • \(\cdots\)

                          因此可以把直径的一个端点当作根 \(\mathtt{rt}\),然后去跑个重链剖分(树剖)

                          然后在所有切出的链中找最优的 \(2y-1\) 条链

                          注:下文中的链均指树剖后的链

                          为什么是 \(2y-1\)条链?怎么最优?

                          首先每条切出来的链一定包含叶子结点,从重链剖分的性质可以得出

                          那么就是选 \(2y-1\) 个叶子,\(-1\) 是因为根节点必选,但它不是叶子。

                          而两两叶子对应的链可以通过根节点并成一条链,显然这是优的。

                          考虑两两叶子的全序关系。我们令 \(\mathtt{len}_i\) 表示叶子 \(i\) 对应的链的长度

                          显然我们选的一定是 \(\mathtt{len}_i\) 最大的 \(2y-1\) 个叶子。

                          现在考虑 \(x\) 的情况

                          \(\mathtt{rnk}_i\) 表示按 \(\mathtt{len}_i\) 降序排序后的叶子 \(i\) 的排名

                          如果 \(x\)在最优方案中已经被选了,即 \(\mathtt{rnk}_x\le 2y-1\) ,那么直接输出即可

                          如果 \(x\)不在最优方案中,考虑两种方案的最优者

                          • \(\mathtt{rnk}_{2y-1}\)扔了,把 \(i\) 加进去

                          • \(i\)\(\mathtt{rt}\)的路径找出来,取其中深度最大的一个结点 \(\delta\)

                            使得 \(\delta\)在原最优方案中被选了,然后把 \(\delta\)下面原来连的砍了(害怕),把它与 \(x\) 接上。

                            这个可以用倍增数组来 \(O(\log n)\)查。

                          然后会发现第二行在 \(y=1\)的情况就挂了

                          事实上只有 \(y=1\)会挂。特判即可。

                          为什么呢?因为 \(y=1\)时的最优方案不一定会把 \(\mathtt{rt}\)选进去

                          稍微因此有点区别。我们还是找到这样的 \(\delta\)

                          然后尝试把它与直径另一端连一下

                          可以证明,这样一定是最优的

                          然后这题就没了,恭喜您做出了Codeforces评分3300的题目Orz

                          时间复杂度 \(O(Q \log n)\),因为链数与 \(\Theta(\log n)\)同阶,对复杂度影响不大

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N ((int)(1e5+15))#define M ((int)(2e5+15))#define LN 18struct Edge{int u,v,w,next;} e[M];int n,rt,pos=1,ccnt,head[N],dep[N],fa[N],sum[N];int grd[N],len[N],rnk[N],sz[N],chain[N],son[N],top[N],q[N][LN];void up(int &x,int y){ x = ( x > y ) ? x : y;}void down(int &x,int y) { x =  ( x > y ) ? y : x;}void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dfs1(int u,int Fa){    if(dep[u] > dep[rt]) rt=u;    for(int i=head[u],v; i; i=e[i].next)    {        if((v=e[i].v) != Fa)            dep[v] = dep[u] + e[i].w, dfs1(v,u);    }}void dfs2(int u){    int mx=-1;    for(int i=head[u],v; i; i=e[i].next)    {        if((v=e[i].v) != fa[u])        {            fa[v] = u; dep[v] = dep[u] + e[i].w;            dfs2(v); up(sz[u], sz[v] + e[i].w);            sz[v] + e[i].w > mx ? (son[u] = v, mx = sz[v] + e[i].w ): 0;        }    }}void dfs3(int u,int ftop){    top[u] = ftop;    if(u == ftop)        {q[u][0] = top[fa[u]]; for(int i=0; q[u][i]; i++) q[u][i+1]=q[q[u][i]][i];}    grd[u] = (ftop == rt ? u : grd[fa[u]]);    if(!son[u]) return len[ftop] += dep[u] - dep[ftop], void(0);    dfs3(son[u], ftop);    for(int i=head[u],v; i; i=e[i].next)        if(!top[v=e[i].v]) len[v] = e[i].w, dfs3(v,v);}int qry(int u,int r){    for(int i=LN-1; i>=0; i--)        if(q[u][i] && rnk[q[u][i]] >= r) u = q[u][i];    return u;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; cin >> n >> Q;    for(int i=1,u,v,w; i<n; i++)        cin >> u >> v >> w, addEdge(u,v,w), addEdge(v,u,w);    dfs1(1,0); dep[rt]=0; dfs2(rt); dfs3(rt,rt);    for(int i=1; i<=n; i++) if(top[i] == i) chain[ccnt++]=i;    sort(chain, chain + ccnt, [] (int x,int y) {return len[x]>len[y];});    for(int i=0; i<ccnt; i++) rnk[chain[i]]=i, sum[i+1] = sum[i] + len[chain[i]];    for(int u,v,m,ans=0; Q--; )    {        cin >> v >> m; ans%=n;        v = (v + ans - 1) % n + 1; m = (m + ans - 1) % n * 2 + 1;        if(rnk[top[v]] < m) ans=sum[min(m,ccnt)];        else        {            u=fa[qry(top[v],m)];            up(ans=sum[m-1], sum[m]-min(sz[u], dep[grd[u]]));            ans += dep[v] - dep[u] + sz[v];        }        cout << ans << '\n';    }    return 0;}
                          ]]> @@ -37,7 +37,7 @@ /2022/08/22/loj6269-wan-ji-ji-shu-jia-qiang-ban-ti-jie/ - LOJ6269 烷基计数 加强版 题解

                          题目链接:#6269. 烷基计数 加强版

                          题意:众所周知,大连 24 中是一所神奇的学校,在那里,化竞的同学很多都擅长写代码。

                          有一天,化学不及格的胡小兔向化竞巨佬晴岚请教化学题:

                          “$n$ 个碳原子的烷基共有多少种同分异构体?”

                          刚刚得了化竞全市第一的晴岚听了,认为这道题十分简单,建议胡小兔写个程序解决这个问题。但胡小兔弱得连什么是同分异构体都不知道,于是晴岚给胡小兔画了个图——例如 $n=4$ 时 (即丁基),有 $4$ 种同分异构体:

                          同理,其他常见烷基同分异构体数目如下表:

                          n 1 2同分异构体数目 1 1
                          n123456
                          同分异构体数目1124817

                          现在已知碳原子个数 $n$,求对应的烷基有多少种同分异构体。

                          答案对 $10^9 + 7$ 取模。

                          数据范围: $n \le 5\times 10^3$

                          注意:这里的烷基计数不用考虑空间异构,能否稳定存在等各种特殊情况。也就是说,你要求的是 $n$ 个点的每个点度数不超过 $4$ 且根的度数不超过 $3$ 的有根树的数目。

                          据说这种叫:无标号树计数题

                          最优的解法是 $O(n \log^2 n)$ 的,涉及Pólya原理和生成函数,这里暂且不提。

                          首先考虑弱化版 #6185. 烷基计数 的dp ( $n \le 400$ )

                          设 $f_i$ 表示 $i$ -烷基的个数,然后枚举三个子树的大小,乘个组合数就好了

                          实际上因为 $s_1+s_2+s_3=i-1$ ,因此只用枚举两个子树的大小

                          时间复杂度 $O(n^3)$ ,可以过弱化版。

                          考虑这道题怎么做

                          刚刚的dp瓶颈在于枚举了所有的子树大小

                          还记得树上背包吗?我们也有很多子树,但是我们是一个个子树添加的,而不是枚举每个子树的大小。

                          因此可以尝试像树上背包一样,一个个子树的添加

                          可是这道题的子结点没有区别,因此我们按子树大小顺序一一添加

                          枚举一个 $\mathtt{sz}$ (从 $1$ 到 $n$ ) ,然后去对所有的dp值产生贡献

                          或者等价地,在dp中加上一维 $s$ 表示当前允许的子树大小

                          同时我们还要记录当前根节点的度数

                          设 $f_{s,i,j}$ 表示 $i$ -烷基,根节点度数为 $j$ ,所有子树的大小不超过 $s$ 的方案数

                          考虑转移。首先,如果所有子树的大小都小于 $s$ ,则答案为 $f_{s-1,i,j}$

                          否则设存在 $k$ 个子树的大小为 $s$ ,显然 $1\le k \le \min\left\{j,\left\lfloor{\frac{i-1}{s}}\right\rfloor\right\}$ ,

                          把这些子树去掉以后,剩下 $i-sk$ 个点,根节点的度数为 $j-k$ ,且剩余子树大小均不超过 $s-1$

                          对应到状态就是 $f_{s-1,i-sk,j-k}$

                          然后就是考虑这 $k$ 个子树的分布,或者说把这 $k$ 个子树插进去

                          设大小为 $s$ 的子树有 $c(c=\sum f_{s-1,s,j})$ 个(显然这 $c$ 个本质不同)

                          那么这就是一个可重组合,方案数为 $\dbinom{c+k-1}{k}$ ( $c$ 个选 $k$ 个,可重复)

                          则完整转移方程为

                          时间复杂度 $O(n^2m \log m)$ ,其中 $m$ 为度数的限制,即 $4$ 。

                          然后这个第一维显然可以滚动数组。故空间复杂度为 $O(nm)$

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N ((int)(5e3+15))#define M 4const int mod=1e9+7,inv6=(mod+1)/6;int n,f[N][M];void add(int &x,int y){ (x+=y) >= mod? x-=mod : 0;}int C(int n,int m){    switch(m)    {        case 0: return 1;        case 1: return n % mod;        case 2: return n*(n-1)/2 % mod;        default: return n*(n-1) % mod * (n-2) % mod * inv6 % mod;    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    f[1][0]=1;    for(int sz=1,cur; sz<n; ++sz)    {        cur = 0; for(int i=0; i<M; i++) add(cur,f[sz][i]);        for(int i=n; i>=1; i--)            for(int j=1; j<M; j++)                for(int k=1; k<=j && sz*k<i; k++)                    add(f[i][j],f[i-sz*k][j-k]*C(cur+k-1,k)%mod);    }    int res=0; for(int i=0; i<M; i++) add(res,f[n][i]);    cout << res << '\n';    return 0;}

                          参考文献

                          [1] https://yhx-12243.github.io/OI-transit/records/loj6185%3Bloj6269.html

                          ]]> + LOJ6269 烷基计数 加强版 题解

                          题目链接:#6269. 烷基计数加强版

                          题意:众所周知,大连 24中是一所神奇的学校,在那里,化竞的同学很多都擅长写代码。

                          有一天,化学不及格的胡小兔向化竞巨佬晴岚请教化学题:

                          \(n\)个碳原子的烷基共有多少种同分异构体?”

                          刚刚得了化竞全市第一的晴岚听了,认为这道题十分简单,建议胡小兔写个程序解决这个问题。但胡小兔弱得连什么是同分异构体都不知道,于是晴岚给胡小兔画了个图——例如\(n=4\) 时 (即丁基),有 \(4\) 种同分异构体:

                          同理,其他常见烷基同分异构体数目如下表:

                          n123456
                          同分异构体数目1124817

                          现在已知碳原子个数 \(n\),求对应的烷基有多少种同分异构体。

                          答案对 \(10^9 + 7\) 取模。

                          数据范围: \(n \le 5\times10^3\)

                          注意:这里的烷基计数不用考虑空间异构,能否稳定存在等各种特殊情况。也就是说,你要求的是\(n\) 个点的每个点度数不超过 \(4\) 且根的度数不超过 \(3\) 的有根树的数目。

                          据说这种叫:无标号树计数题

                          最优的解法是 \(O(n \log^2 n)\)的,涉及Pólya原理和生成函数,这里暂且不提。

                          首先考虑弱化版 #6185. 烷基计数的dp ( \(n \le 400\)

                          \(f_i\) 表示 \(i\)-烷基的个数,然后枚举三个子树的大小,乘个组合数就好了

                          实际上因为 \(s_1+s_2+s_3=i-1\),因此只用枚举两个子树的大小

                          时间复杂度 \(O(n^3)\),可以过弱化版。

                          考虑这道题怎么做

                          刚刚的dp瓶颈在于枚举了所有的子树大小

                          还记得树上背包吗?我们也有很多子树,但是我们是一个个子树添加的,而不是枚举每个子树的大小。

                          因此可以尝试像树上背包一样,一个个子树的添加

                          可是这道题的子结点没有区别,因此我们按子树大小顺序一一添加

                          枚举一个 \(\mathtt{sz}\) (从 \(1\) 到 \(n\) ) ,然后去对所有的dp值产生贡献

                          或者等价地,在dp中加上一维 \(s\)表示当前允许的子树大小

                          同时我们还要记录当前根节点的度数

                          \(f_{s,i,j}\) 表示 \(i\) -烷基,根节点度数为 \(j\) ,所有子树的大小不超过 \(s\) 的方案数

                          考虑转移。首先,如果所有子树的大小都小于 \(s\) ,则答案为 \(f_{s-1,i,j}\)

                          否则设存在 \(k\) 个子树的大小为\(s\) ,显然 \(1\le k \le\min\left\{j,\left\lfloor{\frac{i-1}{s}}\right\rfloor\right\}\),

                          把这些子树去掉以后,剩下 \(i-sk\)个点,根节点的度数为 \(j-k\),且剩余子树大小均不超过 \(s-1\)

                          对应到状态就是 \(f_{s-1,i-sk,j-k}\)

                          然后就是考虑这 \(k\)个子树的分布,或者说把这 \(k\)个子树插进去

                          设大小为 \(s\) 的子树有 \(c(c=\sum f_{s-1,s,j})\) 个(显然这 \(c\) 个本质不同)

                          那么这就是一个可重组合,方案数为 \(\dbinom{c+k-1}{k}\) ( \(c\) 个选 \(k\) 个,可重复)

                          则完整转移方程为 \[f_{s,i,j} = \sum_{0 \le k \le j \land sk < i} f_{s-1,i-sk,j-k}\times\dbinom{c+k-1}{k}\] 时间复杂度 \(O(n^2m \log m)\),其中 \(m\) 为度数的限制,即 \(4\) 。

                          然后这个第一维显然可以滚动数组。故空间复杂度为 \(O(nm)\)

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N ((int)(5e3+15))#define M 4const int mod=1e9+7,inv6=(mod+1)/6;int n,f[N][M];void add(int &x,int y){ (x+=y) >= mod? x-=mod : 0;}int C(int n,int m){    switch(m)    {        case 0: return 1;        case 1: return n % mod;        case 2: return n*(n-1)/2 % mod;        default: return n*(n-1) % mod * (n-2) % mod * inv6 % mod;    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    f[1][0]=1;    for(int sz=1,cur; sz<n; ++sz)    {        cur = 0; for(int i=0; i<M; i++) add(cur,f[sz][i]);        for(int i=n; i>=1; i--)            for(int j=1; j<M; j++)                for(int k=1; k<=j && sz*k<i; k++)                    add(f[i][j],f[i-sz*k][j-k]*C(cur+k-1,k)%mod);    }    int res=0; for(int i=0; i<M; i++) add(res,f[n][i]);    cout << res << '\n';    return 0;}

                          参考文献

                          [1] https://yhx-12243.github.io/OI-transit/records/loj6185%3Bloj6269.html

                          ]]> @@ -66,7 +66,7 @@ /2022/08/22/luo-gu-p3825-noi2017-you-xi-ti-jie/ - 洛谷P3825 [NOI2017] 游戏 题解

                          题目链接:P3825 [NOI2017] 游戏

                          题意

                          小 L 计划进行 $n$ 场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。

                          小 L 的赛车有三辆,分别用大写字母 $A$、$B$、$C$ 表示。地图一共有四种,分别用小写字母 $x$、$a$、$b$、$c$ 表示。

                          其中,赛车 $A$ 不适合在地图 $a$ 上使用,赛车 $B$ 不适合在地图 $b$ 上使用,赛车 $C$ 不适合在地图 $c$ 上使用,而地图 $x$ 则适合所有赛车参加。

                          适合所有赛车参加的地图并不多见,最多只会有 $d$ 张。

                          $n$ 场游戏的地图可以用一个小写字母组成的字符串描述。例如:$S=\texttt{xaabxcbc}$ 表示小L计划进行 $8$ 场游戏,其中第 $1$ 场和第 $5$ 场的地图类型是 $x$,适合所有赛车,第 $2$ 场和第 $3$ 场的地图是 $a$,不适合赛车 $A$,第 $4$ 场和第 $7$ 场的地图是 $b$,不适合赛车 $B$,第 $6$ 场和第 $8$ 场的地图是 $c$,不适合赛车 $C$。

                          小 L 对游戏有一些特殊的要求,这些要求可以用四元组 $ (i, h_i, j, h_j) $ 来描述,表示若在第 $i$ 场使用型号为 $h_i$ 的车子,则第 $j$ 场游戏要使用型号为 $h_j$ 的车子。

                          你能帮小 L 选择每场游戏使用的赛车吗?如果有多种方案,输出任意一种方案。

                          如果无解,输出 -1

                          什么恶心卡常题,心态要崩了。

                          这篇题解在uoj是过不了Extra Test 12的,但是洛谷可以过hack数据。

                          显然 $\mathtt{2-SAT}$ ,但是 x 的出现意味这道题还要加一个 $\mathtt{3-SAT}$

                          众所周知, $\mathtt{k-SAT}$ 在 $k>2$ 时被证明是NP完全的

                          因此只能暴力枚举所有 x 的选法

                          这里的暴力可以优化为枚举每个 x 地图为 a 地图还是 b 地图

                          这样为什么对呢?

                          因为 a 适合 BCb 适合 AC ,然后我们就把填 ABC 的三种情况都试过了

                          考虑没有 x 的部分怎么做

                          • 如果 $i$ 的位置适合 $h_i$ 赛车,但 $j$ 不适合 $h_j$ 赛车

                            则把 $u_i$ 向 $u_{i}^{\prime}$ 连有向边,表示如果选了 $h_i$ 一定无解

                          • 如果 $i$ 的位置适合 $h_i$ 赛车,$j$ 的位置适合 $h_j$ 赛车

                            那就把 $u_i$ 向 $v_i$ 连有向边,$v_{i}^{\prime}$ 向 $u_{i}^{\prime}$ 连有向边,显然。

                          时间复杂度 $O(2^d \times (n+m))$

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <bitset>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    // #define gc() readchar()    #define gc() getchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }    void readc(char &ch)    {        char t=getchar();        while(!isupper(t)) t=gc();        ch=t;    }}using namespace FastIO;#define N ((int)(2e5+15))bitset<N> vis,ins;mt19937 rd(time(0));int n,m,d,scnt,top,dfncnt,pos=1;char s[N],col[N],a2[N],b2[N];int head[N],id[N],a1[N],b1[N],stk[N],dfn[N],low[N],scc[N];struct Edge{int u,v,next;} e[N];int tran(int x,char c){    if(s[x]=='a') return c=='B'?x:x+n;    if(s[x]=='b'||s[x]=='c') return c=='A'?x:x+n;    if(c=='C') return x+n; return x;}int neg(int x){return x > n ? x-n : x+n;}void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}bool Tarjan(int u){    dfn[u]=low[u]=++dfncnt;    ins[stk[++top]=u]=vis[u]=1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(!vis[v])        {            if(!Tarjan(v)) return 0;            low[u]=min(low[u],low[v]);        }        else if(ins[v]) low[u]=min(low[u],dfn[v]);    }    if(dfn[u]==low[u])    {        int v; scc[u]=++scnt; ins[u]=0;        while(v=stk[top--],v!=u)        {            scc[v]=scnt,ins[v]=0;            if(scc[v]==scc[neg(v)]) return 0;        }    }    return 1;}bool solve(){    scnt=dfncnt=top=0; pos=1; vis=ins=0;    for(int i=1; i<=(n<<1); i++) scc[i]=head[i]=dfn[i]=0;    for(int i=1; i<=m; i++)    {        if(s[a1[i]] != 'x' && s[b1[i]] != 'x')        {            if(a2[i] == s[a1[i]]-32) continue;            int u=tran(a1[i],a2[i]),v;            if(b2[i] == s[b1[i]]-32) {addEdge(u,neg(u)); continue;}            v=tran(b1[i],b2[i]); addEdge(u,v);            addEdge(neg(v),neg(u));        }else        {            char o = s[a1[i]], p = s[b1[i]];            int u,v, x=id[a1[i]], y=id[b1[i]];            if(o == 'x' && p == 'x')            {                if(a2[i] == col[x]) continue;                u=tran(a1[i],a2[i]);                if(b2[i] == col[y]) {addEdge(u,neg(u)); continue;}                v=tran(b1[i],b2[i]); addEdge(u,v);                addEdge(neg(v),neg(u));            }            else if(o == 'x' && p != 'x')            {                if(a2[i] == col[x]) continue;                u=tran(a1[i],a2[i]);                if(b2[i] == s[b1[i]]-32) {addEdge(u,neg(u)); continue;}                v=tran(b1[i],b2[i]); addEdge(u,v);                addEdge(neg(v),neg(u));            }            else            {                if(a2[i] == s[a1[i]]-32) continue;                u=tran(a1[i],a2[i]);                if(b2[i] == col[y]) {addEdge(u,neg(u)); continue;}                v=tran(b1[i],b2[i]); addEdge(u,v);                addEdge(neg(v),neg(u));            }        }    }    for(int i=1; i<=(n<<1); i++) if(!vis[i]&&!Tarjan(i)) return 0;    for(int i=1; i<=n; i++)    {        if(scc[i]<scc[i+n])        {            if(s[i]=='a') pc('B');            else if(s[i]=='b'||s[i]=='c') pc('A');            else if(col[id[i]]=='A') pc('B');            else pc('A');        }else        {            if(s[i]=='a'||s[i]=='b') pc('C');            else if(s[i]=='c') pc('B');            else if(col[id[i]]=='A') pc('C');            else pc('B');        }    }    puts("");    return 1;}signed main(){    int st=clock();    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int _; read(n); read(_); scanf("%s",s+1); read(m);    for(int i=1; i<=n; i++) if(s[i]=='x') id[i]=++d;    for(int i=1; i<=m; i++) read(a1[i]),readc(a2[i]),read(b1[i]),readc(b2[i]);    for(int i=0; i<(1<<d); i++)    {        for(int j=0; j<d; j++) col[j+1]=((i>>j)&1)?'B':'A';        if(solve()) return 0;    }    puts("-1");    // printf("times used %.3lf s",(1.0*clock()-st)/CLOCKS_PER_SEC);    return 0;}
                          ]]> + 洛谷P3825 [NOI2017] 游戏 题解

                          题目链接:P3825[NOI2017] 游戏

                          题意

                          小 L 计划进行 \(n\)场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。

                          小 L 的赛车有三辆,分别用大写字母 \(A\)、\(B\)、\(C\)表示。地图一共有四种,分别用小写字母 \(x\)、\(a\)、\(b\)、\(c\)表示。

                          其中,赛车 \(A\) 不适合在地图 \(a\) 上使用,赛车 \(B\) 不适合在地图 \(b\) 上使用,赛车 \(C\) 不适合在地图 \(c\) 上使用,而地图 \(x\) 则适合所有赛车参加。

                          适合所有赛车参加的地图并不多见,最多只会有 \(d\) 张。

                          \(n\)场游戏的地图可以用一个小写字母组成的字符串描述。例如:\(S=\texttt{xaabxcbc}\) 表示小L计划进行 \(8\) 场游戏,其中第 \(1\) 场和第 \(5\) 场的地图类型是 \(x\),适合所有赛车,第 \(2\) 场和第 \(3\) 场的地图是 \(a\),不适合赛车 \(A\),第 \(4\) 场和第 \(7\) 场的地图是 \(b\),不适合赛车 \(B\),第 \(6\) 场和第 \(8\) 场的地图是 \(c\),不适合赛车 \(C\)。

                          小 L 对游戏有一些特殊的要求,这些要求可以用四元组 $ (i, h_i, j, h_j)$ 来描述,表示若在第 \(i\) 场使用型号为\(h_i\) 的车子,则第 \(j\) 场游戏要使用型号为 \(h_j\) 的车子。

                          你能帮小 L选择每场游戏使用的赛车吗?如果有多种方案,输出任意一种方案。

                          如果无解,输出 -1

                          什么恶心卡常题,心态要崩了。

                          这篇题解在uoj是过不了Extra Test 12的,但是洛谷可以过hack数据。

                          显然 \(\mathtt{2-SAT}\) ,但是x 的出现意味这道题还要加一个 \(\mathtt{3-SAT}\)

                          众所周知, \(\mathtt{k-SAT}\)\(k>2\) 时被证明是NP完全的

                          因此只能暴力枚举所有 x 的选法

                          这里的暴力可以优化为枚举每个 x 地图为 a地图还是 b 地图

                          这样为什么对呢?

                          因为 a 适合 BCb 适合AC ,然后我们就把填 ABC 的三种情况都试过了

                          考虑没有 x 的部分怎么做

                          • 如果 \(i\) 的位置适合 \(h_i\) 赛车,但 \(j\) 不适合 \(h_j\) 赛车

                            则把 \(u_i\)\(u_{i}^{\prime}\) 连有向边,表示如果选了\(h_i\) 一定无解

                          • 如果 \(i\) 的位置适合 \(h_i\) 赛车,\(j\) 的位置适合 \(h_j\) 赛车

                          那就把 \(u_i\)\(v_i\) 连有向边,\(v_{i}^{\prime}\) 向 \(u_{i}^{\prime}\) 连有向边,显然。

                          时间复杂度 \(O(2^d \times(n+m))\)

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <bitset>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    // #define gc() readchar()    #define gc() getchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }    void readc(char &ch)    {        char t=getchar();        while(!isupper(t)) t=gc();        ch=t;    }}using namespace FastIO;#define N ((int)(2e5+15))bitset<N> vis,ins;mt19937 rd(time(0));int n,m,d,scnt,top,dfncnt,pos=1;char s[N],col[N],a2[N],b2[N];int head[N],id[N],a1[N],b1[N],stk[N],dfn[N],low[N],scc[N];struct Edge{int u,v,next;} e[N];int tran(int x,char c){    if(s[x]=='a') return c=='B'?x:x+n;    if(s[x]=='b'||s[x]=='c') return c=='A'?x:x+n;    if(c=='C') return x+n; return x;}int neg(int x){return x > n ? x-n : x+n;}void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}bool Tarjan(int u){    dfn[u]=low[u]=++dfncnt;    ins[stk[++top]=u]=vis[u]=1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(!vis[v])        {            if(!Tarjan(v)) return 0;            low[u]=min(low[u],low[v]);        }        else if(ins[v]) low[u]=min(low[u],dfn[v]);    }    if(dfn[u]==low[u])    {        int v; scc[u]=++scnt; ins[u]=0;        while(v=stk[top--],v!=u)        {            scc[v]=scnt,ins[v]=0;            if(scc[v]==scc[neg(v)]) return 0;        }    }    return 1;}bool solve(){    scnt=dfncnt=top=0; pos=1; vis=ins=0;    for(int i=1; i<=(n<<1); i++) scc[i]=head[i]=dfn[i]=0;    for(int i=1; i<=m; i++)    {        if(s[a1[i]] != 'x' && s[b1[i]] != 'x')        {            if(a2[i] == s[a1[i]]-32) continue;            int u=tran(a1[i],a2[i]),v;            if(b2[i] == s[b1[i]]-32) {addEdge(u,neg(u)); continue;}            v=tran(b1[i],b2[i]); addEdge(u,v);            addEdge(neg(v),neg(u));        }else        {            char o = s[a1[i]], p = s[b1[i]];            int u,v, x=id[a1[i]], y=id[b1[i]];            if(o == 'x' && p == 'x')            {                if(a2[i] == col[x]) continue;                u=tran(a1[i],a2[i]);                if(b2[i] == col[y]) {addEdge(u,neg(u)); continue;}                v=tran(b1[i],b2[i]); addEdge(u,v);                addEdge(neg(v),neg(u));            }            else if(o == 'x' && p != 'x')            {                if(a2[i] == col[x]) continue;                u=tran(a1[i],a2[i]);                if(b2[i] == s[b1[i]]-32) {addEdge(u,neg(u)); continue;}                v=tran(b1[i],b2[i]); addEdge(u,v);                addEdge(neg(v),neg(u));            }            else            {                if(a2[i] == s[a1[i]]-32) continue;                u=tran(a1[i],a2[i]);                if(b2[i] == col[y]) {addEdge(u,neg(u)); continue;}                v=tran(b1[i],b2[i]); addEdge(u,v);                addEdge(neg(v),neg(u));            }        }    }    for(int i=1; i<=(n<<1); i++) if(!vis[i]&&!Tarjan(i)) return 0;    for(int i=1; i<=n; i++)    {        if(scc[i]<scc[i+n])        {            if(s[i]=='a') pc('B');            else if(s[i]=='b'||s[i]=='c') pc('A');            else if(col[id[i]]=='A') pc('B');            else pc('A');        }else        {            if(s[i]=='a'||s[i]=='b') pc('C');            else if(s[i]=='c') pc('B');            else if(col[id[i]]=='A') pc('C');            else pc('B');        }    }    puts("");    return 1;}signed main(){    int st=clock();    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int _; read(n); read(_); scanf("%s",s+1); read(m);    for(int i=1; i<=n; i++) if(s[i]=='x') id[i]=++d;    for(int i=1; i<=m; i++) read(a1[i]),readc(a2[i]),read(b1[i]),readc(b2[i]);    for(int i=0; i<(1<<d); i++)    {        for(int j=0; j<d; j++) col[j+1]=((i>>j)&1)?'B':'A';        if(solve()) return 0;    }    puts("-1");    // printf("times used %.3lf s",(1.0*clock()-st)/CLOCKS_PER_SEC);    return 0;}
                          ]]> @@ -97,7 +97,7 @@ /2022/08/21/luo-gu-p6348-pa2011-journeys-ti-jie/ - 洛谷P6348 [PA2011]Journeys 题解

                          题目链接:P6348 [PA2011]Journeys

                          题意

                          给定 $n$ 个结点, $m$ 次操作

                          每次操作给出 $a,b,c,d$ ,表示

                          也就是编号在 $[a,b]$ 内的所有结点与编号在 $[c,d]$ 内的结点存在边权为 $1$ 的无向边

                          求 $s$ 到每个点的最短路。

                          $1\le n\le 5\times 10^5$,$1\le m\le 10^5$,$1\le a\le b\le n$,$1\le c\le d\le n$。

                          线段树优化建图板子(区间连区间),不会单点连区间的戳这里

                          同样的,我们还是建两棵树,称为「入树」和「出树」

                          先考虑连有向边的情况。

                          对于区间的修改,我们只需要把入树和出树中的结点提出来

                          出树中提取的结点构成 $S$ ,入树中提取的结点构成 $T$

                          我们新建一个虚点 $u$ ,把 $S$ 中的每个结点向 $u$ 连一条边权为 $0$ 的有向边

                          然后再把 $u$ 向 $T$ 中的每个结点分别连一条边权为 $1$ 的有向边

                          然后这道题要连无向边,所以我们反过来再做一次就行了

                          注意!反过来的时候一定要再建一个虚点,不然会出错!(显然)

                          纯文本可能不是很好懂,所以放张图。

                          这道题因为边权只有 $0,1$ ,考虑 $\mathtt{01bfs}$

                          时间复杂度 $O((n+m)\log n)$

                          空间复杂度 $O(2n\log n + 2m)$

                          代码:(常数比较大

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define INF 0x3f3f3f3f#define DEBUG puts("-------------")namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N ((int)(5e5+15))#define M ((int)(1e5+15))const int D=2e6+15;const int D2=N*8+255;int n,m,s,cnt,d[N*8+255+M*2],a[N*8+255];bitset<N*8+255+M*2> vis;struct node1{int v,w;};struct show_l_r{int l,r;}tr[N<<2];vector<node1> g[N*8+255+M*2];#define ls(x) (x<<1)#define rs(x) (x<<1|1)void build(int l,int r,int at){    tr[at].l=l; tr[at].r=r;    if(l==r) return a[l]=at, void(0);    int mid=(l+r)>>1;    g[at].push_back({ls(at),0});    g[at].push_back({rs(at),0});    g[ls(at)+D].push_back({at+D,0});    g[rs(at)+D].push_back({at+D,0});    build(l,mid,ls(at)); build(mid+1,r,rs(at));}void link(int nl,int nr,int tu,int w,int opt,int at){    int l=tr[at].l,r=tr[at].r;    if(nl<=l&&r<=nr)    {        if(opt) g[at+D].push_back({tu,w});        else g[tu].push_back({at,w});        return;    }    int mid=(l+r)>>1;    if(nl<=mid) link(nl,nr,tu,w,opt,ls(at));    if(nr>mid) link(nl,nr,tu,w,opt,rs(at));}deque<int> q;void _01bfs(int st){    memset(d,0x3f,sizeof(d));    d[st]=0; q.push_back(st);    while(!q.empty())    {        int u=q.front(); q.pop_front();        if(vis[u]) continue;        vis[u]=1;        for(auto i : g[u])        {            int v=i.v,w=i.w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                if(!w) q.push_front(v);                else q.push_back(v);            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m); read(s);    build(1,n,1);    for(int i=1,l1,r1,l2,r2,s1,s2; i<=m; i++)    {        read(l1); read(r1); read(l2); read(r2);        // void link(int nl, int nr, int tu, int w, int opt, int at)        s1=i*2-1+D2,s2=i*2+D2;        link(l1,r1,s1,0,1,1);        link(l2,r2,s1,1,0,1);        link(l2,r2,s2,0,1,1);        link(l1,r1,s2,1,0,1);    }    for(int i=1; i<=n; i++)    {        g[a[i]].push_back({a[i]+D,0}),        g[a[i]+D].push_back({a[i],0});    }    _01bfs(a[s]+D);    for(int i=1; i<=n; i++)        write(d[a[i]]!=INF ? d[a[i]] : -1),pc('\n');    return 0;}
                          ]]> + 洛谷P6348 [PA2011]Journeys题解

                          题目链接:P6348[PA2011]Journeys

                          题意

                          给定 \(n\) 个结点, \(m\) 次操作

                          每次操作给出 \(a,b,c,d\) ,表示\[\forall x\in [a,b],\forall y\in [c,d], \exist (x,y) \in E\land (y,x) \inE\] 也就是编号在 \([a,b]\)内的所有结点与编号在 \([c,d]\)内的结点存在边权为 \(1\)的无向边

                          \(s\) 到每个点的最短路。

                          \(1\le n\le 5\times 10^5\)\(1\le m\le 10^5\),\(1\le a\le b\le n\),\(1\le c\le d\le n\)。

                          线段树优化建图板子(区间连区间),不会单点连区间的戳这里

                          同样的,我们还是建两棵树,称为「入树」和「出树」

                          先考虑连有向边的情况。

                          对于区间的修改,我们只需要把入树和出树中的结点提出来

                          出树中提取的结点构成 \(S\) ,入树中提取的结点构成\(T\)

                          我们新建一个虚点 \(u\) ,把 \(S\) 中的每个结点向 \(u\) 连一条边权为 \(0\) 的有向边

                          然后再把 \(u\)\(T\) 中的每个结点分别连一条边权为 \(1\) 的有向边

                          然后这道题要连无向边,所以我们反过来再做一次就行了

                          注意!反过来的时候一定要再建一个虚点,不然会出错!(显然)

                          纯文本可能不是很好懂,所以放张图。

                          这道题因为边权只有 \(0,1\) ,考虑\(\mathtt{01bfs}\)

                          时间复杂度 \(O((n+m)\log n)\)

                          空间复杂度 \(O(2n\log n + 2m)\)

                          代码:(常数比较大

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define INF 0x3f3f3f3f#define DEBUG puts("-------------")namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N ((int)(5e5+15))#define M ((int)(1e5+15))const int D=2e6+15;const int D2=N*8+255;int n,m,s,cnt,d[N*8+255+M*2],a[N*8+255];bitset<N*8+255+M*2> vis;struct node1{int v,w;};struct show_l_r{int l,r;}tr[N<<2];vector<node1> g[N*8+255+M*2];#define ls(x) (x<<1)#define rs(x) (x<<1|1)void build(int l,int r,int at){    tr[at].l=l; tr[at].r=r;    if(l==r) return a[l]=at, void(0);    int mid=(l+r)>>1;    g[at].push_back({ls(at),0});    g[at].push_back({rs(at),0});    g[ls(at)+D].push_back({at+D,0});    g[rs(at)+D].push_back({at+D,0});    build(l,mid,ls(at)); build(mid+1,r,rs(at));}void link(int nl,int nr,int tu,int w,int opt,int at){    int l=tr[at].l,r=tr[at].r;    if(nl<=l&&r<=nr)    {        if(opt) g[at+D].push_back({tu,w});        else g[tu].push_back({at,w});        return;    }    int mid=(l+r)>>1;    if(nl<=mid) link(nl,nr,tu,w,opt,ls(at));    if(nr>mid) link(nl,nr,tu,w,opt,rs(at));}deque<int> q;void _01bfs(int st){    memset(d,0x3f,sizeof(d));    d[st]=0; q.push_back(st);    while(!q.empty())    {        int u=q.front(); q.pop_front();        if(vis[u]) continue;        vis[u]=1;        for(auto i : g[u])        {            int v=i.v,w=i.w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                if(!w) q.push_front(v);                else q.push_back(v);            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m); read(s);    build(1,n,1);    for(int i=1,l1,r1,l2,r2,s1,s2; i<=m; i++)    {        read(l1); read(r1); read(l2); read(r2);        // void link(int nl, int nr, int tu, int w, int opt, int at)        s1=i*2-1+D2,s2=i*2+D2;        link(l1,r1,s1,0,1,1);        link(l2,r2,s1,1,0,1);        link(l2,r2,s2,0,1,1);        link(l1,r1,s2,1,0,1);    }    for(int i=1; i<=n; i++)    {        g[a[i]].push_back({a[i]+D,0}),        g[a[i]+D].push_back({a[i],0});    }    _01bfs(a[s]+D);    for(int i=1; i<=n; i++)        write(d[a[i]]!=INF ? d[a[i]] : -1),pc('\n');    return 0;}
                          ]]> @@ -126,7 +126,7 @@ /2022/08/21/cf786b-legacy-ti-jie/ - CF786B Legacy 题解

                          题目链接:CF786B Legacy

                          题意

                          $n$ 个结点, $q$ 次操作,问操作后从 $s$ 出发的单源最短路

                          操作如下

                          • 输入 1 u v w 表示 $u$ 到 $v$ 连一条有向边
                          • 输入 2 u l r w 表示 $u$ 到 $[l,r]$ 的每个结点连一条有向边
                          • 输入 3 u l r w 表示 $[l,r]$ 的每个结点向 $u$ 连一条有向边

                          对于 $100\%$ 的数据,$1\le n,q \le 10^5$,$1\le w \le 10^9$

                          线段树优化建图的板子

                          考虑建两棵线段树,分别称为「入树」和「出树」

                          入树的父节点向子结点连一条边权为 $0$ 的有向边

                          出树的子节点向父结点连一条边权为 $0$ 的有向边

                          两棵树的叶子结点均表示原图中的点

                          这样每次区间的连边,我们直接在线段树上把管理对应区间的结点当作 $v$ 即可

                          具体地,

                          • 把 $u$ 到 $[l,r]$ 的每个结点连一条有向边

                            我们只要找出在入树上对应的结点,把出树中的 $u$ 向这些结点都连一条有向边就行了

                          • 把 $[l,r]$ 的每个结点向 $u$ 连一条有向边

                            我们只要找出在出树上对应的结点,把这些结点都向入树中的 $u$ 连一条有向边就行了

                          这么说很抽象,给张图大家就懂了 made by diagrams

                          可以证明,这样修改的复杂度为 $O(\log n)$

                          总时间复杂度 $O(n\log n + q \log n)$

                          注意要记录结点 $i$ 对应入树的叶子结点的编号,这两个是不一样的!

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define DEBUG puts("-------------")namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N ((int)(1e5+15))const int D=5e5+15; // 两棵线段树的分界点int n,m,s,d[N*10+255],a[N*10+255]; // a是结点编号对应的线段树叶子结点的编号bitset<N*10+255> vis;struct node1{int v,w;}; // 边struct show_l_r{int l,r;}tr[N<<2]; // 辅助树vector<node1> g[N*10+255]; // 两棵线段树struct node2{int u,dis;};bool operator<(node2 a,node2 b){return a.dis>b.dis;}#define ls(x) (x<<1)#define rs(x) (x<<1|1)void build(int l,int r,int at){    tr[at].l=l; tr[at].r=r;    if(l==r) return a[l]=at, void(0);    int mid=(l+r)>>1;    g[at].push_back({ls(at),0});    g[at].push_back({rs(at),0});    g[ls(at)+D].push_back({at+D,0});    g[rs(at)+D].push_back({at+D,0});    build(l,mid,ls(at)); build(mid+1,r,rs(at));}void link(int nl,int nr,int u,int w,int opt,int at){    int l=tr[at].l,r=tr[at].r;    if(nl<=l&&r<=nr)    {        if(opt) g[at+D].push_back({u,w});        else g[u+D].push_back({at,w});        return;    }    int mid=(l+r)>>1;    if(nl<=mid) link(nl,nr,u,w,opt,ls(at));    if(nr>mid) link(nl,nr,u,w,opt,rs(at));    }void Dijkstra(int st){    priority_queue<node2> q;    memset(d,0x3f,sizeof(d));    d[st]=0; vis=0; q.push({st,0});    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u]) continue;        vis[u]=1;        for(auto i : g[u])        {            int v=i.v,w=i.w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m); read(s);    build(1,n,1);    for(int i=1,op,u,v,l,r,w; i<=m; i++)    {        read(op);        if(op==1)        {            read(u); read(v); read(w);            g[a[u]].push_back({a[v],w});        }        else        {            read(u); read(l); read(r); read(w);            link(l,r,a[u],w,op&1,1); // op=2: u->[l,r] , op=3:[l,r]->u;        }    }    for(int i=1; i<=n; i++)    {        g[a[i]].push_back({a[i]+D,0}),        g[a[i]+D].push_back({a[i],0});    }    Dijkstra(a[s]+D);    for(int i=1; i<=n; i++)        write(d[a[i]]!=INF ? d[a[i]] : -1),pc(" \n"[i==n]);    return 0;}

                          题外话

                          Rick and his co-workers have made a new radioactive formula and a lot of bad guys are after them. So Rick wants to give his legacy to Morty before bad guys catch them.

                          Rick 和他的同事们做出了一种新的带放射性的婴儿食品(???根据图片和原文的确如此…),与此同时很多坏人正追赶着他们。因此 Rick 想在坏人们捉到他之前把他的遗产留给 Morty。

                          带放射性的婴儿食品。给我来一碗,美汁汁

                          ]]> + CF786B Legacy 题解

                          题目链接:CF786BLegacy

                          题意

                          \(n\) 个结点, \(q\) 次操作,问操作后从 \(s\) 出发的单源最短路

                          操作如下

                          • 输入 1 u v w 表示 \(u\) 到 \(v\) 连一条有向边
                          • 输入 2 u l r w 表示 \(u\) 到 \([l,r]\) 的每个结点连一条有向边
                          • 输入 3 u l r w 表示 \([l,r]\) 的每个结点向 \(u\) 连一条有向边

                          对于 \(100\%\) 的数据,\(1\le n,q \le 10^5\),\(1\le w \le 10^9\)

                          线段树优化建图的板子

                          考虑建两棵线段树,分别称为「入树」和「出树」

                          入树的父节点向子结点连一条边权为 \(0\) 的有向边

                          出树的子节点向父结点连一条边权为 \(0\) 的有向边

                          两棵树的叶子结点均表示原图中的点

                          这样每次区间的连边,我们直接在线段树上把管理对应区间的结点当作 \(v\) 即可

                          具体地,

                          • \(u\)\([l,r]\) 的每个结点连一条有向边

                            我们只要找出在入树上对应的结点,把出树中的 \(u\) 向这些结点都连一条有向边就行了

                          • \([l,r]\) 的每个结点向 \(u\) 连一条有向边

                            我们只要找出在出树上对应的结点,把这些结点都向入树中的 \(u\) 连一条有向边就行了

                          这么说很抽象,给张图大家就懂了 made by diagrams

                          可以证明,这样修改的复杂度为 \(O(\logn)\)

                          总时间复杂度 \(O(n\log n + q \logn)\)

                          注意要记录结点 \(i\)对应入树的叶子结点的编号,这两个是不一样的!

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define DEBUG puts("-------------")namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N ((int)(1e5+15))const int D=5e5+15; // 两棵线段树的分界点int n,m,s,d[N*10+255],a[N*10+255]; // a是结点编号对应的线段树叶子结点的编号bitset<N*10+255> vis;struct node1{int v,w;}; // 边struct show_l_r{int l,r;}tr[N<<2]; // 辅助树vector<node1> g[N*10+255]; // 两棵线段树struct node2{int u,dis;};bool operator<(node2 a,node2 b){return a.dis>b.dis;}#define ls(x) (x<<1)#define rs(x) (x<<1|1)void build(int l,int r,int at){    tr[at].l=l; tr[at].r=r;    if(l==r) return a[l]=at, void(0);    int mid=(l+r)>>1;    g[at].push_back({ls(at),0});    g[at].push_back({rs(at),0});    g[ls(at)+D].push_back({at+D,0});    g[rs(at)+D].push_back({at+D,0});    build(l,mid,ls(at)); build(mid+1,r,rs(at));}void link(int nl,int nr,int u,int w,int opt,int at){    int l=tr[at].l,r=tr[at].r;    if(nl<=l&&r<=nr)    {        if(opt) g[at+D].push_back({u,w});        else g[u+D].push_back({at,w});        return;    }    int mid=(l+r)>>1;    if(nl<=mid) link(nl,nr,u,w,opt,ls(at));    if(nr>mid) link(nl,nr,u,w,opt,rs(at));    }void Dijkstra(int st){    priority_queue<node2> q;    memset(d,0x3f,sizeof(d));    d[st]=0; vis=0; q.push({st,0});    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u]) continue;        vis[u]=1;        for(auto i : g[u])        {            int v=i.v,w=i.w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m); read(s);    build(1,n,1);    for(int i=1,op,u,v,l,r,w; i<=m; i++)    {        read(op);        if(op==1)        {            read(u); read(v); read(w);            g[a[u]].push_back({a[v],w});        }        else        {            read(u); read(l); read(r); read(w);            link(l,r,a[u],w,op&1,1); // op=2: u->[l,r] , op=3:[l,r]->u;        }    }    for(int i=1; i<=n; i++)    {        g[a[i]].push_back({a[i]+D,0}),        g[a[i]+D].push_back({a[i],0});    }    Dijkstra(a[s]+D);    for(int i=1; i<=n; i++)        write(d[a[i]]!=INF ? d[a[i]] : -1),pc(" \n"[i==n]);    return 0;}

                          题外话

                          Rick and his co-workers have made a new radioactive formula and a lotof bad guys are after them. So Rick wants to give his legacy to Mortybefore bad guys catch them.

                          Rick和他的同事们做出了一种新的带放射性的婴儿食品(???根据图片和原文的确如此...),与此同时很多坏人正追赶着他们。因此Rick 想在坏人们捉到他之前把他的遗产留给 Morty。

                          带放射性的婴儿食品。给我来一碗,美汁汁

                          ]]> @@ -155,7 +155,7 @@ /2022/08/21/luo-gu-p3311-sdoi2014-shu-shu-ti-jie/ - 洛谷P3311 [SDOI2014] 数数 题解

                          题目链接:P3311 [SDOI2014] 数数

                          题意

                          我们称一个正整数 $x$ 是幸运数,当且仅当它的十进制表示中不包含数字串集合 $s$ 中任意一个元素作为其子串。例如当 $s = \{22, 333, 0233\}$ 时,$233$ 是幸运数,$2333$、$20233$、$3223$ 不是幸运数。给定 $n$ 和 $s$,计算不大于 $n$ 的幸运数个数。

                          答案对 $10^9 + 7$ 取模。

                          $1 \leq n < 10^{1201}$,$1 \leq m \leq 100$,$1 \leq \sum_{i = 1}^m |s_i| \leq 1500$,$\min_{i = 1}^m |s_i| \geq 1$,其中 $|s_i|$ 表示字符串 $s_i$ 的长度。$n$ 没有前导 $0$,但是 $s_i$ 可能有前导 $0$。

                          关键词:数位dp、AC自动机

                          可能的前置芝士:P4052 [JSOI2007]文本生成器 (涉及“危险结点”的定义)

                          和P4052一样,我们先建出 $\tt{AC}$ 自动机,然后找到所有的危险结点

                          因为这道题要求答案不超过 $n$ ,并且 $n < 10^{1201}$ ,考虑数位dp

                          设 $f_{i,j}$ 表示考虑 $n$ 的前 $i$ 位(从高位向低位数),走到 $\tt{AC}$ 自动机的 $j$ 结点的答案。

                          然后用记搜搞搞就好了,懒得写转移方程,直接看代码

                          值得注意的是,如果数位dp的过程中 $j$ 走到了危险结点,

                          则要立刻返回 $0$ ,因为不能包含任何的非法子串 $s_i$

                          时间复杂度 $O(n \times \sum |s_i|)$

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1515)const int mod=1e9+7;int n,num[N],f[N][N];struct Trie{    bitset<N> ed;    int tot,fail[N],trie[N][10];    void insert(string s)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            char c=s[i]-'0';            if(!trie[u][c]) trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1;    }    void build()    {        queue<int> q;        for(int i=0; i<10; i++)            if(trie[0][i]) q.push(trie[0][i]);        while(!q.empty())        {            int u=q.front(); q.pop();            for(int i=0; i<10; i++)            {                if(trie[u][i])                {                    fail[trie[u][i]]=trie[fail[u]][i];                    if(ed[fail[trie[u][i]]]) ed[trie[u][i]]=1;                    q.push(trie[u][i]);                }else trie[u][i]=trie[fail[u]][i];            }        }    }}tr;void add(int &x,int y){ (x+=y) >= mod ? x-=mod : 0;}int dfs(int u,int pos,bool limit,bool qd0){    if(!u) return !tr.ed[pos];    if(tr.ed[pos]) return 0;    if(!limit&&!qd0&&f[u][pos]!=-1) return f[u][pos];    int res=0,up=limit?num[u]:9;    for(int i=0; i<=up; i++)    {        if(!i&&qd0) add(res,dfs(u-1,0,limit&&num[u]==i,1));        else add(res,dfs(u-1,tr.trie[pos][i],limit&&num[u]==i,0));    }    if(!limit&&!qd0) f[u][pos]=res;    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    string s; cin >> s >> n;    int len=s.size();    for(int i=0; i<len; i++) num[len-i]=s[i]-'0';    for(int i=1; i<=n; i++) cin >> s,tr.insert(s);        tr.build(); memset(f,-1,sizeof(f));    int ans=dfs(len,0,1,1); add(ans,mod-1); // no 0    cout << ans << '\n';    return 0;}
                          ]]> + 洛谷P3311 [SDOI2014] 数数题解

                          题目链接:P3311[SDOI2014] 数数

                          题意

                          我们称一个正整数 \(x\)是幸运数,当且仅当它的十进制表示中不包含数字串集合 \(s\) 中任意一个元素作为其子串。例如当 \(s = \{22, 333, 0233\}\) 时,\(233\) 是幸运数,\(2333\)、\(20233\)、\(3223\) 不是幸运数。给定 \(n\) 和 \(s\),计算不大于 \(n\) 的幸运数个数。

                          答案对 \(10^9 + 7\) 取模。

                          \(1 \leq n < 10^{1201}\)\(1 \leq m \leq 100\),\(1 \leq \sum_{i = 1}^m |s_i| \leq1500\),\(\min_{i = 1}^m |s_i| \geq1\),其中 \(|s_i|\) 表示字符串\(s_i\) 的长度。\(n\) 没有前导 \(0\),但是 \(s_i\) 可能有前导 \(0\)。

                          关键词:数位dp、AC自动机

                          可能的前置芝士:P4052[JSOI2007]文本生成器 (涉及“危险结点”的定义)

                          和P4052一样,我们先建出 \(\tt{AC}\)自动机,然后找到所有的危险结点

                          因为这道题要求答案不超过 \(n\),并且 \(n < 10^{1201}\),考虑数位dp

                          \(f_{i,j}\) 表示考虑 \(n\) 的前 \(i\) 位(从高位向低位数),走到 \(\tt{AC}\) 自动机的 \(j\) 结点的答案。

                          然后用记搜搞搞就好了,懒得写转移方程,直接看代码

                          值得注意的是,如果数位dp的过程中 \(j\) 走到了危险结点,

                          则要立刻返回 \(0\),因为不能包含任何的非法子串 \(s_i\)

                          时间复杂度 \(O(n \times \sum|s_i|)\)

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1515)const int mod=1e9+7;int n,num[N],f[N][N];struct Trie{    bitset<N> ed;    int tot,fail[N],trie[N][10];    void insert(string s)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            char c=s[i]-'0';            if(!trie[u][c]) trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1;    }    void build()    {        queue<int> q;        for(int i=0; i<10; i++)            if(trie[0][i]) q.push(trie[0][i]);        while(!q.empty())        {            int u=q.front(); q.pop();            for(int i=0; i<10; i++)            {                if(trie[u][i])                {                    fail[trie[u][i]]=trie[fail[u]][i];                    if(ed[fail[trie[u][i]]]) ed[trie[u][i]]=1;                    q.push(trie[u][i]);                }else trie[u][i]=trie[fail[u]][i];            }        }    }}tr;void add(int &x,int y){ (x+=y) >= mod ? x-=mod : 0;}int dfs(int u,int pos,bool limit,bool qd0){    if(!u) return !tr.ed[pos];    if(tr.ed[pos]) return 0;    if(!limit&&!qd0&&f[u][pos]!=-1) return f[u][pos];    int res=0,up=limit?num[u]:9;    for(int i=0; i<=up; i++)    {        if(!i&&qd0) add(res,dfs(u-1,0,limit&&num[u]==i,1));        else add(res,dfs(u-1,tr.trie[pos][i],limit&&num[u]==i,0));    }    if(!limit&&!qd0) f[u][pos]=res;    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    string s; cin >> s >> n;    int len=s.size();    for(int i=0; i<len; i++) num[len-i]=s[i]-'0';    for(int i=1; i<=n; i++) cin >> s,tr.insert(s);        tr.build(); memset(f,-1,sizeof(f));    int ans=dfs(len,0,1,1); add(ans,mod-1); // no 0    cout << ans << '\n';    return 0;}
                          ]]> @@ -184,7 +184,7 @@ /2022/08/21/luo-gu-p3193-hnoi2008-gt-kao-shi-ti-jie/ - 洛谷P3193 [HNOI2008]GT考试 题解

                          题目链接:P3193 [HNOI2008]GT考试

                          题意

                          阿申准备报名参加 GT 考试,准考证号为 $N$ 位数$X_1,X_2…X_n(0\le X_i\le9)$,他不希望准考证号上出现不吉利的数字。
                          他的不吉利数字$A_1,A_2…A_m(0\le A_i\le 9)$ 有 $M$ 位,不出现是指 $X_1,X_2…X_n$ 中没有恰好一段等于 $A_1,A_2…A_m$,$A_1$ 和$X_1$ 可以为 $0$

                          $N\leq10^9,M\leq20,K\leq1000$

                          显然是矩阵快速幂优化dp(看数据范围)。

                          设 $f_{i,j}$ 表示考虑到第 $i$ 个数,匹配到 $X$ 的第 $j$ 位时的方案数,则

                          注意这里的 $0\le j < m$ ,因为我们不能让它包含完整的 $X$ 。

                          这个 $p$ 不一定是 $0$ 或 $j-1$ ,根据KMP的性质可以很容易发现。

                          看上去这个可以用KMP来搞搞,又长的很像矩阵快速幂的题目

                          于是尝试套路地构造出预处理数组 $g_{i,j}$ ,以产生矩阵快速幂的形式

                          设 $g_{i,j}$ 表示匹配到 $X$ 的第 $i$ 位,有多少种加字符 $0\sim9$ 的方法可以使其匹配到 $j$

                          注意到题目中 $X_1$ 是可以为 $0$ 的,因此不用单独考虑前导零的问题,则

                          注意这里 $k$ 的意义改变为了:枚举已经匹配的长度,尝试加一个字符转移至 $j$ 。

                          然后把式子看成这样的形式 $f^{i}_{j} =\sum f^{i-1}_{k} \times g_{k,j}$ ,显然与矩阵乘法的定义相同

                          我们把 $f$ 和 $g$ 看作两个矩阵(注意 $f$ 是行矩阵),则有

                          然后我们矩阵快速幂求一下就好啦

                          最后答案就是 $\sum\limits_{i=0}^{m-1} f^{n}_{i}$ ,没有 $m-1$ 的原因是,不能包含 $m$

                          时间复杂度 $O(m^3 \log n)$

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(25)typedef vector< vector <int> > mat;char s[N];int n,m,mod,fail[N];void add(int &x,int y){ (x+=y) >= mod ? x-=mod : 0;}mat mul(mat A,mat B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vector<int>(p));    for(int i=0; i<m; i++)        for(int j=0; j<n; j++)            for(int k=0; k<p; k++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int b){    int n=max(A.size(),A[0].size());    mat B(n,vector<int>(n));    for(int i=0; i<n; i++) B[i][i]=1;    for(; b; b>>=1)    {        if(b&1) B=mul(B,A);        A=mul(A,A);    }    return B;}mat kmp(){    for(int i=2,j=0; i<=m; i++)    {        while(j&&s[i]!=s[j+1]) j=fail[j];        if(s[i]==s[j+1])++j;        fail[i]=j;    }    mat A(m,vector<int>(m));    for(int i=0,j; i<m; i++)        for(int c='0'; c<='9'; c++)        {            j=i; while(j&&s[j+1]!=c) j=fail[j];            if(s[j+1]==c) ++j;            add(A[i][j],1);        }    return A;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> mod >> (s+1);    mat G=kmp(); G=qpow(G,n);    int res=0;    for(int i=0; i<m; i++) add(res,G[0][i]);    cout << res << '\n';    return 0;}
                          ]]> + 洛谷P3193 [HNOI2008]GT考试题解

                          题目链接:P3193[HNOI2008]GT考试

                          题意

                          阿申准备报名参加 GT 考试,准考证号为 \(N\) 位数\(X_1,X_2…X_n(0\leX_i\le9)\),他不希望准考证号上出现不吉利的数字。他的不吉利数字\(A_1,A_2…A_m(0\le A_i\le9)\)\(M\) 位,不出现是指\(X_1,X_2…X_n\) 中没有恰好一段等于\(A_1,A_2…A_m\)\(A_1\) 和\(X_1\) 可以为 \(0\)

                          \(N\leq10^9,M\leq20,K\leq1000\)

                          显然是矩阵快速幂优化dp(看数据范围)。

                          \(f_{i,j}\) 表示考虑到第 \(i\) 个数,匹配到 \(X\) 的第 \(j\) 位时的方案数,则 \[f_{i,j} = \sum_{k=0}^{9}f_{i-1,p}\] 注意这里的 \(0\le j < m\),因为我们不能让它包含完整的 \(X\)

                          这个 \(p\) 不一定是 \(0\) 或 \(j-1\) ,根据KMP的性质可以很容易发现。

                          看上去这个可以用KMP来搞搞,又长的很像矩阵快速幂的题目

                          于是尝试套路地构造出预处理数组 \(g_{i,j}\) ,以产生矩阵快速幂的形式

                          \(g_{i,j}\) 表示匹配到 \(X\) 的第 \(i\) 位,有多少种加字符\(0\sim9\) 的方法可以使其匹配到 \(j\)

                          注意到题目中 \(X_1\) 是可以为 \(0\) 的,因此不用单独考虑前导零的问题,则\[f_{i,j} = \sum_{k=0}^{m-1} f_{i-1,k}\times g_{k,j}\] 注意这里 \(k\)的意义改变为了:枚举已经匹配的长度,尝试加一个字符转移至 \(j\) 。

                          然后把式子看成这样的形式 \(f^{i}_{j} =\sumf^{i-1}_{k} \times g_{k,j}\) ,显然与矩阵乘法的定义相同

                          我们把 \(f\)\(g\) 看作两个矩阵(注意 \(f\) 是行矩阵),则有 \[f^{k} = f^{k-1} \times g\] 则 \[f^k=g^k\] 然后我们矩阵快速幂求一下就好啦

                          最后答案就是 \(\sum\limits_{i=0}^{m-1}f^{n}_{i}\) ,没有 \(m-1\)的原因是,不能包含 \(m\)

                          时间复杂度 \(O(m^3 \log n)\)

                          代码:

                          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(25)typedef vector< vector <int> > mat;char s[N];int n,m,mod,fail[N];void add(int &x,int y){ (x+=y) >= mod ? x-=mod : 0;}mat mul(mat A,mat B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vector<int>(p));    for(int i=0; i<m; i++)        for(int j=0; j<n; j++)            for(int k=0; k<p; k++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int b){    int n=max(A.size(),A[0].size());    mat B(n,vector<int>(n));    for(int i=0; i<n; i++) B[i][i]=1;    for(; b; b>>=1)    {        if(b&1) B=mul(B,A);        A=mul(A,A);    }    return B;}mat kmp(){    for(int i=2,j=0; i<=m; i++)    {        while(j&&s[i]!=s[j+1]) j=fail[j];        if(s[i]==s[j+1])++j;        fail[i]=j;    }    mat A(m,vector<int>(m));    for(int i=0,j; i<m; i++)        for(int c='0'; c<='9'; c++)        {            j=i; while(j&&s[j+1]!=c) j=fail[j];            if(s[j+1]==c) ++j;            add(A[i][j],1);        }    return A;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> mod >> (s+1);    mat G=kmp(); G=qpow(G,n);    int res=0;    for(int i=0; i<m; i++) add(res,G[0][i]);    cout << res << '\n';    return 0;}
                          ]]> @@ -215,7 +215,7 @@ /2022/08/20/oi-shu-xue-zong-jie-bo-yi-lun/ - OI数学总结-博弈论

                      施工中,咕咕咕…

                      博弈论基础

                      对于博弈论的题目,重点考虑以下两点

                      • 什么时候可以判定胜利者
                      • 先手的影响
                      ]]> + OI数学总结-博弈论

                      施工中,咕咕咕...

                      博弈论基础

                      对于博弈论的题目,重点考虑以下两点

                      • 什么时候可以判定胜利者
                      • 先手的影响
                      ]]> @@ -242,7 +242,7 @@ /2022/08/20/oi-shu-xue-zong-jie-qi-ta/ - OI数学总结-其他

                      本文充满了从各种地方偷学来的东西以及没什么分类的东西

                      数值积分

                      定积分

                      简单来说,函数 $f(x)$ 在区间 $[l,r]$ 上的定积分 $\int_{l}^{r}f(x)\mathrm{d}x$ 指的是 $f(x)$ 在区间 $[l,r]$ 中于 $x$ 轴围成的区域的面积(其中 $x$ 轴上方的部分为正值, $x$ 轴下方的部分为负值)

                      辛普森法

                      辛普森公式

                      对于一个二次函数 $f(x) = Ax^2 + Bx + C$ ,有

                      推导过程: 对于一个二次函数 $f(x)=Ax^2+Bx+C$ ; 求积分可得 $F(x)=\int_0^x f(x) {\mathrm d}x = \frac{a}{3}x^3+\frac{b}{2}x^2+cx+D$ 在这里 D 是一个常数,那么

                      普通辛普森法

                      咕咕咕…


                      洛必达法则

                      设 $c \in \overline{\mathbb{R}}$(扩展实数) ,两函数 $f(x),g(x)$ 在 $x=c$ 为端点的开区间可微,

                      $\lim\limits_{x \to c}\dfrac{f^{\prime}(x)}{g^{\prime}(x)} \in \overline{\mathbb{R}}$ ,并且 $g^{\prime}(x) \ne 0$ 。

                      如果 $\lim\limits_{x \to c}f(x) = \lim\limits_{x \to c} g(x) = 0$ 或 $\lim\limits_{x \to c}|f(x)| = \lim\limits_{x \to c} |g(x)| = \infty$ 其中一者成立,

                      则称欲求的极限 $\lim\limits_{x \to c}\dfrac{f(x)}{g(x)}$ 为未定式。

                      此时洛必达法则表明


                      取整函数

                      向下取整floor(x) $ = [x] = \lfloor x \rfloor $

                      向上取整 ceil(x) $=\lceil x \rceil$

                      小数部分 $\left\{x\right\} = x-\left\lfloor{x}\right\rfloor$

                      部分性质

                      1. $\left\lfloor{x}\right\rfloor \le x < \left\lfloor{x}\right\rfloor+1$ 当且仅当 $x$ 为正整数时取等号

                      2. $\left\lfloor{k+x}\right\rfloor = k+\left\lfloor{x}\right\rfloor,k\in \mathbb{Z},x\in \mathbb{R}$

                      3. $\left\lfloor{x}\right\rfloor \le \left\lceil{x}\right\rceil$

                      4. 四舍五入 = floor(x+0.5)

                      5. $\left\lceil{x}\right\rceil = - \left\lfloor{-x}\right\rfloor$

                      6. 多种嵌套相当于只看最内层的

                      1. 若 $n$ 为正整数

                      ​ 将 $m=2$ 代入可得

                      ]]> + OI数学总结-其他

                    本文充满了从各种地方偷学来的东西以及没什么分类的东西

                    数值积分

                    定积分

                    简单来说,函数 \(f(x)\) 在区间 \([l,r]\) 上的定积分 \(\int_{l}^{r}f(x)\mathrm{d}x\) 指的是 \(f(x)\) 在区间 \([l,r]\) 中于 \(x\) 轴围成的区域的面积(其中 \(x\) 轴上方的部分为正值, \(x\) 轴下方的部分为负值)

                    辛普森法

                    辛普森公式

                    对于一个二次函数 \(f(x) = Ax^2 + Bx +C\) ,有 \[\int_l^rf(x)\mathrm{d}x =\dfrac{(r-l)\left(f(l)+f(r)+4f\left(\frac{l+r}{2}\right)\right)}{6}\]

                    推导过程: 对于一个二次函数 \(f(x)=Ax^2+Bx+C\) ; 求积分可得 \(F(x)=\int_0^x f(x) {\mathrm d}x =\frac{a}{3}x^3+\frac{b}{2}x^2+cx+D\) 在这里 D是一个常数,那么

                    \[\begin{aligned} \int_l^r f(x) {\mathrm d}x &= F(r)-F(l) \\ &=\frac{a}{3}(r^3-l^3)+\frac{b}{2}(r^2-l^2)+c(r-l) \\&=(r-l)\left(\frac{a}{3}(l^2+r^2+lr)+\frac{b}{2}(l+r)+c\right) \\&=\frac{r-l}{6}(2al^2+2ar^2+2alr+3bl+3br+6c)\\&=\frac{r-l}{6}((al^2+bl+c)+(ar^2+br+c)+4\left(a\left(\frac{l+r}{2}\right)^2+b\left(\frac{l+r}{2}\right)+c)\right)\\&=\frac{r-l}{6}\left(f(l)+f(r)+4f\left(\frac{l+r}{2}\right)\right)\end{aligned}\]

                    普通辛普森法

                    咕咕咕...


                    洛必达法则

                    \(c \in\overline{\mathbb{R}}\)(扩展实数) ,两函数 \(f(x),g(x)\) 在 \(x=c\) 为端点的开区间可微,

                    \(\lim\limits_{x \toc}\dfrac{f^{\prime}(x)}{g^{\prime}(x)} \in\overline{\mathbb{R}}\) ,并且 \(g^{\prime}(x) \ne 0\) 。

                    如果 \(\lim\limits_{x \to c}f(x) =\lim\limits_{x \to c} g(x) = 0\)\(\lim\limits_{x \to c}|f(x)| = \lim\limits_{x \toc} |g(x)| = \infty\) 其中一者成立,

                    则称欲求的极限 \(\lim\limits_{x \toc}\dfrac{f(x)}{g(x)}\) 为未定式。

                    此时洛必达法则表明 \[\lim_{x \to c}\dfrac{f(x)}{g(x)} = \lim_{x \toc}\dfrac{f^{\prime}(x)}{g^{\prime}(x)}\]


                    取整函数

                    向下取整floor(x) $ = [x] = x $

                    向上取整 ceil(x) \(=\lceil x\rceil\)

                    小数部分 \(\left\{x\right\} =x-\left\lfloor{x}\right\rfloor\)

                    部分性质

                    1. \(\left\lfloor{x}\right\rfloor \le x< \left\lfloor{x}\right\rfloor+1\) 当且仅当 \(x\) 为正整数时取等号

                    2. \(\left\lfloor{k+x}\right\rfloor =k+\left\lfloor{x}\right\rfloor,k\in \mathbb{Z},x\in\mathbb{R}\)

                    3. \(\left\lfloor{x}\right\rfloor \le\left\lceil{x}\right\rceil\)

                    4. 四舍五入 = floor(x+0.5)

                    5. \[\begin{aligned}&\left\lceil{x}\right\rceil-\left\lfloor{x}\right\rfloor=\begin{cases}0,&\mbox{if } x \in \mathbb{Z},\\\\1,&\mbox{if }x \notin \mathbb{Z}.\end{cases}\\\\&\left\lfloor{x}\right\rfloor+\left\lfloor{-x}\right\rfloor=\begin{cases}0,&\mbox{if } x \in \mathbb{Z},\\\\-1,&\mbox{if }x \notin \mathbb{Z}.\end{cases}\\\\&\left\lceil{x}\right\rceil+\left\lceil{-x}\right\rceil=\begin{cases}0,&\mbox{if } x \in \mathbb{Z},\\\\1,&\mbox{if }x \notin \mathbb{Z}.\end{cases}\\\\&\{x\}+\{-x\}=\begin{cases}0,&\mbox{if } x \in \mathbb{Z},\\\\1,&\mbox{if }x \notin \mathbb{Z}.\end{cases}\end{aligned}\]

                    6. \(\left\lceil{x}\right\rceil = -\left\lfloor{-x}\right\rfloor\)

                    7. \[\begin{aligned}&\left\lceil\left\lceil{x}\right\rceil\right\rceil=\left\lceil{x}\right\rceil\\\\&\left\lfloor\left\lfloor{x}\right\rfloor\right\rfloor=\left\lfloor{x}\right\rfloor\\\\&\left\{\left\{x\right\}\right\}=\left\{x\right\}\end{aligned}\]

                    8. 多种嵌套相当于只看最内层的

                    \[\begin{aligned} &\left\lfloor{\left\lceil{x}\right\rceil}\right\rfloor=\left\lceil{x}\right\rceil \\ &\left\lceil{\left\lfloor{x}\right\rfloor}\right\rceil=\left\lfloor{x}\right\rfloor \end{aligned}\]

                    1. \(n\) 为正整数

                    \[\begin{aligned}n&=\left\lceil{\dfrac{n}{m}}\right\rceil+\left\lceil{\dfrac{n-1}{m}}\right\rceil+\cdots+ \left\lceil{\dfrac{n-m+1}{m}}\right\rceil\\n&=\left\lfloor{\dfrac{n}{m}}\right\rfloor+\left\lfloor{\dfrac{n+1}{m}}\right\rfloor+\cdots+\left\lfloor{\dfrac{n+m-1}{m}}\right\rfloor\end{aligned}\]

                    ​ 将 \(m=2\) 代入可得 \[n=\left\lfloor{\dfrac{n}{2}}\right\rfloor+\left\lceil{\dfrac{n}{2}}\right\rceil\]

                    ]]> @@ -269,7 +269,7 @@ /2022/08/20/oi-shu-xue-zong-jie-qun-lun/ - OI数学总结-群论

                    施工中,咕咕咕….

                    群的基本概念

                    群的定义

                    群公理包含下述四个性质(有时略去封闭性,只有三个性质)。若集合 $G\neq\varnothing$ 和 $G$ 上的运算 $\cdot$ 构成的代数结构 $(G,\cdot)$ 满足以下性质:

                    1. 封闭性:对于所有 $G$ 中 $a, b$ ,运算 $a·b$ 的结果也在 $G$ 中。
                    2. 结合律:对于 $G$ 中所有的 $a, b, c$ ,等式 $(a \cdot b)\cdot c = a \cdot (b \cdot c)$ 成立。
                    3. 标识元(单位元): $G$ 中存在一个元素 $e$ ,使得对于 $G$ 中的每一个 $a$ ,都有一个 $e \cdot a=a\cdot e=a$ 成立。这样的元素是独一无二的。它被称为群的标识元素。
                    4. 逆元:对于每个 $G$ 中的 $a$ ,总存在 $G$ 中的一个元素 $b$ 使 $a \cdot b = b \cdot a = e$ ,此处 $e$ 为单位元,称 $b$ 为 $a$ 的逆元,记为 $a^{-1}$ 。

                    则称 $(G,\cdot)$ 为一个

                    例如,整数集和整数间的加法 $(\mathbb{Z},+)$ 构成一个群,单位元是 $0$,一个整数的逆元是它的相反数。

                    阶的定义

                    • 一个群的阶是指其势,即其元素的个数;

                    • 一个群内的一个元素 $a$ 的阶(有时称为周期)

                      是指会使得 $a^m = e$ 的最小正整数 $m$ (其中的e为这个群的单位元素,且$a^m$为 $a$ 的 $m$ 次幂)

                      若没有此数存在,则称 $a$ 有无限阶。有限群的所有元素都有有限阶。

                    群的主要类别

                    置换群

                    对于给定的集合 $X$ ,$X$ 到自身的一些置换集合 $G$ 如果在复合运算和求逆运算下封闭,那么称 $G$ 是一个作用于 $X$ 上的群。

                    易证,集合 $S$ 上的所有置换关于置换的乘法满足封闭性、结合律、有单位元(恒等置换,即每个元素映射成它自己)、有逆元(交换置换表示中的上下两行),因此构成一个群。这个群的任意一个 子群 即称为 置换群

                    置换的定义

                    有限集合到自身的双射成为置换。集合 $S = \{a_1,a_2,\cdots ,a_n\}$ 上的置换可以表示为

                    意为将 $a_i$ 映射为 $a_{p_i}$ ,其中 $p_1,p_2,\cdots,p_n$ 是 $1,2,\cdots,n$ 的一个排列

                    显然 $S$ 上所有置换的数量为 $n!$

                    置换的乘法

                    对于两个置换 $f= \begin{pmatrix}a_1,a_2,\cdots,a_n\\a_{p_1},a_{p_2},\cdots,a_{p_n}\end{pmatrix}$和 $g= \begin{pmatrix}a_{p_1},a_{p_2},\cdots,a_{p_n}\\a_{q_1},a_{q_2},\cdots,a_{q_n}\end{pmatrix}$ ,$f$ 和 $g$ 的乘积记为 $f \circ g$ ,其值为

                    简单来说就是先后经过 $f$ 的映射,再经过 $g$ 的映射。

                    循环置换

                    循环置换是一类特殊的置换,可表示为

                    若两个循环置换不含有相同的元素,则称它们是不相交的,有如下定理:

                    任意一个置换都可以分解为若干不相交的循环置换的乘积,例如

                    该定理的证明非常简单。如果把元素视为图的结点,映射关系视为有向边,则每个结点的入度和出度都为 $1$ ,因此形成的图形必定是若干个环的集合,而一个环即可用一个循环置换表示。

                    循环群

                    循环群是最简单的群

                    群 $G$ 中任意一个元素 $a$ 都可以表示为 $a=g^k$ ,其 $k$ 为整数。称 $g$ 为群 $G$ 的生成元。

                    有定理:生成元 $g$ 的阶就是群 $G$ 的阶。

                    阶为 $m$ 的有限循环群 $G$ 同构于模 $m$ 剩余类对于加法构成的群 $Z_m$。

                    Burnside 引理

                    设 $A,B$ 为有限集合,$X$ 为一些从 $A$ 到 $B$ 的映射组成的集合。

                    $G$ 是 $A$ 上的置换群,且 $X$ 中的映射在 $G$ 中的置换作用下封闭。

                    $X/G$ 表示 $G$ 作用在 $X$ 产生的所有等价类的集合

                    (若 $X$ 中的两个映射经过 $G$ 中的置换作用后相等,则它们在同一个等价类中),则

                    其中 $|S|$ 表示集合 $S$ 的基数,即元素个数,且

                    举例:

                    用 $3$ 种颜色给一个立方体染色,求本质不同的方案数

                    本质不同指:经过任意翻转后,两种染色方案均不同

                    则根据上面的模板,有

                    • $A$ :立方体 $6$ 个面的集合
                    • $B$ : $3$ 种颜色的集合
                    • $X$ :直接给每个面染色,不考虑本质不同的方案的集合,共有 $3^6$ 种
                    • $G$ :各种翻转操作构成的置换群
                    • $X/G$ :本质不同的染色方案的集合
                    • $X^g$ :对于某一种反转操作 $g$ ,所有直接染色方案中,经过 $g$ 这种翻转后保持不变的染色方案的集合

                    下面懒得写了,直接看图片吧。

                    ]]> + OI数学总结-群论

                    施工中,咕咕咕....

                    群的基本概念

                    群的定义

                    群公理包含下述四个性质(有时略去封闭性,只有三个性质)。若集合 \(G\neq\varnothing\) 和 \(G\) 上的运算 \(\cdot\) 构成的代数结构 \((G,\cdot)\) 满足以下性质:

                    1. 封闭性:对于所有 \(G\) 中 \(a,b\) ,运算 \(a·b\) 的结果也在\(G\) 中。
                    2. 结合律:对于 \(G\)中所有的 \(a, b, c\) ,等式 \((a \cdot b)\cdot c = a \cdot (b \cdot c)\)成立。
                    3. 标识元(单位元): \(G\) 中存在一个元素 \(e\) ,使得对于 \(G\) 中的每一个 \(a\) ,都有一个 \(e \cdot a=a\cdot e=a\)成立。这样的元素是独一无二的。它被称为群的标识元素。
                    4. 逆元:对于每个 \(G\) 中的 \(a\) ,总存在 \(G\) 中的一个元素 \(b\) 使 \(a \cdotb = b \cdot a = e\) ,此处 \(e\)为单位元,称 \(b\)\(a\) 的逆元,记为 \(a^{-1}\) 。

                    则称 \((G,\cdot)\) 为一个

                    例如,整数集和整数间的加法 \((\mathbb{Z},+)\) 构成一个群,单位元是 \(0\),一个整数的逆元是它的相反数。

                    阶的定义

                    • 一个群的阶是指其势,即其元素的个数;

                    • 一个群内的一个元素 \(a\)的阶(有时称为周期)

                      是指会使得 \(a^m = e\) 的最小正整数\(m\)(其中的e为这个群的单位元素,且\(a^m\)为 \(a\) 的 \(m\) 次幂)

                      若没有此数存在,则称 \(a\)有无限阶。有限群的所有元素都有有限阶。

                    群的主要类别

                    置换群

                    对于给定的集合 \(X\)\(X\) 到自身的一些置换集合 \(G\) 如果在复合运算和求逆运算下封闭,那么称\(G\) 是一个作用于 \(X\) 上的群。

                    易证,集合 \(S\)上的所有置换关于置换的乘法满足封闭性、结合律、有单位元(恒等置换,即每个元素映射成它自己)、有逆元(交换置换表示中的上下两行),因此构成一个群。这个群的任意一个子群 即称为 置换群

                    置换的定义

                    有限集合到自身的双射成为置换。集合 \(S =\{a_1,a_2,\cdots ,a_n\}\) 上的置换可以表示为 \[f = \begin{pmatrix}a_1,a_2,\cdots,a_n\\a_{p_1},a_{p_2},\cdots,a_{p_n}\end{pmatrix}\] 意为将 \(a_i\) 映射为 \(a_{p_i}\) ,其中 \(p_1,p_2,\cdots,p_n\) 是 \(1,2,\cdots,n\) 的一个排列

                    显然 \(S\) 上所有置换的数量为 \(n!\)

                    置换的乘法

                    对于两个置换 \(f=\begin{pmatrix}a_1,a_2,\cdots,a_n\\a_{p_1},a_{p_2},\cdots,a_{p_n}\end{pmatrix}\)\(g=\begin{pmatrix}a_{p_1},a_{p_2},\cdots,a_{p_n}\\a_{q_1},a_{q_2},\cdots,a_{q_n}\end{pmatrix}\)\(f\)\(g\) 的乘积记为 \(f \circ g\) ,其值为 \[f \circ g=\begin{pmatrix}a_1,a_2,\cdots,a_n\\a_{q_1},a_{q_2},\cdots,a_{q_n}\end{pmatrix}\] 简单来说就是先后经过 \(f\)的映射,再经过 \(g\) 的映射。

                    循环置换

                    循环置换是一类特殊的置换,可表示为 \[(a_1,a_2,\cdots,a_m)=\begin{pmatrix}a_1,a_2,\cdots,a_{m-1},a_m\\a_{2},a_{3},\cdots,a_{m},a_1\end{pmatrix}\]若两个循环置换不含有相同的元素,则称它们是不相交的,有如下定理:

                    任意一个置换都可以分解为若干不相交的循环置换的乘积,例如 \[\begin{pmatrix}a_1,a_2,a_3,a_4,a_5\\a_3,a_1,a_2,a_5,a_4\end{pmatrix} =(a_1,a_3,a_2) \circ (a_4,a_5)\]该定理的证明非常简单。如果把元素视为图的结点,映射关系视为有向边,则每个结点的入度和出度都为\(1\),因此形成的图形必定是若干个环的集合,而一个环即可用一个循环置换表示。

                    循环群

                    循环群是最简单的群

                    \(G\) 中任意一个元素 \(a\) 都可以表示为 \(a=g^k\) ,其 \(k\) 为整数。称 \(g\) 为群 \(G\) 的生成元。

                    有定理:生成元 \(g\) 的阶就是群\(G\) 的阶。

                    阶为 \(m\) 的有限循环群 \(G\) 同构于模 \(m\) 剩余类对于加法构成的群 \(Z_m\)。

                    Burnside 引理

                    \(A,B\) 为有限集合,\(X\) 为一些从 \(A\) 到 \(B\) 的映射组成的集合。

                    \(G\)\(A\) 上的置换群,且 \(X\) 中的映射在 \(G\) 中的置换作用下封闭。

                    \(X/G\) 表示 \(G\) 作用在 \(X\) 产生的所有等价类的集合

                    (若 \(X\) 中的两个映射经过 \(G\)中的置换作用后相等,则它们在同一个等价类中),则 \[|X/G| = \dfrac{1}{|G|} \sum_{g \in G} |X^g|\] 其中 \(|S|\) 表示集合 \(S\) 的基数,即元素个数,且 \[X^g = \{x|x\in X,g(x) =x\}\] 举例:

                    \(3\)种颜色给一个立方体染色,求本质不同的方案数

                    本质不同指:经过任意翻转后,两种染色方案均不同

                    则根据上面的模板,有

                    • \(A\) :立方体 \(6\) 个面的集合
                    • \(B\)\(3\) 种颜色的集合
                    • \(X\):直接给每个面染色,不考虑本质不同的方案的集合,共有 \(3^6\) 种
                    • \(G\):各种翻转操作构成的置换群
                    • \(X/G\):本质不同的染色方案的集合
                    • \(X^g\) :对于某一种反转操作 \(g\) ,所有直接染色方案中,经过 \(g\) 这种翻转后保持不变的染色方案的集合

                    下面懒得写了,直接看图片吧。

                    ]]> @@ -296,7 +296,7 @@ /2022/08/20/oi-shu-xue-zong-jie-shu-lun/ - OI数学总结-数论

                    施工中,咕咕咕….

                    积性函数

                    数论中定义:定义域为正整数域的函数 $f(n)$ ,

                    且满足 $\forall a,b \in \mathbb{Z} _+,f(ab)=f(a)f(b) \land \gcd(a,b)=1$


                    完全积性函数

                    数论中定义:定义域为正整数域的函数 $f(n)$ ,

                    且满足 $\forall a,b \in \mathbb{Z} _+,f(ab)=f(a)f(b)$


                    最大公约数(gcd)

                    最大公约数指能够整除多个不全为零的整数的最大正整数

                    小性质: $a\times b = \gcd(a,b) \times \mathrm{lcm}(a,b)$

                    代码求解

                    int gcd(int a,int b){    return b==0?a:gcd(b,a%b);} // 注意这份代码对于(+,-)的情况会求出负数

                    裴蜀定理

                    设 $a,b$ 是不全为零的整数,则存在整数,使得 $ax+by=\gcd(a,b)$

                    证明详见 裴蜀定理及其证明


                    扩展欧几里德算法(Exgcd)

                    板子题: P5656 【模板】二元一次不定方程 (exgcd)

                    若 $b \ne 0$ ,扩展欧几里得算法求出的可行解必有 $|x| \le b ,|y| \le a$ 。

                    int exgcd(int a,int b,int &x,int &y){    int d=a;    if(!b)x=1,y=0;    else d=exgcd(b,a%b,y,x),y-=a/b*x;    return d;}void solve(){    int a,b,c,x,y;    read(a);read(b);read(c);    int d=exgcd(a,b,x,y);    if(c%d!=0){puts("-1");return;} // 无解    x*=c/d;y*=c/d;    int p=b/d,q=a/d,k;    if(x<0)k=ceil((1.0-x)/p),x+=p*k,y-=q*k; // 将x提高到最小正整数    else k=(x-1)/p,x-=p*k,y+=q*k; // 将x降低到最小正整数    if(y>0) // 存在x>0,y>0的解(正整数)    {        write((y-1)/q+1);   pc(' '); // 将y减到1的方案数即为解的个数        write(x);           pc(' '); // 当前的x为最小正整数        write((y-1)%q+1);   pc(' '); // 将y取到最小正整数        write(x+(y-1)/q*p); pc(' '); // 将x提升到最大        write(y);           pc(' '); // 特解即为y的最大值    }else // 仅存在整数解    {        write(x);                       pc(' '); // 当前的x        write(y+q*(int)ceil((1.0-y)/q));pc(' '); // 将y提高到正整数    }    pc('\n');}

                    费马小定理

                    1. 若 $p$ 为素数 , $\gcd(a,p)=1$ 则 $a^{p-1}\equiv 1 \pmod{p}$

                    2. 若 $p$ 为素数 ,则对于任意整数 $a$ ,有 $a^p \equiv a \pmod{p}$

                    常用于 $O(\log n)$ 求解 $n$ 模素数 $p$ 意义下的逆元,详见逆元部分。


                    逆元

                    如果一个线性同余方程组 $ax \equiv 1 \pmod{b}$ ,则 $x$ 称为 $a \bmod b$ 的逆元,记作 $a^{-1}$

                    模意义下的乘法逆元不唯一。

                    如果 $b$ 不为素数,可能不存在逆元。

                    代码求解

                    P3811 【模板】乘法逆元

                    $O(n)$ 预处理 ,$O(1)$ 查询

                    原理:

                    注意空间复杂度为 $O(n)$

                    inv[1]=1;for(int i=2; i<=n; i++)    inv[i]=(p-p/i)*inv[p%i]%p;

                    费马小定理 $O(\log p)$ 查询 $x^{-1}$

                    原理:$x \equiv a^{p-2}\pmod{p}$ (根据费马小定理)

                    $p$ 需要是质数

                    cout << qpow(a,p-2) << endl; // 快速幂

                    $\text{Exgcd}$ $O(\log p)$ 查询 $x^{-1}$

                    int exgcd(int a,int b,int &x,int &y){    int d=a;    if(!b)x=1,y=0;    else d=exgcd(b,a%b,y,x),y-=a/b*x;    return d;}int solve(int a,int p){int x,y;    exgcd(a,p,x,y);    x=(x%p+p)%p;    return x;}

                    $O(n)$ 预处理阶乘的逆元

                    因为 $n!=(n-1)! \cdot n$ ,所以 $((n-1)!)^{-1}=(n!)^{-1} \times n$


                    欧拉函数

                    欧拉函数 $\varphi(n)$ 表示小于等于 $n$ 和 $n$ 互质的数的个数。

                    设 $n=\prod_{i=1}^{s}p_i^{k_i}$

                    代码求解

                    $O(\sqrt{n})$ 求 $\varphi(n)$

                    板子题:poj2407

                    int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n;    while(cin>>n&&n!=0)        cout << Euler_phi(n) << endl;    return 0;}

                    线性筛 $O(n)$ 求解

                    poj2478 (这个题要改成 $\varphi$ 的前缀和)

                    int phi[N],prime[N],pcnt;bool ck[N];void Euler(){    ck[1]=1;    phi[1]=1;    for(int i=2; i<=N-5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            phi[i]=i-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N-5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                phi[pos]=phi[i]*phi[prime[j]];            }            else            {                phi[pos]=phi[i]*prime[j];                break;            }        }    }}

                    欧拉定理

                    若 $\gcd(a,m) = 1$ ,则 $a^{\varphi(m)} \equiv 1\pmod{m}$

                    当 $m$ 为素数时,根据欧拉函数的性质有 $a^{m-1}\equiv 1 \pmod{m}$ ,即费马小定理


                    扩展欧拉定理

                    应用

                    例题:

                    $1\le a,m\le 10^{12},1\le b\le 10^{20000000}$(这个是加强版的,朴素版只要int就行)

                    #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()int a,m,b,flag;int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}int qpow(int a,int b,int p){    int ans=1,base=a%p;    while(b)    {        if(b&1)ans=(__int128)ans*base%p;        base=(__int128)base*base%p;        b>>=1;    }    return ans;}signed main(){    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    scanf("%lld%lld",&a,&m);    a%=m;    int phi=Euler_phi(m);    char ch=gc();    while(!isdigit(ch))ch=gc();    while(isdigit(ch))    {        b=(b<<1)+(b<<3)+(ch^48);        if(b>=phi)flag=1,b%=phi;ch=gc();    }    if(b>=phi)flag=1,b%=phi;    if(flag)b+=phi;    printf("%lld\n",qpow(a,b,m));    return 0;}

                    Lucas定理

                    对于质数 $p$ ,有

                    $\binom{\left\lfloor{n/p}\right\rfloor}{\left\lfloor{m/p}\right\rfloor}$ 采用递归处理,后面的直接 $O(p)$ 预处理组合数

                    板子题:P3807 【模板】卢卡斯定理/Lucas 定理

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int Q,n,m,p;int fac[N];int qpow(int a,int b,int p){int ans=1,base=a;while(b){if(b&1)ans=ans*base%p;base=base*base%p;b>>=1;}return ans;}// n 选 mint C(int n,int m,int p){if(n<m)return 0;return fac[n]*qpow(fac[m],p-2,p)%p*qpow(fac[n-m],p-2,p)%p;}int lucas(int n,int m,int p){if(!m)return 1;return lucas(n/p,m/p,p)*C(n%p,m%p,p)%p;}signed main(){cin >> Q;while(Q--){cin >> n >> m >> p;fac[1]=fac[0]=1;for(int i=2; i<=n+m; i++)fac[i]=fac[i-1]*i%p;cout << lucas(n+m,m,p) << '\n'; // C(n+m,n)}return 0;}

                    中国剩余定理

                    中国剩余定理(CRT)可求解如下形式的一元线性同余方程组

                    其中 $n_1,n_2,\cdots,n_k$ 两两互质 ,扩展中国剩余定理可以解决这个问题

                    板子题:P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪

                    懒得背这个,直接去背扩展中国剩余定理。

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(15)int n,k=1;int a[N],r[N];void exgcd(int a,int b,int &x,int &y){if(!b)x=1,y=0;else exgcd(b,a%b,y,x),y-=a/b*x;}int solve(){int ans=0;for(int i=1; i<=n; i++){int m=k/r[i],b,y;exgcd(m,r[i],b,y);ans=(ans+a[i]%k*m%k*b%k)%k;}return (ans%k+k)%k;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);cin >> n;for(int i=1; i<=n; i++){        cin >> r[i] >> a[i];k*=r[i];}cout << solve() << '\n';return 0;}

                    扩展中国剩余定理

                    只会个板子,没理解,待补充。

                    板子题:P4777 【模板】扩展中国剩余定理(EXCRT)

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,ai[N],bi[N];int exgcd(int a,int b,int &x,int &y){    int d=a;    if(!b)x=1,y=0;    else d=exgcd(b,a%b,y,x),y-=a/b*x;    return d;}int excrt(){    int x,y,k,M=bi[1],ans=ai[1];    for(int i=2; i<=n; i++)    {        int a=M,b=bi[i],c=(ai[i]-ans%b+b)%b;        int gcd=exgcd(a,b,x,y),bg=b/gcd;        if(c%gcd!=0) return -1;        x=(__int128)x*(c/gcd)%bg;        ans+=x*M; M*=bg; ans=(ans%M+M)%M;    }    return (ans%M+M)%M;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> bi[i] >> ai[i];    cout << excrt() << '\n';    return 0;}

                    莫比乌斯函数

                    $\mu$ 为莫比乌斯函数,定义为

                    代码求解

                    线性筛 $O(n)$ 求 $\mu$

                    void Mobius(){    mu[1]=1;    for(int i=2; i<=N-5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            mu[i]=-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N-5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                mu[pos]=-mu[i];            }else            {                mu[pos]=0;                break;            }        }    }}

                    莫比乌斯反演

                    应用

                    一个小转化(仅改变这两个相关和式,左侧其他的 $\Sigma$ 不变)

                    设 $d(x)$为 $x$ 的约数个数,则有


                    拉格朗日定理

                    设 $p$ 为素数,对于模 $p$ 意义下的整系数多项式

                    的同余方程 $f(x) \equiv 0 \pmod{p}$ 在模 $p$ 意义下至多有 $n$ 个不同解

                    应用

                    关于同余方程的引理

                    对于任意 $a$ ,至多有 $n$ 个不同的 $x$ 满足同余方程 $nx \equiv a \pmod{m}$

                    应用于抽象代数的定理中

                    在有限可交换群 $G$ 中,以下两个条件等价:

                    $G$ 是循环群。

                    对于任意一个元素 $a$ ,至多有 $n$ 个不同的元素 $x$ 满足条件 $x^n=a$ 。

                    结论:对于素数 $p$ ,模 $p$ 的简化剩余系 $Z_p^*$ 对于乘法构成的群是循环群


                    原根

                    :满足同余式 $a^n \equiv 1 \pmod{m}$ 的最小正整数 $n$ 存在,这个 $n$ 称作 $a$ 模 $m$ 的阶,记作 $\delta_m(a)$

                    在抽象代数中,这里的“阶”就是模 $m$ 缩剩余系关于乘法形成的群中,元素 $a$ 的阶。记号 $\delta$ 表示阶也只用于这个特殊的群。

                    下面的诸多性质可以直接扩展到抽象代数中阶的性质。

                    阶的性质

                    1. $a,a^2,\dots,a^{\delta_m(a)}$ 模 $m$ 两两不同余

                    2. 若 $a^n \equiv 1 \pmod{m}$ ,则 $\delta_m(a) \mid n$

                    3. 若 $a^p \equiv a^q \pmod{m}$ ,则有 $p\equiv q \pmod{\delta_m(a)}$

                    4. 设 $m \in \mathbb{N}^*,a,b \in \mathbb{Z} ,\gcd(a,m)=\gcd(b,m)=1$ ,则

                      的充分必要条件是

                    5. 设 $k\in \mathbb{N},m\in \mathbb{N}^*,a\in\mathbb{Z} ,\gcd(a,m)=1$ ,则

                    原根:设 $m\in \mathbb{N}^*,a \in \mathbb{Z} $ 若 $\gcd(a,m)=1$ ,且 $\delta_m(a) = \varphi(m)$ ,则称 $a$ 为模 $m$ 的原根

                    在抽象代数中,原根就是循环群的生成元。这个概念只在模 $m$ 缩剩余系关于乘法形成的群中有“原根”这个名字,在一般的循环群中都称作“生成元”。

                    并非每个模 $m$ 缩剩余系关于乘法形成的群都是循环群,存在原根就表明它同构于循环群,如果不存在原根就表明不同构。

                    原根判定定理:设 $m\ge 3,\gcd(a,m)=1$ ,则 $a$ 是模 $m$ 的原根的充要条件是,对于 $\varphi(m)$ 的每个素因数 $p$ ,都有 $a^{\frac{\varphi(m)}{p}}\not\equiv 1 \pmod{m}$

                    原根个数:若一个数 $m$ 有原根,则它的原根个数为 $\varphi(\varphi(m))$

                    原根存在定理:一个数 $m$ 存在原根当且仅当 $m=2,4,p^\alpha,2p^\alpha$ ,其中 $p$ 为奇素数,$\alpha \in \mathbb{N}^*$

                    • 定理1:对于奇素数 $p$ ,$p$ 有原根
                    • 定理2:对于奇素数 $p$ ,$\alpha \in \mathbb{N}^*$ ,$p^\alpha$ 有原根
                    • 定理3:对于奇素数 $p$ ,$\alpha\in\mathbb{N}^*$ ,$2p^\alpha$ 的原根存在
                    • 定理4:对于 $m\ne2,4$ ,且不存在奇素数 $p$ 及 $\alpha \in \mathbb{N}^*$ 使得 $m=p^\alpha,2p^\alpha$ ,模 $m$ 的原根不存在

                    二次剩余

                    对于二次同余方程 $x^2\equiv n \pmod{p}$ 有解,则称 $n$ 为 $p$ 的二次剩余,$x$ 为该二次同余方程的解

                    二次剩余 $n$ 就是一个二次项 $\bmod p$ 后的剩余,$n$ 不可以是 $p$ 的倍数

                    Legendre 符号(中间那个发音是g不是j)

                    Legendre 符号为完全积性函数

                    Euler 判别准则

                    对于奇素数

                    引理:令 $p$ 为素数和模 $p$ 意义下原根 $g$ 并令 $a\equiv g^k \pmod{p}$ ,那么 $x^2 \equiv a \pmod{p}$ 有解当且仅当 $k$ 为偶数

                    二次剩余和二次非剩余的数量

                    对于奇素数 $p$ 和集合 $\{1,2,\dots,p-1\}$ ,在模 $p$ 意义下二次剩余的数量等于二次非剩余的数量

                    引理:对于 $d \mid (p-1)$ 和奇素数 $p\in \mathbb{Z} $ ,$x^d \equiv 1 \pmod{p}$ 恰有 $d$ 个解

                    ]]> + OI数学总结-数论

                    施工中,咕咕咕....

                    积性函数

                    数论中定义:定义域为正整数域的函数 \(f(n)\) ,

                    且满足 \(\forall a,b \in \mathbb{Z}_+,f(ab)=f(a)f(b) \land \gcd(a,b)=1\)


                    完全积性函数

                    数论中定义:定义域为正整数域的函数 \(f(n)\) ,

                    且满足 \(\forall a,b \in \mathbb{Z}_+,f(ab)=f(a)f(b)\)


                    最大公约数(gcd)

                    最大公约数指能够整除多个不全为零的整数的最大正整数

                    小性质: \(a\times b = \gcd(a,b) \times\mathrm{lcm}(a,b)\)

                    代码求解

                    int gcd(int a,int b){    return b==0?a:gcd(b,a%b);} // 注意这份代码对于(+,-)的情况会求出负数

                    裴蜀定理

                    \(a,b\)不全为零的整数,则存在整数,使得 \(ax+by=\gcd(a,b)\)

                    证明详见 裴蜀定理及其证明


                    扩展欧几里德算法(Exgcd)

                    板子题: P5656【模板】二元一次不定方程 (exgcd)

                    \(b \ne 0\),扩展欧几里得算法求出的可行解必有 \(|x| \le b,|y| \le a\)

                    int exgcd(int a,int b,int &x,int &y){    int d=a;    if(!b)x=1,y=0;    else d=exgcd(b,a%b,y,x),y-=a/b*x;    return d;}void solve(){    int a,b,c,x,y;    read(a);read(b);read(c);    int d=exgcd(a,b,x,y);    if(c%d!=0){puts("-1");return;} // 无解    x*=c/d;y*=c/d;    int p=b/d,q=a/d,k;    if(x<0)k=ceil((1.0-x)/p),x+=p*k,y-=q*k; // 将x提高到最小正整数    else k=(x-1)/p,x-=p*k,y+=q*k; // 将x降低到最小正整数    if(y>0) // 存在x>0,y>0的解(正整数)    {        write((y-1)/q+1);   pc(' '); // 将y减到1的方案数即为解的个数        write(x);           pc(' '); // 当前的x为最小正整数        write((y-1)%q+1);   pc(' '); // 将y取到最小正整数        write(x+(y-1)/q*p); pc(' '); // 将x提升到最大        write(y);           pc(' '); // 特解即为y的最大值    }else // 仅存在整数解    {        write(x);                       pc(' '); // 当前的x        write(y+q*(int)ceil((1.0-y)/q));pc(' '); // 将y提高到正整数    }    pc('\n');}

                    费马小定理

                    1. \(p\) 为素数 , \(\gcd(a,p)=1\) 则 \(a^{p-1}\equiv 1 \pmod{p}\)

                    2. \(p\) 为素数 ,则对于任意整数\(a\) ,有 \(a^p \equiv a \pmod{p}\)

                    常用于 \(O(\log n)\) 求解 \(n\) 模素数 \(p\) 意义下的逆元,详见逆元部分。


                    逆元

                    如果一个线性同余方程组 \(ax \equiv 1\pmod{b}\) ,则 \(x\) 称为 \(a \bmod b\) 的逆元,记作 \(a^{-1}\)

                    模意义下的乘法逆元不唯一。

                    如果 \(b\)不为素数,可能不存在逆元。

                    代码求解

                    P3811【模板】乘法逆元

                    \(O(n)\)预处理 ,\(O(1)\) 查询

                    原理: \[\begin{aligned}i^{-1} &\equiv\begin{cases}1,&\text{if }i=1,\\\\-\left\lfloor\dfrac{p}{i}\right\rfloor \left(p\bmod{i}\right)^{-1},&\text{otherwises.}\end{cases} \pmod{p}\end{aligned}\]

                    注意空间复杂度为 \(O(n)\)

                    inv[1]=1;for(int i=2; i<=n; i++)    inv[i]=(p-p/i)*inv[p%i]%p;

                    费马小定理 \(O(\log p)\) 查询 \(x^{-1}\)

                    原理:\(x \equiv a^{p-2}\pmod{p}\)(根据费马小定理)

                    \(p\) 需要是质数

                    cout << qpow(a,p-2) << endl; // 快速幂

                    \(\text{Exgcd}\) \(O(\log p)\) 查询 \(x^{-1}\)

                    int exgcd(int a,int b,int &x,int &y){    int d=a;    if(!b)x=1,y=0;    else d=exgcd(b,a%b,y,x),y-=a/b*x;    return d;}int solve(int a,int p){int x,y;    exgcd(a,p,x,y);    x=(x%p+p)%p;    return x;}

                    \(O(n)\)预处理阶乘的逆元

                    因为 \(n!=(n-1)! \cdot n\) ,所以\(((n-1)!)^{-1}=(n!)^{-1} \timesn\)


                    欧拉函数

                    欧拉函数 \(\varphi(n)\) 表示小于等于\(n\)\(n\) 互质的数的个数。

                    \(n=\prod_{i=1}^{s}p_i^{k_i}\)\[\varphi(n) = n \prod\limits_{i=1}^s\left(\dfrac{p_i-1}{p_i}\right) =\prod(p_i-1)\times p_i^{k_i-1}\]

                    代码求解

                    \(O(\sqrt{n})\) 求 \(\varphi(n)\)

                    板子题:poj2407

                    int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n;    while(cin>>n&&n!=0)        cout << Euler_phi(n) << endl;    return 0;}

                    线性筛 \(O(n)\)求解

                    poj2478(这个题要改成 \(\varphi\)的前缀和)

                    int phi[N],prime[N],pcnt;bool ck[N];void Euler(){    ck[1]=1;    phi[1]=1;    for(int i=2; i<=N-5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            phi[i]=i-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N-5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                phi[pos]=phi[i]*phi[prime[j]];            }            else            {                phi[pos]=phi[i]*prime[j];                break;            }        }    }}

                    欧拉定理

                    \(\gcd(a,m) = 1\) ,则 \(a^{\varphi(m)} \equiv 1\pmod{m}\)

                    \(m\)为素数时,根据欧拉函数的性质有 \(a^{m-1}\equiv1 \pmod{m}\) ,即费马小定理


                    扩展欧拉定理

                    \[\begin{aligned}a^b \equiv\begin{cases}a^b,&b< \varphi(m)\\\\a^{b \ \bmod \ \varphi(m)+\varphi(m)},&b\ge \varphi(m)\end{cases} \pmod{m}\end{aligned}\]

                    应用

                    例题:

                    \[a^b\bmod m\] \(1\le a,m\le 10^{12},1\le b\le10^{20000000}\)(这个是加强版的,朴素版只要int就行)

                    #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()int a,m,b,flag;int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}int qpow(int a,int b,int p){    int ans=1,base=a%p;    while(b)    {        if(b&1)ans=(__int128)ans*base%p;        base=(__int128)base*base%p;        b>>=1;    }    return ans;}signed main(){    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    scanf("%lld%lld",&a,&m);    a%=m;    int phi=Euler_phi(m);    char ch=gc();    while(!isdigit(ch))ch=gc();    while(isdigit(ch))    {        b=(b<<1)+(b<<3)+(ch^48);        if(b>=phi)flag=1,b%=phi;ch=gc();    }    if(b>=phi)flag=1,b%=phi;    if(flag)b+=phi;    printf("%lld\n",qpow(a,b,m));    return 0;}

                    Lucas定理

                    对于质数 \(p\) ,有 \[\dbinom{n}{m} \bmod p =\dbinom{\left\lfloor{n/p}\right\rfloor}{\left\lfloor{m/p}\right\rfloor}\times \dbinom{n \bmod p}{m \bmod p} \bmod p\] \(\binom{\left\lfloor{n/p}\right\rfloor}{\left\lfloor{m/p}\right\rfloor}\)采用递归处理,后面的直接 \(O(p)\)预处理组合数

                    板子题:P3807【模板】卢卡斯定理/Lucas 定理

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int Q,n,m,p;int fac[N];int qpow(int a,int b,int p){int ans=1,base=a;while(b){if(b&1)ans=ans*base%p;base=base*base%p;b>>=1;}return ans;}// n 选 mint C(int n,int m,int p){if(n<m)return 0;return fac[n]*qpow(fac[m],p-2,p)%p*qpow(fac[n-m],p-2,p)%p;}int lucas(int n,int m,int p){if(!m)return 1;return lucas(n/p,m/p,p)*C(n%p,m%p,p)%p;}signed main(){cin >> Q;while(Q--){cin >> n >> m >> p;fac[1]=fac[0]=1;for(int i=2; i<=n+m; i++)fac[i]=fac[i-1]*i%p;cout << lucas(n+m,m,p) << '\n'; // C(n+m,n)}return 0;}

                    中国剩余定理

                    中国剩余定理(CRT)可求解如下形式的一元线性同余方程组 \[\begin{cases} x \equiv b_1\ ({\rm mod}\ a_1)\\\\ x\equiv b_2\ ({\rm mod}\ a_2)\\ \\\dots\\\\ x \equiv b_n\ ({\rm mod}\ a_n)\end{cases}\] 其中 \(n_1,n_2,\cdots,n_k\)两两互质 ,扩展中国剩余定理可以解决这个问题

                    板子题:P1495【模板】中国剩余定理(CRT)/ 曹冲养猪

                    懒得背这个,直接去背扩展中国剩余定理。

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(15)int n,k=1;int a[N],r[N];void exgcd(int a,int b,int &x,int &y){if(!b)x=1,y=0;else exgcd(b,a%b,y,x),y-=a/b*x;}int solve(){int ans=0;for(int i=1; i<=n; i++){int m=k/r[i],b,y;exgcd(m,r[i],b,y);ans=(ans+a[i]%k*m%k*b%k)%k;}return (ans%k+k)%k;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);cin >> n;for(int i=1; i<=n; i++){        cin >> r[i] >> a[i];k*=r[i];}cout << solve() << '\n';return 0;}

                    扩展中国剩余定理

                    只会个板子,没理解,待补充。

                    板子题:P4777【模板】扩展中国剩余定理(EXCRT)

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,ai[N],bi[N];int exgcd(int a,int b,int &x,int &y){    int d=a;    if(!b)x=1,y=0;    else d=exgcd(b,a%b,y,x),y-=a/b*x;    return d;}int excrt(){    int x,y,k,M=bi[1],ans=ai[1];    for(int i=2; i<=n; i++)    {        int a=M,b=bi[i],c=(ai[i]-ans%b+b)%b;        int gcd=exgcd(a,b,x,y),bg=b/gcd;        if(c%gcd!=0) return -1;        x=(__int128)x*(c/gcd)%bg;        ans+=x*M; M*=bg; ans=(ans%M+M)%M;    }    return (ans%M+M)%M;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> bi[i] >> ai[i];    cout << excrt() << '\n';    return 0;}

                    莫比乌斯函数

                    \(\mu\) 为莫比乌斯函数,定义为 \[\begin{aligned}\mu(n) = \begin{cases}1,&n=1,\\\\0,&n\text{含有平方因子},\\\\(-1)^k,&k\text{ 为 }n \text{ 本质不同质因子个数}\end{cases}\end{aligned}\]

                    代码求解

                    线性筛 \(O(n)\)\(\mu\)

                    void Mobius(){    mu[1]=1;    for(int i=2; i<=N-5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            mu[i]=-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N-5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                mu[pos]=-mu[i];            }else            {                mu[pos]=0;                break;            }        }    }}

                    ## 莫比乌斯反演

                    \[\sum_{d\mid \gcd(i,j)}\mu(d)=[\gcd(i,j)=1]=[i\perp j]\]

                    应用

                    一个小转化(仅改变这两个相关和式,左侧其他的 \(\Sigma\) 不变) \[\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=k]=\sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[\gcd(i,j)=1]\] 设 \(d(x)\)\(x\) 的约数个数,则有 \[d(ij)=\sum_{x\mid i}\sum_{y \mid j}[\gcd(x,y)=1]\]


                    拉格朗日定理

                    \(p\) 为素数,对于模 \(p\) 意义下的整系数多项式 \[f(x) = \sum_{n\ge 0}a_nx^n,p\nmid a_n\] 的同余方程 \(f(x) \equiv 0\pmod{p}\) 在模 \(p\)意义下至多有 \(n\) 个不同解

                    应用

                    关于同余方程的引理

                    对于任意 \(a\) ,至多有 \(n\) 个不同的 \(x\) 满足同余方程 \(nx \equiv a \pmod{m}\)

                    应用于抽象代数的定理中

                    在有限可交换群 \(G\)中,以下两个条件等价:

                    \(G\) 是循环群。

                    对于任意一个元素 \(a\) ,至多有\(n\) 个不同的元素 \(x\) 满足条件 \(x^n=a\) 。

                    结论:对于素数 \(p\) ,模 \(p\) 的简化剩余系 \(Z_p^*\) 对于乘法构成的群是循环群


                    原根

                    :满足同余式 \(a^n\equiv 1 \pmod{m}\) 的最小正整数 \(n\) 存在,这个 \(n\) 称作 \(a\) 模 \(m\) 的阶,记作 \(\delta_m(a)\)

                    在抽象代数中,这里的“阶”就是模 \(m\)缩剩余系关于乘法形成的群中,元素 \(a\)的阶。记号 \(\delta\)表示阶也只用于这个特殊的群。

                    下面的诸多性质可以直接扩展到抽象代数中阶的性质。

                    阶的性质

                    1. \(a,a^2,\dots,a^{\delta_m(a)}\)\(m\) 两两不同余

                    2. \(a^n \equiv 1 \pmod{m}\),则 \(\delta_m(a) \mid n\)

                    3. \(a^p \equiv a^q \pmod{m}\),则有 \(p\equiv q\pmod{\delta_m(a)}\)

                    4. \(m \in \mathbb{N}^*,a,b \in\mathbb{Z} ,\gcd(a,m)=\gcd(b,m)=1\) ,则 \[\delta_m(ab)=\delta_m(a)\delta_m(b)\] 的充分必要条件是 \[\gcd(\delta_m(a),\delta_m(b))=1\]

                    5. \(k\in \mathbb{N},m\in\mathbb{N}^*,a\in\mathbb{Z} ,\gcd(a,m)=1\) ,则 \[\delta_m(a^k)=\dfrac{\delta_m(a)}{\gcd(\delta_m(a),k)}\]

                    原根:设 $m^*,a $ 若 \(\gcd(a,m)=1\) ,且 \(\delta_m(a) = \varphi(m)\) ,则称 \(a\) 为模 \(m\) 的原根

                    在抽象代数中,原根就是循环群的生成元。这个概念只在模 \(m\)缩剩余系关于乘法形成的群中有“原根”这个名字,在一般的循环群中都称作“生成元”。

                    并非每个模 \(m\)缩剩余系关于乘法形成的群都是循环群,存在原根就表明它同构于循环群,如果不存在原根就表明不同构。

                    原根判定定理:设 \(m\ge3,\gcd(a,m)=1\) ,则 \(a\) 是模\(m\) 的原根的充要条件是,对于 \(\varphi(m)\) 的每个素因数 \(p\) ,都有 \(a^{\frac{\varphi(m)}{p}}\not\equiv 1\pmod{m}\)

                    原根个数:若一个数 \(m\) 有原根,则它的原根个数为 \(\varphi(\varphi(m))\)

                    原根存在定理:一个数 \(m\) 存在原根当且仅当 \(m=2,4,p^\alpha,2p^\alpha\) ,其中 \(p\) 为奇素数,\(\alpha \in \mathbb{N}^*\)

                    • 定理1:对于奇素数 \(p\)\(p\) 有原根
                    • 定理2:对于奇素数 \(p\)\(\alpha \in \mathbb{N}^*\) ,\(p^\alpha\) 有原根
                    • 定理3:对于奇素数 \(p\)\(\alpha\in\mathbb{N}^*\) ,\(2p^\alpha\) 的原根存在
                    • 定理4:对于 \(m\ne2,4\),且不存在奇素数 \(p\)\(\alpha \in \mathbb{N}^*\) 使得 \(m=p^\alpha,2p^\alpha\) ,模 \(m\) 的原根不存在

                    二次剩余

                    对于二次同余方程 \(x^2\equiv n\pmod{p}\) 有解,则称 \(n\)\(p\) 的二次剩余,\(x\) 为该二次同余方程的解

                    二次剩余 \(n\) 就是一个二次项 \(\bmod p\) 后的剩余,\(n\) 不可以是 \(p\) 的倍数

                    Legendre 符号(中间那个发音是g不是j) \[\left(\dfrac{a}{p}\right)=\begin{cases}1,&p\nmid a \text{ 且 }a \text{ 是模 }p \text{ 的二次剩余} ,\\-1,&p\nmid a \text{ 且 }a \text{ 不是模 }p \text{ 的二次剩余} ,\\0,&p\mid a.\end{cases}\] Legendre 符号为完全积性函数

                    Euler 判别准则

                    对于奇素数 \[a^{\frac{p-1}{2}} \equiv \left(\dfrac{a}{p}\right)\equiv\begin{cases}1 \pmod{p}, \text{ 若 }x^2 \equiv a \pmod{p} \text{ 有解},\\\\-1 \pmod{p}, \text{ 若 }x^2 \equiv a \pmod{p} \text{ 无解}.\end{cases}\]

                    引理:令 \(p\) 为素数和模 \(p\) 意义下原根 \(g\) 并令 \(a\equiv g^k \pmod{p}\) ,那么 \(x^2 \equiv a \pmod{p}\) 有解当且仅当 \(k\) 为偶数

                    二次剩余和二次非剩余的数量

                    对于奇素数 \(p\) 和集合 \(\{1,2,\dots,p-1\}\) ,在模 \(p\)意义下二次剩余的数量等于二次非剩余的数量

                    引理:对于 \(d \mid (p-1)\) 和奇素数$p $ ,\(x^d \equiv 1 \pmod{p}\) 恰有\(d\) 个解

                    ]]> @@ -323,7 +323,7 @@ /2022/08/20/cf708e-student-s-camp-ti-jie/ - CF708E Student’s Camp 题解

                    题目链接:CF708E Student’s Camp

                    题意

                    有一个 $(n+2) \times m$ 的网格。

                    给定 $n,m,a,b,k$ ,记 $p=\frac{a}{b}$

                    除了第 $0$ 行和第 $n+1$ 行,其他每一行

                    • 白天最左边的格子有 $p$ 的概率被吹走。
                    • 夜晚最右边的格子有 $p$ 的概率被吹走。

                    如果网格(包括第 $0$ 行和第 $n+1$ 行)被分成了两个及以上连通分量,则称网格不连通。

                    求 $k$ 天后,网格始终保持连通的概率

                    $n,m \le 1.5 \times 10^3,~k \le 10^5,~a < b \le 10^9$,答案对 $10^9+7$ 取模。

                    特别🐮🍺的一道题,考虑概率dp。

                    下文中出现的奇怪变量名(因为参考自 link),先给出对应的代码形式

                    $\mathtt{pl}_i\to $ pl[i] ,$\mathtt{pr}_i \to$ pr[i]

                    $F_{i,j} \to$ F[i][j] , $\mathtt{sl}_{i,j} \to$ sl[i][j] , $\mathtt{sr}_{i,j} \to$ sr[i][j]

                    注意到每一行是相互独立的,故

                    设 $\mathtt{pl}_i$ 表示某一行在 $k$ 个白天后,最左边的砖块为第 $i$ 块的概率

                    设 $\mathtt{pr}_i$ 表示某一行在 $k$ 个夜晚后,最右边的砖块为第 $i$ 块的概率

                    根据小学数学,如果把这里这个 $i$ 看作一个离散随机变量,则它一定服从二项分布

                    记 $q=1-p$ ,即

                    根据二项分布的对称性可得 $\mathtt{pr}_{m-i} = \mathtt{pl}_{i+1}$

                    根据砖块吹走的性质,最后每一行一定是连续的一段,

                    故设 $f_{i,l,r}$ 表示 $k$ 天后,从下往上数第 $i$ 行仅剩下砖块 $l,l+1,\cdots,r$ 的概率

                    如果建筑没有倒塌,那么上下两行一定是有交集的。

                    这个交集并不是很好枚举,但是没交集的部分还是比较好枚举的

                    设 $F_{i,r}$ 表示 $k$ 天后,从下往上数第 $i$ 行最右边的砖块为第 $r$ 块的概率,则

                    设 $\mathtt{sr}_{i,j}$ 表示 $k$ 天后,从下往上数第 $i$ 行最右边的砖块为第 $j$ 块或更靠左的概率,则

                    设 $\mathtt{sl}_{i,j}$ 表示 $k$ 天后,从下往上数第 $i$ 行最左边的砖块为第 $j$ 块或更靠右的概率,则

                    这个可以由对称性 $\mathtt{sr}_{i,m-j+1}$ 的值推得。

                    故转移方程为

                    于是

                    设 $\mathtt{s1}_i = \sum_{k=1}^{i} \mathtt{pl}_k,~\mathtt{s2}_i = \sum_{k=1}^{i} \mathtt{pl}_k \times \mathtt{sr}_{i-1,k-1}$ ,则

                    然后我们就能 $O(nm)$ 求解啦!

                    最终的答案就是 $\mathtt{sr}_{n,m}$

                    呼,总算写完了,累死了(

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)#define K (int)(1e5+15)const int mod=1e9+7;int n,m,i,j,k,p,q;int P[K],Q[K],C[N],inv[N]; // p^i, q^i, C(k,i), i^-1int pl[N],pr[N],s1[N];int F[N][N],sl[N][N],sr[N][N],s2[N];int qpow(int a,int b){    int ans=1,base=a%mod;    for(; b; b>>=1)    {        if(b&1) ans=ans*base%mod;        base=base*base%mod;    }    return ans;}int mul(int cnt, ...){    va_list ptr; va_start(ptr,cnt);    int res=1;    for(int i=0; i<cnt; i++)        res=res*va_arg(ptr,int)%mod;    va_end(ptr);    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> i >> j >> k;    q=i*qpow(j,mod-2)%mod;    p=((1-q+mod)%mod+mod)%mod;    inv[1]=C[0]=P[0]=Q[0]=1; C[1]=k;    for(i=1; i<=k; i++) {P[i]=P[i-1]*p%mod; Q[i]=Q[i-1]*q%mod;}    for(i=2; i<=m; i++)    {        inv[i]=(mod-mod/i) * inv[mod%i] % mod;        C[i]=mul(3, C[i-1], (k-i+1), inv[i]);    }    for(i=0; i<=k && i<m; i++) pr[m-i]=pl[i+1]=mul(3, C[i], Q[i], P[k-i]);    for(i=1; i<=m; i++) s1[i]=(s1[i-1]+pl[i]) % mod;    sr[0][m]=1;    for(i=1; i<=n; i++)    {        for(j=1; j<=m; j++)            s2[j] = (s2[j-1] + pl[j] * sr[i-1][j-1]) % mod;        for(j=1; j<=m; j++)        {            F[i][j] = pr[j] * ((sr[i-1][m] - sl[i-1][j+1]) * s1[j] %mod - s2[j]) % mod;            F[i][j] < 0 ? F[i][j] += mod : 0;        }        for(j=1; j<=m; j++)        {            sr[i][j] = (sr[i][j-1]+F[i][j]) % mod;            sl[i][m-j+1]=sr[i][j];        }    }    cout << sr[n][m] << '\n';    return 0;}
                    ]]> + CF708E Student's Camp 题解

                    题目链接:CF708EStudent's Camp

                    题意

                    有一个 \((n+2) \times m\)的网格。

                    给定 \(n,m,a,b,k\) ,记 \(p=\frac{a}{b}\)

                    除了第 \(0\) 行和第 \(n+1\) 行,其他每一行

                    • 白天最左边的格子有 \(p\)的概率被吹走。
                    • 夜晚最右边的格子有 \(p\)的概率被吹走。

                    如果网格(包括第 \(0\) 行和第 \(n+1\)行)被分成了两个及以上连通分量,则称网格不连通。

                    \(k\)天后,网格始终保持连通的概率

                    \(n,m \le 1.5 \times 10^3,~k \le 10^5,~a< b \le 10^9\),答案对 \(10^9+7\) 取模。

                    特别🐮🍺的一道题,考虑概率dp。

                    下文中出现的奇怪变量名(因为参考自 link),先给出对应的代码形式

                    $_i$ pl[i]\(\mathtt{pr}_i\to\) pr[i]

                    \(F_{i,j} \to\) F[i][j]\(\mathtt{sl}_{i,j} \to\)sl[i][j]\(\mathtt{sr}_{i,j}\to\) sr[i][j]

                    注意到每一行是相互独立的,故

                    \(\mathtt{pl}_i\) 表示某一行在\(k\) 个白天后,最左边的砖块为第 \(i\) 块的概率

                    \(\mathtt{pr}_i\) 表示某一行在\(k\) 个夜晚后,最右边的砖块为第 \(i\) 块的概率

                    根据小学数学,如果把这里这个 \(i\)看作一个离散随机变量,则它一定服从二项分布

                    \(q=1-p\) ,即 \[\mathtt{pl}_{i+1} = \dbinom{k}{i} q^{i} p^{k-i}\] 根据二项分布的对称性可得 \(\mathtt{pr}_{m-i} = \mathtt{pl}_{i+1}\)

                    根据砖块吹走的性质,最后每一行一定是连续的一段,

                    故设 \(f_{i,l,r}\) 表示 \(k\) 天后,从下往上数第 \(i\) 行仅剩下砖块 \(l,l+1,\cdots,r\) 的概率

                    如果建筑没有倒塌,那么上下两行一定是有交集的。

                    这个交集并不是很好枚举,但是没交集的部分还是比较好枚举的

                    \(F_{i,r}\) 表示 \(k\) 天后,从下往上数第 \(i\) 行最右边的砖块为第 \(r\) 块的概率,则 \[F_{i,r} = \sum_{l=1}^{r} f_{i,l,r}\] 设 \(\mathtt{sr}_{i,j}\) 表示\(k\) 天后,从下往上数第 \(i\) 行最右边的砖块为第 \(j\) 块或更靠左的概率,则\[\mathtt{sr}_{i,j} = \sum_{r=1}^{j}F_{i,r} = \sum_{r=1}^{j}\sum_{l=1}^{r}f_{i,l,r} = \sum_{1 \le l\le r \le j} f_{i,l,r}\]\(\mathtt{sl}_{i,j}\) 表示\(k\) 天后,从下往上数第 \(i\) 行最左边的砖块为第 \(j\) 块或更靠右的概率,则\[\mathtt{sl}_{i,j} = \sum_{j \le l \le r \le m} f_{i,l,r}\] 这个可以由对称性 \(\mathtt{sr}_{i,m-j+1}\) 的值推得。

                    故转移方程为 \[\begin{aligned}f_{i,l,r} &= \mathtt{pl}_l \times \mathtt{pr}_{r} \times \sum_{[j,k]\cap [l,r] \ne \varnothing} f_{i-1,j,k}\\\\&=\mathtt{pl}_l\times \mathtt{pr}_r \times \left(\sum_{1 \le j\le k \le m}f_{i-1,j,k} - \sum_{1 \le j \le k < l}f_{i-1,j,k}-\sum_{r<j\le k \le m} f_{i-1,j,k} \right)\\\\&=\mathtt{pl}_l\times \mathtt{pr}_r \times (\mathtt{sr}_{i-1,m}- \mathtt{sr}_{i-1,l-1} -\mathtt{sr}_{i-1,r+1})\end{aligned}\] 于是 \[\begin{aligned}F_{i,r} &= \sum_{l=1}^{r} f_{i,l,r}\\&=\sum_{l=1}^{r}{\left(\mathtt{pl}_l\times \mathtt{pr}_r \times(\mathtt{sr}_{i-1,m} - \mathtt{sr}_{i-1,l-1}-\mathtt{sr}_{i-1,r+1})\right)}\\&= \mathtt{pr}_r \times \left(\left(\sum_{l=1}^{r}\mathtt{pl}_l\right) \times (\mathtt{sr}_{i-1,m}-\mathtt{sl}_{i-1,r+1})- \sum_{l=1}^{r} (\mathtt{pl}_l \times \mathtt{sr}_{i-1,l-1})\right)\end{aligned}\]\(\mathtt{s1}_i = \sum_{k=1}^{i}\mathtt{pl}_k,~\mathtt{s2}_i = \sum_{k=1}^{i} \mathtt{pl}_k \times\mathtt{sr}_{i-1,k-1}\) ,则 \[F_{i,r} = \mathtt{pr}_r \times (\mathtt{s1}_r \times(\mathtt{sr}_{i-1,m} - \mathtt{sl}_{i-1,r+1})-\mathtt{s2}_r)\] 然后我们就能 \(O(nm)\)求解啦!

                    最终的答案就是 \(\mathtt{sr}_{n,m}\)

                    呼,总算写完了,累死了(

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)#define K (int)(1e5+15)const int mod=1e9+7;int n,m,i,j,k,p,q;int P[K],Q[K],C[N],inv[N]; // p^i, q^i, C(k,i), i^-1int pl[N],pr[N],s1[N];int F[N][N],sl[N][N],sr[N][N],s2[N];int qpow(int a,int b){    int ans=1,base=a%mod;    for(; b; b>>=1)    {        if(b&1) ans=ans*base%mod;        base=base*base%mod;    }    return ans;}int mul(int cnt, ...){    va_list ptr; va_start(ptr,cnt);    int res=1;    for(int i=0; i<cnt; i++)        res=res*va_arg(ptr,int)%mod;    va_end(ptr);    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> i >> j >> k;    q=i*qpow(j,mod-2)%mod;    p=((1-q+mod)%mod+mod)%mod;    inv[1]=C[0]=P[0]=Q[0]=1; C[1]=k;    for(i=1; i<=k; i++) {P[i]=P[i-1]*p%mod; Q[i]=Q[i-1]*q%mod;}    for(i=2; i<=m; i++)    {        inv[i]=(mod-mod/i) * inv[mod%i] % mod;        C[i]=mul(3, C[i-1], (k-i+1), inv[i]);    }    for(i=0; i<=k && i<m; i++) pr[m-i]=pl[i+1]=mul(3, C[i], Q[i], P[k-i]);    for(i=1; i<=m; i++) s1[i]=(s1[i-1]+pl[i]) % mod;    sr[0][m]=1;    for(i=1; i<=n; i++)    {        for(j=1; j<=m; j++)            s2[j] = (s2[j-1] + pl[j] * sr[i-1][j-1]) % mod;        for(j=1; j<=m; j++)        {            F[i][j] = pr[j] * ((sr[i-1][m] - sl[i-1][j+1]) * s1[j] %mod - s2[j]) % mod;            F[i][j] < 0 ? F[i][j] += mod : 0;        }        for(j=1; j<=m; j++)        {            sr[i][j] = (sr[i][j-1]+F[i][j]) % mod;            sl[i][m-j+1]=sr[i][j];        }    }    cout << sr[n][m] << '\n';    return 0;}
                    ]]> @@ -352,7 +352,7 @@ /2022/08/19/cf178f3-representative-sampling-ti-jie/ - CF178F3 Representative Sampling 题解

                    题目链接:CF178F3 Representative Sampling

                    题意

                    有 $n$ 个字符串 $a_i$ ,从中选取 $k$ 个,使它们两两之间的 $\text{LCP}$(最长公共前缀)之和最大。输出这个最大值。

                    $k\le n\le 2000,|a_i|\le 500$

                    看到最长公共前缀我还愣了一下,这不裸的 $\tt{trie}$ 树吗,然后想了半天不会。。

                    考虑建 $\tt{trie}$ 树然后在上面做树上背包

                    注意这里背包能选的“物品”只有 $\tt{trie}$ 树上「是字符串结尾的结点」

                    方便起见,称「是字符串结尾的结点」为「串结点」

                    两两串结点的贡献,也就是他们的最长公共前缀(LCP)长度,其实就是他们的LCA深度。

                    记 $i$ 的子结点 $c_1,c_2,\cdots,c_{\lambda_i}$ ,设 $f_{c_k,j}$ 表示以 $i$ 为根的子树中,只考虑 $c_1,c_2,\cdots,c_k$ ,选 $j$ 个串结点,「两两串结点的LCA」的深度之和($i$ 深度为 $0$ ),

                    设 $g_{i,j}$ 表示以 $i$ 为根的子树中,选 $j$ 个串结点,「两两串结点的LCA」的深度之和 ($i$ 深度为 $0$ ),则

                    注意到两个串只会在他们的LCA处更新dp值,因此有很多结点都是没用的

                    我们直接把这棵树缩成一个带权树,保留可以成为LCA的结点

                    于是我们就可以 $O(n^2)$ 通过这题了

                    u1s1,代码是学的yhx巨佬的1,懒得改了(

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)#define S (int)(555)#define L (int)(1e6+15)string s;int n,m,tot=1,cnt,ed[L],val[L],id[L];int o[L],sz[L],trie[L][26],f[N*3][N],g[N*3][N];void up(int &x,int y){ x < y ? x = y : 0;}int C2(int x){return x*(x-1)>>1;}void insert(string s){    int u=1;    for(int i=0; i<s.size(); i++)    {        int c=s[i]-'a';        if(!trie[u][c]) trie[u][c]=++tot;        u=trie[u][c];    }    ++ed[u];}void dfs(int u){    id[++cnt]=u; o[u]=cnt;    sz[u]=ed[u]; int v,la=0;    for(int i=0; i<26; i++)        if((v=trie[u][i])!=0)        {            dfs(v);            sz[u]+=sz[v];        }    int lim=min(sz[u],m);    for(int i=0; i<26; i++)        if((v=trie[u][i])!=0)        {            for(int j=lim; j>=1; j--)                for(int k=0; k<=j && k<=sz[v]; k++)                    up(f[o[v]][j],f[o[la]][j-k]+g[o[v]][k]+C2(k)*(val[v]+1));            la=v;        }    memcpy(g[o[u]],f[o[la]],N<<2);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> s;        insert(s);    }    for(int i=tot,j,k; i>1; i--)        if(!ed[i])        {            for(j=k=0; j<26; j++)                if(trie[i][j]) {if(k)break; k=trie[i][j];}            if(j==26&&(!ed[k]))            {                val[i]=val[k]+1;                memcpy(trie[i],trie[k],26<<2);            }        }    dfs(1);    cout << g[1][m] << '\n';    return 0;}

                    参考文献

                    1. https://yhx-12243.github.io/OI-transit/records/cf178F3.html
                    ]]> + CF178F3 RepresentativeSampling 题解

                    题目链接:CF178F3Representative Sampling

                    题意

                    \(n\) 个字符串 \(a_i\) ,从中选取 \(k\) 个,使它们两两之间的 \(\text{LCP}\)(最长公共前缀)之和最大。输出这个最大值。

                    \(k\le n\le 2000,|a_i|\le 500\)

                    看到最长公共前缀我还愣了一下,这不裸的 \(\tt{trie}\)树吗,然后想了半天不会。。

                    考虑建 \(\tt{trie}\)树然后在上面做树上背包

                    注意这里背包能选的“物品”只有 \(\tt{trie}\) 树上「是字符串结尾的结点」

                    方便起见,称「是字符串结尾的结点」为「串结点」

                    两两串结点的贡献,也就是他们的最长公共前缀(LCP)长度,其实就是他们的LCA深度。

                    \(i\) 的子结点 \(c_1,c_2,\cdots,c_{\lambda_i}\) ,设 \(f_{c_k,j}\) 表示以 \(i\) 为根的子树中,只考虑 \(c_1,c_2,\cdots,c_k\) ,选 \(j\)个串结点,「两两串结点的LCA」的深度之和(\(i\) 深度为 \(0\) ),

                    \(g_{i,j}\) 表示以 \(i\) 为根的子树中,选 \(j\) 个串结点,「两两串结点的LCA」的深度之和(\(i\) 深度为 \(0\) ),则 \[f_{c_k,j} = \max\limits_{0 \le l \le \min \{j,\text{size}(c_k)\}}\left\{f_{c_{k-1},j-l}+g_{c_k,l}+\dbinom{l}{2}\right\}\]注意到两个串只会在他们的LCA处更新dp值,因此有很多结点都是没用的

                    我们直接把这棵树缩成一个带权树,保留可以成为LCA的结点

                    于是我们就可以 \(O(n^2)\)通过这题了

                    u1s1,代码是学的yhx巨佬的1,懒得改了(

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)#define S (int)(555)#define L (int)(1e6+15)string s;int n,m,tot=1,cnt,ed[L],val[L],id[L];int o[L],sz[L],trie[L][26],f[N*3][N],g[N*3][N];void up(int &x,int y){ x < y ? x = y : 0;}int C2(int x){return x*(x-1)>>1;}void insert(string s){    int u=1;    for(int i=0; i<s.size(); i++)    {        int c=s[i]-'a';        if(!trie[u][c]) trie[u][c]=++tot;        u=trie[u][c];    }    ++ed[u];}void dfs(int u){    id[++cnt]=u; o[u]=cnt;    sz[u]=ed[u]; int v,la=0;    for(int i=0; i<26; i++)        if((v=trie[u][i])!=0)        {            dfs(v);            sz[u]+=sz[v];        }    int lim=min(sz[u],m);    for(int i=0; i<26; i++)        if((v=trie[u][i])!=0)        {            for(int j=lim; j>=1; j--)                for(int k=0; k<=j && k<=sz[v]; k++)                    up(f[o[v]][j],f[o[la]][j-k]+g[o[v]][k]+C2(k)*(val[v]+1));            la=v;        }    memcpy(g[o[u]],f[o[la]],N<<2);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> s;        insert(s);    }    for(int i=tot,j,k; i>1; i--)        if(!ed[i])        {            for(j=k=0; j<26; j++)                if(trie[i][j]) {if(k)break; k=trie[i][j];}            if(j==26&&(!ed[k]))            {                val[i]=val[k]+1;                memcpy(trie[i],trie[k],26<<2);            }        }    dfs(1);    cout << g[1][m] << '\n';    return 0;}

                    参考文献


                    1. https://yhx-12243.github.io/OI-transit/records/cf178F3.html↩︎

                    ]]> @@ -383,7 +383,7 @@ /2022/08/17/bzoj2141-pai-dui-ti-jie/ - BZOJ2141 排队 题解

                    题目链接:#2141. 排队

                    题意

                    一句话题意:

                    给定 $n$ 个数,$Q$ 次询问,每次交换两个不同位置的数,然后输出逆序对数。

                    $1 \le n \le 2\times 10^4,~1\le Q \le 2 \times 10^3$

                    排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家乐和和。红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。

                    不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第i个小朋友的身高为 $h_i (1\le h_i \le 10^9)$ ,我们定义一个序列的杂乱程度为:满足 $h_i>h_j$ 的 $(i, j)$ 数量。幼儿园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的杂乱程度。为方便幼儿园阿姨统计,在未进行任何交换操作时,你也应该输出该序列的杂乱程度。

                    这题还是比较水的。

                    首先朴素的逆序对不会有人不会吧(不会吧不会吧 ,不会的戳这里

                    考虑一次修改会对答案产生什么影响

                    设交换 $a_l$ 和 $a_r$ ,则显然只有 $(l,r)$ 的数会影响答案

                    于是我们依次考虑每个 $a_i (l < i < r)$

                    经过观察可以发现, $a_i$ 对答案的影响和它与 $a_l,a_r$ 的大小有关

                    则 $a_i$ 对答案的贡献为

                    其中 $\mathrm{sgn}(x)=\begin{cases}1,&x>0\\0,&x=0\\ -1,&x<0\end{cases}$

                    时间复杂度 $O(n \log n + nm)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e4+15)int n,Q;int a[N],b[N],tmp[N];int sgn(int x){return (x>0)-(x<0);}int solve(int l,int r){    if(l==r) return 0;    int mid=(l+r) >> 1,res=0;    res+=solve(l,mid)+solve(mid+1,r);    int i=l,j=mid+1,k=l;    for(; i<=mid; i++)    {        while(b[i]>b[j] && j<=r)            res+=mid-i+1,tmp[k++]=b[j++];        tmp[k++]=b[i];    }    while(j<=r) tmp[k++]=b[j++];    for(int t=l; t<=r; t++) b[t]=tmp[t];    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i],b[i]=a[i];    int ans=solve(1,n),l,r;    cout << ans << '\n';    for(cin >> Q; Q--; )    {        cin >> l >> r; if(l>r) swap(l,r);        if(a[l]==a[r]) cout << ans << '\n';        else        {            for(int i=l; i<r; i++)                ans+=sgn(a[i]-a[l])+sgn(a[r]-a[i]);            swap(a[l],a[r]);            cout << ans << '\n';        }    }    return 0;}
                    ]]> + BZOJ2141 排队 题解

                    题目链接:#2141.排队

                    题意

                    一句话题意:

                    给定 \(n\) 个数,\(Q\)次询问,每次交换两个不同位置的数,然后输出逆序对数。

                    \(1 \le n \le 2\times 10^4,~1\le Q \le 2\times 10^3\)

                    排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家乐和和。红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。

                    不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第i个小朋友的身高为\(h_i (1\le h_i \le 10^9)\),我们定义一个序列的杂乱程度为:满足 \(h_i>h_j\) 的 \((i, j)\)数量。幼儿园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的杂乱程度。为方便幼儿园阿姨统计,在未进行任何交换操作时,你也应该输出该序列的杂乱程度。

                    这题还是比较水的。

                    首先朴素的逆序对不会有人不会吧(不会吧不会吧 ,不会的戳这里

                    考虑一次修改会对答案产生什么影响

                    设交换 \(a_l\)\(a_r\) ,则显然只有 \((l,r)\) 的数会影响答案

                    于是我们依次考虑每个 \(a_i (l < i <r)\)

                    经过观察可以发现, \(a_i\)对答案的影响和它与 \(a_l,a_r\)的大小有关

                    \(a_i\) 对答案的贡献为 \[\mathrm{sgn}(a_i-a_l) + \mathrm{sgn}(a_r-a_i)\] 其中 \(\mathrm{sgn}(x)=\begin{cases}1,&x>0\\0,&x=0\\-1,&x<0\end{cases}\)

                    时间复杂度 \(O(n \log n + nm)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e4+15)int n,Q;int a[N],b[N],tmp[N];int sgn(int x){return (x>0)-(x<0);}int solve(int l,int r){    if(l==r) return 0;    int mid=(l+r) >> 1,res=0;    res+=solve(l,mid)+solve(mid+1,r);    int i=l,j=mid+1,k=l;    for(; i<=mid; i++)    {        while(b[i]>b[j] && j<=r)            res+=mid-i+1,tmp[k++]=b[j++];        tmp[k++]=b[i];    }    while(j<=r) tmp[k++]=b[j++];    for(int t=l; t<=r; t++) b[t]=tmp[t];    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i],b[i]=a[i];    int ans=solve(1,n),l,r;    cout << ans << '\n';    for(cin >> Q; Q--; )    {        cin >> l >> r; if(l>r) swap(l,r);        if(a[l]==a[r]) cout << ans << '\n';        else        {            for(int i=l; i<r; i++)                ans+=sgn(a[i]-a[l])+sgn(a[r]-a[i]);            swap(a[l],a[r]);            cout << ans << '\n';        }    }    return 0;}
                    ]]> @@ -408,7 +408,7 @@ /2022/08/16/xiao-lan-shu-16.1/ - 16.1

                    施工中,咕咕咕….

                    计数原理

                    加法原理

                    设完成某件事有 $m$ 类不同的方法,

                    第 $i(1\le i \le m)$ 类方法中有 $n_i$ 种不同的方法,则完成这件事共有

                    种方法。

                    在严格化的数学中,加法原理是有关集合大小的事实。1

                    断言任意有限多个两两互斥的集合大小之和,等于其并集的大小

                    以符号表示为,若集合 $S_1,S_2,\cdots,S_n$ 两两互斥,则有

                    容斥原理可以视为加法原理的推广。

                    乘法原理

                    设完成某件事需要 $m$ 个步骤,

                    做第 $i$ 步有 $n_i$ 种不同的方法,则完成这件事共有

                    在集合论中,乘法原理可以设为基数乘积的定义。2

                    对于集合 $S_1,S_2,\cdots,S_n$ ,以 $|S_i|$ 表示 $S_i$ 的元素个数(基数),则有

                    其中 $S_i \times S_j$ 表示笛卡尔积。 $S_i$ 可以是无穷集。参见基数和选择公理。


                    排列组合

                    下面两个指的是无重复的排列与组合

                    排列数

                    从 $n$ 个不同元素中取 $k(k\le n)$ 个不同元素,并按一定顺序排成一列的方案数

                    也可写作 $\mathrm{P}_{n}^{k}$ 或直接用组合数写法 $k! \times \binom{n}{k}$

                    组合数

                    从 $n$ 个不同元素中取 $k(k \le n)$ 个不同元素的方案数

                    高中一般记作 $\mathrm{C}_n^{k}$ ,不是很推荐使用。

                    特别地,规定当 $k > n$ 时, $\mathrm{A}_n^{k} = \mathrm{C}_n^{k}=0$

                    插板法

                    $n$ 个相同的球分 $m$ 个组,不能有组为空

                    方案数

                    $n$ 个相同球分 $m$ 个组,可以有组为空

                    方案数

                    可重复的排列

                    从 $n$ 个不同元素中取 $k(k\le n)$ 个元素(同一元素允许重复取出),并按一定顺序排成一列的方案数为

                    可重复的组合

                    从 $n$ 个不同元素中取 $k(k \le n)$ 个元素(同一元素允许重复取出)的方案数为

                    简单证明

                    把每次选的看作

                    然后令 $y_i = x_i + i-1$

                    得到

                    不难发现这就是 $\binom{n+m-1}{m}$。证毕。


                    排列组合的扩展

                    请注意区分多重组合数多重集的组合数

                    小蓝书上写的「不全相异元素的全排列」就是多重组合数(多重集的排列数)

                    多重集&多重组合数

                    多重集是指包含重复元素的广义集合。3

                    设 $S=\{n_1 \cdot a_1,~n_2 \cdot a_2,\cdots,n_k\cdot a_k\}$ 表示由 $n_1$ 个 $a_1$ ,$n_2$ 个 $a_2$ ,$\cdots$ ,$n_k$ 个 $a_k$ 组成的多重集, $S$ 的全排列个数为

                    相当于把相同元素的排列数除掉了。

                    具体地,如果 $n$ 个元素中,分别有 $n_1,n_2,\cdots,n_k$ 个元素相同,且 $n_1 + n_2 + \cdots + n_k = n$ ,则这 $n$ 个元素的全排列称为多重集的排列数,或称作多重组合数

                    其不同的排列个数记为 $\binom{n}{n_1,n_2,\cdots,n_k}$,则

                    (小蓝书上写的是空格而不是逗号)

                    不难发现, $\dbinom{n}{m} = \dbinom{n}{m,n-m}$ ,只不过后者比较繁琐,一般不写。

                    多组组合数

                    把 $n$ 个相异元素分为 $k (k \le n)$ 个按照一定顺序排列成的组,其中第 $i$ 组有 $n_i$ 个元素( $\sum n_i = n$ ),则不同的分组方法的种数为

                    简单证明

                    根据组合数的性质,不难推出方案数为

                    通过展开上面的式子,可以得到

                    证毕。

                    多重集的组合数1

                    咕咕咕。

                    多重集的组合数2

                    咕咕咕。

                    不相邻的排列

                    $1\sim n$ 这 $n$ 个自然数中选 $k$ 个,这 $k$ 个数任何两个都不相邻的方案数

                    证明

                    感谢 AprilGrimoire 老师的耐心解释 我太蠢了

                    因为选出来的 $k$ 个数按照一定顺序排列(组合嘛)

                    不妨假设这个顺序满足

                    $a_i$ 表示选的第 $i$ 个数

                    设合法的答案序列为 $a_1,a_2,\cdots,a_k$ ,并让原序列按 $1\sim n$ 排好

                    我们将「前 $k-1$ 个被选中的数」在原序列中分别与其右侧相邻的数合并

                    因为 $a_i$ 和 $a_{i+1}$ 一定不相邻,因此合并不会改变选出 $k$ 个的事实

                    我们不需要知道这 $k-1$ 个数具体是什么

                    因为对于每一个合法的答案序列,都存在唯一的合并方法

                    这样就形成了 $n-k+1$ 个新的块,并且这些块不再受到“相邻”的限制

                    然后就在这 $n-k+1$ 个块中选 $k$ 个即可。

                    答案就是 $\dbinom{n-k+1}{k}$


                    练习题

                    例1

                    从 $1,2,3,\cdots,100$ 中取 $3$ 个数,使这 $3$ 个数恰好成等差数列的不同取法有___种。

                    解法1

                    设取出的 $3$ 个数构成首项为 $a$ ,公差为 $d$ 的等差数列:

                    则 $a+2d \le 100$ ,则

                    整理一下就是

                    对于每个确定的 $d$ ,$a$ 都有 $100-2d$ 种取法,则答案为

                    解法2

                    设取出的 $3$ 个数分别为 $a_1,a_2,a_3$ ,它们构成公差为 $d$ 的等差数列

                    于是 $a_2=\dfrac{a_1+a_3}{2}$ 可被 $a_1,a_3$ 唯一确定,且 $a_1,a_3$ 的奇偶性相同

                    • 若 $a_1$ 与 $a_3$ 同为偶数,则有 $\mathrm{C}_{50}^{2}$ 种取法
                    • 若 $a_1$ 与 $a_3$ 同为计数,则有 $\mathrm{C}_{50}^{2}$ 种取法

                    则共有 $2 \times \mathrm{C}_{50}^2$ 种取法。


                    其他题还在咕咕咕。


                    参考文献

                    1. https://zh.wikipedia.org/wiki/%E5%8A%A0%E6%B3%95%E5%8E%9F%E7%90%86
                    2. https://zh.wikipedia.org/wiki/%E4%B9%98%E6%B3%95%E5%8E%9F%E7%90%86
                    3. https://oi-wiki.org/math/combinatorics/combination/
                    ]]> + 16.1

                    施工中,咕咕咕....

                    计数原理

                    加法原理

                    设完成某件事有 \(m\)类不同的方法,

                    \(i(1\le i \le m)\) 类方法中有\(n_i\) 种不同的方法,则完成这件事共有\[n_1 + n_2 + \cdots + n_m\] 种方法。

                    在严格化的数学中,加法原理是有关集合大小的事实。1

                    断言任意有限多个两两互斥的集合大小之和,等于其并集的大小

                    以符号表示为,若集合 \(S_1,S_2,\cdots,S_n\) 两两互斥,则有 \[|S_1|+|S_2| + \cdots + |S_n| = |S_1 \cup S_2 \cup \cdots S_n|\] 容斥原理可以视为加法原理的推广。

                    乘法原理

                    设完成某件事需要 \(m\) 个步骤,

                    做第 \(i\) 步有 \(n_i\) 种不同的方法,则完成这件事共有 \[n_1 \times n_2 \times \cdots \times n_m\]

                    在集合论中,乘法原理可以设为基数乘积的定义。2

                    对于集合 \(S_1,S_2,\cdots,S_n\) ,以\(|S_i|\) 表示 \(S_i\) 的元素个数(基数),则有 \[|S_1| \times |S_2| \times \cdots \times |S_n| = |S_1 \times S_2 \times\cdots \times S_n|\] 其中 \(S_i \times S_j\)表示笛卡尔积。 \(S_i\)可以是无穷集。参见基数和选择公理。


                    排列组合

                    下面两个指的是无重复的排列与组合

                    排列数

                    \(n\) 个不同元素中取 \(k(k\le n)\)个不同元素,并按一定顺序排成一列的方案数 \[\mathrm{A}_{n}^{k} = \frac{n!}{(n-k)!}\] 也可写作 \(\mathrm{P}_{n}^{k}\) 或直接用组合数写法\(k! \times \binom{n}{k}\)

                    组合数

                    \(n\) 个不同元素中取 \(k(k \le n)\) 个不同元素的方案数 \[\binom{n}{k} = \dfrac{n!}{k!(n-k)!}\] 高中一般记作 \(\mathrm{C}_n^{k}\) ,不是很推荐使用。

                    特别地,规定当 \(k >n\) 时, \(\mathrm{A}_n^{k} =\mathrm{C}_n^{k}=0\)

                    插板法

                    \(n\) 个相同的球分 \(m\) 个组,不能有组为空

                    方案数 \[\dbinom{n-1}{m}\] \(n\) 个相同球分 \(m\) 个组,可以有组为空

                    方案数 \[\dbinom{n-1+m}{m}\]

                    可重复的排列

                    \(n\) 个不同元素中取 \(k(k\le n)\)个元素(同一元素允许重复取出),并按一定顺序排成一列的方案数为 \[n^k\]

                    可重复的组合

                    \(n\) 个不同元素中取 \(k(k \le n)\)个元素(同一元素允许重复取出)的方案数为 \[\binom{n+m-1}{m}\]

                    简单证明

                    把每次选的看作 \[\{x_1,x_2,\cdots,x_m\} \quad(1\le x_1 \le x_2 \le \dots \le x_m \le n)\] 然后令 \(y_i = x_i +i-1\)

                    得到 \[\{y_1,y_2,\cdots,y_m\} \quad (1 \le y_1 < y_2 < \cdots \le n+m-1)\] 不难发现这就是 \(\binom{n+m-1}{m}\)。证毕。


                    排列组合的扩展

                    请注意区分多重组合数多重集的组合数

                    小蓝书上写的「不全相异元素的全排列」就是多重组合数(多重集的排列数)

                    多重集&多重组合数

                    多重集是指包含重复元素的广义集合。3

                    \(S=\{n_1 \cdot a_1,~n_2 \cdota_2,\cdots,n_k\cdot a_k\}\) 表示由 \(n_1\) 个 \(a_1\) ,\(n_2\) 个 \(a_2\) ,\(\cdots\) ,\(n_k\) 个 \(a_k\) 组成的多重集, \(S\) 的全排列个数为 \[\dbinom{n}{n_1,n_2,\cdots,n_k} = \dfrac{n!}{n_1!n_2!\cdots n_k!}\] 相当于把相同元素的排列数除掉了。

                    具体地,如果 \(n\) 个元素中,分别有\(n_1,n_2,\cdots,n_k\) 个元素相同,且\(n_1 + n_2 + \cdots + n_k = n\) ,则这\(n\)个元素的全排列称为多重集的排列数,或称作多重组合数

                    其不同的排列个数记为 \(\binom{n}{n_1,n_2,\cdots,n_k}\),则 \[\dbinom{n}{n_1,n_2,\cdots,n_k} = \dfrac{n!}{n_1!n_2!\cdots n_k!}\] (小蓝书上写的是空格而不是逗号)

                    不难发现, \(\dbinom{n}{m} =\dbinom{n}{m,n-m}\) ,只不过后者比较繁琐,一般不写。

                    多组组合数

                    \(n\) 个相异元素分为 \(k (k \le n)\)个按照一定顺序排列成的组,其中第 \(i\)组有 \(n_i\) 个元素( \(\sum n_i = n\) ),则不同的分组方法的种数为\[\dbinom{n}{n_1,n_2,\cdots,n_k} = \dfrac{n!}{n_1!n_2!\cdots n_k!}\]

                    简单证明

                    根据组合数的性质,不难推出方案数为 \[\binom{n}{n_1} \times \binom{n-n_1}{n_2}\times\binom{n-n_1-n_2}{n_3}\times \cdots \times \binom{n_k-\sum_{1\le i<k}n_i}{n_k}\] 通过展开上面的式子,可以得到 \[\dfrac{n!}{n_1!n_2!\cdots n_k!}\] 证毕。

                    多重集的组合数1

                    咕咕咕。

                    多重集的组合数2

                    咕咕咕。

                    不相邻的排列

                    \(1\sim n\)\(n\) 个自然数中选 \(k\) 个,这 \(k\) 个数任何两个都不相邻的方案数 \[\dbinom{n-k+1}{k}\]

                    证明

                    感谢 AprilGrimoire老师的耐心解释 我太蠢了

                    因为选出来的 \(k\)个数按照一定顺序排列(组合嘛)

                    不妨假设这个顺序满足 \[a_1 < a_2 < \cdots < a_k\] \(a_i\) 表示选的第 \(i\) 个数

                    设合法的答案序列为 \(a_1,a_2,\cdots,a_k\) ,并让原序列按 \(1\sim n\) 排好

                    我们将「前 \(k-1\)个被选中的数」在原序列中分别与其右侧相邻的数合并

                    因为 \(a_i\)\(a_{i+1}\) 一定不相邻,因此合并不会改变选出\(k\) 个的事实

                    我们不需要知道这 \(k-1\)个数具体是什么

                    因为对于每一个合法的答案序列,都存在唯一的合并方法

                    这样就形成了 \(n-k+1\)个新的块,并且这些块不再受到“相邻”的限制

                    然后就在这 \(n-k+1\) 个块中选 \(k\) 个即可。

                    答案就是 \(\dbinom{n-k+1}{k}\)


                    练习题

                    例1

                    \(1,2,3,\cdots,100\) 中取 \(3\) 个数,使这 \(3\)个数恰好成等差数列的不同取法有___种。

                    解法1

                    设取出的 \(3\) 个数构成首项为 \(a\) ,公差为 \(d\) 的等差数列: \[a,a+d,a+2d \quad (a,d \in \mathbb{Z}_+)\] 则 \(a+2d \le 100\) ,则\[1 \le d \le \left\lfloor{\dfrac{100-a}{2}}\right\rfloor \le\left\lfloor{\dfrac{100-1}{2}}\right\rfloor = 49\] 整理一下就是 \[1\le d \le 49\\ a\le 100-2d\] 对于每个确定的 \(d\)\(a\) 都有 \(100-2d\) 种取法,则答案为 \[\sum_{d=1}^{49}(100-2d) = 49\times 100 -2 \times \frac{49(49+1)}{2} =2450\] 解法2

                    设取出的 \(3\) 个数分别为 \(a_1,a_2,a_3\) ,它们构成公差为 \(d\) 的等差数列

                    于是 \(a_2=\dfrac{a_1+a_3}{2}\) 可被\(a_1,a_3\) 唯一确定,且 \(a_1,a_3\) 的奇偶性相同

                    • \(a_1\)\(a_3\) 同为偶数,则有 \(\mathrm{C}_{50}^{2}\) 种取法
                    • \(a_1\)\(a_3\) 同为计数,则有 \(\mathrm{C}_{50}^{2}\) 种取法

                    则共有 \(2 \times\mathrm{C}_{50}^2\) 种取法。


                    其他题还在咕咕咕。


                    参考文献


                    1. https://zh.wikipedia.org/wiki/%E5%8A%A0%E6%B3%95%E5%8E%9F%E7%90%86↩︎

                    2. https://zh.wikipedia.org/wiki/%E4%B9%98%E6%B3%95%E5%8E%9F%E7%90%86↩︎

                    3. https://oi-wiki.org/math/combinatorics/combination/↩︎

                    ]]> @@ -433,7 +433,7 @@ /2022/08/15/cf1491f-magnets-ti-jie/ - CF1491F Magnets 题解

                    题目链接:CF1491F Magnets

                    题意

                    _这是一个交互题。_

                    早苗有 $n$ 块磁石,编号为 $1,2,\cdots,n$。每块磁石的磁极可能是正极,负极,也可能没有磁性。她希望你能帮她找出所有没有磁性的磁石。

                    万幸的是,你有一台磁力检测仪。你每次可以将每个磁石放在这台机器的左托盘,右托盘或者不放。

                    机器将会返回此时的磁力强度。设托盘左边有 $n_1$ 个磁石为正极,$s_1$ 个磁石为负极,托盘右边中有 $n_2$ 磁石为正极,$s_2$ 个磁石为负极,则返回的磁力强度为 $ n_1n_2+s_1s_2-n_1s_2-n_2s_1 $。

                    如果一次测试中磁力强度的绝对值大于 $n$,这台机器就会坏掉。

                    你需要在 $n+\lfloor\log_2n\rfloor$ 次测试内找到所有没有磁性的磁石的编号,同时不能弄坏机器。

                    保证存在至少 $2$ 块磁石有磁性且至少 $1$ 块磁石没有磁性。


                    输入格式

                    仅一行,包含一个正整数 $T$($1\leq T\leq 100$),表示数据的组数。


                    对于每组数据,你需要先读入一个正整数 $n$($3\leq n\leq 2000$),代表磁石的数量,保证每组数据的 $n$ 之和不超过 $2000$。

                    接下来,你可以向交互库提出若干次询问。对于每个询问,你需要输出三行:

                    • 第一行输出 ? L R,其中 $L$ 代表放在左托盘的磁石个数,$R$ 代表放在右托盘的磁石个数。
                    • 第二行输出 $L$ 个数 $a_1,a_2,\cdots,a_L$,代表放在左托盘的磁石编号。
                    • 第三行输出 $R$ 个数 $b_1,b_2,\cdots,b_R$,代表放在右托盘的磁石编号。

                    显然,你需要保证 $a_i\neq b_j$。

                    接下来,你需要刷新缓冲区。

                    (刷新缓冲区教程)

                    最后,你需要读入一个整数 $F$,代表这次测试的磁性大小。

                    如果你已经确定了答案,输出一行 ! k A 表示答案,其中 $k$ 代表没有磁性的磁石数量,$A$ 为一个长度为 $k$ 的序列,代表所有没有磁性的磁石编号。如果这不是最后一组数据,你应该继续处理下一组数据。如果这是最后一组数据,你应该立刻结束你的程序。

                    此题中,交互库是固定的。 每个磁石的状态不随着你的测试而改变。

                    先化简一下柿子,可得 $(n_1 - s_1) (n_2 - s_2)$

                    不难发现,只要天平一侧没有带磁极的,返回值一定是 $0$

                    但是直接求解依旧相当困难,因为返回值为 $0$ 的情况太多

                    考虑返回值不为 $0$ 时的情况,此时左右必定各有不少于 $1$ 个带磁极的

                    这意味着如果我们找到了一个带磁极的,问题求解将变得相当容易

                    于是我们枚举一个 $i$ ,每次询问 $A=\{i\},B=\{1,\cdots,i-1\}$ ,直到返回值不为 $0$ 。

                    这样的好处在于:如果返回值不为 $0$ ,则 $i$ 一定带磁极

                    知道 $i$ 带磁极,那 $i+1$ 到 $n$ 的就可以一个个查了

                    同时可知 $\{1,\dots,i-1\}$ 中必定有且仅有一个带磁极的

                    这个带磁极的可以通过询问 $A=\{i\}$ 和 $B = \{1,\cdots,j\}$ ,这里的 $j$ 是枚举的

                    仔细观察可以发现,这个 $j$ 没必要一个个询问,它是有单调性的。

                    时间复杂度 $O(Qn)$ ,然后我们就可以愉快地通过此题啦(

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int Q,n,F;vector<int> ans;signed main(){    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    scanf("%lld",&Q);    while(Q--)    {        scanf("%lld",&n); ans.clear();        for(int i=2; i<=n; i++)        {            printf("? 1 %lld\n%lld\n",i-1,i);            for(int j=1; j<i; j++) printf("%lld%c",j," \n"[j==i-1]);            fflush(stdout); scanf("%lld",&F);            if(!F) continue;            for(int j=i+1; j<=n; j++)            {                printf("? 1 1\n%lld\n%lld\n",i,j);                fflush(stdout); scanf("%lld",&F);                 if(!F) ans.push_back(j);            }            int l=1,r=i-1;            while(l<r)            {                int mid=(l+r)>>1;                printf("? 1 %lld\n%lld\n",mid,i);                for(int j=1; j<=mid; j++) printf("%lld%c",j," \n"[j==mid]);                fflush(stdout); scanf("%lld",&F);                if(F) r=mid; else l=mid+1;            }            for(int j=1; j<i; j++)                if(j!=l) ans.push_back(j);            int len=ans.size(); printf("! %lld ",len);            for(int j=0; j<len; j++) printf("%lld%c",ans[j]," \n"[j==len-1]);            fflush(stdout); break;        }    }    return 0;}
                    ]]> + CF1491F Magnets 题解

                    题目链接:CF1491FMagnets

                    题意

                    这是一个交互题。

                    早苗有 \(n\) 块磁石,编号为 \(1,2,\cdots,n\)。每块磁石的磁极可能是正极,负极,也可能没有磁性。她希望你能帮她找出所有没有磁性的磁石。

                    万幸的是,你有一台磁力检测仪。你每次可以将每个磁石放在这台机器的左托盘,右托盘或者不放。

                    机器将会返回此时的磁力强度。设托盘左边有 \(n_1\) 个磁石为正极,\(s_1\) 个磁石为负极,托盘右边中有 \(n_2\) 磁石为正极,\(s_2\) 个磁石为负极,则返回的磁力强度为 $n_1n_2+s_1s_2-n_1s_2-n_2s_1 $。

                    如果一次测试中磁力强度的绝对值大于 \(n\),这台机器就会坏掉。

                    你需要在 \(n+\lfloor\log_2n\rfloor\)次测试内找到所有没有磁性的磁石的编号,同时不能弄坏机器。

                    保证存在至少 \(2\)块磁石有磁性且至少 \(1\)块磁石没有磁性。


                    输入格式

                    仅一行,包含一个正整数 \(T\)\(1\leq T\leq 100\)),表示数据的组数。


                    对于每组数据,你需要先读入一个正整数 \(n\)(\(3\leqn\leq 2000\)),代表磁石的数量,保证每组数据的 \(n\) 之和不超过 \(2000\)。

                    接下来,你可以向交互库提出若干次询问。对于每个询问,你需要输出三行:

                    • 第一行输出 ? L R,其中 \(L\) 代表放在左托盘的磁石个数,\(R\) 代表放在右托盘的磁石个数。
                    • 第二行输出 \(L\) 个数 \(a_1,a_2,\cdots,a_L\),代表放在左托盘的磁石编号。
                    • 第三行输出 \(R\) 个数 \(b_1,b_2,\cdots,b_R\),代表放在右托盘的磁石编号。

                    显然,你需要保证 \(a_i\neqb_j\)

                    接下来,你需要刷新缓冲区。

                    (刷新缓冲区教程)

                    最后,你需要读入一个整数 \(F\),代表这次测试的磁性大小。

                    如果你已经确定了答案,输出一行 ! k A 表示答案,其中\(k\) 代表没有磁性的磁石数量,\(A\) 为一个长度为 \(k\)的序列,代表所有没有磁性的磁石编号。如果这不是最后一组数据,你应该继续处理下一组数据。如果这是最后一组数据,你应该立刻结束你的程序。

                    此题中,交互库是固定的。每个磁石的状态不随着你的测试而改变。

                    先化简一下柿子,可得 \((n_1 - s_1) (n_2 -s_2)\)

                    不难发现,只要天平一侧没有带磁极的,返回值一定是 \(0\)

                    但是直接求解依旧相当困难,因为返回值为 \(0\) 的情况太多

                    考虑返回值不为 \(0\)时的情况,此时左右必定各有不少于 \(1\)个带磁极的

                    这意味着如果我们找到了一个带磁极的,问题求解将变得相当容易

                    于是我们枚举一个 \(i\) ,每次询问\(A=\{i\},B=\{1,\cdots,i-1\}\),直到返回值不为 \(0\)

                    这样的好处在于:如果返回值不为 \(0\),则 \(i\) 一定带磁极

                    知道 \(i\) 带磁极,那 \(i+1\) 到 \(n\) 的就可以一个个查了

                    同时可知 \(\{1,\dots,i-1\}\)中必定有且仅有一个带磁极的

                    这个带磁极的可以通过询问 \(A=\{i\}\)\(B = \{1,\cdots,j\}\) ,这里的\(j\) 是枚举的

                    仔细观察可以发现,这个 \(j\)没必要一个个询问,它是有单调性的。 \[\texttt{总查询数} =n-1 + \left\lceil{\log_2 n}\right\rceil \le n +\left\lfloor{\log_2 n}\right\rfloor\] 时间复杂度 \(O(Qn)\),然后我们就可以愉快地通过此题啦(

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int Q,n,F;vector<int> ans;signed main(){    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    scanf("%lld",&Q);    while(Q--)    {        scanf("%lld",&n); ans.clear();        for(int i=2; i<=n; i++)        {            printf("? 1 %lld\n%lld\n",i-1,i);            for(int j=1; j<i; j++) printf("%lld%c",j," \n"[j==i-1]);            fflush(stdout); scanf("%lld",&F);            if(!F) continue;            for(int j=i+1; j<=n; j++)            {                printf("? 1 1\n%lld\n%lld\n",i,j);                fflush(stdout); scanf("%lld",&F);                 if(!F) ans.push_back(j);            }            int l=1,r=i-1;            while(l<r)            {                int mid=(l+r)>>1;                printf("? 1 %lld\n%lld\n",mid,i);                for(int j=1; j<=mid; j++) printf("%lld%c",j," \n"[j==mid]);                fflush(stdout); scanf("%lld",&F);                if(F) r=mid; else l=mid+1;            }            for(int j=1; j<i; j++)                if(j!=l) ans.push_back(j);            int len=ans.size(); printf("! %lld ",len);            for(int j=0; j<len; j++) printf("%lld%c",ans[j]," \n"[j==len-1]);            fflush(stdout); break;        }    }    return 0;}
                    ]]> @@ -460,7 +460,7 @@ /2022/08/15/luo-gu-p2396-yyy-loves-maths-vii-ti-jie/ - 洛谷P2396 yyy loves Maths VII 题解

                    题目链接:P2396 yyy loves Maths VII

                    题意

                    一群同学在和 yyy 玩一个游戏。

                    每次,他们会给 yyy $n$ 张卡片,卡片上有数字,所有的数字都是“幸运数字”,我们认为第 $i$ 张卡片上数字是 $a_{i}$。

                    每次 yyy 可以选择向前走 $a_{i}$ 步并且丢掉第 $i$ 张卡片。当他手上没有卡片的时候他就赢了。

                    但是呢,大家对“厄运数字”的位置布置下了陷阱,如果 yyy 停在这个格子上,那么他就输了。注意:即使到了终点,但是这个位置是厄运数字,那么也输了。

                    现在,有些同学开始问:yyy 有多大的概率会赢呢?

                    大家觉得这是个好问题,有人立即让 yyy 写个程序:“电脑运行速度很快!$24$ 的阶乘也不过就 $620\,448\,401\,733\,239\,439\,360\,000$,yyy 你快写个程序来算一算。”

                    yyy 表示很无语,他表示他不想算概率,最多算算赢的方案数,而且是对 $10^9+7$ 取模后的值。

                    大家都不会写程序,只好妥协。

                    但是这时候 yyy 为难了,$24!$ 太大了,要跑好长时间。

                    他时间严重不够!需要你的帮助!

                    由于 yyy 人格分裂,某个数字可能既属于幸运数字又属于厄运数字。

                    $50\%$ 的数据 $n \leq 23$ , $100\%$ 的数据 $n \leq 24$。

                    注意到 $n \le 24$ ,并且每张牌只能用一次,考虑状压dp。

                    对于每个状态 $i$ ,枚举它是从哪个地方转移来的

                    具体地,设 $f_i$ 表示状态为 $i$ 的方案数,则

                    显然答案为 $f_{\text{mx}} , \text{mx}= 2^n -1$。

                    同时我们还要记录一个 $\text{dis}_i$ 表示 $i$ 状态对应的路径长

                    当 $\text{dis}_i$ 为“厄运数字”时,不转移 $f_i$ ,即 $f_i = 0$ 。

                    时间复杂度 $O(n 2^n)$ ,卡常题。

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)((1<<24)+5)const int p=1e9+7;int n,m,b0,b1; signed dis[N],f[N];template<typename T> void add(T &x,T y){x=(x+(y%p))%p;}void solve(int x){    for(int i=x,j; i; i^=j) j=i&(-i),add(f[x],f[x^j]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=0; i<n; i++) cin >> dis[1 << i];    cin >> m;    if(m>0) cin >> b0;    if(m>1) cin >> b1;    f[0]=1;    int mx=(1<<n)-1;    for(int i=1,j; i<=mx; i++)    {        j=i&(-i); dis[i]=dis[i^j]+dis[j];        if(dis[i]!=b0&&dis[i]!=b1) solve(i);    }    cout << f[mx] << '\n';    return 0;}
                    ]]> + 洛谷P2396 yyy loves MathsVII 题解

                    题目链接:P2396 yyyloves Maths VII

                    题意

                    一群同学在和 yyy 玩一个游戏。

                    每次,他们会给 yyy \(n\)张卡片,卡片上有数字,所有的数字都是“幸运数字”,我们认为第 \(i\) 张卡片上数字是 \(a_{i}\)。

                    每次 yyy 可以选择向前走 \(a_{i}\)步并且丢掉第 \(i\)张卡片。当他手上没有卡片的时候他就赢了。

                    但是呢,大家对“厄运数字”的位置布置下了陷阱,如果 yyy停在这个格子上,那么他就输了。注意:即使到了终点,但是这个位置是厄运数字,那么也输了。

                    现在,有些同学开始问:yyy 有多大的概率会赢呢?

                    大家觉得这是个好问题,有人立即让 yyy写个程序:“电脑运行速度很快!\(24\)的阶乘也不过就 \(620\,448\,401\,733\,239\,439\,360\,000\),yyy你快写个程序来算一算。”

                    yyy 表示很无语,他表示他不想算概率,最多算算赢的方案数,而且是对\(10^9+7\) 取模后的值。

                    大家都不会写程序,只好妥协。

                    但是这时候 yyy 为难了,\(24!\)太大了,要跑好长时间。

                    他时间严重不够!需要你的帮助!

                    由于 yyy 人格分裂,某个数字可能既属于幸运数字又属于厄运数字。

                    \(50\%\) 的数据 \(n \leq 23\) , \(100\%\) 的数据 \(n \leq 24\)。

                    注意到 \(n \le 24\),并且每张牌只能用一次,考虑状压dp。

                    对于每个状态 \(i\),枚举它是从哪个地方转移来的

                    具体地,设 \(f_i\) 表示状态为 \(i\) 的方案数,则 \[f_i = \sum f_{i \setminus \{j\}} \times [j \in i]\] 显然答案为 \(f_{\text{mx}} ,\text{mx}= 2^n -1\)

                    同时我们还要记录一个 \(\text{dis}_i\) 表示 \(i\) 状态对应的路径长

                    \(\text{dis}_i\)为“厄运数字”时,不转移 \(f_i\) ,即\(f_i = 0\)

                    时间复杂度 \(O(n 2^n)\),卡常题。

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)((1<<24)+5)const int p=1e9+7;int n,m,b0,b1; signed dis[N],f[N];template<typename T> void add(T &x,T y){x=(x+(y%p))%p;}void solve(int x){    for(int i=x,j; i; i^=j) j=i&(-i),add(f[x],f[x^j]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=0; i<n; i++) cin >> dis[1 << i];    cin >> m;    if(m>0) cin >> b0;    if(m>1) cin >> b1;    f[0]=1;    int mx=(1<<n)-1;    for(int i=1,j; i<=mx; i++)    {        j=i&(-i); dis[i]=dis[i^j]+dis[j];        if(dis[i]!=b0&&dis[i]!=b1) solve(i);    }    cout << f[mx] << '\n';    return 0;}
                    ]]> @@ -487,7 +487,7 @@ /2022/08/14/cf1073e-segment-sum-ti-jie/ - CF1073E Segment Sum 题解

                    题目链接:CF1073E Segment Sum

                    题意

                    给定 $l,r,k$ ,求 $[l,r]$ 有多少个数满足「不包含超过 $k$ 个数码」,输出他们的和 $\bmod {998244353}$

                    $0 \le k\le 10,~1\le l ,r \le 10^{18}$

                    做了这题才发现自己对数位dp的掌握很烂

                    其实很水,用个状态 st 记录每个数码是否出现过

                    设 $f_{i,j}$ 表示只考虑前 $i$ 位(从低位向高位计数),数码状态为 $j$ 的数字的个数

                    设 $g_{i,j}$ 表示只考虑前 $i$ 位(从低位向高位计数),数码状态为 $j$ 的数字的和

                    其中 $d$ 表示第 $i-1$ 位要填啥, $j^{\prime}$ 表示转移后的 $j$ ,懒得讲,直接看代码

                    注意考虑一下前导零什么的就好了(因为这里前导零会影响数码出现次数

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <map>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef pair<int,int> P;#define F first#define S secondconst int p=998244353;#define N (int)()int l,r,k,num[25],pwd[25]; P f[25][(1<<10)+15];int popc(int x){int c=0; while(x)x-=x&(-x),++c; return c;}// int popc(int x){return __builtin_popcount(x);}P dfs(int u,int st,bool limit,bool qd0){    if(!u) return {popc(st) <= k,0};    if((!limit) && (!qd0) && f[u][st].F!=-1)        return f[u][st];    P res={0,0},tmp; int up=limit?num[u]:9;    for(int i=0,nx; i<=up; i++)    {        nx=(qd0 && (!i)) ? st : (st|(1<<i));        tmp=dfs(u-1,nx,limit&&i==num[u],qd0&&i==0);        res={(res.F+tmp.F)%p, (tmp.F*pwd[u-1]%p*i%p +res.S+tmp.S)%p};    }    if((!limit) && (!qd0)) f[u][st]=res;    return res;}int solve(int x){    int cnt=0;    while(x){num[++cnt]=x%10,x/=10;}    return dfs(cnt,0,1,1).S;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(f,-1,sizeof(f)); pwd[0]=1;    for(int i=1; i<=20; i++) pwd[i]=pwd[i-1]*10%p;    cin >> l >> r >> k;    cout << ((solve(r)-solve(l-1))%p+p)%p << '\n';    return 0;}
                    ]]> + CF1073E Segment Sum 题解

                    题目链接:CF1073ESegment Sum

                    题意

                    给定 \(l,r,k\) ,求 \([l,r]\) 有多少个数满足「不包含超过 \(k\) 个数码」,输出他们的和 \(\bmod {998244353}\)

                    \(0 \le k\le 10,~1\le l ,r \le10^{18}\)

                    做了这题才发现自己对数位dp的掌握很烂

                    其实很水,用个状态 st 记录每个数码是否出现过

                    \(f_{i,j}\) 表示只考虑前 \(i\) 位(从低位向高位计数),数码状态为\(j\) 的数字的个数

                    \(g_{i,j}\) 表示只考虑前 \(i\) 位(从低位向高位计数),数码状态为\(j\) 的数字的和

                    \[f_{i,j} = \sum f_{i-1,j^{\prime}}\\\\g_{i,j} = \sum 10^{i-1} \times d \times f_{i-1,j^{\prime}} +g_{i,j^{\prime}}\] 其中 \(d\) 表示第 \(i-1\) 位要填啥, \(j^{\prime}\) 表示转移后的 \(j\) ,懒得讲,直接看代码

                    注意考虑一下前导零什么的就好了(因为这里前导零会影响数码出现次数

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <map>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef pair<int,int> P;#define F first#define S secondconst int p=998244353;#define N (int)()int l,r,k,num[25],pwd[25]; P f[25][(1<<10)+15];int popc(int x){int c=0; while(x)x-=x&(-x),++c; return c;}// int popc(int x){return __builtin_popcount(x);}P dfs(int u,int st,bool limit,bool qd0){    if(!u) return {popc(st) <= k,0};    if((!limit) && (!qd0) && f[u][st].F!=-1)        return f[u][st];    P res={0,0},tmp; int up=limit?num[u]:9;    for(int i=0,nx; i<=up; i++)    {        nx=(qd0 && (!i)) ? st : (st|(1<<i));        tmp=dfs(u-1,nx,limit&&i==num[u],qd0&&i==0);        res={(res.F+tmp.F)%p, (tmp.F*pwd[u-1]%p*i%p +res.S+tmp.S)%p};    }    if((!limit) && (!qd0)) f[u][st]=res;    return res;}int solve(int x){    int cnt=0;    while(x){num[++cnt]=x%10,x/=10;}    return dfs(cnt,0,1,1).S;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(f,-1,sizeof(f)); pwd[0]=1;    for(int i=1; i<=20; i++) pwd[i]=pwd[i-1]*10%p;    cin >> l >> r >> k;    cout << ((solve(r)-solve(l-1))%p+p)%p << '\n';    return 0;}
                    ]]> @@ -514,7 +514,7 @@ /2022/08/14/sp10606-balnum-balanced-numbers-ti-jie/ - SP10606 BALNUM - Balanced Numbers 题解

                    题目链接:SP10606 BALNUM - Balanced Numbers

                    题意

                    一个数被称为是平衡的数

                    当且仅当对于所有出现过的数位(即 $0$ 到 $9$ )

                    每个偶数出现奇数次,每个奇数出现偶数次。

                    给定 $A,B$,请统计出 $[A,B]$ 内所有平衡数的个数。

                    $1\leq A\leq B\leq 10^{19}$

                    数位dp就是套板子,然后我现在还没记住板子(

                    考虑记录每个数的奇偶性,用状态 st 表示

                    第 $i(1\le i \le 9)$ 位为 $0$ 表示出现次数为偶,否则为 $1$

                    再记录一个 vis ,表示这个数是否出现了

                    这两个东西可以压到一个 pair 里,这样方便开数组(和其他题解学的

                    因为这道题是统计出现次数的,所以前导零会影响计数

                    所以还要记录一下有没有前导零,代码里用 qd0 表示了(

                    最后 check 一下状态啥的,就好了(

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <map>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef pair<int,int> P;#define F first#define S second#define N (int)(1.5e6+15)int tot,num[33],f[33][N];map<P,int> mp;bool check(int st,int vis){    for(int i=0; i<=9; i++)    {        if((vis>>i)&1)        {            if((i&1) && ((st>>i)&1)) return 0; // odd but odd            if((!(i&1)) && (!((st>>i)&1))) return 0; // even but even        }    }    return 1;}int dfs(int u,int st,int vis,bool limit,bool qd0){    if(!u) return check(st,vis);    if((!limit) && (!qd0) && f[u][mp[{st,vis}]]!=-1)        return f[u][mp[{st,vis}]];    int res=0,up=limit?num[u]:9;    for(int i=0; i<=up; i++)    {        if(qd0&&(!i))res+=dfs(u-1,st,vis,limit&&i==num[u],1);        else res+=dfs(u-1,st^(1<<i),vis|(1<<i),limit&&i==num[u],0);    }    if((!limit)&&(!qd0)) mp[{st,vis}]=++tot,f[u][tot]=res;    return res;}int solve(int x){    int cnt=0;    while(x) num[++cnt]=x%10,x/=10;    return dfs(cnt,0,0,1,1);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(f,-1,sizeof(f));    int Q,l,r; cin >> Q;    while(Q--)    {        cin >> l >> r;        cout << solve(r)-solve(l-1) << '\n';    }    return 0;}
                    ]]> + SP10606 BALNUM -Balanced Numbers 题解

                    题目链接:SP10606BALNUM - Balanced Numbers

                    题意

                    一个数被称为是平衡的数

                    当且仅当对于所有出现过的数位(即 \(0\) 到 \(9\) )

                    每个偶数出现奇数次,每个奇数出现偶数次。

                    给定 \(A,B\),请统计出 \([A,B]\) 内所有平衡数的个数。

                    \(1\leq A\leq B\leq 10^{19}\)

                    数位dp就是套板子,然后我现在还没记住板子(

                    考虑记录每个数的奇偶性,用状态 st 表示

                    \(i(1\le i \le 9)\) 位为 \(0\) 表示出现次数为偶,否则为 \(1\)

                    再记录一个 vis ,表示这个数是否出现了

                    这两个东西可以压到一个 pair里,这样方便开数组(和其他题解学的

                    因为这道题是统计出现次数的,所以前导零会影响计数

                    所以还要记录一下有没有前导零,代码里用 qd0 表示了(

                    最后 check 一下状态啥的,就好了(

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <map>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef pair<int,int> P;#define F first#define S second#define N (int)(1.5e6+15)int tot,num[33],f[33][N];map<P,int> mp;bool check(int st,int vis){    for(int i=0; i<=9; i++)    {        if((vis>>i)&1)        {            if((i&1) && ((st>>i)&1)) return 0; // odd but odd            if((!(i&1)) && (!((st>>i)&1))) return 0; // even but even        }    }    return 1;}int dfs(int u,int st,int vis,bool limit,bool qd0){    if(!u) return check(st,vis);    if((!limit) && (!qd0) && f[u][mp[{st,vis}]]!=-1)        return f[u][mp[{st,vis}]];    int res=0,up=limit?num[u]:9;    for(int i=0; i<=up; i++)    {        if(qd0&&(!i))res+=dfs(u-1,st,vis,limit&&i==num[u],1);        else res+=dfs(u-1,st^(1<<i),vis|(1<<i),limit&&i==num[u],0);    }    if((!limit)&&(!qd0)) mp[{st,vis}]=++tot,f[u][tot]=res;    return res;}int solve(int x){    int cnt=0;    while(x) num[++cnt]=x%10,x/=10;    return dfs(cnt,0,0,1,1);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(f,-1,sizeof(f));    int Q,l,r; cin >> Q;    while(Q--)    {        cin >> l >> r;        cout << solve(r)-solve(l-1) << '\n';    }    return 0;}
                    ]]> @@ -541,7 +541,7 @@ /2022/08/12/uoj66-xin-nian-de-qiao-ke-li-bang-ti-jie/ - UOJ66 新年的巧克力棒 题解

                    题目链接:#66. 新年的巧克力棒

                    题意:马上就要到羊年了,羊村一片欢腾,懒羊羊则懒洋洋地躺在草坪上吃新年的巧克力棒。

                    他手上的巧克力棒是个由 $n$ 个巧克力单元格组成的长度为 $n$ 的长条,现在懒羊羊想把巧克力棒掰开成一个个小单元格。

                    初始时懒羊羊会把这根巧克力棒丢在草坪上,然后每次懒羊羊会从草坪上拿起一根长度大于 $1$ 的巧克力棒,然后从某两个相邻的单元格的间隙处掰开变成两根巧克力棒,然后把这两根巧克力棒丢在草坪上。懒羊羊初始愉悦值为 $0$,每次掰开巧克力棒后如果这两根巧克力棒长度相等,那么懒羊羊将提升 $1$ 点愉悦值。

                    当然,草坪上全是长度为 $1$ 的巧克力棒时懒羊羊就会停止操作。现在懒羊羊想知道,他能获得的愉悦值最多是多少?

                    设 $f_i$ 表示长度为 $i$ 的巧(áo)克(lì)力(gèi)能获得的最大价值,则

                    然后这东西是 $O(n^2)$ 的,看上去也没什么能优化的

                    注意到有 $n \le 10^3$ 的部分分,说明我们推的柿子是大概率正确的

                    好像没什么思路,但是这个 $n\le 10^9$ 印发了我们的好奇心(确信

                    然后打打表试试,打表代码如下:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1005)int f[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    for(int i=2; i<=100; i++)        for(int j=1; j<i; j++)            f[i]=max(f[i],f[j]+f[i-j]+(j==i-j));    for(int i=1; i<=20; i++)        cout << i << " " << f[i] << '\n';    return 0;}

                    表:

                    1 02 13 14 35 36 47 48 79 710 811 812 1013 1014 1115 1116 1517 1518 1619 1620 18

                    观察到 $1,2,4,8$ 都减了 $1$ ,而 $6,10$ 减了 $2$

                    前面这个又是 $2$ 的次幂,难道是??

                    答案确实是 $n - \text{popc}(n)$ ,这里的 $\text{popc}(n)$ 表示 $n$ 在二进制下的 $1$ 的个数

                    于是超短码就出现啦:

                    #include <cstdio>signed main(){    int x,y;     for(scanf("%d",&x); x--;)        scanf("%d",&y),printf("%d\n",y-__builtin_popcount(y));    return 0;}

                    至于为什么答案是这个,可以看看这里的证明 link

                    ]]> + UOJ66 新年的巧克力棒 题解

                    题目链接:#66.新年的巧克力棒

                    题意:马上就要到羊年了,羊村一片欢腾,懒羊羊则懒洋洋地躺在草坪上吃新年的巧克力棒。

                    他手上的巧克力棒是个由 \(n\)个巧克力单元格组成的长度为 \(n\)的长条,现在懒羊羊想把巧克力棒掰开成一个个小单元格。

                    初始时懒羊羊会把这根巧克力棒丢在草坪上,然后每次懒羊羊会从草坪上拿起一根长度大于\(1\)的巧克力棒,然后从某两个相邻的单元格的间隙处掰开变成两根巧克力棒,然后把这两根巧克力棒丢在草坪上。懒羊羊初始愉悦值为\(0\),每次掰开巧克力棒后如果这两根巧克力棒长度相等,那么懒羊羊将提升\(1\) 点愉悦值。

                    当然,草坪上全是长度为 \(1\)的巧克力棒时懒羊羊就会停止操作。现在懒羊羊想知道,他能获得的愉悦值最多是多少?

                    \(f_i\) 表示长度为 \(i\)的巧(áo)克(lì)力(gèi)能获得的最大价值,则 \[f_i = \max\limits_{1 \le j < i}\left\{f_j + f_{i-j} + [j=i-j]\right\}\] 然后这东西是 \(O(n^2)\)的,看上去也没什么能优化的

                    注意到有 \(n \le 10^3\)的部分分,说明我们推的柿子是大概率正确的

                    好像没什么思路,但是这个 \(n\le10^9\) 印发了我们的好奇心(确信

                    然后打打表试试,打表代码如下:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1005)int f[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    for(int i=2; i<=100; i++)        for(int j=1; j<i; j++)            f[i]=max(f[i],f[j]+f[i-j]+(j==i-j));    for(int i=1; i<=20; i++)        cout << i << " " << f[i] << '\n';    return 0;}

                    表:

                    1 02 13 14 35 36 47 48 79 710 811 812 1013 1014 1115 1116 1517 1518 1619 1620 18

                    观察到 \(1,2,4,8\) 都减了 \(1\) ,而 \(6,10\) 减了 \(2\)

                    前面这个又是 \(2\)的次幂,难道是??

                    答案确实是 \(n -\text{popc}(n)\) ,这里的 \(\text{popc}(n)\) 表示 \(n\) 在二进制下的 \(1\) 的个数

                    于是超短码就出现啦:

                    #include <cstdio>signed main(){    int x,y;     for(scanf("%d",&x); x--;)        scanf("%d",&y),printf("%d\n",y-__builtin_popcount(y));    return 0;}

                    至于为什么答案是这个,可以看看这里的证明 link

                    ]]> @@ -568,7 +568,7 @@ /2022/08/11/luo-gu-p2292-hnoi2004-l-yu-yan-ti-jie/ - 洛谷P2292 [HNOI2004] L 语言 题解

                    题目链接:P2292 [HNOI2004] L 语言

                    题意

                    标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的。现在你要处理的就是一段没有标点的文章。

                    一段文章 $T$ 是由若干小写字母构成。一个单词 $W$ 也是由若干小写字母构成。一个字典 $D$ 是若干个单词的集合。我们称一段文章 $T$ 在某个字典 $D$ 下是可以被理解的,是指如果文章 $T$ 可以被分成若干部分,且每一个部分都是字典 $D$ 中的单词。

                    例如字典 $D$ 中包括单词 $\texttt{is},\texttt{name},\texttt{what},\texttt{your}$,则文章 $\texttt{whatisyourname}$ 是在字典 $D$ 下可以被理解的,因为它可以分成 $4$ 个单词:$\texttt{what},\texttt{is},\texttt{your},\texttt{name}$,且每个单词都属于字典 $D$,而文章 $\texttt{whatisyouname}$ 在字典 $D$ 下不能被理解,但可以在字典 $D’=D\cup\{\texttt{you}\}$ 下被理解。这段文章的一个前缀 $\texttt{whatis}$,也可以在字典 $D$ 下被理解,而且是在字典 $D$ 下能够被理解的最长的前缀。

                    给定一个字典 $D$,你的程序需要判断若干段文章在字典 $D$ 下是否能够被理解。并给出其在字典 $D$ 下能够被理解的最长前缀的位置。

                    • 对于 $100\%$ 的数据,保证 $1 \leq n \leq 20$,$1 \leq m \leq 50$,$1 \leq |s| \leq 20$,$1 \leq |t| \leq 2 \times 10^6$,$s$ 与 $t$ 中均只含小写英文字母。

                    设 $f_i$ 表示前 $i$ 个字符是否可行,则有

                    其中 $t[j+1,i]$ 表示文本串的 $[j+1,i]$ ,$S$ 表示所有 $s_i$ 构成的不可重集

                    注意到 $1\le |s_i| \le 20$ ,因此我们可以建 $\tt{AC}$ 自动机然后暴力转移。

                    具体地,我们把所有 $s_i$ 都插入 $\tt{AC}$ 自动机,

                    然后预处理出每个结点在 $\text{fail}$ 树上的 $i (i \le 20)$ 级祖先(认为儿子存在指向父亲的边)

                    这个信息可以压缩为一个二进制数,不妨设为 $g_u$ 。

                    转移时,设当前所在的结点为 $u$ ,同时维护 $f_j(i-20 < j \le i)$ 的状态。

                    后者又可以压到一个二进制数 $x$ 里,即 $x$ 二进制下第 $k(0\le k \le 20)$ 位为 $1$ 表示 $f_{i-k-1}=\text{True}$ 。

                    转移时,若 $g_u \text{ and } x = \text{True}$ ,则 $f_i=\text{True}$ 。这里的 $\text{and}$ 表示二进制下按位与。

                    时间复杂度 $O(20 \times \sum s_i + \sum t_i)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(405)#define M (int)(2e6+15)int n,m;bitset<M> f;char s[N],t[M];struct Trie{    int tot,trie[N][26],ed[N],dep[N],g[N],fail[N];    void insert(char *s)    {        int u=0,l=strlen(s+1);        for(int i=1; i<=l; i++)        {            int c=s[i]-'a';            if(!trie[u][c]) trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1; dep[u]=l;    }    void build()    {        queue<int> q;        for(int i=0; i<26; i++)            if(trie[0][i]) q.push(trie[0][i]);        while(!q.empty())        {            int u=q.front(); q.pop();            for(int i=0; i<26; i++)            {                if(trie[u][i])                {                    fail[trie[u][i]]=trie[fail[u]][i];                    q.push(trie[u][i]);                }else trie[u][i]=trie[fail[u]][i];            }        }        for(int u=1; u<=tot; u++)            for(int i=u; i; i=fail[i])                if(ed[i] && dep[i] <= 21) g[u]|=(1ll << (dep[i]-1));    }    int solve(char *s)    {        int l=strlen(s+1),u=0,x=0;        for(int i=1; i<=l; i++)        {            u=trie[u][s[i]-'a'];            x=((x<<1) | f[i-1]) & ((1ll<<21)-1);            f[i]=(x&g[u])!=0;        }        for(int i=l; i>=1; i--) if(f[i]) return i;        return 0;    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> (s+1);        tr.insert(s);    } tr.build(); f[0]=1;    while(m--)    {        cin >> (t+1);        cout << tr.solve(t) << '\n';    }    return 0;}
                    ]]> + 洛谷P2292 [HNOI2004] L 语言题解

                    题目链接:P2292[HNOI2004] L 语言

                    题意

                    标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的。现在你要处理的就是一段没有标点的文章。

                    一段文章 \(T\)是由若干小写字母构成。一个单词 \(W\)也是由若干小写字母构成。一个字典 \(D\)是若干个单词的集合。我们称一段文章 \(T\) 在某个字典 \(D\) 下是可以被理解的,是指如果文章 \(T\)可以被分成若干部分,且每一个部分都是字典 \(D\) 中的单词。

                    例如字典 \(D\) 中包括单词 \(\texttt{is},\texttt{name},\texttt{what},\texttt{your}\),则文章\(\texttt{whatisyourname}\) 是在字典\(D\) 下可以被理解的,因为它可以分成\(4\) 个单词:\(\texttt{what},\texttt{is},\texttt{your},\texttt{name}\),且每个单词都属于字典\(D\),而文章 \(\texttt{whatisyouname}\) 在字典 \(D\) 下不能被理解,但可以在字典 \(D'=D\cup\{\texttt{you}\}\)下被理解。这段文章的一个前缀 \(\texttt{whatis}\),也可以在字典 \(D\) 下被理解,而且是在字典 \(D\) 下能够被理解的最长的前缀。

                    给定一个字典 \(D\),你的程序需要判断若干段文章在字典 \(D\) 下是否能够被理解。并给出其在字典 \(D\) 下能够被理解的最长前缀的位置。

                    • 对于 \(100\%\) 的数据,保证 \(1 \leq n \leq 20\),\(1 \leq m \leq 50\),\(1 \leq |s| \leq 20\),\(1 \leq |t| \leq 2 \times 10^6\),\(s\) 与 \(t\) 中均只含小写英文字母。

                    \(f_i\) 表示前 \(i\) 个字符是否可行,则有 \[f_i = \bigvee\limits_{j-\max\{|s_i|\}}^{i-1} \left\{f_j\,\land\,t{[j+1,i]} \in S \right\}\] 其中 \(t[j+1,i]\)表示文本串的 \([j+1,i]\)\(S\) 表示所有 \(s_i\) 构成的不可重集

                    注意到 \(1\le |s_i| \le 20\),因此我们可以建 \(\tt{AC}\)自动机然后暴力转移。

                    具体地,我们把所有 \(s_i\) 都插入\(\tt{AC}\) 自动机,

                    然后预处理出每个结点在 \(\text{fail}\) 树上的 \(i (i \le 20)\)级祖先(认为儿子存在指向父亲的边)

                    这个信息可以压缩为一个二进制数,不妨设为 \(g_u\) 。

                    转移时,设当前所在的结点为 \(u\),同时维护 \(f_j(i-20 < j \le i)\)的状态。

                    后者又可以压到一个二进制数 \(x\)里,即 \(x\) 二进制下第 \(k(0\le k \le 20)\) 位为 \(1\) 表示 \(f_{i-k-1}=\text{True}\) 。

                    转移时,若 \(g_u \text{ and } x =\text{True}\) ,则 \(f_i=\text{True}\) 。这里的 \(\text{and}\) 表示二进制下按位与。

                    时间复杂度 \(O(20 \times \sum s_i + \sumt_i)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(405)#define M (int)(2e6+15)int n,m;bitset<M> f;char s[N],t[M];struct Trie{    int tot,trie[N][26],ed[N],dep[N],g[N],fail[N];    void insert(char *s)    {        int u=0,l=strlen(s+1);        for(int i=1; i<=l; i++)        {            int c=s[i]-'a';            if(!trie[u][c]) trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1; dep[u]=l;    }    void build()    {        queue<int> q;        for(int i=0; i<26; i++)            if(trie[0][i]) q.push(trie[0][i]);        while(!q.empty())        {            int u=q.front(); q.pop();            for(int i=0; i<26; i++)            {                if(trie[u][i])                {                    fail[trie[u][i]]=trie[fail[u]][i];                    q.push(trie[u][i]);                }else trie[u][i]=trie[fail[u]][i];            }        }        for(int u=1; u<=tot; u++)            for(int i=u; i; i=fail[i])                if(ed[i] && dep[i] <= 21) g[u]|=(1ll << (dep[i]-1));    }    int solve(char *s)    {        int l=strlen(s+1),u=0,x=0;        for(int i=1; i<=l; i++)        {            u=trie[u][s[i]-'a'];            x=((x<<1) | f[i-1]) & ((1ll<<21)-1);            f[i]=(x&g[u])!=0;        }        for(int i=l; i>=1; i--) if(f[i]) return i;        return 0;    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> (s+1);        tr.insert(s);    } tr.build(); f[0]=1;    while(m--)    {        cin >> (t+1);        cout << tr.solve(t) << '\n';    }    return 0;}
                    ]]> @@ -597,7 +597,7 @@ /2022/08/09/luo-gu-p1305-xin-er-cha-shu-ti-jie/ - 洛谷P1305 新二叉树 题解

                    题目链接:P1305 新二叉树

                    题意

                    输入一串二叉树,输出其前序遍历。

                    输入1

                    6abcbdicj*d**i**j**

                    输出1

                    abdicj

                    虽然是大水题,但是有个解法还是很妙的

                    利用前序遍历的性质,它的子树一定在它后面。

                    直接贴代码了,感觉很妙:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n;string s,t;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> s;    for(int i=2; i<=n; i++)    {        cin >> t;        int x=s.find(t[0]);        s.erase(x,1); s.insert(x,t);    }    for(int i=0; i<s.size(); i++)        if(s[i]!='*') cout << s[i];    return 0;}
                    ]]> + 洛谷P1305 新二叉树 题解

                    题目链接:P1305新二叉树

                    题意

                    输入一串二叉树,输出其前序遍历。

                    输入1

                    6abcbdicj*d**i**j**

                    输出1

                    abdicj

                    虽然是大水题,但是有个解法还是很妙的

                    利用前序遍历的性质,它的子树一定在它后面。

                    直接贴代码了,感觉很妙:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n;string s,t;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> s;    for(int i=2; i<=n; i++)    {        cin >> t;        int x=s.find(t[0]);        s.erase(x,1); s.insert(x,t);    }    for(int i=0; i<s.size(); i++)        if(s[i]!='*') cout << s[i];    return 0;}
                    ]]> @@ -622,7 +622,7 @@ /2022/08/09/luo-gu-p1229-bian-li-wen-ti-ti-jie/ - 洛谷P1229 遍历问题 题解

                    题目链接:P1229 遍历问题

                    题意

                    我们都很熟悉二叉树的前序、中序、后序遍历,在数据结构中常提出这样的问题:已知一棵二叉树的前序和中序遍历,求它的后序遍历,相应的,已知一棵二叉树的后序遍历和中序遍历序列你也能求出它的前序遍历。然而给定一棵二叉树的前序和后序遍历,你却不能确定其中序遍历序列,考虑如下图中的几棵二叉树:

                    所有这些二叉树都有着相同的前序遍历和后序遍历,但中序遍历却不相同。

                    输出可能的中序遍历序列的总数,结果不超过长整型数。

                    首先这个遍历方式都知道吧

                    • 前序遍历:根左右
                    • 后序遍历:左右根

                    等等,如果根只有一个儿子怎么办?

                    好问题,我们并不知道他到底是左儿子还是右儿子

                    根据乘法原理,设有 $k$ 个结点 $u_i$ 满足 $\left|\left\{v \mid v \in \text{son}(u)\right\}\right|=1$

                    则答案为 $2^k$ 。直接 $O(n^2)$ 跑一下就好啦!

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2333)int n,m,res;char a[N],b[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (a+1) >> (b+1); n=strlen(a+1); m=strlen(b+1);     for(int i=1; i<n; i++)        for(int j=2; j<=m; j++)            if(a[i]==b[j] && a[i+1]==b[j-1]) ++res;    cout << (1ull << res) << '\n';    return 0;}
                    ]]> + 洛谷P1229 遍历问题 题解

                    题目链接:P1229遍历问题

                    题意

                    我们都很熟悉二叉树的前序、中序、后序遍历,在数据结构中常提出这样的问题:已知一棵二叉树的前序和中序遍历,求它的后序遍历,相应的,已知一棵二叉树的后序遍历和中序遍历序列你也能求出它的前序遍历。然而给定一棵二叉树的前序和后序遍历,你却不能确定其中序遍历序列,考虑如下图中的几棵二叉树:

                    所有这些二叉树都有着相同的前序遍历和后序遍历,但中序遍历却不相同。

                    输出可能的中序遍历序列的总数,结果不超过长整型数。

                    首先这个遍历方式都知道吧

                    • 前序遍历:根左右
                    • 后序遍历:左右根

                    等等,如果根只有一个儿子怎么办?

                    好问题,我们并不知道他到底是左儿子还是右儿子

                    根据乘法原理,设有 \(k\) 个结点\(u_i\) 满足 \(\left|\left\{v \mid v \in\text{son}(u)\right\}\right|=1\)

                    则答案为 \(2^k\) 。直接 \(O(n^2)\) 跑一下就好啦!

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2333)int n,m,res;char a[N],b[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (a+1) >> (b+1); n=strlen(a+1); m=strlen(b+1);     for(int i=1; i<n; i++)        for(int j=2; j<=m; j++)            if(a[i]==b[j] && a[i+1]==b[j-1]) ++res;    cout << (1ull << res) << '\n';    return 0;}
                    ]]> @@ -647,7 +647,7 @@ /2022/08/09/luo-gu-p1983-noip2013-pu-ji-zu-che-zhan-fen-ji-ti-jie/ - 洛谷P1983 [NOIP2013 普及组] 车站分级 题解

                    题目链接:P1983 [NOIP2013 普及组] 车站分级

                    题意

                    一条单向的铁路线上,依次有编号为 $1, 2,\dots, n $ 的 $n$ 个火车站。每个火车站都有一个级别,最低为 $1$ 级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站 $x$,则始发站、终点站之间所有级别大于等于火车站 $x$ 的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)

                    例如,下表是 $5$ 趟车次的运行情况。其中,前$ 4$ 趟车次均满足要求,而第 $5$ 趟车次由于停靠了 $3$ 号火车站( $2$ 级)却未停靠途经的 $6$ 号火车站(亦为 $2$ 级)而不满足要求。

                    现有 $m$ 趟车次的运行情况(全部满足要求),试推算这 $n$ 个火车站至少分为几个不同的级别。

                    对于 $100\%$ 的数据,$1 \le n, m \le 1000$。

                    考虑建图。

                    对于一趟车,我们把所有「不停靠的站」向「停靠的站」连一条有向边

                    即边 $u \to v$ 表示 $u$ 的级别在 $v$ 之前。然后跑一遍 toposort 就好了

                    时间复杂度 $O(n^2)$ 感觉有点水,应该还可以加强的。

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)bitset<N> bi; queue<int> q;int n,m,pos,res,a[N],tmp[N],g[N][N],in[N],f[N];void topo(){    for(int i=1; i<=n; i++)        if(!in[i]) q.push(i);    fill(f+1,f+1+n,1);    while(!q.empty())    {        int u=q.front(); q.pop();        for(int i=1; i<=n; i++)        {            if(!g[u][i]) continue;            if(!(--in[i]))                f[i]=max(f[i],f[u]+1),q.push(i);        }    }    cout << *max_element(f+1,f+1+n) << '\n';}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,t; i<=m; i++)    {        bi=0; cin >> t;        for(int j=1; j<=t; j++)            cin >> a[j],bi[a[j]]=1;        for(int j=a[1]; j<=a[t]; j++)        {            if(bi[j]) continue;            for(int k=1; k<=t; k++)                if(!g[j][a[k]]) g[j][a[k]]=1,++in[a[k]];        }    } topo();    return 0;}
                    ]]> + 洛谷P1983 [NOIP2013普及组] 车站分级 题解

                    题目链接:P1983[NOIP2013 普及组] 车站分级

                    题意

                    一条单向的铁路线上,依次有编号为 $1, 2,, n $ 的 \(n\)个火车站。每个火车站都有一个级别,最低为 \(1\)级。现有若干趟车次在这条线路上行驶,每一趟都满足如下要求:如果这趟车次停靠了火车站\(x\),则始发站、终点站之间所有级别大于等于火车站\(x\)的都必须停靠。(注意:起始站和终点站自然也算作事先已知需要停靠的站点)

                    例如,下表是 \(5\)趟车次的运行情况。其中,前$ 4$ 趟车次均满足要求,而第 \(5\) 趟车次由于停靠了 \(3\) 号火车站( \(2\) 级)却未停靠途经的 \(6\) 号火车站(亦为 \(2\) 级)而不满足要求。

                    现有 \(m\)趟车次的运行情况(全部满足要求),试推算这 \(n\) 个火车站至少分为几个不同的级别。

                    对于 \(100\%\) 的数据,\(1 \le n, m \le 1000\)。

                    考虑建图。

                    对于一趟车,我们把所有「不停靠的站」向「停靠的站」连一条有向边

                    即边 \(u \to v\) 表示 \(u\) 的级别在 \(v\) 之前。然后跑一遍 toposort 就好了

                    时间复杂度 \(O(n^2)\)感觉有点水,应该还可以加强的。

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)bitset<N> bi; queue<int> q;int n,m,pos,res,a[N],tmp[N],g[N][N],in[N],f[N];void topo(){    for(int i=1; i<=n; i++)        if(!in[i]) q.push(i);    fill(f+1,f+1+n,1);    while(!q.empty())    {        int u=q.front(); q.pop();        for(int i=1; i<=n; i++)        {            if(!g[u][i]) continue;            if(!(--in[i]))                f[i]=max(f[i],f[u]+1),q.push(i);        }    }    cout << *max_element(f+1,f+1+n) << '\n';}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,t; i<=m; i++)    {        bi=0; cin >> t;        for(int j=1; j<=t; j++)            cin >> a[j],bi[a[j]]=1;        for(int j=a[1]; j<=a[t]; j++)        {            if(bi[j]) continue;            for(int k=1; k<=t; k++)                if(!g[j][a[k]]) g[j][a[k]]=1,++in[a[k]];        }    } topo();    return 0;}
                    ]]> @@ -674,7 +674,7 @@ /2022/08/09/mo-ni-sai-ti-jiang-jie-20/ - 模拟赛题讲解[20]

                    来自 AprilGrimoire 2022-08-09 noi.ac #2775

                    题目描述

                    小 Z 想出几道送分题。

                    小 Z 有一列脑洞(共 $m(m \leq 10^9)$ 个)。脑洞分为白色与黑色,白色脑洞会让题目难度降低,黑色脑洞会让题目难度提高。因为小 Z 很懒,她决定将脑洞分为连续的若干段,将每段缝合成一道送分题。为了达到雨露均沾的送分效果,小 Z 希望每段中黑色脑洞所占的比例相同。小 Z 想知道她最多能缝合出几道题?

                    输入格式

                    第一行一个整数 $n(n \leq 10^6)$

                    接下来 $n$ 行按顺序描述了小 Z 的脑洞。其中第 $i$ 行一个正整数 $r_i$ 和字符 $w_i$。这表示接下来有连续 $r_i$个颜色为 $w_i$ 的脑洞( W 代表白色,B 代表黑色)。

                    输出格式

                    一行一个整数,表示小 Z 最多能缝合出的题目数。

                    输入1

                    1 B1 W2 W2 B1 W1 B

                    输出1

                    3

                    输入2

                    1 W2 W1 B2 B1 W4 B1 B2 B1 B1 B

                    输出2

                    1

                    输入3

                    1 B1 B1 W1 W1 B1 B1 B1 W1 B1 W1 B1 B1 W1 W1 W1 B1 W1 B1 B1 B

                    输出3

                    3

                    数据规模与约定

                    时空限制:1s,512MiB

                    对于所有数据,保证 $n \leq 10^6$

                    子任务一(25pts):满足 $n \leq 20, r_i=1$

                    子任务二(25pts):满足 $n \leq 1000, r_i=1$

                    子任务三(20pts):满足 $n \leq 1000$

                    子任务四(30pts):无特殊约定

                    题解

                    注意到其实这个比例是固定的,我们直接暴力扫即可

                    也就是说,每一段的比例等于整个序列的比例

                    一个比较有趣的理解是,

                    DNA双链上,每一条链都有 $20\%$ 的 $A$ ,那么整个双链就有 $20\%$ 的 $A$

                    那就直接从左往右扫,然后每ok就立刻分为一段

                    这里用gcd什么的可以更精确,但是我选择double

                    时间复杂度 $O(n)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define double long doublenamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)const double eps=1e-13;int n,s1,s2,t1,t2,res=0;struct node{int x; char c;}a[N];int dcmp(double x){    if(fabs(x)<=eps) return 0;    return x>eps?1:-1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n; char ch;    for(int i=1; i<=n; i++)    {        cin >> a[i].x >> a[i].c;        if(a[i].c=='W') s1+=a[i].x;        if(a[i].c=='B') s2+=a[i].x;    }    if(!s1) return cout << s2 << '\n',0; // no w    if(!s2) return cout << s1 << '\n',0; // no b    double k=1.0*s1/s2;    for(int i=1; i<=n+1; i++)    {        if(t1&&t2&&!dcmp(1.0*t1/t2-k))            ++res,t1=t2=0;        if(a[i].c=='W') t1+=a[i].x;        else t2+=a[i].x;    }    cout << res << '\n';    return 0;}
                    ]]> + 模拟赛题讲解[20]

                    来自 AprilGrimoire2022-08-09 noi.ac #2775

                    题目描述

                    小 Z 想出几道送分题。

                    小 Z 有一列脑洞(共 \(m(m \leq10^9)\)个)。脑洞分为白色与黑色,白色脑洞会让题目难度降低,黑色脑洞会让题目难度提高。因为小Z很懒,她决定将脑洞分为连续的若干段,将每段缝合成一道送分题。为了达到雨露均沾的送分效果,小Z 希望每段中黑色脑洞所占的比例相同。小 Z想知道她最多能缝合出几道题?

                    输入格式

                    第一行一个整数 \(n(n \leq10^6)\)

                    接下来 \(n\) 行按顺序描述了小 Z的脑洞。其中第 \(i\) 行一个正整数 \(r_i\) 和字符 \(w_i\)。这表示接下来有连续 \(r_i\)个颜色为 \(w_i\) 的脑洞( W代表白色,B 代表黑色)。

                    输出格式

                    一行一个整数,表示小 Z 最多能缝合出的题目数。

                    输入1

                    1 B1 W2 W2 B1 W1 B

                    输出1

                    3

                    输入2

                    1 W2 W1 B2 B1 W4 B1 B2 B1 B1 B

                    输出2

                    1

                    输入3

                    1 B1 B1 W1 W1 B1 B1 B1 W1 B1 W1 B1 B1 W1 W1 W1 B1 W1 B1 B1 B

                    输出3

                    3

                    数据规模与约定

                    时空限制:1s,512MiB

                    对于所有数据,保证 \(n \leq10^6\)

                    子任务一(25pts):满足 \(n \leq 20,r_i=1\)

                    子任务二(25pts):满足 \(n \leq 1000,r_i=1\)

                    子任务三(20pts):满足 \(n \leq1000\)

                    子任务四(30pts):无特殊约定

                    题解

                    注意到其实这个比例是固定的,我们直接暴力扫即可

                    也就是说,每一段的比例等于整个序列的比例

                    一个比较有趣的理解是,

                    DNA双链上,每一条链都有 \(20\%\)\(A\) ,那么整个双链就有 \(20\%\) 的 \(A\)

                    那就直接从左往右扫,然后每ok就立刻分为一段

                    这里用gcd什么的可以更精确,但是我选择double

                    时间复杂度 \(O(n)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define double long doublenamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)const double eps=1e-13;int n,s1,s2,t1,t2,res=0;struct node{int x; char c;}a[N];int dcmp(double x){    if(fabs(x)<=eps) return 0;    return x>eps?1:-1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n; char ch;    for(int i=1; i<=n; i++)    {        cin >> a[i].x >> a[i].c;        if(a[i].c=='W') s1+=a[i].x;        if(a[i].c=='B') s2+=a[i].x;    }    if(!s1) return cout << s2 << '\n',0; // no w    if(!s2) return cout << s1 << '\n',0; // no b    double k=1.0*s1/s2;    for(int i=1; i<=n+1; i++)    {        if(t1&&t2&&!dcmp(1.0*t1/t2-k))            ++res,t1=t2=0;        if(a[i].c=='W') t1+=a[i].x;        else t2+=a[i].x;    }    cout << res << '\n';    return 0;}
                    ]]> @@ -701,7 +701,7 @@ /2022/08/08/mo-ni-sai-ti-jiang-jie-19/ - 模拟赛题讲解[19]

                    来自 yukuai26 2022-08-08 noi.ac #2771

                    题目背景

                    $\text{yukuai26}$ 喜欢爬山,所以他选择绕着山跑圈

                    题目描述

                    $\text{yukuai26}$ 会跑一个环形路径,每个路径有一个高度 $h$,他会顺时针或者逆时针沿着路径跑,一旦确定方向就不会换方向。

                    由于他喜欢爬山,所以如果前一次跑步时上坡,他希望下一次是下坡,同样的,如果前一次跑步时下坡,下一次他希望是上坡,并且他不希望重复经过同一地点,所以他跑过所有点时就会停下来。

                    现在 $\text{yukuai26}$ 想知道,他最多可以跑过多少点呢?

                    一句话题意,给你一个环,环上每个点有一个高度,你要找到其中最长的一条路径 $d_1,d_2….d_l$ , 满足 $d_i$和 $d_{i+1}$ 相邻且所有 $d_i$ 互不相同,且 $h_{d_1} \leq h_{d_2} \geq h_{d_3} \leq h_{d_4}…$或者 $h_{d_1} \geq h_{d_2} \leq h_{d_3} \geq h_{d_4}….$

                    问最长的 $l$ 是多少。

                    输入格式

                    第一行一个数 $n$ ,表示环形路径的长度

                    第二行 $n$ 个数 $h_i$ ,表示 $i$ 号点的高度

                    输出格式

                    一行一个数,表示最长的路径长度是多少

                    输入1

                    66 2 7 3 5 7

                    输出1

                    5

                    输入2

                    91 7 3 6 8 6 3 1 1

                    输出2

                    7

                    数据范围

                    对于 $50$% 的数据 $n \leq 100$

                    对于 $70$% 的数据 $n \leq 1000$

                    对于 $100$% 的数据 $n \leq 10^5$ , $h_i \leq 10^4$

                    题解

                    首先断环为链。

                    注意到以 $i$ 为起点的极大合法段,设其为 $[i,i+\text{sz}_i-1]$

                    不难发现 $\forall x \in [i,i+\text{sz}_i-1]$ ,$x$ 与 $i$ 重合的那种合法段是一样的。

                    这里的重合指的是以 $i$ 为起点的那个子段在 $x$ 处切一刀,分割线到 $i+\text{sz}_i-1$ 的那一段。

                    设 $f_{i,0/1}$ 表示以 $i$ 结尾的极大路径,下一个要朝上走还是朝下走

                    时间复杂度 $O(n)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,a[N],f[N][2];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i],a[i+n]=a[i];    f[1][0]=f[1][1]=1;    for(int i=2; i<=(n<<1); i++)    {        f[i][0]=f[i][1]=1;        if(a[i-1]<=a[i]) f[i][0]=max(f[i][0],f[i-1][1]+1);        if(a[i-1]>=a[i]) f[i][1]=max(f[i][1],f[i-1][0]+1);    }    int res=0;    for(int i=1; i<=(n<<1); i++)        res=max(res,max(f[i][0],f[i][1]));    cout << min(n,res) << '\n';    return 0;}
                    ]]> + 模拟赛题讲解[19]

                    来自 yukuai262022-08-08 noi.ac #2771

                    题目背景

                    \(\text{yukuai26}\)喜欢爬山,所以他选择绕着山跑圈

                    题目描述

                    \(\text{yukuai26}\)会跑一个环形路径,每个路径有一个高度 \(h\),他会顺时针或者逆时针沿着路径跑,一旦确定方向就不会换方向。

                    由于他喜欢爬山,所以如果前一次跑步时上坡,他希望下一次是下坡,同样的,如果前一次跑步时下坡,下一次他希望是上坡,并且他不希望重复经过同一地点,所以他跑过所有点时就会停下来。

                    现在 \(\text{yukuai26}\)想知道,他最多可以跑过多少点呢?

                    一句话题意,给你一个环,环上每个点有一个高度,你要找到其中最长的一条路径\(d_1,d_2....d_l\) , 满足 \(d_i\)和 \(d_{i+1}\) 相邻且所有 \(d_i\) 互不相同,且 \(h_{d_1} \leq h_{d_2} \geq h_{d_3} \leqh_{d_4}...\)或者 \(h_{d_1} \geq h_{d_2}\leq h_{d_3} \geq h_{d_4}....\)

                    问最长的 \(l\) 是多少。

                    输入格式

                    第一行一个数 \(n\),表示环形路径的长度

                    第二行 \(n\) 个数 \(h_i\) ,表示 \(i\) 号点的高度

                    输出格式

                    一行一个数,表示最长的路径长度是多少

                    输入1

                    66 2 7 3 5 7

                    输出1

                    5

                    输入2

                    91 7 3 6 8 6 3 1 1

                    输出2

                    7

                    数据范围

                    对于 \(50\)% 的数据 \(n \leq 100\)

                    对于 \(70\)% 的数据 \(n \leq 1000\)

                    对于 \(100\)% 的数据 \(n \leq 10^5\) , \(h_i \leq 10^4\)

                    题解

                    首先断环为链。

                    注意到以 \(i\)为起点的极大合法段,设其为 \([i,i+\text{sz}_i-1]\)

                    不难发现 \(\forall x \in[i,i+\text{sz}_i-1]\)\(x\)\(i\) 重合的那种合法段是一样的。

                    这里的重合指的是以 \(i\)为起点的那个子段在 \(x\)处切一刀,分割线到 \(i+\text{sz}_i-1\)的那一段。

                    \(f_{i,0/1}\) 表示以 \(i\)结尾的极大路径,下一个要朝上走还是朝下走 \[f_{i,0} = \max\{1,(f_{i-1,1}+1)\times [a_{i-1}\le a_i]\}\\f_{i,1} = \max\{1,(f_{i-1,0}+1)\times [a_{i-1}\ge a_i]\}\] 时间复杂度 \(O(n)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,a[N],f[N][2];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i],a[i+n]=a[i];    f[1][0]=f[1][1]=1;    for(int i=2; i<=(n<<1); i++)    {        f[i][0]=f[i][1]=1;        if(a[i-1]<=a[i]) f[i][0]=max(f[i][0],f[i-1][1]+1);        if(a[i-1]>=a[i]) f[i][1]=max(f[i][1],f[i-1][0]+1);    }    int res=0;    for(int i=1; i<=(n<<1); i++)        res=max(res,max(f[i][0],f[i][1]));    cout << min(n,res) << '\n';    return 0;}
                    ]]> @@ -728,7 +728,7 @@ /2022/08/08/mo-ni-sai-ti-jiang-jie-18/ - 模拟赛题讲解[18]

                    来自 yukuai26 2022-08-08 noi.ac #2773

                    题目背景

                    搞个大新闻. jpg

                    题目描述

                    你来到了幻想乡。首先你准备搞一个大新闻,获得文文的报道并变得出名,你决定对幻想乡的股票系统下手。

                    你通过猪脚光环了解到了接下来 $n$ 天的股票价格 $a_i$,你每天可以买一个股票或卖出一个股票或者什么都不做,一天只能干一件事。

                    那么为了搞个大事情,你希望赚的钱最多,那么你最多能获得多少钱呢? (注意到最终手上的股票不能算钱, 并且你可以暂时亏损)

                    输入格式

                    第一行一个整数 $n$,表示天数

                    接下来一行 $n$ 个数,表示每天股票的价格

                    输出格式

                    一行一个数,表示最大能挣的钱

                    输入1

                    910 5 4 7 9 12 6 2 10

                    输出1

                    20

                    输入2

                    203 1 8 1 7 9 5 6 5 3 5 8 2 7 9 3 2 3 8 4

                    输出2

                    41

                    数据范围

                    打包测试

                    测试包 1: $n \leq 15,~55$ 分

                    测试包 2: $n \leq 100,~a_i \leq 100 ,~10$分

                    测试包 3: $n \leq 5000 ,~20$分

                    测试包 4: $a_i \leq a_{i+1} ,~10$分

                    测试包 5: 无特殊限制 $5$ 分

                    对于 $100\%$ 的数据 $1 \leq a_i \leq 10^6,1 \leq n \leq 3\times 10^5$

                    题解

                    好可怕的贪心!QAQ

                    样例一某种程度上提示了我们,如何选择更好的一天卖出很重要

                    而很多时候我们只是不知道之后还有更好的机会卖出,显然反悔型贪心。

                    倒着考虑每天,维护一个可以卖的时候的大根堆

                    对于每个数 $a_i$ ,如果它比堆顶小,则把堆顶 $a_k$ 踢掉,代表在这一天买了股票,在堆顶卖出

                    并且, $a_i$ 也要入堆,因为不一定这一天买是最优的

                    当 $a_i$ 入堆以后,在未来某次决策中

                    如果有一个数 $a_j$ 加入,并踢掉了 $a_i$

                    这就等价于在 $a_j$ 买入, $a_i$ 卖出,再在 $a_i$ 买入,再在之前那个 $a_k$ 卖出

                    加粗的部分在实际决策时是不会做的,这个决策最后就等价于 $a_j$ 买 $a_k$ 卖

                    值得注意的时,此时我们还要把 $a_i$ 入堆,因为它也可以卖东西了(这个在代码里用 $\text{vis}$ 数组标记)

                    然后这题就搞定了,好难啊

                    时间复杂度 $O(n\log n)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e5+15)bitset<N> vis;int n,res,a[N];struct cmp{bool operator()(int x,int y){return a[x]<a[y];}};priority_queue<int,vector<int>,cmp> q;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    for(int i=n; i>=1; i--)    {        if(!q.empty())        {            int x=q.top();            if(a[i]<a[x])            {                q.pop(); res+=a[x]-a[i];                q.push(i); vis[i]=1;                if(vis[x]) vis[x]=0,q.push(x);            }else q.push(i);        }else q.push(i);    }    cout << res << '\n';    return 0;}
                    ]]> + 模拟赛题讲解[18]

                    来自 yukuai262022-08-08 noi.ac #2773

                    题目背景

                    搞个大新闻. jpg

                    题目描述

                    你来到了幻想乡。首先你准备搞一个大新闻,获得文文的报道并变得出名,你决定对幻想乡的股票系统下手。

                    你通过猪脚光环了解到了接下来 \(n\)天的股票价格 \(a_i\),你每天可以买一个股票或卖出一个股票或者什么都不做,一天只能干一件事。

                    那么为了搞个大事情,你希望赚的钱最多,那么你最多能获得多少钱呢?(注意到最终手上的股票不能算钱, 并且你可以暂时亏损)

                    输入格式

                    第一行一个整数 \(n\),表示天数

                    接下来一行 \(n\)个数,表示每天股票的价格

                    输出格式

                    一行一个数,表示最大能挣的钱

                    输入1

                    910 5 4 7 9 12 6 2 10

                    输出1

                    20

                    输入2

                    203 1 8 1 7 9 5 6 5 3 5 8 2 7 9 3 2 3 8 4

                    输出2

                    41

                    数据范围

                    打包测试

                    测试包 1: \(n \leq 15,~55\)

                    测试包 2: \(n \leq 100,~a_i \leq 100,~10\)

                    测试包 3: \(n \leq 5000 ,~20\)

                    测试包 4: \(a_i \leq a_{i+1},~10\)

                    测试包 5: 无特殊限制 \(5\)

                    对于 \(100\%\) 的数据 \(1 \leq a_i \leq 10^6,1 \leq n \leq 3\times10^5\)

                    题解

                    好可怕的贪心!QAQ

                    样例一某种程度上提示了我们,如何选择更好的一天卖出很重要

                    而很多时候我们只是不知道之后还有更好的机会卖出,显然反悔型贪心。

                    倒着考虑每天,维护一个可以卖的时候的大根堆

                    对于每个数 \(a_i\),如果它比堆顶小,则把堆顶 \(a_k\)踢掉,代表在这一天买了股票,在堆顶卖出

                    并且, \(a_i\)也要入堆,因为不一定这一天买是最优的

                    \(a_i\)入堆以后,在未来某次决策中

                    如果有一个数 \(a_j\) 加入,并踢掉了\(a_i\)

                    这就等价于在 \(a_j\) 买入,\(a_i\) 卖出,再在 \(a_i\) 买入,再在之前那个 \(a_k\) 卖出

                    加粗的部分在实际决策时是不会做的,这个决策最后就等价于 \(a_j\) 买 \(a_k\) 卖

                    值得注意的时,此时我们还要把 \(a_i\)入堆,因为它也可以卖东西了(这个在代码里用 \(\text{vis}\) 数组标记)

                    然后这题就搞定了,好难啊

                    时间复杂度 \(O(n\log n)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e5+15)bitset<N> vis;int n,res,a[N];struct cmp{bool operator()(int x,int y){return a[x]<a[y];}};priority_queue<int,vector<int>,cmp> q;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    for(int i=n; i>=1; i--)    {        if(!q.empty())        {            int x=q.top();            if(a[i]<a[x])            {                q.pop(); res+=a[x]-a[i];                q.push(i); vis[i]=1;                if(vis[x]) vis[x]=0,q.push(x);            }else q.push(i);        }else q.push(i);    }    cout << res << '\n';    return 0;}
                    ]]> @@ -755,7 +755,7 @@ /2022/08/08/luo-gu-p3243-hnoi2015-cai-yao-zhi-zuo-ti-jie/ - 洛谷P3243 [HNOI2015]菜肴制作 题解

                    题目链接:洛谷P3243 [HNOI2015]菜肴制作 题解

                    题意: $t$ 组数据,每组要做 $n$ 个菜,但是需要满足 $m$ 个限制。

                    每个限制形如 $(i,j)$ ,指菜肴 $i$ 要在菜肴 $j$ 之前做。(这里的 $i,j$ 都是编号)

                    在满足所有限制的前提下,要保证编号越小的菜肴越早做

                    对于每组数据,求出最优的菜肴制作顺序,或者输出 Impossible! 表示无解

                    $m$ 条限制中可能存在完全相同的。

                    $1\le t \le 3,~ 1 \le n,m \le 10^5$

                    显然建图然后topo,有环就无解。

                    但是怎么拓扑呢?字典序最小?

                    显然,这并不显然是错的

                    例: $(2,4),~(3,1)$

                    字典序最小的算出来的是 2,3,1,4 ,而答案是 3,1,2,4

                    原因在于字典序最小,不一定能保证 $1$ 尽可能在前面

                    即字典序最小,不一定保证编号越小的菜肴越早做

                    重新考虑怎么做

                    题目要求编号越小的菜肴越早做

                    因此标号越大的菜肴就要越晚做

                    则每个菜肴在它的合法范围内一定是放到最后面时最优

                    于是考虑建反图求字典序最大的拓扑序

                    时间复杂度 $O(tn)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <bitset>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)int n,m,pos=1,head[N],in[N],ans[N];struct Edge{int u,v,next;} e[N];priority_queue<int> q;void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos; ++in[v];}void topo(){    for(int i=n; i>=1; i--)        if(!in[i]) q.push(i);    int cnt=0;    while(!q.empty())    {        int u=q.top(); q.pop();        ans[n-(++cnt)+1]=u;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(!(--in[v])) q.push(v);        }    }    if(cnt!=n) return puts("Impossible!"),void(0);    for(int i=1; i<=n; i++)        write(ans[i]),pc(" \n"[i==n]);}void clear(){    pos=1;    for(int i=1; i<=n; i++)        head[i]=in[i]=0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; read(Q);    while(Q--)    {        clear(); read(n); read(m);        for(int i=1,u,v; i<=m; i++)        {            read(u); read(v);            addEdge(v,u);        } topo();    }    return 0;}
                    ]]> + 洛谷P3243 [HNOI2015]菜肴制作题解

                    题目链接:洛谷P3243[HNOI2015]菜肴制作 题解

                    题意\(t\)组数据,每组要做 \(n\)个菜,但是需要满足 \(m\) 个限制。

                    每个限制形如 \((i,j)\) ,指菜肴\(i\) 要在菜肴 \(j\) 之前做。(这里的 \(i,j\) 都是编号)

                    在满足所有限制的前提下,要保证编号越小的菜肴越早做

                    对于每组数据,求出最优的菜肴制作顺序,或者输出Impossible! 表示无解

                    \(m\)条限制中可能存在完全相同的。

                    \(1\le t \le 3,~ 1 \le n,m \le10^5\)

                    显然建图然后topo,有环就无解。

                    但是怎么拓扑呢?字典序最小?

                    显然,这并不显然是错的

                    例: \((2,4),~(3,1)\)

                    字典序最小的算出来的是 2,3,1,4 ,而答案是3,1,2,4

                    原因在于字典序最小,不一定能保证 \(1\) 尽可能在前面

                    即字典序最小,不一定保证编号越小的菜肴越早做

                    重新考虑怎么做

                    题目要求编号越小的菜肴越早做

                    因此标号越大的菜肴就要越晚做

                    则每个菜肴在它的合法范围内一定是放到最后面时最优

                    于是考虑建反图求字典序最大的拓扑序

                    时间复杂度 \(O(tn)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <bitset>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)int n,m,pos=1,head[N],in[N],ans[N];struct Edge{int u,v,next;} e[N];priority_queue<int> q;void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos; ++in[v];}void topo(){    for(int i=n; i>=1; i--)        if(!in[i]) q.push(i);    int cnt=0;    while(!q.empty())    {        int u=q.top(); q.pop();        ans[n-(++cnt)+1]=u;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(!(--in[v])) q.push(v);        }    }    if(cnt!=n) return puts("Impossible!"),void(0);    for(int i=1; i<=n; i++)        write(ans[i]),pc(" \n"[i==n]);}void clear(){    pos=1;    for(int i=1; i<=n; i++)        head[i]=in[i]=0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; read(Q);    while(Q--)    {        clear(); read(n); read(m);        for(int i=1,u,v; i<=m; i++)        {            read(u); read(v);            addEdge(v,u);        } topo();    }    return 0;}
                    ]]> @@ -784,7 +784,7 @@ /2022/08/08/mo-ni-sai-ti-jiang-jie-17/ - 模拟赛题讲解[17]

                    来自 yukuai26 2022-08-08 noi.ac #2772

                    题目描述

                    $\text{ysgh}$ 有一个长度为 $m$ 的,元素两两不同的序列。

                    $\text{emoairx}$ 想要知道这个序列里的元素及其顺序,她用一个神奇的望远镜可以看到序列里的一部分元素及其相对顺序。

                    他得到了很多组观测数据。

                    他想要知道这个序列应该是什么样的。

                    假如存在多组解,输出长度最小的解。

                    假如还是存在多组解,输出字典序最小的解。

                    数据保证有解。

                    输入格式

                    第一行一个正整数 $n$,表示有多少组观测数据

                    接下来 $n$ 行,每行第一个正整数 $k_{i}$ 表示第 $i$ 次观测观测到了多少个数据。

                    这行接下来 $k_{i}$ 个数描述序列中的 $k_{i}$ 个元素,需要保证原序列中的这几个元素一定要按照该顺序出现

                    输出格式

                    一行若干个正整数,描述长度最小前提下,字典序最小的合法的序列。

                    输入1

                    33 1 3 43 2 3 42 6 5

                    输出1

                    1 2 3 4 6 5

                    样例解释

                    诸如 2 1 3 4 6 51 2 3 4 6 5 7 也是合法的序列,但是它们长度和字典序不及 1 2 3 4 6 5

                    数据范围

                    对于 $60\%$ 的数据,满足 $n\le 10$

                    另存在 $20\%$ 数据,满足 $k_{i}=2$

                    对于前 $90\%$ 数据,满足若$\sum k_{i}\le 3\times 10^3$

                    对于 $100\%$ 的数据,满足 $k_{i}\ge 1,\sum k_{i}\le 3\times 10^5$ 保证读入的元素大小 $\leq 10^6$ , 并且所有读入的数都是正整数

                    题解

                    考虑建图,边 $u \to v$ 表示 $v$ 需要在 $u$ 后面

                    因为有解,所以图是个DAG

                    什么?DAG?直接topo!

                    因为要求一个字典序最小的拓扑序,因此用一个小根堆来维护

                    yukuai26: “有一位选手偏不这么干,他偏要倒过来,建反图求字典序最大的。”

                    yukuai26:“字典序是从前往后比的,不是从后往前比的。”

                    然后那位选手喜提10pts。不愧是我呐QAQ

                    然后老师在讲题时忘记了hack是什么

                    然后有一位叫 Gordonlu 的巨佬给出了如下hack Orz

                    显然答案应该是1 3 4 5 9 2

                    但是反过来会变成 1 9 2 3 4 5

                    我只能说,唉,我tcl

                    时间复杂度 $O(\sum k_i)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <bitset>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(3e5+15)#define M (int)(1e6+15)bitset<M> bi;int n,pos=1,head[M],tmp[N],p[N],in[M],ans[N];struct Edge{int u,v,next;} e[N];priority_queue< int,vector<int>,greater<int> > q;void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos; ++in[v];}void topo(){    sort(p+1,p+1+n);    for(int i=1; i<=n; i++)        if(!in[p[i]]) q.push(p[i]);    int cnt=0;    while(!q.empty())    {        int u=q.top(); q.pop();        ans[++cnt]=u;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(!(--in[v])) q.push(v);        }    }    for(int i=1; i<=n; i++)        write(ans[i]),pc(" \n"[i==n]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; read(Q);    for(int i=1,t; i<=Q; i++)    {        read(t);        for(int j=1; j<=t; j++)        {            read(tmp[j]);            if(!bi[tmp[j]]) bi[tmp[j]]=1,p[++n]=tmp[j];        }        for(int j=2; j<=t; j++) addEdge(tmp[j-1],tmp[j]);    }    topo();    return 0;}
                    ]]> + 模拟赛题讲解[17]

                    来自 yukuai262022-08-08 noi.ac #2772

                    题目描述

                    \(\text{ysgh}\) 有一个长度为 \(m\) 的,元素两两不同的序列。

                    \(\text{emoairx}\)想要知道这个序列里的元素及其顺序,她用一个神奇的望远镜可以看到序列里的一部分元素及其相对顺序。

                    他得到了很多组观测数据。

                    他想要知道这个序列应该是什么样的。

                    假如存在多组解,输出长度最小的解。

                    假如还是存在多组解,输出字典序最小的解。

                    数据保证有解。

                    输入格式

                    第一行一个正整数 \(n\),表示有多少组观测数据

                    接下来 \(n\) 行,每行第一个正整数\(k_{i}\) 表示第 \(i\) 次观测观测到了多少个数据。

                    这行接下来 \(k_{i}\)个数描述序列中的 \(k_{i}\)个元素,需要保证原序列中的这几个元素一定要按照该顺序出现

                    输出格式

                    一行若干个正整数,描述长度最小前提下,字典序最小的合法的序列。

                    输入1

                    33 1 3 43 2 3 42 6 5

                    输出1

                    1 2 3 4 6 5

                    样例解释

                    诸如 2 1 3 4 6 51 2 3 4 6 5 7也是合法的序列,但是它们长度和字典序不及 1 2 3 4 6 5

                    数据范围

                    对于 \(60\%\) 的数据,满足 \(n\le 10\)

                    另存在 \(20\%\) 数据,满足 \(k_{i}=2\)

                    对于前 \(90\%\) 数据,满足若\(\sum k_{i}\le 3\times 10^3\)

                    对于 \(100\%\) 的数据,满足 \(k_{i}\ge 1,\sum k_{i}\le 3\times 10^5\)保证读入的元素大小 \(\leq 10^6\) ,并且所有读入的数都是正整数

                    题解

                    考虑建图,边 \(u \to v\) 表示 \(v\) 需要在 \(u\) 后面

                    因为有解,所以图是个DAG

                    什么?DAG?直接topo!

                    因为要求一个字典序最小的拓扑序,因此用一个小根堆来维护

                    yukuai26:“有一位选手偏不这么干,他偏要倒过来,建反图求字典序最大的。”

                    yukuai26:“字典序是从前往后比的,不是从后往前比的。”

                    然后那位选手喜提10pts。不愧是我呐QAQ

                    然后老师在讲题时忘记了hack是什么

                    然后有一位叫 Gordonlu的巨佬给出了如下hack Orz

                    显然答案应该是1 3 4 5 9 2

                    但是反过来会变成 1 9 2 3 4 5

                    我只能说,唉,我tcl

                    时间复杂度 \(O(\sum k_i)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <bitset>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(3e5+15)#define M (int)(1e6+15)bitset<M> bi;int n,pos=1,head[M],tmp[N],p[N],in[M],ans[N];struct Edge{int u,v,next;} e[N];priority_queue< int,vector<int>,greater<int> > q;void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos; ++in[v];}void topo(){    sort(p+1,p+1+n);    for(int i=1; i<=n; i++)        if(!in[p[i]]) q.push(p[i]);    int cnt=0;    while(!q.empty())    {        int u=q.top(); q.pop();        ans[++cnt]=u;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(!(--in[v])) q.push(v);        }    }    for(int i=1; i<=n; i++)        write(ans[i]),pc(" \n"[i==n]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; read(Q);    for(int i=1,t; i<=Q; i++)    {        read(t);        for(int j=1; j<=t; j++)        {            read(tmp[j]);            if(!bi[tmp[j]]) bi[tmp[j]]=1,p[++n]=tmp[j];        }        for(int j=2; j<=t; j++) addEdge(tmp[j-1],tmp[j]);    }    topo();    return 0;}
                    ]]> @@ -813,7 +813,7 @@ /2022/08/07/mo-ni-sai-ti-jiang-jie-16/ - 模拟赛题讲解[16]

                    来自 yukuai26 2022-08-07 noi.ac #2764

                    题目描述

                    紫开始研究特殊的括号序列

                    这些特殊括号序列的形式是若干个右括号(可以没有)在前,若干个左括号(可以没有)在后。

                    比如 (()))))( 都是特殊的括号序列

                    显然刚开始给出的特殊的括号序列都不合法。

                    紫决定把所有序列首尾相连(按照紫想要的顺序排列),然后她再删除若干个括号,她希望最终删完括号的序列是一个合法的括号序列。

                    以下合法的括号序列定义

                    1、空串是一个合法的括号序列

                    2、若 $A,B$ 分别是合法的括号序列,则 $AB$ 是合法的括号序列

                    3、若 $A$ 是合法的括号序列,则 $(A)$ 是合法的括号序列

                    紫想要知道,她最少需要删除多少个括号。

                    输入格式

                    第一行一个正整数 n,表示有多少个括号序列

                    接下来 n 行,每行两个非负整数 $a_{i},b_{i}$

                    $a_{i}$ 表示第 $i$ 个括号序列的右括号数,$b_{i}$ 表示第 $i$ 个括号序列的左括号数

                    输出格式

                    一个整数表示最少需要删除多少个括号。

                    输入1

                    31 32 21 1

                    输出1

                    4

                    样例解释

                    最优的方案之一最终序列是 )((( )( ))((

                    左右各删两个即可

                    最优方案并不唯一

                    数据规模与约定

                    对于 $40\%$ 的数据,满足 $n\le 10$

                    对于 $60\%$ 的数据,满足 $n\le 20$

                    另存在 $20\%$ 数据,满足若 $a_{i}\le a_{j}$ 则 $b_{i}\ge b_{j}$

                    对于 $100\%$ 的数据,满足 $n\le 100000,0\le a_{i},b_{i}\le 10^9$

                    题解

                    这里先放个巨佬在考场代码里写的(有删改) Orz

                    /*升特殊的括号序列:a<b 平特殊的括号序列:a=b 降特殊的括号序列:a>b最优解必然是:升升....升平....平平降降....降而每次选择一定选升特殊的括号序列中a最小,平特殊的括号序列随便,降特殊的括号序列b最大(相当于反着操作) 根据贪心模拟括号判定 */ 

                    好的,开始我们的讲解

                    首先,别搞错了,(是左括号,) 是右括号,给出的是这样的 ))((((

                    考虑贪心。假如把所有的括号序列按照 $a_i > b_i$ 或 $a_i \le b_i$ 进行分类

                    则第二类一定会放在第一类的前面,否则一定不优。

                    因为 $a_i > b_i$ 的会让未匹配的左括号变少

                    反之,在末尾加入一个第二类的,会使未匹配的左括号数增多

                    此时第一类中的右括号无法匹配这些增加的左括号,显然这不优。

                    然后我们就可以考虑同一类中的选哪个了

                    显然我们会想尽可能早匹配掉左括号

                    每次加入一个第二类的,一定会导致未被匹配的左括号数增加 $x (x\in \mathbb{N})$ 个

                    那么这个结论有什么用呢,别急,后面会用到。

                    比起寄希望于后面第一类的右括号来匹配他们,不如直接在第二类内匹配掉尽可能多的

                    或者说,我们要最小化第二类中的未匹配的左括号

                    这等价于要最大化第二类中的匹配的右括号

                    我们刚刚说了,左括号的数量是单调不降的

                    而右括号是有可能匹配失败的,因为它前面可能没有左括号跟它匹配

                    于是,我们对于第二类的,直接按右括号数量 $a_i$ 从小到大排序就好了

                    同理,我们把上面的推导颠倒过来,可以退出第一类的情况是按左括号的数量 $b_i$ 从大到小排序。

                    时间复杂度 $O(n \log n)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,p1,p2,ans,now;char s[N];struct node{int l,r;}t1[N],t2[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v; i<=n; i++)    {        cin >> u >> v;        if(u>v) t2[++p2]={u,v};        else t1[++p1]={u,v};    }    sort(t1+1,t1+1+p1,[](node a,node b){return a.l<b.l;});    sort(t2+1,t2+1+p2,[](node a,node b){return a.r>b.r;});    int now=0;    for(int i=1; i<=p1; i++)    {        if(t1[i].l>now) ans+=t1[i].l-now;        now-=min(t1[i].l,now);        now+=t1[i].r;    }    for(int i=1; i<=p2; i++)    {        if(t2[i].l>now) ans+=t2[i].l-now;        now-=min(t2[i].l,now);        now+=t2[i].r;    }    cout << ans+now << '\n';    return 0;}
                    ]]> + 模拟赛题讲解[16]

                    来自 yukuai262022-08-07 noi.ac #2764

                    题目描述

                    紫开始研究特殊的括号序列

                    这些特殊括号序列的形式是若干个右括号(可以没有)在前,若干个左括号(可以没有)在后。

                    比如 (()))))(都是特殊的括号序列

                    显然刚开始给出的特殊的括号序列都不合法。

                    紫决定把所有序列首尾相连(按照紫想要的顺序排列),然后她再删除若干个括号,她希望最终删完括号的序列是一个合法的括号序列。

                    以下合法的括号序列定义

                    1、空串是一个合法的括号序列

                    2、若 \(A,B\)分别是合法的括号序列,则 \(AB\)是合法的括号序列

                    3、若 \(A\) 是合法的括号序列,则\((A)\) 是合法的括号序列

                    紫想要知道,她最少需要删除多少个括号。

                    输入格式

                    第一行一个正整数 n,表示有多少个括号序列

                    接下来 n 行,每行两个非负整数 \(a_{i},b_{i}\)

                    \(a_{i}\) 表示第 \(i\) 个括号序列的右括号数,\(b_{i}\) 表示第 \(i\) 个括号序列的左括号数

                    输出格式

                    一个整数表示最少需要删除多少个括号。

                    输入1

                    31 32 21 1

                    输出1

                    4

                    样例解释

                    最优的方案之一最终序列是 )((( )( ))((

                    左右各删两个即可

                    最优方案并不唯一

                    数据规模与约定

                    对于 \(40\%\) 的数据,满足 \(n\le 10\)

                    对于 \(60\%\) 的数据,满足 \(n\le 20\)

                    另存在 \(20\%\) 数据,满足若 \(a_{i}\le a_{j}\) 则 \(b_{i}\ge b_{j}\)

                    对于 \(100\%\) 的数据,满足 \(n\le 100000,0\le a_{i},b_{i}\le 10^9\)

                    题解

                    这里先放个巨佬在考场代码里写的(有删改) Orz

                    /*升特殊的括号序列:a<b 平特殊的括号序列:a=b 降特殊的括号序列:a>b最优解必然是:升升....升平....平平降降....降而每次选择一定选升特殊的括号序列中a最小,平特殊的括号序列随便,降特殊的括号序列b最大(相当于反着操作) 根据贪心模拟括号判定 */ 

                    好的,开始我们的讲解

                    首先,别搞错了,(是左括号,)是右括号,给出的是这样的 ))((((

                    考虑贪心。假如把所有的括号序列按照 \(a_i> b_i\)\(a_i \le b_i\)进行分类

                    则第二类一定会放在第一类的前面,否则一定不优。

                    因为 \(a_i > b_i\)的会让未匹配的左括号变少

                    反之,在末尾加入一个第二类的,会使未匹配的左括号数增多

                    此时第一类中的右括号无法匹配这些增加的左括号,显然这不优。

                    然后我们就可以考虑同一类中的选哪个了

                    显然我们会想尽可能早匹配掉左括号

                    每次加入一个第二类的,一定会导致未被匹配的左括号数增加 \(x (x\in \mathbb{N})\) 个

                    那么这个结论有什么用呢,别急,后面会用到。

                    比起寄希望于后面第一类的右括号来匹配他们,不如直接在第二类内匹配掉尽可能多的

                    或者说,我们要最小化第二类中的未匹配的左括号

                    这等价于要最大化第二类中的匹配的右括号

                    我们刚刚说了,左括号的数量是单调不降的

                    而右括号是有可能匹配失败的,因为它前面可能没有左括号跟它匹配

                    于是,我们对于第二类的,直接按右括号数量 \(a_i\) 从小到大排序就好了

                    同理,我们把上面的推导颠倒过来,可以退出第一类的情况是按左括号的数量\(b_i\) 从大到小排序。

                    时间复杂度 \(O(n \log n)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,p1,p2,ans,now;char s[N];struct node{int l,r;}t1[N],t2[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v; i<=n; i++)    {        cin >> u >> v;        if(u>v) t2[++p2]={u,v};        else t1[++p1]={u,v};    }    sort(t1+1,t1+1+p1,[](node a,node b){return a.l<b.l;});    sort(t2+1,t2+1+p2,[](node a,node b){return a.r>b.r;});    int now=0;    for(int i=1; i<=p1; i++)    {        if(t1[i].l>now) ans+=t1[i].l-now;        now-=min(t1[i].l,now);        now+=t1[i].r;    }    for(int i=1; i<=p2; i++)    {        if(t2[i].l>now) ans+=t2[i].l-now;        now-=min(t2[i].l,now);        now+=t2[i].r;    }    cout << ans+now << '\n';    return 0;}
                    ]]> @@ -838,7 +838,7 @@ /2022/08/07/cf1713a-traveling-salesman-problem-ti-jie/ - CF1713A Traveling Salesman Problem 题解

                    题目链接:CF1713 Traveling Salesman Problem

                    题意:$t$ 组数据,每组数据给定 $n$ 个在坐标轴上的点 $(x_i,y_i)$ ,求最小的路径满足

                    • 起点和终点均为 $(0,0)$
                    • 每一步只能走到 $(x+1,y),~(x-1,y),~(x,y+1),~(x,y-1)$四者中的一个

                    例如下图

                    $1 \le t \le 100,~1\le n \le 100,~-100 \le x_i,y_i \le 100$

                    考虑四个方向上距离 $(0,0)$ 最远的所构成的合法矩形(满足走法的矩形)

                    对矩形进行一定的伸缩,一定能走完所有的点,并且一定是最优的,

                    例如题目里给出的这个图,如果 $(0,1)$ 有个点,也是可以走到的

                    显然, $(0,0)$ 也可以走到,然后就没了

                    时间复杂度 $O(tn)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n;void solve(){    cin >> n;    int minx=0,miny=0,maxx=0,maxy=0;    for(int i=1,x,y; i<=n; i++)    {        cin >> x >> y;        minx=min(x,minx);        maxx=max(x,maxx);        miny=min(y,miny);        maxy=max(y,maxy);    }    cout << 2*(maxx+maxy-minx-miny) << '\n';}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; cin >> Q;    while(Q--) solve();    return 0;}
                    ]]> + CF1713A TravelingSalesman Problem 题解

                    题目链接:CF1713 TravelingSalesman Problem

                    题意\(t\)组数据,每组数据给定 \(n\)在坐标轴上的\((x_i,y_i)\) ,求最小的路径满足

                    • 起点和终点均为 \((0,0)\)
                    • 每一步只能走到 \((x+1,y),~(x-1,y),~(x,y+1),~(x,y-1)\)四者中的一个

                    例如下图

                    \(1 \le t \le 100,~1\le n \le 100,~-100 \lex_i,y_i \le 100\)

                    考虑四个方向上距离 \((0,0)\)最远的所构成的合法矩形(满足走法的矩形)

                    对矩形进行一定的伸缩,一定能走完所有的点,并且一定是最优的,

                    例如题目里给出的这个图,如果 \((0,1)\) 有个点,也是可以走到的

                    显然, \((0,0)\)也可以走到,然后就没了

                    时间复杂度 \(O(tn)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n;void solve(){    cin >> n;    int minx=0,miny=0,maxx=0,maxy=0;    for(int i=1,x,y; i<=n; i++)    {        cin >> x >> y;        minx=min(x,minx);        maxx=max(x,maxx);        miny=min(y,miny);        maxy=max(y,maxy);    }    cout << 2*(maxx+maxy-minx-miny) << '\n';}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; cin >> Q;    while(Q--) solve();    return 0;}
                    ]]> @@ -863,7 +863,7 @@ /2022/08/07/mo-ni-sai-ti-jiang-jie-15/ - 模拟赛题讲解[15]

                    来自 yukuai26 2022-08-07 noi.ac #2763

                    题目描述

                    幻想乡有 $n$ 个建筑,每个建筑里住了一些居民。

                    有一些双向道路连接居民的家,共有 $n-1$ 条道路,恰好让所有居民的家能够互相到达。

                    其中标号为 $i$ 的建筑居住了 $i$ 名幻想乡的居民。

                    现在居民想要互相拜访,但是一次拜访需要花费 $\text{dis}(u,v)$ 的代价,其中 $u,v$ 分别是两人所在的建筑,$\text{dis}$ 表示这两个建筑之间的最短路。

                    ⑨想要知道, 所有幻想乡人都拜访其他人所需要的代价之和。

                    答案对 $10^9+7$ 取模。

                    输入格式

                    第一行一个整数 $n$。

                    接下来 $n-1$ 行, 每行 $2$ 个整数 $n-1$ 个整数描述 $n-1$ 条道路。

                    输出格式

                    一个整数,表示总花费之和。

                    输入1

                    51 22 32 41 5

                    输出1

                    184

                    数据范围

                    对于 $30\%$ 的数据,满足 $n≤200$

                    对于 $60\%$ 的数据,满足 $n≤3000$

                    对于 $100\%$ 的数据,满足 $n≤1000000$

                    题解

                    直接去算是 $O(n^2)$ 的,显然这是不可接受的

                    注意到询问是问的总和,因此我们可以考虑每条边的贡献

                    考虑一条边会被经过几次,它就会贡献多少

                    显然会经过

                    其中 $S=\sum_{1\le i \le n} i,~~s_u = u+\sum_{v \in \text{son}(u)}v$

                    然后把每条边都加上就好了

                    时间复杂度 $O(n)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)const int p=1e9+7;int n,res,sum[N],S;vector<int> g[N];void add(int &x,int y){x=(x%p+y%p)%p;}void dfs(int u,int f){    sum[u]=u;    for(int v : g[u])    {        if(v==f) continue;        dfs(v,u);        add(sum[u],sum[v]);    }    add(res,sum[u]%p*(((S-sum[u])%p+p)%p)%p);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1,u,v; i<n; i++)    {        read(u); read(v);        g[u].push_back(v);        g[v].push_back(u);    }    for(int i=1; i<=n; i++) add(S,i);    dfs(1,1);    write((res%p+p)%p); pc('\n');    return 0;}
                    ]]> + 模拟赛题讲解[15]

                    来自 yukuai262022-08-07 noi.ac#2763

                    题目描述

                    幻想乡有 \(n\)个建筑,每个建筑里住了一些居民。

                    有一些双向道路连接居民的家,共有 \(n-1\)条道路,恰好让所有居民的家能够互相到达。

                    其中标号为 \(i\) 的建筑居住了 \(i\) 名幻想乡的居民。

                    现在居民想要互相拜访,但是一次拜访需要花费 \(\text{dis}(u,v)\) 的代价,其中 \(u,v\) 分别是两人所在的建筑,\(\text{dis}\)表示这两个建筑之间的最短路。

                    ⑨想要知道, 所有幻想乡人都拜访其他人所需要的代价之和。

                    答案对 \(10^9+7\) 取模。

                    输入格式

                    第一行一个整数 \(n\)

                    接下来 \(n-1\) 行, 每行 \(2\) 个整数 \(n-1\) 个整数描述 \(n-1\) 条道路。

                    输出格式

                    一个整数,表示总花费之和。

                    输入1

                    51 22 32 41 5

                    输出1

                    184

                    数据范围

                    对于 \(30\%\) 的数据,满足 \(n≤200\)

                    对于 \(60\%\) 的数据,满足 \(n≤3000\)

                    对于 \(100\%\) 的数据,满足 \(n≤1000000\)

                    题解

                    直接去算是 \(O(n^2)\)的,显然这是不可接受的

                    注意到询问是问的总和,因此我们可以考虑每条边的贡献

                    考虑一条边会被经过几次,它就会贡献多少

                    显然会经过 \[s_u \times (S-s_u)\] 其中 \(S=\sum_{1\le i \le n} i,~~s_u= u+\sum_{v \in \text{son}(u)}v\)

                    然后把每条边都加上就好了

                    时间复杂度 \(O(n)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)const int p=1e9+7;int n,res,sum[N],S;vector<int> g[N];void add(int &x,int y){x=(x%p+y%p)%p;}void dfs(int u,int f){    sum[u]=u;    for(int v : g[u])    {        if(v==f) continue;        dfs(v,u);        add(sum[u],sum[v]);    }    add(res,sum[u]%p*(((S-sum[u])%p+p)%p)%p);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1,u,v; i<n; i++)    {        read(u); read(v);        g[u].push_back(v);        g[v].push_back(u);    }    for(int i=1; i<=n; i++) add(S,i);    dfs(1,1);    write((res%p+p)%p); pc('\n');    return 0;}
                    ]]> @@ -890,7 +890,7 @@ /2022/08/06/mo-ni-sai-ti-jiang-jie-14/ - 模拟赛题讲解[14]

                    来自 yukuai26 2022-08-06 noi.ac #2755

                    题目背景

                    $\text{yukuai26}$ 喜欢算术,但他又菜又爱玩,所以需要你的帮助

                    题目描述

                    小明给你 $n$ 个正整数,他想取一些数加起来(可以取 $0$ 个数),问能得到的最大的能被 $3$ 整除的和是多少

                    输入格式

                    第一行一个整数 $n$

                    第二行 $n$ 个正整数, 表示小明给你的 $n$ 个正整数

                    输出格式

                    一个整数,表示最大的能被 $3$ 整除的和

                    输入1

                    41 2 3 4

                    输出1

                    9

                    输入2

                    3693 647 550

                    输出2

                    1890

                    数据范围

                    总共 $10$ 个测试点

                    对于测试点 $1,2$,$n \leq 3$

                    对于测试点 $3$,$n \leq 10$

                    对于测试点 $4,5$,保证小明给你的数只有 $3$ 的倍数

                    对于测试点 $6$,保证小明给的数对 $3$取模为 $0$ 或 $1$

                    对于测试点 $7$,$a_i \leq 100$

                    对于所有测试点 $n \leq 100000 ,a_i \leq 1000$

                    题解

                    解法一 贪心

                    先把所有的数都加上

                    那么最后的和模 $3$ 只有 $3$ 种情况

                    • $S\bmod 3=0$ 直接输出答案即可
                    • $S\bmod 3=1$ 去掉一个最小的模 $3$ 余 $1$ 的数即可
                    • $S\bmod 3=2$ 去掉「两个最小的模 $3$ 余 $1$ 的数」和「一个最小的模 $3$ 余 $1$ 的数」中的较小者即可

                    时间复杂度 $O(n\log n)$

                    代码没写,因为考场上dp过了

                    解法二 DP

                    对于每个数,我们可以看作重量为 $w_i = x \bmod 3,v_i=x$ 的物品

                    设 $f_{i,j}$ 表示只考虑前 $i$ 个物品,总和模 $3$ 为 $j$ 时的最大总和,则

                    然后就是简单的01背包啦

                    时间复杂度 $O(n)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,o,res,w[N],v[N],f[N][3];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,x; i<=n; i++)    {        cin >> x;        if(x%3==0) res+=x;        else ++o,w[o]=x%3,v[o]=x;    }    memset(f,0xc0,sizeof(f));    f[0][0]=0;    for(int i=0; i<o; i++)        for(int j=0; j<=2; j++)            f[i+1][(j+w[i+1])%3]=max(f[i][(j+w[i+1])%3],f[i][j]+v[i+1]);    cout << res+f[o][0] << '\n';    return 0;}
                    ]]> + 模拟赛题讲解[14]

                    来自 yukuai262022-08-06 noi.ac #2755

                    题目背景

                    \(\text{yukuai26}\)喜欢算术,但他又菜又爱玩,所以需要你的帮助

                    题目描述

                    小明给你 \(n\)个正整数,他想取一些数加起来(可以取 \(0\) 个数),问能得到的最大的能被 \(3\) 整除的和是多少

                    输入格式

                    第一行一个整数 \(n\)

                    第二行 \(n\) 个正整数,表示小明给你的 \(n\) 个正整数

                    输出格式

                    一个整数,表示最大的能被 \(3\)整除的和

                    输入1

                    41 2 3 4

                    输出1

                    9

                    输入2

                    3693 647 550

                    输出2

                    1890

                    数据范围

                    总共 \(10\) 个测试点

                    对于测试点 \(1,2\)\(n \leq 3\)

                    对于测试点 \(3\)\(n \leq 10\)

                    对于测试点 \(4,5\),保证小明给你的数只有 \(3\) 的倍数

                    对于测试点 \(6\),保证小明给的数对\(3\)取模为 \(0\) 或 \(1\)

                    对于测试点 \(7\)\(a_i \leq 100\)

                    对于所有测试点 \(n \leq 100000 ,a_i \leq1000\)

                    题解

                    解法一 贪心

                    先把所有的数都加上

                    那么最后的和模 \(3\) 只有 \(3\) 种情况

                    • \(S\bmod 3=0\)直接输出答案即可
                    • \(S\bmod 3=1\) 去掉一个最小的模\(3\)\(1\) 的数即可
                    • \(S\bmod 3=2\) 去掉「两个最小的模\(3\)\(1\) 的数」和「一个最小的模 \(3\) 余 \(1\) 的数」中的较小者即可

                    时间复杂度 \(O(n\log n)\)

                    代码没写,因为考场上dp过了

                    解法二 DP

                    对于每个数,我们可以看作重量为 \(w_i = x\bmod 3,v_i=x\) 的物品

                    \(f_{i,j}\) 表示只考虑前 \(i\) 个物品,总和模 \(3\) 为 \(j\) 时的最大总和,则 \[f_{i+1,(j+w_{i+1}) \,\bmod \,3} = \max\{f_{i,(j+w_{i+1}) \,\bmod\,3},f_{i,j}+v_{i+1}\}\] 然后就是简单的01背包啦

                    时间复杂度 \(O(n)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,o,res,w[N],v[N],f[N][3];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,x; i<=n; i++)    {        cin >> x;        if(x%3==0) res+=x;        else ++o,w[o]=x%3,v[o]=x;    }    memset(f,0xc0,sizeof(f));    f[0][0]=0;    for(int i=0; i<o; i++)        for(int j=0; j<=2; j++)            f[i+1][(j+w[i+1])%3]=max(f[i][(j+w[i+1])%3],f[i][j]+v[i+1]);    cout << res+f[o][0] << '\n';    return 0;}
                    ]]> @@ -917,7 +917,7 @@ /2022/08/06/mo-ni-sai-ti-jiang-jie-13/ - 模拟赛题讲解[13]

                    来自 yukuai26 2022-08-06 noi.ac #2757

                    据说是从JOI搬过来的,赛时有位巨佬18min就A了 Orz

                    题目描述

                    橙 —— 作为幽幽子大人的朋友的使者, 非常贪玩, 也喜欢思考有趣 的问题。

                    她在冥界思考一个有趣的问题, 希望赶到的灵梦能够解答。

                    一个字符串由 $\tt{a,b,c}$三种字母组成, 它最多能分成多少个 $\tt{abc}$ 和 $\tt{cbc}$ 的子序列呢?

                    具体的, 一个字符串 $str$ 可以由选择 $3$ 个位置 $i < j < k$ 使得 $s_i,s_j,s_k$ 依次连接组成 $\tt{abc}$ 或 $\tt{cbc}$ ,

                    这样算分出一个字符串, 接着把 $3$ 个位置上的字母删掉, 剩下的字母重新组成新的字符串并接着提取。

                    输入格式

                    第一行一个正整数 $n$ 表示字符串的长度

                    接下来一个长度为 $n$ 的字符串, 保证由 $\tt{a,b,c}$ 组成

                    输出格式

                    一行一个数,表示最大能分成的个数

                    输入1

                    6abcbcc

                    输出1

                    2

                    输入2

                    5abcbb

                    输出2

                    1

                    数据范围

                    打包测试

                    对于测试包 $1$,分值 $40$ 分,$n \leq 15$

                    对于测试包 $2$,分值 $30$ 分,$n \leq 50$

                    对于测试包 $3$,分值 $20$ 分,$n \leq 3000$

                    对于测试包 $4$,分值 $10$ 分,$n \leq 10^6$

                    题解

                    Subtask 1 40pts

                    注意到 $\tt{c}$ 可以作为结尾,也可以作为开头

                    考虑对于所有的 $\tt{c}$ 暴搜他作为第一个还是第三个

                    如果是第一个就变成 $\tt{a}$ ,计算即可。

                    Subtask 2 30pts

                    考虑从后往前做

                    设 $f_{i,j,k}$ 表示最后 $i$ 位,有 $j$ 个 $\tt{bc}$ ,$k$ 个 $\tt{c}$ ,最多能有多少 $\tt{abc}$ 或 $\tt{cbc}$ ,转移 $O(1)$。

                    Subtask 3 20pts

                    把所有作为 $\tt{abc}$ 和 $\tt{cbc}$ 末尾的 $\tt{c}$ 放在原串的最后面

                    具体地,如果串里面出现了

                    我们强制做如下匹配

                    这样的好处在于,对于合法的方案,一定存在一个分界线,满足

                    • 分界线左侧的所有 $\tt{c}$ 均可以看作 $\tt{a}$
                    • 分界线右侧仅由 $\tt{bc}$ 构成,并且与分界线左侧每个 $\tt{a/c}$ 两两配对

                    考虑枚举这个分界线,时间复杂度 $O(n^2)$

                    Subtask 4 10pts

                    注意到如果右侧的 $\tt{bc}$ 不够了,分界线理应往左移

                    如果左侧的 $\tt{c}$ 过多了,分界线理应往右移

                    实际上是存在单调性的。考虑二分分界线。

                    时间复杂度 $O(n \log n)$ ,可以通过此题

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n;bool vis[N];char s[N];bool check(int mid){    for(int i=1; i<=n; i++) vis[i]=0;    int cnt_a=0,cnt_b=0,cnt_c=0;    for(int i=n; i>=1; i--)    {        if(s[i]=='c'&&cnt_c<mid) vis[i]=1,++cnt_c;        else if(s[i]=='b'&&cnt_b<cnt_c) vis[i]=1,++cnt_b;    }    if(cnt_b < mid)return 0;    cnt_a=cnt_b=0;    for(int i=n; i>=1; i--)    {        if(s[i]=='b'){if(vis[i]) ++cnt_b; else continue;}        else if(!vis[i] && cnt_a < cnt_b) ++cnt_a;    }    return cnt_a==mid;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> (s+1);    int l=0,r=n/3,mid;    while(l<r)    {        mid=(l+r+1) >> 1;        if(check(mid)) l=mid;        else r=mid-1;    }    cout << l << '\n';    return 0;}

                    Extra 更快的解法

                    时间复杂度 $O(n)$ ,目前q779还不会,因此把老师的题解搬过来了

                    O(n)考虑从后往前做,记录bc和c的个数对于已经有bc时出现c的情况,先匹配成cbc,并记录这个c的位置对于没有c并且出现b的情况,也记录这个b的位置这时出现a并且没有bc,我们考虑反悔操作,把前面的cbc拆开变成abc,这时候会多出一个c,根据前面记录的b的位置与c的位置视情况会多出一个c或者bc

                    老师的代码:

                    #include<bits/stdc++.h>#define For(i,j,k) for (int i=(int)(j);i<=(int)(k);i++)#define Rep(i,j,k) for (int i=(int)(j);i>=(int)(k);i--)#define ll long longusing namespace std;const int N=1000005;int n,ans;char s[N];int f[N][3];int g[N][3];int main(){scanf("%d%s",&n,s+1);For(i,1,n){For(j,0,2) f[i][j]=f[i-1][j];if (s[i]=='a'||s[i]=='c') ++f[i][0];else if (f[i][0]) --f[i][0],++f[i][1];}Rep(i,n,1){For(j,0,2) g[i][j]=g[i+1][j];if (s[i]=='c') ++g[i][0];else if (s[i]=='b'){if (g[i][0]) --g[i][0],++g[i][1];}else{if (g[i][1]) --g[i][1],++g[i][2];}}For(i,0,n){int sum=g[i+1][2],v;v=min(f[i][0],g[i+1][1]);sum+=v; f[i][0]-=v; g[i+1][1]-=v;v=min(f[i][1],g[i+1][0]);sum+=v; f[i][1]-=v; g[i+1][0]-=v;v=min(f[i][1],g[i+1][1]);sum+=v; f[i][1]-=v; g[i+1][1]-=v;ans=max(ans,sum);}printf("%d\n",ans);}
                    ]]> + 模拟赛题讲解[13]

                    来自 yukuai262022-08-06 noi.ac #2757

                    据说是从JOI搬过来的,赛时有位巨佬18min就A了 Orz

                    题目描述

                    橙 ---- 作为幽幽子大人的朋友的使者, 非常贪玩, 也喜欢思考有趣的问题。

                    她在冥界思考一个有趣的问题, 希望赶到的灵梦能够解答。

                    一个字符串由 \(\tt{a,b,c}\)三种字母组成,它最多能分成多少个 \(\tt{abc}\)\(\tt{cbc}\) 的子序列呢?

                    具体的, 一个字符串 \(str\)可以由选择 \(3\) 个位置 \(i < j < k\) 使得 \(s_i,s_j,s_k\) 依次连接组成 \(\tt{abc}\) 或 \(\tt{cbc}\) ,

                    这样算分出一个字符串, 接着把 \(3\)个位置上的字母删掉, 剩下的字母重新组成新的字符串并接着提取。

                    输入格式

                    第一行一个正整数 \(n\)表示字符串的长度

                    接下来一个长度为 \(n\) 的字符串,保证由 \(\tt{a,b,c}\) 组成

                    输出格式

                    一行一个数,表示最大能分成的个数

                    输入1

                    6abcbcc

                    输出1

                    2

                    输入2

                    5abcbb

                    输出2

                    1

                    数据范围

                    打包测试

                    对于测试包 \(1\),分值 \(40\) 分,\(n \leq15\)

                    对于测试包 \(2\),分值 \(30\) 分,\(n \leq50\)

                    对于测试包 \(3\),分值 \(20\) 分,\(n \leq3000\)

                    对于测试包 \(4\),分值 \(10\) 分,\(n \leq10^6\)

                    题解

                    Subtask 1 40pts

                    注意到 \(\tt{c}\)可以作为结尾,也可以作为开头

                    考虑对于所有的 \(\tt{c}\)暴搜他作为第一个还是第三个

                    如果是第一个就变成 \(\tt{a}\),计算即可。

                    Subtask 2 30pts

                    考虑从后往前做

                    \(f_{i,j,k}\) 表示最后 \(i\) 位,有 \(j\) 个 \(\tt{bc}\) ,\(k\) 个 \(\tt{c}\) ,最多能有多少 \(\tt{abc}\) 或 \(\tt{cbc}\) ,转移 \(O(1)\)。

                    Subtask 3 20pts

                    把所有作为 \(\tt{abc}\)\(\tt{cbc}\) 末尾的 \(\tt{c}\) 放在原串的最后面

                    具体地,如果串里面出现了 \[\tt{a\dots b\dots c \dots c \dots b \dots c \dots}\] 我们强制做如下匹配 \[\tt{\color{red}{a} \,\color{black}{\dots} \,\color{red}{b} \,\color{black}{\dots} \,\color{blue}{c} \,\color{black}{\dots}\,\color{red}{c} \,\color{black}{\dots}\,\color{blue}{b}\, \color{black}{\dots} \,\color{blue}{c}\, \color{black}{\dots}}\,\]这样的好处在于,对于合法的方案,一定存在一个分界线,满足

                    • 分界线左侧的所有 \(\tt{c}\)均可以看作 \(\tt{a}\)
                    • 分界线右侧仅由 \(\tt{bc}\)构成,并且与分界线左侧每个 \(\tt{a/c}\)两两配对

                    考虑枚举这个分界线,时间复杂度 \(O(n^2)\)

                    Subtask 4 10pts

                    注意到如果右侧的 \(\tt{bc}\)不够了,分界线理应往左移

                    如果左侧的 \(\tt{c}\)过多了,分界线理应往右移

                    实际上是存在单调性的。考虑二分分界线。

                    时间复杂度 \(O(n \log n)\),可以通过此题

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n;bool vis[N];char s[N];bool check(int mid){    for(int i=1; i<=n; i++) vis[i]=0;    int cnt_a=0,cnt_b=0,cnt_c=0;    for(int i=n; i>=1; i--)    {        if(s[i]=='c'&&cnt_c<mid) vis[i]=1,++cnt_c;        else if(s[i]=='b'&&cnt_b<cnt_c) vis[i]=1,++cnt_b;    }    if(cnt_b < mid)return 0;    cnt_a=cnt_b=0;    for(int i=n; i>=1; i--)    {        if(s[i]=='b'){if(vis[i]) ++cnt_b; else continue;}        else if(!vis[i] && cnt_a < cnt_b) ++cnt_a;    }    return cnt_a==mid;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> (s+1);    int l=0,r=n/3,mid;    while(l<r)    {        mid=(l+r+1) >> 1;        if(check(mid)) l=mid;        else r=mid-1;    }    cout << l << '\n';    return 0;}

                    Extra 更快的解法

                    时间复杂度 \(O(n)\),目前q779还不会,因此把老师的题解搬过来了

                    O(n)考虑从后往前做,记录bc和c的个数对于已经有bc时出现c的情况,先匹配成cbc,并记录这个c的位置对于没有c并且出现b的情况,也记录这个b的位置这时出现a并且没有bc,我们考虑反悔操作,把前面的cbc拆开变成abc,这时候会多出一个c,根据前面记录的b的位置与c的位置视情况会多出一个c或者bc

                    老师的代码:

                    #include<bits/stdc++.h>#define For(i,j,k) for (int i=(int)(j);i<=(int)(k);i++)#define Rep(i,j,k) for (int i=(int)(j);i>=(int)(k);i--)#define ll long longusing namespace std;const int N=1000005;int n,ans;char s[N];int f[N][3];int g[N][3];int main(){scanf("%d%s",&n,s+1);For(i,1,n){For(j,0,2) f[i][j]=f[i-1][j];if (s[i]=='a'||s[i]=='c') ++f[i][0];else if (f[i][0]) --f[i][0],++f[i][1];}Rep(i,n,1){For(j,0,2) g[i][j]=g[i+1][j];if (s[i]=='c') ++g[i][0];else if (s[i]=='b'){if (g[i][0]) --g[i][0],++g[i][1];}else{if (g[i][1]) --g[i][1],++g[i][2];}}For(i,0,n){int sum=g[i+1][2],v;v=min(f[i][0],g[i+1][1]);sum+=v; f[i][0]-=v; g[i+1][1]-=v;v=min(f[i][1],g[i+1][0]);sum+=v; f[i][1]-=v; g[i+1][0]-=v;v=min(f[i][1],g[i+1][1]);sum+=v; f[i][1]-=v; g[i+1][1]-=v;ans=max(ans,sum);}printf("%d\n",ans);}
                    ]]> @@ -942,7 +942,7 @@ /2022/08/06/mo-ni-sai-ti-jiang-jie-12/ - 模拟赛题讲解[12]

                    来自 yukuai26 2022-08-06 noi.ac #2756

                    题目描述

                    曾经有一个 oj 叫做 bzoj, 里面有一题 bzoj1002 叫狼抓兔子

                    由于 1002 过于有名且显眼,很多人 A 掉了他,每 A 一次兔子就会少一只,兔子死亡惨重,救救兔子!

                    兔子为了反击策划了一次行动。

                    刚开始狼在二维平面上的 $(0,0)$ ,他希望走到 $(10^7,0)$,而有 n 只兔子分为处于位置 $(x_{i},y_{i})$。

                    当狼与某只兔子的欧几里得距离$\leq p$ 时($p$可以视为一个无限接近于 $0$ 的正实数, 事实上可以理解为狼和兔子非常非常接近),兔子就会干扰狼,这样狼会被干扰 $t$ 秒,

                    随后这只兔子完全任务就会离开。

                    兔子的目标是使得狼受到尽可能被更多的兔子干扰。

                    已知狼和兔子运动速度都为 1m/s。另外狼和兔子都知道所有动物的实时位置和实时速度方向。

                    现在围观的 yukuai26 想知道,假如兔子和狼都绝对聪明,狼会受到多少次兔子的干扰

                    输入格式

                    第一行两个非负整数 $n,t$ ,表示兔子的个数和每次眩晕的时间。

                    接下来 $n$ 行每行两个整数 $x_{i},y_{i}$ ,表示每只兔子的位置

                    输出格式

                    一个正整数表示答案

                    输入1

                    3 11 11 21000000000 0

                    输出1

                    2

                    样例解释

                    第一只兔子选择去 $(1,0)$ 处等狼,假如狼往右走就会经过 $(1,0)$ , 假如往上下走,那么兔子可以跟着一起上下移动, 时刻保持在狼右边。

                    所以狼必定被第一只兔子干扰。第一只兔子干扰狼 $1$ 秒,这 $1$ 秒时间可以让第二只兔子跑过来干扰。

                    但第三只兔子太远了,所以不可能干扰到狼。

                    数据规模与约定

                    对于前 $20$% 的数据,$n=1$

                    对于前 $40$% 的数据,$t=0$

                    对于另 $30$% 的数据,$x_{i}\ge 10^7$

                    对于 $100$% 的数据,$1 \le n \le 5\times 10^5,~-10^9 \leq x_{i},y_{i} \leq 10^9,t \le 10^3$

                    题解

                    一个很神奇的结论是,这些🐰有的去拦截🐺,不如直接在终点等着

                    然后就直接把所有🐰按距终点的距离排个序算算就好了

                    时间复杂度 $O(n\log n)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e5+15)#define pf(x) ((x)*(x))struct node{int x,y;} a[N];int n,t,c=1e7;int dis(node k){return pf(k.x-10000000)+pf(k.y);}bool cmp(node x,node y){return dis(x)<dis(y);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> t;    for(int i=1; i<=n; i++)        cin >> a[i].x >> a[i].y;    sort(a+1,a+1+n,cmp);    for(int i=1; i<=n; i++)    {        if(c*c>=dis(a[i])) c+=t;        else return cout << i-1,0;    }    cout << n << '\n';    return 0;}
                    ]]> + 模拟赛题讲解[12]

                    来自 yukuai262022-08-06 noi.ac #2756

                    题目描述

                    曾经有一个 oj 叫做 bzoj, 里面有一题 bzoj1002 叫狼抓兔子

                    由于 1002 过于有名且显眼,很多人 A 掉了他,每 A一次兔子就会少一只,兔子死亡惨重,救救兔子!

                    兔子为了反击策划了一次行动。

                    刚开始狼在二维平面上的 \((0,0)\),他希望走到 \((10^7,0)\),而有 n只兔子分为处于位置 \((x_{i},y_{i})\)。

                    当狼与某只兔子的欧几里得距离\(\leqp\) 时(\(p\)可以视为一个无限接近于 \(0\) 的正实数,事实上可以理解为狼和兔子非常非常接近),兔子就会干扰狼,这样狼会被干扰\(t\) 秒,

                    随后这只兔子完全任务就会离开。

                    兔子的目标是使得狼受到尽可能被更多的兔子干扰。

                    已知狼和兔子运动速度都为1m/s。另外狼和兔子都知道所有动物的实时位置和实时速度方向。

                    现在围观的 yukuai26想知道,假如兔子和狼都绝对聪明,狼会受到多少次兔子的干扰

                    输入格式

                    第一行两个非负整数 \(n,t\),表示兔子的个数和每次眩晕的时间。

                    接下来 \(n\) 行每行两个整数 \(x_{i},y_{i}\) ,表示每只兔子的位置

                    输出格式

                    一个正整数表示答案

                    输入1

                    3 11 11 21000000000 0

                    输出1

                    2

                    样例解释

                    第一只兔子选择去 \((1,0)\)处等狼,假如狼往右走就会经过 \((1,0)\), 假如往上下走,那么兔子可以跟着一起上下移动, 时刻保持在狼右边。

                    所以狼必定被第一只兔子干扰。第一只兔子干扰狼 \(1\) 秒,这 \(1\) 秒时间可以让第二只兔子跑过来干扰。

                    但第三只兔子太远了,所以不可能干扰到狼。

                    数据规模与约定

                    对于前 \(20\)% 的数据,\(n=1\)

                    对于前 \(40\)% 的数据,\(t=0\)

                    对于另 \(30\)% 的数据,\(x_{i}\ge 10^7\)

                    对于 \(100\)% 的数据,\(1 \le n \le 5\times 10^5,~-10^9 \leq x_{i},y_{i}\leq 10^9,t \le 10^3\)

                    题解

                    一个很神奇的结论是,这些🐰有的去拦截🐺,不如直接在终点等着

                    然后就直接把所有🐰按距终点的距离排个序算算就好了

                    时间复杂度 \(O(n\log n)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e5+15)#define pf(x) ((x)*(x))struct node{int x,y;} a[N];int n,t,c=1e7;int dis(node k){return pf(k.x-10000000)+pf(k.y);}bool cmp(node x,node y){return dis(x)<dis(y);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> t;    for(int i=1; i<=n; i++)        cin >> a[i].x >> a[i].y;    sort(a+1,a+1+n,cmp);    for(int i=1; i<=n; i++)    {        if(c*c>=dis(a[i])) c+=t;        else return cout << i-1,0;    }    cout << n << '\n';    return 0;}
                    ]]> @@ -967,7 +967,7 @@ /2022/08/04/mo-ni-sai-ti-jiang-jie-11/ - 模拟赛题讲解[11]

                    来自 xpp 2022-08-04 noi.ac #2731

                    题目描述

                    xpp在玩祖玛游戏,他心血来潮想根据祖玛游戏出一个题。

                    给一个序列 $a_1,a_2,\dots,a_n$ ,你每次可以选择相同且相邻的三个数删除他们,如果序列中存在这样相同且相邻的三个数,xpp将不断将他们删除,xpp想知道最后序列中还剩几个数呢?

                    输入格式

                    第一行一个正整数 $T(1\le T\le 10)$ 表示数据组数。

                    对于每组数据,第一行一个正整数 $n$ 表示序列长度,第二行 $n$ 个正整数表示 $a[1\dots n]$。

                    输出格式

                    对于每组数据输出一行,表示答案。

                    输入1

                    271 1 1 2 2 2 381 2 2 2 1 1 3 2

                    输出1

                    12

                    数据规模

                    对于 $30\%$ 的数据,$3\le n\le 10,~1\le a_i\le 10$

                    对于 $60\%$ 的数据,$3\le n\le 10^3,~1\le a_i\le 10^3$

                    对于 $100\%$ 的数据,$1\le a_i\le 10^9,~\sum n≤10^6$

                    题解

                    考虑用一个栈维护,满三个就不断的弹出

                    时间复杂度 $O(\sum n)$ 大水题

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)int stk[N],top;void solve(){    top=0; int n,res; read(n); res=n;    for(int i=1,x; i<=n; i++)    {        read(x);        stk[++top]=x;        while(top>=3 && stk[top]==stk[top-1]&&stk[top-1]==stk[top-2])            top-=3,res-=3;    }    write(res); pc('\n');}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; read(Q);    while(Q--) solve();    return 0;}
                    ]]> + 模拟赛题讲解[11]

                    来自 xpp 2022-08-04noi.ac #2731

                    题目描述

                    xpp在玩祖玛游戏,他心血来潮想根据祖玛游戏出一个题。

                    给一个序列 \(a_1,a_2,\dots,a_n\),你每次可以选择相同且相邻的三个数删除他们,如果序列中存在这样相同且相邻的三个数,xpp将不断将他们删除,xpp想知道最后序列中还剩几个数呢?

                    输入格式

                    第一行一个正整数 \(T(1\le T\le 10)\)表示数据组数。

                    对于每组数据,第一行一个正整数 \(n\)表示序列长度,第二行 \(n\) 个正整数表示\(a[1\dots n]\)

                    输出格式

                    对于每组数据输出一行,表示答案。

                    输入1

                    271 1 1 2 2 2 381 2 2 2 1 1 3 2

                    输出1

                    12

                    数据规模

                    对于 \(30\%\) 的数据,\(3\le n\le 10,~1\le a_i\le 10\)

                    对于 \(60\%\) 的数据,\(3\le n\le 10^3,~1\le a_i\le 10^3\)

                    对于 \(100\%\) 的数据,\(1\le a_i\le 10^9,~\sum n≤10^6\)

                    题解

                    考虑用一个栈维护,满三个就不断的弹出

                    时间复杂度 \(O(\sum n)\) 大水题

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)int stk[N],top;void solve(){    top=0; int n,res; read(n); res=n;    for(int i=1,x; i<=n; i++)    {        read(x);        stk[++top]=x;        while(top>=3 && stk[top]==stk[top-1]&&stk[top-1]==stk[top-2])            top-=3,res-=3;    }    write(res); pc('\n');}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; read(Q);    while(Q--) solve();    return 0;}
                    ]]> @@ -994,7 +994,7 @@ /2022/08/03/mo-ni-sai-ti-jiang-jie-10/ - 模拟赛题讲解[10]

                    来自 xpp 2022-08-03 noi.ac #2723

                    题目描述

                    lzr给了xpp一个字符串 $s$ 和一个字符串 $t$ ,xpp想通过 $s$ 构造出 $t$ ,构造方式为xpp每次选择 $s$ 的一个子序列,放到当前串的后面,xpp想知道他需要构造多少次才能构造出 $t$ 呢?

                    输入格式

                    第一行一个数 $T$ 表示数据组数。

                    对于每组数据第一行一个字符串 $s$ ,第二行一个字符串 $t$ ,含义见题目描述

                    输出格式

                    每组数据输出一行表示次数。如果不能输出 $-1$。

                    输入1

                    3aabceaceabacabaaaxtyyyt

                    输出1

                    1-13

                    数据规模

                    对于 $30\%$ 的数据, $\texttt{字符串长度} \le 100$

                    对于 $100\%$ 的数据,$\texttt{字符串长度} \le 10^5,~1\le T\le 10$

                    题解

                    暴力的解法就是遍历 $t$ ,然后维护一个指针 $p$ 在 $s$ 上跳

                    仔细观察可以发现,如果 $s=\tt{aaabbbbbbc}$ , $t=\tt{ac}$

                    那么 $p$ 就会慢悠悠的从 $s$ 的第一个 $\tt{a}$ 一直跑到最后的 $\tt{c}$

                    考虑维护一个 $\text{nx}_{i,j}$ 表示 $i$ 后面出现的第一个字符 $j$ 的位置

                    这个东西的预处理还是很有趣的,我当时没想到怎么搞,然后就用二分草过了

                    for(int i=0; i<26; i++)    for(int j=1; j<=n+1; j++)        nx[i][j]=n+1;for(int i=0; i<26; i++)    for(int j=n; j>=1; j--)        nx[i][j]=(s[j]=='a'+i)?j:nx[i][j+1];

                    时间复杂度 $O(Qn)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,m,nx[26][N];char s[N],t[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q;cin >> Q;    while(Q--)    {        cin >> (s+1) >> (t+1);        n=strlen(s+1); m=strlen(t+1);        for(int i=0; i<26; i++)            for(int j=1; j<=n+1; j++)                nx[i][j]=n+1;        for(int i=0; i<26; i++)            for(int j=n; j>=1; j--)                nx[i][j]=(s[j]=='a'+i)?j:nx[i][j+1];        int p=1,ans=1;        for(int i=1; i<=m; i++)        {            int c=t[i]-'a';            if(nx[c][1]==n+1) {ans=-1; break;}            if(nx[c][p]==n+1) ++ans,p=1;            p=nx[c][p]+1;        }        cout << ans << '\n';    }    return 0;}

                    顺便附上我的考场代码(二分写法)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,m,p;vector<int> ck[27];string s,t;void solve(){    cin >> s >> t; p=-1; int res=0;    n=s.size(); m=t.size();    for(int i=0; i<26; i++) ck[i].clear();    for(int i=0; i<n; i++) ck[s[i]-'a'].push_back(i);    for(int i=0; i<m; i++)    {        int c=t[i]-'a';        if(ck[c].empty()) return cout << "-1\n",void(0);        int nx=upper_bound(ck[c].begin(),ck[c].end(),p)-ck[c].begin();        if(nx>=ck[c].size())        {            ++res;p=ck[c][0];        }        else p=ck[c][nx];        // cout << p << '\n';    }    cout << res+1 << '\n';}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; cin >> Q;    while(Q--) solve();    return 0;}
                    ]]> + 模拟赛题讲解[10]

                    来自 xpp 2022-08-03noi.ac #2723

                    题目描述

                    lzr给了xpp一个字符串 \(s\)和一个字符串 \(t\) ,xpp想通过 \(s\) 构造出 \(t\) ,构造方式为xpp每次选择 \(s\)的一个子序列,放到当前串的后面,xpp想知道他需要构造多少次才能构造出\(t\) 呢?

                    输入格式

                    第一行一个数 \(T\)表示数据组数。

                    对于每组数据第一行一个字符串 \(s\),第二行一个字符串 \(t\),含义见题目描述

                    输出格式

                    每组数据输出一行表示次数。如果不能输出 \(-1\)。

                    输入1

                    3aabceaceabacabaaaxtyyyt

                    输出1

                    1-13

                    数据规模

                    对于 \(30\%\) 的数据, \(\texttt{字符串长度} \le 100\)

                    对于 \(100\%\) 的数据,\(\texttt{字符串长度} \le 10^5,~1\le T\le10\)

                    题解

                    暴力的解法就是遍历 \(t\),然后维护一个指针 \(p\)\(s\) 上跳

                    仔细观察可以发现,如果 \(s=\tt{aaabbbbbbc}\) , \(t=\tt{ac}\)

                    那么 \(p\) 就会慢悠悠的从 \(s\) 的第一个 \(\tt{a}\) 一直跑到最后的 \(\tt{c}\)

                    考虑维护一个 \(\text{nx}_{i,j}\)表示 \(i\) 后面出现的第一个字符 \(j\) 的位置

                    这个东西的预处理还是很有趣的,我当时没想到怎么搞,然后就用二分草过了

                    for(int i=0; i<26; i++)    for(int j=1; j<=n+1; j++)        nx[i][j]=n+1;for(int i=0; i<26; i++)    for(int j=n; j>=1; j--)        nx[i][j]=(s[j]=='a'+i)?j:nx[i][j+1];

                    时间复杂度 \(O(Qn)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,m,nx[26][N];char s[N],t[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q;cin >> Q;    while(Q--)    {        cin >> (s+1) >> (t+1);        n=strlen(s+1); m=strlen(t+1);        for(int i=0; i<26; i++)            for(int j=1; j<=n+1; j++)                nx[i][j]=n+1;        for(int i=0; i<26; i++)            for(int j=n; j>=1; j--)                nx[i][j]=(s[j]=='a'+i)?j:nx[i][j+1];        int p=1,ans=1;        for(int i=1; i<=m; i++)        {            int c=t[i]-'a';            if(nx[c][1]==n+1) {ans=-1; break;}            if(nx[c][p]==n+1) ++ans,p=1;            p=nx[c][p]+1;        }        cout << ans << '\n';    }    return 0;}

                    顺便附上我的考场代码(二分写法)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,m,p;vector<int> ck[27];string s,t;void solve(){    cin >> s >> t; p=-1; int res=0;    n=s.size(); m=t.size();    for(int i=0; i<26; i++) ck[i].clear();    for(int i=0; i<n; i++) ck[s[i]-'a'].push_back(i);    for(int i=0; i<m; i++)    {        int c=t[i]-'a';        if(ck[c].empty()) return cout << "-1\n",void(0);        int nx=upper_bound(ck[c].begin(),ck[c].end(),p)-ck[c].begin();        if(nx>=ck[c].size())        {            ++res;p=ck[c][0];        }        else p=ck[c][nx];        // cout << p << '\n';    }    cout << res+1 << '\n';}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; cin >> Q;    while(Q--) solve();    return 0;}
                    ]]> @@ -1021,7 +1021,7 @@ /2022/08/02/cf553a-kyoya-and-colored-balls-ti-jie/ - CF553A Kyoya and Colored Balls 题解

                    题目链接:CF553A Kyoya and Colored Balls

                    题意

                    一个袋子中有 $n$ 个彩球,他们用 $k$ 种不同的颜色染色。颜色被从 $1$ 到 $k$ 编号。同一种颜色的球看成是一样的。现在从袋中一个一个的拿出球来,直到拿完所有的球。对于所有颜色为 $i(1 \le i \le k-1)$ 的球,他的最后一个球总是在编号比他大的球拿完之前拿完,问这样情况有多少种,答案对 $10^9+7$ 取模。

                    输入单组测试数据。 第一行给出一个整数 $k(1 \le k \le 1000)$,表示球的种类。 接下来 $k$ 行,每行一个整数 $c_i$,表示第 $i$ 种颜色的球有 $c_i$ 个 $(1 \le c_i \le 1000)$。 球的总数目不超过 $1000$。

                    发烧真的无聊所以就写写题目啥的吧23333

                    考虑依次插入每种颜色的球,不妨设现在要插入的球颜色为 $i$ ,有 $x_i$ 个

                    设 $n_i$ 为之前已经插入的球的数量,即

                    对于 $i$ 的最后一个球,一定是要放在序列的最后面的

                    剩下的 $x_i-1$ 个球可以随便在 $n_i-1$ 个空隙里面放,每个空隙可以放多个球

                    或者也可以认为,把剩下的 $x_i-1$ 个球分成 $n_i-1$ 个组,每个组可以为空

                    显然根据插板法可知共有 $\dbinom{n_i+x_i-1}{n_i}$ 种方案

                    因此总的答案就是

                    时间复杂度 $O(n^2)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)const int p=1e9+7;int n,k,res=1,C[N][N];void mul(int &x,int y){x=x*(y%p)%p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    for(int i=0; i<=1000; i++)    {        C[i][0]=1;        for(int j=1; j<=i; j++)            C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;    }    cin >> k;    for(int i=1,x; i<=k; i++)    {        cin >> x;        mul(res,C[n+x-1][n]);        n+=x;    }    cout << res << '\n';    return 0;}

                    不行了要命了。

                    ]]> + CF553A Kyoya and ColoredBalls 题解

                    题目链接:CF553AKyoya and Colored Balls

                    题意

                    一个袋子中有 \(n\) 个彩球,他们用\(k\) 种不同的颜色染色。颜色被从 \(1\) 到 \(k\)编号。同一种颜色的球看成是一样的。现在从袋中一个一个的拿出球来,直到拿完所有的球。对于所有颜色为\(i(1 \le i \le k-1)\)的球,他的最后一个球总是在编号比他大的球拿完之前拿完,问这样情况有多少种,答案对\(10^9+7\) 取模。

                    输入单组测试数据。 第一行给出一个整数 \(k(1\le k \le 1000)\),表示球的种类。 接下来 \(k\) 行,每行一个整数 \(c_i\),表示第 \(i\) 种颜色的球有 \(c_i\) 个 \((1 \lec_i \le 1000)\)。 球的总数目不超过 \(1000\)。

                    发烧真的无聊所以就写写题目啥的吧23333

                    考虑依次插入每种颜色的球,不妨设现在要插入的球颜色为 \(i\) ,有 \(x_i\) 个

                    \(n_i\)为之前已经插入的球的数量,即 \[n_i = \sum _{j=1}^{i-1} x_i\] 对于 \(i\)的最后一个球,一定是要放在序列的最后面的

                    剩下的 \(x_i-1\) 个球可以随便在\(n_i-1\)个空隙里面放,每个空隙可以放多个球

                    或者也可以认为,把剩下的 \(x_i-1\)个球分成 \(n_i-1\)个组,每个组可以为空

                    显然根据插板法可知共有 \(\dbinom{n_i+x_i-1}{n_i}\) 种方案

                    因此总的答案就是 \[\prod_{i=1}^{k}\dbinom{n_i+x_i-1}{n_i}\] 时间复杂度 \(O(n^2)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)const int p=1e9+7;int n,k,res=1,C[N][N];void mul(int &x,int y){x=x*(y%p)%p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    for(int i=0; i<=1000; i++)    {        C[i][0]=1;        for(int j=1; j<=i; j++)            C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;    }    cin >> k;    for(int i=1,x; i<=k; i++)    {        cin >> x;        mul(res,C[n+x-1][n]);        n+=x;    }    cout << res << '\n';    return 0;}

                    不行了要命了。

                    ]]> @@ -1048,7 +1048,7 @@ /2022/08/02/chu-sai-fu-xi/ - 初赛复习

                    施工中……

                    参考文献等等补上来。

                    IT发展历史

                    第一台计算机:ENIAC,1946年

                    应用:计算,数据储存处理,通信,辅助工作等

                    第一个程序员:Ada(女),有为此命名的程序语言

                    图灵奖(计算机),菲尔兹奖(数学),诺贝尔奖(物化生经济文学和平)

                    ACM(美国计算机学会)IEEE(电气电子工程师学会)CCF(懂得都懂)

                    图灵奖由ACM设立,获得图灵奖的华人学者只有姚期智一人,名称取自计算机科学的先驱、英国科学家艾伦·麦席森·图灵,是计算机界最负盛名、最崇高的一个奖项,有“计算机界的诺贝尔奖”之称。

                    计算机组成

                    冯诺依曼结构:

                    输入设备:将信息输入进计算机的是输入设备

                    输出设备:将计算机的信息输出给人的是输出设备

                    特例:触摸屏既是输入设备又是输出设备

                    CPU字长:32位/64位 (CPU一次能并行处理的二进制位数,一定是8的倍数)

                    位(Bit):一个01

                    字节(Byte):八个01

                    1PB=1024TB,1TB=1024GB,1GB=1024KB,1KB=1024Byte,1Byte=8Bit

                    小例子:

                    int a[10000005];

                    所占空间为 $(10^7+5)\times 4 /1024 /1024 \approx 38.15\texttt{ MB }$

                    内存:RAM(随机存储器),断电会丢失数据

                    CPU、GPU等固件上装配的ROM(只读存储器)所存储的数据无法被改写或删除,也不会因电源关闭而丢失

                    摩尔定律:18个月翻倍(很快就要失效了)

                    软件系统

                    操作系统Windows,DOS,UNIX,Linux,MacOS,Android,IOS等

                    应用软件:浏览器、办公软件、游戏等等

                    机器语言、汇编语言、高级语言

                    编码

                    ASCII编码(英文),BGK编码(早期中文,2字节),UTF-8(新&常用,目前3字节)

                    图片:黑白(01),24位彩色(RGB)

                    文件系统扩展名:

                    图片:jpg(jpeg),gif,png,tiff

                    文档:txt,docx,pdf

                    声音:mp3/视频:avi,rm,mp4

                    可执行文件:exe(Windows),无(Linux)

                    NOI系列

                    NOI:1984年

                    NOIP:1995~2019,2020~至今

                    可以带文具(不可草稿纸),水,衣服,证件

                    2022年之后只能用C++了

                    程序设计

                    编译:代码->可执行文件(机器码):C/C++,Pascal

                    解释:一行一行运行:Python,JacaScript,PHP,BASIC

                    特例:java不太好定义是编译执行的还是解释执行

                    时间复杂度:数据规模增长和运行时间增长的趋势

                    基本算法

                    排序算法

                    排序稳定性:元素相等时的相对顺序不改变,则排序算法具有稳定性。

                    排序算法平均时间复杂度最坏时间复杂度最好时间复杂度空间复杂度稳定性
                    冒泡排序$O(n^2)$$O(n^2)$$O(n)$$O(1)$yes
                    直接选择排序$O(n^2)$$O(n^2)$$O(n)$$O(1)$no
                    直接插入排序$O(n^2)$$O(n^2)$$O(n)$$O(1)$yes
                    快速排序$O(n \log n)$$O(n^2)$$O(n \log n)$$O(n \log n)$no
                    堆排序$O(n \log n)$$O(n \log n)$$O(n \log n)$$O(1)$no
                    希尔排序$O(n \log n$)$O(ns)$$O(n)$$O(1)$no
                    归并排序$O(n \log n)$$O(n \log n)$$O(n \log n)$$O(n)$yes
                    计数排序$O(n + k)$$O(n + k)$$O(n+k)$$O(n+k)$yes
                    基数排序$O(n+k)$$O(n+k)$$O(n+k)$$O(n+k)$yes

                    贪心

                    正确性比较难处理,一般考场上选择打表找规律(或者凭直觉

                    二分

                    二分答案要求答案在一定范围内具有“单调性”。

                    时间复杂度 $O(\log N)$ ,$N$ 表示二分的范围大小

                    递归&分治

                    递归的思想在于把原问题拆解成若干性质相同的子问题

                    与递归分治有关的时间复杂度分析:主定理

                    简单数据结构

                    好的我们来讲一讲树套树

                    出栈序列方案数:卡特兰数 $C(n) = \dfrac{1}{n+1} \dbinom{2n}{n}$

                    队列

                    队列:先进先出

                    链表

                    o->o->o->o

                    一种说法是:任意两个结点连通(无向图)且有唯一一条路径

                    二叉树

                    $n$ 个结点 $n-1$ 条边的连通图

                    每个结点的至多有两个子结点

                    满二叉树第 $i(i\ge 1)$ 层的结点个数为 $2^{i-1}$

                    $k(k\ge 1)$ 层的二叉树有 $2^{k}-1$ 个结点(证明显然)

                    前序遍历:根左右

                    中序遍历:左根右

                    后序遍历:左右根

                    表达式树

                    建表达式树的方法(这个是自己想的方法)

                    给定中缀表达式(就是1+1=2这种)

                    对于同一运算等级的,看作一个块递归

                    如果有多个,随便选一个分界线递归(那我肯定选最前面的)

                    a+b*c+d ,看作 ab*cd 三个同一运算等级的块

                    这里有三个,所以随便选一个,比如选第一个+

                    然后递归子树 b*c+d ,以此类推。

                    最后的树长这样(不唯一)

                    不难发现它的中序遍历就是中缀表达式。

                    好像有不少概念,到时候整理一个比较全的

                    这里写几个重点的:

                    完全图:两两都有连边的无向图

                    结点的度数:

                    • 有向图:入度(入边数量)+出度(出边数量)
                    • 无向图:与其相连的边的数量

                    重边:两个相同结点存在多条边,且方向相同(无向图)

                    自环:起点和终点相同的边与这个结点构成的环

                    简单图:无重边无自环

                    DAG(有向无环图):显然。


                    参考文献
                    [1] https://blog.csdn.net/pange1991/article/details/85460755

                    ]]> + 初赛复习

                    施工中......

                    参考文献等等补上来。

                    IT发展历史

                    第一台计算机:ENIAC,1946年

                    应用:计算,数据储存处理,通信,辅助工作等

                    第一个程序员:Ada(女),有为此命名的程序语言

                    图灵奖(计算机),菲尔兹奖(数学),诺贝尔奖(物化生经济文学和平)

                    ACM(美国计算机学会)IEEE(电气电子工程师学会)CCF(懂得都懂)

                    图灵奖由ACM设立,获得图灵奖的华人学者只有姚期智一人,名称取自计算机科学的先驱、英国科学家艾伦·麦席森·图灵,是计算机界最负盛名、最崇高的一个奖项,有“计算机界的诺贝尔奖”之称。

                    计算机组成

                    冯诺依曼结构:

                    输入设备:将信息输入进计算机的是输入设备

                    输出设备:将计算机的信息输出给人的是输出设备

                    特例:触摸屏既是输入设备又是输出设备

                    CPU字长:32位/64位(CPU一次能并行处理的二进制位数,一定是8的倍数)

                    位(Bit):一个01

                    字节(Byte):八个01

                    1PB=1024TB,1TB=1024GB,1GB=1024KB,1KB=1024Byte,1Byte=8Bit

                    小例子:

                    int a[10000005];

                    所占空间为 \((10^7+5)\times 4 /1024 /1024\approx 38.15\texttt{ MB }\)

                    内存:RAM(随机存储器),断电会丢失数据

                    CPU、GPU等固件上装配的ROM(只读存储器)所存储的数据无法被改写或删除,也不会因电源关闭而丢失

                    摩尔定律:18个月翻倍(很快就要失效了)

                    软件系统

                    操作系统Windows,DOS,UNIX,Linux,MacOS,Android,IOS等

                    应用软件:浏览器、办公软件、游戏等等

                    机器语言、汇编语言、高级语言

                    编码

                    ASCII编码(英文),BGK编码(早期中文,2字节),UTF-8(新&常用,目前3字节)

                    图片:黑白(01),24位彩色(RGB)

                    文件系统扩展名:

                    图片:jpg(jpeg),gif,png,tiff

                    文档:txt,docx,pdf

                    声音:mp3/视频:avi,rm,mp4

                    可执行文件:exe(Windows),无(Linux)

                    NOI系列

                    NOI:1984年

                    NOIP:19952019,2020至今

                    可以带文具(不可草稿纸),水,衣服,证件

                    2022年之后只能用C++了

                    程序设计

                    编译:代码->可执行文件(机器码):C/C++,Pascal

                    解释:一行一行运行:Python,JacaScript,PHP,BASIC

                    特例:java不太好定义是编译执行的还是解释执行

                    时间复杂度:数据规模增长和运行时间增长的趋势

                    基本算法

                    排序算法

                    排序稳定性:元素相等时的相对顺序不改变,则排序算法具有稳定性。

                    排序算法平均时间复杂度最坏时间复杂度最好时间复杂度空间复杂度稳定性
                    冒泡排序\(O(n^2)\)\(O(n^2)\)\(O(n)\)\(O(1)\)yes
                    直接选择排序\(O(n^2)\)\(O(n^2)\)\(O(n)\)\(O(1)\)no
                    直接插入排序\(O(n^2)\)\(O(n^2)\)\(O(n)\)\(O(1)\)yes
                    快速排序\(O(n \log n)\)\(O(n^2)\)\(O(n \log n)\)\(O(n \log n)\)no
                    堆排序\(O(n \log n)\)\(O(n \log n)\)\(O(n \log n)\)\(O(1)\)no
                    希尔排序\(O(n \log n\))\(O(ns)\)\(O(n)\)\(O(1)\)no
                    归并排序\(O(n \log n)\)\(O(n \log n)\)\(O(n \log n)\)\(O(n)\)yes
                    计数排序\(O(n + k)\)\(O(n + k)\)\(O(n+k)\)\(O(n+k)\)yes
                    基数排序\(O(n+k)\)\(O(n+k)\)\(O(n+k)\)\(O(n+k)\)yes

                    贪心

                    正确性比较难处理,一般考场上选择打表找规律(或者凭直觉

                    二分

                    二分答案要求答案在一定范围内具有“单调性”。

                    时间复杂度 \(O(\log N)\)\(N\) 表示二分的范围大小

                    递归&分治

                    递归的思想在于把原问题拆解成若干性质相同的子问题

                    与递归分治有关的时间复杂度分析:主定理

                    简单数据结构

                    好的我们来讲一讲树套树

                    出栈序列方案数:卡特兰数 \(C(n) =\dfrac{1}{n+1} \dbinom{2n}{n}\)

                    队列

                    队列:先进先出

                    链表

                    o->o->o->o

                    一种说法是:任意两个结点连通(无向图)且有唯一一条路径

                    二叉树

                    \(n\) 个结点 \(n-1\) 条边的连通图

                    每个结点的至多有两个子结点

                    满二叉树第 \(i(i\ge 1)\)层的结点个数为 \(2^{i-1}\)

                    \(k(k\ge 1)\) 层的二叉树有 \(2^{k}-1\) 个结点(证明显然)

                    前序遍历:根左右

                    中序遍历:左根右

                    后序遍历:左右根

                    表达式树

                    建表达式树的方法(这个是自己想的方法)

                    给定中缀表达式(就是1+1=2这种)

                    对于同一运算等级的,看作一个块递归

                    如果有多个,随便选一个分界线递归(那我肯定选最前面的)

                    a+b*c+d ,看作ab*cd三个同一运算等级的块

                    这里有三个,所以随便选一个,比如选第一个+

                    然后递归子树 b*c+d ,以此类推。

                    最后的树长这样(不唯一)

                    不难发现它的中序遍历就是中缀表达式。

                    好像有不少概念,到时候整理一个比较全的

                    这里写几个重点的:

                    完全图:两两都有连边的无向图

                    结点的度数:

                    • 有向图:入度(入边数量)+出度(出边数量)
                    • 无向图:与其相连的边的数量

                    重边:两个相同结点存在多条边,且方向相同(无向图)

                    自环:起点和终点相同的边与这个结点构成的环

                    简单图:无重边无自环

                    DAG(有向无环图):显然。


                    参考文献 [1] https://blog.csdn.net/pange1991/article/details/85460755

                    ]]> @@ -1073,7 +1073,7 @@ /2022/07/31/cf852b-neural-network-country-ti-jie/ - CF852B Neural Network country 题解

                    题目链接:CF852B Neural Network country

                    题意

                    给定一个包含一个出发点 $s$ ,终止点 $t$ 以及 $L$ 层 $V_1,V_2,\dots,V_L$ ,每层都包含 $N$ 个顶点的带边权有向分层图。从出发点 $s$ 到第一层 $V_1$ 中每个顶点都有一条带权边,从最后一层 $V_L$ 到终止点 $t$ 中每个顶点也都有一条带权边。对于所有 $1\le i<L$ ,第 $i$ 层的每个顶点到第 $i+1$ 层中的每个顶点都会连有一条带权有向边,且第 $i$ 层的每个顶点连出的边权完全相同,并且任意相邻两层之间所连边权完全相同。可以参照样例解释中链接内图示结合样例帮助理解。

                    现给出该分层图,你需要计算有多少种从 $s$ 走到 $t$ 的路径,使得路径上经过边权值和能被 $M$ 整除?由于答案可能过大,你需要将答案对 $10^9+7$ 取模后输出。

                    对于 $100\%$ 的数据,$1\le N\le 10^5,~2\le L\le 10^5,~2\le M\le 50,~0\le a_i,b_i,c_i<M$

                    感谢Roundgod老师的耐心指导,我还是太蠢了QAQ

                    这道题其实就是给了一个有特殊性质的分层图

                    然后用了 $s$ 和 $t$ 连上这个分层图

                    这个分层图的特殊性质就是

                    每个上一层的结点 $u_i$ 到下一层的结点 $v_i$ ,边权只由 $v_i$ 决定

                    因此对于分层图里面同一层的点,我们可以看作同一个点

                    每个点和下一层的点都有 $n$ 中走法,也就对应了路径和的 $O(n)$ 种变化

                    这种题的常见思路就是,算出不同路径和的答案

                    然后把能被 $M$ 整除的答案加上,就是最终答案

                    仔细思考一下,我们有没有必要记录整个路径和?

                    注意到 $M \le 50$ ,这说明路径和模 $M$ 的剩余系很小

                    而一条路径的边权和能否被 $M$ 整除,就等价于模 $M$ 等于 $0$

                    这样我们就可以很容易的设出dp状态了

                    这里我们只算分层图上的 $1\sim L-1$ 层,因为第 $L$ 层显然不满足特殊性质

                    因为第 $L$ 层每个点到 $t$ 的边权不同,我们要单独处理。

                    设 $f_{i,j}$ 表示走到分层图上的第 $i$ 层,从第一层出发,边权和模 $M$ 为 $j$ 的方案数

                    转移方程就是

                    其中 $A_{k,j}$ 表示存在 $k$ 向 $j$ 的转移,这个可以预处理出来

                    稍微整理一下,可得

                    可以发现这个柿子就是矩阵乘法的定义,只不过 $f$ 是个 $1 \times M$ 的矩阵

                    于是写成矩阵乘法的形式,就是

                    考虑矩阵快速幂计算

                    实际上的答案是 $A \times B^{L-2} \times C$(和上面写的稍微有些区别)

                    分别对应 $s \to V_1,V_1 \to V_{L-1},V_{L-1}\to t$

                    时间复杂度 $O(M^3 \log n)$

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)const int mod=1e9+7;typedef vector<int> vec;typedef vector<vec> mat;void add(int &x,int y){x+=y%mod; if(x>=mod)x-=mod;}mat mul(mat &A,mat &B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vec(p));    for(int k=0; k<m; k++)        for(int i=0; i<n; i++)            for(int j=0; j<p; j++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int k){    int n=A.size();     mat B(n,vec(n));    for(int i=0; i<n; i++) B[i][i]=1;    while(k)    {        if(k&1) B=mul(B,A);        A=mul(A,A); k>>=1;    }    return B;}void print(mat &A){    int n=A.size(),m=A[0].size();    for(int i=0; i<n; i++)        for(int j=0; j<m; j++)            cout << A[i][j] << " \n"[j==m-1];}int n,m,l,a[N],b[N],c[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> l >> m;    for(int i=0; i<n; i++) cin >> a[i];    for(int i=0; i<n; i++) cin >> b[i];    for(int i=0; i<n; i++) cin >> c[i];    mat A(m,vec(m)),B(m,vec(m)),C(m,vec(m));    for(int i=0; i<n; i++) ++A[0][a[i]%m];    for(int i=1; i<m; i++)        for(int j=0; j<m; j++)            A[i][j]=A[0][(j-i+m)%m];    for(int i=0; i<n; i++) ++C[0][(c[i]+b[i])%m];    for(int i=1; i<m; i++)        for(int j=0; j<m; j++)            C[i][j]=C[0][(j-i+m)%m];    for(int i=0; i<n; i++) ++ B[0][b[i]%m];    for(int i=1; i<m; i++)        for(int j=0; j<m; j++)            B[i][j]=B[0][(j-i+m)%m];    B=qpow(B,l-2); A=mul(A,B); A=mul(A,C);    cout << A[0][0] << '\n';    return 0;}
                    ]]> + CF852B Neural Networkcountry 题解

                    题目链接:CF852BNeural Network country

                    题意

                    给定一个包含一个出发点 \(s\),终止点 \(t\) 以及 \(L\) 层 \(V_1,V_2,\dots,V_L\) ,每层都包含 \(N\) 个顶点的带边权有向分层图。从出发点\(s\) 到第一层 \(V_1\) 中每个顶点都有一条带权边,从最后一层\(V_L\) 到终止点 \(t\) 中每个顶点也都有一条带权边。对于所有\(1\le i<L\) ,第 \(i\) 层的每个顶点到第 \(i+1\)层中的每个顶点都会连有一条带权有向边,且第 \(i\)层的每个顶点连出的边权完全相同,并且任意相邻两层之间所连边权完全相同。可以参照样例解释中链接内图示结合样例帮助理解。

                    现给出该分层图,你需要计算有多少种从 \(s\) 走到 \(t\) 的路径,使得路径上经过边权值和能被\(M\)整除?由于答案可能过大,你需要将答案对 \(10^9+7\) 取模后输出。

                    对于 \(100\%\) 的数据,\(1\le N\le 10^5,~2\le L\le 10^5,~2\le M\le 50,~0\lea_i,b_i,c_i<M\)

                    感谢Roundgod老师的耐心指导,我还是太蠢了QAQ

                    这道题其实就是给了一个有特殊性质的分层图

                    然后用了 \(s\)\(t\) 连上这个分层图

                    这个分层图的特殊性质就是

                    每个上一层的结点 \(u_i\)到下一层的结点 \(v_i\)边权只由 \(v_i\)决定

                    因此对于分层图里面同一层的点,我们可以看作同一个点

                    每个点和下一层的点都有 \(n\)中走法,也就对应了路径和的 \(O(n)\)种变化

                    这种题的常见思路就是,算出不同路径和的答案

                    然后把能被 \(M\)整除的答案加上,就是最终答案

                    仔细思考一下,我们有没有必要记录整个路径和?

                    注意到 \(M \le 50\) ,这说明路径和模\(M\) 的剩余系很小

                    而一条路径的边权和能否被 \(M\)整除,就等价于模 \(M\) 等于 \(0\)

                    这样我们就可以很容易的设出dp状态了

                    这里我们只算分层图上的 \(1\sim L-1\)层,因为第 \(L\)层显然不满足特殊性质

                    因为第 \(L\) 层每个点到 \(t\) 的边权不同,我们要单独处理。

                    \(f_{i,j}\) 表示走到分层图上的第\(i\) 层,从第一层出发,边权和模 \(M\) 为 \(j\) 的方案数

                    转移方程就是 \[f_{i,j}=\sum_{0\le k < M} f_{i-1,k} \times A_{k,j}\] 其中 \(A_{k,j}\) 表示存在\(k\)\(j\) 的转移,这个可以预处理出来

                    稍微整理一下,可得 \[f^{i}_j = \sum _{0 \le k < M} f^{i-1}_{k} \times A_{k,j}\] 可以发现这个柿子就是矩阵乘法的定义,只不过 \(f\) 是个 \(1\times M\) 的矩阵

                    于是写成矩阵乘法的形式,就是 \[f^k = f^{k-1} \times A = f^{k-2} \times A^2 = A^k\] 考虑矩阵快速幂计算

                    实际上的答案是 \(A \times B^{L-2} \timesC\)(和上面写的稍微有些区别)

                    分别对应 \(s \to V_1,V_1 \toV_{L-1},V_{L-1}\to t\)

                    时间复杂度 \(O(M^3 \log n)\)

                    代码:

                    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)const int mod=1e9+7;typedef vector<int> vec;typedef vector<vec> mat;void add(int &x,int y){x+=y%mod; if(x>=mod)x-=mod;}mat mul(mat &A,mat &B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vec(p));    for(int k=0; k<m; k++)        for(int i=0; i<n; i++)            for(int j=0; j<p; j++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int k){    int n=A.size();     mat B(n,vec(n));    for(int i=0; i<n; i++) B[i][i]=1;    while(k)    {        if(k&1) B=mul(B,A);        A=mul(A,A); k>>=1;    }    return B;}void print(mat &A){    int n=A.size(),m=A[0].size();    for(int i=0; i<n; i++)        for(int j=0; j<m; j++)            cout << A[i][j] << " \n"[j==m-1];}int n,m,l,a[N],b[N],c[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> l >> m;    for(int i=0; i<n; i++) cin >> a[i];    for(int i=0; i<n; i++) cin >> b[i];    for(int i=0; i<n; i++) cin >> c[i];    mat A(m,vec(m)),B(m,vec(m)),C(m,vec(m));    for(int i=0; i<n; i++) ++A[0][a[i]%m];    for(int i=1; i<m; i++)        for(int j=0; j<m; j++)            A[i][j]=A[0][(j-i+m)%m];    for(int i=0; i<n; i++) ++C[0][(c[i]+b[i])%m];    for(int i=1; i<m; i++)        for(int j=0; j<m; j++)            C[i][j]=C[0][(j-i+m)%m];    for(int i=0; i<n; i++) ++ B[0][b[i]%m];    for(int i=1; i<m; i++)        for(int j=0; j<m; j++)            B[i][j]=B[0][(j-i+m)%m];    B=qpow(B,l-2); A=mul(A,B); A=mul(A,C);    cout << A[0][0] << '\n';    return 0;}
                    ]]> @@ -1100,7 +1100,7 @@ /2022/07/31/cf691e-xor-sequences-ti-jie/ - CF691E Xor-sequences 题解

            题目链接:CF691E Xor-sequences

            题意:给定大小为 $n$ 的集合 $\{a_1,\dots,a_n\}$,从集合中选择 $k$ 个数组成一个序列 $x_1 , \dots , x_k$ (可以重复选择),使得序列满足 $x_i$ 与 $x_i +1$ 异或的二进制中 $1$ 的个数是 $3$ 的倍数

            问长度为 $k$ 的满足条件的序列的种数,答案对 $10^9 + 7$取模。

            $1\le n\le 100,~1\le k,a_i \le 10^{18}$

            设 $f_{i,j}$ 表示序列长度为 $i$ 且最后一个数为 $a_j$ 时的方案数,则

            其中 $g_{k,j}$ 表示 $\left[3 \mid (a_k \oplus a_j)_{\texttt{ 2进制下1的数量}}\right]$

            可以发现这个形式就是矩阵乘法的定义式

            考虑矩阵快速幂优化dp

            时间复杂度 $O(n^3 \log k)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()typedef vector< vector<int> > mat;const int mod=1e9+7;void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}mat mul(mat A,mat B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vector<int>(p));    for(int k=0; k<m; k++)        for(int i=0; i<n; i++)              for(int j=0; j<p; j++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int k){    int n=max(A.size(),A[0].size());     mat B(n,vector<int>(n));    for(int i=0; i<n; i++) B[i][i]=1;    while(k)    {        if(k&1) B=mul(B,A);        A=mul(A,A); k>>=1;    }    return B;}bool check(int x){    int res=0;    while(x)++res,x-=(x&(-x));    return res%3==0;}int n,k,a[115]; signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> k;    mat A(1,vector<int>(n)),B(n,vector<int>(n));    for(int i=0; i<n; i++)        cin >> a[i],A[0][i]=1;    for(int i=0; i<n; i++)        for(int j=i; j<n; j++)            if(check(a[i]^a[j]))                B[i][j]=B[j][i]=1;    B=qpow(B,k-1); A=mul(A,B);    int res=0;    for(int i=0; i<n; i++)        add(res,A[0][i]);    cout << res << '\n';    return 0;}
            ]]> + CF691E Xor-sequences 题解

            题目链接:CF691EXor-sequences

            题意:给定大小为 \(n\) 的集合 \(\{a_1,\dots,a_n\}\),从集合中选择 \(k\) 个数组成一个序列 \(x_1 , \dots , x_k\)(可以重复选择),使得序列满足 \(x_i\)\(x_i +1\) 异或的二进制中 \(1\) 的个数是 \(3\) 的倍数

            问长度为 \(k\)的满足条件的序列的种数,答案对 \(10^9 +7\)取模。

            \(1\le n\le 100,~1\le k,a_i \le10^{18}\)

            \(f_{i,j}\) 表示序列长度为 \(i\) 且最后一个数为 \(a_j\) 时的方案数,则 \[f_{0,j}=1\\\\f_{i,j}=\sum_{1\le k \le n}f_{i-1,k}\times g_{k,j}\] 其中 \(g_{k,j}\) 表示 \(\left[3 \mid (a_k \oplus a_j)_{\texttt{2进制下1的数量}}\right]\)

            可以发现这个形式就是矩阵乘法的定义式

            考虑矩阵快速幂优化dp

            时间复杂度 \(O(n^3 \log k)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()typedef vector< vector<int> > mat;const int mod=1e9+7;void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}mat mul(mat A,mat B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vector<int>(p));    for(int k=0; k<m; k++)        for(int i=0; i<n; i++)              for(int j=0; j<p; j++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int k){    int n=max(A.size(),A[0].size());     mat B(n,vector<int>(n));    for(int i=0; i<n; i++) B[i][i]=1;    while(k)    {        if(k&1) B=mul(B,A);        A=mul(A,A); k>>=1;    }    return B;}bool check(int x){    int res=0;    while(x)++res,x-=(x&(-x));    return res%3==0;}int n,k,a[115]; signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> k;    mat A(1,vector<int>(n)),B(n,vector<int>(n));    for(int i=0; i<n; i++)        cin >> a[i],A[0][i]=1;    for(int i=0; i<n; i++)        for(int j=i; j<n; j++)            if(check(a[i]^a[j]))                B[i][j]=B[j][i]=1;    B=qpow(B,k-1); A=mul(A,B);    int res=0;    for(int i=0; i<n; i++)        add(res,A[0][i]);    cout << res << '\n';    return 0;}
            ]]> @@ -1129,7 +1129,7 @@ /2022/07/30/atcoder-educational-dp-contest-r-walk-ti-jie/ - Atcoder Educational DP Contest R Walk 题解

            题目链接:Atcoder Educational DP Contest R Walk

            题意

            给一张 $N$ 个结点的有向简单图,给出邻接矩阵 $A$ ,求长度为 $K$ 的路径条数,答案对 $10^9+7$ 取模。

            $1 \le N \le 50,1\le K \le 10^{18}$ , $a_{i,j}$ is 0 or 1 ,$a_{i,i}=0$

            设 $f_{k,i,j}$ 表示 $i$ 到 $j$ 路径长为 $k$ 的方案数,则

            之所以不从 $k-t$ 转移,而从 $k-1$ 转移,是因为可能存在重复计数

            比如这张图

            就可能重复计数

            上面的式子可以进一步化简

            这个形式是不是很熟悉?

            考虑矩阵快速幂优化dp

            关于为什么可以用矩阵快速幂,这里解释一下

            由于矩阵乘法的定义为

            而这里的形式和矩阵乘法的定义完全一致

            因此我们可以看作三个矩阵的关系,即

            稍微化一化柿子,可得

            至此可知,答案就是

            时间复杂度 $O(n^3 \log k)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef vector< vector<int> > mat;#define N (int)()const int mod=1e9+7;void add(int &x,int y){x+=y; if(x>=mod) x-=mod;}mat mul(mat A,mat B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vector<int>(p));    for(int k=0; k<m; k++)        for(int i=0; i<n; i++)            for(int j=0; j<p; j++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int k){    int n=A.size();    mat B(n,vector<int>(n));    for(int i=0; i<A.size(); i++) B[i][i]=1;    while(k)    {        if(k&1) B=mul(B,A);        A=mul(A,A); k>>=1;    }    return B;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n,k; cin >> n >> k;    mat a(n,vector<int>(n));    for(int i=0; i<n; i++)        for(int j=0; j<n; j++)            cin >> a[i][j];    int res=0; a=qpow(a,k);    for(int i=0; i<n; i++)        for(int j=0; j<n; j++)            add(res,a[i][j]);    cout << res << '\n';    return 0;}
            ]]> + Atcoder EducationalDP Contest R Walk 题解

            题目链接:AtcoderEducational DP Contest R Walk

            题意

            给一张 \(N\)个结点的有向简单图,给出邻接矩阵 \(A\),求长度为 \(K\) 的路径条数,答案对\(10^9+7\) 取模。

            \(1 \le N \le 50,1\le K \le10^{18}\)\(a_{i,j}\) is 0 or1 ,\(a_{i,i}=0\)

            \(f_{k,i,j}\) 表示 \(i\) 到 \(j\) 路径长为 \(k\) 的方案数,则 \[f_{k,i,j}=\sum_{(s,j) \in E} f_{k-1,i,s}\] 之所以不从 \(k-t\) 转移,而从\(k-1\)转移,是因为可能存在重复计数

            比如这张图

            就可能重复计数

            上面的式子可以进一步化简 \[f_{k,i,j} = \sum_{1\le s \le n} f_{k-1,i,s}\times A_{s,j}\] 这个形式是不是很熟悉? \[f^{0}_{i,j} = A_{i,j}\\f^{k}_{i,j}=\sum f^{k-1}_{i,t}\times A_{t,j}\] 考虑矩阵快速幂优化dp

            关于为什么可以用矩阵快速幂,这里解释一下

            由于矩阵乘法的定义为 \[(AB)_{i,j}=\sum_{k=1}^{m}A_{i,k}B_{k,j}\quad i\in[1,n],j\in[1,p]\] 而这里的形式和矩阵乘法的定义完全一致

            因此我们可以看作三个矩阵的关系,即 \[f^k=f^{k-1}\times A\] 稍微化一化柿子,可得 \[f^k = f^{k-2} A^2 = \dots = A^k\] 至此可知,答案就是 \[\sum_{1\le i,j \le n}f^{k}_{i,j} = \sum_{1\le i,j \le n}A^{k}_{i,j}\] 时间复杂度 \(O(n^3 \logk)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef vector< vector<int> > mat;#define N (int)()const int mod=1e9+7;void add(int &x,int y){x+=y; if(x>=mod) x-=mod;}mat mul(mat A,mat B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vector<int>(p));    for(int k=0; k<m; k++)        for(int i=0; i<n; i++)            for(int j=0; j<p; j++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int k){    int n=A.size();    mat B(n,vector<int>(n));    for(int i=0; i<A.size(); i++) B[i][i]=1;    while(k)    {        if(k&1) B=mul(B,A);        A=mul(A,A); k>>=1;    }    return B;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n,k; cin >> n >> k;    mat a(n,vector<int>(n));    for(int i=0; i<n; i++)        for(int j=0; j<n; j++)            cin >> a[i][j];    int res=0; a=qpow(a,k);    for(int i=0; i<n; i++)        for(int j=0; j<n; j++)            add(res,a[i][j]);    cout << res << '\n';    return 0;}
            ]]> @@ -1158,7 +1158,7 @@ /2022/07/30/oi-shu-xue-zong-jie-ji-chu-gong-shi-jie-lun/ - OI数学总结-基础公式&结论

            施工中,咕咕咕…

            • $A(n) = 1 + \dfrac{1}{\sqrt{2}}+\dfrac{1}{\sqrt{3}}+\dots+\dfrac{1}{\sqrt{n}}$ ,积分逼近有 $A(n) = \Theta(\sqrt{n})$
            • $B(n) = 1+\sqrt{2}+\sqrt{3}+\dots\sqrt{n}$ ,积分逼近有 $B(n) = \Theta(n\sqrt{n})$
            • 调和级数求和 $C(n) = 1 + \dfrac{1}{2}+\dfrac{1}{3}+\dots+\dfrac{1}{n}$ ,积分逼近有 $C(n) = \Theta(\ln n)$

            等差数列前缀和:$S(n) = \dfrac{n(a_1+a_n)}{2} = na_1 + \dfrac{n(n-1)}{2}d$

            等比数列前缀和:$S(n) = \dfrac{a_1(1-q^n)}{1-q},q\ne 1$

            一些常见的前缀和

            • $\sum\limits_{i=1}^{n}i = \dfrac{n(n+1)}{2}$
            • $\sum\limits_{i=1}^{n}i^2 = \dfrac{n(n+1)(2n+1)}{6}$
            • $\sum\limits_{i=1}^{n}i^3 = \left(\dfrac{n(n+1)}{2}\right)^2$

            快速开平方(用手开平方的方法)

            正整数 $a$ 满足 $a^2\le x < (a+1)^2$

            极化恒等式

            向量旋转公式

            oi-wiki

            公式:(逆时针旋转角度 $\vartheta$ )

            简记为 $\tt{xC-yS,xS+yC}$

            ]]> + OI数学总结-基础公式&结论

            施工中,咕咕咕...

            • \(A(n) = 1 +\dfrac{1}{\sqrt{2}}+\dfrac{1}{\sqrt{3}}+\dots+\dfrac{1}{\sqrt{n}}\),积分逼近有 \(A(n) =\Theta(\sqrt{n})\)
            • \(B(n) =1+\sqrt{2}+\sqrt{3}+\dots\sqrt{n}\) ,积分逼近有 \(B(n) = \Theta(n\sqrt{n})\)
            • 调和级数求和 \(C(n) = 1 +\dfrac{1}{2}+\dfrac{1}{3}+\dots+\dfrac{1}{n}\) ,积分逼近有 \(C(n) = \Theta(\ln n)\)

            等差数列前缀和:\(S(n) =\dfrac{n(a_1+a_n)}{2} = na_1 + \dfrac{n(n-1)}{2}d\)

            等比数列前缀和:\(S(n) =\dfrac{a_1(1-q^n)}{1-q},q\ne 1\)

            一些常见的前缀和

            • \(\sum\limits_{i=1}^{n}i =\dfrac{n(n+1)}{2}\)
            • \(\sum\limits_{i=1}^{n}i^2 =\dfrac{n(n+1)(2n+1)}{6}\)
            • \(\sum\limits_{i=1}^{n}i^3 =\left(\dfrac{n(n+1)}{2}\right)^2\)

            快速开平方(用手开平方的方法) \[\begin{aligned}\sqrt{x}&= a+\dfrac{x-a^2}{\sqrt{x}+a} \\&\approxa+\dfrac{x-a^2}{2\times a}\end{aligned}\]

            正整数 \(a\) 满足 \(a^2\le x < (a+1)^2\)

            极化恒等式 \[\boldsymbol{a} \cdot \boldsymbol{b} = \dfrac{1}{4}\left(\left(\boldsymbol{a}+\boldsymbol{b}\right)^2-\left(\boldsymbol{a}-\boldsymbol{b}\right)^2\right)\] 向量旋转公式

            oi-wiki

            公式:(逆时针旋转角度 \(\vartheta\)\[\boldsymbol{a} = (x,y)\\\boldsymbol{b}=(x\cos\vartheta-y\sin\vartheta,x\sin\vartheta+y\cos\vartheta)\] 简记为 \(\tt{xC-yS,xS+yC}\)

            ]]> @@ -1183,7 +1183,7 @@ /2022/07/30/xian-xing-dai-shu-ju-zhen/ - 线性代数-矩阵

            施工中,咕咕咕….

            矩阵乘法

            矩阵乘法的定义

            矩阵乘法的定义

            一个 $n \times m$ 的矩阵和 $m\times p$ 的矩阵相乘,答案为一个 $n \times p$ 的矩阵

            快速手算

            之前我是把右矩阵逆时针转90度然后和第一个算的

            然后我算了几万年都没真正记住怎么算 QAQ

            现在新的算法,超好记的有没有qwqqqwqqwqqqwqwq

            例:

            第一行:

            第二行

            最后的答案

            特殊矩阵

            单位矩阵

            满足对于任意矩阵 $A$ 有

            矩阵乘法的性质

            矩阵乘法具有结合律

            矩阵乘法具有左右分配律

            矩阵乘法不一定具有交换律

            矩阵快速幂

            优化递推

            例:

            尝试构造矩阵使得

            显然 $M$ 是一个 $3\times 3$ 的矩阵,不难发现

            然后我们可以用 $M^{k-1}$ 快速计算 $f_{k}$

            常见数据范围:$10^{18}$

            板子题:P1962 斐波那契数列

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef vector< vector<int> > mat;#define N (int)()const int mod=1e9+7;void add(int &x,int y){x+=y; if(x>=mod)x-=mod;}// (n*m) x (m*p) = (n*p)mat mul(mat &A,mat &B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vector<int>(p));    for(int k=0; k<m; k++)        for(int i=0; i<n; i++)            for(int j=0; j<p; j++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int k){    int n=max(A.size(),A[0].size());     mat B(n,vector<int>(n));    for(int i=0; i<n; i++) B[i][i]=1;    while(k)    {        if(k&1) B=mul(B,A);        A=mul(A,A); k>>=1;    }    return B;}void print(mat &A){    int n=A.size(),m=A[0].size();    for(int i=0; i<n; i++)        for(int j=0; j<m; j++)            cout << A[i][j] << " \n"[j==m-1];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n; cin >> n;    if(n<=2) cout << "1";    else    {        mat A(2,vector<int>(2)),B(2,vector<int>(1));        A[0][0]=A[0][1]=A[1][0]=1;        B[0][0]=B[1][0]=1;        A=qpow(A,n-2); A=mul(A,B);        cout << A[0][0] << '\n';    }    return 0;}
            ]]> + 线性代数-矩阵

            施工中,咕咕咕....

            矩阵乘法

            矩阵乘法的定义

            矩阵乘法的定义 \[(AB)_{i,j}=\sum_{k=1}^{m}A_{i,k}B_{k,j}\quad i\in[1,n],j\in[1,p]\] 一个 \(n \times m\) 的矩阵和\(m\times p\)的矩阵相乘,答案为一个 \(n \timesp\) 的矩阵

            快速手算

            之前我是把右矩阵逆时针转90度然后和第一个算的

            然后我算了几万年都没真正记住怎么算 QAQ

            现在新的算法,超好记的有没有qwqqqwqqwqqqwqwq

            例: \[\begin{bmatrix}1&2\\3&4\\\end{bmatrix} \times\begin{bmatrix}5&6\\7&8\\\end{bmatrix}\] 第一行: \[\begin{matrix}1\times5 & 1 \times 6\\ +&+\\2\times 7&2 \times 8\\ \downarrow & \downarrow\\ 19 & 22\end{matrix}\] 第二行 \[\begin{matrix}3\times 5& 3\times 6\\+&+\\4 \times 7& 4\times 8\\\downarrow&\downarrow\\43&50\end{matrix}\]

            最后的答案 \[\begin{bmatrix}19&22\\43&50\\\end{bmatrix}\]

            特殊矩阵

            单位矩阵 \[I=\begin{bmatrix}1&&&\\&1&&\\&&\ddots&\\&&&1\\\end{bmatrix}\] 满足对于任意矩阵 \(A\)\[AI=IA=A\]

            矩阵乘法的性质

            矩阵乘法具有结合律 \[(AB)C=A(BC)\] 矩阵乘法具有左右分配律 \[(A+B)C=AC+BC\\C(A+B)=CA+CB\] 矩阵乘法不一定具有交换律 \[AB\ne BA\]

            矩阵快速幂

            优化递推

            例: \[f_i = 2f_{i-1}+4f_{i-2}+3f_{i-3}\] 尝试构造矩阵使得 \[\begin{bmatrix}f_{i}\\f_{i-1}\\f_{i-2}\end{bmatrix}=M\times\begin{bmatrix}f_{i-1}\\f_{i-2}\\f_{i-3}\end{bmatrix}\] 显然 \(M\) 是一个 \(3\times 3\) 的矩阵,不难发现 \[M=\begin{bmatrix}2&4&3\\1&0&0\\0&1&0\end{bmatrix}\] 然后我们可以用 \(M^{k-1}\)快速计算 \(f_{k}\)

            常见数据范围:\(10^{18}\)

            板子题:P1962斐波那契数列

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef vector< vector<int> > mat;#define N (int)()const int mod=1e9+7;void add(int &x,int y){x+=y; if(x>=mod)x-=mod;}// (n*m) x (m*p) = (n*p)mat mul(mat &A,mat &B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vector<int>(p));    for(int k=0; k<m; k++)        for(int i=0; i<n; i++)            for(int j=0; j<p; j++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int k){    int n=max(A.size(),A[0].size());     mat B(n,vector<int>(n));    for(int i=0; i<n; i++) B[i][i]=1;    while(k)    {        if(k&1) B=mul(B,A);        A=mul(A,A); k>>=1;    }    return B;}void print(mat &A){    int n=A.size(),m=A[0].size();    for(int i=0; i<n; i++)        for(int j=0; j<m; j++)            cout << A[i][j] << " \n"[j==m-1];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n; cin >> n;    if(n<=2) cout << "1";    else    {        mat A(2,vector<int>(2)),B(2,vector<int>(1));        A[0][0]=A[0][1]=A[1][0]=1;        B[0][0]=B[1][0]=1;        A=qpow(A,n-2); A=mul(A,B);        cout << A[0][0] << '\n';    }    return 0;}
            ]]> @@ -1208,7 +1208,7 @@ /2022/07/30/abc156e-roaming-ti-jie/ - ABC156E Roaming 题解

            题目链接:ABC156E Roaming

            题意

            翻译来自我们模拟赛,可能和原题有区别

            输入两个数字 $n,k$ ,初始时有 $n$ 个盒子,每个盒子中都装着一个小球,$n$ 个小球之间不可区分。现在你要进行下列操作恰好 $k$ 次

            • 选择任意一个盒子中的一个小球,将它放到一个不同的盒子中

            你需要算出在 $k$ 次操作之后,小球的不同的摆放方式有多少种,答案对 $10^9+7$ 取模。两种摆放小球的方式被认为是不同的,当且仅当存在某一个盒子在两种摆放方式中放了不同个数的小球。

            $3 \le n \le 10^5,~2 \le k \le 10^9$

            Roundgod老师:这道题需要很好的观察能力

            注意到 $k \ge n-1$ 时方案数等于 $n-1$ 的方案数

            因为至多 $n-1$ 步就可以摆出所有的情况,例如 5,0,0,0,0 只要 $4$ 步

            步数是可以浪费的,比如本来要把 $1$ 的一个球移到 $2$ ,

            可以先移到 $3$ 再移到 $2$ ,这样就浪费了一步,这样我们就可以把 $k$ 步一直浪费到 $n-1$ 步。

            还有一个性质,如果某种情况 $0$ 的个数小于等于 $k$ ,那么一定可以达到,很显然。

            • 当 $k \ge n-1$ 时,答案为

            其实它就是 $x_1+\dots + x_n=n$ ,$x$ 的非负解个数

            • 当 $k<n-1$ 步时,答案为

              这里我们做的就是枚举 $0$ 的个数。

              第一个是枚举 $0$ 的位置,第二个是 $x_1+\dots + x_{n-i}=n$ 的正整数解的个数

            这个题稍微有点乱,因为性质很多,但是仔细思考一下还是颇有乐趣的。

            时间复杂度 $O(n)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(4e5+15)const int p=1e9+7;int n,k,fac[N],invf[N];int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1) ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}int inv(int x){return qpow(x,p-2);}void init(){    invf[0]=fac[0]=1;    for(int i=1; i<=N-5; i++)        fac[i]=fac[i-1]*i%p;    invf[N-5]=inv(fac[N-5]);    for(int i=N-5-1; i>=1; i--)        invf[i]=invf[i+1]*(i+1)%p;}int A(int n,int k){    if(n<k) return 0;    return fac[n]*invf[n-k]%p;}int C(int n,int k){    if(n<k) return 0;    return fac[n]*invf[k]%p*invf[n-k]%p;}void add(int &x,int y){x+=y;if(x>=p)x-=p;}void dec(int &x,int y){x-=y;if(x<0)x+=p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    init();    cin >> n >> k;    if(k>=n)return cout << C(2*n-1,n-1),0;    int res=0;    for(int i=0; i<=k; i++)        add(res,C(n,i)*C(n-1,n-i-1)%p);    cout << res;    return 0;}
            ]]> + ABC156E Roaming 题解

            题目链接:ABC156ERoaming

            题意

            翻译来自我们模拟赛,可能和原题有区别

            输入两个数字 \(n,k\) ,初始时有\(n\)个盒子,每个盒子中都装着一个小球,\(n\)个小球之间不可区分。现在你要进行下列操作恰好 \(k\) 次

            • 选择任意一个盒子中的一个小球,将它放到一个不同的盒子中

            你需要算出在 \(k\)次操作之后,小球的不同的摆放方式有多少种,答案对 \(10^9+7\)取模。两种摆放小球的方式被认为是不同的,当且仅当存在某一个盒子在两种摆放方式中放了不同个数的小球。

            \(3 \le n \le 10^5,~2 \le k \le10^9\)

            Roundgod老师:这道题需要很好的观察能力

            注意到 \(k \ge n-1\) 时方案数等于\(n-1\) 的方案数

            因为至多 \(n-1\)步就可以摆出所有的情况,例如 5,0,0,0,0 只要 \(4\) 步

            步数是可以浪费的,比如本来要把 \(1\) 的一个球移到 \(2\) ,

            可以先移到 \(3\) 再移到 \(2\) ,这样就浪费了一步,这样我们就可以把\(k\) 步一直浪费到 \(n-1\) 步。

            还有一个性质,如果某种情况 \(0\) 的个数小于等于 \(k\),那么一定可以达到,很显然。

            • \(k \ge n-1\) 时,答案为\[\dbinom{2n-1}{n-1}\]

              其实它就是 \(x_1+\dots + x_n=n\)\(x\) 的非负解个数

            • \(k<n-1\) 步时,答案为\[\sum_{i=0}^{k} \dbinom{n}{i}\dbinom{n-1}{n-i-1}\] 这里我们做的就是枚举 \(0\)的个数。

              第一个是枚举 \(0\) 的位置,第二个是\(x_1+\dots + x_{n-i}=n\)的正整数解的个数

            这个题稍微有点乱,因为性质很多,但是仔细思考一下还是颇有乐趣的。

            时间复杂度 \(O(n)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(4e5+15)const int p=1e9+7;int n,k,fac[N],invf[N];int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1) ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}int inv(int x){return qpow(x,p-2);}void init(){    invf[0]=fac[0]=1;    for(int i=1; i<=N-5; i++)        fac[i]=fac[i-1]*i%p;    invf[N-5]=inv(fac[N-5]);    for(int i=N-5-1; i>=1; i--)        invf[i]=invf[i+1]*(i+1)%p;}int A(int n,int k){    if(n<k) return 0;    return fac[n]*invf[n-k]%p;}int C(int n,int k){    if(n<k) return 0;    return fac[n]*invf[k]%p*invf[n-k]%p;}void add(int &x,int y){x+=y;if(x>=p)x-=p;}void dec(int &x,int y){x-=y;if(x<0)x+=p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    init();    cin >> n >> k;    if(k>=n)return cout << C(2*n-1,n-1),0;    int res=0;    for(int i=0; i<=k; i++)        add(res,C(n,i)*C(n-1,n-i-1)%p);    cout << res;    return 0;}
            ]]> @@ -1235,7 +1235,7 @@ /2022/07/29/abc167e-colorful-blocks-ti-jie/ - ABC167E Colorful Blocks 题解

            题目链接:ABC167E Colorful Blocks

            题意

            翻译从我们模拟赛搬过来的,稍微有些不一样。

            有 $n$ 个小球按照编号为 $1−n$ 从左至右放成一排,你现在要给每个小球染上 $1−m$ 中的颜色,并且满足至多存在 $k$ 对相邻位置上小球的颜色相同

            你需要输出一共有多少种不同的给小球染色的方案,两种染色方案被认为是不同的当且仅当两种方案中存在一个小球被染上了不同的颜色。由于答案可能过大,你需要对 $998244353$ 取模后输出。

            $1 \le n,m \le 2\times 10^5,~0\le 0 \le n-1$

            简单组合数学。考虑 $k$ 对相邻的情况,

            其实就是在前 $n-1$ 个球中选 $k$ 个和它右边的合并

            因此答案就是

            前面的 $m \times(m-1)^{n-i-1}$ 可能比较难理解

            其实就是一个经典模型:

            $n$ 个方格,$m$ 种颜色,相邻不能涂同一种颜色,方案数?

            则第一个可以涂 $m$ 种颜色中的任意一种颜色,

            剩下的每个都只能涂 $m-1$ 种颜色

            故这个模型的答案就是 $m \times (m-1)^{n-1}$

            然后就可以理解这个柿子了吧

            时间复杂度 $O(n \log N)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)const int p=998244353;int n,m,k,fac[N];int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1) ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}int inv(int x){return qpow(x,p-2);}void init(){    fac[0]=1;    for(int i=1; i<=n+5; i++)        fac[i]=fac[i-1]*i%p;}void add(int &x,int y){x=(x+y%p)%p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> k;    int res=0; init();    for(int i=0; i<=k; i++)        add(res,m%p*qpow(m-1,n-i-1)%p*fac[n-1]%p*inv(fac[i])%p*inv(fac[n-i-1])%p);    cout << res << '\n';    return 0;}
            ]]> + ABC167E Colorful Blocks 题解

            题目链接:ABC167EColorful Blocks

            题意

            翻译从我们模拟赛搬过来的,稍微有些不一样。

            \(n\) 个小球按照编号为 \(1−n\)从左至右放成一排,你现在要给每个小球染上 \(1−m\) 中的颜色,并且满足至多存在\(k\)对相邻位置上小球的颜色相同

            你需要输出一共有多少种不同的给小球染色的方案,两种染色方案被认为是不同的当且仅当两种方案中存在一个小球被染上了不同的颜色。由于答案可能过大,你需要对\(998244353\) 取模后输出。

            \(1 \le n,m \le 2\times 10^5,~0\le 0 \len-1\)

            简单组合数学。考虑 \(k\)对相邻的情况,

            其实就是在前 \(n-1\) 个球中选 \(k\) 个和它右边的合并

            因此答案就是 \[\sum_{i=0}^{k} m \times(m-1)^{n-i-1}\times \dbinom{n-1}{i}\] 前面的 \(m\times(m-1)^{n-i-1}\) 可能比较难理解

            其实就是一个经典模型:

            \(n\) 个方格,\(m\)种颜色,相邻不能涂同一种颜色,方案数?

            则第一个可以涂 \(m\)种颜色中的任意一种颜色,

            剩下的每个都只能涂 \(m-1\)种颜色

            故这个模型的答案就是 \(m \times(m-1)^{n-1}\)

            然后就可以理解这个柿子了吧

            时间复杂度 \(O(n \log N)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)const int p=998244353;int n,m,k,fac[N];int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1) ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}int inv(int x){return qpow(x,p-2);}void init(){    fac[0]=1;    for(int i=1; i<=n+5; i++)        fac[i]=fac[i-1]*i%p;}void add(int &x,int y){x=(x+y%p)%p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> k;    int res=0; init();    for(int i=0; i<=k; i++)        add(res,m%p*qpow(m-1,n-i-1)%p*fac[n-1]%p*inv(fac[i])%p*inv(fac[n-i-1])%p);    cout << res << '\n';    return 0;}
            ]]> @@ -1262,7 +1262,7 @@ /2022/07/29/mo-ni-sai-ti-jiang-jie-9/ - 模拟赛题讲解[9]

            来自 Roundgod 2022-07-29 noi.ac #2693

            原题来自 ABC172E NEQ

            问题描述

            给定 $n,m$,你需要计算满足以下条件的数组对 $A=[a_1,a_2,\dots,a_n]$ 和 $B=[b_1,b_2,\dots,b_n]$ 的个数:

            1. 对于所有 $1\leq i\leq n$ ,都有 $1\leq a_i\leq m$ 以及 $1\leq b_i\leq m$.
            2. 对于所有 $1\leq i\leq n$ ,都有 $a_i\neq b_i$.
            3. 对于所有的 $1\leq i\lt j\leq n$ ,都有 $a_i\neq a_j$ 且 $b_i\neq b_j$.

            由于答案可能过大,你需要输出答案对 $10^9+7$取模后的值。

            输入格式

            输入第一行包含一个整数 $t(1\leq t\leq 10)$ ,表示数据的组数。 对于每组测试数据,输入为一行,包含两个整数 $n,m$。

            输出格式

            对于每组测试数据,在一行中输出一个整数,表示答案。

            输入1

            2 22 31 110 10114514 1919810

            输出1

            2180306442892145678131

            样例说明

            对于样例的第一组测试数据, 合法数组对有以下 $2$ 种:

            数据范围

            对于 $20\%$ 的数据,$1\leq n\leq m\leq 10$

            对于 $100\%$ 的数据,$1\leq n\leq m\leq 2\times 10^6$

            题解

            显然的容斥题。设性质 $p_i:a_i=b_i,S \subseteq [m]$ (这里 $[m]=\{1,2,\dots,m\}$)

            则我们可以用这个经典公式来计算题目要求的 $a_i \ne b_i$

            考虑这里的 $N(S)$ 如何计算。不难发现

            中括号前面的指对 $S$ 指定的这 $i$ 个 $a_j=b_j$ 染色,后者就是对 $a_j \ne b_j$ 的染色

            因为 $a,b$ 此时染色相互独立,所以直接乘法原理(就是那个平方)

            你敢信我模拟赛这么水的题没想出来,主要还是 组合数 学的不扎实

            所以答案就是

            这里的 $i$ 就是在枚举 $|S|$ , $\binom{n}{i}$ 就是大小等于 $i$ 的 $S$ 的个数

            然后预处理一下阶乘啊逆元啊什么的就好啦

            时间复杂度 $O(Qn)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e6+15)const int p=1e9+7;int n,m,fac[N],invf[N];int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1) ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}int inv(int x){return qpow(x,p-2);}void init(){    invf[0]=fac[0]=1;    for(int i=1; i<=N-5; i++)        fac[i]=fac[i-1]*i%p;    invf[N-5]=inv(fac[N-5]);    for(int i=N-5-1; i>=1; i--)        invf[i]=invf[i+1]*(i+1)%p;}int A(int n,int k){    if(n<k) return 0;    return fac[n]*invf[n-k]%p;}int C(int n,int k){    if(n<k) return 0;    return fac[n]*invf[k]%p*invf[n-k]%p;}void add(int &x,int y){x+=y;if(x>=p)x-=p;}void dec(int &x,int y){x-=y;if(x<0)x+=p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    init(); int Q; cin >> Q;    while(Q--)    {        cin >> n >> m;        int res=0;        for(int i=0; i<=n; i++)        {            int tmp=C(n,i)%p*A(m,i)%p*A(m-i,n-i)%p*A(m-i,n-i)%p;            if(i&1) dec(res,tmp); else add(res,tmp);        }        cout << res << '\n';    }    return 0;}
            ]]> + 模拟赛题讲解[9]

            来自 Roundgod2022-07-29 noi.ac #2693

            原题来自 ABC172ENEQ

            问题描述

            给定 \(n,m\),你需要计算满足以下条件的数组对 \(A=[a_1,a_2,\dots,a_n]\) 和 \(B=[b_1,b_2,\dots,b_n]\) 的个数:

            1. 对于所有 \(1\leq i\leq n\) ,都有\(1\leq a_i\leq m\) 以及 \(1\leq b_i\leq m\).
            2. 对于所有 \(1\leq i\leq n\) ,都有\(a_i\neq b_i\).
            3. 对于所有的 \(1\leq i\lt j\leq n\),都有 \(a_i\neq a_j\)\(b_i\neq b_j\).

            由于答案可能过大,你需要输出答案对 \(10^9+7\)取模后的值。

            输入格式

            输入第一行包含一个整数 \(t(1\leq t\leq10)\) ,表示数据的组数。对于每组测试数据,输入为一行,包含两个整数 \(n,m\)。

            输出格式

            对于每组测试数据,在一行中输出一个整数,表示答案。

            输入1

            2 22 31 110 10114514 1919810

            输出1

            2180306442892145678131

            样例说明

            对于样例的第一组测试数据, 合法数组对有以下 \(2\) 种:

            \[A=[1,2],B=[2,1];A=[2,1],B=[1,2]\]

            数据范围

            对于 \(20\%\) 的数据,\(1\leq n\leq m\leq 10\)

            对于 \(100\%\) 的数据,\(1\leq n\leq m\leq 2\times 10^6\)

            题解

            显然的容斥题。设性质 \(p_i:a_i=b_i,S\subseteq [m]\) (这里 \([m]=\{1,2,\dots,m\}\))

            则我们可以用这个经典公式来计算题目要求的 \(a_i \ne b_i\) \[\sum_{S \subseteq [m]} (-1)^{|S|} \left|N(S)\right|\] 考虑这里的 \(N(S)\)如何计算。不难发现 \[N(S) = \dbinom{m}{i}i! \left[ \left((n-i)! \right)\dbinom{m-i}{n-i}\right]^2\] 中括号前面的指对 \(S\)指定的这 \(i\)\(a_j=b_j\) 染色,后者就是对 \(a_j \ne b_j\) 的染色

            因为 \(a,b\)此时染色相互独立,所以直接乘法原理(就是那个平方)

            你敢信我模拟赛这么水的题没想出来,主要还是 组合数学的不扎实

            所以答案就是 \[\sum_{i=0}^{n}(-1)^i\dbinom{n}{i}\dbinom{m}{i}i! \left[ \left((n-i)!\right)\dbinom{m-i}{n-i} \right]^2\] 这里的 \(i\) 就是在枚举 \(|S|\) , \(\binom{n}{i}\) 就是大小等于 \(i\) 的 \(S\) 的个数

            然后预处理一下阶乘啊逆元啊什么的就好啦

            时间复杂度 \(O(Qn)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e6+15)const int p=1e9+7;int n,m,fac[N],invf[N];int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1) ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}int inv(int x){return qpow(x,p-2);}void init(){    invf[0]=fac[0]=1;    for(int i=1; i<=N-5; i++)        fac[i]=fac[i-1]*i%p;    invf[N-5]=inv(fac[N-5]);    for(int i=N-5-1; i>=1; i--)        invf[i]=invf[i+1]*(i+1)%p;}int A(int n,int k){    if(n<k) return 0;    return fac[n]*invf[n-k]%p;}int C(int n,int k){    if(n<k) return 0;    return fac[n]*invf[k]%p*invf[n-k]%p;}void add(int &x,int y){x+=y;if(x>=p)x-=p;}void dec(int &x,int y){x-=y;if(x<0)x+=p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    init(); int Q; cin >> Q;    while(Q--)    {        cin >> n >> m;        int res=0;        for(int i=0; i<=n; i++)        {            int tmp=C(n,i)%p*A(m,i)%p*A(m-i,n-i)%p*A(m-i,n-i)%p;            if(i&1) dec(res,tmp); else add(res,tmp);        }        cout << res << '\n';    }    return 0;}
            ]]> @@ -1289,7 +1289,7 @@ /2022/07/29/hdu4135-co-prime-ti-jie/ - hdu4135 Co-prime 题解

            题目链接:hdu4135 Co-prime

            题意

            $T$ 组数据,每组给出 $a,b,n$ ,求区间 $[a,b]$ 中有多少个数与 $n$ 互质。

            Given a number $N$, you are asked to count the number of integers between $A$ and $B$ inclusive which are relatively prime to $N$.
            Two integers are said to be co-prime or relatively prime if they have no common positive divisors other than $1$ or, equivalently, if their greatest common divisor is $1$. The number $1$ is relatively prime to every integer.

            $1 \le a\le b\le 10^{15},~1 \le n \le 10^9$

            首先一个常见转化 $\text{ans} = f(b)-f(a-1)$

            其中 $f(k)$ 表示 $[1,k]$ 中与 $n$ 互质的数的个数

            注意到如果 $x$ 与 $n=\prod\limits_{1\le i \le t} p_i^{k_i}$ 互质,

            则对于任意 $p_i$ ,均有 $p_i \nmid x$

            直接去算并不好算,考虑容斥

            下面会涉及一些定义,我放在了文章末尾

            在这题里,“坏性质”就是与 $p_i$ 不互质

            我们 $O(2^t)$ 枚举 $S$ (显然 $t \le \log n$ )

            举个例子,假设 $S_i = \{1,2\}$ ,

            则表示能放到 $N(S)$ 中的一定要有 $P_1,P_2$ 的性质

            在这题里,就是能放到 $N(S)$ 里的 $x$ 满足 $p_1 \mid x \,\land \, p_2 \mid x$

            时间复杂度为 $O(Q\times 2^{t})$

            代码采用比较好理解的位运算写法

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(25)int pcnt,p[N];void getp(int n){    pcnt=0;    for(int i=2; i<=n/i; i++)    {        if(n%i==0) p[++pcnt]=i;        while(n%i==0) n/=i;    }    if(n>1) p[++pcnt]=n;}int solve(int m){    int ans=m;    for(int i=1; i<(1<<pcnt); i++)    {        int res=1,c=0;        for(int j=0; j<pcnt; j++)            if(i&(1ll<<j)) ++c,res*=p[j+1];        if(c&1) ans-=m/res;        else ans+=m/res;    }    return ans;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q,a,b,n;    scanf("%lld",&Q);    for(int t=1; t<=Q; t++)    {        scanf("%lld%lld%lld",&a,&b,&n); getp(n);        printf("Case #%lld: %lld\n",t,solve(b)-solve(a-1));    }    return 0;}

            其实这一段是我打算放到总结里面的(2022.7.29现在还在咕咕咕)

            不过我刚学容斥,所以这篇题解就是用来理清一下思路的 awa

            下面的全是抄的Roundgod老师的PPT qwq

            容斥原理(principle of inclusion-exclusion): 令 $X$ 为一个有限集合,$P_1, P_2,\dots , P_m$ 是一些性质的集合,对于任意 $S \subseteq [m]$ ,定义 $N(S) = \{x \in X \mid \forall i \in S : x \text{ has } P_i\}$ 。那么 $X$ 中不满足任何一个性质 $P_i$ 的元素个数

            对于任意的 $S \subseteq [m]$ ,如果 $N(S)$ 只和 $S$ 的大小(即 $|S|$ )有关,那么式子可以化简为

            其中 $N(i)$ 表示对于任意满足 $S \subseteq [m],|S|=i$ 的 $N(S)$ 值

            容斥原理的常用技巧

            1. 定义一些“坏性质” $P_1,\dots,P_m$
            2. 找到对于每个 $S\subseteq [m]$ 计算 $N(S)$ 的方法
            3. 套用容斥原理计算不满足任何一个“坏性质”的元素个数
            ]]> + hdu4135 Co-prime 题解

            题目链接:hdu4135Co-prime

            题意

            \(T\) 组数据,每组给出 \(a,b,n\) ,求区间 \([a,b]\) 中有多少个数与 \(n\) 互质。

            Given a number \(N\), you are askedto count the number of integers between \(A\) and \(B\) inclusive which are relatively prime to\(N\). Two integers are said to beco-prime or relatively prime if they have no common positive divisorsother than \(1\) or, equivalently, iftheir greatest common divisor is \(1\).The number \(1\) is relatively prime toevery integer.

            \(1 \le a\le b\le 10^{15},~1 \le n \le10^9\)

            首先一个常见转化 \(\text{ans} =f(b)-f(a-1)\)

            其中 \(f(k)\) 表示 \([1,k]\) 中与 \(n\) 互质的数的个数

            注意到如果 \(x\)\(n=\prod\limits_{1\le i \le t} p_i^{k_i}\)互质,

            则对于任意 \(p_i\) ,均有 \(p_i \nmid x\)

            直接去算并不好算,考虑容斥

            下面会涉及一些定义,我放在了文章末尾

            在这题里,“坏性质”就是与 \(p_i\)不互质

            我们 \(O(2^t)\) 枚举 \(S\) (显然 \(t\le \log n\)

            举个例子,假设 \(S_i = \{1,2\}\)

            则表示能放到 \(N(S)\) 中的一定要有\(P_1,P_2\) 的性质

            在这题里,就是能放到 \(N(S)\) 里的\(x\) 满足 \(p_1 \mid x \,\land \, p_2 \mid x\)

            时间复杂度为 \(O(Q\times2^{t})\)

            代码采用比较好理解的位运算写法

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(25)int pcnt,p[N];void getp(int n){    pcnt=0;    for(int i=2; i<=n/i; i++)    {        if(n%i==0) p[++pcnt]=i;        while(n%i==0) n/=i;    }    if(n>1) p[++pcnt]=n;}int solve(int m){    int ans=m;    for(int i=1; i<(1<<pcnt); i++)    {        int res=1,c=0;        for(int j=0; j<pcnt; j++)            if(i&(1ll<<j)) ++c,res*=p[j+1];        if(c&1) ans-=m/res;        else ans+=m/res;    }    return ans;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q,a,b,n;    scanf("%lld",&Q);    for(int t=1; t<=Q; t++)    {        scanf("%lld%lld%lld",&a,&b,&n); getp(n);        printf("Case #%lld: %lld\n",t,solve(b)-solve(a-1));    }    return 0;}

            其实这一段是我打算放到总结里面的(2022.7.29现在还在咕咕咕)

            不过我刚学容斥,所以这篇题解就是用来理清一下思路的 awa

            下面的全是抄的Roundgod老师的PPTqwq

            容斥原理(principle of inclusion-exclusion): 令 \(X\) 为一个有限集合\(P_1, P_2,\dots , P_m\)是一些性质的集合,对于任意 \(S \subseteq [m]\) ,定义 \(N(S) = \{x \in X \mid \forall i \in S : x \text{has } P_i\}\) 。那么 \(X\)不满足任何一个性质 \(P_i\)的元素个数\[\sum_{S \subseteq [m]} (-1)^{|S|} \left|N(S)\right|\] 对于任意的 \(S \subseteq[m]\)如果 \(N(S)\)只和 \(S\) 的大小(即 \(|S|\) )有关,那么式子可以化简为\[\sum_{i=0}^{m} (-1)^i \binom{m}{i} N(i)\] 其中 \(N(i)\)表示对于任意满足 \(S \subseteq[m],|S|=i\)\(N(S)\)

            容斥原理的常用技巧

            1. 定义一些“坏性质” \(P_1,\dots,P_m\)
            2. 找到对于每个 \(S\subseteq [m]\)计算 \(N(S)\) 的方法
            3. 套用容斥原理计算不满足任何一个“坏性质”的元素个数
            ]]> @@ -1316,7 +1316,7 @@ /2022/07/29/oi-shu-xue-zong-jie-zu-he-shu-xue/ - OI数学总结-组合数学

            施工中,咕咕咕….

            排列组合

            更多详见 小蓝书 16.1

            下面两个指的是无重复的排列与组合

            排列数

            从 $n$ 个不同元素中取 $k(k\le n)$ 个不同元素,并按一定顺序排成一列的方案数

            也可写作 $\mathrm{P}_{n}^{k}$ 或直接用组合数写法 $k! \times \binom{n}{k}$

            代码求解

            $O(n)$ 计算

            例如求解 $A_{n-m+1}^{m} \bmod p$

            int res=1;for(int i=n-m+1; i>=n-2*m+2; i--)    res=res*i%p;cout << res << '\n'

            组合数

            从 $n$ 个不同元素中取 $k(k \le n)$ 个不同元素的方案数

            高中一般记作 $\mathrm{C}_n^{k}$ ,不是很推荐使用。

            特别地,规定当 $k > n$ 时, $\mathrm{A}_n^{k} = \mathrm{C}_n^{k}=0$

            代码求解

            $O(n^2)$ 递推
            for(int i=0; i<=1000; i++){    C[i][0]=1;    for(int j=1; j<=i; j++)        C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;}
            $O(p)$ Lucas定理

            详见 [OI数学总结-数论]

            P3807 【模板】卢卡斯定理/Lucas 定理

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int Q,n,m,p;int fac[N];int qpow(int a,int b,int p){int ans=1,base=a;while(b){if(b&1)ans=ans*base%p;base=base*base%p;b>>=1;}return ans;}// n 选 mint C(int n,int m,int p){if(n<m)return 0;return fac[n]*qpow(fac[m],p-2,p)%p*qpow(fac[n-m],p-2,p)%p;}int lucas(int n,int m,int p){if(!m)return 1;return lucas(n/p,m/p,p)*C(n%p,m%p,p)%p;}signed main(){cin >> Q;while(Q--){cin >> n >> m >> p;fac[1]=fac[0]=1;for(int i=2; i<=n+m; i++)fac[i]=fac[i-1]*i%p;cout << lucas(n+m,m,p) << '\n'; // C(n+m,n)}return 0;}

            容斥原理

            容斥原理(principle of inclusion-exclusion): 令 $X$ 为一个有限集合,$P_1, P_2,\dots , P_m$ 是一些性质的集合,对于任意 $S \subseteq [m]$ ,定义 $N(S) = \{x \in X \mid \forall i \in S : x \text{ has } P_i\}$ 。那么 $X$ 中不满足任何一个性质 $P_i$ 的元素个数

            对于任意的 $S \subseteq [m]$ ,如果 $N(S)$ 只和 $S$ 的大小(即 $|S|$ )有关,那么式子可以化简为

            其中 $N(i)$ 表示对于任意满足 $S \subseteq [m],|S|=i$ 的 $N(S)$ 值

            容斥原理的常用技巧

            1. 定义一些“坏性质” $P_1,\dots,P_m$
            2. 找到对于每个 $S\subseteq [m]$ 计算 $N(S)$ 的方法
            3. 套用容斥原理计算不满足任何一个“坏性质”的元素个数

            鸽巢原理(抽屉原理)

            假如有 $n+1$个元素放到 $n$ 个集合中去,其中必定有一个集合里至少有两个元素。

            应用

            1. 任意 $n+1$ 个数中至少有 $2$ 个数与 $n$ 同余

            第二类斯特林数

            $\begin{Bmatrix}n\\m\end{Bmatrix}$ 或 $S(n,m)$ ,表示将 $n$ 个两两不同的元素,划分为 $m$ 个互不区分的非空子集的方案数


            伯努利数

            常用于等幂求和( $m$ 次幂和的公式)

            记 $S_m(n) = \sum\limits_{i=0}^{n-1} i^m$ ,则

            其中 $B_k$ 为伯努利数,定义如下

            例如 $\binom{2}{0} B_0 = \binom{2}{1}B_1 = 0$

            $n$01234567$\dots$
            $B_n$$1$$-\frac{1}{2}$$\frac{1}{6}$$0$$-\frac{1}{30}$$0$$\frac{1}{42}$$0$$\dots$

            代码求解

            咕咕咕…


            贝尔数(Bell数)

            $B_n$ 是基数为 $n$ 的集合的划分方法的数目

            集合 $S$ 的一个划分定义为 $S$ 的两两不相交的非空子集的族,它们的并为 $S$

            例如 $B_3=5$ ,因为 $3$ 个元素的集合 $a,b,c$ 有 $5$ 种不同的划分方法

            贝尔数适合递推公式

            贝尔三角形(类似于杨辉三角)

            则有 $B_n = A_{n,1}$

            代码求解

            咕咕咕….

            ]]> + OI数学总结-组合数学

            施工中,咕咕咕....

            排列组合

            更多详见 小蓝书16.1

            下面两个指的是无重复的排列与组合

            排列数

            \(n\) 个不同元素中取 \(k(k\le n)\)个不同元素,并按一定顺序排成一列的方案数 \[\mathrm{A}_{n}^{k} = \frac{n!}{(n-k)!}\] 也可写作 \(\mathrm{P}_{n}^{k}\) 或直接用组合数写法\(k! \times \binom{n}{k}\)

            代码求解

            \(O(n)\) 计算

            例如求解 \(A_{n-m+1}^{m} \bmodp\)

            int res=1;for(int i=n-m+1; i>=n-2*m+2; i--)    res=res*i%p;cout << res << '\n'

            组合数

            \(n\) 个不同元素中取 \(k(k \le n)\) 个不同元素的方案数 \[\binom{n}{k} = \dfrac{n!}{k!(n-k)!}\] 高中一般记作 \(\mathrm{C}_n^{k}\) ,不是很推荐使用。

            特别地,规定当 \(k >n\) 时, \(\mathrm{A}_n^{k} =\mathrm{C}_n^{k}=0\)

            代码求解

            \(O(n^2)\) 递推
            for(int i=0; i<=1000; i++){    C[i][0]=1;    for(int j=1; j<=i; j++)        C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;}
            \(O(p)\)Lucas定理

            详见 [OI数学总结-数论]

            P3807【模板】卢卡斯定理/Lucas 定理

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int Q,n,m,p;int fac[N];int qpow(int a,int b,int p){int ans=1,base=a;while(b){if(b&1)ans=ans*base%p;base=base*base%p;b>>=1;}return ans;}// n 选 mint C(int n,int m,int p){if(n<m)return 0;return fac[n]*qpow(fac[m],p-2,p)%p*qpow(fac[n-m],p-2,p)%p;}int lucas(int n,int m,int p){if(!m)return 1;return lucas(n/p,m/p,p)*C(n%p,m%p,p)%p;}signed main(){cin >> Q;while(Q--){cin >> n >> m >> p;fac[1]=fac[0]=1;for(int i=2; i<=n+m; i++)fac[i]=fac[i-1]*i%p;cout << lucas(n+m,m,p) << '\n'; // C(n+m,n)}return 0;}

            容斥原理

            容斥原理(principle of inclusion-exclusion): 令 \(X\) 为一个有限集合\(P_1, P_2,\dots , P_m\)是一些性质的集合,对于任意 \(S \subseteq [m]\) ,定义 \(N(S) = \{x \in X \mid \forall i \in S : x \text{has } P_i\}\) 。那么 \(X\)不满足任何一个性质 \(P_i\)的元素个数\[\sum_{S \subseteq [m]} (-1)^{|S|} \left|N(S)\right|\] 对于任意的 \(S \subseteq[m]\)如果 \(N(S)\)只和 \(S\) 的大小(即 \(|S|\) )有关,那么式子可以化简为\[\sum_{i=0}^{m} (-1)^i \binom{m}{i} N(i)\] 其中 \(N(i)\)表示对于任意满足 \(S \subseteq[m],|S|=i\)\(N(S)\)

            容斥原理的常用技巧

            1. 定义一些“坏性质” \(P_1,\dots,P_m\)
            2. 找到对于每个 \(S\subseteq [m]\)计算 \(N(S)\) 的方法
            3. 套用容斥原理计算不满足任何一个“坏性质”的元素个数

            鸽巢原理(抽屉原理)

            假如有 \(n+1\)个元素放到 \(n\)个集合中去,其中必定有一个集合里至少有两个元素。

            应用

            1. 任意 \(n+1\) 个数中至少有 \(2\) 个数与 \(n\) 同余

            第二类斯特林数

            \(\begin{Bmatrix}n\\m\end{Bmatrix}\)\(S(n,m)\) ,表示\(n\) 个两两不同的元素,划分为 \(m\) 个互不区分的非空子集的方案数\[\begin{Bmatrix}n \\ 0\end{Bmatrix} = [n = 0]\]

            \[\begin{Bmatrix}n\\m\end{Bmatrix} =\begin{Bmatrix}n-1\\m-1\end{Bmatrix}+m\times\begin{Bmatrix}n-1\\k\end{Bmatrix}\]


            伯努利数

            常用于等幂求和( \(m\)次幂和的公式)

            \(S_m(n) = \sum\limits_{i=0}^{n-1}i^m\) ,则 \[S_m(n) = \dfrac{1}{m+1} \sum_{k=0}^{m} \dbinom{m+1}{k} B_kn^{m+1-k}\] 其中 \(B_k\)为伯努利数,定义如下 \[B_0 = 1\\\sum_{j=0}^{m}\dbinom{m+1}{j}B_j = 0,\quad m>0\] 例如 \(\binom{2}{0} B_0 =\binom{2}{1}B_1 = 0\)

            \(n\)01234567\(\dots\)
            \(B_n\)\(1\)\(-\frac{1}{2}\)\(\frac{1}{6}\)\(0\)\(-\frac{1}{30}\)\(0\)\(\frac{1}{42}\)\(0\)\(\dots\)

            代码求解

            咕咕咕...


            贝尔数(Bell数)

            \(B_n\) 是基数为 \(n\) 的集合的划分方法的数目

            集合 \(S\) 的一个划分定义为 \(S\) 的两两不相交的非空子集的族,它们的并为\(S\)

            例如 \(B_3=5\) ,因为 \(3\) 个元素的集合 \(a,b,c\) 有 \(5\) 种不同的划分方法 \[\{ \{ a \}, \{ b \}, \{ c \} \} \\\{ \{ a \}, \{ b,c \} \} \\\{ \{b \}, \{ a,c \} \} \\\{ \{ c \}, \{ a,b \} \} \\\{ \{ a,b,c \} \}\] 贝尔数适合递推公式 \[B_0=B_1=1\\\\B_{n+1} = \sum_{k=0}^{n}\dbinom{n}{k}B_k,\quad n \ge 1\] 贝尔三角形(类似于杨辉三角) \[\begin{matrix}1\\1&2\\2&3&5\\5&7&10&15\\15&20&27&37&52\\52&67&87&114&151&203\\\end{matrix}\]

            \[A_{i,j} = \begin{cases} 1, &i=1\land j=1 \\\\ A_{i-1,j-1}, &i\ne 1 \land j=1 \\\\ A_{i,j-1}+A_{i-1,j-1}, &i\ne 1 \land j \ne 1\end{cases}\]

            则有 \(B_n = A_{n,1}\)

            代码求解

            咕咕咕....

            ]]> @@ -1343,7 +1343,7 @@ /2022/07/29/cf1000d-yet-another-problem-on-a-subsequence-ti-jie/ - CF1000D Yet Another Problem On a Subsequence 题解

            题目链接:CF1000D Yet Another Problem On a Subsequence

            题意

            如果一个数组 $[a_1,a_2,a_3,…,a_n]a_1=n-1$ 并且 $a_1>0$ ,这个数组就被叫为好数组,如果一个序列能正好分为多个好数组,ta就被叫为好序列,现在给定一个序列,求这个序列有多少好子序列,答案对 $998244353$ 取模。

            The sequence of integers $ a_1, a_2, \dots, a_k $ is called a good array if $ a_1 = k - 1 $ and $ a_1 > 0 $ . For example, the sequences $ [3, -1, 44, 0], [1, -99] $ are good arrays, and the sequences $ [3, 7, 8], [2, 5, 4, 1], [0] $ — are not.

            A sequence of integers is called good if it can be divided into a positive number of good arrays. Each good array should be a subsegment of sequence and each element of the sequence should belong to exactly one array. For example, the sequences $ [2, -3, 0, 1, 4] $ , $ [1, 2, 3, -3, -9, 4] $ are good, and the sequences $ [2, -3, 0, 1] $ , $ [1, 2, 3, -3 -9, 4, 1] $ — are not.

            For a given sequence of numbers, count the number of its subsequences that are good sequences, and print the number of such subsequences modulo $998244353$.

            设 $f_i$ 表示以 $i$ 结尾的 good sequences 的数量

            当 $a_i\le 0$ 时,显然 $f_i=0$

            当 $a_i >0$ 时,我们可以在 $[i+a_i+1,n]$ 之间找一个good sequences拼接

            然后 $[i,j]$ 之间可以随便选 $a_i+1$ 个数,因此有

            时间复杂度 $O(n^2)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)const int p=998244353;int n,f[N],a[N],C[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    C[0][0]=1;    for(int i=1; i<=N-5; i++)        for(int j=1; j<=i; j++)            C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;    // for(int i=1; i<=10; i++)    //     for(int j=1; j<=10; j++)    //         cout << C[i][j] << " \n"[j==10];    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    f[n+1]=1;    for(int i=n; i>=1; i--)    {        if(a[i]<=0) continue;        for(int j=i+a[i]+1; j<=n+1; j++)            if(a[i]+1<=j-i) f[i]=(f[i]+f[j]*C[j-i][a[i]+1]%p)%p;    }    int res=0;    for(int i=1; i<=n; i++)        res=(res+f[i])%p;    cout << res << '\n';    return 0;}
            ]]> + CF1000D YetAnother Problem On a Subsequence 题解

            题目链接:CF1000DYet Another Problem On a Subsequence

            题意

            如果一个数组 \([a_1,a_2,a_3,...,a_n]a_1=n-1\) 并且 \(a_1>0\),这个数组就被叫为好数组,如果一个序列能正好分为多个好数组,ta就被叫为好序列,现在给定一个序列,求这个序列有多少好子序列,答案对\(998244353\) 取模。

            The sequence of integers $ a_1, a_2, , a_k $ is called a good arrayif $ a_1 = k - 1 $ and $ a_1 > 0 $ . For example, the sequences $ [3,-1, 44, 0], [1, -99] $ are good arrays, and the sequences $ [3, 7, 8],[2, 5, 4, 1], [0] $ — are not.

            A sequence of integers is called good if it can be divided into apositive number of good arrays. Each good array should be a subsegmentof sequence and each element of the sequence should belong to exactlyone array. For example, the sequences $ [2, -3, 0, 1, 4] $ , $ [1, 2, 3,-3, -9, 4] $ are good, and the sequences $ [2, -3, 0, 1] $ , $ [1, 2, 3,-3 -9, 4, 1] $ — are not.

            For a given sequence of numbers, count the number of its subsequencesthat are good sequences, and print the number of such subsequencesmodulo \(998244353\).

            \(f_i\) 表示以 \(i\) 结尾的 good sequences 的数量

            \(a_i\le 0\) 时,显然 \(f_i=0\)

            \(a_i >0\) 时,我们可以在\([i+a_i+1,n]\) 之间找一个goodsequences拼接

            然后 \([i,j]\) 之间可以随便选 \(a_i+1\) 个数,因此有 \[f_i = \sum f_j \times \dbinom{i-j}{a_i+1}\] 时间复杂度 \(O(n^2)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)const int p=998244353;int n,f[N],a[N],C[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    C[0][0]=1;    for(int i=1; i<=N-5; i++)        for(int j=1; j<=i; j++)            C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;    // for(int i=1; i<=10; i++)    //     for(int j=1; j<=10; j++)    //         cout << C[i][j] << " \n"[j==10];    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    f[n+1]=1;    for(int i=n; i>=1; i--)    {        if(a[i]<=0) continue;        for(int j=i+a[i]+1; j<=n+1; j++)            if(a[i]+1<=j-i) f[i]=(f[i]+f[j]*C[j-i][a[i]+1]%p)%p;    }    int res=0;    for(int i=1; i<=n; i++)        res=(res+f[i])%p;    cout << res << '\n';    return 0;}
            ]]> @@ -1372,7 +1372,7 @@ /2022/07/29/abc210e-ring-mst-ti-jie/ - ABC210E Ring MST 题解

            题目链接:ABC210E Ring MST

            题意:给定一张 $n$ 个点的无向图,顶点的编号为 $0,1,\dots,n−1$ 。同时给出两个长度为 $m$ 的数组 $a_1,a_2,\dots,a_m$ 和 $b_1,b_2,\dots,b_m$ 。初始时图中并没有任何边,你可以按照以下操作加边:

            选择一个 $1\le i\le m$ 和一个 $0<x<n$ ,并在顶点 $x$ 和顶点 $(x+a_i) \bmod n$ 中添加一条长度为 $b_i$ 的边。

            你现在想要知道,你添加的边的长度总和至少为多少,才能使得整个图连通?如果无论如何都不能使整个图连通,输出 $−1$ 。

            数据范围:

            $1 \le a_i < n \le 10^9,~1 \le m \le 10^5,1\le b_i \le 10^9$

            首先我们可以想到把 $(x,(x+a_i)\bmod n)$ 看作一条边

            然后答案就是最小生成树,跑个 $\text{kruskal}$

            不过这个是 $O(nm)$ 的,这是不能接受的

            于是我们模拟 $\text{kruskal}$ 的算法过程,不过更加快捷

            把 $(a_i,b_i)$ 按 $b_i$ 排序升序排序,保证有 $\forall i < j,~b_i\le b_j$

            然后考虑尽可能的用较前的 $(a_i,b_i)$ 来建边

            有一个结论:令 $g=\gcd(a_i,n)$ ,

            所有模 $g$ 相同的结点均可以连到同一个连通块,并且会有 $g$ 个这样的连通块

            注:此时我们尽可能在用 $a_i$ 建边,这里连到同一个连通块就是指尽可能建边的结果

            这个结论其实证起来很简单

            方便起见,我们固定 $0$ 为起点,显然这不会影响答案

            则只用 $a_i$ 从 $0$ 可以走到的点一定是

            其中 $x,y \in \mathbb{Z}$ 。

            令 $c=a_ix+ny$ ,根据裴蜀定理可知,如果有解,则 $g \mid c$

            注意我们这里是 $c$ ,实际上我们还会从 $0,1,\dots,g-1$ 出发去连边

            不难发现这样我们会连出 $g$ 个这样的连通块

            然后我们试着去推广这个结论

            如果我们用前 $i$ 个 $a_i$ ,则他们一共有 $g=\gcd(a_1,a_2,\dots,a_i,n)$ 个连通块

            详细证明我也不太会,但是这个就是个很显然的结论

            不难发现,当 $g=1$ 时,图就联通了。

            然后这题就没了。边权只要用 $\sum(g_{i-1}-g_{i}) \times b_{i}$ 来算就行啦

            这题我在Roundgod老师的指导下想了一个小时左右才想明白 QAQ

            时间复杂度 $O(m\log m)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define F first#define S second#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()typedef pair<int,int> P;int n,m;vector<P> vec;int gcd(int a,int b){return b==0?a:gcd(b,a%b);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m; vec.clear();    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        vec.push_back({v,u});    }    sort(vec.begin(),vec.end());    int ans=0,g=n;    for(int i=0; i<m; i++)    {        int tmp=g;        g=gcd(g,vec[i].S);        ans+=(tmp-g)*vec[i].F;    }    if(g!=1) cout << "-1\n";    else cout << ans << '\n';    return 0;}
            ]]> + ABC210E Ring MST 题解

            题目链接:ABC210E RingMST

            题意:给定一张 \(n\) 个点的无向图,顶点的编号为 \(0,1,\dots,n−1\) 。同时给出两个长度为 \(m\) 的数组 \(a_1,a_2,\dots,a_m\) 和 \(b_1,b_2,\dots,b_m\)。初始时图中并没有任何边,你可以按照以下操作加边:

            选择一个 \(1\le i\le m\) 和一个\(0<x<n\) ,并在顶点 \(x\) 和顶点 \((x+a_i) \bmod n\) 中添加一条长度为 \(b_i\) 的边。

            你现在想要知道,你添加的边的长度总和至少为多少,才能使得整个图连通?如果无论如何都不能使整个图连通,输出\(−1\)

            数据范围:

            \(1 \le a_i < n \le 10^9,~1 \le m \le10^5,1\le b_i \le 10^9\)

            首先我们可以想到把 \((x,(x+a_i)\bmodn)\) 看作一条边

            然后答案就是最小生成树,跑个 \(\text{kruskal}\)

            不过这个是 \(O(nm)\)的,这是不能接受的

            于是我们模拟 \(\text{kruskal}\)的算法过程,不过更加快捷

            \((a_i,b_i)\)\(b_i\) 排序升序排序,保证有 \(\forall i < j,~b_i\le b_j\)

            然后考虑尽可能的用较前的 \((a_i,b_i)\) 来建边

            有一个结论:令 \(g=\gcd(a_i,n)\) ,

            所有模 \(g\)相同的结点均可以连到同一个连通块,并且会有 \(g\) 个这样的连通块

            注:此时我们尽可能在用 \(a_i\)建边,这里连到同一个连通块就是指尽可能建边的结果

            这个结论其实证起来很简单

            方便起见,我们固定 \(0\)为起点,显然这不会影响答案

            则只用 \(a_i\)\(0\) 可以走到的点一定是 \[(a_ix+ny) \bmod n\] 其中 \(x,y \in \mathbb{Z}\)

            \(c=a_ix+ny\),根据裴蜀定理可知,如果有解,则 \(g \midc\)

            注意我们这里是 \(c\),实际上我们还会从 \(0,1,\dots,g-1\)出发去连边

            不难发现这样我们会连出 \(g\)个这样的连通块

            然后我们试着去推广这个结论

            如果我们用前 \(i\)\(a_i\) ,则他们一共有 \(g=\gcd(a_1,a_2,\dots,a_i,n)\) 个连通块

            详细证明我也不太会,但是这个就是个很显然的结论

            不难发现,当 \(g=1\)时,图就联通了。

            然后这题就没了。边权只要用 \(\sum(g_{i-1}-g_{i}) \times b_{i}\)来算就行啦

            这题我在Roundgod老师的指导下想了一个小时左右才想明白QAQ

            时间复杂度 \(O(m\log m)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define F first#define S second#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()typedef pair<int,int> P;int n,m;vector<P> vec;int gcd(int a,int b){return b==0?a:gcd(b,a%b);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m; vec.clear();    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        vec.push_back({v,u});    }    sort(vec.begin(),vec.end());    int ans=0,g=n;    for(int i=0; i<m; i++)    {        int tmp=g;        g=gcd(g,vec[i].S);        ans+=(tmp-g)*vec[i].F;    }    if(g!=1) cout << "-1\n";    else cout << ans << '\n';    return 0;}
            ]]> @@ -1401,7 +1401,7 @@ /2022/07/29/arc060b-digit-sum-ti-jie/ - ARC060B Digit Sum 题解

            题目链接:ARC060B Digit Sum

            题意

            对于任意非负整数 $x$ 和 $m(2\le m)$ ,定义 $f_m(x)$ 为 $x$ 在 $m$ 进制下的各位数字之和。

            例如$f_{10}(87654)=8+7+6+5+4=30,~f_{100}(87654)=8+76+54=138$ 。给定两个整数 $x$ 和 $n$ 。

            你需要计算是否存在某个 $m\ge 2$ 使得 $f_m(x)=n$ 。

            如果存在,输出最小的满足条件的 $m$ ;如果不存在,输出 $−1$ 。

            数据范围:

            $ 1 \le x,n \le 10^{11}$

            设 $x$ 化为 $m$ 进制后为 $\overline{x_1x_2\dots x_t}$

            不难发现当 $t \ge 3$ 时, $m\le \sqrt{x}-\epsilon,\epsilon \in \mathbb{N}$

            例如当 $t=3$ 时,设 $x$ 化为 $m$ 进制后为 $\overline{abc}$ ,则

            显然有 $m\le \sqrt{x}-\epsilon,\epsilon \in \mathbb{N}$

            因此我们暴力枚举所有 $m\le \sqrt{x}-\epsilon,\epsilon \in \mathbb{N}$ ,对应 $t\ge 3$ 的情况

            接下来 $m > \sqrt{x}-\epsilon,\epsilon \in \mathbb{N}$ 的情况,直接枚举 $m$ 肯定是不行的

            注意到此时 $x$ 满足

            因为有 $m > \sqrt{x}-\epsilon,\epsilon \in \mathbb{N}$ ,因此 $a \le \sqrt{x}-\epsilon^{\prime},\epsilon^{\prime} \in \mathbb{N}$

            于是我们只要枚举这个 $a$ ,然后判断此时的 $m=\frac{x-(n-a)}{a}$ 是否合法即可

            这里的合法指 $x$ 在当前 $m$ 进制下是 $am+b$

            时间复杂度 $O(\sqrt{x})$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int x,n,ans;void check(int m){    if(m<2) return;    int res=0,tmp=x;    while(tmp)    {        res+=tmp%m;        tmp/=m;    }    if(res==n) ans=min(ans,m);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> x >> n; ans=INF;    for(int m=2; m*m<=x; m++) check(m);    if(x==n) ans=min(ans,n+1);    for(int a=1; a*a<=x; a++) check((x-(n-a))/a);    if(ans==INF) cout << "-1\n";    else cout << ans << '\n';    return 0;}
            ]]> + ARC060B Digit Sum 题解

            题目链接:ARC060B DigitSum

            题意

            对于任意非负整数 \(x\)\(m(2\le m)\) ,定义 \(f_m(x)\) 为 \(x\) 在 \(m\) 进制下的各位数字之和。

            例如\(f_{10}(87654)=8+7+6+5+4=30,~f_{100}(87654)=8+76+54=138\)。给定两个整数 \(x\)\(n\) 。

            你需要计算是否存在某个 \(m\ge 2\)使得 \(f_m(x)=n\)

            如果存在,输出最小的满足条件的 \(m\);如果不存在,输出 \(−1\)

            数据范围:

            $ 1 x,n ^{11}$

            \(x\) 化为 \(m\) 进制后为 \(\overline{x_1x_2\dots x_t}\)

            不难发现当 \(t \ge 3\) 时, \(m\le \sqrt{x}-\epsilon,\epsilon \in\mathbb{N}\)

            例如当 \(t=3\) 时,设 \(x\) 化为 \(m\) 进制后为 \(\overline{abc}\) ,则 \[x=am^2+bm+c\] 显然有 \(m\le\sqrt{x}-\epsilon,\epsilon \in \mathbb{N}\)

            因此我们暴力枚举所有 \(m\le\sqrt{x}-\epsilon,\epsilon \in \mathbb{N}\) ,对应 \(t\ge 3\) 的情况

            接下来 \(m > \sqrt{x}-\epsilon,\epsilon\in \mathbb{N}\) 的情况,直接枚举 \(m\) 肯定是不行的

            注意到此时 \(x\) 满足 \[x=am+b\] 因为有 \(m >\sqrt{x}-\epsilon,\epsilon \in \mathbb{N}\) ,因此 \(a \le \sqrt{x}-\epsilon^{\prime},\epsilon^{\prime}\in \mathbb{N}\)

            于是我们只要枚举这个 \(a\),然后判断此时的 \(m=\frac{x-(n-a)}{a}\) 是否合法即可

            这里的合法指 \(x\) 在当前 \(m\) 进制下是 \(am+b\)

            时间复杂度 \(O(\sqrt{x})\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int x,n,ans;void check(int m){    if(m<2) return;    int res=0,tmp=x;    while(tmp)    {        res+=tmp%m;        tmp/=m;    }    if(res==n) ans=min(ans,m);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> x >> n; ans=INF;    for(int m=2; m*m<=x; m++) check(m);    if(x==n) ans=min(ans,n+1);    for(int a=1; a*a<=x; a++) check((x-(n-a))/a);    if(ans==INF) cout << "-1\n";    else cout << ans << '\n';    return 0;}
            ]]> @@ -1428,7 +1428,7 @@ /2022/07/28/shu-xue-chang-shu-biao/ - 数学常数表

            常数表

            $e = \tt{2.718281828}$

            $\pi = \tt{3.141592654}$

            $\ln \pi = \tt{1.1447}$

            $\sqrt{\pi} = \tt{1.7725}$

            自然对数表$N$$\ln N$
            $\ln2 = \tt{0.69}$$\tt{2}$$\tt{0.6931}$
            $\ln3 = \tt{1.10}$$\tt{3}$$\tt{1.0986}$
            $\ln5 = \tt{1.61}$$\tt{5}$$\tt{1.6094}$
            $\ln7 = \tt{1.95}$$\tt{7}$$\tt{1.9459}$
            $\ln11 = \tt{2.40}$$\tt{11}$$\tt{2.3979}$
            $\ln13 = \tt{2.56}$$\tt{13}$$\tt{2.5649}$
            $\ln17 = \tt{2.83}$$\tt{17}$$\tt{2.8332}$
            $\ln19 = \tt{2.94}$$\tt{19}$$\tt{2.9444}$
            $\ln23 = \tt{3.14}$$\tt{23}$$\tt{3.1355}$
            $\ln29 = \tt{3.37}$$\tt{29}$$\tt{3.3673}$
            $\ln31 = \tt{3.43}$$\tt{31}$$\tt{3.4340}$
            $\exp$ 表$N$$\exp N$
            $\exp{2} = \tt{7.39}$$\tt{2}$$\tt{7.3891}$
            $\exp{3} = \tt{20.09}$$\tt{3}$$\tt{20.0855}$
            $\exp{5} = \tt{148.41}$$\tt{5}$$\tt{148.4132}$
            $\exp{7} = \tt{1096.63}$$\tt{7}$$\tt{}$$\tt{1096.6332}$
            $\exp{11} = \tt{59874.14}$$\tt{11}$$\tt{59874.1417}$
            $\exp{13} = \tt{442413.39}$$\tt{13}$$\tt{442413.3920}$
            ]]> + 数学常数表

            常数表

            \(e = \tt{2.718281828}\)

            \(\pi = \tt{3.141592654}\)

            \(\ln \pi = \tt{1.1447}\)

            \(\sqrt{\pi} = \tt{1.7725}\)

            自然对数表\(N\)\(\ln N\)
            \(\ln2 = \tt{0.69}\)\(\tt{2}\)\(\tt{0.6931}\)
            \(\ln3 = \tt{1.10}\)\(\tt{3}\)\(\tt{1.0986}\)
            \(\ln5 = \tt{1.61}\)\(\tt{5}\)\(\tt{1.6094}\)
            \(\ln7 = \tt{1.95}\)\(\tt{7}\)\(\tt{1.9459}\)
            \(\ln11 = \tt{2.40}\)\(\tt{11}\)\(\tt{2.3979}\)
            \(\ln13 = \tt{2.56}\)\(\tt{13}\)\(\tt{2.5649}\)
            \(\ln17 = \tt{2.83}\)\(\tt{17}\)\(\tt{2.8332}\)
            \(\ln19 = \tt{2.94}\)\(\tt{19}\)\(\tt{2.9444}\)
            \(\ln23 = \tt{3.14}\)\(\tt{23}\)\(\tt{3.1355}\)
            \(\ln29 = \tt{3.37}\)\(\tt{29}\)\(\tt{3.3673}\)
            \(\ln31 = \tt{3.43}\)\(\tt{31}\)\(\tt{3.4340}\)
            \(\exp\)\(N\)\(\exp N\)
            \(\exp{2} = \tt{7.39}\)\(\tt{2}\)\(\tt{7.3891}\)
            \(\exp{3} = \tt{20.09}\)\(\tt{3}\)\(\tt{20.0855}\)
            \(\exp{5} = \tt{148.41}\)\(\tt{5}\)\(\tt{148.4132}\)
            \(\exp{7} = \tt{1096.63}\)\(\tt{7}\)\(\tt{}\)\(\tt{1096.6332}\)
            \(\exp{11} = \tt{59874.14}\)\(\tt{11}\)\(\tt{59874.1417}\)
            \(\exp{13} = \tt{442413.39}\)\(\tt{13}\)\(\tt{442413.3920}\)
            ]]> @@ -1453,7 +1453,7 @@ /2022/07/28/cf906d-power-tower-ti-jie/ - CF906D Power Tower 题解

            题目链接:CF906D Power Tower

            题意:给定长度为 $n$ 的序列 $a_i$ 和模数 $p$

            $Q$ 次询问区间 $[l,r]$ 的

            $1 \le n \le 10^5,~1 \le p,a_i \le 10^9,~1\le Q \le 10^5$

            显然扩展欧拉定理的应用

            关于 $O(\varphi^*(n)=1)\approx O(\log n)$ 的证明,可以看这里 link

            注:这里的 $O(\varphi^*(n)=1)$ 指最小的 $x$ 使得 $\varphi^{x}(n)=1$

            因此对于每个询问,我们直接从左往右跑就好了,递归实现

            注意快速幂要满足扩欧的性质哦,不要乱模

            时间复杂度 $O(Q \log \max\{a_i\})$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <unordered_map>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)int n,m,a[N];unordered_map<int,int>phi;int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0) n/=i;        }    if(n>1) ans=ans/n*(n-1);    return ans;}int qpow(int a,int b,int p){    int ans=1,base=a;    while(b)    {        if(b&1)        {            ans=ans*base;            if(ans>=p) ans=ans%p+p;        }        base=base*base;        if(base>=p) base=base%p+p;        b>>=1;    }    return ans;}int solve(int x,int p,int r){    if(p==1||x==r+1) return 1;    return qpow(a[x],solve(x+1,phi[p],r),p);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(m); phi[1]=1;    for(int now=m; now!=1; now=phi[now])        phi[now]=Euler_phi(now);    for(int i=1; i<=n; i++) read(a[i]);    int Q; read(Q);    for(int l,r; Q--; )    {        read(l); read(r);        write(solve(l,m,r)%m); pc('\n');    }    return 0;}
            ]]> + CF906D Power Tower 题解

            题目链接:CF906DPower Tower

            题意:给定长度为 \(n\) 的序列 \(a_i\) 和模数 \(p\)

            \(Q\) 次询问区间 \([l,r]\) 的 \[a_l^{ {a_{l+1}^{ {a_{l+2}^{ ^{ {.}^{ {.}^{ {.}^{ {a_{r} } } } } } } } }} }\] \(1 \le n \le 10^5,~1 \le p,a_i \le10^9,~1\le Q \le 10^5\)

            显然扩展欧拉定理的应用

            关于 \(O(\varphi^*(n)=1)\approx O(\logn)\) 的证明,可以看这里 link

            注:这里的 \(O(\varphi^*(n)=1)\)指最小的 \(x\) 使得 \(\varphi^{x}(n)=1\)

            因此对于每个询问,我们直接从左往右跑就好了,递归实现

            注意快速幂要满足扩欧的性质哦,不要乱模

            时间复杂度 \(O(Q \log\max\{a_i\})\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <unordered_map>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)int n,m,a[N];unordered_map<int,int>phi;int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0) n/=i;        }    if(n>1) ans=ans/n*(n-1);    return ans;}int qpow(int a,int b,int p){    int ans=1,base=a;    while(b)    {        if(b&1)        {            ans=ans*base;            if(ans>=p) ans=ans%p+p;        }        base=base*base;        if(base>=p) base=base%p+p;        b>>=1;    }    return ans;}int solve(int x,int p,int r){    if(p==1||x==r+1) return 1;    return qpow(a[x],solve(x+1,phi[p],r),p);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(m); phi[1]=1;    for(int now=m; now!=1; now=phi[now])        phi[now]=Euler_phi(now);    for(int i=1; i<=n; i++) read(a[i]);    int Q; read(Q);    for(int l,r; Q--; )    {        read(l); read(r);        write(solve(l,m,r)%m); pc('\n');    }    return 0;}
            ]]> @@ -1480,7 +1480,7 @@ /2022/07/28/ju-ti-shu-xue-2.3-he-shi-de-chu-li/ - 2.3和式的处理

            设 $K$ 为任意一个有限整数集合,则有

            扰动法求解未知和式的封闭形式

            然后通过将它的最后一项和第一项分离出来,用两种方法重新改写 $S_{n+1}$

            然后尝试用 $S_n$ 将它表示出来

            例子:求一般的几何级数的和

            由 $(1)$ 中一般的扰动法格式可得

            显然有

            练习:求下列和式的封闭形式

            答案:

            过程


            ]]> + 2.3和式的处理

            \(K\)为任意一个有限整数集合,则有 \[\begin{aligned}\sum_{k\in K}ca_k&=c\sum_{k\in K}a_k &(\text{分配律})\\\\\sum_{k\in K}(a_k+b_k) &= \sum_{k\in K}a_k + \sum_{k\in K}b_k&(\text{结合律})\\\\\sum_{k\in K}a_k&=\sum_{p(k)\in K}a_{p(k)} &(\text{交换律})\end{aligned}\] 扰动法求解未知和式的封闭形式 \[S_n = \sum_{0 \le k\le n}a_k\] 然后通过将它的最后一项和第一项分离出来,用两种方法重新改写\(S_{n+1}\) \[\begin{aligned}S_{n}+a_{n+1} = \sum_{0 \le k\le n+1}a_k &= a_0+\sum_{1\le k \len+1} a_k\\\\&=a_0+\sum_{1 \le k+1 \le n+1}a_{k+1}\\\\&=a_0+\sum_{0\le k\le n}a_{k+1}\end{aligned}\] 即 \[S_n+a_{n+1}=a_0+\sum_{0\le k\le n}a_{k+1}\tag{1}\] 然后尝试用 \(S_n\)将它表示出来

            例子:求一般的几何级数的和 \[S_n = \sum_{0\le k\le n}ax^k\]\((1)\)中一般的扰动法格式可得 \[S_n + ax^{n+1} = ax^{0}+\sum_{0\le k\le n}ax^{k+1}\] 显然有 \[S_n+ax^{n+1}=ax^0+xS_n\]\[S_n=\dfrac{a-ax^{n+1}}{1-x}\] 练习:求下列和式的封闭形式 \[S_n = \sum_{0 \le k \le n}k2^k\] 答案: \[S_n=(n-1)2^{n+1}+2\]

            过程

            \[S_n + a_{n+1} = a_0 + \sum_{0 \le k \le n} a_{k+1}\\\\S_n + (n+1)2^{n+1} = 2\sum_{0\le k\le n}k2^k + \sum_{0\le k\len}2^{k+1}\\\\S_n + (n+1)2^{n+1} = 2S_n + 2^{n+2}-2\\\\S_n = (n-1)2^{n+1}+2\]


            ]]> @@ -1505,7 +1505,7 @@ /2022/07/28/cf1485c-floor-and-mod-ti-jie/ - CF1485C Floor and Mod 题解

            题目链接:CF1485C Floor and Mod

            题意:求 $1\le a\le x,1\le b\le y$ 且 $\lfloor\frac{a}{b}\rfloor =a\bmod b$ 的$(a,b)$ 个数

            这道题有点麻烦 QAQ

            先推一波柿子

            显然 $b +1 \mid a$ ,同时因为 $a \bmod b <b$

            所以 $\dfrac{a}{b+1} < b \Rightarrow a<b^2 + b \Rightarrow a \in [0,b^2+b-1]$

            则满足条件的 $a$ 的个数为 $\left\lfloor\dfrac{\min\{x,b^2+b-1\}}{b+1}\right\rfloor$

            答案就是

            但是这个东西好像不好用数论分块

            仔细观察,当 $b \ge \sqrt{x}-\epsilon,\epsilon\in \mathbb{N}$ 时,柿子是这样的

            其实写成 $\sum\limits_{b=2}^{y+1}\left\lfloor\dfrac{x} {b}\right\rfloor$ 可以更明显地看出这是个数论分块的板子

            对于 $b < \sqrt{x}-\epsilon,\epsilon\in \mathbb{N}$ 的情况,直接暴力枚举即好了

            时间复杂度 $O(Q \sqrt{x})$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q,a,b,x,y,res;    cin >> Q;    while(Q--)    {        cin >> x >> y;        a=1,b=1,res=0;        for(; b*b+b-1<x&&b<=y; b++)            res+=(b*b+b-1)/(b+1);        for(int l=b+1,r; l<=min(x,y+1); l=r+1)        {            r=min(x/(x/l),y+1);            res+=x/l*(r-l+1);        }        cout << res << '\n';    }    return 0;}
            ]]> + CF1485C Floor and Mod 题解

            题目链接:CF1485CFloor and Mod

            题意:求 \(1\le a\lex,1\le b\le y\)\(\lfloor\frac{a}{b}\rfloor =a\bmod b\)的\((a,b)\) 个数

            这道题有点麻烦 QAQ

            先推一波柿子 \[\begin{aligned}\left\lfloor{\frac{a}{b}}\right\rfloor &= a \bmod b\\\\\left\lfloor{\frac{a}{b}}\right\rfloor &=a-\left\lfloor{\frac{a}{b}}\right\rfloor\times b\\\\\left\lfloor{\frac{a}{b}}\right\rfloor &=\frac{a}{b+1}\end{aligned}\] 显然 \(b +1 \mid a\),同时因为 \(a \bmod b <b\)

            所以 \(\dfrac{a}{b+1} < b \Rightarrowa<b^2 + b \Rightarrow a \in [0,b^2+b-1]\)

            则满足条件的 \(a\) 的个数为 \(\left\lfloor\dfrac{\min\{x,b^2+b-1\}}{b+1}\right\rfloor\)

            答案就是 \[\sum_{b=1}^{y}\left\lfloor\dfrac{\min\{x,b^2+b-1\}}{b+1}\right\rfloor\] 但是这个东西好像不好用数论分块

            仔细观察,当 \(b \ge\sqrt{x}-\epsilon,\epsilon\in \mathbb{N}\) 时,柿子是这样的 \[\sum_{b=1}^{y}\left\lfloor\dfrac{x}{b+1}\right\rfloor\] 其实写成 \(\sum\limits_{b=2}^{y+1}\left\lfloor\dfrac{x}{b}\right\rfloor\) 可以更明显地看出这是个数论分块的板子

            对于 \(b < \sqrt{x}-\epsilon,\epsilon\in\mathbb{N}\) 的情况,直接暴力枚举即好了

            时间复杂度 \(O(Q \sqrt{x})\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q,a,b,x,y,res;    cin >> Q;    while(Q--)    {        cin >> x >> y;        a=1,b=1,res=0;        for(; b*b+b-1<x&&b<=y; b++)            res+=(b*b+b-1)/(b+1);        for(int l=b+1,r; l<=min(x,y+1); l=r+1)        {            r=min(x/(x/l),y+1);            res+=x/l*(r-l+1);        }        cout << res << '\n';    }    return 0;}
            ]]> @@ -1532,7 +1532,7 @@ /2022/07/28/cf427c-checkposts-ti-jie/ - CF427C Checkposts 题解

            题目链接:CF427C Checkposts

            题意

            懒得贴翻译,那个翻译太烂了

            Your city has $ n $ junctions. There are $ m $ one-way roads between the junctions. As a mayor of the city, you have to ensure the security of all the junctions.

            To ensure the security, you have to build some police checkposts. Checkposts can only be built in a junction. A checkpost at junction $ i $ can protect junction $ j $ if either $ i=j $ or the police patrol car can go to $ j $ from $ i $ and then come back to $ i $ .

            Building checkposts costs some money. As some areas of the city are more expensive than others, building checkpost at some junctions might cost more money than other junctions.

            You have to determine the minimum possible money needed to ensure the security of all the junctions. Also you have to find the number of ways to ensure the security in minimum price and in addition in minimum number of checkposts. Two ways are different if any of the junctions contains a checkpost in one of them and do not contain in the other.

            不难发现,一个强连通分量内的答案就是最小的那个花费

            而这个强连通分量里的方案数就是和最小花费相等的结点数

            然后求个强连通分量,乘法原理搞一搞就好了

            时间复杂度 $O(n + m)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)const int p=1e9+7;vector<int> g1[N],g2[N];int n,m,idx,scnt;int sz[N],val[N],dfn[N],scc[N],mn[N],vis[N];void addEdge1(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u])        if(!vis[v]) dfs1(v);    dfn[++idx]=u;}void dfs2(int u,int id){    scc[u]=id;    mn[id]=min(mn[id],val[u]);    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=n; i++)        if(!vis[i]) dfs1(i);    scnt=0;    for(int i=n; i>=1; i--)        if(!scc[dfn[i]])        {            ++scnt;            dfs2(dfn[i],scnt);        }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> val[i],mn[i]=INF;    cin >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge1(u,v);    }    kosaraju();    for(int i=1; i<=n; i++)        if(val[i]==mn[scc[i]])++sz[scc[i]];    int ans1=0,ans2=1;    for(int i=1; i<=scnt; i++)        ans1+=mn[i],ans2=ans2*sz[i]%p;    cout << ans1 << ' ' << ans2 << '\n';    return 0;}
            ]]> + CF427C Checkposts 题解

            题目链接:CF427CCheckposts

            题意

            懒得贴翻译,那个翻译太烂了

            Your city has $ n $ junctions. There are $ m $ one-way roads betweenthe junctions. As a mayor of the city, you have to ensure the securityof all the junctions.

            To ensure the security, you have to build some police checkposts.Checkposts can only be built in a junction. A checkpost at junction $ i$ can protect junction $ j $ if either $ i=j $ or the police patrol carcan go to $ j $ from $ i $ and then come back to $ i $ .

            Building checkposts costs some money. As some areas of the city aremore expensive than others, building checkpost at some junctions mightcost more money than other junctions.

            You have to determine the minimum possible money needed to ensure thesecurity of all the junctions. Also you have to find the number of waysto ensure the security in minimum price and in addition in minimumnumber of checkposts. Two ways are different if any of the junctionscontains a checkpost in one of them and do not contain in the other.

            不难发现,一个强连通分量内的答案就是最小的那个花费

            而这个强连通分量里的方案数就是和最小花费相等的结点数

            然后求个强连通分量,乘法原理搞一搞就好了

            时间复杂度 \(O(n + m)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)const int p=1e9+7;vector<int> g1[N],g2[N];int n,m,idx,scnt;int sz[N],val[N],dfn[N],scc[N],mn[N],vis[N];void addEdge1(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u])        if(!vis[v]) dfs1(v);    dfn[++idx]=u;}void dfs2(int u,int id){    scc[u]=id;    mn[id]=min(mn[id],val[u]);    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=n; i++)        if(!vis[i]) dfs1(i);    scnt=0;    for(int i=n; i>=1; i--)        if(!scc[dfn[i]])        {            ++scnt;            dfs2(dfn[i],scnt);        }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> val[i],mn[i]=INF;    cin >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge1(u,v);    }    kosaraju();    for(int i=1; i<=n; i++)        if(val[i]==mn[scc[i]])++sz[scc[i]];    int ans1=0,ans2=1;    for(int i=1; i<=scnt; i++)        ans1+=mn[i],ans2=ans2*sz[i]%p;    cout << ans1 << ' ' << ans2 << '\n';    return 0;}
            ]]> @@ -1559,7 +1559,7 @@ /2022/07/28/luo-gu-p2424-yue-shu-he-ti-jie/ - 洛谷P2424 约数和 题解

            题目链接:P2424 约数和

            题意

            对于一个数 $X$,函数 $f(X)$ 表示 $X$ 所有约数的和。例如:$f(6)=1+2+3+6=12$。对于一个 $X$,Smart 可以很快的算出 $f(X)$。现在的问题是,给定两个正整数 $X,Y(X<Y)$,Smart 希望尽快地算出 $f(X)+f(X+1)+\dots +f(Y)$的值,你能帮助 Smart 算出这个值吗?

            对于 $100\%$ 的数据有 $1\leq X<Y\leq 2\times 10^9$。

            设 $f(n) = \sum_{i=1}^{n} g(i)$

            其中 $g(i)$ 表示 $i$ 的约数个数和

            变换枚举顺序

            不难发现,$\sum_{i=1}^{n} [j \mid i]$ 其实就是求 $1$ 到 $n$ 中包含约束 $j$ 的数的数量,则

            也就是

            然后就是数论分块板子了 awa,答案就是 $f(r)-f(l-1)$

            这里前缀和是 $\sum_{i=l}^{r} = \dfrac{1}{2}(l+r)\times(r-l+1)$

            有没有一种可能,我居然差点忘了这个前缀和是怎么算的

            时间复杂度 $O(\sqrt{n})$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int solve(int n){    int res=0;    for(int l=1,r; l<=n; l=r+1)    {        r=n/(n/l);        res+=(n/l)*(r-l+1)*(l+r)/2;    }    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int l,r; cin >> l >> r;    cout << solve(r)-solve(l-1) << '\n';    return 0;}
            ]]> + 洛谷P2424 约数和 题解

            题目链接:P2424约数和

            题意

            对于一个数 \(X\),函数 \(f(X)\) 表示 \(X\) 所有约数的和。例如:\(f(6)=1+2+3+6=12\)。对于一个 \(X\),Smart 可以很快的算出 \(f(X)\)。现在的问题是,给定两个正整数 \(X,Y(X<Y)\),Smart 希望尽快地算出 \(f(X)+f(X+1)+\dots +f(Y)\)的值,你能帮助Smart 算出这个值吗?

            对于 \(100\%\) 的数据有 \(1\leq X<Y\leq 2\times 10^9\)。

            \(f(n) = \sum_{i=1}^{n}g(i)\)

            其中 \(g(i)\) 表示 \(i\) 的约数个数和 \[\begin{aligned}f(n) &= \sum_{i=1}^{n} \sum_{j \mid i}^{n} j\end{aligned}\] 变换枚举顺序 \[\sum_{j=1}^{n}j\sum_{i=1}^{n} [j \mid i]\] 不难发现,\(\sum_{i=1}^{n} [j \midi]\) 其实就是求 \(1\)\(n\) 中包含约束 \(j\) 的数的数量,则 \[\sum_{j=1}^{n} j \left\lfloor\frac{n}{j}\right\rfloor\] 也就是 \[f(n) = \sum_{i=1}^{n} i \left\lfloor\frac{n}{i}\right\rfloor\] 然后就是数论分块板子了 awa,答案就是 \(f(r)-f(l-1)\)

            这里前缀和是 \(\sum_{i=l}^{r} =\dfrac{1}{2}(l+r)\times(r-l+1)\)

            有没有一种可能,我居然差点忘了这个前缀和是怎么算的

            时间复杂度 \(O(\sqrt{n})\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int solve(int n){    int res=0;    for(int l=1,r; l<=n; l=r+1)    {        r=n/(n/l);        res+=(n/l)*(r-l+1)*(l+r)/2;    }    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int l,r; cin >> l >> r;    cout << solve(r)-solve(l-1) << '\n';    return 0;}
            ]]> @@ -1586,7 +1586,7 @@ /2022/07/28/cf1295d-same-gcds-ti-jie/ - CF1295D Same GCDs 题解

            题目链接:CF1295D Same GCDs

            题意

            多组测试数据, $T \leq 50,\ 1 \leq a < m \leq 10^{10}$

            设 $p=\gcd(a,m)$ ,则原式可化为

            即求

            时间复杂度 $O(Q\sqrt{n})$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int gcd(int a,int b){return b==0?a:gcd(b,a%b);}int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0) n/=i;        }    if(n>1) ans=ans/n*(n-1);    return ans;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; cin >> Q;    for(int a,m; Q--;)    {        cin >> a >> m;        cout << Euler_phi(m/gcd(a,m)) << '\n';    }    return 0;}
            ]]> + CF1295D Same GCDs 题解

            题目链接:CF1295DSame GCDs

            题意

            \[\sum_{x=0}^{m-1} [\gcd(a, m) = \gcd(a + x, m)]\] 多组测试数据, \(T \leq 50,\ 1 \leq a< m \leq 10^{10}\)

            \(p=\gcd(a,m)\) ,则原式可化为\[\begin{aligned}\sum_{x=0}^{m-1} [\gcd(a+x,m) = p] &=\sum_{x=0}^{m-1}\left[\gcd\left(\frac{a+x}{p},\frac{m}{p}\right)=1\right]\\\\&=\sum_{x=0}^{m-1}\left[\gcd\left(\left(\frac{a+x}{p} \bmod\frac{m}{p}\right),\frac{m}{p}\right)=1\right]\\\\&= \varphi\left(\frac{m}{p}\right)\end{aligned}\] 即求 \[\varphi\left(\frac{m}{\gcd(a,m)}\right)\] 时间复杂度 \(O(Q\sqrt{n})\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int gcd(int a,int b){return b==0?a:gcd(b,a%b);}int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0) n/=i;        }    if(n>1) ans=ans/n*(n-1);    return ans;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; cin >> Q;    for(int a,m; Q--;)    {        cin >> a >> m;        cout << Euler_phi(m/gcd(a,m)) << '\n';    }    return 0;}
            ]]> @@ -1613,7 +1613,7 @@ /2022/07/28/mo-ni-sai-ti-jiang-jie-8/ - 模拟赛题讲解[8]

            来自 Roundgod 2022-07-27 noi.ac #2681

            题目描述

            给定一个有向图 $G=(V,E)$ ,其中顶点个数 $\vert V\vert=n$, 边数 $\vert E\vert=m$。顶点编号为 $1-n$ ,边的编号为 $1-m$ ,第 $i$ 条边连接顶点 $a_i$ 和 $b_i$ 。

            对于图中的每个顶点 $v$ ,你需要判断它属于以下哪种:

            如果不存在从 $1$ 到 $v$ 的路径,输出 $0$ 。

            如果存在恰好一条从 $1$ 到 $v$ 的路径,输出 $1$ 。

            如果存在多于一条且有限的从 $1$ 到 $v$ 的路径,输出 $2$ 。

            如果存在无限条从 $1$ 到 $v$ 的路径,输出 $3$ 。

            输入格式

            输入第一行包含 $2$ 个整数 $n,m$,分别表示图的顶点数和图的边数。

            接下来 $m$行,每行包含两个整数 $a_i,b_i(1\leq a_i,b_i\leq n,a_i\neq b_i)$ ,表示第 $i$ 条边从顶点 $a_i$ 指向顶点 $b_i$ 。

            题目保证图中不包含任何重边和自环。

            输出格式

            在一行中输出 $n$ 个在 $\{0,1,2,3\}$ 中的数字,第 $i$ 个表示顶点 $i$ 属于的种类。

            输入1

            3 31 22 33 1

            输出1

            3 3 3

            输入2

            5 0

            输出2

            1 0 0 0 0

            输入3

            4 41 22 31 44 3

            输出3

            1 1 2 1

            数据范围

            对于 $50\%$ 的数据,$1\leq n\leq 10^5,~1\leq m\leq 5\times 10^5$ ,并且保证图中不存在环。

            对于 $100\%$ 的数据,$1\leq n\leq 10^5,~1\leq m\leq 5\times 10^5$。

            题解

            注意是路径,不是简单路径!

            不难想到,先强连通分量缩点一下

            如果存在一条 $1$ 到 $v$ 的路径包含了一个强连通分量大小超过 $1$ 的结点

            那么他们的路径就有无数条

            其他么简单 $\text{topo}$ 一下就好了,或者 $\text{bfs}$ 估计也是可以的

            然后q779又写挂了23333

            时间复杂度 $O(n+m)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)bool vis[N];vector<int> g1[N],g2[N],vec[N];int n,m,idx,scnt,in[N],inc[N];int sz[N],f[N],ok[N],scc[N],dfn[N];void addEdge(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u])        if(!vis[v]) dfs1(v);    dfn[++idx]=u;}void dfs2(int u,int id){    scc[u]=id; ++sz[id];    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=n; i++)        if(!vis[i]) dfs1(i);    for(int i=n; i>=1; i--)        if(!scc[dfn[i]])        {            ++scnt;            dfs2(dfn[i],scnt);        }}void topo(){    queue<int> q; ok[scc[1]]=1; f[scc[1]]=1;    for(int i=1; i<=scnt; i++)        if(!in[i]) q.push(i);    while(!q.empty())    {        int u=q.front(); q.pop();        if(ok[u]&&sz[u]>1) inc[u]=1;        for(int v : vec[u])        {            ok[v]|=ok[u]; inc[v]|=inc[u];            f[v]=min(2ll,f[u]+f[v]);            if(!(--in[v])) q.push(v);        }    }    for(int i=1,u; i<=n; i++)    {        u=scc[i];        if(!ok[u]) cout << "0";        else if(inc[u]) cout << "3";        else cout << f[u];        cout << " \n"[i==n];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge(u,v);    }    kosaraju();    for(int i=1; i<=n; i++)        for(int j : g1[i])        {            int u=scc[i],v=scc[j];            if(u!=v) vec[u].push_back(v),++in[v];        }    topo();    return 0;}
            ]]> + 模拟赛题讲解[8]

            来自 Roundgod2022-07-27 noi.ac #2681

            题目描述

            给定一个有向图 \(G=(V,E)\),其中顶点个数 \(\vert V\vert=n\), 边数\(\vert E\vert=m\)。顶点编号为 \(1-n\) ,边的编号为 \(1-m\) ,第 \(i\) 条边连接顶点 \(a_i\) 和 \(b_i\) 。

            对于图中的每个顶点 \(v\),你需要判断它属于以下哪种:

            如果不存在从 \(1\)\(v\) 的路径,输出 \(0\) 。

            如果存在恰好一条从 \(1\)\(v\) 的路径,输出 \(1\) 。

            如果存在多于一条且有限的从 \(1\)\(v\) 的路径,输出 \(2\) 。

            如果存在无限条从 \(1\)\(v\) 的路径,输出 \(3\) 。

            输入格式

            输入第一行包含 \(2\) 个整数 \(n,m\),分别表示图的顶点数和图的边数。

            接下来 \(m\)行,每行包含两个整数\(a_i,b_i(1\leq a_i,b_i\leq n,a_i\neqb_i)\) ,表示第 \(i\) 条边从顶点\(a_i\) 指向顶点 \(b_i\) 。

            题目保证图中不包含任何重边和自环。

            输出格式

            在一行中输出 \(n\) 个在 \(\{0,1,2,3\}\) 中的数字,第 \(i\) 个表示顶点 \(i\) 属于的种类。

            输入1

            3 31 22 33 1

            输出1

            3 3 3

            输入2

            5 0

            输出2

            1 0 0 0 0

            输入3

            4 41 22 31 44 3

            输出3

            1 1 2 1

            数据范围

            对于 \(50\%\) 的数据,\(1\leq n\leq 10^5,~1\leq m\leq 5\times10^5\) ,并且保证图中不存在环。

            对于 \(100\%\) 的数据,\(1\leq n\leq 10^5,~1\leq m\leq 5\times10^5\)。

            题解

            注意是路径,不是简单路径!

            不难想到,先强连通分量缩点一下

            如果存在一条 \(1\)\(v\) 的路径包含了一个强连通分量大小超过\(1\) 的结点

            那么他们的路径就有无数条

            其他么简单 \(\text{topo}\)一下就好了,或者 \(\text{bfs}\)估计也是可以的

            然后q779又写挂了23333

            时间复杂度 \(O(n+m)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)bool vis[N];vector<int> g1[N],g2[N],vec[N];int n,m,idx,scnt,in[N],inc[N];int sz[N],f[N],ok[N],scc[N],dfn[N];void addEdge(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u])        if(!vis[v]) dfs1(v);    dfn[++idx]=u;}void dfs2(int u,int id){    scc[u]=id; ++sz[id];    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=n; i++)        if(!vis[i]) dfs1(i);    for(int i=n; i>=1; i--)        if(!scc[dfn[i]])        {            ++scnt;            dfs2(dfn[i],scnt);        }}void topo(){    queue<int> q; ok[scc[1]]=1; f[scc[1]]=1;    for(int i=1; i<=scnt; i++)        if(!in[i]) q.push(i);    while(!q.empty())    {        int u=q.front(); q.pop();        if(ok[u]&&sz[u]>1) inc[u]=1;        for(int v : vec[u])        {            ok[v]|=ok[u]; inc[v]|=inc[u];            f[v]=min(2ll,f[u]+f[v]);            if(!(--in[v])) q.push(v);        }    }    for(int i=1,u; i<=n; i++)    {        u=scc[i];        if(!ok[u]) cout << "0";        else if(inc[u]) cout << "3";        else cout << f[u];        cout << " \n"[i==n];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge(u,v);    }    kosaraju();    for(int i=1; i<=n; i++)        for(int j : g1[i])        {            int u=scc[i],v=scc[j];            if(u!=v) vec[u].push_back(v),++in[v];        }    topo();    return 0;}
            ]]> @@ -1640,7 +1640,7 @@ /2022/07/28/mo-ni-sai-ti-jiang-jie-7/ - 模拟赛题讲解[7]

            来自 Roundgod 2022-07-27 noi.ac #2682

            题目描述

            给定一个有向图 $G=(V,E)$ ,其中顶点个数 $\vert V\vert=n$ ,边数 $\vert E\vert=m$ 。顶点编号为 $1-n$ ,边的编号为 $1-m$ ,第 $i$ 条边连接顶点 $a_i$ 和 $b_i$。

            你最少需要添加多少条有向边,才能使得能够从 $1$ 到达所有顶点?

            输入格式

            输入第一行包含 $2$个整数 $n,m$,分别表示图的顶点数和图的边数。

            接下来 $m$行,每行包含两个整数 $a_i,b_i(1\leq a_i,b_i\leq n,a_i\neq b_i)$ ,表示第 $i$ 条边从顶点 $a_i$ 指向顶点 $b_i$。

            输入保证图中不存在重边和自环。

            输出格式

            在一行中输出一个整数,表示最少需要添加的有向边条数。

            输入1

            9 91 21 32 31 55 66 11 89 87 1

            输出1

            3

            输入2

            5 41 22 33 44 1

            输出2

            1

            数据范围

            对于 $50\%$ 的数据,$1\leq n\leq 5000,~1\leq m\leq 5000$

            对于 $100\%$ 的数据,$1\leq n\leq 2\times 10^5,~1\leq m\leq 2\times 10^5$

            题解

            先强连通分量缩点一下,正确性显然。

            然后对于 $1$ 本来就能到的结点,根本不用管

            对于 $1$ 不能到的结点,我们只要连入度为 $0$ 的那些就行了

            因为连了那些点,其他点都可以走到

            时间复杂度 $O(n+m)$

            我的考场代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)bool vis[N];int n,m,idx,scnt,dfn[N],in[N],scc[N];vector<int> g1[N],g2[N];void addEdge1(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u])        if(!vis[v]) dfs1(v);    dfn[++idx]=u;}void dfs2(int u,int id){    scc[u]=id;    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=n; i++)        if(!vis[i]) dfs1(i);    scnt=0;    for(int i=n; i>=1; i--)        if(!scc[dfn[i]])        {            ++scnt;            dfs2(dfn[i],scnt);        }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge1(u,v);    }    kosaraju();    for(int i=1; i<=n; i++)        for(int j : g1[i])        {            int u=scc[i],v=scc[j];            if(u!=v) ++in[v];        }    int res=0;    // for(int i=1; i<=n; i++)    //     cout << scc[i] << " \n"[i==n];    for(int i=1; i<=scnt; i++)        if(i!=scc[1]) res+=(in[i]==0);    cout << res << '\n';    return 0;}
            ]]> + 模拟赛题讲解[7]

            来自 Roundgod2022-07-27 noi.ac #2682

            题目描述

            给定一个有向图 \(G=(V,E)\),其中顶点个数 \(\vert V\vert=n\),边数 \(\vert E\vert=m\) 。顶点编号为\(1-n\) ,边的编号为 \(1-m\) ,第 \(i\) 条边连接顶点 \(a_i\) 和 \(b_i\)。

            你最少需要添加多少条有向边,才能使得能够从 \(1\) 到达所有顶点?

            输入格式

            输入第一行包含 \(2\)个整数 \(n,m\),分别表示图的顶点数和图的边数。

            接下来 \(m\)行,每行包含两个整数\(a_i,b_i(1\leq a_i,b_i\leq n,a_i\neqb_i)\) ,表示第 \(i\) 条边从顶点\(a_i\) 指向顶点 \(b_i\)。

            输入保证图中不存在重边和自环。

            输出格式

            在一行中输出一个整数,表示最少需要添加的有向边条数。

            输入1

            9 91 21 32 31 55 66 11 89 87 1

            输出1

            3

            输入2

            5 41 22 33 44 1

            输出2

            1

            数据范围

            对于 \(50\%\) 的数据,\(1\leq n\leq 5000,~1\leq m\leq 5000\)

            对于 \(100\%\) 的数据,\(1\leq n\leq 2\times 10^5,~1\leq m\leq 2\times10^5\)

            题解

            先强连通分量缩点一下,正确性显然。

            然后对于 \(1\)本来就能到的结点,根本不用管

            对于 \(1\)不能到的结点,我们只要连入度为 \(0\)的那些就行了

            因为连了那些点,其他点都可以走到

            时间复杂度 \(O(n+m)\)

            我的考场代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)bool vis[N];int n,m,idx,scnt,dfn[N],in[N],scc[N];vector<int> g1[N],g2[N];void addEdge1(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u])        if(!vis[v]) dfs1(v);    dfn[++idx]=u;}void dfs2(int u,int id){    scc[u]=id;    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=n; i++)        if(!vis[i]) dfs1(i);    scnt=0;    for(int i=n; i>=1; i--)        if(!scc[dfn[i]])        {            ++scnt;            dfs2(dfn[i],scnt);        }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge1(u,v);    }    kosaraju();    for(int i=1; i<=n; i++)        for(int j : g1[i])        {            int u=scc[i],v=scc[j];            if(u!=v) ++in[v];        }    int res=0;    // for(int i=1; i<=n; i++)    //     cout << scc[i] << " \n"[i==n];    for(int i=1; i<=scnt; i++)        if(i!=scc[1]) res+=(in[i]==0);    cout << res << '\n';    return 0;}
            ]]> @@ -1667,7 +1667,7 @@ /2022/07/27/mo-ni-sai-ti-jiang-jie-6/ - 模拟赛题讲解[6]

            来自 Roundgod 2022-07-27 noi.ac #2683

            题目描述

            给定一个连通无向图 $G=(V,E)$ ,其中顶点个数 $\vert V\vert=n$ , 边数 $\vert E\vert=m$ 。顶点编号为 $1-n$ , 边的编号为 $1-m$ ,第 $i$条边连接顶点 $a_i$ 和 $b_i$。输入保证图中不存在重边。

            你现在需要对图中的每条边定向: 对于原图中的每条无向边 $(u,v)$,你需要选择将它变为有向边 $(u,v)$或者有向边 $(v,u)$.

            对于图 $G$中的每个顶点 $v\in V$,定义 $r_v$ 为将图定向之后 $v$ 能够到达的顶点个数,你需要选择一种定向使得最大化 $\min\limits_{v \in V}r_v$,也就是最大化所有 $r_v$ 的 最小值。输出这个最大化的值。

            输入格式

            输入第一行包含 $2$个整数 $n,m$,分别表示图的顶点数和图的边数。

            接下来 $m$行,每行包含两个整数 $a_i,b_i(1\leq a_i,b_i\leq n,a_i\neq b_i)$ ,表示第 $i$条边连接顶点 $a_i$ 和顶点 $b_i$ 。

            输出格式

            在一行中输出一个整数,表示答案。

            输入1

            4 41 22 33 44 1

            输出1

            4

            输入2

            7 94 32 67 14 17 33 57 46 52 5

            输出2

            4

            数据范围

            对于 $20\%$ 的数据,$1\leq n\leq 20,~1\leq m\leq 50$

            对于 $50\%$ 的数据,$1\leq n\leq 2000,~1\leq m\leq 5000$

            对于 $100\%$ 的数据,$1\leq n\leq 10^5,~1\leq m\leq 2\times 10^5$

            题解

            不难发现,一个边双连通分量,一定可以通过给边标向变成强连通分量

            而这道题的答案,其实就是所有边双连通分量中最大的那个所包含的结点数

            证明

            首先我们先证明答案至多为 $\max\left\{|V^{\prime}_i|\right\}$

            不难发现,我们对所有的桥(割边)标向后,一定会有一个边双无出边

            而这个边双的大小 $|V_i^{\prime}|$ 会影响全局的答案,于是要贪心的使这个大小取最大。证毕。

            然后我们证明答案至少为 $\max\left\{|V_i^{\prime}|\right\}$

            以这个最大的边双为根节点(边双缩点后一定是棵树),其他的结点全部往父亲指

            这样每个边双至少可以走到这个满足 $|V^{\prime}|=\max\left\{|V_i^{\prime}|\right\}$ 的 $V^{\prime}$ 。证毕。

            你敢信这段东西我花了3min速记的,不然会忘记老师讲的证明的,2333

            时间复杂度 $O(n+m)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)#define M (int)(2e5+15)int n,m,top,pos=1,ecnt,dfncnt;int dfn[N],low[N],head[N],stk[N],sz[N],ecc[N];struct Edge{int u,v,next;}e[M<<1];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void tarjan(int u,int in_edge){    dfn[u]=low[u]=++dfncnt;    stk[++top]=u;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(!dfn[v])        {            tarjan(v,i);            low[u]=min(low[u],low[v]);        }else if(i!=(in_edge^1))            low[u]=min(low[u],dfn[v]);    }    if(low[u]==dfn[u])    {        ecc[u]=++ecnt; ++sz[ecnt];        while(stk[top]!=u)            ecc[stk[top--]]=ecnt,++sz[ecnt];        --top;    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge(u,v); addEdge(v,u);    }    for(int i=1; i<=n; i++)        if(!dfn[i]) top=0,tarjan(i,0);    int res=0;    for(int i=1; i<=ecnt; i++)        res=max(res,sz[i]);    cout << res << '\n';    return 0;}
            ]]> + 模拟赛题讲解[6]

            来自 Roundgod2022-07-27 noi.ac #2683

            题目描述

            给定一个连通无向图 \(G=(V,E)\),其中顶点个数 \(\vert V\vert=n\) ,边数 \(\vert E\vert=m\) 。顶点编号为\(1-n\) , 边的编号为 \(1-m\) ,第 \(i\)条边连接顶点 \(a_i\) 和 \(b_i\)。输入保证图中不存在重边。

            你现在需要对图中的每条边定向: 对于原图中的每条无向边 \((u,v)\),你需要选择将它变为有向边 \((u,v)\)或者有向边 \((v,u)\).

            对于图 \(G\)中的每个顶点 \(v\in V\),定义 \(r_v\) 为将图定向之后 \(v\)能够到达的顶点个数,你需要选择一种定向使得最大化 \(\min\limits_{v \inV}r_v\),也就是最大化所有 \(r_v\) 的 最小值。输出这个最大化的值。

            输入格式

            输入第一行包含 \(2\)个整数 \(n,m\),分别表示图的顶点数和图的边数。

            接下来 \(m\)行,每行包含两个整数\(a_i,b_i(1\leq a_i,b_i\leq n,a_i\neqb_i)\) ,表示第 \(i\)条边连接顶点 \(a_i\) 和顶点 \(b_i\) 。

            输出格式

            在一行中输出一个整数,表示答案。

            输入1

            4 41 22 33 44 1

            输出1

            4

            输入2

            7 94 32 67 14 17 33 57 46 52 5

            输出2

            4

            数据范围

            对于 \(20\%\) 的数据,\(1\leq n\leq 20,~1\leq m\leq 50\)

            对于 \(50\%\) 的数据,\(1\leq n\leq 2000,~1\leq m\leq 5000\)

            对于 \(100\%\) 的数据,\(1\leq n\leq 10^5,~1\leq m\leq 2\times10^5\)

            题解

            不难发现,一个边双连通分量,一定可以通过给边标向变成强连通分量

            而这道题的答案,其实就是所有边双连通分量中最大的那个所包含的结点数

            证明

            首先我们先证明答案至多为 \(\max\left\{|V^{\prime}_i|\right\}\)

            不难发现,我们对所有的桥(割边)标向后,一定会有一个边双无出边

            而这个边双的大小 \(|V_i^{\prime}|\)会影响全局的答案,于是要贪心的使这个大小取最大。证毕。

            然后我们证明答案至少为 \(\max\left\{|V_i^{\prime}|\right\}\)

            以这个最大的边双为根节点(边双缩点后一定是棵树),其他的结点全部往父亲指

            这样每个边双至少可以走到这个满足 \(|V^{\prime}|=\max\left\{|V_i^{\prime}|\right\}\)的 \(V^{\prime}\) 。证毕。

            你敢信这段东西我花了3min速记的,不然会忘记老师讲的证明的,2333

            时间复杂度 \(O(n+m)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)#define M (int)(2e5+15)int n,m,top,pos=1,ecnt,dfncnt;int dfn[N],low[N],head[N],stk[N],sz[N],ecc[N];struct Edge{int u,v,next;}e[M<<1];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void tarjan(int u,int in_edge){    dfn[u]=low[u]=++dfncnt;    stk[++top]=u;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(!dfn[v])        {            tarjan(v,i);            low[u]=min(low[u],low[v]);        }else if(i!=(in_edge^1))            low[u]=min(low[u],dfn[v]);    }    if(low[u]==dfn[u])    {        ecc[u]=++ecnt; ++sz[ecnt];        while(stk[top]!=u)            ecc[stk[top--]]=ecnt,++sz[ecnt];        --top;    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge(u,v); addEdge(v,u);    }    for(int i=1; i<=n; i++)        if(!dfn[i]) top=0,tarjan(i,0);    int res=0;    for(int i=1; i<=ecnt; i++)        res=max(res,sz[i]);    cout << res << '\n';    return 0;}
            ]]> @@ -1694,7 +1694,7 @@ /2022/07/27/luo-gu-p2341-usaco03fall-haoi2006-shou-huan-ying-de-niu-g-ti-jie/ - 洛谷P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G 题解

            题目链接:P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G

            题意

            每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 $A$ 喜欢 $B$,$B$ 喜欢 $C$,那么 $A$ 也喜欢 $C$。牛栏里共有 $N$ 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

            对于 $100\%$ 的数据,$1\le N\le10^4$,$1\le M\le5\times 10^4$。

            首先考虑最简单的情况

            如果给定的是一个DAG(有向无环图)

            当且仅当存在两个及以上出度为 $0$ 的结点时,答案为 $0$ (无解)

            但是题目给的不一定是DAG

            注意到每个强连通分量内的结点两两可达

            用这题的语言就是,某个强连通分量内的所有结点可以同时为“明星”

            于是考虑强连通分量缩点,接下来就是DAG的情况了

            注意此时的答案,如果有解的话,就是那个点对应的强连通分量大小

            时间复杂度 $O(n+m)$ ,采用 $\text{kosaraju}$ 算法

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)vector<int> g1[N],g2[N];int n,m,cnt,scnt,scc[N],out[N],sz[N],dfn[N],vis[N];void addEdge1(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u])        if(!vis[v]) dfs1(v);    dfn[++cnt]=u;}void dfs2(int u,int id){    scc[u]=id; ++sz[id];    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=n; i++)        if(!vis[i]) dfs1(i);    scnt=0;    for(int i=n; i>=1; i--)        if(!scc[dfn[i]])        {            ++scnt;            dfs2(dfn[i],scnt);        }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge1(u,v);    }    kosaraju();    for(int i=1; i<=n; i++)        for(int j : g1[i])        {            int u=scc[i],v=scc[j];            if(u!=v) ++out[u];        }    int p=0;    for(int i=1; i<=scnt; i++)    {        if(out[i]) continue;        if(p) return cout << 0,0;        p=i;    }    cout << sz[p] << '\n';    return 0;}
            ]]> + # 洛谷P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G 题解

            题目链接:P2341[USACO03FALL / HAOI2006] 受欢迎的牛 G

            题意

            每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果\(A\) 喜欢 \(B\),\(B\)喜欢 \(C\),那么 \(A\) 也喜欢 \(C\)。牛栏里共有 \(N\)头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

            对于 \(100\%\) 的数据,\(1\le N\le10^4\),\(1\le M\le5\times 10^4\)。

            首先考虑最简单的情况

            如果给定的是一个DAG(有向无环图)

            当且仅当存在两个及以上出度为 \(0\)的结点时,答案为 \(0\) (无解)

            但是题目给的不一定是DAG

            注意到每个强连通分量内的结点两两可达

            用这题的语言就是,某个强连通分量内的所有结点可以同时为“明星”

            于是考虑强连通分量缩点,接下来就是DAG的情况了

            注意此时的答案,如果有解的话,就是那个点对应的强连通分量大小

            时间复杂度 \(O(n+m)\) ,采用 \(\text{kosaraju}\) 算法

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)vector<int> g1[N],g2[N];int n,m,cnt,scnt,scc[N],out[N],sz[N],dfn[N],vis[N];void addEdge1(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u])        if(!vis[v]) dfs1(v);    dfn[++cnt]=u;}void dfs2(int u,int id){    scc[u]=id; ++sz[id];    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=n; i++)        if(!vis[i]) dfs1(i);    scnt=0;    for(int i=n; i>=1; i--)        if(!scc[dfn[i]])        {            ++scnt;            dfs2(dfn[i],scnt);        }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge1(u,v);    }    kosaraju();    for(int i=1; i<=n; i++)        for(int j : g1[i])        {            int u=scc[i],v=scc[j];            if(u!=v) ++out[u];        }    int p=0;    for(int i=1; i<=scnt; i++)    {        if(out[i]) continue;        if(p) return cout << 0,0;        p=i;    }    cout << sz[p] << '\n';    return 0;}
            ]]>
            @@ -1721,7 +1721,7 @@ /2022/07/27/mo-ni-sai-ti-jiang-jie-5/ - 模拟赛题讲解[5]

            来自 Roundgod 2022-07-26 noi.ac #2678

            题目描述

            对于任意非负整数 $x$ 和 $m(2\le m\le 10)$ ,定义 $f_m(x)$ 为 $x$ 在 $m$ 进制下的各位数字之和。给定两个整数 $k$ 和 $m$ 。你需要计算在所有满足是 $k$ 倍数正数 $x$ 中,最小的 $f_m(x)$ 值是多少。

            输入格式

            输入第一行包含两个整数 $k,m(2\le m\le 10)$ 。

            输出格式

            在一行中输出一个整数,表示答案。

            输入1

            5 10

            输出1

            1

            输入2

            79992 10

            输出2

            36

            输入3

            4 5

            输出3

            4

            数据范围

            对于 $30\%$ 的数据,$1\le k\le 100$

            对于 $100\%$ 的数据,$1\le k\le 10^6$

            题解

            尝试以最小的花费构造一个数使得其是 $k$ 的倍数

            如果我们把这个构造的过程放在模 $k$ 的意义下

            问题就转化为了,如何以最小的花费构造一个 $0$

            设 $d_x,~x \in [0,k)$ 表示构造一个模 $k$ 意义下为 $x$ 的数的最小花费

            然后尝试转移?

            对于构造的数,我们可以乘 $m$ ,也可以加 $1$

            前者对应的花费为 $0$ ,后者对应的花费为 $1$

            为什么只加 $1$ 呢?因为加 $2$ 等价于两次加 $1$

            于是,这个题就转化为了同余最短路

            然后就是同余最短路,甚至不用 $\text{Dijkstra}$ ,直接01bfs就好了

            考场直接打暴力骗分,至今不是很理解同余最短路,有待补充

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int k,m,d[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> k >> m;    for(int i=0; i<k; i++) d[i]=INF;    d[1]=1; deque<int> q;    q.push_back(1);    while(!q.empty())    {        int u=q.front(); q.pop_front();        if(!u){return cout << d[0] << '\n',0;}        int v=u*m%k; if(d[v]>d[u]){d[v]=d[u]; q.push_front(v);}        v=(u+1)%k; if(d[v]>d[u]+1){d[v]=d[u]+1; q.push_back(v);}    }    cout << d[0] << '\n';    return 0;}
            ]]> + 模拟赛题讲解[5]

            来自 Roundgod2022-07-26 noi.ac #2678

            题目描述

            对于任意非负整数 \(x\)\(m(2\le m\le 10)\) ,定义 \(f_m(x)\) 为 \(x\) 在 \(m\) 进制下的各位数字之和。给定两个整数\(k\)\(m\) 。你需要计算在所有满足是 \(k\)倍数正数 \(x\) 中,最小的 \(f_m(x)\) 值是多少。

            输入格式

            输入第一行包含两个整数 \(k,m(2\le m\le10)\)

            输出格式

            在一行中输出一个整数,表示答案。

            输入1

            5 10

            输出1

            1

            输入2

            79992 10

            输出2

            36

            输入3

            4 5

            输出3

            4

            数据范围

            对于 \(30\%\) 的数据,\(1\le k\le 100\)

            对于 \(100\%\) 的数据,\(1\le k\le 10^6\)

            题解

            尝试以最小的花费构造一个数使得其是 \(k\) 的倍数

            如果我们把这个构造的过程放在模 \(k\)的意义下

            问题就转化为了,如何以最小的花费构造一个 \(0\)

            \(d_x,~x \in [0,k)\)表示构造一个模 \(k\) 意义下为 \(x\) 的数的最小花费

            然后尝试转移?

            对于构造的数,我们可以乘 \(m\),也可以加 \(1\)

            前者对应的花费为 \(0\),后者对应的花费为 \(1\)

            为什么只加 \(1\) 呢?因为加 \(2\) 等价于两次加 \(1\)

            于是,这个题就转化为了同余最短路

            然后就是同余最短路,甚至不用 \(\text{Dijkstra}\) ,直接01bfs就好了

            考场直接打暴力骗分,至今不是很理解同余最短路,有待补充

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int k,m,d[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> k >> m;    for(int i=0; i<k; i++) d[i]=INF;    d[1]=1; deque<int> q;    q.push_back(1);    while(!q.empty())    {        int u=q.front(); q.pop_front();        if(!u){return cout << d[0] << '\n',0;}        int v=u*m%k; if(d[v]>d[u]){d[v]=d[u]; q.push_front(v);}        v=(u+1)%k; if(d[v]>d[u]+1){d[v]=d[u]+1; q.push_back(v);}    }    cout << d[0] << '\n';    return 0;}
            ]]> @@ -1750,7 +1750,7 @@ /2022/07/27/mo-ni-sai-ti-jiang-jie-4/ - 模拟赛题讲解[4]

            来自 Roundgod 2022-07-26 noi.ac #2680

            问题描述

            Berland由 $n$ 个城市和 $m$ 条双向道路构成,其中第 $i$ 条道路连接城市 $a_i$ 和 $b_i$ ,并且距离 $c_i$ 公里.

            你想要开车在Berland进行自驾游。已知车子的油箱最多能装 $L$ 升油,并且车子每行进一公里都要耗费恰好一升油。当你到达某个城市的时候,你可以选择将油箱加满油(也可以不加)。你不能在道路中间加油。

            现在给出 $q$ 组如下形式的询问: 给定起点 $s$ 和终点 $t$ ,问如果初始时油箱装满从城市 $s$ 出发,最少要多少次加满油才能到达城市 $t$ ,如果无法到达输出 $−1$.

            输入格式

            输入第一行包含 $3$ 个整数 $n,m,L$分别表示城市的个数,道路的条数以及油箱的容量。

            接下来 $m$ 行,每行包含三个整数 $a_i,b_i,c_i(1\le a_i,b_i\le n,a_i\ne b_i)$ ,表示第 $i$ 条道路连接的城市以及道路长度。

            接下来一行输出一个整数 $q$ ,表示询问的组数。

            接下来 $q$ 行,每行输入两个整数 $s_i,t_i(1\le s_i,t_i\le n,s_i \ne t_i)$ ,表示每组询问。

            输出格式

            对于每组询问,你需要在一行中输出一个整数表示答案。

            输入1

            3 2 51 2 32 3 323 21 3

            输出1

            01

            输入2

            4 0 112 1

            输出2

            -1

            数据范围

            对于 $40\%$ 的数据,$1\le n\le 300,~1\le m\le 2000,~1\le L\le 10^9,~1\le q\le n(n−1),~c_i=L$

            对于 $100\%$ 的数据,$1\le n\le 300,~1\le m\le \frac{n(n−1)}{2},~1\le L\le 10^9,~1\le q\le n(n−1),~1\le c_i\le 10^9$

            题解

            关于老师数据挂了,改完我就rank1这件事

            首先,如果一条路径的边权和小于等于 $L$ ,那我们只要加一次油就可以了

            因此我们可以认为在路径上的这些结点两两有花费为 $1$ 的边权(加油次数)

            然后两遍 $\text{Floyd}$ 就好了,具体可以看代码

            有一说一,虽然思路是这样的,但是我的实现和讲解的不太一样

            所以我把标程搬过来了,可以参考一下

            有没有一种可能,我自己都没看懂自己的写法

            其实我的实现是 $f_{i,j}$ 表示从 $i$ 出发到 $j$

            因为只有一个起点,因此另一个起点加满的油在合并时是要加上的,故

            时间复杂度 $O(n^3)$

            我的考场代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x7f7f7f7f#define N (int)(305)int n,m,L,Q,f[N][N],g[N][N],tmp[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> L;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            f[i][j]=g[i][j]=tmp[i][j]=INF;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        g[u][v]=min(g[u][v],w);        tmp[u][v]=tmp[v][u]=g[v][u]=g[u][v];    }    for(int k=1; k<=n; k++)    {        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                tmp[i][j]=min(tmp[i][j],tmp[i][k]+tmp[k][j]);        int ok=0;        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                if(tmp[i][j]<=L) g[i][j]=min(g[i][j],tmp[i][j]),ok=1;        if(!ok) break;    }    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            if(g[i][j]<=L) f[i][j]=0;    for(int k=1; k<=n; k++)        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                f[i][j]=min(f[i][j],f[i][k]+f[k][j]+1);    cin >> Q;    for(int x,y; Q--;)    {        cin >> x >> y;        cout << ((f[x][y]==INF)?-1:f[x][y]) << '\n';    }    return 0;}

            老师的std:

            #pragma GCC optimize(3)#include<bits/stdc++.h>#define MAXN 305#define INF 1000000001#define MOD 1000000007#define F first#define S secondusing namespace std;typedef long long ll;typedef pair<int,int> P;int n,m,l,q;int d[MAXN][MAXN];void floyd_warshall(){    for(int k=1;k<=n;k++)        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++)                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);}int main(){    scanf("%d%d%d",&n,&m,&l);    for(int i=1;i<=n;i++)        for(int j=1;j<=n;j++)            d[i][j]=(i==j?0:INF);    for(int i=1;i<=m;i++)    {        int u,v,w;        scanf("%d%d%d",&u,&v,&w);        d[u][v]=min(d[u][v],w);        d[v][u]=min(d[v][u],w);    }    floyd_warshall();    for(int i=1;i<=n;i++)        for(int j=1;j<=n;j++)            if(d[i][j]>l) d[i][j]=INF; else d[i][j]=(i==j?0:1);    floyd_warshall();    scanf("%d",&q);    for(int i=0;i<q;i++)    {        int u,v;        scanf("%d%d",&u,&v);        printf("%d\n",d[u][v]==INF?-1:d[u][v]-1);    }    return 0;}
            ]]> + 模拟赛题讲解[4]

            来自 Roundgod2022-07-26 noi.ac #2680

            问题描述

            Berland由 \(n\) 个城市和 \(m\) 条双向道路构成,其中第 \(i\) 条道路连接城市 \(a_i\) 和 \(b_i\) ,并且距离 \(c_i\) 公里.

            你想要开车在Berland进行自驾游。已知车子的油箱最多能装 \(L\)升油,并且车子每行进一公里都要耗费恰好一升油。当你到达某个城市的时候,你可以选择将油箱加满油(也可以不加)。你不能在道路中间加油。

            现在给出 \(q\) 组如下形式的询问:给定起点 \(s\) 和终点 \(t\) ,问如果初始时油箱装满从城市 \(s\)出发,最少要多少次加满油才能到达城市 \(t\) ,如果无法到达输出 \(−1\).

            输入格式

            输入第一行包含 \(3\) 个整数 \(n,m,L\)分别表示城市的个数,道路的条数以及油箱的容量。

            接下来 \(m\) 行,每行包含三个整数\(a_i,b_i,c_i(1\le a_i,b_i\le n,a_i\neb_i)\) ,表示第 \(i\)条道路连接的城市以及道路长度。

            接下来一行输出一个整数 \(q\),表示询问的组数。

            接下来 \(q\) 行,每行输入两个整数\(s_i,t_i(1\le s_i,t_i\le n,s_i \net_i)\) ,表示每组询问。

            输出格式

            对于每组询问,你需要在一行中输出一个整数表示答案。

            输入1

            3 2 51 2 32 3 323 21 3

            输出1

            01

            输入2

            4 0 112 1

            输出2

            -1

            数据范围

            对于 \(40\%\) 的数据,\(1\le n\le 300,~1\le m\le 2000,~1\le L\le10^9,~1\le q\le n(n−1),~c_i=L\)

            对于 \(100\%\) 的数据,\(1\le n\le 300,~1\le m\le \frac{n(n−1)}{2},~1\leL\le 10^9,~1\le q\le n(n−1),~1\le c_i\le 10^9\)

            题解

            关于老师数据挂了,改完我就rank1这件事

            首先,如果一条路径的边权和小于等于 \(L\) ,那我们只要加一次油就可以了

            因此我们可以认为在路径上的这些结点两两有花费为 \(1\) 的边权(加油次数)

            然后两遍 \(\text{Floyd}\)就好了,具体可以看代码

            有一说一,虽然思路是这样的,但是我的实现和讲解的不太一样

            所以我把标程搬过来了,可以参考一下

            有没有一种可能,我自己都没看懂自己的写法

            其实我的实现是 \(f_{i,j}\)表示\(i\) 出发\(j\)

            因为只有一个起点,因此另一个起点加满的油在合并时是要加上的,故 \[f_{i,j}=\min\{f_{i,j},~f_{i,k}+f_{k,j}+1\}\] 时间复杂度 \(O(n^3)\)

            我的考场代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x7f7f7f7f#define N (int)(305)int n,m,L,Q,f[N][N],g[N][N],tmp[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> L;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            f[i][j]=g[i][j]=tmp[i][j]=INF;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        g[u][v]=min(g[u][v],w);        tmp[u][v]=tmp[v][u]=g[v][u]=g[u][v];    }    for(int k=1; k<=n; k++)    {        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                tmp[i][j]=min(tmp[i][j],tmp[i][k]+tmp[k][j]);        int ok=0;        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                if(tmp[i][j]<=L) g[i][j]=min(g[i][j],tmp[i][j]),ok=1;        if(!ok) break;    }    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            if(g[i][j]<=L) f[i][j]=0;    for(int k=1; k<=n; k++)        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                f[i][j]=min(f[i][j],f[i][k]+f[k][j]+1);    cin >> Q;    for(int x,y; Q--;)    {        cin >> x >> y;        cout << ((f[x][y]==INF)?-1:f[x][y]) << '\n';    }    return 0;}

            老师的std:

            #pragma GCC optimize(3)#include<bits/stdc++.h>#define MAXN 305#define INF 1000000001#define MOD 1000000007#define F first#define S secondusing namespace std;typedef long long ll;typedef pair<int,int> P;int n,m,l,q;int d[MAXN][MAXN];void floyd_warshall(){    for(int k=1;k<=n;k++)        for(int i=1;i<=n;i++)            for(int j=1;j<=n;j++)                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);}int main(){    scanf("%d%d%d",&n,&m,&l);    for(int i=1;i<=n;i++)        for(int j=1;j<=n;j++)            d[i][j]=(i==j?0:INF);    for(int i=1;i<=m;i++)    {        int u,v,w;        scanf("%d%d%d",&u,&v,&w);        d[u][v]=min(d[u][v],w);        d[v][u]=min(d[v][u],w);    }    floyd_warshall();    for(int i=1;i<=n;i++)        for(int j=1;j<=n;j++)            if(d[i][j]>l) d[i][j]=INF; else d[i][j]=(i==j?0:1);    floyd_warshall();    scanf("%d",&q);    for(int i=0;i<q;i++)    {        int u,v;        scanf("%d%d",&u,&v);        printf("%d\n",d[u][v]==INF?-1:d[u][v]-1);    }    return 0;}
            ]]> @@ -1777,7 +1777,7 @@ /2022/07/27/mo-ni-sai-ti-jiang-jie-3/ - 模拟赛题讲解[3]

            来自 Roundgod 2022-07-26 noi.ac #2679

            题目描述

            给定一个无向图 $G=(V,E)$ ,其中顶点个数 $|V|=n$ ,边数 $|E|=m$ 。顶点编号为 $1−N$, 边的编号为 $1−M$ 。其中 $i$ 号顶点有一个权值 $h_i$ .

            你现在可以从 $1$ 号顶点开始沿着图上的边行走。你初始时的得分为 $0$ ,当你经过一条边 $(u,v)$ 时,你的得分会如下更新:

            1.如果 $h_u>h_v$ ,你的得分会增加 $h_u−h_v$ 。

            2.如果 $h_u<h_v$ ,你的得分会减少 $2(h_v−h_u)$ 。

            3.如果 $h_u=h_v$,你的得分会不变.

            你想要知道,经过任意行走后(也可以不行走),你可以得到的最大得分是多少?

            输入格式

            输入第一行包含 $3$ 个整数 $n,m$ ,分别表示图的顶点数和图的边数。

            接下来一行包含 $n$ 个整数 $h_1,h_2,\dots ,h_n$ ,表示每个顶点的初始权值。

            接下来 $m$ 行,每行 $u_i,v_i(1\le u_i,v_i\le n,u_i\ne v_i)$ ,表示第 $i$ 条边。

            输入格式

            在一行中输出一个数,表示你可以得到的最大得分。

            输入1

            4 410 8 12 51 21 32 33 4

            输出1

            3

            输入2

            2 10 101 2

            输出2

            0

            数据范围

            对于 $30\%$ 的数据,$1\le n\le 200,~1\le m\le 1000,~1\le h_i\le 10^9$

            对于 $100\%$ 的数据,$1\le n\le 2\times 10^5,~1\le m\le 2\times 10^5,~1\le h_i\le 10^{18}$

            题解

            不难发现这个图就是要找个最长路

            常见的trick就是把边权取反后跑最短路

            注:这里以及下文中的取反变成其相反数

            而这道题的边权其实很好推

            注意到会有负权边,且 $n \le 2 \times 10^5$

            直接用spfa或者dijkstra肯定是不行的(虽然数据没成功卡掉)

            还记得什么“黑科技”可以让 $\text{Dijkstra}$ 跑负权图吗?

            答案是势能函数,也叫权重函数,思想来源于 Johnson 全源最短路算法

            说的简单一点,一般势能函数就是把负权改成非负权,然后跑 $\text{Dijkstra}$

            在本题中,我们可以设

            于是

            不难发现,此时 $w^{\prime}(u,v) \le 0$ 恒成立

            设 $d_i=f_i+h_i-h_1$ ,其中 $f_i$ 为我们要求的最大分数(最长路长度)

            可以发现,如果我们要求出 $f_i$ ,就需要求出 $d_i$

            而这个 $d_i$ 就是使用势能函数作边权后 $1$ 到 $i$ 的最长路

            还记得刚刚的trick吗?我们要求的是边权取反后的最短路

            这样我们把 $-w^{\prime}(u,v)$ 加入,跑个最短路,就能求出答案了

            此时求出的其实是 $d^{\prime}_i=-f_i-h_i+h_1$

            移项得 $f_i=h_1-h_i-d^{\prime}_i$

            时间复杂度 $O(n \log m)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,m,pos=1,h[N],d[N],head[N],vis[N];struct Edge{int u,v,w,next;}e[N<<1];struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}priority_queue<node> q;void dijkstra(int st){    for(int i=1; i<=n; i++) d[i]=INF;    d[st]=0; q.push({st,0});    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u]) continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[v]>d[u]+e[i].w)            {                d[v]=d[u]+e[i].w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++) cin >> h[i];    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        if(h[u]<=h[v]) addEdge(u,v,h[v]-h[u]);        else addEdge(u,v,0);        if(h[v]<=h[u]) addEdge(v,u,h[u]-h[v]);        else addEdge(v,u,0);    }    dijkstra(1);    int res=0;    for(int i=1; i<=n; i++)        res=max(res,h[1]-h[i]-d[i]);    cout << res << '\n';    return 0;}

            哎我草,困死了,集训还是蛮累的

            ]]> + 模拟赛题讲解[3]

            来自 Roundgod2022-07-26 noi.ac #2679

            题目描述

            给定一个无向图 \(G=(V,E)\),其中顶点个数 \(|V|=n\) ,边数 \(|E|=m\) 。顶点编号为 \(1−N\), 边的编号为 \(1−M\) 。其中 \(i\) 号顶点有一个权值 \(h_i\) .

            你现在可以从 \(1\)号顶点开始沿着图上的边行走。你初始时的得分为 \(0\) ,当你经过一条边 \((u,v)\) 时,你的得分会如下更新:

            1.如果 \(h_u>h_v\),你的得分会增加 \(h_u−h_v\)

            2.如果 \(h_u<h_v\),你的得分会减少 \(2(h_v−h_u)\)

            3.如果 \(h_u=h_v\),你的得分会不变.

            你想要知道,经过任意行走后(也可以不行走),你可以得到的最大得分是多少?

            输入格式

            输入第一行包含 \(3\) 个整数 \(n,m\) ,分别表示图的顶点数和图的边数。

            接下来一行包含 \(n\) 个整数 \(h_1,h_2,\dots ,h_n\),表示每个顶点的初始权值。

            接下来 \(m\) 行,每行 \(u_i,v_i(1\le u_i,v_i\le n,u_i\ne v_i)\),表示第 \(i\) 条边。

            输入格式

            在一行中输出一个数,表示你可以得到的最大得分。

            输入1

            4 410 8 12 51 21 32 33 4

            输出1

            3

            输入2

            2 10 101 2

            输出2

            0

            数据范围

            对于 \(30\%\) 的数据,\(1\le n\le 200,~1\le m\le 1000,~1\le h_i\le10^9\)

            对于 \(100\%\) 的数据,\(1\le n\le 2\times 10^5,~1\le m\le 2\times10^5,~1\le h_i\le 10^{18}\)

            题解

            不难发现这个图就是要找个最长路

            常见的trick就是把边权取反后跑最短路

            注:这里以及下文中的取反变成其相反数

            而这道题的边权其实很好推 \[w(u,v)=\begin{cases}2h_u-2h_v,&h_u<h_v\\\\0,&h_u=h_v\\\\h_u-h_v,&h_u>h_v\end{cases}\] 注意到会有负权边,且 \(n \le 2\times 10^5\)

            直接用spfa或者dijkstra肯定是不行的(虽然数据没成功卡掉)

            还记得什么“黑科技”可以让 \(\text{Dijkstra}\) 跑负权图吗?

            答案是势能函数,也叫权重函数,思想来源于 Johnson全源最短路算法

            说的简单一点,一般势能函数就是把负权改成非负权,然后跑 \(\text{Dijkstra}\)

            在本题中,我们可以设 \[w^{\prime}(u,v)=w(u,v)-(h_u-h_v)\] 于是 \[w^{\prime}(u,v)=\begin{cases}h_u-h_v,&h_u<h_v\\\\0,&h_u\ge h_v\end{cases}\] 不难发现,此时 \(w^{\prime}(u,v) \le0\) 恒成立

            \(d_i=f_i+h_i-h_1\) ,其中 \(f_i\)为我们要求的最大分数(最长路长度)

            可以发现,如果我们要求出 \(f_i\),就需要求出 \(d_i\)

            而这个 \(d_i\)就是使用势能函数作边权后 \(1\)\(i\) 的最长路

            还记得刚刚的trick吗?我们要求的是边权取反后的最短路

            这样我们把 \(-w^{\prime}(u,v)\)加入,跑个最短路,就能求出答案了

            此时求出的其实是 \(d^{\prime}_i=-f_i-h_i+h_1\)

            移项得 \(f_i=h_1-h_i-d^{\prime}_i\)

            时间复杂度 \(O(n \log m)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,m,pos=1,h[N],d[N],head[N],vis[N];struct Edge{int u,v,w,next;}e[N<<1];struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}priority_queue<node> q;void dijkstra(int st){    for(int i=1; i<=n; i++) d[i]=INF;    d[st]=0; q.push({st,0});    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u]) continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[v]>d[u]+e[i].w)            {                d[v]=d[u]+e[i].w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++) cin >> h[i];    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        if(h[u]<=h[v]) addEdge(u,v,h[v]-h[u]);        else addEdge(u,v,0);        if(h[v]<=h[u]) addEdge(v,u,h[u]-h[v]);        else addEdge(v,u,0);    }    dijkstra(1);    int res=0;    for(int i=1; i<=n; i++)        res=max(res,h[1]-h[i]-d[i]);    cout << res << '\n';    return 0;}

            哎我草,困死了,集训还是蛮累的

            ]]> @@ -1804,7 +1804,7 @@ /2022/07/26/luo-gu-p8060-poi2003-sums-ti-jie/ - 洛谷P8060 [POI2003] Sums 题解

            题目链接:P8060 [POI2003] Sums

            题目描述

            我们给定一个整数集合 $A$。考虑一个非负整数集合 $A’$,所有属于 $A’$ 的集合的数 $x$ 满足当且仅当能被表示成一些属于 $A$ 的元素的和(数字可重复)。

            比如,当 $A = \{2,5,7\}$,属于 $A’$ 的数为:$0$($0$ 个元素的和),$2$,$4$($2 + 2$)和 $12$($5 + 7$ or $7 + 5$ or $2 + 2 + 2 + 2 + 2 + 2$);但是元素 $1$ 和 $3$ 不属于 $A’$。

            输入格式

            第一行有一个整数 $n$,代表集合 $A$ 的元素个数。接下来每行一个数 $a_i$ 描述一个元素。$A = \{a_1,a_2,…,a_n\}$。

            接下来一个整数 $k$,然后每行一个整数,分别代表 $b_1,b_2,…,b_k$。

            输出格式

            输出 $k$ 行。如果 $b_i$ 属于 $A’$,第 $i$ 行打印 TAK,否则打印 NIE

            数据范围

            对于所有数据,$1 \le n \le 5 \times 10^3$,$1 \le k \le 10^4$,$1 \le a_1 < a_2 < … < a_n \le 5 \times 10^4$,$0 \le b_i \le 10^9$。

            考虑随便取一个模数 $a_i$ ,建议从小到大排序后选 $a_1$

            然后求出 凑出每个 $x \in [0,a_1)$ 的所需的最小代价

            或者说,用最少的 $a_i$ 凑出一个模 $a_1$ 意义下的 $x$ ,记这个数为 $d_x$

            而同余最短路干的事情就是:求模 $M$ 余 $x$ 的最小基数 $d_x$

            这样对于每个询问,我们只要看看 $d_{k\,\bmod\, a_1}$ 是不是小于 $k$ 就可以了

            时间复杂度 $O(n a_i \log a_i)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)#define M (int)(5e5+15)int n,Q,a[N],d[M];struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}priority_queue<node> q;void dijkstra(){    for(int i=0; i<a[1]; i++) d[i]=INF;    d[0]=0; q.push({0,0});    while(!q.empty())    {        int u=q.top().u,dis=q.top().dis; q.pop();        if(dis!=d[u]) continue; // 一个点可能被入队多次        for(int i=2; i<=n; i++)        {            int v=(u+a[i])%a[1];            if(d[v]>d[u]+a[i])            {                d[v]=d[u]+a[i];                q.push({v,d[v]});            }        }                }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    sort(a+1,a+1+n); dijkstra();    cin >> Q;    for(int x; Q--;)    {        cin >> x;        if(d[x%a[1]]<=x) cout << "TAK\n";        else cout << "NIE\n";    }    return 0;}
            ]]> + 洛谷P8060 [POI2003] Sums 题解

            题目链接:P8060[POI2003] Sums

            题目描述

            我们给定一个整数集合 \(A\)。考虑一个非负整数集合 \(A'\),所有属于 \(A'\) 的集合的数 \(x\) 满足当且仅当能被表示成一些属于 \(A\) 的元素的和(数字可重复)。

            比如,当 \(A = \{2,5,7\}\),属于\(A'\) 的数为:\(0\)(\(0\)个元素的和),\(2\)\(4\)(\(2 +2\))和 \(12\)\(5 + 7\) or \(7 +5\) or \(2 + 2 + 2 + 2 + 2 +2\));但是元素 \(1\)\(3\) 不属于 \(A'\)。

            输入格式

            第一行有一个整数 \(n\),代表集合\(A\) 的元素个数。接下来每行一个数\(a_i\) 描述一个元素。\(A = \{a_1,a_2,...,a_n\}\)。

            接下来一个整数 \(k\),然后每行一个整数,分别代表 \(b_1,b_2,...,b_k\)。

            输出格式

            输出 \(k\) 行。如果 \(b_i\) 属于 \(A'\),第 \(i\) 行打印 TAK,否则打印NIE

            数据范围

            对于所有数据,\(1 \le n \le 5 \times10^3\)\(1 \le k \le10^4\)\(1 \le a_1 < a_2 < ...< a_n \le 5 \times 10^4\)\(0 \leb_i \le 10^9\)

            考虑随便取一个模数 \(a_i\),建议从小到大排序后选 \(a_1\)

            然后求出 凑出每个 \(x \in [0,a_1)\)的所需的最小代价

            或者说,用最少的 \(a_i\) 凑出一个模\(a_1\) 意义下的 \(x\) ,记这个数为 \(d_x\)

            而同余最短路干的事情就是:求模 \(M\)\(x\) 的最小基数 \(d_x\)

            这样对于每个询问,我们只要看看 \(d_{k\,\bmod\, a_1}\) 是不是小于 \(k\) 就可以了

            时间复杂度 \(O(n a_i \log a_i)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)#define M (int)(5e5+15)int n,Q,a[N],d[M];struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}priority_queue<node> q;void dijkstra(){    for(int i=0; i<a[1]; i++) d[i]=INF;    d[0]=0; q.push({0,0});    while(!q.empty())    {        int u=q.top().u,dis=q.top().dis; q.pop();        if(dis!=d[u]) continue; // 一个点可能被入队多次        for(int i=2; i<=n; i++)        {            int v=(u+a[i])%a[1];            if(d[v]>d[u]+a[i])            {                d[v]=d[u]+a[i];                q.push({v,d[v]});            }        }                }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    sort(a+1,a+1+n); dijkstra();    cin >> Q;    for(int x; Q--;)    {        cin >> x;        if(d[x%a[1]]<=x) cout << "TAK\n";        else cout << "NIE\n";    }    return 0;}
            ]]> @@ -1835,7 +1835,7 @@ /2022/07/26/oi-tricks/ - OI tricks

            平时做题的时候发现的一些技巧,还没有仔细整理

            因此本文比较像草稿般的个人总结

            1.终极快读

            namespace FastIO{#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e6+15)char buf1[SIZ],*p1,*p2;char readchar(){if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>void write(T k){if(k<0){k=-k;pc('-');}static T stk[66];T top=0;do{stk[top++]=k%10,k/=10;}while(k);while(top){pc(stk[--top]+'0');}}}using namespace FastIO;

            2.区间的平均数问题

            例:询问一个区间是否平均数大于 $m$

            解法:把原区间每个数减去 $m$ ,这样修改后的区间和大于等于 $\boldsymbol{0}$ ,则平均数大于 $m$​

            (这个很还是好想的,平均数大于 $m$ ,那么和就大于 $m\times$区间长度)


            3.秦九韶算法

            double cal(double x){    double ans=0;    for(int i=0; i<=n; i++)        ans=ans*x+a[i];    return ans;}

            4.全局k大值的维护

            开一个小根堆,先插入 $k$ 个极小值,这样堆顶就是第 $k$ 大,(最大的在堆底)每次pop()+push()以保证始终是 $k$ 个,如果有重复计算或统计的值要把 $k$ 乘以那个值


            5.lambda表达式

            1. 简洁版cmp

              sort(a+1,a+n+1,[](int x,int y){    return rk[x] == rk[y]?rk[x+w]<rk[y+w]:rk[x]<rk[y];});
            2. 一般情况

              auto f=[=](int x){    // do something...};// 中括号里面=是传值,&是传地址

            6.顺反则逆

            如果发现 $\boldsymbol{+1}$ 标记的操作要反过来跑,可以考虑改成 $\boldsymbol{-1}$ ,然后正着跑


            7.枚举二元函数

            例如 f(a,b) ,如果有单调性(比如单调递增)且 $a,b$ 的范围可以线性求解的话,

            则可以 $a=1 \to n,b=n$ ,随着循环去减 $b$ ,这样就是线性的

            例:abc246D

            #include<bits/stdc++.h>using namespace std;long long f(long long a,long long b){    return (a*a*a + a*a*b + a*b*b + b*b*b);}int main(){    long long n;    cin >> n;    long long x=8e18;    long long j=1000000;    for(long long i=0;i<=1000000;i++)    {        while(f(i,j)>=n && j>=0)        {            x=min(x,f(i,j));            j--;        }    }    cout << x << '\n';    return 0;}

            可以优化,变成

            #include<bits/stdc++.h>using namespace std;long long f(long long a,long long b){    return (a*a*a + a*a*b + a*b*b + b*b*b);}int main(){    long long n;    cin >> n;    long long x=8e18;    long long i=0,j=1000000;    while(i<=j)    {        long long cf=f(i,j);        if(cf>=n){x=min(x,cf);j--;}        else{i++;}    }    cout << x << '\n';    return 0;}

            类似于双指针的写法


            8.最大值&最小值的转化

            $\max z = -\min (-z)$


            9.枚举子矩阵

            1. 枚举左下和右上的点,$O(n^4)$
            2. 枚举上边界和下边界,左右通过某些题的贪心性质搞成线性 例:洛谷P1369 $O(n^3)$

            10.快速计算平方根

            正整数 $a$ 满足 $a^2\le x < (a+1)^2$


            11.判断有多少个结点可以到达 $n$ 结点

            建反图,然后从 $n$ 结点跑 bfs 即可


            12.找每个结点能到达的点权最小结点

            可以把所有的结点按点权升序排序,然后从最小点权的开始,去尝试影响别的结点的答案

            这样做是 $O(n\log n)$ 的


            13.直径两端到直径上一点的最近距离

            从 $x$ 端跑个dis数组,答案就是min(dis[u],dis[y]-dis[u])


            14.有向图路径长度必须恰好t从1到达n的方案数

            P4159 [SCOI2009] 迷路

            1. 从样例一发现,并且不考虑题目问题,01邻接矩阵相乘就是通过2条边到达节点的方案数
            2. 对于非01邻接矩阵,使用拆点


            15.树上选一条路径,路径上每条边的边权异或x

            AT3913 XOR Tree

            可以把边权化点权

            把每个结点的点权定义为 该结点所有相邻的边的边权异或和

            这样就把路径改边权转化为了修改两个端点


            16.因子的一些等价表示

            P3935 Calculating

            这个柿子就可以用数论分块来搞了

            1. 若 $n = \prod_{i=1}^{s}p_i^{k_i}$

            注意是因数个数!不是质因数个数!


            17.循环改写->前缀和优化

            常见于dp,例题:P4099 [HEOI2013]SAO

            for(int i=1; i<=n; i++)for(int j=i; j<=n; j++)dp[i][j];// 可以转化为以下形式for(int j=1; j<=n; o++)for(int i=1; i<=j; i++)dp[i][j];

            18.乘法转log 防止爆long long

            类似于哈希

            要记录这样的值,又不想用哈希

            可以转化为


            19.移项后二分

            求下面柿子的最大值

            可以假设

            然后

            while(r-l>1e-6){    double mid=(l+r)/2;    if(ck(mid))l=mid;    else r=mid;}

            20.判正环 —改符号-> 判负环

            RT.P2868 [USACO07DEC]Sightseeing Cows G


            21.数组中下一个相同的数

            for(int i=n; i>=1; i--){    nx[i]=first[val[i]];    first[val[i]]=i;} // 数组是val[]

            22.经典n^3枚举矩阵

            $O(n^2)$ 枚举上下界,$O(n)$ 处理中间一条

            把上界到下界压到一个数上,这样就变成了一行

            枚举上界或者下界也是有用的想法


            23.环上选不相邻序列,权值最大

            见P1484&P1792,反悔型贪心,即a-b-c,选b,反悔选a,c(看作一个几点)


            24.反悔型贪心常用技巧

            1. 比如什么原价 $w_i$ ,用优惠券 $p_i$ ,反悔的做法是新增一个物品价格为 $w_i-p_i$ 以腾出优惠券
            2. 延迟删除:比起去优先队列里找元素删,不如延迟删除,即用个vis记录是否被删除,然后每次取堆顶的时候判断

            25.标记优化树状数组的频繁清空操作

            详见 link ,其实也叫时间戳优化

            适用场景:

            给定 $T$ 组数据,每组数据有 $Q$ 个询问,询问给定 $n$ 个数的区间和

            数据范围:$T\le 2\times 10^5,~\sum Q \le 2\times 10^5,~n\le 5\times 10^5$

            对于每组数据不能直接去清空数组,因为 $T \times n$ 肯定爆炸

            考虑维护一个时间戳now,也就是现在是第几组数据

            int tree[N],t[N],now=0;void add(int x,int v){    for(int i=x; i<=n; i+=lowbit(i))        if(t[i]==now)tree[i]+=v;        else t[i]=now,tree[i]=v; // tree[i] = 0 + v}int sum(int x){    int res=0;    for(int i=x; i; i-=lowbit(i))        if(t[i]==now) res+=tree[i];        else t[i]=now,tree[i]=0; // res+=0}

            26.枚举技巧

            求柿子的最大值

            假设

            然后按 $d1-d2$ 排序,接下来?


            1. nx[i][j] 表示 $i$ 后面出现的第一个字符 $j$ 的位置
            for(int i=0; i<26; i++)    for(int j=1; j<=n+1; j++)        nx[i][j]=n+1;for(int i=0; i<26; i++)    for(int j=n; j>=1; j--)        nx[i][j]=(s[j]=='a'+i)?j:nx[i][j+1];

            1. $x \equiv x_\texttt{十进制下的每个数位的和} \bmod p$

            29.$a_i \leftarrow a_{i-1}+a_{i+1}-a_i$

            等价于交换 $a_i-a_{i-1}$ 和 $a_{i+1}-a_i$

            例如

            a[3]-a[2]=5a[4]-a[3]=4

            交换以后变成

            a[3]-a[2]=4a[4]-a[3]=5

            30.判断两个非最简分数是否相等

            struct node {int x,y;};bool equal(node a,node b){return a.x*b.y==a.y*b.x;    }

            31.异或=模2意义下的加法

            32.

            // 返回x符号正负(1或-1)int sgn(int x){return (x>0)-(x<0);}
            ]]> + OI tricks

            平时做题的时候发现的一些技巧,还没有仔细整理

            因此本文比较像草稿般的个人总结

            1.终极快读

            namespace FastIO{#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e6+15)char buf1[SIZ],*p1,*p2;char readchar(){if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>void write(T k){if(k<0){k=-k;pc('-');}static T stk[66];T top=0;do{stk[top++]=k%10,k/=10;}while(k);while(top){pc(stk[--top]+'0');}}}using namespace FastIO;

            2.区间的平均数问题

            例:询问一个区间是否平均数大于 \(m\)

            解法:把原区间每个数减去 \(m\),这样修改后的区间和大于等于 \(\boldsymbol{0}\) ,则平均数大于 \(m\)​

            (这个很还是好想的,平均数大于 \(m\),那么和就大于 \(m\times\)区间长度)


            3.秦九韶算法

            double cal(double x){    double ans=0;    for(int i=0; i<=n; i++)        ans=ans*x+a[i];    return ans;}

            4.全局k大值的维护

            开一个小根堆,先插入 \(k\)个极小值,这样堆顶就是第 \(k\)大,(最大的在堆底)每次pop()+push()以保证始终是 \(k\) 个,如果有重复计算或统计的值要把 \(k\) 乘以那个值


            5.lambda表达式

            1. 简洁版cmp

              sort(a+1,a+n+1,[](int x,int y){    return rk[x] == rk[y]?rk[x+w]<rk[y+w]:rk[x]<rk[y];});
            2. 一般情况

              auto f=[=](int x){    // do something...};// 中括号里面=是传值,&是传地址

            6.顺反则逆

            如果发现 \(\boldsymbol{+1}\)标记的操作要反过来跑,可以考虑改成\(\boldsymbol{-1}\),然后正着跑


            7.枚举二元函数

            例如 f(a,b) ,如果有单调性(比如单调递增)且 \(a,b\) 的范围可以线性求解的话,

            则可以 \(a=1 \to n,b=n\),随着循环去减 \(b\),这样就是线性的

            例:abc246D

            #include<bits/stdc++.h>using namespace std;long long f(long long a,long long b){    return (a*a*a + a*a*b + a*b*b + b*b*b);}int main(){    long long n;    cin >> n;    long long x=8e18;    long long j=1000000;    for(long long i=0;i<=1000000;i++)    {        while(f(i,j)>=n && j>=0)        {            x=min(x,f(i,j));            j--;        }    }    cout << x << '\n';    return 0;}

            可以优化,变成

            #include<bits/stdc++.h>using namespace std;long long f(long long a,long long b){    return (a*a*a + a*a*b + a*b*b + b*b*b);}int main(){    long long n;    cin >> n;    long long x=8e18;    long long i=0,j=1000000;    while(i<=j)    {        long long cf=f(i,j);        if(cf>=n){x=min(x,cf);j--;}        else{i++;}    }    cout << x << '\n';    return 0;}

            类似于双指针的写法


            8.最大值&最小值的转化

            \(\max z = -\min (-z)\)


            9.枚举子矩阵

            1. 枚举左下和右上的点,\(O(n^4)\)
            2. 枚举上边界和下边界,左右通过某些题的贪心性质搞成线性 例:洛谷P1369\(O(n^3)\)

            10.快速计算平方根 \[\begin{aligned}\sqrt{x}&= a+\dfrac{x-a^2}{\sqrt{x}+a} \\&\approxa+\dfrac{x-a^2}{2\times a}\end{aligned}\]

            正整数 \(a\) 满足 \(a^2\le x < (a+1)^2\)


            11.判断有多少个结点可以到达 \(n\)结点

            建反图,然后从 \(n\) 结点跑 bfs即可


            12.找每个结点能到达的点权最小结点

            可以把所有的结点按点权升序排序,然后从最小点权的开始,去尝试影响别的结点的答案

            这样做是 \(O(n\log n)\)


            13.直径两端到直径上一点的最近距离

            \(x\)端跑个dis数组,答案就是min(dis[u],dis[y]-dis[u])


            14.有向图路径长度必须恰好t从1到达n的方案数

            P4159 [SCOI2009]迷路

            1. 从样例一发现,并且不考虑题目问题,01邻接矩阵相乘就是通过2条边到达节点的方案数
            2. 对于非01邻接矩阵,使用拆点


            15.树上选一条路径,路径上每条边的边权异或x

            AT3913 XORTree

            可以把边权化点权

            把每个结点的点权定义为该结点所有相邻的边的边权异或和

            这样就把路径改边权转化为了修改两个端点


            16.因子的一些等价表示

            P3935Calculating

            \[\sum_{i=1}^{n}\sum_{d\mid i}1 = \sum_{i=1}^{n}\left\lfloor{\dfrac{n}{i}}\right\rfloor\]

            这个柿子就可以用数论分块来搞了

            1. \(n =\prod_{i=1}^{s}p_i^{k_i}\)

            \[\prod_{i=1}^{s}{(k_i+1)}=\sum_{d\mid n}1 = n \text{ 的因数个数}\]

            注意是因数个数!不是质因数个数!


            17.循环改写->前缀和优化

            常见于dp,例题:P4099[HEOI2013]SAO

            for(int i=1; i<=n; i++)for(int j=i; j<=n; j++)dp[i][j];// 可以转化为以下形式for(int j=1; j<=n; o++)for(int i=1; i<=j; i++)dp[i][j];

            18.乘法转log 防止爆long long

            类似于哈希

            要记录这样的值,又不想用哈希 \[a\times b \times c\] 可以转化为 \[\log a + \log b + \log c\]


            19.移项后二分

            求下面柿子的最大值 \[\dfrac{\sum a_i}{\sum b_i}\] 可以假设 \[\dfrac{\sum a_i}{\sum b_i} \ge x\] 然后 \[\sum a_i - x \sum b_i \ge 0\]

            while(r-l>1e-6){    double mid=(l+r)/2;    if(ck(mid))l=mid;    else r=mid;}

            20.判正环 --改符号-> 判负环

            RT.P2868[USACO07DEC]Sightseeing Cows G


            21.数组中下一个相同的数

            for(int i=n; i>=1; i--){    nx[i]=first[val[i]];    first[val[i]]=i;} // 数组是val[]

            22.经典n^3枚举矩阵

            \(O(n^2)\) 枚举上下界,\(O(n)\) 处理中间一条

            把上界到下界压到一个数上,这样就变成了一行

            枚举上界或者下界也是有用的想法


            23.环上选不相邻序列,权值最大

            见P1484&P1792,反悔型贪心,即a-b-c,选b,反悔选a,c(看作一个几点)


            24.反悔型贪心常用技巧

            1. 比如什么原价 \(w_i\) ,用优惠券\(p_i\),反悔的做法是新增一个物品价格为 \(w_i-p_i\) 以腾出优惠券
            2. 延迟删除:比起去优先队列里找元素删,不如延迟删除,即用个vis记录是否被删除,然后每次取堆顶的时候判断

            25.标记优化树状数组的频繁清空操作

            详见 link,其实也叫时间戳优化

            适用场景:

            给定 \(T\) 组数据,每组数据有 \(Q\) 个询问,询问给定 \(n\) 个数的区间和

            数据范围\(T\le 2\times10^5,~\sum Q \le 2\times 10^5,~n\le 5\times 10^5\)

            对于每组数据不能直接去清空数组,因为 \(T\times n\) 肯定爆炸

            考虑维护一个时间戳now,也就是现在是第几组数据

            int tree[N],t[N],now=0;void add(int x,int v){    for(int i=x; i<=n; i+=lowbit(i))        if(t[i]==now)tree[i]+=v;        else t[i]=now,tree[i]=v; // tree[i] = 0 + v}int sum(int x){    int res=0;    for(int i=x; i; i-=lowbit(i))        if(t[i]==now) res+=tree[i];        else t[i]=now,tree[i]=0; // res+=0}

            26.枚举技巧

            求柿子的最大值 \[\min(d1[u]+d2[v]+1,d2[u]+d1[v]+1)\] 假设 \[d1[u]+d2[v]\le d1[v]+d2[u]\\d1[u]-d2[u]\le d1[v]-d2[v]\] 然后按 \(d1-d2\)排序,接下来?


            1. nx[i][j] 表示 \(i\)后面出现的第一个字符 \(j\) 的位置
            for(int i=0; i<26; i++)    for(int j=1; j<=n+1; j++)        nx[i][j]=n+1;for(int i=0; i<26; i++)    for(int j=n; j>=1; j--)        nx[i][j]=(s[j]=='a'+i)?j:nx[i][j+1];

            1. \(x \equivx_\texttt{十进制下的每个数位的和} \bmod p\)

            29.\(a_i \leftarrowa_{i-1}+a_{i+1}-a_i\)

            等价于交换 \(a_i-a_{i-1}\)\(a_{i+1}-a_i\)

            例如

            a[3]-a[2]=5a[4]-a[3]=4

            交换以后变成

            a[3]-a[2]=4a[4]-a[3]=5

            30.判断两个非最简分数是否相等

            struct node {int x,y;};bool equal(node a,node b){return a.x*b.y==a.y*b.x;    }

            31.异或=模2意义下的加法

            // 返回x符号正负(1或-1)int sgn(int x){return (x>0)-(x<0);}
            ]]> @@ -1860,7 +1860,7 @@ /2022/07/26/mo-ni-sai-ti-jiang-jie-2/ - 模拟赛题讲解[2]

            来自 Roundgod 2022-07-25 noi.ac #2677

            题目描述

            给定一个有向图 $G=(V,E)$ ,其中顶点个数 $|V|=n$ ,边数 $|E|=m$ 。顶点编号为 $1−N$ ,边的编号为 $1−M$ ,第 $i$ 条边连接顶点 $a_i$ 和 $b_i$ 。

            Makima会从图中的某个顶点出发并重复沿着某条有向边行走。你需要计算:存在多少个顶点 $v$ ,使得Makima可以从点 $v$ 出发无限地走下去?

            输入格式

            输入第一行包含 $2$ 个整数 $n,m$ ,分别表示图的顶点数和图的边数。

            接下来 $m$ 行,每行包含两个整数 $a_i,b_i(1\le a_i,b_i\le n,a_i\ne b_i)$ ,表示第 $i$ 条边从顶点 $a_i$ 指向顶点 $b_i$ 。

            输出格式

            在一行中输出一个整数,表示满足条件的顶点个数。

            输入1

            5 51 22 33 44 24 5

            输出1

            4

            输入2

            3 21 22 1

            输出2

            2

            数据范围

            对于 $50\%$ 的数据,$1\le n\le 2000,~1\le m\le 10000$

            对于 $100\%$ 的数据,$1\le n\le 2\times 10^5,~1\le m\le 2\times 10^5$

            题解

            这题还是比较简单的,赛时秒了

            不难发现,如果一个结点出发可以走到一个环上

            这个点、这个点到环的路径上的所有点以及所有能走到这个点的点 都是满足要求的点

            显然问题就变成了:找到哪些结点是死路(走不到环上)

            考虑建反图然后跑 $\text{topo}$ 排序。

            可以认为这个过程是,倒着思考,从死路出发,看看它能祸害多少结点

            好像这个题可以用 $\text{Tarjan}$ ,不过我这个不太熟,等熟练了再说吧。

            时间复杂度 $O(n)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e5+15)int n,m,pos=1,head[N],in[N];queue<int> q;struct Edge{int u,v,next;} e[N];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m);    for(int i=1,u,v; i<=m; i++)    {        read(u); read(v);        addEdge(v,u); ++in[u];    }    int res=n;    for(int i=1; i<=n; i++)        if(!in[i]) q.push(i);    while(!q.empty())    {        int u=q.front(); q.pop();        --res;        for(int i=head[u]; i; i=e[i].next)        {            if(!(--in[e[i].v]))                q.push(e[i].v);        }    }    write(res);    return 0;}
            ]]> + 模拟赛题讲解[2]

            来自 Roundgod2022-07-25 noi.ac #2677

            题目描述

            给定一个有向图 \(G=(V,E)\),其中顶点个数 \(|V|=n\) ,边数 \(|E|=m\) 。顶点编号为 \(1−N\) ,边的编号为 \(1−M\) ,第 \(i\) 条边连接顶点 \(a_i\) 和 \(b_i\) 。

            Makima会从图中的某个顶点出发并重复沿着某条有向边行走。你需要计算:存在多少个顶点\(v\) ,使得Makima可以从点 \(v\) 出发无限地走下去?

            输入格式

            输入第一行包含 \(2\) 个整数 \(n,m\) ,分别表示图的顶点数和图的边数。

            接下来 \(m\) 行,每行包含两个整数\(a_i,b_i(1\le a_i,b_i\le n,a_i\neb_i)\) ,表示第 \(i\) 条边从顶点\(a_i\) 指向顶点 \(b_i\) 。

            输出格式

            在一行中输出一个整数,表示满足条件的顶点个数。

            输入1

            5 51 22 33 44 24 5

            输出1

            4

            输入2

            3 21 22 1

            输出2

            2

            数据范围

            对于 \(50\%\) 的数据,\(1\le n\le 2000,~1\le m\le 10000\)

            对于 \(100\%\) 的数据,\(1\le n\le 2\times 10^5,~1\le m\le 2\times10^5\)

            题解

            这题还是比较简单的,赛时秒了

            不难发现,如果一个结点出发可以走到一个环上

            这个点、这个点到环的路径上的所有点以及所有能走到这个点的点都是满足要求的点

            显然问题就变成了:找到哪些结点是死路(走不到环上)

            考虑建反图然后跑 \(\text{topo}\)排序。

            可以认为这个过程是,倒着思考,从死路出发,看看它能祸害多少结点

            好像这个题可以用 \(\text{Tarjan}\),不过我这个不太熟,等熟练了再说吧。

            时间复杂度 \(O(n)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e5+15)int n,m,pos=1,head[N],in[N];queue<int> q;struct Edge{int u,v,next;} e[N];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m);    for(int i=1,u,v; i<=m; i++)    {        read(u); read(v);        addEdge(v,u); ++in[u];    }    int res=n;    for(int i=1; i<=n; i++)        if(!in[i]) q.push(i);    while(!q.empty())    {        int u=q.front(); q.pop();        --res;        for(int i=head[u]; i; i=e[i].next)        {            if(!(--in[e[i].v]))                q.push(e[i].v);        }    }    write(res);    return 0;}
            ]]> @@ -1887,7 +1887,7 @@ /2022/07/26/mo-ni-sai-ti-jiang-jie-1/ - 模拟赛题讲解[1]

            来自 Roundgod 2022-07-25 noi.ac #2676

            题目描述

            给定一个带权连通无向图 $G=(V,E)$ ,其中顶点个数 $|V|=n$ ,边数$|E|=m$ 图中可能包含重边以及自环。顶点编号为 $1−n$ ,边的编号为 $1−m$ ,第 $i$ 条边连接顶点 $a_i$ 和 $b_i$,权值为 $c_i$. 输入保证图中所有边的权值各不相同,即对于所有$1\le i<j \le n$ ,都有 $c_i\ne c_j$ 。

            你现在需要处理 $q$ 个询问,其中第 $k$ 个询问是一个三元组 $(u_i,v_i,w_i)$ ,表示:

            如果在图 $G$ 中添加一条连接 $u_i$ 和 $v_i$ 权值为 $w_i$ 的边得到图 $G^{\prime}$ ,图 $G^{\prime}$ 的最小生成树是否一定包含新添加的边?

            询问保证对于所有 $1 \le j \le m$ ,都有 $w_i\ne c_j$ 。

            输入格式

            输入第一行包含 $3$ 个整数 $n,m,q$ ,分别表示图的顶点数,图的边数,以及询问的个数。

            接下来 $m$ 行,每行包含三个整数 $a_i,b_i,c_i(1\le a_i,b_i\le n)$ ,表示第 $i$ 条边连接的顶点以及权值。

            接下来 $q$ 行,每行 $u_i,v_i,w_i(1\le u_i,v_i \le n)$ , 表示第 $i$ 组询问。

            输出格式

            对于每组询问,根据答案你需要在一行中输出 Yes 或者 No

            输入1

            5 6 31 2 22 3 31 3 62 4 54 5 93 5 81 3 13 4 73 5 7

            输出1

            YesNoYes

            数据范围

            对于 $50\%$ 的数据,$1\le n \le 200,~1\le m \le 1000,~1\le q\le 1000,~1\le c_i,w_i \le 10^9$

            对于 $100\%$的数据,$1\le n\le 2\times 10^5,~1\le m\le 2\times 10^5,~1\le q\le 2\times 10^5,~1\le c_i,w_i\le 10^{18}$

            题解

            这个题还是蛮巧妙的

            首先考虑 $q=1$ 的情况,

            显然此时我们可以把这条边直接加进去跑一遍 $\text{kruskal}$

            然后遇到这条边的时候,判断能否加入最小生成树

            然后我们考虑 $q>1$ 的情况

            最简单的想法是对于每个询问都跑一次,显然不可接受

            仔细思考一下,就会发现,其实我们在判断的时候,

            还是在原生成树的基础上尝试加入这条边的

            而这个生成树是会把边一条一条从小到大加进去的

            所以如果 $w_i$ 比较小,那么它会被先询问到

            考虑离线处理所有询问,直接加入生成树去跑,然后只连原图的边

            时间复杂度 $O((m+q) \log (m+q))$

            数组别忘了开两倍哦~

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <set>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e5+15)int n,m,Q,pos,f[N],r[N],ans[N];set<int> s;struct Edge{int u,v,w,id;} e[N];bool operator<(Edge a,Edge b){return a.w<b.w;}void init(int n){for(int i=1; i<=n; i++) f[i]=i,r[i]=0;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int x,int y){    x=find(x); y=find(y);    if(x==y) return;    if(r[x]<r[y]) f[x]=y;    else f[y]=x,r[x]+=r[x]==r[y];}void kruskal(){    sort(e+1,e+1+pos); init(n);    for(int i=1; i<=pos; i++)    {        if(e[i].id==-1)        {            if(find(e[i].u)!=find(e[i].v))                merge(e[i].u,e[i].v);        }else        {            if(find(e[i].u)!=find(e[i].v))                ans[e[i].id]=1;            else ans[e[i].id]=0;        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m); read(Q);    for(int i=1,u,v,w; i<=m; i++)    {        read(u); read(v); read(w);        s.insert(w); e[++pos]={u,v,w,-1};    }    for(int i=1,u,v,w; i<=Q; i++)    {        read(u); read(v); read(w);        s.insert(w); e[++pos]={u,v,w,i};    }    kruskal();    for(int i=1; i<=Q; i++)        if(ans[i]) puts("Yes");        else puts("No");    return 0;}
            ]]> + 模拟赛题讲解[1]

            来自 Roundgod2022-07-25 noi.ac #2676

            题目描述

            给定一个带权连通无向图 \(G=(V,E)\),其中顶点个数 \(|V|=n\) ,边数\(|E|=m\)图中可能包含重边以及自环。顶点编号为\(1−n\) ,边的编号为 \(1−m\) ,第 \(i\) 条边连接顶点 \(a_i\) 和 \(b_i\),权值为 \(c_i\).输入保证图中所有边的权值各不相同,即对于所有\(1\le i<j \le n\) ,都有 \(c_i\ne c_j\) 。

            你现在需要处理 \(q\) 个询问,其中第\(k\) 个询问是一个三元组 \((u_i,v_i,w_i)\) ,表示:

            如果在图 \(G\) 中添加一条连接 \(u_i\) 和 \(v_i\) 权值为 \(w_i\) 的边得到图 \(G^{\prime}\) ,图 \(G^{\prime}\)的最小生成树是否一定包含新添加的边?

            询问保证对于所有 \(1 \le j \le m\),都有 \(w_i\ne c_j\)

            输入格式

            输入第一行包含 \(3\) 个整数 \(n,m,q\),分别表示图的顶点数,图的边数,以及询问的个数。

            接下来 \(m\) 行,每行包含三个整数\(a_i,b_i,c_i(1\le a_i,b_i\le n)\),表示第 \(i\)条边连接的顶点以及权值。

            接下来 \(q\) 行,每行 \(u_i,v_i,w_i(1\le u_i,v_i \le n)\) , 表示第\(i\) 组询问。

            输出格式

            对于每组询问,根据答案你需要在一行中输出 Yes 或者No

            输入1

            5 6 31 2 22 3 31 3 62 4 54 5 93 5 81 3 13 4 73 5 7

            输出1

            YesNoYes

            数据范围

            对于 \(50\%\) 的数据,\(1\le n \le 200,~1\le m \le 1000,~1\le q\le1000,~1\le c_i,w_i \le 10^9\)

            对于 \(100\%\)的数据,\(1\le n\le 2\times 10^5,~1\le m\le 2\times10^5,~1\le q\le 2\times 10^5,~1\le c_i,w_i\le 10^{18}\)

            题解

            这个题还是蛮巧妙的

            首先考虑 \(q=1\) 的情况,

            显然此时我们可以把这条边直接加进去跑一遍 \(\text{kruskal}\)

            然后遇到这条边的时候,判断能否加入最小生成树

            然后我们考虑 \(q>1\) 的情况

            最简单的想法是对于每个询问都跑一次,显然不可接受

            仔细思考一下,就会发现,其实我们在判断的时候,

            还是在原生成树的基础上尝试加入这条边的

            而这个生成树是会把边一条一条从小到大加进去的

            所以如果 \(w_i\)比较小,那么它会被先询问到

            考虑离线处理所有询问,直接加入生成树去跑,然后只连原图的边

            时间复杂度 \(O((m+q) \log(m+q))\)

            数组别忘了开两倍哦~

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <set>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e5+15)int n,m,Q,pos,f[N],r[N],ans[N];set<int> s;struct Edge{int u,v,w,id;} e[N];bool operator<(Edge a,Edge b){return a.w<b.w;}void init(int n){for(int i=1; i<=n; i++) f[i]=i,r[i]=0;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int x,int y){    x=find(x); y=find(y);    if(x==y) return;    if(r[x]<r[y]) f[x]=y;    else f[y]=x,r[x]+=r[x]==r[y];}void kruskal(){    sort(e+1,e+1+pos); init(n);    for(int i=1; i<=pos; i++)    {        if(e[i].id==-1)        {            if(find(e[i].u)!=find(e[i].v))                merge(e[i].u,e[i].v);        }else        {            if(find(e[i].u)!=find(e[i].v))                ans[e[i].id]=1;            else ans[e[i].id]=0;        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m); read(Q);    for(int i=1,u,v,w; i<=m; i++)    {        read(u); read(v); read(w);        s.insert(w); e[++pos]={u,v,w,-1};    }    for(int i=1,u,v,w; i<=Q; i++)    {        read(u); read(v); read(w);        s.insert(w); e[++pos]={u,v,w,i};    }    kruskal();    for(int i=1; i<=Q; i++)        if(ans[i]) puts("Yes");        else puts("No");    return 0;}
            ]]> @@ -1914,7 +1914,7 @@ /2022/07/25/cf1468j-road-reform-ti-jie/ - CF1468J Road Reform 题解

            题目链接:CF1468J Road Reform

            题意

            给定一个有 $n$ 个节点,$m$ 条无向带权边的图,和一个参数 $k$,第 $i$ 条边权值为 $s_i$。

            现在你要保留这个图中的 $n-1$ 条边使得这个图变成一棵树,然后你可以对这棵树上的任意边进行修改,每次修改可以使这个边的权值加上一或减去一。

            现在你需要使所有边权的最大值正好等于 $k$,求所有保留方案的最小操作数。

            $T$ 组询问。

            保证初始时给定的图满足任意两个点互相可达,没有重边或自环。

            $1\leq T\leq 10^3$

            $1\leq n\leq2\times10^5,n-1\leq m\leq \min(\frac{n(n+1)}{2},2\times10^5)$

            $\sum n,\sum m\leq2\times10^5,~1\leq k,s_i\leq 10^9$

            模拟赛A题爆零,于是有了这篇题解

            感谢 @Roundgod 老师的耐心解释 Orz

            本题要根据最小生成树的情况分类讨论

            • 如果最小生成树的最大边超过 $k$

              则此时的最优方案即,将最小生成树上所有大于 $k$ 的边都减成 $k$

            • 如果最小生成树的最大边不超过 $k$

              此时有两种选择

              • 第一种,把最小生成树中的最大边抬到 $k$
              • 第二种,把不在最小生成树中的一条比 $k$ 大的边(显然要尽可能小些)加入最小生成树,然后去掉任意一条环上的边

              实现的时候,用mnimxi记录这两个值,最后去个 $\min$ 就好了

              注意,这是在最小生成树的最大边不超过 $k$ 的前提下!

            时间复杂度 $O(m \log m)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e5+15)int n,m,k,mxi,mni,res,ans,f[N],r[N];struct Edge{int u,v,w;} e[N];bool operator<(Edge a,Edge b){return a.w<b.w;}void init(int n){for(int i=1; i<=n; i++) f[i]=i,r[i]=0;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){    u=find(u); v=find(v);    if(u==v)return;    if(r[u]<r[v]) f[u]=v;    else    {        f[v]=u;        if(r[u]==r[v]) ++r[u];    }}void kruskal(){    sort(e+1,e+1+m);    ans=INF; res=0; mxi=INF; mni=INF;    for(int i=1; i<=m; i++)    {        int u=e[i].u,v=e[i].v,w=e[i].w;        if(w>=k)mxi=min(mxi,w-k);        else if(w<=k)mni=min(mni,k-w);        if(find(u)!=find(v))        {            merge(u,v);            if(w>k) res+=w-k;        }    }    if(res>0) ans=res;    else ans=min(mxi,mni);    write(ans);pc('\n');}void solve(){    // clear();    read(n); read(m); read(k); init(n);    for(int i=1,u,v,w; i<=m; i++)    {        read(u); read(v); read(w);        e[i]={u,v,w};    }    kruskal();}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q=1; read(Q);    while(Q--) solve();    return 0;}
            ]]> + CF1468J Road Reform 题解

            题目链接:CF1468JRoad Reform

            题意

            给定一个有 \(n\) 个节点,\(m\) 条无向带权边的图,和一个参数 \(k\),第 \(i\) 条边权值为 \(s_i\)。

            现在你要保留这个图中的 \(n-1\)条边使得这个图变成一棵树,然后你可以对这棵树上的任意边进行修改,每次修改可以使这个边的权值加上一或减去一。

            现在你需要使所有边权的最大值正好等于 \(k\),求所有保留方案的最小操作数。

            \(T\) 组询问。

            保证初始时给定的图满足任意两个点互相可达,没有重边或自环。

            \(1\leq T\leq 10^3\)

            \(1\leq n\leq2\times10^5,n-1\leq m\leq\min(\frac{n(n+1)}{2},2\times10^5)\)

            \(\sum n,\sum m\leq2\times10^5,~1\leqk,s_i\leq 10^9\)

            模拟赛A题爆零,于是有了这篇题解

            感谢 @Roundgod老师的耐心解释 Orz

            本题要根据最小生成树的情况分类讨论

            • 如果最小生成树的最大边超过 \(k\)

              则此时的最优方案即,将最小生成树上所有大于 \(k\) 的边都减成 \(k\)

            • 如果最小生成树的最大边不超过 \(k\)

              此时有两种选择

              • 第一种,把最小生成树中的最大边抬到 \(k\)
              • 第二种,把不在最小生成树中的一条比 \(k\)大的边(显然要尽可能小些)加入最小生成树,然后去掉任意一条环上的边

              实现的时候,用mnimxi记录这两个值,最后去个\(\min\) 就好了

              注意,这是在最小生成树的最大边不超过 \(k\) 的前提下!

            时间复杂度 \(O(m \log m)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e5+15)int n,m,k,mxi,mni,res,ans,f[N],r[N];struct Edge{int u,v,w;} e[N];bool operator<(Edge a,Edge b){return a.w<b.w;}void init(int n){for(int i=1; i<=n; i++) f[i]=i,r[i]=0;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){    u=find(u); v=find(v);    if(u==v)return;    if(r[u]<r[v]) f[u]=v;    else    {        f[v]=u;        if(r[u]==r[v]) ++r[u];    }}void kruskal(){    sort(e+1,e+1+m);    ans=INF; res=0; mxi=INF; mni=INF;    for(int i=1; i<=m; i++)    {        int u=e[i].u,v=e[i].v,w=e[i].w;        if(w>=k)mxi=min(mxi,w-k);        else if(w<=k)mni=min(mni,k-w);        if(find(u)!=find(v))        {            merge(u,v);            if(w>k) res+=w-k;        }    }    if(res>0) ans=res;    else ans=min(mxi,mni);    write(ans);pc('\n');}void solve(){    // clear();    read(n); read(m); read(k); init(n);    for(int i=1,u,v,w; i<=m; i++)    {        read(u); read(v); read(w);        e[i]={u,v,w};    }    kruskal();}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q=1; read(Q);    while(Q--) solve();    return 0;}
            ]]> @@ -1943,7 +1943,7 @@ /2022/07/25/cf1304e-1-trees-and-queries-ti-jie/ - CF1304E 1-Trees and Queries 题解

            题目链接:CF1304E 1-Trees and Queries

            题意

            题面是从模拟赛搬过来的,因此掺杂了某些奇怪的成分(?

            注:和原题好像就x,y,a,b反了一下

            A国总共有N一座美丽的城市,一共有N-1条道路两两连接这些城市,使得任意两座城市之间都通过道路连通。小虚接到了公司出差的派遣,来到了A国,在经过七天繁忙的工作后,小虚终于有时间在回程之前好好在A国旅游了。因为尚且不知道公司能报销的旅游额度,小虚哥罗列了很多份旅游计划。每个旅游计划包括出发城市编号x,结束城市编号y,以及公司能报销的道路费用k(即公司报销的差旅费能让小虚最多走k条道路),勤俭持家的小虚哥当然不会放过这样的旅游好机会,因此他无论如何,一定会走满k条道路(这导致了小虚可能会重复走某些道路以及重复到达某些城市),因此,小虚想请你帮他计算一下,对于每一份旅游计划,是否存在一个可能的旅游顺序,使得小虚从x城市出发,一共走k条道路(一定要走满k条),最终在城市y结束旅行,同时,对于一份计划,聪明的小虚还留了一招,那就是这份计划可以允许小虚坐飞机在城市a到城市b之间互相到达,每坐一次飞机也会消耗一次报销机会(相当于城市a和城市b之间多了一条临时道路,仅对这份计划有效)如果可以的话,那么小虚会发出形如“得~(一声)得~(更高的一声)得~(二声)得~(二声)”的得意声音输入第一行为一个整数n接下来的n-1行,每行输入两个整数u,v,表示城市u和城市v之间有一条道路连接。接下来一行输入一个整数Q,代表小虚的旅游计划数,接下来每行输入一个计划,包含五个数字a,b,x,y,k,具体含义如上文所述输出包含Q行,对于每一份计划,如果可行,输出"YES",否则,输出"NO" 3<=n<=1e5,1<=Q<=1e5,1<=k<=1e9,保证a≠b对于40%的数据,满足n,Q<=4000

            可以关注下钻虚哥,是为很鬼畜的巨佬(?

            不难发现,如果不加边,那能不能走到,与路径的奇偶性有关

            如果加了边,就有可能增加一条改变奇偶性的路径

            然后就很简单了,只要求个lca搞个树上路径就好了

            然后我模拟赛就翻车了,寄。

            时间复杂度 $O(Q\log n)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)int n,pos=1,head[N],lg[N],dep[N],fa[N][22];struct Edge{int u,v,next;}e[N<<1];int eq(int a,int b){return (a&1)==(b&1);}void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int f){    fa[u][0]=f; dep[u]=dep[f]+1;    for(int i=1; i<=lg[dep[u]]; i++)        fa[u][i]=fa[fa[u][i-1]][i-1];    for(int i=head[u]; i; i=e[i].next)        if(e[i].v!=f) dfs(e[i].v,u);}int lca(int x,int y){    if(dep[x]<dep[y])swap(x,y);    while(dep[x]>dep[y])x=fa[x][lg[dep[x]-dep[y]]-1];    if(x==y)return x;    for(int i=lg[dep[x]]-1; i>=0; i--)    {        if(fa[x][i]!=fa[y][i])            x=fa[x][i],y=fa[y][i];    }    return fa[x][0];}int dis(int x,int y){    int d=lca(x,y);    return abs(dep[x]-dep[d])+abs(dep[y]-dep[d]);}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1,u,v; i<n; i++)    {        read(u);read(v);        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        lg[i]=lg[i-1]+((1<<lg[i-1])==i);    dfs(1,0); dep[0]=0;    int a,b,x,y,k,Q;    // for(int i=1; i<=n; i++)    //     cout << dep[i] << ' ';    for(read(Q); Q--;)    {        read(a);read(b);read(x);read(y);read(k);        int tmp=dis(x,y);        if(k>=tmp&&eq(tmp,k)){ puts("YES"); continue;}        tmp=dis(x,a)+dis(y,b)+1;        if(k>=tmp&&eq(tmp,k)){ puts("YES"); continue;}        tmp=dis(x,b)+dis(y,a)+1;        if(k>=tmp&&eq(tmp,k)){ puts("YES"); continue;}        puts("NO");    }    return 0;}
            ]]> + CF1304E 1-Trees and Queries题解

            题目链接:CF1304E1-Trees and Queries

            题意

            题面是从模拟赛搬过来的,因此掺杂了某些奇怪的成分(?

            注:和原题好像就x,y,a,b反了一下

            A国总共有N一座美丽的城市,一共有N-1条道路两两连接这些城市,使得任意两座城市之间都通过道路连通。小虚接到了公司出差的派遣,来到了A国,在经过七天繁忙的工作后,小虚终于有时间在回程之前好好在A国旅游了。因为尚且不知道公司能报销的旅游额度,小虚哥罗列了很多份旅游计划。每个旅游计划包括出发城市编号x,结束城市编号y,以及公司能报销的道路费用k(即公司报销的差旅费能让小虚最多走k条道路),勤俭持家的小虚哥当然不会放过这样的旅游好机会,因此他无论如何,一定会走满k条道路(这导致了小虚可能会重复走某些道路以及重复到达某些城市),因此,小虚想请你帮他计算一下,对于每一份旅游计划,是否存在一个可能的旅游顺序,使得小虚从x城市出发,一共走k条道路(一定要走满k条),最终在城市y结束旅行,同时,对于一份计划,聪明的小虚还留了一招,那就是这份计划可以允许小虚坐飞机在城市a到城市b之间互相到达,每坐一次飞机也会消耗一次报销机会(相当于城市a和城市b之间多了一条临时道路,仅对这份计划有效)如果可以的话,那么小虚会发出形如“得~(一声)得~(更高的一声)得~(二声)得~(二声)”的得意声音输入第一行为一个整数n接下来的n-1行,每行输入两个整数u,v,表示城市u和城市v之间有一条道路连接。接下来一行输入一个整数Q,代表小虚的旅游计划数,接下来每行输入一个计划,包含五个数字a,b,x,y,k,具体含义如上文所述输出包含Q行,对于每一份计划,如果可行,输出"YES",否则,输出"NO" 3<=n<=1e5,1<=Q<=1e5,1<=k<=1e9,保证a≠b对于40%的数据,满足n,Q<=4000

            可以关注下钻虚哥,是为很鬼畜的巨佬(?

            不难发现,如果不加边,那能不能走到,与路径的奇偶性有关

            如果加了边,就有可能增加一条改变奇偶性的路径

            然后就很简单了,只要求个lca搞个树上路径就好了

            然后我模拟赛就翻车了,寄。

            时间复杂度 \(O(Q\log n)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)int n,pos=1,head[N],lg[N],dep[N],fa[N][22];struct Edge{int u,v,next;}e[N<<1];int eq(int a,int b){return (a&1)==(b&1);}void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int f){    fa[u][0]=f; dep[u]=dep[f]+1;    for(int i=1; i<=lg[dep[u]]; i++)        fa[u][i]=fa[fa[u][i-1]][i-1];    for(int i=head[u]; i; i=e[i].next)        if(e[i].v!=f) dfs(e[i].v,u);}int lca(int x,int y){    if(dep[x]<dep[y])swap(x,y);    while(dep[x]>dep[y])x=fa[x][lg[dep[x]-dep[y]]-1];    if(x==y)return x;    for(int i=lg[dep[x]]-1; i>=0; i--)    {        if(fa[x][i]!=fa[y][i])            x=fa[x][i],y=fa[y][i];    }    return fa[x][0];}int dis(int x,int y){    int d=lca(x,y);    return abs(dep[x]-dep[d])+abs(dep[y]-dep[d]);}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1,u,v; i<n; i++)    {        read(u);read(v);        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        lg[i]=lg[i-1]+((1<<lg[i-1])==i);    dfs(1,0); dep[0]=0;    int a,b,x,y,k,Q;    // for(int i=1; i<=n; i++)    //     cout << dep[i] << ' ';    for(read(Q); Q--;)    {        read(a);read(b);read(x);read(y);read(k);        int tmp=dis(x,y);        if(k>=tmp&&eq(tmp,k)){ puts("YES"); continue;}        tmp=dis(x,a)+dis(y,b)+1;        if(k>=tmp&&eq(tmp,k)){ puts("YES"); continue;}        tmp=dis(x,b)+dis(y,a)+1;        if(k>=tmp&&eq(tmp,k)){ puts("YES"); continue;}        puts("NO");    }    return 0;}
            ]]> @@ -1970,7 +1970,7 @@ /2022/07/25/cf645d-robot-rapping-results-report-ti-jie/ - CF645D Robot Rapping Results Report 题解

            题目链接:CF645D Robot Rapping Results Report

            题意

            $n$ 个机器人,每个机器人有一个不同的级别,级别介于 $1\sim n$,高级别的可以打败低级别的,现在给出 $n$ 个机器人的 $m$ 场比赛胜负情况,问最少需要前几场比赛就可以确定每个机器人的级别。

            输入格式

            第一行两个整数 $n$ 和 $m$ 表示机器人个数和比赛场数,之后 $m$ 行每行两个整数 $u$ 和 $v$ 表示机器人 $u$ 打败机器人 $v$。

            输出格式

            如果这 $m$ 场比赛可以确定每个机器人的级别则输出最少需要前几场比赛就可以确定,否则输出 $-1$。

            数据范围

            $2\leq n\leq 10^5$,$1\leq m\leq \min(\frac{n\times (n-1)}{2},10^5)$。

            不难发现这里有个单调性

            即越多的边越有可能确定每个机器人的级别

            当然如果所有边都不能确定的话,就输出无解即可

            所以解法一:二分可能的边数,然后topo,时间复杂度 $O(m \log m)$

            假如在某一时间队列中的元素多于一个,则这个图的拓扑序不只一种即为错误

            代码没写,基本上就是topo板子加一个if(q.size()>1) return 0;

            解法二:考虑直接topo,然后记录每个结点最少需要几条边才能确定

            答案就是所需边数量最大值。

            如果某一等级,即代码中的dfn,出现了不止一次

            则这两个机器人我们无法确定到底谁更强,则无解

            这个解法太难想到了,我看了题解才知道的 qwq

            时间复杂度 $O(n)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,m,ck,pos=1,id[N],in[N],mk[N],dfn[N],head[N];queue<int> q;struct Edge{int u,v,id,next;}e[N];void addEdge(int u,int v,int id){    e[++pos]={u,v,id,head[u]};    head[u]=pos;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge(u,v,i); ++in[v];    }    for(int i=1; i<=n; i++)        if(!in[i])        {            if(ck) return cout << "-1",0;            q.push(i); ck=1;        }    while(!q.empty())    {        int u=q.front(); q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(dfn[v]==dfn[u]+1) id[v]=min(id[v],e[i].id);            if(dfn[v]<dfn[u]+1) dfn[v]=dfn[u]+1,id[v]=e[i].id;            if(!(--in[v])) q.push(v);        }    }    for(int i=1; i<=n; i++)    {        if(mk[dfn[i]]) return cout << "-1",0;        mk[dfn[i]]=1;    }    int res=0;    for(int i=1; i<=n; i++)         res=max(res,id[i]);    cout << res << '\n';    return 0;}

            参考文献

            [1] https://www.luogu.com.cn/blog/EnochWenzhou/solution-cf645d

            ]]> + CF645D Robot RappingResults Report 题解

            题目链接:CF645DRobot Rapping Results Report

            题意

            \(n\)个机器人,每个机器人有一个不同的级别,级别介于 \(1\simn\),高级别的可以打败低级别的,现在给出 \(n\) 个机器人的 \(m\)场比赛胜负情况,问最少需要前几场比赛就可以确定每个机器人的级别。

            输入格式

            第一行两个整数 \(n\)\(m\) 表示机器人个数和比赛场数,之后 \(m\) 行每行两个整数 \(u\) 和 \(v\) 表示机器人 \(u\) 打败机器人 \(v\)。

            输出格式

            如果这 \(m\)场比赛可以确定每个机器人的级别则输出最少需要前几场比赛就可以确定,否则输出\(-1\)

            数据范围

            \(2\leq n\leq 10^5\)\(1\leq m\leq \min(\frac{n\times(n-1)}{2},10^5)\)。

            不难发现这里有个单调性

            即越多的边越有可能确定每个机器人的级别

            当然如果所有边都不能确定的话,就输出无解即可

            所以解法一:二分可能的边数,然后topo,时间复杂度\(O(m \log m)\)

            假如在某一时间队列中的元素多于一个,则这个图的拓扑序不只一种即为错误

            代码没写,基本上就是topo板子加一个if(q.size()>1) return 0;

            解法二:考虑直接topo,然后记录每个结点最少需要几条边才能确定

            答案就是所需边数量最大值。

            如果某一等级,即代码中的dfn,出现了不止一次

            则这两个机器人我们无法确定到底谁更强,则无解

            这个解法太难想到了,我看了题解才知道的 qwq

            时间复杂度 \(O(n)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,m,ck,pos=1,id[N],in[N],mk[N],dfn[N],head[N];queue<int> q;struct Edge{int u,v,id,next;}e[N];void addEdge(int u,int v,int id){    e[++pos]={u,v,id,head[u]};    head[u]=pos;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge(u,v,i); ++in[v];    }    for(int i=1; i<=n; i++)        if(!in[i])        {            if(ck) return cout << "-1",0;            q.push(i); ck=1;        }    while(!q.empty())    {        int u=q.front(); q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(dfn[v]==dfn[u]+1) id[v]=min(id[v],e[i].id);            if(dfn[v]<dfn[u]+1) dfn[v]=dfn[u]+1,id[v]=e[i].id;            if(!(--in[v])) q.push(v);        }    }    for(int i=1; i<=n; i++)    {        if(mk[dfn[i]]) return cout << "-1",0;        mk[dfn[i]]=1;    }    int res=0;    for(int i=1; i<=n; i++)         res=max(res,id[i]);    cout << res << '\n';    return 0;}

            参考文献

            [1] https://www.luogu.com.cn/blog/EnochWenzhou/solution-cf645d

            ]]> @@ -1997,7 +1997,7 @@ /2022/07/24/oi-yi-cuo-dian-c-yu-fa/ - OI易错点-C++语法

            不保证本文内容完全正确,仅仅是个人总结!!

            islowerisalphaisdigit 这种函数,返回值不是bool!!

            在不同的机器上跑出来是不一样的!!!!不要直接加上它!!!

            lower_bound(begin,end,x,greater<int>()) 返回第一个小于等于 $x$ 的位置

            去重 m=unique(b+1,b+1+n)-b-1;

            string==find() 是 $O(n)$ 的!

            注意 char 数组后面有个 \0因此多测一般不用清空但是\0后面的不会被清空

            next_permutation(a+1,a+1+n)下一个排列!到了5 4 3 2 1这种就结束了!!

            同理有 prev_permutation(a+1,a+1+n)

            凡是位运算都加个括号,比如 $\&$ 的优先级比 == 还要低!!!(待考证)

            判断两数的奇偶性是否相同不可以用异或因为高位还有数

            __gnu_pbds的头文件可以用 #include <bits/extc++.h>

            memcpy(a,b,sizeof(b)) 把 b 复制到 a

            自定义优先队列

            struct cmp{    bool operator()(int a,int b)    {return h[a]<h[b];}};priority_queue<int,vector<int>,cmp> q; // 每次返回h最大的那个

            str.c_str() 可以把string转化为char[] ,然后printf("%s\n",res.c_str());

            string str

            1. .find(‘a’)返回第一次出现a的位置
            2. .find(str_1)就是KMP
            3. .find(str_1,pos) 找pos起str_1的位置
            4. .substr(begin,length) begin开始length个字符

            string::find(str,pos),没找到返回的是 18446744073709551615 ,即string::npos

            char 数则较用 strcmp(<a>,<b>)不要直接用 ==会出问题

            reverse 函数 [first,last).

            fixed表示定点数

            • 如果不用fixed直接用setprecision(x)就是最多有效位数为 $x$ ,而且是科学计数法+四舍五入
            • 如果用了fixed 那么setprecision(x) 就是小数点后最多位数 $x$ ,非科学计数法+四舍五入

            atan2(y,x) 返回的值是方位角(极角)的值

            atan(y,x) 返回的值是反正切的值

            其实就是说,atan2(y,x)

            在一、二象限的时候返回的是 $[0,\pi]$ 的值

            在三、四象限的时候,返回的是 $(-\pi,0]$ 的值

            atan(y,x) 的返回值只有 $\left[-\dfrac{\pi}{2},\dfrac{\pi}{2}\right]$

            scanf(“%lf%lf”,&x,&y) 读double

            -0x3f3f3f3f3f3f3f3f-1 = (long long)0xc0c0c0c0c0c0c0c0注意后面一个数本质是 unsigned long long 的,要转成 long long !!!!

            合并两端有序的序列(归并的中间步骤)inplace_merge(l,mid,r)

            这里的 mid归并时右边一段的开头元素的位置!!

            还有 r最右边的元素的右边一个位置!!

            如果答案是 -1e-10 这种答案,要注意直接赋值为 0 ,不然会输出 -0.0000

            POJ上提交不能用万能头,而且要用 printf + %f 来输出 double

            shuffle(p+1,p+1+n,rd); 不用 rd()

            #define sum(x) ((x)*(x+1)/2) 注意宏定义一定要所有的 $x$ 都套个括号

            %llu 输出unsigned long long

            getline(cin,str[pos]);

            set::count 同 map,都是 $O(\log n)$

            scanf("%s",a);n=strlen(a+1)

            注意左移运算符

            如果写1<<31ll(long long)(1<<31)会爆int

            要写成1ll<<31

            判断第 $i$ 位是否为$1$

            ((m&(1ll<<(i-1)))!=0) // 一定要写 !=0 而且要套括号!((m>>(i-1))&1) // 这个安全一点

            终极快读不要和 scanf(“%s”,a+1) 这种混用!

            assert(x),当 $x$ 为 $\text{true}$ 时什么都不做

            double占8字节,long double占16字节,

            cmp最好不要写在结构体里

            因为成员函数指针和普通函数指针不同,前者实际上是这样子的。

            bool cmp(node*this, int a, int b) 

            开三次根号的方法(不要用pow

            1. cbrt() 答案为double,可以放long long进去

            2. 牛顿迭代法

            3. 二分。

            笛卡尔树是一种二叉树。

            stoi()stoll()

            对于

            bool operator<(node x,node y){return sum[x.id]>sum[y.id];}priority_queue<q779> q;

            这样的重载,sum的值一定要在push前就设置好!!!!

            ]]> + OI易错点-C++语法

            不保证本文内容完全正确,仅仅是个人总结!!

            islowerisalphaisdigit这种函数,返回值不是bool!!

            在不同的机器上跑出来是不一样的!!!!不要直接加上它!!!

            lower_bound(begin,end,x,greater<int>())返回第一个小于等于 \(x\) 的位置

            去重 m=unique(b+1,b+1+n)-b-1;

            string==find()\(O(n)\) 的!

            注意 char 数组后面有个 \0因此多测一般不用清空但是\0后面的不会被清空

            next_permutation(a+1,a+1+n)下一个排列!到了5 4 3 2 1这种就结束了!!

            同理有 prev_permutation(a+1,a+1+n)

            凡是位运算都加个括号,比如 \(\&\) 的优先级比 ==还要低!!!(待考证)

            判断两数的奇偶性是否相同不可以用异或因为高位还有数

            __gnu_pbds的头文件可以用#include <bits/extc++.h>

            memcpy(a,b,sizeof(b)) 把 b 复制到 a

            自定义优先队列

            struct cmp{    bool operator()(int a,int b)    {return h[a]<h[b];}};priority_queue<int,vector<int>,cmp> q; // 每次返回h最大的那个

            str.c_str() 可以把string转化为char[],然后printf("%s\n",res.c_str());

            string str

            1. .find(‘a’)返回第一次出现a的位置
            2. .find(str_1)就是KMP
            3. .find(str_1,pos) 找pos起str_1的位置
            4. .substr(begin,length) begin开始length个字符

            string::find(str,pos),没找到返回的是18446744073709551615 ,即string::npos

            char 数则较用 strcmp(<a>,<b>)不要直接用 ==会出问题

            reverse 函数 [first,last).

            fixed表示定点数

            • 如果不用fixed直接用setprecision(x)就是最多有效位数为\(x\) ,而且是科学计数法+四舍五入
            • 如果用了fixed 那么setprecision(x)就是小数点后最多位数 \(x\),非科学计数法+四舍五入

            atan2(y,x) 返回的值是方位角(极角)的值

            atan(y,x) 返回的值是反正切的值 \[\begin{aligned}\operatorname{atan2}(y,x)=\begin{cases}\arctan(\frac{y}{x})&x>0\\\arctan(\frac{y}{x})+\pi&y\ge0,x<0\\\arctan(\frac{y}{x})-\pi&y<0,x<0\\+\frac{\pi}{2}&y>0,x=0\\-\frac{\pi}{2}&y<0,x=0\\\text{undefined}&y=0,x=0\end{cases}\end{aligned}\]

            其实就是说,atan2(y,x)

            在一、二象限的时候返回的是 \([0,\pi]\) 的值

            在三、四象限的时候,返回的是 \((-\pi,0]\) 的值

            atan(y,x) 的返回值只有 \(\left[-\dfrac{\pi}{2},\dfrac{\pi}{2}\right]\)

            scanf(“%lf%lf”,&x,&y) 读double

            -0x3f3f3f3f3f3f3f3f-1 = (long long)0xc0c0c0c0c0c0c0c0注意后面一个数本质是 unsigned long long 的,要转成 long long !!!!

            合并两端有序的序列(归并的中间步骤)inplace_merge(l,mid,r)

            这里的 mid归并时右边一段的开头元素的位置!!

            还有 r最右边的元素的右边一个位置!!

            如果答案是 -1e-10 这种答案,要注意直接赋值为0 ,不然会输出 -0.0000

            POJ上提交不能用万能头,而且要用 printf + %f来输出 double

            shuffle(p+1,p+1+n,rd); 不用 rd()

            #define sum(x) ((x)*(x+1)/2) 注意宏定义一定要所有的\(x\) 都套个括号

            %llu 输出unsigned long long

            getline(cin,str[pos]);

            set::count 同 map,都是 \(O(\log n)\)

            scanf("%s",a);n=strlen(a+1)

            注意左移运算符

            如果写1<<31ll(long long)(1<<31)会爆int

            要写成1ll<<31

            判断第 \(i\) 位是否为\(1\)

            ((m&(1ll<<(i-1)))!=0) // 一定要写 !=0 而且要套括号!((m>>(i-1))&1) // 这个安全一点

            终极快读不要和 scanf(“%s”,a+1) 这种混用!

            assert(x),当 \(x\)\(\text{true}\) 时什么都不做

            double占8字节,long double占16字节,

            cmp最好不要写在结构体里

            因为成员函数指针和普通函数指针不同,前者实际上是这样子的。

            bool cmp(node*this, int a, int b) 

            开三次根号的方法(不要用pow

            1. cbrt()答案为double,可以放long long进去

            2. 牛顿迭代法 \[x_{i+1}=x_i-\dfrac{f(x_i)}{f^{\prime}(x_i)}\]

            3. 二分。

            笛卡尔树是一种二叉树。

            stoi()stoll()

            对于

            bool operator<(node x,node y){return sum[x.id]>sum[y.id];}priority_queue<q779> q;

            这样的重载,sum的值一定要在push前就设置好!!!!

            ]]> @@ -2022,7 +2022,7 @@ /2022/07/24/luo-gu-p2704-noi2001-pao-bing-zhen-di-ti-jie/ - 洛谷P2704 [NOI2001] 炮兵阵地 题解

            题目链接:P2704 [NOI2001] 炮兵阵地

            题意

            司令部的将军们打算在 $N\times M$ 的网格地图上部署他们的炮兵部队。

            一个 $N\times M$ 的地图由 $N$ 行 $M$ 列组成,地图的每一格可能是山地(用 $\texttt{H}$ 表示),也可能是平原(用 $\texttt{P}$ 表示),如下图。

            在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

            如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

            图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。

            现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

            对于 $100\%$ 的数据,$N\le 100$,$M\le 10$,保证字符仅包含 ph

            状压神仙题

            考虑压缩一行上每列的状态,然后再一行行的搞

            具体地,我们记录每一行所有列是否可以放炮兵

            然后预处理所有合法的放炮兵的方法(不考虑地图的状况)

            枚举转移即可,注意要滚动数组,还要单独处理前两行

            时间复杂度 $O(n2^{3m}) = O(n8^m)$

            代码:(f[i][j][k]表示前两行的状态是ijk是滚动数组)

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1024+5)char x;int n,m,res,mx,st[N],f[N][N][3],F[105],sum[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m; mx=(1<<m);    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)            cin >> x,F[i]=(F[i]<<1)+(x=='H');    for(int i=0; i<mx; i++)    {        sum[i]=__builtin_popcount(i); // 不建议使用,我是懒得写 qwq        st[i]=((i&(i<<1))==0)&&((i&(i<<2))==0);    }    for(int i=0; i<mx; i++)        if(st[i]&&((i&(F[1]))==0))            f[0][i][0]=sum[i];    for(int i=0; i<mx; i++)        for(int j=0; j<mx; j++)            if(st[i]&&st[j]&&((i&j)==0)&&((i&F[1])==0)&&((j&F[2])==0))                f[i][j][1]=sum[i]+sum[j];    for(int t=2; t<n; t++)        for(int i=0; i<mx; i++)        {            if((!st[i])||(i&F[t-1])) continue;            for(int j=0; j<mx; j++)            {                if((!st[j])||(j&F[t])||(i&j)) continue;                for(int k=0; k<mx; k++)                {                    if((!st[k])||(i&k)||(j&k)||(k&F[t+1])) continue;                    f[j][k][t%3]=max(f[j][k][t%3],f[i][j][((t-1)%3+3)%3]+sum[k]);                }            }        }    for(int i=0; i<mx; i++)        for(int j=0; j<mx; j++)            res=max(res,f[i][j][((n-1)%3+3)%3]);    cout << res << '\n';    return 0;}
            ]]> + 洛谷P2704 [NOI2001] 炮兵阵地题解

            题目链接:P2704[NOI2001] 炮兵阵地

            题意

            司令部的将军们打算在 \(N\times M\)的网格地图上部署他们的炮兵部队。

            一个 \(N\times M\) 的地图由 \(N\) 行 \(M\) 列组成,地图的每一格可能是山地(用\(\texttt{H}\) 表示),也可能是平原(用\(\texttt{P}\) 表示),如下图。

            在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:

            如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。

            图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。

            现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。

            对于 \(100\%\) 的数据,\(N\le 100\),\(M\le 10\),保证字符仅包含 ph

            状压神仙题

            考虑压缩一行上每列的状态,然后再一行行的搞

            具体地,我们记录每一行所有列是否可以放炮兵

            然后预处理所有合法的放炮兵的方法(不考虑地图的状况)

            枚举转移即可,注意要滚动数组,还要单独处理前两行

            时间复杂度 \(O(n2^{3m}) =O(n8^m)\)

            代码:(f[i][j][k]表示前两行的状态是ijk是滚动数组)

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1024+5)char x;int n,m,res,mx,st[N],f[N][N][3],F[105],sum[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m; mx=(1<<m);    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)            cin >> x,F[i]=(F[i]<<1)+(x=='H');    for(int i=0; i<mx; i++)    {        sum[i]=__builtin_popcount(i); // 不建议使用,我是懒得写 qwq        st[i]=((i&(i<<1))==0)&&((i&(i<<2))==0);    }    for(int i=0; i<mx; i++)        if(st[i]&&((i&(F[1]))==0))            f[0][i][0]=sum[i];    for(int i=0; i<mx; i++)        for(int j=0; j<mx; j++)            if(st[i]&&st[j]&&((i&j)==0)&&((i&F[1])==0)&&((j&F[2])==0))                f[i][j][1]=sum[i]+sum[j];    for(int t=2; t<n; t++)        for(int i=0; i<mx; i++)        {            if((!st[i])||(i&F[t-1])) continue;            for(int j=0; j<mx; j++)            {                if((!st[j])||(j&F[t])||(i&j)) continue;                for(int k=0; k<mx; k++)                {                    if((!st[k])||(i&k)||(j&k)||(k&F[t+1])) continue;                    f[j][k][t%3]=max(f[j][k][t%3],f[i][j][((t-1)%3+3)%3]+sum[k]);                }            }        }    for(int i=0; i<mx; i++)        for(int j=0; j<mx; j++)            res=max(res,f[i][j][((n-1)%3+3)%3]);    cout << res << '\n';    return 0;}
            ]]> @@ -2049,7 +2049,7 @@ /2022/07/24/cf898f-restoring-the-expression-ti-jie/ - CF898F Restoring the Expression 题解

            题目链接:CF898F Restoring the Expression

            题意

            给你一个数字字符串s,现在你要这个字符串切割成三个部分a,b,c,即s=a+b+c(这里的+为字符串相加,即后面的字符串拼接在前面的字符串后面)同时要满足a,b,c三个字符串代表的十进制数字满足 a+b=c(注意a,b,c都不能有前导0,如数字 0099 是非法的)问是否有这样一种分隔方式,题目保证有一组解,输出+号最靠前的一组答案输入一行一个字符串s,输出一行,形如a+b=c,满足s=a+b+c(注意要输出'+'号和'='号)|s|<=1e6,'0'<=si<='9'input:12345168output:123+45=168

            模拟赛秒出结论,结果脑抽挂了。。

            这题是字符串哈希,其实很好想

            一般的字符串哈希函数都是这么定义的

            有没有觉得这个形式很熟悉?

            如果 $x=10$ ?我相信你一定明白了

            这个字符串哈希的过程,

            其实就是把这一段字符表示成了模 $M$ 意义下的 $x$ 进制数

            因此,哈希,就没了。边界比较麻烦,要稍微处理一下

            我们模拟赛没卡单哈,据说CF卡了那些经典素数,比如9982443531e9+7

            所以我就照着题解写了个双哈,模数是9982448531e9+33

            至于为什么要卡,因为 $10$ 不是质数,冲突的概率会变大很多

            时间复杂度 $O(n)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)const int base=10;const int mod1=998244853;const int mod2=1e9+33;char s[N];int n,pos1,pos2,hsh[2][N],pwd[2][N];int gethash1(int l,int r){    int res=(hsh[0][r]-hsh[0][l-1]*pwd[0][r-l+1]%mod1)%mod1;    res=(res+mod1)%mod1;    return res;}int gethash2(int l,int r){    int res=(hsh[1][r]-hsh[1][l-1]*pwd[1][r-l+1]%mod2)%mod2;    res=(res+mod2)%mod2;    return res;}bool check(int l,int r){    if(s[l]!='0') return 1;    for(int i=l+1; i<=r; i++)        if(s[i]=='0') return 0;    return 1;}bool checkr1(int len,int n,int flag){    int l=n-(len<<1)+flag;    if(l<1) return 0;    return (((gethash1(1,l)+gethash1(l+1,n-len))%mod1==gethash1(n-len+1,n))&&check(l+1,n-len));}bool checkr2(int len,int n,int flag){    int l=n-(len<<1)+flag;    if(l<1) return 0;    return (((gethash2(1,l)+gethash2(l+1,n-len))%mod2==gethash2(n-len+1,n))&&check(l+1,n-len));}bool checkl1(int len,int n,int flag){    int l=len+flag-1;    if(l<1) return 0;    return (((gethash1(1,l)+gethash1(l+1,n-len))%mod1==gethash1(n-len+1,n))&&check(l+1,n-len));}bool checkl2(int len,int n,int flag){    int l=len+flag-1;    if(l<1) return 0;    return (((gethash2(1,l)+gethash2(l+1,n-len))%mod2==gethash2(n-len+1,n))&&check(l+1,n-len));}void updater(int len,int flag){    if(n-(len<<1)+flag<pos1) pos1=n-(len<<1)+flag,pos2=n-len;}void updatel(int len,int flag){    if(len+flag-1<pos1)pos1=len+flag-1,pos2=n-len;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (s+1); n=strlen(s+1);    pwd[0][0]=pwd[1][0]=1; pos1=pos2=n;    for(int i=1; i<=n; i++)    {        pwd[0][i]=pwd[0][i-1]*base%mod1;        hsh[0][i]=(hsh[0][i-1]*base%mod1+s[i]-'0')%mod1;        pwd[1][i]=pwd[1][i-1]*base%mod2;        hsh[1][i]=(hsh[1][i-1]*base%mod2+s[i]-'0')%mod2;    }    for(int len=n/3; len<=(n>>1); ++len)    {        if(!check(n-len+1,n)) continue;        if(checkr1(len,n,0)&&checkr2(len,n,0)) updater(len,0);if(checkr1(len,n,1)&&checkr2(len,n,1)) updater(len,1);if(checkl1(len,n,0)&&checkl2(len,n,0)) updatel(len,0);if(checkl1(len,n,1)&&checkl2(len,n,1)) updatel(len,1);    }    for(int i=1; i<=pos1; i++) cout << s[i];    cout << '+';    for(int i=pos1+1; i<=pos2; i++) cout << s[i];    cout << '=';    for(int i=pos2+1; i<=n; i++) cout << s[i];    cout << '\n';    return 0;}

            @zx2017:现在做没有意义的事就是为了以后能做有意义的事。

            @zx2017:要提高你们的游戏品味。

            @zx2017:你们有人打星际吗?

            有一说一,挺向往这样的快乐机房生活的。

            可惜没有这样的条件,终究还是孤身一人吧。

            参考文献

            [1] https://www.luogu.com.cn/blog/xixike/solution-cf898f

            ]]> + CF898F Restoring theExpression 题解

            题目链接:CF898FRestoring the Expression

            题意

            给你一个数字字符串s,现在你要这个字符串切割成三个部分a,b,c,即s=a+b+c(这里的+为字符串相加,即后面的字符串拼接在前面的字符串后面)同时要满足a,b,c三个字符串代表的十进制数字满足 a+b=c(注意a,b,c都不能有前导0,如数字 0099 是非法的)问是否有这样一种分隔方式,题目保证有一组解,输出+号最靠前的一组答案输入一行一个字符串s,输出一行,形如a+b=c,满足s=a+b+c(注意要输出'+'号和'='号)|s|<=1e6,'0'<=si<='9'input:12345168output:123+45=168

            模拟赛秒出结论,结果脑抽挂了。。

            这题是字符串哈希,其实很好想

            一般的字符串哈希函数都是这么定义的 \[f(s[1\dots l])=\sum_{i=1}^{l} s[i]\times b^{l-i} \mod M\] 有没有觉得这个形式很熟悉? \[ax^3+bx^2+cx+d\] 如果 \(x=10\)?我相信你一定明白了

            这个字符串哈希的过程,

            其实就是把这一段字符表示成了模 \(M\)意义下的 \(x\) 进制数

            因此,哈希,就没了。边界比较麻烦,要稍微处理一下

            我们模拟赛没卡单哈,据说CF卡了那些经典素数,比如9982443531e9+7

            所以我就照着题解写了个双哈,模数是9982448531e9+33

            至于为什么要卡,因为 \(10\)不是质数,冲突的概率会变大很多

            时间复杂度 \(O(n)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)const int base=10;const int mod1=998244853;const int mod2=1e9+33;char s[N];int n,pos1,pos2,hsh[2][N],pwd[2][N];int gethash1(int l,int r){    int res=(hsh[0][r]-hsh[0][l-1]*pwd[0][r-l+1]%mod1)%mod1;    res=(res+mod1)%mod1;    return res;}int gethash2(int l,int r){    int res=(hsh[1][r]-hsh[1][l-1]*pwd[1][r-l+1]%mod2)%mod2;    res=(res+mod2)%mod2;    return res;}bool check(int l,int r){    if(s[l]!='0') return 1;    for(int i=l+1; i<=r; i++)        if(s[i]=='0') return 0;    return 1;}bool checkr1(int len,int n,int flag){    int l=n-(len<<1)+flag;    if(l<1) return 0;    return (((gethash1(1,l)+gethash1(l+1,n-len))%mod1==gethash1(n-len+1,n))&&check(l+1,n-len));}bool checkr2(int len,int n,int flag){    int l=n-(len<<1)+flag;    if(l<1) return 0;    return (((gethash2(1,l)+gethash2(l+1,n-len))%mod2==gethash2(n-len+1,n))&&check(l+1,n-len));}bool checkl1(int len,int n,int flag){    int l=len+flag-1;    if(l<1) return 0;    return (((gethash1(1,l)+gethash1(l+1,n-len))%mod1==gethash1(n-len+1,n))&&check(l+1,n-len));}bool checkl2(int len,int n,int flag){    int l=len+flag-1;    if(l<1) return 0;    return (((gethash2(1,l)+gethash2(l+1,n-len))%mod2==gethash2(n-len+1,n))&&check(l+1,n-len));}void updater(int len,int flag){    if(n-(len<<1)+flag<pos1) pos1=n-(len<<1)+flag,pos2=n-len;}void updatel(int len,int flag){    if(len+flag-1<pos1)pos1=len+flag-1,pos2=n-len;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (s+1); n=strlen(s+1);    pwd[0][0]=pwd[1][0]=1; pos1=pos2=n;    for(int i=1; i<=n; i++)    {        pwd[0][i]=pwd[0][i-1]*base%mod1;        hsh[0][i]=(hsh[0][i-1]*base%mod1+s[i]-'0')%mod1;        pwd[1][i]=pwd[1][i-1]*base%mod2;        hsh[1][i]=(hsh[1][i-1]*base%mod2+s[i]-'0')%mod2;    }    for(int len=n/3; len<=(n>>1); ++len)    {        if(!check(n-len+1,n)) continue;        if(checkr1(len,n,0)&&checkr2(len,n,0)) updater(len,0);if(checkr1(len,n,1)&&checkr2(len,n,1)) updater(len,1);if(checkl1(len,n,0)&&checkl2(len,n,0)) updatel(len,0);if(checkl1(len,n,1)&&checkl2(len,n,1)) updatel(len,1);    }    for(int i=1; i<=pos1; i++) cout << s[i];    cout << '+';    for(int i=pos1+1; i<=pos2; i++) cout << s[i];    cout << '=';    for(int i=pos2+1; i<=n; i++) cout << s[i];    cout << '\n';    return 0;}

            @zx2017:现在做没有意义的事就是为了以后能做有意义的事。

            @zx2017:要提高你们的游戏品味。

            @zx2017:你们有人打星际吗?

            有一说一,挺向往这样的快乐机房生活的。

            可惜没有这样的条件,终究还是孤身一人吧。

            参考文献

            [1] https://www.luogu.com.cn/blog/xixike/solution-cf898f

            ]]> @@ -2078,7 +2078,7 @@ /2022/07/23/cf808g-anthem-of-berland-ti-jie/ - CF808G Anthem of Berland 题解

            题目链接:CF808G Anthem of Berland

            题意

            给定 $s$ 串和 $t$ 串,其中 $s$ 串包含小写字母和问号,$t$ 串只包含小写字母。

            假设共有 $k$ 个问号。

            你需要给把每个问号变成一个小写字母,共有 $26^k$ 种可能。

            对于每种可能,设 $t$ 匹配 $s$ 的次数为 $f_i$,请输出 $\max(f_i)$ 。

            $1\leq |s|,|t|\leq 10^5,|s|\times |t|\leq 10^7$

            样例三告诉你了,这题不是贪心。

            设 $f_i$ 表示前 $i$ 个字符的最大答案

            似乎不好转移,因为我们不知道上一个放的 $t$ 在哪里

            显然 $f_i$ 可以从 $f_{i-m}$ 转移过来,因为这里正好可以放得下一个 $t$

            但是 $t$ 是可以重叠放的,例如 $t=\tt{aabaa}$

            于是我们直接跳 $m$ 的 $\tt{fail}$ 数组,然后一个个转移吗?

            不,因为我们不知道 $f_{i-(m-j)}$ 是否放了 $t$

            注:放 $t$ 的方法是把新的 $t$ 的前缀和上一个 $t$ 的后缀重合着放

            不难发现这里要用到 $\tt{kmp}$ ,正确性显然。

            因此设 $g_i$ 表示最后一个放的 $t$ 以 $i$ 结尾,前 $i$ 个字符的最大答案

            因为 $f_i$ 表示可以放或者不放 $t$ ,所以转移方程是 $f_i=\max\{f_{i-1},g_i\}$

            时间复杂度 $O(|s| \times |t|)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)char s[N],t[N];int n,m,f[N],g[N],fail[N];bool check(int p){    for(int j=1; j<=m; j++)        if(s[p-j+1]!=t[m-j+1]&&s[p-j+1]!='?')            return 0;    return 1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (s+1) >> (t+1);    n=strlen(s+1); m=strlen(t+1);    for(int i=2,j=0; i<=m; i++)    {        while(j&&t[i]!=t[j+1])j=fail[j];        if(t[i]==t[j+1]) ++j;        fail[i]=j;    }    for(int i=1; i<=n; i++)    {        f[i]=f[i-1];        if(!check(i))continue;        g[i]=f[i-m]+1;        for(int j=fail[m]; j; j=fail[j])            g[i]=max(g[i],g[i-(m-j)]+1);        f[i]=max(f[i],g[i]);    }    cout << f[n] << '\n';    return 0;}
            ]]> + CF808G Anthem of Berland题解

            题目链接:CF808GAnthem of Berland

            题意

            给定 \(s\) 串和 \(t\) 串,其中 \(s\) 串包含小写字母和问号,\(t\) 串只包含小写字母。

            假设共有 \(k\) 个问号。

            你需要给把每个问号变成一个小写字母,共有 \(26^k\) 种可能。

            对于每种可能,设 \(t\) 匹配 \(s\) 的次数为 \(f_i\),请输出 \(\max(f_i)\) 。

            \(1\leq |s|,|t|\leq 10^5,|s|\times |t|\leq10^7\)

            样例三告诉你了,这题不是贪心。

            \(f_i\) 表示前 \(i\) 个字符的最大答案

            似乎不好转移,因为我们不知道上一个放的 \(t\) 在哪里

            显然 \(f_i\) 可以从 \(f_{i-m}\)转移过来,因为这里正好可以放得下一个 \(t\)

            但是 \(t\) 是可以重叠放的,例如\(t=\tt{aabaa}\)

            于是我们直接跳 \(m\)\(\tt{fail}\) 数组,然后一个个转移吗?

            不,因为我们不知道 \(f_{i-(m-j)}\)是否放了 \(t\)

            注:放 \(t\) 的方法是把新的 \(t\) 的前缀和上一个 \(t\) 的后缀重合着放

            不难发现这里要用到 \(\tt{kmp}\),正确性显然。

            因此设 \(g_i\) 表示最后一个放的\(t\)\(i\) 结尾,前 \(i\) 个字符的最大答案 \[g_i=\max\{g_{i-(m-j)}+1,g_i\}\] 因为 \(f_i\)表示可以放或者不放 \(t\),所以转移方程是 \(f_i=\max\{f_{i-1},g_i\}\)

            时间复杂度 \(O(|s| \times |t|)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)char s[N],t[N];int n,m,f[N],g[N],fail[N];bool check(int p){    for(int j=1; j<=m; j++)        if(s[p-j+1]!=t[m-j+1]&&s[p-j+1]!='?')            return 0;    return 1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (s+1) >> (t+1);    n=strlen(s+1); m=strlen(t+1);    for(int i=2,j=0; i<=m; i++)    {        while(j&&t[i]!=t[j+1])j=fail[j];        if(t[i]==t[j+1]) ++j;        fail[i]=j;    }    for(int i=1; i<=n; i++)    {        f[i]=f[i-1];        if(!check(i))continue;        g[i]=f[i-m]+1;        for(int j=fail[m]; j; j=fail[j])            g[i]=max(g[i],g[i-(m-j)]+1);        f[i]=max(f[i],g[i]);    }    cout << f[n] << '\n';    return 0;}
            ]]> @@ -2107,7 +2107,7 @@ /2022/07/23/mi-ma-sheng-cheng-qi/ - 密码生成器

            简单的小破密码生成器 qwq

            也不知道搞了这个有啥用处

            目前还比较脆弱,不支持不合法输入

            反正就是个瞎搞的东西 qwq

            代码:

            /*Name: 密码生成器1.0Author: q779Date: 2021.5.20Function:{可以生成长度4-256的密码允许包含数字、大小写字母以及部分特殊字符可以用于制作密码字典}*/#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define  num_start 0#define  num_end 9#define  Upper_start 10#define  Upper_end 35#define  Lower_start 36#define  Lower_end 61#define  Sp_char_start 62#define  Sp_char_end 69#define  min_len 4#define  max_len 256string pwdch="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*";void check_pwd(int &start,int &end,bool num,bool Upper,bool Lower,bool Sp_char){if(!(num||Upper||Lower||Sp_char))    {        cout << "输入不合法!" << endl;        return ;    }if(num) start=num_start;else if(Upper) start=Upper_start;else if(Lower) start=Lower_start; else start=Sp_char_start;if(Sp_char) end=Sp_char_end;else if(Lower) end=Lower_end;else if(Upper) end=Upper_end;else end=num_end;}void make_pwd(bool num,bool Upper,bool Lower,bool Sp_char,int len){if(len>max_len||len<min_len){cout << "不合法的输入" << endl;        return;}int start,end;check_pwd(start,end,num,Upper,Lower,Sp_char); int idx;string res;for(int i=1; i<=len; i++){doidx=rand()%(end-start+1)+start;        while(!(num && num_start <= idx && idx <= num_end ||Upper && Upper_start <= idx && idx <= Upper_end ||Lower && Lower_start <= idx && idx <=Lower_end ||Sp_char && Sp_char_start <=idx && idx <=Sp_char_end));res+=pwdch[idx];}cout << res << endl;} signed main(){    srand((unsigned)time(NULL));    ios::sync_with_stdio(0);        bool num,Upper,Lower,Sp_char;    int len,times;    cout << "欢迎使用密码生成器1.0" << endl;    cout << "请输入生成密码的限制条件" << endl;    cout << "是否包含数字? [0/1] > "; cin >> num;    cout << "是否包含大写字母? [0/1] > "; cin >> Upper;    cout << "是否包含小写字母? [0/1] > "; cin >> Lower;    cout << "是否包含特殊字符? [0/1] > "; cin >> Sp_char;    cout << "生成的长度? [4-256] > "; cin >> len;    cout << "生成数量? [0/1] > "; cin >> times;        #define  LOCAL    #ifdef LOCAL        freopen("pwdmaker.txt","w",stdout);    #endiffor(int i=1; i<=times; i++)        make_pwd(num,Upper,Lower,Sp_char,len);     #ifdef LOCAL        fclose(stdout);        // system("cp pwdmaker.txt /home/qry");//复制到主目录下// 这里的路径可以自行修改    #endif    return 0;} 
            ]]> + 密码生成器

            简单的小破密码生成器 qwq

            也不知道搞了这个有啥用处

            目前还比较脆弱,不支持不合法输入

            反正就是个瞎搞的东西 qwq

            代码:

            /*Name: 密码生成器1.0Author: q779Date: 2021.5.20Function:{可以生成长度4-256的密码允许包含数字、大小写字母以及部分特殊字符可以用于制作密码字典}*/#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define  num_start 0#define  num_end 9#define  Upper_start 10#define  Upper_end 35#define  Lower_start 36#define  Lower_end 61#define  Sp_char_start 62#define  Sp_char_end 69#define  min_len 4#define  max_len 256string pwdch="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*";void check_pwd(int &start,int &end,bool num,bool Upper,bool Lower,bool Sp_char){if(!(num||Upper||Lower||Sp_char))    {        cout << "输入不合法!" << endl;        return ;    }if(num) start=num_start;else if(Upper) start=Upper_start;else if(Lower) start=Lower_start; else start=Sp_char_start;if(Sp_char) end=Sp_char_end;else if(Lower) end=Lower_end;else if(Upper) end=Upper_end;else end=num_end;}void make_pwd(bool num,bool Upper,bool Lower,bool Sp_char,int len){if(len>max_len||len<min_len){cout << "不合法的输入" << endl;        return;}int start,end;check_pwd(start,end,num,Upper,Lower,Sp_char); int idx;string res;for(int i=1; i<=len; i++){doidx=rand()%(end-start+1)+start;        while(!(num && num_start <= idx && idx <= num_end ||Upper && Upper_start <= idx && idx <= Upper_end ||Lower && Lower_start <= idx && idx <=Lower_end ||Sp_char && Sp_char_start <=idx && idx <=Sp_char_end));res+=pwdch[idx];}cout << res << endl;} signed main(){    srand((unsigned)time(NULL));    ios::sync_with_stdio(0);        bool num,Upper,Lower,Sp_char;    int len,times;    cout << "欢迎使用密码生成器1.0" << endl;    cout << "请输入生成密码的限制条件" << endl;    cout << "是否包含数字? [0/1] > "; cin >> num;    cout << "是否包含大写字母? [0/1] > "; cin >> Upper;    cout << "是否包含小写字母? [0/1] > "; cin >> Lower;    cout << "是否包含特殊字符? [0/1] > "; cin >> Sp_char;    cout << "生成的长度? [4-256] > "; cin >> len;    cout << "生成数量? [0/1] > "; cin >> times;        #define  LOCAL    #ifdef LOCAL        freopen("pwdmaker.txt","w",stdout);    #endiffor(int i=1; i<=times; i++)        make_pwd(num,Upper,Lower,Sp_char,len);     #ifdef LOCAL        fclose(stdout);        // system("cp pwdmaker.txt /home/qry");//复制到主目录下// 这里的路径可以自行修改    #endif    return 0;} 
            ]]> @@ -2132,7 +2132,7 @@ /2022/07/23/san-zi-qi-mo-ni-qi-for-linux/ - 三子棋模拟器 for linux

            中考前一周开摆写的

            采用的是minimax算法和 $\alpha - \beta$ 剪枝

            目前是基于规则的估价函数(因为q779不会AI)

            目前在含C++环境的 ubuntu20.04 和 macOS Monterey 12.4下都是可以编译的

            代码:

            //######////      三子棋 for linux//      Dev By q779//      First Update 2021.6.6//      Latest Update 2022.7.23//      AI LEVEL  1.0////#######include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF (int)0x3f3f3f3f#define maxDEP 5struct Node{    int x=-1,y=-1,val=-INF;};//棋子:AI为1,玩家为2int eval(int b[5][5],int k){    int mark=k%2==0?2:1;//对手的    int count=0;    for(int i=1; i<=3; i++)    {        if((b[i][1]==mark||!b[i][1]) && (b[i][2]==mark||!b[i][2]) && (b[i][3]==mark||!b[i][3]))count++;        if((b[1][i]==mark||!b[1][i]) && (b[2][i]==mark||!b[2][i]) && (b[3][i]==mark||!b[3][i]))count++;        if(b[i][1]==1 && b[i][2]==1 && b[i][3]==1)return 50;        if(b[1][i]==1 && b[2][i]==1 && b[3][i]==1)return 50;        if(b[i][1]==2 && b[i][2]==2 && b[i][3]==2)return -50;        if(b[1][i]==2 && b[2][i]==2 && b[3][i]==2)return -50;    }    if((b[1][1]==1 && b[2][2]==1 && b[3][3]==1) || (b[1][3]==1 && b[2][2]==1 && b[3][1]==1))return 50;    if((b[1][1]==2 && b[2][2]==2 && b[3][3]==2) || (b[1][3]==2 && b[2][2]==2 && b[3][1]==2))return -50;    if((b[1][1]==mark||b[1][1]==0) && (b[2][2]==mark||b[2][2]==0) && (b[3][3]==mark||b[3][3]==0))count++;    if((b[1][3]==mark||b[1][3]==0) && (b[2][2]==mark||b[2][2]==0) && (b[3][1]==mark||b[3][1]==0))count++;    return mark==1?count:-count;}//棋盘struct board{    int body[5][5]={0};//棋盘    int count=0;    char AI='O',player='X';    char kong=' ';    //清空棋盘    void clear(){memset(body,0,sizeof(body));}    //输出棋盘    void print()    {        puts("    1   2   3  ");        for(int i=1; i<=3;i++)        {            puts("  -------------");            printf("%c | ",char(i+48));            for(int j=1; j<=3; j++)            {                if(body[i][j]==0)printf("  | ");                if(body[i][j]==1)printf("%c | ",AI);                if(body[i][j]==2)printf("%c | ",player);                if(j==3)puts("");            }        }        puts("  -------------");                        }    Node dfs(int k, int alpha, int beta)    {        if(k==maxDEP || count==9)        {            return Node({-1,-1,eval(body,k)});        }        int x=-1,y=-1;        for(int i=1; i<=3; i++)        for(int j=1; j<=3; j++)            if(body[i][j]==0)            {                body[i][j]=k%2==0?1:2;                count++;                Node node=dfs(k+1,alpha,beta);                if(k%2==0)//max                {                    if(node.val > alpha)                    {                        alpha=node.val;                        x=i,y=j;                    }                    if(node.val >= beta)                    {                        body[i][j]=0;count--;                        return Node({-1,-1,node.val});                    }                }else//min                {                    if(node.val < beta)                    {                        beta=node.val;                        x=i,y=j;                    }                    if(node.val <= alpha)                    {                        body[i][j]=0;count--;                        return Node({-1,-1,node.val});                    }                }                body[i][j]=0;                count--;            }        return Node({x,y,k%2==0?alpha:beta});    }    //循环游戏的主体    void Loop()    {        print();        bool flag=1;        while(count<9)        {                        int tx,ty;            SCAN:;            printf("input 坐标  列+[空格]+行 >");            scanf("%lld%lld",&ty,&tx);            if(tx<1||ty<1||tx>3||ty>3)            {                puts("非法输入!");                goto SCAN;            }            if(body[tx][ty])            {                puts("那里已经有棋子了!");                goto SCAN;            }            //放棋            body[tx][ty]=2;count++;            puts("");            if(!flag)system("clear");flag=0;            if(eval(body,0)>=50)            {                system("clear");                puts("你被人工智能打败了!");                puts("");                print();                return ;            }else if(eval(body,0)<=-50)            {                system("clear");                puts("你战胜了人工智能!");                puts("");                print();                return;            }else if(count==9)            {                system("clear");                puts("平局,显然你打不过人工智能了!");                puts("");                print();                return;            }            //AI下棋            Node node = dfs(0,-INF,INF);            if(body[node.x][node.y]==0)            {                body[node.x][node.y]=1;                puts("");puts("");                printf("AI 在 第%lld列 第%lld行 放了一颗棋子\n",node.y,node.x);                puts("");puts("");system("clear");                count++;                if(eval(body,0)>=50)                {                    system("clear");                    puts("你被人工智能打败了!");                    puts("");                    print();                    return ;                }            }else            {                if(eval(body,0)<=-50)                {                    system("clear");                    puts("你战胜了人工智能!");                    puts("");                    print();                    return;                }else                {                    system("clear");                    puts("平局,显然你打不过人工智能了!");                    puts("");                    print();                    return;                }            }            print();        }    }    }main_body;void start_game(){    int choose;    printf("欢迎来到三子棋游戏!\n\n");    printf("请选择你的棋子样式\n");    puts("");    puts("X  *********  [1]");    puts("$  *********  [2]");    puts("");    printf("默认样式为 X\n");    printf("> ");    scanf("%lld",&choose);    if(choose==1) main_body.player='X';    if(choose==2) main_body.player='$';    system("clear");    printf("\n祝你好运\n\n\n\nPause any key to continue.\n");    getchar();getchar();    system("clear");    main_body.Loop();}signed main(){    start_game();    return 0;}
            ]]> + 三子棋模拟器 for linux

            中考前一周开摆写的

            采用的是minimax算法和 \(\alpha -\beta\) 剪枝

            目前是基于规则的估价函数(因为q779不会AI)

            目前在含C++环境的 ubuntu20.04 和 macOS Monterey12.4下都是可以编译的

            代码:

            //######////      三子棋 for linux//      Dev By q779//      First Update 2021.6.6//      Latest Update 2022.7.23//      AI LEVEL  1.0////#######include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF (int)0x3f3f3f3f#define maxDEP 5struct Node{    int x=-1,y=-1,val=-INF;};//棋子:AI为1,玩家为2int eval(int b[5][5],int k){    int mark=k%2==0?2:1;//对手的    int count=0;    for(int i=1; i<=3; i++)    {        if((b[i][1]==mark||!b[i][1]) && (b[i][2]==mark||!b[i][2]) && (b[i][3]==mark||!b[i][3]))count++;        if((b[1][i]==mark||!b[1][i]) && (b[2][i]==mark||!b[2][i]) && (b[3][i]==mark||!b[3][i]))count++;        if(b[i][1]==1 && b[i][2]==1 && b[i][3]==1)return 50;        if(b[1][i]==1 && b[2][i]==1 && b[3][i]==1)return 50;        if(b[i][1]==2 && b[i][2]==2 && b[i][3]==2)return -50;        if(b[1][i]==2 && b[2][i]==2 && b[3][i]==2)return -50;    }    if((b[1][1]==1 && b[2][2]==1 && b[3][3]==1) || (b[1][3]==1 && b[2][2]==1 && b[3][1]==1))return 50;    if((b[1][1]==2 && b[2][2]==2 && b[3][3]==2) || (b[1][3]==2 && b[2][2]==2 && b[3][1]==2))return -50;    if((b[1][1]==mark||b[1][1]==0) && (b[2][2]==mark||b[2][2]==0) && (b[3][3]==mark||b[3][3]==0))count++;    if((b[1][3]==mark||b[1][3]==0) && (b[2][2]==mark||b[2][2]==0) && (b[3][1]==mark||b[3][1]==0))count++;    return mark==1?count:-count;}//棋盘struct board{    int body[5][5]={0};//棋盘    int count=0;    char AI='O',player='X';    char kong=' ';    //清空棋盘    void clear(){memset(body,0,sizeof(body));}    //输出棋盘    void print()    {        puts("    1   2   3  ");        for(int i=1; i<=3;i++)        {            puts("  -------------");            printf("%c | ",char(i+48));            for(int j=1; j<=3; j++)            {                if(body[i][j]==0)printf("  | ");                if(body[i][j]==1)printf("%c | ",AI);                if(body[i][j]==2)printf("%c | ",player);                if(j==3)puts("");            }        }        puts("  -------------");                        }    Node dfs(int k, int alpha, int beta)    {        if(k==maxDEP || count==9)        {            return Node({-1,-1,eval(body,k)});        }        int x=-1,y=-1;        for(int i=1; i<=3; i++)        for(int j=1; j<=3; j++)            if(body[i][j]==0)            {                body[i][j]=k%2==0?1:2;                count++;                Node node=dfs(k+1,alpha,beta);                if(k%2==0)//max                {                    if(node.val > alpha)                    {                        alpha=node.val;                        x=i,y=j;                    }                    if(node.val >= beta)                    {                        body[i][j]=0;count--;                        return Node({-1,-1,node.val});                    }                }else//min                {                    if(node.val < beta)                    {                        beta=node.val;                        x=i,y=j;                    }                    if(node.val <= alpha)                    {                        body[i][j]=0;count--;                        return Node({-1,-1,node.val});                    }                }                body[i][j]=0;                count--;            }        return Node({x,y,k%2==0?alpha:beta});    }    //循环游戏的主体    void Loop()    {        print();        bool flag=1;        while(count<9)        {                        int tx,ty;            SCAN:;            printf("input 坐标  列+[空格]+行 >");            scanf("%lld%lld",&ty,&tx);            if(tx<1||ty<1||tx>3||ty>3)            {                puts("非法输入!");                goto SCAN;            }            if(body[tx][ty])            {                puts("那里已经有棋子了!");                goto SCAN;            }            //放棋            body[tx][ty]=2;count++;            puts("");            if(!flag)system("clear");flag=0;            if(eval(body,0)>=50)            {                system("clear");                puts("你被人工智能打败了!");                puts("");                print();                return ;            }else if(eval(body,0)<=-50)            {                system("clear");                puts("你战胜了人工智能!");                puts("");                print();                return;            }else if(count==9)            {                system("clear");                puts("平局,显然你打不过人工智能了!");                puts("");                print();                return;            }            //AI下棋            Node node = dfs(0,-INF,INF);            if(body[node.x][node.y]==0)            {                body[node.x][node.y]=1;                puts("");puts("");                printf("AI 在 第%lld列 第%lld行 放了一颗棋子\n",node.y,node.x);                puts("");puts("");system("clear");                count++;                if(eval(body,0)>=50)                {                    system("clear");                    puts("你被人工智能打败了!");                    puts("");                    print();                    return ;                }            }else            {                if(eval(body,0)<=-50)                {                    system("clear");                    puts("你战胜了人工智能!");                    puts("");                    print();                    return;                }else                {                    system("clear");                    puts("平局,显然你打不过人工智能了!");                    puts("");                    print();                    return;                }            }            print();        }    }    }main_body;void start_game(){    int choose;    printf("欢迎来到三子棋游戏!\n\n");    printf("请选择你的棋子样式\n");    puts("");    puts("X  *********  [1]");    puts("$  *********  [2]");    puts("");    printf("默认样式为 X\n");    printf("> ");    scanf("%lld",&choose);    if(choose==1) main_body.player='X';    if(choose==2) main_body.player='$';    system("clear");    printf("\n祝你好运\n\n\n\nPause any key to continue.\n");    getchar();getchar();    system("clear");    main_body.Loop();}signed main(){    start_game();    return 0;}
            ]]> @@ -2157,7 +2157,7 @@ /2022/07/23/luo-gu-p3538-poi2012-okr-a-horrible-poem-ti-jie/ - 洛谷P3538 [POI2012]OKR-A Horrible Poem 题解

            题目链接:P3538 [POI2012]OKR-A Horrible Poem

            题意

            给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节。如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到。第一行一个正整数n(n≤500 000),表示S的长度。第二行n个小写英文字母,表示字符串S。第三行一个正整数q(q≤2 000 000),表示询问次数。下面q行每行两个正整数a,b(1≤a≤b≤n),表示询问字符串S[a…b]的最短循环节长度。

            考虑循环节与原串的关系

            首先循环节的长度一定是原串长度的因数

            这样枚举长度就可以 $O(\log n)$ 了

            具体地,用欧拉筛预处理出 $1\sim n$ 中所有数的最小因数 $g_i$ ,

            然后直接跳 $g$ 数组即可

            那么确定是否是原串的循环节呢?

            设当前枚举的串为 $A$ (不是在说字符 $\tt{A}$ 哦)

            如果它是循环节,那么原串一定长这样

            方便起见,我们考虑这样的情况

            仔细想一想,会发现其实没必要一个个比较

            我们只要比较 $\color{red}{AAAA}\color{black}{A}$ 和 $\color{black}{A}\color{red}{AAAA}$ 就可以了(只比较红色部分)

            字符串比较?考虑 $\tt{Hash}$ 。

            更多关于字符串哈希,见 CF1200E Compress Words 题解OI模板-字符串

            时间复杂度 $O(m \log n)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)const int hash_base=31;const int hash_mod=998244353;bool ck[N]; char s[N];int n,pcnt,pri[N],g[N],hsh[N],pwd[N];void euler(){    for(int i=2; i<=n; i++)    {        if(!ck[i])        {            pri[++pcnt]=i;            g[i]=i;        }        for(int j=1; j<=pcnt&&pri[j]*i<=n; j++)        {            ck[pri[j]*i]=1; g[pri[j]*i]=pri[j];            if(i%pri[j]==0) break;        }    }}int gethash(int l,int r){    int res=(hsh[r]-hsh[l-1]*pwd[r-l+1])%hash_mod;    res=(res+hash_mod)%hash_mod;    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n; euler();    cin >> (s+1);    pwd[0]=1; hsh[0]=0;    for(int i=1; i<=n; i++)    {        pwd[i]=pwd[i-1]*hash_base%hash_mod;        hsh[i]=(hsh[i-1]*hash_base+s[i])%hash_mod;    }    int Q,l,r,len,ans;    for(cin >> Q; Q--;)    {        cin >> l >> r;        ans=len=r-l+1;        if(gethash(l+1,r)==gethash(l,r-1))            {cout << "1\n";continue;}        while(len>1)        {            if(gethash(l+ans/g[len],r)==gethash(l,r-ans/g[len]))                ans/=g[len];            len/=g[len];        }        cout << ans << '\n';    }    return 0;}
            ]]> + 洛谷P3538[POI2012]OKR-A Horrible Poem 题解

            题目链接:P3538[POI2012]OKR-A Horrible Poem

            题意

            给出一个由小写英文字母组成的字符串S,再给出q个询问,要求回答S某个子串的最短循环节。如果字符串B是字符串A的循环节,那么A可以由B重复若干次得到。第一行一个正整数n(n≤500 000),表示S的长度。第二行n个小写英文字母,表示字符串S。第三行一个正整数q(q≤2 000 000),表示询问次数。下面q行每行两个正整数a,b(1≤a≤b≤n),表示询问字符串S[a…b]的最短循环节长度。

            考虑循环节与原串的关系

            首先循环节的长度一定是原串长度的因数

            这样枚举长度就可以 \(O(\log n)\)

            具体地,用欧拉筛预处理出 \(1\sim n\)中所有数的最小因数 \(g_i\)

            然后直接跳 \(g\) 数组即可

            那么确定是否是原串的循环节呢?

            设当前枚举的串为 \(A\)(不是在说字符 \(\tt{A}\) 哦)

            如果它是循环节,那么原串一定长这样 \[A\dots AA\] 方便起见,我们考虑这样的情况 \[AAAAA\] 仔细想一想,会发现其实没必要一个个比较

            我们只要比较 \(\color{red}{AAAA}\color{black}{A}\) 和\(\color{black}{A}\color{red}{AAAA}\)就可以了(只比较红色部分)

            字符串比较?考虑 \(\tt{Hash}\)

            更多关于字符串哈希,见 CF1200ECompress Words 题解 和 OI模板-字符串

            时间复杂度 \(O(m \log n)\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)const int hash_base=31;const int hash_mod=998244353;bool ck[N]; char s[N];int n,pcnt,pri[N],g[N],hsh[N],pwd[N];void euler(){    for(int i=2; i<=n; i++)    {        if(!ck[i])        {            pri[++pcnt]=i;            g[i]=i;        }        for(int j=1; j<=pcnt&&pri[j]*i<=n; j++)        {            ck[pri[j]*i]=1; g[pri[j]*i]=pri[j];            if(i%pri[j]==0) break;        }    }}int gethash(int l,int r){    int res=(hsh[r]-hsh[l-1]*pwd[r-l+1])%hash_mod;    res=(res+hash_mod)%hash_mod;    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n; euler();    cin >> (s+1);    pwd[0]=1; hsh[0]=0;    for(int i=1; i<=n; i++)    {        pwd[i]=pwd[i-1]*hash_base%hash_mod;        hsh[i]=(hsh[i-1]*hash_base+s[i])%hash_mod;    }    int Q,l,r,len,ans;    for(cin >> Q; Q--;)    {        cin >> l >> r;        ans=len=r-l+1;        if(gethash(l+1,r)==gethash(l,r-1))            {cout << "1\n";continue;}        while(len>1)        {            if(gethash(l+ans/g[len],r)==gethash(l,r-ans/g[len]))                ans/=g[len];            len/=g[len];        }        cout << ans << '\n';    }    return 0;}
            ]]> @@ -2186,7 +2186,7 @@ /2022/07/23/cf888e-maximum-subsequence-ti-jie/ - CF888E Maximum Subsequence 题解

            题目链接:CF888E Maximum Subsequence

            题意

            给定N个数,第i个数的值为Ai,你现在可以从中选择一些数字,问选出数字的和模P最大为多少。输入第一行为N,P第二行为N个数,第i个数字为Ai1<=n<=35,1<=P<=1e9,0<=Ai<=1e9

            模拟赛合并复杂度寄了,特写此篇题解

            这个 $n\le 35$ 就很明显是折半搜索

            我们用 $p,q$ 两个数组记录两部分搜索出来的所有可能的数

            $2^{17}=131072$ ,因此数组完全够

            关键在于如何合并两部分的答案

            直接两层循环枚举那么复杂度依旧是 $O(2^n)$ (我就是这么寄的

            注意到 $p,q$ 满足 $0\le p_i,q_j<m$

            因此对于每个 $p_i$ 我们只要找到最大的一个 $q_j$ 满足

            正确性显然。

            这样只要排个序然后维护双指针就好了

            时间复杂度 $O(2^{n/2}\log 2^{n/2})$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e5+15)int k,t,n,m,b,a[45],p[N],q[N];void dfs1(int i,int sum){    if(i==b){p[++k]=sum,p[++k]=(sum+a[b])%m; return;}    dfs1(i+1,sum); dfs1(i+1,(sum+a[i])%m);}void dfs2(int i,int sum){    if(i==n){q[++t]=sum,q[++t]=(sum+a[n])%m; return;}    dfs2(i+1,sum); dfs2(i+1,(sum+a[i])%m);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;b=n>>1;    for(int i=1; i<=n; i++) cin >> a[i];    if(n==1) return cout << a[1]%m,0;    dfs1(1,0);dfs2(b+1,0);    int i=0,j=t,res=0;    sort(p+1,p+1+k);sort(q+1,q+1+t);    for(; i<=k; i++)    {        while(p[i]+q[j]>=m)--j;        res=max(res,p[i]+q[j]);    }    res=max(res,(p[k]+q[t])%m);    cout << res << '\n';    return 0;}
            ]]> + CF888E Maximum Subsequence题解

            题目链接:CF888EMaximum Subsequence

            题意

            给定N个数,第i个数的值为Ai,你现在可以从中选择一些数字,问选出数字的和模P最大为多少。输入第一行为N,P第二行为N个数,第i个数字为Ai1<=n<=35,1<=P<=1e9,0<=Ai<=1e9

            模拟赛合并复杂度寄了,特写此篇题解

            这个 \(n\le 35\)就很明显是折半搜索

            我们用 \(p,q\)两个数组记录两部分搜索出来的所有可能的数

            \(2^{17}=131072\),因此数组完全够

            关键在于如何合并两部分的答案

            直接两层循环枚举那么复杂度依旧是 \(O(2^n)\) (我就是这么寄的

            注意到 \(p,q\) 满足 \(0\le p_i,q_j<m\)

            因此对于每个 \(p_i\)我们只要找到最大的一个 \(q_j\) 满足\[p_i+q_j<m\] 正确性显然。

            这样只要排个序然后维护双指针就好了

            时间复杂度 \(O(2^{n/2}\log2^{n/2})\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e5+15)int k,t,n,m,b,a[45],p[N],q[N];void dfs1(int i,int sum){    if(i==b){p[++k]=sum,p[++k]=(sum+a[b])%m; return;}    dfs1(i+1,sum); dfs1(i+1,(sum+a[i])%m);}void dfs2(int i,int sum){    if(i==n){q[++t]=sum,q[++t]=(sum+a[n])%m; return;}    dfs2(i+1,sum); dfs2(i+1,(sum+a[i])%m);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;b=n>>1;    for(int i=1; i<=n; i++) cin >> a[i];    if(n==1) return cout << a[1]%m,0;    dfs1(1,0);dfs2(b+1,0);    int i=0,j=t,res=0;    sort(p+1,p+1+k);sort(q+1,q+1+t);    for(; i<=k; i++)    {        while(p[i]+q[j]>=m)--j;        res=max(res,p[i]+q[j]);    }    res=max(res,(p[k]+q[t])%m);    cout << res << '\n';    return 0;}
            ]]> @@ -2211,7 +2211,7 @@ /2022/07/22/luo-gu-p1879-usaco06nov-corn-fields-g-ti-jie/ - 洛谷P1879 [USACO06NOV]Corn Fields G 题解

            题目链接:P1879 [USACO06NOV]Corn Fields G

            题意

            农场主 $\rm John$ 新买了一块长方形的新牧场,这块牧场被划分成 $M$ 行 $N$ 列 $(1 \le M \le 12; 1 \le N \le 12)$,每一格都是一块正方形的土地。 $\rm John$ 打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。

            遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是 $\rm John$ 不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。

            $\rm John$ 想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

            状压经典题

            首先把每一行的情况压到状态 $F_i$ 中

            可以预处理所有状态(选择哪些种草)在行上是否合法

            例如两块草不能相邻选,当然此时不用考虑可不可以种

            判断依据如下

            int mx=1<<n;for(int i=0; i<mx; i++)    st[i]=( ((i&(i<<1))==0) && ((i&(i>>1))==0) );

            然后枚举状态转移

            时间复杂度 $O(n2^{2n})$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(15)const int p=1e9;bool st[5005];int n,m,mp[N][N],F[N],f[N][5005];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> m >> n;    for(int i=1; i<=m; i++)        for(int j=1; j<=n; j++)            cin >> mp[i][j];    for(int i=1; i<=m; i++)        for(int j=1; j<=n; j++)            F[i]=(F[i]<<1)+mp[i][j];    int mx=1<<n;    for(int i=0; i<mx; i++)        st[i]=( ((i&(i<<1))==0) && ((i&(i>>1))==0) );    f[0][0]=1;    for(int i=1; i<=m; i++)        for(int j=0; j<mx; j++)            if(st[j] && ((j&F[i])==j))                for(int k=0; k<mx; k++)                    if((k&j)==0) f[i][j]=(f[i][j]+f[i-1][k])%p;    int res=0;    for(int i=0; i<mx; i++)        res+=f[m][i],res%=p;    cout << res << '\n';    return 0;}
            ]]> + 洛谷P1879[USACO06NOV]Corn Fields G 题解

            题目链接:P1879[USACO06NOV]Corn Fields G

            题意

            农场主 \(\rm John\)新买了一块长方形的新牧场,这块牧场被划分成 \(M\) 行 \(N\) 列 \((1 \le M\le 12; 1 \le N \le 12)\),每一格都是一块正方形的土地。 \(\rm John\)打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。

            遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是\(\rm John\)不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。

            \(\rm John\)想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)

            状压经典题

            首先把每一行的情况压到状态 \(F_i\)

            可以预处理所有状态(选择哪些种草)在行上是否合法

            例如两块草不能相邻选,当然此时不用考虑可不可以种

            判断依据如下

            int mx=1<<n;for(int i=0; i<mx; i++)    st[i]=( ((i&(i<<1))==0) && ((i&(i>>1))==0) );

            然后枚举状态转移

            时间复杂度 \(O(n2^{2n})\)

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(15)const int p=1e9;bool st[5005];int n,m,mp[N][N],F[N],f[N][5005];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> m >> n;    for(int i=1; i<=m; i++)        for(int j=1; j<=n; j++)            cin >> mp[i][j];    for(int i=1; i<=m; i++)        for(int j=1; j<=n; j++)            F[i]=(F[i]<<1)+mp[i][j];    int mx=1<<n;    for(int i=0; i<mx; i++)        st[i]=( ((i&(i<<1))==0) && ((i&(i>>1))==0) );    f[0][0]=1;    for(int i=1; i<=m; i++)        for(int j=0; j<mx; j++)            if(st[j] && ((j&F[i])==j))                for(int k=0; k<mx; k++)                    if((k&j)==0) f[i][j]=(f[i][j]+f[i-1][k])%p;    int res=0;    for(int i=0; i<mx; i++)        res+=f[m][i],res%=p;    cout << res << '\n';    return 0;}
            ]]> @@ -2238,7 +2238,7 @@ /2022/07/22/luo-gu-p2900-usaco08mar-land-acquisition-g-ti-jie/ - 洛谷P2900 [USACO08MAR]Land Acquisition G 题解

            题目链接:P2900 [USACO08MAR]Land Acquisition G

            题意

            Farmer John 准备扩大他的农场,眼前他正在考虑购买 $N$ 块长方形的土地。

            如果 FJ 单买一块土地,价格就是土地的面积。但他可以选择并购一组土地,并购的价格为这些土地中最大的长乘以最大的宽。比如 FJ 并购一块 $3 \times 5$ 和一块 $5 \times 3$ 的土地,他只需要支付 $5 \times 5=25$ 元, 比单买合算。

            FJ 希望买下所有的土地。他发现,将这些土地分成不同的小组来并购可以节省经费。 给定每份土地的尺寸,请你帮助他计算购买所有土地所需的最小费用。

            第一行一个整数 $N$($1 \leq N \leq 5 \times 10^4$)。

            接下来 $N$ 行,每行两个整数 $w_i$ 和 $l_i$,代表第 $i$ 块土地的长和宽。保证土地的长和宽不超过 $10^6$。

            输出买下所有土地的最小费用。

            斜率优化板子题。

            首先可以推出来一个原始的转移方程

            设 $f_i$ 表示买前 $i$ 个土地的最小花费

            但是这样子根本没法优化。

            考虑怎样的土地可以对答案计算产生贡献(也就是必须计算花费的)

            或者说,怎样的土地不能产生贡献

            设土地 $i,j$ 满足 $w_i \ge w_j,~l_i \ge l_j$

            则购买 $i$ 的时候, $j$ 可以与其并购并且不增加额外的花费

            因此我们将所有物品按 $w_i$ 从大到小排序后所有能产生贡献的一定 $l_i$ 比之前大

            即 $\forall i<j,w_i\ge w_j,l_i\le l_j$

            至此,我们可以推出更好的转移方程

            设 $f_i$ 表示买前 $i$ 个土地的最小花费

            当 $i$ 固定时,考虑怎样的一个 $j\,(i>j>k)$ 使得 $j$ 比 $k$ 更优

            由于 $w$ 是递减的,即 $w_{k+1} \ge w_{j+1}$ ,因此移项可得

            把右边的看作斜率,然后就是斜率优化

            因为要求最小化 $f_i$ ,因此我们要维护一个下凸壳

            关于凸壳的问题,可以看这篇 P3195 [HNOI2008]玩具装箱

            时间复杂度 $O(n \log n)$

            代码:

            #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)struct node{    int x,y;}a[N];int n,st,en,q[N],f[N];double slope(int i,int j){    return 1.0*(f[i]-f[j])/(a[j+1].x-a[i+1].x);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i].x >> a[i].y;    sort(a+1,a+1+n,[](node a,node b)    {        return a.x==b.x?a.y>b.y:a.x>b.x;    });    int t=0;    for(int i=1; i<=n; i++)        if(a[i].y>a[t].y) a[++t]=a[i];    n=t; st=en=1;    for(int i=1; i<=n; i++)    {        while(st<en&&slope(q[st],q[st+1])<=a[i].y)++st; // 去掉不优的点        f[i]=f[q[st]]+a[q[st]+1].x*a[i].y;        while(st<en&&slope(q[en-1],i)<slope(q[en-1],q[en]))--en; // 维护下凸壳        q[++en]=i;    }    cout << f[n] << '\n';    return 0;}

            参考文献

            [1] https://www.luogu.com.cn/problem/solution/P2900

            ]]> + 洛谷P2900[USACO08MAR]Land Acquisition G 题解

          题目链接:P2900[USACO08MAR]Land Acquisition G

          题意

          Farmer John 准备扩大他的农场,眼前他正在考虑购买 \(N\) 块长方形的土地。

          如果 FJ单买一块土地,价格就是土地的面积。但他可以选择并购一组土地,并购的价格为这些土地中最大的长乘以最大的宽。比如FJ 并购一块 \(3 \times 5\) 和一块 \(5 \times 3\) 的土地,他只需要支付 \(5 \times 5=25\) 元, 比单买合算。

          FJ希望买下所有的土地。他发现,将这些土地分成不同的小组来并购可以节省经费。给定每份土地的尺寸,请你帮助他计算购买所有土地所需的最小费用。

          第一行一个整数 \(N\)\(1 \leq N \leq 5 \times 10^4\))。

          接下来 \(N\) 行,每行两个整数 \(w_i\) 和 \(l_i\),代表第 \(i\) 块土地的长和宽。保证土地的长和宽不超过\(10^6\)

          输出买下所有土地的最小费用。

          斜率优化板子题。

          首先可以推出来一个原始的转移方程

          \(f_i\) 表示买前 \(i\) 个土地的最小花费 \[f_i=\min\{f_{j} + \text{mxw}\{j+1,i\} \times \text{mxl}\{j+1,i\}\}\] 但是这样子根本没法优化。

          考虑怎样的土地可以对答案计算产生贡献(也就是必须计算花费的)

          或者说,怎样的土地不能产生贡献

          设土地 \(i,j\) 满足 \(w_i \ge w_j,~l_i \ge l_j\)

          则购买 \(i\) 的时候, \(j\) 可以与其并购并且不增加额外的花费

          因此我们将所有物品按 \(w_i\)从大到小排序后所有能产生贡献的一定 \(l_i\) 比之前大

          \(\forall i<j,w_i\ge w_j,l_i\lel_j\)

          至此,我们可以推出更好的转移方程

          \(f_i\) 表示买前 \(i\) 个土地的最小花费 \[f_i = \min\{f_j + w_{j+1}\times l_i \}\] 当 \(i\)固定时,考虑怎样的一个 \(j\,(i>j>k)\) 使得 \(j\) 比 \(k\) 更优 \[f_{j}+w_{j+1}\times l_i \le f_k + w_{k+1} \times l_i\\f_j-f_k \le l_i \times (w_{k+1}-w_{j+1})\] 由于 \(w\) 是递减的,即 \(w_{k+1} \ge w_{j+1}\) ,因此移项可得 \[\dfrac{f_j-f_k}{w_{k+1}-w_{j+1}} \le l_i\] 把右边的看作斜率,然后就是斜率优化

          因为要求最小化 \(f_i\),因此我们要维护一个下凸壳

          关于凸壳的问题,可以看这篇 P3195[HNOI2008]玩具装箱

          时间复杂度 \(O(n \log n)\)

          代码:

          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)struct node{    int x,y;}a[N];int n,st,en,q[N],f[N];double slope(int i,int j){    return 1.0*(f[i]-f[j])/(a[j+1].x-a[i+1].x);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i].x >> a[i].y;    sort(a+1,a+1+n,[](node a,node b)    {        return a.x==b.x?a.y>b.y:a.x>b.x;    });    int t=0;    for(int i=1; i<=n; i++)        if(a[i].y>a[t].y) a[++t]=a[i];    n=t; st=en=1;    for(int i=1; i<=n; i++)    {        while(st<en&&slope(q[st],q[st+1])<=a[i].y)++st; // 去掉不优的点        f[i]=f[q[st]]+a[q[st]+1].x*a[i].y;        while(st<en&&slope(q[en-1],i)<slope(q[en-1],q[en]))--en; // 维护下凸壳        q[++en]=i;    }    cout << f[n] << '\n';    return 0;}

          参考文献

          [1] https://www.luogu.com.cn/problem/solution/P2900

          ]]> @@ -2267,7 +2267,7 @@ /2022/07/22/cf479e-riding-in-a-lift-ti-jie/ - CF479E Riding in a Lift 题解

          题目链接:CF479E Riding in a Lift

          题意

          现在有n个传送点呈序列排列,编号为1到n每一次可以通过折跃从一个传送点传送到另一处传送点,由于折跃需要消耗巨大的能量,因此每次传送的距离小于出发传送点到能量水晶的距离,能量水晶在编号为b的传送点上,即对于编号为x的出发点,目标传送点的编号y要满足如下的要求:|x-y|<|x-b| (x≠y)同时,由于能量水晶储存着巨大的能量,严禁随意接近,因此你不能传送到b号传送点。无聊的你想知道,在a号传送点的你,经过k次折跃之后,能有多少种不同的传送点访问序列输入一行四个数:n,a,b,k,其中 2≤n≤5000,1≤k≤5000,1≤a,b≤n,且保证a≠b输出一行为总的序列数,答案要对1000000007取模(1e9+7)

          dp大水题,模拟赛花了十几分钟就A了

          用刷表法更新答案

          每个点 $j$ (除了 $b$ ,它不更新)能更新的范围都是

          直接暴力去更新肯定是不行的

          注意到其实它的更新是区间加,考虑差分,然后去掉「跳到自己」的贡献

          时间复杂度 $O(nk)$

          代码:

          #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e3+15)const int p=1e9+7;int n,a,b,k,f[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> a >> b >> k;    f[0][a]=1;    for(int i=0; i<k; i++)    {        for(int j=1; j<=n; j++)        {            if(j==b)continue;            int t=abs(j-b);            int l=max(1ll,j-t+1),r=min(n,j+t-1);            f[i+1][l]=(f[i+1][l]+f[i][j])%p;            f[i+1][r+1]=((f[i+1][r+1]-f[i][j])%p+p)%p;            // for(int k=l; k<=r; k++)            //     if(k!=b&&k!=j)f[i+1][k]=(f[i+1][k]+f[i][j])%p;        }        for(int j=1; j<=n; j++)                f[i+1][j]=(f[i+1][j-1]+f[i+1][j])%p;            for(int j=1; j<=n; j++)                f[i+1][j]=((f[i+1][j]-f[i][j])%p+p)%p;    }    int res=0;    for(int i=1; i<=n; i++)        res=((res+f[k][i])%p+p)%p;    cout << res << '\n';    return 0;}
          ]]> + CF479E Riding in a Lift 题解

        题目链接:CF479ERiding in a Lift

        题意

        现在有n个传送点呈序列排列,编号为1到n每一次可以通过折跃从一个传送点传送到另一处传送点,由于折跃需要消耗巨大的能量,因此每次传送的距离小于出发传送点到能量水晶的距离,能量水晶在编号为b的传送点上,即对于编号为x的出发点,目标传送点的编号y要满足如下的要求:|x-y|<|x-b| (x≠y)同时,由于能量水晶储存着巨大的能量,严禁随意接近,因此你不能传送到b号传送点。无聊的你想知道,在a号传送点的你,经过k次折跃之后,能有多少种不同的传送点访问序列输入一行四个数:n,a,b,k,其中 2≤n≤5000,1≤k≤5000,1≤a,b≤n,且保证a≠b输出一行为总的序列数,答案要对1000000007取模(1e9+7)

        dp大水题,模拟赛花了十几分钟就A了

        用刷表法更新答案

        每个点 \(j\) (除了 \(b\) ,它不更新)能更新的范围都是 \[\left[\max(1,j-|j-b|+1),j) \cup (j,\min(n,j+|j-b|-1)\right]\] 直接暴力去更新肯定是不行的

        注意到其实它的更新是区间加,考虑差分,然后去掉「跳到自己」的贡献

        时间复杂度 \(O(nk)\)

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e3+15)const int p=1e9+7;int n,a,b,k,f[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> a >> b >> k;    f[0][a]=1;    for(int i=0; i<k; i++)    {        for(int j=1; j<=n; j++)        {            if(j==b)continue;            int t=abs(j-b);            int l=max(1ll,j-t+1),r=min(n,j+t-1);            f[i+1][l]=(f[i+1][l]+f[i][j])%p;            f[i+1][r+1]=((f[i+1][r+1]-f[i][j])%p+p)%p;            // for(int k=l; k<=r; k++)            //     if(k!=b&&k!=j)f[i+1][k]=(f[i+1][k]+f[i][j])%p;        }        for(int j=1; j<=n; j++)                f[i+1][j]=(f[i+1][j-1]+f[i+1][j])%p;            for(int j=1; j<=n; j++)                f[i+1][j]=((f[i+1][j]-f[i][j])%p+p)%p;    }    int res=0;    for(int i=1; i<=n; i++)        res=((res+f[k][i])%p+p)%p;    cout << res << '\n';    return 0;}
        ]]> @@ -2294,7 +2294,7 @@ /2022/07/21/luo-gu-p2657-scoi2009-windy-shu-ti-jie/ - 洛谷P2657 [SCOI2009] windy 数 题解

        题目链接:P2657 [SCOI2009] windy 数

        题意

        不含前导零且相邻两个数字之差至少为 $2$ 的正整数被称为 windy 数。windy 想知道,在 $a$ 和 $b$ 之间,包括 $a$ 和 $b$ ,总共有多少个 windy 数?

        对于全部的测试点,保证 $1 \leq a \leq b \leq 2 \times 10^9$。

        常规的数位dp,只需要记录上一位的数字即可

        但是注意,最高位是不需要限制的

        具体的,我们是从高位往低位做dp的

        至于为什么高位往低位做dp,可以看下面的解释

        因为低位开始会导致无法判断是否超过了当前的限制

        例如 $4234$ ,假设我们从高位开始做

        如果以 $4$ 开始,可以发现接下来一位不能超过 $3$

        如果用 $0 \sim 3$ ,则接下来没有任何限制

        但是如果我们从低位开始做,比如填了一个 $8$

        那么最后我们有可能推出来个 $4238$ 之类的

        因为无法判断

        回到刚刚的问题,我们从高位往低位做数位dp

        那么第一位,也就是我们推的某个数的最高位,

        是可以随便填而不受到大于 $2$ 那个限制的(即前导零不能限制实际的最高位选择)

        因此我们还需要记录是否之前全是 $0$

        复杂度不太清楚,不过应该和位数有关,这题的位数就十几,所以基本上是 $O(1)$ 的

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n,a[15],num[15],f[15][15];int dfs(int u,int pre,int st,int limit){    if(!u) return 1;    if(!limit&&f[u][pre]!=-1)        return f[u][pre];    int up=limit?num[u]:9,ans=0;    for(int i=0; i<=up; i++)    {        if(abs(i-pre)<2) continue;        if(st&&!i) ans+=dfs(u-1,-2,1,limit&&num[u]==i);        else ans+=dfs(u-1,i,0,limit&&num[u]==i);    }    if(!limit&&!st) f[u][pre]=ans;    return ans;}int solve(int x){    int len=0;    while(x>0)    {        num[++len]=x%10;        x/=10;    }    return dfs(len,-2,1,1);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(f,-1,sizeof(f));    int l,r; cin >> l >> r;    cout << solve(r)-solve(l-1) << '\n';    return 0;}
        ]]> + 洛谷P2657 [SCOI2009] windy 数题解

        题目链接:P2657[SCOI2009] windy 数

        题意

        不含前导零且相邻两个数字之差至少为 \(2\) 的正整数被称为 windy 数。windy想知道,在 \(a\)\(b\) 之间,包括 \(a\) 和 \(b\) ,总共有多少个 windy 数?

        对于全部的测试点,保证 \(1 \leq a \leq b\leq 2 \times 10^9\)

        常规的数位dp,只需要记录上一位的数字即可

        但是注意,最高位是不需要限制的

        具体的,我们是从高位往低位做dp的

        至于为什么高位往低位做dp,可以看下面的解释

        因为低位开始会导致无法判断是否超过了当前的限制

        例如 \(4234\),假设我们从高位开始做

        如果以 \(4\)开始,可以发现接下来一位不能超过 \(3\)

        如果用 \(0 \sim 3\),则接下来没有任何限制

        但是如果我们从低位开始做,比如填了一个 \(8\)

        那么最后我们有可能推出来个 \(4238\)之类的

        因为无法判断

        回到刚刚的问题,我们从高位往低位做数位dp

        那么第一位,也就是我们推的某个数的最高位,

        是可以随便填而不受到大于 \(2\)那个限制的(即前导零不能限制实际的最高位选择)

        因此我们还需要记录是否之前全是 \(0\)

        复杂度不太清楚,不过应该和位数有关,这题的位数就十几,所以基本上是\(O(1)\)

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n,a[15],num[15],f[15][15];int dfs(int u,int pre,int st,int limit){    if(!u) return 1;    if(!limit&&f[u][pre]!=-1)        return f[u][pre];    int up=limit?num[u]:9,ans=0;    for(int i=0; i<=up; i++)    {        if(abs(i-pre)<2) continue;        if(st&&!i) ans+=dfs(u-1,-2,1,limit&&num[u]==i);        else ans+=dfs(u-1,i,0,limit&&num[u]==i);    }    if(!limit&&!st) f[u][pre]=ans;    return ans;}int solve(int x){    int len=0;    while(x>0)    {        num[++len]=x%10;        x/=10;    }    return dfs(len,-2,1,1);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(f,-1,sizeof(f));    int l,r; cin >> l >> r;    cout << solve(r)-solve(l-1) << '\n';    return 0;}
        ]]> @@ -2321,7 +2321,7 @@ /2022/07/21/luo-gu-p1410-zi-xu-lie-ti-jie/ - 洛谷P1410 子序列 题解

        题目链接:P1410 子序列

        题意

        给定一个长度为 $N$($N$ 为偶数)的序列,问能否将其划分为两个长度为 $N / 2$ 的严格递增子序列。

        【数据范围】

        共三组数据,每组数据行数<=50,0 <= 输入的所有数 <= 10^9

        第一组(30%):N <= 20

        第二组(30%):N <= 100

        第三组(40%):N <= 2000

        首先可以有个原始的思路

        设 $f_{i,j,k}$ 表示前 $i$ 位,以 $i$ 结尾的上升子序列长度为 $j$ ,另一个上升子序列以 $k$ 结尾是否可行。

        如果仅仅用是否可行去做,比较浪费。

        注意到我们一定希望 $a_k$ 尽可能小,这个很好用dp去算

        考虑把 $k$ 这一维去掉,移到 $f$ 里让它算。

        设 $f_{i,j}$ 表示以 $i$ 结尾的上升子序列长度为 $j$ ,不以 $i$ 结尾的另一条上升子序列的结尾的最小值。

        如果 $a_i < a_{i+1}$ ,那么 $a_{i+1}$ 可以被加入以 $i$ 结尾的上升子序列

        如果 $f_{i,j}<a_{i+1}$ ,那么 $a_{i+1}$ 可以被加入不以 $i$ 结尾的上升子序列

        注意此时如果加入不以 $i$ 结尾的上升子序列更优的话,

        • 不以 $i$ 结尾的那个子序列变成了以 $i+1$ 结尾的子序列
        • 以 $i$ 结尾的那个子序列变成了不以 $i+1$ 结尾的子序列

        时间复杂度 $O(Qn^2)$

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,a[N],f[N][N];void clear(){    for(int i=0; i<=n; i++)        for(int j=0; j<=n; j++)            f[i][j]=INF;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(cin >> n)    {        clear();        for(int i=1; i<=n; i++) cin >> a[i];        f[1][1]=-1;        for(int i=1; i<=n; i++)            for(int j=1; j<=i; j++)            if(f[i][j]!=INF)            {                if(a[i]<a[i+1])                    f[i+1][j+1]=min(f[i+1][j+1],f[i][j]);                if(f[i][j]<a[i+1])                    f[i+1][i-j+1]=min(f[i+1][i-j+1],a[i]);            }                            cout << (f[n][n/2]!=INF?"Yes!\n":"No!\n");    }    return 0;}
        ]]> + 洛谷P1410 子序列 题解

        题目链接:P1410子序列

        题意

        给定一个长度为 \(N\)\(N\)为偶数)的序列,问能否将其划分为两个长度为 \(N/ 2\) 的严格递增子序列。

        【数据范围】

        共三组数据,每组数据行数<=50,0 <= 输入的所有数 <= 10^9

        第一组(30%):N <= 20

        第二组(30%):N <= 100

        第三组(40%):N <= 2000

        首先可以有个原始的思路

        \(f_{i,j,k}\) 表示前 \(i\) 位,以 \(i\) 结尾的上升子序列长度为 \(j\) ,另一个上升子序列以 \(k\) 结尾是否可行。

        如果仅仅用是否可行去做,比较浪费。

        注意到我们一定希望 \(a_k\)尽可能小,这个很好用dp去算

        考虑把 \(k\) 这一维去掉,移到 \(f\) 里让它算。

        \(f_{i,j}\) 表示以 \(i\) 结尾的上升子序列长度为 \(j\) ,不以 \(i\)结尾的另一条上升子序列的结尾的最小值。

        如果 \(a_i < a_{i+1}\) ,那么\(a_{i+1}\) 可以被加入以 \(i\) 结尾的上升子序列 \[f_{i+1,j+1}=\min\{f_{i+1,j+1},f_{i,j}\}\] 如果 \(f_{i,j}<a_{i+1}\),那么 \(a_{i+1}\) 可以被加入不以 \(i\) 结尾的上升子序列

        注意此时如果加入不以 \(i\)结尾的上升子序列更优的话,

        • 不以 \(i\) 结尾的那个子序列变成了以\(i+1\) 结尾的子序列
        • \(i\) 结尾的那个子序列变成了不以\(i+1\) 结尾的子序列

        \[f_{i+1,i-j+1}=\min\{f_{i+1,i-j+1},a_i\}\]

        时间复杂度 \(O(Qn^2)\)

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,a[N],f[N][N];void clear(){    for(int i=0; i<=n; i++)        for(int j=0; j<=n; j++)            f[i][j]=INF;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(cin >> n)    {        clear();        for(int i=1; i<=n; i++) cin >> a[i];        f[1][1]=-1;        for(int i=1; i<=n; i++)            for(int j=1; j<=i; j++)            if(f[i][j]!=INF)            {                if(a[i]<a[i+1])                    f[i+1][j+1]=min(f[i+1][j+1],f[i][j]);                if(f[i][j]<a[i+1])                    f[i+1][i-j+1]=min(f[i+1][i-j+1],a[i]);            }                            cout << (f[n][n/2]!=INF?"Yes!\n":"No!\n");    }    return 0;}
        ]]> @@ -2348,7 +2348,7 @@ /2022/07/21/cf914d-bash-and-a-tough-math-puzzle-ti-jie/ - CF914D Bash and a Tough Math Puzzle 题解

        题目链接:CF914D Bash and a Tough Math Puzzle

        题意

        • 给定长度为 $n$ 的序列 $a$。$m$ 次操作。操作有两种:
          1. 1 l r x:若能在 $a_l\sim a_r$ 中 至多 修改一个数,使得 $\gcd(a_l,a_{l+1},\cdots,a_r)=x$,输出 YES,否则输出 NO。注意:我们不需要进行实际的改动。
          2. 2 i y:将 $a_i$ 修改为 $y$。
        • $1\le n\le 5\times 10^5$,$1\le m\le 4\times 10^5$,$1\le a_i,x,y\le 10^9$。

        设区间内不能被 $x$ 整除的数的个数为 $\text{cnt}$

        • 若 $\text{cnt}=0$ ,则改不改无所谓
        • 若 $\text{cnt}=1$ ,则直接把那个数修改为 $x$ 即可
        • 若 $\text{cnt}>1$ ,则无解,输出NO

        考虑用线段树维护区间 $\gcd$

        查询时,只进入区间 $\gcd$ 能被 $x$ 整除的子结点

        如果到达叶子就 ++cnt

        时间复杂度 $O(n \log^2 n)$

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e5+15)int n,Q,cnt,seg[N<<2];#define ls(x) ((x)<<1)#define rs(x) ((x)<<1|1)int gcd(int a,int b){return b==0?a:gcd(b,a%b);}void push_up(int at){    seg[at]=gcd(seg[ls(at)],seg[rs(at)]);}void build(int l,int r,int at){    if(l==r)    {        read(seg[at]);        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void modify(int x,int l,int r,int k,int at){    if(l==r)    {        seg[at]=k;        return;    }    int mid=(l+r)>>1;    if(x<=mid) modify(x,l,mid,k,ls(at));    else modify(x,mid+1,r,k,rs(at));    push_up(at);}void query(int nl,int nr,int l,int r,int k,int at){    if(cnt>1) return;    if(l==r)    {        ++cnt;        return;    }    int mid=(l+r)>>1;    if(nl<=mid&&seg[ls(at)]%k) query(nl,nr,l,mid,k,ls(at));    if(nr>mid&&seg[rs(at)]%k) query(nl,nr,mid+1,r,k,rs(at));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    build(1,n,1); read(Q);    for(int op,x,y,z; Q--;)    {        read(op); read(x); read(y);        if(op==1)        {            read(z);            cnt=0,query(x,y,1,n,z,1);            puts((cnt>1)?"NO":"YES");        }else        {            modify(x,1,n,y,1);        }    }    return 0;}
        ]]> + CF914D Bash and aTough Math Puzzle 题解

        题目链接:CF914DBash and a Tough Math Puzzle

        题意

        • 给定长度为 \(n\) 的序列 \(a\)。\(m\)次操作。操作有两种:
          1. 1 l r x:若能在 \(a_l\sima_r\)至多 修改一个数,使得 \(\gcd(a_l,a_{l+1},\cdots,a_r)=x\),输出YES,否则输出NO。注意:我们不需要进行实际的改动。
          2. 2 i y:将 \(a_i\)修改为 \(y\)
        • \(1\le n\le 5\times 10^5\)\(1\le m\le 4\times 10^5\),\(1\le a_i,x,y\le 10^9\)。

        设区间内不能被 \(x\)整除的数的个数为 \(\text{cnt}\)

        • \(\text{cnt}=0\),则改不改无所谓
        • \(\text{cnt}=1\),则直接把那个数修改为 \(x\) 即可
        • \(\text{cnt}>1\),则无解,输出NO

        考虑用线段树维护区间 \(\gcd\)

        查询时,只进入区间 \(\gcd\) 能被\(x\) 整除的子结点

        如果到达叶子就 ++cnt

        时间复杂度 \(O(n \log^2 n)\)

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e5+15)int n,Q,cnt,seg[N<<2];#define ls(x) ((x)<<1)#define rs(x) ((x)<<1|1)int gcd(int a,int b){return b==0?a:gcd(b,a%b);}void push_up(int at){    seg[at]=gcd(seg[ls(at)],seg[rs(at)]);}void build(int l,int r,int at){    if(l==r)    {        read(seg[at]);        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void modify(int x,int l,int r,int k,int at){    if(l==r)    {        seg[at]=k;        return;    }    int mid=(l+r)>>1;    if(x<=mid) modify(x,l,mid,k,ls(at));    else modify(x,mid+1,r,k,rs(at));    push_up(at);}void query(int nl,int nr,int l,int r,int k,int at){    if(cnt>1) return;    if(l==r)    {        ++cnt;        return;    }    int mid=(l+r)>>1;    if(nl<=mid&&seg[ls(at)]%k) query(nl,nr,l,mid,k,ls(at));    if(nr>mid&&seg[rs(at)]%k) query(nl,nr,mid+1,r,k,rs(at));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    build(1,n,1); read(Q);    for(int op,x,y,z; Q--;)    {        read(op); read(x); read(y);        if(op==1)        {            read(z);            cnt=0,query(x,y,1,n,z,1);            puts((cnt>1)?"NO":"YES");        }else        {            modify(x,1,n,y,1);        }    }    return 0;}
        ]]> @@ -2377,7 +2377,7 @@ /2022/07/21/cf475d-cgcdssq-ti-jie/ - CF475D CGCDSSQ 题解

        题目链接:CF475D CGCDSSQ

        题意

        给出一个长度为 $n$ 的序列和 $q$ 个询问,

        每个询问输出一行,询问满足 $\gcd\{a_l,a_{l+1},\dots,a_r\}=x$ 的 $[l,r]$ 的对数

        $ 1 \le n \le 10^5,~1 \le q \le 3 \times 10^5,~1 \le a_i,x_i \le 10^9$

        这题有一个很有趣的结论

        区间 $\gcd$ 最多有 $O(n\log \max\{a_i\})$ 种可能的值

        证明:

        原命题等价于: $l$ 固定时,$[l,r]$ 的区间 $\gcd$ 至多有 $O(\log \max\{a_i\})$ 种取值。

        假设区间 $\gcd$ 在 $i$ 处发生了变化,此时新的 $\gcd$ 一定是原 $\gcd$ 的因数

        因此新的 $\gcd$ 至少缩小了一半,则至多有 $O(\log \max\{a_i\})$ 种取值。

        这样我们就可以预处理所有答案,用个unordered_map记录了

        注:这里其实可以优化一下,不用记录所有答案,只记录询问的答案

        那么答案怎么统计呢?

        区间 $\gcd$ 直接用st表维护,应该很容易想到。

        当 $l$ 固定时,至多有 $O(\log \max\{a_i\})$ 种取值

        而这个值一定随着右端点的增大单调不升

        考虑二分右端点,然后记录下答案啥的

        时间复杂度 $O(n\log n \log \max \{a_i\})$

        注意st表一定要预处理查询时候的lg[]数组,因为log()常数比较大

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <unordered_map>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)#define M (int)(3e5+15)int n,m,a[N],q[M],lg[N],f[N][20];unordered_map<int,int>ans;int gcd(int a,int b){return b==0?a:gcd(b,a%b);}void change(int u,int x){f[u][0]=x;for(int i=1; u-(1<<i)>=0; i++)f[u][i]=gcd(f[u][i-1],f[u-(1<<(i-1))][i-1]);}int query(int l,int r){int k=lg[r-l+1];return gcd(f[l+(1<<k)-1][k],f[r][k]);}int find(int g,int l,int i){    int r=n;    while(l<r)    {        int mid=(l+r+1)>>1;        if(query(i,mid)==g)l=mid;        else r=mid-1;    }    return l;}void solve(int i){    int last,now=i,g=a[i];    while(1)    {        last=now; now=find(g,now,i);        if(ans[g])ans[g]+=now-last+1;        if(now==n) return;        ++now; g=query(i,now);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1,x; i<=n; i++)    {        read(a[i]),change(i,a[i]);        lg[i]=(double)log(i)/log(2);    }    read(m);    for(int i=1; i<=m; i++)        read(q[i]),ans[q[i]]=1; // 临时用作标记,以节约空间    for(int i=1; i<=n; i++) solve(i);    for(int i=1; i<=m; i++)        write(ans[q[i]]-1),pc('\n'); // 去掉刚刚的标记    return 0;}
        ]]> + CF475D CGCDSSQ 题解

        题目链接:CF475DCGCDSSQ

        题意

        给出一个长度为 \(n\) 的序列和 \(q\) 个询问,

        每个询问输出一行,询问满足 \(\gcd\{a_l,a_{l+1},\dots,a_r\}=x\) 的 \([l,r]\) 的对数

        $ 1 n ^5,~1 q ^5,~1 a_i,x_i ^9$

        这题有一个很有趣的结论

        区间 \(\gcd\) 最多有 \(O(n\log \max\{a_i\})\) 种可能的值

        证明:

        原命题等价于: \(l\) 固定时,\([l,r]\) 的区间 \(\gcd\) 至多有 \(O(\log \max\{a_i\})\) 种取值。

        假设区间 \(\gcd\)\(i\) 处发生了变化,此时新的 \(\gcd\) 一定是原 \(\gcd\) 的因数

        因此新的 \(\gcd\)至少缩小了一半,则至多有 \(O(\log\max\{a_i\})\) 种取值。

        这样我们就可以预处理所有答案,用个unordered_map记录了

        注:这里其实可以优化一下,不用记录所有答案,只记录询问的答案

        那么答案怎么统计呢?

        区间 \(\gcd\)直接用st表维护,应该很容易想到。

        \(l\) 固定时,至多有 \(O(\log \max\{a_i\})\) 种取值

        而这个值一定随着右端点的增大单调不升

        考虑二分右端点,然后记录下答案啥的

        时间复杂度 \(O(n\log n \log \max\{a_i\})\)

        注意st表一定要预处理查询时候的lg[]数组,因为log()常数比较大

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <unordered_map>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)#define M (int)(3e5+15)int n,m,a[N],q[M],lg[N],f[N][20];unordered_map<int,int>ans;int gcd(int a,int b){return b==0?a:gcd(b,a%b);}void change(int u,int x){f[u][0]=x;for(int i=1; u-(1<<i)>=0; i++)f[u][i]=gcd(f[u][i-1],f[u-(1<<(i-1))][i-1]);}int query(int l,int r){int k=lg[r-l+1];return gcd(f[l+(1<<k)-1][k],f[r][k]);}int find(int g,int l,int i){    int r=n;    while(l<r)    {        int mid=(l+r+1)>>1;        if(query(i,mid)==g)l=mid;        else r=mid-1;    }    return l;}void solve(int i){    int last,now=i,g=a[i];    while(1)    {        last=now; now=find(g,now,i);        if(ans[g])ans[g]+=now-last+1;        if(now==n) return;        ++now; g=query(i,now);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1,x; i<=n; i++)    {        read(a[i]),change(i,a[i]);        lg[i]=(double)log(i)/log(2);    }    read(m);    for(int i=1; i<=m; i++)        read(q[i]),ans[q[i]]=1; // 临时用作标记,以节约空间    for(int i=1; i<=n; i++) solve(i);    for(int i=1; i<=m; i++)        write(ans[q[i]]-1),pc('\n'); // 去掉刚刚的标记    return 0;}
        ]]> @@ -2406,7 +2406,7 @@ /2022/07/21/luo-gu-p6225-ejoi2019-yi-huo-cheng-zi-ti-jie/ - 洛谷P6225 [eJOI2019] 异或橙子 题解

        题目链接:P6225 [eJOI2019] 异或橙子

        题意

        序列上有n个值,第i个值为Ai一段区间[l,r]的异或和为 A(l)^..^A(r)一段区间[l,r]的答案是把所有区间[i,j]的异或和结果异或起来(l<=i<=j<=r)例如区间[2,4]的答案为 A2^A3^A4^(A2^A3)^(A3^A4)^(A2^A3^A4)现在给定m次操作:第一种操作输入i,j,将Ai值修改为j 第二种操作输入i,j,求区间[i,j]的答案输入第一行为n,m第二行n个值,为A1,...An之后m行,每一行输入操作的类型和对应的i,j对于每个第二类操作,输出区间答案n,m<=2e5

        其实就是单点修改+查询下面这个柿子

        考虑区间中一个数的出现次数

        设 $l \le i \le r$ ,则 $i$ 在区间 $[l,r]$ 的异或和的异或和中出现的次数为

        推导过程:

        包含 $i$ 的区间 $[l^{\prime},r^{\prime}]$一定满足 $l^{\prime} \le i \le r^{\prime}$

        显然 $l^{\prime}$ 有 $i-l+1$ 种取值,$r^{\prime}$ 有 $r-i+1$ 种取值,易得上式。

        根据异或的自反性,则 $i$ 的贡献取决于它出现次数的奇偶性

        显然当 $(i-l+1)$ 和 $(r-i+1)$ 都为奇数时 $i$ 的出现次数为奇数

        不难发现,此时 $i,l,r$ 同奇偶

        故实际的贡献形如10101

        考虑用两个树状数组,分别维护在奇、偶位置上的数的异或和

        时间复杂度 $O(m \log n)$

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,Q,a[N];int lowbit(int x){return x&(-x);}struct BIT{    int tree[N];    void add(int x,int v)    {        for(int i=x; i<=n; i+=lowbit(i))            tree[i]^=v;    }    int sum(int x)    {        int res=0;        for(int i=x; i; i-=lowbit(i))            res^=tree[i];        return res;    }}tr[2];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> Q;    for(int i=1; i<=n; i++)        cin >> a[i],tr[i&1].add(i,a[i]);    for(int op,x,y; Q--;)    {        cin >> op >> x >> y;        if(op==1) tr[x&1].add(x,a[x]^y),a[x]=y;        else        {            if((x+y)&1) cout << "0\n";            else cout << (tr[x&1].sum(y)^tr[x&1].sum(x-1)) << '\n';        }    }    return 0;}
        ]]> + 洛谷P6225 [eJOI2019] 异或橙子题解

        题目链接:P6225[eJOI2019] 异或橙子

        题意

        序列上有n个值,第i个值为Ai一段区间[l,r]的异或和为 A(l)^..^A(r)一段区间[l,r]的答案是把所有区间[i,j]的异或和结果异或起来(l<=i<=j<=r)例如区间[2,4]的答案为 A2^A3^A4^(A2^A3)^(A3^A4)^(A2^A3^A4)现在给定m次操作:第一种操作输入i,j,将Ai值修改为j 第二种操作输入i,j,求区间[i,j]的答案输入第一行为n,m第二行n个值,为A1,...An之后m行,每一行输入操作的类型和对应的i,j对于每个第二类操作,输出区间答案n,m<=2e5

        其实就是单点修改+查询下面这个柿子 \[\bigoplus_{i=l}^{r}\bigoplus_{j=i}^{r}a_j\] 考虑区间中一个数的出现次数

        \(l \le i \le r\) ,则 \(i\) 在区间 \([l,r]\) 的异或和的异或和中出现的次数为\[(i-l+1) \times (r-i+1)\] 推导过程:

        包含 \(i\) 的区间 \([l^{\prime},r^{\prime}]\)一定满足 \(l^{\prime} \le i \le r^{\prime}\)

        显然 \(l^{\prime}\)\(i-l+1\) 种取值,\(r^{\prime}\) 有 \(r-i+1\) 种取值,易得上式。

        根据异或的自反性,则 \(i\)的贡献取决于它出现次数的奇偶性

        显然当 \((i-l+1)\)\((r-i+1)\) 都为奇数时 \(i\) 的出现次数为奇数

        不难发现,此时 \(i,l,r\) 同奇偶

        故实际的贡献形如10101

        考虑用两个树状数组,分别维护在奇、偶位置上的数的异或和

        时间复杂度 \(O(m \log n)\)

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,Q,a[N];int lowbit(int x){return x&(-x);}struct BIT{    int tree[N];    void add(int x,int v)    {        for(int i=x; i<=n; i+=lowbit(i))            tree[i]^=v;    }    int sum(int x)    {        int res=0;        for(int i=x; i; i-=lowbit(i))            res^=tree[i];        return res;    }}tr[2];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> Q;    for(int i=1; i<=n; i++)        cin >> a[i],tr[i&1].add(i,a[i]);    for(int op,x,y; Q--;)    {        cin >> op >> x >> y;        if(op==1) tr[x&1].add(x,a[x]^y),a[x]=y;        else        {            if((x+y)&1) cout << "0\n";            else cout << (tr[x&1].sum(y)^tr[x&1].sum(x-1)) << '\n';        }    }    return 0;}
        ]]> @@ -2433,7 +2433,7 @@ /2022/07/20/cf877e-danil-and-a-part-time-job-ti-jie/ - CF877E Danil and a Part-time Job 题解

        题目链接:CF877E Danil and a Part-time Job

        题意

        一棵树有n个点,根结点编号为1,每个点的权值都是1或0m次操作:操作1(get):询问一个点x的子树里有多少个1操作2(pow):将一个点x的子树中所有节点的权值取反对于每一个操作1,输出答案输入格式:第一行一个整数n第二行共n−1个整数,第i个数xi表示xi是i+1的父亲,第三行给出n个点的初始权值第四行一个整数m接下来m行输入操作的类型和对应的节点遍号对于每个操作1,输出1的个数n,m<=2e5

        询问&修改子树,显然需要用dfs序,然后用线段树维护

        取反操作其实就是把区间和变成区间长度减区间和

        而两次取反操作等于啥也没干

        因此下传懒标记的时候,直接加个 $1$ 就好了

        时间复杂度 $O(m \log n)$

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)char op[15];int n,Q,pos=1,dfncnt;int a[N],sum[N<<2],tag[N<<2];int id[N],head[N],t[N],sz[N],val[N];struct Edge{int u,v,next;}e[N];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}#define ls(x) ((x)<<1)#define rs(x) ((x)<<1|1)void push_up(int at){    sum[at]=sum[ls(at)]+sum[rs(at)];}void build(int l,int r,int at){    if(l==r){sum[at]=a[l];return;}    int mid=(l+r)>>1;    build(l,mid,ls(at)); build(mid+1,r,rs(at));    push_up(at);}void proc(int l,int r,int k,int at){    sum[at]=(r-l+1)-sum[at];    tag[at]+=k;tag[at]&=1;}void push_down(int l,int r,int at){    if(tag[at]&1)    {        int mid=(l+r)>>1;        proc(l,mid,tag[at],ls(at));        proc(mid+1,r,tag[at],rs(at));        tag[at]=0;    }}void update(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)    {        sum[at]=(r-l+1)-sum[at];        ++tag[at];tag[at]&=1;        return;    }    push_down(l,r,at);    int mid=(l+r)>>1;    if(nl<=mid) update(nl,nr,l,mid,ls(at));    if(nr>mid) update(nl,nr,mid+1,r,rs(at));    push_up(at);}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        return sum[at];    int mid=(l+r)>>1;    int res=0;    push_down(l,r,at);    if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));    if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));    return res;}void dfs(int u){    id[u]=++dfncnt; sz[u]=1; a[id[u]]=val[u];    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v; dfs(v);        sz[u]+=sz[v];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=2,f; i<=n; i++)        cin >> f,addEdge(f,i);    for(int i=1; i<=n; i++) cin >> val[i];    dfs(1); build(1,n,1);    cin >> Q;    for(int x; Q--;)    {        cin >> op >> x;        if(op[0]=='p')update(id[x],id[x]+sz[x]-1,1,n,1);        else cout << query(id[x],id[x]+sz[x]-1,1,n,1) << '\n';    }    return 0;}
        ]]> + CF877E Danil and aPart-time Job 题解

        题目链接:CF877EDanil and a Part-time Job

        题意

        一棵树有n个点,根结点编号为1,每个点的权值都是1或0m次操作:操作1(get):询问一个点x的子树里有多少个1操作2(pow):将一个点x的子树中所有节点的权值取反对于每一个操作1,输出答案输入格式:第一行一个整数n第二行共n−1个整数,第i个数xi表示xi是i+1的父亲,第三行给出n个点的初始权值第四行一个整数m接下来m行输入操作的类型和对应的节点遍号对于每个操作1,输出1的个数n,m<=2e5

        询问&修改子树,显然需要用dfs序,然后用线段树维护

        取反操作其实就是把区间和变成区间长度减区间和

        而两次取反操作等于啥也没干

        因此下传懒标记的时候,直接加个 \(1\)就好了

        时间复杂度 \(O(m \log n)\)

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)char op[15];int n,Q,pos=1,dfncnt;int a[N],sum[N<<2],tag[N<<2];int id[N],head[N],t[N],sz[N],val[N];struct Edge{int u,v,next;}e[N];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}#define ls(x) ((x)<<1)#define rs(x) ((x)<<1|1)void push_up(int at){    sum[at]=sum[ls(at)]+sum[rs(at)];}void build(int l,int r,int at){    if(l==r){sum[at]=a[l];return;}    int mid=(l+r)>>1;    build(l,mid,ls(at)); build(mid+1,r,rs(at));    push_up(at);}void proc(int l,int r,int k,int at){    sum[at]=(r-l+1)-sum[at];    tag[at]+=k;tag[at]&=1;}void push_down(int l,int r,int at){    if(tag[at]&1)    {        int mid=(l+r)>>1;        proc(l,mid,tag[at],ls(at));        proc(mid+1,r,tag[at],rs(at));        tag[at]=0;    }}void update(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)    {        sum[at]=(r-l+1)-sum[at];        ++tag[at];tag[at]&=1;        return;    }    push_down(l,r,at);    int mid=(l+r)>>1;    if(nl<=mid) update(nl,nr,l,mid,ls(at));    if(nr>mid) update(nl,nr,mid+1,r,rs(at));    push_up(at);}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        return sum[at];    int mid=(l+r)>>1;    int res=0;    push_down(l,r,at);    if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));    if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));    return res;}void dfs(int u){    id[u]=++dfncnt; sz[u]=1; a[id[u]]=val[u];    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v; dfs(v);        sz[u]+=sz[v];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=2,f; i<=n; i++)        cin >> f,addEdge(f,i);    for(int i=1; i<=n; i++) cin >> val[i];    dfs(1); build(1,n,1);    cin >> Q;    for(int x; Q--;)    {        cin >> op >> x;        if(op[0]=='p')update(id[x],id[x]+sz[x]-1,1,n,1);        else cout << query(id[x],id[x]+sz[x]-1,1,n,1) << '\n';    }    return 0;}
        ]]> @@ -2462,7 +2462,7 @@ /2022/07/20/shi-jian-chuo-you-hua-shu-zhuang-shu-zu-de-pin-fan-qing-kong/ - 时间戳优化树状数组的频繁清空

        来自@zx2017老师的ppt Orz

        适用场景:

        给定 $T$ 组数据,每组数据有 $Q$ 个询问,询问给定 $n$ 个数的区间和

        数据范围:$T\le 2\times 10^5,~\sum Q \le 2\times 10^5,~n\le 5\times 10^5$

        注意,这里只限制了 $\sum Q$ ,并没有限制 $\sum n$

        对于每组数据不能直接去清空数组,因为 $T \times n$ 肯定爆炸

        考虑维护一个时间戳now,也就是现在是第几组数据

        对于是这组数据的,也就是t[i]==now的情况,直接用

        如果不是这组数据的,我们要先清零,再加。

        注意到 tree[i]=0,tree[i]+=v 其实等价于 tree[i]=v

        由于我们只有查询到一个树上结点,才会去判断它的时间戳

        因此这类题目的时间复杂度可以控制在 $O(\sum Q \log \sum Q)$

        修改部分的代码:

        int tree[N],t[N],now=0;void add(int x,int v){    for(int i=x; i<=n; i+=lowbit(i))        if(t[i]==now)tree[i]+=v;        else t[i]=now,tree[i]=v; // tree[i] = 0 + v}int sum(int x){    int res=0;    for(int i=x; i; i-=lowbit(i))        if(t[i]==now) res+=tree[i];        else t[i]=now,tree[i]=0; // res+=0   return res;}

        然后我感觉这个东西是可以搞到线段树上的

        具体的,维护每个树上结点的时间戳 t[N<<2]

        这样可能可以维护更多的区间性质

        问了一下老师,确实可以这么去搞

        代码还没写,目前没找到类似思想的题目,但是这个技巧挺有趣的。

        upd.20220722 P1120 小木棍 ,清空临时标记数组vis,类似于这个优化

        这么一说,好像匈牙利算法也是可以这么优化的来着。。。

        ]]> + 时间戳优化树状数组的频繁清空

        来自@zx2017老师的ppt Orz

        适用场景:

        给定 \(T\) 组数据,每组数据有 \(Q\) 个询问,询问给定 \(n\) 个数的区间和

        数据范围\(T\le 2\times10^5,~\sum Q \le 2\times 10^5,~n\le 5\times 10^5\)

        注意,这里只限制了 \(\sum Q\),并没有限制 \(\sum n\)

        对于每组数据不能直接去清空数组,因为 \(T\times n\) 肯定爆炸

        考虑维护一个时间戳now,也就是现在是第几组数据

        对于是这组数据的,也就是t[i]==now的情况,直接用

        如果不是这组数据的,我们要先清零,再加。

        注意到 tree[i]=0,tree[i]+=v 其实等价于tree[i]=v

        由于我们只有查询到一个树上结点,才会去判断它的时间戳

        因此这类题目的时间复杂度可以控制在 \(O(\sumQ \log \sum Q)\)

        修改部分的代码:

        int tree[N],t[N],now=0;void add(int x,int v){    for(int i=x; i<=n; i+=lowbit(i))        if(t[i]==now)tree[i]+=v;        else t[i]=now,tree[i]=v; // tree[i] = 0 + v}int sum(int x){    int res=0;    for(int i=x; i; i-=lowbit(i))        if(t[i]==now) res+=tree[i];        else t[i]=now,tree[i]=0; // res+=0   return res;}

        然后我感觉这个东西是可以搞到线段树上的

        具体的,维护每个树上结点的时间戳 t[N<<2]

        这样可能可以维护更多的区间性质

        问了一下老师,确实可以这么去搞

        代码还没写,目前没找到类似思想的题目,但是这个技巧挺有趣的。

        upd.20220722 P1120 小木棍,清空临时标记数组vis,类似于这个优化

        这么一说,好像匈牙利算法也是可以这么优化的来着。。。

        ]]> @@ -2487,7 +2487,7 @@ /2022/07/20/luo-gu-p5142-qu-jian-fang-chai-ti-jie/ - 洛谷P5142 区间方差 题解

        题目链接:P5142 区间方差

        题意

        对于一个长度为 $n$ 的序列 $a_1,a_2,a_3\cdots a_n$,我们定义它的平均数 $a$ 为:

        并定义它的方差 $d$ 为:

        现在给定一个长度为 $n$ 的序列 $a_1,a_2\cdots a_n$。你需要支持两种操作。每种操作的格式为 c x y

        若 $c=1$,为修改操作,代表将 $a_x$ 赋值为 $y$。

        若 $c=2$,为查询操作,代表查询 $a_x$ 到 $a_y$ 的方差。

        为了避免浮点数误差,请以分数取模形式输出结果(对 1000000007($10^9+7$)取模)。

        对于 $100\%$ 的数据,$1\leq n,m\leq 1\times 10^5$,$1\leq a_i\leq 1\times 10^9$,$1\leq x\leq n$。对于操作 1,$1\leq y\leq 1\times 10^9$。对于操作2,$x\leq y\leq n$。

        方差有个更快的公式

        推导过程很简单,只要把那个 $(a-a_i)^2$ 展开就好了

        这样我们就只要维护两个数组

        单点修改都不需要懒标记,很水吧

        不过这个取模除法,所以要算个逆元

        因为 $10^9+7$ 是个质数,而且 $a_i \le 10^9$

        所以直接用费马小定理那个东西求个逆元就好了

        时间复杂度 $O(m \log (na_i))$

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)const int p=1e9+7;int n,Q,a[N],sum1[N<<2],sum2[N<<2];#define ls(x) ((x)<<1)#define rs(x) ((x)<<1|1)int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1) ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}int inv(int x){return qpow(x,p-2);}void push_up(int at){    sum1[at]=(sum1[ls(at)]+sum1[rs(at)])%p;    sum2[at]=(sum2[ls(at)]+sum2[rs(at)])%p;}void build(int l,int r,int at){    if(l==r)    {        sum1[at]=a[l]%p;        sum2[at]=a[l]%p*a[l]%p;        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void modify(int x,int l,int r,int k,int at){    if(l==r)    {        sum1[at]=k%p;        sum2[at]=k%p*k%p;        return;    }    int mid=(l+r)>>1;    if(x<=mid)modify(x,l,mid,k,ls(at));    else modify(x,mid+1,r,k,rs(at));    push_up(at);}int query1(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr) return sum1[at]%p;    int mid=(l+r)>>1;    int res=0;    if(nl<=mid) res=(res+query1(nl,nr,l,mid,ls(at)))%p;    if(nr>mid) res=(res+query1(nl,nr,mid+1,r,rs(at)))%p;    return res;}int query2(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr) return sum2[at]%p;    int mid=(l+r)>>1;    int res=0;    if(nl<=mid) res=(res+query2(nl,nr,l,mid,ls(at)))%p;    if(nr>mid) res=(res+query2(nl,nr,mid+1,r,rs(at)))%p;    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    for(int op,x,y; Q--; )    {        read(op); read(x); read(y);        if(op==1) modify(x,1,n,y,1);        else        {            int t1=query2(x,y,1,n,1);            int t2=query1(x,y,1,n,1);            int t3=inv(y-x+1);            t1=t1%p*t3%p;            t2=t2%p*t3%p;t2=t2%p*t2%p;            write(((t1-t2)%p+p)%p);pc('\n');        }    }    return 0;}
        ]]> + 洛谷P5142 区间方差 题解

        题目链接:P5142区间方差

        题意

        对于一个长度为 \(n\) 的序列 \(a_1,a_2,a_3\cdots a_n\),我们定义它的平均数\(a\) 为:

        \[a=\frac{1}{n}\sum_{i=1}^{n}a_i\]

        并定义它的方差 \(d\) 为:

        \[d=\frac{1}{n}\sum_{i=1}^{n}(a_i-a)^2\] 现在给定一个长度为 \(n\)的序列 \(a_1,a_2\cdotsa_n\)。你需要支持两种操作。每种操作的格式为c x y

        \(c=1\),为修改操作,代表将 \(a_x\) 赋值为 \(y\)。

        \(c=2\),为查询操作,代表查询\(a_x\)\(a_y\) 的方差。

        为了避免浮点数误差,请以分数取模形式输出结果(对 1000000007(\(10^9+7\))取模)。

        对于 \(100\%\) 的数据,\(1\leq n,m\leq 1\times 10^5\),\(1\leq a_i\leq 1\times 10^9\),\(1\leq x\leq n\)。对于操作 1,\(1\leq y\leq 1\times10^9\)。对于操作2,\(x\leq y\leqn\)

        方差有个更快的公式 \[\dfrac{\sum_{i=1}^{n} a_i^2}{n} - a^2\] 推导过程很简单,只要把那个 \((a-a_i)^2\) 展开就好了

        这样我们就只要维护两个数组 \[\begin{aligned}S_1 &= \sum_{i=l}^{r}a_i\\S_2&= \sum_{i=l}^{r}a_i^2\end{aligned}\] 单点修改都不需要懒标记,很水吧

        不过这个取模除法,所以要算个逆元

        因为 \(10^9+7\) 是个质数,而且 \(a_i \le 10^9\)

        所以直接用费马小定理那个东西求个逆元就好了

        时间复杂度 \(O(m \log (na_i))\)

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)const int p=1e9+7;int n,Q,a[N],sum1[N<<2],sum2[N<<2];#define ls(x) ((x)<<1)#define rs(x) ((x)<<1|1)int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1) ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}int inv(int x){return qpow(x,p-2);}void push_up(int at){    sum1[at]=(sum1[ls(at)]+sum1[rs(at)])%p;    sum2[at]=(sum2[ls(at)]+sum2[rs(at)])%p;}void build(int l,int r,int at){    if(l==r)    {        sum1[at]=a[l]%p;        sum2[at]=a[l]%p*a[l]%p;        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void modify(int x,int l,int r,int k,int at){    if(l==r)    {        sum1[at]=k%p;        sum2[at]=k%p*k%p;        return;    }    int mid=(l+r)>>1;    if(x<=mid)modify(x,l,mid,k,ls(at));    else modify(x,mid+1,r,k,rs(at));    push_up(at);}int query1(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr) return sum1[at]%p;    int mid=(l+r)>>1;    int res=0;    if(nl<=mid) res=(res+query1(nl,nr,l,mid,ls(at)))%p;    if(nr>mid) res=(res+query1(nl,nr,mid+1,r,rs(at)))%p;    return res;}int query2(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr) return sum2[at]%p;    int mid=(l+r)>>1;    int res=0;    if(nl<=mid) res=(res+query2(nl,nr,l,mid,ls(at)))%p;    if(nr>mid) res=(res+query2(nl,nr,mid+1,r,rs(at)))%p;    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    for(int op,x,y; Q--; )    {        read(op); read(x); read(y);        if(op==1) modify(x,1,n,y,1);        else        {            int t1=query2(x,y,1,n,1);            int t2=query1(x,y,1,n,1);            int t3=inv(y-x+1);            t1=t1%p*t3%p;            t2=t2%p*t3%p;t2=t2%p*t2%p;            write(((t1-t2)%p+p)%p);pc('\n');        }    }    return 0;}
        ]]> @@ -2516,7 +2516,7 @@ /2022/07/19/oi-mo-ban/ - OI模板

        由于文件比较多,分为了多个部分。

        Parts包含内容
        OI模板-图论最短路算法、最小生成树、线段树优化建图、判负环、kosaraju算法、Tarjan算法 [连通性问题]、欧拉路径、欧拉回路、2-SAT、网络流算法、最小树形图、二分图、无向图的最小环问题
        OI模板-字符串字符串哈希、KMP、ExKMP、Manacher、Trie树、AC自动机、后缀数组、后缀自动机 SAM、广义后缀自动机 ExSAM、回文自动机 PAM、子序列自动机
        OI模板-算法排序算法、CDQ分治、LCA、高精度加减乘除、高精度封装版
        OI模板-其他光速幂、$O(1)$ 快速乘、二维数点、乘法取模封装
        OI模板-计算几何二维凸包、平面最近点对、半平面交、旋转卡壳、扫描线、随机增量法
        OI模板-数据结构并查集、单调队列、二叉堆、左偏树(可并堆)、珂朵莉树、平衡树、可持久化平衡树、ST表、树链剖分、树套树、线段树、李超线段树、线段树分裂、静态仙人掌、可持久化数组、可持久化线段树(主席树)
        OI模板-数学目前不全、快速幂、矩阵快速幂、判断素数、线性筛、二次剩余、康托展开、逆康托展开
        ]]> + OI模板

        由于文件比较多,分为了多个部分。

        Parts包含内容
        OI模板-图论最短路算法、最小生成树、线段树优化建图、判负环、kosaraju算法、Tarjan算法[连通性问题]、欧拉路径、欧拉回路、2-SAT、网络流算法、最小树形图、二分图、无向图的最小环问题
        OI模板-字符串字符串哈希、KMP、ExKMP、Manacher、Trie树、AC自动机、后缀数组、后缀自动机SAM、广义后缀自动机 ExSAM、回文自动机 PAM、子序列自动机
        OI模板-算法排序算法、CDQ分治、LCA、高精度加减乘除、高精度封装版
        OI模板-其他光速幂、\(O(1)\)快速乘、二维数点、乘法取模封装
        OI模板-计算几何二维凸包、平面最近点对、半平面交、旋转卡壳、扫描线、随机增量法
        OI模板-数据结构并查集、单调队列、二叉堆、左偏树(可并堆)、珂朵莉树、平衡树、可持久化平衡树、ST表、树链剖分、树套树、线段树、李超线段树、线段树分裂、静态仙人掌、可持久化数组、可持久化线段树(主席树)
        OI模板-数学目前不全、快速幂、矩阵快速幂、判断素数、线性筛、二次剩余、康托展开、逆康托展开
        ]]> @@ -2541,7 +2541,7 @@ /2022/07/19/oi-mo-ban-tu-lun/ - OI模板-图论

        最短路算法

        dijkstra

        P4779 【模板】单源最短路径(标准版)

        优先队列优化 $O((n+m)\log m)$

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)#define M (int)(2e5+15)int n,m,pos=1,d[N],vis[N],head[N];struct Edge{int u,v,w,next;} e[M];struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}priority_queue<node> q;void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dijkstra(int st){    for(int i=1; i<=n; i++) vis[i]=0,d[i]=INF;    d[st]=0; q.push({st,0});    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u]) continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int st; cin >> n >> m >> st;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);    }    dijkstra(st);    for(int i=1; i<=n; i++)        cout << (d[i]==INF?-1:d[i]) << " \n"[i==n];    return 0;}

        SPFA

        时间复杂度 $O(nm)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;int d[N],vis[N];struct Edge{    int u,v,w,next;}e[N];int pos=1,head[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}queue<int> q;void spfa(){    memset(d,0x3f,(n+1)*sizeof(int));    memset(vis,0,(n+1)*sizeof(int));    d[1]=0;vis[1]=1;    q.push(1);    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[v]>d[u]+e[i].w)            {                d[v]=d[u]+e[i].w;                if(!vis[v])                    q.push(v),vis[v]=1;            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin >> n >> m;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);    }    spfa();    for(int i=1; i<=n; i++)        cout << (d[i]==INF?-1:d[i]) << " \n"[i==n];    return 0;}

        分层图最短路

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+5)#define M (int)(5e4+5)#define K (int)(11)struct Edge{    int u,v,w,next;}e[M*(K+1)*4];int n,m,k,s,t,pos=1,head[N*(K+1)],d[N*(K+1)],vis[N*(K+1)];struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}priority_queue<node> q;void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dijkstra(int st){    memset(d,0x3f,sizeof(d));    q.push({st,0}); d[st]=0;    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> k >> s >> t; ++s; ++t;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w; ++u; ++v;        addEdge(u,v,w);addEdge(v,u,w);        for(int j=1; j<=k; j++)        {            addEdge(j*n+u,j*n+v,w);            addEdge(j*n+v,j*n+u,w);            addEdge((j-1)*n+u,j*n+v,0);            addEdge((j-1)*n+v,j*n+u,0);        }    }    for(int i=1; i<=k; i++)        addEdge((i-1)*n+t,i*n+t,0);    dijkstra(s);    cout << d[k*n+t] << '\n';    return 0;}

        最小生成树

        都是无向图!!!!有向图的叫最小树形图!!!

        P3366 【模板】最小生成树

        kruskal

        时间复杂度 $O(m\log m)$

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e3+15)#define M (int)(2e5+15)int n,m,pos,res,head[N],f[N];struct Edge{int u,v,w;}e[M];bool operator<(Edge a,Edge b){return a.w<b.w;}void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}int kruskal(){    sort(e+1,e+1+m); int res=0,cnt=0;    for(int i=1; i<=m&&cnt<n-1; i++)        if(find(e[i].u)!=find(e[i].v))        {            merge(e[i].u,e[i].v);            res+=e[i].w; ++cnt;        }    return (cnt!=n-1)?-1:res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m; init(n);    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        e[i]={u,v,w};    }    int ans=kruskal();    if(ans==-1)cout << "orz";    else cout << ans;    return 0;}

        Boruvka

        时间复杂度 $O(m\log n)$

        空间复杂度 $O(m)$

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e3+15)#define M (int)(2e5+15)int n,m,head[N],f[N],best[M],vis[M];struct Edge{int u,v,w;}e[M];void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}bool cmp(int a,int b){    if(!b) return 1;    if(e[a].w==e[b].w) return a<b;    return e[a].w<e[b].w;}int boruvka(){    int res=0,cnt=0,ok=1;    while(ok)    {        for(int i=0; i<=m; i++) best[i]=0;        for(int i=1,u,v; i<=m; i++)        {            if(vis[i]) continue;            u=find(e[i].u),v=find(e[i].v);            if(u==v) continue;            if(cmp(i,best[u])) best[u]=i;            if(cmp(i,best[v])) best[v]=i;        }        ok=0;        for(int i=1; i<=n&&cnt<n-1; i++)            if(best[i]&&!vis[best[i]])            {                vis[best[i]]=1; ++cnt; ok=1;                res+=e[best[i]].w;                merge(e[best[i]].u,e[best[i]].v);            }    }    return (cnt!=n-1)?-1:res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m; init(n);    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        e[i]={u,v,w};    }    int ans=boruvka();    if(ans==-1) cout << "orz";    else cout << ans << '\n';    return 0;}

        Prim

        优先队列优化的时间复杂度 $O(m\log n)$

        斐波那契堆优化的时间复杂度 $O(n\log n)$

        下面的代码是无优化邻接矩阵版的,$O(n^2)$ ,仅适用于稠密图 $n\le 2000$ ,不然会挂(MLE或TLE)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;int ans,d[N],vis[N],g[N][N];void prim(){memset(d,0x3f,(n+1)*sizeof(int));for(int i=1; i<n; i++){int u=0;for(int j=1; j<=n; j++)if(!vis[j]&&(!u||d[j]<d[u])) u=j;vis[u]=1;for(int j=1; j<=n; j++)if(!vis[j])d[j]=min(d[j],g[u][j]);}for(int i=2; i<=n; i++)ans+=d[i];}signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> m;for(int i=1; i<=n; i++)for(int j=1; j<=n; j++)if(i!=j)g[i][j]=INF;for(int i=1,u,v,w; i<=m; i++){cin >> u >> v >> w;g[v][u]=g[u][v]=min(g[u][v],w);}prim();cout << ans << endl;return 0;}

        LCT

        比较离谱的解法之一

        大常数

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(4e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,idx,ans,cnt;namespace LCT{    struct node    {        int ch[2],w,id,fa,sz,mx,tag;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void push_up(int x)    {        t[x].id=x;t[x].mx=t[x].w;        if(t[t[x].ch[0]].mx>t[x].mx)            t[x].mx=t[t[x].ch[0]].mx,t[x].id=t[t[x].ch[0]].id;        if(t[t[x].ch[1]].mx>t[x].mx)            t[x].mx=t[t[x].ch[1]].mx,t[x].id=t[t[x].ch[1]].id;    }    void push_down(int x)    {        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag=0;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0; x; y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y,push_up(x);    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void split(int x,int y)    {        make_root(x);        access(y);splay(y);    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    int ck(int x,int y)    {        make_root(x);        return find_root(y)!=x;    }    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])        {            t[x].ch[1]=t[y].fa=0;            push_up(x);        }    }}signed main(){    using namespace LCT;    read(n);read(m);    idx=n;    for(int i=1,x,y,z; i<=m; i++)    {        read(x);read(y);read(z);        t[++idx].w=z;        if(x!=y&&ck(x,y))link(x,idx),link(idx,y),ans+=z,++cnt;        else        {            split(x,y);int now=t[y].id;            if(t[now].mx<=z)continue;            ans-=t[now].mx-z;splay(now);            t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0;            link(x,idx);link(idx,y);        }    }    if(cnt<n-1)puts("orz");    else write(ans),pc('\n');    return 0;}

        线段树优化建图

        板子1

        CF786B Legacy题解

        $n$ 个结点, $q$ 次操作,问操作后从 $s$ 出发的单源最短路

        操作如下

        • 输入 1 u v w 表示 $u$ 到 $v$ 连一条有向边
        • 输入 2 u l r w 表示 $u$ 到 $[l,r]$ 的每个结点连一条有向边
        • 输入 3 u l r w 表示 $[l,r]$ 的每个结点向 $u$ 连一条有向边

        对于 $100\%$ 的数据,$1\le n,q \le 10^5$,$1\le w \le 10^9$

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define DEBUG puts("-------------")namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N ((int)(1e5+15))const int D=5e5+15; // 两棵线段树的分界点int n,m,s,d[N*10+255],a[N*10+255]; // a是结点编号对应的线段树叶子结点的编号bitset<N*10+255> vis;struct node1{int v,w;}; // 边struct show_l_r{int l,r;}tr[N<<2]; // 辅助树vector<node1> g[N*10+255]; // 两棵线段树struct node2{int u,dis;};bool operator<(node2 a,node2 b){return a.dis>b.dis;}#define ls(x) (x<<1)#define rs(x) (x<<1|1)void build(int l,int r,int at){    tr[at].l=l; tr[at].r=r;    if(l==r) return a[l]=at, void(0);    int mid=(l+r)>>1;    g[at].push_back({ls(at),0});    g[at].push_back({rs(at),0});    g[ls(at)+D].push_back({at+D,0});    g[rs(at)+D].push_back({at+D,0});    build(l,mid,ls(at)); build(mid+1,r,rs(at));}void link(int nl,int nr,int u,int w,int opt,int at){    int l=tr[at].l,r=tr[at].r;    if(nl<=l&&r<=nr)    {        if(opt) g[at+D].push_back({u,w});        else g[u+D].push_back({at,w});        return;    }    int mid=(l+r)>>1;    if(nl<=mid) link(nl,nr,u,w,opt,ls(at));    if(nr>mid) link(nl,nr,u,w,opt,rs(at));    }void Dijkstra(int st){    priority_queue<node2> q;    memset(d,0x3f,sizeof(d));    d[st]=0; vis=0; q.push({st,0});    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u]) continue;        vis[u]=1;        for(auto i : g[u])        {            int v=i.v,w=i.w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m); read(s);    build(1,n,1);    for(int i=1,op,u,v,l,r,w; i<=m; i++)    {        read(op);        if(op==1)        {            read(u); read(v); read(w);            g[a[u]].push_back({a[v],w});        }        else        {            read(u); read(l); read(r); read(w);            link(l,r,a[u],w,op&1,1); // op=2: u->[l,r] , op=3:[l,r]->u;        }    }    for(int i=1; i<=n; i++)    {        g[a[i]].push_back({a[i]+D,0}),        g[a[i]+D].push_back({a[i],0});    }    Dijkstra(a[s]+D);    for(int i=1; i<=n; i++)        write(d[a[i]]!=INF ? d[a[i]] : -1),pc(" \n"[i==n]);    return 0;}

        板子2

        P6348 [PA2011]Journeys

        咕咕咕。。。


        判负环

        spfa,这份代码是从结点 $1$ 为起点的

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;int d[N],vis[N],cnt[N];struct Edge{    int v,w,next;};vector<Edge> vec[N];queue<int> q;bool spfa(){    memset(d,0x3f,(n+1)*sizeof(int));    memset(vis,0,(n+1)*sizeof(int));    memset(cnt,0,(n+1)*sizeof(int));    d[1]=0;vis[1]=1;cnt[1]=1;    q.push(1);    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=0; i<vec[u].size(); i++)        {            int v=vec[u][i].v,w=vec[u][i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                if(!vis[v])                {                    if(++cnt[v]>=n)                        return 1;                    q.push(v),vis[v]=1;                }            }        }    }    return 0;}signed main(){    ios::sync_with_stdio(0);    int Q;    cin >> Q;    while(Q--)    {        cin >> n >> m;        for(int i=1,u,v,w; i<=m; i++)        {            cin >> u >> v >> w;            vec[u].push_back({v,w});            if(w>=0)                vec[v].push_back({u,w});        }        cout << (spfa()?"YES":"NO") << endl;        for(int i=1; i<=n; i++)            vec[i].clear();    }        return 0;}

        更为通用的写法如下(有向图)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m,st;int d[N],vis[N],cnt[N];struct Edge{    int v,w,next;};vector<Edge> vec[N];queue<int> q;bool spfa(){    memset(d,0x3f,(n+2)*sizeof(int));    memset(vis,0,(n+2)*sizeof(int));    memset(cnt,0,(n+2)*sizeof(int));    d[st]=0;vis[st]=1;cnt[st]=1;    q.push(st);    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=0; i<vec[u].size(); i++)        {            int v=vec[u][i].v,w=vec[u][i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                if(!vis[v])                {                    if(++cnt[v]>=n) // 不变,只有无向图才变n+1                        return 1; // 指虚拟结点                    q.push(v),vis[v]=1;                }                                }        }    }    return 0;}signed main(){    ios::sync_with_stdio(0);    int Q;    cin >> Q;    while(Q--)    {        cin >> n >> m;        for(int i=1,u,v,w; i<=m; i++)        {            cin >> u >> v >> w;            vec[u].push_back({v,w,(int)vec[v].size()});            if(w>=0)                vec[v].push_back({u,w,(int)vec[u].size()-1});        }        st=0;        for(int i=1; i<=n; i++)            vec[0].push_back({i,0});        cout << (spfa()?"YES":"NO") << endl;        for(int i=0; i<=n; i++)            vec[i].clear();    }    return 0;}

        kosaraju算法

        时间复杂度 $O(n+m)$

        空间复杂度 $O(m)$

        例题:P3387 【模板】缩点

        有向图强连通分量缩点

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)bool vis[N];vector<int> g1[N],g2[N],vec[N];int n,m,cnt,scnt,f[N],sum[N],in[N],val[N],scc[N],dfn[N];void addEdge1(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void addEdge2(int u,int v){    vec[u].push_back(v);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u])        if(!vis[v]) dfs1(v);    dfn[++cnt]=u;}void dfs2(int u,int id){    scc[u]=id; sum[id]+=val[u];    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=n; i++)        if(!vis[i]) dfs1(i);    scnt=0;    for(int i=n; i>=1; i--)        if(!scc[dfn[i]])        {            ++scnt;            dfs2(dfn[i],scnt);        }}int topo(){    queue<int> q;    for(int i=1; i<=scnt; i++)        if(!in[i]) q.push(i),f[i]=sum[i];    while(!q.empty())    {        int u=q.front(); q.pop();        for(int v:vec[u])        {            f[v]=max(f[v],f[u]+sum[v]);            if(!(--in[v])) q.push(v);        }    }    return *max_element(f+1,f+1+scnt);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> val[i];    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge1(u,v);    }    kosaraju();    for(int i=1,u,v; i<=n; i++)        for(int j : g1[i])        {            u=scc[i],v=scc[j];            if(u!=v) addEdge2(u,v),++in[v];        }    cout << topo() << '\n';    return 0;}

        Tarjan算法 [连通性问题]

        无向图

        割边(桥)

        时间复杂度 $O(n)$

        空间复杂度 $O(m)$

        例题:T103481 【模板】割边

        #include <bits/stdc++.h>using namespace std;#define int long long#define M (int)(6e5+25)#define N (int)(5e4+25)struct Edge{    int u,v,next;}e[M];int n,m,ans;int pos=2,head[N],dfn[N],low[N],dfncnt,cut[M];void addEdge(int u,int v){    e[pos]={u,v,head[u]};    head[u]=pos++;}void tarjan(int u,int in_edge){    dfn[u]=low[u]=++dfncnt;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(!dfn[v])        {            tarjan(v,i);            low[u]=min(low[u],low[v]);            if(low[v]>dfn[u])                cut[i]=cut[i^1]=1;        }else if(i!=(in_edge^1))            low[u]=min(low[u],dfn[v]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        if(!dfn[i])tarjan(i,0);    for(int i=2; i<pos; i+=2)        if(cut[i])++ans;    cout << ans << endl;    return 0;}

        割点(割顶)

        区别于桥,这个不用管fa结点,但是注意rt要有两个子结点都是“割点性质“

        时间复杂度 $O(n)$

        空间复杂度 $O(m)$

        例题:P3388 【模板】割点(割顶)

        #include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(2e4+15)#define M (int)(2e5+15)struct node{int u,v,next;}e[M];int n,m,ans;int pos=2,head[N],dfncnt,dfn[N],low[N],cut[N];void addEdge(int u,int v){e[pos]={u,v,head[u]};head[u]=pos++;}void tarjan(int u,int rt){dfn[u]=low[u]=++dfncnt;int cnt=0;for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(!dfn[v]){tarjan(v,rt);low[u]=min(low[u],low[v]);if(low[v]>=dfn[u]){++cnt;if(u!=rt||cnt>1)cut[u]=1;}}low[u]=min(low[u],dfn[v]);}}signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> m;for(int i=1,u,v; i<=m; i++){cin >> u >> v;addEdge(u,v);addEdge(v,u);}for(int i=1; i<=n; i++)if(!dfn[i])tarjan(i,i);for(int i=1; i<=n; i++)if(cut[i])++ans;cout << ans << endl;for(int i=1; i<=n; i++)if(cut[i])cout << i << " \n"[!--ans];return 0;}

        边双连通分量

        时间复杂度 $O(n)$

        空间复杂度 $O(m)$

        upd20220806 有个板子题P8436 【模板】边双连通分量

        例题:P2860 [USACO06JAN]Redundant Paths G

        例题的代码:

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(5e3+15)#define M (int)(2e4+15)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,next;}e[M];int n,m,ans,ecc[N],top;int pos=2,head[N],stk[N];int dfn[N],low[N],dfncnt,ecnt,in[N];void addEdge(int u,int v){    e[pos]={u,v,head[u]};    head[u]=pos++;}void tarjan(int u,int in_edge){    dfn[u]=low[u]=++dfncnt;    stk[++top]=u;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(!dfn[v])        {            tarjan(v,i);            low[u]=min(low[u],low[v]);        }else if(i!=(in_edge^1))            low[u]=min(low[u],dfn[v]);    }    if(low[u]==dfn[u])    {        ecc[u]=++ecnt;        while(stk[top]!=u)            ecc[stk[top--]]=ecnt;        --top;    }}signed main(){    read(n);read(m);    for(int i=1,u,v; i<=m; i++)    {        read(u);read(v);        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        if(!dfn[i])top=0,tarjan(i,0);    for(int i=2; i<pos; i++)        if(ecc[e[i].u]!=ecc[e[i].v])            ++in[ecc[e[i].u]],++in[ecc[e[i].v]];    int ans=0;    for(int i=1; i<=ecnt; i++)        if(in[i]==2)++ans;    // 对于所有双连通分量,其缩点(e-dcc)后    // 连(叶结点数+1)/2(即叶结点数除以2向上取整)条边    // 可以使整个图变成双连通图    write((ans+1)>>1);pc('\n');    return 0;}

        点双连通分量

        时间复杂度 $O(n)$

        空间复杂度 $O(m)$

        例题:T103492 【模板】点双连通分量

        注:下面的第二种写法才可通过例题!

        因为第一种把割点存在了第一个,和例题的标程不一样,然后例题没写special judge.

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(5e4+15)#define M (int)(6e5+15)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,next;}e[M];int n,m,vcnt,cut[N],stk[N],top;int pos=2,head[N],dfn[N],low[N],dfncnt;vector<int> vcc[N];void addEdge(int u,int v){    e[pos]={u,v,head[u]};    head[u]=pos++;}void tarjan(int u,int rt){    dfn[u]=low[u]=++dfncnt;    stk[++top]=u;int cnt=0;    if(u==rt&&!head[u])    {        vcc[++vcnt].push_back(u);        return;    }    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(!dfn[v])        {            tarjan(v,rt);            low[u]=min(low[u],low[v]);            if(low[v]>=dfn[u])            {                ++cnt;                if(u!=rt||cnt>1)cut[u]=1;                // 第一种写法,割点在vcc[vcnt][0]                vcc[++vcnt].push_back(u);                while(stk[top]!=v)                    vcc[vcnt].push_back(stk[top--]);                vcc[vcnt].push_back(stk[top--]);                /* 第二种写法                    int z;++vcnt;                    do {                        z=stk[top--];                        vcc[vcnt].push_back(z);                    } while (z!=v);                    vcc[vcnt].push_back(u);                */            }        }else low[u]=min(low[u],dfn[v]);    }}signed main(){    read(n);read(m);    for(int i=1,u,v; i<=m; i++)    {        read(u);read(v);        if(u==v)continue;        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        if(!dfn[i])top=0,tarjan(i,i);    for(int i=1; i<=vcnt; i++)        for(int j=0; j<vcc[i].size(); j++)            write(vcc[i][j]),pc(" \n"[j+1==vcc[i].size()]);    return 0;}

        有向图

        强连通分量

        时间复杂度 $O(n+m)$

        空间复杂度 $O(m)$

        例题:P2746 [USACO5.3]校园网Network of Schools

        要注意强连通图统计缩点后的出、入度要特判!

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(105)int n,dfn[N],low[N],in[N],top;int out[N],stk[N],instk[N],dfncnt,scnt,scc[N];vector<int> g[N];void tarjan(int u){    dfn[u]=low[u]=++dfncnt;    stk[++top]=u;instk[u]=1;    for(int v:g[u])    {        if(!dfn[v])        {            tarjan(v);            low[u]=min(low[u],low[v]);        }else if(instk[v])            low[u]=min(low[u],dfn[v]);    }    if(low[u]==dfn[u])    {        scc[u]=++scnt;instk[u]=0;        while(stk[top]!=u)        {            scc[stk[top]]=scnt;            instk[stk[top--]]=0;        }        --top;    }}signed main(){    ios::sync_with_stdio(0);    cin >> n;    for(int i=1,v; i<=n; i++)    {        while(cin >> v && v)            g[i].push_back(v);    }    for(int i=1; i<=n; i++)        if(!dfn[i])tarjan(i);    for(int i=1; i<=n; i++)        for(int j:g[i])        {            if(scc[i]!=scc[j])                ++out[scc[i]],++in[scc[j]];        }    int x=0,y=0;    for(int i=1; i<=scnt; i++)    {        if(!in[i])++x;        if(!out[i])++y;    }    cout << x << endl;    if(scnt==1)cout << 0 << endl;    else cout << max(x,y) << endl;    return 0;}

        强连通分量缩点

        时间复杂度 $O(n)$

        空间复杂度 $O(m)$

        例题:P3387 【模板】缩点

        #include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(1e4+15)#define M (int)(1e5+15)struct Edge{    int u,v,next;}e1[M],e2[M];int n,m,val[N],sum[N];int pos1=1,head1[N],pos2=1,head2[N];int dfncnt,dfn[N],low[N],scnt,dp[N];int stk[N],top,instk[N],scc[N],in[N];void addEdge1(int u,int v){    e1[++pos1]={u,v,head1[u]};    head1[u]=pos1;}void addEdge2(int u,int v){    e2[++pos2]={u,v,head2[u]};    head2[u]=pos2;}void tarjan(int u){    dfn[u]=low[u]=++dfncnt;    stk[++top]=u;instk[u]=1;    for(int i=head1[u]; i; i=e1[i].next)    {        int v=e1[i].v;        if(!dfn[v])        {            tarjan(v);            low[u]=min(low[u],low[v]);        }else if(instk[v])low[u]=min(low[u],dfn[v]);            }    if(low[u]==dfn[u])    {        scc[u]=++scnt;instk[u]=0;        sum[scnt]+=val[u];        while(stk[top]!=u)        {            scc[stk[top]]=scnt;            sum[scnt]+=val[stk[top]];            instk[stk[top--]]=0;        }        --top;    }}int topo(){    queue<int> q;    for(int i=1; i<=scnt; i++)        if(!in[i])            q.push(i),dp[i]=sum[i];    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head2[u]; i; i=e2[i].next)        {            int v=e2[i].v;            dp[v]=max(dp[v],dp[u]+sum[v]);            if(!--in[v])q.push(v);        }    }    return *max_element(dp+1,dp+1+scnt);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> val[i];    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge1(u,v);    }    for(int i=1; i<=n; i++)        if(!dfn[i])tarjan(i);    for(int i=2; i<=pos1; i++)    {        int u=scc[e1[i].u],v=scc[e1[i].v];        if(u!=v)        {            addEdge2(u,v);            ++in[v];        }    }    cout << topo() << endl;    return 0;}

        欧拉路径

        P7771 【模板】欧拉路径

        求的是字典序最小的欧拉路径

        /*g++ test_20220124.cpp -o test_20220124./test_20220124*/#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)#define gc() getchar()#define pc(a) putchar(a)template<typename T>void read(T &k){char ch=gc(); T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}int n,m,cnt,s,t;struct node{int v,id;bool operator<(node &o){return v<o.v;}};vector<node> vec[MAXN];int vis[MAXN<<1],in[MAXN],out[MAXN],top,stk[MAXN<<1],del[MAXN]; // del防极限数据 (1->2) * 1e5 && (2->1) * 1e5void dfs(int u){for(int i=del[u]; i<vec[u].size(); i=max(i+1,del[u])/*可能被更新了*/){if(!vis[vec[u][i].id]){vis[vec[u][i].id]=1;del[u]=i+1;dfs(vec[u][i].v);}}stk[++top]=u;}signed main(){read(n);read(m);for(int i=1,u,v; i<=m; i++){read(u);read(v);++out[u];++in[v];vec[u].push_back({v,i});}for(int i=1; i<=n; i++){if(in[i]!=out[i]){++cnt;if(in[i]==out[i]-1)s=i;if(in[i]==out[i]+1)t=i;}}if(cnt!=0&&cnt!=2)return puts("No"),0;if(cnt==0)s=t=1;if(!s||!t)return puts("No"),0;for(int i=1; i<=n; i++)sort(vec[i].begin(),vec[i].end());dfs(s);while(top>0){write(stk[top]);pc(" \n"[top==1]);--top;}return 0;}

        欧拉回路

        hierholzer算法?待补


        2-SAT

        P4782 【模板】2-SAT 问题

        kosaraju实现,注意跑dfs和开空间都要两倍。

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <bitset>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)int n,m,tim,scnt,dfn[N<<1],scc[N<<1];bitset<(N<<1)> vis;vector<int> g1[N<<1],g2[N<<1];void addEdge(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u]) if(!vis[v]) dfs1(v);    dfn[++tim]=u;}void dfs2(int u,int id){    scc[u]=id;    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=(n<<1); i++)        if(!vis[i]) dfs1(i);    scnt=0;    for(int i=(n<<1); i>=1; i--)        if(!scc[dfn[i]]) dfs2(dfn[i],++scnt);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m); vis=0;    for(int i=1,a,b,va,vb; i<=m; i++)    {        read(a); read(va); read(b); read(vb);        addEdge(a+n*(va&1), b+n*(vb^1));        addEdge(b+n*(vb&1), a+n*(va^1));    }    kosaraju();    for(int i=1; i<=n; i++)        if(scc[i]==scc[i+n]) return puts("IMPOSSIBLE"),0;    puts("POSSIBLE");    for(int i=1; i<=n; i++)        write((int)(scc[i]>scc[i+n])),pc(" \n"[i==n]);    return 0;}

        网络流算法

        有向图网络最大流

        这里默认s与t连通,实际上代码里写了判断

        Dinic

        时间复杂度 $O(n^2m)$

        空间复杂度 $O(m)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)#define M (int)(2e5+15)#define gc() getchar()#define pc(a) putchar(a)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,w,next;}e[M];int n,m,s,t,ans;int pos=1,head[N],dep[N],now[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}queue<int> q;bool bfs(){    memset(dep,0x3f,(n+1)*sizeof(int));    q.push(s);dep[s]=1;now[s]=head[s];    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(e[i].w>0&&dep[v]==INF)            {                dep[v]=dep[u]+1;                now[v]=head[v];                q.push(v);            }        }    }    return dep[t]!=INF;}int dfs(int u,int in){    if(u==t)        return in;    int out=0;    for(int i=now[u]; i&&in; i=e[i].next)    {        int v=e[i].v;        if(e[i].w>0&&dep[v]==dep[u]+1)        {            int res=dfs(v,min(in,e[i].w));            e[i].w-=res;            e[i^1].w+=res;            in-=res;            out+=res;        }    }    if(!out)dep[u]=INF;    return out;}void Dinic(){    while(bfs())        ans+=dfs(s,INF);}signed main(){    read(n);read(m);read(s);read(t);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,0);    }    write(ans);pc('\n');    return 0;}

        ISAP

        时间复杂度 $O(n^2m)$

        空间复杂度 $O(m)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)#define M (int)(2e4+15)#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+5)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}struct Edge{    int u,v,w,next;}e[M];int n,m,s,t,ans;int pos=1,head[N],dep[N],now[N],gap[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}queue<int> q;bool bfs(){    memset(dep,0x3f,(n+1)*sizeof(int));    q.push(t);dep[t]=1;gap[1]=1;    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(e[i^1].w>0&&dep[v]==INF)            {                dep[v]=dep[u]+1;                gap[dep[v]]++;                q.push(v);            }        }    }    return dep[s]!=INF;}int dfs(int u,int in){    if(u==t)        return in;    int out=0;    for(int i=now[u]; i; i=e[i].next)    {        int v=e[i].v;now[u]=i;        if(e[i].w>0&&dep[u]==dep[v]+1)        {            int res=dfs(v,min(in,e[i].w));            e[i].w-=res;            e[i^1].w+=res;            in-=res;            out+=res;        }        if(!in)return out;    }    if(!--gap[dep[u]])dep[s]=n+1;    gap[++dep[u]]++;    return out;}void ISAP(){    bfs();    while(dep[s]<=n)    {        memcpy(now,head,(n+1)*sizeof(int));        ans+=dfs(s,INF);    }}signed main(){    read(n);read(m);read(s);read(t);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,0);    }    ISAP();    write(ans);pc('\n');    return 0;}

        HLPP

        n=5e3,m=5e4

        时间复杂度 $O(n^2\sqrt{m}\log n)$

        空间复杂度 $O(m)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f// #define INF 0x3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define N (int)(2e3+15)#define M (int)(3e5+15)#define SIZ (int)(1e5+5)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}struct Edge{int v,w,next;};vector<Edge> vec[N];int n,m,s,t,now;int gap[N],h[N],val[N],vis[N];struct cmp{    bool operator()(int a,int b)        {return h[a]<h[b];}};priority_queue<int,vector<int>,cmp> q;queue<int> b;bool bfs(){    memset(h,0x3f,(n+1)*sizeof(int));    b.push(t);h[t]=0;vis[t]=1;    while(!b.empty())    {        int u=b.front();b.pop();        vis[u]=0;        for(int i=0; i<vec[u].size(); i++)        {            int v=vec[u][i].v,j=vec[u][i].next;            if(vec[v][j].w>0&&h[v]>h[u]+1)            {                h[v]=h[u]+1;                if(!vis[v])                    b.push(v),vis[v]=1;            }        }    }    return h[s]!=INF;}void relabel(int u){    h[u]=INF;    for(int i=0; i<vec[u].size(); i++)    {        int v=vec[u][i].v;        if(vec[u][i].w>0&&h[u]>h[v]+1)            h[u]=h[v]+1;    }}int HLPP(){    if(!bfs())return -1;    h[s]=n;    for(int i=1; i<=n; i++)        if(h[i]!=INF)++gap[h[i]];    for(int i=0; i<vec[s].size(); i++)    {        int v=vec[s][i].v,w=vec[s][i].w,j=vec[s][i].next;        if(w>0)        {            val[s]-=w;            val[v]+=w;            vec[s][i].w-=w;            vec[v][j].w+=w;            if(!vis[v]&&v!=t&&v!=s)                q.push(v),vis[v]=1;        }    }    while(!q.empty())    {        int u=q.top();        q.pop();vis[u]=0;        if(h[u]==INF)continue; // !!!!!!!!!!!        for(int i=0; i<vec[u].size(); i++)        {            int v=vec[u][i].v,j=vec[u][i].next;            if(vec[u][i].w>0&&h[u]==h[v]+1)            {                int w=min(vec[u][i].w,val[u]);                val[u]-=w;                val[v]+=w;                vec[u][i].w-=w;                vec[v][j].w+=w;                if(!vis[v]&&v!=t&&v!=s)                    q.push(v),vis[v]=1;                if(!val[u])break;            }        }        if(!val[u])continue;        if(!--gap[h[u]])        {            for(int i=1; i<=n; i++)                if(i!=s&&i!=t&&h[u]<h[i]&&h[i]<=n)                    h[i]=n+1;        }        relabel(u);++gap[h[u]];        q.push(u);vis[u]=1;    }    return val[t];}signed main(){    read(n);read(m);read(s);read(t);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        vec[u].push_back({v,w,(int)vec[v].size()});        vec[v].push_back({u,0,(int)vec[u].size()-1});    }    write(HLPP());pc('\n');    return 0;}

        有向图最小费用最大流

        时间复杂度 $O(n^2m)$

        空间复杂度 $O(m)$

        1. vector写法,C++14 1.41s(O2 542ms)
        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define N (int)(5e3+10)#define M (int)(1e6+25)#define SIZ (int)(1e5+5)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{int v,w,c,next;};struct node{int u,i,j;}pre[N];vector<Edge> vec[N];int n,m,s,t,flow,cost;int in[N],d[N],vis[N];queue<int> q;bool spfa(){    memset(d,0x3f,(n+1)*sizeof(int));    memset(in,0x3f,(n+1)*sizeof(int));    memset(vis,0,(n+1)*sizeof(int));    q.push(s);vis[s]=1;d[s]=0;pre[t].u=0;    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=0; i<vec[u].size(); i++)        {            int v=vec[u][i].v,w=vec[u][i].w;            int c=vec[u][i].c,j=vec[u][i].next;            if(w>0&&d[v]>d[u]+c)            {                d[v]=d[u]+c;                pre[v]={u,i,j};                in[v]=min(in[u],w);                if(!vis[v])                    q.push(v),vis[v]=1;            }        }    }    return pre[t].u!=0;}void Dinic(){    while(spfa())    {        flow+=in[t];        cost+=in[t]*d[t];        int u,i,j,v=t;        while(v!=s)        {            u=pre[v].u,i=pre[v].i,j=pre[v].j;            vec[u][i].w-=in[t];            vec[v][j].w+=in[t];            v=pre[v].u;        }    }}signed main(){    read(n);read(m);read(s);read(t);    for(int i=1,u,v,w,c; i<=m; i++)    {        read(u);read(v);read(w);read(c);        vec[u].push_back({v,w,c,(int)vec[v].size()});        vec[v].push_back({u,0,-c,(int)vec[u].size()-1});    }    Dinic();    write(flow);pc(' ');write(cost);pc('\n');    return 0;}
        1. 链式前向星写法,C++14 1.95s(O2 1.45s)
        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define N (int)(5e3+10)#define M (int)(1e6+25)#define SIZ (int)(1e5+5)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,w,c,next;}e[M];int n,m,s,t,flow,cost;int pos=1,head[N],in[N],d[N],pre[N],last[N],vis[N];void addEdge(int u,int v,int w,int c){    e[++pos]={u,v,w,c,head[u]};    head[u]=pos;}struct node{    int u,dis;    bool operator<(const node &o)const        {return dis>o.dis;}};queue<int> q;bool spfa(){    memset(d,0x3f,(n+1)*sizeof(int));    memset(in,0x3f,(n+1)*sizeof(int));    memset(vis,0,(n+1)*sizeof(int));    q.push(s);vis[s]=1;d[s]=0;pre[t]=0;    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(e[i].w>0&&d[v]>d[u]+e[i].c)            {                d[v]=d[u]+e[i].c;                pre[v]=u;                last[v]=i;                in[v]=min(in[u],e[i].w);                if(!vis[v])                    q.push(v),vis[v]=1;            }        }    }    return pre[t]!=0;}void Dinic(){    while(spfa())    {        flow+=in[t];        cost+=in[t]*d[t];        int at=t;        while(at!=s)        {            e[last[at]].w-=in[t];            e[last[at]^1].w+=in[t];            at=pre[at];        }    }}signed main(){    read(n);read(m);read(s);read(t);    for(int i=1,u,v,w,c; i<=m; i++)    {        read(u);read(v);read(w);read(c);        addEdge(u,v,w,c);addEdge(v,u,0,-c);    }    Dinic();    write(flow);pc(' ');write(cost);pc('\n');    return 0;}

        有向图有源汇上下界最大流

        例题:P5192 Zoj3229 Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流

        本例题建模:

        1. 先建立一个源点
        2. 从源点到每个少女,流量为 $\left[G_i,+\infty\right)$
        3. 从每个少女到每一天,流量为 $[l_i,r_i]$
        4. 从每一天到汇点,流量为 $[0,D_i]$

        然后跑有源汇上下界最大流就好了

        1. ISAP版 C++14 O2 189ms
        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f// #define gc() readchar()#define gc() getchar()#define pc(a) putchar(a)#define N (int)(5e4+15)#define M (int)(4e5+15)#define SIZ (int)(1e5+15)// char buf1[SIZ],*p1=buf1,*p2=buf1;// char readchar()// {//     if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);//     return p1==p2?EOF:*p1++;// }template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}struct Edge{    int u,v,w,next;}e[M];int n,m,s,t,s0,t0;int pos=1,head[N],dep[N],now[N],gap[N],A[N];void add(int u,int v,int w){    e[++pos]={u,v,w,head[u]};head[u]=pos;    e[++pos]={v,u,0,head[v]};head[v]=pos;}void addEdge(int u,int v,int l,int r){    add(u,v,r-l);    A[u]-=l;A[v]+=l;}queue<int> q;bool bfs(){    memset(dep,0x3f,(n+m+5)*sizeof(int));    memset(gap,0,(n+m+5)*sizeof(int));    q.push(t0);dep[t0]=1;gap[1]=1;    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(e[i^1].w&&dep[v]==INF)            {                dep[v]=dep[u]+1;                gap[dep[v]]++;                q.push(v);            }        }    }    return dep[s0]!=INF;}int dfs(int u,int in){    if(u==t0)return in;    int out=0;    for(int i=now[u]; i&&in; i=e[i].next)    {        int v=e[i].v;now[u]=i;        if(e[i].w&&dep[u]==dep[v]+1)        {            int res=dfs(v,min(in,e[i].w));            e[i].w-=res;            e[i^1].w+=res;            in-=res;            out+=res;        }        if(!in)return out;    }    if(!--gap[dep[u]])dep[s0]=n+m+5;    gap[++dep[u]]++;    return out;}int ISAP(){    if(!bfs())return 0;    int res=0;    while(dep[s0]<=n+m+4)    {        memcpy(now,head,sizeof(head));        res+=dfs(s0,INF);    }    return res;}signed main(){    while(~scanf("%lld%lld",&n,&m))    {        memset(head,0,(n+m+5)*sizeof(int));        memset(A,0,(n+m+5)*sizeof(int));        s=0;t=n+m+1;pos=1;        int tot=0;        for(int i=1; i<=m; i++)        {            int x;read(x);            addEdge(s,i,x,INF);        }        for(int i=1,C,D; i<=n; i++)        {            read(C);read(D);            addEdge(m+i,t,0,D);            for(int j=1,x,L,R; j<=C; j++)            {                read(x);read(L);read(R);                addEdge(x+1,m+i,L,R);            }        }        s0=n+m+2;t0=n+m+3;        for(int i=0; i<=n+m+1; i++)        {            if(A[i]>0)add(s0,i,A[i]),tot+=A[i];            else if(A[i]<0)add(i,t0,-A[i]);        }        add(t,s,INF);        if(ISAP()<tot){puts("-1\n");continue;}        int res=e[pos].w;        e[pos].w=e[pos-1].w=0;        s0=s;t0=t;        write(res+ISAP());puts("\n");    }    return 0;}

        无向图无源汇最大流

        Stoer-Wagner

        斯托瓦格纳?

        1. 朴素写法

          时间复杂度 $O(nm+n^3)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(666)int n,m,d[N][N],s,t,dap[N],vis[N],w[N],ord[N];int proc(int x){    memset(vis,0,(n+1)*sizeof(int));    memset(w,0,(n+1)*sizeof(int));    w[0]=-1;    for(int i=1; i<=n-x+1; i++)    {        int mx=0;        for(int j=1; j<=n; j++)            if(!dap[j]&&!vis[j]&&w[j]>w[mx])mx=j;        vis[mx]=1;ord[i]=mx;        for(int j=1; j<=n; j++)            if(!dap[j]&&!vis[j])w[j]+=d[mx][j];    }    s=ord[n-x];t=ord[n-x+1];    return w[t];}int sw(){    int res=INF;    for(int i=1; i<n; i++)    {        res=min(res,proc(i));        dap[t]=1;        for(int j=1; j<=n; j++)        {            d[s][j]+=d[t][j];            d[j][s]+=d[j][t];        }    }    return res;}signed main(){    read(n);read(m);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        d[u][v]+=w;d[v][u]+=w;    }    write(sw());pc('\n');    return 0;}
        1. 堆优化(意义不大)

          时间复杂度 $O(nm+n^2\log n)$

          还没写


        最小树形图

        最小树形图 朱刘算法

        时间复杂度 $O(nm)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define M (int)(1e4+15)#define N (int)(1e3+5)struct Edge{    int u,v,w,next;}e[M];int n,m,rt;int pre[N],ine[N];int vis[N],id[N];int pos=1,head[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}int solve(){    int ans=0;    while(1)    {        for(int i=1; i<=n; i++)            ine[i]=INF;        for(int i=2; i<=pos; i++)        {            int u=e[i].u,v=e[i].v;            if(u!=v&&e[i].w<ine[v])                ine[v]=e[i].w,pre[v]=u;        }        for(int i=1; i<=n; i++)            if(i!=rt&&ine[i]==INF)return INF;        int cnt=0;        for(int i=1; i<=n; i++)            vis[i]=id[i]=0;        for(int i=1; i<=n; i++)        {            if(i==rt)continue;            ans+=ine[i];            int v=i;            while(vis[v]!=i&&!id[v]&&v!=rt)            {                vis[v]=i;                v=pre[v];            }            if(!id[v]&&v!=rt)            {                id[v]=++cnt;                for(int u=pre[v]; u!=v; u=pre[u])                    id[u]=cnt;            }        }        if(!cnt)break;        for(int i=1; i<=n; i++)            if(!id[i])id[i]=++cnt;        for(int i=2; i<=pos; i++)        {            int u=e[i].u,v=e[i].v;            e[i].u=id[u];e[i].v=id[v];            if(id[u]!=id[v])e[i].w-=ine[v];        }        rt=id[rt];        n=cnt;    }    return ans;}signed main(){    read(n);read(m);read(rt);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);    }    int res=solve();    write(res==INF?-1:res);pc('\n');    return 0;}

        最小树形图 Tarjan的DMST

        时间复杂度 $O(m+n\log m)$

        代码如下

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e3+15)#define M (int)(2e4+15)#define ls(x) t[x].ch[0]#define rs(x) t[x].ch[1]int n,m,r,cnt,f[N];int vis[N],stk[N],top;queue<int> q[N];namespace leftist{    struct node    {        int ch[2],u,v,w,dist,tag;    }t[M];    int tot,rt[M];    int New(int u,int v,int w)    {        t[++tot]={0,0,u,v,w};        return tot;    }    void push_down(int x)    {        t[ls(x)].tag+=t[x].tag;        t[ls(x)].w+=t[x].tag;        t[rs(x)].tag+=t[x].tag;        t[rs(x)].w+=t[x].tag;        t[x].tag=0;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        push_down(x);push_down(y);        if(t[x].w>t[y].w)swap(x,y);        rs(x)=merge(rs(x),y);        if(t[rs(x)].dist>t[ls(x)].dist)            swap(ls(x),rs(x));        t[x].dist=t[rs(x)].dist+1;        return x;    }    int remove(int x)    {        push_down(x);        return merge(ls(x),rs(x));    }    void build()    {        for(int i=1; i<=n; i++)        {            if(q[i].empty())continue;            while(q[i].size()!=1)            {                int p1=q[i].front();q[i].pop();                int p2=q[i].front();q[i].pop();                p1=merge(p1,p2);                q[i].push(p1);            }            if(!q[i].empty())            {                rt[i]=merge(rt[i],q[i].front());                q[i].pop();            }        }    }}int find(int x){return f[x]==x?f[x]:f[x]=find(f[x]);}signed main(){    using namespace leftist;    read(n);read(m);read(r);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        int p=New(u,v,w);        q[v].push(p);        // rt[v]=merge(rt[v],p);    }    for(int i=1; i<=n; i++)    {        int p=New(i>1?i-1:n,i,INF);        q[i].push(p);        // rt[i]=merge(rt[i],p);    }    build();    for(int i=1; i<=2*n; i++)f[i]=i;    stk[++top]=r;vis[r]=1;    int ans=0,cnt=n;    while(rt[stk[top]])    {        int &p=rt[stk[top]];        int u=find(t[p].u);        if(u==stk[top])        {            p=remove(p);            continue;        }        if(!vis[u])        {            stk[++top]=u;            vis[u]=1;            continue;        }        int q=++cnt;        while(vis[u])        {            int v=stk[top--];            vis[v]=0;f[v]=q;            node *tmp=&t[rt[v]];            tmp->tag-=tmp->w;            if(find(tmp->v)!=find(r))                ans+=tmp->w;            rt[v]=remove(rt[v]);            rt[q]=merge(rt[q],rt[v]);        }        stk[++top]=q;        vis[q]=1;    }    ans=ans>=INF?-1:ans;    write(ans);pc('\n');    return 0;}

        改了一下范围是这样的

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e5+15)#define M (int)(2e6+15)#define ls(x) t[x].ch[0]#define rs(x) t[x].ch[1]int n,m,r,cnt,f[N];int vis[N],stk[N],top;queue<int> q[N];namespace leftist{    struct node    {        int ch[2],u,v,w,dist,tag;    }t[M];    int tot,rt[M];    int New(int u,int v,int w)    {        t[++tot]={0,0,u,v,w};        return tot;    }    void push_down(int x)    {        t[ls(x)].tag+=t[x].tag;        t[ls(x)].w+=t[x].tag;        t[rs(x)].tag+=t[x].tag;        t[rs(x)].w+=t[x].tag;        t[x].tag=0;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        push_down(x);push_down(y);        if(t[x].w>t[y].w)swap(x,y);        rs(x)=merge(rs(x),y);        if(t[rs(x)].dist>t[ls(x)].dist)            swap(ls(x),rs(x));        t[x].dist=t[rs(x)].dist+1;        return x;    }    int remove(int x)    {        push_down(x);        return merge(ls(x),rs(x));    }    void build()    {        for(int i=1; i<=n; i++)        {            if(q[i].empty())continue;            while(q[i].size()!=1)            {                int p1=q[i].front();q[i].pop();                int p2=q[i].front();q[i].pop();                p1=merge(p1,p2);                q[i].push(p1);            }            if(!q[i].empty())            {                rt[i]=merge(rt[i],q[i].front());                q[i].pop();            }        }    }}int find(int x){return f[x]==x?f[x]:f[x]=find(f[x]);}signed main(){    using namespace leftist;    read(n);read(m);read(r);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        int p=New(u,v,w);        q[v].push(p);        // rt[v]=merge(rt[v],p);    }    for(int i=1; i<=n; i++)    {        int p=New(i>1?i-1:n,i,INF);        q[i].push(p);        // rt[i]=merge(rt[i],p);    }    build();    for(int i=1; i<=2*n; i++)f[i]=i;    stk[++top]=r;vis[r]=1;    int ans=0,cnt=n;    while(rt[stk[top]])    {        int &p=rt[stk[top]];        int u=find(t[p].u);        if(u==stk[top])        {            p=remove(p);            continue;        }        if(!vis[u])        {            stk[++top]=u;            vis[u]=1;            continue;        }        int q=++cnt;        while(vis[u])        {            int v=stk[top--];            vis[v]=0;f[v]=q;            node *tmp=&t[rt[v]];            tmp->tag-=tmp->w;            if(find(tmp->v)!=find(r))                ans+=tmp->w;            rt[v]=remove(rt[v]);            rt[q]=merge(rt[q],rt[v]);        }        stk[++top]=q;        vis[q]=1;    }    ans=ans>=INF?-1:ans;    write(ans);pc('\n');    return 0;}

        二分图

        二分图最大匹配

        匈牙利算法

        时间复杂度 $O(nm)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+5)#define M (int)(1e5+15)#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e6+5)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc(); T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}int n1,n2,m,vis[N],mch[N],ans;vector<int> vec[N];bool dfs(int u,int now){    if(vis[u]==now)return 0;    vis[u]=now;    for(int v:vec[u])        if(!mch[v]||dfs(mch[v],now))        {            mch[v]=u;            return 1;        }    return 0;}signed main(){    read(n1);read(n2);read(m);    for(int i=1,u,v; i<=m; i++)    {        read(u);read(v);        vec[u].push_back(v);    }    for(int i=1; i<=n1; i++)        ans+=dfs(i,i);    write(ans);pc('\n');    return 0;}

        ISAP

        时间复杂度 $O(m\sqrt{n})$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+5)#define M (int)(5e4+15)#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e6+5)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}struct Edge{    int u,v,w,next;}e[M<<3];int n1,n2,m,pos=1,ans,head[N],now[N],s,t,gap[N],dep[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}queue<int> q;void bfs(){    memset(dep,0x3f,sizeof(dep));    q.push(t);dep[t]=1;gap[1]=1;    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(e[i^1].w>0&&dep[v]==INF)            {                dep[v]=dep[u]+1;                gap[dep[v]]++;                q.push(v);            }        }    }}int dfs(int u,int in){    if(u==t)return in;    int out=0;    for(int i=now[u]; i; i=e[i].next)    {        int v=e[i].v;now[u]=i;        if(e[i].w>0&&dep[u]==dep[v]+1)        {            int res=dfs(v,min(in,e[i].w));            e[i].w-=res;            e[i^1].w+=res;            in-=res;            out+=res;        }        if(!in)return out;    }    if(!--gap[dep[u]])dep[s]=n1+n2+3;    gap[++dep[u]]++;    return out;}signed main(){    read(n1);read(n2);read(m);    s=1;t=n1+n2+2;    for(int i=1,u,v; i<=m; i++)    {        read(u);read(v);        addEdge(u+1,v+n1+1,1);addEdge(v+n1+1,u+1,0);    }    for(int i=1; i<=n1; i++)        addEdge(s,i+1,1),addEdge(i+1,s,0);    for(int i=1; i<=n2; i++)        addEdge(n1+i+1,t,1),addEdge(t,n1+i+1,0);        bfs();    while(dep[s]<=n1+n2+2)        memcpy(now,head,sizeof(head)),        ans+=dfs(s,INF);    write(ans);pc('\n');    return 0;}

        二分图最大权完美匹配

        KM算法

        时间复杂度 $O(n^3)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(555)int n,m;int g[N][N],slack[N];int lx[N],ly[N],px[N],py[N],pre[N],d;bool vx[N],vy[N];void aug(int v){    int t;    while(v)    {        t=px[pre[v]];        px[pre[v]]=v;        py[v]=pre[v];        v=t;    }}queue<int> q;void bfs(int s){    memset(vx,0,sizeof(vx));    memset(vy,0,sizeof(vy));    for(int i=1; i<=n; i++)        slack[i]=INF;    while(!q.empty())q.pop();    q.push(s);    while(1)    {        while(!q.empty())        {            int u=q.front();q.pop();            vx[u]=1;            for(int i=1; i<=n; i++)            {                if(!vy[i]&&lx[u]+ly[i]-g[u][i]<slack[i])                {                    slack[i]=lx[u]+ly[i]-g[u][i];                    pre[i]=u;                    if(!slack[i])                    {                        vy[i]=1;                        if(!py[i]){aug(i);return;}                        else q.push(py[i]);                    }                }            }        }        int d=INF;        for(int i=1; i<=n; i++)            if(!vy[i])d=min(d,slack[i]);        for(int i=1; i<=n; i++)        {            if(vx[i])lx[i]-=d;            if(vy[i])ly[i]+=d;            else slack[i]-=d;        }        for(int i=1; i<=n; i++)        {            if(!vy[i]&&!slack[i])            {                vy[i]=1;                if(!py[i]){aug(i);return;}                else q.push(py[i]);            }        }    }}int KM(){    int res=0;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            lx[i]=max(lx[i],g[i][j]);    for(int i=1; i<=n; i++)bfs(i);    for(int i=1; i<=n; i++)        res+=g[py[i]][i];    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> m;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            g[i][j]=-INF;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        g[u][v]=max(g[u][v],w);    }    cout << KM() << endl;    for(int i=1; i<=n; i++)        cout << py[i] << " \n"[i==n];    return 0;}

        一般图最大匹配

        带花树算法

        时间复杂度 $O(n^3)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,m,cnt,vis[N];int mch[N],pre[N],f[N],col[N];vector<int> vec[N];int find(int x){return f[x]==x?x:f[x]=find(f[x]);}queue<int> q;void aug(int v){    int t;    while(v)    {        t=mch[pre[v]];        mch[v]=pre[v];        mch[pre[v]]=v;        v=t;    }}int lca(int u,int v){    ++cnt;    u=find(u);v=find(v);    while(vis[u]!=cnt)    {        vis[u]=cnt;        u=find(pre[mch[u]]);        if(v)swap(u,v);    }    return u;}void shrink(int u,int v,int p){    while(find(u)!=p)    {        pre[u]=v;        v=mch[u];        if(col[v]==2)            col[v]=1,q.push(v);        f[u]=p;f[v]=p;        u=pre[v];    }}bool bfs(int s){    for(int i=1; i<=n; i++)        f[i]=i,pre[i]=col[i]=0;    while(!q.empty())q.pop();    q.push(s);col[s]=1;    while(!q.empty())    {        int u=q.front();q.pop();        for(int v:vec[u])        {            if(!col[v])            {                pre[v]=u;                if(!mch[v]){aug(v);return 1;}                else col[v]=2,col[mch[v]]=1,q.push(mch[v]);            }else if(col[v]==1&&find(u)!=find(v))            {                int p=lca(u,v);                shrink(u,v,p);shrink(v,u,p);            }        }    }    return 0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);    }    int res=0;    for(int i=1; i<=n; i++)        res+=!mch[i]&&bfs(i);    cout << res << endl;    for(int i=1; i<=n; i++)        cout << mch[i] << " \n"[i==n];    return 0;}

        一般图最大权匹配

        不会 qwq 感觉要等到我大学才会去学,咕咕咕…


        无向图的最小环问题

        下面这个代码求的是至少包含 $3$ 个点的环

        要根据题意判断

         #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3fint n,m,ans=INF;int g[105][105],f[105][105];signed main(){    ios::sync_with_stdio(0);    cin >> n >> m;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            g[i][j]=f[i][j]=INF;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        g[u][v]=min(g[u][v],w);        g[v][u]=min(g[v][u],w);        f[u][v]=min(f[u][v],w);        f[v][u]=min(f[v][u],w);    }    for(int k=1; k<=n; k++)    {        for(int i=1; i<k; i++)            for(int j=i+1; j<k; j++)                ans=min(ans,f[i][j]+g[i][k]+g[k][j]);        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);    }    if(ans==INF)cout << "No solution.";    else cout << ans;    return 0;}

        ]]> + OI模板-图论

        最短路算法

        dijkstra

        P4779【模板】单源最短路径(标准版)

        优先队列优化 \(O((n+m)\log m)\)

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)#define M (int)(2e5+15)int n,m,pos=1,d[N],vis[N],head[N];struct Edge{int u,v,w,next;} e[M];struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}priority_queue<node> q;void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dijkstra(int st){    for(int i=1; i<=n; i++) vis[i]=0,d[i]=INF;    d[st]=0; q.push({st,0});    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u]) continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int st; cin >> n >> m >> st;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);    }    dijkstra(st);    for(int i=1; i<=n; i++)        cout << (d[i]==INF?-1:d[i]) << " \n"[i==n];    return 0;}

        SPFA

        时间复杂度 \(O(nm)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;int d[N],vis[N];struct Edge{    int u,v,w,next;}e[N];int pos=1,head[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}queue<int> q;void spfa(){    memset(d,0x3f,(n+1)*sizeof(int));    memset(vis,0,(n+1)*sizeof(int));    d[1]=0;vis[1]=1;    q.push(1);    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[v]>d[u]+e[i].w)            {                d[v]=d[u]+e[i].w;                if(!vis[v])                    q.push(v),vis[v]=1;            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin >> n >> m;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);    }    spfa();    for(int i=1; i<=n; i++)        cout << (d[i]==INF?-1:d[i]) << " \n"[i==n];    return 0;}

        分层图最短路

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+5)#define M (int)(5e4+5)#define K (int)(11)struct Edge{    int u,v,w,next;}e[M*(K+1)*4];int n,m,k,s,t,pos=1,head[N*(K+1)],d[N*(K+1)],vis[N*(K+1)];struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}priority_queue<node> q;void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dijkstra(int st){    memset(d,0x3f,sizeof(d));    q.push({st,0}); d[st]=0;    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> k >> s >> t; ++s; ++t;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w; ++u; ++v;        addEdge(u,v,w);addEdge(v,u,w);        for(int j=1; j<=k; j++)        {            addEdge(j*n+u,j*n+v,w);            addEdge(j*n+v,j*n+u,w);            addEdge((j-1)*n+u,j*n+v,0);            addEdge((j-1)*n+v,j*n+u,0);        }    }    for(int i=1; i<=k; i++)        addEdge((i-1)*n+t,i*n+t,0);    dijkstra(s);    cout << d[k*n+t] << '\n';    return 0;}

        最小生成树

        都是无向图!!!!有向图的叫最小树形图!!!

        P3366【模板】最小生成树

        kruskal

        时间复杂度 \(O(m\log m)\)

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e3+15)#define M (int)(2e5+15)int n,m,pos,res,head[N],f[N];struct Edge{int u,v,w;}e[M];bool operator<(Edge a,Edge b){return a.w<b.w;}void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}int kruskal(){    sort(e+1,e+1+m); int res=0,cnt=0;    for(int i=1; i<=m&&cnt<n-1; i++)        if(find(e[i].u)!=find(e[i].v))        {            merge(e[i].u,e[i].v);            res+=e[i].w; ++cnt;        }    return (cnt!=n-1)?-1:res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m; init(n);    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        e[i]={u,v,w};    }    int ans=kruskal();    if(ans==-1)cout << "orz";    else cout << ans;    return 0;}

        Boruvka

        时间复杂度 \(O(m\log n)\)

        空间复杂度 \(O(m)\)

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e3+15)#define M (int)(2e5+15)int n,m,head[N],f[N],best[M],vis[M];struct Edge{int u,v,w;}e[M];void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}bool cmp(int a,int b){    if(!b) return 1;    if(e[a].w==e[b].w) return a<b;    return e[a].w<e[b].w;}int boruvka(){    int res=0,cnt=0,ok=1;    while(ok)    {        for(int i=0; i<=m; i++) best[i]=0;        for(int i=1,u,v; i<=m; i++)        {            if(vis[i]) continue;            u=find(e[i].u),v=find(e[i].v);            if(u==v) continue;            if(cmp(i,best[u])) best[u]=i;            if(cmp(i,best[v])) best[v]=i;        }        ok=0;        for(int i=1; i<=n&&cnt<n-1; i++)            if(best[i]&&!vis[best[i]])            {                vis[best[i]]=1; ++cnt; ok=1;                res+=e[best[i]].w;                merge(e[best[i]].u,e[best[i]].v);            }    }    return (cnt!=n-1)?-1:res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m; init(n);    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        e[i]={u,v,w};    }    int ans=boruvka();    if(ans==-1) cout << "orz";    else cout << ans << '\n';    return 0;}

        Prim

        优先队列优化的时间复杂度 \(O(m\logn)\)

        斐波那契堆优化的时间复杂度 \(O(n\logn)\)

        下面的代码是无优化邻接矩阵版的,\(O(n^2)\) ,仅适用于稠密图\(n\le 2000\),不然会挂(MLE或TLE)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;int ans,d[N],vis[N],g[N][N];void prim(){memset(d,0x3f,(n+1)*sizeof(int));for(int i=1; i<n; i++){int u=0;for(int j=1; j<=n; j++)if(!vis[j]&&(!u||d[j]<d[u])) u=j;vis[u]=1;for(int j=1; j<=n; j++)if(!vis[j])d[j]=min(d[j],g[u][j]);}for(int i=2; i<=n; i++)ans+=d[i];}signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> m;for(int i=1; i<=n; i++)for(int j=1; j<=n; j++)if(i!=j)g[i][j]=INF;for(int i=1,u,v,w; i<=m; i++){cin >> u >> v >> w;g[v][u]=g[u][v]=min(g[u][v],w);}prim();cout << ans << endl;return 0;}

        LCT

        比较离谱的解法之一

        大常数

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(4e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,idx,ans,cnt;namespace LCT{    struct node    {        int ch[2],w,id,fa,sz,mx,tag;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void push_up(int x)    {        t[x].id=x;t[x].mx=t[x].w;        if(t[t[x].ch[0]].mx>t[x].mx)            t[x].mx=t[t[x].ch[0]].mx,t[x].id=t[t[x].ch[0]].id;        if(t[t[x].ch[1]].mx>t[x].mx)            t[x].mx=t[t[x].ch[1]].mx,t[x].id=t[t[x].ch[1]].id;    }    void push_down(int x)    {        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag=0;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0; x; y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y,push_up(x);    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void split(int x,int y)    {        make_root(x);        access(y);splay(y);    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    int ck(int x,int y)    {        make_root(x);        return find_root(y)!=x;    }    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])        {            t[x].ch[1]=t[y].fa=0;            push_up(x);        }    }}signed main(){    using namespace LCT;    read(n);read(m);    idx=n;    for(int i=1,x,y,z; i<=m; i++)    {        read(x);read(y);read(z);        t[++idx].w=z;        if(x!=y&&ck(x,y))link(x,idx),link(idx,y),ans+=z,++cnt;        else        {            split(x,y);int now=t[y].id;            if(t[now].mx<=z)continue;            ans-=t[now].mx-z;splay(now);            t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0;            link(x,idx);link(idx,y);        }    }    if(cnt<n-1)puts("orz");    else write(ans),pc('\n');    return 0;}

        线段树优化建图

        板子1

        CF786BLegacy题解

        \(n\) 个结点, \(q\) 次操作,问操作后从 \(s\) 出发的单源最短路

        操作如下

        • 输入 1 u v w 表示 \(u\) 到 \(v\) 连一条有向边
        • 输入 2 u l r w 表示 \(u\) 到 \([l,r]\) 的每个结点连一条有向边
        • 输入 3 u l r w 表示 \([l,r]\) 的每个结点向 \(u\) 连一条有向边

        对于 \(100\%\) 的数据,\(1\le n,q \le 10^5\),\(1\le w \le 10^9\)

        代码:

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <queue>#include <bitset>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define DEBUG puts("-------------")namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N ((int)(1e5+15))const int D=5e5+15; // 两棵线段树的分界点int n,m,s,d[N*10+255],a[N*10+255]; // a是结点编号对应的线段树叶子结点的编号bitset<N*10+255> vis;struct node1{int v,w;}; // 边struct show_l_r{int l,r;}tr[N<<2]; // 辅助树vector<node1> g[N*10+255]; // 两棵线段树struct node2{int u,dis;};bool operator<(node2 a,node2 b){return a.dis>b.dis;}#define ls(x) (x<<1)#define rs(x) (x<<1|1)void build(int l,int r,int at){    tr[at].l=l; tr[at].r=r;    if(l==r) return a[l]=at, void(0);    int mid=(l+r)>>1;    g[at].push_back({ls(at),0});    g[at].push_back({rs(at),0});    g[ls(at)+D].push_back({at+D,0});    g[rs(at)+D].push_back({at+D,0});    build(l,mid,ls(at)); build(mid+1,r,rs(at));}void link(int nl,int nr,int u,int w,int opt,int at){    int l=tr[at].l,r=tr[at].r;    if(nl<=l&&r<=nr)    {        if(opt) g[at+D].push_back({u,w});        else g[u+D].push_back({at,w});        return;    }    int mid=(l+r)>>1;    if(nl<=mid) link(nl,nr,u,w,opt,ls(at));    if(nr>mid) link(nl,nr,u,w,opt,rs(at));    }void Dijkstra(int st){    priority_queue<node2> q;    memset(d,0x3f,sizeof(d));    d[st]=0; vis=0; q.push({st,0});    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u]) continue;        vis[u]=1;        for(auto i : g[u])        {            int v=i.v,w=i.w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m); read(s);    build(1,n,1);    for(int i=1,op,u,v,l,r,w; i<=m; i++)    {        read(op);        if(op==1)        {            read(u); read(v); read(w);            g[a[u]].push_back({a[v],w});        }        else        {            read(u); read(l); read(r); read(w);            link(l,r,a[u],w,op&1,1); // op=2: u->[l,r] , op=3:[l,r]->u;        }    }    for(int i=1; i<=n; i++)    {        g[a[i]].push_back({a[i]+D,0}),        g[a[i]+D].push_back({a[i],0});    }    Dijkstra(a[s]+D);    for(int i=1; i<=n; i++)        write(d[a[i]]!=INF ? d[a[i]] : -1),pc(" \n"[i==n]);    return 0;}

        板子2

        P6348[PA2011]Journeys

        咕咕咕。。。


        判负环

        spfa,这份代码是从结点 \(1\)为起点的

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;int d[N],vis[N],cnt[N];struct Edge{    int v,w,next;};vector<Edge> vec[N];queue<int> q;bool spfa(){    memset(d,0x3f,(n+1)*sizeof(int));    memset(vis,0,(n+1)*sizeof(int));    memset(cnt,0,(n+1)*sizeof(int));    d[1]=0;vis[1]=1;cnt[1]=1;    q.push(1);    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=0; i<vec[u].size(); i++)        {            int v=vec[u][i].v,w=vec[u][i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                if(!vis[v])                {                    if(++cnt[v]>=n)                        return 1;                    q.push(v),vis[v]=1;                }            }        }    }    return 0;}signed main(){    ios::sync_with_stdio(0);    int Q;    cin >> Q;    while(Q--)    {        cin >> n >> m;        for(int i=1,u,v,w; i<=m; i++)        {            cin >> u >> v >> w;            vec[u].push_back({v,w});            if(w>=0)                vec[v].push_back({u,w});        }        cout << (spfa()?"YES":"NO") << endl;        for(int i=1; i<=n; i++)            vec[i].clear();    }        return 0;}

        更为通用的写法如下(有向图)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m,st;int d[N],vis[N],cnt[N];struct Edge{    int v,w,next;};vector<Edge> vec[N];queue<int> q;bool spfa(){    memset(d,0x3f,(n+2)*sizeof(int));    memset(vis,0,(n+2)*sizeof(int));    memset(cnt,0,(n+2)*sizeof(int));    d[st]=0;vis[st]=1;cnt[st]=1;    q.push(st);    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=0; i<vec[u].size(); i++)        {            int v=vec[u][i].v,w=vec[u][i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                if(!vis[v])                {                    if(++cnt[v]>=n) // 不变,只有无向图才变n+1                        return 1; // 指虚拟结点                    q.push(v),vis[v]=1;                }                                }        }    }    return 0;}signed main(){    ios::sync_with_stdio(0);    int Q;    cin >> Q;    while(Q--)    {        cin >> n >> m;        for(int i=1,u,v,w; i<=m; i++)        {            cin >> u >> v >> w;            vec[u].push_back({v,w,(int)vec[v].size()});            if(w>=0)                vec[v].push_back({u,w,(int)vec[u].size()-1});        }        st=0;        for(int i=1; i<=n; i++)            vec[0].push_back({i,0});        cout << (spfa()?"YES":"NO") << endl;        for(int i=0; i<=n; i++)            vec[i].clear();    }    return 0;}

        kosaraju算法

        时间复杂度 \(O(n+m)\)

        空间复杂度 \(O(m)\)

        例题:P3387【模板】缩点

        有向图强连通分量缩点

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)bool vis[N];vector<int> g1[N],g2[N],vec[N];int n,m,cnt,scnt,f[N],sum[N],in[N],val[N],scc[N],dfn[N];void addEdge1(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void addEdge2(int u,int v){    vec[u].push_back(v);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u])        if(!vis[v]) dfs1(v);    dfn[++cnt]=u;}void dfs2(int u,int id){    scc[u]=id; sum[id]+=val[u];    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=n; i++)        if(!vis[i]) dfs1(i);    scnt=0;    for(int i=n; i>=1; i--)        if(!scc[dfn[i]])        {            ++scnt;            dfs2(dfn[i],scnt);        }}int topo(){    queue<int> q;    for(int i=1; i<=scnt; i++)        if(!in[i]) q.push(i),f[i]=sum[i];    while(!q.empty())    {        int u=q.front(); q.pop();        for(int v:vec[u])        {            f[v]=max(f[v],f[u]+sum[v]);            if(!(--in[v])) q.push(v);        }    }    return *max_element(f+1,f+1+scnt);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> val[i];    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge1(u,v);    }    kosaraju();    for(int i=1,u,v; i<=n; i++)        for(int j : g1[i])        {            u=scc[i],v=scc[j];            if(u!=v) addEdge2(u,v),++in[v];        }    cout << topo() << '\n';    return 0;}

        Tarjan算法 [连通性问题]

        无向图

        割边(桥)

        时间复杂度 \(O(n)\)

        空间复杂度 \(O(m)\)

        例题:T103481【模板】割边

        #include <bits/stdc++.h>using namespace std;#define int long long#define M (int)(6e5+25)#define N (int)(5e4+25)struct Edge{    int u,v,next;}e[M];int n,m,ans;int pos=2,head[N],dfn[N],low[N],dfncnt,cut[M];void addEdge(int u,int v){    e[pos]={u,v,head[u]};    head[u]=pos++;}void tarjan(int u,int in_edge){    dfn[u]=low[u]=++dfncnt;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(!dfn[v])        {            tarjan(v,i);            low[u]=min(low[u],low[v]);            if(low[v]>dfn[u])                cut[i]=cut[i^1]=1;        }else if(i!=(in_edge^1))            low[u]=min(low[u],dfn[v]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        if(!dfn[i])tarjan(i,0);    for(int i=2; i<pos; i+=2)        if(cut[i])++ans;    cout << ans << endl;    return 0;}

        割点(割顶)

        区别于桥,这个不用管fa结点,但是注意rt要有两个子结点都是“割点性质“

        时间复杂度 \(O(n)\)

        空间复杂度 \(O(m)\)

        例题:P3388【模板】割点(割顶)

        #include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(2e4+15)#define M (int)(2e5+15)struct node{int u,v,next;}e[M];int n,m,ans;int pos=2,head[N],dfncnt,dfn[N],low[N],cut[N];void addEdge(int u,int v){e[pos]={u,v,head[u]};head[u]=pos++;}void tarjan(int u,int rt){dfn[u]=low[u]=++dfncnt;int cnt=0;for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(!dfn[v]){tarjan(v,rt);low[u]=min(low[u],low[v]);if(low[v]>=dfn[u]){++cnt;if(u!=rt||cnt>1)cut[u]=1;}}low[u]=min(low[u],dfn[v]);}}signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> m;for(int i=1,u,v; i<=m; i++){cin >> u >> v;addEdge(u,v);addEdge(v,u);}for(int i=1; i<=n; i++)if(!dfn[i])tarjan(i,i);for(int i=1; i<=n; i++)if(cut[i])++ans;cout << ans << endl;for(int i=1; i<=n; i++)if(cut[i])cout << i << " \n"[!--ans];return 0;}

        边双连通分量

        时间复杂度 \(O(n)\)

        空间复杂度 \(O(m)\)

        upd20220806 有个板子题P8436【模板】边双连通分量了

        例题:P2860[USACO06JAN]Redundant Paths G

        例题的代码:

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(5e3+15)#define M (int)(2e4+15)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,next;}e[M];int n,m,ans,ecc[N],top;int pos=2,head[N],stk[N];int dfn[N],low[N],dfncnt,ecnt,in[N];void addEdge(int u,int v){    e[pos]={u,v,head[u]};    head[u]=pos++;}void tarjan(int u,int in_edge){    dfn[u]=low[u]=++dfncnt;    stk[++top]=u;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(!dfn[v])        {            tarjan(v,i);            low[u]=min(low[u],low[v]);        }else if(i!=(in_edge^1))            low[u]=min(low[u],dfn[v]);    }    if(low[u]==dfn[u])    {        ecc[u]=++ecnt;        while(stk[top]!=u)            ecc[stk[top--]]=ecnt;        --top;    }}signed main(){    read(n);read(m);    for(int i=1,u,v; i<=m; i++)    {        read(u);read(v);        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        if(!dfn[i])top=0,tarjan(i,0);    for(int i=2; i<pos; i++)        if(ecc[e[i].u]!=ecc[e[i].v])            ++in[ecc[e[i].u]],++in[ecc[e[i].v]];    int ans=0;    for(int i=1; i<=ecnt; i++)        if(in[i]==2)++ans;    // 对于所有双连通分量,其缩点(e-dcc)后    // 连(叶结点数+1)/2(即叶结点数除以2向上取整)条边    // 可以使整个图变成双连通图    write((ans+1)>>1);pc('\n');    return 0;}

        点双连通分量

        时间复杂度 \(O(n)\)

        空间复杂度 \(O(m)\)

        例题:T103492【模板】点双连通分量

        注:下面的第二种写法才可通过例题!

        因为第一种把割点存在了第一个,和例题的标程不一样,然后例题没写specialjudge.

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(5e4+15)#define M (int)(6e5+15)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,next;}e[M];int n,m,vcnt,cut[N],stk[N],top;int pos=2,head[N],dfn[N],low[N],dfncnt;vector<int> vcc[N];void addEdge(int u,int v){    e[pos]={u,v,head[u]};    head[u]=pos++;}void tarjan(int u,int rt){    dfn[u]=low[u]=++dfncnt;    stk[++top]=u;int cnt=0;    if(u==rt&&!head[u])    {        vcc[++vcnt].push_back(u);        return;    }    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(!dfn[v])        {            tarjan(v,rt);            low[u]=min(low[u],low[v]);            if(low[v]>=dfn[u])            {                ++cnt;                if(u!=rt||cnt>1)cut[u]=1;                // 第一种写法,割点在vcc[vcnt][0]                vcc[++vcnt].push_back(u);                while(stk[top]!=v)                    vcc[vcnt].push_back(stk[top--]);                vcc[vcnt].push_back(stk[top--]);                /* 第二种写法                    int z;++vcnt;                    do {                        z=stk[top--];                        vcc[vcnt].push_back(z);                    } while (z!=v);                    vcc[vcnt].push_back(u);                */            }        }else low[u]=min(low[u],dfn[v]);    }}signed main(){    read(n);read(m);    for(int i=1,u,v; i<=m; i++)    {        read(u);read(v);        if(u==v)continue;        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        if(!dfn[i])top=0,tarjan(i,i);    for(int i=1; i<=vcnt; i++)        for(int j=0; j<vcc[i].size(); j++)            write(vcc[i][j]),pc(" \n"[j+1==vcc[i].size()]);    return 0;}

        有向图

        强连通分量

        时间复杂度 \(O(n+m)\)

        空间复杂度 \(O(m)\)

        例题:P2746[USACO5.3]校园网Network of Schools

        要注意强连通图统计缩点后的出、入度要特判!

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(105)int n,dfn[N],low[N],in[N],top;int out[N],stk[N],instk[N],dfncnt,scnt,scc[N];vector<int> g[N];void tarjan(int u){    dfn[u]=low[u]=++dfncnt;    stk[++top]=u;instk[u]=1;    for(int v:g[u])    {        if(!dfn[v])        {            tarjan(v);            low[u]=min(low[u],low[v]);        }else if(instk[v])            low[u]=min(low[u],dfn[v]);    }    if(low[u]==dfn[u])    {        scc[u]=++scnt;instk[u]=0;        while(stk[top]!=u)        {            scc[stk[top]]=scnt;            instk[stk[top--]]=0;        }        --top;    }}signed main(){    ios::sync_with_stdio(0);    cin >> n;    for(int i=1,v; i<=n; i++)    {        while(cin >> v && v)            g[i].push_back(v);    }    for(int i=1; i<=n; i++)        if(!dfn[i])tarjan(i);    for(int i=1; i<=n; i++)        for(int j:g[i])        {            if(scc[i]!=scc[j])                ++out[scc[i]],++in[scc[j]];        }    int x=0,y=0;    for(int i=1; i<=scnt; i++)    {        if(!in[i])++x;        if(!out[i])++y;    }    cout << x << endl;    if(scnt==1)cout << 0 << endl;    else cout << max(x,y) << endl;    return 0;}

        强连通分量缩点

        时间复杂度 \(O(n)\)

        空间复杂度 \(O(m)\)

        例题:P3387【模板】缩点

        #include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(1e4+15)#define M (int)(1e5+15)struct Edge{    int u,v,next;}e1[M],e2[M];int n,m,val[N],sum[N];int pos1=1,head1[N],pos2=1,head2[N];int dfncnt,dfn[N],low[N],scnt,dp[N];int stk[N],top,instk[N],scc[N],in[N];void addEdge1(int u,int v){    e1[++pos1]={u,v,head1[u]};    head1[u]=pos1;}void addEdge2(int u,int v){    e2[++pos2]={u,v,head2[u]};    head2[u]=pos2;}void tarjan(int u){    dfn[u]=low[u]=++dfncnt;    stk[++top]=u;instk[u]=1;    for(int i=head1[u]; i; i=e1[i].next)    {        int v=e1[i].v;        if(!dfn[v])        {            tarjan(v);            low[u]=min(low[u],low[v]);        }else if(instk[v])low[u]=min(low[u],dfn[v]);            }    if(low[u]==dfn[u])    {        scc[u]=++scnt;instk[u]=0;        sum[scnt]+=val[u];        while(stk[top]!=u)        {            scc[stk[top]]=scnt;            sum[scnt]+=val[stk[top]];            instk[stk[top--]]=0;        }        --top;    }}int topo(){    queue<int> q;    for(int i=1; i<=scnt; i++)        if(!in[i])            q.push(i),dp[i]=sum[i];    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head2[u]; i; i=e2[i].next)        {            int v=e2[i].v;            dp[v]=max(dp[v],dp[u]+sum[v]);            if(!--in[v])q.push(v);        }    }    return *max_element(dp+1,dp+1+scnt);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> val[i];    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        addEdge1(u,v);    }    for(int i=1; i<=n; i++)        if(!dfn[i])tarjan(i);    for(int i=2; i<=pos1; i++)    {        int u=scc[e1[i].u],v=scc[e1[i].v];        if(u!=v)        {            addEdge2(u,v);            ++in[v];        }    }    cout << topo() << endl;    return 0;}

        欧拉路径

        P7771【模板】欧拉路径

        求的是字典序最小的欧拉路径

        /*g++ test_20220124.cpp -o test_20220124./test_20220124*/#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)#define gc() getchar()#define pc(a) putchar(a)template<typename T>void read(T &k){char ch=gc(); T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}int n,m,cnt,s,t;struct node{int v,id;bool operator<(node &o){return v<o.v;}};vector<node> vec[MAXN];int vis[MAXN<<1],in[MAXN],out[MAXN],top,stk[MAXN<<1],del[MAXN]; // del防极限数据 (1->2) * 1e5 && (2->1) * 1e5void dfs(int u){for(int i=del[u]; i<vec[u].size(); i=max(i+1,del[u])/*可能被更新了*/){if(!vis[vec[u][i].id]){vis[vec[u][i].id]=1;del[u]=i+1;dfs(vec[u][i].v);}}stk[++top]=u;}signed main(){read(n);read(m);for(int i=1,u,v; i<=m; i++){read(u);read(v);++out[u];++in[v];vec[u].push_back({v,i});}for(int i=1; i<=n; i++){if(in[i]!=out[i]){++cnt;if(in[i]==out[i]-1)s=i;if(in[i]==out[i]+1)t=i;}}if(cnt!=0&&cnt!=2)return puts("No"),0;if(cnt==0)s=t=1;if(!s||!t)return puts("No"),0;for(int i=1; i<=n; i++)sort(vec[i].begin(),vec[i].end());dfs(s);while(top>0){write(stk[top]);pc(" \n"[top==1]);--top;}return 0;}

        欧拉回路

        hierholzer算法?待补


        2-SAT

        P4782 【模板】2-SAT问题

        kosaraju实现,注意跑dfs和开空间都要两倍。

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cstdarg>#include <cmath>#include <iomanip>#include <random>#include <bitset>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)int n,m,tim,scnt,dfn[N<<1],scc[N<<1];bitset<(N<<1)> vis;vector<int> g1[N<<1],g2[N<<1];void addEdge(int u,int v){    g1[u].push_back(v);    g2[v].push_back(u);}void dfs1(int u){    vis[u]=1;    for(int v : g1[u]) if(!vis[v]) dfs1(v);    dfn[++tim]=u;}void dfs2(int u,int id){    scc[u]=id;    for(int v : g2[u])        if(!scc[v]) dfs2(v,id);}void kosaraju(){    for(int i=1; i<=(n<<1); i++)        if(!vis[i]) dfs1(i);    scnt=0;    for(int i=(n<<1); i>=1; i--)        if(!scc[dfn[i]]) dfs2(dfn[i],++scnt);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n); read(m); vis=0;    for(int i=1,a,b,va,vb; i<=m; i++)    {        read(a); read(va); read(b); read(vb);        addEdge(a+n*(va&1), b+n*(vb^1));        addEdge(b+n*(vb&1), a+n*(va^1));    }    kosaraju();    for(int i=1; i<=n; i++)        if(scc[i]==scc[i+n]) return puts("IMPOSSIBLE"),0;    puts("POSSIBLE");    for(int i=1; i<=n; i++)        write((int)(scc[i]>scc[i+n])),pc(" \n"[i==n]);    return 0;}

        网络流算法

        有向图网络最大流

        这里默认s与t连通,实际上代码里写了判断

        Dinic

        时间复杂度 \(O(n^2m)\)

        空间复杂度 \(O(m)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)#define M (int)(2e5+15)#define gc() getchar()#define pc(a) putchar(a)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,w,next;}e[M];int n,m,s,t,ans;int pos=1,head[N],dep[N],now[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}queue<int> q;bool bfs(){    memset(dep,0x3f,(n+1)*sizeof(int));    q.push(s);dep[s]=1;now[s]=head[s];    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(e[i].w>0&&dep[v]==INF)            {                dep[v]=dep[u]+1;                now[v]=head[v];                q.push(v);            }        }    }    return dep[t]!=INF;}int dfs(int u,int in){    if(u==t)        return in;    int out=0;    for(int i=now[u]; i&&in; i=e[i].next)    {        int v=e[i].v;        if(e[i].w>0&&dep[v]==dep[u]+1)        {            int res=dfs(v,min(in,e[i].w));            e[i].w-=res;            e[i^1].w+=res;            in-=res;            out+=res;        }    }    if(!out)dep[u]=INF;    return out;}void Dinic(){    while(bfs())        ans+=dfs(s,INF);}signed main(){    read(n);read(m);read(s);read(t);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,0);    }    write(ans);pc('\n');    return 0;}

        ISAP

        时间复杂度 \(O(n^2m)\)

        空间复杂度 \(O(m)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)#define M (int)(2e4+15)#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+5)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}struct Edge{    int u,v,w,next;}e[M];int n,m,s,t,ans;int pos=1,head[N],dep[N],now[N],gap[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}queue<int> q;bool bfs(){    memset(dep,0x3f,(n+1)*sizeof(int));    q.push(t);dep[t]=1;gap[1]=1;    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(e[i^1].w>0&&dep[v]==INF)            {                dep[v]=dep[u]+1;                gap[dep[v]]++;                q.push(v);            }        }    }    return dep[s]!=INF;}int dfs(int u,int in){    if(u==t)        return in;    int out=0;    for(int i=now[u]; i; i=e[i].next)    {        int v=e[i].v;now[u]=i;        if(e[i].w>0&&dep[u]==dep[v]+1)        {            int res=dfs(v,min(in,e[i].w));            e[i].w-=res;            e[i^1].w+=res;            in-=res;            out+=res;        }        if(!in)return out;    }    if(!--gap[dep[u]])dep[s]=n+1;    gap[++dep[u]]++;    return out;}void ISAP(){    bfs();    while(dep[s]<=n)    {        memcpy(now,head,(n+1)*sizeof(int));        ans+=dfs(s,INF);    }}signed main(){    read(n);read(m);read(s);read(t);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,0);    }    ISAP();    write(ans);pc('\n');    return 0;}

        HLPP

        n=5e3,m=5e4

        时间复杂度 \(O(n^2\sqrt{m}\logn)\)

        空间复杂度 \(O(m)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f// #define INF 0x3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define N (int)(2e3+15)#define M (int)(3e5+15)#define SIZ (int)(1e5+5)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}struct Edge{int v,w,next;};vector<Edge> vec[N];int n,m,s,t,now;int gap[N],h[N],val[N],vis[N];struct cmp{    bool operator()(int a,int b)        {return h[a]<h[b];}};priority_queue<int,vector<int>,cmp> q;queue<int> b;bool bfs(){    memset(h,0x3f,(n+1)*sizeof(int));    b.push(t);h[t]=0;vis[t]=1;    while(!b.empty())    {        int u=b.front();b.pop();        vis[u]=0;        for(int i=0; i<vec[u].size(); i++)        {            int v=vec[u][i].v,j=vec[u][i].next;            if(vec[v][j].w>0&&h[v]>h[u]+1)            {                h[v]=h[u]+1;                if(!vis[v])                    b.push(v),vis[v]=1;            }        }    }    return h[s]!=INF;}void relabel(int u){    h[u]=INF;    for(int i=0; i<vec[u].size(); i++)    {        int v=vec[u][i].v;        if(vec[u][i].w>0&&h[u]>h[v]+1)            h[u]=h[v]+1;    }}int HLPP(){    if(!bfs())return -1;    h[s]=n;    for(int i=1; i<=n; i++)        if(h[i]!=INF)++gap[h[i]];    for(int i=0; i<vec[s].size(); i++)    {        int v=vec[s][i].v,w=vec[s][i].w,j=vec[s][i].next;        if(w>0)        {            val[s]-=w;            val[v]+=w;            vec[s][i].w-=w;            vec[v][j].w+=w;            if(!vis[v]&&v!=t&&v!=s)                q.push(v),vis[v]=1;        }    }    while(!q.empty())    {        int u=q.top();        q.pop();vis[u]=0;        if(h[u]==INF)continue; // !!!!!!!!!!!        for(int i=0; i<vec[u].size(); i++)        {            int v=vec[u][i].v,j=vec[u][i].next;            if(vec[u][i].w>0&&h[u]==h[v]+1)            {                int w=min(vec[u][i].w,val[u]);                val[u]-=w;                val[v]+=w;                vec[u][i].w-=w;                vec[v][j].w+=w;                if(!vis[v]&&v!=t&&v!=s)                    q.push(v),vis[v]=1;                if(!val[u])break;            }        }        if(!val[u])continue;        if(!--gap[h[u]])        {            for(int i=1; i<=n; i++)                if(i!=s&&i!=t&&h[u]<h[i]&&h[i]<=n)                    h[i]=n+1;        }        relabel(u);++gap[h[u]];        q.push(u);vis[u]=1;    }    return val[t];}signed main(){    read(n);read(m);read(s);read(t);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        vec[u].push_back({v,w,(int)vec[v].size()});        vec[v].push_back({u,0,(int)vec[u].size()-1});    }    write(HLPP());pc('\n');    return 0;}

        有向图最小费用最大流

        时间复杂度 \(O(n^2m)\)

        空间复杂度 \(O(m)\)

        1. vector写法,C++14 1.41s(O2 542ms)
        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define N (int)(5e3+10)#define M (int)(1e6+25)#define SIZ (int)(1e5+5)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{int v,w,c,next;};struct node{int u,i,j;}pre[N];vector<Edge> vec[N];int n,m,s,t,flow,cost;int in[N],d[N],vis[N];queue<int> q;bool spfa(){    memset(d,0x3f,(n+1)*sizeof(int));    memset(in,0x3f,(n+1)*sizeof(int));    memset(vis,0,(n+1)*sizeof(int));    q.push(s);vis[s]=1;d[s]=0;pre[t].u=0;    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=0; i<vec[u].size(); i++)        {            int v=vec[u][i].v,w=vec[u][i].w;            int c=vec[u][i].c,j=vec[u][i].next;            if(w>0&&d[v]>d[u]+c)            {                d[v]=d[u]+c;                pre[v]={u,i,j};                in[v]=min(in[u],w);                if(!vis[v])                    q.push(v),vis[v]=1;            }        }    }    return pre[t].u!=0;}void Dinic(){    while(spfa())    {        flow+=in[t];        cost+=in[t]*d[t];        int u,i,j,v=t;        while(v!=s)        {            u=pre[v].u,i=pre[v].i,j=pre[v].j;            vec[u][i].w-=in[t];            vec[v][j].w+=in[t];            v=pre[v].u;        }    }}signed main(){    read(n);read(m);read(s);read(t);    for(int i=1,u,v,w,c; i<=m; i++)    {        read(u);read(v);read(w);read(c);        vec[u].push_back({v,w,c,(int)vec[v].size()});        vec[v].push_back({u,0,-c,(int)vec[u].size()-1});    }    Dinic();    write(flow);pc(' ');write(cost);pc('\n');    return 0;}
        1. 链式前向星写法,C++14 1.95s(O2 1.45s)
        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define N (int)(5e3+10)#define M (int)(1e6+25)#define SIZ (int)(1e5+5)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,w,c,next;}e[M];int n,m,s,t,flow,cost;int pos=1,head[N],in[N],d[N],pre[N],last[N],vis[N];void addEdge(int u,int v,int w,int c){    e[++pos]={u,v,w,c,head[u]};    head[u]=pos;}struct node{    int u,dis;    bool operator<(const node &o)const        {return dis>o.dis;}};queue<int> q;bool spfa(){    memset(d,0x3f,(n+1)*sizeof(int));    memset(in,0x3f,(n+1)*sizeof(int));    memset(vis,0,(n+1)*sizeof(int));    q.push(s);vis[s]=1;d[s]=0;pre[t]=0;    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(e[i].w>0&&d[v]>d[u]+e[i].c)            {                d[v]=d[u]+e[i].c;                pre[v]=u;                last[v]=i;                in[v]=min(in[u],e[i].w);                if(!vis[v])                    q.push(v),vis[v]=1;            }        }    }    return pre[t]!=0;}void Dinic(){    while(spfa())    {        flow+=in[t];        cost+=in[t]*d[t];        int at=t;        while(at!=s)        {            e[last[at]].w-=in[t];            e[last[at]^1].w+=in[t];            at=pre[at];        }    }}signed main(){    read(n);read(m);read(s);read(t);    for(int i=1,u,v,w,c; i<=m; i++)    {        read(u);read(v);read(w);read(c);        addEdge(u,v,w,c);addEdge(v,u,0,-c);    }    Dinic();    write(flow);pc(' ');write(cost);pc('\n');    return 0;}

        有向图有源汇上下界最大流

        例题:P5192 Zoj3229Shoot the Bullet|东方文花帖|【模板】有源汇上下界最大流

        本例题建模:

        1. 先建立一个源点
        2. 从源点到每个少女,流量为 \(\left[G_i,+\infty\right)\)
        3. 从每个少女到每一天,流量为 \([l_i,r_i]\)
        4. 从每一天到汇点,流量为 \([0,D_i]\)

        然后跑有源汇上下界最大流就好了

        1. ISAP版 C++14 O2 189ms
        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f// #define gc() readchar()#define gc() getchar()#define pc(a) putchar(a)#define N (int)(5e4+15)#define M (int)(4e5+15)#define SIZ (int)(1e5+15)// char buf1[SIZ],*p1=buf1,*p2=buf1;// char readchar()// {//     if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);//     return p1==p2?EOF:*p1++;// }template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}struct Edge{    int u,v,w,next;}e[M];int n,m,s,t,s0,t0;int pos=1,head[N],dep[N],now[N],gap[N],A[N];void add(int u,int v,int w){    e[++pos]={u,v,w,head[u]};head[u]=pos;    e[++pos]={v,u,0,head[v]};head[v]=pos;}void addEdge(int u,int v,int l,int r){    add(u,v,r-l);    A[u]-=l;A[v]+=l;}queue<int> q;bool bfs(){    memset(dep,0x3f,(n+m+5)*sizeof(int));    memset(gap,0,(n+m+5)*sizeof(int));    q.push(t0);dep[t0]=1;gap[1]=1;    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(e[i^1].w&&dep[v]==INF)            {                dep[v]=dep[u]+1;                gap[dep[v]]++;                q.push(v);            }        }    }    return dep[s0]!=INF;}int dfs(int u,int in){    if(u==t0)return in;    int out=0;    for(int i=now[u]; i&&in; i=e[i].next)    {        int v=e[i].v;now[u]=i;        if(e[i].w&&dep[u]==dep[v]+1)        {            int res=dfs(v,min(in,e[i].w));            e[i].w-=res;            e[i^1].w+=res;            in-=res;            out+=res;        }        if(!in)return out;    }    if(!--gap[dep[u]])dep[s0]=n+m+5;    gap[++dep[u]]++;    return out;}int ISAP(){    if(!bfs())return 0;    int res=0;    while(dep[s0]<=n+m+4)    {        memcpy(now,head,sizeof(head));        res+=dfs(s0,INF);    }    return res;}signed main(){    while(~scanf("%lld%lld",&n,&m))    {        memset(head,0,(n+m+5)*sizeof(int));        memset(A,0,(n+m+5)*sizeof(int));        s=0;t=n+m+1;pos=1;        int tot=0;        for(int i=1; i<=m; i++)        {            int x;read(x);            addEdge(s,i,x,INF);        }        for(int i=1,C,D; i<=n; i++)        {            read(C);read(D);            addEdge(m+i,t,0,D);            for(int j=1,x,L,R; j<=C; j++)            {                read(x);read(L);read(R);                addEdge(x+1,m+i,L,R);            }        }        s0=n+m+2;t0=n+m+3;        for(int i=0; i<=n+m+1; i++)        {            if(A[i]>0)add(s0,i,A[i]),tot+=A[i];            else if(A[i]<0)add(i,t0,-A[i]);        }        add(t,s,INF);        if(ISAP()<tot){puts("-1\n");continue;}        int res=e[pos].w;        e[pos].w=e[pos-1].w=0;        s0=s;t0=t;        write(res+ISAP());puts("\n");    }    return 0;}

        无向图无源汇最大流

        Stoer-Wagner

        斯托瓦格纳?

        1. 朴素写法

          时间复杂度 \(O(nm+n^3)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(666)int n,m,d[N][N],s,t,dap[N],vis[N],w[N],ord[N];int proc(int x){    memset(vis,0,(n+1)*sizeof(int));    memset(w,0,(n+1)*sizeof(int));    w[0]=-1;    for(int i=1; i<=n-x+1; i++)    {        int mx=0;        for(int j=1; j<=n; j++)            if(!dap[j]&&!vis[j]&&w[j]>w[mx])mx=j;        vis[mx]=1;ord[i]=mx;        for(int j=1; j<=n; j++)            if(!dap[j]&&!vis[j])w[j]+=d[mx][j];    }    s=ord[n-x];t=ord[n-x+1];    return w[t];}int sw(){    int res=INF;    for(int i=1; i<n; i++)    {        res=min(res,proc(i));        dap[t]=1;        for(int j=1; j<=n; j++)        {            d[s][j]+=d[t][j];            d[j][s]+=d[j][t];        }    }    return res;}signed main(){    read(n);read(m);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        d[u][v]+=w;d[v][u]+=w;    }    write(sw());pc('\n');    return 0;}
        1. 堆优化(意义不大)

          时间复杂度 \(O(nm+n^2\log n)\)

          还没写


        最小树形图

        最小树形图 朱刘算法

        时间复杂度 \(O(nm)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define M (int)(1e4+15)#define N (int)(1e3+5)struct Edge{    int u,v,w,next;}e[M];int n,m,rt;int pre[N],ine[N];int vis[N],id[N];int pos=1,head[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}int solve(){    int ans=0;    while(1)    {        for(int i=1; i<=n; i++)            ine[i]=INF;        for(int i=2; i<=pos; i++)        {            int u=e[i].u,v=e[i].v;            if(u!=v&&e[i].w<ine[v])                ine[v]=e[i].w,pre[v]=u;        }        for(int i=1; i<=n; i++)            if(i!=rt&&ine[i]==INF)return INF;        int cnt=0;        for(int i=1; i<=n; i++)            vis[i]=id[i]=0;        for(int i=1; i<=n; i++)        {            if(i==rt)continue;            ans+=ine[i];            int v=i;            while(vis[v]!=i&&!id[v]&&v!=rt)            {                vis[v]=i;                v=pre[v];            }            if(!id[v]&&v!=rt)            {                id[v]=++cnt;                for(int u=pre[v]; u!=v; u=pre[u])                    id[u]=cnt;            }        }        if(!cnt)break;        for(int i=1; i<=n; i++)            if(!id[i])id[i]=++cnt;        for(int i=2; i<=pos; i++)        {            int u=e[i].u,v=e[i].v;            e[i].u=id[u];e[i].v=id[v];            if(id[u]!=id[v])e[i].w-=ine[v];        }        rt=id[rt];        n=cnt;    }    return ans;}signed main(){    read(n);read(m);read(rt);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);    }    int res=solve();    write(res==INF?-1:res);pc('\n');    return 0;}

        最小树形图 Tarjan的DMST

        时间复杂度 \(O(m+n\log m)\)

        代码如下

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e3+15)#define M (int)(2e4+15)#define ls(x) t[x].ch[0]#define rs(x) t[x].ch[1]int n,m,r,cnt,f[N];int vis[N],stk[N],top;queue<int> q[N];namespace leftist{    struct node    {        int ch[2],u,v,w,dist,tag;    }t[M];    int tot,rt[M];    int New(int u,int v,int w)    {        t[++tot]={0,0,u,v,w};        return tot;    }    void push_down(int x)    {        t[ls(x)].tag+=t[x].tag;        t[ls(x)].w+=t[x].tag;        t[rs(x)].tag+=t[x].tag;        t[rs(x)].w+=t[x].tag;        t[x].tag=0;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        push_down(x);push_down(y);        if(t[x].w>t[y].w)swap(x,y);        rs(x)=merge(rs(x),y);        if(t[rs(x)].dist>t[ls(x)].dist)            swap(ls(x),rs(x));        t[x].dist=t[rs(x)].dist+1;        return x;    }    int remove(int x)    {        push_down(x);        return merge(ls(x),rs(x));    }    void build()    {        for(int i=1; i<=n; i++)        {            if(q[i].empty())continue;            while(q[i].size()!=1)            {                int p1=q[i].front();q[i].pop();                int p2=q[i].front();q[i].pop();                p1=merge(p1,p2);                q[i].push(p1);            }            if(!q[i].empty())            {                rt[i]=merge(rt[i],q[i].front());                q[i].pop();            }        }    }}int find(int x){return f[x]==x?f[x]:f[x]=find(f[x]);}signed main(){    using namespace leftist;    read(n);read(m);read(r);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        int p=New(u,v,w);        q[v].push(p);        // rt[v]=merge(rt[v],p);    }    for(int i=1; i<=n; i++)    {        int p=New(i>1?i-1:n,i,INF);        q[i].push(p);        // rt[i]=merge(rt[i],p);    }    build();    for(int i=1; i<=2*n; i++)f[i]=i;    stk[++top]=r;vis[r]=1;    int ans=0,cnt=n;    while(rt[stk[top]])    {        int &p=rt[stk[top]];        int u=find(t[p].u);        if(u==stk[top])        {            p=remove(p);            continue;        }        if(!vis[u])        {            stk[++top]=u;            vis[u]=1;            continue;        }        int q=++cnt;        while(vis[u])        {            int v=stk[top--];            vis[v]=0;f[v]=q;            node *tmp=&t[rt[v]];            tmp->tag-=tmp->w;            if(find(tmp->v)!=find(r))                ans+=tmp->w;            rt[v]=remove(rt[v]);            rt[q]=merge(rt[q],rt[v]);        }        stk[++top]=q;        vis[q]=1;    }    ans=ans>=INF?-1:ans;    write(ans);pc('\n');    return 0;}

        改了一下范围是这样的

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e5+15)#define M (int)(2e6+15)#define ls(x) t[x].ch[0]#define rs(x) t[x].ch[1]int n,m,r,cnt,f[N];int vis[N],stk[N],top;queue<int> q[N];namespace leftist{    struct node    {        int ch[2],u,v,w,dist,tag;    }t[M];    int tot,rt[M];    int New(int u,int v,int w)    {        t[++tot]={0,0,u,v,w};        return tot;    }    void push_down(int x)    {        t[ls(x)].tag+=t[x].tag;        t[ls(x)].w+=t[x].tag;        t[rs(x)].tag+=t[x].tag;        t[rs(x)].w+=t[x].tag;        t[x].tag=0;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        push_down(x);push_down(y);        if(t[x].w>t[y].w)swap(x,y);        rs(x)=merge(rs(x),y);        if(t[rs(x)].dist>t[ls(x)].dist)            swap(ls(x),rs(x));        t[x].dist=t[rs(x)].dist+1;        return x;    }    int remove(int x)    {        push_down(x);        return merge(ls(x),rs(x));    }    void build()    {        for(int i=1; i<=n; i++)        {            if(q[i].empty())continue;            while(q[i].size()!=1)            {                int p1=q[i].front();q[i].pop();                int p2=q[i].front();q[i].pop();                p1=merge(p1,p2);                q[i].push(p1);            }            if(!q[i].empty())            {                rt[i]=merge(rt[i],q[i].front());                q[i].pop();            }        }    }}int find(int x){return f[x]==x?f[x]:f[x]=find(f[x]);}signed main(){    using namespace leftist;    read(n);read(m);read(r);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        int p=New(u,v,w);        q[v].push(p);        // rt[v]=merge(rt[v],p);    }    for(int i=1; i<=n; i++)    {        int p=New(i>1?i-1:n,i,INF);        q[i].push(p);        // rt[i]=merge(rt[i],p);    }    build();    for(int i=1; i<=2*n; i++)f[i]=i;    stk[++top]=r;vis[r]=1;    int ans=0,cnt=n;    while(rt[stk[top]])    {        int &p=rt[stk[top]];        int u=find(t[p].u);        if(u==stk[top])        {            p=remove(p);            continue;        }        if(!vis[u])        {            stk[++top]=u;            vis[u]=1;            continue;        }        int q=++cnt;        while(vis[u])        {            int v=stk[top--];            vis[v]=0;f[v]=q;            node *tmp=&t[rt[v]];            tmp->tag-=tmp->w;            if(find(tmp->v)!=find(r))                ans+=tmp->w;            rt[v]=remove(rt[v]);            rt[q]=merge(rt[q],rt[v]);        }        stk[++top]=q;        vis[q]=1;    }    ans=ans>=INF?-1:ans;    write(ans);pc('\n');    return 0;}

        二分图

        二分图最大匹配

        匈牙利算法

        时间复杂度 \(O(nm)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+5)#define M (int)(1e5+15)#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e6+5)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc(); T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}int n1,n2,m,vis[N],mch[N],ans;vector<int> vec[N];bool dfs(int u,int now){    if(vis[u]==now)return 0;    vis[u]=now;    for(int v:vec[u])        if(!mch[v]||dfs(mch[v],now))        {            mch[v]=u;            return 1;        }    return 0;}signed main(){    read(n1);read(n2);read(m);    for(int i=1,u,v; i<=m; i++)    {        read(u);read(v);        vec[u].push_back(v);    }    for(int i=1; i<=n1; i++)        ans+=dfs(i,i);    write(ans);pc('\n');    return 0;}

        ISAP

        时间复杂度 \(O(m\sqrt{n})\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+5)#define M (int)(5e4+15)#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e6+5)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}struct Edge{    int u,v,w,next;}e[M<<3];int n1,n2,m,pos=1,ans,head[N],now[N],s,t,gap[N],dep[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}queue<int> q;void bfs(){    memset(dep,0x3f,sizeof(dep));    q.push(t);dep[t]=1;gap[1]=1;    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(e[i^1].w>0&&dep[v]==INF)            {                dep[v]=dep[u]+1;                gap[dep[v]]++;                q.push(v);            }        }    }}int dfs(int u,int in){    if(u==t)return in;    int out=0;    for(int i=now[u]; i; i=e[i].next)    {        int v=e[i].v;now[u]=i;        if(e[i].w>0&&dep[u]==dep[v]+1)        {            int res=dfs(v,min(in,e[i].w));            e[i].w-=res;            e[i^1].w+=res;            in-=res;            out+=res;        }        if(!in)return out;    }    if(!--gap[dep[u]])dep[s]=n1+n2+3;    gap[++dep[u]]++;    return out;}signed main(){    read(n1);read(n2);read(m);    s=1;t=n1+n2+2;    for(int i=1,u,v; i<=m; i++)    {        read(u);read(v);        addEdge(u+1,v+n1+1,1);addEdge(v+n1+1,u+1,0);    }    for(int i=1; i<=n1; i++)        addEdge(s,i+1,1),addEdge(i+1,s,0);    for(int i=1; i<=n2; i++)        addEdge(n1+i+1,t,1),addEdge(t,n1+i+1,0);        bfs();    while(dep[s]<=n1+n2+2)        memcpy(now,head,sizeof(head)),        ans+=dfs(s,INF);    write(ans);pc('\n');    return 0;}

        二分图最大权完美匹配

        KM算法

        时间复杂度 \(O(n^3)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(555)int n,m;int g[N][N],slack[N];int lx[N],ly[N],px[N],py[N],pre[N],d;bool vx[N],vy[N];void aug(int v){    int t;    while(v)    {        t=px[pre[v]];        px[pre[v]]=v;        py[v]=pre[v];        v=t;    }}queue<int> q;void bfs(int s){    memset(vx,0,sizeof(vx));    memset(vy,0,sizeof(vy));    for(int i=1; i<=n; i++)        slack[i]=INF;    while(!q.empty())q.pop();    q.push(s);    while(1)    {        while(!q.empty())        {            int u=q.front();q.pop();            vx[u]=1;            for(int i=1; i<=n; i++)            {                if(!vy[i]&&lx[u]+ly[i]-g[u][i]<slack[i])                {                    slack[i]=lx[u]+ly[i]-g[u][i];                    pre[i]=u;                    if(!slack[i])                    {                        vy[i]=1;                        if(!py[i]){aug(i);return;}                        else q.push(py[i]);                    }                }            }        }        int d=INF;        for(int i=1; i<=n; i++)            if(!vy[i])d=min(d,slack[i]);        for(int i=1; i<=n; i++)        {            if(vx[i])lx[i]-=d;            if(vy[i])ly[i]+=d;            else slack[i]-=d;        }        for(int i=1; i<=n; i++)        {            if(!vy[i]&&!slack[i])            {                vy[i]=1;                if(!py[i]){aug(i);return;}                else q.push(py[i]);            }        }    }}int KM(){    int res=0;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            lx[i]=max(lx[i],g[i][j]);    for(int i=1; i<=n; i++)bfs(i);    for(int i=1; i<=n; i++)        res+=g[py[i]][i];    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> m;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            g[i][j]=-INF;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        g[u][v]=max(g[u][v],w);    }    cout << KM() << endl;    for(int i=1; i<=n; i++)        cout << py[i] << " \n"[i==n];    return 0;}

        一般图最大匹配

        带花树算法

        时间复杂度 \(O(n^3)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,m,cnt,vis[N];int mch[N],pre[N],f[N],col[N];vector<int> vec[N];int find(int x){return f[x]==x?x:f[x]=find(f[x]);}queue<int> q;void aug(int v){    int t;    while(v)    {        t=mch[pre[v]];        mch[v]=pre[v];        mch[pre[v]]=v;        v=t;    }}int lca(int u,int v){    ++cnt;    u=find(u);v=find(v);    while(vis[u]!=cnt)    {        vis[u]=cnt;        u=find(pre[mch[u]]);        if(v)swap(u,v);    }    return u;}void shrink(int u,int v,int p){    while(find(u)!=p)    {        pre[u]=v;        v=mch[u];        if(col[v]==2)            col[v]=1,q.push(v);        f[u]=p;f[v]=p;        u=pre[v];    }}bool bfs(int s){    for(int i=1; i<=n; i++)        f[i]=i,pre[i]=col[i]=0;    while(!q.empty())q.pop();    q.push(s);col[s]=1;    while(!q.empty())    {        int u=q.front();q.pop();        for(int v:vec[u])        {            if(!col[v])            {                pre[v]=u;                if(!mch[v]){aug(v);return 1;}                else col[v]=2,col[mch[v]]=1,q.push(mch[v]);            }else if(col[v]==1&&find(u)!=find(v))            {                int p=lca(u,v);                shrink(u,v,p);shrink(v,u,p);            }        }    }    return 0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);    }    int res=0;    for(int i=1; i<=n; i++)        res+=!mch[i]&&bfs(i);    cout << res << endl;    for(int i=1; i<=n; i++)        cout << mch[i] << " \n"[i==n];    return 0;}

        一般图最大权匹配

        不会 qwq 感觉要等到我大学才会去学,咕咕咕...


        无向图的最小环问题

        下面这个代码求的是至少包含 \(3\)个点的环

        要根据题意判断

         #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3fint n,m,ans=INF;int g[105][105],f[105][105];signed main(){    ios::sync_with_stdio(0);    cin >> n >> m;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            g[i][j]=f[i][j]=INF;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        g[u][v]=min(g[u][v],w);        g[v][u]=min(g[v][u],w);        f[u][v]=min(f[u][v],w);        f[v][u]=min(f[v][u],w);    }    for(int k=1; k<=n; k++)    {        for(int i=1; i<k; i++)            for(int j=i+1; j<k; j++)                ans=min(ans,f[i][j]+g[i][k]+g[k][j]);        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);    }    if(ans==INF)cout << "No solution.";    else cout << ans;    return 0;}

        ]]> @@ -2566,7 +2566,7 @@ /2022/07/19/oi-mo-ban-zi-fu-chuan/ - OI模板-字符串

        字符串哈希

        单哈希

        给定 $N$ 个字符串(第 $i$ 个字符串长度为 $M_i$,字符串内包含数字、大小写字母,大小写敏感),请求出 $N$ 个字符串中共有多少个不同的字符串。

        P3370 【模板】字符串哈希

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)#define M (int)(1515)const int hash_base=31;const int hash_mod=998244353;char str[M];int n,cnt=1,t[N];struct hash_str{    int len,hsh[M],pwd[M];    void Hash(int l,char *str)    {        len=l; pwd[0]=1; hsh[0]=0;        for(int i=1; i<=l; i++)        {            pwd[i]=pwd[i-1]*hash_base%hash_mod;            hsh[i]=(hsh[i-1]*hash_base%hash_mod+str[i])%hash_mod;        }    }    int gethash(int l,int r)    {        int res=(hsh[r]-hsh[l-1]*pwd[r-l+1])%hash_mod;        res=(res+hash_mod)%hash_mod;        return res;    }}p;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> (str+1);        p.Hash(strlen(str+1),str);        t[i]=p.gethash(1,p.len);    }    sort(t+1,t+1+n);    for(int i=2; i<=n; i++)        if(t[i]!=t[i-1])++cnt;    cout << cnt << '\n';    return 0;}

        双哈希

        详见CF1200E Compress Words 题解

        给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀 与 待插入串的前缀的最大匹配串

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+5)#define hash_cnt 2const int hash_base[hash_cnt]={29,31};const int hash_mod[hash_cnt]={1000000007,998244353};struct hash_str{    char s[N];    int len,hsh[hash_cnt][N];    int pwd[hash_cnt][N];        void init()    {        len=0;        for(int i=0; i<hash_cnt; i++)        {            hsh[i][0]=0;            pwd[i][0]=1;        }    }    hash_str(){init();}    void add(char ch)    {        s[++len]=ch;        for(int i=0; i<hash_cnt; i++)        {            pwd[i][len]=pwd[i][len-1]*hash_base[i]%hash_mod[i];            hsh[i][len]=(hsh[i][len-1]*hash_base[i]+ch)%hash_mod[i];        }    }    vector<int> gethash(int l,int r)    {        vector<int> res(hash_cnt,0);        for(int i=0; i<hash_cnt; i++)        {            int t=(hsh[i][r]-hsh[i][l-1]*pwd[i][r-l+1])%hash_mod[i];            t=(t+hash_mod[i])%hash_mod[i];            res[i]=t;        }        return res;    }}s,t;bool equal(const vector<int> &vec1,const vector<int> &vec2){    assert(vec1.size()==vec2.size());    for(int i=0; i<vec1.size(); i++)        if(vec1[i]!=vec2[i])return 0;    return 1;}int n;char str[N];void solve(char *str){    int len=strlen(str+1);    t.init();    for(int i=1; i<=len; i++)        t.add(str[i]);    int d=1;    for(int j=min(len,s.len); j>=1; j--)        if(equal(t.gethash(1,j),s.gethash(s.len-j+1,s.len)))            {d=j+1;break;}    for(int i=d; i<=len; i++)        s.add(str[i]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> str+1;        solve(str);    }    cout << s.s+1 << endl;    return 0;}

        KMP

        时间复杂度 $O(n+m)$

        空间复杂度 $O(n+m)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e6+5)int n,m,fail[MAXN];char t[MAXN],s[MAXN];signed main(){scanf("%s\n%s\n",t+1,s+1);n=strlen(t+1);m=strlen(s+1);for(int i=2,j=0; i<=m; i++){while(j&&s[i]!=s[j+1])j=fail[j];if(s[i]==s[j+1])++j;fail[i]=j;}for(int i=1,j=0; i<=n; i++){while(j&&t[i]!=s[j+1])j=fail[j];if(t[i]==s[j+1])++j;if(j==m)printf("%lld\n",i-m+1);}for(int i=1; i<=m; i++)printf("%lld%c",fail[i]," \n"[i==m]);return 0;}

        ExKMP

        时间复杂度 $O(n+m)$

        空间复杂度 $O(n+m)$

        b 与 b的后缀 的最大前缀、b 与 a的后缀 的最大前缀

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e7+5)char a[N],b[N];int z[N],p[N],n,m;int ans1,ans2;void exkmp(){    z[1]=m;    for(int i=2,l=0,r=0; i<=m; i++)    {        if(i<=r)z[i]=min(r-i+1,z[i-l+1]);        while(i+z[i]<=m&&b[i+z[i]]==b[z[i]+1])++z[i];        if(i+z[i]-1>=r)l=i,r=i+z[i]-1;    }    for(int i=1,l=0,r=0; i<=n; i++)    {        if(i<=r)p[i]=min(r-i+1,z[i-l+1]);        while(i+p[i]<=n&&a[i+p[i]]==b[p[i]+1])++p[i];        if(i+p[i]-1>=r)l=i,r=i+p[i]-1;    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> a+1 >> b+1;    n=strlen(a+1);m=strlen(b+1);    exkmp();    int res=0;    for(int i=1; i<=m; i++)res^=i*(z[i]+1);    cout << res << endl;    res=0;    for(int i=1; i<=n; i++)res^=i*(p[i]+1);    cout << res << endl;    return 0;}

        Manacher

        时间复杂度 $O(n)$

        空间复杂度 $O(n)$

        内存105.53MB(比较极限的数据)

        aabaa#a#a#b#a#a## a # a # b # a # a #1 2 3 2 1 6 1 2 3 2 1
        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1.1e7+15)char s[N<<1];int n,p[N<<1];void Manacher(int l){    int r=1,mid=1,ans=0;    for(int i=1; i<=l; i++)    {        p[i]=(i<=r)?min(p[(mid<<1)-i],r-i+1):1;        while(s[i-p[i]]==s[i+p[i]]) ++p[i];        if(i+p[i]-1>r)r=i+p[i]-1,mid=i;        // 这里如果写 i+p[i]-1>=r 则会在r相等时优先更新mid        // 对p数组的求解没有影响,但是在某些题目中会出现问题        ans=max(ans,p[i]-1);    }    cout << ans << '\n';}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (s+1); n=strlen(s+1);    for(int i=n; i; i--)        s[i*2]=s[i],s[i*2+1]='#';    s[0]='$';s[1]='#';    Manacher(n*2+1);    return 0;}

        Trie树

        这个是老版本的(The XOR Largest Pair),太简单了不写一遍了,详见AC自动机即可

        时间复杂度 $O(\max{\{|s_i|\}})$

        空间复杂度 $O(k\sum |s_i|),k$ 为字符集范围

        #include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>void write(T k){if(k<0){putchar('-');k=-k;}if(k>9)write(k/10);putchar(k%10+'0');}int n,ans=0;int a[MAXN];struct Trie{int trie[MAXN*32][3],tot=1; // trie的大小要注意void insert(int x){int p=0;for(int i=31; i>=0; --i) // 其实只要30就可以了{int k=(x>>i)&1;if(!trie[p][k])trie[p][k]=++tot;p=trie[p][k];}}int qry(int x){int ans=0,p=0;for(int i=31; i>=0; --i){int k=(x>>i)&1;if(trie[p][k^1])p=trie[p][k^1],ans|=(1<<i); // 尽可能选择这一位相反的else p=trie[p][k];}return ans;}}t[1]; // 这个[1]并没有什么用 qwqsigned main(){read(n);for(int i=1; i<=n; i++){read(a[i]);t[0].insert(a[i]);ans=max(ans,t[0].qry(a[i]));}write(ans);putchar('\n');return 0;}

        AC自动机

        AC自动机(简单版)

        时间复杂度 $O(\sum |s_i| + |S|)$

        空间复杂度 $O(k\sum |s_i| + |S|)$ , $k$ 为字符集大小

        #include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e6+5)int n;queue<int>q;char tmp[MAXN],t[MAXN];int trie[MAXN][28],e[MAXN],tot,fail[MAXN];void insert(int l,char *s){int p=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[p][c])trie[p][c]=++tot;p=trie[p][c];}++e[p];}void build(){for(int i=0; i<26; i++)if(trie[0][i])q.push(trie[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];q.push(trie[u][i]);}else trie[u][i]=trie[fail[u]][i];}}}int query(int l,char *t){int res=0,u=0;for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];for(int j=u; j&&e[j]!=-1; j=fail[j])res+=e[j],e[j]=-1;}return res;}signed main(){scanf("%lld",&n);for(int i=1; i<=n; i++){scanf("%s",tmp+1);insert(strlen(tmp+1),tmp);}scanf("%s",t+1);build();printf("%lld\n",query(strlen(t+1),t));return 0;}

        AC自动机(加强版)

        最坏时间复杂度 $O(T|S|\max\{|s_i|\})$

        空间复杂度 $O(k\sum|s_i|+|S|)$ , $k$ 为字符集大小

        #include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(155)#define S (int)(75)#define SZ (int)(155*75)#define L (int)(1e6+5)char t[L],s[N][S];int n,cnt[N],e[SZ],val[SZ];int trie[SZ][32],tot,fail[SZ];void init(){memset(fail,0,sizeof(fail));memset(cnt,0,sizeof(cnt));memset(e,0,sizeof(e));memset(trie,0,sizeof(trie));memset(val,0,sizeof(val));tot=0;}void insert(int l,char *s,int id){int u=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[u][c])trie[u][c]=++tot;u=trie[u][c];}e[u]=id;}queue<int>q;void build(){for(int i=0; i<26; i++)if(trie[0][i])q.push(trie[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];q.push(trie[u][i]);}else trie[u][i]=trie[fail[u]][i];}}}int AC(int l,char *t){int u=0,res=0;for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];for(int j=u; j; j=fail[j])++val[j];}for(int i=1; i<=tot; i++) if(e[i]){res=max(res,val[i]);cnt[e[i]]=val[i];}return res;}signed main(){while(scanf("%lld",&n)&&n){init();for(int i=1; i<=n; i++){scanf("%s\n",s[i]+1);insert(strlen(s[i]+1),s[i],i);}scanf("%s\n",t+1);build();int mx=AC(strlen(t+1),t);printf("%lld\n",mx);for(int i=1; i<=n; i++)if(cnt[i]==mx)printf("%s\n",s[i]+1);}return 0;}

        AC自动机(二次加强版)

        时间复杂度 $O\left(\sum|s_i|+|S|\right)$

        空间复杂度 $O\left(\sum|s_i|+|S|\right)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(2e5+5)#define L (int)(2e6+5)char t[L],s[N];int n,ans[N],e[N],val[N],in[N];int trie[N][32],tot,fail[N],f[N];void init(){for(int i=1; i<=n; i++)f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}void insert(int l,char *s,int id){int u=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[u][c])trie[u][c]=++tot;u=trie[u][c];}if(!e[u])e[u]=id;else merge(id,e[u]);}queue<int>q;void build(){for(int i=0; i<26; i++)if(trie[0][i])q.push(trie[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];++in[trie[fail[u]][i]];q.push(trie[u][i]);}else trie[u][i]=trie[fail[u]][i];}}}void AC(int l,char *t){int u=0;for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];++val[u];}for(int i=1; i<=tot; i++)if(!in[i])q.push(i);while(!q.empty()){int u=q.front();q.pop();if(e[u])ans[e[u]]=val[u];val[fail[u]]+=val[u];if(!--in[fail[u]])q.push(fail[u]);}}signed main(){scanf("%lld",&n); init();for(int i=1; i<=n; i++){scanf("%s\n",s+1);insert(strlen(s+1),s,i);}scanf("%s\n",t+1);build();AC(strlen(t+1),t);for(int i=1; i<=n; i++)printf("%lld\n",ans[find(i)]);return 0;}

        后缀数组

        1. 朴素写法 时间复杂度 $O(n\log n)$
        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)char s[N];int n,m,sa[N],rk[N<<1],tmp[N<<1],cnt[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> s+1;    n=strlen(s+1);m=max(n,333ll);    for(int i=1; i<=n; i++)++cnt[rk[i]=s[i]];    for(int i=1; i<=m; i++)cnt[i]+=cnt[i-1];    for(int i=n; i>=1; i--)sa[cnt[rk[i]]--]=i;    auto f = [=](int w)    {        memset(cnt,0,(m+1)*sizeof(int));        for(int i=1; i<=n; i++)tmp[i]=sa[i];        for(int i=1; i<=n; i++)++cnt[rk[tmp[i]+w]];        for(int i=1; i<=m; i++)cnt[i]+=cnt[i-1];        for(int i=n; i>=1; i--)sa[cnt[rk[tmp[i]+w]]--]=tmp[i];    };    for(int w=1; w<n; w<<=1)    {        f(w);f(0);        for(int i=1; i<=n; i++)tmp[i]=rk[i];        for(int i=1,p=0; i<=n; i++)        {            if(tmp[sa[i]]==tmp[sa[i-1]]&&            tmp[sa[i]+w]==tmp[sa[i-1]+w])            {                rk[sa[i]]=p;            }else rk[sa[i]]=++p;        }    }    for(int i=1; i<=n; i++)        cout << sa[i] << " \n"[i==n];    return 0;}

        后缀自动机 SAM

        时间复杂度 $O(n)$

        空间复杂度 $O(n)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e6+15)struct node{    signed ch[26],len,fa;}t[N];struct Edge{    int u,v,next;}e[N];string s;int ans,dep[N],head[N],pos=1,tot=1,lst=1;void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void update(int c){    int p=lst,now=lst=++tot;    dep[tot]=1;    t[now].len=t[p].len+1;    for(; p&&!t[p].ch[c]; p=t[p].fa)        t[p].ch[c]=now;    if(!p)t[now].fa=1;    else    {        int q=t[p].ch[c];        if(t[q].len==t[p].len+1)            t[now].fa=q;        else        {            int clone=++tot;            t[clone]=t[q];            t[clone].len=t[p].len+1;            t[q].fa=t[now].fa=clone;            for(; p&&t[p].ch[c]==q; p=t[p].fa)                t[p].ch[c]=clone;        }    }}void dfs(int u){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        dfs(v);dep[u]+=dep[v];    }    if(dep[u]!=1)ans=max(ans,dep[u]*t[u].len);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> s;    for(int i=0; i<s.size(); i++)        update(s[i]-'a');    for(int i=2; i<=tot; i++)        addEdge(t[i].fa,i);    dfs(1);    cout << ans << endl;    return 0;}

        广义后缀自动机 ExSAM

        之前看到巨佬的一个hack,还不确定我这个对不对

        这里是讨论 link

        时间复杂度 $O(n|\Sigma|)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e6+15)#define M (int)(1e6+15)struct trie_node{    signed ch[26],fa,val;}tr[M];struct SAM_node{    signed ch[26],len,fa;}sam[N];string s;int n,cnt=1,tot=1,lst=1,pos[N];void insert(string s){    int p=1;    for(int i=0; i<s.size(); i++)    {        int c=s[i]-'a';        if(!tr[p].ch[c])        {            tr[p].ch[c]=++cnt;            tr[cnt].fa=p;            tr[cnt].val=c;        }        p=tr[p].ch[c];    }}int update(int c,int lst){    int p=lst,now=lst=++tot;    sam[now].len=sam[p].len+1;    for(; p&&!sam[p].ch[c]; p=sam[p].fa)        sam[p].ch[c]=now;    if(!p)sam[now].fa=1;    else    {        int q=sam[p].ch[c];        if(sam[q].len==sam[p].len+1)            sam[now].fa=q;        else        {            int clone=++tot;            sam[clone]=sam[q];            sam[clone].len=sam[p].len+1;            sam[q].fa=sam[now].fa=clone;            for(; p&&sam[p].ch[c]==q; p=sam[p].fa)                sam[p].ch[c]=clone;        }    }    return now;}queue<int> q;void build(){    for(int i=0; i<26; i++)        if(tr[1].ch[i])q.push(tr[1].ch[i]);    pos[1]=1;    while(!q.empty())    {        int u=q.front();q.pop();        pos[u]=update(tr[u].val,pos[tr[u].fa]);        for(int i=0; i<26; i++)            if(tr[u].ch[i])q.push(tr[u].ch[i]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)         cin >> s,insert(s);    build();    int res=0;    for(int i=2; i<=tot; i++)        res+=sam[i].len-sam[sam[i].fa].len;    cout << res << endl;    return 0;}

        回文自动机 PAM

        时间复杂度 $O(n)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)char s[N];signed ch[N][26];int n,ans,fail[N],cnt[N],len[N],tot=1,last,lstans;int New(int x){    len[++tot]=x;    return tot;}int getfail(int x,int n){    while(s[n-len[x]-1]!=s[n])        x=fail[x];    return x;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> s+1;    fail[0]=1;len[1]=-1;    for(int i=1,p,q; s[i]; i++)    {        if(i>1)s[i]=(s[i]+lstans-97)%26+97;        int c=s[i]-'a';        p=getfail(last,i);        if(!ch[p][c])        {            q=New(len[p]+2);            fail[q]=ch[getfail(fail[p],i)][c];            cnt[tot]=cnt[fail[tot]]+1;            ch[p][c]=q;        }        last=ch[p][c];        lstans=cnt[last];        cout << lstans << " ";    }    return 0;}

        子序列自动机

        时间复杂度 $O(n\log n)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define SIZ (int)(1e5+15)#define gc() getchar()#define pc(a) putchar(a)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)vector<int>pos[N];int n,Q,_,m,a[N],b[N],lstpos,ok;signed main(){    read(_);read(n);read(Q);read(_);    for(int i=1; i<=n; i++)    {        read(a[i]);        pos[a[i]].push_back(i);    }    while(Q--)    {        read(m);ok=1;lstpos=0;        for(int i=1; i<=m; i++)            read(b[i]);        for(int i=1; i<=m; i++)        {            auto p=upper_bound(pos[b[i]].begin(),            pos[b[i]].end(),lstpos);            if(p==pos[b[i]].end()){ok=0;break;}            lstpos=*p;        }        puts(ok?"Yes":"No");    }    return 0;}
        ]]> + OI模板-字符串

        字符串哈希

        单哈希

        给定 \(N\) 个字符串(第 \(i\) 个字符串长度为 \(M_i\),字符串内包含数字、大小写字母,大小写敏感),请求出\(N\)个字符串中共有多少个不同的字符串。

        P3370【模板】字符串哈希

        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)#define M (int)(1515)const int hash_base=31;const int hash_mod=998244353;char str[M];int n,cnt=1,t[N];struct hash_str{    int len,hsh[M],pwd[M];    void Hash(int l,char *str)    {        len=l; pwd[0]=1; hsh[0]=0;        for(int i=1; i<=l; i++)        {            pwd[i]=pwd[i-1]*hash_base%hash_mod;            hsh[i]=(hsh[i-1]*hash_base%hash_mod+str[i])%hash_mod;        }    }    int gethash(int l,int r)    {        int res=(hsh[r]-hsh[l-1]*pwd[r-l+1])%hash_mod;        res=(res+hash_mod)%hash_mod;        return res;    }}p;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> (str+1);        p.Hash(strlen(str+1),str);        t[i]=p.gethash(1,p.len);    }    sort(t+1,t+1+n);    for(int i=2; i<=n; i++)        if(t[i]!=t[i-1])++cnt;    cout << cnt << '\n';    return 0;}

        双哈希

        详见CF1200ECompress Words 题解

        给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀 与待插入串的前缀的最大匹配串

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+5)#define hash_cnt 2const int hash_base[hash_cnt]={29,31};const int hash_mod[hash_cnt]={1000000007,998244353};struct hash_str{    char s[N];    int len,hsh[hash_cnt][N];    int pwd[hash_cnt][N];        void init()    {        len=0;        for(int i=0; i<hash_cnt; i++)        {            hsh[i][0]=0;            pwd[i][0]=1;        }    }    hash_str(){init();}    void add(char ch)    {        s[++len]=ch;        for(int i=0; i<hash_cnt; i++)        {            pwd[i][len]=pwd[i][len-1]*hash_base[i]%hash_mod[i];            hsh[i][len]=(hsh[i][len-1]*hash_base[i]+ch)%hash_mod[i];        }    }    vector<int> gethash(int l,int r)    {        vector<int> res(hash_cnt,0);        for(int i=0; i<hash_cnt; i++)        {            int t=(hsh[i][r]-hsh[i][l-1]*pwd[i][r-l+1])%hash_mod[i];            t=(t+hash_mod[i])%hash_mod[i];            res[i]=t;        }        return res;    }}s,t;bool equal(const vector<int> &vec1,const vector<int> &vec2){    assert(vec1.size()==vec2.size());    for(int i=0; i<vec1.size(); i++)        if(vec1[i]!=vec2[i])return 0;    return 1;}int n;char str[N];void solve(char *str){    int len=strlen(str+1);    t.init();    for(int i=1; i<=len; i++)        t.add(str[i]);    int d=1;    for(int j=min(len,s.len); j>=1; j--)        if(equal(t.gethash(1,j),s.gethash(s.len-j+1,s.len)))            {d=j+1;break;}    for(int i=d; i<=len; i++)        s.add(str[i]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> str+1;        solve(str);    }    cout << s.s+1 << endl;    return 0;}

        KMP

        时间复杂度 \(O(n+m)\)

        空间复杂度 \(O(n+m)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e6+5)int n,m,fail[MAXN];char t[MAXN],s[MAXN];signed main(){scanf("%s\n%s\n",t+1,s+1);n=strlen(t+1);m=strlen(s+1);for(int i=2,j=0; i<=m; i++){while(j&&s[i]!=s[j+1])j=fail[j];if(s[i]==s[j+1])++j;fail[i]=j;}for(int i=1,j=0; i<=n; i++){while(j&&t[i]!=s[j+1])j=fail[j];if(t[i]==s[j+1])++j;if(j==m)printf("%lld\n",i-m+1);}for(int i=1; i<=m; i++)printf("%lld%c",fail[i]," \n"[i==m]);return 0;}

        ExKMP

        时间复杂度 \(O(n+m)\)

        空间复杂度 \(O(n+m)\)

        b 与 b的后缀 的最大前缀、b 与 a的后缀 的最大前缀

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e7+5)char a[N],b[N];int z[N],p[N],n,m;int ans1,ans2;void exkmp(){    z[1]=m;    for(int i=2,l=0,r=0; i<=m; i++)    {        if(i<=r)z[i]=min(r-i+1,z[i-l+1]);        while(i+z[i]<=m&&b[i+z[i]]==b[z[i]+1])++z[i];        if(i+z[i]-1>=r)l=i,r=i+z[i]-1;    }    for(int i=1,l=0,r=0; i<=n; i++)    {        if(i<=r)p[i]=min(r-i+1,z[i-l+1]);        while(i+p[i]<=n&&a[i+p[i]]==b[p[i]+1])++p[i];        if(i+p[i]-1>=r)l=i,r=i+p[i]-1;    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> a+1 >> b+1;    n=strlen(a+1);m=strlen(b+1);    exkmp();    int res=0;    for(int i=1; i<=m; i++)res^=i*(z[i]+1);    cout << res << endl;    res=0;    for(int i=1; i<=n; i++)res^=i*(p[i]+1);    cout << res << endl;    return 0;}

        Manacher

        时间复杂度 \(O(n)\)

        空间复杂度 \(O(n)\)

        内存105.53MB(比较极限的数据)

        aabaa#a#a#b#a#a## a # a # b # a # a #1 2 3 2 1 6 1 2 3 2 1
        #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1.1e7+15)char s[N<<1];int n,p[N<<1];void Manacher(int l){    int r=1,mid=1,ans=0;    for(int i=1; i<=l; i++)    {        p[i]=(i<=r)?min(p[(mid<<1)-i],r-i+1):1;        while(s[i-p[i]]==s[i+p[i]]) ++p[i];        if(i+p[i]-1>r)r=i+p[i]-1,mid=i;        // 这里如果写 i+p[i]-1>=r 则会在r相等时优先更新mid        // 对p数组的求解没有影响,但是在某些题目中会出现问题        ans=max(ans,p[i]-1);    }    cout << ans << '\n';}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (s+1); n=strlen(s+1);    for(int i=n; i; i--)        s[i*2]=s[i],s[i*2+1]='#';    s[0]='$';s[1]='#';    Manacher(n*2+1);    return 0;}

        Trie树

        这个是老版本的(The XOR LargestPair),太简单了不写一遍了,详见AC自动机即可

        时间复杂度 \(O(\max{\{|s_i|\}})\)

        空间复杂度 \(O(k\sum |s_i|),k\)为字符集范围

        #include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>void write(T k){if(k<0){putchar('-');k=-k;}if(k>9)write(k/10);putchar(k%10+'0');}int n,ans=0;int a[MAXN];struct Trie{int trie[MAXN*32][3],tot=1; // trie的大小要注意void insert(int x){int p=0;for(int i=31; i>=0; --i) // 其实只要30就可以了{int k=(x>>i)&1;if(!trie[p][k])trie[p][k]=++tot;p=trie[p][k];}}int qry(int x){int ans=0,p=0;for(int i=31; i>=0; --i){int k=(x>>i)&1;if(trie[p][k^1])p=trie[p][k^1],ans|=(1<<i); // 尽可能选择这一位相反的else p=trie[p][k];}return ans;}}t[1]; // 这个[1]并没有什么用 qwqsigned main(){read(n);for(int i=1; i<=n; i++){read(a[i]);t[0].insert(a[i]);ans=max(ans,t[0].qry(a[i]));}write(ans);putchar('\n');return 0;}

        AC自动机

        AC自动机(简单版)

        时间复杂度 \(O(\sum |s_i| +|S|)\)

        空间复杂度 \(O(k\sum |s_i| + |S|)\)\(k\) 为字符集大小

        #include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e6+5)int n;queue<int>q;char tmp[MAXN],t[MAXN];int trie[MAXN][28],e[MAXN],tot,fail[MAXN];void insert(int l,char *s){int p=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[p][c])trie[p][c]=++tot;p=trie[p][c];}++e[p];}void build(){for(int i=0; i<26; i++)if(trie[0][i])q.push(trie[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];q.push(trie[u][i]);}else trie[u][i]=trie[fail[u]][i];}}}int query(int l,char *t){int res=0,u=0;for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];for(int j=u; j&&e[j]!=-1; j=fail[j])res+=e[j],e[j]=-1;}return res;}signed main(){scanf("%lld",&n);for(int i=1; i<=n; i++){scanf("%s",tmp+1);insert(strlen(tmp+1),tmp);}scanf("%s",t+1);build();printf("%lld\n",query(strlen(t+1),t));return 0;}

        AC自动机(加强版)

        最坏时间复杂度 \(O(T|S|\max\{|s_i|\})\)

        空间复杂度 \(O(k\sum|s_i|+|S|)\)\(k\) 为字符集大小

        #include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(155)#define S (int)(75)#define SZ (int)(155*75)#define L (int)(1e6+5)char t[L],s[N][S];int n,cnt[N],e[SZ],val[SZ];int trie[SZ][32],tot,fail[SZ];void init(){memset(fail,0,sizeof(fail));memset(cnt,0,sizeof(cnt));memset(e,0,sizeof(e));memset(trie,0,sizeof(trie));memset(val,0,sizeof(val));tot=0;}void insert(int l,char *s,int id){int u=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[u][c])trie[u][c]=++tot;u=trie[u][c];}e[u]=id;}queue<int>q;void build(){for(int i=0; i<26; i++)if(trie[0][i])q.push(trie[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];q.push(trie[u][i]);}else trie[u][i]=trie[fail[u]][i];}}}int AC(int l,char *t){int u=0,res=0;for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];for(int j=u; j; j=fail[j])++val[j];}for(int i=1; i<=tot; i++) if(e[i]){res=max(res,val[i]);cnt[e[i]]=val[i];}return res;}signed main(){while(scanf("%lld",&n)&&n){init();for(int i=1; i<=n; i++){scanf("%s\n",s[i]+1);insert(strlen(s[i]+1),s[i],i);}scanf("%s\n",t+1);build();int mx=AC(strlen(t+1),t);printf("%lld\n",mx);for(int i=1; i<=n; i++)if(cnt[i]==mx)printf("%s\n",s[i]+1);}return 0;}

        AC自动机(二次加强版)

        时间复杂度 \(O\left(\sum|s_i|+|S|\right)\)

        空间复杂度 \(O\left(\sum|s_i|+|S|\right)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(2e5+5)#define L (int)(2e6+5)char t[L],s[N];int n,ans[N],e[N],val[N],in[N];int trie[N][32],tot,fail[N],f[N];void init(){for(int i=1; i<=n; i++)f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}void insert(int l,char *s,int id){int u=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[u][c])trie[u][c]=++tot;u=trie[u][c];}if(!e[u])e[u]=id;else merge(id,e[u]);}queue<int>q;void build(){for(int i=0; i<26; i++)if(trie[0][i])q.push(trie[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];++in[trie[fail[u]][i]];q.push(trie[u][i]);}else trie[u][i]=trie[fail[u]][i];}}}void AC(int l,char *t){int u=0;for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];++val[u];}for(int i=1; i<=tot; i++)if(!in[i])q.push(i);while(!q.empty()){int u=q.front();q.pop();if(e[u])ans[e[u]]=val[u];val[fail[u]]+=val[u];if(!--in[fail[u]])q.push(fail[u]);}}signed main(){scanf("%lld",&n); init();for(int i=1; i<=n; i++){scanf("%s\n",s+1);insert(strlen(s+1),s,i);}scanf("%s\n",t+1);build();AC(strlen(t+1),t);for(int i=1; i<=n; i++)printf("%lld\n",ans[find(i)]);return 0;}

        后缀数组

        1. 朴素写法 时间复杂度 \(O(n\logn)\)
        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)char s[N];int n,m,sa[N],rk[N<<1],tmp[N<<1],cnt[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> s+1;    n=strlen(s+1);m=max(n,333ll);    for(int i=1; i<=n; i++)++cnt[rk[i]=s[i]];    for(int i=1; i<=m; i++)cnt[i]+=cnt[i-1];    for(int i=n; i>=1; i--)sa[cnt[rk[i]]--]=i;    auto f = [=](int w)    {        memset(cnt,0,(m+1)*sizeof(int));        for(int i=1; i<=n; i++)tmp[i]=sa[i];        for(int i=1; i<=n; i++)++cnt[rk[tmp[i]+w]];        for(int i=1; i<=m; i++)cnt[i]+=cnt[i-1];        for(int i=n; i>=1; i--)sa[cnt[rk[tmp[i]+w]]--]=tmp[i];    };    for(int w=1; w<n; w<<=1)    {        f(w);f(0);        for(int i=1; i<=n; i++)tmp[i]=rk[i];        for(int i=1,p=0; i<=n; i++)        {            if(tmp[sa[i]]==tmp[sa[i-1]]&&            tmp[sa[i]+w]==tmp[sa[i-1]+w])            {                rk[sa[i]]=p;            }else rk[sa[i]]=++p;        }    }    for(int i=1; i<=n; i++)        cout << sa[i] << " \n"[i==n];    return 0;}

        后缀自动机 SAM

        时间复杂度 \(O(n)\)

        空间复杂度 \(O(n)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e6+15)struct node{    signed ch[26],len,fa;}t[N];struct Edge{    int u,v,next;}e[N];string s;int ans,dep[N],head[N],pos=1,tot=1,lst=1;void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void update(int c){    int p=lst,now=lst=++tot;    dep[tot]=1;    t[now].len=t[p].len+1;    for(; p&&!t[p].ch[c]; p=t[p].fa)        t[p].ch[c]=now;    if(!p)t[now].fa=1;    else    {        int q=t[p].ch[c];        if(t[q].len==t[p].len+1)            t[now].fa=q;        else        {            int clone=++tot;            t[clone]=t[q];            t[clone].len=t[p].len+1;            t[q].fa=t[now].fa=clone;            for(; p&&t[p].ch[c]==q; p=t[p].fa)                t[p].ch[c]=clone;        }    }}void dfs(int u){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        dfs(v);dep[u]+=dep[v];    }    if(dep[u]!=1)ans=max(ans,dep[u]*t[u].len);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> s;    for(int i=0; i<s.size(); i++)        update(s[i]-'a');    for(int i=2; i<=tot; i++)        addEdge(t[i].fa,i);    dfs(1);    cout << ans << endl;    return 0;}

        广义后缀自动机 ExSAM

        之前看到巨佬的一个hack,还不确定我这个对不对

        这里是讨论 link

        时间复杂度 \(O(n|\Sigma|)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e6+15)#define M (int)(1e6+15)struct trie_node{    signed ch[26],fa,val;}tr[M];struct SAM_node{    signed ch[26],len,fa;}sam[N];string s;int n,cnt=1,tot=1,lst=1,pos[N];void insert(string s){    int p=1;    for(int i=0; i<s.size(); i++)    {        int c=s[i]-'a';        if(!tr[p].ch[c])        {            tr[p].ch[c]=++cnt;            tr[cnt].fa=p;            tr[cnt].val=c;        }        p=tr[p].ch[c];    }}int update(int c,int lst){    int p=lst,now=lst=++tot;    sam[now].len=sam[p].len+1;    for(; p&&!sam[p].ch[c]; p=sam[p].fa)        sam[p].ch[c]=now;    if(!p)sam[now].fa=1;    else    {        int q=sam[p].ch[c];        if(sam[q].len==sam[p].len+1)            sam[now].fa=q;        else        {            int clone=++tot;            sam[clone]=sam[q];            sam[clone].len=sam[p].len+1;            sam[q].fa=sam[now].fa=clone;            for(; p&&sam[p].ch[c]==q; p=sam[p].fa)                sam[p].ch[c]=clone;        }    }    return now;}queue<int> q;void build(){    for(int i=0; i<26; i++)        if(tr[1].ch[i])q.push(tr[1].ch[i]);    pos[1]=1;    while(!q.empty())    {        int u=q.front();q.pop();        pos[u]=update(tr[u].val,pos[tr[u].fa]);        for(int i=0; i<26; i++)            if(tr[u].ch[i])q.push(tr[u].ch[i]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)         cin >> s,insert(s);    build();    int res=0;    for(int i=2; i<=tot; i++)        res+=sam[i].len-sam[sam[i].fa].len;    cout << res << endl;    return 0;}

        回文自动机 PAM

        时间复杂度 \(O(n)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)char s[N];signed ch[N][26];int n,ans,fail[N],cnt[N],len[N],tot=1,last,lstans;int New(int x){    len[++tot]=x;    return tot;}int getfail(int x,int n){    while(s[n-len[x]-1]!=s[n])        x=fail[x];    return x;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> s+1;    fail[0]=1;len[1]=-1;    for(int i=1,p,q; s[i]; i++)    {        if(i>1)s[i]=(s[i]+lstans-97)%26+97;        int c=s[i]-'a';        p=getfail(last,i);        if(!ch[p][c])        {            q=New(len[p]+2);            fail[q]=ch[getfail(fail[p],i)][c];            cnt[tot]=cnt[fail[tot]]+1;            ch[p][c]=q;        }        last=ch[p][c];        lstans=cnt[last];        cout << lstans << " ";    }    return 0;}

        子序列自动机

        时间复杂度 \(O(n\log n)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define SIZ (int)(1e5+15)#define gc() getchar()#define pc(a) putchar(a)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)vector<int>pos[N];int n,Q,_,m,a[N],b[N],lstpos,ok;signed main(){    read(_);read(n);read(Q);read(_);    for(int i=1; i<=n; i++)    {        read(a[i]);        pos[a[i]].push_back(i);    }    while(Q--)    {        read(m);ok=1;lstpos=0;        for(int i=1; i<=m; i++)            read(b[i]);        for(int i=1; i<=m; i++)        {            auto p=upper_bound(pos[b[i]].begin(),            pos[b[i]].end(),lstpos);            if(p==pos[b[i]].end()){ok=0;break;}            lstpos=*p;        }        puts(ok?"Yes":"No");    }    return 0;}
        ]]> @@ -2591,7 +2591,7 @@ /2022/07/19/oi-mo-ban-suan-fa/ - OI模板-算法

        排序算法

        其他乱七八糟的毛用没有(归并、松氏基排除外,还没补上来,咕咕咕…)

        基本上一个sort全部搞定

        计数排序

        时间复杂度 $O(n)$

        空间复杂度 $O(\max\{n,\max\limits_{0<i\le n}a_i\})$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,w,a[N],b[N],cnt[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i],++cnt[a[i]],w=max(w,a[i]);    for(int i=1; i<=w; i++)cnt[i]+=cnt[i-1];    for(int i=n; i>=1; i--)        b[cnt[a[i]]--]=a[i];    /*    逆序    for(int i=n; i>=1; i--)        b[n-cnt[a[i]]+1]=a[i],--cnt[a[i]];    */    for(int i=1; i<=n; i++)        cout << b[i] << " \n"[i==n];    return 0;}

        基数排序

        时间复杂度 $O(n)$

        空间复杂度 $O(n)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,w,a[N],cnt[N];void rdsort(int n,int *a){    int *b=new int[n+5];    int *cnt=new int[1<<8];    int mask=(1<<8)-1;    int *x=a,*y=b;    for(int i=0; i<64; i+=8)    {        for(int j=0; j!=(1<<8); j++)cnt[j]=0;        for(int j=1; j<=n; j++)++cnt[x[j]>>i&mask];        for(int j=1; j!=(1<<8); j++)cnt[j]+=cnt[j-1];        for(int j=n; j>=1; j--)y[cnt[x[j]>>i&mask]--]=x[j];        swap(x,y);    }    delete[] b;    delete[] cnt;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    rdsort(n,a);    for(int i=1; i<=n; i++)        cout << a[i] << " \n"[i==n];    return 0;}

        CDQ分治

        P3810 【模板】三维偏序(陌上花开)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)#define K (int)(2e5+15)int n,k,pos;struct node{    int x,y,z,w,ans;    bool operator!=(node &o)const        {return x!=o.x||y!=o.y||z!=o.z;}}a[N],b[N],tmp[N];int tree[K],cnt[N];bool cmpx(node a,node b){    if(a.x==b.x)    {        if(a.y==b.y)            return a.z<b.z;        return a.y<b.y;    }    return a.x<b.x;}bool cmpy(node a,node b){    if(a.y==b.y)        return a.z<b.z;    return a.y<b.y;}#define lowbit(x) (x&(-x))void add(int x,int v){    while(x&&x<=k)    {        tree[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=tree[x];        x-=lowbit(x);    }    return res;}void cdq(int l,int r){    if(l==r)return;    int mid=(l+r)>>1;    cdq(l,mid);cdq(mid+1,r);    int i=mid+1,j=l,res=0,cnt=0;    for(;i<=r; i++)    {        while(a[j].y<=a[i].y&&j<=mid)        {            add(a[j].z,a[j].w);            tmp[++cnt]=a[j++];        }        a[i].ans+=sum(a[i].z);        tmp[++cnt]=a[i];    }    for(int i=l; i<j; i++)        add(a[i].z,-a[i].w);    while(j<=mid)tmp[++cnt]=a[j++];    for(int i=1; i<=cnt; i++)        a[l+i-1]=tmp[i];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> k;    for(int i=1; i<=n; i++)        cin >> b[i].x >> b[i].y >> b[i].z;    sort(b+1,b+1+n,cmpx);    int c=0;    for(int i=1; i<=n; i++)    {        ++c;        if(b[i]!=b[i+1])            a[++pos]=b[i],a[pos].w=c,c=0;    }    cdq(1,pos);    for(int i=1; i<=pos; i++)        cnt[a[i].ans+a[i].w-1]+=a[i].w;    for(int i=0; i<n; i++)        cout << cnt[i] << endl;    return 0;}

        LCA

        LCA 倍增

        时间复杂度 $O(\log n)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e5+5)struct Edge{int u,v,next;}e[MAXN<<1];int n,m,st;int lg[MAXN],fa[MAXN][22],dep[MAXN];int head[MAXN],pos=1;template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}void add(R int u,R int v){e[pos]={u,v,head[u]};head[u]=pos++;}void dfs(R int u,R int f){fa[u][0]=f;dep[u]=dep[f]+1;for(R int i=1; i<=lg[dep[u]]; i++)fa[u][i]=fa[fa[u][i-1]][i-1];for(R int i=head[u]; i; i=e[i].next){R int v=e[i].v;if(v==f)continue;dfs(v,u);}}int LCA(R int x,R int y){if(dep[x]<dep[y])swap(x,y);while(dep[x]>dep[y])x=fa[x][lg[dep[x]-dep[y]]-1];if(x==y)return x;for(R int k=lg[dep[x]]-1; k>=0; k--){if(fa[x][k]!=fa[y][k])x=fa[x][k],y=fa[y][k];}return fa[x][0];}signed main(){read(n);read(m);read(st);for(R int i=1,u,v; i<n; i++){read(u);read(v);add(u,v);add(v,u);}for(R int i=1; i<=n; i++)lg[i]=lg[i-1]+(1<<lg[i-1]==i);dfs(st,0);while(m--){R int x,y;read(x);read(y);printf("%lld\n",LCA(x,y));}return 0;}

        LCA 树链剖分

        时间复杂度 $O(\log n)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(5e5+25)struct Edge{int u,v,next;}e[N<<1];int pos=1,n,Q,rt,p,head[N];int id[N],idx,fa[N],son[N];int sz[N],top[N],dep[N];void addEdge(int u,int v){e[pos]={u,v,head[u]};head[u]=pos++;}void dfs(int u,int f,int d){fa[u]=f;sz[u]=1;dep[u]=d;int mx=-1;for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==f)continue;dfs(v,u,d+1);sz[u]+=sz[v];if(sz[v]>mx)mx=sz[v],son[u]=v;}}void dfs(int u,int ftop){/id[u]=++idx;top[u]=ftop;if(!son[u])return;dfs(son[u],ftop);for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==fa[u]||v==son[u])continue;dfs(v,v);}}int lca(int x,int y){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);x=fa[top[x]];}return dep[x]<dep[y]?x:y;}signed main(){read(n);read(Q);read(rt);for(int i=1,u,v; i<n; i++){read(u);read(v);addEdge(u,v);addEdge(v,u);}dfs(rt,0,1);dfs(rt,rt);while(Q--){int u,v;read(u);read(v);write(lca(u,v));pc('\n');}return 0;}

        高精度加减乘除

        #include<bits/stdc++.h>using namespace std;#define int long long#define R registerstring s1,s2;int compare(string str1,string str2){    if(str1.size()==str2.size())return str1.compare(str2);    return str1.size()<str2.size()?-1:1;}string add(string str1,string str2){    int len1=str1.size();    int len2=str2.size();    if(len1<len2)    {        for(R int i=1; i<=len2-len1; i++)            str1='0'+str1;    }else for(R int i=1; i<=len1-len2; i++)            str2='0'+str2;    string str;    int len=str1.size();    int jw=0;    for(R int i=len-1; i>=0; i--)    {        int tmp=str1[i]-'0'+str2[i]-'0'+jw;        jw=tmp/10;        tmp%=10;        str=char(tmp+'0')+str;    }if(jw)str=char(jw+'0')+str;    str.erase(0,str.find_first_not_of('0'));    if(str.empty())str="0";    return str;}string sub(string str1,string str2){    string str;    int len1=str1.size();    int len2=str2.size();    if(len1>len2)    {        for(R int i=1; i<=len1-len2; i++)            str2='0'+str2;    }    int len=str1.size();    int jw=0;    for(R int i=len-1; i>=0; i--)    {        if(str1[i]<str2[i]+jw)        {            str=char(str1[i]-str2[i]-jw+10+'0')+str;            jw=1;        }else        {            str=char(str1[i]-str2[i]-jw+'0')+str;            jw=0;        }    }    str.erase(0,str.find_first_not_of('0'));    if(str.empty())str="0";    return str;}string mul(string str1,string str2){    int len1=str1.size();    int len2=str2.size();    if(str1=="0"||str2=="0")return "0";    string tmpstr;    string str;    for(R int i=len1-1; i>=0; i--)    {        tmpstr="";        int idx=str1[i]-'0';        int jw=0;        if(idx)        {            for(R int j=1; j<=len1-1-i; j++)                tmpstr+='0';            for(R int j=len2-1; j>=0; j--)            {                int tmp=idx*(str2[j]-'0')+jw;                jw=tmp/10;                tmp%=10;                tmpstr=char(tmp+'0')+tmpstr;            }if(jw)tmpstr=char(jw+'0')+tmpstr;        }        str=add(str,tmpstr);    }    str.erase(0,str.find_first_not_of('0'));    if(str.empty())str="0";    return str;}string div(string str1,string str2){    string ans;    ans="";    if(str1=="0")return ans="0";    int check=compare(str1,str2);    if(check==-1)return ans="0";    if(check==0)return ans="1";    int len1=str1.size();    int len2=str2.size();    string tmpstr;    tmpstr.append(str1,0,len2-1);    for(R int i=len2-1; i<len1; i++)    {        tmpstr+=str1[i];        tmpstr.erase(0,tmpstr.find_first_not_of('0'));        if(tmpstr.empty())tmpstr="0";        for(string str="9";str[0]>='0';str[0]--)        {            string tmp=mul(str,str2);            if(compare(tmp,tmpstr)<=0)            {                ans+=str[0];                tmpstr=sub(tmpstr,tmp);                break;            }        }    }    ans.erase(0,ans.find_first_not_of('0'));    return ans;}signed main(){    // ...    return 0;}

        高精度封装版

        网上搜的。有空搞吧。忘记出处了,很久前复制的

        #include <bits/stdc++.h>using namespace std;const int maxn=5009;struct bignum{int num[maxn],len;bignum(){memset(num,0,sizeof(num));}};bool comp(bignum a,bignum b)//比较函数{if(a.len!=b.len)return (a.len>b.len)?1:0;for(int i=a.len-1;i>=0;i--)if(a.num[i]!=b.num[i])return (a.num[i]>b.num[i])?1:0;return -1;//相等}bignum operator / (bignum a,int b)//高精除以低精度{bignum c=a;int tw=0,len=a.len;for(int i=len-1;i>=0;i--){int k=(tw*10+c.num[i])/b;tw=(tw*10+c.num[i])%b;c.num[i]=k;}while(c.num[len-1]==0)len--;c.len=len;return c;}bignum init(bignum &a,string s){int len=s.length();if(s[0]=='-')//处理负数len--,a.num[maxn-1]=-1;//这样len-i-1就不会到0去,且留下是负数的标记for(int i=0;i<len;i++)a.num[i]=s[len-i-1]-'0';a.len=len;return a;}bignum Add(bignum &a,bignum &b){bignum c;int len=max(a.len,b.len);for(int i=0;i<len;i++){c.num[i]+=(a.num[i]+b.num[i]);c.num[i+1]+=c.num[i]/10;c.num[i]%=10;}if(c.num[len])len++;//加法最多进1位c.len=len;return c;}bignum dmull(bignum &a,int s)//高精乘低精{for(int i=0;i<a.len;i++)a.num[i]*=s;for(int i=0;i<a.len;i++){if(a.num[i]>=10){if(i==a.len-1)a.len++;a.num[i+1]+=a.num[i]/10;a.num[i]%=10;}}return a;}bignum Hmull(bignum &a,bignum &b)//高精乘高精{bignum c;for(int i=0;i<a.len;i++)for(int j=0;j<b.len;j++){c.num[i+j]+=(a.num[i]*b.num[j]);c.num[i+j+1]+=c.num[i+j]/10;c.num[i+j]%=10;}int len=a.len+b.len;while(c.num[len-1]==0&&len>1)len--;c.len=len;return c;}bignum ddiv(bignum a,bignum b,bignum &c,int &f)//低精度除法{//c是除的结果,f是余数for(int i-a.len-1;i>=0;i--){f=f*10+a.num[i];//慢慢做求余c.num[i]=f/b;f%=b;}while(len>1&&c.num[len-1]==0)len--;c.len=len;}void print(bignum &a){for(int i=a.len-1;i>=0;i--)cout<<a.num[i];}int main(){int n;cin>>n;bignum temp,ans;//下面是求解S=1!+2!+3!....+n!init(ans,"1");init(temp,"1");for(int i=2;i<=n;i++){temp=dmull(temp,i);ans=Add(ans,temp);}print(ans);}

        ]]> + OI模板-算法

        排序算法

        其他乱七八糟的毛用没有(归并、松氏基排除外,还没补上来,咕咕咕...)

        基本上一个sort全部搞定

        计数排序

        时间复杂度 \(O(n)\)

        空间复杂度 \(O(\max\{n,\max\limits_{0<i\len}a_i\})\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,w,a[N],b[N],cnt[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i],++cnt[a[i]],w=max(w,a[i]);    for(int i=1; i<=w; i++)cnt[i]+=cnt[i-1];    for(int i=n; i>=1; i--)        b[cnt[a[i]]--]=a[i];    /*    逆序    for(int i=n; i>=1; i--)        b[n-cnt[a[i]]+1]=a[i],--cnt[a[i]];    */    for(int i=1; i<=n; i++)        cout << b[i] << " \n"[i==n];    return 0;}

        基数排序

        时间复杂度 \(O(n)\)

        空间复杂度 \(O(n)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,w,a[N],cnt[N];void rdsort(int n,int *a){    int *b=new int[n+5];    int *cnt=new int[1<<8];    int mask=(1<<8)-1;    int *x=a,*y=b;    for(int i=0; i<64; i+=8)    {        for(int j=0; j!=(1<<8); j++)cnt[j]=0;        for(int j=1; j<=n; j++)++cnt[x[j]>>i&mask];        for(int j=1; j!=(1<<8); j++)cnt[j]+=cnt[j-1];        for(int j=n; j>=1; j--)y[cnt[x[j]>>i&mask]--]=x[j];        swap(x,y);    }    delete[] b;    delete[] cnt;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    rdsort(n,a);    for(int i=1; i<=n; i++)        cout << a[i] << " \n"[i==n];    return 0;}

        CDQ分治

        P3810【模板】三维偏序(陌上花开)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)#define K (int)(2e5+15)int n,k,pos;struct node{    int x,y,z,w,ans;    bool operator!=(node &o)const        {return x!=o.x||y!=o.y||z!=o.z;}}a[N],b[N],tmp[N];int tree[K],cnt[N];bool cmpx(node a,node b){    if(a.x==b.x)    {        if(a.y==b.y)            return a.z<b.z;        return a.y<b.y;    }    return a.x<b.x;}bool cmpy(node a,node b){    if(a.y==b.y)        return a.z<b.z;    return a.y<b.y;}#define lowbit(x) (x&(-x))void add(int x,int v){    while(x&&x<=k)    {        tree[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=tree[x];        x-=lowbit(x);    }    return res;}void cdq(int l,int r){    if(l==r)return;    int mid=(l+r)>>1;    cdq(l,mid);cdq(mid+1,r);    int i=mid+1,j=l,res=0,cnt=0;    for(;i<=r; i++)    {        while(a[j].y<=a[i].y&&j<=mid)        {            add(a[j].z,a[j].w);            tmp[++cnt]=a[j++];        }        a[i].ans+=sum(a[i].z);        tmp[++cnt]=a[i];    }    for(int i=l; i<j; i++)        add(a[i].z,-a[i].w);    while(j<=mid)tmp[++cnt]=a[j++];    for(int i=1; i<=cnt; i++)        a[l+i-1]=tmp[i];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> k;    for(int i=1; i<=n; i++)        cin >> b[i].x >> b[i].y >> b[i].z;    sort(b+1,b+1+n,cmpx);    int c=0;    for(int i=1; i<=n; i++)    {        ++c;        if(b[i]!=b[i+1])            a[++pos]=b[i],a[pos].w=c,c=0;    }    cdq(1,pos);    for(int i=1; i<=pos; i++)        cnt[a[i].ans+a[i].w-1]+=a[i].w;    for(int i=0; i<n; i++)        cout << cnt[i] << endl;    return 0;}

        LCA

        LCA 倍增

        时间复杂度 \(O(\log n)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e5+5)struct Edge{int u,v,next;}e[MAXN<<1];int n,m,st;int lg[MAXN],fa[MAXN][22],dep[MAXN];int head[MAXN],pos=1;template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}void add(R int u,R int v){e[pos]={u,v,head[u]};head[u]=pos++;}void dfs(R int u,R int f){fa[u][0]=f;dep[u]=dep[f]+1;for(R int i=1; i<=lg[dep[u]]; i++)fa[u][i]=fa[fa[u][i-1]][i-1];for(R int i=head[u]; i; i=e[i].next){R int v=e[i].v;if(v==f)continue;dfs(v,u);}}int LCA(R int x,R int y){if(dep[x]<dep[y])swap(x,y);while(dep[x]>dep[y])x=fa[x][lg[dep[x]-dep[y]]-1];if(x==y)return x;for(R int k=lg[dep[x]]-1; k>=0; k--){if(fa[x][k]!=fa[y][k])x=fa[x][k],y=fa[y][k];}return fa[x][0];}signed main(){read(n);read(m);read(st);for(R int i=1,u,v; i<n; i++){read(u);read(v);add(u,v);add(v,u);}for(R int i=1; i<=n; i++)lg[i]=lg[i-1]+(1<<lg[i-1]==i);dfs(st,0);while(m--){R int x,y;read(x);read(y);printf("%lld\n",LCA(x,y));}return 0;}

        LCA 树链剖分

        时间复杂度 \(O(\log n)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(5e5+25)struct Edge{int u,v,next;}e[N<<1];int pos=1,n,Q,rt,p,head[N];int id[N],idx,fa[N],son[N];int sz[N],top[N],dep[N];void addEdge(int u,int v){e[pos]={u,v,head[u]};head[u]=pos++;}void dfs(int u,int f,int d){fa[u]=f;sz[u]=1;dep[u]=d;int mx=-1;for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==f)continue;dfs(v,u,d+1);sz[u]+=sz[v];if(sz[v]>mx)mx=sz[v],son[u]=v;}}void dfs(int u,int ftop){/id[u]=++idx;top[u]=ftop;if(!son[u])return;dfs(son[u],ftop);for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==fa[u]||v==son[u])continue;dfs(v,v);}}int lca(int x,int y){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);x=fa[top[x]];}return dep[x]<dep[y]?x:y;}signed main(){read(n);read(Q);read(rt);for(int i=1,u,v; i<n; i++){read(u);read(v);addEdge(u,v);addEdge(v,u);}dfs(rt,0,1);dfs(rt,rt);while(Q--){int u,v;read(u);read(v);write(lca(u,v));pc('\n');}return 0;}

        高精度加减乘除

        #include<bits/stdc++.h>using namespace std;#define int long long#define R registerstring s1,s2;int compare(string str1,string str2){    if(str1.size()==str2.size())return str1.compare(str2);    return str1.size()<str2.size()?-1:1;}string add(string str1,string str2){    int len1=str1.size();    int len2=str2.size();    if(len1<len2)    {        for(R int i=1; i<=len2-len1; i++)            str1='0'+str1;    }else for(R int i=1; i<=len1-len2; i++)            str2='0'+str2;    string str;    int len=str1.size();    int jw=0;    for(R int i=len-1; i>=0; i--)    {        int tmp=str1[i]-'0'+str2[i]-'0'+jw;        jw=tmp/10;        tmp%=10;        str=char(tmp+'0')+str;    }if(jw)str=char(jw+'0')+str;    str.erase(0,str.find_first_not_of('0'));    if(str.empty())str="0";    return str;}string sub(string str1,string str2){    string str;    int len1=str1.size();    int len2=str2.size();    if(len1>len2)    {        for(R int i=1; i<=len1-len2; i++)            str2='0'+str2;    }    int len=str1.size();    int jw=0;    for(R int i=len-1; i>=0; i--)    {        if(str1[i]<str2[i]+jw)        {            str=char(str1[i]-str2[i]-jw+10+'0')+str;            jw=1;        }else        {            str=char(str1[i]-str2[i]-jw+'0')+str;            jw=0;        }    }    str.erase(0,str.find_first_not_of('0'));    if(str.empty())str="0";    return str;}string mul(string str1,string str2){    int len1=str1.size();    int len2=str2.size();    if(str1=="0"||str2=="0")return "0";    string tmpstr;    string str;    for(R int i=len1-1; i>=0; i--)    {        tmpstr="";        int idx=str1[i]-'0';        int jw=0;        if(idx)        {            for(R int j=1; j<=len1-1-i; j++)                tmpstr+='0';            for(R int j=len2-1; j>=0; j--)            {                int tmp=idx*(str2[j]-'0')+jw;                jw=tmp/10;                tmp%=10;                tmpstr=char(tmp+'0')+tmpstr;            }if(jw)tmpstr=char(jw+'0')+tmpstr;        }        str=add(str,tmpstr);    }    str.erase(0,str.find_first_not_of('0'));    if(str.empty())str="0";    return str;}string div(string str1,string str2){    string ans;    ans="";    if(str1=="0")return ans="0";    int check=compare(str1,str2);    if(check==-1)return ans="0";    if(check==0)return ans="1";    int len1=str1.size();    int len2=str2.size();    string tmpstr;    tmpstr.append(str1,0,len2-1);    for(R int i=len2-1; i<len1; i++)    {        tmpstr+=str1[i];        tmpstr.erase(0,tmpstr.find_first_not_of('0'));        if(tmpstr.empty())tmpstr="0";        for(string str="9";str[0]>='0';str[0]--)        {            string tmp=mul(str,str2);            if(compare(tmp,tmpstr)<=0)            {                ans+=str[0];                tmpstr=sub(tmpstr,tmp);                break;            }        }    }    ans.erase(0,ans.find_first_not_of('0'));    return ans;}signed main(){    // ...    return 0;}

        高精度封装版

        网上搜的。有空搞吧。忘记出处了,很久前复制的

        #include <bits/stdc++.h>using namespace std;const int maxn=5009;struct bignum{int num[maxn],len;bignum(){memset(num,0,sizeof(num));}};bool comp(bignum a,bignum b)//比较函数{if(a.len!=b.len)return (a.len>b.len)?1:0;for(int i=a.len-1;i>=0;i--)if(a.num[i]!=b.num[i])return (a.num[i]>b.num[i])?1:0;return -1;//相等}bignum operator / (bignum a,int b)//高精除以低精度{bignum c=a;int tw=0,len=a.len;for(int i=len-1;i>=0;i--){int k=(tw*10+c.num[i])/b;tw=(tw*10+c.num[i])%b;c.num[i]=k;}while(c.num[len-1]==0)len--;c.len=len;return c;}bignum init(bignum &a,string s){int len=s.length();if(s[0]=='-')//处理负数len--,a.num[maxn-1]=-1;//这样len-i-1就不会到0去,且留下是负数的标记for(int i=0;i<len;i++)a.num[i]=s[len-i-1]-'0';a.len=len;return a;}bignum Add(bignum &a,bignum &b){bignum c;int len=max(a.len,b.len);for(int i=0;i<len;i++){c.num[i]+=(a.num[i]+b.num[i]);c.num[i+1]+=c.num[i]/10;c.num[i]%=10;}if(c.num[len])len++;//加法最多进1位c.len=len;return c;}bignum dmull(bignum &a,int s)//高精乘低精{for(int i=0;i<a.len;i++)a.num[i]*=s;for(int i=0;i<a.len;i++){if(a.num[i]>=10){if(i==a.len-1)a.len++;a.num[i+1]+=a.num[i]/10;a.num[i]%=10;}}return a;}bignum Hmull(bignum &a,bignum &b)//高精乘高精{bignum c;for(int i=0;i<a.len;i++)for(int j=0;j<b.len;j++){c.num[i+j]+=(a.num[i]*b.num[j]);c.num[i+j+1]+=c.num[i+j]/10;c.num[i+j]%=10;}int len=a.len+b.len;while(c.num[len-1]==0&&len>1)len--;c.len=len;return c;}bignum ddiv(bignum a,bignum b,bignum &c,int &f)//低精度除法{//c是除的结果,f是余数for(int i-a.len-1;i>=0;i--){f=f*10+a.num[i];//慢慢做求余c.num[i]=f/b;f%=b;}while(len>1&&c.num[len-1]==0)len--;c.len=len;}void print(bignum &a){for(int i=a.len-1;i>=0;i--)cout<<a.num[i];}int main(){int n;cin>>n;bignum temp,ans;//下面是求解S=1!+2!+3!....+n!init(ans,"1");init(temp,"1");for(int i=2;i<=n;i++){temp=dmull(temp,i);ans=Add(ans,temp);}print(ans);}

        ]]> @@ -2616,7 +2616,7 @@ /2022/07/19/oi-mo-ban-qi-ta/ - OI模板-其他

        光速幂

        仅适用于int 且 底数相同,即 $a^b$ , $a$ 不变,复杂度约为 $O(\sqrt{n})$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int a,b,p;int pw1[N],pw2[N];int solve(){    pw1[0]=pw2[0]=1;    for(int i=1; i<1<<17; i++)        pw1[i]=pw1[i-1]*a%p;    pw2[1]=pw1[(1<<17)-1]*a%p;    for(int i=2; i<1<<17; i++)        pw2[i]=pw2[i-1]*pw2[1]%p;    return pw1[b&131071]*pw2[b>>17]%p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    scanf("%lld%lld%lld",&a,&b,&p);    printf("%lld^%lld mod %lld=%lld\n",a,b,p,solve());    return 0;}

        O(1)快速乘

        // 以下是O(1)快速乘+快速幂,可以防 10^18 的情况#define int long longint mul(int a,int b,int p){    int res=(__int128)a*b%p;    return res;}int qpow(int a,int b,int p){    int ans=1,base=a;    while(b)    {        if(b&1)ans=mul(ans,base,p);        base=mul(base,base,p);        b>>=1;    }    return ans;}

        二维数点

        P2163 [SHOI2007]园丁的烦恼

        二维数点

        二维数点 分治

        二维CDQ

        时间复杂度 $O(n\log n)$

        #include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(5e5+15)struct node{    int x,y,typ,add,id,ans;}t[N*5],tmp[N*5];int n,Q,pos,ans[N];void add(int a,int b,int c,int d=0,int e=0){    t[++pos]={a,b,c,d,e};}bool cmp(node a,node b){    if(a.x==b.x)        return a.typ<b.typ;    return a.x<b.x;}void cdq(int l,int r){    if(l==r)return;    int mid=(l+r)>>1;    cdq(l,mid);cdq(mid+1,r);    int i=mid+1,j=l,res=0,cnt=0;    for(;i<=r; i++)    {        while(t[j].y<=t[i].y&&j<=mid)        {            if(t[j].typ==1)++res;            tmp[++cnt]=t[j++];        }        if(t[i].typ==2)t[i].ans+=res;        tmp[++cnt]=t[i];    }    while(j<=mid)        tmp[++cnt]=t[j++];    for(int i=1; i<=cnt; i++)        t[l+i-1]=tmp[i];}signed main(){    read(n);read(Q);    for(int i=1,x,y; i<=n; i++)    {        read(x);read(y);        add(x,y,1);    }    for(int i=1,a,b,c,d; i<=Q; i++)    {        read(a);read(b);read(c);read(d);        add(a-1,b-1, 2, 1,i);        add(  c,  d, 2, 1,i);        add(a-1,  d, 2,-1,i);        add(  c,b-1, 2,-1,i);    }    sort(t+1,t+1+pos,cmp);    cdq(1,pos);    for(int i=1; i<=pos; i++)    {        if(t[i].typ==2)            ans[t[i].id]+=t[i].add*t[i].ans;    }    for(int i=1; i<=Q; i++)        write(ans[i]),pc('\n');    return 0;}

        二维数点 树状数组

        时间复杂度 $O(n\log n)$

        #include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(5e5+15)struct node{    int x,y,typ,add,id,ans;}t[N*5];int n,Q,pos,tree[N],tmp[N*5],ans[N];void addQ(int a,int b,int c,int d=0,int e=0){    t[++pos]={a,b,c,d,e};}#define lowbit(x) (x&(-x))void add(int x,int v){    while(x&&x<=n)    {        tree[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=tree[x];        x-=lowbit(x);    }    return res;}bool cmp(node a,node b){    if(a.x==b.x)        return a.typ<b.typ;    return a.x<b.x;}void init(){    for(int i=1; i<=pos; i++)        tmp[i]=t[i].y;    sort(tmp+1,tmp+1+pos);    int len=unique(tmp+1,tmp+1+pos)-tmp-1;    for(int i=1; i<=pos; i++)        t[i].y=lower_bound(tmp+1,tmp+1+len,t[i].y)-tmp;}signed main(){    read(n);read(Q);    for(int i=1,x,y; i<=n; i++)    {        read(x);read(y);        addQ(x,y,1);    }    for(int i=1,a,b,c,d; i<=Q; i++)    {        read(a);read(b);read(c);read(d);        addQ(a-1,b-1,2,1,i);        addQ(c,d,2,1,i);        addQ(a-1,d,2,-1,i);        addQ(c,b-1,2,-1,i);    }    init();    sort(t+1,t+1+pos,cmp);    for(int i=1; i<=pos; i++)    {        if(t[i].typ==1)            add(t[i].y,1);        if(t[i].typ==2)            t[i].ans+=sum(t[i].y);    }    for(int i=1; i<=pos; i++)    {        if(t[i].typ==2)            ans[t[i].id]+=t[i].add*t[i].ans;    }    for(int i=1; i<=Q; i++)        write(ans[i]),pc('\n');    return 0;}

        乘法取模封装

        需要头文件 cstdarg

        int mul(int cnt, ...){    va_list ptr; va_start(ptr,cnt);    int res=1;    for(int i=0; i<cnt; i++)        res=res*va_arg(ptr,int)%mod;    va_end(ptr);    return res;}

        调用方式如下:

        计算 $(a \times b \times c \times d )\bmod M$

        ans = mul(4, a, b, c, d);
        ]]> + OI模板-其他

        光速幂

        仅适用于int 且 底数相同,即 \(a^b\) , \(a\) 不变,复杂度约为 \(O(\sqrt{n})\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int a,b,p;int pw1[N],pw2[N];int solve(){    pw1[0]=pw2[0]=1;    for(int i=1; i<1<<17; i++)        pw1[i]=pw1[i-1]*a%p;    pw2[1]=pw1[(1<<17)-1]*a%p;    for(int i=2; i<1<<17; i++)        pw2[i]=pw2[i-1]*pw2[1]%p;    return pw1[b&131071]*pw2[b>>17]%p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    scanf("%lld%lld%lld",&a,&b,&p);    printf("%lld^%lld mod %lld=%lld\n",a,b,p,solve());    return 0;}

        O(1)快速乘

        // 以下是O(1)快速乘+快速幂,可以防 10^18 的情况#define int long longint mul(int a,int b,int p){    int res=(__int128)a*b%p;    return res;}int qpow(int a,int b,int p){    int ans=1,base=a;    while(b)    {        if(b&1)ans=mul(ans,base,p);        base=mul(base,base,p);        b>>=1;    }    return ans;}

        二维数点

        P2163[SHOI2007]园丁的烦恼

        二维数点

        二维数点 分治

        二维CDQ

        时间复杂度 \(O(n\log n)\)

        #include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(5e5+15)struct node{    int x,y,typ,add,id,ans;}t[N*5],tmp[N*5];int n,Q,pos,ans[N];void add(int a,int b,int c,int d=0,int e=0){    t[++pos]={a,b,c,d,e};}bool cmp(node a,node b){    if(a.x==b.x)        return a.typ<b.typ;    return a.x<b.x;}void cdq(int l,int r){    if(l==r)return;    int mid=(l+r)>>1;    cdq(l,mid);cdq(mid+1,r);    int i=mid+1,j=l,res=0,cnt=0;    for(;i<=r; i++)    {        while(t[j].y<=t[i].y&&j<=mid)        {            if(t[j].typ==1)++res;            tmp[++cnt]=t[j++];        }        if(t[i].typ==2)t[i].ans+=res;        tmp[++cnt]=t[i];    }    while(j<=mid)        tmp[++cnt]=t[j++];    for(int i=1; i<=cnt; i++)        t[l+i-1]=tmp[i];}signed main(){    read(n);read(Q);    for(int i=1,x,y; i<=n; i++)    {        read(x);read(y);        add(x,y,1);    }    for(int i=1,a,b,c,d; i<=Q; i++)    {        read(a);read(b);read(c);read(d);        add(a-1,b-1, 2, 1,i);        add(  c,  d, 2, 1,i);        add(a-1,  d, 2,-1,i);        add(  c,b-1, 2,-1,i);    }    sort(t+1,t+1+pos,cmp);    cdq(1,pos);    for(int i=1; i<=pos; i++)    {        if(t[i].typ==2)            ans[t[i].id]+=t[i].add*t[i].ans;    }    for(int i=1; i<=Q; i++)        write(ans[i]),pc('\n');    return 0;}

        二维数点 树状数组

        时间复杂度 \(O(n\log n)\)

        #include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(5e5+15)struct node{    int x,y,typ,add,id,ans;}t[N*5];int n,Q,pos,tree[N],tmp[N*5],ans[N];void addQ(int a,int b,int c,int d=0,int e=0){    t[++pos]={a,b,c,d,e};}#define lowbit(x) (x&(-x))void add(int x,int v){    while(x&&x<=n)    {        tree[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=tree[x];        x-=lowbit(x);    }    return res;}bool cmp(node a,node b){    if(a.x==b.x)        return a.typ<b.typ;    return a.x<b.x;}void init(){    for(int i=1; i<=pos; i++)        tmp[i]=t[i].y;    sort(tmp+1,tmp+1+pos);    int len=unique(tmp+1,tmp+1+pos)-tmp-1;    for(int i=1; i<=pos; i++)        t[i].y=lower_bound(tmp+1,tmp+1+len,t[i].y)-tmp;}signed main(){    read(n);read(Q);    for(int i=1,x,y; i<=n; i++)    {        read(x);read(y);        addQ(x,y,1);    }    for(int i=1,a,b,c,d; i<=Q; i++)    {        read(a);read(b);read(c);read(d);        addQ(a-1,b-1,2,1,i);        addQ(c,d,2,1,i);        addQ(a-1,d,2,-1,i);        addQ(c,b-1,2,-1,i);    }    init();    sort(t+1,t+1+pos,cmp);    for(int i=1; i<=pos; i++)    {        if(t[i].typ==1)            add(t[i].y,1);        if(t[i].typ==2)            t[i].ans+=sum(t[i].y);    }    for(int i=1; i<=pos; i++)    {        if(t[i].typ==2)            ans[t[i].id]+=t[i].add*t[i].ans;    }    for(int i=1; i<=Q; i++)        write(ans[i]),pc('\n');    return 0;}

        乘法取模封装

        需要头文件 cstdarg

        int mul(int cnt, ...){    va_list ptr; va_start(ptr,cnt);    int res=1;    for(int i=0; i<cnt; i++)        res=res*va_arg(ptr,int)%mod;    va_end(ptr);    return res;}

        调用方式如下:

        计算 \((a \times b \times c \times d )\bmodM\)

        ans = mul(4, a, b, c, d);
        ]]> @@ -2641,7 +2641,7 @@ /2022/07/19/oi-mo-ban-ji-suan-ji-he/ - OI模板-计算几何

        二维凸包

        Andrew

        时间复杂度 $O(n \log n)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n;struct vct{    double x,y;    vct operator-(const vct &o)const    {        return (vct){x-o.x,y-o.y};    }}p[N],ans[N];int stk[N],top,used[N];int cmp(vct a,vct b){    return a.x==b.x?a.y<b.y:a.x<b.x;}double cross(vct a,vct b){    return a.x*b.y-a.y*b.x;}double dis(vct a,vct b){    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(2);    cin >> n;    for(int i=1; i<=n; i++)        cin >> p[i].x >> p[i].y;    sort(p+1,p+1+n,cmp);    stk[++top]=1;    for(int i=2; i<=n; i++)    {        while(top>1&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    int tmp=top;    for(int i=n-1; i>=1; i--)    {        if(!used[i])        {            while(top>tmp&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)                used[stk[top--]]=0;            used[i]=1;            stk[++top]=i;        }    }    for(int i=1; i<=top; i++)        ans[i]=p[stk[i]];    double res=0;    for(int i=2; i<=top; i++)        res+=dis(ans[i-1],ans[i]);    cout << res << endl;    return 0;}

        Graham

        时间复杂度 $O(n\log n)$

        #include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(1e5+15)int n;struct node{    double x,y;}p[N],stk[N];double cross(node a1,node a2,node b1,node b2){    return (a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y);}double dis(node a,node b){    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}bool cmp(node a,node b){    double tmp=cross(p[1],a,p[1],b);    if(tmp>0)return 1;    if(tmp==0&&dis(p[1],a)<=dis(p[1],b))        return 1;    return 0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(2);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> p[i].x >> p[i].y;        if(i!=1&&p[i].y<=p[1].y&&p[i].x<=p[1].x)            swap(p[1],p[i]);    }    sort(p+2,p+1+n,cmp);    stk[1]=p[1];    int top=1;    for(int i=2; i<=n; i++)    {        while(top>1&&cross(stk[top-1],stk[top],stk[top],p[i])<=0)            --top;        stk[++top]=p[i];    }    double ans=0;    stk[top+1]=p[1];    for(int i=1; i<=top; i++)        ans+=dis(stk[i],stk[i+1]);    cout << ans << endl;    return 0;}

        平面最近点对

        P7883 平面最近点对(加强加强版)

        时间复杂度 $O(n\log n)$

        注意下面的代码输出的是平方

        改为求sqrt一定要改成fabs !!!!!!!!!!!

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(4e5+15)int n;struct node{    int x,y;}p[N],tmp[N];#define pf(x) ((x)*(x))int dis(node a,node b){    return pf(a.x-b.x)+pf(a.y-b.y);}int cmpx(node a,node b){return a.x<b.x;}int cmpy(node a,node b){return a.y<b.y;}int cdq(int l,int r){    if(l==r)return INF;    if(l+1==r)    {        if(p[l].y>p[r].y)swap(p[l],p[r]);        return dis(p[l],p[r]);    }    int mid=(l+r)>>1,t=p[mid].x;    int d=min(cdq(l,mid),cdq(mid+1,r));    inplace_merge(p+l,p+mid+1,p+r+1,cmpy);    int cnt=0;    for(int i=l; i<=r; i++)        if(pf(p[i].x-t)<d)            tmp[++cnt]=p[i];    for(int i=1; i<=cnt; i++)        for(int j=i+1; j<=cnt&&pf(tmp[i].y-tmp[j].y)<d; j++)            d=min(d,dis(tmp[i],tmp[j]));    return d;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(4);    cin >> n;    for(int i=1; i<=n; i++)        cin >> p[i].x >> p[i].y;    sort(p+1,p+1+n,cmpx);    cout << cdq(1,n) << endl;    return 0;}

        半平面交

        P4196 [CQOI2006]凸多边形 /【模板】半平面交

        1. 给出了凸多边形,逆时针给出各个顶点的坐标

        时间复杂度 $O(n\log n)$ (不是题目里的 $n$ )

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)const double eps=1e-10;struct vct{double x,y;}p[N],in[N];#define pf(x) ((x)*(x))vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}struct line{    vct p,way;    double k;    void mkline(vct a,vct b)    {        p=a;way=b;        k=atan2(b.y,b.x);    }}a[N];double dcmp(double x){    if(fabs(x)<=eps)return 0;    if(x>eps)return 1;    return -1;}bool operator<(line a,line b){    int d=dcmp(a.k-b.k);    if(d==0)return dcmp(cross(b.p-a.p,b.way))>0;    return d<0;}bool onright(vct a,line b){return dcmp(cross(b.way,a-b.p))<0;}vct intersect(line a,line b){    double t=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*x;}int st,en;line que[N];bool halfplane(int n){    que[st=en=1]=a[1];    for(int i=2; i<=n; i++)    {        while(st<en&&onright(p[en],a[i]))--en;        while(st<en&&onright(p[st+1],a[i]))++st;        que[++en]=a[i];        if(st<en)p[en]=intersect(que[en-1],que[en]);    }    while(st<en&&onright(p[en],que[st]))--en;    if(en-st<=1)return 0;    p[st]=intersect(que[st],que[en]);    return 1;}double calc(vct *a,int n){    double res=0;    for(int i=1; i<n; i++)        res+=cross(a[i],a[i+1]);    res+=cross(a[n],a[1]);    if(fabs(res/=2)<=eps)res=0;    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(3);    int n,o=0,oo=0;    cin >> n;    while(n--)    {        int m;cin >> m;        for(int i=1; i<=m; i++)            cin >> in[i].x >> in[i].y;        for(int i=1; i<m; i++)            a[++oo].mkline(in[i],in[i+1]-in[i]);        a[++oo].mkline(in[m],in[1]-in[m]);    }    sort(a+1,a+1+oo);    for(int i=1; i<=oo; i++)        if(i==1||dcmp(a[i].k-a[i-1].k))            a[++o]=a[i];    if(!halfplane(o))cout << 0.0 << endl;    else cout << calc(p+st-1,en-st+1) << endl;    return 0;}

        poj2451

        1. 给出了向量

        时间复杂度同上

        注意POJ上提交要用printf() + %f ,所以下面这个代码交上去的时候要改一下

        #include <cstdio>#include <iostream>#include <vector>#include <algorithm>#include <cmath>#include <iomanip>using namespace std;#define int long long// #define double long double#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)const double eps=1e-10;struct vct{double x,y;}p[N];#define pf(x) ((x)*(x))vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}struct line{    vct p,way;    double k;    void mkline(vct a,vct b)    {        p=a;way=b;        k=atan2(b.y,b.x);    }    void mk(double x1,double y1,double x2,double y2)    {        mkline({x1,y1},{x2-x1,y2-y1});    }}a[N];int dcmp(double x){    if(fabs(x)<=eps)return 0;    return x>eps?1:-1;}bool operator<(line a,line b){    int d=dcmp(a.k-b.k);    if(d==0)return dcmp(cross(b.p-a.p,b.way))>0;    return d<0;}bool onright(vct a,line b){return dcmp(cross(b.way,a-b.p))<0;}vct intersect(line a,line b){    double x=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*x;}int st,en;line que[N];bool halfplane(int n){    que[st=en=1]=a[1];    for(int i=2; i<=n; i++)    {        while(st<en&&onright(p[en],a[i]))--en;        while(st<en&&onright(p[st+1],a[i]))++st;        que[++en]=a[i];        if(st<en)p[en]=intersect(que[en-1],que[en]);    }    while(st<en&&onright(p[en],que[st]))--en;    if(en-st<=1)return 0;    p[st]=intersect(que[st],que[en]);    return 1;}double calc(vct *a,int n){    double res=0;    for(int i=1; i<n; i++)        res+=cross(a[i],a[i+1]);    res+=cross(a[n],a[1]);    if(fabs(res/=2)<=eps)res=0;    return res;}signed main(){    int n,o=0,oo=0;    scanf("%lld",&n);    while(n--)    {        double x1,x2,y1,y2;        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);        if(x1==x2&&y1==y2)continue;        a[++oo].mk(x1,y1,x2,y2);    }    a[++oo].mk(0,0,10000,0);    a[++oo].mk(10000,0,10000,10000);    a[++oo].mk(10000,10000,0,10000);    a[++oo].mk(0,10000,0,0);    sort(a+1,a+1+oo);    a[0].k=a[1].k-1;    for(int i=1; i<=oo; i++)        if(i==1||dcmp(a[i-1].k-a[i].k))            a[++o]=a[i];    if(!halfplane(o))puts("0.0");    else printf("%.1lf\n",calc(p+st-1,en-st+1));    return 0;}

        旋转卡壳

        时间复杂度 $O(n\log n)$

        其实是二维凸包的复杂度,查找只有 $O(n)$

        注意下面这份代码写的是距离的平方

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(5e4+15)struct vct{    int x,y;    }p[N],s[N];vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}int cross(vct a,vct b){    return a.x*b.y-a.y*b.x;}#define pf(x) ((x)*(x))int dis(vct a,vct b){    return pf(a.x-b.x)+pf(a.y-b.y);}int sq(int a,int b,int c){    return abs(cross(p[b]-p[a],p[c]-p[b]));}int n,used[N],stk[N],top;int getmx(){    int a=2,mx=-INF;    if(top<4)        return dis(s[1],s[2]);    for(int i=2; i<=top; i++)    {        while(cross(s[a%top+1]-s[i],s[i-1]-s[i])>=cross(s[a]-s[i],s[i-1]-s[i]))            a=a%top+1;        mx=max(mx,max(dis(s[i-1],s[a]),dis(s[i],s[a])));    }    return mx;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)        cin >> p[i].x >> p[i].y;    sort(p+1,p+1+n,[](vct a,vct b)    {        return a.x==b.x?a.y<b.y:a.x<b.x;    });    stk[++top]=1;    for(int i=2; i<=n; i++)    {        while(top>1&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    int tmp=top;    for(int i=n-1; i>=1; i--)    {        if(used[i])continue;        while(top>tmp&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    for(int i=1; i<=top; i++)        s[i]=p[stk[i]];    cout << getmx() << endl;     return 0;}

        扫描线

        扫描线 面积并

        P5490 【模板】扫描线

        时间复杂度 $O(n\log n)$

        #include <bits/stdc++.h>using namespace std;#define int long long// #define double long double#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define lb lower_bound#define N (int)(2e5+15)int n,x[N];struct sline{    int l,r,h,mark;}s[N];bool operator<(sline a,sline b){return a.h<b.h;}struct node{    int l,r,sum,len;//  sum: 被完全覆盖的次数//  len: 区间内被截的长度}t[N<<2];#define ls(at) (at<<1)#define rs(at) (at<<1|1)void build(int l,int r,int at){    t[at].l=l;t[at].r=r;    if(l==r)return;    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));}void push_up(int at){    int l=t[at].l,r=t[at].r;    if(t[at].sum)        t[at].len=x[r+1]-x[l];    else if(l==r)t[at].len=0;    else t[at].len=t[ls(at)].len+t[rs(at)].len;}void update(int nl,int nr,int c,int at){    int l=t[at].l,r=t[at].r;    if(nl<=l&&r<=nr)    {        t[at].sum+=c;        push_up(at);        return;    }    int mid=(l+r)>>1;    if(nl<=mid)update(nl,nr,c,ls(at));    if(nr>mid)update(nl,nr,c,rs(at));    push_up(at);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1,x1,x2,y1,y2; i<=n; i++)    {        cin >> x1 >> y1 >> x2 >> y2;        x[2*i-1]=x1;x[2*i]=x2;        s[2*i-1]={x1,x2,y1,1};        s[2*i]={x1,x2,y2,-1};    }    n<<=1;    sort(s+1,s+1+n);    sort(x+1,x+1+n);    int pos=unique(x+1,x+1+n)-x-1;    for(int i=1; i<=n; i++)    {        s[i].l=lb(x+1,x+1+pos,s[i].l)-x;        s[i].r=lb(x+1,x+1+pos,s[i].r)-x-1;    }    build(1,pos,1);    int res=0;    for(int i=1; i<=n; i++)    {        update(s[i].l,s[i].r,s[i].mark,1);        res+=t[1].len*(s[i+1].h-s[i].h);    }    cout << res << endl;    return 0;}

        扫描线 周长并(并的周长)

        P1856 [IOI1998] [USACO5.5] 矩形周长Picture

        【旧代码,懒得维护】

        跑了两边扫描线(纵、横)

        注意不要忘记把最后一条线搞一下

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define lb lower_bound#define N (int)(1e4+15)int n,ans;struct process{    struct sline    {        int l,r,h,mark;        bool operator<(const sline &o)const        {            if(h==o.h)                return mark>o.mark;            return h<o.h;        }    }s[N];    struct node    {        int l,r,sum,len;    }t[N<<2];    int x[N],pre;    #define ls(at) (at<<1)    #define rs(at) (at<<1|1)    void build(int l,int r,int at)    {        t[at].l=l;t[at].r=r;        if(l==r)return;        int mid=(l+r)>>1;        build(l,mid,ls(at));        build(mid+1,r,rs(at));    }    void push_up(int at)    {        int l=t[at].l,r=t[at].r;        if(t[at].sum)            t[at].len=x[r+1]-x[l];        else if(l==r)t[at].len=0;        else t[at].len=t[ls(at)].len+t[rs(at)].len;    }    void update(int nl,int nr,int c,int at)    {        int l=t[at].l,r=t[at].r;        if(nl<=l&&r<=nr)        {            t[at].sum+=c;            push_up(at);            return;        }        int mid=(l+r)>>1;        if(nl<=mid)update(nl,nr,c,ls(at));        if(nr>mid)update(nl,nr,c,rs(at));        push_up(at);    }    void solve()    {        sort(s+1,s+1+n);        sort(x+1,x+1+n);        int pos=unique(x+1,x+1+n)-x-1;        for(int i=1; i<=n; i++)        {            s[i].l=lb(x+1,x+1+pos,s[i].l)-x;            s[i].r=lb(x+1,x+1+pos,s[i].r)-x-1;        }        build(1,pos,1);        for(int i=1; i<n; i++)        {            update(s[i].l,s[i].r,s[i].mark,1);            ans+=abs(t[1].len-pre);            pre=t[1].len;        }        ans+=x[s[n].r+1]-x[s[n].l];    }}p1,p2;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1,x1,x2,y1,y2; i<=n; i++)    {        cin >> x1 >> y1 >> x2 >> y2;        p1.x[2*i-1]=x1;p1.x[2*i]=x2;        p1.s[2*i-1]={x1,x2,y1,1};        p1.s[2*i]={x1,x2,y2,-1};        p2.x[2*i-1]=y1;p2.x[2*i]=y2;        p2.s[2*i-1]={y1,y2,x1,1};        p2.s[2*i]={y1,y2,x2,-1};    }    n<<=1;    p1.solve();p2.solve();    cout << ans << endl;    return 0;}

        随机增量法

        时间复杂度 $O(n)$

        三点确定一个圆,故三层循环

        证:

        例题:P1742 最小圆覆盖

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)#define double long doubleint n;const double eps=1e-10;#define pf(x) ((x)*(x))struct vct{double x,y;}p[N],C;vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double dis(vct a,vct b){return sqrt(pf(a.x-b.x)+pf(a.y-b.y));}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}vct rot(vct a){return {-a.y,a.x};}struct line{    vct p,way;    void mkline(vct a,vct b){p=a;way=b;}    void mk(double x1,double y1,double x2,double y2)    {mkline({x1,y1},{x2-x1,y2-y1});}};vct intersect(line a,line b){    double t=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*t;}vct getcircle(vct a,vct b,vct c){    line p,q;    p.mkline((a+b)/2,rot(b-a));    q.mkline((a+c)/2,rot(c-a));    return intersect(p,q);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);vct p=(a.p+b.p)/2;    cout << fixed << setprecision(10);    mt19937_64 rd(time(0));    cin >> n;    for(int i=1; i<=n; i++)        cin >> p[i].x >> p[i].y;    shuffle(p+1,p+1+n,rd);    double r=0;    for(int i=1; i<=n; i++) if(len(C-p[i])>r)    {        C=p[i];r=0;                for(int j=1; j<i; j++) if(len(C-p[j])>r)        {            C=(p[i]+p[j])/2;            r=len(C-p[j]);                        for(int k=1; k<j; k++) if(len(C-p[k])>r)            {                C=getcircle(p[i],p[j],p[k]);                r=len(C-p[k]);            }        }    }    cout << r << endl;    cout << C.x << " " << C.y << endl;    return 0;}
        ]]> + OI模板-计算几何

        二维凸包

        Andrew

        时间复杂度 \(O(n \log n)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n;struct vct{    double x,y;    vct operator-(const vct &o)const    {        return (vct){x-o.x,y-o.y};    }}p[N],ans[N];int stk[N],top,used[N];int cmp(vct a,vct b){    return a.x==b.x?a.y<b.y:a.x<b.x;}double cross(vct a,vct b){    return a.x*b.y-a.y*b.x;}double dis(vct a,vct b){    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(2);    cin >> n;    for(int i=1; i<=n; i++)        cin >> p[i].x >> p[i].y;    sort(p+1,p+1+n,cmp);    stk[++top]=1;    for(int i=2; i<=n; i++)    {        while(top>1&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    int tmp=top;    for(int i=n-1; i>=1; i--)    {        if(!used[i])        {            while(top>tmp&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)                used[stk[top--]]=0;            used[i]=1;            stk[++top]=i;        }    }    for(int i=1; i<=top; i++)        ans[i]=p[stk[i]];    double res=0;    for(int i=2; i<=top; i++)        res+=dis(ans[i-1],ans[i]);    cout << res << endl;    return 0;}

        Graham

        时间复杂度 \(O(n\log n)\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(1e5+15)int n;struct node{    double x,y;}p[N],stk[N];double cross(node a1,node a2,node b1,node b2){    return (a2.x-a1.x)*(b2.y-b1.y)-(b2.x-b1.x)*(a2.y-a1.y);}double dis(node a,node b){    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}bool cmp(node a,node b){    double tmp=cross(p[1],a,p[1],b);    if(tmp>0)return 1;    if(tmp==0&&dis(p[1],a)<=dis(p[1],b))        return 1;    return 0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(2);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> p[i].x >> p[i].y;        if(i!=1&&p[i].y<=p[1].y&&p[i].x<=p[1].x)            swap(p[1],p[i]);    }    sort(p+2,p+1+n,cmp);    stk[1]=p[1];    int top=1;    for(int i=2; i<=n; i++)    {        while(top>1&&cross(stk[top-1],stk[top],stk[top],p[i])<=0)            --top;        stk[++top]=p[i];    }    double ans=0;    stk[top+1]=p[1];    for(int i=1; i<=top; i++)        ans+=dis(stk[i],stk[i+1]);    cout << ans << endl;    return 0;}

        平面最近点对

        P7883平面最近点对(加强加强版)

        时间复杂度 \(O(n\log n)\)

        注意下面的代码输出的是平方

        改为求sqrt一定要改成fabs !!!!!!!!!!!

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(4e5+15)int n;struct node{    int x,y;}p[N],tmp[N];#define pf(x) ((x)*(x))int dis(node a,node b){    return pf(a.x-b.x)+pf(a.y-b.y);}int cmpx(node a,node b){return a.x<b.x;}int cmpy(node a,node b){return a.y<b.y;}int cdq(int l,int r){    if(l==r)return INF;    if(l+1==r)    {        if(p[l].y>p[r].y)swap(p[l],p[r]);        return dis(p[l],p[r]);    }    int mid=(l+r)>>1,t=p[mid].x;    int d=min(cdq(l,mid),cdq(mid+1,r));    inplace_merge(p+l,p+mid+1,p+r+1,cmpy);    int cnt=0;    for(int i=l; i<=r; i++)        if(pf(p[i].x-t)<d)            tmp[++cnt]=p[i];    for(int i=1; i<=cnt; i++)        for(int j=i+1; j<=cnt&&pf(tmp[i].y-tmp[j].y)<d; j++)            d=min(d,dis(tmp[i],tmp[j]));    return d;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(4);    cin >> n;    for(int i=1; i<=n; i++)        cin >> p[i].x >> p[i].y;    sort(p+1,p+1+n,cmpx);    cout << cdq(1,n) << endl;    return 0;}

        半平面交

        P4196[CQOI2006]凸多边形 /【模板】半平面交

        1. 给出了凸多边形,逆时针给出各个顶点的坐标

        时间复杂度 \(O(n\log n)\)(不是题目里的 \(n\)

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)const double eps=1e-10;struct vct{double x,y;}p[N],in[N];#define pf(x) ((x)*(x))vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}struct line{    vct p,way;    double k;    void mkline(vct a,vct b)    {        p=a;way=b;        k=atan2(b.y,b.x);    }}a[N];double dcmp(double x){    if(fabs(x)<=eps)return 0;    if(x>eps)return 1;    return -1;}bool operator<(line a,line b){    int d=dcmp(a.k-b.k);    if(d==0)return dcmp(cross(b.p-a.p,b.way))>0;    return d<0;}bool onright(vct a,line b){return dcmp(cross(b.way,a-b.p))<0;}vct intersect(line a,line b){    double t=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*x;}int st,en;line que[N];bool halfplane(int n){    que[st=en=1]=a[1];    for(int i=2; i<=n; i++)    {        while(st<en&&onright(p[en],a[i]))--en;        while(st<en&&onright(p[st+1],a[i]))++st;        que[++en]=a[i];        if(st<en)p[en]=intersect(que[en-1],que[en]);    }    while(st<en&&onright(p[en],que[st]))--en;    if(en-st<=1)return 0;    p[st]=intersect(que[st],que[en]);    return 1;}double calc(vct *a,int n){    double res=0;    for(int i=1; i<n; i++)        res+=cross(a[i],a[i+1]);    res+=cross(a[n],a[1]);    if(fabs(res/=2)<=eps)res=0;    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(3);    int n,o=0,oo=0;    cin >> n;    while(n--)    {        int m;cin >> m;        for(int i=1; i<=m; i++)            cin >> in[i].x >> in[i].y;        for(int i=1; i<m; i++)            a[++oo].mkline(in[i],in[i+1]-in[i]);        a[++oo].mkline(in[m],in[1]-in[m]);    }    sort(a+1,a+1+oo);    for(int i=1; i<=oo; i++)        if(i==1||dcmp(a[i].k-a[i-1].k))            a[++o]=a[i];    if(!halfplane(o))cout << 0.0 << endl;    else cout << calc(p+st-1,en-st+1) << endl;    return 0;}

        poj2451

        1. 给出了向量

        时间复杂度同上

        注意POJ上提交要用printf() + %f,所以下面这个代码交上去的时候要改一下

        #include <cstdio>#include <iostream>#include <vector>#include <algorithm>#include <cmath>#include <iomanip>using namespace std;#define int long long// #define double long double#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)const double eps=1e-10;struct vct{double x,y;}p[N];#define pf(x) ((x)*(x))vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}struct line{    vct p,way;    double k;    void mkline(vct a,vct b)    {        p=a;way=b;        k=atan2(b.y,b.x);    }    void mk(double x1,double y1,double x2,double y2)    {        mkline({x1,y1},{x2-x1,y2-y1});    }}a[N];int dcmp(double x){    if(fabs(x)<=eps)return 0;    return x>eps?1:-1;}bool operator<(line a,line b){    int d=dcmp(a.k-b.k);    if(d==0)return dcmp(cross(b.p-a.p,b.way))>0;    return d<0;}bool onright(vct a,line b){return dcmp(cross(b.way,a-b.p))<0;}vct intersect(line a,line b){    double x=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*x;}int st,en;line que[N];bool halfplane(int n){    que[st=en=1]=a[1];    for(int i=2; i<=n; i++)    {        while(st<en&&onright(p[en],a[i]))--en;        while(st<en&&onright(p[st+1],a[i]))++st;        que[++en]=a[i];        if(st<en)p[en]=intersect(que[en-1],que[en]);    }    while(st<en&&onright(p[en],que[st]))--en;    if(en-st<=1)return 0;    p[st]=intersect(que[st],que[en]);    return 1;}double calc(vct *a,int n){    double res=0;    for(int i=1; i<n; i++)        res+=cross(a[i],a[i+1]);    res+=cross(a[n],a[1]);    if(fabs(res/=2)<=eps)res=0;    return res;}signed main(){    int n,o=0,oo=0;    scanf("%lld",&n);    while(n--)    {        double x1,x2,y1,y2;        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);        if(x1==x2&&y1==y2)continue;        a[++oo].mk(x1,y1,x2,y2);    }    a[++oo].mk(0,0,10000,0);    a[++oo].mk(10000,0,10000,10000);    a[++oo].mk(10000,10000,0,10000);    a[++oo].mk(0,10000,0,0);    sort(a+1,a+1+oo);    a[0].k=a[1].k-1;    for(int i=1; i<=oo; i++)        if(i==1||dcmp(a[i-1].k-a[i].k))            a[++o]=a[i];    if(!halfplane(o))puts("0.0");    else printf("%.1lf\n",calc(p+st-1,en-st+1));    return 0;}

        旋转卡壳

        时间复杂度 \(O(n\log n)\)

        其实是二维凸包的复杂度,查找只有 \(O(n)\)

        注意下面这份代码写的是距离的平方

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(5e4+15)struct vct{    int x,y;    }p[N],s[N];vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}int cross(vct a,vct b){    return a.x*b.y-a.y*b.x;}#define pf(x) ((x)*(x))int dis(vct a,vct b){    return pf(a.x-b.x)+pf(a.y-b.y);}int sq(int a,int b,int c){    return abs(cross(p[b]-p[a],p[c]-p[b]));}int n,used[N],stk[N],top;int getmx(){    int a=2,mx=-INF;    if(top<4)        return dis(s[1],s[2]);    for(int i=2; i<=top; i++)    {        while(cross(s[a%top+1]-s[i],s[i-1]-s[i])>=cross(s[a]-s[i],s[i-1]-s[i]))            a=a%top+1;        mx=max(mx,max(dis(s[i-1],s[a]),dis(s[i],s[a])));    }    return mx;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)        cin >> p[i].x >> p[i].y;    sort(p+1,p+1+n,[](vct a,vct b)    {        return a.x==b.x?a.y<b.y:a.x<b.x;    });    stk[++top]=1;    for(int i=2; i<=n; i++)    {        while(top>1&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    int tmp=top;    for(int i=n-1; i>=1; i--)    {        if(used[i])continue;        while(top>tmp&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    for(int i=1; i<=top; i++)        s[i]=p[stk[i]];    cout << getmx() << endl;     return 0;}

        扫描线

        扫描线 面积并

        P5490【模板】扫描线

        时间复杂度 \(O(n\log n)\)

        #include <bits/stdc++.h>using namespace std;#define int long long// #define double long double#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define lb lower_bound#define N (int)(2e5+15)int n,x[N];struct sline{    int l,r,h,mark;}s[N];bool operator<(sline a,sline b){return a.h<b.h;}struct node{    int l,r,sum,len;//  sum: 被完全覆盖的次数//  len: 区间内被截的长度}t[N<<2];#define ls(at) (at<<1)#define rs(at) (at<<1|1)void build(int l,int r,int at){    t[at].l=l;t[at].r=r;    if(l==r)return;    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));}void push_up(int at){    int l=t[at].l,r=t[at].r;    if(t[at].sum)        t[at].len=x[r+1]-x[l];    else if(l==r)t[at].len=0;    else t[at].len=t[ls(at)].len+t[rs(at)].len;}void update(int nl,int nr,int c,int at){    int l=t[at].l,r=t[at].r;    if(nl<=l&&r<=nr)    {        t[at].sum+=c;        push_up(at);        return;    }    int mid=(l+r)>>1;    if(nl<=mid)update(nl,nr,c,ls(at));    if(nr>mid)update(nl,nr,c,rs(at));    push_up(at);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1,x1,x2,y1,y2; i<=n; i++)    {        cin >> x1 >> y1 >> x2 >> y2;        x[2*i-1]=x1;x[2*i]=x2;        s[2*i-1]={x1,x2,y1,1};        s[2*i]={x1,x2,y2,-1};    }    n<<=1;    sort(s+1,s+1+n);    sort(x+1,x+1+n);    int pos=unique(x+1,x+1+n)-x-1;    for(int i=1; i<=n; i++)    {        s[i].l=lb(x+1,x+1+pos,s[i].l)-x;        s[i].r=lb(x+1,x+1+pos,s[i].r)-x-1;    }    build(1,pos,1);    int res=0;    for(int i=1; i<=n; i++)    {        update(s[i].l,s[i].r,s[i].mark,1);        res+=t[1].len*(s[i+1].h-s[i].h);    }    cout << res << endl;    return 0;}

        扫描线 周长并(并的周长)

        P1856 [IOI1998][USACO5.5] 矩形周长Picture

        【旧代码,懒得维护】

        跑了两边扫描线(纵、横)

        注意不要忘记把最后一条线搞一下

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define lb lower_bound#define N (int)(1e4+15)int n,ans;struct process{    struct sline    {        int l,r,h,mark;        bool operator<(const sline &o)const        {            if(h==o.h)                return mark>o.mark;            return h<o.h;        }    }s[N];    struct node    {        int l,r,sum,len;    }t[N<<2];    int x[N],pre;    #define ls(at) (at<<1)    #define rs(at) (at<<1|1)    void build(int l,int r,int at)    {        t[at].l=l;t[at].r=r;        if(l==r)return;        int mid=(l+r)>>1;        build(l,mid,ls(at));        build(mid+1,r,rs(at));    }    void push_up(int at)    {        int l=t[at].l,r=t[at].r;        if(t[at].sum)            t[at].len=x[r+1]-x[l];        else if(l==r)t[at].len=0;        else t[at].len=t[ls(at)].len+t[rs(at)].len;    }    void update(int nl,int nr,int c,int at)    {        int l=t[at].l,r=t[at].r;        if(nl<=l&&r<=nr)        {            t[at].sum+=c;            push_up(at);            return;        }        int mid=(l+r)>>1;        if(nl<=mid)update(nl,nr,c,ls(at));        if(nr>mid)update(nl,nr,c,rs(at));        push_up(at);    }    void solve()    {        sort(s+1,s+1+n);        sort(x+1,x+1+n);        int pos=unique(x+1,x+1+n)-x-1;        for(int i=1; i<=n; i++)        {            s[i].l=lb(x+1,x+1+pos,s[i].l)-x;            s[i].r=lb(x+1,x+1+pos,s[i].r)-x-1;        }        build(1,pos,1);        for(int i=1; i<n; i++)        {            update(s[i].l,s[i].r,s[i].mark,1);            ans+=abs(t[1].len-pre);            pre=t[1].len;        }        ans+=x[s[n].r+1]-x[s[n].l];    }}p1,p2;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1,x1,x2,y1,y2; i<=n; i++)    {        cin >> x1 >> y1 >> x2 >> y2;        p1.x[2*i-1]=x1;p1.x[2*i]=x2;        p1.s[2*i-1]={x1,x2,y1,1};        p1.s[2*i]={x1,x2,y2,-1};        p2.x[2*i-1]=y1;p2.x[2*i]=y2;        p2.s[2*i-1]={y1,y2,x1,1};        p2.s[2*i]={y1,y2,x2,-1};    }    n<<=1;    p1.solve();p2.solve();    cout << ans << endl;    return 0;}

        随机增量法

        时间复杂度 \(O(n)\)

        三点确定一个圆,故三层循环

        证: \[\begin{aligned}T_1(n) &= O(n) + \sum\limits_{i=1}^{n}\dfrac{3}{i}T_2(i) \\T_2(n) &= O(n) + \sum\limits_{i=1}^{n}\dfrac{3}{i}T_3(i) \\T_3(n) &= O(n)\\\\\therefore T_1(n)&=T_2(n)=T_3(n)=O(n)\end{aligned}\] 例题:P1742最小圆覆盖

        #include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)#define double long doubleint n;const double eps=1e-10;#define pf(x) ((x)*(x))struct vct{double x,y;}p[N],C;vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double dis(vct a,vct b){return sqrt(pf(a.x-b.x)+pf(a.y-b.y));}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}vct rot(vct a){return {-a.y,a.x};}struct line{    vct p,way;    void mkline(vct a,vct b){p=a;way=b;}    void mk(double x1,double y1,double x2,double y2)    {mkline({x1,y1},{x2-x1,y2-y1});}};vct intersect(line a,line b){    double t=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*t;}vct getcircle(vct a,vct b,vct c){    line p,q;    p.mkline((a+b)/2,rot(b-a));    q.mkline((a+c)/2,rot(c-a));    return intersect(p,q);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);vct p=(a.p+b.p)/2;    cout << fixed << setprecision(10);    mt19937_64 rd(time(0));    cin >> n;    for(int i=1; i<=n; i++)        cin >> p[i].x >> p[i].y;    shuffle(p+1,p+1+n,rd);    double r=0;    for(int i=1; i<=n; i++) if(len(C-p[i])>r)    {        C=p[i];r=0;                for(int j=1; j<i; j++) if(len(C-p[j])>r)        {            C=(p[i]+p[j])/2;            r=len(C-p[j]);                        for(int k=1; k<j; k++) if(len(C-p[k])>r)            {                C=getcircle(p[i],p[j],p[k]);                r=len(C-p[k]);            }        }    }    cout << r << endl;    cout << C.x << " " << C.y << endl;    return 0;}
        ]]> @@ -2666,7 +2666,7 @@ /2022/07/19/oi-mo-ban-shu-ju-jie-gou/ - OI模板-数据结构

      并查集

      @Roundgod老师说

      按秩合并+路径压缩才能保证并查集查询的复杂度为 $O(\alpha(n))$

      只用一个就是 $O(\log n)$ 的,不过要特意构造数据卡才会到这个上界

      int f[N],r[N]; // r[] 为深度void init(int n){for(int i=1; i<=n; i++) f[i]=i,r[i]=0;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int x,int y){    x=find(x); y=find(y);    if(x==y) return;    if(r[x]<r[y]) f[x]=y;    else f[y]=x,r[x]+=r[x]==r[y];}

      种类并查集

      P2024 食物链

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)int n,Q,f[N<<2];void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> Q;    int res=0; init(3*n);    for(int op,x,y; Q--;)    {        cin >> op >> x >> y;        if(x>n||y>n){++res; continue;}        if(op==1)        {            if(find(x)==find(y+n)||find(x)==find(y+2*n)){++res; continue;}            merge(x,y); merge(x+n,y+n); merge(x+2*n,y+2*n);        }else        {            if(find(x)==find(y)||find(x)==find(y+n)){++res; continue;}            merge(x,y+2*n); merge(x+n,y); merge(x+2*n,y+n);        }    }    cout << res << '\n';    return 0;}

      带权并查集

      待写


      单调队列

      时间复杂度 $O(n)$

      空间复杂度 $O(n)$

      手写版本(20220514)

      队列用的 (l,r] 写法

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)int n,k,a[N],st,en,q[N];void solve(int n,int len,int f){    st=en=0;    for(int i=1; i<=n; i++)    {        if(f==1)while(st<en&&a[i]<a[q[en]])--en;        if(f==2)while(st<en&&a[i]>a[q[en]])--en;        q[++en]=i;        while(st<en&&q[st+1]+len<=i)++st;        if(i>=len)write(a[q[st+1]]),pc(" \n"[i==n]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(k);    for(int i=1; i<=n; i++)        read(a[i]);    solve(n,k,1); // min    solve(n,k,2); // max    return 0;}

      stl版本(20211022)

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define MAXN (int)(1e6+5)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}struct node{    int id,num; // 下标,数};int n,k;int a[MAXN];deque<node> q1,q2;signed main(){    read(n);read(k);    for(int i=1; i<=n; i++)        read(a[i]);    for(int i=1; i<=n; i++) // min    {        while(!q1.empty()&&a[i]<q1.back().num)q1.pop_back();        q1.push_back({i,a[i]});        while(!q1.empty()&&q1.front().id<=i-k)q1.pop_front();        if(i>=k){write(q1.front().num);pc(" \n"[i==n]);}    }    for(int i=1; i<=n; i++) // max    {        while(!q2.empty()&&a[i]>q2.back().num)q2.pop_back();        q2.push_back({i,a[i]});        while(!q2.empty()&&q2.front().id<=i-k)q2.pop_front();        if(i>=k){write(q2.front().num);pc(" \n"[i==n]);}    }    return 0;}

      二叉堆

      时间复杂度 $O(n\log n)$

      空间复杂度 $O(m^2)$

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long longnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e6+15)int n,d[N],pos;void push(int x){for(int f=x>>1;f>0&&d[f]>d[x];swap(d[f],d[x]),x=f,f>>=1);}void pop(int x){for(int s=x<<1; s<=pos;){if(s<pos&&d[s]>d[s|1])s|=1; // 左右中最大的if(d[x]>d[s])swap(d[x],d[s]),x=s,s<<=1; // 去掉中间的else break;}}signed main(){read(n);for(int i=1; i<=n; i++){int op,x;read(op);if(op==1){read(x);d[++pos]=x;push(pos);}if(op==2){write(d[1]);pc('\n');}if(op==3){swap(d[1],d[pos]);--pos;pop(1);}}return 0;}

      左偏树(可并堆)

      时间复杂度 $O(n\log n)$

      写法1

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)int n,m;int ls[N],rs[N],del[N],dist[N],rt[N];struct node{    int num,id;    bool operator<(node &o)const    {        if(num==o.num)return id<o.id;        return num<o.num;    }}v[N];int merge(int x,int y){    if(!x||!y)return x|y;    if(v[y]<v[x])swap(x,y);    rs[x]=merge(rs[x],y);    if(dist[rs[x]]>dist[ls[x]])swap(ls[x],rs[x]);    dist[x]=dist[rs[x]]+1;    return x;}int find(int x){return rt[x]==x?x:rt[x]=find(rt[x]);}signed main(){    read(n);read(m);    for(int i=1; i<=n; i++)    {        read(v[i].num);        v[i].id=i;rt[i]=i;    }    while(m--)    {        int op,x,y;        read(op);read(x);        if(op==1)        {            read(y);            if(del[x]||del[y])continue;            x=find(x);y=find(y);            if(x!=y)rt[x]=rt[y]=merge(x,y);        }else        {            if(del[x]){puts("-1");continue;}            x=find(x);del[x]=1;            write(v[x].num);pc('\n');            rt[ls[x]]=rt[rs[x]]=rt[x]=merge(ls[x],rs[x]);            ls[x]=rs[x]=dist[x]=0; // 这句可以不写,这样最多就安全一点(?        }    }    return 0;}

      写法2(封装)

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)#define ls(at) t[at].ch[0]#define rs(at) t[at].ch[1]int n,m;struct node{    int ch[2],num,id,del,dist,rt;    bool operator<(node &o)const    {        if(num==o.num)return id<o.id;        return num<o.num;    }}t[N];int merge(int x,int y){    if(!x||!y)return x|y;    if(t[y]<t[x])swap(x,y);    rs(x)=merge(rs(x),y);    if(t[rs(x)].dist>t[ls(x)].dist)swap(ls(x),rs(x));    t[x].dist=t[rs(x)].dist+1;    return x;}int find(int x){    return t[x].rt==x?x:t[x].rt=find(t[x].rt);}signed main(){    read(n);read(m);    for(int i=1; i<=n; i++)    {        read(t[i].num);        t[i].id=i;t[i].rt=i;    }    while(m--)    {        int op,x,y;        read(op);read(x);        if(op==1)        {            read(y);            if(t[x].del||t[y].del)continue;            x=find(x);y=find(y);            if(x!=y)t[x].rt=t[y].rt=merge(x,y);        }else        {            if(t[x].del){puts("-1");continue;}            x=find(x);t[x].del=1;            write(t[x].num);pc('\n');            t[ls(x)].rt=t[rs(x)].rt=t[x].rt=merge(ls(x),rs(x));        }    }    return 0;}

      珂朵莉树

      #include <bits/stdc++.h>using namespace std;#define int long long#define R register#define mod (int)(1e9+7)#define MAXN (int)(1e5+5)int n,m,seed,vmax;int qpow(R int a,R int b,R int p){R int ans=1,base=a;while(b){if(b&1)ans=(ans%p*base%p)%p;base=(base%p*base%p)%p;b>>=1;}return ans%p;}struct node{int l,r;mutable int v;const bool operator<(const node &o)const{return l<o.l;}};int rnd(){R int ret=seed;seed=(seed*7+13)%mod;return ret;}int a[MAXN];set<node> s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos) return it;it--;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}inline void add(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);for(R set<node>::iterator it=itl; it!=itr; it++)it->v+=k;}inline void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);//[itl,itr)s.insert({l,r,k});}struct Rank{int num,cnt;const bool operator<(const Rank &o)const{return num<o.num;}};inline int rnk(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);vector<Rank> v;for(R set<node>::iterator it=itl; it!=itr; it++)v.push_back({it->v,it->r - it->l +1});sort(v.begin(),v.end());for(R int i=0; i<v.size(); i++)if(v[i].cnt<k)k-=v[i].cnt;else return v[i].num;return -1;}inline int calP(R int l,R int r,R int x,R int y){R int ans=0;set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)ans=(ans%y+qpow(it->v,x,y)%y*(it->r - it->l +1)%y)%y;return ans;}signed main(){scanf("%lld%lld%lld%lld",&n,&m,&seed,&vmax);for(R int i=1; i<=n; i++){a[i]=(rnd()%vmax)+1;s.insert({i,i,a[i]});}for(R int i=1; i<=m; i++){R int op,l,r,x,y;///op=(rnd()%4)+1;l=(rnd()%n)+1;r=(rnd()%n)+1;if(l>r)swap(l,r);if(op==3){x=(rnd()%(r-l+1))+1;}else {x=(rnd()%vmax)+1;}if(op==4){y=(rnd()%vmax)+1;}///if(op==1)add(l,r,x);else if(op==2)assign(l,r,x);else if(op==3)printf("%lld\n",rnk(l,r,x));else printf("%lld\n",calP(l,r,x,y));}return 0;}

      平衡树

      FHQ_Treap

      时间复杂度 $O(n\log n)$

      空间复杂度 $O(n)$

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)#define gc() getchar()#define pc(a) putchar(a)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n;namespace FHQ_Treap{    struct node    {        int ch[2],num,rnd,sz;    }t[N];    int tot,rt;    mt19937_64 rd(time(0));    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    void push_up(int x)    {        t[x].sz=t[ls(x)].sz+t[rs(x)].sz+1;    }    int New(int x)    {        t[++tot]={0,0,x,(int)rd(),1};        return tot;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        if(t[x].rnd<t[y].rnd)        {            rs(x)=merge(rs(x),y);            push_up(x);return x;        }else        {            ls(y)=merge(x,ls(y));            push_up(y);return y;        }    }    void split(int at,int k,int &x,int &y)    {        if(!at)x=y=0;        else        {            if(t[at].num<=k)                x=at,split(rs(at),k,rs(at),y);            else                y=at,split(ls(at),k,x,ls(at));            push_up(at);        }    }    int kth(int at,int x)    {        while(1)        {            if(x<=t[ls(at)].sz)                at=ls(at);            else if(x==t[ls(at)].sz+1)                return at;            else            {                x-=t[ls(at)].sz+1;                at=rs(at);            }        }    }    void insert(int a)    {        int x,y;        split(rt,a,x,y);        rt=merge(merge(x,New(a)),y);    }    void remove(int a)    {        int x,y,z;        split(rt,a,x,z);        split(x,a-1,x,y);        y=merge(ls(y),rs(y));        rt=merge(merge(x,y),z);    }    void getrank(int a)    {        int x,y;        split(rt,a-1,x,y);        write(t[x].sz+1);pc('\n');        rt=merge(x,y);    }    void getkth(int a)    {        write(t[kth(rt,a)].num);        pc('\n');    }    void getpre(int a)    {        int x,y;        split(rt,a-1,x,y);        if(!x)write(-INF);        else write(t[kth(x,t[x].sz)].num);        pc('\n');rt=merge(x,y);    }    void getnext(int a)    {        int x,y;        split(rt,a,x,y);        if(!y)write(INF);        else write(t[kth(y,1)].num);        pc('\n');rt=merge(x,y);    }}signed main(){    using namespace FHQ_Treap;    read(n);    for(int i=1,op,a; i<=n; i++)    {        read(op);read(a);        if(op==1){insert(a);}        if(op==2){remove(a);}        if(op==3){getrank(a);}        if(op==4){getkth(a);}        if(op==5){getpre(a);}        if(op==6){getnext(a);}    }}

      替罪羊树

      小常数还好写

      时间复杂度 $O(n\log n)$

      空间复杂度 $O(n)$

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}namespace BT{    #define N (int)(1e5+15)    struct node    {        int ch[2],num,cnt,s,sz,sd;    }t[N];    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    double alpha=0.7;    int tot,rt;    int tmp[N];    int New(int x)    {        t[++tot]={0,0,x,1,1,1,1};        return tot;    }    void push_up(int at)    {        t[at].s=t[ls(at)].s+t[rs(at)].s+1;        t[at].sz=t[ls(at)].sz+t[rs(at)].sz+t[at].cnt;        t[at].sd=t[ls(at)].sd+t[rs(at)].sd+(t[at].cnt!=0);    }    bool CanR(int at)    {        double x=t[at].s*alpha;        return (t[at].cnt)&&(x<=(double)max(t[ls(at)].s,t[rs(at)].s)||        (double)t[at].sd<=x);    }    void CanR_flatten(int &idx,int at)    {        if(!at)return;        CanR_flatten(idx,ls(at));        if(t[at].cnt)tmp[++idx]=at;        CanR_flatten(idx,rs(at));    }    int CanR_build(int l,int r)    {        if(l>=r)return 0;        int mid=(l+r)>>1;        int &at=tmp[mid];        ls(at)=CanR_build(l,mid);        rs(at)=CanR_build(mid+1,r);        push_up(at);        return at;    }    void rebuild(int &at)    {        int idx=0;        CanR_flatten(idx,at);        at=CanR_build(1,idx+1);    }    void insert(int x,int &at)    {        if(!at){at=New(x);return;}        if(t[at].num==x)++t[at].cnt;        else if(x<t[at].num)insert(x,ls(at));        else insert(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    void remove(int x,int &at)    {        if(!at)return;        if(t[at].num==x)        {            if(t[at].cnt)--t[at].cnt;        }else if(x<t[at].num)remove(x,ls(at));        else remove(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    int getval(int x,int at)    {        if(!at)return INF;        if(x<=t[ls(at)].sz)return getval(x,ls(at));        if(x<=t[ls(at)].sz+t[at].cnt)return t[at].num;        else return getval(x-t[ls(at)].sz-t[at].cnt,rs(at));    }    int uprbd(int x,int at)    {        if(!at)return 1;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz+t[at].cnt+1;        else if(x<t[at].num)return uprbd(x,ls(at));        else return uprbd(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int uprbd_gt(int x,int at)    {        if(!at)return 0;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz;        else if(x<t[at].num)return uprbd_gt(x,ls(at));        else return uprbd_gt(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int getpre(int x,int at){return getval(uprbd_gt(x,at),at);}    int getnext(int x,int at){return getval(uprbd(x,at),at);}}signed main(){using namespace BT;int n;read(n);for(int i=1; i<=n; i++){int op,x;read(op);read(x);if(op==1)insert(x,rt);if(op==2)remove(x,rt);if(op==3){write(uprbd_gt(x,rt)+1);pc('\n');}if(op==4){write(getval(x,rt));pc('\n');}if(op==5){write(getpre(x,rt));pc('\n');}if(op==6){write(getnext(x,rt));pc('\n');}}return 0;}

      Splay

      大常数

      时间复杂度 $O(n\log n)$

      空间复杂度 $O(n)$

      #include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(1e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n;namespace Splay{    struct node    {        int ch[2],num,fa,cnt,sz;    }t[MAXN];    int tot,rt;    int New(int x,int f)    {        t[++tot]={0,0,x,f,1,1};        if(f)t[f].ch[x>t[f].num]=tot;        return tot;    }    void push_up(int at)    {        t[at].sz=t[t[at].ch[0]].sz        +t[t[at].ch[1]].sz+t[at].cnt;    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x,int goal)    {        while(t[x].fa!=goal)        {            int y=t[x].fa;            int z=t[y].fa;            if(z!=goal)            (t[y].ch[1]==x)^(t[z].ch[1]==y)?rotate(x):rotate(y);            rotate(x);        }        if(!goal)rt=x;    }    void find(int x)    {        int at=rt;        if(!at)return;        while(t[at].ch[x>t[at].num]&&x!=t[at].num)            at=t[at].ch[x>t[at].num];        splay(at,0);    }    int get(int x,int f)    {        find(x);        int at=rt;        if(t[at].num<x&&!f)return at;        if(t[at].num>x&&f)return at;        at=t[at].ch[f];        while(t[at].ch[f^1])at=t[at].ch[f^1];        return at;    }    void insert(int x)    {        int at=rt,f=0;        while(at&&x!=t[at].num)            f=at,at=t[at].ch[x>t[at].num];        if(at)++t[at].cnt;        else at=New(x,f);        splay(at,0);    }    void remove(int x)    {        int pre=get(x,0);        int nxt=get(x,1);        splay(pre,0);splay(nxt,pre);        int &del=t[nxt].ch[0];        if(t[del].cnt>1)        {            --t[del].cnt;            splay(del,0);        }else del=0;    }    int kth(int x)    {        int at=rt;        if(t[at].sz<x)            return INF;        while(1)        {            int p=t[at].ch[0];            if(x>t[p].sz+t[at].cnt)            {                x-=t[p].sz+t[at].cnt;                at=t[at].ch[1];            }else            {                if(x<=t[p].sz)                    at=p;                else return t[at].num;            }        }    }}signed main(){    using namespace Splay;    insert(-INF);    insert(INF);    read(n);for(int i=1; i<=n; i++){int op,x;read(op);read(x);if(op==1){insert(x);}if(op==2){remove(x);}if(op==3){find(x);write(t[t[rt].ch[0]].sz);pc('\n');}if(op==4){write(kth(x+1));pc('\n');}if(op==5){write(t[get(x,0)].num);pc('\n');}if(op==6){write(t[get(x,1)].num);pc('\n');}}    return 0;}

      可持久化平衡树

      采用FHQ_Treap

      时间复杂度 $O(n\log n)$

      空间复杂度 $O(n\log n)$

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)#define gc() getchar()#define pc(a) putchar(a)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n;namespace FHQ_Treap{    struct node    {        int ch[2],num,rnd,sz;    }t[N*50];    int tot,T[N];    mt19937_64 rd(time(0));    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    void push_up(int x)    {        t[x].sz=t[ls(x)].sz+t[rs(x)].sz+1;    }    int New(int x)    {        t[++tot]={0,0,x,(int)rd(),1};        return tot;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        if(t[x].rnd<t[y].rnd)        {            int p=++tot;t[p]=t[x];            rs(p)=merge(rs(p),y);            push_up(p);return p;        }else        {            int p=++tot;t[p]=t[y];            ls(p)=merge(x,ls(p));            push_up(p);return p;        }    }    void split(int at,int k,int &x,int &y)    {        if(!at)x=y=0;        else        {            if(t[at].num<=k)            {                x=++tot;t[x]=t[at];                split(rs(x),k,rs(x),y);                push_up(x);            }            else            {                y=++tot;t[y]=t[at];                split(ls(y),k,x,ls(y));                push_up(y);            }        }    }    int kth(int at,int x)    {        while(1)        {            if(x<=t[ls(at)].sz)                at=ls(at);            else if(x==t[ls(at)].sz+1)                return at;            else            {                x-=t[ls(at)].sz+1;                at=rs(at);            }        }    }    void insert(int a,int &rt)    {        int x,y;        split(rt,a,x,y);        rt=merge(merge(x,New(a)),y);    }    void remove(int a,int &rt)    {        int x,y,z;        split(rt,a,x,z);        split(x,a-1,x,y);        y=merge(ls(y),rs(y));        rt=merge(merge(x,y),z);    }    void getrank(int a,int &rt)    {        int x,y;        split(rt,a-1,x,y);        write(t[x].sz+1);pc('\n');        rt=merge(x,y);    }    void getkth(int a,int &rt)    {        write(t[kth(rt,a)].num);        pc('\n');    }    void getpre(int a,int &rt)    {        int x,y;        split(rt,a-1,x,y);        if(!x)write(-2147483647+1);        else write(t[kth(x,t[x].sz)].num);        pc('\n');rt=merge(x,y);    }    void getnext(int a,int &rt)    {        int x,y;        split(rt,a,x,y);        if(!y)write(2147483647-1);        else write(t[kth(y,1)].num);        pc('\n');rt=merge(x,y);    }}signed main(){    using namespace FHQ_Treap;    read(n);    for(int i=1,op,a,now; i<=n; i++)    {        read(now);read(op);read(a);        T[i]=T[now];        if(op==1){insert(a,T[i]);}        if(op==2){remove(a,T[i]);}        if(op==3){getrank(a,T[i]);}        if(op==4){getkth(a,T[i]);}        if(op==5){getpre(a,T[i]);}        if(op==6){getnext(a,T[i]);}    }}

      ST表

      一定要预处理 $\log_2 n$ !! 不然查询的复杂度是假的!

      因为 log() 函数的复杂度不超过log级,但是常数大!

      时间复杂度 $O(n\log n)$

      空间复杂度 $O(n\log n)$

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+5)int n,m,lg[N],f[N][21];void change(int u,int x){f[u][0]=x;for(int i=1; u-(1<<i)>=0; i++)f[u][i]=max(f[u][i-1],f[u-(1<<(i-1))][i-1]);}int query(int l,int r){int k=lg[r-l+1];return max(f[l+(1<<k)-1][k],f[r][k]);}signed main(){read(n);read(m);for(int i=1,x; i<=n; i++){read(x); change(i,x);        lg[i]=(double)log(i)/log(2);}for(int i=1,l,r; i<=m; i++){read(l);read(r);write(query(l,r));pc('\n');}return 0;}

      树链剖分

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+5)struct Edge{int u,v,next;}e[N<<2];int pos=1,n,Q,rt,p,head[N];int id[N],idx,t[N],a[N],fa[N],son[N];int sz[N],top[N],ans[N<<2],tag[N<<2],dep[N];void addEdge(int u,int v){e[pos]={u,v,head[u]};head[u]=pos++;}void dfs(int u,int f,int d){fa[u]=f;sz[u]=1;dep[u]=d;int mx=-1;for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==f)continue;dfs(v,u,d+1);sz[u]+=sz[v];if(sz[v]>mx)mx=sz[v],son[u]=v;}}void dfs(int u,int ftop){id[u]=++idx;top[u]=ftop;a[idx]=t[u];if(!son[u])return;dfs(son[u],ftop);for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==fa[u]||v==son[u])continue;dfs(v,v);}}void push_up(int at){ans[at]=ans[at<<1]+ans[at<<1|1];}void proc(int l,int r,int at){int u=at>>1;ans[at]=(ans[at]+tag[u]*(r-l+1)%p)%p;tag[at]=(tag[at]+tag[u])%p;}void push_down(int l,int r,int at){int mid=(l+r)>>1;proc(l,mid,at<<1);proc(mid+1,r,at<<1|1);tag[at]=0;}void build(int l,int r,int at){tag[at]=0;if(l==r){ans[at]=a[l];return;}int mid=(l+r)>>1;build(l,mid,at<<1);build(mid+1,r,at<<1|1);push_up(at);}void update(int nl,int nr,int l,int r,int k,int at){if(nl<=l&&r<=nr){ans[at]=(ans[at]+k%p*(r-l+1)%p)%p;tag[at]=(tag[at]+k)%p;return;}push_down(l,r,at);int mid=(l+r)>>1;if(nl<=mid)update(nl,nr,l,mid,k,at<<1);if(nr>mid)update(nl,nr,mid+1,r,k,at<<1|1);push_up(at);}int query(int nl,int nr,int l,int r,int at){int res=0;if(nl<=l&&r<=nr){return ans[at]%p;}int mid=(l+r)>>1;push_down(l,r,at);if(nl<=mid)res=(res+query(nl,nr,l,mid,at<<1))%p;if(nr>mid)res=(res+query(nl,nr,mid+1,r,at<<1|1))%p;return res;}void upRange(int x,int y,int k){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);update(id[top[x]],id[x],1,n,k,1);x=fa[top[x]];}if(dep[x]>dep[y])swap(x,y);update(id[x],id[y],1,n,k,1);}int qRange(int x,int y){int res=0;while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);res=(res+query(id[top[x]],id[x],1,n,1))%p;x=fa[top[x]];}if(dep[x]>dep[y])swap(x,y);res=(res+query(id[x],id[y],1,n,1))%p;return res;}void upSon(int x,int k){update(id[x],id[x]+sz[x]-1,1,n,k,1);}int qSon(int x){return query(id[x],id[x]+sz[x]-1,1,n,1)%p;}signed main(){read(n);read(Q);read(rt);read(p);for(int i=1; i<=n; i++)read(t[i]);for(int i=1,u,v; i<n; i++){read(u);read(v);addEdge(u,v);addEdge(v,u);}dfs(rt,0,1);dfs(rt,rt);build(1,n,1);while(Q--){int op,x,y,k;read(op);if(op==1){read(x);read(y);read(k);upRange(x,y,k);}if(op==2){read(x);read(y);write(qRange(x,y));pc('\n');}if(op==3){read(x);read(k);upSon(x,k);}if(op==4){read(x);write(qSon(x));pc('\n');}}return 0;}

      树套树

      树套树 位置线段树套权值平衡树

      P3380 【模板】二逼平衡树(树套树)

      注意二分的方向

      #include <bits/stdc++.h>using namespace std;// #define int long long#define INF 2147483647// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)int n,Q,a[N];namespace BT{    struct node    {        int ch[2],num,cnt,s,sz,sd;    }t[N*35];    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    const double alpha=0.75;    int tot,rt,tmp[N*35];    int New(int x)    {        t[++tot]={0,0,x,1,1,1,1};        return tot;    }    void push_up(int at)    {        t[at].s=t[ls(at)].s+t[rs(at)].s+1;        t[at].sz=t[ls(at)].sz+t[rs(at)].sz+t[at].cnt;        t[at].sd=t[ls(at)].sd+t[rs(at)].sd+(t[at].cnt!=0);    }    bool CanR(int at)    {        double x=t[at].s*alpha;        return (t[at].cnt)        &&(x<=(double)max(t[ls(at)].s,t[rs(at)].s)||(double)t[at].sd<=x);    }    void CanR_flatten(int &idx,int at)    {        if(!at)return;        CanR_flatten(idx,ls(at));        if(t[at].cnt)tmp[++idx]=at;        CanR_flatten(idx,rs(at));    }    int CanR_build(int l,int r)    {        if(l>=r)return 0;        int mid=(l+r)>>1;        int &at=tmp[mid];        ls(at)=CanR_build(l,mid);        rs(at)=CanR_build(mid+1,r);        push_up(at);        return at;    }    void rebuild(int &at)    {        int idx=0;        CanR_flatten(idx,at);        at=CanR_build(1,idx+1);    }    void insert(int x,int &at)    {        if(!at){at=New(x);return;}        if(t[at].num==x)++t[at].cnt;        else if(x<t[at].num)insert(x,ls(at));        else insert(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    void remove(int x,int &at)    {        if(!at)return;        if(t[at].num==x)        {            if(t[at].cnt)--t[at].cnt;        }else if(x<t[at].num)remove(x,ls(at));        else remove(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    int getval(int x,int at)    {        if(!at)return INF;        if(x<=t[ls(at)].sz)return getval(x,ls(at));        if(x<=t[ls(at)].sz+t[at].cnt)return t[at].num;        else return getval(x-t[ls(at)].sz-t[at].cnt,rs(at));    }    int uprbd(int x,int at)    {        if(!at)return 1;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz+t[at].cnt+1;        else if(x<t[at].num)return uprbd(x,ls(at));        else return uprbd(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int uprbd_gt(int x,int at)    {        if(!at)return 0;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz;        else if(x<t[at].num)return uprbd_gt(x,ls(at));        else return uprbd_gt(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int getpre(int x,int at){int res=getval(uprbd_gt(x,at),at);return res==INF?-INF:res;}int getnext(int x,int at){int res=getval(uprbd(x,at),at);return res==INF?INF:res;}}namespace SEG{    struct node    {        int l,r,rt;    }s[N<<2];    #define lc(at) (at<<1)    #define rc(at) (at<<1|1)    void build(int l,int r,int at)    {        s[at].l=l;s[at].r=r;        for(int i=l; i<=r; i++)        BT::insert(a[i],s[at].rt);        if(l==r)return;        int mid=(l+r)>>1;        build(l,mid,lc(at));        build(mid+1,r,rc(at));    }    void update(int pos,int k,int at)    {        BT::remove(a[pos],s[at].rt);        BT::insert(k,s[at].rt);        if(s[at].l==s[at].r)return;        int mid=(s[at].l+s[at].r)>>1;        if(pos<=mid)update(pos,k,lc(at));        else update(pos,k,rc(at));    }    int getrank(int L,int R,int k,int at)    {        int l=s[at].l,r=s[at].r;        if(l>R||r<L)return 0;        if(L<=l&&r<=R)return BT::uprbd_gt(k,s[at].rt);        return getrank(L,R,k,lc(at))+getrank(L,R,k,rc(at));    }    // a=getrank(x),b=getrank(y)    // a=b => x=y    // x=y =/> a=b     // int getkth(int L,int R,int k)    // {    //     int l=0,r=1e8+5;    //     while(l<r)    //     {    //         int mid=(l+r)>>1;    //         if(getrank(L,R,mid,1)+1>=k)r=mid;    //         else l=mid+1;    //     }    //     return l;    // }    int getkth(int L,int R,int k)    {        int l=0,r=1e8+5;        while(l<r)        {            int mid=(l+r+1)>>1;            if(getrank(L,R,mid,1)+1<=k)l=mid;            else r=mid-1;        }        return l;    }    int getpre(int L,int R,int k,int at)    {        int l=s[at].l,r=s[at].r;        if(l>R||r<L)return -INF;        if(L<=l&&r<=R)            return BT::getpre(k,s[at].rt);        return max(getpre(L,R,k,lc(at)),getpre(L,R,k,rc(at)));    }    int getnext(int L,int R,int k,int at)    {        int l=s[at].l,r=s[at].r;        if(l>R||r<L)return INF;        if(L<=l&&r<=R)            return BT::getnext(k,s[at].rt);        return min(getnext(L,R,k,lc(at)),getnext(L,R,k,rc(at)));    }}signed main(){    using namespace SEG;    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    int op,x,y,k;    while(Q--)    {        read(op);read(x);read(y);        if(op==3)        {            update(x,y,1);            a[x]=y;        }else        {            read(k);            if(op==1)write(getrank(x,y,k,1)+1);            else if(op==2)write(getkth(x,y,k));            else if(op==4)write(getpre(x,y,k,1));            else if(op==5)write(getnext(x,y,k,1));            pc('\n');        }    }    return 0;}

      线段树

      线段树1 朴素写法

      P3372 【模板】线段树 1

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)int n,Q;int a[N];struct node{    int sum,tag;}t[N<<2];#define ls(at) (at<<1)#define rs(at) (at<<1|1)void push_up(int at){    t[at].sum=t[ls(at)].sum+t[rs(at)].sum;}void build(int l,int r,int at){    if(l==r)    {        t[at].sum=a[l];        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void proc(int l,int r,int k,int at){    t[at].sum+=k*(r-l+1);    t[at].tag+=k;}void push_down(int l,int r,int at){    if(t[at].tag)    {        int mid=(l+r)>>1;        proc(l,mid,t[at].tag,ls(at));        proc(mid+1,r,t[at].tag,rs(at));    }    t[at].tag=0;}void update(int nl,int nr,int l,int r,int k,int at){    if(nl<=l&&r<=nr)    {        t[at].sum+=k*(r-l+1);        t[at].tag+=k;        return;    }    int mid=(l+r)>>1;    push_down(l,r,at);    if(nl<=mid)update(nl,nr,l,mid,k,ls(at));    if(nr>mid)update(nl,nr,mid+1,r,k,rs(at));    push_up(at);}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        {return t[at].sum;}    int mid=(l+r)>>1,res=0;    push_down(l,r,at);    if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));    if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));    return res;}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    while(Q--)    {        int op,l,r,k;        read(op);        if(op==1)        {            read(l);read(r);read(k);            update(l,r,1,n,k,1);        }else        {            read(l);read(r);            write(query(l,r,1,n,1));pc('\n');        }    }    return 0;}

      还有一种比较好的query写法,如下

      int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        // return ...;    if(nr<=mid)return query(nl,nr,l,mid,ls(at));    if(nl>mid)return query(nl,nr,mid+1,r,rs(at));    return query(nl,nr,l,mid,ls(at))+query(nl,nr,mid+1,r,rs(at));}

      线段树1 标记永久化+动态开点

      P3372 【模板】线段树 1

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e6+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)struct node{    int ch[2],ans,tag;}t[N<<2];int rt,tot;int n,Q,a[N];#define ls(at) t[at].ch[0]#define rs(at) t[at].ch[1]void insert(int nl,int nr,int l,int r,int x,int &at){    if(!at)at=++tot;    t[at].ans+=x*(min(r,nr)-max(l,nl)+1);    if(nl<=l&&r<=nr)    {        t[at].tag+=x;        return;    }    int mid=(l+r)>>1;    if(nl<=mid)insert(nl,nr,l,mid,x,ls(at));    if(nr>mid)insert(nl,nr,mid+1,r,x,rs(at));}void pusht(int l,int r,int x,int &at){    if(!at)at=++tot;    t[at].ans+=(r-l+1)*x;    t[at].tag+=x;}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        return t[at].ans;    int mid=(l+r)>>1;    if(t[at].tag)    {        pusht(l,mid,t[at].tag,ls(at));        pusht(mid+1,r,t[at].tag,rs(at));        t[at].tag=0;    }    int res=0;    if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));    if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));    return res;}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)    {        read(a[i]);        insert(i,i,1,n,a[i],rt);    }    while(Q--)    {        int op,l,r,x;        read(op);read(l);read(r);        if(op==1){read(x);insert(l,r,1,n,x,rt);}        else write(query(l,r,1,n,rt)),pc('\n');    }    return 0;}

      李超线段树

      P4097 [HEOI2013]Segment

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)#define M (int)(4e4+15)#define ls(at) (at<<1)#define rs(at) (at<<1|1)const int mod1=39989;const int mod2=1e9;struct line{    double k,b;}li[N];int bs[M<<2],n,lstans,cnt;double calc(int id,int x){    return li[id].k*x+li[id].b;}void init(int x1,int y1,int x2,int y2){    ++cnt;    if(x1==x2)    {        li[cnt].k=0;        li[cnt].b=max(y1,y2);    }else    {        li[cnt].k=1.0*(y1-y2)/(x1-x2);        li[cnt].b=y1-li[cnt].k*x1;    }}void update(int nl,int nr,int l,int r,int nw,int at){    int u=bs[at];    int mid=(l+r)>>1;    double now=calc(nw,mid),resu=calc(u,mid);    if(nl<=l&&r<=nr)    {        if(l==r)        {            if(now>resu)                bs[at]=nw;            return;        }        if(li[nw].k>li[u].k)        {            if(now>resu)            {                bs[at]=nw;                update(nl,nr,l,mid,u,ls(at));            }else update(nl,nr,mid+1,r,nw,rs(at));        }else if(li[nw].k<li[u].k)        {            if(now>resu)            {                bs[at]=nw;                update(nl,nr,mid+1,r,u,rs(at));            }else update(nl,nr,l,mid,nw,ls(at));        }else if(li[nw].b>li[u].b)bs[at]=nw;        return;    }    if(nl<=mid)update(nl,nr,l,mid,nw,ls(at));    if(nr>mid)update(nl,nr,mid+1,r,nw,rs(at));}struct node{double v;int x;};node max(node a,node b){    if(a.v==b.v)return a.x<b.x?a:b;    return a.v>b.v?a:b;}node query(int l,int r,int x,int at){    if(r<x||l>x)return {0,0};    int mid=(l+r)>>1;    node tmp={calc(bs[at],x),bs[at]};    if(l==r)return tmp;    return max(tmp,max(query(l,mid,x,ls(at)),query(mid+1,r,x,rs(at))));}#define change1(x) (x=(x+lstans-1+mod1)%mod1+1)#define change2(x) (x=(x+lstans-1+mod2)%mod2+1)signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    int op,x1,y1,x2,y2;    for(int i=1; i<=n; i++)    {        read(op);        if(op==1)        {            read(x1);read(y1);read(x2);read(y2);            change1(x1);change1(x2);change2(y1);change2(y2);            if(x1>x2)swap(x1,x2),swap(y1,y2);            init(x1,y1,x2,y2);update(x1,x2,1,M,cnt,1);        }else        {            int x;read(x);change1(x);            write(lstans=query(1,M,x,1).x);pc('\n');        }    }    return 0;}

      线段树分裂

      P5494 【模板】线段树分裂

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e5+15)int n,Q,tot,rcnt,seq=1;int bac[N*35],ch[N*35][2],rt[N],val[N*35];int newnod(){return rcnt?bac[rcnt--]:++tot;}#define ls(at) ch[at][0]#define rs(at) ch[at][1]void del(int at){    bac[++rcnt]=at;    ls(at)=rs(at)=val[at]=0;}void modify(int l,int r,int pos,int v,int &at){    if(!at)at=newnod();    val[at]+=v;    if(l==r)return;    int mid=(l+r)>>1;    if(pos<=mid)modify(l,mid,pos,v,ls(at));    else modify(mid+1,r,pos,v,rs(at));}int query(int nl,int nr,int l,int r,int at){    if(nr<l||r<nl)return 0;    if(nl<=l&&r<=nr)return val[at];    int mid=(l+r)>>1;    return query(nl,nr,l,mid,ls(at))+query(nl,nr,mid+1,r,rs(at));}int kth(int l,int r,int k,int at){    if(l==r)return l;    int mid=(l+r)>>1;    if(k<=val[ls(at)])return kth(l,mid,k,ls(at));    else return kth(mid+1,r,k-val[ls(at)],rs(at));}int merge(int x,int y){    if(!x||!y)return x|y;    val[x]+=val[y];    ls(x)=merge(ls(x),ls(y));    rs(x)=merge(rs(x),rs(y));    del(y);    return x;}void split(int x,int &y,int k){    if(!x)return;    y=newnod();    int v=val[ls(x)];    if(k>v)split(rs(x),rs(y),k-v);    else    {        swap(rs(x),rs(y));        if(k<v)split(ls(x),ls(y),k);    }    val[y]=val[x]-k;    val[x]=k;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    read(n);read(Q);    for(int i=1,x; i<=n; i++)    {        read(x);        modify(1,n,i,x,rt[1]);    }    int op,x,y,z;    while(Q--)    {        read(op);        if(op==0)        {            read(x);read(y);read(z);            int k1=query(1,z,1,n,rt[x]);            int k2=query(y,z,1,n,rt[x]);            int tmp=0;            split(rt[x],rt[++seq],k1-k2);            split(rt[seq],tmp,k2);            rt[x]=merge(rt[x],tmp);        }else if(op==1)        {            read(x);read(y);            rt[x]=merge(rt[x],rt[y]);        }else if(op==2)        {            read(x);read(y);read(z);            modify(1,n,z,y,rt[x]);        }else if(op==3)        {            read(x);read(y);read(z);            write(query(y,z,1,n,rt[x]));pc('\n');        }else if(op==4)        {            read(x);read(y);            if(val[rt[x]]<y){puts("-1");continue;}            write(kth(1,n,y,rt[x]));pc('\n');        }    }    return 0;}

      静态仙人掌

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void print(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(4e4+15)int n,m,Q,dfncnt,ext;int dfn[N],low[N],fa[N],ans;int top[N],son[N],sz[N],dep[N],b[N],sum[N],dis[N];struct Edge{    int v,w;};vector<Edge> vec[N],exv[N];int find(int u,int f){    int res;    while(top[u]!=top[f])    {        res=top[u];        u=fa[top[u]];    }    return u==f?res:son[f];}int lca(int x,int y){    while(top[x]!=top[y])    {        if(dep[top[x]]<dep[top[y]])swap(x,y);        x=fa[top[x]];    }    return dep[x]<dep[y]?x:y;}void dfs(int u,int f,int d){    dep[u]=d+1;fa[u]=f;sz[u]=1;    int mx=-1;    for(int i=0; i<exv[u].size(); i++)    {        int v=exv[u][i].v;        if(v==f)continue;        dis[v]=dis[u]+exv[u][i].w;        dfs(v,u,d+1);        sz[u]+=sz[v];        if(sz[v]>mx)mx=sz[v],son[u]=v;    }}void dfs(int u,int ftop){    top[u]=ftop;    if(!son[u])return;    dfs(son[u],ftop);    for(int i=0; i<exv[u].size(); i++)    {        int v=exv[u][i].v;        if(v==fa[u]||v==son[u])            continue;        dfs(v,v);    }}void proc(int u,int v,int w){    ++ext;    int pw,pre=w,x=v;    while(x!=fa[u])    {        sum[x]=pre;        pre+=b[x];        x=fa[x];    }    sum[ext]=sum[u];    sum[u]=0;x=v;    while(x!=fa[u])    {        pw=min(sum[x],sum[ext]-sum[x]);        exv[ext].push_back({x,pw});        exv[x].push_back({ext,pw});        x=fa[x];    }}void tarjan(int u,int f){    dfn[u]=low[u]=++dfncnt;    for(int i=0; i<vec[u].size(); i++)    {        int v=vec[u][i].v,w=vec[u][i].w;        if(v==f)continue;        if(!dfn[v])        {            fa[v]=u;b[v]=w;            tarjan(v,u);            low[u]=min(low[u],low[v]);        }else low[u]=min(low[u],dfn[v]);        if(low[v]>dfn[u])        {            exv[u].push_back({v,w});            exv[v].push_back({u,w});        }    }    for(int i=0; i<vec[u].size(); i++)    {        int v=vec[u][i].v;        if(fa[v]!=u&&dfn[v]>dfn[u])            proc(u,v,vec[u][i].w);    }}signed main(){    read(n);read(m);read(Q);ext=n;    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        vec[u].push_back({v,w});        vec[v].push_back({u,w});    }    tarjan(1,0);dfs(1,0,1);dfs(1,1);    while(Q--)    {        int u,v,p,A,B;read(u);read(v);        p=lca(u,v);        if(p<=n)ans=dis[u]+dis[v]-(dis[p]<<1);        else        {            A=find(u,p);B=find(v,p);            ans=dis[u]+dis[v]-dis[A]-dis[B];            int tmp=abs(sum[A]-sum[B]);            ans+=min(tmp,sum[p]-tmp);        }        print(ans);pc('\n');    }    return 0;}

      可持久化数组

      1. 主席树实现被卡了long long

      时间复杂度 $O(n\log n)$

      #include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+25)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e6+5)int n,Q;int a[N],T[N],tot;struct node{    int ch[2],num;}t[N<<5];#define ls(at) t[at].ch[0]#define rs(at) t[at].ch[1]int build(int l,int r){    int at=++tot,mid=(l+r)>>1;    if(l==r){t[at].num=a[l];return at;}    ls(at)=build(l,mid);    rs(at)=build(mid+1,r);    return at;}int update(int pre,int l,int r,int x,int v){    int at=++tot,mid=(l+r)>>1;    t[at]=t[pre];    if(l==r)    {        t[at].num=v;        return at;    }    if(x<=mid)ls(at)=update(ls(pre),l,mid,x,v);    else rs(at)=update(rs(pre),mid+1,r,x,v);    return at;}int query(int at,int l,int r,int x){    if(l==r)return t[at].num;    int mid=(l+r)>>1;    if(x<=mid)return query(ls(at),l,mid,x);    else return query(rs(at),mid+1,r,x);}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    T[0]=build(1,n);    for(int i=1; i<=Q; i++)    {        int rt,op,x,y;        read(rt);read(op);read(x);        if(op==1)        {            read(y);            T[i]=update(T[rt],1,n,x,y);        }else        {            write(query(T[rt],1,n,x));            pc('\n');T[i]=T[rt];        }    }    return 0;}
      1. Elegia神仙的神仙解法(离线解法)

        根据依赖关系,且可逆操作,建关系树,然后跑dfs

        时间复杂度 $O(n)$

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+25)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e6+5)int n,Q;int a[N],op[N],k[N],x[N],ans[N];vector<int>vec[N];void dfs(int u){    if(op[u]==2)    {        ans[u]=a[k[u]];        for(int v:vec[u])dfs(v);    }else    {        swap(a[k[u]],x[u]);        for(int v:vec[u])dfs(v);        swap(a[k[u]],x[u]);    }}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    op[0]=2;    for(int i=1; i<=Q; i++)    {        int rt;        read(rt);read(op[i]);read(k[i]);        if(op[i]==1)            read(x[i]);        vec[rt].push_back(i);    }    dfs(0);    for(int i=1; i<=Q; i++)        if(op[i]==2)            write(ans[i]),pc('\n');    return 0;}

      可持久化线段树(主席树)

      区间 $k$ 小值

      时间复杂度 $O(n\log n)$

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+25)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e5+5)int n,m,Q;int a[N],b[N];int T[N],tot;struct node{    int ch[2],sum;}t[N<<5];#define ls(at) t[at].ch[0]#define rs(at) t[at].ch[1]int build(int l,int r){    int at=++tot,mid=(l+r)>>1;    if(l<r)    {        ls(at)=build(l,mid);        rs(at)=build(mid+1,r);    }    return at;}int update(int pre,int l,int r,int x){    int at=++tot,mid=(l+r)>>1;    t[at]=t[pre];++t[at].sum;    if(l<r)    {        if(x<=mid)ls(at)=update(ls(pre),l,mid,x);        else rs(at)=update(rs(pre),mid+1,r,x);    }    return at;}int query(int u,int v,int l,int r,int k){    if(l==r)return l;    int x=t[ls(v)].sum-t[ls(u)].sum;    int mid=(l+r)>>1;    if(x>=k)return query(ls(u),ls(v),l,mid,k);    else return query(rs(u),rs(v),mid+1,r,k-x);}void init(){    sort(b+1,b+1+n);    m=unique(b+1,b+1+n)-b-1;    T[0]=build(1,m);    for(int i=1; i<=n; i++)    {        a[i]=lower_bound(b+1,b+1+m,a[i])-b;        T[i]=update(T[i-1],1,m,a[i]);    }}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]),b[i]=a[i];    init();    while(Q--)    {        int l,r,k;read(l);read(r);read(k);        write(b[query(T[l-1],T[r],1,m,k)]);        pc('\n');    }    return 0;}
      ]]> + OI模板-数据结构

      并查集

      @Roundgod老师说

      按秩合并+路径压缩才能保证并查集查询的复杂度为 \(O(\alpha(n))\)

      只用一个就是 \(O(\log n)\)的,不过要特意构造数据卡才会到这个上界

      int f[N],r[N]; // r[] 为深度void init(int n){for(int i=1; i<=n; i++) f[i]=i,r[i]=0;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int x,int y){    x=find(x); y=find(y);    if(x==y) return;    if(r[x]<r[y]) f[x]=y;    else f[y]=x,r[x]+=r[x]==r[y];}

      种类并查集

      P2024 食物链

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)int n,Q,f[N<<2];void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> Q;    int res=0; init(3*n);    for(int op,x,y; Q--;)    {        cin >> op >> x >> y;        if(x>n||y>n){++res; continue;}        if(op==1)        {            if(find(x)==find(y+n)||find(x)==find(y+2*n)){++res; continue;}            merge(x,y); merge(x+n,y+n); merge(x+2*n,y+2*n);        }else        {            if(find(x)==find(y)||find(x)==find(y+n)){++res; continue;}            merge(x,y+2*n); merge(x+n,y); merge(x+2*n,y+n);        }    }    cout << res << '\n';    return 0;}

      带权并查集

      待写


      单调队列

      时间复杂度 \(O(n)\)

      空间复杂度 \(O(n)\)

      手写版本(20220514)

      队列用的 (l,r] 写法

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)int n,k,a[N],st,en,q[N];void solve(int n,int len,int f){    st=en=0;    for(int i=1; i<=n; i++)    {        if(f==1)while(st<en&&a[i]<a[q[en]])--en;        if(f==2)while(st<en&&a[i]>a[q[en]])--en;        q[++en]=i;        while(st<en&&q[st+1]+len<=i)++st;        if(i>=len)write(a[q[st+1]]),pc(" \n"[i==n]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(k);    for(int i=1; i<=n; i++)        read(a[i]);    solve(n,k,1); // min    solve(n,k,2); // max    return 0;}

      stl版本(20211022)

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define MAXN (int)(1e6+5)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}struct node{    int id,num; // 下标,数};int n,k;int a[MAXN];deque<node> q1,q2;signed main(){    read(n);read(k);    for(int i=1; i<=n; i++)        read(a[i]);    for(int i=1; i<=n; i++) // min    {        while(!q1.empty()&&a[i]<q1.back().num)q1.pop_back();        q1.push_back({i,a[i]});        while(!q1.empty()&&q1.front().id<=i-k)q1.pop_front();        if(i>=k){write(q1.front().num);pc(" \n"[i==n]);}    }    for(int i=1; i<=n; i++) // max    {        while(!q2.empty()&&a[i]>q2.back().num)q2.pop_back();        q2.push_back({i,a[i]});        while(!q2.empty()&&q2.front().id<=i-k)q2.pop_front();        if(i>=k){write(q2.front().num);pc(" \n"[i==n]);}    }    return 0;}

      二叉堆

      时间复杂度 \(O(n\log n)\)

      空间复杂度 \(O(m^2)\)

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long longnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e6+15)int n,d[N],pos;void push(int x){for(int f=x>>1;f>0&&d[f]>d[x];swap(d[f],d[x]),x=f,f>>=1);}void pop(int x){for(int s=x<<1; s<=pos;){if(s<pos&&d[s]>d[s|1])s|=1; // 左右中最大的if(d[x]>d[s])swap(d[x],d[s]),x=s,s<<=1; // 去掉中间的else break;}}signed main(){read(n);for(int i=1; i<=n; i++){int op,x;read(op);if(op==1){read(x);d[++pos]=x;push(pos);}if(op==2){write(d[1]);pc('\n');}if(op==3){swap(d[1],d[pos]);--pos;pop(1);}}return 0;}

      左偏树(可并堆)

      时间复杂度 \(O(n\log n)\)

      写法1

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)int n,m;int ls[N],rs[N],del[N],dist[N],rt[N];struct node{    int num,id;    bool operator<(node &o)const    {        if(num==o.num)return id<o.id;        return num<o.num;    }}v[N];int merge(int x,int y){    if(!x||!y)return x|y;    if(v[y]<v[x])swap(x,y);    rs[x]=merge(rs[x],y);    if(dist[rs[x]]>dist[ls[x]])swap(ls[x],rs[x]);    dist[x]=dist[rs[x]]+1;    return x;}int find(int x){return rt[x]==x?x:rt[x]=find(rt[x]);}signed main(){    read(n);read(m);    for(int i=1; i<=n; i++)    {        read(v[i].num);        v[i].id=i;rt[i]=i;    }    while(m--)    {        int op,x,y;        read(op);read(x);        if(op==1)        {            read(y);            if(del[x]||del[y])continue;            x=find(x);y=find(y);            if(x!=y)rt[x]=rt[y]=merge(x,y);        }else        {            if(del[x]){puts("-1");continue;}            x=find(x);del[x]=1;            write(v[x].num);pc('\n');            rt[ls[x]]=rt[rs[x]]=rt[x]=merge(ls[x],rs[x]);            ls[x]=rs[x]=dist[x]=0; // 这句可以不写,这样最多就安全一点(?        }    }    return 0;}

      写法2(封装)

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)#define ls(at) t[at].ch[0]#define rs(at) t[at].ch[1]int n,m;struct node{    int ch[2],num,id,del,dist,rt;    bool operator<(node &o)const    {        if(num==o.num)return id<o.id;        return num<o.num;    }}t[N];int merge(int x,int y){    if(!x||!y)return x|y;    if(t[y]<t[x])swap(x,y);    rs(x)=merge(rs(x),y);    if(t[rs(x)].dist>t[ls(x)].dist)swap(ls(x),rs(x));    t[x].dist=t[rs(x)].dist+1;    return x;}int find(int x){    return t[x].rt==x?x:t[x].rt=find(t[x].rt);}signed main(){    read(n);read(m);    for(int i=1; i<=n; i++)    {        read(t[i].num);        t[i].id=i;t[i].rt=i;    }    while(m--)    {        int op,x,y;        read(op);read(x);        if(op==1)        {            read(y);            if(t[x].del||t[y].del)continue;            x=find(x);y=find(y);            if(x!=y)t[x].rt=t[y].rt=merge(x,y);        }else        {            if(t[x].del){puts("-1");continue;}            x=find(x);t[x].del=1;            write(t[x].num);pc('\n');            t[ls(x)].rt=t[rs(x)].rt=t[x].rt=merge(ls(x),rs(x));        }    }    return 0;}

      珂朵莉树

      #include <bits/stdc++.h>using namespace std;#define int long long#define R register#define mod (int)(1e9+7)#define MAXN (int)(1e5+5)int n,m,seed,vmax;int qpow(R int a,R int b,R int p){R int ans=1,base=a;while(b){if(b&1)ans=(ans%p*base%p)%p;base=(base%p*base%p)%p;b>>=1;}return ans%p;}struct node{int l,r;mutable int v;const bool operator<(const node &o)const{return l<o.l;}};int rnd(){R int ret=seed;seed=(seed*7+13)%mod;return ret;}int a[MAXN];set<node> s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos) return it;it--;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}inline void add(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);for(R set<node>::iterator it=itl; it!=itr; it++)it->v+=k;}inline void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);//[itl,itr)s.insert({l,r,k});}struct Rank{int num,cnt;const bool operator<(const Rank &o)const{return num<o.num;}};inline int rnk(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);vector<Rank> v;for(R set<node>::iterator it=itl; it!=itr; it++)v.push_back({it->v,it->r - it->l +1});sort(v.begin(),v.end());for(R int i=0; i<v.size(); i++)if(v[i].cnt<k)k-=v[i].cnt;else return v[i].num;return -1;}inline int calP(R int l,R int r,R int x,R int y){R int ans=0;set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)ans=(ans%y+qpow(it->v,x,y)%y*(it->r - it->l +1)%y)%y;return ans;}signed main(){scanf("%lld%lld%lld%lld",&n,&m,&seed,&vmax);for(R int i=1; i<=n; i++){a[i]=(rnd()%vmax)+1;s.insert({i,i,a[i]});}for(R int i=1; i<=m; i++){R int op,l,r,x,y;///op=(rnd()%4)+1;l=(rnd()%n)+1;r=(rnd()%n)+1;if(l>r)swap(l,r);if(op==3){x=(rnd()%(r-l+1))+1;}else {x=(rnd()%vmax)+1;}if(op==4){y=(rnd()%vmax)+1;}///if(op==1)add(l,r,x);else if(op==2)assign(l,r,x);else if(op==3)printf("%lld\n",rnk(l,r,x));else printf("%lld\n",calP(l,r,x,y));}return 0;}

      平衡树

      FHQ_Treap

      时间复杂度 \(O(n\log n)\)

      空间复杂度 \(O(n)\)

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)#define gc() getchar()#define pc(a) putchar(a)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n;namespace FHQ_Treap{    struct node    {        int ch[2],num,rnd,sz;    }t[N];    int tot,rt;    mt19937_64 rd(time(0));    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    void push_up(int x)    {        t[x].sz=t[ls(x)].sz+t[rs(x)].sz+1;    }    int New(int x)    {        t[++tot]={0,0,x,(int)rd(),1};        return tot;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        if(t[x].rnd<t[y].rnd)        {            rs(x)=merge(rs(x),y);            push_up(x);return x;        }else        {            ls(y)=merge(x,ls(y));            push_up(y);return y;        }    }    void split(int at,int k,int &x,int &y)    {        if(!at)x=y=0;        else        {            if(t[at].num<=k)                x=at,split(rs(at),k,rs(at),y);            else                y=at,split(ls(at),k,x,ls(at));            push_up(at);        }    }    int kth(int at,int x)    {        while(1)        {            if(x<=t[ls(at)].sz)                at=ls(at);            else if(x==t[ls(at)].sz+1)                return at;            else            {                x-=t[ls(at)].sz+1;                at=rs(at);            }        }    }    void insert(int a)    {        int x,y;        split(rt,a,x,y);        rt=merge(merge(x,New(a)),y);    }    void remove(int a)    {        int x,y,z;        split(rt,a,x,z);        split(x,a-1,x,y);        y=merge(ls(y),rs(y));        rt=merge(merge(x,y),z);    }    void getrank(int a)    {        int x,y;        split(rt,a-1,x,y);        write(t[x].sz+1);pc('\n');        rt=merge(x,y);    }    void getkth(int a)    {        write(t[kth(rt,a)].num);        pc('\n');    }    void getpre(int a)    {        int x,y;        split(rt,a-1,x,y);        if(!x)write(-INF);        else write(t[kth(x,t[x].sz)].num);        pc('\n');rt=merge(x,y);    }    void getnext(int a)    {        int x,y;        split(rt,a,x,y);        if(!y)write(INF);        else write(t[kth(y,1)].num);        pc('\n');rt=merge(x,y);    }}signed main(){    using namespace FHQ_Treap;    read(n);    for(int i=1,op,a; i<=n; i++)    {        read(op);read(a);        if(op==1){insert(a);}        if(op==2){remove(a);}        if(op==3){getrank(a);}        if(op==4){getkth(a);}        if(op==5){getpre(a);}        if(op==6){getnext(a);}    }}

      替罪羊树

      小常数还好写

      时间复杂度 \(O(n\log n)\)

      空间复杂度 \(O(n)\)

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}namespace BT{    #define N (int)(1e5+15)    struct node    {        int ch[2],num,cnt,s,sz,sd;    }t[N];    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    double alpha=0.7;    int tot,rt;    int tmp[N];    int New(int x)    {        t[++tot]={0,0,x,1,1,1,1};        return tot;    }    void push_up(int at)    {        t[at].s=t[ls(at)].s+t[rs(at)].s+1;        t[at].sz=t[ls(at)].sz+t[rs(at)].sz+t[at].cnt;        t[at].sd=t[ls(at)].sd+t[rs(at)].sd+(t[at].cnt!=0);    }    bool CanR(int at)    {        double x=t[at].s*alpha;        return (t[at].cnt)&&(x<=(double)max(t[ls(at)].s,t[rs(at)].s)||        (double)t[at].sd<=x);    }    void CanR_flatten(int &idx,int at)    {        if(!at)return;        CanR_flatten(idx,ls(at));        if(t[at].cnt)tmp[++idx]=at;        CanR_flatten(idx,rs(at));    }    int CanR_build(int l,int r)    {        if(l>=r)return 0;        int mid=(l+r)>>1;        int &at=tmp[mid];        ls(at)=CanR_build(l,mid);        rs(at)=CanR_build(mid+1,r);        push_up(at);        return at;    }    void rebuild(int &at)    {        int idx=0;        CanR_flatten(idx,at);        at=CanR_build(1,idx+1);    }    void insert(int x,int &at)    {        if(!at){at=New(x);return;}        if(t[at].num==x)++t[at].cnt;        else if(x<t[at].num)insert(x,ls(at));        else insert(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    void remove(int x,int &at)    {        if(!at)return;        if(t[at].num==x)        {            if(t[at].cnt)--t[at].cnt;        }else if(x<t[at].num)remove(x,ls(at));        else remove(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    int getval(int x,int at)    {        if(!at)return INF;        if(x<=t[ls(at)].sz)return getval(x,ls(at));        if(x<=t[ls(at)].sz+t[at].cnt)return t[at].num;        else return getval(x-t[ls(at)].sz-t[at].cnt,rs(at));    }    int uprbd(int x,int at)    {        if(!at)return 1;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz+t[at].cnt+1;        else if(x<t[at].num)return uprbd(x,ls(at));        else return uprbd(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int uprbd_gt(int x,int at)    {        if(!at)return 0;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz;        else if(x<t[at].num)return uprbd_gt(x,ls(at));        else return uprbd_gt(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int getpre(int x,int at){return getval(uprbd_gt(x,at),at);}    int getnext(int x,int at){return getval(uprbd(x,at),at);}}signed main(){using namespace BT;int n;read(n);for(int i=1; i<=n; i++){int op,x;read(op);read(x);if(op==1)insert(x,rt);if(op==2)remove(x,rt);if(op==3){write(uprbd_gt(x,rt)+1);pc('\n');}if(op==4){write(getval(x,rt));pc('\n');}if(op==5){write(getpre(x,rt));pc('\n');}if(op==6){write(getnext(x,rt));pc('\n');}}return 0;}

      Splay

      大常数

      时间复杂度 \(O(n\log n)\)

      空间复杂度 \(O(n)\)

      #include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(1e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n;namespace Splay{    struct node    {        int ch[2],num,fa,cnt,sz;    }t[MAXN];    int tot,rt;    int New(int x,int f)    {        t[++tot]={0,0,x,f,1,1};        if(f)t[f].ch[x>t[f].num]=tot;        return tot;    }    void push_up(int at)    {        t[at].sz=t[t[at].ch[0]].sz        +t[t[at].ch[1]].sz+t[at].cnt;    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x,int goal)    {        while(t[x].fa!=goal)        {            int y=t[x].fa;            int z=t[y].fa;            if(z!=goal)            (t[y].ch[1]==x)^(t[z].ch[1]==y)?rotate(x):rotate(y);            rotate(x);        }        if(!goal)rt=x;    }    void find(int x)    {        int at=rt;        if(!at)return;        while(t[at].ch[x>t[at].num]&&x!=t[at].num)            at=t[at].ch[x>t[at].num];        splay(at,0);    }    int get(int x,int f)    {        find(x);        int at=rt;        if(t[at].num<x&&!f)return at;        if(t[at].num>x&&f)return at;        at=t[at].ch[f];        while(t[at].ch[f^1])at=t[at].ch[f^1];        return at;    }    void insert(int x)    {        int at=rt,f=0;        while(at&&x!=t[at].num)            f=at,at=t[at].ch[x>t[at].num];        if(at)++t[at].cnt;        else at=New(x,f);        splay(at,0);    }    void remove(int x)    {        int pre=get(x,0);        int nxt=get(x,1);        splay(pre,0);splay(nxt,pre);        int &del=t[nxt].ch[0];        if(t[del].cnt>1)        {            --t[del].cnt;            splay(del,0);        }else del=0;    }    int kth(int x)    {        int at=rt;        if(t[at].sz<x)            return INF;        while(1)        {            int p=t[at].ch[0];            if(x>t[p].sz+t[at].cnt)            {                x-=t[p].sz+t[at].cnt;                at=t[at].ch[1];            }else            {                if(x<=t[p].sz)                    at=p;                else return t[at].num;            }        }    }}signed main(){    using namespace Splay;    insert(-INF);    insert(INF);    read(n);for(int i=1; i<=n; i++){int op,x;read(op);read(x);if(op==1){insert(x);}if(op==2){remove(x);}if(op==3){find(x);write(t[t[rt].ch[0]].sz);pc('\n');}if(op==4){write(kth(x+1));pc('\n');}if(op==5){write(t[get(x,0)].num);pc('\n');}if(op==6){write(t[get(x,1)].num);pc('\n');}}    return 0;}

      可持久化平衡树

      采用FHQ_Treap

      时间复杂度 \(O(n\log n)\)

      空间复杂度 \(O(n\log n)\)

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)#define gc() getchar()#define pc(a) putchar(a)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n;namespace FHQ_Treap{    struct node    {        int ch[2],num,rnd,sz;    }t[N*50];    int tot,T[N];    mt19937_64 rd(time(0));    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    void push_up(int x)    {        t[x].sz=t[ls(x)].sz+t[rs(x)].sz+1;    }    int New(int x)    {        t[++tot]={0,0,x,(int)rd(),1};        return tot;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        if(t[x].rnd<t[y].rnd)        {            int p=++tot;t[p]=t[x];            rs(p)=merge(rs(p),y);            push_up(p);return p;        }else        {            int p=++tot;t[p]=t[y];            ls(p)=merge(x,ls(p));            push_up(p);return p;        }    }    void split(int at,int k,int &x,int &y)    {        if(!at)x=y=0;        else        {            if(t[at].num<=k)            {                x=++tot;t[x]=t[at];                split(rs(x),k,rs(x),y);                push_up(x);            }            else            {                y=++tot;t[y]=t[at];                split(ls(y),k,x,ls(y));                push_up(y);            }        }    }    int kth(int at,int x)    {        while(1)        {            if(x<=t[ls(at)].sz)                at=ls(at);            else if(x==t[ls(at)].sz+1)                return at;            else            {                x-=t[ls(at)].sz+1;                at=rs(at);            }        }    }    void insert(int a,int &rt)    {        int x,y;        split(rt,a,x,y);        rt=merge(merge(x,New(a)),y);    }    void remove(int a,int &rt)    {        int x,y,z;        split(rt,a,x,z);        split(x,a-1,x,y);        y=merge(ls(y),rs(y));        rt=merge(merge(x,y),z);    }    void getrank(int a,int &rt)    {        int x,y;        split(rt,a-1,x,y);        write(t[x].sz+1);pc('\n');        rt=merge(x,y);    }    void getkth(int a,int &rt)    {        write(t[kth(rt,a)].num);        pc('\n');    }    void getpre(int a,int &rt)    {        int x,y;        split(rt,a-1,x,y);        if(!x)write(-2147483647+1);        else write(t[kth(x,t[x].sz)].num);        pc('\n');rt=merge(x,y);    }    void getnext(int a,int &rt)    {        int x,y;        split(rt,a,x,y);        if(!y)write(2147483647-1);        else write(t[kth(y,1)].num);        pc('\n');rt=merge(x,y);    }}signed main(){    using namespace FHQ_Treap;    read(n);    for(int i=1,op,a,now; i<=n; i++)    {        read(now);read(op);read(a);        T[i]=T[now];        if(op==1){insert(a,T[i]);}        if(op==2){remove(a,T[i]);}        if(op==3){getrank(a,T[i]);}        if(op==4){getkth(a,T[i]);}        if(op==5){getpre(a,T[i]);}        if(op==6){getnext(a,T[i]);}    }}

      ST表

      一定要预处理 \(\log_2 n\)!! 不然查询的复杂度是假的!

      因为 log()函数的复杂度不超过log级,但是常数大!

      时间复杂度 \(O(n\log n)\)

      空间复杂度 \(O(n\log n)\)

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+5)int n,m,lg[N],f[N][21];void change(int u,int x){f[u][0]=x;for(int i=1; u-(1<<i)>=0; i++)f[u][i]=max(f[u][i-1],f[u-(1<<(i-1))][i-1]);}int query(int l,int r){int k=lg[r-l+1];return max(f[l+(1<<k)-1][k],f[r][k]);}signed main(){read(n);read(m);for(int i=1,x; i<=n; i++){read(x); change(i,x);        lg[i]=(double)log(i)/log(2);}for(int i=1,l,r; i<=m; i++){read(l);read(r);write(query(l,r));pc('\n');}return 0;}

      树链剖分

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+5)struct Edge{int u,v,next;}e[N<<2];int pos=1,n,Q,rt,p,head[N];int id[N],idx,t[N],a[N],fa[N],son[N];int sz[N],top[N],ans[N<<2],tag[N<<2],dep[N];void addEdge(int u,int v){e[pos]={u,v,head[u]};head[u]=pos++;}void dfs(int u,int f,int d){fa[u]=f;sz[u]=1;dep[u]=d;int mx=-1;for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==f)continue;dfs(v,u,d+1);sz[u]+=sz[v];if(sz[v]>mx)mx=sz[v],son[u]=v;}}void dfs(int u,int ftop){id[u]=++idx;top[u]=ftop;a[idx]=t[u];if(!son[u])return;dfs(son[u],ftop);for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==fa[u]||v==son[u])continue;dfs(v,v);}}void push_up(int at){ans[at]=ans[at<<1]+ans[at<<1|1];}void proc(int l,int r,int at){int u=at>>1;ans[at]=(ans[at]+tag[u]*(r-l+1)%p)%p;tag[at]=(tag[at]+tag[u])%p;}void push_down(int l,int r,int at){int mid=(l+r)>>1;proc(l,mid,at<<1);proc(mid+1,r,at<<1|1);tag[at]=0;}void build(int l,int r,int at){tag[at]=0;if(l==r){ans[at]=a[l];return;}int mid=(l+r)>>1;build(l,mid,at<<1);build(mid+1,r,at<<1|1);push_up(at);}void update(int nl,int nr,int l,int r,int k,int at){if(nl<=l&&r<=nr){ans[at]=(ans[at]+k%p*(r-l+1)%p)%p;tag[at]=(tag[at]+k)%p;return;}push_down(l,r,at);int mid=(l+r)>>1;if(nl<=mid)update(nl,nr,l,mid,k,at<<1);if(nr>mid)update(nl,nr,mid+1,r,k,at<<1|1);push_up(at);}int query(int nl,int nr,int l,int r,int at){int res=0;if(nl<=l&&r<=nr){return ans[at]%p;}int mid=(l+r)>>1;push_down(l,r,at);if(nl<=mid)res=(res+query(nl,nr,l,mid,at<<1))%p;if(nr>mid)res=(res+query(nl,nr,mid+1,r,at<<1|1))%p;return res;}void upRange(int x,int y,int k){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);update(id[top[x]],id[x],1,n,k,1);x=fa[top[x]];}if(dep[x]>dep[y])swap(x,y);update(id[x],id[y],1,n,k,1);}int qRange(int x,int y){int res=0;while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);res=(res+query(id[top[x]],id[x],1,n,1))%p;x=fa[top[x]];}if(dep[x]>dep[y])swap(x,y);res=(res+query(id[x],id[y],1,n,1))%p;return res;}void upSon(int x,int k){update(id[x],id[x]+sz[x]-1,1,n,k,1);}int qSon(int x){return query(id[x],id[x]+sz[x]-1,1,n,1)%p;}signed main(){read(n);read(Q);read(rt);read(p);for(int i=1; i<=n; i++)read(t[i]);for(int i=1,u,v; i<n; i++){read(u);read(v);addEdge(u,v);addEdge(v,u);}dfs(rt,0,1);dfs(rt,rt);build(1,n,1);while(Q--){int op,x,y,k;read(op);if(op==1){read(x);read(y);read(k);upRange(x,y,k);}if(op==2){read(x);read(y);write(qRange(x,y));pc('\n');}if(op==3){read(x);read(k);upSon(x,k);}if(op==4){read(x);write(qSon(x));pc('\n');}}return 0;}

      树套树

      树套树位置线段树套权值平衡树

      P3380【模板】二逼平衡树(树套树)

      注意二分的方向

      #include <bits/stdc++.h>using namespace std;// #define int long long#define INF 2147483647// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)int n,Q,a[N];namespace BT{    struct node    {        int ch[2],num,cnt,s,sz,sd;    }t[N*35];    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    const double alpha=0.75;    int tot,rt,tmp[N*35];    int New(int x)    {        t[++tot]={0,0,x,1,1,1,1};        return tot;    }    void push_up(int at)    {        t[at].s=t[ls(at)].s+t[rs(at)].s+1;        t[at].sz=t[ls(at)].sz+t[rs(at)].sz+t[at].cnt;        t[at].sd=t[ls(at)].sd+t[rs(at)].sd+(t[at].cnt!=0);    }    bool CanR(int at)    {        double x=t[at].s*alpha;        return (t[at].cnt)        &&(x<=(double)max(t[ls(at)].s,t[rs(at)].s)||(double)t[at].sd<=x);    }    void CanR_flatten(int &idx,int at)    {        if(!at)return;        CanR_flatten(idx,ls(at));        if(t[at].cnt)tmp[++idx]=at;        CanR_flatten(idx,rs(at));    }    int CanR_build(int l,int r)    {        if(l>=r)return 0;        int mid=(l+r)>>1;        int &at=tmp[mid];        ls(at)=CanR_build(l,mid);        rs(at)=CanR_build(mid+1,r);        push_up(at);        return at;    }    void rebuild(int &at)    {        int idx=0;        CanR_flatten(idx,at);        at=CanR_build(1,idx+1);    }    void insert(int x,int &at)    {        if(!at){at=New(x);return;}        if(t[at].num==x)++t[at].cnt;        else if(x<t[at].num)insert(x,ls(at));        else insert(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    void remove(int x,int &at)    {        if(!at)return;        if(t[at].num==x)        {            if(t[at].cnt)--t[at].cnt;        }else if(x<t[at].num)remove(x,ls(at));        else remove(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    int getval(int x,int at)    {        if(!at)return INF;        if(x<=t[ls(at)].sz)return getval(x,ls(at));        if(x<=t[ls(at)].sz+t[at].cnt)return t[at].num;        else return getval(x-t[ls(at)].sz-t[at].cnt,rs(at));    }    int uprbd(int x,int at)    {        if(!at)return 1;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz+t[at].cnt+1;        else if(x<t[at].num)return uprbd(x,ls(at));        else return uprbd(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int uprbd_gt(int x,int at)    {        if(!at)return 0;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz;        else if(x<t[at].num)return uprbd_gt(x,ls(at));        else return uprbd_gt(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int getpre(int x,int at){int res=getval(uprbd_gt(x,at),at);return res==INF?-INF:res;}int getnext(int x,int at){int res=getval(uprbd(x,at),at);return res==INF?INF:res;}}namespace SEG{    struct node    {        int l,r,rt;    }s[N<<2];    #define lc(at) (at<<1)    #define rc(at) (at<<1|1)    void build(int l,int r,int at)    {        s[at].l=l;s[at].r=r;        for(int i=l; i<=r; i++)        BT::insert(a[i],s[at].rt);        if(l==r)return;        int mid=(l+r)>>1;        build(l,mid,lc(at));        build(mid+1,r,rc(at));    }    void update(int pos,int k,int at)    {        BT::remove(a[pos],s[at].rt);        BT::insert(k,s[at].rt);        if(s[at].l==s[at].r)return;        int mid=(s[at].l+s[at].r)>>1;        if(pos<=mid)update(pos,k,lc(at));        else update(pos,k,rc(at));    }    int getrank(int L,int R,int k,int at)    {        int l=s[at].l,r=s[at].r;        if(l>R||r<L)return 0;        if(L<=l&&r<=R)return BT::uprbd_gt(k,s[at].rt);        return getrank(L,R,k,lc(at))+getrank(L,R,k,rc(at));    }    // a=getrank(x),b=getrank(y)    // a=b => x=y    // x=y =/> a=b     // int getkth(int L,int R,int k)    // {    //     int l=0,r=1e8+5;    //     while(l<r)    //     {    //         int mid=(l+r)>>1;    //         if(getrank(L,R,mid,1)+1>=k)r=mid;    //         else l=mid+1;    //     }    //     return l;    // }    int getkth(int L,int R,int k)    {        int l=0,r=1e8+5;        while(l<r)        {            int mid=(l+r+1)>>1;            if(getrank(L,R,mid,1)+1<=k)l=mid;            else r=mid-1;        }        return l;    }    int getpre(int L,int R,int k,int at)    {        int l=s[at].l,r=s[at].r;        if(l>R||r<L)return -INF;        if(L<=l&&r<=R)            return BT::getpre(k,s[at].rt);        return max(getpre(L,R,k,lc(at)),getpre(L,R,k,rc(at)));    }    int getnext(int L,int R,int k,int at)    {        int l=s[at].l,r=s[at].r;        if(l>R||r<L)return INF;        if(L<=l&&r<=R)            return BT::getnext(k,s[at].rt);        return min(getnext(L,R,k,lc(at)),getnext(L,R,k,rc(at)));    }}signed main(){    using namespace SEG;    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    int op,x,y,k;    while(Q--)    {        read(op);read(x);read(y);        if(op==3)        {            update(x,y,1);            a[x]=y;        }else        {            read(k);            if(op==1)write(getrank(x,y,k,1)+1);            else if(op==2)write(getkth(x,y,k));            else if(op==4)write(getpre(x,y,k,1));            else if(op==5)write(getnext(x,y,k,1));            pc('\n');        }    }    return 0;}

      线段树

      线段树1 朴素写法

      P3372 【模板】线段树1

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)int n,Q;int a[N];struct node{    int sum,tag;}t[N<<2];#define ls(at) (at<<1)#define rs(at) (at<<1|1)void push_up(int at){    t[at].sum=t[ls(at)].sum+t[rs(at)].sum;}void build(int l,int r,int at){    if(l==r)    {        t[at].sum=a[l];        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void proc(int l,int r,int k,int at){    t[at].sum+=k*(r-l+1);    t[at].tag+=k;}void push_down(int l,int r,int at){    if(t[at].tag)    {        int mid=(l+r)>>1;        proc(l,mid,t[at].tag,ls(at));        proc(mid+1,r,t[at].tag,rs(at));    }    t[at].tag=0;}void update(int nl,int nr,int l,int r,int k,int at){    if(nl<=l&&r<=nr)    {        t[at].sum+=k*(r-l+1);        t[at].tag+=k;        return;    }    int mid=(l+r)>>1;    push_down(l,r,at);    if(nl<=mid)update(nl,nr,l,mid,k,ls(at));    if(nr>mid)update(nl,nr,mid+1,r,k,rs(at));    push_up(at);}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        {return t[at].sum;}    int mid=(l+r)>>1,res=0;    push_down(l,r,at);    if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));    if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));    return res;}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    while(Q--)    {        int op,l,r,k;        read(op);        if(op==1)        {            read(l);read(r);read(k);            update(l,r,1,n,k,1);        }else        {            read(l);read(r);            write(query(l,r,1,n,1));pc('\n');        }    }    return 0;}

      还有一种比较好的query写法,如下

      int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        // return ...;    if(nr<=mid)return query(nl,nr,l,mid,ls(at));    if(nl>mid)return query(nl,nr,mid+1,r,rs(at));    return query(nl,nr,l,mid,ls(at))+query(nl,nr,mid+1,r,rs(at));}

      线段树1 标记永久化+动态开点

      P3372 【模板】线段树1

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e6+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)struct node{    int ch[2],ans,tag;}t[N<<2];int rt,tot;int n,Q,a[N];#define ls(at) t[at].ch[0]#define rs(at) t[at].ch[1]void insert(int nl,int nr,int l,int r,int x,int &at){    if(!at)at=++tot;    t[at].ans+=x*(min(r,nr)-max(l,nl)+1);    if(nl<=l&&r<=nr)    {        t[at].tag+=x;        return;    }    int mid=(l+r)>>1;    if(nl<=mid)insert(nl,nr,l,mid,x,ls(at));    if(nr>mid)insert(nl,nr,mid+1,r,x,rs(at));}void pusht(int l,int r,int x,int &at){    if(!at)at=++tot;    t[at].ans+=(r-l+1)*x;    t[at].tag+=x;}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        return t[at].ans;    int mid=(l+r)>>1;    if(t[at].tag)    {        pusht(l,mid,t[at].tag,ls(at));        pusht(mid+1,r,t[at].tag,rs(at));        t[at].tag=0;    }    int res=0;    if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));    if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));    return res;}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)    {        read(a[i]);        insert(i,i,1,n,a[i],rt);    }    while(Q--)    {        int op,l,r,x;        read(op);read(l);read(r);        if(op==1){read(x);insert(l,r,1,n,x,rt);}        else write(query(l,r,1,n,rt)),pc('\n');    }    return 0;}

      李超线段树

      P4097[HEOI2013]Segment

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)#define M (int)(4e4+15)#define ls(at) (at<<1)#define rs(at) (at<<1|1)const int mod1=39989;const int mod2=1e9;struct line{    double k,b;}li[N];int bs[M<<2],n,lstans,cnt;double calc(int id,int x){    return li[id].k*x+li[id].b;}void init(int x1,int y1,int x2,int y2){    ++cnt;    if(x1==x2)    {        li[cnt].k=0;        li[cnt].b=max(y1,y2);    }else    {        li[cnt].k=1.0*(y1-y2)/(x1-x2);        li[cnt].b=y1-li[cnt].k*x1;    }}void update(int nl,int nr,int l,int r,int nw,int at){    int u=bs[at];    int mid=(l+r)>>1;    double now=calc(nw,mid),resu=calc(u,mid);    if(nl<=l&&r<=nr)    {        if(l==r)        {            if(now>resu)                bs[at]=nw;            return;        }        if(li[nw].k>li[u].k)        {            if(now>resu)            {                bs[at]=nw;                update(nl,nr,l,mid,u,ls(at));            }else update(nl,nr,mid+1,r,nw,rs(at));        }else if(li[nw].k<li[u].k)        {            if(now>resu)            {                bs[at]=nw;                update(nl,nr,mid+1,r,u,rs(at));            }else update(nl,nr,l,mid,nw,ls(at));        }else if(li[nw].b>li[u].b)bs[at]=nw;        return;    }    if(nl<=mid)update(nl,nr,l,mid,nw,ls(at));    if(nr>mid)update(nl,nr,mid+1,r,nw,rs(at));}struct node{double v;int x;};node max(node a,node b){    if(a.v==b.v)return a.x<b.x?a:b;    return a.v>b.v?a:b;}node query(int l,int r,int x,int at){    if(r<x||l>x)return {0,0};    int mid=(l+r)>>1;    node tmp={calc(bs[at],x),bs[at]};    if(l==r)return tmp;    return max(tmp,max(query(l,mid,x,ls(at)),query(mid+1,r,x,rs(at))));}#define change1(x) (x=(x+lstans-1+mod1)%mod1+1)#define change2(x) (x=(x+lstans-1+mod2)%mod2+1)signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    int op,x1,y1,x2,y2;    for(int i=1; i<=n; i++)    {        read(op);        if(op==1)        {            read(x1);read(y1);read(x2);read(y2);            change1(x1);change1(x2);change2(y1);change2(y2);            if(x1>x2)swap(x1,x2),swap(y1,y2);            init(x1,y1,x2,y2);update(x1,x2,1,M,cnt,1);        }else        {            int x;read(x);change1(x);            write(lstans=query(1,M,x,1).x);pc('\n');        }    }    return 0;}

      线段树分裂

      P5494【模板】线段树分裂

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e5+15)int n,Q,tot,rcnt,seq=1;int bac[N*35],ch[N*35][2],rt[N],val[N*35];int newnod(){return rcnt?bac[rcnt--]:++tot;}#define ls(at) ch[at][0]#define rs(at) ch[at][1]void del(int at){    bac[++rcnt]=at;    ls(at)=rs(at)=val[at]=0;}void modify(int l,int r,int pos,int v,int &at){    if(!at)at=newnod();    val[at]+=v;    if(l==r)return;    int mid=(l+r)>>1;    if(pos<=mid)modify(l,mid,pos,v,ls(at));    else modify(mid+1,r,pos,v,rs(at));}int query(int nl,int nr,int l,int r,int at){    if(nr<l||r<nl)return 0;    if(nl<=l&&r<=nr)return val[at];    int mid=(l+r)>>1;    return query(nl,nr,l,mid,ls(at))+query(nl,nr,mid+1,r,rs(at));}int kth(int l,int r,int k,int at){    if(l==r)return l;    int mid=(l+r)>>1;    if(k<=val[ls(at)])return kth(l,mid,k,ls(at));    else return kth(mid+1,r,k-val[ls(at)],rs(at));}int merge(int x,int y){    if(!x||!y)return x|y;    val[x]+=val[y];    ls(x)=merge(ls(x),ls(y));    rs(x)=merge(rs(x),rs(y));    del(y);    return x;}void split(int x,int &y,int k){    if(!x)return;    y=newnod();    int v=val[ls(x)];    if(k>v)split(rs(x),rs(y),k-v);    else    {        swap(rs(x),rs(y));        if(k<v)split(ls(x),ls(y),k);    }    val[y]=val[x]-k;    val[x]=k;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    read(n);read(Q);    for(int i=1,x; i<=n; i++)    {        read(x);        modify(1,n,i,x,rt[1]);    }    int op,x,y,z;    while(Q--)    {        read(op);        if(op==0)        {            read(x);read(y);read(z);            int k1=query(1,z,1,n,rt[x]);            int k2=query(y,z,1,n,rt[x]);            int tmp=0;            split(rt[x],rt[++seq],k1-k2);            split(rt[seq],tmp,k2);            rt[x]=merge(rt[x],tmp);        }else if(op==1)        {            read(x);read(y);            rt[x]=merge(rt[x],rt[y]);        }else if(op==2)        {            read(x);read(y);read(z);            modify(1,n,z,y,rt[x]);        }else if(op==3)        {            read(x);read(y);read(z);            write(query(y,z,1,n,rt[x]));pc('\n');        }else if(op==4)        {            read(x);read(y);            if(val[rt[x]]<y){puts("-1");continue;}            write(kth(1,n,y,rt[x]));pc('\n');        }    }    return 0;}

      静态仙人掌

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void print(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(4e4+15)int n,m,Q,dfncnt,ext;int dfn[N],low[N],fa[N],ans;int top[N],son[N],sz[N],dep[N],b[N],sum[N],dis[N];struct Edge{    int v,w;};vector<Edge> vec[N],exv[N];int find(int u,int f){    int res;    while(top[u]!=top[f])    {        res=top[u];        u=fa[top[u]];    }    return u==f?res:son[f];}int lca(int x,int y){    while(top[x]!=top[y])    {        if(dep[top[x]]<dep[top[y]])swap(x,y);        x=fa[top[x]];    }    return dep[x]<dep[y]?x:y;}void dfs(int u,int f,int d){    dep[u]=d+1;fa[u]=f;sz[u]=1;    int mx=-1;    for(int i=0; i<exv[u].size(); i++)    {        int v=exv[u][i].v;        if(v==f)continue;        dis[v]=dis[u]+exv[u][i].w;        dfs(v,u,d+1);        sz[u]+=sz[v];        if(sz[v]>mx)mx=sz[v],son[u]=v;    }}void dfs(int u,int ftop){    top[u]=ftop;    if(!son[u])return;    dfs(son[u],ftop);    for(int i=0; i<exv[u].size(); i++)    {        int v=exv[u][i].v;        if(v==fa[u]||v==son[u])            continue;        dfs(v,v);    }}void proc(int u,int v,int w){    ++ext;    int pw,pre=w,x=v;    while(x!=fa[u])    {        sum[x]=pre;        pre+=b[x];        x=fa[x];    }    sum[ext]=sum[u];    sum[u]=0;x=v;    while(x!=fa[u])    {        pw=min(sum[x],sum[ext]-sum[x]);        exv[ext].push_back({x,pw});        exv[x].push_back({ext,pw});        x=fa[x];    }}void tarjan(int u,int f){    dfn[u]=low[u]=++dfncnt;    for(int i=0; i<vec[u].size(); i++)    {        int v=vec[u][i].v,w=vec[u][i].w;        if(v==f)continue;        if(!dfn[v])        {            fa[v]=u;b[v]=w;            tarjan(v,u);            low[u]=min(low[u],low[v]);        }else low[u]=min(low[u],dfn[v]);        if(low[v]>dfn[u])        {            exv[u].push_back({v,w});            exv[v].push_back({u,w});        }    }    for(int i=0; i<vec[u].size(); i++)    {        int v=vec[u][i].v;        if(fa[v]!=u&&dfn[v]>dfn[u])            proc(u,v,vec[u][i].w);    }}signed main(){    read(n);read(m);read(Q);ext=n;    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        vec[u].push_back({v,w});        vec[v].push_back({u,w});    }    tarjan(1,0);dfs(1,0,1);dfs(1,1);    while(Q--)    {        int u,v,p,A,B;read(u);read(v);        p=lca(u,v);        if(p<=n)ans=dis[u]+dis[v]-(dis[p]<<1);        else        {            A=find(u,p);B=find(v,p);            ans=dis[u]+dis[v]-dis[A]-dis[B];            int tmp=abs(sum[A]-sum[B]);            ans+=min(tmp,sum[p]-tmp);        }        print(ans);pc('\n');    }    return 0;}

      可持久化数组

      1. 主席树实现被卡了long long

      时间复杂度 \(O(n\log n)\)

      #include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+25)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e6+5)int n,Q;int a[N],T[N],tot;struct node{    int ch[2],num;}t[N<<5];#define ls(at) t[at].ch[0]#define rs(at) t[at].ch[1]int build(int l,int r){    int at=++tot,mid=(l+r)>>1;    if(l==r){t[at].num=a[l];return at;}    ls(at)=build(l,mid);    rs(at)=build(mid+1,r);    return at;}int update(int pre,int l,int r,int x,int v){    int at=++tot,mid=(l+r)>>1;    t[at]=t[pre];    if(l==r)    {        t[at].num=v;        return at;    }    if(x<=mid)ls(at)=update(ls(pre),l,mid,x,v);    else rs(at)=update(rs(pre),mid+1,r,x,v);    return at;}int query(int at,int l,int r,int x){    if(l==r)return t[at].num;    int mid=(l+r)>>1;    if(x<=mid)return query(ls(at),l,mid,x);    else return query(rs(at),mid+1,r,x);}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    T[0]=build(1,n);    for(int i=1; i<=Q; i++)    {        int rt,op,x,y;        read(rt);read(op);read(x);        if(op==1)        {            read(y);            T[i]=update(T[rt],1,n,x,y);        }else        {            write(query(T[rt],1,n,x));            pc('\n');T[i]=T[rt];        }    }    return 0;}
      1. Elegia神仙的神仙解法(离线解法)

        根据依赖关系,且可逆操作,建关系树,然后跑dfs

        时间复杂度 \(O(n)\)

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+25)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e6+5)int n,Q;int a[N],op[N],k[N],x[N],ans[N];vector<int>vec[N];void dfs(int u){    if(op[u]==2)    {        ans[u]=a[k[u]];        for(int v:vec[u])dfs(v);    }else    {        swap(a[k[u]],x[u]);        for(int v:vec[u])dfs(v);        swap(a[k[u]],x[u]);    }}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    op[0]=2;    for(int i=1; i<=Q; i++)    {        int rt;        read(rt);read(op[i]);read(k[i]);        if(op[i]==1)            read(x[i]);        vec[rt].push_back(i);    }    dfs(0);    for(int i=1; i<=Q; i++)        if(op[i]==2)            write(ans[i]),pc('\n');    return 0;}

      可持久化线段树(主席树)

      区间 \(k\) 小值

      时间复杂度 \(O(n\log n)\)

      #include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+25)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e5+5)int n,m,Q;int a[N],b[N];int T[N],tot;struct node{    int ch[2],sum;}t[N<<5];#define ls(at) t[at].ch[0]#define rs(at) t[at].ch[1]int build(int l,int r){    int at=++tot,mid=(l+r)>>1;    if(l<r)    {        ls(at)=build(l,mid);        rs(at)=build(mid+1,r);    }    return at;}int update(int pre,int l,int r,int x){    int at=++tot,mid=(l+r)>>1;    t[at]=t[pre];++t[at].sum;    if(l<r)    {        if(x<=mid)ls(at)=update(ls(pre),l,mid,x);        else rs(at)=update(rs(pre),mid+1,r,x);    }    return at;}int query(int u,int v,int l,int r,int k){    if(l==r)return l;    int x=t[ls(v)].sum-t[ls(u)].sum;    int mid=(l+r)>>1;    if(x>=k)return query(ls(u),ls(v),l,mid,k);    else return query(rs(u),rs(v),mid+1,r,k-x);}void init(){    sort(b+1,b+1+n);    m=unique(b+1,b+1+n)-b-1;    T[0]=build(1,m);    for(int i=1; i<=n; i++)    {        a[i]=lower_bound(b+1,b+1+m,a[i])-b;        T[i]=update(T[i-1],1,m,a[i]);    }}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]),b[i]=a[i];    init();    while(Q--)    {        int l,r,k;read(l);read(r);read(k);        write(b[query(T[l-1],T[r],1,m,k)]);        pc('\n');    }    return 0;}
      ]]> @@ -2691,7 +2691,7 @@ /2022/07/19/luo-gu-p2391-bai-xue-ai-ai-ti-jie/ - 洛谷P2391 白雪皑皑 题解

      题目链接:P2391 白雪皑皑

      题意

      现在有 $n$ 片雪花排成一列。 pty 要对雪花进行 $m$ 次染色操作,第 $i$ 次染色操作中,把第 $((i\times p+q)\bmod n)+1$ 片雪花和第 $((i\times q+p)\bmod n)+1$ 片雪花之间的雪花(包括端点)染成颜色 $i$。其中 $p,q$ 是给定的两个正整数。他想知道最后 $n$ 片雪花被染成了什么颜色。没有被染色输出 $0$。

      $1\leq n\leq 10^6,~1\leq m\leq 10^7$。

      保证 $1\leq m\times p+q,m\times q+p\leq 2\times 10^9$。

      一句话题意:线性区间推平(区间赋同一个值),全局询问。

      我们模拟赛t3,也是我想了几个月结果有原题的东西,不写题解亏大了

      考虑倒序处理修改,因为一个位置的值一定由它最后一次被修改的数决定

      暴力推平?对于被推平的点,不可以再推了,所以我们要跳过它

      这就需要维护每个点右侧的那个未被推平的结点

      可以考虑链表或者并查集维护,这里用并查集

      时间复杂度 $O(n)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+25)int n,m,p,q,val[N],f[N];void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);        read(n);init(n+5);    read(m);read(p);read(q);    for(int i=m; i>=1; i--)    {        int l=(1ll*i*p+q)%n+1;        int r=(1ll*i*q+p)%n+1;        if(l>r)swap(l,r);        for(int j=l; j<=r;)        {            int k=find(j); if(k>r)break;            val[k]=i; f[k]=find(k+1); j=f[k];        }    }    for(int i=1; i<=n; i++) write(val[i]),pc('\n');    return 0;}

      是不是很神奇,我之前还想过差分、单调栈、线段覆盖、二维凸包(?等一大堆东西

      ]]> + 洛谷P2391 白雪皑皑 题解

      题目链接:P2391白雪皑皑

      题意

      现在有 \(n\) 片雪花排成一列。 pty要对雪花进行 \(m\) 次染色操作,第 \(i\) 次染色操作中,把第 \(((i\times p+q)\bmod n)+1\) 片雪花和第 \(((i\times q+p)\bmod n)+1\)片雪花之间的雪花(包括端点)染成颜色 \(i\)。其中 \(p,q\) 是给定的两个正整数。他想知道最后\(n\)片雪花被染成了什么颜色。没有被染色输出 \(0\)。

      \(1\leq n\leq 10^6,~1\leq m\leq10^7\)

      保证 \(1\leq m\times p+q,m\times q+p\leq2\times 10^9\)

      一句话题意:线性区间推平(区间赋同一个值),全局询问。

      我们模拟赛t3,也是我想了几个月结果有原题的东西,不写题解亏大了

      考虑倒序处理修改,因为一个位置的值一定由它最后一次被修改的数决定

      暴力推平?对于被推平的点,不可以再推了,所以我们要跳过它

      这就需要维护每个点右侧的那个未被推平的结点

      可以考虑链表或者并查集维护,这里用并查集

      时间复杂度 \(O(n)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+25)int n,m,p,q,val[N],f[N];void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);        read(n);init(n+5);    read(m);read(p);read(q);    for(int i=m; i>=1; i--)    {        int l=(1ll*i*p+q)%n+1;        int r=(1ll*i*q+p)%n+1;        if(l>r)swap(l,r);        for(int j=l; j<=r;)        {            int k=find(j); if(k>r)break;            val[k]=i; f[k]=find(k+1); j=f[k];        }    }    for(int i=1; i<=n; i++) write(val[i]),pc('\n');    return 0;}

      是不是很神奇,我之前还想过差分、单调栈、线段覆盖、二维凸包(?等一大堆东西

      ]]> @@ -2716,7 +2716,7 @@ /2022/07/18/luo-gu-p2024-noi2001-shi-wu-lian-ti-jie/ - 洛谷P2024 [NOI2001] 食物链 题解

      题目链接:P2024 [NOI2001] 食物链

      题意:动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形。A 吃 B,B 吃 C,C 吃 A。

      现有 N 个动物,以 1 - N 编号。每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。

      有人用两种说法对这 N 个动物所构成的食物链关系进行描述:

      • 第一种说法是1 X Y,表示 X 和 Y 是同类。
      • 第二种说法是2 X Y,表示 X 吃 Y 。

      此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

      • 当前的话与前面的某些真的话冲突,就是假话
      • 当前的话中 X 或 Y 比 N 大,就是假话
      • 当前的话表示 X 吃 X,就是假话

      你的任务是根据给定的 N 和 K 句话,输出假话的总数。

      $1 \le N \le 5\times10^4,~1 \le K \le 10^5$

      考虑种类并查集。

      我们维护三个并查集,分别存同类、食物和天敌

      这里可以用1倍~3倍来划分,即 xx+nx+2*n

      例如 xx+n 合并,表示 x 及其同类均能吃 x+n 及其同类

      不用A,B,C作为划分依据是因为题目没说每个动物的种类

      并且在本题中,准确的种类并没有什么用处。

      对于「X和Y是同类」,看上去只要把同类的并查集合并

      但其实其他两个并查集也要合并,因为有可能X存在已知的天敌

      而X和Y是同类,所以Y也有这个(些)天敌

      同样的,对于「X吃Y」,也需要对两两关系进行处理

      时间复杂度 $O(m)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)int n,Q,f[N<<2];void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> Q;    int res=0; init(3*n);    for(int op,x,y; Q--;)    {        cin >> op >> x >> y;        if(x>n||y>n){++res; continue;}        if(op==1)        {            if(find(x)==find(y+n)||find(x)==find(y+2*n)){++res; continue;}            merge(x,y); merge(x+n,y+n); merge(x+2*n,y+2*n);        }else        {            if(find(x)==find(y)||find(x)==find(y+n)){++res; continue;}            merge(x,y+2*n); merge(x+n,y); merge(x+2*n,y+n);        }    }    cout << res << '\n';    return 0;}

      参考文献

      [1] https://www.luogu.com.cn/blog/DanKuroto/solution-p2024

      ]]> + 洛谷P2024 [NOI2001] 食物链题解

      题目链接:P2024[NOI2001] 食物链

      题意:动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A 吃 B,B 吃 C,C 吃 A。

      现有 N 个动物,以 1 - N 编号。每个动物都是 A,B,C中的一种,但是我们并不知道它到底是哪一种。

      有人用两种说法对这 N 个动物所构成的食物链关系进行描述:

      • 第一种说法是1 X Y,表示 X 和 Y 是同类。
      • 第二种说法是2 X Y,表示 X 吃 Y 。

      此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

      • 当前的话与前面的某些真的话冲突,就是假话
      • 当前的话中 X 或 Y 比 N 大,就是假话
      • 当前的话表示 X 吃 X,就是假话

      你的任务是根据给定的 N 和 K 句话,输出假话的总数。

      \(1 \le N \le 5\times10^4,~1 \le K \le10^5\)

      考虑种类并查集。

      我们维护三个并查集,分别存同类、食物和天敌

      这里可以用1倍~3倍来划分,即xx+nx+2*n

      例如 xx+n 合并,表示 x及其同类均能吃 x+n 及其同类

      不用A,B,C作为划分依据是因为题目没说每个动物的种类

      并且在本题中,准确的种类并没有什么用处。

      对于「X和Y是同类」,看上去只要把同类的并查集合并

      但其实其他两个并查集也要合并,因为有可能X存在已知的天敌

      而X和Y是同类,所以Y也有这个(些)天敌

      同样的,对于「X吃Y」,也需要对两两关系进行处理

      时间复杂度 \(O(m)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)int n,Q,f[N<<2];void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> Q;    int res=0; init(3*n);    for(int op,x,y; Q--;)    {        cin >> op >> x >> y;        if(x>n||y>n){++res; continue;}        if(op==1)        {            if(find(x)==find(y+n)||find(x)==find(y+2*n)){++res; continue;}            merge(x,y); merge(x+n,y+n); merge(x+2*n,y+2*n);        }else        {            if(find(x)==find(y)||find(x)==find(y+n)){++res; continue;}            merge(x,y+2*n); merge(x+n,y); merge(x+2*n,y+n);        }    }    cout << res << '\n';    return 0;}

      参考文献

      [1] https://www.luogu.com.cn/blog/DanKuroto/solution-p2024

      ]]> @@ -2745,7 +2745,7 @@ /2022/07/18/luo-gu-p1792-guo-jia-ji-xun-dui-chong-shu-ti-jie/ - 洛谷P1792 [国家集训队]种树 题解

      题目链接:P1792 [国家集训队]种树

      题意

      A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。

      园林部门得到指令后,初步规划出 $n$ 个种树的位置,顺时针编号 $1$ 到 $n$。并且每个位置都有一个美观度 $A_i$,如果在这里种树就可以得到这 $A_i$ 的美观度。但由于 $A$ 城市土壤肥力欠佳,两棵树决不能种在相邻的位置($i$ 号位置和 $i+1$ 号位置叫相邻位置。值得注意的是 $1$ 号和 $n$ 号也算相邻位置)。

      最终市政府给园林部门提供了 $m$ 棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将 $m$ 棵树苗全部种上,给出无解信息。

      $m\le n \le 2\times 10^5$,$-1000\le A_i\le1000$。

      考虑反悔型贪心,维护一个大根堆,先把所有结点加进去

      然后每次取出权值最大的点,把它加入答案

      如何反悔呢?

      我们再加入一个权值为 $a_{l_i} + a_{r_i}-a_i$ 的节点,表示反悔结点 $i$ 时的答案

      不难发现这样我们再次选到 $a_i$ 的时候,虽然选的是它,但是其实对答案的贡献是它左右两个结点。

      时间复杂度 $O(m \log n)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e5+25)bool vis[N];int n,m,a[N],l[N],r[N];struct node{int val,id;};bool operator<(node a,node b){return a.val<b.val;}priority_queue<node> q;signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(m);    if(n<m*2)return puts("Error!"),0;    for(int i=1; i<=n; i++)    {        read(a[i]);        l[i]=i-1;r[i]=i+1;        q.push({a[i],i});    }    l[1]=n; r[n]=1; int res=0;    while(m--)    {        while(vis[q.top().id])q.pop();        node u=q.top(); q.pop();        res+=u.val;        int id=u.id;        vis[l[id]]=vis[r[id]]=1;        a[id]=a[l[id]]+a[r[id]]-a[id];        l[id]=l[l[id]];r[l[id]]=id;        r[id]=r[r[id]];l[r[id]]=id;        q.push({a[id],id});    }    write(res);    return 0;}
      ]]> + 洛谷P1792 [国家集训队]种树题解

      题目链接:P1792[国家集训队]种树

      题意

      A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树。

      园林部门得到指令后,初步规划出 \(n\)个种树的位置,顺时针编号 \(1\)\(n\)。并且每个位置都有一个美观度 \(A_i\),如果在这里种树就可以得到这 \(A_i\) 的美观度。但由于 \(A\)城市土壤肥力欠佳,两棵树决不能种在相邻的位置(\(i\) 号位置和 \(i+1\) 号位置叫相邻位置。值得注意的是 \(1\) 号和 \(n\) 号也算相邻位置)。

      最终市政府给园林部门提供了 \(m\)棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大。如果无法将\(m\)棵树苗全部种上,给出无解信息。

      \(m\le n \le 2\times 10^5\)\(-1000\le A_i\le1000\)。

      考虑反悔型贪心,维护一个大根堆,先把所有结点加进去

      然后每次取出权值最大的点,把它加入答案

      如何反悔呢?

      我们再加入一个权值为 \(a_{l_i} +a_{r_i}-a_i\) 的节点,表示反悔结点 \(i\) 时的答案

      不难发现这样我们再次选到 \(a_i\)的时候,虽然选的是它,但是其实对答案的贡献是它左右两个结点。

      时间复杂度 \(O(m \log n)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e5+25)bool vis[N];int n,m,a[N],l[N],r[N];struct node{int val,id;};bool operator<(node a,node b){return a.val<b.val;}priority_queue<node> q;signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(m);    if(n<m*2)return puts("Error!"),0;    for(int i=1; i<=n; i++)    {        read(a[i]);        l[i]=i-1;r[i]=i+1;        q.push({a[i],i});    }    l[1]=n; r[n]=1; int res=0;    while(m--)    {        while(vis[q.top().id])q.pop();        node u=q.top(); q.pop();        res+=u.val;        int id=u.id;        vis[l[id]]=vis[r[id]]=1;        a[id]=a[l[id]]+a[r[id]]-a[id];        l[id]=l[l[id]];r[l[id]]=id;        r[id]=r[r[id]];l[r[id]]=id;        q.push({a[id],id});    }    write(res);    return 0;}
      ]]> @@ -2774,7 +2774,7 @@ /2022/07/18/luo-gu-p1016-noip1999-ti-gao-zu-lu-xing-jia-de-yu-suan-ti-jie/ - 洛谷P1016 [NOIP1999 提高组] 旅行家的预算 题解

      题目链接:P1016 [NOIP1999 提高组] 旅行家的预算

      题意

      一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离 $D_1$、汽车油箱的容量 $C$(以升为单位)、每升汽油能行驶的距离 $D_2$、出发点每升汽油价格$P$和沿途油站数 $N$($N$ 可以为零),油站 $i$ 离出发点的距离 $D_i$、每升汽油价格 $P_i$($i=1,2,…,N$)。计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出 No Solution

      $N \le 6$,其余数字$ \le 500$。

      经典的反悔型贪心

      每次碰到一个加油站,烧掉最便宜的油(从上一个位置到现在位置)

      接着退掉比当前加油站贵的油(反悔),然后直接加满当前加油站的油

      记得设一个终点站,显然它和起点的距离为 $d_1$ ,推掉所有多的油

      这个油价的维护直接用单调队列维护,这里用deque<node>来搞方便一些

      时间复杂度 $O(n)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <deque>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(15)int n;double d1,c,d2,p[N],d[N];struct node{double cost,x;};deque<node> q;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> d1 >> c >> d2 >> p[0] >> n;    for(int i=1; i<=n; i++)    {        cin >> d[i] >> p[i];        if(d[i]-d[i-1]>c*d2)            return cout << "No Solution\n",0;    }    d[n+1]=d1;    double nc=c,res=0;    q.push_back({p[0],nc});    res+=p[0]*c;    for(int i=1; i<=n+1; i++)    {        double nd=(d[i]-d[i-1])/d2;        while(!q.empty()&&nd>0)        {            node u=q.front(); q.pop_front();            if(u.x>nd)            {                nc-=nd;                q.push_front({u.cost,u.x-nd});                break;            }            nc-=u.x; nd-=u.x;        }        if(i==n+1)        {            while(!q.empty())            {                res-=q.front().cost*q.front().x;                q.pop_front();            }            break;        }        while(!q.empty()&&q.back().cost>p[i])        {            res-=q.back().cost*q.back().x;            nc-=q.back().x;            q.pop_back();        }        res+=(c-nc)*p[i];        q.push_back({p[i],c-nc});        nc=c;    }    cout << fixed << setprecision(2) << res << '\n';    return 0;}

      参考文献

      [1] https://www.luogu.com.cn/blog/hongzy/solution-p1016

      ]]> + 洛谷P1016 [NOIP1999提高组] 旅行家的预算 题解

      题目链接:P1016[NOIP1999 提高组] 旅行家的预算

      题意

      一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离\(D_1\)、汽车油箱的容量 \(C\)(以升为单位)、每升汽油能行驶的距离\(D_2\)、出发点每升汽油价格\(P\)和沿途油站数 \(N\)(\(N\)可以为零),油站 \(i\) 离出发点的距离\(D_i\)、每升汽油价格 \(P_i\)(\(i=1,2,…,N\))。计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出No Solution

      \(N \le 6\),其余数字$ $。

      经典的反悔型贪心

      每次碰到一个加油站,烧掉最便宜的油(从上一个位置到现在位置)

      接着退掉比当前加油站贵的油(反悔),然后直接加满当前加油站的油

      记得设一个终点站,显然它和起点的距离为 \(d_1\) ,推掉所有多的油

      这个油价的维护直接用单调队列维护,这里用deque<node>来搞方便一些

      时间复杂度 \(O(n)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <deque>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(15)int n;double d1,c,d2,p[N],d[N];struct node{double cost,x;};deque<node> q;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> d1 >> c >> d2 >> p[0] >> n;    for(int i=1; i<=n; i++)    {        cin >> d[i] >> p[i];        if(d[i]-d[i-1]>c*d2)            return cout << "No Solution\n",0;    }    d[n+1]=d1;    double nc=c,res=0;    q.push_back({p[0],nc});    res+=p[0]*c;    for(int i=1; i<=n+1; i++)    {        double nd=(d[i]-d[i-1])/d2;        while(!q.empty()&&nd>0)        {            node u=q.front(); q.pop_front();            if(u.x>nd)            {                nc-=nd;                q.push_front({u.cost,u.x-nd});                break;            }            nc-=u.x; nd-=u.x;        }        if(i==n+1)        {            while(!q.empty())            {                res-=q.front().cost*q.front().x;                q.pop_front();            }            break;        }        while(!q.empty()&&q.back().cost>p[i])        {            res-=q.back().cost*q.back().x;            nc-=q.back().x;            q.pop_back();        }        res+=(c-nc)*p[i];        q.push_back({p[i],c-nc});        nc=c;    }    cout << fixed << setprecision(2) << res << '\n';    return 0;}

      参考文献

      [1] https://www.luogu.com.cn/blog/hongzy/solution-p1016

      ]]> @@ -2801,7 +2801,7 @@ /2022/07/18/luo-gu-p1631-xu-lie-he-bing-ti-jie/ - 洛谷P1631 序列合并 题解

      题目链接:P1631 序列合并

      题意

      有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到$N^2$个和,求这$N^2$个和中最小的N个。

      对于100%的数据中,满足1<=N<=100000。

      维护一个大根堆,初始有 $N$ 个极大值

      贪心地选择较小的 $A_i$ 和较小的 $B_i$ 合并

      然后有一个简单易懂的剪枝,具体见代码

      时间复杂度 $O(n \log n)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)priority_queue<int> q;int n,a[N],b[N],c[N],pos;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++) cin >> a[i];    for(int i=1; i<=n; i++) cin >> b[i];    for(int i=1; i<=n; i++) q.push(INF);    // sort(a+1,a+1+n); sort(b+1,b+1+n);    for(int i=1; i<=n; i++)    {        for(int j=1; j<=n; j++)        {            int tmp=a[i]+b[j];            if(tmp<q.top())            {                q.pop();                q.push(tmp);            }else break;        }    }    while(!q.empty())    {        c[++pos]=q.top();        q.pop();    }    for(int i=pos; i>=1; i--)        cout << c[i] << " \n"[i==1];    return 0;}
      ]]> + 洛谷P1631 序列合并 题解

      题目链接:P1631序列合并

      题意

      有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到\(N^2\)个和,求这\(N^2\)个和中最小的N个。

      对于100%的数据中,满足1<=N<=100000。

      维护一个大根堆,初始有 \(N\)个极大值

      贪心地选择较小的 \(A_i\) 和较小的\(B_i\) 合并

      然后有一个简单易懂的剪枝,具体见代码

      时间复杂度 \(O(n \log n)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)priority_queue<int> q;int n,a[N],b[N],c[N],pos;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++) cin >> a[i];    for(int i=1; i<=n; i++) cin >> b[i];    for(int i=1; i<=n; i++) q.push(INF);    // sort(a+1,a+1+n); sort(b+1,b+1+n);    for(int i=1; i<=n; i++)    {        for(int j=1; j<=n; j++)        {            int tmp=a[i]+b[j];            if(tmp<q.top())            {                q.pop();                q.push(tmp);            }else break;        }    }    while(!q.empty())    {        c[++pos]=q.top();        q.pop();    }    for(int i=pos; i>=1; i--)        cout << c[i] << " \n"[i==1];    return 0;}
      ]]> @@ -2828,7 +2828,7 @@ /2022/07/18/luo-gu-p3871-tjoi2010-zhong-wei-shu-ti-jie/ - 洛谷P3871 [TJOI2010]中位数 题解

      题目链接:P3871 [TJOI2010]中位数

      题意

      给定一个由N个元素组成的整数序列,现在有两种操作:

      1 add a

      在该序列的最后添加一个整数a,组成长度为N + 1的整数序列

      2 mid
      输出当前序列的中位数

      中位数是指将一个序列按照从小到大排序后处在中间位置的数。(若序列长度为偶数,则指处在中间位置的两个数中较小的那个)

      例1:1 2 13 14 15 16 中位数为13

      例2:1 3 5 7 10 11 17 中位数为7

      例3:1 1 1 2 3 中位数为1

      对于100%的数据,1 ≤ N ≤ 100,000,0 ≤ M ≤ 10,000

      序列中整数的绝对值不超过1,000,000,000,序列中的数可能有重复

      考虑维护一个对顶堆,详细见 link

      然后就是板子题,注意对询问分类讨论

      时间复杂度 $O((n+m) \log (n+m))$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()priority_queue<int> q1;priority_queue<int,vector<int>,greater<int> > q2;void clear(){    while(!q1.empty())q1.pop();    while(!q2.empty())q2.pop();}void solve(int x,int f){    if(f)    {        if(q1.size()<q2.size())cout << q2.top() << '\n';        else cout << q1.top() << '\n';    }else    {        if(!q1.empty()&&x<=q1.top())q1.push(x);        else q2.push(x);        while(abs((int)(q1.size()-q2.size()))>1)        {            if(q1.size()>q2.size())q2.push(q1.top()),q1.pop();            else q1.push(q2.top()),q2.pop();        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n,x; char op[5];    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> x;        solve(x,0);    }    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> op;        if(op[0]=='a')        {            cin >> x;            solve(x,0);        }else solve(x,1);    }    return 0;}
      ]]> + 洛谷P3871 [TJOI2010]中位数题解

      题目链接:P3871[TJOI2010]中位数

      题意

      给定一个由N个元素组成的整数序列,现在有两种操作:

      1 add a

      在该序列的最后添加一个整数a,组成长度为N + 1的整数序列

      2 mid 输出当前序列的中位数

      中位数是指将一个序列按照从小到大排序后处在中间位置的数。(若序列长度为偶数,则指处在中间位置的两个数中较小的那个)

      例1:1 2 13 14 15 16 中位数为13

      例2:1 3 5 7 10 11 17 中位数为7

      例3:1 1 1 2 3 中位数为1

      对于100%的数据,1 ≤ N ≤ 100,000,0 ≤ M ≤ 10,000

      序列中整数的绝对值不超过1,000,000,000,序列中的数可能有重复

      考虑维护一个对顶堆,详细见 link

      然后就是板子题,注意对询问分类讨论

      时间复杂度 \(O((n+m) \log(n+m))\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()priority_queue<int> q1;priority_queue<int,vector<int>,greater<int> > q2;void clear(){    while(!q1.empty())q1.pop();    while(!q2.empty())q2.pop();}void solve(int x,int f){    if(f)    {        if(q1.size()<q2.size())cout << q2.top() << '\n';        else cout << q1.top() << '\n';    }else    {        if(!q1.empty()&&x<=q1.top())q1.push(x);        else q2.push(x);        while(abs((int)(q1.size()-q2.size()))>1)        {            if(q1.size()>q2.size())q2.push(q1.top()),q1.pop();            else q1.push(q2.top()),q2.pop();        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n,x; char op[5];    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> x;        solve(x,0);    }    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> op;        if(op[0]=='a')        {            cin >> x;            solve(x,0);        }else solve(x,1);    }    return 0;}
      ]]> @@ -2853,7 +2853,7 @@ /2022/07/17/luo-gu-p3522-poi2011-tem-temperature-ti-jie/ - 洛谷P3522 [POI2011]TEM-Temperature 题解

      题目链接:P3522 [POI2011]TEM-Temperature

      题意

      某国进行了连续 $n$ ( $1 \le n \le 1,000,000$)天的温度测量,测量存在误差,测量结果是第 $i$ 天温度在 $[l_i,r_i]$ 范围内。

      求最长的连续的一段,满足该段内可能温度不降。

      不难发现合法区间为 $\max\{l_i\}>r_{i+1}$

      考虑单调队列维护最大值

      注意答案的更新应该用(i-1)-q[st]+1

      因为如果有个 $l_i$ 特别大,把当前维护的合法的 $l$ 都给pop掉了

      计数不应当是从现在的 $l_i$ 开始,而是最后一个被pop掉的

      这样用q[st]正好(我的代码是(l,r]的)

      时间复杂度 $O(n)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)int n,st,en,res=1,l[N],r[N],q[N];signed main(){    read(n); st=en=1;    for(int i=1; i<=n; i++)    {        read(l[i]);read(r[i]);        while(st<en&&l[q[st+1]]>r[i])++st;        if(st<en)res=max(res,i-q[st]);        while(st<en&&l[q[en]]<l[i])--en;        q[++en]=i;    }    write(res);    return 0;}
      ]]> + 洛谷P3522[POI2011]TEM-Temperature 题解

      题目链接:P3522[POI2011]TEM-Temperature

      题意

      某国进行了连续 \(n\) ( \(1 \le n \le1,000,000\))天的温度测量,测量存在误差,测量结果是第 \(i\) 天温度在 \([l_i,r_i]\) 范围内。

      求最长的连续的一段,满足该段内可能温度不降。

      不难发现合法区间为 \(\max\{l_i\}>r_{i+1}\)

      考虑单调队列维护最大值

      注意答案的更新应该用(i-1)-q[st]+1

      因为如果有个 \(l_i\)特别大,把当前维护的合法的 \(l\)都给pop掉了

      计数不应当是从现在的 \(l_i\)开始,而是最后一个被pop掉的

      这样用q[st]正好(我的代码是(l,r]的)

      时间复杂度 \(O(n)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)int n,st,en,res=1,l[N],r[N],q[N];signed main(){    read(n); st=en=1;    for(int i=1; i<=n; i++)    {        read(l[i]);read(r[i]);        while(st<en&&l[q[st+1]]>r[i])++st;        if(st<en)res=max(res,i-q[st]);        while(st<en&&l[q[en]]<l[i])--en;        q[++en]=i;    }    write(res);    return 0;}
      ]]> @@ -2880,7 +2880,7 @@ /2022/07/17/luo-gu-p2422-liang-hao-de-gan-jue-ti-jie/ - 洛谷P2422 良好的感觉 题解

      题目链接:P2422 良好的感觉

      题意

      kkk 做了一个人体感觉分析器。每一天,人都有一个感受值 $A_i$,$A_i$ 越大,表示人感觉越舒适。在一段时间 $\left[i, j\right]$ 内,人的舒适程度定义为 $\left[i, j\right]$ 中最不舒服的那一天的感受值 $\times$ $\left[i, j\right]$中每一天感受值的和。现在给出 kkk 在连续 $N$ 天中的感受值,请问,在哪一段时间,kkk 感觉最舒适?

      对于 $100\%$ 的数据,$1\le N\le 100000$,$1\le \texttt{感受值}\le 1000000$。

      题意简化一下就是

      找到一个区间,使得区间最小值乘以区间的和最大

      考虑每个最小值对区间的贡献

      设当前的数为 $a_i$ ,

      它能成为最小值的区间 $[l,r]$ 满足 $l\in [j+1,i],r\in [i,k-1]$

      其中 $j$ 是所有 $a_j <a_i$ 中最大的 $j$ ,$k$ 是所有 $a_k < a_i$ 中最小的 $k$

      设 $f_i$ 表示前 $i$ 个数中的最大答案

      这里的 $j,k$ 和上面同义,$S_i$ 为前缀和,即 $S_i = \sum_{j=1}^{i}a_j$

      于是用单调栈维护最小值,从左往右扫,就能把左半部分搞定了

      那么右半部分怎么搞呢?

      不难发现 $a_k$ 一定是第一个加入单调栈以后把 $a_i$ 踢出去的数

      我们只要在踢出 $a_i$ 的时候把 $i$ 到 $k-1$ 的贡献给加到 $f_i$ 上就好了

      注意最后一定要把 $a_{n+1}$ 设为一个极小值,使其能把前面所有的踢出去

      时间复杂度 $O(n)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,top,a[N],stk[N],f[N],sum[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    a[++n]=0;    for(int i=1; i<=n; i++)    {        sum[i]=sum[i-1]+a[i];        while(a[stk[top]]>a[i])        {            f[stk[top]]+=sum[i-1]-sum[stk[top]];            --top;        }        f[i]=sum[i]-sum[stk[top]];        stk[++top]=i;    }    int res=0;    for(int i=1; i<n; i++)        res=max(res,f[i]*a[i]);    cout << res << '\n';    return 0;}
      ]]> + 洛谷P2422 良好的感觉 题解

      题目链接:P2422良好的感觉

      题意

      kkk 做了一个人体感觉分析器。每一天,人都有一个感受值 \(A_i\),\(A_i\) 越大,表示人感觉越舒适。在一段时间\(\left[i, j\right]\)内,人的舒适程度定义为 \(\left[i,j\right]\) 中最不舒服的那一天的感受值 \(\times\) \(\left[i,j\right]\)中每一天感受值的和。现在给出 kkk 在连续 \(N\) 天中的感受值,请问,在哪一段时间,kkk感觉最舒适?

      对于 \(100\%\) 的数据,\(1\le N\le 100000\),\(1\le \texttt{感受值}\le 1000000\)。

      题意简化一下就是

      找到一个区间,使得区间最小值乘以区间的和最大

      考虑每个最小值对区间的贡献

      设当前的数为 \(a_i\)

      它能成为最小值的区间 \([l,r]\) 满足\(l\in [j+1,i],r\in [i,k-1]\)

      其中 \(j\) 是所有 \(a_j <a_i\) 中最大的 \(j\) ,\(k\) 是所有 \(a_k< a_i\) 中最小的 \(k\)

      \(f_i\) 表示前 \(i\) 个数中的最大答案 \[f_i = (f_j + S_i-S_j)+(f_k+S_{k-1}-S_i)\] 这里的 \(j,k\)和上面同义,\(S_i\) 为前缀和,即 \(S_i = \sum_{j=1}^{i}a_j\)

      于是用单调栈维护最小值,从左往右扫,就能把左半部分搞定了

      那么右半部分怎么搞呢?

      不难发现 \(a_k\)一定是第一个加入单调栈以后把 \(a_i\)踢出去的数

      我们只要在踢出 \(a_i\) 的时候把\(i\)\(k-1\) 的贡献给加到 \(f_i\) 上就好了

      注意最后一定要把 \(a_{n+1}\)设为一个极小值,使其能把前面所有的踢出去

      时间复杂度 \(O(n)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,top,a[N],stk[N],f[N],sum[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    a[++n]=0;    for(int i=1; i<=n; i++)    {        sum[i]=sum[i-1]+a[i];        while(a[stk[top]]>a[i])        {            f[stk[top]]+=sum[i-1]-sum[stk[top]];            --top;        }        f[i]=sum[i]-sum[stk[top]];        stk[++top]=i;    }    int res=0;    for(int i=1; i<n; i++)        res=max(res,f[i]*a[i]);    cout << res << '\n';    return 0;}
      ]]> @@ -2909,7 +2909,7 @@ /2022/07/17/luo-gu-p3512-poi2010-pil-pilots-ti-jie/ - 洛谷P3512 [POI2010]PIL-Pilots 题解

      题目链接:P3512 [POI2010]PIL-Pilots

      题意:给定 $n$ 个数,找一个最长区间,使得区间中的最大值减最小值不超过 $k$

      题面写的太烂了

      考虑用两个单调队列分别维护从 $1$ 到 $i$ 的最大值和最小值

      注意pop()时候的判断,具体见代码

      时间复杂度 $O(n)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define INF 0x3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(3e6+15)int n,k,pre,res,a[N];int st1,en1,st2,en2,q1[N],q2[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(k);read(n);    for(int i=1; i<=n; i++) read(a[i]);    if(n==1)return write(1),0;    st1=st2=en1=en2=1;    q1[++en1]=1;q2[++en2]=1; pre=1;    for(int i=2; i<=n; i++)    {        while(st1<en1&&a[q1[en1]]<a[i])--en1;        while(st2<en2&&a[q2[en2]]>a[i])--en2;        q1[++en1]=i; q2[++en2]=i;        while(a[q1[st1+1]]-a[q2[st2+1]]>k)        {            if(q1[st1+1]<q2[st2+1])                pre=q1[st1+1]+1,++st1;            else pre=q2[st2+1]+1,++st2;        }        res=max(res,i-pre+1);    }    write(res);    return 0;}
      ]]> + 洛谷P3512 [POI2010]PIL-Pilots题解

      题目链接:P3512[POI2010]PIL-Pilots

      题意:给定 \(n\)个数,找一个最长区间,使得区间中的最大值减最小值不超过 \(k\)

      题面写的太烂了

      考虑用两个单调队列分别维护从 \(1\)\(i\) 的最大值和最小值

      注意pop()时候的判断,具体见代码

      时间复杂度 \(O(n)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define INF 0x3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(3e6+15)int n,k,pre,res,a[N];int st1,en1,st2,en2,q1[N],q2[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(k);read(n);    for(int i=1; i<=n; i++) read(a[i]);    if(n==1)return write(1),0;    st1=st2=en1=en2=1;    q1[++en1]=1;q2[++en2]=1; pre=1;    for(int i=2; i<=n; i++)    {        while(st1<en1&&a[q1[en1]]<a[i])--en1;        while(st2<en2&&a[q2[en2]]>a[i])--en2;        q1[++en1]=i; q2[++en2]=i;        while(a[q1[st1+1]]-a[q2[st2+1]]>k)        {            if(q1[st1+1]<q2[st2+1])                pre=q1[st1+1]+1,++st1;            else pre=q2[st2+1]+1,++st2;        }        res=max(res,i-pre+1);    }    write(res);    return 0;}
      ]]> @@ -2934,7 +2934,7 @@ /2022/07/16/uva11362-phone-list-ti-jie/ - UVA11362 Phone List 题解

      题目链接:UVA11362 Phone List

      题意:给定$n$个长度不超过$10$的数字串,判断是否有两个字符串$A$和$B$,满足$A$是$B$的前缀,若有,输出NO,若没有,输出YES

      其实可以去建个 $\tt{trie}$ 树然后dfs的

      但是方便起见,直接sort就能过

      时间复杂度 $O(Qn^2)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)int Q,n;string s[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> Q;    while(Q--)    {        cin >> n;        for(int i=1; i<=n; i++)            cin >> s[i];        sort(s+1,s+1+n);        int ok=0;        for(int i=2; i<=n&&!ok; i++)            if(s[i].find(s[i-1])==0)ok=1;        cout << (ok?"NO\n":"YES\n");    }    return 0;}
      ]]> + UVA11362 Phone List 题解

      题目链接:UVA11362 PhoneList

      题意:给定\(n\)个长度不超过\(10\)的数字串,判断是否有两个字符串\(A\)和\(B\),满足\(A\)是\(B\)的前缀,若有,输出NO,若没有,输出YES

      其实可以去建个 \(\tt{trie}\)树然后dfs的

      但是方便起见,直接sort就能过

      时间复杂度 \(O(Qn^2)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)int Q,n;string s[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> Q;    while(Q--)    {        cin >> n;        for(int i=1; i<=n; i++)            cin >> s[i];        sort(s+1,s+1+n);        int ok=0;        for(int i=2; i<=n&&!ok; i++)            if(s[i].find(s[i-1])==0)ok=1;        cout << (ok?"NO\n":"YES\n");    }    return 0;}
      ]]> @@ -2959,7 +2959,7 @@ /2022/07/15/luo-gu-p4052-jsoi2007-wen-ben-sheng-cheng-qi-ti-jie/ - 洛谷P4052 [JSOI2007]文本生成器 题解

      题目链接:P4052 [JSOI2007]文本生成器

      题意

      JSOI 交给队员 ZYX 一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是 GW 文本生成器 v6 版。

      该软件可以随机生成一些文章——总是生成一篇长度固定且完全随机的文章。 也就是说,生成的文章中每个字符都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章 $s$ 包含单词 $t$,当且仅当单词 $t$ 是文章 $s$ 的子串)。但是,即使按照这样的标准,使用者现在使用的 GW 文本生成器 v6 版所生成的文章也是几乎完全不可读的。ZYX 需要指出 GW 文本生成器 v6 生成的所有文本中,可读文本的数量,以便能够成功获得 v7 更新版。你能帮助他吗?

      答案对 $10^4 + 7$ 取模。

      对于全部的测试点,保证:

      • $1 \leq n \leq 60$,$1 \leq m \leq 100$。
      • $1 \leq |s_i| \leq 100$,其中 $|s_i|$ 表示字符串 $s_i$ 的长度。
      • $s_i$ 中只含大写英文字母。

      显然要建 $\tt{AC}$ 自动机。包含单词,比较麻烦。

      考虑容斥,计算不包含单词的数量(不可读文本数量)

      「可读文本数量」等于「所有文本数量」减去「不可读文本数量」

      「所有文本数量」显然是 $26^m$

      不难发现,一个不可读文本在 $\tt{AC}$ 自动机上跑的时候,是不会碰到「危险结点」的

      「危险结点」包括「是字符串结尾」的结点和「某个或某些后缀是单词」的结点

      危险结点的处理比较简单,只要在建自动机的时候处理一下就好了,具体看代码

      然后这个题目是计数题,考虑dp

      设 $f_{i,j}$ 表示走到 $j$ 号结点,文本长度为 $i$ 时的方案数

      显然 $f_{0,0} = 1$

      这里的 $(u,v) \in E$ 表示在 $\tt{AC}$ 自动机上存在 $u$ 指向 $v$ 的边

      「不可读文本数量」就等于 $\sum_{0 \le i \le \text{tot}} f_{m,i}$

      时间复杂度 $O(26 \times m \sum |s_i|)$

      完整代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(105)#define L (int)(6e3+15)const int p=1e4+7;void add(int &a,int b){a=(a%p+b%p)%p;}int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1) ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}struct Trie{    queue<int> q;    int tot,trie[L][26],ed[L],fail[L],f[N][L];    void insert(string s)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            int c=s[i]-'A';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1;    }    void build()    {        for(int i=0; i<26; i++)            if(trie[0][i])q.push(trie[0][i]);        while(!q.empty())        {            int u=q.front(); q.pop();            for(int i=0; i<26; i++)            {                if(trie[u][i])                {                    fail[trie[u][i]]=trie[fail[u]][i];                    if(ed[fail[trie[u][i]]])ed[trie[u][i]]=1;                    q.push(trie[u][i]);                }else trie[u][i]=trie[fail[u]][i];            }        }    }    void solve(int m)    {        f[0][0]=1;        for(int i=0; i<m; i++)            for(int j=0; j<=tot; j++)                for(int k=0; k<26; k++)                    if(!ed[trie[j][k]])                        add(f[i+1][trie[j][k]],f[i][j]);        int res=qpow(26,m);        for(int i=0; i<=tot; i++)            res=((res-f[m][i])%p+p)%p;        cout << res << '\n';    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n,m; string s;    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> s;        tr.insert(s);    }    tr.build(); tr.solve(m);    return 0;}
      ]]> + 洛谷P4052[JSOI2007]文本生成器 题解

      题目链接:P4052[JSOI2007]文本生成器

      题意

      JSOI 交给队员 ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是GW 文本生成器 v6 版。

      该软件可以随机生成一些文章——总是生成一篇长度固定且完全随机的文章。也就是说,生成的文章中每个字符都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章\(s\) 包含单词 \(t\),当且仅当单词 \(t\) 是文章 \(s\)的子串)。但是,即使按照这样的标准,使用者现在使用的 GW 文本生成器 v6版所生成的文章也是几乎完全不可读的。ZYX 需要指出 GW 文本生成器 v6生成的所有文本中,可读文本的数量,以便能够成功获得 v7更新版。你能帮助他吗?

      答案对 \(10^4 + 7\) 取模。

      对于全部的测试点,保证:

      • \(1 \leq n \leq 60\)\(1 \leq m \leq 100\)。
      • \(1 \leq |s_i| \leq 100\),其中\(|s_i|\) 表示字符串 \(s_i\) 的长度。
      • \(s_i\) 中只含大写英文字母。

      显然要建 \(\tt{AC}\)自动机。包含单词,比较麻烦。

      考虑容斥,计算不包含单词的数量(不可读文本数量)

      「可读文本数量」等于「所有文本数量」减去「不可读文本数量」

      「所有文本数量」显然是 \(26^m\)

      不难发现,一个不可读文本在 \(\tt{AC}\)自动机上跑的时候,是不会碰到「危险结点」的

      「危险结点」包括「是字符串结尾」的结点和「某个或某些后缀是单词」的结点

      危险结点的处理比较简单,只要在建自动机的时候处理一下就好了,具体看代码

      然后这个题目是计数题,考虑dp

      \(f_{i,j}\) 表示走到 \(j\) 号结点,文本长度为 \(i\) 时的方案数

      显然 \(f_{0,0} = 1\) \[f_{i+1,v} = \sum_{(u,v) \in E} f_{i,u}\] 这里的 \((u,v) \in E\) 表示在\(\tt{AC}\) 自动机上存在 \(u\) 指向 \(v\) 的边

      「不可读文本数量」就等于 \(\sum_{0 \le i\le \text{tot}} f_{m,i}\)

      时间复杂度 \(O(26 \times m \sum|s_i|)\)

      完整代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(105)#define L (int)(6e3+15)const int p=1e4+7;void add(int &a,int b){a=(a%p+b%p)%p;}int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1) ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}struct Trie{    queue<int> q;    int tot,trie[L][26],ed[L],fail[L],f[N][L];    void insert(string s)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            int c=s[i]-'A';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1;    }    void build()    {        for(int i=0; i<26; i++)            if(trie[0][i])q.push(trie[0][i]);        while(!q.empty())        {            int u=q.front(); q.pop();            for(int i=0; i<26; i++)            {                if(trie[u][i])                {                    fail[trie[u][i]]=trie[fail[u]][i];                    if(ed[fail[trie[u][i]]])ed[trie[u][i]]=1;                    q.push(trie[u][i]);                }else trie[u][i]=trie[fail[u]][i];            }        }    }    void solve(int m)    {        f[0][0]=1;        for(int i=0; i<m; i++)            for(int j=0; j<=tot; j++)                for(int k=0; k<26; k++)                    if(!ed[trie[j][k]])                        add(f[i+1][trie[j][k]],f[i][j]);        int res=qpow(26,m);        for(int i=0; i<=tot; i++)            res=((res-f[m][i])%p+p)%p;        cout << res << '\n';    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n,m; string s;    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> s;        tr.insert(s);    }    tr.build(); tr.solve(m);    return 0;}
      ]]> @@ -2990,7 +2990,7 @@ /2022/07/15/luo-gu-p4113-heoi2012-cai-hua-ti-jie/ - 洛谷P4113 [HEOI2012]采花 题解

      题目链接:P4113 [HEOI2012]采花

      题意:萧薰儿是古国的公主,平时的一大爱好是采花。

      今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。

      花园足够大,容纳了 $n$ 朵花,共有 $c$ 种颜色,用整数 $1 \sim c$ 表示。且花是排成一排的,以便于公主采花。公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴。同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。

      由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了 $m$ 个行程,然后一一向你询问公主能采到的花共有几种不同的颜色。

      $1 \leq n, c, m \leq 2 \times 10^6$。

      对于全部的测试点,保证 $1 \leq x_i \leq c$,$1 \leq l \leq r \leq n$。

      其实就是 P1972 [SDOI2009] HH的项链 改了一改,

      本题具体的做法是预处理出所有第二次出现的数的贡献,然后离线处理询问。

      (作者语文比较烂,不知道这句话放哪里比较好,就放前面来了)

      下面是详细的解释。


      设数组 $\text{val} = \{1,2,2,3,1,1,3\}$

      要询问一个区间 $[1,7]=\{1,2,2,3,1,1,3\}$

      每个数的贡献是这样的 $\{0,0,1,0,1,0,1\}$

      实际上我们只需要处理第二次出现的数的贡献即可

      因为出现两次是容易处理的,并且小于的不会产生贡献,大于的不会增加贡献

      不难发现区间询问可以用树状数组维护贡献

      一个暴力的想法是,对于每个询问 $[l,r]$ ,我们扫一遍 $[1,l-1]$

      为什么要扫一遍呢?因为对于出现大于两次的,

      我们只把贡献记在了它第二次出现的位置上,

      而这个数可能在 $[1,l-1]$ 和 $[l,r]$ 都出现了两次及以上

      当然这样暴力扫肯定是不行的,考虑离线处理询问。

      把询问按照 $l$ 从小到大排序,这样我们只要在询问的过程中扫就可以了

      时间复杂度 $O(m \log n)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e6+15)struct node{    int l,r,id;}q[N];int n,k,m,b[N],val[N],nx[N],first[N],ans[N];struct BIT{    int tree[N];    int lowbit(int x){return x&(-x);}    void add(int x,int v)    {        for(; x<=n; x+=lowbit(x))            tree[x]+=v;    }    int qSum(int x)    {        int res=0;        for(; x>=1; x-=lowbit(x))            res+=tree[x];        return res;    }}tr;signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(k);read(m);    for(int i=1; i<=n; i++)         read(val[i]);    for(int i=n; i>=1; i--)    {        nx[i]=first[val[i]];        first[val[i]]=i;    }    for(int i=1; i<=n; i++)        if(++b[val[i]]==2)tr.add(i,1);    for(int i=1; i<=m; i++)    {        read(q[i].l);read(q[i].r);        q[i].id=i;    }    sort(q+1,q+1+m,[](node a,node b){return a.l<b.l;});    int now=1;    for(int i=1; i<=m; i++)    {        for(; now<q[i].l; ++now)        {            if(nx[now])tr.add(nx[now],-1);            if(nx[nx[now]])tr.add(nx[nx[now]],1);        }        ans[q[i].id]=tr.qSum(q[i].r)-tr.qSum(q[i].l);    }    for(int i=1; i<=m; i++)        write(ans[i]),pc('\n');    return 0;}

      参考文献

      [1] https://www.luogu.com.cn/blog/zykykyk/solution-p4113

      ]]> + 洛谷P4113 [HEOI2012]采花 题解

      题目链接:P4113[HEOI2012]采花

      题意:萧薰儿是古国的公主,平时的一大爱好是采花。

      今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花。

      花园足够大,容纳了 \(n\) 朵花,共有\(c\) 种颜色,用整数 \(1 \sim c\)表示。且花是排成一排的,以便于公主采花。公主每次采花后会统计采到的花的颜色数,颜色数越多她会越高兴。同时,她有一癖好,她不允许最后自己采到的花中,某一颜色的花只有一朵。为此,公主每采一朵花,要么此前已采到此颜色的花,要么有相当正确的直觉告诉她,她必能再次采到此颜色的花。

      由于时间关系,公主只能走过花园连续的一段进行采花,便让女仆福涵洁安排行程。福涵洁综合各种因素拟定了\(m\)个行程,然后一一向你询问公主能采到的花共有几种不同的颜色。

      \(1 \leq n, c, m \leq 2 \times10^6\)

      对于全部的测试点,保证 \(1 \leq x_i \leqc\)\(1 \leq l \leq r \leqn\)

      其实就是 P1972[SDOI2009] HH的项链 改了一改,

      本题具体的做法是预处理出所有第二次出现的数的贡献,然后离线处理询问。

      (作者语文比较烂,不知道这句话放哪里比较好,就放前面来了)

      下面是详细的解释。


      设数组 \(\text{val} =\{1,2,2,3,1,1,3\}\)

      要询问一个区间 \([1,7]=\{1,2,2,3,1,1,3\}\)

      每个数的贡献是这样的 \(\{0,0,1,0,1,0,1\}\)

      实际上我们只需要处理第二次出现的数的贡献即可

      因为出现两次是容易处理的,并且小于的不会产生贡献,大于的不会增加贡献

      不难发现区间询问可以用树状数组维护贡献

      一个暴力的想法是,对于每个询问 \([l,r]\) ,我们扫一遍 \([1,l-1]\)

      为什么要扫一遍呢?因为对于出现大于两次的,

      我们只把贡献记在了它第二次出现的位置上,

      而这个数可能在 \([1,l-1]\)\([l,r]\) 都出现了两次及以上

      当然这样暴力扫肯定是不行的,考虑离线处理询问。

      把询问按照 \(l\)从小到大排序,这样我们只要在询问的过程中扫就可以了

      时间复杂度 \(O(m \log n)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(2e6+15)struct node{    int l,r,id;}q[N];int n,k,m,b[N],val[N],nx[N],first[N],ans[N];struct BIT{    int tree[N];    int lowbit(int x){return x&(-x);}    void add(int x,int v)    {        for(; x<=n; x+=lowbit(x))            tree[x]+=v;    }    int qSum(int x)    {        int res=0;        for(; x>=1; x-=lowbit(x))            res+=tree[x];        return res;    }}tr;signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(k);read(m);    for(int i=1; i<=n; i++)         read(val[i]);    for(int i=n; i>=1; i--)    {        nx[i]=first[val[i]];        first[val[i]]=i;    }    for(int i=1; i<=n; i++)        if(++b[val[i]]==2)tr.add(i,1);    for(int i=1; i<=m; i++)    {        read(q[i].l);read(q[i].r);        q[i].id=i;    }    sort(q+1,q+1+m,[](node a,node b){return a.l<b.l;});    int now=1;    for(int i=1; i<=m; i++)    {        for(; now<q[i].l; ++now)        {            if(nx[now])tr.add(nx[now],-1);            if(nx[nx[now]])tr.add(nx[nx[now]],1);        }        ans[q[i].id]=tr.qSum(q[i].r)-tr.qSum(q[i].l);    }    for(int i=1; i<=m; i++)        write(ans[i]),pc('\n');    return 0;}

      参考文献

      [1] https://www.luogu.com.cn/blog/zykykyk/solution-p4113

      ]]> @@ -3017,7 +3017,7 @@ /2022/07/14/luo-gu-p3294-scoi2016-bei-dan-ci-ti-jie/ - 洛谷P3294 [SCOI2016]背单词 题解

      题目链接:P3294 [SCOI2016]背单词

      题意:给定 $n$ 个不同的字符串,求一个最优顺序使得花费最小

      设当前字符串为 $a$ ,位于排列中的 $x$ 位置

      • 如果 $a$ 存在后缀且 $a$ 的后缀在 $a$ 之后,花费增加 $n^2$
      • 如果 $a$ 不存在后缀,则花费增加 $x$
      • 设 $y$ 为 $a$ 之前与其相距最小的,且是 $a$ 的后缀的字符串的位置(前提是 $a$ 存在后缀),则花费增加 $x-y$

      根据贪心,前两个条件基本上没有用

      同时为了方便处理,把所有字符串反转,考虑其前缀。

      原问题转化为:

      设某个字符串为 $a_i$ ,位于排列中的 $x_i$ 位置

      • $a_i$ 所有前缀必须在 $a_i$ 之前

      • 设 $y_i$ 为 $a_i$ 之前与其相距最小的,且是 $a_i$ 的前缀的字符串的位置

        如果 $a$ 不存在前缀,则 $y_i=0$ ,记 $v_i=x_i-y_i$

      给定 $n$ 个字符串,求一种排列顺序,使得 $\sum v_i$ 最小,求出这个最小值。

      考虑建 $\tt{trie}$ 树以存储字符串

      然后重构整棵树,只保留「是字符串结尾的结点」,注意根节点也要保留

      然后对于每个结点,将其子结点按子树大小从小到大排序

      然后跑一遍dfs就好了

      重构是为了方便排序子结点。

      排序的原因:

      设当前结点为 $u$ ,则 $u$ 的所有子结点都有相同的前缀

      这个前缀就是根节点到 $u$ 形成的字符串。

      对于排序后在后面的子结点,想在排列中距离 $u$ 尽可能小

      就需要前面的子结点所在子树大小尽可能小,这样他们之间夹着的字符串就少

      根据贪心,不难发现这样可以最小化答案

      时间复杂度 $O(L \log L)$ ,其中 $L=\sum |s_i|$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define ll long long// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define L (int)(5.1e5+15)bool cmp(int a,int b);struct Trie{    vector<int> vec[L]; ll res;    int tot,trie[L][26],ed[L],last[L],sz[L],cnt;    void insert(string s)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            int c=s[i]-'a';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1;    }    void rebd(int u)    {        if(ed[u]&&u)        {            vec[last[u]].push_back(u);            last[u]=u;        }        for(int i=0; i<26; i++)            if(trie[u][i])            {                int v=trie[u][i];                last[v]=last[u]; rebd(v);            }    }    void dfs(int u)    {        sz[u]=1;        for(int i=0; i<vec[u].size(); i++)        {            dfs(vec[u][i]);            sz[u]+=sz[vec[u][i]];        }        sort(vec[u].begin(),vec[u].end(),cmp);    }    void solve(int u)    {        int dfn=cnt++;        for(int i=0; i<vec[u].size(); i++)        {            res+=cnt-dfn;            solve(vec[u][i]);        }    }    void main()    {        ed[0]=1; rebd(0);        dfs(0); solve(0);        cout << res << '\n';    }}tr;bool cmp(int a,int b){return tr.sz[a]<tr.sz[b];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n; string s;    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> s;        reverse(s.begin(),s.end());        tr.insert(s);    }    tr.main();    return 0;}
      ]]> + 洛谷P3294 [SCOI2016]背单词题解

      题目链接:P3294[SCOI2016]背单词

      题意:给定 \(n\)个不同的字符串,求一个最优顺序使得花费最小

      设当前字符串为 \(a\) ,位于排列中的\(x\) 位置

      • 如果 \(a\) 存在后缀且 \(a\) 的后缀在 \(a\) 之后,花费增加 \(n^2\)
      • 如果 \(a\) 不存在后缀,则花费增加\(x\)
      • \(y\)\(a\)之前与其相距最小的,且是 \(a\) 的后缀的字符串的位置(前提是 \(a\) 存在后缀),则花费增加 \(x-y\)

      根据贪心,前两个条件基本上没有用

      同时为了方便处理,把所有字符串反转,考虑其前缀。

      原问题转化为:

      设某个字符串为 \(a_i\),位于排列中的 \(x_i\) 位置

      • \(a_i\) 所有前缀必须在 \(a_i\) 之前

      • \(y_i\)\(a_i\)之前与其相距最小的,且是 \(a_i\) 的前缀的字符串的位置

      如果 \(a\) 不存在前缀,则 \(y_i=0\) ,记 \(v_i=x_i-y_i\)

      给定 \(n\)个字符串,求一种排列顺序,使得 \(\sumv_i\) 最小,求出这个最小值。

      考虑建 \(\tt{trie}\)树以存储字符串

      然后重构整棵树,只保留「是字符串结尾的结点」,注意根节点也要保留

      然后对于每个结点,将其子结点按子树大小从小到大排序

      然后跑一遍dfs就好了

      重构是为了方便排序子结点。

      排序的原因:

      设当前结点为 \(u\) ,则 \(u\) 的所有子结点都有相同的前缀

      这个前缀就是根节点到 \(u\)形成的字符串。

      对于排序后在后面的子结点,想在排列中距离 \(u\) 尽可能小

      就需要前面的子结点所在子树大小尽可能小,这样他们之间夹着的字符串就少

      根据贪心,不难发现这样可以最小化答案

      时间复杂度 \(O(L \log L)\) ,其中\(L=\sum |s_i|\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define ll long long// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define L (int)(5.1e5+15)bool cmp(int a,int b);struct Trie{    vector<int> vec[L]; ll res;    int tot,trie[L][26],ed[L],last[L],sz[L],cnt;    void insert(string s)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            int c=s[i]-'a';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1;    }    void rebd(int u)    {        if(ed[u]&&u)        {            vec[last[u]].push_back(u);            last[u]=u;        }        for(int i=0; i<26; i++)            if(trie[u][i])            {                int v=trie[u][i];                last[v]=last[u]; rebd(v);            }    }    void dfs(int u)    {        sz[u]=1;        for(int i=0; i<vec[u].size(); i++)        {            dfs(vec[u][i]);            sz[u]+=sz[vec[u][i]];        }        sort(vec[u].begin(),vec[u].end(),cmp);    }    void solve(int u)    {        int dfn=cnt++;        for(int i=0; i<vec[u].size(); i++)        {            res+=cnt-dfn;            solve(vec[u][i]);        }    }    void main()    {        ed[0]=1; rebd(0);        dfs(0); solve(0);        cout << res << '\n';    }}tr;bool cmp(int a,int b){return tr.sz[a]<tr.sz[b];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n; string s;    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> s;        reverse(s.begin(),s.end());        tr.insert(s);    }    tr.main();    return 0;}
      ]]> @@ -3044,7 +3044,7 @@ /2022/07/14/luo-gu-p2444-poi2000-bing-du-ti-jie/ - 洛谷P2444 [POI2000]病毒 题解

      题目链接:P2444 [POI2000]病毒

      题意

      二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

      示例:

      例如如果 $\{011, 11, 00000\}$ 为病毒代码段,那么一个可能的无限长安全代码就是 $010101 \ldots$。如果 $\{01, 11, 000000\}$ 为病毒代码段,那么就不存在一个无限长的安全代码。

      现在给出所有的病毒代码段,判断是否存在无限长的安全代码。

      $1 \leq n \leq 2000$,所有病毒代码段的总长度不超过 $3 \times 10^4$。

      不难发现这是一个 $\tt{AC}$ 自动机的题目

      考虑一个安全代码在 $\tt{AC}$ 自动机上是怎么跑的

      显然它会从 $0$ 结点开始跑,然后最后在一个环上面跑

      这个环显然不能包含「是字符串结尾的结点」

      仔细想一想,其实不止这些结点!

      对于一个结点 $u$ ,如果它的 $\tt{fail}$ 指针所指向的结点是「是字符串结尾的结点」

      那么 $u$ 也是不能走的我们称这些结点为 「含病毒后缀的结点」

      当然,如果一个结点的 $\tt{fail}$ 指针所指向的结点是「含病毒后缀的结点」,

      那么这个结点也是「含病毒后缀的结点」,同样不可以走

      说了这么多,其实只需要在板子上加一句

      if(ed[fail[trie[u][i]]])++ed[trie[u][i]];

      就好了

      时间复杂度 $O(\sum |s_i|)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)#define L (int)(3e4+15)struct Trie{    queue<int> q;    int tot,trie[L][2],ed[L],fail[L];    void insert(string s)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            int c=s[i]-'0';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        ++ed[u];    }    void build()    {        for(int i=0; i<2; i++)            if(trie[0][i])q.push(trie[0][i]);        while(!q.empty())        {            int u=q.front(); q.pop();            for(int i=0; i<2; i++)            {                if(trie[u][i])                {                    fail[trie[u][i]]=trie[fail[u]][i];                    if(ed[fail[trie[u][i]]])++ed[trie[u][i]];                    q.push(trie[u][i]);                }else trie[u][i]=trie[fail[u]][i];            }        }    }    int ins[L],vis[L];    bool dfs(int u)    {        ins[u]=1;        for(int i=0; i<2; i++)        {            int v=trie[u][i];            if(ins[v])return 1;            if(vis[v]||ed[v])continue;            vis[v]=1; if(dfs(v))return 1;        }        ins[u]=0;        return 0;    }    void solve()    {        for(int i=1; i<=tot; i++)            ins[i]=vis[i]=0;        vis[0]=1;        cout << (dfs(0)?"TAK\n":"NIE\n");    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n; string s;    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> s;        tr.insert(s);    }    tr.build();tr.solve();    return 0;}
      ]]> + 洛谷P2444 [POI2000]病毒 题解

      题目链接:P2444[POI2000]病毒

      题意

      二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

      示例:

      例如如果 \(\{011, 11, 00000\}\)为病毒代码段,那么一个可能的无限长安全代码就是 \(010101 \ldots\)。如果 \(\{01, 11, 000000\}\)为病毒代码段,那么就不存在一个无限长的安全代码。

      现在给出所有的病毒代码段,判断是否存在无限长的安全代码。

      \(1 \leq n \leq2000\),所有病毒代码段的总长度不超过 \(3 \times 10^4\)。

      不难发现这是一个 \(\tt{AC}\)自动机的题目

      考虑一个安全代码在 \(\tt{AC}\)自动机上是怎么跑的

      显然它会从 \(0\)结点开始跑,然后最后在一个环上面跑

      这个环显然不能包含「是字符串结尾的结点」

      仔细想一想,其实不止这些结点!

      对于一个结点 \(u\) ,如果它的 \(\tt{fail}\)指针所指向的结点是「是字符串结尾的结点」

      那么 \(u\)也是不能走的我们称这些结点为 「含病毒后缀的结点」

      当然,如果一个结点的 \(\tt{fail}\)指针所指向的结点是「含病毒后缀的结点」,

      那么这个结点也是「含病毒后缀的结点」,同样不可以走

      说了这么多,其实只需要在板子上加一句

      if(ed[fail[trie[u][i]]])++ed[trie[u][i]];

      就好了

      时间复杂度 \(O(\sum |s_i|)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)#define L (int)(3e4+15)struct Trie{    queue<int> q;    int tot,trie[L][2],ed[L],fail[L];    void insert(string s)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            int c=s[i]-'0';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        ++ed[u];    }    void build()    {        for(int i=0; i<2; i++)            if(trie[0][i])q.push(trie[0][i]);        while(!q.empty())        {            int u=q.front(); q.pop();            for(int i=0; i<2; i++)            {                if(trie[u][i])                {                    fail[trie[u][i]]=trie[fail[u]][i];                    if(ed[fail[trie[u][i]]])++ed[trie[u][i]];                    q.push(trie[u][i]);                }else trie[u][i]=trie[fail[u]][i];            }        }    }    int ins[L],vis[L];    bool dfs(int u)    {        ins[u]=1;        for(int i=0; i<2; i++)        {            int v=trie[u][i];            if(ins[v])return 1;            if(vis[v]||ed[v])continue;            vis[v]=1; if(dfs(v))return 1;        }        ins[u]=0;        return 0;    }    void solve()    {        for(int i=1; i<=tot; i++)            ins[i]=vis[i]=0;        vis[0]=1;        cout << (dfs(0)?"TAK\n":"NIE\n");    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n; string s;    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> s;        tr.insert(s);    }    tr.build();tr.solve();    return 0;}
      ]]> @@ -3071,7 +3071,7 @@ /2022/07/13/luo-gu-p3966-tjoi2013-dan-ci-ti-jie/ - 洛谷P3966 [TJOI2013]单词 题解

      题目链接:P3966 [TJOI2013]单词

      题意

      小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次。

      $1 \leq n \leq 200$,单词总长度不超过 $10^6$。

      瞎搞做法能跑到最优解前3页也是有趣

      这个文章看样例应该是单词间有空格的

      于是我们就把所有单词全部加上去,用#分隔

      然后就变成 P5357 AC自动机二次加强版 了

      具体的就是把暴力跳fail数组改成topo排序更新

      时间复杂度 $O(26 \times \sum s_i)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(205)#define L (int)(1e6+215)int n,f[N],fail[L],val[L],in[L],ans[N];string s,t;struct Trie{    queue<int> q;    int tot,trie[L][26],ed[L];    void insert(string s,int id)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            int c=s[i]-'a';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        if(!ed[u])ed[u]=id;        f[id]=ed[u];    }    void build()    {        for(int i=0; i<26; i++)            if(trie[0][i])q.push(trie[0][i]);        while(!q.empty())        {            int u=q.front();q.pop();            for(int i=0; i<26; i++)            {                if(trie[u][i])                {                    fail[trie[u][i]]=trie[fail[u]][i];                    ++in[trie[fail[u]][i]];                    q.push(trie[u][i]);                }else trie[u][i]=trie[fail[u]][i];            }        }    }    void AC()    {        int u=0;        for(int i=0; i<t.size(); i++)        {            if(t[i]=='#')u=0;            else u=trie[u][t[i]-'a'];            ++val[u];        }        for(int i=1; i<=tot; i++)            if(!in[i])q.push(i);        while(!q.empty())        {            u=q.front(); q.pop();            if(ed[u])ans[ed[u]]=val[u];            val[fail[u]]+=val[u];            if(!--in[fail[u]])q.push(fail[u]);        }    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> s; t+=s+"#";        tr.insert(s,i);    }    tr.build(); tr.AC();    for(int i=1; i<=n; i++)        cout << ans[f[i]] << '\n';    return 0;}
      ]]> + 洛谷P3966 [TJOI2013]单词 题解

      题目链接:P3966[TJOI2013]单词

      题意

      小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次。

      \(1 \leq n \leq200\),单词总长度不超过 \(10^6\)。

      瞎搞做法能跑到最优解前3页也是有趣

      这个文章看样例应该是单词间有空格的

      于是我们就把所有单词全部加上去,用#分隔

      然后就变成 P5357 AC自动机二次加强版 了

      具体的就是把暴力跳fail数组改成topo排序更新

      时间复杂度 \(O(26 \times \sums_i)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(205)#define L (int)(1e6+215)int n,f[N],fail[L],val[L],in[L],ans[N];string s,t;struct Trie{    queue<int> q;    int tot,trie[L][26],ed[L];    void insert(string s,int id)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            int c=s[i]-'a';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        if(!ed[u])ed[u]=id;        f[id]=ed[u];    }    void build()    {        for(int i=0; i<26; i++)            if(trie[0][i])q.push(trie[0][i]);        while(!q.empty())        {            int u=q.front();q.pop();            for(int i=0; i<26; i++)            {                if(trie[u][i])                {                    fail[trie[u][i]]=trie[fail[u]][i];                    ++in[trie[fail[u]][i]];                    q.push(trie[u][i]);                }else trie[u][i]=trie[fail[u]][i];            }        }    }    void AC()    {        int u=0;        for(int i=0; i<t.size(); i++)        {            if(t[i]=='#')u=0;            else u=trie[u][t[i]-'a'];            ++val[u];        }        for(int i=1; i<=tot; i++)            if(!in[i])q.push(i);        while(!q.empty())        {            u=q.front(); q.pop();            if(ed[u])ans[ed[u]]=val[u];            val[fail[u]]+=val[u];            if(!--in[fail[u]])q.push(fail[u]);        }    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> s; t+=s+"#";        tr.insert(s,i);    }    tr.build(); tr.AC();    for(int i=1; i<=n; i++)        cout << ans[f[i]] << '\n';    return 0;}
      ]]> @@ -3098,7 +3098,7 @@ /2022/07/13/luo-gu-p4407-jsoi2009-dian-zi-zi-dian-ti-jie/ - 洛谷P4407 [JSOI2009] 电子字典 题解

      题目链接:P4407 [JSOI2009] 电子字典

      题意:人们在英文字典中查找某个单词的时候可能不知道该单词的完整拼法,而只知道该单词的一个错误的近似拼法,这时人们可能陷入困境,为了查找一个单词而浪费大量的时间。带有模糊查询功能的电子字典能够从一定程度上解决这一问题:用户只要输入一个字符串,电子字典就返回与该单词编辑距离最小的几个单词供用户选择。

      字符串a与字符串 $b$ 的编辑距离是指:允许对 $a$ 或 $b$ 串进行下列“编辑”操作,将 $a$ 变为 $b$ 或 $b$ 变为 $a$ ,最少“编辑”次数即为距离。

      1. 删除串中某个位置的字母;
      2. 添加一个字母到串中某个位置;
      3. 替换串中某一位置的一个字母为另一个字母;

      给定 $n$ 个不同的单词,$m$ 次询问,对于一个待查询字符串,如果它是单词,则返回 $-1$;如果它不是单词,则返回字典中有多少个单词与它的编辑距离为 $1$ 。

      $n,m \le 10,000$ ,每个单词长度 $\le 20$ ,每个待查询字符串长度 $\le 20$

      首先建 $\tt{trie}$ 树很容易想到

      注意到这个数据范围很小,尝试在 $\tt{trie}$ 树上暴搜。

      显然我们需要记录的状态有:

      1. 当前所在结点 $u$
      2. 当前枚举的字符串长度 $l$ (实际上并不完全是,只是判断长度用的)
      3. 是否使用修改 $\text{ck}=0/1$ ( $0$ 表示没有使用修改)

      下面是算法分析,比较抽象,建议直接看代码。所以为什么我要写

      方便起见,记当前字符为 $s_l$

      当 $\text{ck}=1$ 时,如果 $u$ 存在字符为 $s_l$ 的子结点,直接搜。

      当 $\text{ck}=0$ 时,除了搜子结点,还需要考虑每个操作的处理

      • 操作1:删除某个字符。直接搜 $l+1$ 就好了
      • 操作2:增加某个字符。直接枚举字符,搜 $l$ 就好了
      • 操作3:替换某个字符。枚举一个不同于 $s_{l+1}$ 的字符,搜 $l+1$

      注意去重!!!!

      因为 $\tt{abc}$ 在 $\tt{a}$ 前后增加一个 $\tt{a}$ ,都会变成 $\tt{aabc}$ !!!

      时间复杂度分析

      比较有趣。

      对于一个长度为 $20$ 的串,思考我们至多枚举了多少修改串

      一共有 $21$ 个位置可添加字符,$20$ 个字符可替换,$20$ 个字符可删除,则

      $\tt{trie}$ 树树高至多在 $20$ ,$m\le 10^4$

      则粗略算一下,计算量为

      当然这个估计显然是过于大了。

      因为很多枚举的修改串拥有相同前缀

      也就是说,这些串并不是每个都要从上到下跑 $\tt{trie}$ 树的

      而且我们有很多的修改串会被剪掉,这样看来完全可过

      强行写个时间复杂度,那可能是 $O(\max\{s_i\}\times\sum |s_i|^2)$ 吧

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,m,vis[N],q[N],pos;string s;struct Trie{    int tot,trie[N][26],ori,len,ed[N];    void insert(string s)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            int c=s[i]-'a';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1;    }    void dfs(int u,int l,int ck)    {        if(l==len&&ed[u]&&!ck)            return ori=1,void(0);        if(l==len&&ed[u])        {            if(!vis[u]) vis[q[++pos]=u]=1;            return ;        }        int c=s[l]-'a';        if(!ck)        {            if(l<len)dfs(u,l+1,1);            for(int i=0; i<26; i++)                if(trie[u][i])                {                    dfs(trie[u][i],l,1);                    if(i!=c)dfs(trie[u][i],l+1,1);                }        }        if(l==len)return;        if(trie[u][c])dfs(trie[u][c],l+1,ck);    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> s,tr.insert(s);    for(int i=1; i<=m; i++)    {        cin >> s; tr.ori=0; tr.len=s.size(); tr.dfs(0,0,0);        if(tr.ori)cout << "-1\n";        else cout << pos << '\n';        for(int i=1; i<=pos; i++)            vis[q[i]]=0;        pos=0;    }    return 0;}
      ]]> + 洛谷P4407 [JSOI2009] 电子字典题解

      题目链接:P4407[JSOI2009] 电子字典

      题意:人们在英文字典中查找某个单词的时候可能不知道该单词的完整拼法,而只知道该单词的一个错误的近似拼法,这时人们可能陷入困境,为了查找一个单词而浪费大量的时间。带有模糊查询功能的电子字典能够从一定程度上解决这一问题:用户只要输入一个字符串,电子字典就返回与该单词编辑距离最小的几个单词供用户选择。

      字符串a与字符串 \(b\)的编辑距离是指:允许对 \(a\)\(b\) 串进行下列“编辑”操作,将 \(a\) 变为 \(b\) 或 \(b\) 变为 \(a\) ,最少“编辑”次数即为距离。

      1. 删除串中某个位置的字母;
      2. 添加一个字母到串中某个位置;
      3. 替换串中某一位置的一个字母为另一个字母;

      给定 \(n\) 个不同的单词,\(m\)次询问,对于一个待查询字符串,如果它是单词,则返回 \(-1\);如果它不是单词,则返回字典中有多少个单词与它的编辑距离为\(1\)

      \(n,m \le 10,000\) ,每个单词长度\(\le 20\) ,每个待查询字符串长度 \(\le 20\)

      首先建 \(\tt{trie}\)树很容易想到

      注意到这个数据范围很小,尝试在 \(\tt{trie}\) 树上暴搜。

      显然我们需要记录的状态有:

      1. 当前所在结点 \(u\)
      2. 当前枚举的字符串长度 \(l\)(实际上并不完全是,只是判断长度用的)
      3. 是否使用修改 \(\text{ck}=0/1\)\(0\) 表示没有使用修改)

      下面是算法分析,比较抽象,建议直接看代码。所以为什么我要写

      方便起见,记当前字符为 \(s_l\)

      \(\text{ck}=1\) 时,如果 \(u\) 存在字符为 \(s_l\) 的子结点,直接搜。

      \(\text{ck}=0\)时,除了搜子结点,还需要考虑每个操作的处理

      • 操作1:删除某个字符。直接搜 \(l+1\)就好了
      • 操作2:增加某个字符。直接枚举字符,搜 \(l\) 就好了
      • 操作3:替换某个字符。枚举一个不同于 \(s_{l+1}\) 的字符,搜 \(l+1\)

      注意去重!!!!

      因为 \(\tt{abc}\)\(\tt{a}\) 前后增加一个 \(\tt{a}\) ,都会变成 \(\tt{aabc}\) !!!

      时间复杂度分析

      比较有趣。

      对于一个长度为 \(20\)的串,思考我们至多枚举了多少修改串

      一共有 \(21\)个位置可添加字符,\(20\)个字符可替换,\(20\) 个字符可删除,则\[(21+20) \times 26 + 20 = 1086\] \(\tt{trie}\) 树树高至多在\(20\)\(m\le 10^4\)

      则粗略算一下,计算量为 \[1086 \times 2\times 10^5 = 2.172\times 10^8\] 当然这个估计显然是过于大了。

      因为很多枚举的修改串拥有相同前缀

      也就是说,这些串并不是每个都要从上到下跑 \(\tt{trie}\) 树的

      而且我们有很多的修改串会被剪掉,这样看来完全可过

      强行写个时间复杂度,那可能是 \(O(\max\{s_i\}\times\sum |s_i|^2)\) 吧

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,m,vis[N],q[N],pos;string s;struct Trie{    int tot,trie[N][26],ori,len,ed[N];    void insert(string s)    {        int u=0;        for(int i=0; i<s.size(); i++)        {            int c=s[i]-'a';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1;    }    void dfs(int u,int l,int ck)    {        if(l==len&&ed[u]&&!ck)            return ori=1,void(0);        if(l==len&&ed[u])        {            if(!vis[u]) vis[q[++pos]=u]=1;            return ;        }        int c=s[l]-'a';        if(!ck)        {            if(l<len)dfs(u,l+1,1);            for(int i=0; i<26; i++)                if(trie[u][i])                {                    dfs(trie[u][i],l,1);                    if(i!=c)dfs(trie[u][i],l+1,1);                }        }        if(l==len)return;        if(trie[u][c])dfs(trie[u][c],l+1,ck);    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> s,tr.insert(s);    for(int i=1; i<=m; i++)    {        cin >> s; tr.ori=0; tr.len=s.size(); tr.dfs(0,0,0);        if(tr.ori)cout << "-1\n";        else cout << pos << '\n';        for(int i=1; i<=pos; i++)            vis[q[i]]=0;        pos=0;    }    return 0;}
      ]]> @@ -3127,7 +3127,7 @@ /2022/07/13/luo-gu-p2922-usaco08dec-secret-message-g-ti-jie/ - 洛谷P2922 [USACO08DEC]Secret Message G 题解

      题目链接:P2922 [USACO08DEC]Secret Message G

      题意

      一句话题意:$m$ 次询问,查询已有的 $n$ 个 $\tt{01}$ 串中,有多少串 $t_i$ 能使询问串 $s$ 成为其前缀。

      贝茜正在领导奶牛们逃跑.为了联络,奶牛们互相发送秘密信息.

      信息是二进制的,共有 $M$($1 \le M \le 50000$)条,反间谍能力很强的约翰已经部分拦截了这些信息,知道了第 $i$ 条二进制信息的前 $b_i$($l \le b_i \le 10000$)位,他同时知道,奶牛使用 $N$($1 \le N \le 50000$)条暗号.但是,他仅仅知道第 $j$ 条暗号的前 $c_j$($1 \le c_j \le 10000$)位。

      对于每条暗号 $j$,他想知道有多少截得的信息能够和它匹配。也就是说,有多少信息和这条暗号有着相同的前缀。当然,这个前缀长度必须等于暗号和那条信息长度的较小者。

      在输入文件中,位的总数(即 $\sum b_i + \sum c_i$)不会超过 $500000$。

      考虑建 $\tt{trie}$ 树处理

      没了,就这么简单。

      具体见代码,有一些小的细节要注意

      时间复杂度 $O(\sum b_i + M \sum c_i)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)string str;int n,m,k,tot;struct Trie{    int trie[N][2],ed[N],sum[N];    void insert(int l)    {        int u=0;        for(int i=1,c; i<=l; i++)        {            cin >> c;            if(!trie[u][c])trie[u][c]=++tot;            ++sum[u=trie[u][c]];        }        ++ed[u];    }    int query(int l)    {        int u=0,ok=1,res=0;        for(int i=1,c; i<=l; i++)        {            cin >> c;            if(!trie[u][c])            {                while(++i<=l) cin >> c;                return res;            };            res+=ed[u=trie[u][c]];        }        return res-ed[u]+sum[u]; // ed[u]是sum[u]的子集    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,x; i<=n; i++)    {        cin >> x;        tr.insert(x);    }    for(int i=1,x; i<=m; i++)    {        cin >> x;        cout << tr.query(x) << '\n';    }    return 0;}
      ]]> + 洛谷P2922[USACO08DEC]Secret Message G 题解

      题目链接:P2922[USACO08DEC]Secret Message G

      题意

      一句话题意:\(m\) 次询问,查询已有的\(n\)\(\tt{01}\) 串中,有多少串 \(t_i\) 能使询问串 \(s\) 成为其前缀。

      贝茜正在领导奶牛们逃跑.为了联络,奶牛们互相发送秘密信息.

      信息是二进制的,共有 \(M\)\(1 \le M \le50000\))条,反间谍能力很强的约翰已经部分拦截了这些信息,知道了第\(i\) 条二进制信息的前 \(b_i\)(\(l \leb_i \le 10000\))位,他同时知道,奶牛使用 \(N\)(\(1 \le N\le 50000\))条暗号.但是,他仅仅知道第 \(j\) 条暗号的前 \(c_j\)(\(1 \lec_j \le 10000\))位。

      对于每条暗号 \(j\),他想知道有多少截得的信息能够和它匹配。也就是说,有多少信息和这条暗号有着相同的前缀。当然,这个前缀长度必须等于暗号和那条信息长度的较小者。

      在输入文件中,位的总数(即 \(\sum b_i +\sum c_i\))不会超过 \(500000\)。

      考虑建 \(\tt{trie}\) 树处理

      没了,就这么简单。

      具体见代码,有一些小的细节要注意

      时间复杂度 \(O(\sum b_i + M \sumc_i)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)string str;int n,m,k,tot;struct Trie{    int trie[N][2],ed[N],sum[N];    void insert(int l)    {        int u=0;        for(int i=1,c; i<=l; i++)        {            cin >> c;            if(!trie[u][c])trie[u][c]=++tot;            ++sum[u=trie[u][c]];        }        ++ed[u];    }    int query(int l)    {        int u=0,ok=1,res=0;        for(int i=1,c; i<=l; i++)        {            cin >> c;            if(!trie[u][c])            {                while(++i<=l) cin >> c;                return res;            };            res+=ed[u=trie[u][c]];        }        return res-ed[u]+sum[u]; // ed[u]是sum[u]的子集    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,x; i<=n; i++)    {        cin >> x;        tr.insert(x);    }    for(int i=1,x; i<=m; i++)    {        cin >> x;        cout << tr.query(x) << '\n';    }    return 0;}
      ]]> @@ -3154,7 +3154,7 @@ /2022/07/12/luo-gu-p3586-poi2015-log-ti-jie/ - 洛谷P3586 [POI2015] LOG 题解

      题目链接:P3586 [POI2015] LOG

      题意

      维护一个长度为 $n$ 的序列,一开始都是 $0$,支持以下两种操作:

      1. U k a 将序列中第 $k$ 个数修改为 $a$。
      2. Z c s 在这个序列上,每次选出 $c$ 个正数,并将它们都减去 $1$,询问能否进行 $s$ 次操作。

      每次询问独立,即每次询问不会对序列进行修改。

      【数据范围】

      对于 $100\%$ 的数据,$1\leq n,m\leq 10^6$,$1\leq k,c\leq n$,$0\leq a\leq 10^9$,$1\leq s\leq 10^9$。

      不是区间询问。注意了嗷。

      先考虑2操作

      对于大于 $s$ 的数,显然最多只能减 $s$ 次

      对于小于等于 $s$ 的数,肯定要把它全部减成 $0$

      设 $S=\sum x_i [x_i\le s]$ ,则剩下的 $c\times s - S$ 由大于 $s$ 的数贡献

      设 $\text{cnt} = \sum [x_i \le s]$ ,则大于 $s$ 的数共有 $(n-\text{cnt})$ 个

      于是我们只要判断下面的柿子是否成立

      问题就转化为了如何求解 $S$ 和 $\text{cnt}$

      • 「比一个数小的数的数量」,显然树状数组

      • 「比一个数小的数的和」,我们把这个和看成这些数的权值

        那么这个就是上个问题的 add(x,1) 改成 add(x,x),还是树状数组

      当然,我们直接用树状数组肯定是不行的,因为 $a\le 10^9$

      考虑把原数组(本题中只有 $0$ )和所有询问中的 $a,s$ 离散化

      相信学过主席树的都知道这个操作吧(其实没学过也没关系)

      时间复杂度 $O(m \log m)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n,m,o,b[N],t[N];struct Query{int op,c,s;}q[N];struct BIT{    int tree[N];    int lowbit(int x){return x&(-x);}    void add(int x,int v)    {        for(; x&&x<=o; x+=lowbit(x))            tree[x]+=v;    }    int qSum(int x)    {        int res=0;        for(; x>=1; x-=lowbit(x))            res+=tree[x];        return res;    }}t1,t2;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    char ch; b[++o]=0;    cin >> n >> m;    for(int i=1; i<=m; i++)    {        cin >> ch >> q[i].c >> q[i].s;        q[i].op=(ch=='U')?1:2; b[++o]=q[i].s;    }    sort(b+1,b+1+o);    o=unique(b+1,b+1+o)-b-1;    for(int i=1,x; i<=m; i++)    {        q[i].s=lower_bound(b+1,b+1+o,q[i].s)-b;        if(q[i].op==1)        {            if(t[q[i].c]) // 删除这个数之前去掉它的贡献                x=t[q[i].c],t1.add(x,-1),t2.add(x,-b[x]);            x=t[q[i].c]=q[i].s; // 这里的x是我从其他题解学的,只是减少代码用的            t1.add(x,1);t2.add(x,b[x]);        }        else        {            x=t1.qSum(o)-t1.qSum(q[i].s-1);            int sum=q[i].s?t2.qSum(q[i].s-1):0;            cout << ((sum>=(q[i].c-x)*(b[q[i].s]))?"TAK\n":"NIE\n");        }    }    return 0;}
      ]]> + 洛谷P3586 [POI2015] LOG 题解

      题目链接:P3586[POI2015] LOG

      题意

      维护一个长度为 \(n\)的序列,一开始都是 \(0\),支持以下两种操作:

      1. U k a 将序列中第 \(k\)个数修改为 \(a\)
      2. Z c s 在这个序列上,每次选出 \(c\) 个正数,并将它们都减去 \(1\),询问能否进行 \(s\) 次操作。

      每次询问独立,即每次询问不会对序列进行修改。

      【数据范围】

      对于 \(100\%\) 的数据,\(1\leq n,m\leq 10^6\),\(1\leq k,c\leq n\),\(0\leq a\leq 10^9\),\(1\leq s\leq 10^9\)。

      不是区间询问。注意了嗷。

      先考虑2操作

      对于大于 \(s\) 的数,显然最多只能减\(s\)

      对于小于等于 \(s\)的数,肯定要把它全部减成 \(0\)

      \(S=\sum x_i [x_i\le s]\),则剩下的 \(c\times s - S\) 由大于\(s\) 的数贡献

      \(\text{cnt} = \sum [x_i \le s]\),则大于 \(s\) 的数共有 \((n-\text{cnt})\) 个

      于是我们只要判断下面的柿子是否成立 \[(n-\text{cnt}) \times s \ge c \times s -S\]\[S \ge s \times (c-n+\text{cnt})\] 问题就转化为了如何求解 \(S\)\(\text{cnt}\)

      • 「比一个数小的数的数量」,显然树状数组

      • 「比一个数小的数的和」,我们把这个和看成这些数的权值

        那么这个就是上个问题的 add(x,1) 改成add(x,x),还是树状数组

      当然,我们直接用树状数组肯定是不行的,因为 \(a\le 10^9\)

      考虑把原数组(本题中只有 \(0\))和所有询问中的 \(a,s\) 离散化

      相信学过主席树的都知道这个操作吧(其实没学过也没关系)

      时间复杂度 \(O(m \log m)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n,m,o,b[N],t[N];struct Query{int op,c,s;}q[N];struct BIT{    int tree[N];    int lowbit(int x){return x&(-x);}    void add(int x,int v)    {        for(; x&&x<=o; x+=lowbit(x))            tree[x]+=v;    }    int qSum(int x)    {        int res=0;        for(; x>=1; x-=lowbit(x))            res+=tree[x];        return res;    }}t1,t2;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    char ch; b[++o]=0;    cin >> n >> m;    for(int i=1; i<=m; i++)    {        cin >> ch >> q[i].c >> q[i].s;        q[i].op=(ch=='U')?1:2; b[++o]=q[i].s;    }    sort(b+1,b+1+o);    o=unique(b+1,b+1+o)-b-1;    for(int i=1,x; i<=m; i++)    {        q[i].s=lower_bound(b+1,b+1+o,q[i].s)-b;        if(q[i].op==1)        {            if(t[q[i].c]) // 删除这个数之前去掉它的贡献                x=t[q[i].c],t1.add(x,-1),t2.add(x,-b[x]);            x=t[q[i].c]=q[i].s; // 这里的x是我从其他题解学的,只是减少代码用的            t1.add(x,1);t2.add(x,b[x]);        }        else        {            x=t1.qSum(o)-t1.qSum(q[i].s-1);            int sum=q[i].s?t2.qSum(q[i].s-1):0;            cout << ((sum>=(q[i].c-x)*(b[q[i].s]))?"TAK\n":"NIE\n");        }    }    return 0;}
      ]]> @@ -3181,7 +3181,7 @@ /2022/07/12/luo-gu-p3879-tjoi2010-yue-du-li-jie-ti-jie/ - 洛谷P3879 [TJOI2010] 阅读理解 题解

      题目链接:P3879 [TJOI2010] 阅读理解

      题意

      英语老师留了 $N$ 篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些生词都在哪几篇短文中出现过。

      第一行为整数 $N$ ,表示短文篇数,其中每篇短文只含空格和小写字母。

      按下来的 $N$ 行,每行描述一篇短文。每行的开头是一个整数 $L$ ,表示这篇短文由 $L$ 个单词组成。接下来是 $L$ 个单词,单词之间用一个空格分隔。

      然后为一个整数 $M$ ,表示要做几次询问。后面有 $M$ 行,每行表示一个要统计的生词。

      对于 $100\%$ 的数据,$1\le M\le 10^4$,$1\le N\le 10^3$ 。

      每篇短文长度(含相邻单词之间的空格)$\le 5\times 10^3$ 字符,每个单词长度 $\le 20$ 字符。

      要写 $\tt{trie}$ 也不是不行。但是懒。

      直接用 $\tt{unordered_map+vector}$ 水过

      跑的还蛮快。注意要去重(不想用set,常数太大)

      时间复杂度 $O(\sum |s_i|\times m)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <unordered_map>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e3+15)int n,Q;string s;int vis[N];unordered_map<string,vector<int> >mp;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,x; i<=n; i++)    {        cin >> x;        for(int j=1; j<=x; j++)            cin >> s,mp[s].push_back(i);    }    cin >> Q;    while(Q--)    {        cin >> s;        for(int i=1; i<=n; i++) vis[i]=0;        for(int i=0; i<mp[s].size(); i++)            if(!vis[mp[s][i]])            {                cout << mp[s][i] << ' ';                vis[mp[s][i]]=1;            }        cout << '\n';    }    return 0;}
      ]]> + 洛谷P3879 [TJOI2010] 阅读理解题解

      题目链接:P3879[TJOI2010] 阅读理解

      题意

      英语老师留了 \(N\)篇阅读理解作业,但是每篇英文短文都有很多生词需要查字典,为了节约时间,现在要做个统计,算一算某些生词都在哪几篇短文中出现过。

      第一行为整数 \(N\),表示短文篇数,其中每篇短文只含空格和小写字母。

      按下来的 \(N\)行,每行描述一篇短文。每行的开头是一个整数 \(L\) ,表示这篇短文由 \(L\) 个单词组成。接下来是 \(L\) 个单词,单词之间用一个空格分隔。

      然后为一个整数 \(M\),表示要做几次询问。后面有 \(M\)行,每行表示一个要统计的生词。

      对于 \(100\%\) 的数据,\(1\le M\le 10^4\),\(1\le N\le 10^3\) 。

      每篇短文长度(含相邻单词之间的空格)\(\le5\times 10^3\) 字符,每个单词长度 \(\le20\) 字符。

      要写 \(\tt{trie}\)也不是不行。但是懒。

      直接用 \(\tt{unordered\_map+vector}\) 水过

      跑的还蛮快。注意要去重(不想用set,常数太大)

      时间复杂度 \(O(\sum |s_i|\timesm)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <unordered_map>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e3+15)int n,Q;string s;int vis[N];unordered_map<string,vector<int> >mp;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,x; i<=n; i++)    {        cin >> x;        for(int j=1; j<=x; j++)            cin >> s,mp[s].push_back(i);    }    cin >> Q;    while(Q--)    {        cin >> s;        for(int i=1; i<=n; i++) vis[i]=0;        for(int i=0; i<mp[s].size(); i++)            if(!vis[mp[s][i]])            {                cout << mp[s][i] << ' ';                vis[mp[s][i]]=1;            }        cout << '\n';    }    return 0;}
      ]]> @@ -3208,7 +3208,7 @@ /2022/07/12/luo-gu-p1168-zhong-wei-shu-ti-jie/ - 洛谷P1168 中位数 题解

      题目链接:P1168 中位数

      题意

      给出一个长度为$N$的非负整数序列$A_i$,对于所有$1 ≤ k ≤ (N + 1) / 2$,输出$A_1, A_1 \sim A_3, …,A_1 \sim A_{2k - 1}$的中位数。即前$1,3,5,…$个数的中位数。

      对于$100\%$的数据,$N ≤ 100000$。

      当然可以用vector来搞啦,但是这样就常数大而且没劲了

      考虑对顶堆(那篇讲对顶堆的是在绕口令吧。明明很好懂的)

      维护两个堆,分别是大根堆和小根堆

      有个性质:大根堆q1里的所有值小于小根堆q2里的所有值

      然后每次把比q1堆首元素大的元素扔到q2里去,反之塞进q1里

      这样还是满足性质的

      但是我们要中位数,这就需要q1和q2的size只相差一,才好处理

      怎么搞呢,比如q1的size太大了,那我们就把q1的堆首一个个扔到q2里去

      这样仍然是满足性质的

      注意到每个元素最多入堆2次,因此单次插入操作是 $O(\log n)$ 的

      时间复杂度 $O(n\log n)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n;priority_queue<int> q1;priority_queue<int,vector<int>,greater<int>>q2;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    int x; cin >> x;    q1.push(x); cout << x << '\n';    for(int i=2; i<=n; i++)    {        cin >> x;        if(x>q1.top())q2.push(x);        else q1.push(x);        while(abs((int)(q1.size()-q2.size()))>1)        {            if(q1.size()>q2.size())                q2.push(q1.top()),q1.pop();            else q1.push(q2.top()),q2.pop();        }        if(i&1) cout << (q1.size()>q2.size()?q1.top():q2.top()) << '\n';    }    return 0;}
      ]]> + 洛谷P1168 中位数 题解

      题目链接:P1168中位数

      题意

      给出一个长度为\(N\)的非负整数序列\(A_i\),对于所有\(1 ≤ k ≤ (N + 1) / 2\),输出\(A_1, A_1 \sim A_3, …,A_1 \sim A_{2k -1}\)的中位数。即前\(1,3,5,…\)个数的中位数。

      对于\(100\%\)的数据,\(N ≤ 100000\)。

      当然可以用vector来搞啦,但是这样就常数大而且没劲了

      考虑对顶堆(那篇讲对顶堆的是在绕口令吧。明明很好懂的)

      维护两个堆,分别是大根堆和小根堆

      有个性质:大根堆q1里的所有值小于小根堆q2里的所有值

      然后每次把比q1堆首元素大的元素扔到q2里去,反之塞进q1里

      这样还是满足性质的

      但是我们要中位数,这就需要q1和q2的size只相差一,才好处理

      怎么搞呢,比如q1的size太大了,那我们就把q1的堆首一个个扔到q2里去

      这样仍然是满足性质的

      注意到每个元素最多入堆2次,因此单次插入操作是 \(O(\log n)\) 的

      时间复杂度 \(O(n\log n)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n;priority_queue<int> q1;priority_queue<int,vector<int>,greater<int>>q2;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    int x; cin >> x;    q1.push(x); cout << x << '\n';    for(int i=2; i<=n; i++)    {        cin >> x;        if(x>q1.top())q2.push(x);        else q1.push(x);        while(abs((int)(q1.size()-q2.size()))>1)        {            if(q1.size()>q2.size())                q2.push(q1.top()),q1.pop();            else q1.push(q2.top()),q2.pop();        }        if(i&1) cout << (q1.size()>q2.size()?q1.top():q2.top()) << '\n';    }    return 0;}
      ]]> @@ -3235,7 +3235,7 @@ /2022/07/12/luo-gu-p3958-noip2017-ti-gao-zu-nai-luo-ti-jie/ - 洛谷P3958 [NOIP2017 提高组] 奶酪 题解

      题目链接:P3958 [NOIP2017 提高组] 奶酪

      题意

      现有一块大奶酪,它的高度为 $h$,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中,奶酪的下表面为 $z = 0$,奶酪的上表面为 $z = h$。

      现在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交,Jerry 则可以从奶酪下表面跑进空洞;如果一个空洞与上表面相切或是相交,Jerry 则可以从空洞跑到奶酪上表面。

      位于奶酪下表面的 Jerry 想知道,在不破坏奶酪的情况下,能否利用已有的空洞跑 到奶酪的上表面去?

      空间内两点 $P_1(x_1,y_1,z_1)$、$P_2(x_2,y_2,z_2)$ 的距离公式如下:

      对于 $100\%$ 的数据,$1 \le n \le 1\times 10^3$,$1 \le h , r \le 10^9$,$T \le 20$,坐标的绝对值不超过 $10^9$。

      基本思想使用并查集维护连通性

      单独处理与边界相交的空洞,分别用数组 $c_1,c_2$ 记录

      然后对于每个空洞两两判断是否相交,如果相交则并查集合并

      时间复杂度 $O(Qn^2)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)#define pf(x) ((x)*(x))int Q,n,h,r,cnt1,cnt2,x[N],y[N],z[N],c1[N],c2[N],f[N];void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}int _dis(int x1,int y1,int z1,int x2,int y2,int z2){return pf(x1-x2)+pf(y1-y2)+pf(z1-z2);}int dis(int a,int b){return _dis(x[a],y[a],z[a],x[b],y[b],z[b]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> Q;    while(Q--)    {        cin >> n >> h >> r;        init(n); cnt1=cnt2=0;        for(int i=1; i<=n; i++)        {            cin >> x[i] >> y[i] >> z[i];            if(z[i]+r>=h) c1[++cnt1]=i;            if(z[i]-r<=0) c2[++cnt2]=i;            for(int j=1; j<i; j++)                if(dis(i,j)<=4*r*r) merge(i,j);        }        int ok=0;        for(int i=1; i<=cnt1&&!ok; i++)            for(int j=1; j<=cnt2&&!ok; j++)                if(find(c1[i])==find(c2[j]))ok=1;        cout << (ok?"Yes":"No") << '\n';    }    return 0;}
      ]]> + 洛谷P3958 [NOIP2017 提高组]奶酪 题解

      题目链接:P3958[NOIP2017 提高组] 奶酪

      题意

      现有一块大奶酪,它的高度为 \(h\),它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中,奶酪的下表面为\(z = 0\),奶酪的上表面为 \(z = h\)。

      现在,奶酪的下表面有一只小老鼠Jerry,它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则Jerry可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交,Jerry则可以从奶酪下表面跑进空洞;如果一个空洞与上表面相切或是相交,Jerry则可以从空洞跑到奶酪上表面。

      位于奶酪下表面的 Jerry想知道,在不破坏奶酪的情况下,能否利用已有的空洞跑 到奶酪的上表面去?

      空间内两点 \(P_1(x_1,y_1,z_1)\)、\(P_2(x_2,y_2,z_2)\) 的距离公式如下:

      \[\mathrm{dist}(P_1,P_2)=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2}\]

      对于 \(100\%\) 的数据,\(1 \le n \le 1\times 10^3\),\(1 \le h , r \le 10^9\),\(T \le 20\),坐标的绝对值不超过 \(10^9\)。

      基本思想使用并查集维护连通性

      单独处理与边界相交的空洞,分别用数组 \(c_1,c_2\) 记录

      然后对于每个空洞两两判断是否相交,如果相交则并查集合并

      时间复杂度 \(O(Qn^2)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)#define pf(x) ((x)*(x))int Q,n,h,r,cnt1,cnt2,x[N],y[N],z[N],c1[N],c2[N],f[N];void init(int n){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}int _dis(int x1,int y1,int z1,int x2,int y2,int z2){return pf(x1-x2)+pf(y1-y2)+pf(z1-z2);}int dis(int a,int b){return _dis(x[a],y[a],z[a],x[b],y[b],z[b]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> Q;    while(Q--)    {        cin >> n >> h >> r;        init(n); cnt1=cnt2=0;        for(int i=1; i<=n; i++)        {            cin >> x[i] >> y[i] >> z[i];            if(z[i]+r>=h) c1[++cnt1]=i;            if(z[i]-r<=0) c2[++cnt2]=i;            for(int j=1; j<i; j++)                if(dis(i,j)<=4*r*r) merge(i,j);        }        int ok=0;        for(int i=1; i<=cnt1&&!ok; i++)            for(int j=1; j<=cnt2&&!ok; j++)                if(find(c1[i])==find(c2[j]))ok=1;        cout << (ok?"Yes":"No") << '\n';    }    return 0;}
      ]]> @@ -3262,7 +3262,7 @@ /2022/07/12/luo-gu-p1816-zhong-cheng-ti-jie/ - 洛谷P1816 忠诚 题解

      题目链接:P1816 忠诚

      题意:区间min。

      BIT写法也就图一乐(其实不建议)

      主要在询问上有些区别,

      例如如果 x-lowbit(x) 小于 $l$ ,则此时不能直接 x-=lowbit(x)

      因为这样会把 $[l,r]$ 之外的东西给弄进来,所以这部分直接暴力处理

      复杂度不清楚,应该还是 $O(\log n)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n,m,a[N],tree[N];int lowbit(int x){return x&(-x);}void update(int x,int v){    for(; x<=n; x+=lowbit(x))        tree[x]=min(tree[x],v);}int qMin(int l,int r){    int x=r,mn=INF;    for(; x>=l;)    {        if(x-lowbit(x)>l)        {            mn=min(mn,tree[x]);            x-=lowbit(x);        }        else mn=min(mn,a[x]),--x;    }    return mn;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(tree,0x3f,sizeof(tree));    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> a[i];        update(i,a[i]);    }    for(int i=1,l,r; i<=m; i++)    {        cin >> l >> r;        cout << qMin(l,r) << ' ';    }    return 0;}
      ]]> + 洛谷P1816 忠诚 题解

      题目链接:P1816忠诚

      题意:区间min。

      BIT写法也就图一乐(其实不建议)

      主要在询问上有些区别,

      例如如果 x-lowbit(x) 小于 \(l\) ,则此时不能直接x-=lowbit(x)

      因为这样会把 \([l,r]\)之外的东西给弄进来,所以这部分直接暴力处理

      复杂度不清楚,应该还是 \(O(\logn)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n,m,a[N],tree[N];int lowbit(int x){return x&(-x);}void update(int x,int v){    for(; x<=n; x+=lowbit(x))        tree[x]=min(tree[x],v);}int qMin(int l,int r){    int x=r,mn=INF;    for(; x>=l;)    {        if(x-lowbit(x)>l)        {            mn=min(mn,tree[x]);            x-=lowbit(x);        }        else mn=min(mn,a[x]),--x;    }    return mn;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(tree,0x3f,sizeof(tree));    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> a[i];        update(i,a[i]);    }    for(int i=1,l,r; i<=m; i++)    {        cin >> l >> r;        cout << qMin(l,r) << ' ';    }    return 0;}
      ]]> @@ -3287,7 +3287,7 @@ /2022/07/12/luo-gu-p4185-usaco18jan-mootube-g-ti-jie/ - 洛谷P4185 [USACO18JAN]MooTube G 题解

      题目链接:P4185 [USACO18JAN]MooTube G

      题意

      给出一棵无根树, $m \le 10^5$ 次询问,给出 $k,v$ ,询问共有多少结点 $u$ 满足 $u$ 到 $v$ 的路径上最小边权不小于 $k$ 。

      考虑把所有相互满足条件的结点用并查集合并

      注意到当 $k$ 减小时,合并后的连通块数量单调递减

      考虑将询问按 $k$ 降序排序,离线处理

      每次去找没合并过的边显然太麻烦

      继续利用单调性,将所有边按边权降序排序

      这样我们扫一遍就能处理所有询问了

      复杂度瓶颈在于排序。

      时间复杂度 $O(m \log m)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)struct Edge {int u,v,w;}e[N];struct Query{int k,v,id;}q[N];int n,m,f[N],sz[N],ans[N];void init(int n){for(int i=1; i<=n; i++) f[i]=i,sz[i]=1;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){    u=find(u);v=find(v);    f[u]=v;    sz[v]+=sz[u];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    init(n);    for(int i=1; i<n; i++)        cin >> e[i].u >> e[i].v >> e[i].w;    for(int i=1; i<=m; i++)        cin >> q[i].k >> q[i].v,q[i].id=i;    sort(e+1,e+1+n,[](Edge a,Edge b){return a.w>b.w;});    sort(q+1,q+1+m,[](Query a,Query b){return a.k>b.k;});    for(int i=1,j=1; i<=m; i++)    {        for(; j<=n&&q[i].k<=e[j].w; j++)            merge(e[j].u,e[j].v);        ans[q[i].id]=sz[find(q[i].v)]-1;    }    for(int i=1; i<=m; i++)        cout << ans[i] << '\n';    return 0;}
      ]]> + 洛谷P4185[USACO18JAN]MooTube G 题解

      题目链接:P4185[USACO18JAN]MooTube G

      题意

      给出一棵无根树, \(m \le 10^5\)次询问,给出 \(k,v\) ,询问共有多少结点\(u\) 满足 \(u\) 到 \(v\) 的路径上最小边权不小于 \(k\) 。

      考虑把所有相互满足条件的结点用并查集合并

      注意到当 \(k\)减小时,合并后的连通块数量单调递减

      考虑将询问按 \(k\)降序排序,离线处理

      每次去找没合并过的边显然太麻烦

      继续利用单调性,将所有边按边权降序排序

      这样我们扫一遍就能处理所有询问了

      复杂度瓶颈在于排序。

      时间复杂度 \(O(m \log m)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)struct Edge {int u,v,w;}e[N];struct Query{int k,v,id;}q[N];int n,m,f[N],sz[N],ans[N];void init(int n){for(int i=1; i<=n; i++) f[i]=i,sz[i]=1;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){    u=find(u);v=find(v);    f[u]=v;    sz[v]+=sz[u];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    init(n);    for(int i=1; i<n; i++)        cin >> e[i].u >> e[i].v >> e[i].w;    for(int i=1; i<=m; i++)        cin >> q[i].k >> q[i].v,q[i].id=i;    sort(e+1,e+1+n,[](Edge a,Edge b){return a.w>b.w;});    sort(q+1,q+1+m,[](Query a,Query b){return a.k>b.k;});    for(int i=1,j=1; i<=m; i++)    {        for(; j<=n&&q[i].k<=e[j].w; j++)            merge(e[j].u,e[j].v);        ans[q[i].id]=sz[find(q[i].v)]-1;    }    for(int i=1; i<=m; i++)        cout << ans[i] << '\n';    return 0;}
      ]]> @@ -3316,7 +3316,7 @@ /2022/07/12/luo-gu-p2085-zui-xiao-han-shu-zhi-ti-jie/ - 洛谷P2085 最小函数值 题解

      题目链接:P2085 最小函数值

      题意

      有 $n$ 个函数,分别为 $F_1,F_2,\dots,F_n$。定义 $F_i(x)=A_ix^2+B_ix+C_i(x\in\mathbb N^*)$。给定这些 $A_i$、$B_i$ 和 $C_i$,请求出所有函数的所有函数值中最小的 $m$ 个(如有重复的要输出多个)。

      $1 \leq A_i\le10,~10 \le B_i\le100,~100 \le C_i\le10^4$。

      这是一篇随机化优化的奇怪题解

      题面十分不清楚。所以稍微修改了一下下。

      注意这里的数据范围

      根据二次函数对称轴 $x=-\dfrac{b}{2a}$

      不难发现这些函数均在 $[0,+\infty)$ 单调递增

      那就很简单了,直接用个大根堆维护 $k$ 大值的方法搞一搞就好了

      然后发现时间复杂度最坏似乎 $O(nm\log m)$

      因此我们可以用随机化乱搞一下,防止特意构造的数据 $😎$

      时间复杂度上界其实还是 $O(nm\log m)$

      但是实际的复杂度比这个要低一些(然而因为常数又慢了一些

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)mt19937 rd(time(0));priority_queue<int> q;int n,m,a[N],b[N],c[N],tmp[N],ans[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=m; i++)         tmp[i]=i, q.push(INF);    shuffle(tmp+1,tmp+1+n,rd);    for(int i=1; i<=n; i++)        cin >> a[tmp[i]] >> b[tmp[i]] >> c[tmp[i]];    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)        {            int k=a[i]*j*j+b[i]*j+c[i];            if(k<q.top()){q.pop();q.push(k);}            else break;        }        for(int i=m; i>=1; i--)        ans[i]=q.top(),q.pop();    for(int i=1; i<=m; i++)        cout << ans[i] << " \n"[i==m];    return 0;}
      ]]> + 洛谷P2085 最小函数值 题解

      题目链接:P2085最小函数值

      题意

      \(n\) 个函数,分别为 \(F_1,F_2,\dots,F_n\)。定义 \(F_i(x)=A_ix^2+B_ix+C_i(x\in\mathbbN^*)\)。给定这些 \(A_i\)\(B_i\) 和 \(C_i\),请求出所有函数的所有函数值中最小的\(m\) 个(如有重复的要输出多个)。

      \(1 \leq A_i\le10,~10 \le B_i\le100,~100\le C_i\le10^4\)

      这是一篇随机化优化的奇怪题解

      题面十分不清楚。所以稍微修改了一下下。

      注意这里的数据范围

      根据二次函数对称轴 \(x=-\dfrac{b}{2a}\)

      不难发现这些函数均在 \([0,+\infty)\)单调递增

      那就很简单了,直接用个大根堆维护 \(k\) 大值的方法搞一搞就好了

      然后发现时间复杂度最坏似乎 \(O(nm\logm)\)

      因此我们可以用随机化乱搞一下,防止特意构造的数据 \(😎\)

      时间复杂度上界其实还是 \(O(nm\logm)\)

      但是实际的复杂度比这个要低一些(然而因为常数又慢了一些

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+15)mt19937 rd(time(0));priority_queue<int> q;int n,m,a[N],b[N],c[N],tmp[N],ans[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=m; i++)         tmp[i]=i, q.push(INF);    shuffle(tmp+1,tmp+1+n,rd);    for(int i=1; i<=n; i++)        cin >> a[tmp[i]] >> b[tmp[i]] >> c[tmp[i]];    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)        {            int k=a[i]*j*j+b[i]*j+c[i];            if(k<q.top()){q.pop();q.push(k);}            else break;        }        for(int i=m; i>=1; i--)        ans[i]=q.top(),q.pop();    for(int i=1; i<=m; i++)        cout << ans[i] << " \n"[i==m];    return 0;}
      ]]> @@ -3343,7 +3343,7 @@ /2022/07/11/luo-gu-p2679-noip2015-ti-gao-zu-zi-chuan-ti-jie/ - 洛谷P2679 [NOIP2015 提高组] 子串 题解

      题目链接:P2679 [NOIP2015 提高组] 子串

      题意

      有两个仅包含小写英文字母的字符串 $A$ 和 $B$。

      现在要从字符串 $A$ 中取出 $d$ 个互不重叠的非空子串,然后把这 $d$ 个子串按照其在字符串 $A$ 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 $B$ 相等?

      注意:子串取出的位置不同也认为是不同的方案。

      输出答案对 $10^9+7$ 取模的结果。

      对于所有 $10$ 组数据: $1\le n \le 1000,~1 \le m \le 200,~1 \le d \le m$。

      显然的字符串类型dp

      一个原始的思路是:

      设 $f_{i,j,k}$ 表示仅考虑 $A$ 的前 $i$ 个字符,$B$ 匹配到第 $j$ 个字符,用了 $k$ 个子串的方案数。

      然后发现我们并不知道 $A_i$ 是否与 $B_j$ 匹配,无法转移

      于是状态就变成了

      设 $f_{i,j,k}$ 表示仅考虑 $A$ 的前 $i$ 个字符,$B$ 匹配到第 $j$ 个字符,用了 $k$ 个子串,且 $A_i$ 为最后一个匹配字符的方案数。

      这样就可以转移了

      显然 $i$ 这一维可以用滚动数组滚掉

      那么 $\sum_{t=1}^{p+1} f_{i-t,j-t,k-1}$ 怎么处理呢

      其实我们已经在之前的计算中把 $f_{i-t,j-t,k-1}$ 计算出来了

      考虑动态维护一个前缀和,在遇到 $A_i\ne B_j$ 的时候中断,具体见代码

      时间复杂度 $O(nmk)$

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(205)const int p=1e9+7;char a[1005],b[N];int n,m,d,f[N][N],sum[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    f[0][0]=1;    cin >> n >> m >> d >> a >> b;    for(int i=1; i<=n; i++)        for(int j=m; j>=1; j--)            for(int k=d; k>=1; k--)            {                if(a[i-1]==b[j-1])                    sum[j][k]=sum[j-1][k]+f[j-1][k-1];                else sum[j][k]=0;                f[j][k]=(f[j][k]+sum[j][k]%p)%p;            }    cout << f[m][d] << '\n';    return 0;}
      ]]> + 洛谷P2679 [NOIP2015 提高组]子串 题解

      题目链接:P2679[NOIP2015 提高组] 子串

      题意

      有两个仅包含小写英文字母的字符串 \(A\) 和 \(B\)。

      现在要从字符串 \(A\) 中取出 \(d\) 个互不重叠的非空子串,然后把这 \(d\) 个子串按照其在字符串 \(A\)中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串\(B\) 相等?

      注意:子串取出的位置不同也认为是不同的方案。

      输出答案对 \(10^9+7\)取模的结果。

      对于所有 \(10\) 组数据: \(1\le n \le 1000,~1 \le m \le 200,~1 \le d \lem\)。

      显然的字符串类型dp

      一个原始的思路是:

      \(f_{i,j,k}\) 表示仅考虑 \(A\) 的前 \(i\) 个字符,\(B\) 匹配到第 \(j\) 个字符,用了 \(k\) 个子串的方案数。

      然后发现我们并不知道 \(A_i\) 是否与\(B_j\) 匹配,无法转移

      于是状态就变成了

      \(f_{i,j,k}\) 表示仅考虑 \(A\) 的前 \(i\) 个字符,\(B\) 匹配到第 \(j\) 个字符,用了 \(k\) 个子串,且 \(A_i\) 为最后一个匹配字符的方案数。

      这样就可以转移了 \[\\f_{i,j,k} = \begin{cases}1,&,i=j=k=0\\\\f_{i-1,j,k}&,A_i \ne B_j\\\\f_{i-1,j,k}+\sum_{t=1}^{p+1} f_{i-t,j-t,k-1}&,\forall d \in[0,p], A_{i-d} = B_{j-d} \, \land \, A_{i-p-1} \ne B_{j-p-1}\end{cases}\] 显然 \(i\)这一维可以用滚动数组滚掉

      那么 \(\sum_{t=1}^{p+1}f_{i-t,j-t,k-1}\) 怎么处理呢

      其实我们已经在之前的计算中把 \(f_{i-t,j-t,k-1}\) 计算出来了

      考虑动态维护一个前缀和,在遇到 \(A_i\neB_j\) 的时候中断,具体见代码

      时间复杂度 \(O(nmk)\)

      代码:

      #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(205)const int p=1e9+7;char a[1005],b[N];int n,m,d,f[N][N],sum[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    f[0][0]=1;    cin >> n >> m >> d >> a >> b;    for(int i=1; i<=n; i++)        for(int j=m; j>=1; j--)            for(int k=d; k>=1; k--)            {                if(a[i-1]==b[j-1])                    sum[j][k]=sum[j-1][k]+f[j-1][k-1];                else sum[j][k]=0;                f[j][k]=(f[j][k]+sum[j][k]%p)%p;            }    cout << f[m][d] << '\n';    return 0;}
      ]]> @@ -3372,7 +3372,7 @@ /2022/07/11/luo-gu-p1868-ji-e-de-nai-niu-ti-jie/ - 洛谷P1868 饥饿的奶牛 题解

    题目链接:P1868 饥饿的奶牛

    题意

    有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字。

    现用汉语翻译为:

    有 $N$ 个区间,每个区间 $x,y$ 表示提供的 $x\sim y$ 共 $y-x+1$ 堆优质牧草。你可以选择任意区间但不能有重复的部分。

    对于奶牛来说,自然是吃的越多越好,然而奶牛智商有限,现在请你帮助他。

    $1 \leq n \leq 1.5 \times 10^5$,$0 \leq x \leq y \leq 3 \times 10^6$。

    设 $f_i$ 表示只考虑前 $i$ 个位置能吃到的最多牧草

    这里的 $\text{Edge}(i)$ 表示以 $i$ 结尾的所有区间组成的集合

    对于每个区间 $[x,y]$ ,我们建一条 $y \to x$ 的边

    这样就可以枚举 $j$ 了

    时间复杂度 $O(m)$

    代码:

    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1.5e5+15)#define M (int)(3e6+15)struct Edge{int u,v,next;}e[N];int n,l,pos=1,f[M],head[M];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v; i<=n; i++)    {        cin >> u >> v;        addEdge(v,u);        l=max(l,v);    }    for(int u=1; u<=l; u++)    {        f[u]=f[u-1];        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            f[u]=max(f[u],f[(!v)?0:v-1]+(u-v+1));        }    }    cout << f[l] << '\n';    return 0;}
    ]]> + 洛谷P1868 饥饿的奶牛 题解

    题目链接:P1868饥饿的奶牛

    题意

    有一条奶牛冲出了围栏,来到了一处圣地(对于奶牛来说),上面用牛语写着一段文字。

    现用汉语翻译为:

    \(N\) 个区间,每个区间 \(x,y\) 表示提供的 \(x\sim y\) 共 \(y-x+1\)堆优质牧草。你可以选择任意区间但不能有重复的部分。

    对于奶牛来说,自然是吃的越多越好,然而奶牛智商有限,现在请你帮助他。

    \(1 \leq n \leq 1.5 \times10^5\)\(0 \leq x \leq y \leq 3 \times10^6\)

    \(f_i\) 表示只考虑前 \(i\) 个位置能吃到的最多牧草 \[f_i = \max_{j \in \text{Edge}(i)}\left\{f_{j-1} + i-j+1\right\}\] 这里的 \(\text{Edge}(i)\)表示以 \(i\)结尾的所有区间组成的集合

    对于每个区间 \([x,y]\) ,我们建一条\(y \to x\) 的边

    这样就可以枚举 \(j\)

    时间复杂度 \(O(m)\)

    代码:

    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1.5e5+15)#define M (int)(3e6+15)struct Edge{int u,v,next;}e[N];int n,l,pos=1,f[M],head[M];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v; i<=n; i++)    {        cin >> u >> v;        addEdge(v,u);        l=max(l,v);    }    for(int u=1; u<=l; u++)    {        f[u]=f[u-1];        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            f[u]=max(f[u],f[(!v)?0:v-1]+(u-v+1));        }    }    cout << f[l] << '\n';    return 0;}
    ]]> @@ -3399,7 +3399,7 @@ /2022/07/10/luo-gu-p4799-ceoi2015-day2-shi-jie-bing-qiu-jin-biao-sai-ti-jie/ - 洛谷P4799 [CEOI2015 Day2] 世界冰球锦标赛 题解

    题目链接:P4799 [CEOI2015 Day2] 世界冰球锦标赛

    题意

    译自 CEOI2015 Day2 T1「Ice Hockey World Championship

    今年的世界冰球锦标赛在捷克举行。Bobek 已经抵达布拉格,他不是任何团队的粉丝,也没有时间观念。他只是单纯的想去看几场比赛。如果他有足够的钱,他会去看所有的比赛。不幸的是,他的财产十分有限,他决定把所有财产都用来买门票。

    给出 Bobek 的预算和每场比赛的票价,试求:如果总票价不超过预算,他有多少种观赛方案。如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。

    数据组号$1-2$$3-4$$5-7$$8-10$
    $N \leq$$10$$20$$40$$40$
    $M \leq$$10^6$$10^{18}$$10^6$$10^{18}$

    首先这个 $20$ 就很有趣 显然暴搜是吧

    那么 $40$ 的情况怎么处理呢

    考虑折半搜索。

    折半搜索的思想就是

    把原来的问题拆分成两部分分别暴搜

    然后合并两个部分的答案

    显然折半搜索的优劣取决于合并的复杂度

    在这题里,我们分别搜前半部分和后半部分

    合并的话,直接看代码

    dfs(1,mid,0,sum1,cnt1);dfs(mid+1,n,0,sum2,cnt2);sort(sum2+1,sum2+1+cnt2);for(int i=1; i<=cnt1; i++)    res+=upper_bound(sum2+1,sum2+1+cnt2,m-sum1[i])-sum2-1;cout << res << '\n';

    这里的 $\tt{upper_bound}$ 其实很好理解

    就是严格大于 $m-s_1$ 的那个 $s_2$ 的位置

    显然那个 $s_2$ 之前的都可以取

    时间复杂度 $O(2^{\frac{n}{2}} \log 2^{\frac{n}{2}}) \approx O(n2^{\frac{n}{2}})$

    代码:

    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)((1<<20)+15)int n,m,res,cnt1,cnt2,val[N],sum1[N],sum2[N];void dfs(int l,int r,int sum,int a[],int &cnt){    if(sum>m) return;    if(l>r)    {        a[++cnt]=sum;        return;    }    dfs(l+1,r,sum+val[l],a,cnt);    dfs(l+1,r,sum,a,cnt);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> val[i];    int mid=n/2;    dfs(1,mid,0,sum1,cnt1);    dfs(mid+1,n,0,sum2,cnt2);    sort(sum2+1,sum2+1+cnt2);    for(int i=1; i<=cnt1; i++)        res+=upper_bound(sum2+1,sum2+1+cnt2,m-sum1[i])-sum2-1;    cout << res << '\n';    return 0;}
    ]]> + 洛谷P4799 [CEOI2015Day2] 世界冰球锦标赛 题解

    题目链接:P4799[CEOI2015 Day2] 世界冰球锦标赛

    题意

    译自 CEOI2015 Day2 T1「Ice HockeyWorld Championship」

    今年的世界冰球锦标赛在捷克举行。Bobek已经抵达布拉格,他不是任何团队的粉丝,也没有时间观念。他只是单纯的想去看几场比赛。如果他有足够的钱,他会去看所有的比赛。不幸的是,他的财产十分有限,他决定把所有财产都用来买门票。

    给出 Bobek的预算和每场比赛的票价,试求:如果总票价不超过预算,他有多少种观赛方案。如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。

    数据组号\(1-2\)\(3-4\)\(5-7\)\(8-10\)
    \(N \leq\)\(10\)\(20\)\(40\)\(40\)
    \(M \leq\)\(10^6\)\(10^{18}\)\(10^6\)\(10^{18}\)

    首先这个 \(20\) 就很有趣显然暴搜是吧

    那么 \(40\) 的情况怎么处理呢

    考虑折半搜索。

    折半搜索的思想就是

    把原来的问题拆分成两部分分别暴搜

    然后合并两个部分的答案

    显然折半搜索的优劣取决于合并的复杂度

    在这题里,我们分别搜前半部分和后半部分

    合并的话,直接看代码

    dfs(1,mid,0,sum1,cnt1);dfs(mid+1,n,0,sum2,cnt2);sort(sum2+1,sum2+1+cnt2);for(int i=1; i<=cnt1; i++)    res+=upper_bound(sum2+1,sum2+1+cnt2,m-sum1[i])-sum2-1;cout << res << '\n';

    这里的 \(\tt{upper\_bound}\)其实很好理解

    就是严格大于 \(m-s_1\) 的那个 \(s_2\) 的位置

    显然那个 \(s_2\) 之前的都可以取

    时间复杂度 \(O(2^{\frac{n}{2}} \log2^{\frac{n}{2}}) \approx O(n2^{\frac{n}{2}})\)

    代码:

    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)((1<<20)+15)int n,m,res,cnt1,cnt2,val[N],sum1[N],sum2[N];void dfs(int l,int r,int sum,int a[],int &cnt){    if(sum>m) return;    if(l>r)    {        a[++cnt]=sum;        return;    }    dfs(l+1,r,sum+val[l],a,cnt);    dfs(l+1,r,sum,a,cnt);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> val[i];    int mid=n/2;    dfs(1,mid,0,sum1,cnt1);    dfs(mid+1,n,0,sum2,cnt2);    sort(sum2+1,sum2+1+cnt2);    for(int i=1; i<=cnt1; i++)        res+=upper_bound(sum2+1,sum2+1+cnt2,m-sum1[i])-sum2-1;    cout << res << '\n';    return 0;}
    ]]> @@ -3424,7 +3424,7 @@ /2022/07/10/luo-gu-p3435-poi2006-okr-periods-of-words-ti-jie/ - 洛谷P3435 [POI2006] OKR-Periods of Words 题解

    题目链接:P3435 [POI2006] OKR-Periods of Words

    题意

    对于一个仅含小写字母的字符串 $a$,$p$ 为 $a$ 的前缀且 $p\ne a$,那么我们称 $p$ 为 $a$ 的 proper 前缀。

    规定字符串 $Q$(可以是空串)表示 $a$ 的周期,当且仅当 $Q$ 是 $a$ 的 proper 前缀且 $a$ 是 $Q+Q$ 的前缀。

    例如 ababab 的一个周期,因为 ababab 的 proper 前缀,且 ababab+ab 的前缀。

    求给定字符串所有前缀的最大周期长度之和。

    $1\le |a| \le 10^6$

    考虑如何最大化周期 $Q$

    对于某个前缀,例如 $s = \tt{aabaaaab}$

    不难发现它的最长周期就是 $\tt{aabaa}$

    仔细观察 $\tt{\color{red}{aab}\color{blue}{aa}\color{red}{aab}}$

    其实这个 $Q = \tt{\color{red}{aab}\color{blue}{aa}}$

    就是 $s$ 的最短非空border $\tt{\color{red}{aab}}$ 加上中间那一段东西

    这样对于每个 $i$ ,我们只要找到它的最短border就好了

    这个东西可以通过跳fail数组实现

    但是如果每次都暴力跳,显然会有很多重复步骤

    这个跳的过程,是不是很熟悉?并查集也是这么跳的对吧!

    并查集可以路径压缩,这里fail数组我们也可以路径压缩。

    时间复杂度 $O(n)$

    代码:

    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)char s[N];int n,res,fail[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> (s+1);    for(int i=2,j=0; i<=n; i++)    {        while(j&&s[i]!=s[j+1])j=fail[j];        if(s[i]==s[j+1])++j;        fail[i]=j;    }    for(int i=1,j; i<=n; i++)    {        j=i;        while(fail[j]) j=fail[j];        if(fail[i]!=0) fail[i]=j;        res+=(i-j);    }    cout << res << '\n';    return 0;}
    ]]> + 洛谷P3435 [POI2006]OKR-Periods of Words 题解

    题目链接:P3435[POI2006] OKR-Periods of Words

    题意

    对于一个仅含小写字母的字符串 \(a\),\(p\)\(a\) 的前缀且 \(p\ne a\),那么我们称 \(p\) 为 \(a\) 的 proper 前缀。

    规定字符串 \(Q\)(可以是空串)表示\(a\) 的周期,当且仅当 \(Q\) 是 \(a\) 的 proper 前缀且 \(a\) 是 \(Q+Q\) 的前缀。

    例如 ababab 的一个周期,因为ababab 的 proper 前缀,且ababab+ab 的前缀。

    求给定字符串所有前缀的最大周期长度之和。

    \(1\le |a| \le 10^6\)

    考虑如何最大化周期 \(Q\)

    对于某个前缀,例如 \(s =\tt{aabaaaab}\)

    不难发现它的最长周期就是 \(\tt{aabaa}\)

    仔细观察 \(\tt{\color{red}{aab}\color{blue}{aa}\color{red}{aab}}\)

    其实这个 \(Q =\tt{\color{red}{aab}\color{blue}{aa}}\)

    就是 \(s\)最短非空border \(\tt{\color{red}{aab}}\)加上中间那一段东西

    这样对于每个 \(i\),我们只要找到它的最短border就好了

    这个东西可以通过跳fail数组实现

    但是如果每次都暴力跳,显然会有很多重复步骤

    这个跳的过程,是不是很熟悉?并查集也是这么跳的对吧!

    并查集可以路径压缩,这里fail数组我们也可以路径压缩。

    时间复杂度 \(O(n)\)

    代码:

    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)char s[N];int n,res,fail[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> (s+1);    for(int i=2,j=0; i<=n; i++)    {        while(j&&s[i]!=s[j+1])j=fail[j];        if(s[i]==s[j+1])++j;        fail[i]=j;    }    for(int i=1,j; i<=n; i++)    {        j=i;        while(fail[j]) j=fail[j];        if(fail[i]!=0) fail[i]=j;        res+=(i-j);    }    cout << res << '\n';    return 0;}
    ]]> @@ -3451,7 +3451,7 @@ /2022/07/10/luo-gu-p4287-shoi2011-shuang-bei-hui-wen-ti-jie/ - 洛谷P4287 [SHOI2011]双倍回文 题解

    题目链接:P4287 [SHOI2011]双倍回文

    题意

    记字符串 $w$ 的倒置为 $w^R$ 。例如 $(\tt{abcd})^R=\tt{dcba}$ , $(\tt{abba})^R=\tt{abba}$ 。

    对字符串 $x$ ,如果 $x$ 满足 $x^R=x$ ,则称之为回文;例如 $\tt{abba}$ 是一个回文,而 $\tt{abed}$ 不是。

    如果 $x$ 能够写成的 $ww^Rww^R$ 形式,则称它是一个“双倍回文”。换句话说,若要 $x$ 是双倍回文,它的长度必须是 $4$ 的倍数,而且 $x$ , $x$ 的前半部分, $x$ 的后半部分都要是回文。例如 $\tt{abbaabba}$ 是一个双倍回文,而 $\tt{abaaba}$ 不是,因为它的长度不是 $4$ 的倍数。

    $x$ 的子串是指在$x$中连续的一段字符所组成的字符串。例如 $\tt{be}$ 是 $\tt{abed}$ 的子串,而 $\tt{ac}$ 不是。

    $x$ 的回文子串,就是指满足回文性质的 $x$ 的子串。

    $x$ 的双倍回文子串,就是指满足双倍回文性质的 $x$ 的子串。

    你的任务是,对于给定的字符串,计算它的最长双倍回文子串的长度。

    $n \le 500000$

    考虑Manacher求解

    对于修改后字符串的 $i$ 位置的初始回文半径 $p_i$ (暴力拓展前的半径)

    如果 $i-p_i+1 \le \text{mid}$ ,则存在一个极大双倍回文子串

    这个极大双倍回文子串在修改后的串中,

    以「以 $\text{mid}$ 为中心的回文串」的形式存在

    注意题目要求的是 $4$ 倍数的串,则对于刚才的求解方法,

    只要限制 $i$ 为奇数时更新答案、$\text{mid}$ 和 $r$

    此时 $i$ 的回文半径 $p_i^{\prime}-1$ 一定是偶串的长度

    注意 $r$ 相同时,不要优先更新 $\text{mid}$

    否则根据贪心,不难发现会失去某些极大值,导致答案出错

    这里给个hack(想了我几个小时才想出来

    input:12abcbbbbbbbbaoutput:8

    时间复杂度 $O(n)$

    代码:

    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)char s[N<<1];int n,p[N<<1];void Manacher(int l){    int r=1,mid=1,ans=0;    // 这里的i+=2可以写i++    // 但是要把下面两个if都加上 (i&1) && ...    // 没必要。还慢。    for(int i=1; i<=l; i+=2)    {        p[i]=(i<=r)?min(p[(mid<<1)-i],r-i+1):1;        if(i<=r&&i-p[i]+1<=mid) ans=max(ans,2*(i-mid));        // cout << i << " " << mid << " " << r << '\n';        while(s[i-p[i]]==s[i+p[i]]) ++p[i];        if(i+p[i]-1>r)r=i+p[i]-1,mid=i;        // cout << i << " " << mid << " " << r << '\n';    }    cout << ans << '\n';    // for(int i=1; i<=l; i++)    //     cout << p[i] << " \n"[i==l];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("P4287.out","w",stdout);    cin >> n >> (s+1);    s[0]='$';    for(int i=n; i; i--)        s[i*2+1]='#',s[i*2]=s[i];    s[1]='#';    Manacher(n*2+1);    return 0;}
    ]]> + 洛谷P4287 [SHOI2011]双倍回文题解

    题目链接:P4287[SHOI2011]双倍回文

    题意

    记字符串 \(w\) 的倒置为 \(w^R\) 。例如 \((\tt{abcd})^R=\tt{dcba}\) , \((\tt{abba})^R=\tt{abba}\) 。

    对字符串 \(x\) ,如果 \(x\) 满足 \(x^R=x\) ,则称之为回文;例如 \(\tt{abba}\) 是一个回文,而 \(\tt{abed}\) 不是。

    如果 \(x\) 能够写成的 \(ww^Rww^R\)形式,则称它是一个“双倍回文”。换句话说,若要 \(x\) 是双倍回文,它的长度必须是 \(4\) 的倍数,而且 \(x\) , \(x\) 的前半部分, \(x\) 的后半部分都要是回文。例如 \(\tt{abbaabba}\) 是一个双倍回文,而 \(\tt{abaaba}\) 不是,因为它的长度不是 \(4\) 的倍数。

    \(x\) 的子串是指在\(x\)中连续的一段字符所组成的字符串。例如\(\tt{be}\)\(\tt{abed}\) 的子串,而 \(\tt{ac}\) 不是。

    \(x\)的回文子串,就是指满足回文性质的 \(x\)的子串。

    \(x\)的双倍回文子串,就是指满足双倍回文性质的 \(x\) 的子串。

    你的任务是,对于给定的字符串,计算它的最长双倍回文子串的长度。

    \(n \le 500000\)

    考虑Manacher求解

    对于修改后字符串的 \(i\)位置的初始回文半径 \(p_i\)(暴力拓展前的半径)

    如果 \(i-p_i+1 \le \text{mid}\),则存在一个极大双倍回文子串

    这个极大双倍回文子串在修改后的串中,

    以「以 \(\text{mid}\)为中心的回文串」的形式存在

    注意题目要求的是 \(4\)倍数的串,则对于刚才的求解方法,

    只要限制 \(i\)为奇数时更新答案、\(\text{mid}\)\(r\)

    此时 \(i\) 的回文半径 \(p_i^{\prime}-1\) 一定是偶串的长度

    注意 \(r\) 相同时,不要优先更新\(\text{mid}\)

    否则根据贪心,不难发现会失去某些极大值,导致答案出错

    这里给个hack(想了我几个小时才想出来

    input:12abcbbbbbbbbaoutput:8

    时间复杂度 \(O(n)\)

    代码:

    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)char s[N<<1];int n,p[N<<1];void Manacher(int l){    int r=1,mid=1,ans=0;    // 这里的i+=2可以写i++    // 但是要把下面两个if都加上 (i&1) && ...    // 没必要。还慢。    for(int i=1; i<=l; i+=2)    {        p[i]=(i<=r)?min(p[(mid<<1)-i],r-i+1):1;        if(i<=r&&i-p[i]+1<=mid) ans=max(ans,2*(i-mid));        // cout << i << " " << mid << " " << r << '\n';        while(s[i-p[i]]==s[i+p[i]]) ++p[i];        if(i+p[i]-1>r)r=i+p[i]-1,mid=i;        // cout << i << " " << mid << " " << r << '\n';    }    cout << ans << '\n';    // for(int i=1; i<=l; i++)    //     cout << p[i] << " \n"[i==l];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("P4287.out","w",stdout);    cin >> n >> (s+1);    s[0]='$';    for(int i=n; i; i--)        s[i*2+1]='#',s[i*2]=s[i];    s[1]='#';    Manacher(n*2+1);    return 0;}
    ]]> @@ -3478,7 +3478,7 @@ /2022/07/10/luo-gu-p2375-noi2014-dong-wu-yuan-ti-jie/ - 洛谷P2375 [NOI2014] 动物园 题解

    题目链接:P2375 [NOI2014] 动物园

    题意

    近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法。

    某天,园长给动物们讲解KMP算法。

    园长:“对于一个字符串$S$,它的长度为$L$。我们可以在$O(L)$的时间内,求出一个名为next的数组。有谁预习了next数组的含义吗?”

    熊猫:“对于字符串$S$的前$i$个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作$next[i]$。”

    园长:“非常好!那你能举个例子吗?”

    熊猫:“例$S$为abcababc,则$next[5]=2$。因为$S$的前$5$个字符为abcabab既是它的后缀又是它的前缀,并且找不到一个更长的字符串满足这个性质。同理,还可得出$next[1] = next[2] = next[3] = 0$,$next[4] = next[6] = 1$,$next[7] = 2$,$next[8] = 3$。”

    园长表扬了认真预习的熊猫同学。随后,他详细讲解了如何在$O(L)$的时间内求出next数组。

    下课前,园长提出了一个问题:“KMP算法只能求出next数组。我现在希望求出一个更强大num数组一一对于字符串$S$的前$i$个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作$num[i]$。例如$S$为aaaaa,则$num[4] = 2$。这是因为$S$的前$4$个字符为aaaa,其中aaa都满足性质‘既是后缀又是前缀’,同时保证这个后缀与这个前缀不重叠。而aaa虽然满足性质‘既是后缀又是前缀’,但遗憾的是这个后缀与这个前缀重叠了,所以不能计算在内。同理,$num[1] = 0,num[2] = num[3] = 1,num[5] = 2$。”

    最后,园长给出了奖励条件,第一个做对的同学奖励巧克力一盒。听了这句话,睡了一节课的企鹅立刻就醒过来了!但企鹅并不会做这道题,于是向参观动物园的你寻求帮助。你能否帮助企鹅写一个程序求出$num$数组呢?

    特别地,为了避免大量的输出,你不需要输出$num[i]$分别是多少,你只需要输出所有$(num[i]+1)$的乘积,对$1,000,000,007$取模的结果即可。

    $N ≤ 5, L ≤ 1,000,000$

    注:border为公共前后缀

    考虑 $S$ 的最长border $s$

    显然 $s$ 的最长border一定也是 $S$ 的border

    则 $\text{num}_{S} = \text{num}_s+1$(先不考虑重叠问题)

    对于重叠的情况直接跳fail数组即可

    时间复杂度 $O(\sum|s_i|)$

    代码:

    #include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)const int p=1e9+7;char s[N];int n,fail[N],res,num[N];void solve(){    for(int i=2,j=0; i<=n; i++)    {        while(j&&s[i]!=s[j+1])j=fail[j];        if(s[i]==s[j+1])++j;        fail[i]=j; num[i]=num[j]+1;    }    for(int i=2,j=0; i<=n; i++)    {        while(j&&s[i]!=s[j+1])j=fail[j];        if(s[i]==s[j+1])++j;        while((j<<1)>i)j=fail[j];        res=res*(num[j]+1)%p;    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; cin >> Q;    while(Q--)    {        res=1;num[1]=1;        cin >> (s+1); n=strlen(s+1);        solve(); cout << res << '\n';    }    return 0;}
    ]]> + 洛谷P2375 [NOI2014] 动物园题解

题目链接:P2375[NOI2014] 动物园

题意

近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法。

某天,园长给动物们讲解KMP算法。

园长:“对于一个字符串\(S\),它的长度为\(L\)。我们可以在\(O(L)\)的时间内,求出一个名为next的数组。有谁预习了next数组的含义吗?”

熊猫:“对于字符串\(S\)的前\(i\)个字符构成的子串,既是它的后缀又是它的前缀的字符串中(它本身除外),最长的长度记作\(next[i]\)。”

园长:“非常好!那你能举个例子吗?”

熊猫:“例\(S\)为abcababc,则\(next[5]=2\)。因为\(S\)的前\(5\)个字符为abcabab既是它的后缀又是它的前缀,并且找不到一个更长的字符串满足这个性质。同理,还可得出\(next[1] = next[2] = next[3] = 0\),\(next[4] = next[6] = 1\),\(next[7] = 2\),\(next[8] = 3\)。”

园长表扬了认真预习的熊猫同学。随后,他详细讲解了如何在\(O(L)\)的时间内求出next数组。

下课前,园长提出了一个问题:“KMP算法只能求出next数组。我现在希望求出一个更强大num数组一一对于字符串\(S\)的前\(i\)个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作\(num[i]\)。例如\(S\)为aaaaa,则\(num[4] = 2\)。这是因为\(S\)的前\(4\)个字符为aaaa,其中aaa都满足性质‘既是后缀又是前缀’,同时保证这个后缀与这个前缀不重叠。而aaa虽然满足性质‘既是后缀又是前缀’,但遗憾的是这个后缀与这个前缀重叠了,所以不能计算在内。同理,\(num[1] = 0,num[2] = num[3] = 1,num[5] =2\)。”

最后,园长给出了奖励条件,第一个做对的同学奖励巧克力一盒。听了这句话,睡了一节课的企鹅立刻就醒过来了!但企鹅并不会做这道题,于是向参观动物园的你寻求帮助。你能否帮助企鹅写一个程序求出\(num\)数组呢?

特别地,为了避免大量的输出,你不需要输出\(num[i]\)分别是多少,你只需要输出所有\((num[i]+1)\)的乘积,对\(1,000,000,007\)取模的结果即可。

\(N ≤ 5, L ≤ 1,000,000\)

注:border为公共前后缀

考虑 \(S\) 的最长border \(s\)

显然 \(s\) 的最长border一定也是\(S\) 的border

\(\text{num}_{S} =\text{num}_s+1\)(先不考虑重叠问题)

对于重叠的情况直接跳fail数组即可

时间复杂度 \(O(\sum|s_i|)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)const int p=1e9+7;char s[N];int n,fail[N],res,num[N];void solve(){    for(int i=2,j=0; i<=n; i++)    {        while(j&&s[i]!=s[j+1])j=fail[j];        if(s[i]==s[j+1])++j;        fail[i]=j; num[i]=num[j]+1;    }    for(int i=2,j=0; i<=n; i++)    {        while(j&&s[i]!=s[j+1])j=fail[j];        if(s[i]==s[j+1])++j;        while((j<<1)>i)j=fail[j];        res=res*(num[j]+1)%p;    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q; cin >> Q;    while(Q--)    {        res=1;num[1]=1;        cin >> (s+1); n=strlen(s+1);        solve(); cout << res << '\n';    }    return 0;}
]]> @@ -3505,7 +3505,7 @@ /2022/07/10/luo-gu-p4568-jloi2011-fei-xing-lu-xian-ti-jie/ - 洛谷P4568 [JLOI2011] 飞行路线 题解

题目链接:P4568 [JLOI2011] 飞行路线

题意

Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 $n$ 个城市设有业务,设这些城市分别标记为 $0$ 到 $n-1$,一共有 $m$ 种航线,每种航线连接两个城市,并且航线有一定的价格。

Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 $k$ 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?

对于 $100\%$ 的数据,$2 \le n \le 10^4$,$1 \le m \le 5\times 10^4$,$0 \le k \le 10$,$0\le s,t,a,b\le n$,$a\ne b$,$0\le c\le 10^3$。

分层图板子题

不写具体做法了,就总结几个易错点

  • 无向图的e数组开

    $M$ 稍微大一点(比如5e4+5)用于后面的额外边

  • 额外边:如果 $s\to t$ 的路径,结点数小于 $k$

    则需要单独建边

    for(int i=1; i<=k; i++)    addEdge((i-1)*n+t,i*n+t,0);

    这样答案才是 d[k*n+t]

时间复杂度 $O(nk\log mk)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+5)#define M (int)(5e4+5)#define K (int)(11)struct Edge{    int u,v,w,next;}e[M*(K+1)*4];int n,m,k,s,t,pos=1,head[N*(K+1)],d[N*(K+1)],vis[N*(K+1)];struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}priority_queue<node> q;void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dijkstra(int st){    memset(d,0x3f,sizeof(d));    q.push({st,0}); d[st]=0;    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> k >> s >> t; ++s; ++t;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w; ++u; ++v;        addEdge(u,v,w);addEdge(v,u,w);        for(int j=1; j<=k; j++)        {            addEdge(j*n+u,j*n+v,w);            addEdge(j*n+v,j*n+u,w);            addEdge((j-1)*n+u,j*n+v,0);            addEdge((j-1)*n+v,j*n+u,0);        }    }    for(int i=1; i<=k; i++)        addEdge((i-1)*n+t,i*n+t,0);    dijkstra(s);    cout << d[k*n+t] << '\n';    return 0;}
]]> + 洛谷P4568 [JLOI2011] 飞行路线题解

题目链接:P4568[JLOI2011] 飞行路线

题意

Alice 和 Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在\(n\)个城市设有业务,设这些城市分别标记为 \(0\) 到 \(n-1\),一共有 \(m\)种航线,每种航线连接两个城市,并且航线有一定的价格。

Alice 和 Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多\(k\) 种航线上搭乘飞机。那么 Alice 和Bob 这次出行最少花费多少?

对于 \(100\%\) 的数据,\(2 \le n \le 10^4\),\(1 \le m \le 5\times 10^4\),\(0 \le k \le 10\),\(0\le s,t,a,b\le n\),\(a\ne b\),\(0\lec\le 10^3\)

分层图板子题

不写具体做法了,就总结几个易错点

  • 无向图的e数组开 \[M\times (K+1) \times 4\] \(M\)稍微大一点(比如5e4+5)用于后面的额外边

  • 额外边:如果 \(s\to t\)的路径,结点数小于 \(k\)

    则需要单独建边

    for(int i=1; i<=k; i++)    addEdge((i-1)*n+t,i*n+t,0);

    这样答案才是 d[k*n+t]

时间复杂度 \(O(nk\log mk)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+5)#define M (int)(5e4+5)#define K (int)(11)struct Edge{    int u,v,w,next;}e[M*(K+1)*4];int n,m,k,s,t,pos=1,head[N*(K+1)],d[N*(K+1)],vis[N*(K+1)];struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}priority_queue<node> q;void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dijkstra(int st){    memset(d,0x3f,sizeof(d));    q.push({st,0}); d[st]=0;    while(!q.empty())    {        int u=q.top().u; q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> k >> s >> t; ++s; ++t;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w; ++u; ++v;        addEdge(u,v,w);addEdge(v,u,w);        for(int j=1; j<=k; j++)        {            addEdge(j*n+u,j*n+v,w);            addEdge(j*n+v,j*n+u,w);            addEdge((j-1)*n+u,j*n+v,0);            addEdge((j-1)*n+v,j*n+u,0);        }    }    for(int i=1; i<=k; i++)        addEdge((i-1)*n+t,i*n+t,0);    dijkstra(s);    cout << d[k*n+t] << '\n';    return 0;}
]]> @@ -3532,7 +3532,7 @@ /2022/07/09/luo-gu-p4551-zui-chang-yi-huo-lu-jing-ti-jie/ - 洛谷P4551 最长异或路径 题解

题目链接:P4551 最长异或路径

题意:给定一棵 $n$ 个点的带权树,结点下标从 $1$ 开始到 $n$。寻找树中找两个结点,求最长的异或路径。

异或路径指的是指两个结点之间唯一路径上的所有边权的异或。

$1\le n \le 100000,~0 < u,v \le n,~0 \le w < 2^{31}$。

首先可以想到一个常用trick:前缀和处理边权异或和

然后对于每个异或和,我们都要找到一个和它异或值尽可能大的前缀和

直接枚举是不行的,考虑建01trie

01trie其实很简单,就是把每个数字的二进制形式看成字符串去建trie

贪心地选择高位,具体看代码就可以了,很简单

时间复杂度 $O(n \log \max\{w_i\})$ ,其实就是 $O(30 \times n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;// #define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+5)struct Edge{int u,v,w,next;}e[N<<1];int n,pos=1,sum[N],head[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}struct Trie{    int trie[N*30][2],tot;    void insert(int x)    {        int u=0;        for(int i=30; i>=0; i--)        {            bool c=x&(1<<i);            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }    }    int query(int x)    {        int res=0,u=0;        for(int i=30; i>=0; i--)        {            bool c=!(x&(1<<i));            if(trie[u][c])            {                res+=(1<<i);                u=trie[u][c];            }else u=trie[u][!c];        }        return res;    }}tr;void dfs(int u,int f){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v,w=e[i].w;        if(v==f)continue;        sum[v]=sum[u]^w; dfs(v,u);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v,w; i<n; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w); addEdge(v,u,w);    }    dfs(1,1);    for(int i=1; i<=n; i++)        tr.insert(sum[i]);    int res=0;    for(int i=1; i<=n; i++)        res=max(res,tr.query(sum[i]));    cout << res << '\n';    return 0;}
]]> + 洛谷P4551 最长异或路径 题解

题目链接:P4551最长异或路径

题意:给定一棵 \(n\) 个点的带权树,结点下标从 \(1\) 开始到 \(n\)。寻找树中找两个结点,求最长的异或路径。

异或路径指的是指两个结点之间唯一路径上的所有边权的异或。

\(1\le n \le 100000,~0 < u,v \le n,~0\le w < 2^{31}\)

首先可以想到一个常用trick:前缀和处理边权异或和

然后对于每个异或和,我们都要找到一个和它异或值尽可能大的前缀和

直接枚举是不行的,考虑建01trie

01trie其实很简单,就是把每个数字的二进制形式看成字符串去建trie

贪心地选择高位,具体看代码就可以了,很简单

时间复杂度 \(O(n \log \max\{w_i\})\),其实就是 \(O(30 \times n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;// #define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+5)struct Edge{int u,v,w,next;}e[N<<1];int n,pos=1,sum[N],head[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}struct Trie{    int trie[N*30][2],tot;    void insert(int x)    {        int u=0;        for(int i=30; i>=0; i--)        {            bool c=x&(1<<i);            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }    }    int query(int x)    {        int res=0,u=0;        for(int i=30; i>=0; i--)        {            bool c=!(x&(1<<i));            if(trie[u][c])            {                res+=(1<<i);                u=trie[u][c];            }else u=trie[u][!c];        }        return res;    }}tr;void dfs(int u,int f){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v,w=e[i].w;        if(v==f)continue;        sum[v]=sum[u]^w; dfs(v,u);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v,w; i<n; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w); addEdge(v,u,w);    }    dfs(1,1);    for(int i=1; i<=n; i++)        tr.insert(sum[i]);    int res=0;    for(int i=1; i<=n; i++)        res=max(res,tr.query(sum[i]));    cout << res << '\n';    return 0;}
]]> @@ -3561,7 +3561,7 @@ /2022/07/09/luo-gu-p3698-cqoi2017-xiao-q-de-qi-pan-ti-jie/ - 洛谷P3698 [CQOI2017]小Q的棋盘 题解

题目链接:P3698 [CQOI2017]小Q的棋盘

题意

小 Q 正在设计一种棋类游戏。

在小 Q 设计的游戏中,棋子可以放在棋盘上的格点中。某些格点之间有连线,棋子只能在有连线的格点之间移动。整个棋盘上共有 $V$ 个格点,编号为$0,1,2,\dots , V−1$,它们是连通的,也就是说棋子从任意格点出发,总能到达所有的格点。小 Q 在设计棋盘时,还保证棋子从一个格点移动到另外任一格点的路径是唯一的。

小 Q 现在想知道,当棋子从格点 $0$ 出发,移动 $N$ 步最多能经过多少格点。格点可以重复经过多次,但不重复计数。

对于 $100\%$ 的测试点,$N,V \le 100,~0 \le a_i,b_i< V$ 。

显然树上背包

值得注意的是,我们并不知道走的路线有没有回到 $u$

因此要多加一维

设 $f_{u,j,0/1}$ 表示 $u$ 所在子树,走了 $j$ 步,$0/1$ 表示是/否回到 $u$

不难发现

注意一下边界问题就好了

时间复杂度 $O(nm^2)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)struct Edge{int u,v,next;}e[N<<1];int n,m,pos=1,f[N][N][2],head[N];#define Max(a,b) a=max(a,b)void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int fa){    f[u][0][0]=f[u][0][1]=1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==fa)continue;        dfs(v,u);        for(int j=m; j>=0; j--)        {            Max(f[u][j][1],f[u][j-1][0]+f[v][0][1]);            for(int k=2; k<=j; k++)            {                Max(f[u][j][0],f[u][j-k][0]+f[v][k-2][0]);                Max(f[u][j][1],max(f[u][j-k][1]+f[v][k-2][0],f[u][j-k][0]+f[v][k-1][1]));            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(f,0xc0,sizeof(f));    cin >> n >> m;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u+1,v+1);        addEdge(v+1,u+1);    }    dfs(1,1);    int res=0;    for(int i=0; i<=m; i++)        res=max(res,f[1][i][1]);    cout << res << '\n';    return 0;}

upd.20220722

模拟赛考原题写挂了,顺便发现一个问题

虽然老师数据挂了我才挂的,但是还是发现了这个问题

为什么答案要用

int res=0;for(int i=0; i<=m; i++)    res=max(res,f[1][i][1]);cout << res << '\n';

而不用

cout << g[1][m] << '\n';

从 $m$ 的角度看,多走肯定不会减少答案

但是如果 $m > 2(n-1)$ ,那么按照正常的dp是走不出这样的步数的

所以可以用前者输出答案,或者读入后加一句

m=min(m,2*n-2);

然后其实memset没什么必要,具体看下面代码

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)int n,m,pos=1,head[N],f[N][N],g[N][N];struct Edge{int u,v,next;}e[N<<1];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int fa){    f[u][0]=g[u][0]=1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v; if(v==fa) continue;        dfs(v,u);        for(int j=m; j>=1; j--)            for(int k=0; k<j; k++)            {                g[u][j]=max(g[u][j],f[u][j-k-1]+g[v][k]);                if(j-k-2>=0)                {                    f[u][j]=max(f[u][j],f[u][j-k-2]+f[v][k]);                    g[u][j]=max(g[u][j],g[u][j-k-2]+f[v][k]);                }            }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    // memset(f,0xc0,sizeof(f));    // memset(g,0xc0,sizeof(g));    cin >> n >> m; m=min(m,2*n-2);    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u+1,v+1); addEdge(v+1,u+1);    }    dfs(1,1);    cout << g[1][m] << '\n';    // int res=0;    // for(int i=0; i<=m; i++)    //     res=max(res,g[1][i]);    // cout << res << '\n';    return 0;}
]]> + 洛谷P3698 [CQOI2017]小Q的棋盘题解

题目链接:P3698[CQOI2017]小Q的棋盘

题意

小 Q 正在设计一种棋类游戏。

在小 Q设计的游戏中,棋子可以放在棋盘上的格点中。某些格点之间有连线,棋子只能在有连线的格点之间移动。整个棋盘上共有\(V\) 个格点,编号为\(0,1,2,\dots ,V−1\),它们是连通的,也就是说棋子从任意格点出发,总能到达所有的格点。小Q在设计棋盘时,还保证棋子从一个格点移动到另外任一格点的路径是唯一的。

小 Q 现在想知道,当棋子从格点 \(0\)出发,移动 \(N\)步最多能经过多少格点。格点可以重复经过多次,但不重复计数。

对于 \(100\%\) 的测试点,\(N,V \le 100,~0 \le a_i,b_i< V\) 。

显然树上背包

值得注意的是,我们并不知道走的路线有没有回到 \(u\)

因此要多加一维

\(f_{u,j,0/1}\) 表示 \(u\) 所在子树,走了 \(j\) 步,\(0/1\) 表示是/否回到 \(u\)

不难发现 \[f_{u,j,0}=\max_{v \in\text{son}(u)}\left\{f_{u,j,0},f_{u,j-k,0}+f_{v,k-2,0}\right\}\\f_{u,j,1}=\max_{v \in\text{son}(u)}\left\{f_{u,j,1},f_{u,j-k,1}+f_{v,k-2,0},f_{u,j-k,0}+f_{v,k-1,1}\right\}\] 注意一下边界问题就好了

时间复杂度 \(O(nm^2)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)struct Edge{int u,v,next;}e[N<<1];int n,m,pos=1,f[N][N][2],head[N];#define Max(a,b) a=max(a,b)void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int fa){    f[u][0][0]=f[u][0][1]=1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==fa)continue;        dfs(v,u);        for(int j=m; j>=0; j--)        {            Max(f[u][j][1],f[u][j-1][0]+f[v][0][1]);            for(int k=2; k<=j; k++)            {                Max(f[u][j][0],f[u][j-k][0]+f[v][k-2][0]);                Max(f[u][j][1],max(f[u][j-k][1]+f[v][k-2][0],f[u][j-k][0]+f[v][k-1][1]));            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(f,0xc0,sizeof(f));    cin >> n >> m;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u+1,v+1);        addEdge(v+1,u+1);    }    dfs(1,1);    int res=0;    for(int i=0; i<=m; i++)        res=max(res,f[1][i][1]);    cout << res << '\n';    return 0;}

upd.20220722

模拟赛考原题写挂了,顺便发现一个问题

虽然老师数据挂了我才挂的,但是还是发现了这个问题

为什么答案要用

int res=0;for(int i=0; i<=m; i++)    res=max(res,f[1][i][1]);cout << res << '\n';

而不用

cout << g[1][m] << '\n';

\(m\)的角度看,多走肯定不会减少答案

但是如果 \(m > 2(n-1)\),那么按照正常的dp是走不出这样的步数的

所以可以用前者输出答案,或者读入后加一句

m=min(m,2*n-2);

然后其实memset没什么必要,具体看下面代码

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)int n,m,pos=1,head[N],f[N][N],g[N][N];struct Edge{int u,v,next;}e[N<<1];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int fa){    f[u][0]=g[u][0]=1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v; if(v==fa) continue;        dfs(v,u);        for(int j=m; j>=1; j--)            for(int k=0; k<j; k++)            {                g[u][j]=max(g[u][j],f[u][j-k-1]+g[v][k]);                if(j-k-2>=0)                {                    f[u][j]=max(f[u][j],f[u][j-k-2]+f[v][k]);                    g[u][j]=max(g[u][j],g[u][j-k-2]+f[v][k]);                }            }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    // memset(f,0xc0,sizeof(f));    // memset(g,0xc0,sizeof(g));    cin >> n >> m; m=min(m,2*n-2);    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u+1,v+1); addEdge(v+1,u+1);    }    dfs(1,1);    cout << g[1][m] << '\n';    // int res=0;    // for(int i=0; i<=m; i++)    //     res=max(res,g[1][i]);    // cout << res << '\n';    return 0;}
]]> @@ -3590,7 +3590,7 @@ /2022/07/09/luo-gu-p1005-noip2007-ti-gao-zu-ju-zhen-qu-shu-you-xi-ti-jie/ - 洛谷P1005 [NOIP2007 提高组] 矩阵取数游戏 题解

题目链接:P1005 [NOIP2007 提高组] 矩阵取数游戏

题意

帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 $n \times m$ 的矩阵,矩阵中的每个元素 $a_{i,j}$ 均为非负整数。游戏规则如下:

  1. 每次取数时须从每行各取走一个元素,共 $n$ 个。经过 $m$ 次后取完矩阵内所有元素;
  2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
  3. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 $\times 2^i$,其中 $i$ 表示第 $i$ 次取数(从 $1$ 开始编号);
  4. 游戏结束总得分为 $m$ 次取数得分之和。

帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

对于 $60\%$ 的数据,满足 $1\le n,m\le 30$,答案不超过 $10^{16}$。
对于 $100\%$ 的数据,满足 $1\le n,m\le 80$,$0\le a_{i,j}\le1000$。

注意到每一行的决策与其他行无关

考虑区间dp然后把 $n$ 行答案加起来

设 $f_{i,j}$ 表示区间变为 $[i,j]$ 时的最大分数

不难发现

要用高精度。比较恶心。

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;// #define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(85)const int B=1e4;int n,m,a[N];struct bg{    int num[505],len;    bg()    {        memset(num,0,sizeof(num));        len=0;    }    void print()    {        cout << num[len];        for(int i=len-1; i>0; i--)        {            if(!num[i])cout << "0000";            else            {                for(int k=10; k*num[i]<B; k*=10)                    cout << "0";                cout << num[i];            }        }    }}f[N][N],base[N],ans;bg operator+(bg a,bg b){    bg c;c.len=max(a.len,b.len);    int jw=0;    for(int i=1; i<=c.len; i++)    {        c.num[i]=a.num[i]+b.num[i]+jw;        jw=c.num[i]/B;        c.num[i]%=B;    }    if(jw>0)        c.num[++c.len]=jw;    return c;}bg operator*(bg a,int b){    bg c; c.len=a.len;    int jw=0;    for(int i=1; i<=c.len; i++)    {        c.num[i]=a.num[i]*b+jw;        jw=c.num[i]/B;        c.num[i]%=B;    }    while(jw>0)        c.num[++c.len]=jw%B,jw/=B;    return c;}bg max(bg a,bg b){    if(a.len!=b.len)return a.len<b.len?b:a;    for(int i=a.len; i>0; i--)        if(a.num[i]!=b.num[i])            return a.num[i]>b.num[i]?a:b;    return a;}void init(){    base[0].num[1]=1;    base[0].len=1;    for(int i=1; i<=m+2; i++)        base[i]=base[i-1]*2;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    init();    bg res;    while(n--)    {        memset(f,0,sizeof(f));        for(int i=1; i<=m; i++)            cin >> a[i];        for(int i=1; i<=m; i++)            for(int j=m; j>=i; j--)            {                f[i][j]=max(f[i][j],f[i-1][j]+base[m-j+i-1]*a[i-1]);                f[i][j]=max(f[i][j],f[i][j+1]+base[m-j+i-1]*a[j+1]);            }        bg mx;        for(int i=1; i<=m; i++)            mx=max(mx,f[i][i]+base[m]*a[i]);        res=res+mx;    }    res.print();    return 0;}
]]> + 洛谷P1005 [NOIP2007提高组] 矩阵取数游戏 题解

题目链接:P1005[NOIP2007 提高组] 矩阵取数游戏

题意

帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 \(n \times m\) 的矩阵,矩阵中的每个元素 \(a_{i,j}\) 均为非负整数。游戏规则如下:

  1. 每次取数时须从每行各取走一个元素,共 \(n\) 个。经过 \(m\) 次后取完矩阵内所有元素;
  2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
  3. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 =被取走的元素值 \(\times 2^i\),其中\(i\) 表示第 \(i\) 次取数(从 \(1\) 开始编号);
  4. 游戏结束总得分为 \(m\)次取数得分之和。

帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

对于 \(60\%\) 的数据,满足 \(1\le n,m\le 30\),答案不超过 \(10^{16}\)。 对于 \(100\%\) 的数据,满足 \(1\le n,m\le 80\),\(0\le a_{i,j}\le1000\)。

注意到每一行的决策与其他行无关

考虑区间dp然后把 \(n\)行答案加起来

\(f_{i,j}\) 表示区间变为 \([i,j]\) 时的最大分数

不难发现 \[f_{i,i}=\max\left\{f_{i,i}+a_i\times 2^m\right\}\\f_{i,j}=\max\left\{f_{i-1,j}+a_{i-1} \times2^{m-j+i-1},f_{i,j+1}+a_{j+1} \times 2^{m-j+i-1}\right\}\] 要用高精度。比较恶心。

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;// #define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(85)const int B=1e4;int n,m,a[N];struct bg{    int num[505],len;    bg()    {        memset(num,0,sizeof(num));        len=0;    }    void print()    {        cout << num[len];        for(int i=len-1; i>0; i--)        {            if(!num[i])cout << "0000";            else            {                for(int k=10; k*num[i]<B; k*=10)                    cout << "0";                cout << num[i];            }        }    }}f[N][N],base[N],ans;bg operator+(bg a,bg b){    bg c;c.len=max(a.len,b.len);    int jw=0;    for(int i=1; i<=c.len; i++)    {        c.num[i]=a.num[i]+b.num[i]+jw;        jw=c.num[i]/B;        c.num[i]%=B;    }    if(jw>0)        c.num[++c.len]=jw;    return c;}bg operator*(bg a,int b){    bg c; c.len=a.len;    int jw=0;    for(int i=1; i<=c.len; i++)    {        c.num[i]=a.num[i]*b+jw;        jw=c.num[i]/B;        c.num[i]%=B;    }    while(jw>0)        c.num[++c.len]=jw%B,jw/=B;    return c;}bg max(bg a,bg b){    if(a.len!=b.len)return a.len<b.len?b:a;    for(int i=a.len; i>0; i--)        if(a.num[i]!=b.num[i])            return a.num[i]>b.num[i]?a:b;    return a;}void init(){    base[0].num[1]=1;    base[0].len=1;    for(int i=1; i<=m+2; i++)        base[i]=base[i-1]*2;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    init();    bg res;    while(n--)    {        memset(f,0,sizeof(f));        for(int i=1; i<=m; i++)            cin >> a[i];        for(int i=1; i<=m; i++)            for(int j=m; j>=i; j--)            {                f[i][j]=max(f[i][j],f[i-1][j]+base[m-j+i-1]*a[i-1]);                f[i][j]=max(f[i][j],f[i][j+1]+base[m-j+i-1]*a[j+1]);            }        bg mx;        for(int i=1; i<=m; i++)            mx=max(mx,f[i][i]+base[m]*a[i]);        res=res+mx;    }    res.print();    return 0;}
]]>
@@ -3617,7 +3617,7 @@ /2022/07/09/luo-gu-p2585-zjoi2006-san-se-er-cha-shu-ti-jie/ - 洛谷P2585 [ZJOI2006]三色二叉树 题解

题目链接:P2585 [ZJOI2006]三色二叉树

题意

一棵二叉树可以按照如下规则表示成一个由 $0$、$1$、$2$ 组成的字符序列,我们称之为“二叉树序列 $S$”:

例如,下图所表示的二叉树可以用二叉树序列 $S=\texttt{21200110}$ 来表示。

你的任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不同。给定一颗二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。

对于全部的测试点,保证 $1 \leq |s| \leq 5 \times 10^5$,$s$ 中只含字符 0 1 2

入门级的树形dp

设 $f_i$ 表示染 $i$ 所在子树,$i$ 染绿色的最大/最小绿色结点数,

$g_i$ 表示染 $i$ 所在子树,$i$ 不染绿色的最大/最小绿色结点数。

跑个dfs建树即可

时间复杂度 $O(n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)char s[N];int n,rt,f[N],g[N],ch[N][2],cnt;void build(int &u){    u=++cnt;    int c=s[u]-'0';    if(c==0)return;    if(c==1)build(ch[u][0]);    if(c==2)build(ch[u][0]),build(ch[u][1]);}#define ls(x) (ch[x][0])#define rs(x) (ch[x][1])void solve(int ck){    for(int i=n; i>=1; i--)    {        f[i]=g[ls(i)]+g[rs(i)]+1;        if(!ck)g[i]=max(f[ls(i)]+g[rs(i)],g[ls(i)]+f[rs(i)]);        else g[i]=min(f[ls(i)]+g[rs(i)],g[ls(i)]+f[rs(i)]);    }    if(!ck)cout << max(f[1],g[1]) << ' ';    else cout << min(f[1],g[1]) << '\n';}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (s+1); n=strlen(s+1); build(rt);    solve(0); solve(1);    return 0;}

转载请说明出处

]]>
+ 洛谷P2585[ZJOI2006]三色二叉树 题解

题目链接:P2585[ZJOI2006]三色二叉树

题意

一棵二叉树可以按照如下规则表示成一个由 \(0\)、\(1\)、\(2\)组成的字符序列,我们称之为“二叉树序列 \(S\)”:

\[S=\begin{cases}0& \text表示该树没有子节点\\1S_1& 表示该树有一个节点,S_1 为其子树的二叉树序列\\2S_1S_2& 表示该树由两个子节点,S_1 和 S_2分别表示其两个子树的二叉树序列\end{cases}\]

例如,下图所表示的二叉树可以用二叉树序列 \(S=\texttt{21200110}\) 来表示。

你的任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不同。给定一颗二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。

对于全部的测试点,保证 \(1 \leq |s| \leq 5\times 10^5\)\(s\) 中只含字符0 1 2

入门级的树形dp

\(f_i\) 表示染 \(i\) 所在子树,\(i\) 染绿色的最大/最小绿色结点数,

\(g_i\) 表示染 \(i\) 所在子树,\(i\) 不染绿色的最大/最小绿色结点数。 \[f_i = g_l+g_r+1\\g_i = \max/\min\left\{f_l+g_r,~g_l+f_l\right\}\] 跑个dfs建树即可

时间复杂度 \(O(n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)char s[N];int n,rt,f[N],g[N],ch[N][2],cnt;void build(int &u){    u=++cnt;    int c=s[u]-'0';    if(c==0)return;    if(c==1)build(ch[u][0]);    if(c==2)build(ch[u][0]),build(ch[u][1]);}#define ls(x) (ch[x][0])#define rs(x) (ch[x][1])void solve(int ck){    for(int i=n; i>=1; i--)    {        f[i]=g[ls(i)]+g[rs(i)]+1;        if(!ck)g[i]=max(f[ls(i)]+g[rs(i)],g[ls(i)]+f[rs(i)]);        else g[i]=min(f[ls(i)]+g[rs(i)],g[ls(i)]+f[rs(i)]);    }    if(!ck)cout << max(f[1],g[1]) << ' ';    else cout << min(f[1],g[1]) << '\n';}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (s+1); n=strlen(s+1); build(rt);    solve(0); solve(1);    return 0;}

转载请说明出处

]]>
@@ -3631,10 +3631,10 @@ 算法 - DP - 图论 + DP + 数据结构 @@ -3648,7 +3648,7 @@ /2022/07/08/luo-gu-p5520-yloi2019-qing-yuan-ying-ti-jie/ - 洛谷P5520 [yLOI2019] 青原樱 题解

题目链接:P5520 [yLOI2019] 青原樱

题意

$n$ 个空放 $m$ 个物品,两两物品不能直接相邻,至少空一格

纯数学题。

看看几个空不能放,啊 $m-1$

那能放的就有 $n-m+1$ 个空

没了,水吧。直接算这个就好了。

为什么对呢,因为你在这 $n-m+1$ 个空里面可以随便放

时间复杂度 $O(n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n,m,p,_;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> _ >> n >> m >> p;    int res=1;    for(int i=n-m+1; i>=n-2*m+2; i--)        res=res*i%p;    cout << res << '\n';    return 0;}
]]>
+ 洛谷P5520 [yLOI2019] 青原樱题解

题目链接:P5520[yLOI2019] 青原樱

题意

\(n\) 个空放 \(m\)个物品,两两物品不能直接相邻,至少空一格

纯数学题。

看看几个空不能放,啊 \(m-1\)

那能放的就有 \(n-m+1\) 个空 \[A_{n-m+1}^{m}\] 没了,水吧。直接算这个就好了。

为什么对呢,因为你在这 \(n-m+1\)个空里面可以随便放

时间复杂度 \(O(n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n,m,p,_;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> _ >> n >> m >> p;    int res=1;    for(int i=n-m+1; i>=n-2*m+2; i--)        res=res*i%p;    cout << res << '\n';    return 0;}
]]>
@@ -3675,7 +3675,7 @@ /2022/07/08/luo-gu-p2822-noip2016-ti-gao-zu-zu-he-shu-wen-ti-ti-jie/ - 洛谷P2822 [NOIP2016 提高组] 组合数问题 题解

题目链接:P2822 [NOIP2016 提高组] 组合数问题

题意

组合数 $\binom{n}{m}$ 表示的是从 $n$ 个物品中选出 $m$ 个物品的方案数。举个例子,从 $(1,2,3)$ 三个物品中选择两个物品可以有 $(1,2),(1,3),(2,3)$ 这三种选择方法。根据组合数的定义,我们可以给出计算组合数 $\binom{n}{m}$ 的一般公式:

其中 $n!=1\times2\times\cdots\times n$;特别地,定义 $0!=1$。

小葱想知道如果给定 $n,m$ 和 $k$,对于所有的 $0\leq i\leq n,0\leq j\leq \min \left ( i, m \right )$ 有多少对 $(i,j)$ 满足 $k|\binom{i}{j}$。

  • 对于全部的测试点,保证 $0 \leq n, m \leq 2 \times 10^3$,$1 \leq t \leq 10^4$。

显然杨辉三角的那个预处理

询问可以用二维前缀和来搞

这里的前缀和有个有趣的地方

s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(c[i][j]==0)

这里在获取 $s_{i-1,j}$ 的时候,如果 $j=i$ ,那 $s_{i-1,j}$ 就没有算,会导致出错

解决的方法很简单,直接在每次计算时把 $s_{i,i+1}\leftarrow s_{i,i}$ 就好了

这样的话没有影响前缀和数组的意义,蛮好

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)#define _ 2000int k,c[N][N],s[N][N];void init(){    c[1][1]=1;    for(int i=0; i<=_; i++) c[i][0]=1;    for(int i=2; i<=_; i++)        for(int j=1; j<=i; j++)            c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;    for(int i=2; i<=_; i++)    {        for(int j=1; j<=i; j++)            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(c[i][j]==0);        s[i][i+1]=s[i][i];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q,n,m;    cin >> Q >> k;init();    while(Q--)    {        cin >> n >> m;        if(m>n)m=n;        cout << s[n][m] << '\n';    }    return 0;}
]]>
+ 洛谷P2822 [NOIP2016提高组] 组合数问题 题解

题目链接:P2822[NOIP2016 提高组] 组合数问题

题意

组合数 \(\binom{n}{m}\) 表示的是从\(n\) 个物品中选出 \(m\) 个物品的方案数。举个例子,从 \((1,2,3)\) 三个物品中选择两个物品可以有\((1,2),(1,3),(2,3)\)这三种选择方法。根据组合数的定义,我们可以给出计算组合数 \(\binom{n}{m}\) 的一般公式:

\[\binom{n}{m}=\frac{n!}{m!(n-m)!}\] 其中 \(n!=1\times2\times\cdots\timesn\);特别地,定义 \(0!=1\)

小葱想知道如果给定 \(n,m\)\(k\),对于所有的 \(0\leq i\leq n,0\leq j\leq \min \left ( i, m \right)\) 有多少对 \((i,j)\) 满足\(k|\binom{i}{j}\)

  • 对于全部的测试点,保证 \(0 \leq n, m \leq2 \times 10^3\)\(1 \leq t \leq10^4\)

显然杨辉三角的那个预处理

询问可以用二维前缀和来搞

这里的前缀和有个有趣的地方

s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(c[i][j]==0)

这里在获取 \(s_{i-1,j}\)的时候,如果 \(j=i\) ,那 \(s_{i-1,j}\) 就没有算,会导致出错

解决的方法很简单,直接在每次计算时把 \(s_{i,i+1}\leftarrow s_{i,i}\) 就好了

这样的话没有影响前缀和数组的意义,蛮好

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)#define _ 2000int k,c[N][N],s[N][N];void init(){    c[1][1]=1;    for(int i=0; i<=_; i++) c[i][0]=1;    for(int i=2; i<=_; i++)        for(int j=1; j<=i; j++)            c[i][j]=(c[i-1][j]+c[i-1][j-1])%k;    for(int i=2; i<=_; i++)    {        for(int j=1; j<=i; j++)            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(c[i][j]==0);        s[i][i+1]=s[i][i];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q,n,m;    cin >> Q >> k;init();    while(Q--)    {        cin >> n >> m;        if(m>n)m=n;        cout << s[n][m] << '\n';    }    return 0;}
]]>
@@ -3700,7 +3700,7 @@ /2022/07/05/luo-gu-p4127-ahoi2009-tong-lei-fen-bu-ti-jie/ - 洛谷P4127 [AHOI2009]同类分布 题解

题目链接:P4127 [AHOI2009]同类分布

题意

给出两个数 $a,b$ ,求出 $[a,b]$ 中各位数字之和能整除原数的数的个数。

对于所有的数据, $1 ≤ a ≤ b ≤ 10^{18}$。

明天就期末考了非但不复习还在刷题 qwqqwqwq

因为我们不能确切知道每个数的数位和

但是我们知道它们一定在 $[1,9 \times l]$ 的范围内( $l$ 为最长的位数)

我们枚举所有的数位和并计算每个数位和对应的答案

把这些答案加起来就是 $[0,x]$ 的答案

$[a,b]$ 的答案可以拆分为两个询问 $[0,b]$ 和 $[0,a-1]$

而这里 $a,b \le 10^{18}$ ,直接压入状态显然不可接受

于是我们可以考虑记录模当前枚举的数位和意义下的数

方便起见,我们称当前枚举的数位和为 $m$

设 $f_{i,j,k}$ 表示只考虑前 $i$ 位数(包括前导零),前 $i$ 位的数位和为 $j$ ,当前数模 $m$ 为 $k$ 时的答案

不难发现

其中 $t$ 为第 $i+1$ 位的高位限制

用记搜写法能简洁不少

时间复杂度 $O(9^3\times l^4)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int len,num[25],f[25][205][205];int dfs(int u,int sum,int st,int limit,int p){    if(u>len)    {        if(!sum)return 0;        return st==0&&sum==p;    }    if(!limit&&f[u][sum][st]!=INF)        return f[u][sum][st];    int up=limit?num[len-u+1]:9;    int res=0;    for(int i=0; i<=up; i++)        res+=dfs(u+1,sum+i,(10*st+i)%p,limit&&i==up,p);    return limit?res:f[u][sum][st]=res;}int solve(int x){    int res=0; len=0;    while(x) num[++len]=x%10,x/=10;    for(int m=1; m<=9*len; m++)    {        memset(f,0x3f,sizeof(f));        res+=dfs(1,0,0,1,m);    }    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int l,r; cin >> l >> r;    cout << solve(r)-solve(l-1) << '\n';    return 0;}

没事反正whk暂时开摆了

]]>
+ 洛谷P4127 [AHOI2009]同类分布题解

题目链接:P4127[AHOI2009]同类分布

题意

给出两个数 \(a,b\) ,求出 \([a,b]\)中各位数字之和能整除原数的数的个数。

对于所有的数据, \(1 ≤ a ≤ b ≤10^{18}\)

明天就期末考了非但不复习还在刷题 qwqqwqwq

因为我们不能确切知道每个数的数位和

但是我们知道它们一定在 \([1,9 \timesl]\) 的范围内( \(l\)为最长的位数)

我们枚举所有的数位和并计算每个数位和对应的答案

把这些答案加起来就是 \([0,x]\)的答案

\([a,b]\) 的答案可以拆分为两个询问\([0,b]\)\([0,a-1]\)

而这里 \(a,b \le 10^{18}\),直接压入状态显然不可接受

于是我们可以考虑记录模当前枚举的数位和意义下的数

方便起见,我们称当前枚举的数位和为 \(m\)

\(f_{i,j,k}\) 表示只考虑前 \(i\) 位数(包括前导零),前 \(i\) 位的数位和为 \(j\) ,当前数模 \(m\) 为 \(k\) 时的答案

不难发现 \[f_{i,j,k} = \sum_{0 \le d \le t} f_{i+1,j+d,\left(k\times 10 + d \,\bmod \, m\right)}\] 其中 \(t\) 为第 \(i+1\) 位的高位限制

用记搜写法能简洁不少

时间复杂度 \(O(9^3\times l^4)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int len,num[25],f[25][205][205];int dfs(int u,int sum,int st,int limit,int p){    if(u>len)    {        if(!sum)return 0;        return st==0&&sum==p;    }    if(!limit&&f[u][sum][st]!=INF)        return f[u][sum][st];    int up=limit?num[len-u+1]:9;    int res=0;    for(int i=0; i<=up; i++)        res+=dfs(u+1,sum+i,(10*st+i)%p,limit&&i==up,p);    return limit?res:f[u][sum][st]=res;}int solve(int x){    int res=0; len=0;    while(x) num[++len]=x%10,x/=10;    for(int m=1; m<=9*len; m++)    {        memset(f,0x3f,sizeof(f));        res+=dfs(1,0,0,1,m);    }    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int l,r; cin >> l >> r;    cout << solve(r)-solve(l-1) << '\n';    return 0;}

没事反正whk暂时开摆了

]]>
@@ -3727,7 +3727,7 @@ /2022/07/05/cf1036c-classy-numbers-ti-jie/ - CF1036C Classy Numbers 题解

题目链接:CF1036C Classy Numbers

题意:定义一个数字是“好数”,当且仅当它的十进制表示下有不超过$3$个数字$1 \sim 9$

举个例子:$4,200000,10203$是“好数”,然而$4231,102306,7277420000$不是

给定$[l,r]$,问有多少个$x$使得$l \le x \le r$,且$x$是“好数”

一共有$T(1 \le T \le 10^{4})$组数据,对于每次的询问,输出一行一个整数表示答案

$1 \le l_i \le r_i \le 10^{18}$

一般数位dp可以用来解决 $\sum _{i=l}^{r} f(i)$ 的问题,

其中 $f(i)$ 为与 $i$ 的数位有关的某个函数或判定式

这题稍微变形了一下,那我们就理所当然地记录一下出现数字的情况

设 $f_{i,j}$ 表示满 $i$ 位数,有 $j$ 个非 $0$ 位

采用记忆化搜索,详见代码

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int num[25],f[25][5];// u表示当前位数,st表示当前的j,limit表示是否有高位限制int dfs(int u,int st,bool limit){    if(!u)return 1;    if(!limit&&f[u][st]!=INF)        return f[u][st]; // 记忆化    int up=limit?num[u]:9,ans=0;    for(int i=0; i<=up; i++)    {        if(!i)ans+=dfs(u-1,st,limit&&num[u]==i);        else if(st!=3)ans+=dfs(u-1,st+1,limit&&num[u]==i);    }    if(!limit) f[u][st]=ans; // 只需记录无高位限制的    return ans;}int solve(int x){    int len=0;    while(x>0)    {        num[++len]=x%10;        x/=10;    }    return dfs(len,0,1);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(f,0x3f,sizeof(f));    int Q;cin >> Q;    while(Q--)    {        int l,r;        cin >> l >> r;        cout << solve(r)-solve(l-1) << '\n';    }    return 0;}

参考文献

[1] https://www.luogu.com.cn/blog/rated/solution-cf1036c

]]>
+ CF1036C Classy Numbers 题解

题目链接:CF1036CClassy Numbers

题意:定义一个数字是“好数”,当且仅当它的十进制表示下有不超过\(3\)个数字\(1 \sim9\)

举个例子:\(4,200000,10203\)是“好数”,然而\(4231,102306,7277420000\)不是

给定\([l,r]\),问有多少个\(x\)使得\(l \le x\le r\),且\(x\)是“好数”

一共有\(T(1 \le T \le10^{4})\)组数据,对于每次的询问,输出一行一个整数表示答案

\(1 \le l_i \le r_i \le10^{18}\)

一般数位dp可以用来解决 \(\sum _{i=l}^{r}f(i)\) 的问题,

其中 \(f(i)\) 为与 \(i\) 的数位有关的某个函数或判定式

这题稍微变形了一下,那我们就理所当然地记录一下出现数字的情况

\(f_{i,j}\) 表示满 \(i\) 位数,有 \(j\) 个非 \(0\) 位

采用记忆化搜索,详见代码

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int num[25],f[25][5];// u表示当前位数,st表示当前的j,limit表示是否有高位限制int dfs(int u,int st,bool limit){    if(!u)return 1;    if(!limit&&f[u][st]!=INF)        return f[u][st]; // 记忆化    int up=limit?num[u]:9,ans=0;    for(int i=0; i<=up; i++)    {        if(!i)ans+=dfs(u-1,st,limit&&num[u]==i);        else if(st!=3)ans+=dfs(u-1,st+1,limit&&num[u]==i);    }    if(!limit) f[u][st]=ans; // 只需记录无高位限制的    return ans;}int solve(int x){    int len=0;    while(x>0)    {        num[++len]=x%10;        x/=10;    }    return dfs(len,0,1);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(f,0x3f,sizeof(f));    int Q;cin >> Q;    while(Q--)    {        int l,r;        cin >> l >> r;        cout << solve(r)-solve(l-1) << '\n';    }    return 0;}

参考文献

[1] https://www.luogu.com.cn/blog/rated/solution-cf1036c

]]>
@@ -3754,7 +3754,7 @@ /2022/07/05/luo-gu-p1836-shu-ye-ma-ti-jie/ - 洛谷P1836 数页码 题解

题目链接:P1836 数页码

题意

一本书的页码是从 $1\sim n$ 编号的连续整数:$1,2,3,\cdots,n$。请你求出全部页码中所有单个数字的和,例如第 $123$ 页,它的和就是 $1+2+3=6$。

$1\le n\le 10^9$

容易发现这个东西可以转化为

$1 \sim n$ 中每个数字有多少个

考虑数位dp,详见link

然后把每个数字乘一乘就好了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int x,mi[25],cnt[25],num[25],f[25];void solve(int x,int *cnt){    int len=0;    memset(num,0,sizeof(num));    while(x)    {        num[++len]=x%10;        x/=10;    }    for(int i=len; i>=1; i--)    {        for(int j=0; j<=9; j++)            cnt[j]+=f[i-1]*num[i];        for(int j=0; j<num[i]; j++)            cnt[j]+=mi[i-1];        int res=0;        for(int j=i-1; j>=1; j--)        {            res = res * 10 + num[j];        }        cnt[num[i]]+=res+1;        cnt[0]-=mi[i-1];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    mi[0]=1;    for(int i=1; i<=18; i++)    {        f[i]=f[i-1]*10+mi[i-1];        mi[i]=10*mi[i-1];    }    cin >> x;    solve(x,cnt);    int res=0;    for(int i=0; i<=9; i++)        res+=cnt[i]*i;    cout << res << '\n';    return 0;}

转载请说明出处

]]>
+ 洛谷P1836 数页码 题解

题目链接:P1836数页码

题意

一本书的页码是从 \(1\sim n\)编号的连续整数:\(1,2,3,\cdots,n\)。请你求出全部页码中所有单个数字的和,例如第\(123\) 页,它的和就是 \(1+2+3=6\)。

\(1\le n\le 10^9\)

容易发现这个东西可以转化为

\(1 \sim n\) 中每个数字有多少个

考虑数位dp,详见link

然后把每个数字乘一乘就好了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int x,mi[25],cnt[25],num[25],f[25];void solve(int x,int *cnt){    int len=0;    memset(num,0,sizeof(num));    while(x)    {        num[++len]=x%10;        x/=10;    }    for(int i=len; i>=1; i--)    {        for(int j=0; j<=9; j++)            cnt[j]+=f[i-1]*num[i];        for(int j=0; j<num[i]; j++)            cnt[j]+=mi[i-1];        int res=0;        for(int j=i-1; j>=1; j--)        {            res = res * 10 + num[j];        }        cnt[num[i]]+=res+1;        cnt[0]-=mi[i-1];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    mi[0]=1;    for(int i=1; i<=18; i++)    {        f[i]=f[i-1]*10+mi[i-1];        mi[i]=10*mi[i-1];    }    cin >> x;    solve(x,cnt);    int res=0;    for(int i=0; i<=9; i++)        res+=cnt[i]*i;    cout << res << '\n';    return 0;}

转载请说明出处

]]>
@@ -3781,7 +3781,7 @@ /2022/07/05/sp3928-mdigits-counting-digits-ti-jie/ - SP3928 MDIGITS - Counting Digits 题解

题目链接:SP3928 MDIGITS - Counting Digits

题意

给定两个整数 $a$ 和 $b$,求 $a$ 和 $b$ 之间的所有数字中 $0$ ~ $9$ 出现次数。

例如,$a$ = $1024$,$b$ = $1032$,则 $a$ 和 $b$ 之间共有 $9$ 个数如下:

1024 1025 1026 1027 1028 1029 1030 1031 1032

其中 0 出现 $10$ 次,1 出现 $10$ 次,2 出现 $7$ 次,3 出现 $3$ 次等等……

数位dp的基础题

设 $f_i$ 表示满 $i$ 位数字(包括前导零),每种数字的出现次数

其中 $f_{i-1}\times 10$ 是 $i-1$ 位及以下位的贡献

例如 $\tt{7000\sim7999,~8000\sim8999}$

显然 $\tt{000\sim999}$出现了 $10$ 次

而 $10^{i-1}$ 是第 $i$ 位的贡献,比如 $\tt{9000 \sim 9999}$ ,第 $4$ 位的 $\tt{9}$ 出现了 $10^3$ 次

然后我们再考虑怎么获得答案

首先 $[l,r]$ 可以拆分为 $[0,l-1],~[0,r]$ 两个询问(基本的容斥)

然后考虑一个数 $\tt{\overline{ABC}}$ ,不难发现,$\tt{0}$ 到 $\tt{\overline{A00}}$ 每个非最高位数都出现了 $\tt{A}$ $\times f_2$ 次

而最高位 $\tt{0\sim A-1}$ 都各出现了 $10^2$ 次

注:这里 $\tt 0$ 是前导零,所以其实不会算进去,这里只是为了方便分析

那么 $\tt{A}$ 呢?不难发现它出现了 $\tt{\overline{BC}+1}$ 次

对于 $\tt{B}$ ,同样的处理方式。

怎么一股机翻的味道

然后我们就搞定这道题了

时间复杂度 $O(Qlb)$

其中 $l$ 表示最大位数, $b$ 表示进制,这题里为 $10$

代码:(非dfs写法)

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int a,b,mi[25],cnt1[25],cnt2[25],num[25],f[25];void clear(){    memset(cnt1,0,sizeof(cnt1));    memset(cnt2,0,sizeof(cnt2));}void solve(int x,int *cnt){    int len=0;    memset(num,0,sizeof(num));    while(x)    {        num[++len]=x%10;        x/=10;    }    for(int i=len; i>=1; i--)    {        for(int j=0; j<=9; j++)            cnt[j]+=f[i-1]*num[i];        for(int j=0; j<num[i]; j++)            cnt[j]+=mi[i-1];        int res=0;        for(int j=i-1; j>=1; j--)        {            res = res * 10 + num[j];        }        cnt[num[i]]+=res+1;        cnt[0]-=mi[i-1];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    mi[0]=1;    for(int i=1; i<=18; i++)    {        f[i]=f[i-1]*10+mi[i-1];        mi[i]=10*mi[i-1];    }    while(cin >> a >> b)    {        if(!a&&!b)return 0;        if(a>b)swap(a,b);        clear();        solve(a-1,cnt1);solve(b,cnt2);        for(int i=0; i<=9; i++)            cout << cnt2[i]-cnt1[i] << " \n"[i==9];    }    return 0;}
]]>
+ SP3928 MDIGITS - CountingDigits 题解

题目链接:SP3928MDIGITS - Counting Digits

题意

给定两个整数 \(a\)\(b\),求 \(a\) 和 \(b\) 之间的所有数字中 \(0\) ~ \(9\) 出现次数。

例如,\(a\) = \(1024\),\(b\) = \(1032\),则 \(a\) 和 \(b\) 之间共有 \(9\) 个数如下:

1024 1025 1026 1027 1028 1029 1030 1031 1032

其中 0 出现 \(10\)次,1 出现 \(10\)次,2 出现 \(7\)次,3 出现 \(3\)次等等……

数位dp的基础题

\(f_i\) 表示满 \(i\)位数字(包括前导零),每种数字的出现次数 \[f_1 = 1\\f_i = f_{i-1} \times 10 + 10^{i-1}\] 其中 \(f_{i-1}\times 10\)\(i-1\) 位及以下位的贡献

例如 \(\tt{7000\sim7999,~8000\sim8999}\)

显然 \(\tt{000\sim999}\)出现了 \(10\) 次

\(10^{i-1}\) 是第 \(i\) 位的贡献,比如 \(\tt{9000 \sim 9999}\) ,第 \(4\) 位的 \(\tt{9}\) 出现了 \(10^3\) 次

然后我们再考虑怎么获得答案

首先 \([l,r]\) 可以拆分为 \([0,l-1],~[0,r]\) 两个询问(基本的容斥)

然后考虑一个数 \(\tt{\overline{ABC}}\) ,不难发现,\(\tt{0}\) 到 \(\tt{\overline{A00}}\)每个非最高位数都出现了 \(\tt{A}\) \(\times f_2\) 次

而最高位 \(\tt{0\sim A-1}\)都各出现了 \(10^2\)

注:这里 \(\tt 0\)是前导零,所以其实不会算进去,这里只是为了方便分析

那么 \(\tt{A}\) 呢?不难发现它出现了\(\tt{\overline{BC}+1}\)

对于 \(\tt{B}\),同样的处理方式。

怎么一股机翻的味道

然后我们就搞定这道题了

时间复杂度 \(O(Qlb)\)

其中 \(l\) 表示最大位数, \(b\) 表示进制,这题里为 \(10\)

代码:(非dfs写法)

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int a,b,mi[25],cnt1[25],cnt2[25],num[25],f[25];void clear(){    memset(cnt1,0,sizeof(cnt1));    memset(cnt2,0,sizeof(cnt2));}void solve(int x,int *cnt){    int len=0;    memset(num,0,sizeof(num));    while(x)    {        num[++len]=x%10;        x/=10;    }    for(int i=len; i>=1; i--)    {        for(int j=0; j<=9; j++)            cnt[j]+=f[i-1]*num[i];        for(int j=0; j<num[i]; j++)            cnt[j]+=mi[i-1];        int res=0;        for(int j=i-1; j>=1; j--)        {            res = res * 10 + num[j];        }        cnt[num[i]]+=res+1;        cnt[0]-=mi[i-1];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    mi[0]=1;    for(int i=1; i<=18; i++)    {        f[i]=f[i-1]*10+mi[i-1];        mi[i]=10*mi[i-1];    }    while(cin >> a >> b)    {        if(!a&&!b)return 0;        if(a>b)swap(a,b);        clear();        solve(a-1,cnt1);solve(b,cnt2);        for(int i=0; i<=9; i++)            cout << cnt2[i]-cnt1[i] << " \n"[i==9];    }    return 0;}
]]>
@@ -3808,7 +3808,7 @@ /2022/07/03/luo-gu-p4047-jsoi2010-bu-luo-hua-fen-ti-jie/ - 洛谷P4047 [JSOI2010]部落划分 题解

题目链接:P4047 [JSOI2010]部落划分

题意

聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落之间则经常发生争斗。只是,这一切都成为谜团了——聪聪根本就不知道部落究竟是如何分布的。

不过好消息是,聪聪得到了一份荒岛的地图。地图上标注了 $n$ 个野人居住的地点(可以看作是平面上的坐标)。我们知道,同一个部落的野人总是生活在附近。我们把两个部落的距离,定义为部落中距离最近的那两个居住点的距离。聪聪还获得了一个有意义的信息——这些野人总共被分为了 $k$ 个部落!这真是个好消息。聪聪希望从这些信息里挖掘出所有部落的详细信息。他正在尝试这样一种算法:

对于任意一种部落划分的方法,都能够求出两个部落之间的距离,聪聪希望求出一种部落划分的方法,使靠得最近的两个部落尽可能远离。

例如,下面的左图表示了一个好的划分,而右图则不是。请你编程帮助聪聪解决这个难题。

对于 $100\%$ 的数据,保证 $2 \leq k \leq n \leq 10^3$,$0 \leq x, y \leq 10^4$。

最小距离最大,一眼二分

但是这里给出一个kruskal的解法

我们先把 $O(n^2)$ 条边建出来

然后找到最小生成树

贪心地把最小生成树划分为 $k$ 个连通块

具体的,我们只要依次合并树上最小边

最后一共合并掉 $(n-1)-(k-1) = n-k$ 条边

所以答案就是第 $n-k+1$ 条边的边权

口糊一个正确性证明(可能不是很严谨)

考虑反证法

设最小生成树上的一条边 $w_1(u,v)$ ,将其替换为 $w_2(u,v^{\prime})$ ($w_2>w_1$)

  • 如果 $w_1$ 会被合并而我们合并了 $w_2$ ,好像看不出什么变化

  • 如果 $w_1$ 会被合并而我们没有合并 $w_2$ ,

    那么我们一定合并了一条比 $w_1$ 大的边,而且是最小生成树上的边

    因此答案会偏大

  • 如果 $w_1$ 不会被合并而我们合并了 $w_2$ ,没有这种情况。

  • 如果 $w_1$ 不会被合并而我们没有合并 $w_2$ ,没影响。

时间复杂度 $O(n^2 \log n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)#define M (int)(1e6+15)#define pf(x) ((x)*(x))struct Edge{int u,v,w;}e[M];int n,k,f[N],x[N],y[N],c[N],pos;int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}void addEdge(int u,int v,int w){e[++pos]={u,v,w};}int dis(int i,int j){return pf(x[i]-x[j])+pf(y[i]-y[j]);}void kruskal(){    sort(e+1,e+1+pos,[](Edge a,Edge b)    {        return a.w<b.w;    });    int cnt=0;    for(int i=1; i<=pos&&cnt<n; i++)    {        int u=e[i].u,v=e[i].v;        if(find(u)!=find(v))        {            merge(u,v);            c[++cnt]=e[i].w;        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> k;    for(int i=1; i<=n; i++)        f[i]=i,cin >> x[i] >> y[i];    for(int i=1; i<=n; i++)        for(int j=1; j<i; j++)            addEdge(i,j,dis(i,j));    kruskal();    cout << fixed << setprecision(2);    cout << sqrt((double)c[n-k+1]) << '\n';    return 0;}
]]>
+ 洛谷P4047 [JSOI2010]部落划分题解

题目链接:P4047[JSOI2010]部落划分

题意

聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落之间则经常发生争斗。只是,这一切都成为谜团了——聪聪根本就不知道部落究竟是如何分布的。

不过好消息是,聪聪得到了一份荒岛的地图。地图上标注了 \(n\)个野人居住的地点(可以看作是平面上的坐标)。我们知道,同一个部落的野人总是生活在附近。我们把两个部落的距离,定义为部落中距离最近的那两个居住点的距离。聪聪还获得了一个有意义的信息——这些野人总共被分为了\(k\)个部落!这真是个好消息。聪聪希望从这些信息里挖掘出所有部落的详细信息。他正在尝试这样一种算法:

对于任意一种部落划分的方法,都能够求出两个部落之间的距离,聪聪希望求出一种部落划分的方法,使靠得最近的两个部落尽可能远离。

例如,下面的左图表示了一个好的划分,而右图则不是。请你编程帮助聪聪解决这个难题。

对于 \(100\%\) 的数据,保证 \(2 \leq k \leq n \leq 10^3\),\(0 \leq x, y \leq 10^4\)。

最小距离最大,一眼二分

但是这里给出一个kruskal的解法

我们先把 \(O(n^2)\) 条边建出来

然后找到最小生成树

贪心地把最小生成树划分为 \(k\)个连通块

具体的,我们只要依次合并树上最小边

最后一共合并掉 \((n-1)-(k-1) = n-k\)条边

所以答案就是第 \(n-k+1\)条边的边权

口糊一个正确性证明(可能不是很严谨)

考虑反证法

设最小生成树上的一条边 \(w_1(u,v)\),将其替换为 \(w_2(u,v^{\prime})\)\(w_2>w_1\)

  • 如果 \(w_1\)会被合并而我们合并了 \(w_2\),好像看不出什么变化

  • 如果 \(w_1\)会被合并而我们没有合并 \(w_2\)

    那么我们一定合并了一条比 \(w_1\)大的边,而且是最小生成树上的边

    因此答案会偏大

  • 如果 \(w_1\)不会被合并而我们合并了 \(w_2\),没有这种情况。

  • 如果 \(w_1\)不会被合并而我们没有合并 \(w_2\),没影响。

时间复杂度 \(O(n^2 \log n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)#define M (int)(1e6+15)#define pf(x) ((x)*(x))struct Edge{int u,v,w;}e[M];int n,k,f[N],x[N],y[N],c[N],pos;int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}void addEdge(int u,int v,int w){e[++pos]={u,v,w};}int dis(int i,int j){return pf(x[i]-x[j])+pf(y[i]-y[j]);}void kruskal(){    sort(e+1,e+1+pos,[](Edge a,Edge b)    {        return a.w<b.w;    });    int cnt=0;    for(int i=1; i<=pos&&cnt<n; i++)    {        int u=e[i].u,v=e[i].v;        if(find(u)!=find(v))        {            merge(u,v);            c[++cnt]=e[i].w;        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> k;    for(int i=1; i<=n; i++)        f[i]=i,cin >> x[i] >> y[i];    for(int i=1; i<=n; i++)        for(int j=1; j<i; j++)            addEdge(i,j,dis(i,j));    kruskal();    cout << fixed << setprecision(2);    cout << sqrt((double)c[n-k+1]) << '\n';    return 0;}
]]>
@@ -3835,7 +3835,7 @@ /2022/07/03/luo-gu-p5018-noip2018-pu-ji-zu-dui-cheng-er-cha-shu-ti-jie/ - 洛谷P5018 [NOIP2018 普及组] 对称二叉树 题解

题目链接:P5018 [NOIP2018 普及组] 对称二叉树

题意

一棵有点权的有根树如果满足以下条件,则被轩轩称为对称二叉树:

  1. 二叉树;
  2. 将这棵树所有节点的左右子树交换,新树和原树对应位置的结构相同且点权相等。

下图中节点内的数字为权值,节点外的 $id$ 表示节点编号。

现在给出一棵二叉树,希望你找出它的一棵子树,该子树为对称二叉树,且节点数 最多。请输出这棵子树的节点数。

注意:只有树根的树也是对称二叉树。本题中约定,以节点 $T$ 为子树根的一棵“子 树”指的是:节点$T$ 和它的全部后代节点构成的二叉树。

【数据规模与约定】
共 $25$ 个测试点。
$v_i ≤ 1000$。
测试点 $1 \sim 3, n ≤ 10$,保证根结点的左子树的所有节点都没有右孩子,根结点的右 子树的所有节点都没有左孩子。
测试点 $4 \sim 8, n ≤ 10$。
测试点 $9 \sim 12, n ≤ 10^5$,保证输入是一棵“满二叉树” 。
测试点 $13 \sim 16, n ≤ 10^5$,保证输入是一棵“完全二叉树”。
测试点 $17 \sim 20, n ≤ 10^5$,保证输入的树的点权均为 $1$。
测试点 $21 \sim 25, n ≤ 10^6$。

对于每个点直接暴力dfs它的左右子树判断即可

注意对称的方向不要写错了

时间复杂度 $O(n \log n)$

为什么链不会被卡呢,因为中间判断的时候已经相当于剪枝掉了

只有完全二叉树才会到 $O(n \log n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n,ch[N][2],val[N],sz[N];#define ls(x) ch[x][0]#define rs(x) ch[x][1]void dfs(int u){    sz[u]=1;    if(ls(u)!=-1)    {        dfs(ls(u));        sz[u]+=sz[ls(u)];    }    if(rs(u)!=-1)    {        dfs(rs(u));        sz[u]+=sz[rs(u)];    }}bool solve(int u,int v){    if(u==-1&&v==-1)return 1;    if(u==-1||v==-1)return 0;    return val[u]==val[v]&&solve(ls(u),rs(v))&&solve(rs(u),ls(v));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> val[i];    for(int i=1; i<=n; i++)        cin >> ls(i) >> rs(i);    dfs(1);    int ans=0;    for(int i=1; i<=n; i++)        if(solve(ls(i),rs(i)))            ans=max(ans,sz[i]);    cout << ans << '\n';    return 0;}
]]>
+ 洛谷P5018 [NOIP2018普及组] 对称二叉树 题解

题目链接:P5018[NOIP2018 普及组] 对称二叉树

题意

一棵有点权的有根树如果满足以下条件,则被轩轩称为对称二叉树:

  1. 二叉树;
  2. 将这棵树所有节点的左右子树交换,新树和原树对应位置的结构相同且点权相等。

下图中节点内的数字为权值,节点外的 \(id\) 表示节点编号。

现在给出一棵二叉树,希望你找出它的一棵子树,该子树为对称二叉树,且节点数最多。请输出这棵子树的节点数。

注意:只有树根的树也是对称二叉树。本题中约定,以节点 \(T\) 为子树根的一棵“子 树”指的是:节点\(T\) 和它的全部后代节点构成的二叉树。

【数据规模与约定】 共 \(25\)个测试点。 \(v_i ≤ 1000\)。 测试点\(1 \sim 3, n ≤10\),保证根结点的左子树的所有节点都没有右孩子,根结点的右子树的所有节点都没有左孩子。 测试点 \(4 \sim8, n ≤ 10\)
测试点 \(9 \sim 12, n ≤10^5\),保证输入是一棵“满二叉树” 。
测试点 \(13 \sim 16, n ≤10^5\),保证输入是一棵“完全二叉树”。
测试点 \(17 \sim 20, n ≤10^5\),保证输入的树的点权均为 \(1\)。
测试点 \(21 \sim 25, n ≤ 10^6\)

对于每个点直接暴力dfs它的左右子树判断即可

注意对称的方向不要写错了

时间复杂度 \(O(n \log n)\)

为什么链不会被卡呢,因为中间判断的时候已经相当于剪枝掉了

只有完全二叉树才会到 \(O(n \logn)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n,ch[N][2],val[N],sz[N];#define ls(x) ch[x][0]#define rs(x) ch[x][1]void dfs(int u){    sz[u]=1;    if(ls(u)!=-1)    {        dfs(ls(u));        sz[u]+=sz[ls(u)];    }    if(rs(u)!=-1)    {        dfs(rs(u));        sz[u]+=sz[rs(u)];    }}bool solve(int u,int v){    if(u==-1&&v==-1)return 1;    if(u==-1||v==-1)return 0;    return val[u]==val[v]&&solve(ls(u),rs(v))&&solve(rs(u),ls(v));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> val[i];    for(int i=1; i<=n; i++)        cin >> ls(i) >> rs(i);    dfs(1);    int ans=0;    for(int i=1; i<=n; i++)        if(solve(ls(i),rs(i)))            ans=max(ans,sz[i]);    cout << ans << '\n';    return 0;}
]]>
@@ -3862,7 +3862,7 @@ /2022/07/03/luo-gu-p5536-xr-3-he-xin-cheng-shi-ti-jie/ - 洛谷P5536 【XR-3】核心城市 题解

题目链接:P5536 【XR-3】核心城市

题意

X 国有 $n$ 座城市,$n - 1$ 条长度为 $1$ 的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和道路形成了一棵树。

X 国国王决定将 $k$ 座城市钦定为 X 国的核心城市,这 $k$ 座城市需满足以下两个条件:

  1. 这 $k$ 座城市可以通过道路,在不经过其他城市的情况下两两相互到达。
  2. 定义某个非核心城市与这 $k$ 座核心城市的距离为,这座城市与 $k$ 座核心城市的距离的最小值。那么所有非核心城市中,与核心城市的距离最大的城市,其与核心城市的距离最小。你需要求出这个最小值。

数据范围:

  • $1 \le k < n \le 10 ^ 5$。
  • $1 \le u,v \le n, u \ne v$,保证城市与道路形成一棵树。

不要被树这个东西坑了,其实和树关系不大

我们感性地分析一下,这些核心城市一定是在比较中间的位置

然后这些核心城市到所有边缘结点的距离是相差不超过 $1$ 的

那么我们是不是可以把这个树从外面一圈开始消

然后一个个点的删,最后剩下 $k$ 个点,就是核心城市

这是什么?拓扑排序对吧

时间复杂度 $O(n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)queue<int> q;struct Edge{int u,v,next;}e[N<<1];int n,k,in[N],head[N],pos=1,ans=0,dep[N],vis[N];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;++in[v];}void topo(){    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(--in[v]!=1)continue;            if(!vis[v])            {                vis[v]=1;                dep[v]=dep[u]+1;                ans=max(ans,dep[v]);                q.push(v);--k;                if(k<1)return;            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> k; k=n-k; // 转化一下意义    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        if(in[i]==1&&k>=1)        {            q.push(i);vis[i]=1;            --k;ans=1;dep[i]=1;        }    if(k)topo();    cout << ans << '\n';    return 0;}
]]>
+ 洛谷P5536 【XR-3】核心城市题解

题目链接:P5536【XR-3】核心城市

题意

X 国有 \(n\) 座城市,\(n - 1\) 条长度为 \(1\)的道路,每条道路连接两座城市,且任意两座城市都能通过若干条道路相互到达,显然,城市和道路形成了一棵树。

X 国国王决定将 \(k\) 座城市钦定为 X国的核心城市,这 \(k\)座城市需满足以下两个条件:

  1. \(k\)座城市可以通过道路,在不经过其他城市的情况下两两相互到达。
  2. 定义某个非核心城市与这 \(k\)座核心城市的距离为,这座城市与 \(k\)座核心城市的距离的最小值。那么所有非核心城市中,与核心城市的距离最大的城市,其与核心城市的距离最小。你需要求出这个最小值。

数据范围:

  • \(1 \le k < n \le 10 ^5\)
  • \(1 \le u,v \le n, u \nev\),保证城市与道路形成一棵树。

不要被树这个东西坑了,其实和树关系不大

我们感性地分析一下,这些核心城市一定是在比较中间的位置

然后这些核心城市到所有边缘结点的距离是相差不超过 \(1\) 的

那么我们是不是可以把这个树从外面一圈开始消

然后一个个点的删,最后剩下 \(k\)个点,就是核心城市

这是什么?拓扑排序对吧

时间复杂度 \(O(n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)queue<int> q;struct Edge{int u,v,next;}e[N<<1];int n,k,in[N],head[N],pos=1,ans=0,dep[N],vis[N];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;++in[v];}void topo(){    while(!q.empty())    {        int u=q.front();q.pop();        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(--in[v]!=1)continue;            if(!vis[v])            {                vis[v]=1;                dep[v]=dep[u]+1;                ans=max(ans,dep[v]);                q.push(v);--k;                if(k<1)return;            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> k; k=n-k; // 转化一下意义    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        if(in[i]==1&&k>=1)        {            q.push(i);vis[i]=1;            --k;ans=1;dep[i]=1;        }    if(k)topo();    cout << ans << '\n';    return 0;}
]]>
@@ -3889,7 +3889,7 @@ /2022/07/02/luo-gu-p5194-usaco05dec-scales-s-ti-jie/ - 洛谷P5194 [USACO05DEC]Scales S 题解

题目链接:P5194 [USACO05DEC]Scales S

题意

约翰有一架用来称牛的体重的天平。与之配套的是 $N$ ( $1 \leq N \leq 1000$ )个已知质量的砝码(所有砝码质量的数值都在32位带符号整数范围内)。

每次称牛时,他都把某头奶牛安置在天平的某一边,然后往天平另一边加砝码,直到天平平衡,于是此时砝码的总质量就是牛的质量(约翰不能把砝码放到奶牛的那边,因为奶牛不喜欢称体重,每当约翰把砝码放到她的蹄子底下,她就会尝试把砝码踢到约翰脸上)。

天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于 $C$ ( $1 \leq C \leq 2^{30}$ )时,天平就会被损坏。砝码按照它们质量的大小被排成一行。并且,这一行中从第3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。

约翰想知道,用他所拥有的这些砝码以及这架天平,能称出的质量最大是多少。由于天平的最大承重能力为 $C$ ,他不能把所有砝码都放到天平上。

现在约翰告诉你每个砝码的质量,以及天平能承受的最大质量,你的任务是选出一些砝码,使它们的质量和在不压坏天平的前提下是所有组合中最大的。

显然暴搜+剪枝

我们可以反向搜索,也就是先选大的

不难发现这样更容易产生更大的答案

然后乱搞搞就过了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(55)int sum[N],mx,a[N],n,c;void dfs(int now,int u){    if(u<1)    {        mx=max(mx,now);        return;    }    if(now+sum[u]<=mx) return;    if(now+a[u]<=c)dfs(now+a[u],u-1);    dfs(now,u-1);    }signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> c;    for(int i=1; i<=n; i++)    {        cin >> a[i];        if(a[i]>c){n=i-1;break;}        sum[i]=sum[i-1]+a[i];    }    dfs(0,n);    cout << mx << '\n';    return 0;}
]]>
+ 洛谷P5194 [USACO05DEC]ScalesS 题解

题目链接:P5194[USACO05DEC]Scales S

题意

约翰有一架用来称牛的体重的天平。与之配套的是 \(N\) ( \(1 \leq N\leq 1000\))个已知质量的砝码(所有砝码质量的数值都在32位带符号整数范围内)。

每次称牛时,他都把某头奶牛安置在天平的某一边,然后往天平另一边加砝码,直到天平平衡,于是此时砝码的总质量就是牛的质量(约翰不能把砝码放到奶牛的那边,因为奶牛不喜欢称体重,每当约翰把砝码放到她的蹄子底下,她就会尝试把砝码踢到约翰脸上)。

天平能承受的物体的质量不是无限的,当天平某一边物体的质量大于 \(C\) ( \(1 \leq C\leq 2^{30}\))时,天平就会被损坏。砝码按照它们质量的大小被排成一行。并且,这一行中从第3个砝码开始,每个砝码的质量至少等于前面两个砝码(也就是质量比它小的砝码中质量最大的两个)的质量的和。

约翰想知道,用他所拥有的这些砝码以及这架天平,能称出的质量最大是多少。由于天平的最大承重能力为\(C\),他不能把所有砝码都放到天平上。

现在约翰告诉你每个砝码的质量,以及天平能承受的最大质量,你的任务是选出一些砝码,使它们的质量和在不压坏天平的前提下是所有组合中最大的。

显然暴搜+剪枝

我们可以反向搜索,也就是先选大的

不难发现这样更容易产生更大的答案

然后乱搞搞就过了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(55)int sum[N],mx,a[N],n,c;void dfs(int now,int u){    if(u<1)    {        mx=max(mx,now);        return;    }    if(now+sum[u]<=mx) return;    if(now+a[u]<=c)dfs(now+a[u],u-1);    dfs(now,u-1);    }signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> c;    for(int i=1; i<=n; i++)    {        cin >> a[i];        if(a[i]>c){n=i-1;break;}        sum[i]=sum[i-1]+a[i];    }    dfs(0,n);    cout << mx << '\n';    return 0;}
]]>
@@ -3914,7 +3914,7 @@ /2022/07/02/luo-gu-p3065-usaco12dec-first-g-ti-jie/ - 洛谷P3065 [USACO12DEC]First! G 题解

题目链接:P3065 [USACO12DEC]First! G

题意

Bessie一直在研究字符串。她发现,通过改变字母表的顺序,她可以按改变后的字母表来排列字符串(字典序大小排列)。

例如,Bessie发现,对于字符串串“omm”,“moo”,“mom”和“ommnom”,她可以使用标准字母表使“mom”排在第一个(即字典序最小),她也可以使用字母表“abcdefghijklonmpqrstuvwxyz”使得“omm”排在第一个。然而,Bessie想不出任何方法(改变字母表顺序)使得“moo”或“ommnom”排在第一个。

接下来让我们通过重新排列字母表的顺序来计算输入中有哪些字符串可以排在第一个(即字典序最小),从而帮助Bessie。

要计算字符串X和字符串Y按照重新排列过的字母表顺序来排列的顺序,先找到它们第一个不同的字母X[i]与Y[i],按重排后的字母表顺序比较,若X[i]比Y[i]先,则X的字典序比Y小,即X排在Y前;若没有不同的字母,则比较X与Y长度,若X比Y短,则X的字典序比Y小,即X排在Y前。

显然trie树存字符串

对于每个字符串,我们都假设它可以成为最小字典序的字符

然后依次遍历它的每个字符,尽可能让它们成为最小的

具体的,对于字符 $u$ ,如果我们想让它比 $v$ 字典序小,

我们可以把 $u$ 向 $v$ 连一条有向边

显然无解的情况会成环,这个可以用topo来判

值得注意的是,如果一个串的子串也在输入数据中存在,那么它一定无解

时间复杂度 $O(26 \times \sum |S_i|)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e4+15)#define M (int)(3e5+15)int n,ans;string s[N];bool ok[N];struct Trie{    int tot,trie[M][26],ed[M],e[35][35],in[26];    queue<int> q;    void insert(string str)    {        int u=0;         for(int i=0; i<str.size(); i++)        {            int c=str[i]-'a';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1;    }    void topo()    {        while(!q.empty())q.pop();        for(int i=0; i<26; i++)            if(!in[i])q.push(i);        while(!q.empty())        {            int u=q.front();q.pop();            for(int c=0; c<26; c++)                if(e[u][c]&&(!(--in[c])))                    q.push(c);        }    }    bool solve(string str)    {        int u=0;        memset(e,0,sizeof(e));        memset(in,0,sizeof(in));        for(int i=0; i<str.size(); i++)        {            if(ed[u])return 0;            int c=str[i]-'a';            for(int j=0; j<26; j++)            {                if(j!=c&&trie[u][j]&&!e[c][j])                    e[c][j]=1,++in[j];            }            u=trie[u][c];        }        topo();        for(int i=0; i<26; i++)            if(in[i])return 0;        return 1;    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> s[i];        tr.insert(s[i]);    }    for(int i=1; i<=n; i++)        if(tr.solve(s[i]))        {            ++ans;            ok[i]=1;        }    cout << ans << '\n';    for(int i=1; i<=n; i++)        if(ok[i])cout << s[i] << '\n';    return 0;}
]]>
+ 洛谷P3065 [USACO12DEC]First! G题解

题目链接:P3065[USACO12DEC]First! G

题意

Bessie一直在研究字符串。她发现,通过改变字母表的顺序,她可以按改变后的字母表来排列字符串(字典序大小排列)。

例如,Bessie发现,对于字符串串“omm”,“moo”,“mom”和“ommnom”,她可以使用标准字母表使“mom”排在第一个(即字典序最小),她也可以使用字母表“abcdefghijklonmpqrstuvwxyz”使得“omm”排在第一个。然而,Bessie想不出任何方法(改变字母表顺序)使得“moo”或“ommnom”排在第一个。

接下来让我们通过重新排列字母表的顺序来计算输入中有哪些字符串可以排在第一个(即字典序最小),从而帮助Bessie。

要计算字符串X和字符串Y按照重新排列过的字母表顺序来排列的顺序,先找到它们第一个不同的字母X[i]与Y[i],按重排后的字母表顺序比较,若X[i]比Y[i]先,则X的字典序比Y小,即X排在Y前;若没有不同的字母,则比较X与Y长度,若X比Y短,则X的字典序比Y小,即X排在Y前。\[\sum \left|S_i\right| \le 3 \times 10^5\]

显然trie树存字符串

对于每个字符串,我们都假设它可以成为最小字典序的字符

然后依次遍历它的每个字符,尽可能让它们成为最小的

具体的,对于字符 \(u\),如果我们想让它比 \(v\) 字典序小,

我们可以把 \(u\)\(v\) 连一条有向边

显然无解的情况会成环,这个可以用topo来判

值得注意的是,如果一个串的子串也在输入数据中存在,那么它一定无解

时间复杂度 \(O(26 \times \sum|S_i|)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e4+15)#define M (int)(3e5+15)int n,ans;string s[N];bool ok[N];struct Trie{    int tot,trie[M][26],ed[M],e[35][35],in[26];    queue<int> q;    void insert(string str)    {        int u=0;         for(int i=0; i<str.size(); i++)        {            int c=str[i]-'a';            if(!trie[u][c])trie[u][c]=++tot;            u=trie[u][c];        }        ed[u]=1;    }    void topo()    {        while(!q.empty())q.pop();        for(int i=0; i<26; i++)            if(!in[i])q.push(i);        while(!q.empty())        {            int u=q.front();q.pop();            for(int c=0; c<26; c++)                if(e[u][c]&&(!(--in[c])))                    q.push(c);        }    }    bool solve(string str)    {        int u=0;        memset(e,0,sizeof(e));        memset(in,0,sizeof(in));        for(int i=0; i<str.size(); i++)        {            if(ed[u])return 0;            int c=str[i]-'a';            for(int j=0; j<26; j++)            {                if(j!=c&&trie[u][j]&&!e[c][j])                    e[c][j]=1,++in[j];            }            u=trie[u][c];        }        topo();        for(int i=0; i<26; i++)            if(in[i])return 0;        return 1;    }}tr;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> s[i];        tr.insert(s[i]);    }    for(int i=1; i<=n; i++)        if(tr.solve(s[i]))        {            ++ans;            ok[i]=1;        }    cout << ans << '\n';    for(int i=1; i<=n; i++)        if(ok[i])cout << s[i] << '\n';    return 0;}
]]>
@@ -3943,7 +3943,7 @@ /2022/06/26/luo-gu-p2939-usaco09feb-revamping-trails-g-ti-jie/ - 洛谷P2939 [USACO09FEB]Revamping Trails G 题解

题目链接:P2939 [USACO09FEB]Revamping Trails G

题意

约翰一共有 $N$ 个牧场.由 $M$ 条布满尘埃的小径连接。小径可以双向通行。每天早上约翰从牧场 $1$ 出发到牧场 $N$ 去给奶牛检查身体。

通过每条小径都需要消耗一定的时间。约翰打算升级其中 $K$ 条小径,使之成为高速公路。在高速公路上的通行几乎是瞬间完成的,所以高速公路的通行时间为 $0$。

请帮助约翰决定对哪些小径进行升级,使他每天从 $1$ 号牧场到第 $N$ 号牧场所花的时间最短。

$1 \le n \le 10^4,~1\le m \le 5 \times 10^4 ,~ 1\le k \le 20$

考虑建分层图跑最短路

首先建 $k+1$ 个同样的图

第 $i$ 层的结点 $u_i$ 向 第 $i+1$ 层的 $v_i$ 连边

表示已经用了 $i$ 次修改,现在是第 $i+1$ 次修改,修改的是 $(u,v)$ 这条边

然后跑一个单源最短路就好了

注意存边的数组一般要开 $M \times (K+1) \times 4$

然后有点时候可能会出现走了不到 $k$ 条边就到了的情况

所以要把每个 $in$ 和 $(i+1)n$ 连边

最后答案就是 $d_{kn+n}$

注意 $d$ 数组应该要开long long

时间复杂度 $O(kn\log km)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;// #define int long longtypedef long long ll;#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+5)#define K (int)(22)#define M (int)(5e4+5)struct Edge{int u,v,w,next;}e[M*K*4];struct node{int u; ll dis;};ll d[N*K];bool operator<(node a,node b){return a.dis>b.dis;}int n,m,k,pos=1,head[N*K],vis[N*K];priority_queue<node> q;void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dijkstra(int st){    memset(d,0x3f,sizeof(d));    d[st]=0; q.push({st,0});    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> k;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);addEdge(v,u,w);        for(int j=1; j<=k; j++)        {            addEdge(j*n+u,j*n+v,w);            addEdge(j*n+v,j*n+u,w);            addEdge((j-1)*n+u,j*n+v,0);            addEdge((j-1)*n+v,j*n+u,0);        }    }    for(int i=1; i<=k; i++)        addEdge((i-1)*n+n,i*n+n,0);    dijkstra(1);    cout << d[k*n+n] << '\n';    return 0;}
]]>
+ 洛谷P2939[USACO09FEB]Revamping Trails G 题解

题目链接:P2939[USACO09FEB]Revamping Trails G

题意

约翰一共有 \(N\) 个牧场.由 \(M\)条布满尘埃的小径连接。小径可以双向通行。每天早上约翰从牧场 \(1\) 出发到牧场 \(N\) 去给奶牛检查身体。

通过每条小径都需要消耗一定的时间。约翰打算升级其中 \(K\)条小径,使之成为高速公路。在高速公路上的通行几乎是瞬间完成的,所以高速公路的通行时间为\(0\)

请帮助约翰决定对哪些小径进行升级,使他每天从 \(1\) 号牧场到第 \(N\) 号牧场所花的时间最短。

\(1 \le n \le 10^4,~1\le m \le 5 \times10^4 ,~ 1\le k \le 20\)

考虑建分层图跑最短路

首先建 \(k+1\) 个同样的图

\(i\) 层的结点 \(u_i\) 向 第 \(i+1\) 层的 \(v_i\) 连边

表示已经用了 \(i\) 次修改,现在是第\(i+1\) 次修改,修改的是 \((u,v)\) 这条边

然后跑一个单源最短路就好了

注意存边的数组一般要开 \(M \times (K+1)\times 4\)

然后有点时候可能会出现走了不到 \(k\)条边就到了的情况

所以要把每个 \(in\)\((i+1)n\) 连边

最后答案就是 \(d_{kn+n}\)

注意 \(d\) 数组应该要开long long

时间复杂度 \(O(kn\log km)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;// #define int long longtypedef long long ll;#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+5)#define K (int)(22)#define M (int)(5e4+5)struct Edge{int u,v,w,next;}e[M*K*4];struct node{int u; ll dis;};ll d[N*K];bool operator<(node a,node b){return a.dis>b.dis;}int n,m,k,pos=1,head[N*K],vis[N*K];priority_queue<node> q;void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dijkstra(int st){    memset(d,0x3f,sizeof(d));    d[st]=0; q.push({st,0});    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> k;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);addEdge(v,u,w);        for(int j=1; j<=k; j++)        {            addEdge(j*n+u,j*n+v,w);            addEdge(j*n+v,j*n+u,w);            addEdge((j-1)*n+u,j*n+v,0);            addEdge((j-1)*n+v,j*n+u,0);        }    }    for(int i=1; i<=k; i++)        addEdge((i-1)*n+n,i*n+n,0);    dijkstra(1);    cout << d[k*n+n] << '\n';    return 0;}
]]>
@@ -3972,7 +3972,7 @@ /2022/06/26/luo-gu-p4683-ioi2008-type-printer-ti-jie/ - 洛谷P4683 [IOI2008] Type Printer 题解

题目链接:P4683 [IOI2008] Type Printer

题意

你需要利用一台可移动的打印机打印出$N$个单词。这种可移动式打印机是一种老式打印机,它需要你将一些小的金属块(每个包含一个字母)放到打印机上以组成单词。然后将这些小金属块压在一张纸上以打印出这个词。这种打印机允许你进行下列操作:

  • 在打印机当前词的末端(尾部)添加一个字母;
  • 在打印机当前词的尾部删去一个字母(将打印机当前词的最后一个字母删去)。仅当打印机当前至少有一个字母时才允许进行该操作;
  • 将打印机上的当前词打印出来。
    初始时打印机为空,或者说它不含任何带字母的金属块。打印结束时,允许有部分字母留在打印机内。同时也允许你按照任意的次序打印单词。

由于每一个操作都需要一定时间,所以需要你尽可能减少所需操作的总数目(将操作的总数最小化)。

你需要编写一个程序,给定所要打印的$N$个单词,找出以任意次序打印所有单词所需操作的最小数目,并输出一种这样的操作序列。

对于所有数据,$1\leq N\leq25000$。

显然可以用trie来做(trie上dfs)

因为最后一个输出的单词不用删除

所以我们考虑贪心地把最长的单词最后输出

具体的做法就是把这个最长单词的路径标记,先跑别的最后跑它

时间复杂度 $O(n \Sigma)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2.5e4+15)string s[N];signed trie[N*26][26];int n,tot,c,mxlen,ed[N*26],ck[N*26];void insert(int l,int id){    int u=0;    for(int i=0; i<l; i++)    {        int c=s[id][i]-'a';        if(!trie[u][c])trie[u][c]=++tot;        u=trie[u][c];    }    ed[u]=id;}void proc(int l,int id){    int u=0;    for(int i=0; i<l; i++)    {        int c=s[id][i]-'a';        ck[trie[u][c]]=1;        u=trie[u][c];    }}int num;string str;void dfs(int u){    if(ed[u])    {        ++num;        str+="P";    }    if(num==n)    {        cout << str.size() << '\n';        for(auto i : str)            cout << i << '\n';        exit(0);    }    for(int i=0; i<26; i++)    {        if(trie[u][i]&&!ck[trie[u][i]])        {            str+=char(i+'a');            dfs(trie[u][i]);            str+="-";        }    }    for(int i=0; i<26; i++)    {        if(trie[u][i]&&ck[trie[u][i]])        {            str+=char(i+'a');            dfs(trie[u][i]);            str+="-";        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,l; i<=n; i++)    {        cin >> s[i];        if(s[i].size()>mxlen)        {            mxlen=s[i].size();            c=i;        }    }    for(int i=1; i<=n; i++)    {        insert(s[i].size(),i);        if(c==i) proc(s[i].size(),i);    }    dfs(0);    return 0;}
]]>
+ 洛谷P4683 [IOI2008] TypePrinter 题解

题目链接:P4683[IOI2008] Type Printer

题意

你需要利用一台可移动的打印机打印出\(N\)个单词。这种可移动式打印机是一种老式打印机,它需要你将一些小的金属块(每个包含一个字母)放到打印机上以组成单词。然后将这些小金属块压在一张纸上以打印出这个词。这种打印机允许你进行下列操作:

  • 在打印机当前词的末端(尾部)添加一个字母;
  • 在打印机当前词的尾部删去一个字母(将打印机当前词的最后一个字母删去)。仅当打印机当前至少有一个字母时才允许进行该操作;
  • 将打印机上的当前词打印出来。初始时打印机为空,或者说它不含任何带字母的金属块。打印结束时,允许有部分字母留在打印机内。同时也允许你按照任意的次序打印单词。

由于每一个操作都需要一定时间,所以需要你尽可能减少所需操作的总数目(将操作的总数最小化)。

你需要编写一个程序,给定所要打印的\(N\)个单词,找出以任意次序打印所有单词所需操作的最小数目,并输出一种这样的操作序列。

对于所有数据,\(1\leqN\leq25000\)

显然可以用trie来做(trie上dfs)

因为最后一个输出的单词不用删除

所以我们考虑贪心地把最长的单词最后输出

具体的做法就是把这个最长单词的路径标记,先跑别的最后跑它

时间复杂度 \(O(n \Sigma)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2.5e4+15)string s[N];signed trie[N*26][26];int n,tot,c,mxlen,ed[N*26],ck[N*26];void insert(int l,int id){    int u=0;    for(int i=0; i<l; i++)    {        int c=s[id][i]-'a';        if(!trie[u][c])trie[u][c]=++tot;        u=trie[u][c];    }    ed[u]=id;}void proc(int l,int id){    int u=0;    for(int i=0; i<l; i++)    {        int c=s[id][i]-'a';        ck[trie[u][c]]=1;        u=trie[u][c];    }}int num;string str;void dfs(int u){    if(ed[u])    {        ++num;        str+="P";    }    if(num==n)    {        cout << str.size() << '\n';        for(auto i : str)            cout << i << '\n';        exit(0);    }    for(int i=0; i<26; i++)    {        if(trie[u][i]&&!ck[trie[u][i]])        {            str+=char(i+'a');            dfs(trie[u][i]);            str+="-";        }    }    for(int i=0; i<26; i++)    {        if(trie[u][i]&&ck[trie[u][i]])        {            str+=char(i+'a');            dfs(trie[u][i]);            str+="-";        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,l; i<=n; i++)    {        cin >> s[i];        if(s[i].size()>mxlen)        {            mxlen=s[i].size();            c=i;        }    }    for(int i=1; i<=n; i++)    {        insert(s[i].size(),i);        if(c==i) proc(s[i].size(),i);    }    dfs(0);    return 0;}
]]>
@@ -3999,7 +3999,7 @@ /2022/06/25/luo-gu-p3426-poi2005-sza-template-ti-jie/ - 洛谷P3426 [POI2005]SZA-Template 题解

题目链接:P3426 [POI2005]SZA-Template

题意:你打算在纸上印一串字母。

为了完成这项工作,你决定刻一个印章。印章每使用一次,就会将印章上的所有字母印到纸上。

同一个位置的相同字符可以印多次。例如:用 aba 这个印章可以完成印制 ababa 的工作(中间的 a 被印了两次)。但是,因为印上去的东西不能被抹掉,在同一位置上印不同字符是不允许的。例如:用 aba 这个印章不可以完成印制 abcba 的工作。

因为刻印章是一个不太容易的工作,你希望印章的字符串长度尽可能小。

输入一个长度不超过 $5 \times 10^5$ 的非空字符串(只包含小写字母),代表要在纸上印的字符。

因为印章可以随便印,也就是无所谓什么顺序和数量

所以我们可以考虑什么样的情况可以继续

观察样例

ababbababbabababbabababbababbabaababbaba     ababbaba            ababbaba                   ababbaba                        ababbaba

最前面的ababbababbaba十分有趣

可以发现最左侧的一定是要印一次的(废话)

而重复印刷的显然和kmp的border有关

注:对字符串 $s$ 和 $0 \le r < |s|$ ,

若 $s$ 长度为 $r$ 的前缀和长度为 $r$ 的后缀相等,就称 $s$ 长度为 $r$ 的前缀是 $s$ 的 border。

摘自oi-wiki

考虑先求出border,然后用dp来推

设 $f_i$ 表示印刷 $s$ 的第 $i$ 个前缀的最小印章长度

上界为 $f_i=i$

这里 $f_i$ 在一定条件下是可以从 $\text{fail}_i$ 转移的

第一个看上去复杂,

其实就是 ababbababbaba 这样border有重叠的情况

用个桶维护即可,然后转移一下就好了

时间复杂度 $O(n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)char s[N];int n,fail[N],f[N],h[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (s+1); n=strlen(s+1);    for(int i=2,j=0; i<=n; i++)    {        while(j&&s[i]!=s[j+1])j=fail[j];        if(s[i]==s[j+1])++j;        fail[i]=j;    }    f[1]=1;h[1]=1;    for(int i=2; i<=n; i++)    {        f[i]=i;        if(h[f[fail[i]]]>=i-fail[i])            f[i]=f[fail[i]];        h[f[i]]=i;    }    cout << f[n] << '\n';    return 0;}
]]>
+ 洛谷P3426[POI2005]SZA-Template 题解

题目链接:P3426[POI2005]SZA-Template

题意:你打算在纸上印一串字母。

为了完成这项工作,你决定刻一个印章。印章每使用一次,就会将印章上的所有字母印到纸上。

同一个位置的相同字符可以印多次。例如:用 aba这个印章可以完成印制 ababa 的工作(中间的 a被印了两次)。但是,因为印上去的东西不能被抹掉,在同一位置上印不同字符是不允许的。例如:用aba 这个印章不可以完成印制 abcba 的工作。

因为刻印章是一个不太容易的工作,你希望印章的字符串长度尽可能小。

输入一个长度不超过 \(5 \times 10^5\)的非空字符串(只包含小写字母),代表要在纸上印的字符。

因为印章可以随便印,也就是无所谓什么顺序和数量

所以我们可以考虑什么样的情况可以继续

观察样例

ababbababbabababbabababbababbabaababbaba     ababbaba            ababbaba                   ababbaba                        ababbaba

最前面的ababbababbaba十分有趣

可以发现最左侧的一定是要印一次的(废话)

而重复印刷的显然和kmp的border有关

注:对字符串 \(s\)\(0 \le r < |s|\) ,

\(s\) 长度为 \(r\) 的前缀和长度为 \(r\) 的后缀相等,就称 \(s\) 长度为 \(r\) 的前缀是 \(s\) 的 border。

摘自oi-wiki

考虑先求出border,然后用dp来推

\(f_i\) 表示印刷 \(s\) 的第 \(i\) 个前缀的最小印章长度

上界为 \(f_i=i\)

这里 \(f_i\) 在一定条件下是可以从\(\text{fail}_i\) 转移的 \[f_i = \begin{cases}f_{\text{fail}_i},& \exists \, j \ge i-\text{fail}_i,f_j =f_{\text{fail}_i},\\\\i,&\text{default.}\end{cases}\] 第一个看上去复杂,

其实就是 ababbababbaba 这样border有重叠的情况

用个桶维护即可,然后转移一下就好了

时间复杂度 \(O(n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)char s[N];int n,fail[N],f[N],h[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (s+1); n=strlen(s+1);    for(int i=2,j=0; i<=n; i++)    {        while(j&&s[i]!=s[j+1])j=fail[j];        if(s[i]==s[j+1])++j;        fail[i]=j;    }    f[1]=1;h[1]=1;    for(int i=2; i<=n; i++)    {        f[i]=i;        if(h[f[fail[i]]]>=i-fail[i])            f[i]=f[fail[i]];        h[f[i]]=i;    }    cout << f[n] << '\n';    return 0;}
]]>
@@ -4028,7 +4028,7 @@ /2022/06/21/luo-gu-p2466-sdoi2008-sue-de-xiao-qiu-ti-jie/ - 洛谷P2466 [SDOI2008] Sue 的小球 题解

题目链接:P2466 [SDOI2008] Sue 的小球

题意

Sue 和 Sandy 最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue 有一支轻便小巧的小船。然而,Sue 的目标并不是当一个海盗,而是要收集空中漂浮的彩蛋,Sue 有一个秘密武器,只要她将小船划到一个彩蛋的正下方,然后使用秘密武器便可以在瞬间收集到这个彩蛋。然而,彩蛋有一个魅力值,这个魅力值会随着彩蛋在空中降落的时间而降低,Sue 要想得到更多的分数,必须尽量在魅力值高的时候收集这个彩蛋,而如果一个彩蛋掉入海中,它的魅力值将会变成一个负数,但这并不影响 Sue 的兴趣,因为每一个彩蛋都是不同的,Sue 希望收集到所有的彩蛋。

然而 Sandy 就没有 Sue 那么浪漫了,Sandy 希望得到尽可能多的分数,为了解决这个问题,他先将这个游戏抽象成了如下模型:

将大海近似的看做 $x$ 轴,以 Sue 所在的初始位置作为坐标原点建立一个竖直的平面直角坐标系。

一开始空中有 $N$ 个彩蛋,对于第 $i$ 个彩蛋,他的初始位置用整数坐标 $(x_{i}, y_{i})$ 表示,游戏开始后,它匀速沿 $y$ 轴负方向下落,速度为 $v_{i}$ 单位距离/单位时间。Sue 的初始位置为 $(x_{0}, 0)$,Sue 可以沿 $x$ 轴的正方向或负方向移动,Sue 的移动速度是 $1$ 单位距离/单位时间,使用秘密武器得到一个彩蛋是瞬间的,得分为当前彩蛋的 $y$ 坐标的千分之一。

现在,Sue 和 Sandy 请你来帮忙,为了满足 Sue 和 Sandy 各自的目标,你决定在收集到所有彩蛋的基础上,得到的分数最高。

对于 $100\%$ 的数据,$-10^4 \leq x_{i},y_{i},v_{i} \leq 10^4$,$N \leq 1000$

首先先将彩蛋按 $x$ 排序

因为可以左右走,可以想到这是一个区间dp

设 $f_{0/1,i,j}$ 表示区间 $[i,j]$ 的彩蛋都被收集后的最小花费(代价),0/1表示此时在 $i$ 还是 $j$

转移方程?

注意到当前的决策会受到之前决策的影响,也就是当前的时刻我们并不清楚

考虑增加一维?那就是暴力解法了

那怎么处理这个时刻的问题呢?

我们转化一下刚才的问题,

“当前的决策会受到之前决策的影响”

换句话说,就是当前的决策会影响之后的决策

也就是,现在的时刻会影响之后转移时的物品价值

考虑每次决策时,就把之后的代价先算进去

设 $w_{i,j}$ 表示区间 $[i,j]$ 对之后决策造成的影响(代价),不难发现

显然可以前缀和优化

当然最重要的是,我们可以很容易地推出转移方程了

其中 $S_k = \sum_{i=1}^{k}v_k$

时间复杂度 $O(n^2)$

答案就是 $\sum y_i -\min\left\{f_{0,1,n},f_{1,1,n}\right\}$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,x0,f0[N][N],f1[N][N];struct node{int x,y,v;}a[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> x0;    for(int i=1; i<=n; i++) cin >> a[i].x;    for(int i=1; i<=n; i++) cin >> a[i].y;    for(int i=1; i<=n; i++) cin >> a[i].v;    a[++n].x=x0;    sort(a+1,a+1+n,[](node a,node b){return a.x<b.x;});    memset(f0,0x3f,sizeof(f0));memset(f1,0x3f,sizeof(f1));    for(int i=1; i<=n; i++)        if(a[i].x==x0){f0[i][i]=f1[i][i]=0;}    for(int i=1; i<=n; i++) a[i].v+=a[i-1].v;    for(int len=2; len<=n; len++)        for(int i=1,j=i+len-1; j<=n; i++,j++)        {            f0[i][j]=min(f0[i+1][j]+(a[i+1].x-a[i].x)*(a[i].v+a[n].v-a[j].v),                         f1[i+1][j]+(a[j].x-a[i].x)*(a[i].v+a[n].v-a[j].v));            f1[i][j]=min(f0[i][j-1]+(a[j].x-a[i].x)*(a[i-1].v+a[n].v-a[j-1].v),                         f1[i][j-1]+(a[j].x-a[j-1].x)*(a[i-1].v+a[n].v-a[j-1].v));        }    int sum=0;    for(int i=1; i<=n; i++) sum+=a[i].y;    cout << fixed << setprecision(3);    cout << ((sum-min(f0[1][n],f1[1][n]))/1000.0) << '\n';    return 0;}

参考文献

[1] https://www.luogu.com.cn/blog/Bartholomew/solution-p2466

]]>
+ 洛谷P2466 [SDOI2008] Sue的小球 题解

 题目链接:P2466[SDOI2008] Sue 的小球

题意

Sue 和 Sandy最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue有一支轻便小巧的小船。然而,Sue的目标并不是当一个海盗,而是要收集空中漂浮的彩蛋,Sue有一个秘密武器,只要她将小船划到一个彩蛋的正下方,然后使用秘密武器便可以在瞬间收集到这个彩蛋。然而,彩蛋有一个魅力值,这个魅力值会随着彩蛋在空中降落的时间而降低,Sue要想得到更多的分数,必须尽量在魅力值高的时候收集这个彩蛋,而如果一个彩蛋掉入海中,它的魅力值将会变成一个负数,但这并不影响Sue 的兴趣,因为每一个彩蛋都是不同的,Sue 希望收集到所有的彩蛋。

然而 Sandy 就没有 Sue 那么浪漫了,Sandy希望得到尽可能多的分数,为了解决这个问题,他先将这个游戏抽象成了如下模型:

将大海近似的看做 \(x\) 轴,以 Sue所在的初始位置作为坐标原点建立一个竖直的平面直角坐标系。

一开始空中有 \(N\) 个彩蛋,对于第\(i\) 个彩蛋,他的初始位置用整数坐标\((x_{i}, y_{i})\)表示,游戏开始后,它匀速沿 \(y\)轴负方向下落,速度为 \(v_{i}\)单位距离/单位时间。Sue 的初始位置为 \((x_{0},0)\),Sue 可以沿 \(x\)轴的正方向或负方向移动,Sue 的移动速度是 \(1\)单位距离/单位时间,使用秘密武器得到一个彩蛋是瞬间的,得分为当前彩蛋的\(y\) 坐标的千分之一。

现在,Sue 和 Sandy 请你来帮忙,为了满足 Sue 和 Sandy各自的目标,你决定在收集到所有彩蛋的基础上,得到的分数最高。

对于 \(100\%\) 的数据,\(-10^4 \leq x_{i},y_{i},v_{i} \leq10^4\),\(N \leq 1000\)

首先先将彩蛋按 \(x\) 排序

因为可以左右走,可以想到这是一个区间dp

\(f_{0/1,i,j}\) 表示区间 \([i,j]\)的彩蛋都被收集后的最小花费(代价),0/1表示此时在 \(i\) 还是 \(j\)

转移方程?

注意到当前的决策会受到之前决策的影响,也就是当前的时刻我们并不清楚

考虑增加一维?那就是暴力解法了

那怎么处理这个时刻的问题呢?

我们转化一下刚才的问题,

"当前的决策会受到之前决策的影响"

换句话说,就是当前的决策会影响之后的决策

也就是,现在的时刻会影响之后转移时的物品价值

考虑每次决策时,就把之后的代价先算进去

\(w_{i,j}\) 表示区间 \([i,j]\)对之后决策造成的影响(代价),不难发现 \[w_{i,j} = \sum_{i=0}^{n}v_i - \sum_{k=i}^{j}v_k\] 显然可以前缀和优化

当然最重要的是,我们可以很容易地推出转移方程了 \[f_{0,i,j}=\min\left\{f_{0,i+1,j}+(x_{i+1}-x_i)\times(S_i+S_n-S_j),f_{1,i+1,j}+(x_j-x_i)\times(S_i+S_n-S_j)\right\}\\f_{1,i,j}=\min\left\{f_{0,i,j-1}+(x_j-x_i)\times(S_{i-1}+S_n-S_{j-1}), f_{1,i,j-1}+(x_j-x_{j-1})\times(S_{i-1}+S_n-S-{j-1})\right\}\] 其中 \(S_k =\sum_{i=1}^{k}v_k\)

时间复杂度 \(O(n^2)\)

答案就是 \(\sum y_i-\min\left\{f_{0,1,n},f_{1,1,n}\right\}\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,x0,f0[N][N],f1[N][N];struct node{int x,y,v;}a[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> x0;    for(int i=1; i<=n; i++) cin >> a[i].x;    for(int i=1; i<=n; i++) cin >> a[i].y;    for(int i=1; i<=n; i++) cin >> a[i].v;    a[++n].x=x0;    sort(a+1,a+1+n,[](node a,node b){return a.x<b.x;});    memset(f0,0x3f,sizeof(f0));memset(f1,0x3f,sizeof(f1));    for(int i=1; i<=n; i++)        if(a[i].x==x0){f0[i][i]=f1[i][i]=0;}    for(int i=1; i<=n; i++) a[i].v+=a[i-1].v;    for(int len=2; len<=n; len++)        for(int i=1,j=i+len-1; j<=n; i++,j++)        {            f0[i][j]=min(f0[i+1][j]+(a[i+1].x-a[i].x)*(a[i].v+a[n].v-a[j].v),                         f1[i+1][j]+(a[j].x-a[i].x)*(a[i].v+a[n].v-a[j].v));            f1[i][j]=min(f0[i][j-1]+(a[j].x-a[i].x)*(a[i-1].v+a[n].v-a[j-1].v),                         f1[i][j-1]+(a[j].x-a[j-1].x)*(a[i-1].v+a[n].v-a[j-1].v));        }    int sum=0;    for(int i=1; i<=n; i++) sum+=a[i].y;    cout << fixed << setprecision(3);    cout << ((sum-min(f0[1][n],f1[1][n]))/1000.0) << '\n';    return 0;}

参考文献

[1] https://www.luogu.com.cn/blog/Bartholomew/solution-p2466

]]>
@@ -4055,7 +4055,7 @@ /2022/06/21/luo-gu-p1608-lu-jing-tong-ji-ti-jie/ - 洛谷P1608 路径统计 题解

题目链接:P1608 路径统计

题意

一句话题意:最短路计数。

“RP 餐厅” 的员工素质就是不一般,在齐刷刷的算出同一个电话号码之后,就准备让 HZH,TZY 去送快餐了,他们将自己居住的城市画了一张地图,已知在他们的地图上,有 $N$ 个地方,而且他们目前处在标注为 “1” 的小镇上,而送餐的地点在标注为 “N” 的小镇。(有点废话)除此之外还知道这些道路都是单向的,从小镇 $I$ 到 $J$ 需要花费 $D[I, J]$ 的时间,为了更高效快捷的将快餐送到顾客手中,他们想走一条从小镇 $1$ 到小镇 $N$ 花费最少的一条路,但是他们临出发前,撞到因为在路上堵车而生气的 FYY,深受启发,不能仅知道一条路线,万一。。。于是,他们邀请 FYY 一起来研究起了下一个问题:这个最少花费的路径有多少条?

对于 $100\%$ 的数据 $1\leq N\leq 2000$,$0\leq E\leq N\times (N-1)$,$1\leq C\leq 10$.

最短路计数据说不能用spfa(spfa已死

设 $f_u$ 表示从起点到 $u$ 结点的最短路数量

显然用dijkstra刷表

若 $(u,v)$ 松弛成功,也就是

d[v]>d[u]+w

我们就让f[v]=f[u]

否则如果

d[v]==d[u]+w

就让f[v]+=f[u]

没了,就这么水

时间复杂度 $O(n \log m)$ ,这里我写的邻接矩阵所以 $m \approx n^2$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m,d[N],vis[N],f[N],g[N][N];struct node{    int u,dis;    bool operator<(const node &o)const        {return dis>o.dis;}};priority_queue<node> q;void dijkstra(int st){    memset(d,0x3f,sizeof(d));    d[st]=0;f[st]=1;    q.push({st,0});    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])continue;        vis[u]=1;        for(int v=1,w; v<=n; v++)        {            if((w=g[u][v])>=INF)continue;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                f[v]=f[u];                q.push({v,d[v]});            }else if(d[v]==d[u]+w)f[v]+=f[u];        }    }}   signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            g[i][j]=INF;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        g[u][v]=min(g[u][v],w);    }        dijkstra(1);    if(d[n]>=INF)cout << "No answer\n";    else cout << d[n] << " " << f[n];    return 0;}
]]>
+ 洛谷P1608 路径统计 题解

题目链接:P1608路径统计

题意

一句话题意:最短路计数。

“RP 餐厅”的员工素质就是不一般,在齐刷刷的算出同一个电话号码之后,就准备让 HZH,TZY去送快餐了,他们将自己居住的城市画了一张地图,已知在他们的地图上,有\(N\) 个地方,而且他们目前处在标注为“1” 的小镇上,而送餐的地点在标注为 “N”的小镇。(有点废话)除此之外还知道这些道路都是单向的,从小镇 \(I\) 到 \(J\) 需要花费 \(D[I, J]\)的时间,为了更高效快捷的将快餐送到顾客手中,他们想走一条从小镇 \(1\) 到小镇 \(N\)花费最少的一条路,但是他们临出发前,撞到因为在路上堵车而生气的FYY,深受启发,不能仅知道一条路线,万一。。。于是,他们邀请 FYY一起来研究起了下一个问题:这个最少花费的路径有多少条?

对于 \(100\%\) 的数据 \(1\leq N\leq 2000\),\(0\leq E\leq N\times (N-1)\),\(1\leq C\leq 10\).

最短路计数据说不能用spfa(spfa已死

\(f_u\) 表示从起点到 \(u\) 结点的最短路数量

显然用dijkstra刷表

\((u,v)\) 松弛成功,也就是

d[v]>d[u]+w

我们就让f[v]=f[u]

否则如果

d[v]==d[u]+w

就让f[v]+=f[u]

没了,就这么水

时间复杂度 \(O(n \log m)\),这里我写的邻接矩阵所以 \(m \approxn^2\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m,d[N],vis[N],f[N],g[N][N];struct node{    int u,dis;    bool operator<(const node &o)const        {return dis>o.dis;}};priority_queue<node> q;void dijkstra(int st){    memset(d,0x3f,sizeof(d));    d[st]=0;f[st]=1;    q.push({st,0});    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])continue;        vis[u]=1;        for(int v=1,w; v<=n; v++)        {            if((w=g[u][v])>=INF)continue;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                f[v]=f[u];                q.push({v,d[v]});            }else if(d[v]==d[u]+w)f[v]+=f[u];        }    }}   signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            g[i][j]=INF;    for(int i=1,u,v,w; i<=m; i++)    {        cin >> u >> v >> w;        g[u][v]=min(g[u][v],w);    }        dijkstra(1);    if(d[n]>=INF)cout << "No answer\n";    else cout << d[n] << " " << f[n];    return 0;}
]]>
@@ -4080,7 +4080,7 @@ /2022/06/21/luo-gu-p5440-xr-2-qi-ji-ti-jie/ - 洛谷P5440 【XR-2】奇迹 题解

题目链接:P5440 【XR-2】奇迹

题意

我们称一个日期为一个八位数,第 1~4 位构成年,第 5~6 位构成月,第 7~8 位构成日,不足位数用 0 补足。同时,要求日期所代表的这一天真实存在,且年的范围为 1~9999。

出现奇迹的日期都存在相同的特点:由“日”组成的两位数,由“月+日”组成的四位数,由“年+月+日”组成的八位数均为质数。但并不是所有存在这样特点的日期都一定会出现奇迹。

现在,你得到了一个可能会出现奇迹的日期,然而不幸的是这个日期却是残缺的,八位中可能有若干位无法确定。你需要知道这个日期有多少种可能,这样你才能做好充足的准备去迎接奇迹的到来。

对 $100\%$ 的数据保证 $1 \le T \le 10$。

一个朴素的解法:

预处理所有合法日期,

然后对于每个询问暴力枚举所有合法日期

常数巨大,但是写起来不容易错

其实我也想了很多剪枝方法不过懒得写

代码是学的这篇题解 ,感觉十分简洁

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()const int p[] = {0,3,5,7,11,13,17,19,23,29,31,37};const int d[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};int Q,a[105],t,ans[1000015],tot;char s[15];bool ck(int n){    if(n<3)return n==2;    for(int i=2; i<=n/i; i++)        if(n%i==0)return 0;    return 1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    for(int i=1; i<=12; i++)        for(int j=1; p[j]<=d[i]; j++)            if(ck(i*100+p[j]))                a[++t]=i*100+p[j];    for(int i=4; i<=9999; i+=4)        if((i%100||!(i%400))&&ck(i*10000+229))            ans[++tot]=i*10000+229;    for(int i=1; i<=9999; i++)        for(int j=1; j<=t; j++)            if(ck(i*10000+a[j]))                ans[++tot]=i*10000+a[j];    cin >> Q;    while(Q--)    {        cin >> (s+1);        int res=0,c=1;        for(int i=1; i<=8; i++)            if(s[i]!='-')c=0;        if(c){cout << "55157\n";continue;}        for(int i=1; i<=tot; i++)        {            int now=ans[i],ok=1;            for(int j=8; j>=1; j--,now/=10)                if(s[j]!='-'&&s[j]-'0'!=now%10)                    {ok=0;break;}            res+=ok;        }        cout << res << '\n';    }    return 0;}
]]>
+ 洛谷P5440 【XR-2】奇迹 题解

题目链接:P5440【XR-2】奇迹

题意

我们称一个日期为一个八位数,第 1~4 位构成年,第 5~6位构成月,第 7~8 位构成日,不足位数用 0补足。同时,要求日期所代表的这一天真实存在,且年的范围为 1~9999。

出现奇迹的日期都存在相同的特点:由“日”组成的两位数,由“月+日”组成的四位数,由“年+月+日”组成的八位数均为质数。但并不是所有存在这样特点的日期都一定会出现奇迹。

现在,你得到了一个可能会出现奇迹的日期,然而不幸的是这个日期却是残缺的,八位中可能有若干位无法确定。你需要知道这个日期有多少种可能,这样你才能做好充足的准备去迎接奇迹的到来。

\(100\%\) 的数据保证 \(1 \le T \le 10\)。

一个朴素的解法:

预处理所有合法日期,

然后对于每个询问暴力枚举所有合法日期

常数巨大,但是写起来不容易错

其实我也想了很多剪枝方法不过懒得写

代码是学的这篇题解,感觉十分简洁

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()const int p[] = {0,3,5,7,11,13,17,19,23,29,31,37};const int d[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};int Q,a[105],t,ans[1000015],tot;char s[15];bool ck(int n){    if(n<3)return n==2;    for(int i=2; i<=n/i; i++)        if(n%i==0)return 0;    return 1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    for(int i=1; i<=12; i++)        for(int j=1; p[j]<=d[i]; j++)            if(ck(i*100+p[j]))                a[++t]=i*100+p[j];    for(int i=4; i<=9999; i+=4)        if((i%100||!(i%400))&&ck(i*10000+229))            ans[++tot]=i*10000+229;    for(int i=1; i<=9999; i++)        for(int j=1; j<=t; j++)            if(ck(i*10000+a[j]))                ans[++tot]=i*10000+a[j];    cin >> Q;    while(Q--)    {        cin >> (s+1);        int res=0,c=1;        for(int i=1; i<=8; i++)            if(s[i]!='-')c=0;        if(c){cout << "55157\n";continue;}        for(int i=1; i<=tot; i++)        {            int now=ans[i],ok=1;            for(int j=8; j>=1; j--,now/=10)                if(s[j]!='-'&&s[j]-'0'!=now%10)                    {ok=0;break;}            res+=ok;        }        cout << res << '\n';    }    return 0;}
]]>
@@ -4105,7 +4105,7 @@ /2022/06/21/luo-gu-p1514-noip2010-ti-gao-zu-yin-shui-ru-cheng-ti-jie/ - 洛谷P1514 [NOIP2010 提高组] 引水入城 题解

题目链接:P1514 [NOIP2010 提高组] 引水入城

题意

在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个$N$ 行$\times M$ 列的矩形,如上图所示,其中每个格子都代表一座城市,每座城市都有一个海拔高度。

为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。

因此,只有与湖泊毗邻的第$1$ 行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。由于第$N$ 行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。

首先可以想到我们直接在每个岸边的点跑dfs

同时记录每个点能覆盖的城市数目

那么一个点能覆盖的城市有什么特点呢

如果有解,那么任意蓄水厂能覆盖的都是线段

这个不容易想到,但是很容易证明

这里是一个口糊证明

考虑反证法

设一个蓄水厂 $u$ 能覆盖的最左和最右城市分别为 $l,r$

则 $u,l,r$ 三点一定构成一个类似于三角形的封闭图形

显然其他蓄水厂如果要覆盖 $[l,r]$ 中的点,一定会经过三角形的某条边

如果某个点 $x \in [l,r]$ 不能被这个三角形内任意一点流出的水覆盖

则外界流入的水无论从哪里经过三角形,都无法流到 $x$ ,那么就无解了

因此有解的时候任意蓄水厂能覆盖的城市构成一条线段

知道了线段,怎么算答案呢?

我们可以设一个 $L$ 表示目前覆盖到的城市位置(从左向右覆盖)

而任意一条线段的左端点 $l_i\le L$ 均可以使 $L$ 右移至 $r_i$

于是我们贪心地选择所有满足 $l_i\le L$ 的线段中最大的 $r_i$ ,去更新 $L$

然后就没了

时间复杂度 $O(nm)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,m;int dx[5]={0,0,1,-1};int dy[5]={1,-1,0,0};int vis[N][N],h[N][N],l[N][N],r[N][N];bool safe(int x,int y){return 1<=x&&x<=n&&1<=y&&y<=m;}void dfs(int x,int y){    vis[x][y]=1;    for(int i=0; i<4; i++)    {        int tx=x+dx[i];        int ty=y+dy[i];        if(!safe(tx,ty)||h[tx][ty]>=h[x][y])continue;        if(!vis[tx][ty])dfs(tx,ty);        l[x][y]=min(l[x][y],l[tx][ty]);        r[x][y]=max(r[x][y],r[tx][ty]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(l,0x3f,sizeof(l));    cin >> n >> m;    for(int i=1; i<=m; i++)        l[n][i]=r[n][i]=i;    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)            cin >> h[i][j];    for(int i=1; i<=m; i++)        if(!vis[1][i])dfs(1,i);    bool ok=1;int res=0;    for(int i=1; i<=m; i++)        if(!vis[n][i]) ok=0;++res;    if(!ok)    {        cout << "0\n" << res << '\n';        return 0;    }    int L=1,R=r[1][1];    while(L<=m)    {        for(int i=1; i<=m; i++)            if(l[1][i]<=L)                R=max(R,r[1][i]);        L=R+1; ++res;    }    cout << "1\n" << res << '\n';    return 0;}
]]>
+ 洛谷P1514 [NOIP2010提高组] 引水入城 题解

题目链接:P1514[NOIP2010 提高组] 引水入城

题意

在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个\(N\) 行\(\timesM\)列的矩形,如上图所示,其中每个格子都代表一座城市,每座城市都有一个海拔高度。

为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。

因此,只有与湖泊毗邻的第\(1\)行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。由于第\(N\)行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。

首先可以想到我们直接在每个岸边的点跑dfs

同时记录每个点能覆盖的城市数目

那么一个点能覆盖的城市有什么特点呢

如果有解,那么任意蓄水厂能覆盖的都是线段

这个不容易想到,但是很容易证明

这里是一个口糊证明

考虑反证法

设一个蓄水厂 \(u\)能覆盖的最左和最右城市分别为 \(l,r\)

\(u,l,r\)三点一定构成一个类似于三角形的封闭图形

显然其他蓄水厂如果要覆盖 \([l,r]\)中的点,一定会经过三角形的某条边

如果某个点 \(x \in [l,r]\)不能被这个三角形内任意一点流出的水覆盖

则外界流入的水无论从哪里经过三角形,都无法流到 \(x\) ,那么就无解了

因此有解的时候任意蓄水厂能覆盖的城市构成一条线段

知道了线段,怎么算答案呢?

我们可以设一个 \(L\)表示目前覆盖到的城市位置(从左向右覆盖)

而任意一条线段的左端点 \(l_i\le L\)均可以使 \(L\) 右移至 \(r_i\)

于是我们贪心地选择所有满足 \(l_i\leL\) 的线段中最大的 \(r_i\),去更新 \(L\)

然后就没了

时间复杂度 \(O(nm)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,m;int dx[5]={0,0,1,-1};int dy[5]={1,-1,0,0};int vis[N][N],h[N][N],l[N][N],r[N][N];bool safe(int x,int y){return 1<=x&&x<=n&&1<=y&&y<=m;}void dfs(int x,int y){    vis[x][y]=1;    for(int i=0; i<4; i++)    {        int tx=x+dx[i];        int ty=y+dy[i];        if(!safe(tx,ty)||h[tx][ty]>=h[x][y])continue;        if(!vis[tx][ty])dfs(tx,ty);        l[x][y]=min(l[x][y],l[tx][ty]);        r[x][y]=max(r[x][y],r[tx][ty]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(l,0x3f,sizeof(l));    cin >> n >> m;    for(int i=1; i<=m; i++)        l[n][i]=r[n][i]=i;    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)            cin >> h[i][j];    for(int i=1; i<=m; i++)        if(!vis[1][i])dfs(1,i);    bool ok=1;int res=0;    for(int i=1; i<=m; i++)        if(!vis[n][i]) ok=0;++res;    if(!ok)    {        cout << "0\n" << res << '\n';        return 0;    }    int L=1,R=r[1][1];    while(L<=m)    {        for(int i=1; i<=m; i++)            if(l[1][i]<=L)                R=max(R,r[1][i]);        L=R+1; ++res;    }    cout << "1\n" << res << '\n';    return 0;}
]]>
@@ -4130,7 +4130,7 @@ /2022/06/21/luo-gu-p1535-usaco08mar-cow-travelling-s-ti-jie/ - 洛谷P1535 [USACO08MAR]Cow Travelling S 题解

题目链接:P1535 [USACO08MAR]Cow Travelling S

题意

奶牛们在被划分成 $N$ 行 $M$ 列($2 \leq N,M \leq 100$)的草地上游走, 试图找到整块草地中最美味的牧草。

Farmer John 在某个时刻看见贝茜在位置 $(R_1, C_1)$,恰好 $T$($0 \lt T \leq 15$)秒后,FJ 又在位置 $(R_2, C_2)$ 与贝茜撞了正着。FJ 并不知道在这 $T$ 秒内贝茜是否曾经到过 $(R_2, C_2)$,他能确定的只是,现在贝茜在那里。

设 $S$ 为奶牛在 $T$ 秒内从 $(R_1, C_1)$ 走到 $(R_2, C_2)$ 所能选择的路径总数,FJ 希望有 一个程序来帮他计算这个值。每一秒内,奶牛会水平或垂直地移动 $1$ 单位距离(奶牛总是在移动,不会在某秒内停在它上一秒所在的点)。草地上的某些地方有树,自然,奶牛不能走到树所在的位置,也不会走出草地。

现在你拿到了一张整块草地的地形图,其中 . 表示平坦的草地,* 表示挡路的树。你的任务是计算出,一头在 $T$ 秒内从 $(R_1, C_1)$ 移动到 $(R_2, C_2)$ 的奶牛可能经过的路径有哪些。

一眼看成组合数有救吗

注意到这是个计数dp

设 $f_{i,j,k}$ 表示用 $k$ 步恰好走到 $(i,j)$ 的方案数

这里要用记搜来算dp,刷表法更新(也就是用当前结点的答案去更新别的结点)

为什么用记忆化搜索呢?因为朴素的循环无法保证dp计算的顺序

时间复杂度的宽松上界为 $O(nmt)$

这题数据范围比较小所以据说暴搜剪枝都能过

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)int n,m,t,xa,ya,xb,yb,f[N][N][25];char s[N][N];int dx[4]={0,0,1,-1};int dy[4]={1,-1,0,0};struct node{    int x,y,step;};queue<node> q;bool safe(int x,int y){    return 1<=x&&x<=n&&1<=y&&y<=m&&s[x][y]!='*';}void bfs(){    q.push({xa,ya,0});    f[xa][ya][0]=1;    while(!q.empty())    {        node tmp=q.front();q.pop();        for(int i=0; i<4; i++)        {            int tx=tmp.x+dx[i];            int ty=tmp.y+dy[i];            int ts=tmp.step+1;            if(!safe(tx,ty)||ts>t)continue;            if(f[tx][ty][ts])            {                f[tx][ty][ts]+=f[tmp.x][tmp.y][tmp.step];                continue;            }            f[tx][ty][ts]+=f[tmp.x][tmp.y][tmp.step];            q.push({tx,ty,ts});        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> t;    for(int i=1; i<=n; i++)        cin >> (s[i]+1);    cin >> xa >> ya >> xb >> yb;    bfs();    cout << f[xb][yb][t] << '\n';    return 0;}
]]>
+ 洛谷P1535[USACO08MAR]Cow Travelling S 题解

题目链接:P1535[USACO08MAR]Cow Travelling S

题意

奶牛们在被划分成 \(N\)\(M\) 列(\(2 \leqN,M \leq 100\))的草地上游走,试图找到整块草地中最美味的牧草。

Farmer John 在某个时刻看见贝茜在位置 \((R_1, C_1)\),恰好 \(T\)(\(0 \lt T\leq 15\))秒后,FJ 又在位置 \((R_2,C_2)\) 与贝茜撞了正着。FJ 并不知道在这 \(T\) 秒内贝茜是否曾经到过 \((R_2,C_2)\),他能确定的只是,现在贝茜在那里。

\(S\) 为奶牛在 \(T\) 秒内从 \((R_1, C_1)\) 走到 \((R_2, C_2)\) 所能选择的路径总数,FJ 希望有一个程序来帮他计算这个值。每一秒内,奶牛会水平或垂直地移动 \(1\)单位距离(奶牛总是在移动,不会在某秒内停在它上一秒所在的点)。草地上的某些地方有树,自然,奶牛不能走到树所在的位置,也不会走出草地。

现在你拿到了一张整块草地的地形图,其中 .表示平坦的草地,* 表示挡路的树。你的任务是计算出,一头在\(T\) 秒内从 \((R_1, C_1)\) 移动到 \((R_2, C_2)\)的奶牛可能经过的路径有哪些。

一眼看成组合数有救吗

注意到这是个计数dp

\(f_{i,j,k}\) 表示用 \(k\) 步恰好走到 \((i,j)\) 的方案数

这里要用记搜来算dp,刷表法更新(也就是用当前结点的答案去更新别的结点)

为什么用记忆化搜索呢?因为朴素的循环无法保证dp计算的顺序

时间复杂度的宽松上界为 \(O(nmt)\)

这题数据范围比较小所以据说暴搜剪枝都能过

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)int n,m,t,xa,ya,xb,yb,f[N][N][25];char s[N][N];int dx[4]={0,0,1,-1};int dy[4]={1,-1,0,0};struct node{    int x,y,step;};queue<node> q;bool safe(int x,int y){    return 1<=x&&x<=n&&1<=y&&y<=m&&s[x][y]!='*';}void bfs(){    q.push({xa,ya,0});    f[xa][ya][0]=1;    while(!q.empty())    {        node tmp=q.front();q.pop();        for(int i=0; i<4; i++)        {            int tx=tmp.x+dx[i];            int ty=tmp.y+dy[i];            int ts=tmp.step+1;            if(!safe(tx,ty)||ts>t)continue;            if(f[tx][ty][ts])            {                f[tx][ty][ts]+=f[tmp.x][tmp.y][tmp.step];                continue;            }            f[tx][ty][ts]+=f[tmp.x][tmp.y][tmp.step];            q.push({tx,ty,ts});        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> t;    for(int i=1; i<=n; i++)        cin >> (s[i]+1);    cin >> xa >> ya >> xb >> yb;    bfs();    cout << f[xb][yb][t] << '\n';    return 0;}
]]>
@@ -4157,7 +4157,7 @@ /2022/06/21/luo-gu-p1378-you-di-kuo-zhan-ti-jie/ - 洛谷P1378 油滴扩展 题解

题目链接:P1378 油滴扩展

题意

在一个长方形框子里,最多有 $N$ 个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界。必须等一个油滴扩展完毕才能放置下一个油滴。那么应该按照怎样的顺序在这 $N$ 个点上放置油滴,才能使放置完毕后所有油滴占据的总体积最大呢?(不同的油滴不会相互融合)

注:圆的面积公式 $V = \pi r^2$,其中 $r$ 为圆的半径。

对于 $100\%$ 的数据,$1 \le N \le 6$,坐标范围在 $[-1000, 1000]$ 内。

数据范围告诉我们这就是一道暴搜题

枚举油滴扩展的顺序,然后和已经扩散好的油滴算一下距离

注意距离可能为负,此时新油滴在某油滴内部,此时认为 $r=0$ 即可

最后的答案就是总面积减去油滴面积并

时间复杂度 $O(n\times n!)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(15)const double pi = 3.14159265354;int n,vis[N];double xa,ya,xb,yb,x[N],y[N],S,mx,r[N];#define pf(x) ((x)*(x))double dis(int i,int j){return sqrt(pf(x[i]-x[j])+pf(y[i]-y[j]));}double cal(int i){    double l1=min(abs(x[i]-xa),abs(x[i]-xb));    double l2=min(abs(y[i]-ya),abs(y[i]-yb));    double res=min(l1,l2);    for(int j=1; j<=n; j++)        if(i!=j&&vis[j])        {            double d=dis(i,j);            res=fmin(res,max(d-r[j],0.0));        }    return res;}void dfs(int k,double sum){    if(k>n)    {        mx=max(mx,sum);        return;    }    for(int i=1; i<=n; i++)    {        if(!vis[i])        {            r[i]=cal(i);            vis[i]=1;            dfs(k+1,sum+r[i]*r[i]*pi);            vis[i]=0;        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> xa >> ya >> xb >> yb;    S=abs((xa-xb)*(ya-yb));    for(int i=1; i<=n; i++)        cin >> x[i] >> y[i];    dfs(1,0);    cout << fixed << setprecision(0);    cout << S-mx << endl;    return 0;}
]]>
+ 洛谷P1378 油滴扩展 题解

题目链接:P1378油滴扩展

题意

在一个长方形框子里,最多有 \(N\)个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界。必须等一个油滴扩展完毕才能放置下一个油滴。那么应该按照怎样的顺序在这\(N\)个点上放置油滴,才能使放置完毕后所有油滴占据的总体积最大呢?(不同的油滴不会相互融合)

注:圆的面积公式 \(V = \pir^2\),其中 \(r\)为圆的半径。

对于 \(100\%\) 的数据,\(1 \le N \le 6\),坐标范围在 \([-1000, 1000]\) 内。

数据范围告诉我们这就是一道暴搜题

枚举油滴扩展的顺序,然后和已经扩散好的油滴算一下距离

注意距离可能为负,此时新油滴在某油滴内部,此时认为 \(r=0\) 即可

最后的答案就是总面积减去油滴面积并

时间复杂度 \(O(n\times n!)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(15)const double pi = 3.14159265354;int n,vis[N];double xa,ya,xb,yb,x[N],y[N],S,mx,r[N];#define pf(x) ((x)*(x))double dis(int i,int j){return sqrt(pf(x[i]-x[j])+pf(y[i]-y[j]));}double cal(int i){    double l1=min(abs(x[i]-xa),abs(x[i]-xb));    double l2=min(abs(y[i]-ya),abs(y[i]-yb));    double res=min(l1,l2);    for(int j=1; j<=n; j++)        if(i!=j&&vis[j])        {            double d=dis(i,j);            res=fmin(res,max(d-r[j],0.0));        }    return res;}void dfs(int k,double sum){    if(k>n)    {        mx=max(mx,sum);        return;    }    for(int i=1; i<=n; i++)    {        if(!vis[i])        {            r[i]=cal(i);            vis[i]=1;            dfs(k+1,sum+r[i]*r[i]*pi);            vis[i]=0;        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> xa >> ya >> xb >> yb;    S=abs((xa-xb)*(ya-yb));    for(int i=1; i<=n; i++)        cin >> x[i] >> y[i];    dfs(1,0);    cout << fixed << setprecision(0);    cout << S-mx << endl;    return 0;}
]]>
@@ -4184,7 +4184,7 @@ /2022/06/18/luo-gu-p4983-wang-qing-ti-jie/ - 洛谷P4983 忘情 题解

题目链接:P4983 忘情

题意

“为什么要离开我!”

“因为你没玩儿转!”

“我玩儿转了!”

“那好,你现在就给我维护这么一个式子!”

“为什么要出这么毒瘤的东西。”

“为了恶心你。”

“……”

$……………………………$

你的 $npy$ 为了恶心你,特地请了四位大神和一个辣鸡!

$\rm hdxrie$ 说:“我们得求和。”于是有了 $\sum\limits_{i=1}^{n}x_i$ 。

$\rm Imagine$ 说:“我们得有平均数。”于是有了 $\bar x$ 。

$\rm TimeTraveller$ 说:“我们得有加减乘除。”于是有了一些恶心的组合。

$\rm Althen·Way·Satan$ 说:“我们还得有平方。”于是我们将它平方。

最垃圾的 $\rm ZredXNy$ 说:“那我帮你们整合一下。”

于是,我们得到了这么一个式子 $:$

我们定义一段序列的值为这个,其中 $n$为此序列的元素个数。

我们给定一段长度为 $n$ 的序列,现在要求将它分成 $m$ 段,要求每一段的值的总和最小,求出这个最小值。

对于 $100 \%$ 的数据,$m≤n≤100000$,$1≤x_i≤1000$。

柿子化简懒得讲了,太简单了

最后化简出来每一段的花费就是

其中 $S_k = \sum_{i=1}^{k}x_i$

设 $f_{i,k}$ 表示只考虑前 $i$ 个物品分成 $k$ 段的最小花费

显然可以wqs二分去掉 $k$ 维(证明略)

令 $a_i =S_i+1,b_j=S_j$ ,则

移项得

斜率优化 $O(n\log 10^{16})$

我的写法是二分的那条直线相等时优先选分的段最多的(所谓尽可能多选)

也就是斜率相等时,优先切最右边的点,显然这个点分的段更多一些

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int pf(int x){return x*x;}int n,m,st,en,sum[N],f[N],g[N],q[N];int a(int i){return sum[i]+1;}int b(int i){return sum[i];}int X(int i){return b(i);}int Y(int i){return f[i]+b(i)*b(i);}double slope(int a,int b){return ((double)Y(b)-Y(a))/(X(b)-X(a));}int ck(int d){    f[0]=0; g[0]=0; q[st=en=1]=0;    for(int i=1; i<=n; i++)    {        while(st<en&&slope(q[st],q[st+1])<=2.0*a(i))++st;        f[i]=f[q[st]] + pf(a(i)-b(q[st]))+d;        g[i]=g[q[st]]+1;        while(st<en&&slope(q[en-1],q[en])>=slope(q[en-1],i))--en;        q[++en]=i;    }    return g[n]>=m;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> sum[i];        sum[i]+=sum[i-1];    }    int l=-1e16,r=1e16;    while(l<r)    {        int mid=(l+r+1)>>1;        if(ck(mid))l=mid;        else r=mid-1;    }    ck(l);    cout << f[n]-m*l;    return 0;}

当然这里还有一份尽可能少选的写法

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int pf(int x){return x*x;}int n,m,st,en,sum[N],f[N],g[N],q[N];int a(int i){return sum[i]+1;}int b(int i){return sum[i];}int X(int i){return b(i);}int Y(int i){return f[i]+b(i)*b(i);}double slope(int a,int b){return ((double)Y(b)-Y(a))/(X(b)-X(a));}int ck(int d){    memset(f,0x3f,sizeof(f));    memset(g,0,sizeof(g));    f[0]=0; q[st=en=1]=0;    for(int i=1; i<=n; i++)    {        while(st<en&&slope(q[st],q[st+1])<2.0*a(i))++st;        f[i]=f[q[st]] + pf(a(i)-b(q[st]))+d;        g[i]=g[q[st]]+1;        while(st<en&&slope(q[en-1],q[en])>slope(q[en-1],i))--en;        q[++en]=i;    }    return g[n]<=m;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> sum[i];        sum[i]+=sum[i-1];    }    int l=0,r=1e16;    while(l<r)    {        int mid=(l+r)>>1;        if(ck(mid))r=mid;        else l=mid+1;    }    ck(l);    cout << f[n]-m*l;    return 0;}

有一说一,这个题面怎么有点…(懂得都懂)

]]>
+ 洛谷P4983 忘情 题解

题目链接:P4983忘情

题意

“为什么要离开我!”

“因为你没玩儿转!”

“我玩儿转了!”

“那好,你现在就给我维护这么一个式子!”

“为什么要出这么毒瘤的东西。”

“为了恶心你。”

“......”

\(……………………………\)

你的 \(npy\)为了恶心你,特地请了四位大神和一个辣鸡!

\(\rm hdxrie\)说:“我们得求和。”于是有了 \(\sum\limits_{i=1}^{n}x_i\) 。

\(\rm Imagine\)说:“我们得有平均数。”于是有了 \(\barx\)

\(\rm TimeTraveller\)说:“我们得有加减乘除。”于是有了一些恶心的组合。

\(\rm Althen·Way·Satan\)说:“我们还得有平方。”于是我们将它平方。

最垃圾的 \(\rm ZredXNy\)说:“那我帮你们整合一下。”

于是,我们得到了这么一个式子 \(:\)

\[\frac{\left((\sum\limits_{i=1}^{n}x_i×\bar x)+\bar x\right)^2}{\bar x^2}\]

我们定义一段序列的值为这个,其中 \(n\)为此序列的元素个数。

我们给定一段长度为 \(n\)的序列,现在要求将它分成 \(m\)段,要求每一段的值的总和最小,求出这个最小值。

对于 \(100 \%\) 的数据,\(m≤n≤100000\),\(1≤x_i≤1000\)。

柿子化简懒得讲了,太简单了

最后化简出来每一段的花费就是 \[(S_r-S_{l-1}+1)^2\] 其中 \(S_k =\sum_{i=1}^{k}x_i\)

\(f_{i,k}\) 表示只考虑前 \(i\) 个物品分成 \(k\) 段的最小花费

显然可以wqs二分去掉 \(k\)维(证明略) \[f_i = \min_{j < i}\left\{f_j+(S_i-S_{j}+1)^2\right\}+d\]\(a_i =S_i+1,b_j=S_j\),则 \[\begin{aligned}f_i &= \min_{j < i}\left\{f_j+(a_i-b_j)^2\right\}+d\\&=\min_{j < i}\left\{f_j+a_i^2-2a_ib_j+b_j^2\right\}+d\end{aligned}\] 移项得 \[f_i-a_i^2=\min_{j < i}\left\{f_j-2a_ib_j+b_j^2\right\}+d\]\[\begin{aligned}k_i &= 2a_i\\x_j &= b_j\\y_j &= f_j+b_j^2\\b_i &= f_i-a_i^2\end{aligned}\] 斜率优化 \(O(n\log10^{16})\)

我的写法是二分的那条直线相等时优先选分的段最多的(所谓尽可能多选)

也就是斜率相等时,优先切最右边的点,显然这个点分的段更多一些

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int pf(int x){return x*x;}int n,m,st,en,sum[N],f[N],g[N],q[N];int a(int i){return sum[i]+1;}int b(int i){return sum[i];}int X(int i){return b(i);}int Y(int i){return f[i]+b(i)*b(i);}double slope(int a,int b){return ((double)Y(b)-Y(a))/(X(b)-X(a));}int ck(int d){    f[0]=0; g[0]=0; q[st=en=1]=0;    for(int i=1; i<=n; i++)    {        while(st<en&&slope(q[st],q[st+1])<=2.0*a(i))++st;        f[i]=f[q[st]] + pf(a(i)-b(q[st]))+d;        g[i]=g[q[st]]+1;        while(st<en&&slope(q[en-1],q[en])>=slope(q[en-1],i))--en;        q[++en]=i;    }    return g[n]>=m;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> sum[i];        sum[i]+=sum[i-1];    }    int l=-1e16,r=1e16;    while(l<r)    {        int mid=(l+r+1)>>1;        if(ck(mid))l=mid;        else r=mid-1;    }    ck(l);    cout << f[n]-m*l;    return 0;}

当然这里还有一份尽可能少选的写法

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int pf(int x){return x*x;}int n,m,st,en,sum[N],f[N],g[N],q[N];int a(int i){return sum[i]+1;}int b(int i){return sum[i];}int X(int i){return b(i);}int Y(int i){return f[i]+b(i)*b(i);}double slope(int a,int b){return ((double)Y(b)-Y(a))/(X(b)-X(a));}int ck(int d){    memset(f,0x3f,sizeof(f));    memset(g,0,sizeof(g));    f[0]=0; q[st=en=1]=0;    for(int i=1; i<=n; i++)    {        while(st<en&&slope(q[st],q[st+1])<2.0*a(i))++st;        f[i]=f[q[st]] + pf(a(i)-b(q[st]))+d;        g[i]=g[q[st]]+1;        while(st<en&&slope(q[en-1],q[en])>slope(q[en-1],i))--en;        q[++en]=i;    }    return g[n]<=m;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> sum[i];        sum[i]+=sum[i-1];    }    int l=0,r=1e16;    while(l<r)    {        int mid=(l+r)>>1;        if(ck(mid))r=mid;        else l=mid+1;    }    ck(l);    cout << f[n]-m*l;    return 0;}

有一说一,这个题面怎么有点...(懂得都懂)

]]>
@@ -4211,7 +4211,7 @@ /2022/06/18/luo-gu-p3195-hnoi2008-wan-ju-zhuang-xiang-ti-jie/ - 洛谷P3195 [HNOI2008]玩具装箱 题解

题目链接:P3195 [HNOI2008]玩具装箱

题意

有 $n$ 个玩具,第 $i$ 个玩具的价值为 $c_i$ 。要求将这 $n$ 个玩具排成一排,分成若干段。对于一段 $[l,r]$ ,它的代价为 $(r-l+\sum_{i=l}^{r}c_i-L)^2$ ,其中 $L$ 为一个常量,求分段的最小代价。

$1 \leq n \leq 5 \times 10^4$,$1 \leq L \leq 10^7$,$1 \leq C_i \leq 10^7$

upd. 新增斜率优化新的理解。

令 $f_i$ 表示前 $i$ 个物品分成若干段的最小代价,则

其中 $S_i$ 为前 $i$ 个物品的价值和,即 $S_i = \sum_{j=1}^{i}c_j$

直接去做是 $O(n^2)$ ,考虑斜率优化

令 $a_i = S_i + i,~b_i = S_i + i + L+1$ ,则

移项得

注意到右边的部分有一个 $2a_i$ 导致我们无法单调队列优化

但是我们可以斜率优化,也就是吧 $2a_i$ 看作 $k$

根据斜截式方程可得

可知

则转移方程可以写成如下形式

把 $(x_j,y_j)$ 映射到平面上的点,那么问题就转化为了

找到一个 $1\le j<i$ 最小化过点 $(x_j,y_j)$ 且斜率为 $k_i$ 的直线的 $y$ 轴截距

由于这个 $k_i$ 是严格单调递增的,

不难发现这个点一定在 $(x_j,y_j)$ 点集形成的下凸壳上,

并且这个点 $P_j$ 的 $j$ 是满足直线 $P_jP_{j+1}$ 的斜率 $k_j^{\prime}>k_i$ 最小的 $j$

或者说,这个点和下一个在下凸壳上的点所连产生的直线是第一个斜率比 $k_i$ 大的

考虑如何维护这个下凸壳

刚才我们也说了 $k_i$ 是单调递增的,所以这个下凸壳是一个“动态”的

当凸壳上的某个点 $u$ 和下一个点 $v$ 所成的直线斜率小于 $k_i$ 时,

$u$ 就没用了,该被踢掉了

因此我们可以用单调队列来维护,而不是单调栈

然后每次加点就和二维凸包那个差不多,这里不细讲了(


对于斜率优化,还有另一种理解

首先刚刚的柿子还是要用到的

我们不用刚刚那个

考虑一个 $j~(j>k)$ 在什么情况下比 $k$ 优

显然有

然后推推柿子

注意到 $b_i = S_i + i + L+1$ 是单调递增的

因此有 $b_j > b_k$

则柿子可以化简为

左边这个东西长的很像斜率的定义

因此我们可以维护一个点集,然后两两点去判断

但是这样肯定是不优的

仔细思考一下,可能成为答案的点一定在这个点集的下凸壳上(因为我们要最小化 $ f_j - 2a_ib_j + b_j^2$ ,所以是下凸壳)

于是我们维护一个下凸壳,依次加入点,再踢出那些不优的点


时间复杂度 $O(n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)int n,L;double sum[N],dp[N];int st,en,q[N];double a(int i){return sum[i]+i;}double b(int i){return a(i)+L+1;}double X(int i){return b(i);}double Y(int i){return dp[i]+b(i)*b(i);}double slope(int i,int j){return (Y(i)-Y(j))/(X(i)-X(j));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> L;    for(int i=1; i<=n; i++)    {        cin >> sum[i];        sum[i]+=sum[i-1];    }    st=en=1;    for(int i=1; i<=n; i++)    {        while(st<en&&slope(q[st],q[st+1])<2*a(i))++st;        dp[i]=dp[q[st]]+(a(i)-b(q[st]))*(a(i)-b(q[st]));        while(st<en&&slope(i,q[en-1])<slope(q[en-1],q[en]))--en;        q[++en]=i;    }    cout << (int)dp[n] << '\n';    return 0;}
]]>
+ 洛谷P3195 [HNOI2008]玩具装箱题解

题目链接:P3195[HNOI2008]玩具装箱

题意

\(n\) 个玩具,第 \(i\) 个玩具的价值为 \(c_i\) 。要求将这 \(n\) 个玩具排成一排,分成若干段。对于一段\([l,r]\) ,它的代价为 \((r-l+\sum_{i=l}^{r}c_i-L)^2\) ,其中 \(L\) 为一个常量,求分段的最小代价。

\(1 \leq n \leq 5 \times10^4\)\(1 \leq L \leq10^7\)\(1 \leq C_i \leq10^7\)

upd. 新增斜率优化新的理解。

\(f_i\) 表示前 \(i\) 个物品分成若干段的最小代价,则 \[f_i = \min_{j < i}\left\{f_j + (S_i + i -S_j -j -L-1)^2\right\}\] 其中 \(S_i\) 为前 \(i\) 个物品的价值和,即 \(S_i = \sum_{j=1}^{i}c_j\)

直接去做是 \(O(n^2)\),考虑斜率优化

\(a_i = S_i + i,~b_i = S_i + i +L+1\) ,则 \[\begin{aligned}f_i &= \min_{j < i}\left\{ f_j + (a_i-b_j)^2 \right\}\\&=\min_{j < i}\left\{ f_j + a_i^2 - 2a_ib_j + b_j^2\right\}\end{aligned}\] 移项得 \[f_i - a_i^2 = \min_{j < i}\left\{ f_j -2a_ib_j + b_j^2\right\}\] 注意到右边的部分有一个 \(2a_i\) 导致我们无法单调队列优化

但是我们可以斜率优化,也就是吧 \(2a_i\) 看作 \(k\)

根据斜截式方程可得 \[b=y-kx\] 可知 \[\begin{aligned}b^\prime_i&=f_i-a_i^2\\k_i&=2a_i\\x_j&=b_j\\y_j &= f_j + b_j^2\end{aligned}\] 则转移方程可以写成如下形式 \[b_i^\prime = \min\left\{ y_j-k_ix_j\right\}\]\((x_j,y_j)\)映射到平面上的点,那么问题就转化为了

找到一个 \(1\le j<i\)最小化过点 \((x_j,y_j)\)且斜率为 \(k_i\) 的直线的 \(y\) 轴截距

由于这个 \(k_i\)是严格单调递增的,

不难发现这个点一定在 \((x_j,y_j)\)点集形成的下凸壳上,

并且这个点 \(P_j\)\(j\) 是满足直线 \(P_jP_{j+1}\) 的斜率 \(k_j^{\prime}>k_i\) 最小的 \(j\)

或者说,这个点和下一个在下凸壳上的点所连产生的直线是第一个斜率比\(k_i\) 大的

考虑如何维护这个下凸壳

刚才我们也说了 \(k_i\)是单调递增的,所以这个下凸壳是一个“动态”的

当凸壳上的某个点 \(u\) 和下一个点\(v\) 所成的直线斜率小于 \(k_i\) 时,

\(u\) 就没用了,该被踢掉了

因此我们可以用单调队列来维护,而不是单调栈

然后每次加点就和二维凸包那个差不多,这里不细讲了(


对于斜率优化,还有另一种理解

首先刚刚的柿子还是要用到的 \[f_i-a_i^2=\min_{j < i}\left\{ f_j - 2a_ib_j + b_j^2\right\}\] 我们不用刚刚那个

考虑一个 \(j~(j>k)\)在什么情况下比 \(k\)

显然有 \[f_j-2a_ib_j+b_j^2 < f_k-2a_ib_k+b_k^2\] 然后推推柿子 \[f_j+b_j^2-f_k-b_k^2 < 2a_i(b_j-b_k)\] 注意到 \(b_i = S_i + i +L+1\) 是单调递增的

因此有 \(b_j > b_k\)

则柿子可以化简为 \[\dfrac{f_j+b_j^2-f_k-b_k^2}{b_j-b_k} < 2a_i\] 左边这个东西长的很像斜率的定义

因此我们可以维护一个点集,然后两两点去判断

但是这样肯定是不优的

仔细思考一下,可能成为答案的点一定在这个点集的下凸壳上(因为我们要最小化$ f_j - 2a_ib_j + b_j^2$ ,所以是下凸壳)

于是我们维护一个下凸壳,依次加入点,再踢出那些不优的点


时间复杂度 \(O(n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)int n,L;double sum[N],dp[N];int st,en,q[N];double a(int i){return sum[i]+i;}double b(int i){return a(i)+L+1;}double X(int i){return b(i);}double Y(int i){return dp[i]+b(i)*b(i);}double slope(int i,int j){return (Y(i)-Y(j))/(X(i)-X(j));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> L;    for(int i=1; i<=n; i++)    {        cin >> sum[i];        sum[i]+=sum[i-1];    }    st=en=1;    for(int i=1; i<=n; i++)    {        while(st<en&&slope(q[st],q[st+1])<2*a(i))++st;        dp[i]=dp[q[st]]+(a(i)-b(q[st]))*(a(i)-b(q[st]));        while(st<en&&slope(i,q[en-1])<slope(q[en-1],q[en]))--en;        q[++en]=i;    }    cout << (int)dp[n] << '\n';    return 0;}
]]>
@@ -4240,7 +4240,7 @@ /2022/06/17/cf125e-mst-company-ti-jie/ - CF125E MST Company 题解

题目链接:CF125E MST Company

题意

给你一个有 $n$ 个节点,$m$ 条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足节点 $1$ 正好连了 $k$ 条边。

输出最优方案所选的边

$1 \le n,k \le 5000,~0 \le m \le 10^5,~1 \le w \le 10^5$

这道题的加强版,题解(下文我摘抄过来了一部分,反正都是我写的

我们把与 $s$ 相连的所有边标记为白边,其他边为黑边

然后问题就转化为了选恰好 $k$ 条白边的最小花费

考虑wqs二分,最好用归并优化一下

这题的难点在无解的情况如何判断

  • 白边不够用,如果与 $s$ 相连的边小于 $k$ 条,无解

  • 黑边不够用,也就是生成树不得不包括大于 $k$ 条白边,无解

那道弱化版的题目只要求输出最优解的花费

但是我们都知道wqs二分的那个切线可能同时切到多个点

然后我的代码的写法是权值相等时优先取白边

所以答案中选了 $k$ 条白边后就不要再选白边了(当然这样不会改变权值)

时间复杂度 $O(n\log n + n\log {\max\{w_i\}})$

可以通过所有的hack数据,请放心食用 qwq

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*_p1,*_p2;    char readchar()    {        if(_p1==_p2)_p1=buf1,_p2=buf1+fread(buf1,1,SIZ,stdin);        return _p1==_p2?EOF:*_p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO; #define N (int)(5e3+15)#define M (int)(1e5+15)int n,m,k,p1,p2,cnt,sum,ok,mx,f[N];struct Edge{int u,v,w,c,id;}e[M],c1[M],c2[M];void init(){for(int i=1; i<=n; i++)f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}int cmp(Edge a,Edge b){return a.w<b.w;}int merge(int u,int v){    u=find(u);v=find(v);    if(u==v)return 0;    return f[u]=v,1;}void proc(int mid){    for(int d=1; d<=p1; d++)        c1[d].w-=mid;    int i=1,j=1,pos=0;    for(; i<=p1; i++)    {        while(c1[i].w>c2[j].w&&j<=p2)            e[++pos]=c2[j++];        e[++pos]=c1[i];    }    while(j<=p2)e[++pos]=c2[j++];    for(int d=1; d<=p1; d++)        c1[d].w+=mid;}void kruskal(int mid){    proc(mid); cnt=sum=0;    int tmp=0; init();    for(int i=1; i<=m&&tmp<n; i++)    {        if(ok)        {            int u=e[i].u,v=e[i].v;            if(find(u)!=find(v))            {                if(cnt+e[i].c>k)continue;                else                {                    merge(e[i].u,e[i].v);                    cnt+=e[i].c,sum+=e[i].w,++tmp;                    write(e[i].id);                    pc(" \n"[tmp==n-1]);                }            }        }else if(merge(e[i].u,e[i].v))            cnt+=e[i].c,sum+=e[i].w,++tmp;    }}signed main(){    read(n);read(m);read(k);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        if(u==1||v==1)            c1[++p1]={u,v,w,1,i},mx=max(mx,w);        else c2[++p2]={u,v,w,0,i};    }    if(p1<k)return puts("-1"),0;    sort(c1+1,c1+p1+1,cmp);sort(c2+1,c2+p2+1,cmp);    int l=-mx-5,r=mx+5;    kruskal(l); if(cnt>k)return puts("-1"),0;    while(l<r)    {        int mid=(l+r)>>1;        kruskal(mid);        if(cnt>=k) r=mid;        else l=mid+1;    }    // write(sum+k*l);pc('\n');    write(n-1);pc('\n');    ok=1;kruskal(l);    return 0;}
]]>
+ CF125E MST Company 题解

题目链接:CF125EMST Company

题意

给你一个有 \(n\) 个节点,\(m\)条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足节点 \(1\) 正好连了 \(k\) 条边。

输出最优方案所选的边

\(1 \le n,k \le 5000,~0 \le m \le 10^5,~1\le w \le 10^5\)

这道题的加强版,题解(下文我摘抄过来了一部分,反正都是我写的

我们把与 \(s\)相连的所有边标记为白边,其他边为黑边

然后问题就转化为了选恰好 \(k\)条白边的最小花费

考虑wqs二分,最好用归并优化一下

这题的难点在无解的情况如何判断

  • 白边不够用,如果与 \(s\)相连的边小于 \(k\) 条,无解

  • 黑边不够用,也就是生成树不得不包括大于 \(k\) 条白边,无解

那道弱化版的题目只要求输出最优解的花费

但是我们都知道wqs二分的那个切线可能同时切到多个点

然后我的代码的写法是权值相等时优先取白边

所以答案中选了 \(k\)条白边后就不要再选白边了(当然这样不会改变权值)

时间复杂度 \(O(n\log n + n\log{\max\{w_i\}})\)

可以通过所有的hack数据,请放心食用 qwq

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*_p1,*_p2;    char readchar()    {        if(_p1==_p2)_p1=buf1,_p2=buf1+fread(buf1,1,SIZ,stdin);        return _p1==_p2?EOF:*_p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO; #define N (int)(5e3+15)#define M (int)(1e5+15)int n,m,k,p1,p2,cnt,sum,ok,mx,f[N];struct Edge{int u,v,w,c,id;}e[M],c1[M],c2[M];void init(){for(int i=1; i<=n; i++)f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}int cmp(Edge a,Edge b){return a.w<b.w;}int merge(int u,int v){    u=find(u);v=find(v);    if(u==v)return 0;    return f[u]=v,1;}void proc(int mid){    for(int d=1; d<=p1; d++)        c1[d].w-=mid;    int i=1,j=1,pos=0;    for(; i<=p1; i++)    {        while(c1[i].w>c2[j].w&&j<=p2)            e[++pos]=c2[j++];        e[++pos]=c1[i];    }    while(j<=p2)e[++pos]=c2[j++];    for(int d=1; d<=p1; d++)        c1[d].w+=mid;}void kruskal(int mid){    proc(mid); cnt=sum=0;    int tmp=0; init();    for(int i=1; i<=m&&tmp<n; i++)    {        if(ok)        {            int u=e[i].u,v=e[i].v;            if(find(u)!=find(v))            {                if(cnt+e[i].c>k)continue;                else                {                    merge(e[i].u,e[i].v);                    cnt+=e[i].c,sum+=e[i].w,++tmp;                    write(e[i].id);                    pc(" \n"[tmp==n-1]);                }            }        }else if(merge(e[i].u,e[i].v))            cnt+=e[i].c,sum+=e[i].w,++tmp;    }}signed main(){    read(n);read(m);read(k);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        if(u==1||v==1)            c1[++p1]={u,v,w,1,i},mx=max(mx,w);        else c2[++p2]={u,v,w,0,i};    }    if(p1<k)return puts("-1"),0;    sort(c1+1,c1+p1+1,cmp);sort(c2+1,c2+p2+1,cmp);    int l=-mx-5,r=mx+5;    kruskal(l); if(cnt>k)return puts("-1"),0;    while(l<r)    {        int mid=(l+r)>>1;        kruskal(mid);        if(cnt>=k) r=mid;        else l=mid+1;    }    // write(sum+k*l);pc('\n');    write(n-1);pc('\n');    ok=1;kruskal(l);    return 0;}
]]>
@@ -4269,7 +4269,7 @@ /2022/06/17/luo-gu-p5633-zui-xiao-du-xian-zhi-sheng-cheng-shu-ti-jie/ - 洛谷P5633 最小度限制生成树 题解

题目链接:P5633 最小度限制生成树

题意

给你一个有 $n$ 个节点,$m$ 条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足编号为 $s$ 的节点正好连了 $k$ 条边。

可能会出现无解的情况,如果无解,则输出 Impossible

对于 $100\%$ 的数据,$1\leq n \le 5\times 10^4$,$1\leq m \le 5\times 10^5$,$1\leq k \le 100$,$0\leq w\leq 3\times 10^4$。

如果做过这道题应该会觉得很简单了

我们把与 $s$ 相连的所有边标记为白边,其他边为黑边

然后问题就转化为了选恰好 $k$ 条白边的最小花费

考虑wqs二分,最好用归并优化一下

这题的难点在无解的情况如何判断

  • 白边不够用,如果与 $s$ 相连的边小于 $k$ 条,无解

  • 黑边不够用,也就是生成树不得不包括大于 $k$ 条白边,无解

时间复杂度 $O(n\log n + n\log {\max\{w_i\}})$

可以通过所有的hack数据,请放心食用 qwq

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*_p1,*_p2;    char readchar()    {        if(_p1==_p2)_p1=buf1,_p2=buf1+fread(buf1,1,SIZ,stdin);        return _p1==_p2?EOF:*_p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO; #define N (int)(5e4+15)#define M (int)(5e5+15)int n,m,s,k,p1,p2,cnt,sum,mx,f[N];struct Edge{int u,v,w,c;}e[M],c1[M],c2[M];void init(){for(int i=1; i<=n; i++)f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}int cmp(Edge a,Edge b){return a.w<b.w;}int merge(int u,int v){    u=find(u);v=find(v);    if(u==v)return 0;    return f[u]=v,1;}void proc(int mid){    for(int d=1; d<=p1; d++)        c1[d].w-=mid;    int i=1,j=1,pos=0;    for(; i<=p1; i++)    {        while(c1[i].w>c2[j].w&&j<=p2)            e[++pos]=c2[j++];        e[++pos]=c1[i];    }    while(j<=p2)e[++pos]=c2[j++];    for(int d=1; d<=p1; d++)        c1[d].w+=mid;}void kruskal(int mid){    proc(mid); cnt=sum=0;    int tmp=0; init();    for(int i=1; i<=m&&tmp<n; i++)        if(merge(e[i].u,e[i].v))        {            cnt+=e[i].c;            sum+=e[i].w;++tmp;        }}signed main(){    read(n);read(m);read(s);read(k);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        if(u==s||v==s)             c1[++p1]={u,v,w,1},mx=max(mx,w);        else c2[++p2]={u,v,w,0};    }    if(p1<k)return puts("Impossible"),0;    sort(c1+1,c1+p1+1,cmp);sort(c2+1,c2+p2+1,cmp);    int l=-mx-5,r=mx+5;    kruskal(l); if(cnt>k)return puts("Impossible"),0;    while(l<r)    {        int mid=(l+r)>>1;        kruskal(mid);        if(cnt>=k) r=mid;        else l=mid+1;    }    kruskal(l); write(sum+k*l);    return 0;}
]]>
+ 洛谷P5633 最小度限制生成树题解

题目链接:P5633最小度限制生成树

题意

给你一个有 \(n\) 个节点,\(m\)条边的带权无向图,你需要求得一个生成树,使边权总和最小,且满足编号为\(s\) 的节点正好连了 \(k\) 条边。

可能会出现无解的情况,如果无解,则输出 Impossible

对于 \(100\%\) 的数据,\(1\leq n \le 5\times 10^4\),\(1\leq m \le 5\times 10^5\),\(1\leq k \le 100\),\(0\leq w\leq 3\times 10^4\)。

如果做过这道题应该会觉得很简单了

我们把与 \(s\)相连的所有边标记为白边,其他边为黑边

然后问题就转化为了选恰好 \(k\)条白边的最小花费

考虑wqs二分,最好用归并优化一下

这题的难点在无解的情况如何判断

  • 白边不够用,如果与 \(s\)相连的边小于 \(k\) 条,无解

  • 黑边不够用,也就是生成树不得不包括大于 \(k\) 条白边,无解

时间复杂度 \(O(n\log n + n\log{\max\{w_i\}})\)

可以通过所有的hack数据,请放心食用 qwq

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*_p1,*_p2;    char readchar()    {        if(_p1==_p2)_p1=buf1,_p2=buf1+fread(buf1,1,SIZ,stdin);        return _p1==_p2?EOF:*_p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO; #define N (int)(5e4+15)#define M (int)(5e5+15)int n,m,s,k,p1,p2,cnt,sum,mx,f[N];struct Edge{int u,v,w,c;}e[M],c1[M],c2[M];void init(){for(int i=1; i<=n; i++)f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}int cmp(Edge a,Edge b){return a.w<b.w;}int merge(int u,int v){    u=find(u);v=find(v);    if(u==v)return 0;    return f[u]=v,1;}void proc(int mid){    for(int d=1; d<=p1; d++)        c1[d].w-=mid;    int i=1,j=1,pos=0;    for(; i<=p1; i++)    {        while(c1[i].w>c2[j].w&&j<=p2)            e[++pos]=c2[j++];        e[++pos]=c1[i];    }    while(j<=p2)e[++pos]=c2[j++];    for(int d=1; d<=p1; d++)        c1[d].w+=mid;}void kruskal(int mid){    proc(mid); cnt=sum=0;    int tmp=0; init();    for(int i=1; i<=m&&tmp<n; i++)        if(merge(e[i].u,e[i].v))        {            cnt+=e[i].c;            sum+=e[i].w;++tmp;        }}signed main(){    read(n);read(m);read(s);read(k);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        if(u==s||v==s)             c1[++p1]={u,v,w,1},mx=max(mx,w);        else c2[++p2]={u,v,w,0};    }    if(p1<k)return puts("Impossible"),0;    sort(c1+1,c1+p1+1,cmp);sort(c2+1,c2+p2+1,cmp);    int l=-mx-5,r=mx+5;    kruskal(l); if(cnt>k)return puts("Impossible"),0;    while(l<r)    {        int mid=(l+r)>>1;        kruskal(mid);        if(cnt>=k) r=mid;        else l=mid+1;    }    kruskal(l); write(sum+k*l);    return 0;}
]]>
@@ -4296,7 +4296,7 @@ /2022/06/17/luo-gu-p2619-guo-jia-ji-xun-dui-tree-i-ti-jie/ - 洛谷P2619 [国家集训队]Tree I 题解

题目链接:P2619 [国家集训队]Tree I

题意

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有 $\text{need}$ 条白色边的生成树。

题目保证有解。

对于 $100\%$ 的数据,$n\leq 5\times10^4,m\leq 10^5$。

所有数据边权为 $[1,100]$ 中的正整数。

直接 $O(nm)$ 的dp肯定是不行的,考虑wqs二分。

在这题中,wqs二分做的其实就是给白边增加额外权值

使最优方案恰好选满 $\text{need}$ 条白边 。

假设二分的权值为 $\text{mid}$ ,

若选了大于 $\text{need}$ 条白边,则将白边边权增大,反之同理。

具体的,设选 $x$ 条白边的最小花费 $f(x)$

注意到点集 $(x,f(x))$ 形成一个下凸壳

也就是 $f(x)$ 为一个整数域的下凸函数

我们二分一个斜率 $k$ ,用这条直线去尝试切凸壳

显然这条直线在不确定 $y$ 轴截距时可以与这个凸壳有很多交点,

由斜截式方程得 $y$ 轴截距公式为

代入得

而 $b(x)$ 就是所有白边的边权减去 $k$ 以后选 $x$ 条白边的最小花费(最小生成树)

然后不考虑 $x$ 的限制跑一遍kruskal求出一个修改后的最优解,

根据这个最优解(也就是最小的 $b(x)$ )对应的 $x$ 改变左右端点

因为边权在 $[1,100]$ 间,所以设个 $l=-111,r=111$ 即可

时间复杂度 $O(n\log n\log 222)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)struct Edge{    int u,v,w,c;}e[N];int n,m,need,cnt,sum,u[N],v[N],w[N],c[N],f[N];void init(){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}void kruskal(int mid){    cnt=sum=0;  init();    for(int i=1; i<=m; i++)        if(c[i])e[i]={u[i],v[i],w[i],c[i]};        else e[i]={u[i],v[i],w[i]-mid,c[i]};    sort(e+1,e+1+m,[](Edge a,Edge b)    {        return a.w==b.w?a.c<b.c:a.w<b.w;    });    int tmp=0;    for(int i=1; i<=m&&tmp<n; i++)    {        if(find(e[i].u)!=find(e[i].v))        {            merge(e[i].u,e[i].v);            ++tmp; cnt+=(e[i].c^1);            sum+=e[i].w;        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> need;    for(int i=1; i<=m; i++)    {        cin >> u[i] >> v[i] >> w[i] >> c[i];        u[i]++; v[i]++;    }    int l=-111,r=111,ans=0;    while(l<r)    {        int mid=(l+r)>>1;        kruskal(mid);        if(cnt>=need)        {            r=mid;            ans=sum+need*mid;        }else l=mid+1;    }    cout << ans << '\n';    return 0;}
]]>
+ 洛谷P2619 [国家集训队]Tree I题解

题目链接:P2619[国家集训队]Tree I

题意

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有\(\text{need}\) 条白色边的生成树。

题目保证有解。

对于 \(100\%\) 的数据,\(n\leq 5\times10^4,m\leq 10^5\)。

所有数据边权为 \([1,100]\)中的正整数。

直接 \(O(nm)\)的dp肯定是不行的,考虑wqs二分。

在这题中,wqs二分做的其实就是给白边增加额外权值

使最优方案恰好选满 \(\text{need}\)条白边 。

假设二分的权值为 \(\text{mid}\)

若选了大于 \(\text{need}\)条白边,则将白边边权增大,反之同理。

具体的,设选 \(x\) 条白边的最小花费\(f(x)\)

注意到点集 \((x,f(x))\)形成一个下凸壳

也就是 \(f(x)\)为一个整数域的下凸函数

我们二分一个斜率 \(k\),用这条直线去尝试切凸壳

显然这条直线在不确定 \(y\)轴截距时可以与这个凸壳有很多交点,

由斜截式方程得 \(y\) 轴截距公式为\[b=y-kx\] 代入得 \[b(x)=f(x)-kx\]\(b(x)\)就是所有白边的边权减去 \(k\) 以后选\(x\)条白边的最小花费(最小生成树)

然后不考虑 \(x\)的限制跑一遍kruskal求出一个修改后的最优解,

根据这个最优解(也就是最小的 \(b(x)\) )对应的 \(x\) 改变左右端点

因为边权在 \([1,100]\) 间,所以设个\(l=-111,r=111\) 即可

时间复杂度 \(O(n\log n\log222)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)struct Edge{    int u,v,w,c;}e[N];int n,m,need,cnt,sum,u[N],v[N],w[N],c[N],f[N];void init(){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}void kruskal(int mid){    cnt=sum=0;  init();    for(int i=1; i<=m; i++)        if(c[i])e[i]={u[i],v[i],w[i],c[i]};        else e[i]={u[i],v[i],w[i]-mid,c[i]};    sort(e+1,e+1+m,[](Edge a,Edge b)    {        return a.w==b.w?a.c<b.c:a.w<b.w;    });    int tmp=0;    for(int i=1; i<=m&&tmp<n; i++)    {        if(find(e[i].u)!=find(e[i].v))        {            merge(e[i].u,e[i].v);            ++tmp; cnt+=(e[i].c^1);            sum+=e[i].w;        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> need;    for(int i=1; i<=m; i++)    {        cin >> u[i] >> v[i] >> w[i] >> c[i];        u[i]++; v[i]++;    }    int l=-111,r=111,ans=0;    while(l<r)    {        int mid=(l+r)>>1;        kruskal(mid);        if(cnt>=need)        {            r=mid;            ans=sum+need*mid;        }else l=mid+1;    }    cout << ans << '\n';    return 0;}
]]>
@@ -4323,7 +4323,7 @@ /2022/06/17/luo-gu-p2607-zjoi2008-qi-shi-ti-jie/ - 洛谷P2607 [ZJOI2008] 骑士 题解

题目链接:P2607 [ZJOI2008] 骑士

题意

Z 国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。

最近发生了一件可怕的事情,邪恶的 Y 国发动了一场针对 Z 国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的 Z 国又怎能抵挡的住 Y 国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。

骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。

战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。

为了描述战斗力,我们将骑士按照 $1$ 至 $n$ 编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

对于 $100\%$ 的测试数据,满足 $1\le n \le 10^6$,每名骑士的战斗力都是不大于 $10^6$ 的正整数。

经典的基环树dp题

因为结点只有一个出边,所以一定是基环树

值得注意的是,这道题给出的是一个基环树森林

就多跑几遍就好了,时间复杂度 $O(n)$

这里是并查集判环写法,常数还算可以接受

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)struct Edge{    int u,v,next;}e[N<<1];vector< pair<int,int> >vec;int n,res,pos=1,head[N],val[N],f[N],dp[N][2];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void init(){for(int i=1; i<=n; i++)f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}void dfs(int u,int fa){    dp[u][0]=0;dp[u][1]=val[u];    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==fa)continue;        dfs(v,u);        dp[u][1]+=dp[v][0];        dp[u][0]+=max(dp[v][0],dp[v][1]);    }}signed main(){    read(n);    init();for(int i=1,v; i<=n; i++)    {        read(val[i]);read(v);        if(find(i)==find(v))        {            vec.push_back({i,v});            continue;        }        addEdge(i,v);addEdge(v,i);merge(i,v);    }    int res=0,s,t,tmp;    for(auto i : vec)    {        s=i.first,t=i.second;        dfs(s,s);tmp=dp[s][0];        dfs(t,t);tmp=max(tmp,dp[t][0]);        res+=tmp;    }    printf("%lld\n",res);    return 0;}
]]>
+ 洛谷P2607 [ZJOI2008] 骑士题解

题目链接:P2607[ZJOI2008] 骑士

题意

Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。

最近发生了一件可怕的事情,邪恶的 Y 国发动了一场针对 Z国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的 Z国又怎能抵挡的住 Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。

骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。

战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。

为了描述战斗力,我们将骑士按照 \(1\)\(n\)编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。

对于 \(100\%\) 的测试数据,满足\(1\le n \le10^6\),每名骑士的战斗力都是不大于 \(10^6\) 的正整数。

经典的基环树dp题

因为结点只有一个出边,所以一定是基环树

值得注意的是,这道题给出的是一个基环树森林

就多跑几遍就好了,时间复杂度 \(O(n)\)

这里是并查集判环写法,常数还算可以接受

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)struct Edge{    int u,v,next;}e[N<<1];vector< pair<int,int> >vec;int n,res,pos=1,head[N],val[N],f[N],dp[N][2];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void init(){for(int i=1; i<=n; i++)f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}void dfs(int u,int fa){    dp[u][0]=0;dp[u][1]=val[u];    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==fa)continue;        dfs(v,u);        dp[u][1]+=dp[v][0];        dp[u][0]+=max(dp[v][0],dp[v][1]);    }}signed main(){    read(n);    init();for(int i=1,v; i<=n; i++)    {        read(val[i]);read(v);        if(find(i)==find(v))        {            vec.push_back({i,v});            continue;        }        addEdge(i,v);addEdge(v,i);merge(i,v);    }    int res=0,s,t,tmp;    for(auto i : vec)    {        s=i.first,t=i.second;        dfs(s,s);tmp=dp[s][0];        dfs(t,t);tmp=max(tmp,dp[t][0]);        res+=tmp;    }    printf("%lld\n",res);    return 0;}
]]>
@@ -4337,10 +4337,10 @@ 算法 - DP - 图论 + DP + 数据结构 @@ -4354,7 +4354,7 @@ /2022/06/16/luo-gu-p1453-cheng-shi-huan-lu-ti-jie/ - 洛谷P1453 城市环路 题解

题目链接:P1453 城市环路

题意

整个城市可以看做一个 $n$ 个点,$n$ 条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有 $2$ 条简单路径互通。图中的其它部分皆隶属城市郊区。

现在,有一位名叫 Jim 的同学想在 B 市开店,但是任意一条边的 $2$ 个点不能同时开店,每个点都有一定的人流量,第 $i$ 个点的人流量是 $p_i$,在该点开店的利润就等于 $p_i×k$,其中 $k$ 是一个常数。

Jim 想尽量多的赚取利润,请问他应该在哪些地方开店?

  • 对于 $100\%$ 的数据,保证 $1 \leq n \leq 10^5$,$1 \leq p_i \leq 10^4$,$0 \leq u, v < n$,$0 \leq k \leq 10^4$,$k$ 的小数点后最多有 $6$ 位数字。

显然这是一个基环树dp

对于基环树dp,

基本思想就是在那个环上找两个相邻点

分别作为根节点跑树形dp

因为本题中这两个点不能同时选,所以这么做是可以得到正确答案的

转移方程太简单了,直接看代码吧

相信做这个题的不可能树形dp都不会吧。

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)struct Edge{    int u,v,next;}e[N<<1];int n,fa[N],f[N][2],p[N],head[N],pos=1,S,T;int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int Fa){    f[u][1]=p[u];f[u][0]=0;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==Fa)continue;        dfs(v,u);        f[u][0]+=max(f[v][0],f[v][1]);        f[u][1]+=f[v][0];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> p[i],fa[i]=i;    for(int i=1,u,v; i<=n; i++)    {        cin >> u >> v;++u;++v;        if(find(u)==find(v)){S=u,T=v;continue;}        addEdge(u,v);addEdge(v,u);        fa[find(u)]=find(v);    }    double k; cin >> k;    dfs(S,0); int res=f[S][0];    dfs(T,0); res=max(res,f[T][0]);    cout << fixed << setprecision(1);    cout << (double)res*k << '\n';    return 0;}
]]>
+ 洛谷P1453 城市环路 题解

题目链接:P1453城市环路

题意

整个城市可以看做一个 \(n\)个点,\(n\)条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有\(2\)条简单路径互通。图中的其它部分皆隶属城市郊区。

现在,有一位名叫 Jim 的同学想在 B 市开店,但是任意一条边的 \(2\)个点不能同时开店,每个点都有一定的人流量,第 \(i\) 个点的人流量是 \(p_i\),在该点开店的利润就等于 \(p_i×k\),其中 \(k\) 是一个常数。

Jim 想尽量多的赚取利润,请问他应该在哪些地方开店?

  • 对于 \(100\%\) 的数据,保证 \(1 \leq n \leq 10^5\),\(1 \leq p_i \leq 10^4\),\(0 \leq u, v < n\),\(0 \leq k \leq 10^4\),\(k\) 的小数点后最多有 \(6\) 位数字。

显然这是一个基环树dp

对于基环树dp,

基本思想就是在那个环上找两个相邻点

分别作为根节点跑树形dp

因为本题中这两个点不能同时选,所以这么做是可以得到正确答案的

转移方程太简单了,直接看代码吧

相信做这个题的不可能树形dp都不会吧。

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)struct Edge{    int u,v,next;}e[N<<1];int n,fa[N],f[N][2],p[N],head[N],pos=1,S,T;int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int Fa){    f[u][1]=p[u];f[u][0]=0;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==Fa)continue;        dfs(v,u);        f[u][0]+=max(f[v][0],f[v][1]);        f[u][1]+=f[v][0];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> p[i],fa[i]=i;    for(int i=1,u,v; i<=n; i++)    {        cin >> u >> v;++u;++v;        if(find(u)==find(v)){S=u,T=v;continue;}        addEdge(u,v);addEdge(v,u);        fa[find(u)]=find(v);    }    double k; cin >> k;    dfs(S,0); int res=f[S][0];    dfs(T,0); res=max(res,f[T][0]);    cout << fixed << setprecision(1);    cout << (double)res*k << '\n';    return 0;}
]]>
@@ -4381,7 +4381,7 @@ /2022/06/16/sp1716-gss3-can-you-answer-these-queries-iii-ti-jie/ - SP1716 GSS3 - Can you answer these queries III 题解

题目链接:SP1716 GSS3 - Can you answer these queries III

题意

$n$ 个数, $q$ 次操作

操作0 x y把 $A_x$ 修改为 $y$

操作1 l r询问区间 $[l, r]$ 的最大子段和

$n \le 5\times 10^4,~q \le 5 \times 10^4$

线段树维护最大子段和去年就看到了一直没去学

考虑两个相邻区间合并,设答案为 $\text{res}_u$

  • 若不考虑中间的分界线,则有

  • 若考虑中间的分界线,于是尝试合并,则?

可是我们并不知道 $\text{res}_l$ 和 $\text{res}_r$ 对应的序列,也不知道可否合并等等

注意到合并后的区间包含三种本质不同的子段

  • 全部在左区间
  • 全部在右区间
  • 被中间线分割

在进一步,中间线分割意味着什么?

意味着这是一段从左区间右端点出发的极大子段右区间左端点出发的极大子段合并后产生的极大子段

所以对于每个区间我们要记录从左端点出发的极大子段大小,和右端点出发的极大子段大小

不妨设这两个东西分别为 $\text{prel},\text{prer}$

同时我们还需要记录区间和、区间最大子段和 $\text{sum},\text{res}$

设当前要合并的左右两个区间分别为 $l,r$ ,合并后的区间为 $u$

则有

然后就可以上传信息啦

对于每个询问,也要维护这些信息,可以用结构体来搞

时间复杂度 $O(n \log n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e4+15)int n;int sum[N<<2],Q,a[N];int res[N<<2],prel[N<<2],prer[N<<2];struct qry{    int sum,prel,prer,res;}tmp;#define ls(at) (at<<1)#define rs(at) (at<<1|1)void push_up(int at){    int l=ls(at),r=rs(at);    sum[at]=sum[l]+sum[r];    prel[at]=max(prel[l],sum[l]+prel[r]);    prer[at]=max(prer[r],sum[r]+prer[l]);    res[at]=max(max(res[l],res[r]),prer[l]+prel[r]);}void build(int l,int r,int at){    if(l==r)    {        sum[at]=res[at]=prel[at]=prer[at]=a[l];        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));build(mid+1,r,rs(at));    push_up(at);}void modify(int x,int y,int l,int r,int at){    if(l==r)    {        sum[at]=res[at]=prel[at]=prer[at]=y;        return;    }    int mid=(l+r)>>1;    if(x<=mid) modify(x,y,l,mid,ls(at));    else modify(x,y,mid+1,r,rs(at));    push_up(at);}qry query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        return {sum[at],prel[at],prer[at],res[at]};    int mid=(l+r)>>1;    if(nr<=mid)return query(nl,nr,l,mid,ls(at));    if(nl>mid)return query(nl,nr,mid+1,r,rs(at));    qry L=query(nl,nr,l,mid,ls(at));    qry R=query(nl,nr,mid+1,r,rs(at));    tmp.sum=L.sum+R.sum;    tmp.prel=max(L.prel,L.sum+R.prel);    tmp.prer=max(R.prer,R.sum+L.prer);    tmp.res=max(max(L.res,R.res),L.prer+R.prel);    return tmp;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    read(Q);    while(Q--)    {        int op,x,y;        read(op);read(x);read(y);        if(op){write(query(x,y,1,n,1).res);pc('\n');}        else modify(x,y,1,n,1);    }    return 0;}
]]>
+ SP1716 GSS3- Can you answer these queries III 题解

题目链接:SP1716GSS3 - Can you answer these queries III

题意

\(n\) 个数, \(q\) 次操作

操作0 x y\(A_x\)修改为 \(y\)

操作1 l r询问区间 \([l,r]\) 的最大子段和

\(n \le 5\times 10^4,~q \le 5 \times10^4\)

线段树维护最大子段和去年就看到了一直没去学

考虑两个相邻区间合并,设答案为 \(\text{res}_u\)

  • 若不考虑中间的分界线,则有 \[\text{res}_u = \max\{\text{res}_l , \text{res}_r\}\]

  • 若考虑中间的分界线,于是尝试合并,则?

可是我们并不知道 \(\text{res}_l\)\(\text{res}_r\)对应的序列,也不知道可否合并等等

注意到合并后的区间包含三种本质不同的子段

  • 全部在左区间
  • 全部在右区间
  • 被中间线分割

在进一步,中间线分割意味着什么?

意味着这是一段从左区间右端点出发的极大子段右区间左端点出发的极大子段合并后产生的极大子段

所以对于每个区间我们要记录从左端点出发的极大子段大小,和右端点出发的极大子段大小

不妨设这两个东西分别为 \(\text{prel},\text{prer}\)

同时我们还需要记录区间和、区间最大子段和 \(\text{sum},\text{res}\)

设当前要合并的左右两个区间分别为 \(l,r\) ,合并后的区间为 \(u\)

则有 \[\text{sum}_u = \text{sum}_l + \text{sum}_r\\\text{prel}_u = \max\{\text{prel}_l,~\text{sum}_l+\text{prel}_r\}\\\text{prer}_u = \max\{\text{prer}_r,~\text{sum}_r+\text{prer}_l\}\\\text{res}_u =\max\{\text{res}_l,~\text{res}_r,~\text{prer}_l+\text{prel}_r\}\] 然后就可以上传信息啦

对于每个询问,也要维护这些信息,可以用结构体来搞

时间复杂度 \(O(n \log n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e4+15)int n;int sum[N<<2],Q,a[N];int res[N<<2],prel[N<<2],prer[N<<2];struct qry{    int sum,prel,prer,res;}tmp;#define ls(at) (at<<1)#define rs(at) (at<<1|1)void push_up(int at){    int l=ls(at),r=rs(at);    sum[at]=sum[l]+sum[r];    prel[at]=max(prel[l],sum[l]+prel[r]);    prer[at]=max(prer[r],sum[r]+prer[l]);    res[at]=max(max(res[l],res[r]),prer[l]+prel[r]);}void build(int l,int r,int at){    if(l==r)    {        sum[at]=res[at]=prel[at]=prer[at]=a[l];        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));build(mid+1,r,rs(at));    push_up(at);}void modify(int x,int y,int l,int r,int at){    if(l==r)    {        sum[at]=res[at]=prel[at]=prer[at]=y;        return;    }    int mid=(l+r)>>1;    if(x<=mid) modify(x,y,l,mid,ls(at));    else modify(x,y,mid+1,r,rs(at));    push_up(at);}qry query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        return {sum[at],prel[at],prer[at],res[at]};    int mid=(l+r)>>1;    if(nr<=mid)return query(nl,nr,l,mid,ls(at));    if(nl>mid)return query(nl,nr,mid+1,r,rs(at));    qry L=query(nl,nr,l,mid,ls(at));    qry R=query(nl,nr,mid+1,r,rs(at));    tmp.sum=L.sum+R.sum;    tmp.prel=max(L.prel,L.sum+R.prel);    tmp.prer=max(R.prer,R.sum+L.prer);    tmp.res=max(max(L.res,R.res),L.prer+R.prel);    return tmp;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    read(Q);    while(Q--)    {        int op,x,y;        read(op);read(x);read(y);        if(op){write(query(x,y,1,n,1).res);pc('\n');}        else modify(x,y,1,n,1);    }    return 0;}
]]>
@@ -4406,7 +4406,7 @@ /2022/06/16/luo-gu-p1083-noip2012-ti-gao-zu-jie-jiao-shi-ti-jie/ - 洛谷P1083 [NOIP2012 提高组] 借教室 题解

题目链接:P1083 [NOIP2012 提高组] 借教室

题意

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。

面对海量租借教室的信息,我们自然希望编程解决这个问题。

我们需要处理接下来 $n$ 天的借教室信息,其中第 $i$ 天学校有 $r_i$ 个教室可供租借。共有 $m$ 份订单,每份订单用三个正整数描述,分别为 $d_j,s_j,t_j$,表示某租借者需要从第 $s_j$ 天到第 $t_j$ 天租借教室(包括第 $s_j$ 天和第 $t_j$ 天),每天需要租借 $d_j$ 个教室。

我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供 $d_j$ 个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第 $s_j$ 天到第 $t_j$ 天中有至少一天剩余的教室数量不足 $d_j$ 个。

现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

对于 100% 的数据,有$1 ≤ n,m ≤ 10^6,0 ≤ r_i,d_j≤ 10^9,1 ≤ s_j≤ t_j≤ n$

考虑差分处理每个需求

然后 $O(n)$ 扫一遍,如果不合法,就从最后一个人开始消去影响

直到某个人消去以后没问题了,这个人就是罪魁祸首,输出他就好了

为什么倒着消去影响呢?因为罪魁祸首是所有导致不合法的人里面最靠前的

我们只要求这个不合法的人,因此他后面的人不会影响他的坏(感性理解即可

时间复杂度 $O(n+m)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n,m,a[N],b[N],c[N],l[N],r[N];signed main(){    read(n);read(m);    for(int i=1; i<=n; i++)         read(a[i]);    for(int i=1; i<=m; i++)    {        read(c[i]);read(l[i]);read(r[i]);        b[l[i]]+=c[i];b[r[i]+1]-=c[i];    }    int j=m,res=m,sum=0;    for(int i=1; i<=n; i++)    {        sum+=b[i];        if(sum>a[i])        {            while(sum>a[i])            {                b[l[j]]-=c[j];                b[r[j]+1]+=c[j];                if(l[j]<=i&&i<=r[j])                    sum-=c[j];                j--;            }            res=j;        }    }    if(res<m)printf("-1\n%lld",res+1);    else puts("0");    return 0;}
]]>
+ 洛谷P1083 [NOIP2012提高组] 借教室 题解

题目链接:P1083[NOIP2012 提高组] 借教室

题意

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。

面对海量租借教室的信息,我们自然希望编程解决这个问题。

我们需要处理接下来 \(n\)天的借教室信息,其中第 \(i\) 天学校有\(r_i\) 个教室可供租借。共有 \(m\)份订单,每份订单用三个正整数描述,分别为 \(d_j,s_j,t_j\),表示某租借者需要从第 \(s_j\) 天到第 \(t_j\) 天租借教室(包括第 \(s_j\) 天和第 \(t_j\) 天),每天需要租借 \(d_j\) 个教室。

我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供\(d_j\)个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第\(s_j\) 天到第 \(t_j\) 天中有至少一天剩余的教室数量不足\(d_j\) 个。

现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

对于 100% 的数据,有\(1 ≤ n,m ≤ 10^6,0 ≤r_i,d_j≤ 10^9,1 ≤ s_j≤ t_j≤ n\)

考虑差分处理每个需求

然后 \(O(n)\)扫一遍,如果不合法,就从最后一个人开始消去影响

直到某个人消去以后没问题了,这个人就是罪魁祸首,输出他就好了

为什么倒着消去影响呢?因为罪魁祸首是所有导致不合法的人里面最靠前的

我们只要求这个不合法的人,因此他后面的人不会影响他的坏(感性理解即可

时间复杂度 \(O(n+m)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n,m,a[N],b[N],c[N],l[N],r[N];signed main(){    read(n);read(m);    for(int i=1; i<=n; i++)         read(a[i]);    for(int i=1; i<=m; i++)    {        read(c[i]);read(l[i]);read(r[i]);        b[l[i]]+=c[i];b[r[i]+1]-=c[i];    }    int j=m,res=m,sum=0;    for(int i=1; i<=n; i++)    {        sum+=b[i];        if(sum>a[i])        {            while(sum>a[i])            {                b[l[j]]-=c[j];                b[r[j]+1]+=c[j];                if(l[j]<=i&&i<=r[j])                    sum-=c[j];                j--;            }            res=j;        }    }    if(res<m)printf("-1\n%lld",res+1);    else puts("0");    return 0;}
]]>
@@ -4431,7 +4431,7 @@ /2022/06/16/luo-gu-p4552-poetize6-incdec-sequence-ti-jie/ - 洛谷P4552 [Poetize6] IncDec Sequence 题解

题目链接:P4552 [Poetize6] IncDec Sequence

题意

给定一个长度为 $n$ 的数列 ${a_1,a_2,\cdots,a_n}$,每次可以选择一个区间$[l,r]$,使这个区间内的数都加 $1$ 或者都减 $1$。

请问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。

对于 $100\%$ 的数据,$n\le 100000, 0 \le a_i \le 2^{31}$。

这道题思路比较有趣,代码很简单但是相对来说不好想

本文比较长,建议耐心阅读(当然也可以直接 ⌘+w 或者 ctrl+w

差分,常用于 $O(1)$ 区间加减,全局询问

对于这种看上去也不是用数据结构做的题目,

考虑差分。差分数组 $b_i$ 的定义为 $b_i = a_i-a_{i-1}$

容易发现 $a_i = \sum_{1 \le j \le i}b_i$ ,值得注意的是, $a_1 = b_1$

对于区间 $[l,r]$ 加 $1$ 的修改,只要把 $b_l$ 加上 $1$ ,$b_{r+1}$ 减去 $1$ 就可以了

然后问题就转化为了将 $b_2,b_3,\dots,b_n$ 都变成 $0$ 的最小花费

观察差分的性质,其实每次修改就是在搬运一个 $1$ ,

也就是把它从 $b_l$ 搬到 $b_{r+1}$ ,或者反过来搬

对于每个合法的修改,一定可以完成这样的操作

可以发现当 $r=n$ 时会出现从外面搬过来或者搬到外面去的情况

先不急,我们先考虑 $r<n$ 时

显然我们可以把所有的 $b_i<0$ 和 $b_j >0$ 配对( $2 \le i \le j \le n$ ),然后相互抵消

可以发现这样配对消去的最小花费为 $\min(p,q)$

其中 $p= \sum_{1 \le i \le n} [b_i<0],~q= \sum_{1 \le i \le n} [b_i>0]$

这样配对也会出现最后消不完的情况,

这个时候我们就可以把它们与 $n+1$ 配对,或者 $b_1$

这样的答案是 $\operatorname{abs}(p-q)$

则最后的答案就是 $\min(p,q)+\operatorname{abs}(p-q)=\max(p,q)$

再看第二问,其实很简单

看我们的最后一步,如果和 $b_1$ 配对 $k$ 次,最后的 $a_i$ 就会变成 $b_1+k$

所以方案数就是 $\operatorname{abs}(p-q)+1$ (不与 $b_1$ 配对也是一种情况)

然后就好了,时间复杂度 $O(n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,p,q,a[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    for(int i=2; i<=n; i++)    {        int c=a[i]-a[i-1];        c>0?p+=c:q-=c;    }    cout << max(p,q) << '\n' << abs(p-q)+1 << '\n';    return 0;}
]]>
+ 洛谷P4552 [Poetize6]IncDec Sequence 题解

题目链接:P4552[Poetize6] IncDec Sequence

题意

给定一个长度为 \(n\) 的数列 \({a_1,a_2,\cdots,a_n}\),每次可以选择一个区间\([l,r]\),使这个区间内的数都加 \(1\) 或者都减 \(1\)。

请问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。

对于 \(100\%\) 的数据,\(n\le 100000, 0 \le a_i \le 2^{31}\)。

这道题思路比较有趣,代码很简单但是相对来说不好想

本文比较长,建议耐心阅读(当然也可以直接 ⌘+w 或者ctrl+w

差分,常用于 \(O(1)\)区间加减,全局询问

对于这种看上去也不是用数据结构做的题目,

考虑差分。差分数组 \(b_i\) 的定义为\(b_i = a_i-a_{i-1}\)

容易发现 \(a_i = \sum_{1 \le j \lei}b_i\) ,值得注意的是, \(a_1 =b_1\)

对于区间 \([l,r]\)\(1\) 的修改,只要把 \(b_l\) 加上 \(1\) ,\(b_{r+1}\) 减去 \(1\) 就可以了

然后问题就转化为了将 \(b_2,b_3,\dots,b_n\) 都变成 \(0\) 的最小花费

观察差分的性质,其实每次修改就是在搬运一个 \(1\) ,

也就是把它从 \(b_l\) 搬到 \(b_{r+1}\) ,或者反过来搬

对于每个合法的修改,一定可以完成这样的操作

可以发现当 \(r=n\)时会出现从外面搬过来或者搬到外面去的情况

先不急,我们先考虑 \(r<n\)

显然我们可以把所有的 \(b_i<0\)\(b_j >0\) 配对( \(2 \le i \le j \le n\) ),然后相互抵消

可以发现这样配对消去的最小花费为 \(\min(p,q)\)

其中 \(p= \sum_{1 \le i \le n}[b_i<0],~q= \sum_{1 \le i \le n} [b_i>0]\)

这样配对也会出现最后消不完的情况,

这个时候我们就可以把它们与 \(n+1\)配对,或者 \(b_1\)

这样的答案是 \(\operatorname{abs}(p-q)\)

则最后的答案就是 \(\min(p,q)+\operatorname{abs}(p-q)=\max(p,q)\)

再看第二问,其实很简单

看我们的最后一步,如果和 \(b_1\)配对 \(k\) 次,最后的 \(a_i\) 就会变成 \(b_1+k\)

所以方案数就是 \(\operatorname{abs}(p-q)+1\) (不与 \(b_1\) 配对也是一种情况)

然后就好了,时间复杂度 \(O(n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n,p,q,a[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> a[i];    for(int i=2; i<=n; i++)    {        int c=a[i]-a[i-1];        c>0?p+=c:q-=c;    }    cout << max(p,q) << '\n' << abs(p-q)+1 << '\n';    return 0;}
]]>
@@ -4456,7 +4456,7 @@ /2022/06/16/luo-gu-p5521-yloi2019-mei-shen-bu-jian-dong-ti-jie/ - 洛谷P5521 [yLOI2019] 梅深不见冬 题解

题目链接:P5521 [yLOI2019] 梅深不见冬

题意

给定一棵树,根节点为 $1$ ,有点权 $w_i$ ,在结点上放满梅花

放的要求是,只有一个结点 $u$ 的所有儿子 $v$ 都放满了 $w_v$ 梅花才可以放

梅花可以随时收回,求每个结点放梅花的最小花费


这个背景蛮有趣的,留着(逃

风,吹起梅岭的深冬;霜,如惊涛一样汹涌;
雪,飘落后把所有烧成空,像这场,捕捉不到的梦。
醒来时已是多年之久,宫门铜环才长了铁锈,也开始生出离愁。

——银临《梅深不见冬》

$1 \leq n \leq 10^5 + 4,~1 \leq w_i \leq 1000$。

设第 $i$ 个结点放梅花的最小花费为 $f_i$ ,不难发现

后者是最小花费的下界

观察第一部分

这部分显然与 $\text{son}(u)$ 的排列有关

如何求出这个最优排列?

贪心,按 $g_u = f_u - w_u$ 降序排序即可

考虑 $u$ 仅有 $2$ 个子结点 $x,y$ 的情况,

设 $x$ 在 $y$ 之前更优,则

  • 若 $f_y + w_x \ge f_x$ ,

    因为 $f_y + w_x > f_y$ ,所以右侧取 $f_x+w_y$ 才可能成立

    则可得

    移项得

  • 若 $f_y + w_x < f_x$

    则 $f_x > f_y$,故右侧取 $f_x+w_y$ 时才可能成立

    显然有 $f_x < f_x + w_y$

综上所述,按 $g_u = f_u - w_u$ 降序排序可以获得最优解

时间复杂度 $O(n \log n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)vector<int> son[N];int n,m,w[N],f[N];int cmp(int x,int y){return f[y]-w[y]<f[x]-w[x];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=2,u; i<=n; i++)        cin >> u,son[u].push_back(i);    for(int i=1; i<=n; i++) cin >> w[i];    for(int i=n; i>=1; i--)    {        sort(son[i].begin(),son[i].end(),cmp);        int sum=0;        for(int v : son[i])            f[i]=max(f[i],f[v]+sum),sum+=w[v];        f[i]=max(f[i],sum+w[i]);    }    for(int i=1; i<=n; i++)        cout << f[i] << " \n"[i==n];    return 0;}
]]>
+ 洛谷P5521 [yLOI2019]梅深不见冬 题解

题目链接:P5521[yLOI2019] 梅深不见冬

题意

给定一棵树,根节点为 \(1\) ,有点权\(w_i\) ,在结点上放满梅花

放的要求是,只有一个结点 \(u\)所有儿子 \(v\)都放满了 \(w_v\) 梅花才可以放

梅花可以随时收回,求每个结点放梅花的最小花费


这个背景蛮有趣的,留着(逃

风,吹起梅岭的深冬;霜,如惊涛一样汹涌;
雪,飘落后把所有烧成空,像这场,捕捉不到的梦。
醒来时已是多年之久,宫门铜环才长了铁锈,也开始生出离愁。

——银临《梅深不见冬》

\(1 \leq n \leq 10^5 + 4,~1 \leq w_i \leq1000\)

设第 \(i\) 个结点放梅花的最小花费为\(f_i\) ,不难发现 \[f_u = \max\left\{ \max_{v \in \text{son(u)}} \left\{f_v + \sum_{k \in\text{son}(u)\, \land \,k \text{ before } v} w_k\right\},w_u + \sum_{v\in \text{son}(u)}w_v\right\}\] 后者是最小花费的下界

观察第一部分 \[\max_{v \in \text{son(u)}} \left\{f_v + \sum_{k \in \text{son}(u)\,\land \,k \text{ before } v} w_k\right\}\] 这部分显然与 \(\text{son}(u)\) 的排列有关

如何求出这个最优排列?

贪心,按 \(g_u = f_u - w_u\)降序排序即可

考虑 \(u\) 仅有 \(2\) 个子结点 \(x,y\) 的情况,

\(x\)\(y\) 之前更优,则 \[f_u=\max\{f_x,f_y+w_x\}\le \max\{f_y,f_x+w_y\}\]

  • \(f_y + w_x \ge f_x\)

    因为 \(f_y + w_x > f_y\),所以右侧取 \(f_x+w_y\) 才可能成立

    则可得 \[f_y + w_x \le f_x + w_y\] 移项得 \[f_x-w_x \le f_y-w_y\]

  • \(f_y + w_x < f_x\)

    \(f_x > f_y\),故右侧取 \(f_x+w_y\) 时才可能成立

    显然有 \(f_x < f_x +w_y\)

综上所述,按 \(g_u = f_u - w_u\)降序排序可以获得最优解

时间复杂度 \(O(n \log n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)vector<int> son[N];int n,m,w[N],f[N];int cmp(int x,int y){return f[y]-w[y]<f[x]-w[x];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=2,u; i<=n; i++)        cin >> u,son[u].push_back(i);    for(int i=1; i<=n; i++) cin >> w[i];    for(int i=n; i>=1; i--)    {        sort(son[i].begin(),son[i].end(),cmp);        int sum=0;        for(int v : son[i])            f[i]=max(f[i],f[v]+sum),sum+=w[v];        f[i]=max(f[i],sum+w[i]);    }    for(int i=1; i<=n; i++)        cout << f[i] << " \n"[i==n];    return 0;}
]]>
@@ -4485,7 +4485,7 @@ /2022/06/16/luo-gu-p3131-usaco16jan-subsequences-summing-to-sevens-s-ti-jie/ - 洛谷P3131 [USACO16JAN]Subsequences Summing to Sevens S 题解

题目链接:P3131 [USACO16JAN]Subsequences Summing to Sevens S

题意:给你n个数,分别是a[1],a[2],…,a[n]。求一个最长的区间[x,y],使得区间中的数(a[x],a[x+1],a[x+2],…,a[y-1],a[y])的和能被7整除。输出区间长度。若没有符合要求的区间,输出0。

属于简单萌萌题叭。思路还是挺有趣的。

区间和用个前缀和 $S_i$ ,直接枚举 $l,r$ 肯定不行

这里用个小技巧,如果存在 $j < i$ 满足

所以我们把所有前缀和都去模一下 $7$ 然后找一下最长的区间就好啦

时间复杂度 $O(n)$

注意这里有个坑,$j$ 可以为 $0$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)int n,sum[N],res=-INF,p[15],q[15];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> sum[i];        sum[i]=(sum[i]+sum[i-1])%7;    }    for(int i=n; i>=1; i--) p[sum[i]]=i; // left    for(int i=1; i<=n; i++) q[sum[i]]=i; // right    p[0]=0; // 重要    for(int i=0; i<=6; i++)        if(q[i]) res=max(res,q[i]-p[i]);    cout << res << '\n';    return 0;}
]]>
+ 洛谷P3131[USACO16JAN]Subsequences Summing to Sevens S 题解

题目链接:P3131[USACO16JAN]Subsequences Summing to Sevens S

题意:给你n个数,分别是a[1],a[2],...,a[n]。求一个最长的区间[x,y],使得区间中的数(a[x],a[x+1],a[x+2],...,a[y-1],a[y])的和能被7整除。输出区间长度。若没有符合要求的区间,输出0。

属于简单萌萌题叭。思路还是挺有趣的。

区间和用个前缀和 \(S_i\) ,直接枚举\(l,r\) 肯定不行

这里用个小技巧,如果存在 \(j <i\) 满足 \[S_i \equiv S_j \pmod 7\]\[S_i-S_j \equiv 0 \pmod 7\] 所以我们把所有前缀和都去模一下 \(7\) 然后找一下最长的区间就好啦

时间复杂度 \(O(n)\)

注意这里有个坑,\(j\) 可以为 \(0\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e4+15)int n,sum[N],res=-INF,p[15],q[15];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> sum[i];        sum[i]=(sum[i]+sum[i-1])%7;    }    for(int i=n; i>=1; i--) p[sum[i]]=i; // left    for(int i=1; i<=n; i++) q[sum[i]]=i; // right    p[0]=0; // 重要    for(int i=0; i<=6; i++)        if(q[i]) res=max(res,q[i]-p[i]);    cout << res << '\n';    return 0;}
]]>
@@ -4512,7 +4512,7 @@ /2022/06/16/luo-gu-p2678-noip2015-ti-gao-zu-tiao-shi-tou-ti-jie/ - 洛谷P2678 [NOIP2015 提高组] 跳石头 题解

题目链接:P2678 [NOIP2015 提高组] 跳石头

题意

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 $N$ 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 $M$ 块岩石(不能移走起点和终点的岩石)。

对于 $100\%$的数据,$0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000$。

求最小值最大,二分经典题。

为什么是二分呢?因为我们并不知道最小值是多少

我们可以枚举一个最小值,然后使它尽可能的大

对于有单调性的题目可以使用二分,显然这题有单调性

然后就没啥了,二分一下就好了

注意终点不是 $N$ 。时间复杂度 $O(N \log L)$

相信大家做这题的时候应该二分不太熟,

这里给一个二分的板子(我是在《算法竞赛进阶指南》上学到的)


  • 求枚举值的最小值,则符合条件就使r=mid

    表示答案在 $[l,\text{mid}]$ 间,注意这里 $\text{mid}$ 是可行的。

    例如:在单调递增序列 $a$ 中查找 $\ge x$ 的数中最小的一个 ( $x$ 或 $x$ 的后继)

    while(l<r){    int mid=(l+r)/2; // (l+r)>>1;    if(a[mid] >= x) r=mid;    else l=mid+1;}return a[l];
  • 求枚举值的最大值,则符合条件就使l=mid

    表示答案在 $[\text{mid},r]$ 间,注意这里 $\text{mid}$ 是可行的。

    例如:在单调递增序列 $a$ 中查找 $\le x$ 的数中最大的一个( $x$ 或 $x$ 的前驱)

    while(l<r){    int mid=(l+r+1)/2; // (l+r+1)>>1;    if(a[mid] <= x) l=mid;    else r=mid-1;}return a[l];

题目代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)int n,m,d,a[N],l,r,mid;int ck(int x){    int tot=0,i=0,now=0;    while(i++ <= n)    {        if(a[i]-a[now]<x) ++tot;        else now=i;    }    return tot<=m; }signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> d >> n >> m;    for(int i=1; i<=n; i++)        cin >> a[i];    a[n+1]=d;    l=1;r=d;    while(l<r)    {        int mid=(l+r+1)>>1;        if(ck(mid))l=mid;        else r=mid-1;    }    cout << l << '\n';    return 0;}
]]>
+ 洛谷P2678 [NOIP2015提高组] 跳石头 题解

题目链接:P2678[NOIP2015 提高组] 跳石头

题意

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有\(N\)块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走\(M\)块岩石(不能移走起点和终点的岩石)。

对于 \(100\%\)的数据,\(0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤1,000,000,000\)。

求最小值最大,二分经典题。

为什么是二分呢?因为我们并不知道最小值是多少

我们可以枚举一个最小值,然后使它尽可能的大

对于有单调性的题目可以使用二分,显然这题有单调性

然后就没啥了,二分一下就好了

注意终点不是 \(N\) 。时间复杂度\(O(N \log L)\)

相信大家做这题的时候应该二分不太熟,

这里给一个二分的板子(我是在《算法竞赛进阶指南》上学到的)


  • 求枚举值的最小值,则符合条件就使r=mid

    表示答案在 \([l,\text{mid}]\)间,注意这里 \(\text{mid}\)是可行的。

    例如:在单调递增序列 \(a\) 中查找\(\ge x\) 的数中最小的一个 ( \(x\) 或 \(x\) 的后继)

    while(l<r){    int mid=(l+r)/2; // (l+r)>>1;    if(a[mid] >= x) r=mid;    else l=mid+1;}return a[l];
  • 求枚举值的最大值,则符合条件就使l=mid

    表示答案在 \([\text{mid},r]\)间,注意这里 \(\text{mid}\)是可行的。

    例如:在单调递增序列 \(a\) 中查找\(\le x\) 的数中最大的一个( \(x\) 或 \(x\) 的前驱)

    while(l<r){    int mid=(l+r+1)/2; // (l+r+1)>>1;    if(a[mid] <= x) l=mid;    else r=mid-1;}return a[l];

题目代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)int n,m,d,a[N],l,r,mid;int ck(int x){    int tot=0,i=0,now=0;    while(i++ <= n)    {        if(a[i]-a[now]<x) ++tot;        else now=i;    }    return tot<=m; }signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> d >> n >> m;    for(int i=1; i<=n; i++)        cin >> a[i];    a[n+1]=d;    l=1;r=d;    while(l<r)    {        int mid=(l+r+1)>>1;        if(ck(mid))l=mid;        else r=mid-1;    }    cout << l << '\n';    return 0;}
]]>
@@ -4537,7 +4537,7 @@ /2022/06/16/luo-gu-p1314-noip2011-ti-gao-zu-cong-ming-de-zhi-jian-yuan-ti-jie/ - 洛谷P1314 [NOIP2011 提高组] 聪明的质监员 题解

题目链接:P1314 [NOIP2011 提高组] 聪明的质监员

题意

小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 $n$ 个矿石,从 $1$ 到 $n$ 逐一编号,每个矿石都有自己的重量 $w_i$ 以及价值 $v_i$ 。检验矿产的流程是:

1 、给定 $m$ 个区间 $[l_i,r_i]$;

2 、选出一个参数 $W$;

3 、对于一个区间 $[l_i,r_i]$,计算矿石在这个区间上的检验值 $y_i$:

其中 $j$ 为矿石编号。

这批矿产的检验结果 $y$ 为各个区间的检验值之和。即:$\sum\limits_{i=1}^m y_i$

若这批矿产的检验结果与所给标准值 $s$ 相差太多,就需要再去检验另一批矿产。小T 不想费时间去检验另一批矿产,所以他想通过调整参数 $W$ 的值,让检验结果尽可能的靠近标准值 $s$,即使得 $|s-y|$ 最小。请你帮忙求出这个最小值。

对于 $100\%$ 的数据,有 $1 ≤n ,m≤200,000$,$0 < w_i,v_i≤10^6$,$0 < s≤10^{12}$,$1 ≤l_i ≤r_i ≤n$ 。

注意到其实我们就是要算这个式子

$\sum_{j=l_i}^{r_i}[w_j \ge W]$ 是可以前缀和优化的

但是显然我们要知道 $W$ 才能计算这个柿子

考虑二分一个 $W \in [0,w_{\max}]$

对于绝对值的二分,我们直接拆掉绝对值

  • 当 $s-y<0$ 时, $y$ 减小, $W$ 增大。
  • 当 $s-y>0$ 时, $y$ 增大, $W$ 减小。

  • 当 $s-y=0$ 时 ,直接退出即可。

答案只要在二分的过程中卑微的记录一下就好了

时间复杂度 $O(n \log W)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,m,s,l,r,mid;int w[N],v[N],le[N],ri[N],res=INF,p[N],q[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> s;    for(int i=1; i<=n; i++)    {        cin >> w[i] >> v[i];        r=max(r,w[i]);    }    for(int i=1; i<=m; i++)        cin >> le[i] >> ri[i];    while(l<r)    {        int mid=(l+r+1)>>1;        for(int i=1; i<=n; i++)        {            if(w[i]>mid)                q[i]=q[i-1]+1,p[i]=p[i-1]+v[i];            else                 q[i]=q[i-1],p[i]=p[i-1];        }        int x=0;        for(int i=1; i<=m; i++)            x+=(q[ri[i]]-q[le[i]-1])*(p[ri[i]]-p[le[i]-1]);        int t=s-x;        if(t<0)l=mid;        else if(!t)return cout << 0,0;        else r=mid-1;        res=min(res,abs(t));    }    cout << res << '\n';    return 0;}
]]>
+ 洛谷P1314 [NOIP2011提高组] 聪明的质监员 题解

题目链接:P1314[NOIP2011 提高组] 聪明的质监员

题意

小T是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 \(n\) 个矿石,从 \(1\) 到 \(n\) 逐一编号,每个矿石都有自己的重量 \(w_i\) 以及价值 \(v_i\) 。检验矿产的流程是:

1 、给定 \(m\) 个区间 \([l_i,r_i]\);

2 、选出一个参数 \(W\)

3 、对于一个区间 \([l_i,r_i]\),计算矿石在这个区间上的检验值\(y_i\)\[y_i=\sum\limits_{j=l_i}^{r_i}[w_j \ge W] \times\sum\limits_{j=l_i}^{r_i}[w_j \ge W]v_j\] 其中 \(j\) 为矿石编号。

这批矿产的检验结果 \(y\)为各个区间的检验值之和。即:\(\sum\limits_{i=1}^m y_i\)

若这批矿产的检验结果与所给标准值 \(s\)相差太多,就需要再去检验另一批矿产。小T不想费时间去检验另一批矿产,所以他想通过调整参数 \(W\) 的值,让检验结果尽可能的靠近标准值\(s\),即使得 \(|s-y|\) 最小。请你帮忙求出这个最小值。

对于 \(100\%\) 的数据,有 \(1 ≤n ,m≤200,000\),\(0 < w_i,v_i≤10^6\),\(0 < s≤10^{12}\),\(1 ≤l_i ≤r_i ≤n\) 。

注意到其实我们就是要算这个式子 \[\left|s-\sum_{i=1}^{m}\sum_{j=l_i}^{r_i}[w_j \geW]\sum_{j=l_i}^{r_i}[w_j \ge W]v_j\right|_{\min}\] \(\sum_{j=l_i}^{r_i}[w_j \geW]\) 是可以前缀和优化的

但是显然我们要知道 \(W\)才能计算这个柿子

考虑二分一个 \(W \in[0,w_{\max}]\)

对于绝对值的二分,我们直接拆掉绝对值

  • \(s-y<0\) 时, \(y\) 减小, \(W\) 增大。

  • \(s-y>0\) 时, \(y\) 增大, \(W\) 减小。

  • \(s-y=0\) 时,直接退出即可。

答案只要在二分的过程中卑微的记录一下就好了

时间复杂度 \(O(n \log W)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,m,s,l,r,mid;int w[N],v[N],le[N],ri[N],res=INF,p[N],q[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> s;    for(int i=1; i<=n; i++)    {        cin >> w[i] >> v[i];        r=max(r,w[i]);    }    for(int i=1; i<=m; i++)        cin >> le[i] >> ri[i];    while(l<r)    {        int mid=(l+r+1)>>1;        for(int i=1; i<=n; i++)        {            if(w[i]>mid)                q[i]=q[i-1]+1,p[i]=p[i-1]+v[i];            else                 q[i]=q[i-1],p[i]=p[i-1];        }        int x=0;        for(int i=1; i<=m; i++)            x+=(q[ri[i]]-q[le[i]-1])*(p[ri[i]]-p[le[i]-1]);        int t=s-x;        if(t<0)l=mid;        else if(!t)return cout << 0,0;        else r=mid-1;        res=min(res,abs(t));    }    cout << res << '\n';    return 0;}
]]>
@@ -4564,7 +4564,7 @@ /2022/06/08/luo-gu-p3647-apio2014-lian-zhu-xian-ti-jie/ - 洛谷P3647 [APIO2014] 连珠线 题解

题目链接:P3647 [APIO2014] 连珠线

题意

在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为 $1$ 到 $n$。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:

Append(w, v):一个新的珠子 $w$ 和一个已经添加的珠子 $v$ 用红线连接起来。

Insert(w, u, v):一个新的珠子 $w$ 插入到用红线连起来的两个珠子 $u, v$ 之间。具体过程是删去 $u, v$ 之间红线,分别用蓝线连接 $u, w$ 和 $w, v$。

每条线都有一个长度。游戏结束后,你的最终得分为蓝线长度之和。

给你连珠线游戏结束后的游戏局面,只告诉了你珠子和链的连接方式以及每条线的长度,没有告诉你每条线分别是什么颜色。

你需要写一个程序来找出最大可能得分。即,在所有以给出的最终局面结束的连珠线游戏中找出那个得分最大的,然后输出最大可能得分。

$1 \leq n \leq 200000$。

开学前两天准备狂刷题,结果在这题上卡了一下午(😭

本文主要参考了link,所以主要是自己的总结型题解(q779太菜了

容易发现这是一道树形dp

首先可以想到一个瞎七搭八的dp,也就是设结点 $u$ 和父亲的连边涂什么颜色

但是这个dp不好判断合法性,这篇博客讲的比较详细,不过这个也比较容易发现

于是考虑避免那种“/\形”的蓝色边,只考虑“\类型”的,也就是fa[u]-u-son[u]的

可以发现此时如果固定了一个结点做根,就可以避免“/\形”的蓝色边出现

同时也方便dp了(并且有一种经典换根dp的感觉了,虽然我当时看不出

设 $f[u][0/1]$ 表示 $u$ 所在子树中,$u$ 作为或不作为蓝线的中点是能得到的最大价值

前者比较简单,不是蓝线中点,则可以连蓝线中点,也可以连红线。

后者较为麻烦,首先它和前者唯一的区别在于,它必须有一个儿子作为这条蓝线的终点。显然这个儿子是某个 $f[v][0]$ 。因此我们可以把 $f[u][0]$ 的直接初始化到 $f[u][1]$ 上,然后去掉这个 $v$ 的贡献,并加上它的新贡献。

有点长,不过后面那个max就是 $f[u][0]$ 里面的那个max

然后我们就有了一个固定根情况下的dp,时间复杂度 $O(n^2)$ ,还过不了

然后这里就要换根dp发挥作用了。

我们考虑一个点 $u$ 的儿子变成了 $u$ 的父亲会有什么影响

首先这个儿子对 $u$ 的贡献没了,并且有可能转移方程中的最大值也没了。

(于是我们记录一个次大值,貌似是经典套路)

然后 $u$ 会变成新的儿子给原来的儿子(现在的父亲)贡献

方便起见,我们在第一遍dp的时候用vector记录一个 $g[u][0/1][j]$

表示对于 $u$ ,不考虑第 $j$ 个儿子的贡献时能获得的最大价值

$g[u][0][j]$ 直接减去 $j$ 的贡献就好了,

对于 $g[u][1][j]$ ,如果 $j$ 恰好是最大值,那就加上次大值,否则不变

这里的最大值、次大值就是指 $(2)$ 里的

换根的过程中,枚举 $u$ 的儿子作为整棵树的根。值得注意的是,换根以后 $u$ 的父亲就会变成 $u$ 的儿子。因此我们要先重新算出 $u$ 的父亲对 $u$ 的贡献,然后再换根

时间复杂度 $O(n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)struct Edge{    int u,v,w,next;}e[N<<1];int pos=1,head[N];int n,ans,fa[N],len[N],f[N][2];vector<int> son[N],g[N][2],mx[N]; // mx[u][i] 表示u所在子树不考虑i时的maxvoid addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dfs1(int u,int Fa){    f[u][0]=0;f[u][1]=-INF;    int mx1=-INF,mx2=-INF;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v,w=e[i].w;        if(v==Fa)continue;        len[v]=w;fa[v]=u;        son[u].push_back(v);        dfs1(v,u);        f[u][0]+=max(f[v][0],f[v][1]+w);        int t=f[v][0]+w-max(f[v][0],f[v][1]+w);        if(t>mx1)swap(mx1,mx2),mx1=t;        else if(t>mx2)mx2=t; // 次大值    }    f[u][1]=f[u][0]+mx1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v,w=e[i].w;        if(v==Fa)continue;        g[u][0].push_back(f[u][0]-max(f[v][0],f[v][1]+w));        if(f[v][0]+w-max(f[v][0],f[v][1]+w)==mx1)        {            g[u][1].push_back(g[u][0].back()+mx2);            mx[u].push_back(mx2);        }else        {            g[u][1].push_back(g[u][0].back()+mx1);            mx[u].push_back(mx1);        }    }}void dfs2(int u){    for(int i=0; i<son[u].size(); i++)    {        f[u][0]=g[u][0][i],f[u][1]=g[u][1][i];        if(fa[u])        {            f[u][0]+=max(f[fa[u]][0],f[fa[u]][1]+len[u]); // 注意这里的f已经不是dfs1的f了            f[u][1]=f[u][0]+max(mx[u][i],f[fa[u]][0]+len[u]-max(f[fa[u]][0],f[fa[u]][1]+len[u]));        }        ans=max(ans,f[son[u][i]][0]+max(f[u][0],f[u][1]+len[son[u][i]]));        dfs2(son[u][i]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v,w; i<n; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);addEdge(v,u,w);    }    dfs1(1,1);dfs2(1);    cout << ans << '\n';    return 0;}
]]>
+ 洛谷P3647 [APIO2014] 连珠线题解

题目链接:P3647[APIO2014] 连珠线

题意

在达芬奇时代,有一个流行的儿童游戏称为连珠线。当然,这个游戏是关于珠子和线的。线是红色或蓝色的,珠子被编号为\(1\)\(n\)。这个游戏从一个珠子开始,每次会用如下方式添加一个新的珠子:

Append(w, v):一个新的珠子 \(w\) 和一个已经添加的珠子 \(v\) 用红线连接起来。

Insert(w, u, v):一个新的珠子 \(w\) 插入到用红线连起来的两个珠子 \(u, v\) 之间。具体过程是删去 \(u, v\) 之间红线,分别用蓝线连接 \(u, w\) 和 \(w,v\)

每条线都有一个长度。游戏结束后,你的最终得分为蓝线长度之和。

给你连珠线游戏结束后的游戏局面,只告诉了你珠子和链的连接方式以及每条线的长度,没有告诉你每条线分别是什么颜色。

你需要写一个程序来找出最大可能得分。即,在所有以给出的最终局面结束的连珠线游戏中找出那个得分最大的,然后输出最大可能得分。

\(1 \leq n \leq 200000\)

开学前两天准备狂刷题,结果在这题上卡了一下午(😭

本文主要参考了link,所以主要是自己的总结型题解(q779太菜了

容易发现这是一道树形dp

首先可以想到一个瞎七搭八的dp,也就是设结点 \(u\) 和父亲的连边涂什么颜色

但是这个dp不好判断合法性,这篇博客讲的比较详细,不过这个也比较容易发现

于是考虑避免那种“/\形”的蓝色边,只考虑“\类型”的,也就是fa[u]-u-son[u]的

可以发现此时如果固定了一个结点做根,就可以避免“/\形”的蓝色边出现

同时也方便dp了(并且有一种经典换根dp的感觉了,虽然我当时看不出

\(f[u][0/1]\) 表示 \(u\) 所在子树中,\(u\)作为或不作为蓝线的中点是能得到的最大价值

前者比较简单,不是蓝线中点,则可以连蓝线中点,也可以连红线。 \[f[u][0]=\sum_{v \in \text{son}[u]} \max(f[v][0],f[v][1]+w(u,v)) \tag{1}\]后者较为麻烦,首先它和前者唯一的区别在于,它必须有一个儿子作为这条蓝线的终点。显然这个儿子是某个\(f[v][0]\) 。因此我们可以把 \(f[u][0]\) 的直接初始化到 \(f[u][1]\) 上,然后去掉这个 \(v\) 的贡献,并加上它的新贡献。 \[f[u][1]=f[u][0]+\max_{v \in\text{son}[u]}(f[v][0]+w(u,v)-\max(f[v][0],f[v][1]+w(u,v))) \tag{2}\] 有点长,不过后面那个max就是 \(f[u][0]\) 里面的那个max

然后我们就有了一个固定根情况下的dp,时间复杂度 \(O(n^2)\) ,还过不了

然后这里就要换根dp发挥作用了。

我们考虑一个点 \(u\) 的儿子变成了\(u\) 的父亲会有什么影响

首先这个儿子对 \(u\)的贡献没了,并且有可能转移方程中的最大值也没了。

(于是我们记录一个次大值,貌似是经典套路)

然后 \(u\)会变成新的儿子给原来的儿子(现在的父亲)贡献

方便起见,我们在第一遍dp的时候用vector记录一个 \(g[u][0/1][j]\)

表示对于 \(u\) ,不考虑第 \(j\) 个儿子的贡献时能获得的最大价值

\(g[u][0][j]\) 直接减去 \(j\) 的贡献就好了,

对于 \(g[u][1][j]\) ,如果 \(j\)恰好是最大值,那就加上次大值,否则不变

这里的最大值、次大值就是指 \((2)\)里的 \[\max\limits_{v \in\text{son}[u]}(f[v][0]+w(u,v)-\max(f[v][0],f[v][1]+w(u,v)))\] 换根的过程中,枚举 \(u\)的儿子作为整棵树的根。值得注意的是,换根以后 \(u\) 的父亲就会变成 \(u\) 的儿子。因此我们要先重新算出 \(u\) 的父亲对 \(u\) 的贡献,然后再换根

时间复杂度 \(O(n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)struct Edge{    int u,v,w,next;}e[N<<1];int pos=1,head[N];int n,ans,fa[N],len[N],f[N][2];vector<int> son[N],g[N][2],mx[N]; // mx[u][i] 表示u所在子树不考虑i时的maxvoid addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dfs1(int u,int Fa){    f[u][0]=0;f[u][1]=-INF;    int mx1=-INF,mx2=-INF;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v,w=e[i].w;        if(v==Fa)continue;        len[v]=w;fa[v]=u;        son[u].push_back(v);        dfs1(v,u);        f[u][0]+=max(f[v][0],f[v][1]+w);        int t=f[v][0]+w-max(f[v][0],f[v][1]+w);        if(t>mx1)swap(mx1,mx2),mx1=t;        else if(t>mx2)mx2=t; // 次大值    }    f[u][1]=f[u][0]+mx1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v,w=e[i].w;        if(v==Fa)continue;        g[u][0].push_back(f[u][0]-max(f[v][0],f[v][1]+w));        if(f[v][0]+w-max(f[v][0],f[v][1]+w)==mx1)        {            g[u][1].push_back(g[u][0].back()+mx2);            mx[u].push_back(mx2);        }else        {            g[u][1].push_back(g[u][0].back()+mx1);            mx[u].push_back(mx1);        }    }}void dfs2(int u){    for(int i=0; i<son[u].size(); i++)    {        f[u][0]=g[u][0][i],f[u][1]=g[u][1][i];        if(fa[u])        {            f[u][0]+=max(f[fa[u]][0],f[fa[u]][1]+len[u]); // 注意这里的f已经不是dfs1的f了            f[u][1]=f[u][0]+max(mx[u][i],f[fa[u]][0]+len[u]-max(f[fa[u]][0],f[fa[u]][1]+len[u]));        }        ans=max(ans,f[son[u][i]][0]+max(f[u][0],f[u][1]+len[son[u][i]]));        dfs2(son[u][i]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v,w; i<n; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);addEdge(v,u,w);    }    dfs1(1,1);dfs2(1);    cout << ans << '\n';    return 0;}
]]>
@@ -4593,7 +4593,7 @@ /2022/06/08/luo-gu-p3047-usaco12feb-nearby-cows-g-ti-jie/ - 洛谷P3047 [USACO12FEB]Nearby Cows G 题解

题目链接:P3047 [USACO12FEB]Nearby Cows G

题意

给你一棵 $n$ 个点的树,点带权,对于每个节点求出距离它不超过 $k$ 的所有节点权值和 $m_i$。

「数据范围」
对于 $100\%$ 的数据:$1 \le n \le 10^5$,$1 \le k \le 20$,$0 \le c_i \le 1000$

换根dp,也叫二次扫描法

这题随便找一个根当做树根,然后扫两遍才能出答案

首先设 $f[u][j]$ 表示 $u$ 所在子树,与 $u$ 相距恰好 $j$ 的结点个数,则

这是第一遍dfs,可以发现我们没有从非 $u$ 所在子树获取答案

考虑第二遍dfs,设 $g[u][j]$ 表示在整棵树中与 $u$ 相距恰好 $j$ 的结点个数

这个答案一定是从父节点的 $g$ 转移而来

但是这里会有一个问题,$g[fa][j-1]$ 包含了从 $f[u][j-2]$ 转移来的答案

直接加的话会导致重复,考虑容斥

不懂的话建议画个图,别像我一样一开始干瞪着 qwq

然后 $g$ 可以直接在 $f$ 上搞,节约空间

时间复杂度 $O(n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+5)struct Edge{    int u,v,next;}e[N<<1];int n,k,num;int f[N][21],pos=1,head[N],dep[N];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs1(int u){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;if(dep[v])continue;        dep[v]=dep[u]+1;dfs1(v);        for(int j=1; j<=k; j++)            f[u][j]+=f[v][j-1];    }}void dfs2(int u){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(dep[v]<dep[u])continue;        for(int j=k; j>=2; j--)            f[v][j]-=f[v][j-2];        for(int j=1; j<=k; j++)            f[v][j]+=f[u][j-1];        dfs2(v);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> k;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        cin >> f[i][0];    dep[1]=1;dfs1(1);dfs2(1);    for(int i=1; i<=n; i++)    {        int res=0;        for(int j=0; j<=k; j++)            res+=f[i][j];        cout << res << '\n';    }    return 0;}
]]>
+ 洛谷P3047[USACO12FEB]Nearby Cows G 题解

题目链接:P3047[USACO12FEB]Nearby Cows G

题意

给你一棵 \(n\)个点的树,点带权,对于每个节点求出距离它不超过 \(k\) 的所有节点权值和 \(m_i\)。

「数据范围」 对于 \(100\%\)的数据:\(1 \le n \le 10^5\)\(1 \le k \le 20\),\(0 \le c_i \le 1000\)

换根dp,也叫二次扫描法

这题随便找一个根当做树根,然后扫两遍才能出答案

首先设 \(f[u][j]\) 表示 \(u\) 所在子树,与 \(u\) 相距恰好 \(j\) 的结点个数,则 \[f[u][j]=\sum_{v \in \text{son}[u]} f[v][j-1]\] 这是第一遍dfs,可以发现我们没有从非 \(u\) 所在子树获取答案

考虑第二遍dfs,设 \(g[u][j]\)表示在整棵树中与 \(u\)相距恰好 \(j\) 的结点个数

这个答案一定是从父节点的 \(g\)转移而来

但是这里会有一个问题,\(g[fa][j-1]\)包含了从 \(f[u][j-2]\) 转移来的答案

直接加的话会导致重复,考虑容斥

不懂的话建议画个图,别像我一样一开始干瞪着 qwq\[g[u][j]=f[u][j]+g[fa][j-1]-f[u][j-2]\] 然后 \(g\) 可以直接在 \(f\) 上搞,节约空间

时间复杂度 \(O(n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+5)struct Edge{    int u,v,next;}e[N<<1];int n,k,num;int f[N][21],pos=1,head[N],dep[N];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs1(int u){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;if(dep[v])continue;        dep[v]=dep[u]+1;dfs1(v);        for(int j=1; j<=k; j++)            f[u][j]+=f[v][j-1];    }}void dfs2(int u){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(dep[v]<dep[u])continue;        for(int j=k; j>=2; j--)            f[v][j]-=f[v][j-2];        for(int j=1; j<=k; j++)            f[v][j]+=f[u][j-1];        dfs2(v);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> k;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u,v);addEdge(v,u);    }    for(int i=1; i<=n; i++)        cin >> f[i][0];    dep[1]=1;dfs1(1);dfs2(1);    for(int i=1; i<=n; i++)    {        int res=0;        for(int j=0; j<=k; j++)            res+=f[i][j];        cout << res << '\n';    }    return 0;}
]]>
@@ -4607,10 +4607,10 @@ 算法 - DP - 图论 + DP + @@ -4622,7 +4622,7 @@ /2022/06/07/luo-gu-p4037-jsoi2008-mo-shou-di-tu-ti-jie/ - 洛谷P4037 [JSOI2008]魔兽地图 题解

题目链接:P4037 [JSOI2008]魔兽地图

题意

DotR (Defense of the Robots) Allstars是一个风靡全球的魔兽地图,他的规则简单与同样流行的地图DotA (Defense of the Ancients) Allstars。

DotR里面的英雄只有一个属性——力量。他们需要购买装备来提升自己的力量值,每件装备都可以使佩戴它的英雄的力量值提高固定的点数,所以英雄的力量值等于它购买的所有装备的力量值之和。装备分为基本装备和高级装备两种。基本装备可以直接从商店里面用金币购买,而高级装备需要用基本装备或者较低级的高级装备来合成,合成不需要附加的金币。装备的合成路线可以用一棵树来表示。

比如,Sange and Yasha的合成需要Sange,Yasha和Sange and Yasha Recipe Scroll三样物品。其中Sange又要用Ogre Axe, Belt of Giant Strength和 Sange Recipe Scroll合成。每件基本装备都有数量限制,这限制了你不能无限制地合成某些性价比很高的装备。

现在,英雄Spectre有M个金币,他想用这些钱购买装备使自己的力量值尽量高。你能帮帮他吗?他会教你魔法Haunt(幽灵附体)作为回报的。

容易发现这是一个十分有(dú)趣(liú)的树上背包问题

高级装备和基本装备显然有树形的依赖关系

按照高级->高级->基本的方式进行建图,$(u,v)$ 的边权就是合成 $u$ 需要的 $v$ 个数

于是可以由此建出一个森林,因此最后dp的时候要加个虚拟结点啥的

设 $P[x],L[x],M[x]$ 分别表示物品 $x$ 的价值、购买上限和花费

对于基本装备(叶子结点),显然要进行L[x]=min(L[x],m/M[x])的操作

设 $f[u][j][k]$ 表示 $u$ 装备所在子树,上传 $j$ 个 $u$ 装备用于给上层合成(也就是不计算这 $j$ 个的贡献),且所在子树花费的总金额为 $k$ 是能获得的最大价值

对于一个 $u$ ,它对父亲的贡献分为两方面

  • 它所在的子树的贡献(价值),包括儿子的以及自己没上传的
  • $u$ 上传的 $u$ 装备的个数,以用于父亲的合成

则我们首先要知道对于每个结点究竟合成几个

考虑枚举 $l$ ,表示 $u$ 要合成 $l$ 个(上传+自己私藏的)

然后我们就可以枚举上传几个以及花费多少了

如何知道对 $u$ 所在子树花费 $k$ 能获得的最大价值是多少呢

这个需要我们再单独做一个临时的dp

设 $g[i][j]$ 表示对于 $u$ 所在子树,只考虑 $u$ 的前 $i$ 个儿子所在子树,花费为 $j$ 时能获得的最大价值(这里不用记录 $u$ ,因为只是临时的dp)

则有

  • 这里为什么是 $l\times w(u,v)$ ?因为已经枚举了当前要合成 $l$ 个

  • 这里为什么max的第一个不是 $g[i-1][j+k]$ ?因为我们每个子树都要拿材料啊

    所以这里只是代码这么写而已,

    相当于 $g[i][j+k]=\max\{g[i-1][j]+f[v][l\times w(u,v)][k]\}$

  • 这里为什么是 $j+k$ ?因为我喜欢刷表法,当然可以填表法

然后就可以愉快地推出 $f$ 的转移方程了

细节巨多,详见代码(这次有注释了qwq

时间复杂度的宽松上界为 $O(100\times nm^2)$

实际剪枝+时限3.00s,可以444ms跑过所有点(嘿嘿所以我是目前最优解

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(55)#define SZ (int)(2e3+15)int n,m;int P[N],M[N],L[N],tmp[SZ],h[SZ],f[N][105][SZ],g[SZ];struct Edge{    int u,v,w,next;}e[N*N];int pos=1,head[N],in[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;++in[v];}void DP(int u){    if(!head[u])    {        L[u]=min(L[u],m/M[u]);        for(int i=0; i<=L[u]; i++)            for(int j=i; j<=L[u]; j++)                f[u][i][j*M[u]]=(j-i)*P[u]; // j-i的会被私藏起来 qwq        return;    }    L[u]=INF;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v; DP(v);        L[u]=min(L[u],L[v]/e[i].w); // 木桶能装多少水,和最短的那块板有关        M[u]+=e[i].w*M[v];    }    L[u]=min(L[u],m/M[u]); // 当然了,钱不够“木板”长也是没用的    for(int l=0; l<=L[u]; l++)// 题解区都说是倒序枚举,表示不明白,正着枚举也可以过// 他们的解释是,倒序枚举可以保证每一轮选的g[j]中所包含的决策至少选取了l*w个物品用于合成// 可是如果没法选取l*w个物品的话,每一轮的g[j](也就是tmp[j])肯定是-INF了,应该不会影响答案的正确性// 不太懂他们什么意思,如果我错了欢迎hack awa    {        memset(g,0xc0,sizeof(g));        g[0]=0; // 滚动数组优化        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            for(int j=0; j<=m; j++)                tmp[j]=g[j],g[j]=-INF; // 注意转移方程不是从g[i-1][j+k]转移的            for(int j=0; j<=m; j++)                for(int k=0; tmp[j]>=0&&j+k<=m; k++) // 重要剪枝                    g[j+k]=max(g[j+k],tmp[j]+f[v][l*w][k]);        }        for(int j=0; j<=l; j++)            for(int k=0; k<=m; k++)                f[u][j][k]=max(f[u][j][k],g[k]+(l-j)*P[u]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    memset(f,0xc0,sizeof(f));    for(int i=1,l; i<=n; i++)    {        char ch;        cin >> P[i] >> ch;        if(ch=='A')        {            cin >> l;            for(int j=1,v,w; j<=l; j++)                cin >> v >> w,addEdge(i,v,w);        }        else cin >> M[i] >> L[i];    }    for(int u=1; u<=n; u++)    {        if(!in[u]) // 森林        {            DP(u);            for(int i=0; i<=m; i++)                tmp[i]=h[i],h[i]=0;            for(int i=0; i<=m; i++)                for(int j=0; i+j<=m; j++)                    h[i+j]=max(h[i+j],tmp[i]+f[u][0][j]); // 虚拟结点不用也无法合成物品,显然子结点上传只会浪费        }    }    int res=-INF;    for(int i=0; i<=m; i++)        res=max(res,h[i]);    cout << res << '\n';    return 0;}

参考了很多的题解,就不一一列出来了 qwq

]]>
+ 洛谷P4037 [JSOI2008]魔兽地图题解

题目链接:P4037[JSOI2008]魔兽地图

题意

DotR (Defense of the Robots)Allstars是一个风靡全球的魔兽地图,他的规则简单与同样流行的地图DotA(Defense of the Ancients) Allstars。

DotR里面的英雄只有一个属性——力量。他们需要购买装备来提升自己的力量值,每件装备都可以使佩戴它的英雄的力量值提高固定的点数,所以英雄的力量值等于它购买的所有装备的力量值之和。装备分为基本装备和高级装备两种。基本装备可以直接从商店里面用金币购买,而高级装备需要用基本装备或者较低级的高级装备来合成,合成不需要附加的金币。装备的合成路线可以用一棵树来表示。

比如,Sange and Yasha的合成需要Sange,Yasha和Sange and Yasha RecipeScroll三样物品。其中Sange又要用Ogre Axe, Belt of Giant Strength和 SangeRecipeScroll合成。每件基本装备都有数量限制,这限制了你不能无限制地合成某些性价比很高的装备。

现在,英雄Spectre有M个金币,他想用这些钱购买装备使自己的力量值尽量高。你能帮帮他吗?他会教你魔法Haunt(幽灵附体)作为回报的。

容易发现这是一个十分有(dú)趣(liú)的树上背包问题

高级装备和基本装备显然有树形的依赖关系

按照高级->高级->基本的方式进行建图,\((u,v)\) 的边权就是合成 \(u\) 需要的 \(v\) 个数

于是可以由此建出一个森林,因此最后dp的时候要加个虚拟结点啥的

\(P[x],L[x],M[x]\) 分别表示物品\(x\) 的价值、购买上限和花费

对于基本装备(叶子结点),显然要进行L[x]=min(L[x],m/M[x])的操作

\(f[u][j][k]\) 表示 \(u\) 装备所在子树,上传 \(j\) 个 \(u\) 装备用于给上层合成(也就是不计算这\(j\)个的贡献),且所在子树花费的总金额为 \(k\) 是能获得的最大价值

对于一个 \(u\),它对父亲的贡献分为两方面

  • 它所在的子树的贡献(价值),包括儿子的以及自己没上传的
  • \(u\) 上传的 \(u\) 装备的个数,以用于父亲的合成

则我们首先要知道对于每个结点究竟合成几个

考虑枚举 \(l\) ,表示 \(u\) 要合成 \(l\) 个(上传+自己私藏的)

然后我们就可以枚举上传几个以及花费多少了

如何知道对 \(u\) 所在子树花费 \(k\) 能获得的最大价值是多少呢

这个需要我们再单独做一个临时的dp

\(g[i][j]\) 表示对于 \(u\) 所在子树,只考虑 \(u\) 的前 \(i\) 个儿子所在子树,花费为 \(j\) 时能获得的最大价值(这里不用记录 \(u\) ,因为只是临时的dp)

则有 \[g[i][j+k]=\max(g[i][j+k],g[i-1][j]+f[v][l\times w(u,v)][k])\]

  • 这里为什么是 \(l\times w(u,v)\)?因为已经枚举了当前要合成 \(l\)

  • 这里为什么max的第一个不是 \(g[i-1][j+k]\)?因为我们每个子树都要拿材料啊

    所以这里只是代码这么写而已,

    相当于 \(g[i][j+k]=\max\{g[i-1][j]+f[v][l\timesw(u,v)][k]\}\)

  • 这里为什么是 \(j+k\)?因为我喜欢刷表法,当然可以填表法

然后就可以愉快地推出 \(f\)的转移方程了 \[f[u][j][k]=\max(f[u][j][k],g[k]+(l-j) \times P[u])\] 细节巨多,详见代码(这次有注释了qwq

时间复杂度的宽松上界为 \(O(100\timesnm^2)\)

实际剪枝+时限3.00s,可以444ms跑过所有点(嘿嘿所以我是目前最优解

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(55)#define SZ (int)(2e3+15)int n,m;int P[N],M[N],L[N],tmp[SZ],h[SZ],f[N][105][SZ],g[SZ];struct Edge{    int u,v,w,next;}e[N*N];int pos=1,head[N],in[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;++in[v];}void DP(int u){    if(!head[u])    {        L[u]=min(L[u],m/M[u]);        for(int i=0; i<=L[u]; i++)            for(int j=i; j<=L[u]; j++)                f[u][i][j*M[u]]=(j-i)*P[u]; // j-i的会被私藏起来 qwq        return;    }    L[u]=INF;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v; DP(v);        L[u]=min(L[u],L[v]/e[i].w); // 木桶能装多少水,和最短的那块板有关        M[u]+=e[i].w*M[v];    }    L[u]=min(L[u],m/M[u]); // 当然了,钱不够“木板”长也是没用的    for(int l=0; l<=L[u]; l++)// 题解区都说是倒序枚举,表示不明白,正着枚举也可以过// 他们的解释是,倒序枚举可以保证每一轮选的g[j]中所包含的决策至少选取了l*w个物品用于合成// 可是如果没法选取l*w个物品的话,每一轮的g[j](也就是tmp[j])肯定是-INF了,应该不会影响答案的正确性// 不太懂他们什么意思,如果我错了欢迎hack awa    {        memset(g,0xc0,sizeof(g));        g[0]=0; // 滚动数组优化        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            for(int j=0; j<=m; j++)                tmp[j]=g[j],g[j]=-INF; // 注意转移方程不是从g[i-1][j+k]转移的            for(int j=0; j<=m; j++)                for(int k=0; tmp[j]>=0&&j+k<=m; k++) // 重要剪枝                    g[j+k]=max(g[j+k],tmp[j]+f[v][l*w][k]);        }        for(int j=0; j<=l; j++)            for(int k=0; k<=m; k++)                f[u][j][k]=max(f[u][j][k],g[k]+(l-j)*P[u]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    memset(f,0xc0,sizeof(f));    for(int i=1,l; i<=n; i++)    {        char ch;        cin >> P[i] >> ch;        if(ch=='A')        {            cin >> l;            for(int j=1,v,w; j<=l; j++)                cin >> v >> w,addEdge(i,v,w);        }        else cin >> M[i] >> L[i];    }    for(int u=1; u<=n; u++)    {        if(!in[u]) // 森林        {            DP(u);            for(int i=0; i<=m; i++)                tmp[i]=h[i],h[i]=0;            for(int i=0; i<=m; i++)                for(int j=0; i+j<=m; j++)                    h[i+j]=max(h[i+j],tmp[i]+f[u][0][j]); // 虚拟结点不用也无法合成物品,显然子结点上传只会浪费        }    }    int res=-INF;    for(int i=0; i<=m; i++)        res=max(res,h[i]);    cout << res << '\n';    return 0;}

参考了很多的题解,就不一一列出来了 qwq

]]>
@@ -4651,7 +4651,7 @@ /2022/06/06/oi-mo-ban-shu-xue/ - OI模板-数学

待补全

排列的计算

排列 $A_n^m$ 直接算可以 $O(n)$

例如求解 $A_{n-m+1}^{m} \bmod p$

int res=1;for(int i=n-m+1; i>=n-2*m+2; i--)    res=res*i%p;cout << res << '\n'

快速幂

时间复杂度 $O(\log n)$

空间复杂度 $O(1)$

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}int a,b,p;int qpow(int a,int b){int ans=1,base=a;while(b){if(b&1)ans=ans*base%p;base=base*base%p;b>>=1;}return ans;}signed main(){read(a);read(b);read(p);write(a);pc('^');write(b);pc(' ');printf("mod ");write(p);pc('=');write(qpow(a,b));pc('\n');return 0;}

矩阵快速幂

注意点:

  1. $O(n^3\log n)$
  2. 初始状态
  3. 临时变量不可开过大

新的板子(upd.20220730) P1962 斐波那契数列

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef vector< vector<int> > mat;#define N (int)()const int mod=1e9+7;void add(int &x,int y){x+=y; if(x>=mod)x-=mod;}// (n*m) x (m*p) = (n*p)mat mul(mat &A,mat &B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vector<int>(p));    for(int k=0; k<m; k++)        for(int i=0; i<n; i++)            for(int j=0; j<p; j++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int k){    int n=max(A.size(),A[0].size());     mat B(n,vector<int>(n));    for(int i=0; i<n; i++) B[i][i]=1;    while(k)    {        if(k&1) B=mul(B,A);        A=mul(A,A); k>>=1;    }    return B;}void print(mat &A){    int n=A.size(),m=A[0].size();    for(int i=0; i<n; i++)        for(int j=0; j<m; j++)            cout << A[i][j] << " \n"[j==m-1];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n; cin >> n;    if(n<=2) cout << "1";    else    {        mat A(2,vector<int>(2)),B(2,vector<int>(1));        A[0][0]=A[0][1]=A[1][0]=1;        B[0][0]=B[1][0]=1;        A=qpow(A,n-2); A=mul(A,B);        cout << A[0][0] << '\n';    }    return 0;}

旧的板子

const int p=2009;int n,t;struct mat{    int n,g[205][205];    void clear()    {        memset(g,0,sizeof(g));    }    mat operator*(const mat &o)const    {        static mat tmp;        tmp.n=n;tmp.clear();        for(int i=1; i<=n; i++)            for(int k=1; k<=n; k++)            {                int r=g[i][k]%p;                for(int j=1; j<=n; j++)                    tmp.g[i][j]=(tmp.g[i][j]+r*o.g[k][j]%p)%p;            }        return tmp;    }}M;mat qpow(mat a,int b){    static mat ans,base=a;    ans.n=a.n;ans.clear();    for(int i=1; i<=ans.n; i++)        ans.g[i][i]=1;    while(b)    {        if(b&1)ans=ans*base;        base=base*base;        b>>=1;    }    return ans;}

判断素数

判断素数 朴素版

时间复杂度 $O(Q\sqrt{n})$

#include <bits/stdc++.h>using namespace std;#define int long longint x;bool ck(int x){    if(x<2)return 0;    for(int i=2; i<=x/i; i++)    {        if(x%i==0)            return 0;    }    return 1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    while(cin >> x)        cout << (ck(x)?'Y':'N') << endl;    return 0;}

MillerRabin

$O(Q\log a_i)$

#include <bits/stdc++.h>using namespace std;#define int long long#define test_time 10mt19937 rd(time(0));int mul(int a,int b,int p){    int res=(__int128)a*b%p;    return res;}int qpow(int a,int b,int p){    int ans=1,base=a;    while(b)    {        if(b&1)ans=mul(ans,base,p);        base=mul(base,base,p);        b>>=1;    }    return ans;}bool MillerRabin(int n){    if(n<3||n%2==0)return n==2;    int a=n-1,b=0;    while(a%2==0)a>>=1,++b;    for(int i=1,j; i<=test_time; i++)    {        int x=rd()%(n-2)+2;        int v=qpow(x,a,n);        if(v==1)continue;        for(j=0; j<b; j++)        {            if(v==n-1)break;            v=mul(v,v,n);        }        if(j>=b)return 0;    }     return 1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    int x;    while(cin >> x)        cout << (MillerRabin(x)?'Y':'N') << endl;    return 0;}

线性筛

时间复杂度 近似$O(n)$

空间复杂度 $O(n)$

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(1e8+5)#define MAXM (int)(2e6+5)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}int n,p;bool ck[MAXN];int a[MAXM],tot;void prime(){for(int i=2; i<=n+2; i++){if(!ck[i])a[++tot]=i;for(int j=1; j<=tot&&a[j]*i<=n+2; j++){ck[a[j]*i]=1;if(i%a[j]==0)break;}}}signed main(){read(n);read(p);prime();for(int i=1,k; i<=p; i++){read(k);write(a[k]);pc('\n');}return 0;}

求欧拉函数

  1. 暴力 $O(Q\sqrt{n})$ poj2407
int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n;    while(cin>>n&&n!=0)        cout << Euler_phi(n) << endl;    return 0;}
  1. 线性筛求欧拉函数 $O(n)$

    poj2478 (这个题要改成 $\varphi$ 的前缀和)

int phi[N],prime[N],pcnt;bool ck[N];void Euler(){    ck[1]=1;    phi[1]=1;    for(int i=2; i<=N-5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            phi[i]=i-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N-5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                phi[pos]=phi[i]*phi[prime[j]];            }            else            {                phi[pos]=phi[i]*prime[j];                break;            }        }    }}

二次剩余

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fint Q,n,p;int i2;struct num{    int r,i;};num operator*(num a,num b){    return {((a.r*b.r%p+i2*a.i%p*b.i%p)+p)%p,            ((a.i*b.r%p+a.r*b.i%p)+p)%p};}bool operator==(num a,num b){    return a.r==b.r&&a.i==b.i;}num qpow(num a,int b){    num ans={1,0},base=a;    while(b)    {        if(b&1)ans=ans*base;        base=base*base;        b>>=1;    }    return ans;}bool ck(int x){    return qpow({x,0},(p-1)>>1)==(num){1,0};}mt19937 rd(time(0));void solve(int &x1,int &x2){    int a=rd()%p;    while(!a||ck((a*a+p-n)%p))        a=rd()%p;    i2=(a*a+p-n)%p;    x1=qpow((num){a,1},(p+1)>>1).r;    x2=p-x1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int x1,x2;    cin >> Q;    while(Q--)    {        cin >> n >> p;        if(qpow((num){n,0},(p-1)>>1).r==p-1)            cout << "Hola!" << endl;        else if(!n)cout << 0 << endl;        else        {            solve(x1,x2);            if(x1>x2)swap(x1,x2);            if(x1==x2)cout << x1 << endl;            else cout << x1 << " " << x2 << endl;        }    }    return 0;}

数论分块

板子题 [UVA11526] 待写题解

简单例题1 洛谷P2261 [CQOI2007]余数求和

简单例题2 [P2424 约数和] 待写题解


康托展开

时间复杂度 $O(n\log n)$

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e6+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();};    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}const int mod = 998244353;int n,a[N],fac[N],tree[N];#define lowbit(x) (x&-x);void add(int x,int v){    while(x&&x<=n)    {        tree[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=tree[x];        x-=lowbit(x);    }    return res;}signed main(){    read(n);    fac[0]=1;    for(int i=1; i<=n; i++)        fac[i]=fac[i-1]*i%mod,add(i,1);    int ans=1;    for(int i=1; i<=n; i++)    {        read(a[i]);        ans+=sum(a[i]-1)*fac[n-i]%mod;        ans%=mod;add(a[i],-1);    }    write(ans);pc('\n');    return 0;}

逆康托展开

理论复杂度是 $O(n\log n)$ 的,但是实际上用暴力就行了(n!那么大

这个纯属瞎写的。根本用不到,但是复杂度确实是对的

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,a[N],fac[N],Q;namespace BT{    struct node    {        int ch[2],num,cnt,s,sz,sd;    }t[N];    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    double alpha=0.7;    int tot,rt;    int tmp[N];    int New(int x)    {        t[++tot]={0,0,x,1,1,1,1};        return tot;    }    void push_up(int at)    {        t[at].s=t[ls(at)].s+t[rs(at)].s+1;        t[at].sz=t[ls(at)].sz+t[rs(at)].sz+t[at].cnt;        t[at].sd=t[ls(at)].sd+t[rs(at)].sd+(t[at].cnt!=0);    }    bool CanR(int at)    {        double x=t[at].s*alpha;        return (t[at].cnt)&&(x<=(double)max(t[ls(at)].s,t[rs(at)].s)||        (double)t[at].sd<=x);    }    void CanR_flatten(int &idx,int at)    {        if(!at)return;        CanR_flatten(idx,ls(at));        if(t[at].cnt)tmp[++idx]=at;        CanR_flatten(idx,rs(at));    }    int CanR_build(int l,int r)    {        if(l>=r)return 0;        int mid=(l+r)>>1;        int &at=tmp[mid];        ls(at)=CanR_build(l,mid);        rs(at)=CanR_build(mid+1,r);        push_up(at);        return at;    }    void rebuild(int &at)    {        int idx=0;        CanR_flatten(idx,at);        at=CanR_build(1,idx+1);    }    void insert(int x,int &at)    {        if(!at){at=New(x);return;}        if(t[at].num==x)++t[at].cnt;        else if(x<t[at].num)insert(x,ls(at));        else insert(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    void remove(int x,int &at)    {        if(!at)return;        if(t[at].num==x)        {            if(t[at].cnt)--t[at].cnt;        }else if(x<t[at].num)remove(x,ls(at));        else remove(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    int getval(int x,int at)    {        if(!at)return INF;        if(x<=t[ls(at)].sz)return getval(x,ls(at));        if(x<=t[ls(at)].sz+t[at].cnt)return t[at].num;        else return getval(x-t[ls(at)].sz-t[at].cnt,rs(at));    }    int uprbd(int x,int at)    {        if(!at)return 1;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz+t[at].cnt+1;        else if(x<t[at].num)return uprbd(x,ls(at));        else return uprbd(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int uprbd_gt(int x,int at)    {        if(!at)return 0;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz;        else if(x<t[at].num)return uprbd_gt(x,ls(at));        else return uprbd_gt(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int getpre(int x,int at){return getval(uprbd_gt(x,at),at);}    int getnext(int x,int at){return getval(uprbd(x,at),at);}}namespace BIT{    int tree[N];    #define lowbit(x) (x&-x)    void add(int x,int v)    {        while(x&&x<=n)        {            tree[x]+=v;            x+=lowbit(x);        }    }    int sum(int x)    {        int res=0;        while(x>=1)        {            res+=tree[x];            x-=lowbit(x);        }        return res;    }}signed main(){    using namespace BIT;    using namespace BT;    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> Q;fac[0]=1;    for(int i=1; i<=n; i++)        fac[i]=fac[i-1]*i,add(i,1);    while(Q--)    {        char op;        cin >> op;        if(op=='P')        {            int k,r;cin >> k;            tot=rt=0;--k;            for(int i=1; i<=n; i++)                insert(i,rt);            for(int i=1; i<=n; i++)            {                r=k%fac[n-i];k/=fac[n-i];                int p=getval(k+1,rt);                // cout << p << endl;                cout << p << " \n"[i==n];                remove(p,rt);k=r;            }        }else        {            int ans=1;            memset(tree,0,(n+1)*sizeof(int));            for(int i=1; i<=n; i++) add(i,1);            for(int i=1,x; i<=n; i++)            {                cin >> x;                ans+=sum(x-1)*fac[n-i];                add(x,-1);            }            cout << ans << endl;        }    }    return 0;}

高斯消元

P3389 【模板】高斯消元法

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(150)const int eps=1e-10;int dcmp(int x){if(abs(x)<=eps)return 0; return x>eps?1:-1;}int n;double a[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        for(int j=1; j<=n+1; j++)            cin >> a[i][j];    for(int i=1; i<=n; i++)    {        int p=i;        for(int j=i+1; j<=n; j++)            if(abs(a[j][i])>abs(a[p][i])) p=j;        for(int j=1; j<=n+1; j++)            swap(a[i][j],a[p][j]);        if(!dcmp(a[i][i])) return cout << "No Solution",0;        for(int j=1; j<=n; j++)            if(j!=i)            {                double tmp=a[j][i]/a[i][i];                for(int k=i+1; k<=n+1; k++)                    a[j][k]-=a[i][k]*tmp;            }    }    cout << fixed << setprecision(2);    for(int i=1; i<=n; i++)        cout << a[i][n+1]/a[i][i] << '\n';    return 0;}
]]>
+ OI模板-数学

待补全

排列的计算

排列 \(A_n^m\) 直接算可以 \(O(n)\)

例如求解 \(A_{n-m+1}^{m} \bmodp\)

int res=1;for(int i=n-m+1; i>=n-2*m+2; i--)    res=res*i%p;cout << res << '\n'

快速幂

时间复杂度 \(O(\log n)\)

空间复杂度 \(O(1)\)

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}int a,b,p;int qpow(int a,int b){int ans=1,base=a;while(b){if(b&1)ans=ans*base%p;base=base*base%p;b>>=1;}return ans;}signed main(){read(a);read(b);read(p);write(a);pc('^');write(b);pc(' ');printf("mod ");write(p);pc('=');write(qpow(a,b));pc('\n');return 0;}

矩阵快速幂

注意点:

  1. \(O(n^3\log n)\)
  2. 初始状态
  3. 临时变量不可开过大

新的板子(upd.20220730) P1962 斐波那契数列

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef vector< vector<int> > mat;#define N (int)()const int mod=1e9+7;void add(int &x,int y){x+=y; if(x>=mod)x-=mod;}// (n*m) x (m*p) = (n*p)mat mul(mat &A,mat &B){    int n=A.size(),m=A[0].size(),p=B[0].size();    mat C(n,vector<int>(p));    for(int k=0; k<m; k++)        for(int i=0; i<n; i++)            for(int j=0; j<p; j++)                add(C[i][j],A[i][k]*B[k][j]%mod);    return C;}mat qpow(mat A,int k){    int n=max(A.size(),A[0].size());     mat B(n,vector<int>(n));    for(int i=0; i<n; i++) B[i][i]=1;    while(k)    {        if(k&1) B=mul(B,A);        A=mul(A,A); k>>=1;    }    return B;}void print(mat &A){    int n=A.size(),m=A[0].size();    for(int i=0; i<n; i++)        for(int j=0; j<m; j++)            cout << A[i][j] << " \n"[j==m-1];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n; cin >> n;    if(n<=2) cout << "1";    else    {        mat A(2,vector<int>(2)),B(2,vector<int>(1));        A[0][0]=A[0][1]=A[1][0]=1;        B[0][0]=B[1][0]=1;        A=qpow(A,n-2); A=mul(A,B);        cout << A[0][0] << '\n';    }    return 0;}

旧的板子

const int p=2009;int n,t;struct mat{    int n,g[205][205];    void clear()    {        memset(g,0,sizeof(g));    }    mat operator*(const mat &o)const    {        static mat tmp;        tmp.n=n;tmp.clear();        for(int i=1; i<=n; i++)            for(int k=1; k<=n; k++)            {                int r=g[i][k]%p;                for(int j=1; j<=n; j++)                    tmp.g[i][j]=(tmp.g[i][j]+r*o.g[k][j]%p)%p;            }        return tmp;    }}M;mat qpow(mat a,int b){    static mat ans,base=a;    ans.n=a.n;ans.clear();    for(int i=1; i<=ans.n; i++)        ans.g[i][i]=1;    while(b)    {        if(b&1)ans=ans*base;        base=base*base;        b>>=1;    }    return ans;}

判断素数

判断素数 朴素版

时间复杂度 \(O(Q\sqrt{n})\)

#include <bits/stdc++.h>using namespace std;#define int long longint x;bool ck(int x){    if(x<2)return 0;    for(int i=2; i<=x/i; i++)    {        if(x%i==0)            return 0;    }    return 1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    while(cin >> x)        cout << (ck(x)?'Y':'N') << endl;    return 0;}

MillerRabin

\(O(Q\log a_i)\)

#include <bits/stdc++.h>using namespace std;#define int long long#define test_time 10mt19937 rd(time(0));int mul(int a,int b,int p){    int res=(__int128)a*b%p;    return res;}int qpow(int a,int b,int p){    int ans=1,base=a;    while(b)    {        if(b&1)ans=mul(ans,base,p);        base=mul(base,base,p);        b>>=1;    }    return ans;}bool MillerRabin(int n){    if(n<3||n%2==0)return n==2;    int a=n-1,b=0;    while(a%2==0)a>>=1,++b;    for(int i=1,j; i<=test_time; i++)    {        int x=rd()%(n-2)+2;        int v=qpow(x,a,n);        if(v==1)continue;        for(j=0; j<b; j++)        {            if(v==n-1)break;            v=mul(v,v,n);        }        if(j>=b)return 0;    }     return 1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    int x;    while(cin >> x)        cout << (MillerRabin(x)?'Y':'N') << endl;    return 0;}

线性筛

时间复杂度 近似\(O(n)\)

空间复杂度 \(O(n)\)

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(1e8+5)#define MAXM (int)(2e6+5)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}int n,p;bool ck[MAXN];int a[MAXM],tot;void prime(){for(int i=2; i<=n+2; i++){if(!ck[i])a[++tot]=i;for(int j=1; j<=tot&&a[j]*i<=n+2; j++){ck[a[j]*i]=1;if(i%a[j]==0)break;}}}signed main(){read(n);read(p);prime();for(int i=1,k; i<=p; i++){read(k);write(a[k]);pc('\n');}return 0;}

求欧拉函数

  1. 暴力 \(O(Q\sqrt{n})\) poj2407
int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n;    while(cin>>n&&n!=0)        cout << Euler_phi(n) << endl;    return 0;}
  1. 线性筛求欧拉函数 \(O(n)\)

    poj2478(这个题要改成 \(\varphi\)的前缀和)

int phi[N],prime[N],pcnt;bool ck[N];void Euler(){    ck[1]=1;    phi[1]=1;    for(int i=2; i<=N-5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            phi[i]=i-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N-5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                phi[pos]=phi[i]*phi[prime[j]];            }            else            {                phi[pos]=phi[i]*prime[j];                break;            }        }    }}

二次剩余

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fint Q,n,p;int i2;struct num{    int r,i;};num operator*(num a,num b){    return {((a.r*b.r%p+i2*a.i%p*b.i%p)+p)%p,            ((a.i*b.r%p+a.r*b.i%p)+p)%p};}bool operator==(num a,num b){    return a.r==b.r&&a.i==b.i;}num qpow(num a,int b){    num ans={1,0},base=a;    while(b)    {        if(b&1)ans=ans*base;        base=base*base;        b>>=1;    }    return ans;}bool ck(int x){    return qpow({x,0},(p-1)>>1)==(num){1,0};}mt19937 rd(time(0));void solve(int &x1,int &x2){    int a=rd()%p;    while(!a||ck((a*a+p-n)%p))        a=rd()%p;    i2=(a*a+p-n)%p;    x1=qpow((num){a,1},(p+1)>>1).r;    x2=p-x1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int x1,x2;    cin >> Q;    while(Q--)    {        cin >> n >> p;        if(qpow((num){n,0},(p-1)>>1).r==p-1)            cout << "Hola!" << endl;        else if(!n)cout << 0 << endl;        else        {            solve(x1,x2);            if(x1>x2)swap(x1,x2);            if(x1==x2)cout << x1 << endl;            else cout << x1 << " " << x2 << endl;        }    }    return 0;}

数论分块

板子题 [UVA11526] 待写题解

简单例题1 洛谷P2261[CQOI2007]余数求和

简单例题2 [P2424 约数和] 待写题解


康托展开

时间复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e6+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();};    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}const int mod = 998244353;int n,a[N],fac[N],tree[N];#define lowbit(x) (x&-x);void add(int x,int v){    while(x&&x<=n)    {        tree[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=tree[x];        x-=lowbit(x);    }    return res;}signed main(){    read(n);    fac[0]=1;    for(int i=1; i<=n; i++)        fac[i]=fac[i-1]*i%mod,add(i,1);    int ans=1;    for(int i=1; i<=n; i++)    {        read(a[i]);        ans+=sum(a[i]-1)*fac[n-i]%mod;        ans%=mod;add(a[i],-1);    }    write(ans);pc('\n');    return 0;}

逆康托展开

理论复杂度是 \(O(n\log n)\)的,但是实际上用暴力就行了(n!那么大

这个纯属瞎写的。根本用不到,但是复杂度确实是对的

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,a[N],fac[N],Q;namespace BT{    struct node    {        int ch[2],num,cnt,s,sz,sd;    }t[N];    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    double alpha=0.7;    int tot,rt;    int tmp[N];    int New(int x)    {        t[++tot]={0,0,x,1,1,1,1};        return tot;    }    void push_up(int at)    {        t[at].s=t[ls(at)].s+t[rs(at)].s+1;        t[at].sz=t[ls(at)].sz+t[rs(at)].sz+t[at].cnt;        t[at].sd=t[ls(at)].sd+t[rs(at)].sd+(t[at].cnt!=0);    }    bool CanR(int at)    {        double x=t[at].s*alpha;        return (t[at].cnt)&&(x<=(double)max(t[ls(at)].s,t[rs(at)].s)||        (double)t[at].sd<=x);    }    void CanR_flatten(int &idx,int at)    {        if(!at)return;        CanR_flatten(idx,ls(at));        if(t[at].cnt)tmp[++idx]=at;        CanR_flatten(idx,rs(at));    }    int CanR_build(int l,int r)    {        if(l>=r)return 0;        int mid=(l+r)>>1;        int &at=tmp[mid];        ls(at)=CanR_build(l,mid);        rs(at)=CanR_build(mid+1,r);        push_up(at);        return at;    }    void rebuild(int &at)    {        int idx=0;        CanR_flatten(idx,at);        at=CanR_build(1,idx+1);    }    void insert(int x,int &at)    {        if(!at){at=New(x);return;}        if(t[at].num==x)++t[at].cnt;        else if(x<t[at].num)insert(x,ls(at));        else insert(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    void remove(int x,int &at)    {        if(!at)return;        if(t[at].num==x)        {            if(t[at].cnt)--t[at].cnt;        }else if(x<t[at].num)remove(x,ls(at));        else remove(x,rs(at));        push_up(at);        if(CanR(at))rebuild(at);    }    int getval(int x,int at)    {        if(!at)return INF;        if(x<=t[ls(at)].sz)return getval(x,ls(at));        if(x<=t[ls(at)].sz+t[at].cnt)return t[at].num;        else return getval(x-t[ls(at)].sz-t[at].cnt,rs(at));    }    int uprbd(int x,int at)    {        if(!at)return 1;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz+t[at].cnt+1;        else if(x<t[at].num)return uprbd(x,ls(at));        else return uprbd(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int uprbd_gt(int x,int at)    {        if(!at)return 0;        if(x==t[at].num&&t[at].cnt)return t[ls(at)].sz;        else if(x<t[at].num)return uprbd_gt(x,ls(at));        else return uprbd_gt(x,rs(at))+t[ls(at)].sz+t[at].cnt;    }    int getpre(int x,int at){return getval(uprbd_gt(x,at),at);}    int getnext(int x,int at){return getval(uprbd(x,at),at);}}namespace BIT{    int tree[N];    #define lowbit(x) (x&-x)    void add(int x,int v)    {        while(x&&x<=n)        {            tree[x]+=v;            x+=lowbit(x);        }    }    int sum(int x)    {        int res=0;        while(x>=1)        {            res+=tree[x];            x-=lowbit(x);        }        return res;    }}signed main(){    using namespace BIT;    using namespace BT;    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> Q;fac[0]=1;    for(int i=1; i<=n; i++)        fac[i]=fac[i-1]*i,add(i,1);    while(Q--)    {        char op;        cin >> op;        if(op=='P')        {            int k,r;cin >> k;            tot=rt=0;--k;            for(int i=1; i<=n; i++)                insert(i,rt);            for(int i=1; i<=n; i++)            {                r=k%fac[n-i];k/=fac[n-i];                int p=getval(k+1,rt);                // cout << p << endl;                cout << p << " \n"[i==n];                remove(p,rt);k=r;            }        }else        {            int ans=1;            memset(tree,0,(n+1)*sizeof(int));            for(int i=1; i<=n; i++) add(i,1);            for(int i=1,x; i<=n; i++)            {                cin >> x;                ans+=sum(x-1)*fac[n-i];                add(x,-1);            }            cout << ans << endl;        }    }    return 0;}

高斯消元

P3389【模板】高斯消元法

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(150)const int eps=1e-10;int dcmp(int x){if(abs(x)<=eps)return 0; return x>eps?1:-1;}int n;double a[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        for(int j=1; j<=n+1; j++)            cin >> a[i][j];    for(int i=1; i<=n; i++)    {        int p=i;        for(int j=i+1; j<=n; j++)            if(abs(a[j][i])>abs(a[p][i])) p=j;        for(int j=1; j<=n+1; j++)            swap(a[i][j],a[p][j]);        if(!dcmp(a[i][i])) return cout << "No Solution",0;        for(int j=1; j<=n; j++)            if(j!=i)            {                double tmp=a[j][i]/a[i][i];                for(int k=i+1; k<=n+1; k++)                    a[j][k]-=a[i][k]*tmp;            }    }    cout << fixed << setprecision(2);    for(int i=1; i<=n; i++)        cout << a[i][n+1]/a[i][i] << '\n';    return 0;}
]]>
@@ -4676,7 +4676,7 @@ /2022/06/06/luo-gu-p1272-chong-jian-dao-lu-ti-jie/ - 洛谷P1272 重建道路 题解

题目链接:P1272 重建道路

题意

一场可怕的地震后,人们用 $N$ 个牲口棚(编号 $1\sim N$)重建了农夫 John 的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的。因此,牧场运输系统可以被构建成一棵树。

John 想要知道另一次地震会造成多严重的破坏。有些道路一旦被毁坏,就会使一棵含有 $P$ 个牲口棚的子树和剩余的牲口棚分离,John 想知道这些道路的最小数目。

$1\le N\le 150$,$1\le P\le N$,保证给出的是一棵树。

题目有点难懂,意思就是说切最少刀切出一个大小为 $p$ 的块,显然树上背包

设 $dp[u][j]$ 表示 $u$ 所在子树切出大小为 $j$ 的块并强制包含 $u$ 的最小花费,则

其中 $\text{tmp}[j]$ 为上一轮的 $dp[u][j]$ 。

后面一个柿子比较难懂

考虑一个大小为 $j$ 的块(包含 $u$ )和一个大小为 $k$ 的块(包含 $v$ )合并,显然要把和 $v$ 相连的那条边连上,但是本来是算在里面的(虽然是在最后才算的)

解释可能不是很清楚,直接看代码好了,细节比较多的

这道题的题解真是误导大众,填表法的树上背包是假的啊!

所有sz在dp前加的都是假复杂度。别怪我没提醒你们哦

时间复杂度 $O(np)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(205)int n,p;int tmp[N],sz[N],dp[N][N];vector<int> vec[N];void dfs(int u,int f){    sz[u]=1;dp[u][1]=0;    for(int v : vec[u])        if(v!=f)++dp[u][1];    for(int v : vec[u])    {        if(v==f)continue;        dfs(v,u);        for(int j=0; j<=min(sz[u],p); j++)            tmp[j]=dp[u][j];        for(int j=1; j<=min(sz[u],p); j++)            for(int k=1; k<=min(sz[v],p)&&j+k<=p; k++)                dp[u][j+k]=min(dp[u][j+k],tmp[j]+dp[v][k]-1);        sz[u]+=sz[v];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> p;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);    }    memset(dp,0x3f,sizeof(dp));    dfs(1,1);int res=dp[1][p];    for(int i=2; i<=n; i++)        res=min(res,dp[i][p]+1);    cout << res << '\n';    return 0;}
]]>
+ 洛谷P1272 重建道路 题解

题目链接:P1272重建道路

题意

一场可怕的地震后,人们用 \(N\)个牲口棚(编号 \(1\sim N\))重建了农夫John的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的。因此,牧场运输系统可以被构建成一棵树。

John想要知道另一次地震会造成多严重的破坏。有些道路一旦被毁坏,就会使一棵含有\(P\)个牲口棚的子树和剩余的牲口棚分离,John 想知道这些道路的最小数目。

\(1\le N\le 150\)\(1\le P\le N\),保证给出的是一棵树。

题目有点难懂,意思就是说切最少刀切出一个大小为 \(p\) 的块,显然树上背包

\(dp[u][j]\) 表示 \(u\) 所在子树切出大小为 \(j\) 的块并强制包含 \(u\) 的最小花费,则 \[dp[u][j+k]=\min(dp[u][j+k],\text{tmp}[j]+dp[v][k]-1)\] 其中 \(\text{tmp}[j]\)为上一轮的 \(dp[u][j]\)

后面一个柿子比较难懂

考虑一个大小为 \(j\) 的块(包含\(u\) )和一个大小为 \(k\) 的块(包含 \(v\) )合并,显然要把和 \(v\)相连的那条边连上,但是本来是算在里面的(虽然是在最后才算的)

解释可能不是很清楚,直接看代码好了,细节比较多的

这道题的题解真是误导大众,填表法的树上背包是假的啊!

所有sz在dp前加的都是假复杂度。别怪我没提醒你们哦

时间复杂度 \(O(np)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(205)int n,p;int tmp[N],sz[N],dp[N][N];vector<int> vec[N];void dfs(int u,int f){    sz[u]=1;dp[u][1]=0;    for(int v : vec[u])        if(v!=f)++dp[u][1];    for(int v : vec[u])    {        if(v==f)continue;        dfs(v,u);        for(int j=0; j<=min(sz[u],p); j++)            tmp[j]=dp[u][j];        for(int j=1; j<=min(sz[u],p); j++)            for(int k=1; k<=min(sz[v],p)&&j+k<=p; k++)                dp[u][j+k]=min(dp[u][j+k],tmp[j]+dp[v][k]-1);        sz[u]+=sz[v];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> p;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);    }    memset(dp,0x3f,sizeof(dp));    dfs(1,1);int res=dp[1][p];    for(int i=2; i<=n; i++)        res=min(res,dp[i][p]+1);    cout << res << '\n';    return 0;}
]]>
@@ -4690,10 +4690,10 @@ 算法 - DP - 图论 + DP + 数据结构 @@ -4707,7 +4707,7 @@ /2022/06/06/luo-gu-p1734-zui-da-yue-shu-he-ti-jie/ - 洛谷P1734 最大约数和 题解

题目链接:P1734 最大约数和

题意:选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。

设 $dp[i][j]$ 表示只考虑前 $i$ 个数总和不超过 $j$ 的最大价值,则

$v[i]$ 可以 $O(n \log n)$ 预处理

时间复杂度 $O(n^2)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,v[N],dp[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    auto f = [=](int x)    {        for(int i=1; i*i<=x; i++)            if(x%i==0)v[x]+=i+((x/i!=i)?(x/i):0);        v[x]-=x;    };    for(int i=1; i<=n; i++) f(i);    for(int i=1; i<=n; i++)        for(int j=n; j>=i; j--)            dp[j]=max(dp[j],dp[j-i]+v[i]);    cout << dp[n];    return 0;}
]]>
+ 洛谷P1734 最大约数和 题解

题目链接:P1734最大约数和

题意:选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大。

\(dp[i][j]\) 表示只考虑前 \(i\) 个数总和不超过 \(j\) 的最大价值,则 \[dp[i][j]= \max(dp[i-1][j],dp[i-1][j-i]+v[i])\] \(v[i]\) 可以 \(O(n \log n)\) 预处理

时间复杂度 \(O(n^2)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,v[N],dp[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    auto f = [=](int x)    {        for(int i=1; i*i<=x; i++)            if(x%i==0)v[x]+=i+((x/i!=i)?(x/i):0);        v[x]-=x;    };    for(int i=1; i<=n; i++) f(i);    for(int i=1; i<=n; i++)        for(int j=n; j>=i; j--)            dp[j]=max(dp[j],dp[j-i]+v[i]);    cout << dp[n];    return 0;}
]]>
@@ -4734,7 +4734,7 @@ /2022/06/06/luo-gu-p4322-jsoi2016-zui-jia-tuan-ti-ti-jie/ - 洛谷P4322 [JSOI2016]最佳团体 题解

题目链接:P4322 [JSOI2016]最佳团体

题意

JSOI 信息学代表队一共有 $N$ 名候选人,这些候选人从 $1$ 到 $N$ 编号。方便起见,JYY 的编号是 $0$ 号。每个候选人都由一位编号比他小的候选人$R_i$ 推荐。如果 $R_i = 0$,则说明这个候选人是 JYY 自己看上的。

为了保证团队的和谐,JYY 需要保证,如果招募了候选人 $i$,那么候选人 $R_i$ 也一定需要在团队中。当然了,JYY 自己总是在团队里的。每一个候选人都有一个战斗值 $P_i$ ,也有一个招募费用 $S_i$ 。JYY 希望招募 $K$ 个候选人(JYY 自己不算),组成一个性价比最高的团队。也就是,这 $K$ 个被 JYY 选择的候选人的总战斗值与总招募费用的比值最大。

对于100%的数据满足$1≤K≤N≤2500$,$0<S_i,P_i≤10^4$ ,
$0$ $≤$ $R_i$ $<$ $i$

下面的讲述用 $m$ 代替 $K$ ,$n$ 代替 $N$ 。

这个分数怎么dp呢?

其实这题就是01分数规划+树上背包的简单有趣题

设某次选择的答案为

移项可得

二分一个 $x$ ,注意 $x$ 为实数且 $x \in [1,10^4]$

则dp的价值就变成了价值为 $C_i = P_i - x S_i$ 的人 $i$

设 $dp[u][j]$ 表示 $u$ 子树选恰好 $j$ 个人时能获得的最大价值

则有转移方程

其中 $\text{tmp}[j]$ 为上一轮的 $dp[u][j]$

因为选了儿子一定要选父亲,因此转移时 $j$ 要从 $1$ 开始

然后我们直接把 $0$ 当做一个没有价值的人,然后把m=m+1

每次二分时判断 $dp[u][m]\ge0$ 是否成立,成立就l=mid

然后每次dp的时候sz都要重新算,否则复杂度是假的。

细节比较多,详见代码。

时间复杂度 $O(\log_2 10^4 \times nm)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e3+15)const double eps=1e-13;int n,m,sz[N],s[N],p[N];vector<int> vec[N];double dp[N][N],tmp[N],c[N];void dfs(int u){    sz[u]=1;    dp[u][0]=0;dp[u][1]=c[u];    for(int v : vec[u])    {        dfs(v);        for(int j=1; j<=min(sz[u],m); j++)            tmp[j]=dp[u][j];        for(int j=1; j<=min(sz[u],m); j++)            for(int k=0; k<=min(sz[v],m)&&j+k<=m; k++)                dp[u][j+k]=fmax(dp[u][j+k],tmp[j]+dp[v][k]);        sz[u]+=sz[v];    }}bool ck(double x){    for(int i=0; i<=n; i++)    {        c[i]=p[i]-x*s[i];        for(int j=1; j<=m; j++)            dp[i][j]=-1e100;    }    dfs(0);return dp[0][m]>=-eps;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> m >> n;++m;    for(int i=1,fa; i<=n; i++)    {        cin >> s[i] >> p[i] >> fa;        vec[fa].push_back(i);    }    double l=0,r=1e4;    while(fabs(r-l)>1e-7)    {        double mid=(l+r)/2;        if(ck(mid))l=mid;        else r=mid;    }    cout << fixed << setprecision(3);    cout << l << '\n';    return 0;}
]]>
+ 洛谷P4322 [JSOI2016]最佳团体题解

题目链接:P4322[JSOI2016]最佳团体

题意

JSOI 信息学代表队一共有 \(N\)名候选人,这些候选人从 \(1\)\(N\) 编号。方便起见,JYY 的编号是 \(0\)号。每个候选人都由一位编号比他小的候选人\(R_i\) 推荐。如果 \(R_i = 0\),则说明这个候选人是 JYY自己看上的。

为了保证团队的和谐,JYY 需要保证,如果招募了候选人 \(i\),那么候选人 \(R_i\) 也一定需要在团队中。当然了,JYY自己总是在团队里的。每一个候选人都有一个战斗值 \(P_i\) ,也有一个招募费用 \(S_i\) 。JYY 希望招募 \(K\) 个候选人(JYY自己不算),组成一个性价比最高的团队。也就是,这 \(K\) 个被 JYY选择的候选人的总战斗值与总招募费用的比值最大。

对于100%的数据满足\(1≤K≤N≤2500\),\(0<S_i,P_i≤10^4\) , \(0\) \(≤\)\(R_i\) \(<\) \(i\)

下面的讲述用 \(m\) 代替 \(K\) ,\(n\) 代替 \(N\) 。

这个分数怎么dp呢?

其实这题就是01分数规划+树上背包的简单有趣题

设某次选择的答案为 \[\sum P_i \times \dfrac{1}{\sum S_i}\]\[\sum P_i \times \dfrac{1}{\sum S_i} \ge x\] 移项可得 \[\sum(P_i-xS_i) \ge 0\] 二分一个 \(x\) ,注意 \(x\) 为实数\(x \in [1,10^4]\)

则dp的价值就变成了价值为 \(C_i = P_i - xS_i\) 的人 \(i\)

\(dp[u][j]\) 表示 \(u\) 子树选恰好 \(j\) 个人时能获得的最大价值

则有转移方程 \[dp[u][j+k]=\max_{1 \le j \le \min(m,\text{sz}[u]),0 \le k \le\min(m,\text{sz}[v])}(dp[u][j+k],\text{tmp}[j] + dp[v][k])\] 其中 \(\text{tmp}[j]\)为上一轮的 \(dp[u][j]\)

因为选了儿子一定要选父亲,因此转移时 \(j\) 要从 \(1\) 开始

然后我们直接把 \(0\)当做一个没有价值的人,然后把m=m+1

每次二分时判断 \(dp[u][m]\ge0\)是否成立,成立就l=mid

然后每次dp的时候sz都要重新算,否则复杂度是假的。

细节比较多,详见代码。

时间复杂度 \(O(\log_2 10^4 \timesnm)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e3+15)const double eps=1e-13;int n,m,sz[N],s[N],p[N];vector<int> vec[N];double dp[N][N],tmp[N],c[N];void dfs(int u){    sz[u]=1;    dp[u][0]=0;dp[u][1]=c[u];    for(int v : vec[u])    {        dfs(v);        for(int j=1; j<=min(sz[u],m); j++)            tmp[j]=dp[u][j];        for(int j=1; j<=min(sz[u],m); j++)            for(int k=0; k<=min(sz[v],m)&&j+k<=m; k++)                dp[u][j+k]=fmax(dp[u][j+k],tmp[j]+dp[v][k]);        sz[u]+=sz[v];    }}bool ck(double x){    for(int i=0; i<=n; i++)    {        c[i]=p[i]-x*s[i];        for(int j=1; j<=m; j++)            dp[i][j]=-1e100;    }    dfs(0);return dp[0][m]>=-eps;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> m >> n;++m;    for(int i=1,fa; i<=n; i++)    {        cin >> s[i] >> p[i] >> fa;        vec[fa].push_back(i);    }    double l=0,r=1e4;    while(fabs(r-l)>1e-7)    {        double mid=(l+r)/2;        if(ck(mid))l=mid;        else r=mid;    }    cout << fixed << setprecision(3);    cout << l << '\n';    return 0;}
]]>
@@ -4748,10 +4748,10 @@ 算法 - DP - 图论 + DP + 01分数规划 @@ -4765,7 +4765,7 @@ /2022/06/06/luo-gu-p2868-usaco07dec-sightseeing-cows-g-ti-jie/ - 洛谷P2868 [USACO07DEC]Sightseeing Cows G 题解

题目链接:P2868 [USACO07DEC]Sightseeing Cows G

题意

给定有向图,有点权 $F_i$ 和边权 $T_i$ ,找一条回路(不一定是简单回路),使得

尽可能的大,求出这个值

若回路的结点序列中含相同结点,只计算一次点权

$2 \le n \le 10^3,1\le m \le 5 \times 10^3,1 \le F_i,T_i \le 10^3$

比较经典的01分数规划问题

移项可得

取个反可得

然后就变成了判负环的问题。

二分一个 $x$ ,然后每次跑个spfa检验一下

时间复杂度 $O(\log_2 10^7 \times nm)$

为什么大于也可以求出答案呢?因为当 $x$ 足够精确时,也就是答案了

这里有个问题,题目说的是回路,不是环(简单回路),

不过事实上答案中都是环(简单回路)

这里有个证明,比较繁琐,因此我来提供一种简单证明


证:考虑最简单的情况,

设两个环 $A,B$ 的交集为结点 $u$ (此时图可以是一个8字形)

设 $A$ 的答案是 $x$ ,$B$ 的答案是 $y$

  • 若 $x=y$ ,

    则若合并 $A,B$ ,多出来的几条连接 $u$ 的边会使分母增加

    则答案小于 $x$

    只有这几条边的 $T_i$ 都为 $0$ 时,

    合并后的答案才能达到 $x$ ,但是数据范围里没有这种情况。

    故合并 $A,B$ 一定不是最优的。

  • 若 $x>y$ 或 $x<y$

    那显然合并了就更烂了哇。肯定不是最优的。

证毕。


代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)#define M (int)(5e3+15)struct Edge{    int u,v,T,next;}e[M];int n,m,F[N];double d[N];int head[N],pos=1,vis[N],cnt[N];void addEdge(int u,int v,int T){    e[++pos]={u,v,T,head[u]};    head[u]=pos;}queue<int> q;bool spfa(int st,double x){    while(!q.empty())q.pop();    q.push(st);vis[st]=1;d[st]=0;    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            double w=x*e[i].T-F[u];            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                if(!vis[v])                {                    q.push(v);++cnt[v];vis[v]=1;                    if(cnt[v]>=n)return 1;                }            }        }    }    return 0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++) cin >> F[i];    for(int i=1,u,v,T; i<=m; i++)    {        cin >> u >> v >> T;        addEdge(u,v,T);    }    double l=0,r=1005;    while(fabs(r-l)>1e-4)    {        double mid=(l+r)/2;        bool ok=0;        for(int i=1; i<=n; i++)        {            d[i]=1e66;            cnt[i]=vis[i]=0;        }        for(int i=1; i<=n; i++)        {            if(!cnt[i])            {                if(spfa(i,mid))                    {ok=1;break;}            }        }        ok?l=mid:r=mid;    }    cout << fixed << setprecision(2);    cout << l << '\n';    return 0;}
]]>
+ 洛谷P2868[USACO07DEC]Sightseeing Cows G 题解

题目链接:P2868[USACO07DEC]Sightseeing Cows G

题意

给定有向图,有点权 \(F_i\) 和边权\(T_i\),找一条回路(不一定是简单回路),使得 \[\dfrac{\sum F_i}{\sum T_i}\] 尽可能的大,求出这个值

若回路的结点序列中含相同结点,只计算一次点权

\(2 \le n \le 10^3,1\le m \le 5 \times10^3,1 \le F_i,T_i \le 10^3\)

比较经典的01分数规划问题

\[\sum F_i \times \dfrac{1}{\sum T_i} > x\] 移项可得 \[\sum (F_i - x T_i) > 0\] 取个反可得 \[\sum (xT_i - F_i) < 0\] 然后就变成了判负环的问题。

二分一个 \(x\),然后每次跑个spfa检验一下

时间复杂度 \(O(\log_2 10^7 \timesnm)\)

为什么大于也可以求出答案呢?因为当 \(x\) 足够精确时,也就是答案了

这里有个问题,题目说的是回路,不是环(简单回路),

不过事实上答案中都是环(简单回路)

这里有个证明,比较繁琐,因此我来提供一种简单证明


证:考虑最简单的情况,

设两个环 \(A,B\) 的交集为结点 \(u\) (此时图可以是一个8字形)

\(A\) 的答案是 \(x\) ,\(B\) 的答案是 \(y\)

  • \(x=y\)

    则若合并 \(A,B\) ,多出来的几条连接\(u\) 的边会使分母增加

    则答案小于 \(x\)

    只有这几条边的 \(T_i\) 都为 \(0\) 时,

    合并后的答案才能达到 \(x\),但是数据范围里没有这种情况。

    故合并 \(A,B\)一定不是最优的。

  • \(x>y\)\(x<y\)

    那显然合并了就更烂了哇。肯定不是最优的。

证毕。


代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)#define M (int)(5e3+15)struct Edge{    int u,v,T,next;}e[M];int n,m,F[N];double d[N];int head[N],pos=1,vis[N],cnt[N];void addEdge(int u,int v,int T){    e[++pos]={u,v,T,head[u]};    head[u]=pos;}queue<int> q;bool spfa(int st,double x){    while(!q.empty())q.pop();    q.push(st);vis[st]=1;d[st]=0;    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            double w=x*e[i].T-F[u];            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                if(!vis[v])                {                    q.push(v);++cnt[v];vis[v]=1;                    if(cnt[v]>=n)return 1;                }            }        }    }    return 0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++) cin >> F[i];    for(int i=1,u,v,T; i<=m; i++)    {        cin >> u >> v >> T;        addEdge(u,v,T);    }    double l=0,r=1005;    while(fabs(r-l)>1e-4)    {        double mid=(l+r)/2;        bool ok=0;        for(int i=1; i<=n; i++)        {            d[i]=1e66;            cnt[i]=vis[i]=0;        }        for(int i=1; i<=n; i++)        {            if(!cnt[i])            {                if(spfa(i,mid))                    {ok=1;break;}            }        }        ok?l=mid:r=mid;    }    cout << fixed << setprecision(2);    cout << l << '\n';    return 0;}
]]>
@@ -4796,7 +4796,7 @@ /2022/06/06/poj2976-dropping-tests-ti-jie/ - POJ2976 Dropping tests 题解

题目链接:POJ2976 Dropping tests

题意

Description

In a certain course, you take n tests. If you get ai out of bi questions correct on test i, your cumulative average is defined to be

.

Given your test scores and a positive integer k, determine how high you can make your cumulative average if you are allowed to drop any k of your test scores.

Suppose you take 3 tests with scores of 5/5, 0/1, and 2/6. Without dropping any tests, your cumulative average is . However, if you drop the third test, your cumulative average becomes .

Input

The input test file will contain multiple test cases, each containing exactly three lines. The first line contains two integers, 1 ≤ n ≤ 1000 and 0 ≤ k < n. The second line contains n integers indicating ai for all i. The third line contains n positive integers indicating bi for all i. It is guaranteed that 0 ≤ aibi ≤ 1, 000, 000, 000. The end-of-file is marked by a test case with n = k = 0 and should not be processed.

Output

For each test case, write a single line with the highest cumulative average possible after dropping k of the given test scores. The average should be rounded to the nearest integer.

经典的01分数规划问题

注意到题目要求的就是选 $n-k$ 个物品使得

尽可能的大

不妨假设

移项可得

于是可以二分一个 $x$

每次暴力检验 $ps[i] = a_i - xb_i$

注意要排个序然后取最大的 $n-k$ 个 $ps[i]$

时间复杂度 $O(\log_2 10^6 \times Q n \log n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)const double eps=1e-13;int n,k,a[N],b[N];double ps[N];bool ck(double x){    for(int i=1; i<=n; i++)        ps[i]=a[i]-x*b[i];    sort(ps+1,ps+1+n);    double res=0;    for(int i=k+1; i<=n; i++)        res+=ps[i];    return res>=0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(cin >> n >> k && n)    {        for(int i=1; i<=n; i++) cin >> a[i];        for(int i=1; i<=n; i++) cin >> b[i];        double l=0,r=1;        while(r-l>1e-6)        {            double mid=(l+r)/2;            if(ck(mid))l=mid;            else r=mid;        }        cout << l*100 << endl;    }    return 0;}
]]>
+ POJ2976 Dropping tests 题解

题目链接:POJ2976 Droppingtests

题意

Description

In a certain course, you take n tests. If you getai out of bi questions correct on test i,your cumulative average is defined to be

.

Given your test scores and a positive integer k, determinehow high you can make your cumulative average if you are allowed to dropany k of your test scores.

Suppose you take 3 tests with scores of 5/5, 0/1, and 2/6. Withoutdropping any tests, your cumulative average is . However, if you drop thethird test, your cumulative average becomes .

Input

The input test file will contain multiple test cases, each containingexactly three lines. The first line contains two integers, 1 ≤n ≤ 1000 and 0 ≤ k < n. The second linecontains n integers indicating ai for all i.The third line contains n positive integers indicatingbi for all i. It is guaranteed that 0 ≤ aibi ≤ 1, 000, 000, 000. The end-of-file is marked by a test casewith n = k = 0 and should not be processed.

Output

For each test case, write a single line with the highest cumulativeaverage possible after dropping k of the given test scores. Theaverage should be rounded to the nearest integer.

经典的01分数规划问题

注意到题目要求的就是选 \(n-k\)个物品使得 \[\sum a_i \times \dfrac{1}{\sum b_i}\] 尽可能的大

不妨假设 \[\sum a_i \times \dfrac{1}{\sum b_i} \ge x\] 移项可得 \[\sum a_i - x \sum b_i \ge 0\] 于是可以二分一个 \(x\)

每次暴力检验 \(ps[i] = a_i -xb_i\)

注意要排个序然后取最大的 \(n-k\)\(ps[i]\)

时间复杂度 \(O(\log_2 10^6 \times Q n \logn)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)const double eps=1e-13;int n,k,a[N],b[N];double ps[N];bool ck(double x){    for(int i=1; i<=n; i++)        ps[i]=a[i]-x*b[i];    sort(ps+1,ps+1+n);    double res=0;    for(int i=k+1; i<=n; i++)        res+=ps[i];    return res>=0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(cin >> n >> k && n)    {        for(int i=1; i<=n; i++) cin >> a[i];        for(int i=1; i<=n; i++) cin >> b[i];        double l=0,r=1;        while(r-l>1e-6)        {            double mid=(l+r)/2;            if(ck(mid))l=mid;            else r=mid;        }        cout << l*100 << endl;    }    return 0;}
]]>
@@ -4823,7 +4823,7 @@ /2022/06/05/luo-gu-p1270-fang-wen-mei-zhu-guan-ti-jie/ - 洛谷P1270 “访问”美术馆 题解

题目链接:P1270 “访问”美术馆

题意

经过数月的精心准备,Peer Brelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。Peer知道每个展室里藏画的数量,并且他精确测量了通过每条走廊的时间。由于经验老到,他拿下一幅画需要5秒的时间。你的任务是编一个程序,计算在警察赶来之前,他最多能偷到多少幅画。

显然的树上背包题,还是个二叉树

读入可以用dfs,然后走廊耗时肯定要变原来两倍

设 $dp[u][j]$ 表示 $u$ 所在子树花不超过 $j$ 秒能获得的最大价值,则

如果 $u$ 是叶子结点的话,可以枚举它的耗时然后算能拿几幅画

for(int i=0; i/5<=val[u]&&w[u]+i<=m; i++)    dp[u][w[u]+i]=i/5;

时间复杂度 $O(nm)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(615)int m,w[N],sz[N],val[N],tmp[N],dp[N][N];void init(int u){    cin >> w[u] >> val[u];w[u]<<=1;    if(!val[u])        init(u<<1),init(u<<1|1);}void dfs(int u){    if(val[u])    {        for(int i=0; i/5<=val[u]&&w[u]+i<=m; i++)            dp[u][w[u]+i]=i/5;        return;    }    dfs(u<<1);dfs(u<<1|1);    for(int j=w[u]; j<=m; j++)        for(int k=0; k+w[u]<=j; k++)            dp[u][j]=max(dp[u][j],dp[u<<1][j-w[u]-k]+dp[u<<1|1][k]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> m;--m;    init(1);dfs(1);    cout << dp[1][m] << '\n';    return 0;}
]]>
+ 洛谷P1270 “访问”美术馆 题解

题目链接:P1270“访问”美术馆

题意

经过数月的精心准备,PeerBrelstet,一个出了名的盗画者,准备开始他的下一个行动。艺术馆的结构,每条走廊要么分叉为两条走廊,要么通向一个展览室。Peer知道每个展室里藏画的数量,并且他精确测量了通过每条走廊的时间。由于经验老到,他拿下一幅画需要5秒的时间。你的任务是编一个程序,计算在警察赶来之前,他最多能偷到多少幅画。

显然的树上背包题,还是个二叉树

读入可以用dfs,然后走廊耗时肯定要变原来两倍

\(dp[u][j]\) 表示 \(u\) 所在子树花不超过 \(j\) 秒能获得的最大价值,则 \[dp[u][j] =\max(dp[u][j],dp[\text{ls}][j-k-\text{w}[u]]+dp[\text{rs}][k])\] 如果 \(u\)是叶子结点的话,可以枚举它的耗时然后算能拿几幅画

for(int i=0; i/5<=val[u]&&w[u]+i<=m; i++)    dp[u][w[u]+i]=i/5;

时间复杂度 \(O(nm)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(615)int m,w[N],sz[N],val[N],tmp[N],dp[N][N];void init(int u){    cin >> w[u] >> val[u];w[u]<<=1;    if(!val[u])        init(u<<1),init(u<<1|1);}void dfs(int u){    if(val[u])    {        for(int i=0; i/5<=val[u]&&w[u]+i<=m; i++)            dp[u][w[u]+i]=i/5;        return;    }    dfs(u<<1);dfs(u<<1|1);    for(int j=w[u]; j<=m; j++)        for(int k=0; k+w[u]<=j; k++)            dp[u][j]=max(dp[u][j],dp[u<<1][j-w[u]-k]+dp[u<<1|1][k]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> m;--m;    init(1);dfs(1);    cout << dp[1][m] << '\n';    return 0;}
]]>
@@ -4837,10 +4837,10 @@ 算法 - DP - 图论 + DP + @@ -4852,7 +4852,7 @@ /2022/06/03/luo-gu-p3354-ioi2005-riv-he-liu-ti-jie/ - 洛谷P3354 [IOI2005]Riv 河流 题解

题目链接:P3354 [IOI2005]Riv 河流

题意

几乎整个 Byteland 王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海。这条大河的入海口处有一个村庄——名叫 Bytetown。

在 Byteland 国,有 $n$ 个伐木的村庄,这些村庄都座落在河边。目前在 Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料。木料被砍下后,顺着河流而被运到 Bytetown 的伐木场。Byteland 的国王决定,为了减少运输木料的费用,再额外地建造 $k$ 个伐木场。这 $k$ 个伐木场将被建在其他村庄里。这些伐木场建造后,木料就不用都被送到 Bytetown 了,它们可以在运输过程中第一个碰到的新伐木场被处理。显然,如果伐木场座落的那个村子就不用再付运送木料的费用了。它们可以直接被本村的伐木场处理。

注:所有的河流都不会分叉,形成一棵树,根结点是 Bytetown。

国王的大臣计算出了每个村子每年要产多少木料,你的任务是决定在哪些村子建设伐木场能获得最小的运费。其中运费的计算方法为:每一吨木料每千米 $1$ 分钱。

  • 对于 $50\%$ 的数据,保证 $n\le 20$。
  • 对于 $100\%$ 的数据,保证 $2\le n\le 100$,$1\le k\le \min(n,50)$,$0\le v_i\le n$,$0\le w_i\le 10^4$,$1\le d_i\le 10^4$。
  • 保证每年所有的木料流到 bytetown 的运费不超过 $2\times 10^9$ 分。

题解区写的啥啊 那我来一篇正经的树上背包吧

注意到经典的 $dp[u][j]$ 表示 $u$ 所在子树建了 $j$ 个伐木场的最小花费,是无法计算的,因为我们并不知道子树有多少木头,以及他们会运到哪里

考虑记录一下 $u$ 结点是否放伐木场,同时把最小花费转化为最大价值

怎么转化?对于一个结点,在它上面建伐木场,则从该结点向叶子结点走,

每次遇到的第一个伐木场之前的,都会将木材运至 $u$ ,而不是根节点

设 $f[u][j]$ 表示 $u$ 所在子树建了 $j$ 个伐木场,且 $u$ 不建伐木场的最大价值(也就是最多节省的路费)

设 $g[u][j]$ 表示 $u$ 所在子树建了 $j$ 个伐木场,且 $u$ 必须建伐木场的最大价值

那么对于每个结点,木头要上传(根节点方向)到哪个结点呢

一种写法是增加一维记录最近的祖先伐木场在哪里,然后用栈维护

注:我本来写的是上面的,但是复杂度有点高(虽然能过)

这里讲一种不用增加维度的方法

我们每次假定 $u$ 一定建伐木场,然后对于每个 $u$ 都求出它的 $f$ 数组

同时设一个 $h[u][j]= \max(f[u][j],g[u][j])$ ,

当然实际上我们不需要单独设数组,直接合并到 $f$ 里就行了

然后将 $f$ (也就是 $h$ )转移到 $g$ 上,清空 $f$ 数组

重复这样的过程,然后根节点单独处理即可

时间复杂度 $O(n^2k)$ ,目前没卡常rank3(rank1是假的不管了

好像官方题解是 $O(n^2k^2)$ 的,不过那都是多老的东西了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define N (int)(115)int n,m,ans,val[N],tmp[N];int dis[N],sz[N],f[N][N],g[N][N];struct Edge{int u,v,w,next;}e[N];int pos=1,head[N];void addEdge(int u,int v,int w){e[++pos]={u,v,w,head[u]};head[u]=pos;}void Max(int &a,int b){a=a>b?a:b;} void DP(int u,int gra){f[u][0]=dis[gra]*val[u];for(int i=head[u]; i; i=e[i].next){int v=e[i].v; DP(v,gra);for(int j=0; j<=min(m,sz[u]); j++)tmp[j]=f[u][j];for(int j=0; j<=min(m,sz[u]); j++)for(int k=0; k<=min(m,sz[v])&&j+k<=m; k++)Max(f[u][j+k],tmp[j]+f[v][k]);}for(int j=1; j<=min(m,sz[u]); j++) Max(f[u][j],g[u][j]);}void dfs(int u){sz[u]=1;for(int i=head[u]; i; i=e[i].next){int v=e[i].v;dis[v]=e[i].w+dis[u]; dfs(v); }g[u][0]=0; ans+=dis[u]*val[u];memset(f,0,sizeof(f));for(int i=head[u]; i; i=e[i].next){int v=e[i].v; DP(v,u);for(int j=0; j<=min(m,sz[u]); j++)tmp[j]=g[u][j];for(int j=0; j<=min(m,sz[u]); j++)for(int k=0; k<=min(m,sz[v])&&j+k<=m; k++)Max(g[u][j+k],tmp[j]+f[v][k]);sz[u]+=sz[v];}for(int i=min(m,sz[u]); i>=1; i--)g[u][i]=g[u][i-1]+dis[u]*val[u]; // 强制u建别忘了}signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> m;for(int i=1,v,w; i<=n; i++){cin >> val[i] >> v >> w;addEdge(v,i,w);}dfs(0);int res=0;memset(f,0,sizeof(f));sz[0]=1;for(int i=head[0]; i; i=e[i].next) {int v=e[i].v; DP(v,0);for(int j=0; j<=min(m,sz[0]); j++)tmp[j]=f[0][j];        for(int j=0; j<=min(m,sz[0]); j++)for(int k=0; k<=min(m,sz[v])&&j+k<=m; k++)Max(f[0][j+k],tmp[j]+f[v][k]);sz[0]+=sz[v];}for(int i=1; i<=min(m,sz[0]); i++) res=max(res,f[0][i]);cout << ans-res << '\n';return 0;}
]]>
+ 洛谷P3354 [IOI2005]Riv 河流题解

题目链接:P3354[IOI2005]Riv 河流

题意

几乎整个 Byteland王国都被森林和河流所覆盖。小点的河汇聚到一起,形成了稍大点的河。就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海。这条大河的入海口处有一个村庄——名叫Bytetown。

在 Byteland 国,有 \(n\)个伐木的村庄,这些村庄都座落在河边。目前在Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料。木料被砍下后,顺着河流而被运到Bytetown 的伐木场。Byteland的国王决定,为了减少运输木料的费用,再额外地建造 \(k\) 个伐木场。这 \(k\)个伐木场将被建在其他村庄里。这些伐木场建造后,木料就不用都被送到Bytetown了,它们可以在运输过程中第一个碰到的新伐木场被处理。显然,如果伐木场座落的那个村子就不用再付运送木料的费用了。它们可以直接被本村的伐木场处理。

注:所有的河流都不会分叉,形成一棵树,根结点是 Bytetown。

国王的大臣计算出了每个村子每年要产多少木料,你的任务是决定在哪些村子建设伐木场能获得最小的运费。其中运费的计算方法为:每一吨木料每千米\(1\) 分钱。

  • 对于 \(50\%\) 的数据,保证 \(n\le 20\)。
  • 对于 \(100\%\) 的数据,保证 \(2\le n\le 100\),\(1\le k\le \min(n,50)\),\(0\le v_i\le n\),\(0\le w_i\le 10^4\),\(1\le d_i\le 10^4\)。
  • 保证每年所有的木料流到 bytetown 的运费不超过 \(2\times 10^9\) 分。

题解区写的啥啊 那我来一篇正经的树上背包吧

注意到经典的 \(dp[u][j]\) 表示 \(u\) 所在子树建了 \(j\)个伐木场的最小花费,是无法计算的,因为我们并不知道子树有多少木头,以及他们会运到哪里

考虑记录一下 \(u\)结点是否放伐木场,同时把最小花费转化为最大价值

怎么转化?对于一个结点,在它上面建伐木场,则从该结点向叶子结点走,

每次遇到的第一个伐木场之前的,都会将木材运至 \(u\) ,而不是根节点

\(f[u][j]\) 表示 \(u\) 所在子树建了 \(j\) 个伐木场,\(u\)不建伐木场的最大价值(也就是最多节省的路费)

\(g[u][j]\) 表示 \(u\) 所在子树建了 \(j\) 个伐木场,\(u\) 必须建伐木场的最大价值

那么对于每个结点,木头要上传(根节点方向)到哪个结点呢

一种写法是增加一维记录最近的祖先伐木场在哪里,然后用栈维护

注:我本来写的是上面的,但是复杂度有点高(虽然能过)

这里讲一种不用增加维度的方法

我们每次假定 \(u\)一定建伐木场,然后对于每个 \(u\)都求出它的 \(f\) 数组

同时设一个 \(h[u][j]=\max(f[u][j],g[u][j])\)

当然实际上我们不需要单独设数组,直接合并到 \(f\) 里就行了

然后将 \(f\) (也就是 \(h\) )转移到 \(g\) 上,清空 \(f\) 数组

重复这样的过程,然后根节点单独处理即可

时间复杂度 \(O(n^2k)\),目前没卡常rank3(rank1是假的不管了

好像官方题解是 \(O(n^2k^2)\)的,不过那都是多老的东西了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define N (int)(115)int n,m,ans,val[N],tmp[N];int dis[N],sz[N],f[N][N],g[N][N];struct Edge{int u,v,w,next;}e[N];int pos=1,head[N];void addEdge(int u,int v,int w){e[++pos]={u,v,w,head[u]};head[u]=pos;}void Max(int &a,int b){a=a>b?a:b;} void DP(int u,int gra){f[u][0]=dis[gra]*val[u];for(int i=head[u]; i; i=e[i].next){int v=e[i].v; DP(v,gra);for(int j=0; j<=min(m,sz[u]); j++)tmp[j]=f[u][j];for(int j=0; j<=min(m,sz[u]); j++)for(int k=0; k<=min(m,sz[v])&&j+k<=m; k++)Max(f[u][j+k],tmp[j]+f[v][k]);}for(int j=1; j<=min(m,sz[u]); j++) Max(f[u][j],g[u][j]);}void dfs(int u){sz[u]=1;for(int i=head[u]; i; i=e[i].next){int v=e[i].v;dis[v]=e[i].w+dis[u]; dfs(v); }g[u][0]=0; ans+=dis[u]*val[u];memset(f,0,sizeof(f));for(int i=head[u]; i; i=e[i].next){int v=e[i].v; DP(v,u);for(int j=0; j<=min(m,sz[u]); j++)tmp[j]=g[u][j];for(int j=0; j<=min(m,sz[u]); j++)for(int k=0; k<=min(m,sz[v])&&j+k<=m; k++)Max(g[u][j+k],tmp[j]+f[v][k]);sz[u]+=sz[v];}for(int i=min(m,sz[u]); i>=1; i--)g[u][i]=g[u][i-1]+dis[u]*val[u]; // 强制u建别忘了}signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> m;for(int i=1,v,w; i<=n; i++){cin >> val[i] >> v >> w;addEdge(v,i,w);}dfs(0);int res=0;memset(f,0,sizeof(f));sz[0]=1;for(int i=head[0]; i; i=e[i].next) {int v=e[i].v; DP(v,0);for(int j=0; j<=min(m,sz[0]); j++)tmp[j]=f[0][j];        for(int j=0; j<=min(m,sz[0]); j++)for(int k=0; k<=min(m,sz[v])&&j+k<=m; k++)Max(f[0][j+k],tmp[j]+f[v][k]);sz[0]+=sz[v];}for(int i=1; i<=min(m,sz[0]); i++) res=max(res,f[0][i]);cout << ans-res << '\n';return 0;}
]]>
@@ -4866,10 +4866,10 @@ 算法 - DP - 图论 + DP + @@ -4881,7 +4881,7 @@ /2022/06/02/luo-gu-p3237-hnoi2014-mi-te-yun-shu-ti-jie/ - 洛谷P3237 [HNOI2014]米特运输 题解

题目链接:P3237 [HNOI2014]米特运输

题意

米特是D星球上一种非常神秘的物质,蕴含着巨大的能量。在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题。

D星上有N个城市,我们将其顺序编号为1到N,1号城市为首都。这N个城市由N-1条单向高速通道连接起来,构成一棵以1号城市(首部)为根的树,高速通道的方向由树中的儿子指向父亲。树按深度分层:根结点深度为0,属于第1层;根结点的子节点深度为1,属于第2层;依此类推,深度为i的结点属于第i+l层。

建好高速通道之后,D星人开始考虑如何具体地储存和传输米特资源。由于发展程度不同,每个城市储存米特的能力不尽相同,其中第i个城市建有一个容量为A[i]的米特储存器。这个米特储存器除了具有储存的功能,还具有自动收集米特的能力。

如果到了晚上六点,有某个储存器处于未满的状态,它就会自动收集大气中蕴含的米特能源,在早上六点之前就能收集满;但是,只有在储存器完全空的状态下启动自动收集程序才是安全的,未满而又非空时启动可能有安全隐患。

早上六点到七点间,根节点城市(1号城市)会将其储存器里的米特消耗殆尽。根节点不会自动搜集米特,它只接受子节点传输来的米特。

早上七点,城市之间启动米特传输过程,传输过程逐层递进:先是第2层节点城市向第1层(根节点城市,即1号城市)传输,直到第1层的储存器满或第2层的储存器全为空;然后是第3层向第2层传输,直到对于第2层的每个节点,其储存器满或其予节点(位于第3层)的储存器全为空;依此类推,直到最后一层传输完成。传输过程一定会在晚上六点前完成。

由于技术原因,运输方案需要满足以下条件:

  1. 不能让某个储存器到了晚上六点传输结束时还处于非空但又未满的状态,这个时候储存器仍然会启动自动收集米特的程序,而给已经储存有米特的储存器启动收集程序可能导致危险,也就是说要让储存器到了晚上六点时要么空要么满;

  2. 关于首都——即1号城市的特殊情况, 每天早上六点到七点间1号城市中的米特储存器里的米特会自动被消耗殆尽,即运输方案不需要考虑首都的米特怎么运走;

  3. 除了1号城市,每个节点必须在其子节点城市向它运输米特之前将这座城市的米特储存器中原本存有的米特全部运出去给父节点,不允许储存器中残存的米特与外来的米特发生混合;

  4. 运向某一个城市的若干个来源的米特数量必须完全相同,不然,这些来源不同的米特按不同比例混合之后可能发生危险。

现在D星人已经建立好高速通道,每个城市也有了一定储存容量的米特储存器。为了满足上面的限制条件,可能需要重建一些城市中的米特储存器。你可以,也只能,将某一座城市(包括首都)中原来存在的米特储存器摧毁,再新建一座任意容量的新的米特储存器,其容量可以是小数(在输入数据中,储存器原始容量是正整数,但重建后可以是小数),不能是负数或零,使得需要被重建的米特储存器的数目尽量少。

题目讲了半天其实就是说

  • 给定一棵树,树上每个结点有权值

  • 求最少修改结点权值的次数,使得 $\text{val}[u]=\sum \text{val}[v]$

    其中 $v$ 为 $u$ 的儿子。

不知道谁把它放在树形dp题单里的(恼

手推一下就可以发现,只要确定了一个结点,其他所有的结点都可以确定了

考虑把每个结点确定后形成的树都给算出来

然后在这一堆树里面找到相同树的个数的最大值 $\text{mx}$

答案就是 $n-\text{mx}$

这个树怎么算呢,这里有一张图,来自link

。。。

即设 $f[u]$ 为 $u$ 所形成的树的”特征值”,则有

其中 $d$ 为 $u$ 的祖先结点。

似乎只要把这个值算出来就好了,不过它会爆long long

考虑用 $\log$ 转化计算( $\log (ab) = \log a + \log b$ )

然后比较的时候只要排序一下就好了

时间复杂度 $O(n\log n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define double long double#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)double f[N];vector<int> vec[N];int n,val[N],cnt=1,mn,sz[N],fa[N];const double eps=1e-13;int dcmp(double x){if(fabs(x)<=eps)return 0;return x>eps?1:-1;}void dfs1(int u,int f){    fa[u]=f;sz[u]=0;    for(int v : vec[u])        if(v!=f)sz[u]++,dfs1(v,u);}void dfs2(int u,double sum){    f[u]=sum+log((double)val[u]);    for(int v : vec[u])    {        if(v==fa[u])continue;        dfs2(v,sum+log((double)sz[u]));    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n; mn=n-1;    for(int i=1; i<=n; i++)        cin >> val[i];    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);    }    dfs1(1,1);dfs2(1,log(1.0));    sort(f+1,f+1+n);    for(int i=1; i<n; i++)    {        if(!dcmp(f[i]-f[i+1]))            ++cnt,mn=min(mn,n-cnt);        else cnt=1;    }    cout << mn;    return 0;}
]]>
+ 洛谷P3237 [HNOI2014]米特运输题解

题目链接:P3237[HNOI2014]米特运输

题意

米特是D星球上一种非常神秘的物质,蕴含着巨大的能量。在以米特为主要能源的D星上,这种米特能源的运输和储存一直是一个大问题。

D星上有N个城市,我们将其顺序编号为1到N,1号城市为首都。这N个城市由N-1条单向高速通道连接起来,构成一棵以1号城市(首部)为根的树,高速通道的方向由树中的儿子指向父亲。树按深度分层:根结点深度为0,属于第1层;根结点的子节点深度为1,属于第2层;依此类推,深度为i的结点属于第i+l层。

建好高速通道之后,D星人开始考虑如何具体地储存和传输米特资源。由于发展程度不同,每个城市储存米特的能力不尽相同,其中第i个城市建有一个容量为A[i]的米特储存器。这个米特储存器除了具有储存的功能,还具有自动收集米特的能力。

如果到了晚上六点,有某个储存器处于未满的状态,它就会自动收集大气中蕴含的米特能源,在早上六点之前就能收集满;但是,只有在储存器完全空的状态下启动自动收集程序才是安全的,未满而又非空时启动可能有安全隐患。

早上六点到七点间,根节点城市(1号城市)会将其储存器里的米特消耗殆尽。根节点不会自动搜集米特,它只接受子节点传输来的米特。

早上七点,城市之间启动米特传输过程,传输过程逐层递进:先是第2层节点城市向第1层(根节点城市,即1号城市)传输,直到第1层的储存器满或第2层的储存器全为空;然后是第3层向第2层传输,直到对于第2层的每个节点,其储存器满或其予节点(位于第3层)的储存器全为空;依此类推,直到最后一层传输完成。传输过程一定会在晚上六点前完成。

由于技术原因,运输方案需要满足以下条件:

  1. 不能让某个储存器到了晚上六点传输结束时还处于非空但又未满的状态,这个时候储存器仍然会启动自动收集米特的程序,而给已经储存有米特的储存器启动收集程序可能导致危险,也就是说要让储存器到了晚上六点时要么空要么满;

  2. 关于首都——即1号城市的特殊情况,每天早上六点到七点间1号城市中的米特储存器里的米特会自动被消耗殆尽,即运输方案不需要考虑首都的米特怎么运走;

  3. 除了1号城市,每个节点必须在其子节点城市向它运输米特之前将这座城市的米特储存器中原本存有的米特全部运出去给父节点,不允许储存器中残存的米特与外来的米特发生混合;

  4. 运向某一个城市的若干个来源的米特数量必须完全相同,不然,这些来源不同的米特按不同比例混合之后可能发生危险。

现在D星人已经建立好高速通道,每个城市也有了一定储存容量的米特储存器。为了满足上面的限制条件,可能需要重建一些城市中的米特储存器。你可以,也只能,将某一座城市(包括首都)中原来存在的米特储存器摧毁,再新建一座任意容量的新的米特储存器,其容量可以是小数(在输入数据中,储存器原始容量是正整数,但重建后可以是小数),不能是负数或零,使得需要被重建的米特储存器的数目尽量少。

题目讲了半天其实就是说

  • 给定一棵树,树上每个结点有权值

  • 求最少修改结点权值的次数,使得 \(\text{val}[u]=\sum \text{val}[v]\)

    其中 \(v\)\(u\) 的儿子。

不知道谁把它放在树形dp题单里的(恼

手推一下就可以发现,只要确定了一个结点,其他所有的结点都可以确定了

考虑把每个结点确定后形成的树都给算出来

然后在这一堆树里面找到相同树的个数的最大值 \(\text{mx}\)

答案就是 \(n-\text{mx}\)

这个树怎么算呢,这里有一张图,来自link

。。。

即设 \(f[u]\)\(u\) 所形成的树的"特征值",则有 \[f[u]=\prod \text{sz}[d] \times \text{val}[u]\] 其中 \(d\)\(u\) 的祖先结点。

似乎只要把这个值算出来就好了,不过它会爆long long

考虑用 \(\log\) 转化计算( \(\log (ab) = \log a + \log b\) )

然后比较的时候只要排序一下就好了

时间复杂度 \(O(n\log n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define double long double#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)double f[N];vector<int> vec[N];int n,val[N],cnt=1,mn,sz[N],fa[N];const double eps=1e-13;int dcmp(double x){if(fabs(x)<=eps)return 0;return x>eps?1:-1;}void dfs1(int u,int f){    fa[u]=f;sz[u]=0;    for(int v : vec[u])        if(v!=f)sz[u]++,dfs1(v,u);}void dfs2(int u,double sum){    f[u]=sum+log((double)val[u]);    for(int v : vec[u])    {        if(v==fa[u])continue;        dfs2(v,sum+log((double)sz[u]));    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n; mn=n-1;    for(int i=1; i<=n; i++)        cin >> val[i];    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);    }    dfs1(1,1);dfs2(1,log(1.0));    sort(f+1,f+1+n);    for(int i=1; i<n; i++)    {        if(!dcmp(f[i]-f[i+1]))            ++cnt,mn=min(mn,n-cnt);        else cnt=1;    }    cout << mn;    return 0;}
]]>
@@ -4908,7 +4908,7 @@ /2022/05/31/luo-gu-p1273-you-xian-dian-shi-wang-ti-jie/ - 洛谷P1273 有线电视网 题解

题目链接:P1273 有线电视网

题意

某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。

从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总费用等于传输信号的费用总和。

现在每个用户都准备了一笔费用想观看这场精彩的足球比赛,有线电视网有权决定给哪些用户提供信号而不给哪些用户提供信号。

写一个程序找出一个方案使得有线电视网在不亏本的情况下使观看转播的用户尽可能多。

如果直接去想怎么保证不超过边权和什么的不太好搞

考虑把每个用户数是否能够承担都算出来即可

也就是

设 $dp[i][u][j]$ 表示以 $u$ 为根的子树,仅用前 $i$ 个儿子,满足 $j$ 个客户能获得的最大价值

事实上我们并不是非常关心是否能获得的最大价值,我们只要不亏本的情况下尽可能多让人看罢了(好良心有没有

则有转移方程

可以发现这个东西可以滚动数组优化

然后就有

sz[u]+=sz[v];for(int j=sz[u]; j>=0; j--)    for(int k=1; k<=min(sz[v],j); k++)        dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-e[i].w);

AC?不,这样会TLE。参考讨论 link

因为这样的复杂度并不能保证为严格 $O(n^2)$ 的,这也就是所谓的实现不好。

改成下面的刷表法就好了

for(int j=0; j<=sz[u]; j++)    tmp[j]=dp[u][j];for(int j=0; j<=sz[u]; j++)    for(int k=0; k<=sz[v]; k++)        dp[u][j+k]=max(dp[u][j+k],tmp[j]+dp[v][k]-e[i].w);

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e3+15)int n,m,dp[N][N];struct Edge{    int u,v,w,next;}e[N<<1];int pos=1,head[N],sz[N],val[N],tmp[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dfs(int u,int f){    sz[u]=1;    if(u>n-m) dp[u][1]=val[u];    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u);        //AC        for(int j=0; j<=sz[u]; j++)            tmp[j]=dp[u][j];        for(int j=0; j<=sz[u]; j++)            for(int k=0; k<=sz[v]; k++)                dp[u][j+k]=max(dp[u][j+k],tmp[j]+dp[v][k]-e[i].w);        sz[u]+=sz[v];        // TLE        // sz[u]+=sz[v];        // for(int j=sz[u]; j>=0; j--)        //     for(int k=1; k<=min(sz[v],j); k++)        //         dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-e[i].w);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n-m; i++)    {        int k;cin >> k;        for(int j=1,v,w; j<=k; j++)        {            cin >> v >> w;            addEdge(i,v,w);            addEdge(v,i,w);        }    }    for(int i=n-m+1; i<=n; i++)        cin >> val[i];    memset(dp,0xc0,sizeof(dp));    for(int i=1; i<=n; i++)        dp[i][0]=0;    dfs(1,1);    for(int i=m; i>=0; i--)        if(dp[1][i]>=0)return cout << i,0;    return 0;}
]]>
+ 洛谷P1273 有线电视网 题解

题目链接:P1273有线电视网

题意

某收费有线电视网计划转播一场重要的足球比赛。他们的转播网和用户终端构成一棵树状结构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点。

从转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总费用等于传输信号的费用总和。

现在每个用户都准备了一笔费用想观看这场精彩的足球比赛,有线电视网有权决定给哪些用户提供信号而不给哪些用户提供信号。

写一个程序找出一个方案使得有线电视网在不亏本的情况下使观看转播的用户尽可能多。

如果直接去想怎么保证不超过边权和什么的不太好搞

考虑把每个用户数是否能够承担都算出来即可

也就是

\(dp[i][u][j]\) 表示以 \(u\) 为根的子树,仅用前 \(i\) 个儿子,满足 \(j\) 个客户能获得的最大价值

事实上我们并不是非常关心是否能获得的最大价值,我们只要不亏本的情况下尽可能多让人看罢了(好良心有没有

则有转移方程 \[dp[i][u][j]=\max(dp[i][u][j],dp[i-1][u][j-k]+dp[\text{sz}[v]][v][k]-w(u,v))\] 可以发现这个东西可以滚动数组优化

然后就有 \[dp[u][j]=\max(dp[u][j],dp[u][j-k]+dp[v][k]-w(u,v))\]

sz[u]+=sz[v];for(int j=sz[u]; j>=0; j--)    for(int k=1; k<=min(sz[v],j); k++)        dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-e[i].w);

AC?不,这样会TLE。参考讨论 link

因为这样的复杂度并不能保证为严格 \(O(n^2)\) 的,这也就是所谓的实现不好。

改成下面的刷表法就好了 \[dp[u][j+k]=\max(dp[u][j+k],dp[u][j]+dp[v][k]-w(u,v))\]

for(int j=0; j<=sz[u]; j++)    tmp[j]=dp[u][j];for(int j=0; j<=sz[u]; j++)    for(int k=0; k<=sz[v]; k++)        dp[u][j+k]=max(dp[u][j+k],tmp[j]+dp[v][k]-e[i].w);

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e3+15)int n,m,dp[N][N];struct Edge{    int u,v,w,next;}e[N<<1];int pos=1,head[N],sz[N],val[N],tmp[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dfs(int u,int f){    sz[u]=1;    if(u>n-m) dp[u][1]=val[u];    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u);        //AC        for(int j=0; j<=sz[u]; j++)            tmp[j]=dp[u][j];        for(int j=0; j<=sz[u]; j++)            for(int k=0; k<=sz[v]; k++)                dp[u][j+k]=max(dp[u][j+k],tmp[j]+dp[v][k]-e[i].w);        sz[u]+=sz[v];        // TLE        // sz[u]+=sz[v];        // for(int j=sz[u]; j>=0; j--)        //     for(int k=1; k<=min(sz[v],j); k++)        //         dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]-e[i].w);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n-m; i++)    {        int k;cin >> k;        for(int j=1,v,w; j<=k; j++)        {            cin >> v >> w;            addEdge(i,v,w);            addEdge(v,i,w);        }    }    for(int i=n-m+1; i<=n; i++)        cin >> val[i];    memset(dp,0xc0,sizeof(dp));    for(int i=1; i<=n; i++)        dp[i][0]=0;    dfs(1,1);    for(int i=m; i>=0; i--)        if(dp[1][i]>=0)return cout << i,0;    return 0;}
]]>
@@ -4922,10 +4922,10 @@ 算法 - DP - 图论 + DP + 数据结构 @@ -4939,7 +4939,7 @@ /2022/05/30/luo-gu-p4099-heoi2013-sao-ti-jie/ - 洛谷P4099 [HEOI2013]SAO 题解

题目链接:P4099 [HEOI2013]SAO

题意

Welcome to SAO ( Strange and Abnormal Online)。这是一个 VR MMORPG, 含有 $n$ 个关卡。但是,挑战不同关卡的顺序是一个很大的问题。

某款游戏有 $n-1$ 个对于挑战关卡的限制,诸如第 $i$ 个关卡必须在第 $j$ 个关卡前挑战,或者完成了第 $k$ 个关卡才能挑战第 $l$ 个关卡。并且,如果不考虑限制的方向性,那么在这 $n-1$ 个限制的情况下,任何两个关卡都存在某种程度的关联性。即,我们不能把所有关卡分成两个非空且不相交的子集,使得这两个子集之间没有任何限制。

高质量好题👍 做了我一下午+晚上

首先这个题面的意思:给定一棵有方向的树,求拓扑序总数

注意到这个特殊的性质,可以从树形dp的角度去解决这个问题

设 $dp[u][i]$ 表示结点 $u$ 在其子树的拓扑序中位于第 $i$ 位的方案数

对于每个 $u$ 的儿子 $v$ ,将 $v$ 不断合并到 $u$ 上,则有两种情况

  • 新拓扑序中 $v$ 在 $u$ 前

    考虑枚举一个 $j$ 表示 $v$ 的子树中有 $j$ 个结点合并到了 $u$ 的前面

    则新的状态为 $dp[u][i+j]$

    • 合并后 $u$ 前面有 $i+j-1$ 个元素,

      其中 $j$ 个是 $v$ 的,所以 $i+j-1$ 个格子取 $j$ 个,则为 $\dbinom{i+j-1}{j}$

    • 同理, $u$ 后面有 $\text{sz}[u]+\text{sz}[v]-i-j$ 个元素,

      其中 $\text{sz}[v]-j$ 个是 $v$ 的,则为 $\dbinom{\text{sz}[u]+\text{sz}[v]-i-j}{\text{sz}[v]-j}$

    故转移方程为

    也就是

    for (int i=1;i<=sz[u];++i)    for (int k=1;j<=sz[v];++k)        for (int j=k;j<=sz[v];++j)            dp[u][i+j]+=dp[u][i]*dp[v][k]*C[i+j-1][i-1]*C[sz[u]+sz[v]-i-j][sz[u]-i]);

    考虑变换一下 $j,k$ 的枚举顺序

    for (int i=1;i<=sz[u];++i)    for (int j=1;j<=sz[v];++j)        for (int k=1;k<=j;++k)            dp[u][i+j]+=dp[u][i]*dp[v][k]*C[i+j-1][i-1]*C[sz[u]+sz[v]-i-j][sz[u]-i];

    然后就可以愉快的前缀和优化啦!

  • 新拓扑序中 $v$ 在 $u$ 后

    与上一种情况类似

    for (int i=1;i<=sz[u];++i)    for (int j=0;j<=sz[v];++j)        for (int k=i+1;k<=sz[v];++k)            dp[u][i+j]+=dp[u][i]*dp[v][k]*C[i+j-1][i-1]*C[sz[u]+sz[v]-i-j][sz[u]-i];

    也可以前缀和优化

然后组合数 $O(n^2)$ 预处理

注意由于 dp[u][i+j] 的更新,我们需要记录临时记录旧的 dp[u][i]

所以总的时间复杂度为 $O(n^2)$ (类似于树上背包的复杂度证明,此处略)

别忘了多组数据哦! qwq

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)const int p=1e9+7;struct Edge{    int u,v,w,next;}e[N<<1];int pos=1,head[N],C[N][N];int n,sz[N],dp[N][N],tmp[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void clear(){    pos=1;    for(int i=1; i<=n; i++)    {        head[i]=sz[i]=0;        for(int j=1; j<=n; j++)            dp[i][j]=0;    }}void dfs(int u,int f){    sz[u]=1;dp[u][1]=1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u);        for(int i=1; i<=sz[u]; i++)            tmp[i]=dp[u][i],dp[u][i]=0;        if(e[i].w)        {            for(int i=1; i<=sz[u]; i++)                for(int j=0; j<sz[v]; j++)                {                    dp[u][i+j]=(dp[u][i+j]+C[sz[u]+sz[v]-i-j][sz[v]-j]*                    C[i+j-1][j]%p*tmp[i]%p*(dp[v][sz[v]]-dp[v][j]+p)%p)%p;                }        }else        {            for(int i=1; i<=sz[u]; i++)                for(int j=1; j<=sz[v]; j++)                {                    dp[u][i+j]=(dp[u][i+j]+C[sz[u]+sz[v]-i-j][sz[v]-j]*                    C[i+j-1][j]%p*tmp[i]%p*dp[v][j]%p)%p;                }        }        sz[u]+=sz[v];    }    for (int i=1; i<=sz[u]; i++)        dp[u][i]=(dp[u][i]+dp[u][i-1])%p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    C[0][0]=1;    for(int i=1; i<=N-10; i++)    {        C[i][0]=1;        for(int j=1; j<=i; j++)            C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;    }    int Q;cin >> Q;    while(Q--)    {        clear();        cin >> n;        for(int i=1,u,v; i<n; i++)        {            char ch;cin >> u >> ch >> v;            ++u;++v;            addEdge(u,v,ch=='<');            addEdge(v,u,ch=='>');        }        dfs(1,1);        cout << dp[1][n] << endl;    }    return 0;}

参考文献

[1] https://www.luogu.com.cn/blog/i-am-zhiyangfan/solution-p4099

[2] https://www.luogu.com.cn/blog/ShadowassIIXVIIIIV/solution-p4099

[3] https://m-sea.blog.luogu.org/solution-p4099

]]>
+ 洛谷P4099 [HEOI2013]SAO 题解

题目链接:P4099[HEOI2013]SAO

题意

Welcome to SAO ( Strange and Abnormal Online)。这是一个 VR MMORPG,含有 \(n\)个关卡。但是,挑战不同关卡的顺序是一个很大的问题。

某款游戏有 \(n-1\)个对于挑战关卡的限制,诸如第 \(i\)个关卡必须在第 \(j\)个关卡前挑战,或者完成了第 \(k\)个关卡才能挑战第 \(l\)个关卡。并且,如果不考虑限制的方向性,那么在这 \(n-1\)个限制的情况下,任何两个关卡都存在某种程度的关联性。即,我们不能把所有关卡分成两个非空且不相交的子集,使得这两个子集之间没有任何限制。

高质量好题👍 做了我一下午+晚上

首先这个题面的意思:给定一棵有方向的树,求拓扑序总数

注意到这个特殊的性质,可以从树形dp的角度去解决这个问题

\(dp[u][i]\) 表示结点 \(u\) 在其子树的拓扑序中位于第 \(i\) 位的方案数

对于每个 \(u\) 的儿子 \(v\) ,将 \(v\) 不断合并到 \(u\) 上,则有两种情况

  • 新拓扑序中 \(v\)\(u\) 前

    考虑枚举一个 \(j\) 表示 \(v\) 的子树中有 \(j\) 个结点合并到了 \(u\) 的前面

    则新的状态为 \(dp[u][i+j]\)

    • 合并后 \(u\) 前面有 \(i+j-1\) 个元素,

      其中 \(j\) 个是 \(v\) 的,所以 \(i+j-1\) 个格子取 \(j\) 个,则为 \(\dbinom{i+j-1}{j}\)

    • 同理, \(u\) 后面有 \(\text{sz}[u]+\text{sz}[v]-i-j\)个元素,

      其中 \(\text{sz}[v]-j\) 个是 \(v\) 的,则为 \(\dbinom{\text{sz}[u]+\text{sz}[v]-i-j}{\text{sz}[v]-j}\)

    故转移方程为 \[dp[u][i+j]=dp[u][i+j]+\dbinom{i+j-1}{j}\times\dbinom{\text{sz}[u]+\text{sz}[v]-i-j}{\text{sz}[v]-j} \timesdp[u][i]\times dp[v][k]\] 也就是

    for (int i=1;i<=sz[u];++i)    for (int k=1;j<=sz[v];++k)        for (int j=k;j<=sz[v];++j)            dp[u][i+j]+=dp[u][i]*dp[v][k]*C[i+j-1][i-1]*C[sz[u]+sz[v]-i-j][sz[u]-i]);

    考虑变换一下 \(j,k\) 的枚举顺序

    for (int i=1;i<=sz[u];++i)    for (int j=1;j<=sz[v];++j)        for (int k=1;k<=j;++k)            dp[u][i+j]+=dp[u][i]*dp[v][k]*C[i+j-1][i-1]*C[sz[u]+sz[v]-i-j][sz[u]-i];

    然后就可以愉快的前缀和优化啦!

  • 新拓扑序中 \(v\)\(u\) 后

    与上一种情况类似

    for (int i=1;i<=sz[u];++i)    for (int j=0;j<=sz[v];++j)        for (int k=i+1;k<=sz[v];++k)            dp[u][i+j]+=dp[u][i]*dp[v][k]*C[i+j-1][i-1]*C[sz[u]+sz[v]-i-j][sz[u]-i];

    也可以前缀和优化

然后组合数 \(O(n^2)\) 预处理

注意由于 dp[u][i+j] 的更新,我们需要记录临时记录旧的dp[u][i]

所以总的时间复杂度为 \(O(n^2)\)(类似于树上背包的复杂度证明,此处略)

别忘了多组数据哦! qwq

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)const int p=1e9+7;struct Edge{    int u,v,w,next;}e[N<<1];int pos=1,head[N],C[N][N];int n,sz[N],dp[N][N],tmp[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void clear(){    pos=1;    for(int i=1; i<=n; i++)    {        head[i]=sz[i]=0;        for(int j=1; j<=n; j++)            dp[i][j]=0;    }}void dfs(int u,int f){    sz[u]=1;dp[u][1]=1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u);        for(int i=1; i<=sz[u]; i++)            tmp[i]=dp[u][i],dp[u][i]=0;        if(e[i].w)        {            for(int i=1; i<=sz[u]; i++)                for(int j=0; j<sz[v]; j++)                {                    dp[u][i+j]=(dp[u][i+j]+C[sz[u]+sz[v]-i-j][sz[v]-j]*                    C[i+j-1][j]%p*tmp[i]%p*(dp[v][sz[v]]-dp[v][j]+p)%p)%p;                }        }else        {            for(int i=1; i<=sz[u]; i++)                for(int j=1; j<=sz[v]; j++)                {                    dp[u][i+j]=(dp[u][i+j]+C[sz[u]+sz[v]-i-j][sz[v]-j]*                    C[i+j-1][j]%p*tmp[i]%p*dp[v][j]%p)%p;                }        }        sz[u]+=sz[v];    }    for (int i=1; i<=sz[u]; i++)        dp[u][i]=(dp[u][i]+dp[u][i-1])%p;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    C[0][0]=1;    for(int i=1; i<=N-10; i++)    {        C[i][0]=1;        for(int j=1; j<=i; j++)            C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;    }    int Q;cin >> Q;    while(Q--)    {        clear();        cin >> n;        for(int i=1,u,v; i<n; i++)        {            char ch;cin >> u >> ch >> v;            ++u;++v;            addEdge(u,v,ch=='<');            addEdge(v,u,ch=='>');        }        dfs(1,1);        cout << dp[1][n] << endl;    }    return 0;}

参考文献

[1] https://www.luogu.com.cn/blog/i-am-zhiyangfan/solution-p4099

[2] https://www.luogu.com.cn/blog/ShadowassIIXVIIIIV/solution-p4099

[3] https://m-sea.blog.luogu.org/solution-p4099

]]>
@@ -4968,7 +4968,7 @@ /2022/05/30/luo-gu-p2279-hnoi2003-xiao-fang-ju-de-she-li-ti-jie/ - 洛谷P2279 [HNOI2003]消防局的设立 题解

题目链接:P2279 [HNOI2003]消防局的设立

题意

2020 年,人类在火星上建立了一个庞大的基地群,总共有 $n$ 个基地。起初为了节约材料,人类只修建了 $n-1$ 条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地 $A$ 到基地 $B$ 至少要经过 $d$ 条道路的话,我们称基地A到基地B的距离为 $d$。

由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过 $2$ 的基地的火灾。

你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾。

一开始以为是树形dp(确实是),结果发现贪心可做。

树形dp的状态十分恶心,贪心相对简单

注意到距离不大于 $2$ 的除了爷爷、父亲、儿子、孙子结点,还有它的兄弟结点

对于一个结点,如果我们在它的爷爷放了一个消防站,则可以将其父亲和兄弟全部覆盖

则我们可以每次选出深度最大的结点,把它爷爷放个消防站,然后暴力修改一下

时间复杂度 $O(n^2)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,dep[N],fa[N],vis[N];vector<int> vec[N];struct node{int u;};bool operator<(node a,node b){return dep[a.u]<dep[b.u];}priority_queue<node> q;void dfs1(int u,int f,int d){    dep[u]=d;fa[u]=f;    for(int v : vec[u])    {        if(v==f)continue;        dfs1(v,u,d+1);    }}void dfs2(int u,int d){    if(d>2)return;    vis[u]=1;    for(int v : vec[u])        dfs2(v,d+1);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,v; i<n; i++)    {        cin >> v;        vec[i+1].push_back(v);        vec[v].push_back(i+1);    }    dfs1(1,0,1);    for(int i=1; i<=n; i++)        q.push({i});    int u,res=0;    while(!q.empty())    {        while(!q.empty()&&vis[u=q.top().u])q.pop();        if(q.empty())break;        if(fa[fa[u]])            dfs2(fa[fa[u]],0);        else dfs2(1,0);        ++res;    }    cout << res << endl;    return 0;}
]]>
+ 洛谷P2279[HNOI2003]消防局的设立 题解

题目链接:P2279[HNOI2003]消防局的设立

题意

2020 年,人类在火星上建立了一个庞大的基地群,总共有 \(n\) 个基地。起初为了节约材料,人类只修建了\(n-1\)条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状结构。如果基地\(A\) 到基地 \(B\) 至少要经过 \(d\) 条道路的话,我们称基地A到基地B的距离为\(d\)

由于火星上非常干燥,经常引发火灾,人类决定在火星上修建若干个消防局。消防局只能修建在基地里,每个消防局有能力扑灭与它距离不超过\(2\) 的基地的火灾。

你的任务是计算至少要修建多少个消防局才能够确保火星上所有的基地在发生火灾时,消防队有能力及时扑灭火灾。

一开始以为是树形dp(确实是),结果发现贪心可做。

树形dp的状态十分恶心,贪心相对简单

注意到距离不大于 \(2\)的除了爷爷、父亲、儿子、孙子结点,还有它的兄弟结点

对于一个结点,如果我们在它的爷爷放了一个消防站,则可以将其父亲和兄弟全部覆盖

则我们可以每次选出深度最大的结点,把它爷爷放个消防站,然后暴力修改一下

时间复杂度 \(O(n^2)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <queue>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,dep[N],fa[N],vis[N];vector<int> vec[N];struct node{int u;};bool operator<(node a,node b){return dep[a.u]<dep[b.u];}priority_queue<node> q;void dfs1(int u,int f,int d){    dep[u]=d;fa[u]=f;    for(int v : vec[u])    {        if(v==f)continue;        dfs1(v,u,d+1);    }}void dfs2(int u,int d){    if(d>2)return;    vis[u]=1;    for(int v : vec[u])        dfs2(v,d+1);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,v; i<n; i++)    {        cin >> v;        vec[i+1].push_back(v);        vec[v].push_back(i+1);    }    dfs1(1,0,1);    for(int i=1; i<=n; i++)        q.push({i});    int u,res=0;    while(!q.empty())    {        while(!q.empty()&&vis[u=q.top().u])q.pop();        if(q.empty())break;        if(fa[fa[u]])            dfs2(fa[fa[u]],0);        else dfs2(1,0);        ++res;    }    cout << res << endl;    return 0;}
]]>
@@ -4982,10 +4982,10 @@ 算法 - DP - 图论 + DP + @@ -4997,7 +4997,7 @@ /2022/05/30/luo-gu-p3177-haoi2015-shu-shang-ran-se-ti-jie/ - 洛谷P3177 [HAOI2015] 树上染色 题解

题目链接:P3177 [HAOI2015] 树上染色

题意

有一棵点数为 $n$ 的树,树边有边权。给你一个在 $0 \sim n$ 之内的正整数 $m$ ,你要在这棵树中选择 $m$ 个点,将其染成黑色,并将其他 的 $n-m$ 个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。

upd.20220531 由于本人对树上背包掌握不熟,导致复杂度写假了

其实是题解区普遍写错罢了。

不过原文只是没用刷表法

于是在原文基础上做了修改,不影响观感,请放心食用(逃


显然树上背包

设 $dp[u][j]$ 表示 $u$ 所在子树染了 $j$ 个黑点的最大价值

容易推出一个大概的方程

注:下文会提到这个转移方程是有点问题的

黑点两两距离+白点两两距离

直接去算就是 $O(n^2)$ 的了

考虑更好的计算方法

一条路径会包括若干条边

因为是树所以有很多的边会被重复走过

考虑将距离计算转化为边重复经过次数

根据乘法原理,可知边 $(u,v)$ ( $u$ 为父结点)的贡献为

upd.20220531 然后填表法写出来就是这样的(原文代码)

for(int i=head[u]; i; i=e[i].next){    int v=e[i].v;    if(v==f)continue;    dfs(v,u);    sz[u]+=sz[v];    for(int j=min(sz[u],m); j>=0; j--)    {        if(dp[u][j]>=0) // k正着枚举的时候这个就不用了            dp[u][j]+=dp[v][0]+sz[v]*(n-m-sz[v])*e[i].w;        for(int k=min(sz[v],j); k>0; k--) // 这里正着枚举也可以        {            if(dp[u][j-k]<0)continue;            int val=(k*(m-k)+(sz[v]-k)*(n-m-sz[v]+k))*e[i].w;            dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]+val);        }    }}

不难发现这个其实复杂度是可以被卡到 $O(nm^2)$ 的

所以考虑刷表法,即

这里的 $\text{tmp}[j]$ 是上一轮的 $dp[u][j]$ ,刷表的过程中会被刷坏,所以要先存一下

这样时间复杂度才是严格的 $O(nm)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;struct Edge{    int u,v,w,next;}e[N<<1];int pos=1,head[N],sz[N],tmp[N],dp[N][N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dfs(int u,int f){    sz[u]=1;    dp[u][0]=dp[u][1]=0;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u);        for(int j=0; j<=min(sz[u],m); j++)            tmp[j]=dp[u][j];        for(int j=0; j<=min(sz[u],m); j++)            for(int k=0; k<=sz[v]&&j+k<=m&&tmp[j]>=0; k++)            {                int val=(k*(m-k)+(sz[v]-k)*(n-m-sz[v]+k))*e[i].w;                dp[u][j+k]=max(dp[u][j+k],tmp[j]+dp[v][k]+val);            }        sz[u]+=sz[v];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v,w; i<n; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);addEdge(v,u,w);    }    memset(dp,0xc0,sizeof(dp));    dfs(1,1);    cout << dp[1][m] << endl;    return 0;}

这一段可以跳过,只是详细解释了link的东西,

而且ta的代码复杂度也是假的,因为都是填表法

注意到有些人 $k$ 倒序枚举,要先转移 $k=0$ 的情况,这里解释一下

关于为什么要先将 $k=0$ 的转移

观察方程,因为如果直接倒序枚举,最后一次 $k=0$ 的枚举

会出现 $dp[u][j]=\max(dp[u][j],dp[u][j]+\text{val})$ 的情况

显然此时的 $dp[u][j]$ 已经被更新过了

因此会导致答案有误(偏大)

除了 $k=0$ 的特殊情况,其他时候 $k$ 随便啥顺序枚举都是可以的

]]>
+ 洛谷P3177 [HAOI2015] 树上染色题解

题目链接:P3177[HAOI2015] 树上染色

题意

有一棵点数为 \(n\)的树,树边有边权。给你一个在 \(0 \simn\) 之内的正整数 \(m\),你要在这棵树中选择 \(m\)个点,将其染成黑色,并将其他 的 \(n-m\)个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。

upd.20220531由于本人对树上背包掌握不熟,导致复杂度写假了

其实是题解区普遍写错罢了。

不过原文只是没用刷表法

于是在原文基础上做了修改,不影响观感,请放心食用(逃


显然树上背包

\(dp[u][j]\) 表示 \(u\) 所在子树染了 \(j\) 个黑点的最大价值

容易推出一个大概的方程 \[dp[u][j]=\max(dp[u][j],dp[u][j-k]+dp[v][k]+\text{val})\] 注:下文会提到这个转移方程是有点问题的

黑点两两距离+白点两两距离

直接去算就是 \(O(n^2)\) 的了

考虑更好的计算方法

一条路径会包括若干条边

因为是树所以有很多的边会被重复走过

考虑将距离计算转化为边重复经过次数

根据乘法原理,可知边 \((u,v)\)\(u\) 为父结点)的贡献为 \[\text{val}=w(u,v)\times(k(m-k)+(\text{sz}[v]-k)(n-m-\text{sz}[v]+k))\] upd.20220531然后填表法写出来就是这样的(原文代码)

for(int i=head[u]; i; i=e[i].next){    int v=e[i].v;    if(v==f)continue;    dfs(v,u);    sz[u]+=sz[v];    for(int j=min(sz[u],m); j>=0; j--)    {        if(dp[u][j]>=0) // k正着枚举的时候这个就不用了            dp[u][j]+=dp[v][0]+sz[v]*(n-m-sz[v])*e[i].w;        for(int k=min(sz[v],j); k>0; k--) // 这里正着枚举也可以        {            if(dp[u][j-k]<0)continue;            int val=(k*(m-k)+(sz[v]-k)*(n-m-sz[v]+k))*e[i].w;            dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]+val);        }    }}

不难发现这个其实复杂度是可以被卡到 \(O(nm^2)\) 的

所以考虑刷表法,即 \[dp[u][j+k]=\max(dp[u][j+k],\text{tmp}[j]+dp[v][k]+\text{val})\] 这里的 \(\text{tmp}[j]\)是上一轮的 \(dp[u][j]\),刷表的过程中会被刷坏,所以要先存一下

这样时间复杂度才是严格的 \(O(nm)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <iomanip>#include <random>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;struct Edge{    int u,v,w,next;}e[N<<1];int pos=1,head[N],sz[N],tmp[N],dp[N][N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dfs(int u,int f){    sz[u]=1;    dp[u][0]=dp[u][1]=0;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u);        for(int j=0; j<=min(sz[u],m); j++)            tmp[j]=dp[u][j];        for(int j=0; j<=min(sz[u],m); j++)            for(int k=0; k<=sz[v]&&j+k<=m&&tmp[j]>=0; k++)            {                int val=(k*(m-k)+(sz[v]-k)*(n-m-sz[v]+k))*e[i].w;                dp[u][j+k]=max(dp[u][j+k],tmp[j]+dp[v][k]+val);            }        sz[u]+=sz[v];    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v,w; i<n; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);addEdge(v,u,w);    }    memset(dp,0xc0,sizeof(dp));    dfs(1,1);    cout << dp[1][m] << endl;    return 0;}

这一段可以跳过,只是详细解释了link的东西,

而且ta的代码复杂度也是假的,因为都是填表法

注意到有些人 \(k\)倒序枚举,要先转移 \(k=0\)的情况,这里解释一下

关于为什么要先将 \(k=0\) 的转移

观察方程,因为如果直接倒序枚举,最后一次 \(k=0\) 的枚举

会出现 \(dp[u][j]=\max(dp[u][j],dp[u][j]+\text{val})\)的情况

显然此时的 \(dp[u][j]\)已经被更新过了

因此会导致答案有误(偏大)

除了 \(k=0\) 的特殊情况,其他时候\(k\) 随便啥顺序枚举都是可以的

]]>
@@ -5011,10 +5011,10 @@ 算法 - DP - 图论 + DP + 数据结构 @@ -5028,7 +5028,7 @@ /2022/05/30/luo-gu-p2015-er-cha-ping-guo-shu-ti-jie/ - 洛谷P2015 二叉苹果树 题解

题目链接:P2015 二叉苹果树

题意

有一棵苹果树,如果树枝有分叉,一定是分二叉(就是说没有只有一个儿子的结点)

这棵树共有 $N$ 个结点(叶子点或者树枝分叉点),编号为 $1 \sim N$,树根编号一定是 $1$。

我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有 $4$ 个树枝的树:

2   5 \ /   3   4   \ /    1

现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。

给定需要保留的树枝数量 $m$ ,求出最多能留住多少苹果。

经典的树形依赖背包(树上背包)

设 $dp[u][j]$ 表示 $u$ 结点所在子树保留 $j$ 条边所能得到的最大苹果树

则有

为什么是 $j-k-1$ 呢

因为 $k$ 是 $v$ 所在子树的分配边数,但是你要花一条边去连上 $v$

不然你给了 $v$ 一共 $k$ 条边最后没连上它啥也没有

但是填表法会被卡到 $O(nm^2)$ ,所以考虑刷表法

时间复杂度 $O(nm)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)struct Edge{    int u,v,w,next;}e[N<<1];int pos=1,head[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}int n,m,sz[N],tmp[N],dp[N][N];void dfs(int u,int f){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u);        for(int j=0; j<=min(sz[u],m); j++)            tmp[j]=dp[u][j];        for(int j=0; j<=min(sz[u],m); j++)            for(int k=0; k<=min(sz[v],m)&&j+k+1<=m; k++)                dp[u][j+k+1]=max(dp[u][j+k+1],tmp[j]+dp[v][k]+e[i].w);        sz[u]+=sz[v]+1;        // sz[u]+=sz[v]+1;        // for(int j=min(sz[u],m); j>=1; j--)        //     // for(int k=min(sz[v],j-1); k>=0; k--)        //     for(int k=0; k<=min(sz[v],j-1); k++)        //         dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[v][k]+e[i].w);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v,w; i<n; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);addEdge(v,u,w);    }    dfs(1,1);    cout << dp[1][m] << endl;    return 0;}
]]>
+ 洛谷P2015 二叉苹果树 题解

题目链接:P2015二叉苹果树

题意

有一棵苹果树,如果树枝有分叉,一定是分二叉(就是说没有只有一个儿子的结点)

这棵树共有 \(N\)个结点(叶子点或者树枝分叉点),编号为 \(1\sim N\),树根编号一定是 \(1\)。

我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有\(4\) 个树枝的树:

2   5 \ /   3   4   \ /    1

现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。

给定需要保留的树枝数量 \(m\),求出最多能留住多少苹果。

经典的树形依赖背包(树上背包)

\(dp[u][j]\) 表示 \(u\) 结点所在子树保留 \(j\) 条边所能得到的最大苹果树

则有 \[dp[u][j]=\max(dp[u][j],dp[u][j-k-1]+dp[v][k]+w(u,v))\] 为什么是 \(j-k-1\)

因为 \(k\)\(v\)所在子树的分配边数,但是你要花一条边去连上 \(v\)

不然你给了 \(v\) 一共 \(k\) 条边最后没连上它啥也没有

但是填表法会被卡到 \(O(nm^2)\),所以考虑刷表法

时间复杂度 \(O(nm)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)struct Edge{    int u,v,w,next;}e[N<<1];int pos=1,head[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}int n,m,sz[N],tmp[N],dp[N][N];void dfs(int u,int f){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u);        for(int j=0; j<=min(sz[u],m); j++)            tmp[j]=dp[u][j];        for(int j=0; j<=min(sz[u],m); j++)            for(int k=0; k<=min(sz[v],m)&&j+k+1<=m; k++)                dp[u][j+k+1]=max(dp[u][j+k+1],tmp[j]+dp[v][k]+e[i].w);        sz[u]+=sz[v]+1;        // sz[u]+=sz[v]+1;        // for(int j=min(sz[u],m); j>=1; j--)        //     // for(int k=min(sz[v],j-1); k>=0; k--)        //     for(int k=0; k<=min(sz[v],j-1); k++)        //         dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[v][k]+e[i].w);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v,w; i<n; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);addEdge(v,u,w);    }    dfs(1,1);    cout << dp[1][m] << endl;    return 0;}
]]>
@@ -5042,10 +5042,10 @@ 算法 - DP - 图论 + DP + 数据结构 @@ -5059,7 +5059,7 @@ /2022/05/30/luo-gu-p3174-haoi2009-mao-mao-chong-ti-jie/ - 洛谷P3174 [HAOI2009] 毛毛虫 题解

题目链接:P3174 [HAOI2009] 毛毛虫

题意

对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大。例如下图左边的树(图 $1$)抽出一部分就变成了右边的一个毛毛虫了(图 $2$)。

这道题是可以树形dp的,具体看这里 link,不是本文重点。

设 $f[u]$ 为 $u$ 结点所在子树的最大毛毛虫🐛大小

则有

然后还要维护个次大值啥的,挺麻烦的


其实这道题就是树的直径。两遍dfs就能搞定的问题

为什么树的直径对呢

考虑两条路径,长度相等,则答案一定相等,这个很显然

然后么找个最长的,也就是直径,统计一下答案就好了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e5+15)int n,m,x,y,sz[N],res;vector<int> vec[N];void dfs(int u,int f,int sum){    if(res<sum)res=sum,x=u;    for(int v : vec[u])    {        if(v==f)continue;        dfs(v,u,sum+sz[v]-1);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);        ++sz[u];++sz[v];    }    dfs(1,1,sz[1]);    dfs(x,x,sz[x]);    cout << res+1 << endl;    return 0;}
]]>
+ 洛谷P3174 [HAOI2009] 毛毛虫题解

题目链接:P3174[HAOI2009] 毛毛虫

题意

对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大。例如下图左边的树(图\(1\))抽出一部分就变成了右边的一个毛毛虫了(图\(2\))。

这道题是可以树形dp的,具体看这里 link,不是本文重点。

\(f[u]\)\(u\) 结点所在子树的最大毛毛虫🐛大小

则有 \[f[u]=\max(f[v])+1+\max(\text{cnt}[u]-1,0)\] 然后还要维护个次大值啥的,挺麻烦的


其实这道题就是树的直径。两遍dfs就能搞定的问题

为什么树的直径对呢

考虑两条路径,长度相等,则答案一定相等,这个很显然

然后么找个最长的,也就是直径,统计一下答案就好了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e5+15)int n,m,x,y,sz[N],res;vector<int> vec[N];void dfs(int u,int f,int sum){    if(res<sum)res=sum,x=u;    for(int v : vec[u])    {        if(v==f)continue;        dfs(v,u,sum+sz[v]-1);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,u,v; i<=m; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);        ++sz[u];++sz[v];    }    dfs(1,1,sz[1]);    dfs(x,x,sz[x]);    cout << res+1 << endl;    return 0;}
]]>
@@ -5073,10 +5073,10 @@ 算法 - DP - 图论 + DP + 数据结构 @@ -5090,7 +5090,7 @@ /2022/05/30/luo-gu-p3621-apio2007-feng-ling-ti-jie/ - 洛谷P3621 [APIO2007] 风铃 题解

题目链接:P3621 [APIO2007] 风铃

题意

你准备给弟弟 Ike 买一件礼物,但是,Ike 挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西。

你准备给 Ike 买一个风铃。风铃是一种多层的装饰品,一般挂在天花板上。

每个风铃都包含一些由竖直线连起来的水平杆。每根杆的两头都有线连接,下面或者挂着另一根水平杆,或者挂着一个玩具。下面是一个风铃的例子:

为了满足弟弟,你需要选一个满足下面两个条件的风铃:

  1. 所有的玩具都在同一层(也就是说,每个玩具到天花板之间的杆的个数是一样的)或至多相差一层。

  2. 对于两个相差一层的玩具,左边的玩具比右边的玩具要更靠下一点。

风铃可以按照下面的规则重新排列:任选一根杆,将杆两头的线“交换”。也就是解开一根杆左右两头的线,然后将它们绑到杆的另一头。这个操作不会改变更下面的杆上线的排列顺序。

正在训练信息学奥林匹克的你,决定设计一个算法,判断能否通过重新排列,将一个给定的风铃变为 Ike 喜欢的样子。

考虑上面的例子,上图中的风铃满足条件 $1$,却不满足条件 $2$ ——最左边的那个玩具比它右边的要高。

但是,我们可以通过下面的步骤把这个风铃变成一个 Ike 喜欢的:

  1. 第一步,将杆 $1$ 的左右两边交换,这使得杆 $2$ 和杆 $3$ 的位置互换,交换的结果如下图所示:

  1. 第二步,也是最后一步,将杆 $2$ 的左右两边交换,这使得杆 $4$ 到了左边,原来在左边的玩具到了右边,交换的结果发下图所示:

现在的这个风铃就满足 Ike 的条件了。

你的任务是:给定一个风铃的描述,求出最少需要多少次交换才能使这风铃满足 Ike 的条件(如果可能)。

感觉不算正经树形dp,但是包含树形dp的思想

题目其实就是在问通过最少次交换某些结点的左右子树使原树变成完全二叉树

分类讨论

  • 如果最深的深度和最浅的深度相差超过 $1$ ,无解。
  • 如果是满二叉树,答案为 $0$ 。

上面这两种用一遍dfs就可以搞定

  • 对于一个结点,其左子树只有浅,右子树存在深,则需要交换一次。
  • 对于一个结点,其左子树存在深浅,右子树只有深,则需要交换一次。
  • 对于一个结点,其左右子树均存在深浅,无解。

上面这3种用第二遍dfs搞定

可以用 $0/1/2$ 分别表示:全浅、全深、均有

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long longconst int INF=0x3f3f3f3f3f3f3f3f;const int N=1e5+15;int n,son[N][2],mn=INF,mx,ans;void dfs1(int u,int d){    if(u==-1)    {        mn=min(d,mn);        mx=max(d,mx);        return ;    }    dfs1(son[u][0],d+1);    dfs1(son[u][1],d+1);}int dfs2(int u,int d){    if(u==-1)return (d!=mn);    int x=dfs2(son[u][0],d+1);    int y=dfs2(son[u][1],d+1);    ans+=((!x&&y)||(x==2&&y==1));    if(x==2&&y==2)cout << "-1",exit(0);    if(x==2||y==2||x+y==1)return 2;    if(!(x+y))return 0;    return 1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> son[i][0] >> son[i][1];    dfs1(1,0);    if(mx-mn>1)cout << "-1";    else        if(mx==mn)cout << "0";    else    {        dfs2(1,0);        cout << ans << endl;    }    return 0;}

参考文献

[1] https://www.luogu.com.cn/blog/ak-ioi/solution-p3621

]]>
+ 洛谷P3621 [APIO2007] 风铃题解

题目链接:P3621[APIO2007] 风铃

题意

你准备给弟弟 Ike 买一件礼物,但是,Ike挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西。

你准备给 Ike买一个风铃。风铃是一种多层的装饰品,一般挂在天花板上。

每个风铃都包含一些由竖直线连起来的水平杆。每根杆的两头都有线连接,下面或者挂着另一根水平杆,或者挂着一个玩具。下面是一个风铃的例子:

为了满足弟弟,你需要选一个满足下面两个条件的风铃:

  1. 所有的玩具都在同一层(也就是说,每个玩具到天花板之间的杆的个数是一样的)或至多相差一层。

  2. 对于两个相差一层的玩具,左边的玩具比右边的玩具要更靠下一点。

风铃可以按照下面的规则重新排列:任选一根杆,将杆两头的线“交换”。也就是解开一根杆左右两头的线,然后将它们绑到杆的另一头。这个操作不会改变更下面的杆上线的排列顺序。

正在训练信息学奥林匹克的你,决定设计一个算法,判断能否通过重新排列,将一个给定的风铃变为Ike 喜欢的样子。

考虑上面的例子,上图中的风铃满足条件 \(1\),却不满足条件 \(2\) ——最左边的那个玩具比它右边的要高。

但是,我们可以通过下面的步骤把这个风铃变成一个 Ike 喜欢的:

  1. 第一步,将杆 \(1\)的左右两边交换,这使得杆 \(2\) 和杆\(3\)的位置互换,交换的结果如下图所示:

  1. 第二步,也是最后一步,将杆 \(2\)的左右两边交换,这使得杆 \(4\)到了左边,原来在左边的玩具到了右边,交换的结果发下图所示:

现在的这个风铃就满足 Ike 的条件了。

你的任务是:给定一个风铃的描述,求出最少需要多少次交换才能使这风铃满足Ike 的条件(如果可能)。

感觉不算正经树形dp,但是包含树形dp的思想

题目其实就是在问通过最少次交换某些结点的左右子树使原树变成完全二叉树

分类讨论

  • 如果最深的深度和最浅的深度相差超过 \(1\) ,无解。
  • 如果是满二叉树,答案为 \(0\)

上面这两种用一遍dfs就可以搞定

  • 对于一个结点,其左子树只有浅,右子树存在深,则需要交换一次。
  • 对于一个结点,其左子树存在深浅,右子树只有深,则需要交换一次。
  • 对于一个结点,其左右子树均存在深浅,无解。

上面这3种用第二遍dfs搞定

可以用 \(0/1/2\)分别表示:全浅、全深、均有

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long longconst int INF=0x3f3f3f3f3f3f3f3f;const int N=1e5+15;int n,son[N][2],mn=INF,mx,ans;void dfs1(int u,int d){    if(u==-1)    {        mn=min(d,mn);        mx=max(d,mx);        return ;    }    dfs1(son[u][0],d+1);    dfs1(son[u][1],d+1);}int dfs2(int u,int d){    if(u==-1)return (d!=mn);    int x=dfs2(son[u][0],d+1);    int y=dfs2(son[u][1],d+1);    ans+=((!x&&y)||(x==2&&y==1));    if(x==2&&y==2)cout << "-1",exit(0);    if(x==2||y==2||x+y==1)return 2;    if(!(x+y))return 0;    return 1;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> son[i][0] >> son[i][1];    dfs1(1,0);    if(mx-mn>1)cout << "-1";    else        if(mx==mn)cout << "0";    else    {        dfs2(1,0);        cout << ans << endl;    }    return 0;}

参考文献

[1] https://www.luogu.com.cn/blog/ak-ioi/solution-p3621

]]>
@@ -5119,7 +5119,7 @@ /2022/05/28/luo-gu-p2458-sdoi2006-bao-an-zhan-gang-ti-jie/ - 洛谷P2458 [SDOI2006]保安站岗 题解

题目链接:P2458 [SDOI2006]保安站岗

题意

五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序。

已知整个地下超市的所有通道呈一棵树的形状;某些通道之间可以互相望见。总经理要求所有通道的每个端点(树的顶点)都要有人全天候看守,在不同的通道端点安排保安所需的费用不同。

一个保安一旦站在某个通道的其中一个端点,那么他除了能看守住他所站的那个端点,也能看到这个通道的另一个端点,所以一个保安可能同时能看守住多个端点(树的结点),因此没有必要在每个通道的端点都安排保安。

请你帮助超市经理策划安排,在能看守全部通道端点的前提下,使得花费的经费最少。

注意到这道题目可能会有相邻两个都不放保安的情况

首先想到记录一个结点是否放,以及其儿子、儿子的儿子是否放

但是这样比较麻烦

考虑设 $dp[u][0/1/2]$ 表示 $u$ 结点:

  • $0$ 表示被自己覆盖(自己放保安)
  • $1$ 表示被儿子覆盖(某个(或某些)儿子放保安)
  • $2$ 表示被父亲覆盖(父亲放保安)

容易推出 $0,2$ 的转移方程

而 $1$ 相对较麻烦

首先有从子结点的转移

但是如果所有的儿子都没有在他们自己那放保安,

则还要加上在在某个儿子放保安的最小花费

为什么要减呢?因为在某个 $v$ 放了保安后,就不用 $dp[v][1]$ 的花费了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <vector>using namespace std;#define int long long#define INF 0x3f3f3f3f#define N (int)(2e3+15)int n,val[N];int dp[N][3];vector<int> vec[N];void addEdge(int u,int v){    vec[u].push_back(v);    vec[v].push_back(u);}void dfs(int u,int f){    dp[u][0]=val[u];    int ck=1,mn=INF;    for(int v : vec[u])    {        if(v==f)continue;        dfs(v,u);        dp[u][0]+=min(dp[v][0],min(dp[v][1],dp[v][2]));        dp[u][1]+=min(dp[v][0],dp[v][1]);        dp[u][2]+=min(dp[v][0],dp[v][1]);        if(dp[v][0]<dp[v][1])ck=0;        else mn=min(mn,dp[v][0]-dp[v][1]);    }    if(ck)dp[u][1]+=mn;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,m,v; i<=n; i++)    {        cin >> u >> val[u] >> m;        for(int j=1; j<=m; j++)            cin >> v,addEdge(u,v);    }    dfs(1,1);    cout << min(dp[1][0],dp[1][1]) << endl;    return 0;}
]]>
+ 洛谷P2458 [SDOI2006]保安站岗题解

题目链接:P2458[SDOI2006]保安站岗

题意

五一来临,某地下超市为了便于疏通和指挥密集的人员和车辆,以免造成超市内的混乱和拥挤,准备临时从外单位调用部分保安来维持交通秩序。

已知整个地下超市的所有通道呈一棵树的形状;某些通道之间可以互相望见。总经理要求所有通道的每个端点(树的顶点)都要有人全天候看守,在不同的通道端点安排保安所需的费用不同。

一个保安一旦站在某个通道的其中一个端点,那么他除了能看守住他所站的那个端点,也能看到这个通道的另一个端点,所以一个保安可能同时能看守住多个端点(树的结点),因此没有必要在每个通道的端点都安排保安。

请你帮助超市经理策划安排,在能看守全部通道端点的前提下,使得花费的经费最少。

注意到这道题目可能会有相邻两个都不放保安的情况

首先想到记录一个结点是否放,以及其儿子、儿子的儿子是否放

但是这样比较麻烦

考虑设 \(dp[u][0/1/2]\) 表示 \(u\) 结点:

  • \(0\)表示被自己覆盖(自己放保安)
  • \(1\)表示被儿子覆盖(某个(或某些)儿子放保安)
  • \(2\)表示被父亲覆盖(父亲放保安)

容易推出 \(0,2\) 的转移方程 \[dp[u][0]=\sum \min(dp[v][0/1/2])\\dp[u][2]=\sum \min(dp[v][0/1])\] 而 \(1\) 相对较麻烦

首先有从子结点的转移 \[dp[u][2]=\sum \min(dp[v][0/1])\] 但是如果所有的儿子都没有在他们自己那放保安,

则还要加上在在某个儿子放保安的最小花费

\[\min(dp[v][0]-dp[v][1])\] 为什么要减呢?因为在某个 \(v\) 放了保安后,就不用 \(dp[v][1]\) 的花费了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <vector>using namespace std;#define int long long#define INF 0x3f3f3f3f#define N (int)(2e3+15)int n,val[N];int dp[N][3];vector<int> vec[N];void addEdge(int u,int v){    vec[u].push_back(v);    vec[v].push_back(u);}void dfs(int u,int f){    dp[u][0]=val[u];    int ck=1,mn=INF;    for(int v : vec[u])    {        if(v==f)continue;        dfs(v,u);        dp[u][0]+=min(dp[v][0],min(dp[v][1],dp[v][2]));        dp[u][1]+=min(dp[v][0],dp[v][1]);        dp[u][2]+=min(dp[v][0],dp[v][1]);        if(dp[v][0]<dp[v][1])ck=0;        else mn=min(mn,dp[v][0]-dp[v][1]);    }    if(ck)dp[u][1]+=mn;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,m,v; i<=n; i++)    {        cin >> u >> val[u] >> m;        for(int j=1; j<=m; j++)            cin >> v,addEdge(u,v);    }    dfs(1,1);    cout << min(dp[1][0],dp[1][1]) << endl;    return 0;}
]]>
@@ -5133,10 +5133,10 @@ 算法 - DP - 图论 + DP + @@ -5148,7 +5148,7 @@ /2022/05/28/luo-gu-p2016-zhan-lue-you-xi-ti-jie/ - 洛谷P2016 战略游戏 题解

题目链接:P2016 战略游戏

题意: Bob 要建立一个古城堡,城堡中的路形成一棵无根树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。

注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到。

请你编一程序,给定一树,帮 Bob 计算出他需要放置最少的士兵。

注意这里是相邻的,不是相邻的结点

显然我们需要记录与子结点所连的边是否被监视

于是有

然后就没了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,dp[N][2];vector<int> vec[N];void addEdge(int u,int v){    vec[u].push_back(v);    vec[v].push_back(u);}void dfs(int u,int f){    dp[u][1]=1;dp[u][0]=0;    for(int v : vec[u])    {        if(v==f)continue;        dfs(v,u);        dp[u][0]+=dp[v][1];        dp[u][1]+=min(dp[v][1],dp[v][0]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v,m; i<=n; i++)    {        cin >> u >> m;        for(int j=1; j<=m; j++)            cin >> v,addEdge(u+1,v+1);    }    dfs(1,1);    cout << min(dp[1][0],dp[1][1]) << endl;    return 0;}
]]>
+ 洛谷P2016 战略游戏 题解

题目链接:P2016战略游戏

题意: Bob要建立一个古城堡,城堡中的路形成一棵无根树。他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路。

注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到。

请你编一程序,给定一树,帮 Bob 计算出他需要放置最少的士兵。

注意这里是相邻的,不是相邻的结点

显然我们需要记录与子结点所连的边是否被监视

于是有 \[dp[u][0]=\sum dp[v][1]\\dp[u][1]=\sum \min(dp[v][0],dp[v][1])\] 然后就没了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,dp[N][2];vector<int> vec[N];void addEdge(int u,int v){    vec[u].push_back(v);    vec[v].push_back(u);}void dfs(int u,int f){    dp[u][1]=1;dp[u][0]=0;    for(int v : vec[u])    {        if(v==f)continue;        dfs(v,u);        dp[u][0]+=dp[v][1];        dp[u][1]+=min(dp[v][1],dp[v][0]);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v,m; i<=n; i++)    {        cin >> u >> m;        for(int j=1; j<=m; j++)            cin >> v,addEdge(u+1,v+1);    }    dfs(1,1);    cout << min(dp[1][0],dp[1][1]) << endl;    return 0;}
]]>
@@ -5175,7 +5175,7 @@ /2022/05/28/luo-gu-p4084-usaco17dec-barn-painting-g-ti-jie/ - 洛谷P4084 [USACO17DEC]Barn Painting G 题解

题目链接:P4084 [USACO17DEC]Barn Painting G

题意:题意:给定一颗N个节点组成的树,3种颜色,其中K个节点已染色,要求任意两相邻节点颜色不同,求合法染色方案数。

这个题太水了

设 $dp[u][1/2/3]$ 表示 $u$ 结点涂 $1/2/3$ 颜色的方案数

根据加法原理,

如果 $u$ 已经有颜色了,那么

否则

时间复杂度 $O(n)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)const int p=1e9+7;int n,k;int val[N],dp[N][4];vector<int> vec[N];void add(int &a,int b){a=(a+b%p)%p;}void mul(int &a,int b){a=a%p*b%p;}void dfs(int u,int f){    for(int v : vec[u])    {        if(v==f)continue;        dfs(v,u);        for(int i=1; i<=3; i++)        {            int res=0;            for(int j=1; j<=3; j++)                if(i!=j)add(res,dp[v][j]);            mul(dp[u][i],res);        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> k;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);    }    for(int i=1; i<=n; i++)        for(int j=1; j<=3; j++)            dp[i][j]=1;    for(int i=1,u; i<=k; i++)    {        cin >> u >> val[u];        for(int j=1; j<=3; j++)            if(val[u]!=j)dp[u][j]=0;    }    dfs(1,1);    cout << ((dp[1][1]+dp[1][2]+dp[1][3])%p+p)%p << endl;    return 0;}
]]>
+ 洛谷P4084[USACO17DEC]Barn Painting G 题解

题目链接:P4084[USACO17DEC]Barn Painting G

题意:题意:给定一颗N个节点组成的树,3种颜色,其中K个节点已染色,要求任意两相邻节点颜色不同,求合法染色方案数。

这个题太水了

\(dp[u][1/2/3]\) 表示 \(u\) 结点涂 \(1/2/3\) 颜色的方案数

根据加法原理,

如果 \(u\) 已经有颜色了,那么 \[dp[u][j] = \sum dp[v][k]\times [k\ne j]\times [j=\text{val[u]}]\] 否则 \[dp[u][j] = \sum dp[v][k]\times [k\ne j]\] 时间复杂度 \(O(n)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)const int p=1e9+7;int n,k;int val[N],dp[N][4];vector<int> vec[N];void add(int &a,int b){a=(a+b%p)%p;}void mul(int &a,int b){a=a%p*b%p;}void dfs(int u,int f){    for(int v : vec[u])    {        if(v==f)continue;        dfs(v,u);        for(int i=1; i<=3; i++)        {            int res=0;            for(int j=1; j<=3; j++)                if(i!=j)add(res,dp[v][j]);            mul(dp[u][i],res);        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> k;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);    }    for(int i=1; i<=n; i++)        for(int j=1; j<=3; j++)            dp[i][j]=1;    for(int i=1,u; i<=k; i++)    {        cin >> u >> val[u];        for(int j=1; j<=3; j++)            if(val[u]!=j)dp[u][j]=0;    }    dfs(1,1);    cout << ((dp[1][1]+dp[1][2]+dp[1][3])%p+p)%p << endl;    return 0;}
]]>
@@ -5189,10 +5189,10 @@ 算法 - DP - 图论 + DP + 数据结构 @@ -5206,7 +5206,7 @@ /2022/05/28/luo-gu-p1122-zui-da-zi-shu-he-ti-jie/ - 洛谷P1122 最大子树和 题解

题目链接:P1122 最大子树和

题意

小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花卉的问题。于是当日课后,小明就向老师提出了这个问题:

一株奇怪的花卉,上面共连有 $N$ 朵花,共有 $N-1$ 条枝干将花儿连在一起,并且未修剪时每朵花都不是孤立的。每朵花都有一个“美丽指数”,该数越大说明这朵花越漂亮,也有“美丽指数”为负数的,说明这朵花看着都让人恶心。所谓“修剪”,意为:去掉其中的一条枝条,这样一株花就成了两株,扔掉其中一株。经过一系列“修剪“之后,还剩下最后一株花(也可能是一朵)。老师的任务就是:通过一系列“修剪”(也可以什么“修剪”都不进行),使剩下的那株(那朵)花卉上所有花朵的“美丽指数”之和最大。

老师想了一会儿,给出了正解。小明见问题被轻易攻破,相当不爽,于是又拿来问你。

一眼树形dp

这道题就是要在树上找一个极大权值连通块

显然我们不知道连通块是什么样的,但是不急

树形dp的核心是从子树转移状态

设 $dp[u]$ 表示 $u$ 所在子树中包含 $u$ 的极大连通块大小

在本题中,如果 $u$ 的子结点 $v$ 有 $dp[v] > 0$ ,那么可以把 $v$ 给拼接到 $u$ 上

于是有

最后答案就是 $\max(dp[u])$

时间复杂度 $O(n^2)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <vector>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1.6e4+15)int n,val[N],dp[N];vector<int> vec[N];void dfs(int u,int f){    for(int v:vec[u])        if(v!=f)        {            dfs(v,u);            if(dp[v]>0)dp[u]+=dp[v];        }    dp[u]+=val[u];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> val[i];    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);    }    dfs(1,1);    cout << *max_element(dp+1,dp+1+n) << endl;    return 0;}
]]>
+ 洛谷P1122 最大子树和 题解

题目链接:P1122最大子树和

题意

小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题。一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花卉的问题。于是当日课后,小明就向老师提出了这个问题:

一株奇怪的花卉,上面共连有 \(N\)朵花,共有 \(N-1\)条枝干将花儿连在一起,并且未修剪时每朵花都不是孤立的。每朵花都有一个“美丽指数”,该数越大说明这朵花越漂亮,也有“美丽指数”为负数的,说明这朵花看着都让人恶心。所谓“修剪”,意为:去掉其中的一条枝条,这样一株花就成了两株,扔掉其中一株。经过一系列“修剪“之后,还剩下最后一株花(也可能是一朵)。老师的任务就是:通过一系列“修剪”(也可以什么“修剪”都不进行),使剩下的那株(那朵)花卉上所有花朵的“美丽指数”之和最大。

老师想了一会儿,给出了正解。小明见问题被轻易攻破,相当不爽,于是又拿来问你。

一眼树形dp

这道题就是要在树上找一个极大权值连通块

显然我们不知道连通块是什么样的,但是不急

树形dp的核心是从子树转移状态 \[dp[u] \leftarrow f(dp[v])\]\(dp[u]\) 表示 \(u\) 所在子树中包含 \(u\) 的极大连通块大小

在本题中,如果 \(u\) 的子结点 \(v\) 有 \(dp[v]> 0\) ,那么可以把 \(v\)给拼接到 \(u\)

于是有 \[dp[u]=\sum_{dp[v]>0} dp[v] + \text{val}[u]\] 最后答案就是 \(\max(dp[u])\)

时间复杂度 \(O(n^2)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>#include <vector>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1.6e4+15)int n,val[N],dp[N];vector<int> vec[N];void dfs(int u,int f){    for(int v:vec[u])        if(v!=f)        {            dfs(v,u);            if(dp[v]>0)dp[u]+=dp[v];        }    dp[u]+=val[u];}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)        cin >> val[i];    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        vec[u].push_back(v);        vec[v].push_back(u);    }    dfs(1,1);    cout << *max_element(dp+1,dp+1+n) << endl;    return 0;}
]]>
@@ -5220,10 +5220,10 @@ 算法 - DP - 图论 + DP + @@ -5235,7 +5235,7 @@ /2022/05/27/luo-gu-p3205-hnoi2010-he-chang-dui-ti-jie/ - 洛谷P3205 [HNOI2010]合唱队 题解

题目链接:P3205 [HNOI2010]合唱队

题意

为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A 需要将合唱队的人根据他们的身高排出一个队形。假定合唱队一共 $n$ 个人,第 $i$ 个人的身高为 $h_i$ 米($1000 \le h_i \le 2000$),并已知任何两个人的身高都不同。假定最终排出的队形是 $A$ 个人站成一排,为了简化问题,小 A 想出了如下排队的方式:他让所有的人先按任意顺序站成一个初始队形,然后从左到右按以下原则依次将每个人插入最终棑排出的队形中:

  • 第一个人直接插入空的当前队形中。

  • 对从第二个人开始的每个人,如果他比前面那个人高($h$ 较大),那么将他插入当前队形的最右边。如果他比前面那个人矮($h$ 较小),那么将他插入当前队形的最左边。

当 $n$ 个人全部插入当前队形后便获得最终排出的队形。

例如,有 $6$ 个人站成一个初始队形,身高依次为 $1850, 1900, 1700, 1650, 1800, 1750$,
那么小 A 会按以下步骤获得最终排出的队形:

  • $1850$。

  • $1850, 1900$,因为 $1900 > 1850$。

  • $1700, 1850, 1900$,因为 $1700 < 1900$。

  • $1650, 1700, 1850, 1900$,因为 $1650 < 1700$。

  • $1650, 1700, 1850, 1900, 1800$,因为 $1800 > 1650$。

  • $1750, 1650, 1700, 1850, 1900, 1800$,因为 $1750 < 1800$。

因此,最终排出的队形是 $1750, 1650, 1700, 1850, 1900, 1800$。

小 A 心中有一个理想队形,他想知道多少种初始队形可以获得理想的队形。

请求出答案对 $19650827$ 取模的值。

容易发现方案数与区间的长度有关

并且一个区间的两侧都可以扩展

考虑使用区间dp

这个题看上去还挺乱的,所以我们抓住关键点

如果某一排列中, $b$ 比它前面的 $a$ 小,那么 $b$ 会从左边加入区间

而加入 $b$ 之前, $a$ 只有可能在区间的左端点或右端点

设 $dp[i][j][0/1]$ 表示区间 $[i,j]$ 的方案数,其中:

  • $0$ 表示 $a[i]$ 是本次转移中 从左侧 向区间内加入的数
  • $1$ 表示 $a[j]$ 是本次转移中 从右侧 向区间内加入的数

对于左侧加入的数,即 $a[i]$ ,它的前一个数要么是 $a[i+1]$ ,要么是 $a[j]$

对于右侧加入的数,即 $a[j]$ ,它的前一个数要么是 $a[j-1]$ ,要么是 $a[i]$

如果满足条件即可转移,如下

if(a[i]<a[i+1]) dp[i][j][0]+=dp[i+1][j][0];if(a[i]<a[j]) dp[i][j][0]+=dp[i+1][j][1];if(a[j]>a[j-1]) dp[i][j][1]+=dp[i][j-1][1];if(a[j]>a[i]) dp[i][j][1]+=dp[i][j-1][0];dp[i][j][0]%=p; dp[i][j][1]%=p;dp[i][j][0]%=p; dp[i][j][1]%=p; // p=19650827

考虑边界 $i=j$ 时

此时认为它们都是左侧加入的即可(认为是右侧也可)

dp[i][i][0]=1,而不是dp[i][i][0]=dp[i][i][1]=1

为什么前者是正确的,而后者是错误的?

因为区间长度为 $2$ 时,有 $i+1=j$ 且 $j-1=i$

也就是后面的会多转移一次,根据乘法原理,最后答案会 $\times 2$

因此要去掉这个 $2$ (当然你可以去求个逆元啥的 qwq)

然后我们就可以 $O(n^2)$ 解决这个题目辣!

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1005)const int p=19650827;int n;int a[N],dp[N][N][2];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++) cin >> a[i];    for(int i=1; i<=n; i++) dp[i][i][0]=1;    for(int len=2; len<=n; len++)        for(int i=1,j=i+len-1; j<=n; i++,j++)        {            if(a[i]<a[i+1])dp[i][j][0]+=dp[i+1][j][0];            if(a[i]<a[j])dp[i][j][0]+=dp[i+1][j][1];            if(a[j]>a[j-1])dp[i][j][1]+=dp[i][j-1][1];            if(a[j]>a[i])dp[i][j][1]+=dp[i][j-1][0];            dp[i][j][0]%=p; dp[i][j][1]%=p;        }    cout << ((dp[1][n][0]+dp[1][n][1])%p) << endl;    return 0;}
]]>
+ 洛谷P3205 [HNOI2010]合唱队题解

题目链接:P3205[HNOI2010]合唱队

题意

为了在即将到来的晚会上有更好的演出效果,作为 AAA 合唱队负责人的小 A需要将合唱队的人根据他们的身高排出一个队形。假定合唱队一共 \(n\) 个人,第 \(i\) 个人的身高为 \(h_i\) 米(\(1000\le h_i \le2000\)),并已知任何两个人的身高都不同。假定最终排出的队形是\(A\) 个人站成一排,为了简化问题,小 A想出了如下排队的方式:他让所有的人先按任意顺序站成一个初始队形,然后从左到右按以下原则依次将每个人插入最终棑排出的队形中:

  • 第一个人直接插入空的当前队形中。

  • 对从第二个人开始的每个人,如果他比前面那个人高(\(h\)较大),那么将他插入当前队形的最右边。如果他比前面那个人矮(\(h\)较小),那么将他插入当前队形的最左边。

\(n\)个人全部插入当前队形后便获得最终排出的队形。

例如,有 \(6\)个人站成一个初始队形,身高依次为 \(1850, 1900,1700, 1650, 1800, 1750\)
那么小 A 会按以下步骤获得最终排出的队形:

  • \(1850\)

  • \(1850, 1900\),因为 \(1900 > 1850\)。

  • \(1700, 1850, 1900\),因为 \(1700 < 1900\)。

  • \(1650, 1700, 1850, 1900\),因为\(1650 < 1700\)

  • \(1650, 1700, 1850, 1900,1800\),因为 \(1800 >1650\)

  • \(1750, 1650, 1700, 1850, 1900,1800\),因为 \(1750 <1800\)

因此,最终排出的队形是 \(1750, 1650, 1700,1850, 1900, 1800\)

小 A心中有一个理想队形,他想知道多少种初始队形可以获得理想的队形。

请求出答案对 \(19650827\)取模的值。

容易发现方案数与区间的长度有关

并且一个区间的两侧都可以扩展

考虑使用区间dp

这个题看上去还挺乱的,所以我们抓住关键点

如果某一排列中, \(b\) 比它前面的\(a\) 小,那么 \(b\) 会从左边加入区间

而加入 \(b\) 之前, \(a\) 只有可能在区间的左端点或右端点

\(dp[i][j][0/1]\) 表示区间 \([i,j]\) 的方案数,其中:

  • \(0\) 表示 \(a[i]\) 是本次转移中 从左侧向区间内加入的数
  • \(1\) 表示 \(a[j]\) 是本次转移中 从右侧向区间内加入的数

对于左侧加入的数,即 \(a[i]\),它的前一个数要么是 \(a[i+1]\),要么是 \(a[j]\)

对于右侧加入的数,即 \(a[j]\),它的前一个数要么是 \(a[j-1]\),要么是 \(a[i]\)

如果满足条件即可转移,如下

if(a[i]<a[i+1]) dp[i][j][0]+=dp[i+1][j][0];if(a[i]<a[j]) dp[i][j][0]+=dp[i+1][j][1];if(a[j]>a[j-1]) dp[i][j][1]+=dp[i][j-1][1];if(a[j]>a[i]) dp[i][j][1]+=dp[i][j-1][0];dp[i][j][0]%=p; dp[i][j][1]%=p;dp[i][j][0]%=p; dp[i][j][1]%=p; // p=19650827

考虑边界 \(i=j\)

此时认为它们都是左侧加入的即可(认为是右侧也可)

dp[i][i][0]=1,而不是dp[i][i][0]=dp[i][i][1]=1

为什么前者是正确的,而后者是错误的?

因为区间长度为 \(2\) 时,有 \(i+1=j\) 且 \(j-1=i\)

也就是后面的会多转移一次,根据乘法原理,最后答案会 \(\times 2\)

因此要去掉这个 \(2\)(当然你可以去求个逆元啥的 qwq)

然后我们就可以 \(O(n^2)\)解决这个题目辣!

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1005)const int p=19650827;int n;int a[N],dp[N][N][2];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++) cin >> a[i];    for(int i=1; i<=n; i++) dp[i][i][0]=1;    for(int len=2; len<=n; len++)        for(int i=1,j=i+len-1; j<=n; i++,j++)        {            if(a[i]<a[i+1])dp[i][j][0]+=dp[i+1][j][0];            if(a[i]<a[j])dp[i][j][0]+=dp[i+1][j][1];            if(a[j]>a[j-1])dp[i][j][1]+=dp[i][j-1][1];            if(a[j]>a[i])dp[i][j][1]+=dp[i][j-1][0];            dp[i][j][0]%=p; dp[i][j][1]%=p;        }    cout << ((dp[1][n][0]+dp[1][n][1])%p) << endl;    return 0;}
]]>
@@ -5262,7 +5262,7 @@ /2022/05/27/uva1629-qie-dan-gao-cake-slicing-ti-jie/ - UVA1629 切蛋糕 Cake slicing 题解

题目链接:UVA1629 切蛋糕 Cake slicing

题意:这个翻译够烂的,直接看pdf

翻译:有一个n行m列(1<=n,m<=20)的网络蛋糕上有k个樱桃。每次可以用一刀沿着网络线把蛋糕切成两块,并且只能够直切不能拐弯。要求最后每一块蛋糕上恰好有一个樱桃,且切割线总长度最小。

输入输出格式 输入格式:每次输入有若干组数据。每组数据第一行有三个正整数n m k(行,列,樱桃个数),之后的k行每行两个正整数(樱桃的坐标) 输出格式:输出有若干行,对应每组数据。每行输出两个正整数(id,最小的切割长度)

输入输出样例 输入样例: 3 4 3 1 2 2 3 3 2 输出样例: Case 1: 5

首先普通的二维枚举好像不行

数据范围这么小,于是考虑从小的矩形向大的矩形更新

设 $dp[x_1][y_1][x_2][y_2]$ 表示矩形的左下角为 $(x_1,y_1)$ ,右上角为 $(x_2,y_2)$ 时的最小花费

方便起见,下面转移方程中的 $dp[x_1][y_1][x_2][y_2]$ 用 $d$ 替代

考虑纵切,则有

考虑横切,则有

由于顺推无法保证dp顺序,考虑记忆化搜索

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(25)int n,m,k,id,f[N][N][N][N],sum[N][N];int getsum(int a,int b,int c,int d){return sum[c][d]+sum[a-1][b-1]-sum[c][b-1]-sum[a-1][d];}int dp(int x1,int y1,int x2,int y2){    int t=getsum(x1,y1,x2,y2);    if(!t)return INF;    if(t==1)return 0;    int &d=f[x1][y1][x2][y2];    if(d!=INF)return d;    for(int i=x1; i<x2; i++)        d=min(d,dp(x1,y1,i,y2)+dp(i+1,y1,x2,y2)+y2-y1+1);    for(int i=y1; i<y2; i++)        d=min(d,dp(x1,y1,x2,i)+dp(x1,i+1,x2,y2)+x2-x1+1);    return d;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(cin >> n >> m >> k && n && m)    {        memset(sum,0,sizeof(sum));        memset(f,0x3f,sizeof(f));        for(int i=1,x,y; i<=k; i++)            cin >> x >> y,sum[x][y]=1;        for(int i=1; i<=n; i++) for(int j=1; j<=m; j++)            sum[i][j]+=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];        cout << "Case "<< ++id << ": " << dp(1,1,n,m) << '\n';    }    return 0;}
]]>
+ UVA1629 切蛋糕 Cake slicing题解

题目链接:UVA1629切蛋糕 Cake slicing

题意:这个翻译够烂的,直接看pdf

翻译:有一个n行m列(1<=n,m<=20)的网络蛋糕上有k个樱桃。每次可以用一刀沿着网络线把蛋糕切成两块,并且只能够直切不能拐弯。要求最后每一块蛋糕上恰好有一个樱桃,且切割线总长度最小。

输入输出格式输入格式:每次输入有若干组数据。每组数据第一行有三个正整数n mk(行,列,樱桃个数),之后的k行每行两个正整数(樱桃的坐标)输出格式:输出有若干行,对应每组数据。每行输出两个正整数(id,最小的切割长度)

输入输出样例 输入样例: 3 4 3 1 2 2 3 3 2 输出样例: Case 1: 5

首先普通的二维枚举好像不行

数据范围这么小,于是考虑从小的矩形向大的矩形更新

\(dp[x_1][y_1][x_2][y_2]\)表示矩形的左下角为 \((x_1,y_1)\),右上角为 \((x_2,y_2)\)时的最小花费

方便起见,下面转移方程中的 \(dp[x_1][y_1][x_2][y_2]\) 用 \(d\) 替代

考虑纵切,则有 \[d=\min_{x_1 \le i <x_2}(d,dp[x_1][y_1][i][y_2]+dp[i+1][y_1][x_2][y_2]+y_2-y_1+1)\] 考虑横切,则有 \[d=\min_{y_1 \le i <y_2}(d,dp[x_1][y_1][x_2][i]+dp[x_1][i+1][x_2][y_2]+x_2-x_1+1)\] 由于顺推无法保证dp顺序,考虑记忆化搜索

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(25)int n,m,k,id,f[N][N][N][N],sum[N][N];int getsum(int a,int b,int c,int d){return sum[c][d]+sum[a-1][b-1]-sum[c][b-1]-sum[a-1][d];}int dp(int x1,int y1,int x2,int y2){    int t=getsum(x1,y1,x2,y2);    if(!t)return INF;    if(t==1)return 0;    int &d=f[x1][y1][x2][y2];    if(d!=INF)return d;    for(int i=x1; i<x2; i++)        d=min(d,dp(x1,y1,i,y2)+dp(i+1,y1,x2,y2)+y2-y1+1);    for(int i=y1; i<y2; i++)        d=min(d,dp(x1,y1,x2,i)+dp(x1,i+1,x2,y2)+x2-x1+1);    return d;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(cin >> n >> m >> k && n && m)    {        memset(sum,0,sizeof(sum));        memset(f,0x3f,sizeof(f));        for(int i=1,x,y; i<=k; i++)            cin >> x >> y,sum[x][y]=1;        for(int i=1; i<=n; i++) for(int j=1; j<=m; j++)            sum[i][j]+=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];        cout << "Case "<< ++id << ": " << dp(1,1,n,m) << '\n';    }    return 0;}
]]>
@@ -5289,7 +5289,7 @@ /2022/05/27/uva1437-string-painter-ti-jie/ - UVA1437 String painter 题解

题目链接:UVA1437 String painter

题意:There are two strings A and B with equal length. Both strings are made up of lower case letters. Now you have a powerful string painter. With the help of the painter, you can change a segment of characters of a string to any other character you want. That is, after using the painter, the segment is made up of only one kind of character. Now your task is to change A to B using string painter. What’s the minimum number of operations?

1 <= |A|=|B| <= 100

显然区间dp

考虑A与B完全不同

设 $f[i][j]$ 表示暴力(不管A的情况)修改 $[i,j]$ 的最小花费

那就是这个题目

当 $i=j$ 时,有

当 $i\ne j$ 时,

  • 若 $b[i]=b[j]$ ,此时只需要 $[i+1,j]$ 或 $[i,j-1]$ 首次涂的时候多涂一格即可

  • 若 $b[i]\ne b[j]$ , $b[i],b[j]$ 都可以尝试向内扩展,显然 $[i,j]$ 首次涂会涂两种颜色,考虑枚举其断点 $k$

显然有时候我们的暴力修改时不必要的

设 $g[i]$ 表示 $[1,i]$ 的最小修改,则 $g[i]$ 至多为 $f[1][i]$ ,且有 $g[0]=0$

  • 若 $a[i]=b[i]$ ,则

    显然这个 $g[i-1]$ 一定不大于 $f[1][i]$ ,直接转移即可

  • 若 $a[i] \ne b[i]$ ,则此时 $a[i]$ 是必须得暴力修改的了

    但是这个修改长度我们不知道

    考虑枚举一个断点 $k$ ,也就是 $[k+1,i]$ 第一次涂色涂成 $a[i]$

    为什么不是上个极大公共子串的末尾开始呢?

    考虑这种情况

    zzzzfzzzzabcdfdcba

    最小花费为 $5$

    故转移方程为

时间复杂度 $O(Qn^2)$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(105)char a[N],b[N];int n,f[N][N],g[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(cin >> (a+1) >> (b+1))    {        n=strlen(a+1);        memset(f,0x3f,sizeof(f));        memset(g,0x3f,sizeof(g));        for(int i=1; i<=n; i++)f[i][i]=1;        for(int len=2; len<=n; len++)            for(int i=1,j=i+len-1; j<=n; i++,j++)            {                if(b[i]==b[j])                    f[i][j]=min(f[i+1][j],f[i][j-1]);                else for(int k=i; k<j; k++)                    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);            }        g[0]=0;        for(int i=1; i<=n; i++)        {            if(a[i]==b[i])g[i]=g[i-1];            else for(int k=0; k<i; k++)                g[i]=min(g[i],g[k]+f[k+1][i]);        }        cout << g[n] << '\n';    }    return 0;}
]]>
+ UVA1437 String painter 题解

题目链接:UVA1437String painter

题意:There are two strings A and B with equallength. Both strings are made up of lower case letters. Now you have apowerful string painter. With the help of the painter, you can change asegment of characters of a string to any other character you want. Thatis, after using the painter, the segment is made up of only one kind ofcharacter. Now your task is to change A to B using string painter.What’s the minimum number of operations?

1 <= |A|=|B| <= 100

显然区间dp

考虑A与B完全不同

\(f[i][j]\)表示暴力(不管A的情况)修改 \([i,j]\)的最小花费

那就是这个题目了

\(i=j\) 时,有 \[f[i][j]=1\] 当 \(i\ne j\) 时,

  • \(b[i]=b[j]\) ,此时只需要\([i+1,j]\)\([i,j-1]\) 首次涂的时候多涂一格即可 \[f[i][j]=\min(f[i+1][j],f[i][j-1])\]

  • \(b[i]\ne b[j]\)\(b[i],b[j]\) 都可以尝试向内扩展,显然 \([i,j]\) 首次涂会涂两种颜色,考虑枚举其断点\(k\) \[f[i][j]=\min(f[i][j],f[i][k]+f[k+1][j])\]

显然有时候我们的暴力修改时不必要的

\(g[i]\) 表示 \([1,i]\) 的最小修改,则 \(g[i]\) 至多为 \(f[1][i]\) ,且有 \(g[0]=0\)

  • \(a[i]=b[i]\) ,则 \[g[i]=g[i-1]\] 显然这个 \(g[i-1]\)一定不大于 \(f[1][i]\),直接转移即可

  • \(a[i] \ne b[i]\) ,则此时\(a[i]\) 是必须得暴力修改的了

    但是这个修改长度我们不知道

    考虑枚举一个断点 \(k\) ,也就是\([k+1,i]\) 第一次涂色涂成 \(a[i]\)

    为什么不是上个极大公共子串的末尾开始呢?

    考虑这种情况

    zzzzfzzzzabcdfdcba

    最小花费为 \(5\)

    故转移方程为 \[g[i]=\min_{0 \le k \le i}(g[i],g[k]+f[k+1][i])\]

时间复杂度 \(O(Qn^2)\)

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(105)char a[N],b[N];int n,f[N][N],g[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(cin >> (a+1) >> (b+1))    {        n=strlen(a+1);        memset(f,0x3f,sizeof(f));        memset(g,0x3f,sizeof(g));        for(int i=1; i<=n; i++)f[i][i]=1;        for(int len=2; len<=n; len++)            for(int i=1,j=i+len-1; j<=n; i++,j++)            {                if(b[i]==b[j])                    f[i][j]=min(f[i+1][j],f[i][j-1]);                else for(int k=i; k<j; k++)                    f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);            }        g[0]=0;        for(int i=1; i<=n; i++)        {            if(a[i]==b[i])g[i]=g[i-1];            else for(int k=0; k<i; k++)                g[i]=min(g[i],g[k]+f[k+1][i]);        }        cout << g[n] << '\n';    }    return 0;}
]]>
@@ -5316,7 +5316,7 @@ /2022/05/27/luo-gu-p2890-usaco07open-cheapest-palindrome-g-ti-jie/ - 洛谷P2890 [USACO07OPEN]Cheapest Palindrome G 题解

题目链接:P2890 [USACO07OPEN]Cheapest Palindrome G

题意

Keeping track of all the cows can be a tricky task so Farmer John has installed a system to automate it. He has installed on each cow an electronic ID tag that the system will read as the cows pass by a scanner. Each ID tag’s contents are currently a single string with length M (1 ≤ M ≤ 2,000) characters drawn from an alphabet of N (1 ≤ N ≤ 26) different symbols (namely, the lower-case roman alphabet).

Cows, being the mischievous creatures they are, sometimes try to spoof the system by walking backwards. While a cow whose ID is “abcba” would read the same no matter which direction the she walks, a cow with the ID “abcb” can potentially register as two different IDs (“abcb” and “bcba”).

FJ would like to change the cows’s ID tags so they read the same no matter which direction the cow walks by. For example, “abcb” can be changed by adding “a” at the end to form “abcba” so that the ID is palindromic (reads the same forwards and backwards). Some other ways to change the ID to be palindromic are include adding the three letters “bcb” to the begining to yield the ID “bcbabcb” or removing the letter “a” to yield the ID “bcb”. One can add or remove characters at any location in the string yielding a string longer or shorter than the original string.

Unfortunately as the ID tags are electronic, each character insertion or deletion has a cost (0 ≤ cost ≤ 10,000) which varies depending on exactly which character value to be added or deleted. Given the content of a cow’s ID tag and the cost of inserting or deleting each of the alphabet’s characters, find the minimum cost to change the ID tag so it satisfies FJ’s requirements. An empty ID tag is considered to satisfy the requirements of reading the same forward and backward. Only letters with associated costs can be added to a string.

字串S长M,由N个小写字母构成。欲通过增删字母将其变为回文串,增删特定字母花费不同,求最小花费。

显然,一个串的最小花费可以看作:从串内某个字符开始,一边修改,一边在左右添加原串中相邻的字符,最后总的花费

如果用线性dp,会丢失串的左右端点信息,而且无法从子串转移

因此考虑使用区间dp(其实上面都是废话,题写多了这种就是一眼的事情 qwq)

设 $dp[i][j]$ 表示区间 $[i,j]$ 被修改好的最小花费

不难发现,$i=j$ 时,$dp[i][j]$

当 $i \ne j$ 时,

  • 若 $s[i]=s[j]$ ,显然有

    注意特判 $j-i=1$ 的情况

  • 若 $s[i] \ne s[j]$

    考虑 $[i+1,j]$ 的转移,显然我们有两种选择,即

    • 删除左侧新加上的 $s[i]$
    • 右侧再添加一个 $s[i]$

    而这个选择并不会影响后续的转移,所以我们只要选个 $\min$ 就好了

    $[i,j-1]$ 的转移同理

    故有转移方程

    $\text{val}[s[i]]$ 表示 $s[i]$ 字符的最小花费,就是上面说的那个 $\min$

然后就转移就完了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;char s[N],ch;int v[32],dp[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> m >> n >> (s+1);    for(int i=1,a,b; i<=m; i++)    {        cin >> ch >> a >> b;        v[ch-'a']=min(a,b);    }    memset(dp,0x3f,sizeof(dp));    for(int i=1; i<=n; i++) dp[i][i]=0;    for(int len=2; len<=n; len++)        for(int i=1,j=i+len-1; j<=n; i++,j++)        {            dp[i][j]=min(dp[i+1][j]+v[s[i]-'a'],dp[i][j-1]+v[s[j]-'a']);            if(s[i]==s[j])            {                if(len==2)dp[i][j]=0;                else dp[i][j]=min(dp[i][j],dp[i+1][j-1]);            }        }    cout << dp[1][n] << endl;    return 0;}
]]>
+ 洛谷P2890[USACO07OPEN]Cheapest Palindrome G 题解

题目链接:P2890[USACO07OPEN]Cheapest Palindrome G

题意

Keeping track of all the cows can be a tricky task so Farmer John hasinstalled a system to automate it. He has installed on each cow anelectronic ID tag that the system will read as the cows pass by ascanner. Each ID tag's contents are currently a single string withlength M (1 ≤ M ≤ 2,000) characters drawn from an alphabet of N (1 ≤ N ≤26) different symbols (namely, the lower-case roman alphabet).

Cows, being the mischievous creatures they are, sometimes try tospoof the system by walking backwards. While a cow whose ID is "abcba"would read the same no matter which direction the she walks, a cow withthe ID "abcb" can potentially register as two different IDs ("abcb" and"bcba").

FJ would like to change the cows's ID tags so they read the same nomatter which direction the cow walks by. For example, "abcb" can bechanged by adding "a" at the end to form "abcba" so that the ID ispalindromic (reads the same forwards and backwards). Some other ways tochange the ID to be palindromic are include adding the three letters"bcb" to the begining to yield the ID "bcbabcb" or removing the letter"a" to yield the ID "bcb". One can add or remove characters at anylocation in the string yielding a string longer or shorter than theoriginal string.

Unfortunately as the ID tags are electronic, each character insertionor deletion has a cost (0 ≤ cost ≤ 10,000) which varies depending onexactly which character value to be added or deleted. Given the contentof a cow's ID tag and the cost of inserting or deleting each of thealphabet's characters, find the minimum cost to change the ID tag so itsatisfies FJ's requirements. An empty ID tag is considered to satisfythe requirements of reading the same forward and backward. Only letterswith associated costs can be added to a string.

字串S长M,由N个小写字母构成。欲通过增删字母将其变为回文串,增删特定字母花费不同,求最小花费。

显然,一个串的最小花费可以看作:从串内某个字符开始,一边修改,一边在左右添加原串中相邻的字符,最后总的花费

如果用线性dp,会丢失串的左右端点信息,而且无法从子串转移

因此考虑使用区间dp(其实上面都是废话,题写多了这种就是一眼的事情qwq)

\(dp[i][j]\) 表示区间 \([i,j]\) 被修改好的最小花费

不难发现,\(i=j\) 时,\(dp[i][j]\)

\(i \ne j\) 时,

  • \(s[i]=s[j]\) ,显然有 \[dp[i][j]=\min(dp[i][j],dp[i+1][j-1])\] 注意特判 \(j-i=1\)的情况

  • \(s[i] \ne s[j]\)

    考虑 \([i+1,j]\)的转移,显然我们有两种选择,即

    • 删除左侧新加上的 \(s[i]\)
    • 右侧再添加一个 \(s[i]\)

    而这个选择并不会影响后续的转移,所以我们只要选个 \(\min\) 就好了

    \([i,j-1]\) 的转移同理

    故有转移方程 \[dp[i][j]=\min(dp[i+1][j]+\text{val}[s[i]],dp[i][j-1]+\text{val}[s[j]])\] \(\text{val}[s[i]]\) 表示\(s[i]\)字符的最小花费,就是上面说的那个 \(\min\)

然后就转移就完了

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;char s[N],ch;int v[32],dp[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> m >> n >> (s+1);    for(int i=1,a,b; i<=m; i++)    {        cin >> ch >> a >> b;        v[ch-'a']=min(a,b);    }    memset(dp,0x3f,sizeof(dp));    for(int i=1; i<=n; i++) dp[i][i]=0;    for(int len=2; len<=n; len++)        for(int i=1,j=i+len-1; j<=n; i++,j++)        {            dp[i][j]=min(dp[i+1][j]+v[s[i]-'a'],dp[i][j-1]+v[s[j]-'a']);            if(s[i]==s[j])            {                if(len==2)dp[i][j]=0;                else dp[i][j]=min(dp[i][j],dp[i+1][j-1]);            }        }    cout << dp[1][n] << endl;    return 0;}
]]>
@@ -5343,7 +5343,7 @@ /2022/05/26/luo-gu-p2851-usaco06dec-the-fewest-coins-g-ti-jie/ - 洛谷P2851 [USACO06DEC]The Fewest Coins G 题解

题目链接:P2851 [USACO06DEC]The Fewest Coins G

题意

Farmer John has gone to town to buy some farm supplies. Being a very efficient man, he always pays for his goods in such a way that the smallest number of coins changes hands, i.e., the number of coins he uses to pay plus the number of coins he receives in change is minimized. Help him to determine what this minimum number is.

FJ wants to buy T (1 ≤ T ≤ 10,000) cents of supplies. The currency system has N (1 ≤ N ≤ 100) different coins, with values V1, V2, …, VN (1 ≤ Vi ≤ 120). Farmer John is carrying C1 coins of value V1, C2 coins of value V2, …., and CN coins of value VN (0 ≤ Ci ≤ 10,000). The shopkeeper has an unlimited supply of all the coins, and always makes change in the most efficient manner (although Farmer John must be sure to pay in a way that makes it possible to make the correct change).

农夫John想到镇上买些补给。为了高效地完成任务,他想使硬币的转手次数最少。即使他交付的硬币数与找零得到的的硬币数最少。

John想要买价值为T的东西。有N(1<=n<=100)种不同的货币参与流通,面值分别为V1,V2..Vn (1<=Vi<=120)。John有Ci个面值为Vi的硬币(0<=Ci<=10000)。

我们假设店主有无限多的硬币, 并总按最优方案找零。注意无解输出-1。

不妨把题目转化成下图的形式

设 $f[j]$ 表示付钱的总额为 $j$ 时所需的最小硬币数

显然这是一个多重背包,直接求解即可

然后设 $g[j]$ 表示找零的总额为 $j$ 时的最小硬币数

显然这是一个完全背包,直接求解即可

然后将其合并就好了

本题的关键在于背包容量的上界如何确定

结论:$f$ 背包容量上界为 $T+2V_{\max}^2$ ,即 $g$ 背包容量上界为 $2V_{\max}^2$

证明:翻译+适当修改自 prove ,有错欢迎指出

方便起见,下文中将使用 $V$ 表示 $V_{\max}$

引理:对于背包容量 $m$ 模 $V$ 意义下的每个剩余类,我们都可以使用总额不超过 $V^2$ 的若干硬币来表示。

证明:考虑反证法。

设某种硬币组合的总和 $S \ge V^2$ 并且不可能产生总和为 $S-k\times V,k \in \mathbb{N}$ 的硬币组合

令这种硬币组合形成的多重集为 $\{x_1,x_2,\dots,x_k\}$

因为总和至少为 $V^2$ ,所以 $k\ge V$

考虑对这个多重集做前缀和,也就是把多重集的每个元素以某种方式排列,然后计算 $x_1,x_1+x_2,x_1+x_2+x_3,\dots$

记 $s_n = \sum_{1\le i \le n} x_i$

根据 $S$ 的定义可知, $s_i$ 在模 $V$ 意义下不可能为 $0$

因此 $s_i$ 只有 $V-1$ 种可能性

根据鸽巢原理,至少存在一对 $i,j(1 \le i < j \le k)$ 使得

也就是排列后,区间 $[i,j]$ 的和为 $V$ 的倍数

因此我们可以考虑删除一些能被 $V$ 整除的子集,产生总和为 $S-k\times V,k \in \mathbb{N}$ 的多重集

显然这与我们的假设相矛盾

因此对于背包容量 $m$ 模 $V$ 意义下的每个剩余类,我们都可以使用总额不超过 $V^2$ 的若干硬币来表示。

同时,我们还证明了对于所有总和大于 $V^2$ 的多重集,至少有一个 $V$ 硬币

现在我们知道,对于所有的目标总额 $S \ge 2V^2$ ,使得 $S \bmod V = d$ ,由于我们一定会选择更优的多重集使得 $\sum x_i \bmod V = d$ ,于是在 $d$ 上添加 $V$ 类型的硬币直到达到 $S$ (这里用到了贪心的思想)

我们回到问题,假设 Farmer John 得到 $X$ 元的找零,且 $X \ge 2V^2 + v$

还是刚才那张图

根据 $X$ 的定义,右边黑色的一段至少为 $2V^2$ (这里的 $v$ 表示中间那个被 $T$ “切成”两半的块的超过 $T$ 的部分,这样假设是为了忽略 $T$ 的干扰)

在黑色块中,总和至少为 $V^2$ (实际上是 $2V^2$ ,但只考虑 $V^2$ ),因此我们证明了存在一个黑色块子集可以被 $V$ 整除

此外,找零的总额大于等于 $2V^2$ ,故右边蓝色的一段中, 所有的 $V$ 中至少有 $V^2$ (原文有点拗口,其实意思是:至少用了 $V$ 个 $V$ )

因此我们用蓝色块去消掉了黑色块,而枚举这样的情况并没有意义,它等价于消掉的那种情况

而上面提到了一个更优的方法

因此最优解的找零最多不大于 $2V^2$

也就是 $f$ 背包容量上界为 $T+2V_{\max}^2$ ,即 $g$ 背包容量上界为 $2V_{\max}^2$ 。

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(105)#define M (int)(1e4+15)#define V (int)(120)int n,m;int f[M+2*V*V],g[2*V*V];int v[N],c[N],sum,mx;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> v[i];    for(int i=1; i<=n; i++)    {        cin >> c[i];        sum+=c[i]*v[i];        mx=max(mx,v[i]*v[i]);    }mx<<=1;    if(sum<m)return cout << -1,0;    memset(f,0x3f,sizeof(f));    memset(g,0x3f,sizeof(g));    f[0]=g[0]=0;    for(int i=1; i<=n; i++)        for(int j=v[i]; j<=mx; j++)            g[j]=min(g[j],g[j-v[i]]+1);    mx+=m;    for(int i=1; i<=n; i++)    {        for(int t=1; t<=c[i]; t<<=1)        {            for(int j=mx; j>=t*v[i]; j--)                f[j]=min(f[j],f[j-t*v[i]]+t);            c[i]-=t;        }        if(c[i])            for(int j=mx; j>=c[i]*v[i]; j--)                f[j]=min(f[j],f[j-c[i]*v[i]]+c[i]);    }    int res=INF;    for(int i=m; i<=mx; i++)        res=min(res,f[i]+g[i-m]);    cout << ((res==INF)?(-1):res) << endl;    return 0;}
]]>
+ 洛谷P2851[USACO06DEC]The Fewest Coins G 题解

题目链接:P2851 [USACO06DEC]TheFewest Coins G

题意

Farmer John has gone to town to buy some farm supplies. Being a veryefficient man, he always pays for his goods in such a way that thesmallest number of coins changes hands, i.e., the number of coins heuses to pay plus the number of coins he receives in change is minimized.Help him to determine what this minimum number is.

FJ wants to buy T (1 ≤ T ≤ 10,000) cents of supplies. The currencysystem has N (1 ≤ N ≤ 100) different coins, with values V1, V2, ..., VN(1 ≤ Vi ≤ 120). Farmer John is carrying C1 coins of value V1, C2 coinsof value V2, ...., and CN coins of value VN (0 ≤ Ci ≤ 10,000). Theshopkeeper has an unlimited supply of all the coins, and always makeschange in the most efficient manner (although Farmer John must be sureto pay in a way that makes it possible to make the correct change).

农夫John想到镇上买些补给。为了高效地完成任务,他想使硬币的转手次数最少。即使他交付的硬币数与找零得到的的硬币数最少。

John想要买价值为T的东西。有N(1<=n<=100)种不同的货币参与流通,面值分别为V1,V2..Vn(1<=Vi<=120)。John有Ci个面值为Vi的硬币(0<=Ci<=10000)。

我们假设店主有无限多的硬币,并总按最优方案找零。注意无解输出-1。

不妨把题目转化成下图的形式

\(f[j]\) 表示付钱的总额为 \(j\) 时所需的最小硬币数

显然这是一个多重背包,直接求解即可

然后设 \(g[j]\) 表示找零的总额为\(j\) 时的最小硬币数

显然这是一个完全背包,直接求解即可

然后将其合并就好了

本题的关键在于背包容量的上界如何确定

结论\(f\)背包容量上界为 \(T+2V_{\max}^2\) ,即\(g\) 背包容量上界为 \(2V_{\max}^2\)

证明:翻译+适当修改自 prove,有错欢迎指出

方便起见,下文中将使用 \(V\) 表示\(V_{\max}\)

引理:对于背包容量 \(m\) 模 \(V\)意义下的每个剩余类,我们都可以使用总额不超过 \(V^2\) 的若干硬币来表示。

证明:考虑反证法。

设某种硬币组合的总和 \(S \ge V^2\)并且不可能产生总和为 \(S-k\times V,k \in\mathbb{N}\) 的硬币组合

令这种硬币组合形成的多重集为 \(\{x_1,x_2,\dots,x_k\}\)

因为总和至少为 \(V^2\) ,所以 \(k\ge V\)

考虑对这个多重集做前缀和,也就是把多重集的每个元素以某种方式排列,然后计算\(x_1,x_1+x_2,x_1+x_2+x_3,\dots\)

\(s_n = \sum_{1\le i \le n}x_i\)

根据 \(S\) 的定义可知, \(s_i\) 在模 \(V\) 意义下不可能为 \(0\)

因此 \(s_i\) 只有 \(V-1\) 种可能性

根据鸽巢原理,至少存在一对 \(i,j(1 \le i< j \le k)\) 使得 \[s_j-s_i = d \pmod{V}\] 也就是排列后,区间 \([i,j]\)的和为 \(V\) 的倍数

因此我们可以考虑删除一些能被 \(V\)整除的子集,产生总和为 \(S-k\times V,k \in\mathbb{N}\) 的多重集

显然这与我们的假设相矛盾

因此对于背包容量 \(m\)\(V\)意义下的每个剩余类,我们都可以使用总额不超过 \(V^2\) 的若干硬币来表示。

同时,我们还证明了对于所有总和大于 \(V^2\) 的多重集,至少有一个 \(V\) 硬币

现在我们知道,对于所有的目标总额 \(S \ge2V^2\) ,使得 \(S \bmod V = d\),由于我们一定会选择更优的多重集使得 \(\sumx_i \bmod V = d\) ,于是在 \(d\)上添加 \(V\) 类型的硬币直到达到 \(S\) (这里用到了贪心的思想)

我们回到问题,假设 Farmer John 得到 \(X\) 元的找零,且 \(X \ge 2V^2 + v\)

还是刚才那张图

根据 \(X\)的定义,右边黑色的一段至少为 \(2V^2\)(这里的 \(v\) 表示中间那个被 \(T\) “切成”两半的块的超过 \(T\) 的部分,这样假设是为了忽略 \(T\) 的干扰)

在黑色块中,总和至少为 \(V^2\)(实际上是 \(2V^2\) ,但只考虑 \(V^2\)),因此我们证明了存在一个黑色块子集可以被 \(V\) 整除

此外,找零的总额大于等于 \(2V^2\),故右边蓝色的一段中, 所有的 \(V\)中至少有 \(V^2\)(原文有点拗口,其实意思是:至少用了 \(V\) 个 \(V\) )

因此我们用蓝色块去消掉了黑色块,而枚举这样的情况并没有意义,它等价于消掉的那种情况

而上面提到了一个更优的方法

因此最优解的找零最多不大于 \(2V^2\)

也就是 \(f\) 背包容量上界为 \(T+2V_{\max}^2\) ,即 \(g\) 背包容量上界为 \(2V_{\max}^2\) 。

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(105)#define M (int)(1e4+15)#define V (int)(120)int n,m;int f[M+2*V*V],g[2*V*V];int v[N],c[N],sum,mx;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> v[i];    for(int i=1; i<=n; i++)    {        cin >> c[i];        sum+=c[i]*v[i];        mx=max(mx,v[i]*v[i]);    }mx<<=1;    if(sum<m)return cout << -1,0;    memset(f,0x3f,sizeof(f));    memset(g,0x3f,sizeof(g));    f[0]=g[0]=0;    for(int i=1; i<=n; i++)        for(int j=v[i]; j<=mx; j++)            g[j]=min(g[j],g[j-v[i]]+1);    mx+=m;    for(int i=1; i<=n; i++)    {        for(int t=1; t<=c[i]; t<<=1)        {            for(int j=mx; j>=t*v[i]; j--)                f[j]=min(f[j],f[j-t*v[i]]+t);            c[i]-=t;        }        if(c[i])            for(int j=mx; j>=c[i]*v[i]; j--)                f[j]=min(f[j],f[j-c[i]*v[i]]+c[i]);    }    int res=INF;    for(int i=m; i<=mx; i++)        res=min(res,f[i]+g[i-m]);    cout << ((res==INF)?(-1):res) << endl;    return 0;}
]]>
@@ -5370,7 +5370,7 @@ /2022/05/26/luo-gu-p4170-cqoi2007-tu-se-ti-jie/ - 洛谷P4170 [CQOI2007]涂色 题解

题目链接:P4170 [CQOI2007]涂色

题意

假设你有一条长度为 $5$ 的木板,初始时没有涂过任何颜色。你希望把它的 $5$ 个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 $5$ 的字符串表示这个目标:$\texttt{RGBGR}$。

每次你可以把一段连续的木板涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木板涂成 $\texttt{RRRRR}$,第二次涂成 $\texttt{RGGGR}$,第三次涂成 $\texttt{RGBGR}$,达到目标。

用尽量少的涂色次数达到目标。

考虑区间dp

设 $dp[i][j]$ 表示涂区间 $[i,j]$ 的最小花费

当 $i=j$ 时,有

当 $i\ne j$ 时,

  • 若 $a[i]=a[j]$ ,此时只需要 $[i+1,j]$ 或 $[i,j-1]$ 首次涂的时候多涂一格即可

  • 若 $a[i]\ne a[j]$ , $a[i],a[j]$ 都可以尝试向内扩展,显然 $[i,j]$ 首次涂会涂两种颜色,考虑枚举其断点 $k$

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(55)int n,dp[N][N];char a[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (a+1);    n=strlen(a+1);    memset(dp,0x3f,sizeof(dp));    for(int i=1; i<=n; i++)dp[i][i]=1;    for(int len=2; len<=n; len++)        for(int i=1,j=i+len-1; i+len-1<=n; i++,j++)        {            if(a[i]==a[j])dp[i][j]=min(dp[i+1][j],dp[i][j-1]);            else for(int k=i; k<j; k++)                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);        }    cout << dp[1][n] << endl;    return 0;}
]]>
+ 洛谷P4170 [CQOI2007]涂色 题解

题目链接:P4170[CQOI2007]涂色

题意

假设你有一条长度为 \(5\)的木板,初始时没有涂过任何颜色。你希望把它的 \(5\)个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 \(5\) 的字符串表示这个目标:\(\texttt{RGBGR}\)。

每次你可以把一段连续的木板涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木板涂成\(\texttt{RRRRR}\),第二次涂成 \(\texttt{RGGGR}\),第三次涂成 \(\texttt{RGBGR}\),达到目标。

用尽量少的涂色次数达到目标。

考虑区间dp

\(dp[i][j]\) 表示涂区间 \([i,j]\) 的最小花费

\(i=j\) 时,有 \[dp[i][j]=1\] 当 \(i\ne j\) 时,

  • \(a[i]=a[j]\) ,此时只需要\([i+1,j]\)\([i,j-1]\) 首次涂的时候多涂一格即可 \[dp[i][j]=\min(dp[i+1][j],dp[i][j-1])\]

  • \(a[i]\ne a[j]\)\(a[i],a[j]\) 都可以尝试向内扩展,显然 \([i,j]\) 首次涂会涂两种颜色,考虑枚举其断点\(k\) \[dp[i][j]=\min(dp[i][j],dp[i][k]+dp[k+1][j])\]

代码:

#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <cmath>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(55)int n,dp[N][N];char a[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> (a+1);    n=strlen(a+1);    memset(dp,0x3f,sizeof(dp));    for(int i=1; i<=n; i++)dp[i][i]=1;    for(int len=2; len<=n; len++)        for(int i=1,j=i+len-1; i+len-1<=n; i++,j++)        {            if(a[i]==a[j])dp[i][j]=min(dp[i+1][j],dp[i][j-1]);            else for(int k=i; k<j; k++)                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);        }    cout << dp[1][n] << endl;    return 0;}
]]>
@@ -5397,7 +5397,7 @@ /2022/05/25/at3913-xor-tree-ti-jie/ - AT3913 XOR Tree 题解

题目链接:AT3913 XOR Tree

题意:给你一棵有 $N$ 个节点的树,节点编号从 $0$ 到 $N-1$ , 树边编号从 $1$ 到 $N-1$ 。第 $i$ 条边连接节点 $x_i$ 和 $y_i$ ,其权值为 $a_i$。

你可以对树执行任意次操作,每次操作选取一条链和一个非负整数 $x$,将链上的边的权值与 $x$ 异或成为该边的新权值。

问最少需要多少次操作,使得所有边的权值都为 $0$ 。

$2\le N \le10^5,0\le x_i,y_i \le N-1,0\le a_i \le 15$

首先可以想到:

边权不好处理,将其转化为点权

不过如果直接转化为点权似乎还是不好弄 q779想到这就没了

我们知道异或有自反性 $(p\oplus q \oplus p = q )$ 等性质

则考虑将点权定义为 $\text{val}(u) = \bigoplus\limits_{v_i \in V \land (u,v_i) \in E}w(u,v_i)$

也就是和每个结点直接相邻的边的边权的异或和

此时我们就将边权映射到了点权上(注意本题可以看作一棵无向无根树)

路径 $u-v$ 的修改转化为了 $u,v$ 两个点的修改

因为点权是异或和

所以同时修改路径上某个非端点结点所连的两条边,异或和不变(自反性)

首先,我们来证明几个结论


命题1 :当且仅当 $\sum_{u_i\in V} \text{val}(u_i)=0$ 时,$\sum_{l_i\in E} w(l_i) = 0$。

证明

  • 必要性证明:显然当边权和为 $0$ 时,点权和也为 $0$ 。

  • 充分性证明:设树上的结点数为 $n$ ,若叶子结点 $u$ 的点权为 $0$

    则与其相连的边的边权也为 $0$ ,因此这条边不会影响到 $u$ 的父结点,归纳可得


命题2:一个数字集合通过不断取两个数字并异或上同一个数有解,当且仅当这个数字集合异或和为 $0$ 。

证明:显然,因为每次修改不会改变该集合的异或和。


命题3:$\bigoplus\limits_{u_i\in V} \text{val}(u_i) = 0$ 。

证明:显然,每条边都有两个端点,根据自反性可得。


这样,我们就可以贪心地选择两个点权相同的结点消掉了

可是如果没有没有点权相同的结点了怎么办呢?

可以发现,此时最多有 $16$ 个互不相同的数字,而 $0$ 不用考虑,因此最多只有 $15$ 个数字

考虑状压dp

令 $dp_s$ 为将数字集合 $s$ 全部变为 $0$ 的最小操作数

显然至多要 $|s|-1$ 次操作(也就是原图中一条条边消)

直接去搞是有后效性的,所以考虑如何转移

我们可以枚举 $s$ 的一个子集 $k$ ,则有 $dp_s = \min(dp_s,dp_k+dp_{s/k})$

复杂度怎么样呢?

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,val[N],d[N],cnt[25],res,st,sxr[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1,u,v,w; i<n; i++)        cin >> u >> v >> w,val[u]^=w,val[v]^=w;    for(int i=0; i<n; i++) ++cnt[val[i]];    for(int i=1; i<=15; i++) res+=cnt[i]/2,st|=(cnt[i]&1)<<(i-1);    for(int i=1; i<(1<<15); i++) d[i]=d[i>>1]+(i&1);    for(int i=1; i<(1<<15); i++)--d[i];    for(int i=1; i<(1<<15); i++)        for(int j=0; j<15; j++)if((i>>j)&1)sxr[i]^=(j+1);    for(int i=1; i<(1<<15); i++)    {        if(sxr[i]!=0)continue;        for(int k=(i-1)&i; k; k=(k-1)&i)            if(sxr[k]==0)d[i]=min(d[i],d[k]+d[i^k]);    }    cout << res+d[st] << endl;    return 0;}

讲个笑话,写完自己读一遍差点没读懂

参考文献

[1] https://www.luogu.com.cn/blog/ShadowassIIXVIIIIV/solution-at3913

[2] https://www.luogu.com.cn/blog/xzc/solution-at3913

]]>
+ AT3913 XOR Tree 题解

题目链接:AT3913XOR Tree

题意:给你一棵有 \(N\) 个节点的树,节点编号从 \(0\) 到 \(N-1\) , 树边编号从 \(1\) 到 \(N-1\) 。第 \(i\) 条边连接节点 \(x_i\) 和 \(y_i\) ,其权值为 \(a_i\)。

你可以对树执行任意次操作,每次操作选取一条链和一个非负整数 \(x\),将链上的边的权值与 \(x\) 异或成为该边的新权值。

问最少需要多少次操作,使得所有边的权值都为 \(0\) 。

\(2\le N \le10^5,0\le x_i,y_i \le N-1,0\lea_i \le 15\)

首先可以想到:

边权不好处理,将其转化为点权

不过如果直接转化为点权似乎还是不好弄 q779想到这就没了

我们知道异或有自反性 \((p\oplus q \oplus p= q )\) 等性质

则考虑将点权定义为 \(\text{val}(u) =\bigoplus\limits_{v_i \in V \land (u,v_i) \in E}w(u,v_i)\)

也就是和每个结点直接相邻的边的边权的异或和

此时我们就将边权映射到了点权上(注意本题可以看作一棵无向无根树)

路径 \(u-v\) 的修改转化为了 \(u,v\) 两个点的修改

因为点权是异或和

所以同时修改路径上某个非端点结点所连的两条边,异或和不变(自反性)

首先,我们来证明几个结论


命题1 :当且仅当 \(\sum_{u_i\in V} \text{val}(u_i)=0\)时,\(\sum_{l_i\in E} w(l_i) =0\)

证明

  • 必要性证明:显然当边权和为 \(0\)时,点权和也为 \(0\)

  • 充分性证明:设树上的结点数为 \(n\) ,若叶子结点 \(u\) 的点权为 \(0\)

则与其相连的边的边权也为 \(0\),因此这条边不会影响到 \(u\)的父结点,归纳可得

\[\sum_{u_i\in V} \text{val}(u_i)=0 \Rightarrow \sum_{l_i\in E} w(l_i) = 0\]


命题2:一个数字集合通过不断取两个数字并异或上同一个数有解,当且仅当这个数字集合异或和为\(0\)

证明:显然,因为每次修改不会改变该集合的异或和。


命题3\(\bigoplus\limits_{u_i\in V} \text{val}(u_i) =0\) 。

证明:显然,每条边都有两个端点,根据自反性可得。


这样,我们就可以贪心地选择两个点权相同的结点消掉了

可是如果没有没有点权相同的结点了怎么办呢?

可以发现,此时最多有 \(16\)个互不相同的数字,而 \(0\)不用考虑,因此最多只有 \(15\)个数字

考虑状压dp

\(dp_s\) 为将数字集合 \(s\) 全部变为 \(0\) 的最小操作数

显然至多要 \(|s|-1\)次操作(也就是原图中一条条边消)

直接去搞是有后效性的,所以考虑如何转移

我们可以枚举 \(s\) 的一个子集 \(k\) ,则有 \(dp_s= \min(dp_s,dp_k+dp_{s/k})\)

复杂度怎么样呢? \[\begin{aligned}O\left( \sum\limits_{i=1}^{15}\operatorname{C}_{15}^i2^i + n\right)&= O\left((1+2)^{15}-1 + n\right) \\&= O(3^{15}+n) \\\\&= O(n)\end{aligned}\] 代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)int n,val[N],d[N],cnt[25],res,st,sxr[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1,u,v,w; i<n; i++)        cin >> u >> v >> w,val[u]^=w,val[v]^=w;    for(int i=0; i<n; i++) ++cnt[val[i]];    for(int i=1; i<=15; i++) res+=cnt[i]/2,st|=(cnt[i]&1)<<(i-1);    for(int i=1; i<(1<<15); i++) d[i]=d[i>>1]+(i&1);    for(int i=1; i<(1<<15); i++)--d[i];    for(int i=1; i<(1<<15); i++)        for(int j=0; j<15; j++)if((i>>j)&1)sxr[i]^=(j+1);    for(int i=1; i<(1<<15); i++)    {        if(sxr[i]!=0)continue;        for(int k=(i-1)&i; k; k=(k-1)&i)            if(sxr[k]==0)d[i]=min(d[i],d[k]+d[i^k]);    }    cout << res+d[st] << endl;    return 0;}

讲个笑话,写完自己读一遍差点没读懂

参考文献

[1] https://www.luogu.com.cn/blog/ShadowassIIXVIIIIV/solution-at3913

[2] https://www.luogu.com.cn/blog/xzc/solution-at3913

]]>
@@ -5424,7 +5424,7 @@ /2022/05/25/cf149d-coloring-brackets-ti-jie/ - CF149D Coloring Brackets 题解

题目链接:CF149D Coloring Brackets

题意:给出一个配对的括号序列(如 “$\texttt{(())()}$”、“$\texttt{()}$” 等,“$\texttt{)()}$”、“$\texttt{(()}$”是不符合要求的),对该序列按照以下方法染色。

  1. 一个括号可以染成红色、蓝色或者不染色。
  2. 一对匹配的括号需要且只能将其中一个染色。
  3. 相邻两个括号颜色不能相同(但都可以不染色)。

求符合条件的染色方案数,对 $1000000007$ 取模。

容易发现这个题目就是个区间dp

但是仅仅用 $dp[l][r]$ 并不能表示出两侧括号的颜色

因此设 $dp[l][r][i][j]$ 为区间 $[l,r]$ 内且左右两侧的颜色分别为 $i,j$ 时的方案数 (0为不染色,1为红,2为蓝)

考虑边界:当 $l+1=r$ 时,有

dp[l][r][0][1]=dp[l][r][0][2]=dp[l][r][1][0]=dp[l][r][2][0]=1;

一般地,

当 $l$ 与 $r$ 配对时,有

for(int i=0; i<=2; i++)    for(int j=0; j<=2; j++)    {        if(j!=1) dp[l][r][0][1]=(dp[l][r][0][1]+dp[l+1][r-1][i][j])%mod;        if(j!=2) dp[l][r][0][2]=(dp[l][r][0][2]+dp[l+1][r-1][i][j])%mod;        if(i!=1) dp[l][r][1][0]=(dp[l][r][1][0]+dp[l+1][r-1][i][j])%mod;        if(i!=2) dp[l][r][2][0]=(dp[l][r][2][0]+dp[l+1][r-1][i][j])%mod;    }

当 $l$ 与 $r$ 不配对时,有

以及

等情况,不过本质上是一样的

for(int i=0; i<=2; i++)    for(int j=0; j<=2; j++)        for(int p=0; p<=2; p++)            for(int q=0; q<=2; q++)            {                if(j==1&&p==1||j==2&&p==2)continue;                dp[l][r][i][q]=(dp[l][r][i][q]+dp[l][mch[l]][i][j]*dp[mch[l]+1][r][p][q]%mod)%mod;            }

值得注意的是,本题并不适用一般的dp顺序,而是采用记忆化搜索

因为本题要求配对的括号有且仅有一个被染色,而一般的顺序难以保证配对的括号在同一个区间内,即不能保证转移中用到的序列都是合法的,因此较为麻烦

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(705)const int mod=1e9+7;char a[N];int n,mch[N],stk[N],top,dp[N][N][3][3];void dfs(int l,int r){    if(l+1==r)dp[l][r][0][1]=dp[l][r][0][2]=dp[l][r][1][0]=dp[l][r][2][0]=1;    else if(mch[l]==r)    {        dfs(l+1,r-1);        for(int i=0; i<=2; i++)            for(int j=0; j<=2; j++)            {                if(j!=1) dp[l][r][0][1]=(dp[l][r][0][1]+dp[l+1][r-1][i][j])%mod;        if(j!=2) dp[l][r][0][2]=(dp[l][r][0][2]+dp[l+1][r-1][i][j])%mod;        if(i!=1) dp[l][r][1][0]=(dp[l][r][1][0]+dp[l+1][r-1][i][j])%mod;        if(i!=2) dp[l][r][2][0]=(dp[l][r][2][0]+dp[l+1][r-1][i][j])%mod;            }    }else    {        dfs(l,mch[l]);dfs(mch[l]+1,r);        for(int i=0; i<=2; i++)            for(int j=0; j<=2; j++)                for(int p=0; p<=2; p++)                    for(int q=0; q<=2; q++)                    {                        if(j==1&&p==1||j==2&&p==2)continue;                        dp[l][r][i][q]=(dp[l][r][i][q]+dp[l][mch[l]][i][j]*dp[mch[l]+1][r][p][q]%mod)%mod;                    }                            }}signed main(){    scanf("%s",(a+1));    n=strlen(a+1);    for(int i=1; i<=n; i++)    {        if(a[i]=='(')stk[++top]=i;        else mch[stk[top]]=i,mch[i]=stk[top],--top;    }    dfs(1,n);    int ans=0;    for(int i=0; i<=2; i++)        for(int j=0; j<=2; j++)            ans=(ans+dp[1][n][i][j])%mod;    printf("%lld\n",ans);    return 0;}

本文很多地方参考了这篇文章,写的非常好$✅$

]]>
+ CF149D Coloring Brackets题解

题目链接:CF149DColoring Brackets

题意:给出一个配对的括号序列(如 “\(\texttt{(())()}\)”、“\(\texttt{()}\)” 等,“\(\texttt{)()}\)”、“\(\texttt{(()}\)”是不符合要求的),对该序列按照以下方法染色。

  1. 一个括号可以染成红色、蓝色或者不染色。
  2. 一对匹配的括号需要且只能将其中一个染色。
  3. 相邻两个括号颜色不能相同(但都可以不染色)。

求符合条件的染色方案数,对 \(1000000007\) 取模。

容易发现这个题目就是个区间dp

但是仅仅用 \(dp[l][r]\)并不能表示出两侧括号的颜色

因此设 \(dp[l][r][i][j]\) 为区间\([l,r]\) 内且左右两侧的颜色分别为\(i,j\) 时的方案数(0为不染色,1为红,2为蓝)

考虑边界:当 \(l+1=r\) 时,有 \[\text{()}\]

dp[l][r][0][1]=dp[l][r][0][2]=dp[l][r][1][0]=dp[l][r][2][0]=1;

一般地,

\(l\)\(r\) 配对时,有 \[\text{(}\dots\text{)}\]

for(int i=0; i<=2; i++)    for(int j=0; j<=2; j++)    {        if(j!=1) dp[l][r][0][1]=(dp[l][r][0][1]+dp[l+1][r-1][i][j])%mod;        if(j!=2) dp[l][r][0][2]=(dp[l][r][0][2]+dp[l+1][r-1][i][j])%mod;        if(i!=1) dp[l][r][1][0]=(dp[l][r][1][0]+dp[l+1][r-1][i][j])%mod;        if(i!=2) dp[l][r][2][0]=(dp[l][r][2][0]+dp[l+1][r-1][i][j])%mod;    }

\(l\)\(r\) 不配对时,有 \[\text{(}\dots\text{)}\dots\text{(}\dots\text{)}\] 以及 \[\text{(}\dots\text{)}\text{(}\dots\text{)}\] 等情况,不过本质上是一样的

for(int i=0; i<=2; i++)    for(int j=0; j<=2; j++)        for(int p=0; p<=2; p++)            for(int q=0; q<=2; q++)            {                if(j==1&&p==1||j==2&&p==2)continue;                dp[l][r][i][q]=(dp[l][r][i][q]+dp[l][mch[l]][i][j]*dp[mch[l]+1][r][p][q]%mod)%mod;            }

值得注意的是,本题并不适用一般的dp顺序,而是采用记忆化搜索

因为本题要求配对的括号有且仅有一个被染色,而一般的顺序难以保证配对的括号在同一个区间内,即不能保证转移中用到的序列都是合法的,因此较为麻烦

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(705)const int mod=1e9+7;char a[N];int n,mch[N],stk[N],top,dp[N][N][3][3];void dfs(int l,int r){    if(l+1==r)dp[l][r][0][1]=dp[l][r][0][2]=dp[l][r][1][0]=dp[l][r][2][0]=1;    else if(mch[l]==r)    {        dfs(l+1,r-1);        for(int i=0; i<=2; i++)            for(int j=0; j<=2; j++)            {                if(j!=1) dp[l][r][0][1]=(dp[l][r][0][1]+dp[l+1][r-1][i][j])%mod;        if(j!=2) dp[l][r][0][2]=(dp[l][r][0][2]+dp[l+1][r-1][i][j])%mod;        if(i!=1) dp[l][r][1][0]=(dp[l][r][1][0]+dp[l+1][r-1][i][j])%mod;        if(i!=2) dp[l][r][2][0]=(dp[l][r][2][0]+dp[l+1][r-1][i][j])%mod;            }    }else    {        dfs(l,mch[l]);dfs(mch[l]+1,r);        for(int i=0; i<=2; i++)            for(int j=0; j<=2; j++)                for(int p=0; p<=2; p++)                    for(int q=0; q<=2; q++)                    {                        if(j==1&&p==1||j==2&&p==2)continue;                        dp[l][r][i][q]=(dp[l][r][i][q]+dp[l][mch[l]][i][j]*dp[mch[l]+1][r][p][q]%mod)%mod;                    }                            }}signed main(){    scanf("%s",(a+1));    n=strlen(a+1);    for(int i=1; i<=n; i++)    {        if(a[i]=='(')stk[++top]=i;        else mch[stk[top]]=i,mch[i]=stk[top],--top;    }    dfs(1,n);    int ans=0;    for(int i=0; i<=2; i++)        for(int j=0; j<=2; j++)            ans=(ans+dp[1][n][i][j])%mod;    printf("%lld\n",ans);    return 0;}

本文很多地方参考了这篇文章,写的非常好\(✅\)

]]>
@@ -5451,7 +5451,7 @@ /2022/05/25/cf19b-checkout-assistant-ti-jie/ - CF19B Checkout Assistant 题解

题目链接:CF19B Checkout Assistant

题意:Bob 来到一家现购自运商店,将 $n$ 件商品放入了他的手推车,然后到收银台付款。每件商品由它的价格 $c_i$ 和收银员扫描它的时间 $t_i$ 秒定义。

当收银员正在扫描某件商品时,Bob 可以从他的手推车中偷走某些其它商品。Bob 需要恰好 $1$ 秒来偷走一件商品。Bob 需要付给收银员的最少钱数是多少?请记住,收银员扫描商品的顺序由 Bob 决定。

这个题确实有点难想,因为它的背包容量和物品都是与 $n$ 有关的

观察题目的性质,我们需要带走所有的 $n$ 件物品

而第 $i$ 件物品可以花费 $c_i$ 的价格带走 $t_i+1$ 件物品

为什么是 $+1$ ?因为它本身也会被带走

题目要求的是带走这 $n$ 件物品的最小花费,可以看出这是一个01背包

但是背包的大小并不是很好确定

因为选择的物品它们的 $\sum (t_i+1)$ 很可能会超过 $n$

那么最多会超过多少呢?不难发现背包容量最多为 $n+\max(v_i)+1$

如果超过了这个数,那么当前的选择一定不是优的

由于我们并不知道最后会超过多少,我们就把每一种情况都算出来,然后取个 $\min$ 就好了

设 $dp[i][j]$ 表示只考虑前 $i$ 件物品,恰好装满 $j$ 时的最小花费,则有

滚动数组一下,就是

于是答案就是

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,dp[N*2],w[N],v[N],mx;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    memset(dp,0x3f,sizeof(dp));    dp[0]=0;    for(int i=1; i<=n; i++)    {        cin >> w[i] >> v[i];        ++w[i];        mx=max(mx,w[i]);    }mx+=n;    for(int i=1; i<=n; i++)        for(int j=mx; j>=w[i]; j--)            dp[j]=min(dp[j],dp[j-w[i]]+v[i]);    int res=INF;    for(int i=n; i<=mx; i++)        res=min(res,dp[i]);    cout << res << endl;    return 0;}

以上文章写于q779咖啡因摄入过度时,可能有些抽风

所以速溶咖啡不要一口闷

]]>
+ CF19B Checkout Assistant题解

题目链接:CF19BCheckout Assistant

题意:Bob 来到一家现购自运商店,将 \(n\)件商品放入了他的手推车,然后到收银台付款。每件商品由它的价格 \(c_i\) 和收银员扫描它的时间 \(t_i\) 秒定义。

当收银员正在扫描某件商品时,Bob可以从他的手推车中偷走某些其它商品。Bob 需要恰好 \(1\) 秒来偷走一件商品。Bob需要付给收银员的最少钱数是多少?请记住,收银员扫描商品的顺序由 Bob决定。

这个题确实有点难想,因为它的背包容量和物品都是与 \(n\) 有关的

观察题目的性质,我们需要带走所有的 \(n\) 件物品

而第 \(i\) 件物品可以花费 \(c_i\) 的价格带走 \(t_i+1\) 件物品

为什么是 \(+1\)?因为它本身也会被带走

题目要求的是带走这 \(n\)件物品的最小花费,可以看出这是一个01背包

但是背包的大小并不是很好确定

因为选择的物品它们的 \(\sum(t_i+1)\) 很可能会超过 \(n\)

那么最多会超过多少呢?不难发现背包容量最多为 \(n+\max(v_i)+1\)

如果超过了这个数,那么当前的选择一定不是优的

由于我们并不知道最后会超过多少,我们就把每一种情况都算出来,然后取个\(\min\) 就好了

\(dp[i][j]\) 表示只考虑前 \(i\) 件物品,恰好装满 \(j\) 时的最小花费,则有 \[dp[i][j]=\min(dp[i-1][j],dp[i-1][j-t_i-1]+c_i)\] 滚动数组一下,就是 \[dp[j]=\min(dp[j],dp[j-t_i-1]+c_i)\] 于是答案就是 \[\min_{n \le i \le n+\max(v_k)+1}(dp[i])\] 代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,dp[N*2],w[N],v[N],mx;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    memset(dp,0x3f,sizeof(dp));    dp[0]=0;    for(int i=1; i<=n; i++)    {        cin >> w[i] >> v[i];        ++w[i];        mx=max(mx,w[i]);    }mx+=n;    for(int i=1; i<=n; i++)        for(int j=mx; j>=w[i]; j--)            dp[j]=min(dp[j],dp[j-w[i]]+v[i]);    int res=INF;    for(int i=n; i<=mx; i++)        res=min(res,dp[i]);    cout << res << endl;    return 0;}

以上文章写于q779咖啡因摄入过度时,可能有些抽风

所以速溶咖啡不要一口闷

]]>
@@ -5474,11 +5474,11 @@ - CF294B Shaass and Bookshelf 题解 - - /2022/05/25/cf294b-shaass-and-bookshelf-ti-jie/ + CF219D Choosing Capital for Treeland 题解 + + /2022/05/25/cf219d-choosing-capital-for-treeland-ti-jie/ - CF294B Shaass and Bookshelf 题解

题目链接:CF294B Shaass and Bookshelf

题意:Shaass has $n$ books. He wants to make a bookshelf for all his books. He wants the bookshelf’s dimensions to be as small as possible. The thickness of the $i$ -th book is $t_{i}$ and its pages’ width is equal to $w_{i}$ . The thickness of each book is either $1$ or $2$ . All books have the same page heights.

Shaass puts the books on the bookshelf in the following way. First he selects some of the books and put them vertically. Then he puts the rest of the books horizontally above the vertical books. The sum of the widths of the horizontal books must be no more than the total thickness of the vertical books. A sample arrangement of the books is depicted in the figure.

Help Shaass to find the minimum total thickness of the vertical books.

$1\le n \le 100,1 \le t_i \le 2,1 \le w_i \le 100$

翻译一下就是说要求竖直摆放时厚度和 $\sum t_i$ 最小,同时不竖直摆放的书的宽度和 $\sum w_i$ 不超过厚度和 $\sum t_i$,高度均相等

注意到这个厚度很小,因此我们可以枚举一下厚度和

然后问题就转化为了,厚度和为 $j$ ,要求竖直摆放的那些书宽度和尽可能的大(这样放在上面的宽度和就小了)

于是就转化为了恰好装满的01背包问题

时间复杂度 $O(n^2)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(305)int n;int w[N],v[N],sumw,sumv,dp[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> w[i] >> v[i];        sumw+=w[i];sumv+=v[i];    }    memset(dp,0xc0,sizeof(dp));    dp[0]=0;    for(int i=1; i<=n; i++)        for(int j=sumw; j>=w[i]; j--)            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);    for(int i=1; i<=sumw; i++)    {        if(sumv-dp[i]<=i)        {            cout << i;            return 0;        }    }    return 0;}
]]>
+ CF219D ChoosingCapital for Treeland 题解

题目链接:CF219DChoosing Capital for Treeland

题意:Treeland国有n个城市,这n个城市连成了一颗树,有n-1条道路连接了所有城市。每条道路只能单向通行。现在政府需要决定选择哪个城市为首都。假如城市i成为了首都,那么为了使首都能到达任意一个城市,不得不将一些道路翻转方向,记翻转道路的条数为k。你的任务是找到所有满足k最小的首都。

随便找一个点 \(rt\)跑一遍dfs,求出这个 \(dp[rt]\) 的值

然后 \(dp[u] \to dp[v]\)的转移显然与 \((u,v)\) 有关

判断一下然后转移一下就没了

题外话:真的离了谱了,我换电脑之前写的这篇文章没发博客上,然后今天20220606花了2小时用爬虫和自己写的比对程序搞了半天才发现(恼

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)struct Edge{    int u,v,next;}e[N<<1];int n,dp[N];int pos=1,head[N],r[N<<1];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}int dfs1(int u,int f){    int res=0;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        if(!r[i])++res;        res+=dfs1(v,u);    }    return res;}void dfs2(int u,int f){    dp[u]+=dp[f];    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        if(r[i])++dp[v];        else --dp[v];        dfs2(v,u);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u,v);r[pos]=1;        addEdge(v,u);    }    dp[1]=dfs1(1,0);int mn=INF;    dfs2(1,0);    for(int i=1; i<=n; i++)        mn=min(mn,dp[i]);    cout << mn << endl;    for(int i=1; i<=n; i++)        if(dp[i]==mn)cout << i << " ";    return 0;}
]]>
@@ -5492,7 +5492,7 @@ 算法 - 数学 + DP @@ -5501,11 +5501,11 @@ - CF374C Inna and Dima 题解 - - /2022/05/25/cf374c-inna-and-dima-ti-jie/ + CF346B Lucky Common Subsequence 题解 + + /2022/05/25/cf346b-lucky-common-subsequence-ti-jie/ - CF374C Inna and Dima 题解

题目链接:CF374C Inna and Dima

题意

Inna和Dima在商店买了一张 n * m 的桌子,桌子的每一个单元格上都有一个字符,字符集为(“D”,”I”,”M”,”A”)。

Inna实在是太爱Dima了,所以她想在桌子上找出一条最长的路线,使得这条路线中包含尽可能多的Dima的名字(有序,即必须是连续的DIMA)。

Inna的路线选取方式会遵循以下原则:

1.一开始,Inna会在桌子上找到所有的”D”,把他们作为起点。

2.Inna会在当前格子的周围四个相邻的格子中选取一个格子,她会从”D”走到”I”,从”I”走到”M”,从”M”走到”A”。这样,她就认为自己走完了一次Dima的名字。

3.Inna每走完一个格子,就会在该格子四周相邻的四个格子中选一个走,即上下左右四个方向,(当然,她不能走出这张桌子)。Inna从不跳过一个字母。因此,从字母”D”开始,她总是走到字母”I”,从字母”I”开始,她总是走到字母”M”,从字母”M”开始,她总是走到字母”A”,从字母”A”开始,她总是走到字母”D”。

因为桌子的不同,Inna能走的Dima的名字的次数也不同,有时她可以走有限次,有时她可以走无限次,有时她甚至不能走完一次Dima的名字。

现在,她想知道她能否至少走完一次Dima的名字。若可以,她最多能走多少次或者她能否走无限次?

纯记忆化搜索

从每个D都跑一下

对于无限的情况,

只要判断下一个可行格子是不是已经visited了

注意记忆化搜索使用dis数组更新答案

不是靠每次dfs的step

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,m,ans;char a[N][N];int dx[5]={1,-1,0,0};int dy[5]={0,0,1,-1};int vis[N][N],dis[N][N];bool safe(int x,int y){return 1<=x&&x<=n&&1<=y&&y<=m;}void dfs(int x,int y){    if(dis[x][y])return;    vis[x][y]=1;    dis[x][y]=1;    for(int k=0; k<4; k++)    {        int tx=x+dx[k];        int ty=y+dy[k];        if(!safe(tx,ty))continue;        if(!(a[x][y]=='D'&&a[tx][ty]=='I'||           a[x][y]=='I'&&a[tx][ty]=='M'||           a[x][y]=='M'&&a[tx][ty]=='A'||           a[x][y]=='A'&&a[tx][ty]=='D'))continue;        if(vis[tx][ty]){cout << "Poor Inna!";exit(0);}        dfs(tx,ty);        dis[x][y]=max(dis[x][y],dis[tx][ty]+1);    }    vis[x][y]=0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> a[i]+1;    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)        {            if(a[i][j]=='D'&&!vis[i][j])            dfs(i,j),ans=max(ans,dis[i][j]);        }    ans/=4;    if(ans==0)cout << "Poor Dima!";    else cout << ans;    return 0;}
]]>
+ CF346B Lucky CommonSubsequence 题解

题目链接:CF346BLucky Common Subsequence

题意:通过删除一个字符串中的某些元素而不改变其余元素的顺序,可以派生出该字符串的一个子序列。例如,序列BDF是ABCDEF的子序列。 字符串的子字符串是该字符串的连续子序列。例如,BCD是ABCDEF的子串。你得到了两个字符串s1,s2和另一个名为virus的字符串。你的任务是找到s1和s2的最长公共子序列,同时不包含virus子字符串。

升级版的最长公共子序列问题(LCS问题)

考虑增加一维,称为与 \(c\)串匹配度,即

\(dp[i][j][k]\) 表示:

\(a\) 串匹配到第 \(i\) 位,\(b\) 串匹配到第 \(j\) 位最长公共子序列与 \(c\) 串匹配到第 \(k\) 位。

显然当 \(a[i]\ne b[j]\) 时,有 \(dp[i][j][k]=\max(dp[i][j][k],dp[i][j-1][k],dp[i-1][j][k])\)

\(a[i]=b[j]\) 时,直接去想 \(dp[i][j][k]\) 怎么转移不太好搞

考虑反过来想此时 \(dp[i-1][j-1][k]\)的贡献

显然此时在最长公共子序列上增加一个 \(a[i]\) ,可能会改变匹配的程度

设加上 \(a[i]\) 后的最长公共子序列与\(c\) 串匹配到第 \(p\) 位

则有 \(dp[i][j][p] =dp[i][j][p],dp[i-1][j-1][k]+a[i]\)

注意到这个 \(p\)可以通过跳fail数组得到,考虑KMP

则时间复杂度 \(O(n^3)\)

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)int la,lb,lc;char a[N],b[N],c[N];string dp[N][N][N];int fail[N];void proc(string &a,string b){    if(a.size()<b.size())a=b;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    scanf("%s%s%s",a+1,b+1,c+1);    la=strlen(a+1);lb=strlen(b+1);lc=strlen(c+1);    for(int i=2,j=0; i<=lc; i++)    {        while(j>0&&c[i]!=c[j+1])j=fail[j];        if(c[i]==c[j+1])++j;        fail[i]=j;    }    for(int i=1; i<=la; i++)        for(int j=1; j<=lb; j++)            for(int k=0; k<lc; k++)            {                if(a[i]==b[j])                {                    char ch=a[i];int p=k;                    while(p>0&&ch!=c[p+1])p=fail[p];                    if(ch==c[p+1])++p;                    proc(dp[i][j][p],dp[i-1][j-1][k]+ch);                }                proc(dp[i][j][k],dp[i][j-1][k]);                proc(dp[i][j][k],dp[i-1][j][k]);            }    string res;    for(int i=0; i<lc; i++)        proc(res,dp[la][lb][i]);    if(res.empty())puts("0");    else printf("%s\n",res.c_str());    return 0;}
]]>
@@ -5528,11 +5528,11 @@ - CF219D Choosing Capital for Treeland 题解 - - /2022/05/25/cf219d-choosing-capital-for-treeland-ti-jie/ + CF374C Inna and Dima 题解 + + /2022/05/25/cf374c-inna-and-dima-ti-jie/ - CF219D Choosing Capital for Treeland 题解

题目链接:CF219D Choosing Capital for Treeland

题意:Treeland国有n个城市,这n个城市连成了一颗树,有n-1条道路连接了所有城市。每条道路只能单向通行。现在政府需要决定选择哪个城市为首都。假如城市i成为了首都,那么为了使首都能到达任意一个城市,不得不将一些道路翻转方向,记翻转道路的条数为k。你的任务是找到所有满足k最小的首都。

随便找一个点 $rt$ 跑一遍dfs,求出这个 $dp[rt]$ 的值

然后 $dp[u] \to dp[v]$ 的转移显然与 $(u,v)$ 有关

判断一下然后转移一下就没了

题外话:真的离了谱了,我换电脑之前写的这篇文章没发博客上,然后今天20220606花了2小时用爬虫和自己写的比对程序搞了半天才发现(恼

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)struct Edge{    int u,v,next;}e[N<<1];int n,dp[N];int pos=1,head[N],r[N<<1];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}int dfs1(int u,int f){    int res=0;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        if(!r[i])++res;        res+=dfs1(v,u);    }    return res;}void dfs2(int u,int f){    dp[u]+=dp[f];    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        if(r[i])++dp[v];        else --dp[v];        dfs2(v,u);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u,v);r[pos]=1;        addEdge(v,u);    }    dp[1]=dfs1(1,0);int mn=INF;    dfs2(1,0);    for(int i=1; i<=n; i++)        mn=min(mn,dp[i]);    cout << mn << endl;    for(int i=1; i<=n; i++)        if(dp[i]==mn)cout << i << " ";    return 0;}
]]>
+ CF374C Inna and Dima 题解

题目链接:CF374CInna and Dima

题意

Inna和Dima在商店买了一张 n * m的桌子,桌子的每一个单元格上都有一个字符,字符集为("D","I","M","A")。

Inna实在是太爱Dima了,所以她想在桌子上找出一条最长的路线,使得这条路线中包含尽可能多的Dima的名字(有序,即必须是连续的DIMA)。

Inna的路线选取方式会遵循以下原则:

1.一开始,Inna会在桌子上找到所有的"D",把他们作为起点。

2.Inna会在当前格子的周围四个相邻的格子中选取一个格子,她会从"D"走到"I",从"I"走到"M",从"M"走到"A"。这样,她就认为自己走完了一次Dima的名字。

3.Inna每走完一个格子,就会在该格子四周相邻的四个格子中选一个走,即上下左右四个方向,(当然,她不能走出这张桌子)。Inna从不跳过一个字母。因此,从字母"D"开始,她总是走到字母"I",从字母"I"开始,她总是走到字母"M",从字母"M"开始,她总是走到字母"A",从字母"A"开始,她总是走到字母"D"。

因为桌子的不同,Inna能走的Dima的名字的次数也不同,有时她可以走有限次,有时她可以走无限次,有时她甚至不能走完一次Dima的名字。

现在,她想知道她能否至少走完一次Dima的名字。若可以,她最多能走多少次或者她能否走无限次?

纯记忆化搜索

从每个D都跑一下

对于无限的情况,

只要判断下一个可行格子是不是已经visited了

注意记忆化搜索使用dis数组更新答案

不是靠每次dfs的step

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)int n,m,ans;char a[N][N];int dx[5]={1,-1,0,0};int dy[5]={0,0,1,-1};int vis[N][N],dis[N][N];bool safe(int x,int y){return 1<=x&&x<=n&&1<=y&&y<=m;}void dfs(int x,int y){    if(dis[x][y])return;    vis[x][y]=1;    dis[x][y]=1;    for(int k=0; k<4; k++)    {        int tx=x+dx[k];        int ty=y+dy[k];        if(!safe(tx,ty))continue;        if(!(a[x][y]=='D'&&a[tx][ty]=='I'||           a[x][y]=='I'&&a[tx][ty]=='M'||           a[x][y]=='M'&&a[tx][ty]=='A'||           a[x][y]=='A'&&a[tx][ty]=='D'))continue;        if(vis[tx][ty]){cout << "Poor Inna!";exit(0);}        dfs(tx,ty);        dis[x][y]=max(dis[x][y],dis[tx][ty]+1);    }    vis[x][y]=0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> a[i]+1;    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)        {            if(a[i][j]=='D'&&!vis[i][j])            dfs(i,j),ans=max(ans,dis[i][j]);        }    ans/=4;    if(ans==0)cout << "Poor Dima!";    else cout << ans;    return 0;}
]]>
@@ -5555,11 +5555,11 @@ - CF41D Pawn 题解 - - /2022/05/25/cf41d-pawn-ti-jie/ + CF294B Shaass and Bookshelf 题解 + + /2022/05/25/cf294b-shaass-and-bookshelf-ti-jie/ - CF41D Pawn 题解

题目链接:CF41D Pawn

题意:国际象棋棋盘最底行站了一个兵。 它只有两种行动方式: 向上左或向上右走。 它可以选择从最低行哪个节点开始他的旅程。 每个格子上有0-9颗豌豆,而士兵想移动到最上一行并且积累到尽可能多的豌豆。同时,因为这个士兵必须把豌豆平均分给自己和他的k个兄弟,他所收集到的豌豆必须是k+1的倍数。请找到他可以收集到的最多豌豆,并确定他的操作序列。

规定士兵不能手动扔出豌豆,并且他必须捡起所到达的每一个格子的所有豌豆。

输入格式: 第一行三个整数 $n,m,k(2 \le n,m \le 100, 0 \le k \le 10)$ 行数、列数、士兵的兄弟们。 接下来一个 $n \times m$ 的矩阵,每个元素均是0-9的整数(不空格),描述该格的豌豆。第一行被认为是最上一行,最后一行被认为是最下一行。

输出格式:

如果不能收集到k+1倍数的豌豆,输出-1. 否则,输出第一行一个整数,为最多豌豆数;第二行一个整数,为士兵开始移动的位置;第三行包括n-1个字母L 或 R,表示士兵的行动序列。

如果有多种收集到相同且是k+1倍数数量的豌豆,你可以任意输出一种方案。

令 $dp[i][j][k]$ 为走到第 $i$ 行第 $j$ 列且恰好取了 $k$ 个的方案数

这里不用求方案数,就用bool即可

不习惯从下往上跑就翻转一下

答案就是所有的最后一行所有的 $dp[1][j][c] \bold{\ s.t. \ } k\mid c$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)int n,m,c;int a[N][N];bool dp[N][N][N*10];char d[N][N][N*10];void print(int i,int j,int k){    if(i==1)    {        printf("%lld\n",j);        return;    }    char di=d[i][j][k];    if(di=='L')print(i-1,j+1,k-a[i][j]);    else print(i-1,j-1,k-a[i][j]);    putchar(d[i][j][k]);}signed main(){    scanf("%lld%lld%lld",&n,&m,&c);    for(int i=n; i>=1; i--)        for(int j=1; j<=m; j++)            scanf("%1lld",&a[i][j]);    for(int i=1; i<=m; i++)        dp[1][i][a[1][i]]=1;    for(int i=2; i<=n; i++)        for(int j=1; j<=m; j++)            for(int k=9*n; k>=a[i][j]; --k)            {                if(j-1>=1&&dp[i-1][j-1][k-a[i][j]])                {                    dp[i][j][k]|=dp[i-1][j-1][k-a[i][j]];                    d[i][j][k]='R';                }                if(j+1<=m&&dp[i-1][j+1][k-a[i][j]])                {                    dp[i][j][k]|=dp[i-1][j+1][k-a[i][j]];                    d[i][j][k]='L';                }            }    for(int k=(9*n/(c+1))*(c+1); k>=0; k-=c+1)        for(int i=1; i<=m; i++)            if(dp[n][i][k])            {                printf("%lld\n",k);                print(n,i,k);                return 0;            }    puts("-1");    return 0;}
]]>
+ CF294B Shaass and Bookshelf题解

题目链接:CF294BShaass and Bookshelf

题意:Shaass has \(n\) books. He wants to make a bookshelf forall his books. He wants the bookshelf's dimensions to be as small aspossible. The thickness of the \(i\)-th book is \(t_{i}\) and its pages'width is equal to \(w_{i}\) . Thethickness of each book is either \(1\)or \(2\) . All books have the same pageheights.

Shaass puts the books on the bookshelf in the following way. First heselects some of the books and put them vertically. Then he puts the restof the books horizontally above the vertical books. The sum of thewidths of the horizontal books must be no more than the total thicknessof the vertical books. A sample arrangement of the books is depicted inthe figure.

Help Shaass to find the minimum total thickness of the verticalbooks.

\(1\le n \le 100,1 \le t_i \le 2,1 \le w_i\le 100\)

翻译一下就是说要求竖直摆放时厚度和 \(\sumt_i\) 最小,同时不竖直摆放的书的宽度和 \(\sum w_i\) 不超过厚度和 \(\sum t_i\),高度均相等

注意到这个厚度很小,因此我们可以枚举一下厚度和

然后问题就转化为了,厚度和为 \(j\),要求竖直摆放的那些书宽度和尽可能的大(这样放在上面的宽度和就小了)

于是就转化为了恰好装满的01背包问题

时间复杂度 \(O(n^2)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(305)int n;int w[N],v[N],sumw,sumv,dp[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> w[i] >> v[i];        sumw+=w[i];sumv+=v[i];    }    memset(dp,0xc0,sizeof(dp));    dp[0]=0;    for(int i=1; i<=n; i++)        for(int j=sumw; j>=w[i]; j--)            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);    for(int i=1; i<=sumw; i++)    {        if(sumv-dp[i]<=i)        {            cout << i;            return 0;        }    }    return 0;}
]]>
@@ -5573,7 +5573,7 @@ 算法 - DP + 数学 @@ -5582,11 +5582,11 @@ - CF346B Lucky Common Subsequence 题解 - - /2022/05/25/cf346b-lucky-common-subsequence-ti-jie/ + CF41D Pawn 题解 + + /2022/05/25/cf41d-pawn-ti-jie/ - CF346B Lucky Common Subsequence 题解

题目链接:CF346B Lucky Common Subsequence

题意:通过删除一个字符串中的某些元素而不改变其余元素的顺序,可以派生出该字符串的一个子序列。 例如,序列BDF是ABCDEF的子序列。 字符串的子字符串是该字符串的连续子序列。 例如,BCD是ABCDEF的子串。 你得到了两个字符串s1,s2和另一个名为virus的字符串。你的任务是找到s1和s2的最长公共子序列,同时不包含virus子字符串。

升级版的最长公共子序列问题(LCS问题)

考虑增加一维,称为与 $c$ 串匹配度,即

$dp[i][j][k]$ 表示:

$a$ 串匹配到第 $i$ 位,$b$ 串匹配到第 $j$ 位最长公共子序列与 $c$ 串匹配到第 $k$ 位。

显然当 $a[i]\ne b[j]$ 时,有 $dp[i][j][k]=\max(dp[i][j][k],dp[i][j-1][k],dp[i-1][j][k])$

当 $a[i]=b[j]$ 时,直接去想 $dp[i][j][k]$ 怎么转移不太好搞

考虑反过来想此时 $dp[i-1][j-1][k]$ 的贡献

显然此时在最长公共子序列上增加一个 $a[i]$ ,可能会改变匹配的程度

设加上 $a[i]$ 后的最长公共子序列与 $c$ 串匹配到第 $p$ 位

则有 $dp[i][j][p] = dp[i][j][p],dp[i-1][j-1][k]+a[i]$

注意到这个 $p$ 可以通过跳fail数组得到,考虑KMP

则时间复杂度 $O(n^3)$

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)int la,lb,lc;char a[N],b[N],c[N];string dp[N][N][N];int fail[N];void proc(string &a,string b){    if(a.size()<b.size())a=b;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    scanf("%s%s%s",a+1,b+1,c+1);    la=strlen(a+1);lb=strlen(b+1);lc=strlen(c+1);    for(int i=2,j=0; i<=lc; i++)    {        while(j>0&&c[i]!=c[j+1])j=fail[j];        if(c[i]==c[j+1])++j;        fail[i]=j;    }    for(int i=1; i<=la; i++)        for(int j=1; j<=lb; j++)            for(int k=0; k<lc; k++)            {                if(a[i]==b[j])                {                    char ch=a[i];int p=k;                    while(p>0&&ch!=c[p+1])p=fail[p];                    if(ch==c[p+1])++p;                    proc(dp[i][j][p],dp[i-1][j-1][k]+ch);                }                proc(dp[i][j][k],dp[i][j-1][k]);                proc(dp[i][j][k],dp[i-1][j][k]);            }    string res;    for(int i=0; i<lc; i++)        proc(res,dp[la][lb][i]);    if(res.empty())puts("0");    else printf("%s\n",res.c_str());    return 0;}
]]>
+ CF41D Pawn 题解

题目链接:CF41DPawn

题意:国际象棋棋盘最底行站了一个兵。它只有两种行动方式: 向上左或向上右走。它可以选择从最低行哪个节点开始他的旅程。每个格子上有0-9颗豌豆,而士兵想移动到最上一行并且积累到尽可能多的豌豆。同时,因为这个士兵必须把豌豆平均分给自己和他的k个兄弟,他所收集到的豌豆必须是k+1的倍数。请找到他可以收集到的最多豌豆,并确定他的操作序列。

规定士兵不能手动扔出豌豆,并且他必须捡起所到达的每一个格子的所有豌豆。

输入格式: 第一行三个整数 \(n,m,k(2 \le n,m\le 100, 0 \le k \le 10)\) 行数、列数、士兵的兄弟们。 接下来一个\(n \times m\)的矩阵,每个元素均是0-9的整数(不空格),描述该格的豌豆。第一行被认为是最上一行,最后一行被认为是最下一行。

输出格式:

如果不能收集到k+1倍数的豌豆,输出-1.否则,输出第一行一个整数,为最多豌豆数;第二行一个整数,为士兵开始移动的位置;第三行包括n-1个字母L或 R,表示士兵的行动序列。

如果有多种收集到相同且是k+1倍数数量的豌豆,你可以任意输出一种方案。

\(dp[i][j][k]\) 为走到第 \(i\) 行第 \(j\) 列且恰好取了 \(k\) 个的方案数

这里不用求方案数,就用bool即可

不习惯从下往上跑就翻转一下

答案就是所有的最后一行所有的 \(dp[1][j][c]\bold{\ s.t. \ } k\mid c\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(115)int n,m,c;int a[N][N];bool dp[N][N][N*10];char d[N][N][N*10];void print(int i,int j,int k){    if(i==1)    {        printf("%lld\n",j);        return;    }    char di=d[i][j][k];    if(di=='L')print(i-1,j+1,k-a[i][j]);    else print(i-1,j-1,k-a[i][j]);    putchar(d[i][j][k]);}signed main(){    scanf("%lld%lld%lld",&n,&m,&c);    for(int i=n; i>=1; i--)        for(int j=1; j<=m; j++)            scanf("%1lld",&a[i][j]);    for(int i=1; i<=m; i++)        dp[1][i][a[1][i]]=1;    for(int i=2; i<=n; i++)        for(int j=1; j<=m; j++)            for(int k=9*n; k>=a[i][j]; --k)            {                if(j-1>=1&&dp[i-1][j-1][k-a[i][j]])                {                    dp[i][j][k]|=dp[i-1][j-1][k-a[i][j]];                    d[i][j][k]='R';                }                if(j+1<=m&&dp[i-1][j+1][k-a[i][j]])                {                    dp[i][j][k]|=dp[i-1][j+1][k-a[i][j]];                    d[i][j][k]='L';                }            }    for(int k=(9*n/(c+1))*(c+1); k>=0; k-=c+1)        for(int i=1; i<=m; i++)            if(dp[n][i][k])            {                printf("%lld\n",k);                print(n,i,k);                return 0;            }    puts("-1");    return 0;}
]]>
@@ -5613,7 +5613,7 @@ /2022/05/25/cf526b-om-nom-and-dark-park-ti-jie/ - CF526B Om Nom and Dark Park 题解

题目链接:CF526B Om Nom and Dark Park

题意:给定 $2^{n+1}-1$ 个结点的满二叉树,求保证根到每一个叶子节点的路径权值和相等的情况下,增加权值的最小值

显然不能从根节点开始修改权值,不然也不知道改成什么才行

因此主要思路是从叶子结点向上修改权值,直到根结点为止

方便起见,用 a[x]表示结点 $x$ 到它的父结点的那条边的权值

下文中提到的路径长指一个结点到它叶子结点的最终的距离

可以发现,对于一个待修改结点 $x$ ,假设我们已经知道了它的左右子结点的路径长ls,rs(已经被修改了的路径长,因为是从叶子结点向上修改的)

则结点 $x$ 到左子结点修改前的路径长为a[2*x]+ls,同理右子结点a[2*x+1]+rs

保证根到叶子节点路径权值和相等,因此要把上面这两个路径长中较小的那个补成较大的那个,这样花费肯定是最小的,又保证了以这个结点为根的子树满足题目要求

于是从下往上传递,过程中统计答案

时间复杂度 $O(2^n)$

主要代码很短

#define MAXN (int)(2e6+5)int n,ans,a[MAXN],MX;int dfs(int x){if(x>MX)return 0;int ls=dfs(x<<1),rs=dfs(x<<1|1);ans+=abs(ls+a[x<<1]-rs-a[x<<1|1]); // 每次修改的花费return max(a[x<<1]+ls,a[x<<1|1]+rs);}signed main(){read(n);MX=(1<<(n+1))-1;for(int i=2; i<=MX; i++)read(a[i]);dfs(1);write(ans);return 0;}

题外话

这题面好可爱 qwq

]]>
+ CF526B Om Nom and Dark Park题解

题目链接:CF526B OmNom and Dark Park

题意:给定 \(2^{n+1}-1\)个结点的满二叉树,求保证根到每一个叶子节点的路径权值和相等的情况下,增加权值的最小值

显然不能从根节点开始修改权值,不然也不知道改成什么才行

因此主要思路是从叶子结点向上修改权值,直到根结点为止

方便起见,用 a[x]表示结点 \(x\) 到它的父结点的那条边的权值

下文中提到的路径长指一个结点到它叶子结点的最终的距离

可以发现,对于一个待修改结点 \(x\),假设我们已经知道了它的左右子结点的路径长ls,rs(已经被修改了的路径长,因为是从叶子结点向上修改的)

则结点 \(x\)到左子结点修改前的路径长为a[2*x]+ls,同理右子结点a[2*x+1]+rs

保证根到叶子节点路径权值和相等,因此要把上面这两个路径长中较小的那个补成较大的那个,这样花费肯定是最小的,又保证了以这个结点为根的子树满足题目要求

于是从下往上传递,过程中统计答案

时间复杂度 \(O(2^n)\)

主要代码很短

#define MAXN (int)(2e6+5)int n,ans,a[MAXN],MX;int dfs(int x){if(x>MX)return 0;int ls=dfs(x<<1),rs=dfs(x<<1|1);ans+=abs(ls+a[x<<1]-rs-a[x<<1|1]); // 每次修改的花费return max(a[x<<1]+ls,a[x<<1|1]+rs);}signed main(){read(n);MX=(1<<(n+1))-1;for(int i=2; i<=MX; i++)read(a[i]);dfs(1);write(ans);return 0;}

题外话

这题面好可爱 qwq

]]>
@@ -5640,7 +5640,7 @@ /2022/05/25/luo-gu-p1108-di-jie-gou-mai-ti-jie/ - 洛谷P1108 低价购买 题解

题目链接:P1108 低价购买

题意:“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购买它。买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数。你将被给出一段时间内一支股票每天的出售价( $2^{16}$ 范围内的正整数),你可以选择在哪些天购买这支股票。每次购买都必须遵循“低价购买;再低价购买”的原则。写一个程序计算最大购买次数。

这里是某支股票的价格清单:

日期 1,2,3,4,5,6,7,8,9,10,11,12

价格 68,69,54,64,68,64,70,67,78,62,98,87

最优秀的投资者可以购买最多4次股票,可行方案中的一种是:

日期 2,5,6,10

价格 69,68,64,62

输入格式

第1行: $N(1 \le N \le 5000)$ ,股票发行天数

第2行: $N$个数,是每天的股票价格。

输出格式

两个数:
最大购买次数和拥有最大购买次数的方案数( $\le 2^{31}$ )当二种方案“看起来一样”时(就是说它们构成的价格队列一样的时候),这2种方案被认为是相同的。

第一问就是纯纯的最长下降子序列

就看第二问吧

显然我们不能把每个子序列记下来,然后暴力比对

注意到对于每个 $dp[i]$ ,

我们并不知道除了当前最后一位 $a[i]$ 以外的任何子序列的元素

但是这足以判断是否重复计算了

详见代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e3+15)int n,a[N],dp[N],sum[N];// dp 求的是最长下降子序列长度// sum 求的是方案数signed main(){    read(n);    for(int i=1; i<=n; i++)        read(a[i]);    for(int i=1; i<=n; i++)    {        dp[i]=1;        for(int j=1; j<i; j++)            if(a[i]<a[j])                dp[i]=max(dp[i],dp[j]+1);        for(int j=1; j<i; j++)        {            if(dp[i]==dp[j]&&a[i]==a[j]) // 重复的不再计算                sum[j]=0; // 清零,防止i后面的又把它算进去了            if(dp[i]==dp[j]+1&&a[i]<a[j]) // 继承子问题的方案数                sum[i]+=sum[j];        }        if(!sum[i])sum[i]=1;    }    int mx=*max_element(dp+1,dp+1+n),res=0;    write(mx);pc(' ');    for(int i=1; i<=n; i++)        if(dp[i]==mx)res+=sum[i]; // 根据题意。    write(res);pc('\n');    return 0;}

参考文献

[1] https://www.luogu.com.cn/blog/wjyyy/solution-p1108

]]>
+ 洛谷P1108 低价购买 题解

题目链接:P1108低价购买

题意:“低价购买”这条建议是在奶牛股票市场取得成功的一半规则。要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买;再低价购买”。每次你购买一支股票,你必须用低于你上次购买它的价格购买它。买的次数越多越好!你的目标是在遵循以上建议的前提下,求你最多能购买股票的次数。你将被给出一段时间内一支股票每天的出售价(\(2^{16}\)范围内的正整数),你可以选择在哪些天购买这支股票。每次购买都必须遵循“低价购买;再低价购买”的原则。写一个程序计算最大购买次数。

这里是某支股票的价格清单:

日期 1,2,3,4,5,6,7,8,9,10,11,12

价格 68,69,54,64,68,64,70,67,78,62,98,87

最优秀的投资者可以购买最多4次股票,可行方案中的一种是:

日期 2,5,6,10

价格 69,68,64,62

输入格式

第1行: \(N(1 \le N \le 5000)\),股票发行天数

第2行: \(N\)个数,是每天的股票价格。

输出格式

两个数: 最大购买次数和拥有最大购买次数的方案数( \(\le 2^{31}\))当二种方案“看起来一样”时(就是说它们构成的价格队列一样的时候),这2种方案被认为是相同的。

第一问就是纯纯的最长下降子序列

就看第二问吧

显然我们不能把每个子序列记下来,然后暴力比对

注意到对于每个 \(dp[i]\)

我们并不知道除了当前最后一位 \(a[i]\) 以外的任何子序列的元素

但是这足以判断是否重复计算了

详见代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e3+15)int n,a[N],dp[N],sum[N];// dp 求的是最长下降子序列长度// sum 求的是方案数signed main(){    read(n);    for(int i=1; i<=n; i++)        read(a[i]);    for(int i=1; i<=n; i++)    {        dp[i]=1;        for(int j=1; j<i; j++)            if(a[i]<a[j])                dp[i]=max(dp[i],dp[j]+1);        for(int j=1; j<i; j++)        {            if(dp[i]==dp[j]&&a[i]==a[j]) // 重复的不再计算                sum[j]=0; // 清零,防止i后面的又把它算进去了            if(dp[i]==dp[j]+1&&a[i]<a[j]) // 继承子问题的方案数                sum[i]+=sum[j];        }        if(!sum[i])sum[i]=1;    }    int mx=*max_element(dp+1,dp+1+n),res=0;    write(mx);pc(' ');    for(int i=1; i<=n; i++)        if(dp[i]==mx)res+=sum[i]; // 根据题意。    write(res);pc('\n');    return 0;}

参考文献

[1] https://www.luogu.com.cn/blog/wjyyy/solution-p1108

]]>
@@ -5663,11 +5663,11 @@ - 洛谷P1171 售货员的难题 题解 - - /2022/05/25/luo-gu-p1171-shou-huo-yuan-de-nan-ti-ti-jie/ + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + + /2022/05/25/luo-gu-p1156-la-ji-xian-jing-ti-jie-qian-tan-shua-biao-fa-yu-tian-biao-fa/ - 洛谷P1171 售货员的难题 题解

题目链接:P1171 售货员的难题

题意:TSP问题。

某乡有$n$个村庄($1<n \le 20$),有一个售货员,他要到各个村庄去售货,各村庄之间的路程$s(0<s<1000)$是已知的,且$A$村到$B$村与$B$村到$A$村的路大多不同。为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为$1$,他不知道选择什么样的路线才能使所走的路程最短。请你帮他选择一条最短的路。

题目应该保证了有解。

经典的状压dp

设 $dp[i][j]$ 表示目前走过的结点的状态为 $i$ ,现在在 $j$ 结点

则有

dp[i][j]=min(dp[i][j],dp[i^(1<<(j-1))][k]+g[k][j])

代码:

#include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)((1<<20)+2)int n,g[25][25],dp[N][22];void Min(int &a,int b){if(b<a)a=b;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(dp,0x3f,sizeof(dp));    cin >> n;dp[1][1]=0;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            cin >> g[i][j];    for(int i=1; i<1<<n; i++)    for(int j=1; j<=n; j++)     if( (i>>(j-1))&1 )        for(int k=1; k<=n; k++)        if( k!=j && ((i>>(k-1))&1) )            Min(dp[i][j],dp[i^(1<<(j-1))][k]+g[k][j]);    int mn=0x3f3f3f3f;    for(int i=1; i<=n; i++)        Min(mn,dp[(1<<n)-1][i]+g[i][1]);    cout << mn << endl;    return 0;}
]]>
+ 洛谷P1156 垃圾陷阱题解&浅谈刷表法与填表法

填表法:就是一般的动态规划,当前点的状态,可以直接用状态方程,根据之前点的状态推导出来。

刷表法:由当前点的状态,更新其他点的状态。需要注意:只用当每个状态所依赖的状态对它的影响相互独立。

下面会用一道题目给出两种写法的区别


题目链接:P1156垃圾陷阱

题意吃垃圾就完了

卡门――农夫约翰极其珍视的一条Holsteins奶牛――已经落了到“垃圾井”中。“垃圾井”是农夫们扔垃圾的地方,它的深度为\(D(2 \le D \le 100)\)英尺。

卡门想把垃圾堆起来,等到堆得与井同样高时,她就能逃出井外了。另外,卡门可以通过吃一些垃圾来维持自己的生命。

每个垃圾都可以用来吃或堆放,并且堆放垃圾不用花费卡门的时间。

假设卡门预先知道了每个垃圾扔下的时间\(t(0< t \le1000)\),以及每个垃圾堆放的高度\(h(1\le h \le 25\))和吃进该垃圾能维持生命的时间\(f(1 \le f \le30)\),要求出卡门最早能逃出井外的时间,假设卡门当前体内有足够持续\(10\)小时的能量,如果卡门\(10\)小时内没有进食,卡门就将饿死。

注:如果能量为 \(0\)时恰好有垃圾可以吃,则不会饿死

注意到对于每个垃圾,只有吃掉和堆起来两种方法

\(dp[i][j]\) 表示只考虑前 \(i\) 个垃圾,堆到高度 \(j\) 时,最多可以活到什么时候

容易推出方程 \[dp[i][j]=\max(dp[i-1][j]+v_i,dp[i-1][j-h_i])\] 1.填表法

求出dp数组以后扫一遍就好了,细节较多(其实好像可以滚动数组,但是懒qwq)

注意这里垃圾的高度可能会超过 \(m\),但是数据好像没卡这个东西

处理方法就是把 \(m+\max(h_i)\)都dp一下

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)struct node{    int h,v,t;}a[N];int n,m,dp[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(dp,0xc0,sizeof(dp));    dp[0][0]=10;    cin >> m >> n;    for(int i=1; i<=n; i++)        cin >> a[i].t >> a[i].v >> a[i].h;    sort(a+1,a+1+n,[](node a,node b){return a.t<b.t;});    for(int i=1; i<=n; i++)        for(int j=0; j<=m+25; j++)        {            if(dp[i-1][j]>=a[i].t)                dp[i][j]=max(dp[i][j],dp[i-1][j]+a[i].v);            if(j>=a[i].h&&dp[i-1][j-a[i].h]>=a[i].t)                dp[i][j]=max(dp[i][j],dp[i-1][j-a[i].h]);        }    int res1=-1,res2=-1;    for(int i=1; i<=n; i++)    {        for(int j=0; j<=m+25; j++)        {            if(dp[i][j]>=a[i].t)                res1=max(res1,j);            res2=max(res2,dp[i][j]);        }        if(res1>=m){cout << a[i].t << endl;return 0;}    }    cout << res2 << endl;    return 0;}

2.刷表法

这里可以滚动数组了,不过要两个

否则会导致数据更新错误

当然也可以不用滚动数组位运算那么可爱为什么不写她呐?对不对?

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)struct node{    int h,v,t;}a[N];int n,m,dp[2][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(dp,0xc0,sizeof(dp));    dp[0][0]=10;    cin >> m >> n;    for(int i=1; i<=n; i++)        cin >> a[i].t >> a[i].v >> a[i].h;    sort(a+1,a+1+n,[](node a,node b){return a.t<b.t;});    int res=-1;    for(int i=1; i<=n; i++)    {        int cur=i&1,pre=cur^1;        memset(dp[cur],0xc0,sizeof(dp[cur]));        for(int j=m; j>=0; j--) // 这里就不用m+25了,因为只要超出了直接就输出了        {            if(dp[pre][j]<a[i].t)continue;            if(j+a[i].h>=m)return cout << a[i].t << endl,0;            dp[cur][j+a[i].h]=max(dp[cur][j+a[i].h],dp[pre][j]);            dp[cur][j]=max(dp[cur][j],dp[pre][j]+a[i].v);        }        res=max(res,dp[cur][0]);    }    cout << res << endl;    return 0;}

哼哼,我才不会说我是在考数学的时候推出来的dp方程

转载请说明出处

]]>
@@ -5690,11 +5690,11 @@ - 洛谷P1282 多米诺骨牌 题解 - - /2022/05/25/luo-gu-p1282-duo-mi-nuo-gu-pai-ti-jie/ + 洛谷P1171 售货员的难题 题解 + + /2022/05/25/luo-gu-p1171-shou-huo-yuan-de-nan-ti-ti-jie/ - 洛谷P1282 多米诺骨牌 题解

题目链接:P1282 多米诺骨牌

题意

多米诺骨牌由上下 $2$ 个方块组成,每个方块中有 $1\sim6$ 个点。现有排成行的上方块中点数之和记为 $S_1$,下方块中点数之和记为 $S_2$,它们的差为 $\left|S_1-S_2\right|$。如图,$S_1=6+1+1+1=9$,$S_2=1+5+3+2=11$,$\left|S_1-S_2\right|=2$。每个多米诺骨牌可以旋转 $180°$,使得上下两个方块互换位置。请你计算最少旋转多少次才能使多米诺骨牌上下 $2$ 行点数之差达到最小。

对于图中的例子,只要将最后一个多米诺骨牌旋转 $180°$,即可使上下 $2$ 行点数之差为 $0$。

注意到我们其实并不关心 $S_1,S_2$ 的值,而是关心他们的差值

这个绝对值似乎不好处理,其实只要把正负的情况取个min就可以了

设 $dp[i][j]$ 表示只考虑前 $i$ 个骨牌,差值为 $j$ 时的最小翻转数

这里的 $j$ 定义为 $S_1-S_2$ 或者 $S_2-S_1$ 其实没啥区别,不过下面代码为前者

不难发现

注意到差值最大 $5000$ ,为了避免乱七八糟讨论,就直接循环 $-5000 \sim 5000$ 就好啦

差值可能为负所以数组整体平移一下,然后整个填表法滚一滚啥的就好了

这里好像刷表法写起来不太安全就不写了,虽然没啥问题,但是不算简洁吧(逃

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)const int bd=5e3+15;int n,dp[2][20*N],a[N],b[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(dp,0x3f,sizeof(dp));    cin >> n;dp[0][bd]=0;    for(int i=1; i<=n; i++)        cin >> a[i] >> b[i];    for(int i=1; i<=n; i++)    {        int cur=i&1,pre=cur^1;        memset(dp[cur],0x3f,sizeof(dp[cur]));        for(int j=-5000; j<=5000; j++)            dp[cur][j+bd]=min(dp[pre][j+a[i]-b[i]+bd]+1,dp[pre][j-a[i]+b[i]+bd]);    }    for(int i=0; i<=5000; i++)    {        int t=min(dp[n&1][i+bd],dp[n&1][-i+bd]);        if(t<=n)return cout << t << endl,0;    }    return 0;}
]]>
+ 洛谷P1171 售货员的难题 题解

题目链接:P1171售货员的难题

题意:TSP问题。

某乡有\(n\)个村庄(\(1<n \le20\)),有一个售货员,他要到各个村庄去售货,各村庄之间的路程\(s(0<s<1000)\)是已知的,且\(A\)村到\(B\)村与\(B\)村到\(A\)村的路大多不同。为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为\(1\),他不知道选择什么样的路线才能使所走的路程最短。请你帮他选择一条最短的路。

题目应该保证了有解。

经典的状压dp

\(dp[i][j]\)表示目前走过的结点的状态为 \(i\),现在在 \(j\) 结点

则有

dp[i][j]=min(dp[i][j],dp[i^(1<<(j-1))][k]+g[k][j])

代码:

#include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)((1<<20)+2)int n,g[25][25],dp[N][22];void Min(int &a,int b){if(b<a)a=b;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(dp,0x3f,sizeof(dp));    cin >> n;dp[1][1]=0;    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            cin >> g[i][j];    for(int i=1; i<1<<n; i++)    for(int j=1; j<=n; j++)     if( (i>>(j-1))&1 )        for(int k=1; k<=n; k++)        if( k!=j && ((i>>(k-1))&1) )            Min(dp[i][j],dp[i^(1<<(j-1))][k]+g[k][j]);    int mn=0x3f3f3f3f;    for(int i=1; i<=n; i++)        Min(mn,dp[(1<<n)-1][i]+g[i][1]);    cout << mn << endl;    return 0;}
]]>
@@ -5717,11 +5717,11 @@ - 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 - - /2022/05/25/luo-gu-p1156-la-ji-xian-jing-ti-jie-qian-tan-shua-biao-fa-yu-tian-biao-fa/ + 洛谷P1282 多米诺骨牌 题解 + + /2022/05/25/luo-gu-p1282-duo-mi-nuo-gu-pai-ti-jie/ - 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法

填表法 :就是一般的动态规划,当前点的状态,可以直接用状态方程,根据之前点的状态推导出来。

刷表法:由当前点的状态,更新其他点的状态。需要注意:只用当每个状态所依赖的状态对它的影响相互独立。

下面会用一道题目给出两种写法的区别


题目链接:P1156 垃圾陷阱

题意吃垃圾就完了

卡门――农夫约翰极其珍视的一条Holsteins奶牛――已经落了到“垃圾井”中。“垃圾井”是农夫们扔垃圾的地方,它的深度为$D(2 \le D \le 100)$英尺。

卡门想把垃圾堆起来,等到堆得与井同样高时,她就能逃出井外了。另外,卡门可以通过吃一些垃圾来维持自己的生命。

每个垃圾都可以用来吃或堆放,并且堆放垃圾不用花费卡门的时间。

假设卡门预先知道了每个垃圾扔下的时间$t(0< t \le 1000)$,以及每个垃圾堆放的高度$h(1 \le h \le 25$)和吃进该垃圾能维持生命的时间$f(1 \le f \le 30)$,要求出卡门最早能逃出井外的时间,假设卡门当前体内有足够持续$10$小时的能量,如果卡门$10$小时内没有进食,卡门就将饿死。

注:如果能量为 $0$ 时恰好有垃圾可以吃,则不会饿死

注意到对于每个垃圾,只有吃掉和堆起来两种方法

设 $dp[i][j]$ 表示只考虑前 $i$ 个垃圾,堆到高度 $j$ 时,最多可以活到什么时候

容易推出方程

1.填表法

求出dp数组以后扫一遍就好了,细节较多(其实好像可以滚动数组,但是懒 qwq)

注意这里垃圾的高度可能会超过 $m$ ,但是数据好像没卡这个东西

处理方法就是把 $m+\max(h_i)$ 都dp一下

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)struct node{    int h,v,t;}a[N];int n,m,dp[N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(dp,0xc0,sizeof(dp));    dp[0][0]=10;    cin >> m >> n;    for(int i=1; i<=n; i++)        cin >> a[i].t >> a[i].v >> a[i].h;    sort(a+1,a+1+n,[](node a,node b){return a.t<b.t;});    for(int i=1; i<=n; i++)        for(int j=0; j<=m+25; j++)        {            if(dp[i-1][j]>=a[i].t)                dp[i][j]=max(dp[i][j],dp[i-1][j]+a[i].v);            if(j>=a[i].h&&dp[i-1][j-a[i].h]>=a[i].t)                dp[i][j]=max(dp[i][j],dp[i-1][j-a[i].h]);        }    int res1=-1,res2=-1;    for(int i=1; i<=n; i++)    {        for(int j=0; j<=m+25; j++)        {            if(dp[i][j]>=a[i].t)                res1=max(res1,j);            res2=max(res2,dp[i][j]);        }        if(res1>=m){cout << a[i].t << endl;return 0;}    }    cout << res2 << endl;    return 0;}

2.刷表法

这里可以滚动数组了,不过要两个

否则会导致数据更新错误

当然也可以不用滚动数组 位运算那么可爱为什么不写她呐?对不对?

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)struct node{    int h,v,t;}a[N];int n,m,dp[2][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(dp,0xc0,sizeof(dp));    dp[0][0]=10;    cin >> m >> n;    for(int i=1; i<=n; i++)        cin >> a[i].t >> a[i].v >> a[i].h;    sort(a+1,a+1+n,[](node a,node b){return a.t<b.t;});    int res=-1;    for(int i=1; i<=n; i++)    {        int cur=i&1,pre=cur^1;        memset(dp[cur],0xc0,sizeof(dp[cur]));        for(int j=m; j>=0; j--) // 这里就不用m+25了,因为只要超出了直接就输出了        {            if(dp[pre][j]<a[i].t)continue;            if(j+a[i].h>=m)return cout << a[i].t << endl,0;            dp[cur][j+a[i].h]=max(dp[cur][j+a[i].h],dp[pre][j]);            dp[cur][j]=max(dp[cur][j],dp[pre][j]+a[i].v);        }        res=max(res,dp[cur][0]);    }    cout << res << endl;    return 0;}

哼哼,我才不会说我是在考数学的时候推出来的dp方程

转载请说明出处

]]>
+ 洛谷P1282 多米诺骨牌 题解

题目链接:P1282多米诺骨牌

题意

多米诺骨牌由上下 \(2\)个方块组成,每个方块中有 \(1\sim6\)个点。现有排成行的上方块中点数之和记为 \(S_1\),下方块中点数之和记为 \(S_2\),它们的差为 \(\left|S_1-S_2\right|\)。如图,\(S_1=6+1+1+1=9\),\(S_2=1+5+3+2=11\),\(\left|S_1-S_2\right|=2\)。每个多米诺骨牌可以旋转\(180°\),使得上下两个方块互换位置。请你计算最少旋转多少次才能使多米诺骨牌上下\(2\) 行点数之差达到最小。

对于图中的例子,只要将最后一个多米诺骨牌旋转 \(180°\),即可使上下 \(2\) 行点数之差为 \(0\)。

注意到我们其实并不关心 \(S_1,S_2\)的值,而是关心他们的差值

这个绝对值似乎不好处理,其实只要把正负的情况取个min就可以了

\(dp[i][j]\) 表示只考虑前 \(i\) 个骨牌,差值为 \(j\) 时的最小翻转数

这里的 \(j\) 定义为 \(S_1-S_2\) 或者 \(S_2-S_1\)其实没啥区别,不过下面代码为前者

不难发现

\[dp[i][j]=\min(dp[i-1][j-a[i]+b[i]]+1,dp[i-1][j+a[i]-b[i]])\]

注意到差值最大 \(5000\),为了避免乱七八糟讨论,就直接循环 \(-5000\sim 5000\) 就好啦

差值可能为负所以数组整体平移一下,然后整个填表法滚一滚啥的就好了

这里好像刷表法写起来不太安全就不写了,虽然没啥问题,但是不算简洁吧(逃

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e3+15)const int bd=5e3+15;int n,dp[2][20*N],a[N],b[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    memset(dp,0x3f,sizeof(dp));    cin >> n;dp[0][bd]=0;    for(int i=1; i<=n; i++)        cin >> a[i] >> b[i];    for(int i=1; i<=n; i++)    {        int cur=i&1,pre=cur^1;        memset(dp[cur],0x3f,sizeof(dp[cur]));        for(int j=-5000; j<=5000; j++)            dp[cur][j+bd]=min(dp[pre][j+a[i]-b[i]+bd]+1,dp[pre][j-a[i]+b[i]+bd]);    }    for(int i=0; i<=5000; i++)    {        int t=min(dp[n&1][i+bd],dp[n&1][-i+bd]);        if(t<=n)return cout << t << endl,0;    }    return 0;}
]]>
@@ -5748,7 +5748,7 @@ /2022/05/25/luo-gu-p1284-san-jiao-xing-mu-chang-ti-jie/ - 洛谷P1284 三角形牧场 题解

题目链接:P1284 三角形牧场

题意:和所有人一样,奶牛喜欢变化。它们正在设想新造型的牧场。奶牛建筑师 Hei 想建造围有漂亮白色栅栏的三角形牧场。她拥有 $n$ 块木板,每块的长度 $l_i$ 都是整数,她想用所有的木板围成一个三角形使得牧场面积最大。

请帮助 Hei 小姐构造这样的牧场,并计算出这个最大牧场的面积。

显然我们可以枚举两条边的长度,然后用总和减去这两条边的长度就是第三条边的长度

设 $dp[k][i][j]$ 表示只考虑前 $k$ 个木板是否可以形成 $(i,j,S-i-j)$ 的划分

则有

其中 $\mid$ 表示二进制的或

然后滚动数组一下就完了

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n,sum;int a[55],dp[1615][1615];int ck(int a,int b,int c){    return a+b>c&&a+c>b&&b+c>a;}double sq(int a,int b,int c){    double p=(a+b+c)*1.0/2;    return sqrt(p*(p-a)*(p-b)*(p-c));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    dp[0][0]=1;    for(int i=1; i<=n; i++)        cin >> a[i],sum+=a[i];    for(int k=1; k<=n; k++)        for(int i=sum/2; i>=0; i--)            for(int j=sum/2; j>=0; j--)            {                if(i>=a[k])dp[i][j]|=dp[i-a[k]][j];                if(j>=a[k])dp[i][j]|=dp[i][j-a[k]];            }    double res=-1;    for(int i=sum/2; i>=1; i--)        for(int j=sum/2; j>=1; j--)            if(dp[i][j]&&ck(i,j,sum-i-j))                res=fmax(res,sq(i,j,sum-i-j));    if(res<0)cout << -1 << endl;    else cout << (int)(res*100) << endl;    return 0;}
]]>
+ 洛谷P1284 三角形牧场 题解

题目链接:P1284三角形牧场

题意:和所有人一样,奶牛喜欢变化。它们正在设想新造型的牧场。奶牛建筑师Hei 想建造围有漂亮白色栅栏的三角形牧场。她拥有 \(n\) 块木板,每块的长度 \(l_i\)都是整数,她想用所有的木板围成一个三角形使得牧场面积最大。

请帮助 Hei 小姐构造这样的牧场,并计算出这个最大牧场的面积。

显然我们可以枚举两条边的长度,然后用总和减去这两条边的长度就是第三条边的长度

\(dp[k][i][j]\) 表示只考虑前\(k\) 个木板是否可以形成 \((i,j,S-i-j)\) 的划分

则有 \[dp[k][i][j]=dp[k-1][i][j]\mid dp[k-1][i-a[k]][j]\mid dp[k-1][i][j-a[k]]\] 其中 \(\mid\)表示二进制的或

然后滚动数组一下就完了

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n,sum;int a[55],dp[1615][1615];int ck(int a,int b,int c){    return a+b>c&&a+c>b&&b+c>a;}double sq(int a,int b,int c){    double p=(a+b+c)*1.0/2;    return sqrt(p*(p-a)*(p-b)*(p-c));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    dp[0][0]=1;    for(int i=1; i<=n; i++)        cin >> a[i],sum+=a[i];    for(int k=1; k<=n; k++)        for(int i=sum/2; i>=0; i--)            for(int j=sum/2; j>=0; j--)            {                if(i>=a[k])dp[i][j]|=dp[i-a[k]][j];                if(j>=a[k])dp[i][j]|=dp[i][j-a[k]];            }    double res=-1;    for(int i=sum/2; i>=1; i--)        for(int j=sum/2; j>=1; j--)            if(dp[i][j]&&ck(i,j,sum-i-j))                res=fmax(res,sq(i,j,sum-i-j));    if(res<0)cout << -1 << endl;    else cout << (int)(res*100) << endl;    return 0;}
]]>
@@ -5775,7 +5775,7 @@ /2022/05/25/luo-gu-p1640-scoi2010-lian-xu-gong-ji-you-xi-ti-jie/ - 洛谷P1640 [SCOI2010]连续攻击游戏 题解

题目链接:P1640 [SCOI2010]连续攻击游戏

题意:lxhgww 最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有 $2$ 个属性,这些属性的值用 $[1,10000]$ 之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。游戏进行到最后,lxhgww 遇到了终极 boss,这个终极 boss 很奇怪,攻击他的装备所使用的属性值必须从 $1$ 开始连续递增地攻击,才能对 boss 产生伤害。也就是说一开始的时候,lxhgww 只能使用某个属性值为 $1$ 的装备攻击 boss,然后只能使用某个属性值为 $2$ 的装备攻击 boss,然后只能使用某个属性值为 $3$ 的装备攻击 boss……以此类推。现在 lxhgww 想知道他最多能连续攻击 boss 多少次?

说实话,这题一眼二分图。

左侧点为属性值,右侧点为装备

然后跑个匈牙利算法就好了

如果匹配某个属性值失败就直接跑路退出循环即可

时间复杂度 $O(k^2),k=$ 最大属性值

但是实际上很难跑到这个上界,所以还是蛮快的

代码1:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)int n,m,vis[N],mch[N];vector<int> vec[N];bool dfs(int u,int now){    if(vis[u]==now)return 0;    vis[u]=now;    for(int v:vec[u])        if(!mch[v]||dfs(mch[v],now))        {            mch[v]=u;            return 1;        }    return 0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(m);    for(int i=1,x,y; i<=m; i++)    {        read(x);read(y);        n=max(n,max(x,y));        vec[x].push_back(i);        vec[y].push_back(i);    }    for(int i=1; i<=n; i++)        if(!dfs(i,i))return printf("%lld\n",i-1),0;    printf("%lld\n",n);    return 0;}

但是这个题还有一个超强的做法

注意到我们并不关心选择了哪些装备

直接把每个武器的两个属性值连无向边

用并查集判断连通块是否存在环

对于一个大小为 $d$ 的连通块

  • 如果其不存在环,则一定有办法使得其中 $d-1$ 个点被选到

    显然我们不会选择最大的那个点

  • 如果其存在环,则一定可以使这 $d$ 个点被选到

当然,这些点不一定是连续的 $1,2,3\dots$

因此我们从 $1$ 到 $k+1$ (与上文同义)扫一遍

$k+1$ 的原因是防止正好这所有的属性值都可以选然后就没输出,见代码2

对于不存在环的结点 $i$ ,只要看它是不是这个连通块最大的点即可

这个最大点怎么判断呢?

我们只要维护一下它所在连通块的大小

显然最大的点会最后一次被扫到

那么就搞定了

时间复杂度 $O(k)$ ,跑得飞快

代码2:

#include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e4+15)namespace MERGE{    int f[N],num[N];    int find(int x){return f[x]==x?x:f[x]=find(f[x]);}    void init(int n){for(int i=1; i<=n; i++)f[i]=i,num[i]=1;}}using namespace MERGE;int n,cir[N];signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);init(N-5);    for(int i=1,a,b; i<=n; i++)    {        read(a);read(b);        a=find(a);b=find(b);        if(a==b)        {            cir[a]=1;        }else        {            cir[a]|=cir[b];            num[a]+=num[b];            f[b]=a;        }    }    for(int i=1,id; i<=N-5; i++)        if(!cir[id=find(i)])        {            if(num[id]==1)                return printf("%d\n",i-1),0;            else --num[id];        }    return 0;}

悄悄说一句,Azis的歌真的好听(逃

]]>
+ 洛谷P1640[SCOI2010]连续攻击游戏 题解

题目链接:P1640[SCOI2010]连续攻击游戏

题意:lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有 \(2\) 个属性,这些属性的值用 \([1,10000]\)之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。游戏进行到最后,lxhgww遇到了终极 boss,这个终极 boss 很奇怪,攻击他的装备所使用的属性值必须从\(1\) 开始连续递增地攻击,才能对 boss产生伤害。也就是说一开始的时候,lxhgww 只能使用某个属性值为 \(1\) 的装备攻击boss,然后只能使用某个属性值为 \(2\)的装备攻击 boss,然后只能使用某个属性值为 \(3\) 的装备攻击 boss……以此类推。现在 lxhgww想知道他最多能连续攻击 boss 多少次?

说实话,这题一眼二分图。

左侧点为属性值,右侧点为装备

然后跑个匈牙利算法就好了

如果匹配某个属性值失败就直接跑路退出循环即可

时间复杂度 \(O(k^2),k=\)最大属性值

但是实际上很难跑到这个上界,所以还是蛮快的

代码1:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)int n,m,vis[N],mch[N];vector<int> vec[N];bool dfs(int u,int now){    if(vis[u]==now)return 0;    vis[u]=now;    for(int v:vec[u])        if(!mch[v]||dfs(mch[v],now))        {            mch[v]=u;            return 1;        }    return 0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(m);    for(int i=1,x,y; i<=m; i++)    {        read(x);read(y);        n=max(n,max(x,y));        vec[x].push_back(i);        vec[y].push_back(i);    }    for(int i=1; i<=n; i++)        if(!dfs(i,i))return printf("%lld\n",i-1),0;    printf("%lld\n",n);    return 0;}

但是这个题还有一个超强的做法

注意到我们并不关心选择了哪些装备

直接把每个武器的两个属性值连无向边

用并查集判断连通块是否存在环

对于一个大小为 \(d\) 的连通块

  • 如果其不存在环,则一定有办法使得其中 \(d-1\) 个点被选到

    显然我们不会选择最大的那个点

  • 如果其存在环,则一定可以使这 \(d\) 个点被选到

当然,这些点不一定是连续的 \(1,2,3\dots\)

因此我们从 \(1\)\(k+1\) (与上文同义)扫一遍

\(k+1\)的原因是防止正好这所有的属性值都可以选然后就没输出,见代码2

对于不存在环的结点 \(i\),只要看它是不是这个连通块最大的点即可

这个最大点怎么判断呢?

我们只要维护一下它所在连通块的大小

显然最大的点会最后一次被扫到

那么就搞定了

时间复杂度 \(O(k)\) ,跑得飞快

代码2:

#include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e4+15)namespace MERGE{    int f[N],num[N];    int find(int x){return f[x]==x?x:f[x]=find(f[x]);}    void init(int n){for(int i=1; i<=n; i++)f[i]=i,num[i]=1;}}using namespace MERGE;int n,cir[N];signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);init(N-5);    for(int i=1,a,b; i<=n; i++)    {        read(a);read(b);        a=find(a);b=find(b);        if(a==b)        {            cir[a]=1;        }else        {            cir[a]|=cir[b];            num[a]+=num[b];            f[b]=a;        }    }    for(int i=1,id; i<=N-5; i++)        if(!cir[id=find(i)])        {            if(num[id]==1)                return printf("%d\n",i-1),0;            else --num[id];        }    return 0;}

悄悄说一句,Azis的歌真的好听(逃

]]>
@@ -5800,11 +5800,11 @@ - 洛谷P1858 多人背包 题解 - - /2022/05/25/luo-gu-p1858-duo-ren-bei-bao-ti-jie/ + 洛谷P1772 [ZJOI2006]物流运输 题解 + + /2022/05/25/luo-gu-p1772-zjoi2006-wu-liu-yun-shu-ti-jie/ - 洛谷P1858 多人背包 题解

题目链接:P1858 多人背包

题意:求01背包前k优解的价值和

建议先去读一读《背包九讲》再来看

注意到朴素的 $01$ 背包是这样转移的

我们可以将其看作两个长度为 $1$ 的有序队列合并的结果

则考虑记录 $k$ 优解

在 $dp[j]$ 的基础上增加一维 $k$ ,即 $dp[j][k]$ ,表示 $k$ 优解

则每次将 $dp[j][k_1]$ 与 $dp[j-w[i]][k_2]+c[i]$ 合并

取最优的 $k$ 个解记录到 $dp[j][k_i]$ 即可

显然可以用归并排序来搞定

时间复杂度 $O(nmk)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e2+15)#define M (int)(5e3+15)#define K 55int n,m,k;int dp[M][K],w[N],v[N],t[K];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> k >> m >> n;    for(int i=1; i<=n; i++)        cin >> w[i] >> v[i];    memset(dp,0xc0,sizeof(dp));    dp[0][1]=0;    for(int i=1; i<=n; i++)        for(int j=m; j>=w[i]; j--)        {            int t1=1,t2=1,cnt=0;            while(t1+t2<=k+1)            {                if(dp[j][t1]>dp[j-w[i]][t2]+v[i])                {                    t[++cnt]=dp[j][t1++];                }else                {                    t[++cnt]=dp[j-w[i]][t2++]+v[i];                }            }            for(int d=1; d<=cnt; d++)                dp[j][d]=t[d];        }    int res=0;    for(int i=1; i<=k; i++)        res+=dp[m][i];    cout << res << endl;    return 0;}
]]>
+ 洛谷P1772 [ZJOI2006]物流运输题解

题目链接:P1772[ZJOI2006]物流运输

题意:物流公司要把一批货物从码头 A 运到码头B。由于货物量比较大,需要 \(n\)天才能运完。货物运输过程中一般要转停好几个码头。

物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪。由于各种因素的存在,有的时候某个码头会无法装卸货物。这时候就必须修改运输路线,让货物能够按时到达目的地。

但是修改路线是一件十分麻烦的事情,会带来额外的成本。因此物流公司希望能够订一个\(n\)天的运输计划,使得总成本尽可能地小。

输入格式

第一行是四个整数 \(n,m,k,e\)\(n\) 表示货物运输所需天数,\(m\) 表示码头总数,\(k\) 表示每次修改运输路线所需成本,\(e\) 表示航线条数。

接下来 \(e\)行每行是一条航线描述,包括了三个整数,依次表示航线连接的两个码头编号以及航线长度。其中码头A 编号为 \(1\) ,码头 B 编号为 \(m\) 。单位长度的运输费用为 \(1\)。航线是双向的。

再接下来一行是一个整数 \(d\),后面的\(d\) 行每行是三个整数 \(p,a,b\)。表示编号为 \(p\) 的码头在 \([a,b]\)天之内无法装卸货物。同一个码头有可能在多个时间段内不可用。但任何时间都存在至少一条从码头A 到码头 B 的运输路线。

输出格式

包括了一个整数表示最小的总成本。 总成本为 \(n\) 天运输路线长度之和 \(+k\times\)改变运输路线的次数。

输入输出样例

输入 #1

5 5 10 81 2 11 3 31 4 22 3 22 4 43 4 13 5 24 5 242 2 33 1 13 3 34 4 5

输出 #1

32

说明/提示

【数据范围】 对于 \(100\%\)的数据,\(1 \le n \le 100\)\(1\le m \le 20\)。

【样例输入说明】

img

上图依次表示第 \(1\) 至第 \(5\) 天的情况,阴影表示不可用的码头。

【样例输出说明】

前三天走 \(1 \to 4 \to 5\),后两天走\(1 \to 3 \to 5\),这样总成本为 \((2+2)\times 3+(3+2)\times 2+10=32\) 。

先不考虑图的问题

\(s[i][j]\) 为第 \(i\) 天到第 \(j\) 天不换路的最小花费

\(dp[i]\) 为前 \(i\) 天的最小花费

则有 \[dp[i]=\min_{1\le j\le i}(dp[i],dp[j-1]+s[i][j]\times(i-j+1)+k)\] 即前 \(j-1\)天都不改变路线,第 \(j\) 天到第 \(i\) 天走同一条路线

那么直接dp就完了

现在问题是怎么求出 \(s[i][j]\)

因为要求最小花费,那么一定是这几天都可以走的结点所成的最短路的花费\(\times\) 天数( \(i-j+1\) )

直接暴力跑dijkstra就好了

时间复杂度 \(O(n^3m)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x1f1f1f1f1f1f1f1fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e3+15)struct Edge{    int u,v,w,next;}e[N];int n,m,k,E,d;bool c[N][N],vis[N];int pos=1,head[N],s[N][N],dis[N],dp[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}priority_queue<node> q;int mul(int a,int b){    return a==INF?INF:a*b;}void dijkstra(int st){    memset(dis,0x1f,sizeof(dis));    while(!q.empty())q.pop();    q.push({st,dis[st]=0});    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(dis[v]>dis[u]+w)            {                dis[v]=dis[u]+w;                q.push({v,dis[v]});            }        }    }}signed main(){    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(m);read(k);read(E);    for(int i=1,u,v,w; i<=E; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,w);    }    read(d);    for(int i=1,p,l,r; i<=d; i++)    {        read(p);read(l);read(r);        for(int j=l; j<=r; j++)            c[p][j]=1;    }    for(int i=1; i<=n; i++)        for(int j=i; j<=n; j++)        {            memset(vis,0,sizeof(vis));            for(int a=1; a<=m; a++)                for(int b=i; b<=j; b++)                    if(c[a][b])vis[a]=1;            dijkstra(1);            s[i][j]=dis[m];        }    memset(dp,0x1f,sizeof(dp));    for(int i=1; i<=n; i++)    {        dp[i]=mul(s[1][i],i);        for(int j=i; j>=1; j--)            dp[i]=min(dp[i],dp[j-1]+mul(s[j][i],(i-j+1))+k);    }    printf("%lld\n",dp[n]);    return 0;}
]]>
@@ -5820,6 +5820,8 @@ DP + 数据结构 +
@@ -5827,11 +5829,11 @@ - 洛谷P1772 [ZJOI2006]物流运输 题解 - - /2022/05/25/luo-gu-p1772-zjoi2006-wu-liu-yun-shu-ti-jie/ + 洛谷P1858 多人背包 题解 + + /2022/05/25/luo-gu-p1858-duo-ren-bei-bao-ti-jie/ - 洛谷P1772 [ZJOI2006]物流运输 题解

题目链接:P1772 [ZJOI2006]物流运输

题意:物流公司要把一批货物从码头 A 运到码头 B。由于货物量比较大,需要 $n$ 天才能运完。货物运输过程中一般要转停好几个码头。

物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪。由于各种因素的存在,有的时候某个码头会无法装卸货物。这时候就必须修改运输路线,让货物能够按时到达目的地。

但是修改路线是一件十分麻烦的事情,会带来额外的成本。因此物流公司希望能够订一个 $n$ 天的运输计划,使得总成本尽可能地小。

输入格式

第一行是四个整数 $n,m,k,e$ 。$n$ 表示货物运输所需天数,$m$ 表示码头总数,$k$ 表示每次修改运输路线所需成本,$e$ 表示航线条数。

接下来 $e$ 行每行是一条航线描述,包括了三个整数,依次表示航线连接的两个码头编号以及航线长度。其中码头 A 编号为 $1$ ,码头 B 编号为 $m$ 。单位长度的运输费用为 $1$。航线是双向的。

再接下来一行是一个整数 $d$,后面的 $d$ 行每行是三个整数 $p,a,b$。表示编号为 $p$ 的码头在 $[a,b]$ 天之内无法装卸货物。同一个码头有可能在多个时间段内不可用。但任何时间都存在至少一条从码头 A 到码头 B 的运输路线。

输出格式

包括了一个整数表示最小的总成本。
总成本为 $n$ 天运输路线长度之和 $+k\times$改变运输路线的次数。

输入输出样例

输入 #1

5 5 10 81 2 11 3 31 4 22 3 22 4 43 4 13 5 24 5 242 2 33 1 13 3 34 4 5

输出 #1

32

说明/提示

【数据范围】 对于 $100\%$ 的数据,$1 \le n \le 100$,$1\le m \le 20$。

【样例输入说明】

img

上图依次表示第 $1$ 至第 $5$ 天的情况,阴影表示不可用的码头。

【样例输出说明】

前三天走 $1 \to 4 \to 5$,后两天走 $1 \to 3 \to 5$,这样总成本为 $(2+2)\times 3+(3+2)\times 2+10=32$ 。

先不考虑图的问题

令 $s[i][j]$ 为第 $i$ 天到第 $j$ 天不换路的最小花费

设 $dp[i]$ 为前 $i$ 天的最小花费

则有

即前 $j-1$ 天都不改变路线,第 $j$ 天到第 $i$ 天走同一条路线

那么直接dp就完了

现在问题是怎么求出 $s[i][j]$

因为要求最小花费,那么一定是这几天都可以走的结点所成的最短路的花费 $\times$ 天数( $i-j+1$ )

直接暴力跑dijkstra就好了

时间复杂度 $O(n^3m)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x1f1f1f1f1f1f1f1fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e3+15)struct Edge{    int u,v,w,next;}e[N];int n,m,k,E,d;bool c[N][N],vis[N];int pos=1,head[N],s[N][N],dis[N],dp[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}struct node{int u,dis;};bool operator<(node a,node b){return a.dis>b.dis;}priority_queue<node> q;int mul(int a,int b){    return a==INF?INF:a*b;}void dijkstra(int st){    memset(dis,0x1f,sizeof(dis));    while(!q.empty())q.pop();    q.push({st,dis[st]=0});    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(dis[v]>dis[u]+w)            {                dis[v]=dis[u]+w;                q.push({v,dis[v]});            }        }    }}signed main(){    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);read(m);read(k);read(E);    for(int i=1,u,v,w; i<=E; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,w);    }    read(d);    for(int i=1,p,l,r; i<=d; i++)    {        read(p);read(l);read(r);        for(int j=l; j<=r; j++)            c[p][j]=1;    }    for(int i=1; i<=n; i++)        for(int j=i; j<=n; j++)        {            memset(vis,0,sizeof(vis));            for(int a=1; a<=m; a++)                for(int b=i; b<=j; b++)                    if(c[a][b])vis[a]=1;            dijkstra(1);            s[i][j]=dis[m];        }    memset(dp,0x1f,sizeof(dp));    for(int i=1; i<=n; i++)    {        dp[i]=mul(s[1][i],i);        for(int j=i; j>=1; j--)            dp[i]=min(dp[i],dp[j-1]+mul(s[j][i],(i-j+1))+k);    }    printf("%lld\n",dp[n]);    return 0;}
]]>
+ 洛谷P1858 多人背包 题解

题目链接:P1858多人背包

题意:求01背包前k优解的价值和

建议先去读一读《背包九讲》再来看

注意到朴素的 \(01\) 背包是这样转移的\[dp[j]=\max(dp[j],dp[j-w[i]]+v[i])\] 我们可以将其看作两个长度为 \(1\) 的有序队列合并的结果

则考虑记录 \(k\) 优解

\(dp[j]\) 的基础上增加一维 \(k\) ,即 \(dp[j][k]\) ,表示 \(k\) 优解

则每次将 \(dp[j][k_1]\)\(dp[j-w[i]][k_2]+c[i]\) 合并

取最优的 \(k\) 个解记录到 \(dp[j][k_i]\) 即可

显然可以用归并排序来搞定

时间复杂度 \(O(nmk)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e2+15)#define M (int)(5e3+15)#define K 55int n,m,k;int dp[M][K],w[N],v[N],t[K];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> k >> m >> n;    for(int i=1; i<=n; i++)        cin >> w[i] >> v[i];    memset(dp,0xc0,sizeof(dp));    dp[0][1]=0;    for(int i=1; i<=n; i++)        for(int j=m; j>=w[i]; j--)        {            int t1=1,t2=1,cnt=0;            while(t1+t2<=k+1)            {                if(dp[j][t1]>dp[j-w[i]][t2]+v[i])                {                    t[++cnt]=dp[j][t1++];                }else                {                    t[++cnt]=dp[j-w[i]][t2++]+v[i];                }            }            for(int d=1; d<=cnt; d++)                dp[j][d]=t[d];        }    int res=0;    for(int i=1; i<=k; i++)        res+=dp[m][i];    cout << res << endl;    return 0;}
]]>
@@ -5847,8 +5849,6 @@ DP - 数据结构 -
@@ -5860,7 +5860,7 @@ /2022/05/25/luo-gu-p1944-zui-chang-gua-hao-pi-pei-ti-jie/ - 洛谷P1944 最长括号匹配 题解

题意:对一个由(,),[,]括号组成的字符串,求出其中最长的括号匹配子串。具体来说,满足如下条件的字符串成为括号匹配的字符串:

1.(),[]是括号匹配的字符串。

2.若A是括号匹配的串,则(A),[A]是括号匹配的字符串。

3.若A,B是括号匹配的字符串,则AB也是括号匹配的字符串。

例如:(),[],([]),()()都是括号匹配的字符串,而][,[(])则不是。

字符串A的子串是指由A中连续若干个字符组成的字符串。

例如,A,B,C,ABC,CAB,ABCABC都是ABCABC的子串。空串是任何字符串的子串。

似乎可以用栈模拟,但是这个太没劲了,考虑dp

设 $dp[i]$ 表示以 $s[i]$ 结尾的最长括号匹配长度

显然当 $s[i]$ 为左括号时 $dp[i]=0$

当 $s[i]$ 为右括号时,$dp[i]$ 一定与 $dp[i-1]$ 有关

容易发现 $s[i-dp[i-1]]$ 是 $dp[i-1]$ 的起始字符

则当 $s[i-dp[i-1]-1]$ 与 $s[i]$ 合法匹配时,有

$dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2]$

这里的 $dp[i-dp[i-1]-2]$ 只需要一个例子就好理解了

[]([])123456

假设此时为 $dp[5]=2$ ,对于以 $s[5]$ 结尾的最长括号匹配显然是[]

然后到了 $dp[6]=6$ ,注意到此时 $s[3]$ 被合法利用了,

这样就造成了 $s[3]$ 之前的 $dp[6-dp[5]-2] = dp[2]$ 被重新利用

应该很好理解了

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n,ans,dp[N],id;char s[N];signed main(){    scanf("%s",(s+1));n=strlen(s+1);    for(int i=2; i<=n; i++)    {        if(s[i]=='('||s[i]=='[')continue;        else        {            if(s[i]==')'&&s[i-dp[i-1]-1]=='('||            s[i]==']'&&s[i-dp[i-1]-1]=='[')            {                dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2];                if(dp[i]>ans)ans=dp[i],id=i;            }        }    }    for(int i=id-ans+1; i<=id; i++)        putchar(s[i]);    puts("");    return 0;}
]]>
+ 洛谷P1944 最长括号匹配 题解

题意:对一个由(,),[,]括号组成的字符串,求出其中最长的括号匹配子串。具体来说,满足如下条件的字符串成为括号匹配的字符串:

1.(),[]是括号匹配的字符串。

2.若A是括号匹配的串,则(A),[A]是括号匹配的字符串。

3.若A,B是括号匹配的字符串,则AB也是括号匹配的字符串。

例如:(),[],([]),()()都是括号匹配的字符串,而][,[(])则不是。

字符串A的子串是指由A中连续若干个字符组成的字符串。

例如,A,B,C,ABC,CAB,ABCABC都是ABCABC的子串。空串是任何字符串的子串。

似乎可以用栈模拟,但是这个太没劲了,考虑dp

\(dp[i]\) 表示以 \(s[i]\) 结尾的最长括号匹配长度

显然当 \(s[i]\) 为左括号时 \(dp[i]=0\)

\(s[i]\) 为右括号时,\(dp[i]\) 一定与 \(dp[i-1]\) 有关

容易发现 \(s[i-dp[i-1]]\)\(dp[i-1]\) 的起始字符

则当 \(s[i-dp[i-1]-1]\)\(s[i]\) 合法匹配时,有

\(dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2]\)

这里的 \(dp[i-dp[i-1]-2]\)只需要一个例子就好理解了

[]([])123456

假设此时为 \(dp[5]=2\) ,对于以\(s[5]\)结尾的最长括号匹配显然是[]

然后到了 \(dp[6]=6\) ,注意到此时\(s[3]\) 被合法利用了,

这样就造成了 \(s[3]\) 之前的 \(dp[6-dp[5]-2] = dp[2]\) 被重新利用

应该很好理解了

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+15)int n,ans,dp[N],id;char s[N];signed main(){    scanf("%s",(s+1));n=strlen(s+1);    for(int i=2; i<=n; i++)    {        if(s[i]=='('||s[i]=='[')continue;        else        {            if(s[i]==')'&&s[i-dp[i-1]-1]=='('||            s[i]==']'&&s[i-dp[i-1]-1]=='[')            {                dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2];                if(dp[i]>ans)ans=dp[i],id=i;            }        }    }    for(int i=id-ans+1; i<=id; i++)        putchar(s[i]);    puts("");    return 0;}
]]>
@@ -5885,11 +5885,11 @@ - 洛谷P2170 选学霸 题解 - - /2022/05/25/luo-gu-p2170-xuan-xue-ba-ti-jie/ + 洛谷P2158 [SDOI2008] 仪仗队 题解 + + /2022/05/25/luo-gu-p2158-sdoi2008-yi-zhang-dui-ti-jie/ - 洛谷P2170 选学霸 题解

题目链接:P2170 选学霸

题意:老师想从 $n$ 名学生中选 $m$ 人当学霸,但有 $k$ 人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的 $m$ 尽可能接近。

把所有连通块的大小求出来,统计每个大小各有几个连通块

然后二进制优化的多重背包,不过这道题是可行性问题

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e4+15)int n,m,k,w[N],p,dp[N],s[N];namespace MERGE{    int f[N],sz[N];    void init(int n){for(int i=1; i<=n; i++)sz[i]=1,f[i]=i;}    int find(int x){return f[x]==x?x:f[x]=find(f[x]);}    void merge(int u,int v)    {        u=find(u);v=find(v);        f[u]=v;        sz[v]+=sz[u];    }}using namespace MERGE;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> k;    init(n);    for(int i=1,x,y; i<=k; i++)    {        cin >> x >> y;        if(find(x)!=find(y))merge(x,y);    }    for(int i=1; i<=n; i++)        if(find(i)==i)s[sz[i]]++;    for(int i=1; i<=n; i++)    {        int t=1;        while(s[i]>=t)        {            w[++p]=i*t;            s[i]-=t;t<<=1;        }        w[++p]=i*s[i];    }    dp[0]=1;    for(int i=1; i<=p; i++)        for(int j=n; j>=w[i]; j--)            dp[j]|=dp[j-w[i]];    int res,mn=INF;    for(int i=0; i<=n; i++)        if(dp[i]&&abs(m-i)<mn)            res=i,mn=abs(m-i);    cout << res << endl;    return 0;}
]]>
+ 洛谷P2158 [SDOI2008] 仪仗队题解

题目链接:P2158[SDOI2008] 仪仗队

题意:作为体育委员,C君负责这次运动会仪仗队的训练。仪仗队是由学生组成的 \(N \times N\)的方阵,为了保证队伍在行进中整齐划一,C君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。

现在,C 君希望你告诉他队伍整齐时能看到的学生人数。

稍微画一画图,可以发现我们只要求对角线以下的个数,然后把答案乘 \(2\) 加 \(1\) 即可

把连线看成直角三角形的斜边

可以发现图中对角线以下一共有 \(9\)个互不成比例的三角形 ( \((1,1)\)那个算进去,\((1,0)\) 那个不算)

注:因为把 \((1,0)\)\((1,1)\)那个交换一下,答案不变(不太好讲,意会一下就行

考虑怎样的三角形可以对答案有贡献

显然是满足底和高互质的三角形,而我们只要计算底>高的情况

问题就转化为了求 \(\sum\limits_{i=1}^{n-1}\varphi(i)\) ,这里的 \(\varphi(i)\) 表示欧拉函数

注意是 \(n-1\)哦!(边数=点数-1)

那么搞一个前缀和就好了,注意特判 \(n=1\) 的情况

这题暴力可过,代码如下

时间复杂度 \(O(n\sqrt{n})\)

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(4e4+15)int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}int n,phi[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    if(n==1){cout << 0 << endl;return 0;}    for(int i=1; i<=n; i++)        phi[i]=phi[i-1]+Euler_phi(i);    cout << phi[n-1]*2+1 << endl;    return 0;}

当然 \(O(n)\) 的欧拉筛肯定快啦

代码在这

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(4e4+15)bool ck[N];int n,phi[N],prime[N],pcnt;void Euler(){    if(n==1){cout << 0 << endl;exit(0);}    ck[1]=1;phi[1]=1;    for(int i=2; i<=n; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            phi[i]=i-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=n; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                phi[pos]=phi[i]*phi[prime[j]];            }else            {                phi[pos]=phi[i]*prime[j];                break;            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;Euler();    for(int i=1; i<=n; i++)        phi[i]+=phi[i-1];    cout << phi[n-1]*2+1 << endl;    return 0;}
]]>
@@ -5903,7 +5903,7 @@ 算法 - DP + 数学 @@ -5912,11 +5912,11 @@ - 洛谷P2158 [SDOI2008] 仪仗队 题解 - - /2022/05/25/luo-gu-p2158-sdoi2008-yi-zhang-dui-ti-jie/ + 洛谷P2170 选学霸 题解 + + /2022/05/25/luo-gu-p2170-xuan-xue-ba-ti-jie/ - 洛谷P2158 [SDOI2008] 仪仗队 题解

题目链接:P2158 [SDOI2008] 仪仗队

题意:作为体育委员,C 君负责这次运动会仪仗队的训练。仪仗队是由学生组成的 $N \times N$ 的方阵,为了保证队伍在行进中整齐划一,C 君会跟在仪仗队的左后方,根据其视线所及的学生人数来判断队伍是否整齐(如下图)。

现在,C 君希望你告诉他队伍整齐时能看到的学生人数。

稍微画一画图,可以发现我们只要求对角线以下的个数,然后把答案乘 $2$ 加 $1$ 即可

把连线看成直角三角形的斜边

可以发现图中对角线以下一共有 $9$ 个互不成比例的三角形 ( $(1,1)$ 那个算进去,$(1,0)$ 那个不算)

注:因为把 $(1,0)$ 和 $(1,1)$ 那个交换一下,答案不变(不太好讲,意会一下就行

考虑怎样的三角形可以对答案有贡献

显然是满足底和高互质的三角形,而我们只要计算底>高的情况

问题就转化为了求 $\sum\limits_{i=1}^{n-1} \varphi(i)$ ,这里的 $\varphi(i)$ 表示欧拉函数

注意是 $n-1$ 哦!(边数=点数-1)

那么搞一个前缀和就好了,注意特判 $n=1$ 的情况

这题暴力可过,代码如下

时间复杂度 $O(n\sqrt{n})$

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(4e4+15)int Euler_phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}int n,phi[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    if(n==1){cout << 0 << endl;return 0;}    for(int i=1; i<=n; i++)        phi[i]=phi[i-1]+Euler_phi(i);    cout << phi[n-1]*2+1 << endl;    return 0;}

当然 $O(n)$ 的欧拉筛肯定快啦

代码在这

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(4e4+15)bool ck[N];int n,phi[N],prime[N],pcnt;void Euler(){    if(n==1){cout << 0 << endl;exit(0);}    ck[1]=1;phi[1]=1;    for(int i=2; i<=n; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            phi[i]=i-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=n; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                phi[pos]=phi[i]*phi[prime[j]];            }else            {                phi[pos]=phi[i]*prime[j];                break;            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;Euler();    for(int i=1; i<=n; i++)        phi[i]+=phi[i-1];    cout << phi[n-1]*2+1 << endl;    return 0;}
]]>
+ 洛谷P2170 选学霸 题解

题目链接:P2170选学霸

题意:老师想从 \(n\) 名学生中选 \(m\) 人当学霸,但有 \(k\)人实力相当,如果实力相当的人中,一部分被选上,另一部分没有,同学们就会抗议。所以老师想请你帮他求出他该选多少学霸,才能既不让同学们抗议,又与原来的\(m\) 尽可能接近。

把所有连通块的大小求出来,统计每个大小各有几个连通块

然后二进制优化的多重背包,不过这道题是可行性问题

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e4+15)int n,m,k,w[N],p,dp[N],s[N];namespace MERGE{    int f[N],sz[N];    void init(int n){for(int i=1; i<=n; i++)sz[i]=1,f[i]=i;}    int find(int x){return f[x]==x?x:f[x]=find(f[x]);}    void merge(int u,int v)    {        u=find(u);v=find(v);        f[u]=v;        sz[v]+=sz[u];    }}using namespace MERGE;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> k;    init(n);    for(int i=1,x,y; i<=k; i++)    {        cin >> x >> y;        if(find(x)!=find(y))merge(x,y);    }    for(int i=1; i<=n; i++)        if(find(i)==i)s[sz[i]]++;    for(int i=1; i<=n; i++)    {        int t=1;        while(s[i]>=t)        {            w[++p]=i*t;            s[i]-=t;t<<=1;        }        w[++p]=i*s[i];    }    dp[0]=1;    for(int i=1; i<=p; i++)        for(int j=n; j>=w[i]; j--)            dp[j]|=dp[j-w[i]];    int res,mn=INF;    for(int i=0; i<=n; i++)        if(dp[i]&&abs(m-i)<mn)            res=i,mn=abs(m-i);    cout << res << endl;    return 0;}
]]>
@@ -5930,7 +5930,7 @@ 算法 - 数学 + DP @@ -5939,11 +5939,11 @@ - 洛谷P2257 YY的GCD 题解 - - /2022/05/25/luo-gu-p2257-yy-de-gcd-ti-jie/ + 洛谷P2216 [HAOI2007]理想的正方形 题解 + + /2022/05/25/luo-gu-p2216-haoi2007-li-xiang-de-zheng-fang-xing-ti-jie/ - 洛谷P2257 YY的GCD 题解

题意

多组询问。

给定 $N, M$,求 $1 \leq x \leq N$,$1 \leq y \leq M$ 且 $\gcd(x,y)$ 为质数的 $(x,y)$ 有多少对。

一句话题意

令 $\mathcal{P}$ 为所有素数组成的集合,求

不妨假设 $n\le m$

然后么推推柿子

友情提醒,这道题涉及经典操作:转化枚举量、构造莫比乌斯反演

不知道的可以先去看看别的题,比如这个题 link

注:$\land$ 表示逻辑与,可认为其等价于 “且”

令 $f(x) = \sum\limits_{k \mid x\land k \in \mathcal{P}}\mu\left(\dfrac{x}{k}\right)$

然后这个题其实到这里就差不多了

前面的用数论分块

后面那个 $\mu$ 可以在筛法里面求

时间复杂度 $O(n\log \log n + Q\sqrt{n})$

#include <bits/stdc++.h>using namespace std;// #define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e7+15)int Q,x,y;int prime[6000005],mu[N],pcnt,sum[N];bool ck[N];void Mobius(){    mu[1]=1;    for(int i=2; i<=N-5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            mu[i]=-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N-5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                mu[pos]=-mu[i];            }else            {                mu[pos]=0;                break;            }        }    }    for(int i=1; i<=pcnt; i++)        for(int j=1; prime[i]*j<=N-5; j++)            sum[j*prime[i]]+=mu[j];                for(int i=1; i<=N-5; i++)        sum[i]+=sum[i-1];}long long solve(int n,int m){    long long res=0;    for(int l=1,r; l<=min(n,m); l=r+1)    {        r=min(n/(n/l),m/(m/l));        res+=(long long)(sum[r]-sum[l-1])*(n/l)*(m/l);    }    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(Q);    Mobius();    while(Q--)    {        read(x);read(y);        write(solve(x,y));pc('\n');    }    return 0;}

这份代码很慢,跑了7.74s, 目前最优解就跑了 5.69s (截止20220511)

其实这个 $O(n \log\log n)$ 的枚举并没有必要

考虑在线性筛的过程中更新答案

设 $x=ab$ ,其中 $a$ 为 $x$ 的最小质因数

称 $p$ 为当前线性筛枚举的一个用于标记的素数(prime[j]

  • 若 $x \in \mathcal{P}$ ,则显然 $f(x) = \mu(1) = 1$

  • 若 $x\notin \mathcal P \land b \bmod a = 0$

    则有 $x = a^p b^{\prime},b=a^{p-1}b^{\prime},p$ 为大于 $1$ 的正整数 (即 $x$ 有多个最小质因数 )

    • 若 $b$ 没有多个最小质因数,则当且仅当 $p=a$ 时 $\mu\left(\dfrac{x}{p}\right) = \mu(b) \ne 0$

      因此有 $f(x) = \mu(b)$

    • 若 $b$ 含有多个最小质因数,则 $\forall p \in \mathcal{P}^{\prime},\mu\left(\dfrac{x}{p}\right)=0,\mathcal{P}^{\prime}$ 为当前筛出的素数集

      不过我们无须单独考虑这种情况,因为此时仍然有 $\mu\left(\dfrac{x}{p}\right) = \mu(b)$

  • 若 $x\notin \mathcal{P} \land b \bmod a \ne 0$

    则此时 $x$ 有唯一且最小的质因数 $a$

    显然有 $\mu(x) = -\mu(b)$ ,因为它仅仅多了一个本质不同的质因子

    则对于 $\mu\left(\dfrac{x}{p}\right) = -\mu\left(\dfrac{b}{p}\right)$ 也是成立的

    因此 $f(b)$ 中的每一项在 $f(x)$ 中都能找到对应的

    注意到

    由于 $\gcd(a,b)=1$ (显然),则 $f(x)$ 仅仅比 $f(b)$ 多了一项 $\mu\left(\dfrac{ab}{a}\right) = \mu(b)$

    则有 $f(x) = -f(b)+\mu(b)$

综上,有

于是我们就可以在 $O(n + Q\sqrt{n})$ 的时间复杂度内解决这个问题了

然后稍微卡一卡常,就跑的飞快

对了,目前最优解其实是我的代码哦(虽然没啥用

代码如下

#include <bits/stdc++.h>using namespace std;// #define int long longtypedef long long ll;#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e7+15)int Q,mx,x[10005],y[10005];;int prime[1000005],mu[N],pcnt,f[N];bool ck[N];void Mobius(){    mu[1]=1;    for(int i=2; i<=mx+5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            mu[i]=-1;            f[i]=1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=mx+5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                f[pos]=-f[i]+mu[i];                mu[pos]=-mu[i];            }else            {                f[pos]=mu[i];                mu[pos]=0;                break;            }        }        f[i]+=f[i-1];    }}ll solve(int n,int m){    ll res=0;    for(int l=1,r; l<=min(n,m); l=r+1)    {        r=min(n/(n/l),m/(m/l));        res+=(ll)(f[r]-f[l-1])*(n/l)*(m/l);    }    return res;}signed main(){    read(Q);    for(int i=1; i<=Q; i++)        read(x[i]),read(y[i]);    mx=max(*max_element(x+1,x+1+Q),*max_element(y+1,y+1+Q));    Mobius();    for(int i=1; i<=Q; i++)        write(solve(x[i],y[i])),pc('\n');    return 0;}

如果有什么错误或者问题的话,欢迎留言

参考文献

[1] https://www.luogu.com.cn/blog/An-Amazing-Blog/solution-p2257

[2] https://siyuan.blog.luogu.org/solution-p2257

]]>
+ 洛谷P2216[HAOI2007]理想的正方形 题解

题目链接:P2216[HAOI2007]理想的正方形

题意: 有一个 \(a \timesb\) 的整数组成的矩阵,现请你从中找出一个 \(n \timesn\)的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

去年集训写过这题,也是去年集训唯一过了的(那个数据水

upd.20220722 今年集训又来了。

当时的解法稍微改了一下也可以过,放在本文最后

正解:单调队列

考虑先对每一行跑一次单调队列

构造一个 \(m \times (n-k+1)\)的矩阵(设 \(n\)\(m\) 列)

再交换一下行列,跑出一个 \((m-k+1)\times(n-k+1)\) 的矩阵

然后就直接枚举就好了,其实没啥难的

时间复杂度 \(O(n^2)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e3+15)int st,en,q[N];int n,m,k,in[N][N],a[N][N],b[N][N],mn[N][N],mx[N][N];void solve1(int *num,int rowid,int n,int len,int f){    st=en=0;    for(int i=1; i<=n; i++)    {        if(f==1)while(st<en&&num[i]<num[q[en]])--en;        if(f==2)while(st<en&&num[i]>num[q[en]])--en;        q[++en]=i;        while(st<en&&q[st+1]+len<=i)++st;        if(i>=len&&f==1)a[i-len+1][rowid]=num[q[st+1]];        if(i>=len&&f==2)b[i-len+1][rowid]=num[q[st+1]];    }}void solve2(int *num,int rowid,int n,int len,int f){    st=en=0;    for(int i=1; i<=n; i++)    {        if(f==1)while(st<en&&num[i]<num[q[en]])--en;        if(f==2)while(st<en&&num[i]>num[q[en]])--en;        q[++en]=i;        while(st<en&&q[st+1]+len<=i)++st;        if(i>=len&&f==1)mn[i-len+1][rowid]=num[q[st+1]];        if(i>=len&&f==2)mx[i-len+1][rowid]=num[q[st+1]];    }}signed main(){    read(n);read(m);read(k);    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)            read(in[i][j]);    for(int i=1; i<=n; i++)        solve1(in[i],i,m,k,1),solve1(in[i],i,m,k,2);    for(int i=1; i<=m-k+1; i++)        solve2(a[i],i,n,k,1),solve2(b[i],i,n,k,2);    int ans=INF;    for(int i=1; i<=n-k+1; i++)        for(int j=1; j<=m-k+1; j++)            ans=min(ans,mx[i][j]-mn[i][j]);    printf("%lld\n",ans);    return 0;}

这里是奇怪的解法

构建一个二维的st表,和上面思路差不多

时间复杂度 \(O(n^2\log n)\),也可以过

代码:

#include <bits/stdc++.h>using namespace std;//#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef long long ll;namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define MAXN (int)(1e3+5)int n,m,k,N,M;int MX[MAXN][MAXN];int MN[MAXN][MAXN];int stmx[MAXN][MAXN][11];int stmn[MAXN][MAXN][11];ll ans=INF;void change1(int x,int u,int v){    stmx[x][u][0]=v;    stmn[x][u][0]=v;    for(int i=1; u-(1<<(i-1))>=1; i++)    {        stmx[x][u][i]=max(stmx[x][u][i-1],stmx[x][u-(1<<(i-1))][i-1]);        stmn[x][u][i]=min(stmn[x][u][i-1],stmn[x][u-(1<<(i-1))][i-1]);    }}void change2(int x,int u){    stmx[x][u][0]=MX[x][u];    stmn[x][u][0]=MN[x][u];    for(int i=1; u-(1<<(i-1))>=1; i++)    {        stmx[x][u][i]=max(stmx[x][u][i-1],stmx[x][u-(1<<(i-1))][i-1]);        stmn[x][u][i]=min(stmn[x][u][i-1],stmn[x][u-(1<<(i-1))][i-1]);    }}int qrymx(int x,int l,int r){    int k=(double)log(r-l+1)/log(2);    return max(stmx[x][l+(1<<k)-1][k],stmx[x][r][k]);}   int qrymn(int x,int l,int r){    int k=(double)log(r-l+1)/log(2);    return min(stmn[x][l+(1<<k)-1][k],stmn[x][r][k]);}int qrymx(int x,int l){    int r=l+k-1;    int p=(double)log(r-l+1)/log(2);    return max(stmx[x][l+(1<<p)-1][p],stmx[x][r][p]);}   int qrymn(int x,int l){    int r=l+k-1;    int p=(double)log(r-l+1)/log(2);    return min(stmn[x][l+(1<<p)-1][p],stmn[x][r][p]);}signed main(){    read(n);read(m);read(k);    for(int i=1; i<=n; i++)        for(int j=1,x; j<=m; j++)        {            read(x);            change1(i,j,x);        }    for(int j=1; j<=m-k+1; j++)        for(int i=1; i<=n; i++)            MX[j][i]=qrymx(i,j,j+k-1),            MN[j][i]=qrymn(i,j,j+k-1);    for(int i=1; i<=m-k+1; i++)        for(int j=1; j<=n; j++)            change2(i,j);    for(int i=1; i<=m-k+1; i++)        for(int j=1; j<=n-k+1; j++)        ans=min(ans,(ll)qrymx(i,j)-qrymn(i,j));    printf("%lld\n",ans);    return 0;}
]]>
@@ -5957,7 +5957,7 @@ 算法 - 数学 + 数据结构 @@ -5970,7 +5970,7 @@ /2022/05/25/luo-gu-p2224-hnoi2001-chan-pin-jia-gong-ti-jie/ - 洛谷P2224 [HNOI2001]产品加工 题解

题目链接:P2224 [HNOI2001]产品加工

题意

某加工厂有 A、B 两台机器,来加工的产品可以由其中任何一台机器完成,或者两台机器共同完成。由于受到机器性能和产品特性的限制,不同的机器加工同一产品所需的时间会不同,若同时由两台机器共同进行加工,所完成任务又会不同。

某一天,加工厂接到 $n$ 个产品加工的任务,每个任务的工作量不尽一样。

你的任务就是:已知每个任务在 A 机器上加工所需的时间 $t_1$,B 机器上加工所需的时间 $t_2$ 及由两台机器共同加工所需的时间 $t_3$,请你合理安排任务的调度顺序,使完成所有 $n$ 个任务的总时间最少。

显然dp

设 $dp[i][j]$ 表示只考虑前 $i$ 个物品,第一台机器花了 $j$ 时间,第二台机器花的最少时间

不难发现

然后滚动数组一下就好了

注意这题卡常很恶心(恼

代码:

#include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define INF 0x3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(3e4+65)int n,a[N],b[N],c[N],dp[2][N],sum,cur,pre,mn;int min(int a,int b){return a>b?b:a;}int max(int a,int b){return a>b?a:b;}signed main(){    read(n);    memset(dp,0x3f,sizeof(dp));dp[0][0]=0;    for(int i=1; i<=n; i++)    {        read(a[i]),read(b[i]),read(c[i]);        sum+=max(a[i],max(b[i],c[i]));        cur=i&1,pre=cur^1;        memset(dp[cur],0x3f,(sum+1)*sizeof(int));        for(int j=0; j<=sum; j++)        {            if(b[i])dp[cur][j]=min(dp[cur][j],dp[pre][j]+b[i]);            if(j>=a[i]&&a[i])dp[cur][j]=min(dp[cur][j],dp[pre][j-a[i]]);            if(j>=c[i]&&c[i])dp[cur][j]=min(dp[cur][j],dp[pre][j-c[i]]+c[i]);        }    }    mn=INF;    for(int i=0; i<=sum; i++)        mn=min(mn,max(dp[n&1][i],i));    write(mn);    return 0;}
]]>
+ 洛谷P2224 [HNOI2001]产品加工题解

题目链接:P2224[HNOI2001]产品加工

题意

某加工厂有 A、B两台机器,来加工的产品可以由其中任何一台机器完成,或者两台机器共同完成。由于受到机器性能和产品特性的限制,不同的机器加工同一产品所需的时间会不同,若同时由两台机器共同进行加工,所完成任务又会不同。

某一天,加工厂接到 \(n\)个产品加工的任务,每个任务的工作量不尽一样。

你的任务就是:已知每个任务在 A 机器上加工所需的时间 \(t_1\),B 机器上加工所需的时间 \(t_2\) 及由两台机器共同加工所需的时间 \(t_3\),请你合理安排任务的调度顺序,使完成所有\(n\) 个任务的总时间最少。

显然dp

\(dp[i][j]\) 表示只考虑前 \(i\) 个物品,第一台机器花了 \(j\) 时间,第二台机器花的最少时间

不难发现 \[dp[i][j]=\min(dp[i-1][j]+b[i],dp[i-1][j-a[i]],dp[i-1][j-c[i]]+c[i])\] 然后滚动数组一下就好了

注意这题卡常很恶心(恼

代码:

#include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define INF 0x3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(3e4+65)int n,a[N],b[N],c[N],dp[2][N],sum,cur,pre,mn;int min(int a,int b){return a>b?b:a;}int max(int a,int b){return a>b?a:b;}signed main(){    read(n);    memset(dp,0x3f,sizeof(dp));dp[0][0]=0;    for(int i=1; i<=n; i++)    {        read(a[i]),read(b[i]),read(c[i]);        sum+=max(a[i],max(b[i],c[i]));        cur=i&1,pre=cur^1;        memset(dp[cur],0x3f,(sum+1)*sizeof(int));        for(int j=0; j<=sum; j++)        {            if(b[i])dp[cur][j]=min(dp[cur][j],dp[pre][j]+b[i]);            if(j>=a[i]&&a[i])dp[cur][j]=min(dp[cur][j],dp[pre][j-a[i]]);            if(j>=c[i]&&c[i])dp[cur][j]=min(dp[cur][j],dp[pre][j-c[i]]+c[i]);        }    }    mn=INF;    for(int i=0; i<=sum; i++)        mn=min(mn,max(dp[n&1][i],i));    write(mn);    return 0;}
]]>
@@ -5993,11 +5993,11 @@ - 洛谷P2260 [清华集训2012]模积和 题解 - - /2022/05/25/luo-gu-p2260-qing-hua-ji-xun-2012-mo-ji-he-ti-jie/ + 洛谷P2257 YY的GCD 题解 + + /2022/05/25/luo-gu-p2257-yy-de-gcd-ti-jie/ - 洛谷P2260 [清华集训2012]模积和 题解

题目链接:P2260 [清华集训2012]模积和

题意

根据容斥原理

然后推推柿子

有一个小的公式,这里就不证明了(逃

然后到这里就没啥难度了

考虑数论分块

代码如下

#include <bits/stdc++.h>using namespace std;#define int long longconst int p=19940417;const int inv2=9970209;const int inv6=3323403;#define sum1(x) ((x)%p*(x+1)%p*inv2%p)#define sum2(x) ((x)%p*(x+1)%p*(2*(x)%p+1)%p*inv6%p)int solve(int x){    int res=x%p*x%p;    for(int l=1,r; l<=x; l=r+1)    {        r=x/(x/l);        res=(res-(sum1(r)-sum1(l-1))%p*(x/l)%p)%p;    }    return res;}int n,m,a,b,c,ans;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> m;    if(n>m)swap(n,m);    ans=(solve(n)*solve(m)%p-n%p*n%p*m%p)%p;    for(int l=1,r; l<=n; l=r+1)    {        r=min(n/(n/l),m/(m/l));        a=(a+(sum1(r)-sum1(l-1))%p*m%p*(n/l)%p)%p;        b=(b+(sum1(r)-sum1(l-1))%p*n%p*(m/l)%p)%p;        c=(c+(sum2(r)-sum2(l-1))%p*(n/l)%p*(m/l)%p)%p;    }ans=((ans+a+b-c)%p+p)%p;    cout << ans << endl;    return 0;}
]]>
+ 洛谷P2257 YY的GCD 题解

题意

多组询问。

给定 \(N, M\),求 \(1 \leq x \leq N\),\(1 \leq y \leq M\) 且 \(\gcd(x,y)\) 为质数的 \((x,y)\) 有多少对。

一句话题意

\(\mathcal{P}\)为所有素数组成的集合,求 \[\sum_{i=1}^{n}\sum_{j=1}^{m}\left[\gcd(i,j)\in\mathcal{P}\right]\] 不妨假设 \(n\le m\)

然后么推推柿子

友情提醒,这道题涉及经典操作:转化枚举量、构造莫比乌斯反演

不知道的可以先去看看别的题,比如这个题 link\[\begin{aligned}&\sum_{i=1}^{n}\sum_{j=1}^{m}\left[\gcd(i,j)\in\mathcal{P}\right]\\=&\sum_{k=1}^{n}\sum_{i=1}^{n}\sum_{j=1}^m\left[\gcd(i,j)=k\right][k\in \mathcal{P}]\\=&\sum_{k=1}^{n}\sum_{i=1}^{\left\lfloor{\frac{n}{k}}\right\rfloor}\sum_{j=1}^{\left\lfloor{\frac{m}{k}}\right\rfloor}[\gcd(i,j)=1][k\in \mathcal{P}]\\=&\sum_{k=1}^{n}\sum_{i=1}^{\left\lfloor{\frac{n}{k}}\right\rfloor}\sum_{j=1}^{\left\lfloor{\frac{m}{k}}\right\rfloor}\sum_{d\mid\gcd(i,j)}\mu(d)[k \in \mathcal{P}]\\=&\sum_{k=1}^{n}\sum_{d=1}^{\left\lfloor{\frac{n}{k}}\right\rfloor}\mu(d)\left\lfloor{\dfrac{n}{kd}}\right\rfloor\left\lfloor{\dfrac{m}{kd}}\right\rfloor[k\in \mathcal{P}]\\=&\sum_{l=1}^{n}\left\lfloor{\dfrac{n}{l}}\right\rfloor\left\lfloor{\dfrac{m}{l}}\right\rfloor\sum_{k\mid l\land k \in \mathcal{P}}\mu\left(\dfrac{l}{k}\right)\end{aligned}\] 注:\(\land\)表示逻辑与,可认为其等价于 “且”

\(f(x) = \sum\limits_{k \mid x\land k\in \mathcal{P}}\mu\left(\dfrac{x}{k}\right)\)

然后这个题其实到这里就差不多了

前面的用数论分块

后面那个 \(\mu\)可以在筛法里面求

时间复杂度 \(O(n\log \log n +Q\sqrt{n})\)

#include <bits/stdc++.h>using namespace std;// #define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e7+15)int Q,x,y;int prime[6000005],mu[N],pcnt,sum[N];bool ck[N];void Mobius(){    mu[1]=1;    for(int i=2; i<=N-5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            mu[i]=-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N-5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                mu[pos]=-mu[i];            }else            {                mu[pos]=0;                break;            }        }    }    for(int i=1; i<=pcnt; i++)        for(int j=1; prime[i]*j<=N-5; j++)            sum[j*prime[i]]+=mu[j];                for(int i=1; i<=N-5; i++)        sum[i]+=sum[i-1];}long long solve(int n,int m){    long long res=0;    for(int l=1,r; l<=min(n,m); l=r+1)    {        r=min(n/(n/l),m/(m/l));        res+=(long long)(sum[r]-sum[l-1])*(n/l)*(m/l);    }    return res;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(Q);    Mobius();    while(Q--)    {        read(x);read(y);        write(solve(x,y));pc('\n');    }    return 0;}

这份代码很慢,跑了7.74s, 目前最优解就跑了5.69s (截止20220511)

其实这个 \(O(n \log\log n)\)的枚举并没有必要

考虑在线性筛的过程中更新答案 \[f(x) = \sum\limits_{k \mid x\land k \in\mathcal{P}}\mu\left(\dfrac{x}{k}\right)\]\(x=ab\) ,其中 \(a\) 为 \(x\) 的最小质因数

\(p\)为当前线性筛枚举的一个用于标记的素数(prime[j]

  • \(x \in \mathcal{P}\),则显然 \(f(x) = \mu(1) = 1\)

  • \(x\notin \mathcal P \land b \bmod a= 0\)

    则有 \(x = a^pb^{\prime},b=a^{p-1}b^{\prime},p\) 为大于 \(1\) 的正整数 (即 \(x\) 有多个最小质因数 )

    • \(b\)没有多个最小质因数,则当且仅当 \(p=a\) 时 \(\mu\left(\dfrac{x}{p}\right) = \mu(b) \ne0\)

      因此有 \(f(x) = \mu(b)\)

    • \(b\)含有多个最小质因数,则 \(\forall p \in\mathcal{P}^{\prime},\mu\left(\dfrac{x}{p}\right)=0,\mathcal{P}^{\prime}\)为当前筛出的素数集

      不过我们无须单独考虑这种情况,因为此时仍然有 \(\mu\left(\dfrac{x}{p}\right) =\mu(b)\)

  • \(x\notin \mathcal{P} \land b \bmoda \ne 0\)

    则此时 \(x\) 有唯一且最小的质因数\(a\)

    显然有 \(\mu(x) = -\mu(b)\),因为它仅仅多了一个本质不同的质因子

    则对于 \(\mu\left(\dfrac{x}{p}\right) =-\mu\left(\dfrac{b}{p}\right)\) 也是成立的

    因此 \(f(b)\) 中的每一项在 \(f(x)\) 中都能找到对应的

    注意到 \[f(x) = f(ab) =\sum\limits_{k \mid ab\land k \in\mathcal{P}}\mu\left(\dfrac{ab}{k}\right)\] 由于 \(\gcd(a,b)=1\)(显然),则 \(f(x)\) 仅仅比 \(f(b)\) 多了一项 \(\mu\left(\dfrac{ab}{a}\right) =\mu(b)\)

    则有 \(f(x) =-f(b)+\mu(b)\)

综上,有 \[f(x) = \begin{cases}\mu(1),&x \in \mathcal{P},\\\\\mu(b),&x \notin \mathcal{P} \land b \bmod a=0,\\\\-f(b)+\mu(b),&x\notin \mathcal{P} \land b\bmod a \ne 0.\end{cases}\] 于是我们就可以在 \(O(n +Q\sqrt{n})\) 的时间复杂度内解决这个问题了

然后稍微卡一卡常,就跑的飞快

对了,目前最优解其实是我的代码哦(虽然没啥用

代码如下

#include <bits/stdc++.h>using namespace std;// #define int long longtypedef long long ll;#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e7+15)int Q,mx,x[10005],y[10005];;int prime[1000005],mu[N],pcnt,f[N];bool ck[N];void Mobius(){    mu[1]=1;    for(int i=2; i<=mx+5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            mu[i]=-1;            f[i]=1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=mx+5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                f[pos]=-f[i]+mu[i];                mu[pos]=-mu[i];            }else            {                f[pos]=mu[i];                mu[pos]=0;                break;            }        }        f[i]+=f[i-1];    }}ll solve(int n,int m){    ll res=0;    for(int l=1,r; l<=min(n,m); l=r+1)    {        r=min(n/(n/l),m/(m/l));        res+=(ll)(f[r]-f[l-1])*(n/l)*(m/l);    }    return res;}signed main(){    read(Q);    for(int i=1; i<=Q; i++)        read(x[i]),read(y[i]);    mx=max(*max_element(x+1,x+1+Q),*max_element(y+1,y+1+Q));    Mobius();    for(int i=1; i<=Q; i++)        write(solve(x[i],y[i])),pc('\n');    return 0;}

如果有什么错误或者问题的话,欢迎留言

参考文献

[1] https://www.luogu.com.cn/blog/An-Amazing-Blog/solution-p2257

[2] https://siyuan.blog.luogu.org/solution-p2257

]]>
@@ -6009,6 +6009,8 @@ + 算法 + 数学 @@ -6018,11 +6020,11 @@ - 洛谷P2216 [HAOI2007]理想的正方形 题解 - - /2022/05/25/luo-gu-p2216-haoi2007-li-xiang-de-zheng-fang-xing-ti-jie/ + 洛谷P2260 [清华集训2012]模积和 题解 + + /2022/05/25/luo-gu-p2260-qing-hua-ji-xun-2012-mo-ji-he-ti-jie/ - 洛谷P2216 [HAOI2007]理想的正方形 题解

题目链接:P2216 [HAOI2007]理想的正方形

题意: 有一个 $a \times b$ 的整数组成的矩阵,现请你从中找出一个 $n \times n$的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

去年集训写过这题,也是去年集训唯一过了的(那个数据水

upd.20220722 今年集训又来了。

当时的解法稍微改了一下也可以过,放在本文最后

正解:单调队列

考虑先对每一行跑一次单调队列

构造一个 $m \times (n-k+1)$ 的矩阵(设 $n$ 行 $m$ 列)

再交换一下行列,跑出一个 $(m-k+1)\times (n-k+1)$ 的矩阵

然后就直接枚举就好了,其实没啥难的

时间复杂度 $O(n^2)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e3+15)int st,en,q[N];int n,m,k,in[N][N],a[N][N],b[N][N],mn[N][N],mx[N][N];void solve1(int *num,int rowid,int n,int len,int f){    st=en=0;    for(int i=1; i<=n; i++)    {        if(f==1)while(st<en&&num[i]<num[q[en]])--en;        if(f==2)while(st<en&&num[i]>num[q[en]])--en;        q[++en]=i;        while(st<en&&q[st+1]+len<=i)++st;        if(i>=len&&f==1)a[i-len+1][rowid]=num[q[st+1]];        if(i>=len&&f==2)b[i-len+1][rowid]=num[q[st+1]];    }}void solve2(int *num,int rowid,int n,int len,int f){    st=en=0;    for(int i=1; i<=n; i++)    {        if(f==1)while(st<en&&num[i]<num[q[en]])--en;        if(f==2)while(st<en&&num[i]>num[q[en]])--en;        q[++en]=i;        while(st<en&&q[st+1]+len<=i)++st;        if(i>=len&&f==1)mn[i-len+1][rowid]=num[q[st+1]];        if(i>=len&&f==2)mx[i-len+1][rowid]=num[q[st+1]];    }}signed main(){    read(n);read(m);read(k);    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)            read(in[i][j]);    for(int i=1; i<=n; i++)        solve1(in[i],i,m,k,1),solve1(in[i],i,m,k,2);    for(int i=1; i<=m-k+1; i++)        solve2(a[i],i,n,k,1),solve2(b[i],i,n,k,2);    int ans=INF;    for(int i=1; i<=n-k+1; i++)        for(int j=1; j<=m-k+1; j++)            ans=min(ans,mx[i][j]-mn[i][j]);    printf("%lld\n",ans);    return 0;}

这里是奇怪的解法

构建一个二维的st表,和上面思路差不多

时间复杂度 $O(n^2\log n)$ ,也可以过

代码:

#include <bits/stdc++.h>using namespace std;//#define int long long#define INF 0x3f3f3f3f3f3f3f3ftypedef long long ll;namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define MAXN (int)(1e3+5)int n,m,k,N,M;int MX[MAXN][MAXN];int MN[MAXN][MAXN];int stmx[MAXN][MAXN][11];int stmn[MAXN][MAXN][11];ll ans=INF;void change1(int x,int u,int v){    stmx[x][u][0]=v;    stmn[x][u][0]=v;    for(int i=1; u-(1<<(i-1))>=1; i++)    {        stmx[x][u][i]=max(stmx[x][u][i-1],stmx[x][u-(1<<(i-1))][i-1]);        stmn[x][u][i]=min(stmn[x][u][i-1],stmn[x][u-(1<<(i-1))][i-1]);    }}void change2(int x,int u){    stmx[x][u][0]=MX[x][u];    stmn[x][u][0]=MN[x][u];    for(int i=1; u-(1<<(i-1))>=1; i++)    {        stmx[x][u][i]=max(stmx[x][u][i-1],stmx[x][u-(1<<(i-1))][i-1]);        stmn[x][u][i]=min(stmn[x][u][i-1],stmn[x][u-(1<<(i-1))][i-1]);    }}int qrymx(int x,int l,int r){    int k=(double)log(r-l+1)/log(2);    return max(stmx[x][l+(1<<k)-1][k],stmx[x][r][k]);}   int qrymn(int x,int l,int r){    int k=(double)log(r-l+1)/log(2);    return min(stmn[x][l+(1<<k)-1][k],stmn[x][r][k]);}int qrymx(int x,int l){    int r=l+k-1;    int p=(double)log(r-l+1)/log(2);    return max(stmx[x][l+(1<<p)-1][p],stmx[x][r][p]);}   int qrymn(int x,int l){    int r=l+k-1;    int p=(double)log(r-l+1)/log(2);    return min(stmn[x][l+(1<<p)-1][p],stmn[x][r][p]);}signed main(){    read(n);read(m);read(k);    for(int i=1; i<=n; i++)        for(int j=1,x; j<=m; j++)        {            read(x);            change1(i,j,x);        }    for(int j=1; j<=m-k+1; j++)        for(int i=1; i<=n; i++)            MX[j][i]=qrymx(i,j,j+k-1),            MN[j][i]=qrymn(i,j,j+k-1);    for(int i=1; i<=m-k+1; i++)        for(int j=1; j<=n; j++)            change2(i,j);    for(int i=1; i<=m-k+1; i++)        for(int j=1; j<=n-k+1; j++)        ans=min(ans,(ll)qrymx(i,j)-qrymn(i,j));    printf("%lld\n",ans);    return 0;}
]]>
+ 洛谷P2260[清华集训2012]模积和 题解

题目链接:P2260[清华集训2012]模积和

题意

\[\left(\sum_{i=1}^{n}\sum_{j=1}^{m}(n\bmod i)\times(m\bmod j),i\ne j\right) \mod 19940417\]

根据容斥原理 \[\sum_{i=1}^{n}\sum_{j=1}^{m}(n\bmod i)\times(m\bmod j),i\ne j \\=\sum_{i=1}^{n}\sum_{j=1}^{m}(n\bmod i)\times(m\bmod j) -\sum_{i=1}^{n}(n\bmod i)\times(m\bmod i)\] 然后推推柿子 \[\begin{aligned}&\sum_{i=1}^{n}\sum_{j=1}^{m}(n\bmod i)\times(m\bmod j) -\sum_{i=1}^{n}(n\bmod i)\times(m\bmod i)\\&=\sum_{i=1}^{n}\left(n-i\left\lfloor{\dfrac{n}{i}}\right\rfloor\right)\times\sum_{j=1}^{m}\left(m-j\left\lfloor{\dfrac{m}{j}}\right\rfloor\right)-\sum_{i=1}^{n}\left(n-i\left\lfloor{\dfrac{n}{i}}\right\rfloor\right)\times\left(m-i\left\lfloor{\dfrac{m}{i}}\right\rfloor\right)\\&=\left(n^2-\sum_{i=1}^{n}i\left\lfloor{\dfrac{n}{i}}\right\rfloor\right)\times\left(m^2-\sum_{i=1}^{m}i\left\lfloor{\dfrac{m}{i}}\right\rfloor\right)-\sum_{i=1}^{n}\left(nm-mi\left\lfloor{\dfrac{n}{i}}\right\rfloor-ni\left\lfloor{\dfrac{m}{i}}\right\rfloor+i^2\left\lfloor{\dfrac{n}{i}}\right\rfloor\left\lfloor{\dfrac{m}{i}}\right\rfloor\right)\\&=\left(n^2-\sum_{i=1}^{n}i\left\lfloor{\dfrac{n}{i}}\right\rfloor\right)\times\left(m^2-\sum_{i=1}^{m}i\left\lfloor{\dfrac{m}{i}}\right\rfloor\right)-n^2m+\sum_{i=1}^{n}\left(mi\left\lfloor{\dfrac{n}{i}}\right\rfloor+ni\left\lfloor{\dfrac{m}{i}}\right\rfloor-i^2\left\lfloor{\dfrac{n}{i}}\right\rfloor\left\lfloor{\dfrac{m}{i}}\right\rfloor\right)\end{aligned}\] 有一个小的公式,这里就不证明了(逃 \[\sum_{i=1}^{n}i^2 = \dfrac{n(n+1)(2n+1)}{6}\] 然后到这里就没啥难度了

考虑数论分块

代码如下

#include <bits/stdc++.h>using namespace std;#define int long longconst int p=19940417;const int inv2=9970209;const int inv6=3323403;#define sum1(x) ((x)%p*(x+1)%p*inv2%p)#define sum2(x) ((x)%p*(x+1)%p*(2*(x)%p+1)%p*inv6%p)int solve(int x){    int res=x%p*x%p;    for(int l=1,r; l<=x; l=r+1)    {        r=x/(x/l);        res=(res-(sum1(r)-sum1(l-1))%p*(x/l)%p)%p;    }    return res;}int n,m,a,b,c,ans;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> m;    if(n>m)swap(n,m);    ans=(solve(n)*solve(m)%p-n%p*n%p*m%p)%p;    for(int l=1,r; l<=n; l=r+1)    {        r=min(n/(n/l),m/(m/l));        a=(a+(sum1(r)-sum1(l-1))%p*m%p*(n/l)%p)%p;        b=(b+(sum1(r)-sum1(l-1))%p*n%p*(m/l)%p)%p;        c=(c+(sum2(r)-sum2(l-1))%p*(n/l)%p*(m/l)%p)%p;    }ans=((ans+a+b-c)%p+p)%p;    cout << ans << endl;    return 0;}
]]>
@@ -6034,9 +6036,7 @@ - 算法 - - 数据结构 + 数学 @@ -6049,7 +6049,7 @@ /2022/05/25/luo-gu-p2261-cqoi2007-yu-shu-qiu-he-ti-jie/ - 洛谷P2261 [CQOI2007]余数求和 题解

题目链接:P2261 [CQOI2007]余数求和

题意

给定 $n,k$ ,求

考虑数论分块

注意到

若 $k < n$ ,显然 $\forall i > k$ 有 $\left\lfloor\dfrac{k}{i}\right\rfloor=0$

故可直接计算

若 $k \ge n$ ,则判断一下右边界不要超过 $n$ 即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long longint n,k;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> k;int res=n*k;    for(int l=1,r; l<=min(k,n); l=r+1)    {        r=min(k/(k/l),n);        res-=(k/l)*(r-l+1)*(l+r)/2;    }    cout << res << endl;    return 0;}
]]>
+ 洛谷P2261 [CQOI2007]余数求和题解

题目链接:P2261[CQOI2007]余数求和

题意

给定 \(n,k\) ,求 \[G(n,k)= \sum_{i=1}^{n}k \bmod i\]

考虑数论分块

注意到 \[\begin{aligned}\sum_{i=1}^{n}k \bmod i &=\sum_{i=1}^{n}\left(k-i\left\lfloor\dfrac{k}{i}\right\rfloor\right)\\&=nk-\sum_{i=1}^{n}i\left\lfloor\dfrac{k}{i}\right\rfloor\end{aligned}\]\(k < n\) ,显然 \(\forall i > k\) 有 \(\left\lfloor\dfrac{k}{i}\right\rfloor=0\)

故可直接计算 \[nk-\sum_{i=1}^{k}i\left\lfloor\dfrac{k}{i}\right\rfloor\]\(k \ge n\),则判断一下右边界不要超过 \(n\)即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long longint n,k;signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> k;int res=n*k;    for(int l=1,r; l<=min(k,n); l=r+1)    {        r=min(k/(k/l),n);        res-=(k/l)*(r-l+1)*(l+r)/2;    }    cout << res << endl;    return 0;}
]]>
@@ -6076,7 +6076,7 @@ /2022/05/25/luo-gu-p2350-haoi2012-wai-xing-ren-ti-jie/ - 洛谷P2350 [HAOI2012]外星人 题解

题目链接:P2350 [HAOI2012]外星人

题意

多组数据

给出正整数 $N$ 的标准分解形式 $\prod_{i=1}^{m}p_i^{q_i}$

求最小的正整数 $x$ 使得 $\varphi^{x}(N) = 1$

注意到

对于含质因子 $2$ 的数,迭代一次会消去一个 $2$

对于不含质因子 $2$ 的数,迭代一次会产生至少一个 $2$

不难发现,答案与产生的 $2$ 的个数有关

则对于所有含质因子 $2$ 的 $N$ 答案即为迭代产生的所有的 $2$ 的个数

否则答案就是额外迭代一次后,再重复迭代产生的所有的 $2$ 的个数

设 $f(n)$ 为 $n$ 在迭代过程中产生的 $2$ 的个数 $\left(n=\sum_{i=1}^{s}p_i^{q_i}\right)$

当 $n$ 为素数时,有

当 $n$ 为合数时,有

这个东西可以用筛法去更新答案,预处理即可

时间复杂度 $O(\max(p))$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n;int f[N],prime[N],ck[N],pcnt;void Euler(){    ck[1]=1;f[1]=1;    for(int i=2; i<=N; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            f[i]=f[i-1];        }        for(int j=1; j<=pcnt&&i*prime[j]<=N; j++)        {            int pos=i*prime[j];            ck[pos]=1;f[pos]=f[i]+f[prime[j]];            if(i%prime[j]==0)break;        }    }}void solve(){    // clear();    int res=0,flag=1;    cin >> n;    for(int i=1,p,q; i<=n; i++)    {        cin >> p >> q;        if(p==2)flag=0;        res+=f[p]*q;    }    cout << res+flag << endl;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    int _Q=1;cin >> _Q;    Euler();    while(_Q--)solve();    return 0;}
]]>
+ 洛谷P2350 [HAOI2012]外星人题解

题目链接:P2350[HAOI2012]外星人

题意

多组数据

给出正整数 \(N\) 的标准分解形式\(\prod_{i=1}^{m}p_i^{q_i}\)

求最小的正整数 \(x\) 使得 \(\varphi^{x}(N) = 1\)

注意到 \[\varphi\left(\prod\limits_{i=1}^{m}p_i^{q_i}\right) =\prod_{i=1}^{m}(p_i-1)\times p_i^{q_i-1}\] 对于含质因子 \(2\)的数,迭代一次会消去一个 \(2\)

对于不含质因子 \(2\)的数,迭代一次会产生至少一个 \(2\)

不难发现,答案与产生的 \(2\)的个数有关

则对于所有含质因子 \(2\)\(N\) 答案即为迭代产生的所有的 \(2\) 的个数

否则答案就是额外迭代一次后,再重复迭代产生的所有的 \(2\) 的个数

\(f(n)\)\(n\) 在迭代过程中产生的 \(2\) 的个数 \(\left(n=\sum_{i=1}^{s}p_i^{q_i}\right)\)

\(n\) 为素数时,有 \[f(n)=f(n-1)\] 当 \(n\) 为合数时,有 \[f(n)=\sum_{i=1}^{s} q_i f(p_i)\] 这个东西可以用筛法去更新答案,预处理即可

时间复杂度 \(O(\max(p))\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n;int f[N],prime[N],ck[N],pcnt;void Euler(){    ck[1]=1;f[1]=1;    for(int i=2; i<=N; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            f[i]=f[i-1];        }        for(int j=1; j<=pcnt&&i*prime[j]<=N; j++)        {            int pos=i*prime[j];            ck[pos]=1;f[pos]=f[i]+f[prime[j]];            if(i%prime[j]==0)break;        }    }}void solve(){    // clear();    int res=0,flag=1;    cin >> n;    for(int i=1,p,q; i<=n; i++)    {        cin >> p >> q;        if(p==2)flag=0;        res+=f[p]*q;    }    cout << res+flag << endl;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    int _Q=1;cin >> _Q;    Euler();    while(_Q--)solve();    return 0;}
]]>
@@ -6103,7 +6103,7 @@ /2022/05/25/luo-gu-p2522-haoi2011-problem-b-ti-jie/ - 洛谷P2522 [HAOI2011]Problem b 题解

题目链接:P2522 [HAOI2011]Problem b

题意:对于给出的 $n$ 个询问,每次求有多少个数对 $(x,y)$,满足 $a \le x \le b$,$c \le y \le d$,且 $\gcd(x,y) = k$,$\gcd(x,y)$ 函数为 $x$ 和 $y$ 的最大公约数。

一句话题意:

根据容斥原理,不妨令

则答案为

然后推推柿子

考虑变换求和顺序,得

根据 $1\sim n$ 中 $d$ 的倍数有 $\left\lfloor\dfrac{n}{d}\right\rfloor$ 个,

以及 $\left\lfloor\dfrac{n}{kd}\right\rfloor=\left\lfloor\dfrac{\left\lfloor\frac{n}{k}\right\rfloor}{d}\right\rfloor$

可得

然后数论分块就好了

时间复杂度 $O(N+Q\sqrt{n})$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e4+14)int prime[N],pcnt,mu[N],sum[N];bool ck[N];void Mobius(){    mu[1]=1;    for(int i=2; i<=N; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            mu[i]=-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                mu[pos]=-mu[i];            }else            {                mu[pos]=0;                break;            }        }    }    for(int i=1; i<=N; i++)        sum[i]+=sum[i-1]+mu[i];}int Q,a,b,c,d,k;int solve(int n,int m){    int res=0;    n/=k;m/=k;    for(int l=1,r; l<=min(n,m); l=r+1)    {        r=min(n/(n/l),m/(m/l));        res+=(sum[r]-sum[l-1])*(n/l)*(m/l);    }    return res;}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    Mobius();read(Q);    while(Q--)    {        read(a);read(b);read(c);read(d);read(k);        write(solve(b,d)+solve(a-1,c-1)-solve(b,c-1)-solve(a-1,d));        pc('\n');    }    return 0;}
]]>
+ 洛谷P2522 [HAOI2011]Problem b题解

题目链接:P2522[HAOI2011]Problem b

题意:对于给出的 \(n\) 个询问,每次求有多少个数对 \((x,y)\),满足 \(a\le x \le b\)\(c \le y \led\),且 \(\gcd(x,y) = k\)\(\gcd(x,y)\) 函数为 \(x\) 和 \(y\) 的最大公约数。

一句话题意:

\[\sum_{i=a}^{b}\sum_{j=c}^{d}[\gcd(i,j)=k]\] 根据容斥原理,不妨令 \[F(n,m) = \sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=k]\] 则答案为 \[F(b,d)-F(b,c-1)-F(a-1,d)+F(a-1,c-1)\] 然后推推柿子 \[\begin{aligned}&\sum_{i=1}^{n}\sum_{j=1}^{m}[\gcd(i,j)=k]\\&=\sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[\gcd(i,j)=1]\\&=\sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}\sum_{d\mid\gcd(i,j)}\mu(d)\end{aligned}\] 考虑变换求和顺序,得 \[\sum_{d=1}^{\min\left(\left\lfloor\frac{n}{k}\right\rfloor,\left\lfloor\frac{m}{k}\right\rfloor\right)}\mu(d)\sum_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}[d\midi]\sum_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[d\mid j]\] 根据 \(1\sim n\)\(d\) 的倍数有 \(\left\lfloor\dfrac{n}{d}\right\rfloor\)个,

以及 \(\left\lfloor\dfrac{n}{kd}\right\rfloor=\left\lfloor\dfrac{\left\lfloor\frac{n}{k}\right\rfloor}{d}\right\rfloor\)

可得 \[\sum_{d=1}^{\min\left(\left\lfloor\frac{n}{k}\right\rfloor,\left\lfloor\frac{m}{k}\right\rfloor\right)}\mu(d)\left\lfloor\dfrac{n}{kd}\right\rfloor\left\lfloor\dfrac{m}{kd}\right\rfloor\] 然后数论分块就好了

时间复杂度 \(O(N+Q\sqrt{n})\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e4+14)int prime[N],pcnt,mu[N],sum[N];bool ck[N];void Mobius(){    mu[1]=1;    for(int i=2; i<=N; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            mu[i]=-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                mu[pos]=-mu[i];            }else            {                mu[pos]=0;                break;            }        }    }    for(int i=1; i<=N; i++)        sum[i]+=sum[i-1]+mu[i];}int Q,a,b,c,d,k;int solve(int n,int m){    int res=0;    n/=k;m/=k;    for(int l=1,r; l<=min(n,m); l=r+1)    {        r=min(n/(n/l),m/(m/l));        res+=(sum[r]-sum[l-1])*(n/l)*(m/l);    }    return res;}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    Mobius();read(Q);    while(Q--)    {        read(a);read(b);read(c);read(d);read(k);        write(solve(b,d)+solve(a-1,c-1)-solve(b,c-1)-solve(a-1,d));        pc('\n');    }    return 0;}
]]>
@@ -6130,7 +6130,7 @@ /2022/05/25/luo-gu-p2568-gcd-ti-jie/ - 洛谷P2568 GCD 题解

题目链接:P2568 GCD

题意

给定正整数 $n$,求 $1\le x,y\le n$ 且 $\gcd(x,y)$为素数的数对 $(x,y)$ 有多少对。

$1 \le n\le 10^7$

正着去想不太好搞,那就反过来

考虑一个素数对答案的贡献

显然所有满足条件的 $(x,y)$ 都可以写作 $(ap_i,bp_i)$ 的形式,其中 $\gcd(x,y)=p_i$

注意到 $\gcd(a,b)=1$

则对于每个素数 $p\ (p\le n)$ ,它对答案的贡献就是

其中,$k=\left\lfloor\dfrac{n}{p}\right\rfloor$, $\varphi(n)$ 为欧拉函数

$\times 2$ 是因为 $(x,y)$ 为有序点对,$-1$ 是因为 $(1,1)$ 会被多算一次

直接线性筛 $\varphi(n)$ 然后前缀和一下就好了

时间复杂度 $O(n)$ ,记得开 $\text{long long}$ 哦

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e7+15)int prime[N],pcnt,phi[N],sum[N];bool ck[N];void Euler(int n){    phi[1]=1;ck[1]=1;    for(int i=2; i<=n; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            phi[i]=i-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=n; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                phi[pos]=phi[i]*phi[prime[j]];            }else             {                phi[pos]=phi[i]*prime[j];                break;            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n,res=0;    cin >> n;    Euler(n);    for(int i=1; i<=n; i++)        sum[i]+=sum[i-1]+phi[i];    for(int i=1; i<=pcnt&&prime[i]<=n; i++)        res+=sum[n/prime[i]]*2-1;    cout << res << endl;    return 0;}
]]>
+ 洛谷P2568 GCD 题解

题目链接:P2568GCD

题意

给定正整数 \(n\),求 \(1\le x,y\le n\) 且 \(\gcd(x,y)\)为素数的数对 \((x,y)\) 有多少对。

\(1 \le n\le 10^7\)

正着去想不太好搞,那就反过来

考虑一个素数对答案的贡献

显然所有满足条件的 \((x,y)\)都可以写作 \((ap_i,bp_i)\) 的形式,其中\(\gcd(x,y)=p_i\)

注意到 \(\gcd(a,b)=1\)

则对于每个素数 \(p\ (p\le n)\),它对答案的贡献就是 \[2\sum\limits_{i=1}^{k}\varphi(i)-1\] 其中,\(k=\left\lfloor\dfrac{n}{p}\right\rfloor\),\(\varphi(n)\) 为欧拉函数

\(\times 2\) 是因为 \((x,y)\) 为有序点对,\(-1\) 是因为 \((1,1)\) 会被多算一次

直接线性筛 \(\varphi(n)\)然后前缀和一下就好了

时间复杂度 \(O(n)\) ,记得开 \(\text{long long}\) 哦

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e7+15)int prime[N],pcnt,phi[N],sum[N];bool ck[N];void Euler(int n){    phi[1]=1;ck[1]=1;    for(int i=2; i<=n; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            phi[i]=i-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=n; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                phi[pos]=phi[i]*phi[prime[j]];            }else             {                phi[pos]=phi[i]*prime[j];                break;            }        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int n,res=0;    cin >> n;    Euler(n);    for(int i=1; i<=n; i++)        sum[i]+=sum[i-1]+phi[i];    for(int i=1; i<=pcnt&&prime[i]<=n; i++)        res+=sum[n/prime[i]]*2-1;    cout << res << endl;    return 0;}
]]>
@@ -6157,7 +6157,7 @@ /2022/05/25/luo-gu-p2613-mo-ban-you-li-shu-qu-yu-ti-jie/ - 洛谷P2613 【模板】有理数取余 题解

题目链接:P2613 【模板】有理数取余

题意:给出 $c=\frac{a}{b}$ ,求 $\frac{a}{b} \bmod 19260817$

$0 \le a \le 10^{10001},1\le b \le 10^{10001}$

令 $m=19260817$

注意这里模意义下的有理数其实就是

简单推推柿子吧

显然有

当 $b_0 \ne 0$ 即 $m \nmid b$ 时,有

那么exgcd一下就出来了

当 $b_0=0$ 即 $m \mid b$ 时,分类讨论

  • 当 $m \mid a$ 时, $\forall c \in \Z,bc \equiv a \pmod{m}$ 恒成立,题目保证了没有这种情况
  • 当 $m\nmid a$ 时,$bc \equiv a \pmod{m}$ 无解

因此特判 $b_0 = 0$ 的情况即可

注意读入 $a,b$ 的时候要一边读一边取模

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()const int mod=19260817;int readint(){    char ch=gc();int res=0;    while(!isdigit(ch))ch=gc();    while(isdigit(ch))    {        res=(res<<1)+(res<<3)+(ch^48);        res%=mod;        ch=gc();    }    return res;}int exgcd(int a,int b,int &x,int &y){    int d=a;    if(!b)x=1,y=0;    else d=exgcd(b,a%b,y,x),y-=a/b*x;    return d;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int a,b;    a=readint();b=readint();    if(!b)return cout << "Angry!" << endl,0;    int x,y;    exgcd(b,mod,x,y);    x=(x%mod+mod)%mod;    cout << a*x%mod << endl;    return 0;}
]]>
+ 洛谷P2613 【模板】有理数取余题解

题目链接:P2613【模板】有理数取余

题意:给出 \(c=\frac{a}{b}\) ,求 \(\frac{a}{b} \bmod 19260817\)

\(0 \le a \le 10^{10001},1\le b \le10^{10001}\)

\(m=19260817\)

注意这里模意义下的有理数其实就是 \[\frac{a}{b} \pmod m = ab^{-1} \pmod m\] 简单推推柿子吧

显然有 \[bc \equiv a \pmod{m}\]

\[b_0=b \bmod m,a_0 = a \bmod m\]

\(b_0 \ne 0\)\(m \nmid b\) 时,有 \[c \equiv a_0 b_0^{-1} \pmod{m}\]

那么exgcd一下就出来了

\(b_0=0\)\(m \mid b\) 时,分类讨论

  • \(m \mid a\) 时, \(\forall c \in \Z,bc \equiv a \pmod{m}\)恒成立,题目保证了没有这种情况
  • \(m\nmid a\) 时,\(bc \equiv a \pmod{m}\) 无解

因此特判 \(b_0 = 0\) 的情况即可

注意读入 \(a,b\)的时候要一边读一边取模

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()const int mod=19260817;int readint(){    char ch=gc();int res=0;    while(!isdigit(ch))ch=gc();    while(isdigit(ch))    {        res=(res<<1)+(res<<3)+(ch^48);        res%=mod;        ch=gc();    }    return res;}int exgcd(int a,int b,int &x,int &y){    int d=a;    if(!b)x=1,y=0;    else d=exgcd(b,a%b,y,x),y-=a/b*x;    return d;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int a,b;    a=readint();b=readint();    if(!b)return cout << "Angry!" << endl,0;    int x,y;    exgcd(b,mod,x,y);    x=(x%mod+mod)%mod;    cout << a*x%mod << endl;    return 0;}
]]>
@@ -6184,7 +6184,7 @@ /2022/05/25/luo-gu-p2738-usaco4.1-li-ba-hui-lu-fence-loops-ti-jie/ - 洛谷P2738 [USACO4.1]篱笆回路Fence Loops 题解

题目链接:P2738 [USACO4.1]篱笆回路Fence Loops

题意:农夫布朗的牧场上的篱笆已经失去控制了。它们分成了1~200英尺长的线段。只有在线段的端点处才能连接两个线段,有时给定的一个端点上会有两个以上的篱笆。结果篱笆形成了一张网分割了布朗的牧场。布朗想将牧场恢复原样,出于这个考虑,他首先得知道牧场上哪一块区域的周长最小。 布朗将他的每段篱笆从1到N进行了标号(N=线段的总数)。他知道每段篱笆有如下属性:

该段篱笆的长度

该段篱笆的一端所连接的另一段篱笆的标号

该段篱笆的另一端所连接的另一段篱笆的标号

幸运的是,没有篱笆连接它自身。对于一组有关篱笆如何分割牧场的数据,写一个程序来计算出所有分割出的区域中最小的周长。

例如,标号1~10的篱笆由下图的形式组成(下面的数字是篱笆的标号):

         1 +---------------+ |\             /|2| \7          / | |  \         /  | +---+       /   |6 | 8  \     /10  |3|     \9  /     | |      \ /      | +-------+-------+     4       5

上图中周长最小的区域是由2,7,8号篱笆形成的。

这题建图比较麻烦

考虑将第 $i$ 条边的两个端点记为 $2i-1$ 和 $2i$ ,记录其连接的边的编号

然后 $O(n^2)$ 枚举一下这些边,将本质相同端点合并(用冰茶姬并查集)

去个重然后跑个最小环就好了

时间复杂度 $O(n^3)$

注意 INF 不可以开太大,不然会爆掉的

边去重好像不必要,但是保险起见就去一去

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x1f1f1f1f1f1f1f1fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(205)namespace MERGE{    int fa[N];    void init(int n){for(int i=1; i<=n; i++)fa[i]=i;}    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}    void merge(int u,int v){fa[find(u)]=find(v);}}using namespace MERGE;int n,cnt;struct InEdge{    int u,v,w,lto[N],rto[N];}in[N];int id[N],f[N][N],g[N][N];signed main(){    read(n);init(2*n);    for(int i=1,at,tl,tr; i<=n; i++)    {        read(at);        in[at].u=2*at-1;in[at].v=2*at;        read(in[at].w);read(tl);read(tr);        for(int j=1,x; j<=tl; j++)            read(x),in[at].lto[x]=1;        for(int j=1,x; j<=tr; j++)            read(x),in[at].rto[x]=1;    }    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)        {            if(i==j)continue;            if(in[i].lto[j]&&in[j].lto[i])                merge(in[i].u,in[j].u);            if(in[i].lto[j]&&in[j].rto[i])                merge(in[i].u,in[j].v);            if(in[i].rto[j]&&in[j].lto[i])                merge(in[i].v,in[j].u);            if(in[i].rto[j]&&in[j].rto[i])                merge(in[i].v,in[j].v);        }    for(int i=1; i<=2*n; i++)        if(fa[i]==i)id[i]=++cnt;    memset(g,0x1f,sizeof(g));    memset(f,0x1f,sizeof(f));    for(int i=1; i<=n; i++)    {        int &u=in[i].u,&v=in[i].v;        u=id[find(u)];v=id[find(v)];        g[u][v]=g[v][u]=min(g[u][v],in[i].w);        f[u][v]=f[v][u]=min(f[u][v],in[i].w);    }    int ans=INF;n=cnt;    // cout << cnt << endl;    for(int k=1; k<=n; k++)    {        for(int i=1; i<k; i++)            for(int j=i+1; j<k; j++)                ans=min(ans,f[i][j]+g[i][k]+g[k][j]);        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);    }    printf("%lld\n",ans);    return 0;}
]]>
+ 洛谷P2738[USACO4.1]篱笆回路Fence Loops 题解

题目链接:P2738[USACO4.1]篱笆回路Fence Loops

题意:农夫布朗的牧场上的篱笆已经失去控制了。它们分成了1~200英尺长的线段。只有在线段的端点处才能连接两个线段,有时给定的一个端点上会有两个以上的篱笆。结果篱笆形成了一张网分割了布朗的牧场。布朗想将牧场恢复原样,出于这个考虑,他首先得知道牧场上哪一块区域的周长最小。布朗将他的每段篱笆从1到N进行了标号(N=线段的总数)。他知道每段篱笆有如下属性:

该段篱笆的长度

该段篱笆的一端所连接的另一段篱笆的标号

该段篱笆的另一端所连接的另一段篱笆的标号

幸运的是,没有篱笆连接它自身。对于一组有关篱笆如何分割牧场的数据,写一个程序来计算出所有分割出的区域中最小的周长。

例如,标号1~10的篱笆由下图的形式组成(下面的数字是篱笆的标号):

         1 +---------------+ |\             /|2| \7          / | |  \         /  | +---+       /   |6 | 8  \     /10  |3|     \9  /     | |      \ /      | +-------+-------+     4       5

上图中周长最小的区域是由2,7,8号篱笆形成的。

这题建图比较麻烦

考虑将第 \(i\) 条边的两个端点记为\(2i-1\)\(2i\) ,记录其连接的边的编号

然后 \(O(n^2)\)枚举一下这些边,将本质相同端点合并(用冰茶姬并查集)

去个重然后跑个最小环就好了

时间复杂度 \(O(n^3)\)

注意 INF 不可以开太大,不然会爆掉的

边去重好像不必要,但是保险起见就去一去

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x1f1f1f1f1f1f1f1fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(205)namespace MERGE{    int fa[N];    void init(int n){for(int i=1; i<=n; i++)fa[i]=i;}    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}    void merge(int u,int v){fa[find(u)]=find(v);}}using namespace MERGE;int n,cnt;struct InEdge{    int u,v,w,lto[N],rto[N];}in[N];int id[N],f[N][N],g[N][N];signed main(){    read(n);init(2*n);    for(int i=1,at,tl,tr; i<=n; i++)    {        read(at);        in[at].u=2*at-1;in[at].v=2*at;        read(in[at].w);read(tl);read(tr);        for(int j=1,x; j<=tl; j++)            read(x),in[at].lto[x]=1;        for(int j=1,x; j<=tr; j++)            read(x),in[at].rto[x]=1;    }    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)        {            if(i==j)continue;            if(in[i].lto[j]&&in[j].lto[i])                merge(in[i].u,in[j].u);            if(in[i].lto[j]&&in[j].rto[i])                merge(in[i].u,in[j].v);            if(in[i].rto[j]&&in[j].lto[i])                merge(in[i].v,in[j].u);            if(in[i].rto[j]&&in[j].rto[i])                merge(in[i].v,in[j].v);        }    for(int i=1; i<=2*n; i++)        if(fa[i]==i)id[i]=++cnt;    memset(g,0x1f,sizeof(g));    memset(f,0x1f,sizeof(f));    for(int i=1; i<=n; i++)    {        int &u=in[i].u,&v=in[i].v;        u=id[find(u)];v=id[find(v)];        g[u][v]=g[v][u]=min(g[u][v],in[i].w);        f[u][v]=f[v][u]=min(f[u][v],in[i].w);    }    int ans=INF;n=cnt;    // cout << cnt << endl;    for(int k=1; k<=n; k++)    {        for(int i=1; i<k; i++)            for(int j=i+1; j<k; j++)                ans=min(ans,f[i][j]+g[i][k]+g[k][j]);        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);    }    printf("%lld\n",ans);    return 0;}
]]>
@@ -6211,7 +6211,7 @@ /2022/05/25/luo-gu-p2888-usaco07nov-cow-hurdles-s-ti-jie/ - 洛谷P2888 [USACO07NOV]Cow Hurdles S 题解

题目链接:P2888 [USACO07NOV]Cow Hurdles S

题意:Farmer John 想让她的奶牛准备郡级跳跃比赛,贝茜和她的伙伴们正在练习跨栏。她们很累,所以她们想消耗最少的能量来跨栏。 显然,对于一头奶牛跳过几个矮栏是很容易的,但是高栏却很难。于是,奶牛们总是关心路径上最高的栏的高度。 奶牛的训练场中有 $N (1 ≤ N ≤ 300)$ 个站台,分别标记为 $1\dots N$ 。所有站台之间有 $M (1 ≤ M ≤ 25,000)$ 条单向路径,第i条路经是从站台 $S_i$ 开始,到站台 $E_i$ ,其中最高的栏的高度为$H_i (1 ≤ H_i ≤ 1,000,000)$。无论如何跑,奶牛们都要跨栏。 奶牛们有 $T (1 ≤ T ≤ 40,000)$ 个训练任务要完成。第 $i$ 个任务包含两个数字 $A_i$ 和 $B_i (1 ≤ A_i ≤ N; 1 ≤ B_i ≤ N)$,表示奶牛必须从站台 $A_i$ 跑到站台 $B_i$ ,可以路过别的站台。奶牛们想找一条路径从站台 $A_i$ 到站台 $B_i$ ,使路径上最高的栏的高度最小。 你的任务就是写一个程序,计算出路径上最高的栏的高度的最小值。

分析题目可以发现这是一张具有非负权重的有向图

我们只需要找到一条路径使得这条路径上所有的边权中最大值最小即可

Floyd解法

别看这个 $n\le300$ ,跑起来还是飞快地(数据有点水)

容易推出方程 f[i][j]=min(f[i][j],max(f[i][k],f[k][j]));

不过我有点不放心就加了个小剪枝 qwq 虽然只快了3ms

代码如下

//Floyd#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,Q,f[305][305];signed main(){    read(n);read(m);read(Q);    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            f[i][j]=INF;    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        f[u][v]=min(f[u][v],w);    }    for(int k=1; k<=n; k++)        for(int i=1; i<=n; i++)            if(f[i][k]!=INF)                for(int j=1; j<=n; j++)                    f[i][j]=min(f[i][j],max(f[i][k],f[k][j]));    while(Q--)    {        int x,y;read(x);read(y);        write((f[x][y]==INF)?-1:f[x][y]);pc('\n');    }    return 0;}

其他解法

关于SPFA,她死了。

开个玩笑而已

不过我们可以用理论复杂度较低的dijkstra

注意到 $N\le300$

那么离线解决即可

时间复杂度 $O(N^2\log M + T)$

不过实际情况下由于dijkstra常数较大,跑的甚至比Floyd慢…

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,w,next;}e[50005];struct node{    int u,mn;    bool operator<(const node &o)const    {        return mn>o.mn;    }};int pos=1,head[305];int n,m,Q,d[305][305],vis[305];void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}void dijkstra(int st){    priority_queue<node> q;    memset(vis,0,sizeof(vis));    for(int i=1; i<=n; i++)        d[st][i]=INF;    q.push({st,INF});    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,t=max(d[st][u],e[i].w);            if(t==INF)t=e[i].w;            if(d[st][v]>t)            {                d[st][v]=t;                if(!vis[v])                    q.push({v,d[st][v]});            }        }    }}signed main(){    read(n);read(m);read(Q);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);    }    for(int i=1; i<=n; i++)        dijkstra(i);    while(Q--)    {        int x,y;read(x);read(y);        write((d[x][y]==INF)?-1:d[x][y]);pc('\n');    }    return 0;}
]]>
+ 洛谷P2888[USACO07NOV]Cow Hurdles S 题解

题目链接:P2888[USACO07NOV]Cow Hurdles S

题意:Farmer John想让她的奶牛准备郡级跳跃比赛,贝茜和她的伙伴们正在练习跨栏。她们很累,所以她们想消耗最少的能量来跨栏。显然,对于一头奶牛跳过几个矮栏是很容易的,但是高栏却很难。于是,奶牛们总是关心路径上最高的栏的高度。奶牛的训练场中有 \(N (1 ≤ N ≤ 300)\)个站台,分别标记为 \(1\dots N\)。所有站台之间有 \(M (1 ≤ M ≤ 25,000)\)条单向路径,第i条路经是从站台 \(S_i\)开始,到站台 \(E_i\),其中最高的栏的高度为\(H_i (1 ≤ H_i ≤1,000,000)\)。无论如何跑,奶牛们都要跨栏。 奶牛们有 \(T (1 ≤ T ≤ 40,000)\) 个训练任务要完成。第\(i\) 个任务包含两个数字 \(A_i\) 和 \(B_i (1≤ A_i ≤ N; 1 ≤ B_i ≤ N)\),表示奶牛必须从站台 \(A_i\) 跑到站台 \(B_i\),可以路过别的站台。奶牛们想找一条路径从站台 \(A_i\) 到站台 \(B_i\) ,使路径上最高的栏的高度最小。你的任务就是写一个程序,计算出路径上最高的栏的高度的最小值。

分析题目可以发现这是一张具有非负权重的有向图

我们只需要找到一条路径使得这条路径上所有的边权中最大值最小即可

Floyd解法

别看这个 \(n\le300\),跑起来还是飞快地(数据有点水)

容易推出方程f[i][j]=min(f[i][j],max(f[i][k],f[k][j]));

不过我有点不放心就加了个小剪枝 qwq 虽然只快了3ms

代码如下

//Floyd#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,Q,f[305][305];signed main(){    read(n);read(m);read(Q);    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            f[i][j]=INF;    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        f[u][v]=min(f[u][v],w);    }    for(int k=1; k<=n; k++)        for(int i=1; i<=n; i++)            if(f[i][k]!=INF)                for(int j=1; j<=n; j++)                    f[i][j]=min(f[i][j],max(f[i][k],f[k][j]));    while(Q--)    {        int x,y;read(x);read(y);        write((f[x][y]==INF)?-1:f[x][y]);pc('\n');    }    return 0;}

其他解法

关于SPFA,她死了。

开个玩笑而已

不过我们可以用理论复杂度较低的dijkstra

注意到 \(N\le300\)

那么离线解决即可

时间复杂度 \(O(N^2\log M + T)\)

不过实际情况下由于dijkstra常数较大,跑的甚至比Floyd慢...

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,w,next;}e[50005];struct node{    int u,mn;    bool operator<(const node &o)const    {        return mn>o.mn;    }};int pos=1,head[305];int n,m,Q,d[305][305],vis[305];void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}void dijkstra(int st){    priority_queue<node> q;    memset(vis,0,sizeof(vis));    for(int i=1; i<=n; i++)        d[st][i]=INF;    q.push({st,INF});    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,t=max(d[st][u],e[i].w);            if(t==INF)t=e[i].w;            if(d[st][v]>t)            {                d[st][v]=t;                if(!vis[v])                    q.push({v,d[st][v]});            }        }    }}signed main(){    read(n);read(m);read(Q);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);    }    for(int i=1; i<=n; i++)        dijkstra(i);    while(Q--)    {        int x,y;read(x);read(y);        write((d[x][y]==INF)?-1:d[x][y]);pc('\n');    }    return 0;}
]]>
@@ -6238,7 +6238,7 @@ /2022/05/25/luo-gu-p2912-usaco08oct-pasture-walking-g-ti-jie/ - 洛谷P2912 [USACO08OCT]Pasture Walking G 题解

题目链接:P2912 [USACO08OCT]Pasture Walking G

题意:有 $N(2\le N\le1000)$ 头奶牛,编号为 $1$ 到 $W$ ,它们正在同样编号为 $1$ 到 $N$ 的牧场上行走.为了方便,我们假设编号为 $i$ 的牛恰好在第 $i$ 号牧场上.

有一些牧场间每两个牧场用一条双向道路相连,道路总共有 $N - 1$ 条,奶牛可以在这些道路 上行走.第 $i$ 条道路把第 $A_i$ 个牧场和第 $B_i$ 个牧场连了起来 $(1 \le A_i \le N; 1 \le B_i \le N)$ ,而它的长度 是 $1 \le L_i \le 10,000$.在任意两个牧场间,有且仅有一条由若干道路组成的路径相连.也就是说,所有的道路构成了一棵树.

奶牛们十分希望经常互相见面.它们十分着急,所以希望你帮助它们计划它们的行程,你只 需要计算出 $Q(1 < Q < 1000)$ 对点之间的路径长度•每对点以一个询问 $p_1,p_2 (1 \le p_1 \le N; 1 \le p_2 \le N)$ 的形式给出.

按照解题的思路,首先想到的是Floyd

你看它多简单多好用

然而它的时间复杂度是 $O(n^3)$

我们来观察一下Floyd

for(int k=1; k<=n; k++)    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

由于给定的是一棵树(且无向边),那么

$\forall u \in V$ 有且仅有 $2$ 个的 $v_1,v_2 \in V$ 使得 $(u,v_1) , (u,v_2) \in E$

且 $v_1,v_2$ 只可能是 $u$ 的父亲或儿子

说人话就是每个结点最多连出去两个结点

那么很多时候f[i][k]都是无效的枚举(都是INF啊)

于是我们可以剪枝

for(int k=1; k<=n; k++)    for(int i=1; i<=n; i++)        if(f[i][k]!=INF)            for(int j=1; j<=n; j++)                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

于是时间复杂度就降到了 $O(n^2)$

代码如下 (好丑啊qwq)

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f#define MAXN (int)(1e3+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,Q,f[1005][1005];signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            f[i][j]=INF;    for(int i=1,u,v,w; i<n; i++)    {        read(u);read(v);read(w);        f[u][v]=f[v][u]=w;    }    for(int k=1; k<=n; k++)        for(int i=1; i<=n; i++)            if(f[i][k]!=INF)                for(int j=1; j<=n; j++)                    f[i][j]=min(f[i][j],f[i][k]+f[k][j]);    while(Q--)    {        int x,y;read(x);read(y);        write(f[x][y]);pc('\n');    }    return 0;}
]]>
+ 洛谷P2912[USACO08OCT]Pasture Walking G 题解

题目链接:P2912[USACO08OCT]Pasture Walking G

题意:有 \(N(2\leN\le1000)\) 头奶牛,编号为 \(1\)\(W\) ,它们正在同样编号为 \(1\) 到 \(N\) 的牧场上行走.为了方便,我们假设编号为\(i\) 的牛恰好在第 \(i\) 号牧场上.

有一些牧场间每两个牧场用一条双向道路相连,道路总共有 \(N - 1\) 条,奶牛可以在这些道路 上行走.第\(i\) 条道路把第 \(A_i\) 个牧场和第 \(B_i\) 个牧场连了起来 \((1 \le A_i \le N; 1 \le B_i \le N)\),而它的长度 是 \(1 \le L_i \le10,000\).在任意两个牧场间,有且仅有一条由若干道路组成的路径相连.也就是说,所有的道路构成了一棵树.

奶牛们十分希望经常互相见面.它们十分着急,所以希望你帮助它们计划它们的行程,你只需要计算出 \(Q(1 < Q < 1000)\)对点之间的路径长度•每对点以一个询问 \(p_1,p_2(1 \le p_1 \le N; 1 \le p_2 \le N)\) 的形式给出.

按照解题的思路,首先想到的是Floyd

你看它多简单多好用

然而它的时间复杂度是 \(O(n^3)\)

我们来观察一下Floyd

for(int k=1; k<=n; k++)    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

由于给定的是一棵树(且无向边),那么

\(\forall u \in V\) 有且仅有 \(2\) 个的 \(v_1,v_2 \in V\) 使得 \((u,v_1) , (u,v_2) \in E\)

\(v_1,v_2\) 只可能是 \(u\) 的父亲或儿子

说人话就是每个结点最多连出去两个结点

那么很多时候f[i][k]都是无效的枚举(都是INF啊)

于是我们可以剪枝

for(int k=1; k<=n; k++)    for(int i=1; i<=n; i++)        if(f[i][k]!=INF)            for(int j=1; j<=n; j++)                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

于是时间复杂度就降到了 \(O(n^2)\)

代码如下 (好丑啊qwq)

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f#define MAXN (int)(1e3+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,Q,f[1005][1005];signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            f[i][j]=INF;    for(int i=1,u,v,w; i<n; i++)    {        read(u);read(v);read(w);        f[u][v]=f[v][u]=w;    }    for(int k=1; k<=n; k++)        for(int i=1; i<=n; i++)            if(f[i][k]!=INF)                for(int j=1; j<=n; j++)                    f[i][j]=min(f[i][j],f[i][k]+f[k][j]);    while(Q--)    {        int x,y;read(x);read(y);        write(f[x][y]);pc('\n');    }    return 0;}
]]>
@@ -6265,7 +6265,7 @@ /2022/05/25/luo-gu-p3188-hnoi2007-meng-huan-dao-bao-zhu-ti-jie/ - 洛谷P3188 [HNOI2007]梦幻岛宝珠 题解

题目链接:P3188 [HNOI2007]梦幻岛宝珠

题意

给你 $n$ 颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过 $W$,且总价值最大,并输出最大的总价值。

【数据范围】
对于 $100\%$ 的数据,$1\le n \le 100$,$1\le W,w_i,v_i \le 2^{30}$。
保证每个 $w_i$ 能写成 $a \times 2^b\space (a,b \in \mathbb N)$ 的形式,$a \leq 10$ , $b \leq 30$,且答案不超过 $2^{30}$。

注:下文中将使用 $m$ 代替 $W$

这么大的背包容量,肯定不是朴素的背包问题

可以发现数据范围里强调了 “保证每个 $w_i$ 能写成 $a \times 2^b\space (a,b \in \mathbb N)$ 的形式”

尽管有

但是这里的 $a\le 10$ 却十分特殊,这意味着我们可以从这个角度思考解法

考虑利用泛化物品的思想

首先将每件物品按 $b$ 进行分组,在每一组内dp

设 $f[i][j]$ 为在所有满足 $b=i$ 的物品中选择若干(不妨设为 $k$ )件物品,且总体积 $j^{\prime}$ 满足

时的最大价值。

然后考虑合并泛化物品,即 $b$ 不同的物品之间的转移

令 $g[i][j]$ 表示仅使用 $b \in [0,i]$ 的物品,且 $b=i$ 的物品所占空间为 $j$ ,$b\in [0,i-1]$ 的物品所占空间为 $m\&(2^i-1)$ 时的最大价值

这里的 $\&$ 表示二进制按位与,不难发现 $m\&(2^i-1)$ 就是 $m$ 的第 $1$ 到 $i-1$ 位

转移方程如下

注:这里的第 $i$ 位是从第 $1$ 位开始数的

写成代码就是

g[i][j]=max(g[i][j],f[i][j-k]+g[i-1][min(10*n,k*2+((m>>(i-1))&1))]);

即从 $j\times 2^i$ 中抽出来 $k\times 2^i$ 的体积给 $b \in[0,i-1]$ 的物品

这里加上 $\min(10n,\dots)$ 的原因是 $(\dots)$ 处可能超出了 $\sum\limits_{0\le k \le i-1} j_k$ 的一个较宽松的上限 $10n$

但是这个上界并不容易达到,因此为了避免讨论,使用 $\min$ 即可

因此答案为 $g[m\text{ 的位数}][1]$

时间复杂度 $O(Qn^2\log m)$ ,但是常数巨大无比(别急,后面还有…

代码1:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(115)int n,m,num[32];int a[32][N],v[32][N],f[32][N*10],g[32][N*10];#define mem(x) memset(x,0,sizeof(x));void clear(){mem(a);mem(num);mem(v);mem(f);mem(g);}signed main(){    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(read(n),read(m),n+m!=-2)    {        clear();        for(int i=1,x,y; i<=n; i++)        {            read(x);read(y);            int cnt=0;            while(!(x&1)){x>>=1,++cnt;}            ++num[cnt];            a[cnt][num[cnt]]=x;            v[cnt][num[cnt]]=y;        }        int cnt=32;        while(m<=(1ll<<(--cnt)));        for(int k=0; k<=cnt; k++)            for(int i=1; i<=num[k]; i++)                for(int j=10*num[k]; j>=a[k][i]; j--)                    f[k][j]=max(f[k][j],f[k][j-a[k][i]]+v[k][i]);        for(int i=0; i<=10*num[0]; i++)            g[0][i]=f[0][i];        for(int i=1; i<=cnt; i++)            for(int j=0; j<=10*n; j++)                for(int k=0; k<=j; k++)                    g[i][j]=max(g[i][j],f[i][j-k]+g[i-1][min(10*n,k*2+((m>>(i-1))&1))]);                   write(g[cnt][1]);pc('\n');    }    return 0;}

注意到上面的代码,虽然可以过

但是一共跑了1.20s这是不可接受的(逃

考虑优化一下

注意到这个上界 $10n$ 一般不容易达到(前面提到过

因此我们会做很多无效枚举

可以开一个数组 $s[i]$ 表示所有 $b=i$ 的物品的 $a$ 之和

同时,注意到 $f$ 其实可以重复利用,考虑改变一下枚举顺序

这样就可以在 39ms 左右跑出来啦!

代码2:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(115)int n,m,num[32],s[32];int a[32][N],v[32][N],f[32][N*10];#define mem(x) memset(x,0,sizeof(x));void clear(){mem(a);mem(num);mem(v);mem(f);mem(s);}signed main(){    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(read(n),read(m),n+m!=-2)    {        clear();        for(int i=1,x,y; i<=n; i++)        {            read(x);read(y);            int cnt=0;            while(!(x&1)){x>>=1,++cnt;}            ++num[cnt];s[cnt]+=x;            a[cnt][num[cnt]]=x;            v[cnt][num[cnt]]=y;        }        int cnt=32;        while(m<=(1ll<<(--cnt)));        for(int k=0; k<=cnt; k++)            for(int i=1; i<=num[k]; i++)                for(int j=s[k]; j>=a[k][i]; j--)                    f[k][j]=max(f[k][j],f[k][j-a[k][i]]+v[k][i]);        for(int i=1; i<=cnt; i++)        {            s[i]+=(s[i-1]+1)>>1; // 向上取整            for(int j=s[i]; j>=0; j--) // 倒序枚举,防止答案被错误覆盖                for(int k=0; k<=j; k++)                    f[i][j]=max(f[i][j],f[i][j-k]+f[i-1][min(s[i-1],(k<<1)|((m>>(i-1))&1))]);        }        write(f[cnt][1]);pc('\n');    }    return 0;}

这道题我花了几乎一整天研究(菜死了

网上好多的题解都有错

希望这篇可以帮助到你们

如果有错欢迎指出!

参考文献

[1] https://www.luogu.com.cn/blog/youare251-1/solution-p3188

[2] https://www.luogu.com.cn/user/50047

]]>
+ 洛谷P3188[HNOI2007]梦幻岛宝珠 题解

题目链接:P3188[HNOI2007]梦幻岛宝珠

题意

给你 \(n\)颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过\(W\),且总价值最大,并输出最大的总价值。

【数据范围】 对于 \(100\%\)的数据,\(1\le n \le 100\)\(1\le W,w_i,v_i \le 2^{30}\)。 保证每个\(w_i\) 能写成 \(a \times 2^b\space (a,b \in \mathbb N)\)的形式,\(a \leq 10\) , \(b \leq 30\),且答案不超过 \(2^{30}\)。

注:下文中将使用 \(m\) 代替\(W\)

这么大的背包容量,肯定不是朴素的背包问题

可以发现数据范围里强调了 "保证每个 \(w_i\) 能写成 \(a\times 2^b\space (a,b \in \mathbb N)\) 的形式"

尽管有 \[\forall\, x \in \mathbb{N},\exist\, a,b \in \mathbb{N},\ \bold{s.t. }\ \x=a\times 2^b\] 但是这里的 \(a\le 10\)却十分特殊,这意味着我们可以从这个角度思考解法

考虑利用泛化物品的思想

首先将每件物品按 \(b\)进行分组,在每一组内dp

\(f[i][j]\) 为在所有满足 \(b=i\) 的物品中选择若干(不妨设为 \(k\) )件物品,且总体积 \(j^{\prime}\) 满足 \[\sum{a_k}\times 2^i \le j \times 2^i \Rightarrow \sum a_k \le j\Rightarrow j^{\prime} \le j\] 时的最大价值。

然后考虑合并泛化物品,即 \(b\)不同的物品之间的转移

\(g[i][j]\) 表示仅使用 \(b \in [0,i]\) 的物品,且 \(b=i\) 的物品所占空间为 \(j\) ,\(b\in[0,i-1]\) 的物品所占空间为 \(m\&(2^i-1)\) 时的最大价值

这里的 \(\&\)表示二进制按位与,不难发现 \(m\&(2^i-1)\) 就是 \(m\) 的第 \(1\) 到 \(i-1\) 位

转移方程如下 \[g[i][j]=\max_{0\le k \le j}(g[i][j],f[i][j-k]+g[i-1][\min(10n,2k + [m\text{ 第 }i\text{ 位为 }1]])\] 注:这里的第 \(i\) 位是从第\(1\) 位开始数的

写成代码就是

g[i][j]=max(g[i][j],f[i][j-k]+g[i-1][min(10*n,k*2+((m>>(i-1))&1))]);

即从 \(j\times 2^i\) 中抽出来 \(k\times 2^i\) 的体积给 \(b \in[0,i-1]\) 的物品

这里加上 \(\min(10n,\dots)\)的原因是 \((\dots)\) 处可能超出了 \(\sum\limits_{0\le k \le i-1} j_k\)的一个较宽松的上限 \(10n\)

但是这个上界并不容易达到,因此为了避免讨论,使用 \(\min\) 即可

因此答案为 \(g[m\text{的位数}][1]\)

时间复杂度 \(O(Qn^2\log m)\),但是常数巨大无比(别急,后面还有...

代码1:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(115)int n,m,num[32];int a[32][N],v[32][N],f[32][N*10],g[32][N*10];#define mem(x) memset(x,0,sizeof(x));void clear(){mem(a);mem(num);mem(v);mem(f);mem(g);}signed main(){    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(read(n),read(m),n+m!=-2)    {        clear();        for(int i=1,x,y; i<=n; i++)        {            read(x);read(y);            int cnt=0;            while(!(x&1)){x>>=1,++cnt;}            ++num[cnt];            a[cnt][num[cnt]]=x;            v[cnt][num[cnt]]=y;        }        int cnt=32;        while(m<=(1ll<<(--cnt)));        for(int k=0; k<=cnt; k++)            for(int i=1; i<=num[k]; i++)                for(int j=10*num[k]; j>=a[k][i]; j--)                    f[k][j]=max(f[k][j],f[k][j-a[k][i]]+v[k][i]);        for(int i=0; i<=10*num[0]; i++)            g[0][i]=f[0][i];        for(int i=1; i<=cnt; i++)            for(int j=0; j<=10*n; j++)                for(int k=0; k<=j; k++)                    g[i][j]=max(g[i][j],f[i][j-k]+g[i-1][min(10*n,k*2+((m>>(i-1))&1))]);                   write(g[cnt][1]);pc('\n');    }    return 0;}

注意到上面的代码,虽然可以过

但是一共跑了1.20s这是不可接受的(逃

考虑优化一下

注意到这个上界 \(10n\)一般不容易达到(前面提到过

因此我们会做很多无效枚举

可以开一个数组 \(s[i]\) 表示所有\(b=i\) 的物品的 \(a\) 之和

同时,注意到 \(f\)其实可以重复利用,考虑改变一下枚举顺序

这样就可以在 39ms 左右跑出来啦!

代码2:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(115)int n,m,num[32],s[32];int a[32][N],v[32][N],f[32][N*10];#define mem(x) memset(x,0,sizeof(x));void clear(){mem(a);mem(num);mem(v);mem(f);mem(s);}signed main(){    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    while(read(n),read(m),n+m!=-2)    {        clear();        for(int i=1,x,y; i<=n; i++)        {            read(x);read(y);            int cnt=0;            while(!(x&1)){x>>=1,++cnt;}            ++num[cnt];s[cnt]+=x;            a[cnt][num[cnt]]=x;            v[cnt][num[cnt]]=y;        }        int cnt=32;        while(m<=(1ll<<(--cnt)));        for(int k=0; k<=cnt; k++)            for(int i=1; i<=num[k]; i++)                for(int j=s[k]; j>=a[k][i]; j--)                    f[k][j]=max(f[k][j],f[k][j-a[k][i]]+v[k][i]);        for(int i=1; i<=cnt; i++)        {            s[i]+=(s[i-1]+1)>>1; // 向上取整            for(int j=s[i]; j>=0; j--) // 倒序枚举,防止答案被错误覆盖                for(int k=0; k<=j; k++)                    f[i][j]=max(f[i][j],f[i][j-k]+f[i-1][min(s[i-1],(k<<1)|((m>>(i-1))&1))]);        }        write(f[cnt][1]);pc('\n');    }    return 0;}

这道题我花了几乎一整天研究(菜死了

网上好多的题解都有错

希望这篇可以帮助到你们

如果有错欢迎指出!

参考文献

[1] https://www.luogu.com.cn/blog/youare251-1/solution-p3188

[2] https://www.luogu.com.cn/user/50047

]]>
@@ -6292,7 +6292,7 @@ /2022/05/25/luo-gu-p3297-sdoi2013-tao-kao-ti-jie/ - 洛谷P3297 [SDOI2013] 逃考 题解

题目链接:P3297 [SDOI2013] 逃考

题意:髙考又来了,对于不认真读书的小杨来讲,真不是个好消息。为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶、外公外婆、大舅、大嫂、阿姨……

小杨实在是忍无可忍了,这种生活跟监狱有什么区别!为了他亲爱的小红,为了他的dota,他决定越狱!

假设小杨的家是个 $x_1\times y_1$ 的矩阵,左下角坐标为 $(0,0)$ ,右上角坐标为 $(x_1,y_1)$ 。小杨有 $n$ 个亲戚,驻扎在矩阵里(位置不同,且不在矩阵的边上)。小杨家里的每个地方都被亲戚监控着,而且只被距离最近的亲戚监控:

也就是说假设小杨所在的位置是 $(3,3)$ ,亲戚A在 $(3,0)$ , A距离小杨距离是 $3$;亲戚B在 $(6,7)$ ,则B距离小杨距离是 $5$ 。距离A<距离B,所以 $(3,3)$ 位置由A监控。

如果“最近距离”出现同时有几个亲戚,那么那个位置同时被那几个亲戚监控。

给出小杨的坐标 $(x_0,y_0)$ 。因为被发现的人数越少,越狱成功的机会越大,所以小杨需要你设计一条越狱路线到达矩形的边上,且被发现的人数最少。

ps:小杨走的方向是任意的,也就是说路线上的任意位置需要是实数。

保证一开始小杨只被一个亲戚监控着。

注:这题目题面和数据有点问题,要注意特判掉那些在矩形外面的点

鉴于数据可能改掉(那样我画的图就没用了),给出原来样例的第二个测试数据:

11714 12 7 67 116 97 71 102 201 62 61 12 25 15 213 112 212 713 712 1113 11

本题解法:半平面交+最短路

注意到每个亲戚都有一个管辖的范围

因此我们可以把每个亲戚向与其管辖范围相邻的亲戚连一条边权为 $1$ 的边

然后从第一个监管小杨的亲戚那里跑一下最短路就好了

那么管辖范围怎么求呢?

注意到对于亲戚 $u,v$ ,当小杨从 $u$ 的管辖范围转移到 $v$ 的范围时

一定经过了一条分界线

这条分界线就是直线 $u-v$ 的中垂线(垂直平分线)

这样,每个亲戚的管辖范围就是他与所有其他亲戚的中垂线的半平面交

对于上面那个样例,我画了个图,(没画不合法的点)

看在q779花了1个小时的分上,点个赞吧

其实这个图形就是个泰森多边形(知道也没用

那么这个题好像就没啥难度了啊

时间复杂度 $O(Qn^2\log n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define double long double#define INF 0x3f3f3f3f3f3f3f3f#define lb lower_bound#define N (int)(666)int n,s,t;double w,h;// ---------------vct--------------------const double eps=1e-15;#define pf(x) ((x)*(x))struct vct{double x,y;}lamb,p[N],in[N];vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}double dis(vct a,vct b){return len(a-b);}vct rot(vct a){return {-a.y,a.x};}int dcmp(double x){if(fabs(x)<=eps)return 0;return x>eps?1:-1;}// ---------------line--------------------struct line{    vct p,way;    double k;    int id;    void mkline(vct a,vct b,int ID)    {        p=a;way=b;        k=atan2(b.y,b.x);        id=ID;    }    void mk(double x1,double y1,double x2,double y2,int ID)        {mkline({x1,y1},{x2-x1,y2-y1},ID);}}a[N];vct intersect(line a,line b){    double t=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*t;}line getmidline(vct a,vct b,int id){    line t;    t.mkline((a+b)/2,rot(b-a),id);    return t;}// ---------------SSSP--------------------struct dist{int u,dis;};bool operator<(dist a,dist b){return a.dis>b.dis;}struct Edge{int u,v,w,next;}e[(int)(8e5+15)];priority_queue<dist> q;int d[N],vis[N];int head[N],pos=1;void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}int dijkstra(){    memset(vis,0,(n+15)*sizeof(int));    memset(d,0x3f,(n+15)*sizeof(int));    while(!q.empty())q.pop();    d[s]=0;q.push({s,0});    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }    // for(int i=0; i<=n+5; i++)    //     cout << (d[i]==INF?-1:d[i]) << " \n"[i==n+5];    return d[t];}int init(int k){    int o=0;    for(int i=1; i<=n; i++)    {        if(i==k)continue;        a[++o]=getmidline(in[k],in[i],i);    }    // o=n-1    a[++o].mk(-eps,-eps,w,-eps,0);    a[++o].mk(w,-eps,w,h,0);    a[++o].mk(w,h,-eps,h,0);    a[++o].mk(-eps,h,-eps,-eps,0);    // o=n-1+4=n+3    return o;}// ---------------halfplane--------------------bool onright(vct a,line b){return dcmp(cross(b.way,a-b.p))<0;}bool operator<(line a,line b){    int d=dcmp(a.k-b.k);    if(d==0)return dcmp(cross(b.p-a.p,b.way))>0;    return d<0;}int st,en;line que[N];int qc[N];void proc(int k){    int oo=0,o=0;    for(int i=st; i<=en; i++)        qc[++oo]=que[i].id;    sort(qc+1,qc+1+oo);    o=unique(qc+1,qc+1+oo)-qc-1;    for(int i=1; i<=o; i++)        addEdge(k,qc[i],1),        addEdge(qc[i],k,1);}void halfplane(int k,int lcnt){    int o=0;    // cout << lcnt << endl;    sort(a+1,a+1+lcnt);    // cout << "q779 is very cute." << endl;    for(int i=1; i<=lcnt; i++)        if(i==1||dcmp(a[i].k-a[i-1].k))            a[++o]=a[i];    que[st=en=1]=a[1];    for(int i=2; i<=o; i++)    {        while(st<en&&onright(p[en],a[i]))--en;        while(st<en&&onright(p[st+1],a[i]))++st;        que[++en]=a[i];        if(st<en)p[en]=intersect(que[en-1],que[en]);    }    while(st<en&&onright(p[en],que[st]))--en;    if(en-st<=1)return;    p[st]=intersect(que[st],que[en]);    proc(k);    // cout << pos << endl;}void clear(){    pos=1;t=0;    memset(head,0,sizeof(head));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    int Q;    cin >> Q;    while(Q--)    {        clear();        cin >> n;        if(!n){cout << 0 << endl;continue;}        cin >> w >> h >> lamb.x >> lamb.y;        double len=INF;        int tmp=0,o=0;        for(int i=1; i<=n; i++)        {            double x,y;            cin >> x >> y;            if(x>w||x<0||y>h||y<0)continue;            in[++o]={x,y};            double now=dis(in[i],lamb);            if(dcmp(len-now)>0)len=now,tmp=o;        }        n=o;s=tmp;        // cout << o << endl;        for(int i=1; i<=n; i++)        {            int lcnt=init(i);            // cout << lcnt << endl;            halfplane(i,lcnt);            // cout << "i=" << i << ",";            // for(int j=head[i]; j; j=e[j].next)            //     cout << e[j].v << " ";            // cout << endl;        }        cout << dijkstra() << endl;    }    return 0;}
]]>
+ 洛谷P3297 [SDOI2013] 逃考题解

题目链接:P3297[SDOI2013] 逃考

题意:髙考又来了,对于不认真读书的小杨来讲,真不是个好消息。为了小杨能在家里认真读书,他的亲戚决定驻扎在他的家里监督他学习,有爷爷奶奶、外公外婆、大舅、大嫂、阿姨......

小杨实在是忍无可忍了,这种生活跟监狱有什么区别!为了他亲爱的小红,为了他的dota,他决定越狱!

假设小杨的家是个 \(x_1\times y_1\)的矩阵,左下角坐标为 \((0,0)\),右上角坐标为 \((x_1,y_1)\) 。小杨有\(n\)个亲戚,驻扎在矩阵里(位置不同,且不在矩阵的边上)。小杨家里的每个地方都被亲戚监控着,而且只被距离最近的亲戚监控:

也就是说假设小杨所在的位置是 \((3,3)\) ,亲戚A在 \((3,0)\) , A距离小杨距离是 \(3\);亲戚B在 \((6,7)\) ,则B距离小杨距离是 \(5\) 。距离A<距离B,所以 \((3,3)\) 位置由A监控。

如果“最近距离”出现同时有几个亲戚,那么那个位置同时被那几个亲戚监控。

给出小杨的坐标 \((x_0,y_0)\)。因为被发现的人数越少,越狱成功的机会越大,所以小杨需要你设计一条越狱路线到达矩形的边上,且被发现的人数最少。

ps:小杨走的方向是任意的,也就是说路线上的任意位置需要是实数。

保证一开始小杨只被一个亲戚监控着。

注:这题目题面和数据有点问题,要注意特判掉那些在矩形外面的点

鉴于数据可能改掉(那样我画的图就没用了),给出原来样例的第二个测试数据:

11714 12 7 67 116 97 71 102 201 62 61 12 25 15 213 112 212 713 712 1113 11

本题解法:半平面交+最短路

注意到每个亲戚都有一个管辖的范围

因此我们可以把每个亲戚向与其管辖范围相邻的亲戚连一条边权为 \(1\) 的边

然后从第一个监管小杨的亲戚那里跑一下最短路就好了

那么管辖范围怎么求呢?

注意到对于亲戚 \(u,v\) ,当小杨从\(u\) 的管辖范围转移到 \(v\) 的范围时

一定经过了一条分界线

这条分界线就是直线 \(u-v\)的中垂线(垂直平分线)

这样,每个亲戚的管辖范围就是他与所有其他亲戚的中垂线的半平面交

对于上面那个样例,我画了个图,(没画不合法的点)

看在q779花了1个小时的分上,点个赞吧

其实这个图形就是个泰森多边形(知道也没用

那么这个题好像就没啥难度了啊

时间复杂度 \(O(Qn^2\log n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define double long double#define INF 0x3f3f3f3f3f3f3f3f#define lb lower_bound#define N (int)(666)int n,s,t;double w,h;// ---------------vct--------------------const double eps=1e-15;#define pf(x) ((x)*(x))struct vct{double x,y;}lamb,p[N],in[N];vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}double dis(vct a,vct b){return len(a-b);}vct rot(vct a){return {-a.y,a.x};}int dcmp(double x){if(fabs(x)<=eps)return 0;return x>eps?1:-1;}// ---------------line--------------------struct line{    vct p,way;    double k;    int id;    void mkline(vct a,vct b,int ID)    {        p=a;way=b;        k=atan2(b.y,b.x);        id=ID;    }    void mk(double x1,double y1,double x2,double y2,int ID)        {mkline({x1,y1},{x2-x1,y2-y1},ID);}}a[N];vct intersect(line a,line b){    double t=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*t;}line getmidline(vct a,vct b,int id){    line t;    t.mkline((a+b)/2,rot(b-a),id);    return t;}// ---------------SSSP--------------------struct dist{int u,dis;};bool operator<(dist a,dist b){return a.dis>b.dis;}struct Edge{int u,v,w,next;}e[(int)(8e5+15)];priority_queue<dist> q;int d[N],vis[N];int head[N],pos=1;void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}int dijkstra(){    memset(vis,0,(n+15)*sizeof(int));    memset(d,0x3f,(n+15)*sizeof(int));    while(!q.empty())q.pop();    d[s]=0;q.push({s,0});    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v,w=e[i].w;            if(d[v]>d[u]+w)            {                d[v]=d[u]+w;                q.push({v,d[v]});            }        }    }    // for(int i=0; i<=n+5; i++)    //     cout << (d[i]==INF?-1:d[i]) << " \n"[i==n+5];    return d[t];}int init(int k){    int o=0;    for(int i=1; i<=n; i++)    {        if(i==k)continue;        a[++o]=getmidline(in[k],in[i],i);    }    // o=n-1    a[++o].mk(-eps,-eps,w,-eps,0);    a[++o].mk(w,-eps,w,h,0);    a[++o].mk(w,h,-eps,h,0);    a[++o].mk(-eps,h,-eps,-eps,0);    // o=n-1+4=n+3    return o;}// ---------------halfplane--------------------bool onright(vct a,line b){return dcmp(cross(b.way,a-b.p))<0;}bool operator<(line a,line b){    int d=dcmp(a.k-b.k);    if(d==0)return dcmp(cross(b.p-a.p,b.way))>0;    return d<0;}int st,en;line que[N];int qc[N];void proc(int k){    int oo=0,o=0;    for(int i=st; i<=en; i++)        qc[++oo]=que[i].id;    sort(qc+1,qc+1+oo);    o=unique(qc+1,qc+1+oo)-qc-1;    for(int i=1; i<=o; i++)        addEdge(k,qc[i],1),        addEdge(qc[i],k,1);}void halfplane(int k,int lcnt){    int o=0;    // cout << lcnt << endl;    sort(a+1,a+1+lcnt);    // cout << "q779 is very cute." << endl;    for(int i=1; i<=lcnt; i++)        if(i==1||dcmp(a[i].k-a[i-1].k))            a[++o]=a[i];    que[st=en=1]=a[1];    for(int i=2; i<=o; i++)    {        while(st<en&&onright(p[en],a[i]))--en;        while(st<en&&onright(p[st+1],a[i]))++st;        que[++en]=a[i];        if(st<en)p[en]=intersect(que[en-1],que[en]);    }    while(st<en&&onright(p[en],que[st]))--en;    if(en-st<=1)return;    p[st]=intersect(que[st],que[en]);    proc(k);    // cout << pos << endl;}void clear(){    pos=1;t=0;    memset(head,0,sizeof(head));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    int Q;    cin >> Q;    while(Q--)    {        clear();        cin >> n;        if(!n){cout << 0 << endl;continue;}        cin >> w >> h >> lamb.x >> lamb.y;        double len=INF;        int tmp=0,o=0;        for(int i=1; i<=n; i++)        {            double x,y;            cin >> x >> y;            if(x>w||x<0||y>h||y<0)continue;            in[++o]={x,y};            double now=dis(in[i],lamb);            if(dcmp(len-now)>0)len=now,tmp=o;        }        n=o;s=tmp;        // cout << o << endl;        for(int i=1; i<=n; i++)        {            int lcnt=init(i);            // cout << lcnt << endl;            halfplane(i,lcnt);            // cout << "i=" << i << ",";            // for(int j=head[i]; j; j=e[j].next)            //     cout << e[j].v << " ";            // cout << endl;        }        cout << dijkstra() << endl;    }    return 0;}
]]>
@@ -6317,11 +6317,11 @@ - 洛谷P3327 [SDOI2015]约数个数和 题解 - - /2022/05/25/luo-gu-p3327-sdoi2015-yue-shu-ge-shu-he-ti-jie/ + 洛谷P3336 [ZJOI2013]话旧 题解 + + /2022/05/25/luo-gu-p3336-zjoi2013-hua-jiu-ti-jie/ - 洛谷P3327 [SDOI2015]约数个数和 题解

题目链接:P3327 [SDOI2015]约数个数和

题意:设 $d(x)$ 为 $x$ 的约数个数,给定 $n,m$ ,求

感觉我前几篇题解的无解释推导有点不可读

所以这篇还是写的清楚一点叭

这题需要一个引理(感觉没必要记住证明的方法)

引理:设 $d(x)$ 为 $x$ 的约数个数,则有

证明:摘自 https://siyuan.blog.luogu.org/solution-p3327

我们考虑把每个因子一一映射。

如果 $ij$ 的因子 $k$ 中有一个因子 $p^c$,$i$ 中有因子 $p^a$,$j$ 中有因子 $p^b$。我们规定:

  • 如果 $c\le a$,那么在 $i$ 中选择。
  • 如果 $c>a$,那么我们把 $c$ 减去 $a$,在 $j$ 中选择 $p^{c-a}$(在 jj 中选择 $p^e$ 表示的是 $p^{a+e}$ )

对于 $ij$ 的因子 $k$ 的其他因子同理。于是对于任何一个 $k$ 有一个唯一的映射,且每一个选择对应着唯一的 $k$。

通过如上过程,我们发现:对于 $ij$的因子 $k=\prod {p_i}^{c_i}$,我们不可能同时在 $i$ 和 $j$ 中选择 $p_i$(优先在 $i$ 中选择,如果不够就只在 $j$ 中选择不够的指数),故 $x$ 和 $y$ 必须互质。

等式得证。

有了这个引理,就可以开始搞了

不妨假设 $n \le m$

代入

莫比乌斯反演

这个 $d \mid \gcd(x,y)$ 看着很难受,把它换掉

啊它怎么又回来了,不急,继续推

移一下

可以发现这个正整数 $x$ 的取值范围在 $[1,n]$

而它的出现次数其实就是 $[1,n]$ 中有多少个数有因数 $x$

也就是 $\sum_{x=1}^{n}[x \mid n] = \sum_{x=1}^{n}\left\lfloor\frac{n}{x}\right\rfloor$

$y$ 同理,则可以得到

考虑消掉后面那个东西

注意到 $d \mid \gcd(dx,dy)$ 恒成立,又因为 $\sum_{k\in K}a_k = \sum_{p(k) \in K}a_{p(k)}$

整理一下就是

然后就可以数论分块啦!

代码:

#include <bits/stdc++.h>using namespace std;#define int long longtypedef long long ll;#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e4+15)int prime[N],sum[N],pcnt,mu[N],g[N];bool ck[N];int solve(int n){    int res=0;    for(int l=1,r; l<=n; l=r+1)    {        r=(n/(n/l));        res+=(r-l+1)*(n/l);    }    return res;}void Mobius(){    mu[1]=1;    for(int i=2; i<=N-5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            mu[i]=-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N-5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                mu[pos]=-mu[i];            }else            {                mu[pos]=0;                break;            }        }    }    for(int i=1; i<=N-5; i++)        sum[i]+=sum[i-1]+mu[i];    for(int i=1; i<=N-5; i++)        g[i]=solve(i);}int Q,n,m;signed main(){    Mobius();    read(Q);    while(Q--)    {        read(n);read(m);        if(n>m)swap(n,m);        int res=0;        for(int l=1,r; l<=n; l=r+1)        {            r=min(n/(n/l),m/(m/l));            res+=(sum[r]-sum[l-1])*g[n/l]*g[m/l];        }        write(res);pc('\n');    }    return 0;}
]]>
+ 洛谷P3336 [ZJOI2013]话旧 题解

题目链接:P3336[ZJOI2013]话旧

题意:小林跟着银河队选手去了一趟宇宙比赛,耳濡目染,变得学术起来。回来后,他发现世界大变样了。比丘兽究级进化,成了凤凰兽;金先生因为发了一篇paper,一跃成为教授,也成为了银河队选拔委员会成员。

一日,小林与金教授聊天。金教授回忆起过去的岁月,那些年他学过的电路原理。他曾经对一种三角波很感兴趣,并且进行了一些探究。小林感到很好奇,于是金教授就将课题形式化地说了一遍。

有一定义在 \([0,N]\) 的连续函数\(f(x)\),其中 \(N\) 是整数,满足 \(f(0)=f(N)=0\),它的所有极值点在整数处取到,且\(f(x)\)极小值均是\(0\)。对于任意的 \(0\) 到 \(N-1\) 间的整数 \(I\),\(f(x)\) 在 \((I,I+1)\) 上是斜率为 \(1\)\(-1\) 的一次函数。

金先生研究的是,若他知道其中 \(K\)个整点的函数值,那么:

  1. 有多少个函数满足条件?
  2. 满足条件的函数中,\(f(x)\)的最大值,最大能是多少?

小林思考了一下,便想出了很好的算法。那么作为经过多年训练的你呢?

输入格式

第一行包含两个用空格隔开的整数 \(N,K\)。接下来 \(K\) 行,每行两个整数,表示 \(x_i\) 和 \(f(x_i)\)。

输出格式

一行两个整数,分别对应两个问题的答案。考虑到第一问答案可能很大,你只要输出它除以\(19940417\) 的余数。

输入输出样例

输入 #1

2 0

输出 #1

1 1

输入 #2

6 94 24 22 04 26 05 12 00 00 0

输出 #2

1 2

说明/提示

  • 对于 \(10\%\) 的数据,\(N \leq 10\)。
  • 对于 \(20\%\) 的数据,\(N \leq 50\)。
  • 对于 \(30\%\) 的数据,\(N \leq 100\),\(K\leq 100\)
  • 对于 \(50\%\) 的数据,\(N \leq 10^3\),\(K \leq 10^3\)。
  • 对于 \(70\%\) 的数据,\(N \leq 10^5\)。
  • 另有 \(10\%\) 的数据,\(K=0\)。
  • 对于 \(100\%\) 的数据,\(0 \leq N \leq 10^9\),\(0 \leq K \leq 10^6\)。

题目说了极小值为 \(0\),说明每次下降都是直接一口气到底的

从文字中可以看出题目保证了有解(不然怎么输出最大值)

考虑设计状态 \(dp[i][0/1]\)表示是需要访问到 \(i\)结点时的路径是向上还是向下的

  • \(dp[i][1] \to dp[i+1][0]\)

    显然延长左右的两条路径,可得零点 \(a,b\)

    然后在 \(a,b\)间反复横跳便可以相连了

    \(a,b\) 间的距离为 \(2k\) ,则方案数为 \(2^{k-1}\)

    如下图 \((2k=6)\)

    \          / \        /  \/\/\/\/

    观察到其形成了 \(4\)\/ 的结构,不妨其为齿孔

    \          / \  /\    /  \/  \/\/

    注意到此时除了两侧的齿孔不可扩展以外,其余均可扩展

    故方案数为 \(2^2 = 4\)

  • \(dp[i][0] \to dp[i+1][0]\)

    不妨将 \(i\)与其向上的路径看作一个点,则又可以确定两个零点 \(a,b\)

    而此时左侧的齿孔也可以扩展了,而右侧的仍不可,故方案数为 \(2^k\)

    如下图 \((2k=4)\)

    /\        /  \      /   \/\/\/

    扩展左侧齿孔会变成

     /\      //  \    /    \/\/

    故方案数为 \(2^2 = 4\)

  • \(dp[i][1]\to dp[i+1][1]\)

    与上一种情况类似,将 \(i+1\)与其向下的路径看作一个点

    右侧的齿孔可以扩展,而此时左侧的不可,故方案数为 \(2^k\)

  • \(dp[i][0] \to dp[i+1][1]\)

    考虑将 \(i+1\)与其向下的路径看作一个点

    则此时左侧的齿孔可以扩展,而右侧的不可以

    故方案数为 \(2^k\)

然而有些情况会导致 \(k<0\),因此要特判一下

还有一些特殊的情况,比如 \(i+1\)恰好在 \(i\)的向上/下的路径上,也需要特判

然后最大值的求解,根据贪心,一定先选掉所有的上升

设有 \(a\) 个上升, \(b\) 个下降,则有 \[\begin{cases}a+b = x_2-x_1 \\a-b = y_2-y_1\end{cases}\] 则最大值为 \[y_1+a = \dfrac{1}{2}(x_2+y_2-x_1 + y_1)\] 但是要注意,如果可以选择向上,一定有 \(dp[i][0]\ne0\)

因此对于所有的 \(i\)\(i+1\)的转移,都额外计算一下先走到底再上升的情况,即 \[\dfrac{1}{2}(x_2+y_2-x_1-y_1)\] 代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)const int p=19940417;int n,k,mx;void add(int &a,int b){a=((a+b%p)%p+p)%p;}int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1)ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}struct node{    int x,y;}a[N];bool operator<(node a,node b)    {return a.x<b.x;}bool operator==(node a,node b)    {return a.x==b.x&&a.y==b.y;}int dp[N][2];signed main(){    read(n);read(k);    for(int i=1; i<=k; i++)        read(a[i].x),read(a[i].y);    a[++k]={0,0};a[++k]={n,0};    sort(a+1,a+1+k);k=unique(a+1,a+1+k)-a-1;    dp[1][1]=1;    for(int i=1; i<k; i++)    {        int d=(a[i+1].x-a[i].x-a[i+1].y-a[i].y)>>1;        mx=max(mx,(a[i+1].x+a[i+1].y-a[i].x-a[i].y)>>1);        if(dp[i][0])mx=max(mx,(a[i+1].x+a[i+1].y-a[i].x+a[i].y)>>1);        if(a[i+1].y-a[i].y==a[i].x-a[i+1].x)            add(dp[i+1][1],dp[i][0]+dp[i][1]);        else if(a[i+1].y-a[i].y==a[i+1].x-a[i].x)            add(dp[i+1][0],dp[i][0]+(a[i].y?0:dp[i][1]));        else if(d<0)            add(dp[i+1][1],dp[i][0]);        else if(d==0)            add(dp[i+1][0],dp[i][0]+dp[i][1]),add(dp[i+1][1],dp[i][0]);        else        {            int c=qpow(2,d-1);            if(a[i+1].y)add(dp[i+1][0],(dp[i][1]+2ll*dp[i][0]%p)%p*c);            add(dp[i+1][1],(dp[i][1]+2ll*dp[i][0]%p)%p*c);        }    }    printf("%lld %lld\n",dp[k][1],mx);    return 0;}
]]>
@@ -6344,11 +6344,11 @@ - 洛谷P3336 [ZJOI2013]话旧 题解 - - /2022/05/25/luo-gu-p3336-zjoi2013-hua-jiu-ti-jie/ + 洛谷P3327 [SDOI2015]约数个数和 题解 + + /2022/05/25/luo-gu-p3327-sdoi2015-yue-shu-ge-shu-he-ti-jie/ - 洛谷P3336 [ZJOI2013]话旧 题解

题目链接:P3336 [ZJOI2013]话旧

题意:小林跟着银河队选手去了一趟宇宙比赛,耳濡目染,变得学术起来。回来后,他发现世界大变样了。比丘兽究级进化,成了凤凰兽;金先生因为发了一篇 paper,一跃成为教授,也成为了银河队选拔委员会成员。

一日,小林与金教授聊天。金教授回忆起过去的岁月,那些年他学过的电路原理。他曾经对一种三角波很感兴趣,并且进行了一些探究。小林感到很好奇,于是金教授就将课题形式化地说了一遍。

有一定义在 $[0,N]$ 的连续函数 $f(x)$,其中 $N$ 是整数,满足 $f(0)=f(N)=0$,它的所有极值点在整数处取到,且 $f(x)$ 的极小值均是 $0$。对于任意的 $0$ 到 $N-1$ 间的整数 $I$,$f(x)$ 在 $(I, I+1)$ 上是斜率为 $1$ 或 $-1$ 的一次函数。

金先生研究的是,若他知道其中 $K$ 个整点的函数值,那么:

  1. 有多少个函数满足条件?
  2. 满足条件的函数中,$f(x)$ 的最大值,最大能是多少?

小林思考了一下,便想出了很好的算法。那么作为经过多年训练的你呢?

输入格式

第一行包含两个用空格隔开的整数 $N,K$。接下来 $K$ 行,每行两个整数,表示 $x_i$ 和 $f(x_i)$。

输出格式

一行两个整数,分别对应两个问题的答案。考虑到第一问答案可能很大,你只要输出它除以 $19940417$ 的余数。

输入输出样例

输入 #1

2 0

输出 #1

1 1

输入 #2

6 94 24 22 04 26 05 12 00 00 0

输出 #2

1 2

说明/提示

  • 对于 $10\%$ 的数据,$N \leq 10$。
  • 对于 $20\%$ 的数据,$N \leq 50$。
  • 对于 $30\%$ 的数据,$N \leq 100$,$K \leq 100$。
  • 对于 $50\%$ 的数据,$N \leq 10^3$,$K \leq 10^3$。
  • 对于 $70\%$ 的数据,$N \leq 10^5$。
  • 另有 $10\%$ 的数据,$K=0$。
  • 对于 $100\%$ 的数据,$0 \leq N \leq 10^9$,$0 \leq K \leq 10^6$。

题目说了极小值为 $0$ ,说明每次下降都是直接一口气到底的

从文字中可以看出题目保证了有解(不然怎么输出最大值)

考虑设计状态 $dp[i][0/1]$ 表示是需要访问到 $i$ 结点时的路径是向上还是向下的

  • $dp[i][1] \to dp[i+1][0]$

    显然延长左右的两条路径,可得零点 $a,b$

    然后在 $a,b$ 间反复横跳便可以相连了

    设 $a,b$ 间的距离为 $2k$ ,则方案数为 $2^{k-1}$

    如下图 $(2k=6)$

    \          / \        /  \/\/\/\/

    观察到其形成了 $4$ 个 \/ 的结构,不妨其为齿孔

    \          / \  /\    /  \/  \/\/

    注意到此时除了两侧的齿孔不可扩展以外,其余均可扩展

    故方案数为 $2^2 = 4$

  • $dp[i][0] \to dp[i+1][0]$

    不妨将 $i$ 与其向上的路径看作一个点,则又可以确定两个零点 $a,b$

    而此时左侧的齿孔也可以扩展了,而右侧的仍不可,故方案数为 $2^k$

    如下图 $(2k=4)$

    /\        /  \      /   \/\/\/

    扩展左侧齿孔会变成

     /\      //  \    /    \/\/

    故方案数为 $2^2 = 4$

  • $dp[i][1]\to dp[i+1][1]$

    与上一种情况类似,将 $i+1$ 与其向下的路径看作一个点

    右侧的齿孔可以扩展,而此时左侧的不可,故方案数为 $2^k$

  • $dp[i][0] \to dp[i+1][1]$

    考虑将 $i+1$ 与其向下的路径看作一个点

    则此时左侧的齿孔可以扩展,而右侧的不可以

    故方案数为 $2^k$

然而有些情况会导致 $k<0$ ,因此要特判一下

还有一些特殊的情况,比如 $i+1$ 恰好在 $i$ 的向上/下的路径上,也需要特判

然后最大值的求解,根据贪心,一定先选掉所有的上升

设有 $a$ 个上升, $b$ 个下降,则有

则最大值为

但是要注意,如果可以选择向上,一定有 $dp[i][0]\ne0$

因此对于所有的 $i$ 到 $i+1$ 的转移,都额外计算一下先走到底再上升的情况,即

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e6+15)const int p=19940417;int n,k,mx;void add(int &a,int b){a=((a+b%p)%p+p)%p;}int qpow(int a,int b){    int ans=1,base=a%p;    while(b)    {        if(b&1)ans=ans*base%p;        base=base*base%p;        b>>=1;    }    return ans;}struct node{    int x,y;}a[N];bool operator<(node a,node b)    {return a.x<b.x;}bool operator==(node a,node b)    {return a.x==b.x&&a.y==b.y;}int dp[N][2];signed main(){    read(n);read(k);    for(int i=1; i<=k; i++)        read(a[i].x),read(a[i].y);    a[++k]={0,0};a[++k]={n,0};    sort(a+1,a+1+k);k=unique(a+1,a+1+k)-a-1;    dp[1][1]=1;    for(int i=1; i<k; i++)    {        int d=(a[i+1].x-a[i].x-a[i+1].y-a[i].y)>>1;        mx=max(mx,(a[i+1].x+a[i+1].y-a[i].x-a[i].y)>>1);        if(dp[i][0])mx=max(mx,(a[i+1].x+a[i+1].y-a[i].x+a[i].y)>>1);        if(a[i+1].y-a[i].y==a[i].x-a[i+1].x)            add(dp[i+1][1],dp[i][0]+dp[i][1]);        else if(a[i+1].y-a[i].y==a[i+1].x-a[i].x)            add(dp[i+1][0],dp[i][0]+(a[i].y?0:dp[i][1]));        else if(d<0)            add(dp[i+1][1],dp[i][0]);        else if(d==0)            add(dp[i+1][0],dp[i][0]+dp[i][1]),add(dp[i+1][1],dp[i][0]);        else        {            int c=qpow(2,d-1);            if(a[i+1].y)add(dp[i+1][0],(dp[i][1]+2ll*dp[i][0]%p)%p*c);            add(dp[i+1][1],(dp[i][1]+2ll*dp[i][0]%p)%p*c);        }    }    printf("%lld %lld\n",dp[k][1],mx);    return 0;}
]]>
+ 洛谷P3327[SDOI2015]约数个数和 题解

题目链接:P3327[SDOI2015]约数个数和

题意:设 \(d(x)\)\(x\) 的约数个数,给定 \(n,m\) ,求 \[\sum_{i=1}^{n}\sum_{j=1}^{m}d(ij)\]

感觉我前几篇题解的无解释推导有点不可读

所以这篇还是写的清楚一点叭

这题需要一个引理(感觉没必要记住证明的方法)

引理:设 \(d(x)\)\(x\) 的约数个数,则有 \[d(ij)=\sum_{x \mid i}\sum_{y \mid j}[\gcd(x,y)=1]\]

证明:摘自 https://siyuan.blog.luogu.org/solution-p3327

我们考虑把每个因子一一映射。

如果 \(ij\) 的因子 \(k\) 中有一个因子 \(p^c\),\(i\) 中有因子 \(p^a\),\(j\) 中有因子 \(p^b\)。我们规定:

  • 如果 \(c\le a\),那么在 \(i\) 中选择。
  • 如果 \(c>a\),那么我们把 \(c\) 减去 \(a\),在 \(j\) 中选择 \(p^{c-a}\)(在 jj 中选择 \(p^e\) 表示的是 \(p^{a+e}\) )

对于 \(ij\) 的因子 \(k\) 的其他因子同理。于是对于任何一个 \(k\)有一个唯一的映射,且每一个选择对应着唯一的 \(k\)。

通过如上过程,我们发现:对于 \(ij\)的因子 \(k=\prod {p_i}^{c_i}\),我们不可能同时在\(i\)\(j\) 中选择 \(p_i\)(优先在 \(i\) 中选择,如果不够就只在 \(j\) 中选择不够的指数),故 \(x\) 和 \(y\) 必须互质。

等式得证。

有了这个引理,就可以开始搞了

不妨假设 \(n \le m\) \[\sum_{i=1}^{n}\sum_{j=1}^{m}d(ij)\] 代入 \[\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y \mid j}[\gcd(x,y)=1]\] 莫比乌斯反演 \[\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y \mid j}\sum_{d\mid\gcd(x,y)}\mu(d)\] 这个 \(d \mid \gcd(x,y)\)看着很难受,把它换掉 \[\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y \midj}\sum_{d=1}^{n}\mu(d)[d\mid \gcd(x,y)]\] 啊它怎么又回来了,不急,继续推

移一下 \[\sum_{d=1}^{n}\mu(d)\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x\mid i}\sum_{y\mid j}[d\mid \gcd(x,y)]\] 可以发现这个正整数 \(x\)的取值范围在 \([1,n]\)

而它的出现次数其实就是 \([1,n]\)中有多少个数有因数 \(x\)

也就是 \(\sum_{x=1}^{n}[x \mid n] =\sum_{x=1}^{n}\left\lfloor\frac{n}{x}\right\rfloor\)

\(y\) 同理,则可以得到 \[\sum_{d=1}^{n}\mu(d)\sum_{x=1}^{n}\sum_{y=1}^{m}\left\lfloor{\dfrac{n}{x}}\right\rfloor\left\lfloor{\dfrac{m}{y}}\right\rfloor[d\mid\gcd(x,y)]\] 考虑消掉后面那个东西

注意到 \(d \mid \gcd(dx,dy)\)恒成立,又因为 \(\sum_{k\in K}a_k = \sum_{p(k)\in K}a_{p(k)}\)

\[\sum_{d=1}^{n}\mu(d)\sum_{dx=1}^{n}\sum_{dy=1}^{m}\left\lfloor{\dfrac{n}{dx}}\right\rfloor\left\lfloor{\dfrac{m}{dy}}\right\rfloor\] 整理一下就是 \[\sum_{d=1}^{n}\mu(d)\sum_{x=1}^{\left\lfloor{\frac{n}{d}}\right\rfloor}\left\lfloor{\dfrac{n}{dx}}\right\rfloor\sum_{y=1}^{\left\lfloor{\frac{m}{d}}\right\rfloor}\left\lfloor{\dfrac{m}{dy}}\right\rfloor\] 然后就可以数论分块啦!

代码:

#include <bits/stdc++.h>using namespace std;#define int long longtypedef long long ll;#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e4+15)int prime[N],sum[N],pcnt,mu[N],g[N];bool ck[N];int solve(int n){    int res=0;    for(int l=1,r; l<=n; l=r+1)    {        r=(n/(n/l));        res+=(r-l+1)*(n/l);    }    return res;}void Mobius(){    mu[1]=1;    for(int i=2; i<=N-5; i++)    {        if(!ck[i])        {            prime[++pcnt]=i;            mu[i]=-1;        }        for(int j=1; j<=pcnt&&i*prime[j]<=N-5; j++)        {            int pos=i*prime[j];            ck[pos]=1;            if(i%prime[j])            {                mu[pos]=-mu[i];            }else            {                mu[pos]=0;                break;            }        }    }    for(int i=1; i<=N-5; i++)        sum[i]+=sum[i-1]+mu[i];    for(int i=1; i<=N-5; i++)        g[i]=solve(i);}int Q,n,m;signed main(){    Mobius();    read(Q);    while(Q--)    {        read(n);read(m);        if(n>m)swap(n,m);        int res=0;        for(int l=1,r; l<=n; l=r+1)        {            r=min(n/(n/l),m/(m/l));            res+=(sum[r]-sum[l-1])*g[n/l]*g[m/l];        }        write(res);pc('\n');    }    return 0;}
]]>
@@ -6375,7 +6375,7 @@ /2022/05/25/luo-gu-p3747-liu-sheng-lian-kao-2017-xiang-feng-shi-wen-hou-ti-jie/ - 洛谷P3747 [六省联考 2017] 相逢是问候 题解

题目链接:洛谷P3747 [六省联考 2017] 相逢是问候

题意:B 君希望以维护一个长度为 $n$ 的数组,这个数组的下标为从 $1$ 到 $n$ 的正整数。

一共有 $m$ 个操作,可以分为两种:

  • 0 l r 表示将第 $l$ 个到第 $r$ 个数( $a_l,a_{l+1} …a_r$)中的每一个数 $a_i$ 替换为 $c^{a_i}$ ,即 $c$ 的 $a_i$ 次方,其中 $c$ 是输入的一个常数,也就是执行赋值 $a_i = c^{a_i}$。
  • 1 l r 求第 $l$ 个到第 $r$ 个数的和,也就是输出: $\sum_{i=l}^{r}a_i$

因为这个结果可能会很大,所以你只需要输出结果 $\bmod \ p$ 的值即可。

建议先去做一做 P4139P4145 ,这道题就是这俩的综合加强版本 题解在这里 link1 link2

有个结论

$O(\varphi^* (p)=1)=O(\log p)$

注:这里左边指的是最小的一个正整数 $x$ 使得

这里不再证明,见上面的link1

不难发现,对于某个数至多修改 $O(\log p)$ 次就会变成一个常数

考虑直接在线段树上暴力更新信息

更新的时候不要直接用快速幂,这样会多一个 $O(\log p)$

考虑预处理 $c^i$ 和 $c^{ik}$ ,然后就可以 $O(1)$ 查询了(这个就是光速幂的原理)

$k$ 一般取到 $\sqrt{p}$ ,这里我取了32768,即 $2^{15}$

然后 $p$ 是不变的,所以可以预处理一下 $\varphi^i(p)$

其他的,详见代码

时间复杂度是 $O(n \log n \log p)$ 的

证明:(势能分析)

代码如下,写的很烂 qwq

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e4+15)#define P ((1<<15)-1)int n,p[30],cnt,c,mod;int a[N][30],sum[N<<2],mn[N<<2];int pw1[30][P+15],pw2[30][P+15];#define ls(at) (at<<1)#define rs(at) (at<<1|1)int phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}int Mod(int x,int p){return x>=p?x%p+p:x;}void init(){    for(int i=0; i<=cnt; i++)    {        pw1[i][0]=pw2[i][0]=1;        for(int j=1; j<1<<15; j++)            pw1[i][j]=Mod(pw1[i][j-1]*c,p[i]);        pw2[i][1]=Mod(pw1[i][P]*c,p[i]);        for(int j=2; j<1<<15; j++)            pw2[i][j]=Mod(pw2[i][j-1]*pw2[i][1],p[i]);    }}int Pow(int n,int i){    return Mod(pw1[i][n&P]*pw2[i][n>>15],p[i]);}int calc(int x,int dep,int i){    if(!dep)return Mod(x,p[i]);    if(i==cnt)return 1;    return Pow(calc(x,dep-1,i+1),i);}void push_up(int at){    mn[at]=min(mn[ls(at)],mn[rs(at)]);    sum[at]=sum[ls(at)]+sum[rs(at)];    if(sum[at]>=p[0])sum[at]%=p[0];}void build(int l,int r,int at){    mn[at]=0;    if(l==r){sum[at]=a[l][0];return;}    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void update(int nl,int nr,int l,int r,int at){    if(mn[at]>cnt)return;    if(l==r)    {        ++mn[at];        sum[at]=a[l][mn[at]];        return;    }    int mid=(l+r)>>1;    if(nl<=mid)update(nl,nr,l,mid,ls(at));    if(nr>mid)update(nl,nr,mid+1,r,rs(at));    push_up(at);}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)return sum[at];    if(nl>r||nr<l)return 0;    int mid=(l+r)>>1;    int res=query(nl,nr,l,mid,ls(at))+query(nl,nr,mid+1,r,rs(at));    return res%p[0];}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q,op,l,r;    read(n);read(Q);read(p[0]);read(c);    cnt=0;    while(p[cnt]>1)++cnt,p[cnt]=phi(p[cnt-1]);    init();    for(int i=1; i<=n; i++)    {        read(a[i][0]);        for(int j=1; j<=cnt+1; j++)            a[i][j]=calc(a[i][0],j,0)%p[0];        a[i][0]%=p[0];    }    // for(int i=1; i<=n; i++)    //     for(int j=0; j<=cnt+1; j++)    //         cout << a[i][j] << " \n"[j==cnt+1];    build(1,n,1);    while(Q--)    {        read(op);read(l);read(r);        if(op==0)update(l,r,1,n,1);        else write(query(l,r,1,n,1)),pc('\n');    }    return 0;}

哇。想了一天的毒瘤题。脑袋要裂开了 -> q779太菜了

嗯。本题的综合性非常强。

不就是个 线段树+势能分析+扩展欧拉定理+光速幂 吗(逃

参考文献

[1] https://www.luogu.com.cn/blog/s-r-f/solution-p3747

]]>
+ 洛谷P3747 [六省联考2017] 相逢是问候 题解

题目链接:洛谷P3747[六省联考 2017] 相逢是问候

题意:B 君希望以维护一个长度为 \(n\) 的数组,这个数组的下标为从 \(1\) 到 \(n\) 的正整数。

一共有 \(m\)个操作,可以分为两种:

  • 0 l r 表示将第 \(l\)个到第 \(r\) 个数( \(a_l,a_{l+1} ...a_r\))中的每一个数 \(a_i\) 替换为 \(c^{a_i}\) ,即 \(c\) 的 \(a_i\) 次方,其中 \(c\) 是输入的一个常数,也就是执行赋值 \(a_i = c^{a_i}\)。
  • 1 l r 求第 \(l\)个到第 \(r\) 个数的和,也就是输出:\(\sum_{i=l}^{r}a_i\)

因为这个结果可能会很大,所以你只需要输出结果 \(\bmod \ p\) 的值即可。

建议先去做一做 P4139 和 P4145,这道题就是这俩的综合加强版本 题解在这里 link1link2

有个结论

\(O(\varphi^* (p)=1)=O(\log p)\)

注:这里左边指的是最小的一个正整数 \(x\) 使得 \[\begin{aligned}\begin{matrix}&\underbrace{\varphi(\varphi(\dots\varphi(p)))}=1\\ &x 个\varphi\quad\,\,\,\,\end{matrix}\end{aligned}\] 这里不再证明,见上面的link1

不难发现,对于某个数至多修改 \(O(\logp)\) 次就会变成一个常数

考虑直接在线段树上暴力更新信息

更新的时候不要直接用快速幂,这样会多一个 \(O(\log p)\)

考虑预处理 \(c^i\)\(c^{ik}\) ,然后就可以 \(O(1)\) 查询了(这个就是光速幂的原理)

\(k\) 一般取到 \(\sqrt{p}\),这里我取了32768,即 \(2^{15}\)

然后 \(p\)是不变的,所以可以预处理一下 \(\varphi^i(p)\)

其他的,详见代码

时间复杂度是 \(O(n \log n \log p)\)

证明:(势能分析) \[O\left(2\sqrt{p}+\sqrt{p}{\log p}+n \times \dfrac{n \log p}{n} \times\log n\right) = O(n \log n \log p)\] 代码如下,写的很烂 qwq

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(5e4+15)#define P ((1<<15)-1)int n,p[30],cnt,c,mod;int a[N][30],sum[N<<2],mn[N<<2];int pw1[30][P+15],pw2[30][P+15];#define ls(at) (at<<1)#define rs(at) (at<<1|1)int phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}int Mod(int x,int p){return x>=p?x%p+p:x;}void init(){    for(int i=0; i<=cnt; i++)    {        pw1[i][0]=pw2[i][0]=1;        for(int j=1; j<1<<15; j++)            pw1[i][j]=Mod(pw1[i][j-1]*c,p[i]);        pw2[i][1]=Mod(pw1[i][P]*c,p[i]);        for(int j=2; j<1<<15; j++)            pw2[i][j]=Mod(pw2[i][j-1]*pw2[i][1],p[i]);    }}int Pow(int n,int i){    return Mod(pw1[i][n&P]*pw2[i][n>>15],p[i]);}int calc(int x,int dep,int i){    if(!dep)return Mod(x,p[i]);    if(i==cnt)return 1;    return Pow(calc(x,dep-1,i+1),i);}void push_up(int at){    mn[at]=min(mn[ls(at)],mn[rs(at)]);    sum[at]=sum[ls(at)]+sum[rs(at)];    if(sum[at]>=p[0])sum[at]%=p[0];}void build(int l,int r,int at){    mn[at]=0;    if(l==r){sum[at]=a[l][0];return;}    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void update(int nl,int nr,int l,int r,int at){    if(mn[at]>cnt)return;    if(l==r)    {        ++mn[at];        sum[at]=a[l][mn[at]];        return;    }    int mid=(l+r)>>1;    if(nl<=mid)update(nl,nr,l,mid,ls(at));    if(nr>mid)update(nl,nr,mid+1,r,rs(at));    push_up(at);}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)return sum[at];    if(nl>r||nr<l)return 0;    int mid=(l+r)>>1;    int res=query(nl,nr,l,mid,ls(at))+query(nl,nr,mid+1,r,rs(at));    return res%p[0];}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q,op,l,r;    read(n);read(Q);read(p[0]);read(c);    cnt=0;    while(p[cnt]>1)++cnt,p[cnt]=phi(p[cnt-1]);    init();    for(int i=1; i<=n; i++)    {        read(a[i][0]);        for(int j=1; j<=cnt+1; j++)            a[i][j]=calc(a[i][0],j,0)%p[0];        a[i][0]%=p[0];    }    // for(int i=1; i<=n; i++)    //     for(int j=0; j<=cnt+1; j++)    //         cout << a[i][j] << " \n"[j==cnt+1];    build(1,n,1);    while(Q--)    {        read(op);read(l);read(r);        if(op==0)update(l,r,1,n,1);        else write(query(l,r,1,n,1)),pc('\n');    }    return 0;}

哇。想了一天的毒瘤题。脑袋要裂开了 -> q779太菜了

嗯。本题的综合性非常强。

不就是个 线段树+势能分析+扩展欧拉定理+光速幂吗(逃

参考文献

[1] https://www.luogu.com.cn/blog/s-r-f/solution-p3747

]]>
@@ -6404,7 +6404,7 @@ /2022/05/25/luo-gu-p3935-calculating-ti-jie/ - 洛谷P3935 Calculating 题解

题目链接:P3935 Calculating

题意

若 $n$ 的分解质因数结果为 $\prod_{i=1}^{s}p_i^{k_i}$

令 $f(n) = \prod_{i=1}^{s}(k_i+1)$

根据容斥原理,可以把答案分为两个部分

然后推推柿子

于是有

考虑数论分块

时间复杂度 $O(\sqrt{r})$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fconst int mod=998244353;int L,R;int solve(int n){    int l=1,r=0,res=0;    while(l<=n)    {        r=n/(n/l);        res=(res+(r-l+1)*(n/l)%mod)%mod;        l=r+1;    }    return res%mod;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> L >> R;    cout << (solve(R)-solve(L-1)+mod)%mod << endl;    return 0;}
]]>
+ 洛谷P3935 Calculating 题解

题目链接:P3935Calculating

题意

\(n\) 的分解质因数结果为 \(\prod_{i=1}^{s}p_i^{k_i}\)

\(f(n) =\prod_{i=1}^{s}(k_i+1)\)

\[\sum\limits_{i=l}^{r}f(i) \bmod 998244353\]

根据容斥原理,可以把答案分为两个部分 \[\sum_{i=l}^{r}f(i) = \sum_{i=1}^{r}f(i) - \sum_{i=1}^{l-1}f(i)\] 然后推推柿子 \[\begin{aligned}\sum_{i=1}^{r}f(i) &= \sum_{i=1}^{r}\prod_{j=1}^{s_i}{(k_{ij}+1)}\\&=\sum_{i=1}^{r}\sum_{d\mid i}1\\&=\sum_{i=1}^{r}\left\lfloor\dfrac{r}{i}\right\rfloor\end{aligned}\] 于是有 \[\sum_{i=1}^{n}f(i) =\sum_{i=1}^{n}\left\lfloor{\dfrac{n}{i}}\right\rfloor\] 考虑数论分块

时间复杂度 \(O(\sqrt{r})\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fconst int mod=998244353;int L,R;int solve(int n){    int l=1,r=0,res=0;    while(l<=n)    {        r=n/(n/l);        res=(res+(r-l+1)*(n/l)%mod)%mod;        l=r+1;    }    return res%mod;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> L >> R;    cout << (solve(R)-solve(L-1)+mod)%mod << endl;    return 0;}
]]>
@@ -6431,7 +6431,7 @@ /2022/05/25/luo-gu-p3985-bu-kai-xin-de-jin-ming-ti-jie/ - 洛谷P3985 不开心的金明 题解

题目链接:P3985 不开心的金明

题意

金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说了不算(有很大的限制),而且不超过 $W$ 元钱。”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 $W$ 元。于是,他把每件物品规定了一个重要度整数 $p_i$ 表示。他还从因特网上查到了每件物品的价格 $v_i$ (都是整数元)。

妈妈看到购物单后进行了审查,要求购物单上所有的物品价格的极差(最贵的减去最便宜的)不超过$3$(当然金明至今不知道为什么会这样)。他希望在不超过 $W$ 元(可以等于 $W$ 元)的前提下,使购买的重要度总和 $\sum p_i$ 的最大。

请你帮助金明设计一个满足要求的购物单,你只需要告诉我们重要度的最大的和。

数据保证:$\max(v_i)-\min(v_i) \le 3$

数据范围:$1 \le N \le 100 ,1\le p_i \le 10^7,1 \le W,v_i \le 10^9$

容易发现是01背包

这个极差不超过 $3$ 根本不用管,都保证了。

注意到这个费用很大但是极差很小,于是把所有的花费全部减去 $\min(v_i)$

但是这个 $W$ 巨大,不可能按照 $j=W \to 1$ 去枚举

于是我们找到 $\sum v_i^\prime$ ,从它开始枚举,并增加一维 $k$ 表示装了 $k$ 个物品

这样我们只要判断 $j+k\times\min(v_i) \le W$ 就知道我们枚举的背包容量是否合法了

代码:(变量名和上面的陈述完全不一样)

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(105)int n,m,sum;int w[N],v[N],mn=INF,dp[N*5][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> w[i] >> v[i];        mn=min(mn,w[i]);sum+=w[i]; // 确实,但是,代码写的是w[i] qwq    }    for(int i=1; i<=n; i++) w[i]-=mn;    sum-=n*mn;    for(int i=1; i<=n; i++)        for(int j=sum; j>=w[i]; j--)            for(int k=n; k>=1; k--)                if(j+k*mn<=m)                {                    dp[j][k]=max(dp[j][k],dp[j-w[i]][k-1]+v[i]);                }    int res=0;    for(int i=1; i<=sum; i++)        for(int j=1; j<=n; j++)            res=max(res,dp[i][j]);    cout << res;    return 0;}
]]>
+ 洛谷P3985 不开心的金明 题解

题目链接:P3985不开心的金明

题意

金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说了不算(有很大的限制),而且不超过\(W\)元钱。”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的\(W\)元。于是,他把每件物品规定了一个重要度整数 \(p_i\)表示。他还从因特网上查到了每件物品的价格 \(v_i\) (都是整数元)。

妈妈看到购物单后进行了审查,要求购物单上所有的物品价格的极差(最贵的减去最便宜的)不超过\(3\)(当然金明至今不知道为什么会这样)。他希望在不超过\(W\) 元(可以等于 \(W\) 元)的前提下,使购买的重要度总和 \(\sum p_i\) 的最大。

请你帮助金明设计一个满足要求的购物单,你只需要告诉我们重要度的最大的和。

数据保证:\(\max(v_i)-\min(v_i) \le3\)

数据范围:\(1 \le N \le 100 ,1\le p_i \le10^7,1 \le W,v_i \le 10^9\)

容易发现是01背包

这个极差不超过 \(3\)根本不用管,都保证了。

注意到这个费用很大但是极差很小,于是把所有的花费全部减去 \(\min(v_i)\)

但是这个 \(W\) 巨大,不可能按照\(j=W \to 1\) 去枚举

于是我们找到 \(\sum v_i^\prime\),从它开始枚举,并增加一维 \(k\)表示装了 \(k\) 个物品

这样我们只要判断 \(j+k\times\min(v_i) \leW\) 就知道我们枚举的背包容量是否合法了

代码:(变量名和上面的陈述完全不一样)

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(105)int n,m,sum;int w[N],v[N],mn=INF,dp[N*5][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)    {        cin >> w[i] >> v[i];        mn=min(mn,w[i]);sum+=w[i]; // 确实,但是,代码写的是w[i] qwq    }    for(int i=1; i<=n; i++) w[i]-=mn;    sum-=n*mn;    for(int i=1; i<=n; i++)        for(int j=sum; j>=w[i]; j--)            for(int k=n; k>=1; k--)                if(j+k*mn<=m)                {                    dp[j][k]=max(dp[j][k],dp[j-w[i]][k-1]+v[i]);                }    int res=0;    for(int i=1; i<=sum; i++)        for(int j=1; j<=n; j++)            res=max(res,dp[i][j]);    cout << res;    return 0;}
]]>
@@ -6458,7 +6458,7 @@ /2022/05/25/luo-gu-p4095-heoi2013-eden-de-xin-bei-bao-wen-ti-ti-jie/ - 洛谷P4095 [HEOI2013]Eden 的新背包问题 题解

题目链接:P4095 [HEOI2013]Eden 的新背包问题

题意

失忆的 Eden 总想努力地回忆起过去,然而总是只能清晰地记得那种思念的感觉,却不能回忆起她的音容笑貌。

记忆中,她总是喜欢给 Eden 出谜题:在 valentine’s day 的夜晚,两人在闹市中闲逛时,望着礼品店里精巧玲珑的各式玩偶,她突发奇想,问了 Eden 这样的一个问题:有 $n$ 个玩偶,每个玩偶有对应的价值、价钱,每个玩偶都可以被买有限次,在携带的价钱 $m$ 固定的情况下,如何选择买哪些玩偶以及每个玩偶买多少个,才能使得选择的玩偶总价钱不超过 $m$,且价值和最大。

众所周知的,这是一个很经典的多重背包问题,Eden 很快解决了,不过她似乎因为自己的问题被飞快解决感到了一丝不高兴,于是她希望把问题加难:多次询问,每次询问都将给出新的总价钱,并且会去掉某个玩偶(即这个玩偶不能被选择),再问此时的多重背包的答案(即前一段所叙述的问题)。

这下 Eden 犯难了,不过 Eden 不希望自己被难住,你能帮帮他么?

数据范围:

  • 对于 $100\%$ 的数据,保证 $1 \leq n \leq 1000$,$1 \leq q \leq 3\times 10^5$, $1 \leq a_i,b_i,c_i \leq 100$,$0 \leq d_i < n$,$0 \leq e_i \leq 1000$。
  • $\sum e_i \le 2\times 10^7$ (讨论区补充的)

不难发现,如果不考虑第 $p$ 个物品

最大的价值其实就是 $1\sim p-1$ 和 $p+1 \sim n$ 的最大价值

考虑正反分别跑一遍多重背包,然后对于每个询问暴力合并泛化物品

单调队列优化更快一点,当然二进制拆分也是可以过的。

二进制拆分写法,时间复杂度 $O(m \sum_{i=1}^{n}\log c_i + \sum e_i)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(8e3+15)#define M (int)(1e3+15)int n,cnt,Q,f[N][M],g[N][M],w[N],v[N],id[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1; i<=n; i++)    {        int w1,v1,c,t=1;        read(w1);read(v1);read(c);        while(t<=c)        {            ++cnt;id[cnt]=i;            w[cnt]=t*w1;v[cnt]=t*v1;            c-=t;t<<=1;        }        ++cnt;w[cnt]=c*w1;v[cnt]=c*v1;id[cnt]=i;    }    n=cnt;    for(int i=1; i<=n; i++)    {        for(int j=0; j<=1000; j++) // 不要改成 <w[i] ,会越界的哦~ qwq            f[i][j]=f[i-1][j];        for(int j=w[i]; j<=1000; j++)            f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);    }    for(int i=n; i>=1; i--)    {        for(int j=0; j<=1000; j++)            g[i][j]=g[i+1][j];        for(int j=w[i]; j<=1000; j++)            g[i][j]=max(g[i+1][j],g[i+1][j-w[i]]+v[i]);    }    read(Q);    while(Q--)    {        int p,m,res=0,l=0,r=0;        read(p);read(m);++p;        while(l+1<=n&&id[l+1]<p)++l;        r=l;        while(r+1<=n&&id[r+1]<=p)++r;        r++;        for(int k=0; k<=m; k++)            res=max(res,f[l][k]+g[r][m-k]);        write(res);pc('\n');    }    return 0;}

单调队列优化写法,时间复杂度 $O(nm+\sum e_i)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e3+15)#define M (int)(1e3+15)int n,Q,st,en,t[N],q[N],f[N][M],g[N][M],W[N],V[N],C[N],id[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1; i<=n; i++)        read(W[i]),read(V[i]),read(C[i]);    for(int i=1; i<=n; i++)    {        int &w=W[i],&v=V[i],&c=C[i];        c=min(c,1000/w);        for(int j=1; j<=1000; j++)            f[i][j]=f[i-1][j];        for(int d=0; d<w; d++)        {            st=en=1;            for(int k=0; k*w+d<=1000; k++)            {                int j=k*w+d;                while(st<en&&q[en]<=f[i][j]-k*v)--en;                t[++en]=k;q[en]=f[i][j]-k*v;                while(st<en&&k-t[st+1]>c)++st;                f[i][j]=max(f[i][j],q[st+1]+k*v);            }        }    }    for(int i=n; i>=1; i--)    {        int &w=W[i],&v=V[i],&c=C[i];        c=min(c,1000/w);        for(int j=1; j<=1000; j++)            g[i][j]=g[i+1][j];        for(int d=0; d<w; d++)        {            st=en=1;            for(int k=0; k*w+d<=1000; k++)            {                int j=k*w+d;                while(st<en&&q[en]<=g[i][j]-k*v)--en;                t[++en]=k;q[en]=g[i][j]-k*v;                while(st<en&&k-t[st+1]>c)++st;                g[i][j]=max(g[i][j],q[st+1]+k*v);            }        }    }    read(Q);    while(Q--)    {        int p,m,res=0,l=0,r=0;        read(p);read(m);++p;        for(int k=0; k<=m; k++)            res=max(res,f[p-1][k]+g[p+1][m-k]);        write(res);pc('\n');    }    return 0;}
]]>
+ 洛谷P4095[HEOI2013]Eden 的新背包问题 题解

题目链接:P4095[HEOI2013]Eden 的新背包问题

题意

失忆的 Eden总想努力地回忆起过去,然而总是只能清晰地记得那种思念的感觉,却不能回忆起她的音容笑貌。

记忆中,她总是喜欢给 Eden 出谜题:在 valentine's day的夜晚,两人在闹市中闲逛时,望着礼品店里精巧玲珑的各式玩偶,她突发奇想,问了Eden 这样的一个问题:有 \(n\)个玩偶,每个玩偶有对应的价值、价钱,每个玩偶都可以被买有限次,在携带的价钱\(m\)固定的情况下,如何选择买哪些玩偶以及每个玩偶买多少个,才能使得选择的玩偶总价钱不超过\(m\),且价值和最大。

众所周知的,这是一个很经典的多重背包问题,Eden很快解决了,不过她似乎因为自己的问题被飞快解决感到了一丝不高兴,于是她希望把问题加难:多次询问,每次询问都将给出新的总价钱,并且会去掉某个玩偶(即这个玩偶不能被选择),再问此时的多重背包的答案(即前一段所叙述的问题)。

这下 Eden 犯难了,不过 Eden 不希望自己被难住,你能帮帮他么?

数据范围:

  • 对于 \(100\%\) 的数据,保证 \(1 \leq n \leq 1000\),\(1 \leq q \leq 3\times 10^5\), \(1 \leq a_i,b_i,c_i \leq 100\),\(0 \leq d_i < n\),\(0 \leq e_i \leq 1000\)。
  • \(\sum e_i \le 2\times 10^7\)(讨论区补充的)

不难发现,如果不考虑第 \(p\)个物品

最大的价值其实就是 \(1\sim p-1\)\(p+1 \sim n\) 的最大价值

考虑正反分别跑一遍多重背包,然后对于每个询问暴力合并泛化物品

单调队列优化更快一点,当然二进制拆分也是可以过的。

二进制拆分写法,时间复杂度 \(O(m\sum_{i=1}^{n}\log c_i + \sum e_i)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(8e3+15)#define M (int)(1e3+15)int n,cnt,Q,f[N][M],g[N][M],w[N],v[N],id[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1; i<=n; i++)    {        int w1,v1,c,t=1;        read(w1);read(v1);read(c);        while(t<=c)        {            ++cnt;id[cnt]=i;            w[cnt]=t*w1;v[cnt]=t*v1;            c-=t;t<<=1;        }        ++cnt;w[cnt]=c*w1;v[cnt]=c*v1;id[cnt]=i;    }    n=cnt;    for(int i=1; i<=n; i++)    {        for(int j=0; j<=1000; j++) // 不要改成 <w[i] ,会越界的哦~ qwq            f[i][j]=f[i-1][j];        for(int j=w[i]; j<=1000; j++)            f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);    }    for(int i=n; i>=1; i--)    {        for(int j=0; j<=1000; j++)            g[i][j]=g[i+1][j];        for(int j=w[i]; j<=1000; j++)            g[i][j]=max(g[i+1][j],g[i+1][j-w[i]]+v[i]);    }    read(Q);    while(Q--)    {        int p,m,res=0,l=0,r=0;        read(p);read(m);++p;        while(l+1<=n&&id[l+1]<p)++l;        r=l;        while(r+1<=n&&id[r+1]<=p)++r;        r++;        for(int k=0; k<=m; k++)            res=max(res,f[l][k]+g[r][m-k]);        write(res);pc('\n');    }    return 0;}

单调队列优化写法,时间复杂度 \(O(nm+\sume_i)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e3+15)#define M (int)(1e3+15)int n,Q,st,en,t[N],q[N],f[N][M],g[N][M],W[N],V[N],C[N],id[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1; i<=n; i++)        read(W[i]),read(V[i]),read(C[i]);    for(int i=1; i<=n; i++)    {        int &w=W[i],&v=V[i],&c=C[i];        c=min(c,1000/w);        for(int j=1; j<=1000; j++)            f[i][j]=f[i-1][j];        for(int d=0; d<w; d++)        {            st=en=1;            for(int k=0; k*w+d<=1000; k++)            {                int j=k*w+d;                while(st<en&&q[en]<=f[i][j]-k*v)--en;                t[++en]=k;q[en]=f[i][j]-k*v;                while(st<en&&k-t[st+1]>c)++st;                f[i][j]=max(f[i][j],q[st+1]+k*v);            }        }    }    for(int i=n; i>=1; i--)    {        int &w=W[i],&v=V[i],&c=C[i];        c=min(c,1000/w);        for(int j=1; j<=1000; j++)            g[i][j]=g[i+1][j];        for(int d=0; d<w; d++)        {            st=en=1;            for(int k=0; k*w+d<=1000; k++)            {                int j=k*w+d;                while(st<en&&q[en]<=g[i][j]-k*v)--en;                t[++en]=k;q[en]=g[i][j]-k*v;                while(st<en&&k-t[st+1]>c)++st;                g[i][j]=max(g[i][j],q[st+1]+k*v);            }        }    }    read(Q);    while(Q--)    {        int p,m,res=0,l=0,r=0;        read(p);read(m);++p;        for(int k=0; k<=m; k++)            res=max(res,f[p-1][k]+g[p+1][m-k]);        write(res);pc('\n');    }    return 0;}
]]>
@@ -6487,7 +6487,7 @@ /2022/05/25/luo-gu-p4139-shang-di-yu-ji-he-de-zheng-que-yong-fa-ti-jie/ - 洛谷P4139 上帝与集合的正确用法 题解

题目链接:P4139 上帝与集合的正确用法

题意

有无穷数列

当 $n \to +\infty$ 时,计算

可以证明 $a_n\bmod p$在 $n$ 足够大时为常数

多组数据 $1\le Q\le 10^3$ ,$1 \le p\le 10^7$

其实这个题很简单

根据扩展欧拉定理,有

显然当 $n$ 足够大时,有 $a_{n-1} > \varphi(p)$

可以发现原问题转化为了求解

递归处理即可

关于这个递归为什么是 $O(\log p)$

显然每一次递归 $p$ 都会变成 $\varphi(p)$

根据欧拉函数的公式

设 $n=\prod_{i=1}^{s}p_i^{k_i}$

观察函数,可以发现

  • 当 $n$ 为偶数时,一定会把 $2$ 提出来变成 $1$ ,也就是至少除以 $2$
  • 当 $n$ 为奇数时,一定会把一个素因子提出来变成一个偶数,也就是产生了 $2$ 这个因子
  • 重复以上的过程直到 $n=1$ 结束

因此是 $O(\log p)$ 的

所以总的时间复杂度为 $O(Q\sqrt{p}\log p)$

不用线性筛跑得反而快,因为 $p$ 蛮大的,用了就变成 $O(p+Q\log p)$ 了

数比较大,快速幂可能会爆了 $\text{long long}$ ,所以要用龟速乘或者__int128

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fint phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}int qpow(int a,int b,int p){    int ans=1,base=a%p;    while(b)    {        if(b&1)ans=(__int128)ans*base%p;        base=(__int128)base*base%p;        b>>=1;    }    return ans;}int solve(int p){    if(p==1)return 0;    return qpow(2,solve(phi(p))+phi(p),p);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q,p;    cin >> Q;    while(Q--)    {        cin >> p;        cout << solve(p) << endl;    }    return 0;}

用筛法求 $\varphi(p)$ 跑了 1.52s,非筛法反倒就跑了114ms

]]>
+ 洛谷P4139上帝与集合的正确用法 题解

题目链接:P4139上帝与集合的正确用法

题意

有无穷数列 \[a_0=1,a_n=2^{a_{n-1}}\]\(n \to +\infty\) 时,计算\[a_n \bmod p\]\[{2}^{ {2}^{ {2}^{ {.}^{ {.}^{ {.}^{2} } } } } } \bmod p\] 可以证明 \(a_n\bmod p\)\(n\) 足够大时为常数

多组数据 \(1\le Q\le 10^3\)\(1 \le p\le 10^7\)

其实这个题很简单

根据扩展欧拉定理,有 \[\begin{aligned}a^b \equiv\begin{cases}a^b,&b< \varphi(m)\\\\a^{b \ \bmod \ \varphi(m)+\varphi(m)},&b\ge \varphi(m)\end{cases} \pmod{m}\end{aligned}\] 显然当 \(n\) 足够大时,有\(a_{n-1} > \varphi(p)\)

\[a_{n}\bmod p = 2^{a_{n-1}\bmod\, \varphi(p)+\varphi(p)}\bmod p\] 可以发现原问题转化为了求解 \[a_{n-1} \bmod \varphi(p) + \varphi(p)\] 递归处理即可

关于这个递归为什么是 \(O(\logp)\)

显然每一次递归 \(p\) 都会变成 \(\varphi(p)\)

根据欧拉函数的公式

\(n=\prod_{i=1}^{s}p_i^{k_i}\)\[\varphi(n) = n \prod\limits_{i=1}^s\left(\dfrac{p_i-1}{p_i}\right)\] 观察函数,可以发现

  • \(n\) 为偶数时,一定会把 \(2\) 提出来变成 \(1\) ,也就是至少除以 \(2\)
  • \(n\)为奇数时,一定会把一个素因子提出来变成一个偶数,也就是产生了 \(2\) 这个因子
  • 重复以上的过程直到 \(n=1\)结束

因此是 \(O(\log p)\)

所以总的时间复杂度为 \(O(Q\sqrt{p}\logp)\)

不用线性筛跑得反而快,因为 \(p\)蛮大的,用了就变成 \(O(p+Q\log p)\)

数比较大,快速幂可能会爆了 \(\text{longlong}\) ,所以要用龟速乘或者__int128

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fint phi(int n){    int ans=n;    for(int i=2; i<=n/i; i++)        if(n%i==0)        {            ans=ans/i*(i-1);            while(n%i==0)n/=i;        }    if(n>1)ans=ans/n*(n-1);    return ans;}int qpow(int a,int b,int p){    int ans=1,base=a%p;    while(b)    {        if(b&1)ans=(__int128)ans*base%p;        base=(__int128)base*base%p;        b>>=1;    }    return ans;}int solve(int p){    if(p==1)return 0;    return qpow(2,solve(phi(p))+phi(p),p);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    int Q,p;    cin >> Q;    while(Q--)    {        cin >> p;        cout << solve(p) << endl;    }    return 0;}

用筛法求 \(\varphi(p)\) 跑了1.52s,非筛法反倒就跑了114ms

]]>
@@ -6514,7 +6514,7 @@ /2022/05/25/luo-gu-p4141-xiao-shi-zhi-wu-ti-jie/ - 洛谷P4141 消失之物 题解

题目链接:P4141 消失之物

题意:ftiasch 有 $n$ 个物品, 体积分别是 $w_1,w_2,\dots,w_n$ 。由于她的疏忽,第 $i$ 个物品丢失了。

“要使用剩下的 $n-1$ 物品装满容积为 $x$ 的背包,有几种方法呢?”——这是经典的问题了。

她把答案记为 $\text{cnt}(i,x)$,想要得到所有 $i \in [1,n] ,x \in [1,m]$ 的 $\text{cnt}(i,x)$ 表格。

只需要输出末位数字。

设 $f[i][j]$ 为只用前 $i$ 件物品,不考虑删除任何物品时,恰好装满容量为 $j$ 的背包的方案数,显然有

滚动数组一下就是

f[0]=1;for(int i=1; i<=n; i++)for(int j=m; j>=w[i]; j--)f[j]+=f[j-w[i]];

那么现在求出来的 $f[i]$ 也就是原来的 $f[n][i]$ 了

于是设 $g[i][j]$ 为不考虑物品 $i$ 的贡献恰好装满容量为 $j$ 的背包的方案数

首先容易想到一个看起来正确的柿子(其实是错的)

为什么错呢?因为 $f[n][j-w[i]]$ 中也可能含有 $w[i]$ 的贡献!

那么其实稍微改一改就对了

此时的 $g[i][j-w[i]]$ 恰好删除了物品 $i$ 的贡献,不错

注意这个 $g[i][j]$ 需要 $j$ 顺推,因为我们需要 $g[i][j-w[i]]$

然后这个柿子还是不够精简

注意到我们其实并不需要记录 $i$ 这一状态,因为我们其实不需要保存这些信息

加上之前的滚动数组优化,柿子可以化简为

嗯,其实还可以化简。

memcpy(g,f,sizeof(f));for(int j=w[i]; j<=m; j++)g[j]-=g[j-w[i]];

时间复杂度 $O(nm)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;int w[N],f[N],g[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> w[i];    f[0]=1;    for(int i=1; i<=n; i++)        for(int j=m; j>=w[i]; j--)            f[j]=(f[j]+f[j-w[i]])%10; // 题目只要末位即可    for(int i=1; i<=n; i++)    {        memcpy(g,f,sizeof(f));        for(int j=w[i]; j<=m; j++)            g[j]=(g[j]-g[j-w[i]])%10;        for(int j=1; j<=m; j++)            cout <<(g[j]+10)%10; // +10是防止出现负结果        cout << '\n';    }    return 0;}
]]>
+ 洛谷P4141 消失之物 题解

题目链接:P4141消失之物

题意:ftiasch 有 \(n\) 个物品, 体积分别是 \(w_1,w_2,\dots,w_n\) 。由于她的疏忽,第\(i\) 个物品丢失了。

“要使用剩下的 \(n-1\) 物品装满容积为\(x\)的背包,有几种方法呢?”——这是经典的问题了。

她把答案记为 \(\text{cnt}(i,x)\),想要得到所有 \(i \in [1,n] ,x \in [1,m]\) 的 \(\text{cnt}(i,x)\) 表格。

只需要输出末位数字。

\(f[i][j]\) 为只用前 \(i\)件物品,不考虑删除任何物品时,恰好装满容量为 \(j\) 的背包的方案数,显然有 \[f[i][j]=f[i-1][j]+f[i-1][j-w[i]]\] 滚动数组一下就是

f[0]=1;for(int i=1; i<=n; i++)for(int j=m; j>=w[i]; j--)f[j]+=f[j-w[i]];

那么现在求出来的 \(f[i]\)也就是原来的 \(f[n][i]\)

于是设 \(g[i][j]\) 为不考虑物品\(i\) 的贡献恰好装满容量为 \(j\) 的背包的方案数

首先容易想到一个看起来正确的柿子(其实是错的) \[g[i][j]=f[n][j]-f[n][j-w[i]]\] 为什么错呢?因为 \(f[n][j-w[i]]\) 中也可能含有 \(w[i]\) 的贡献!

那么其实稍微改一改就对了 \[g[i][j]=f[n][j]-g[i][j-w[i]]\] 此时的 \(g[i][j-w[i]]\)恰好删除了物品 \(i\) 的贡献,不错

注意这个 \(g[i][j]\) 需要 \(j\) 顺推,因为我们需要 \(g[i][j-w[i]]\)

然后这个柿子还是不够精简

注意到我们其实并不需要记录 \(i\)这一状态,因为我们其实不需要保存这些信息

加上之前的滚动数组优化,柿子可以化简为 \[g[j]=\begin{cases}f[j]-g[j-w[i]],&j \ge w[i],\\f[j],&\text{default.}\end{cases}\] 嗯,其实还可以化简。

memcpy(g,f,sizeof(f));for(int j=w[i]; j<=m; j++)g[j]-=g[j-w[i]];

时间复杂度 \(O(nm)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e3+15)int n,m;int w[N],f[N],g[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> w[i];    f[0]=1;    for(int i=1; i<=n; i++)        for(int j=m; j>=w[i]; j--)            f[j]=(f[j]+f[j-w[i]])%10; // 题目只要末位即可    for(int i=1; i<=n; i++)    {        memcpy(g,f,sizeof(f));        for(int j=w[i]; j<=m; j++)            g[j]=(g[j]-g[j-w[i]])%10;        for(int j=1; j<=m; j++)            cout <<(g[j]+10)%10; // +10是防止出现负结果        cout << '\n';    }    return 0;}
]]>
@@ -6541,7 +6541,7 @@ /2022/05/25/luo-gu-p4145-shang-di-zao-ti-de-qi-fen-zhong-2-hua-shen-you-li-ge-guo-ti-jie/ - 洛谷P4145 上帝造题的七分钟 2 / 花神游历各国 题解

题目链接:P4145 上帝造题的七分钟 2 / 花神游历各国

题意:维护一个数据结构

  1. 区间sqrt()(对每个数开平方+下取整)

  2. 询问区间和

$\sqrt{a+b} \ne \sqrt{a}+\sqrt{b}$

因此无法用线段树维护

那么怎么搞呢?注意到数据范围中,每个数都为正整数,且不超过 $10^{12}$

$\sqrt{1}=1$

$\left(10^{12}\right)^{2^{-6}} \approx 1$

可以发现,对于一个数,最多进行 $6$ 次sqrt()操作,它就变成 $1$ 不变了

于是我们还是用线段树,每次修改直接暴力递归到叶子节点进行修改

诶?不是说不能用线段树吗?怎么又用了?

因为区间和是可以用线段树维护的呀!qwq 只不过这个开平方没法维护,直接“暴力”

根据势能分析,可知

时间复杂度( $n,m$ 同阶 )

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)#define ls(at) (at<<1)#define rs(at) (at<<1|1)int n,a[N];int mx[N<<2],sum[N<<2];void push_up(int at){    mx[at]=max(mx[ls(at)],mx[rs(at)]);    sum[at]=sum[ls(at)]+sum[rs(at)];}void build(int l,int r,int at){    if(l==r)    {        sum[at]=mx[at]=a[l];        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void update(int nl,int nr,int l,int r,int at){    if(l==r)    {        sum[at]=sqrt(sum[at]);        mx[at]=sqrt(mx[at]);        return;    }    int mid=(l+r)>>1;    if(nl<=mid&&mx[ls(at)]>1)        update(nl,nr,l,mid,ls(at));    if(nr>mid&&mx[rs(at)]>1)        update(nl,nr,mid+1,r,rs(at));    push_up(at);}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)return sum[at];    if(nl>r||nr<l)return 0;    int mid=(l+r)>>1;    return query(nl,nr,l,mid,ls(at))+query(nl,nr,mid+1,r,rs(at));}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    int Q;read(Q);    while(Q--)    {        int op,l,r;        read(op);read(l);read(r);        if(l>r)swap(l,r);        if(op==0)            update(l,r,1,n,1);        else write(query(l,r,1,n,1)),pc('\n');    }    return 0;}
]]>
+ 洛谷P4145上帝造题的七分钟 2 / 花神游历各国 题解

题目链接:P4145上帝造题的七分钟 2 / 花神游历各国

题意:维护一个数据结构

  1. 区间sqrt()(对每个数开平方+下取整)

  2. 询问区间和

\(\sqrt{a+b} \ne\sqrt{a}+\sqrt{b}\)

因此无法用线段树维护

那么怎么搞呢?注意到数据范围中,每个数都为正整数,且不超过 \(10^{12}\)

\(\sqrt{1}=1\)

\(\left(10^{12}\right)^{2^{-6}} \approx1\)

可以发现,对于一个数,最多进行 \(6\)sqrt()操作,它就变成 \(1\) 不变了

于是我们还是用线段树,每次修改直接暴力递归到叶子节点进行修改

诶?不是说不能用线段树吗?怎么又用了?

因为区间和是可以用线段树维护的呀!qwq只不过这个开平方没法维护,直接“暴力”

根据势能分析,可知

时间复杂度( \(n,m\) 同阶 ) \[O\left(n+n\times \dfrac{6n}n\log n\right) = O(n + 6n \log n) = O(n\logn)\] 代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)#define ls(at) (at<<1)#define rs(at) (at<<1|1)int n,a[N];int mx[N<<2],sum[N<<2];void push_up(int at){    mx[at]=max(mx[ls(at)],mx[rs(at)]);    sum[at]=sum[ls(at)]+sum[rs(at)];}void build(int l,int r,int at){    if(l==r)    {        sum[at]=mx[at]=a[l];        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void update(int nl,int nr,int l,int r,int at){    if(l==r)    {        sum[at]=sqrt(sum[at]);        mx[at]=sqrt(mx[at]);        return;    }    int mid=(l+r)>>1;    if(nl<=mid&&mx[ls(at)]>1)        update(nl,nr,l,mid,ls(at));    if(nr>mid&&mx[rs(at)]>1)        update(nl,nr,mid+1,r,rs(at));    push_up(at);}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)return sum[at];    if(nl>r||nr<l)return 0;    int mid=(l+r)>>1;    return query(nl,nr,l,mid,ls(at))+query(nl,nr,mid+1,r,rs(at));}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    int Q;read(Q);    while(Q--)    {        int op,l,r;        read(op);read(l);read(r);        if(l>r)swap(l,r);        if(op==0)            update(l,r,1,n,1);        else write(query(l,r,1,n,1)),pc('\n');    }    return 0;}
]]>
@@ -6570,7 +6570,7 @@ /2022/05/25/luo-gu-p4158-scoi2009-fen-shua-jiang-ti-jie/ - 洛谷P4158 [SCOI2009]粉刷匠 题解

题目链接:P4158 [SCOI2009]粉刷匠

题意:windy有 N 条木板需要被粉刷。 每条木板被分为 M 个格子。 每个格子要被刷成红色或蓝色。

windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。 每个格子最多只能被粉刷一次。

如果windy只能粉刷 T 次,他最多能正确粉刷多少格子?

一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。

比较简单的dp

设 $f[i][j]$ 为前 $i$ 块木板粉刷 $j$ 次的情况下能正确粉刷的最大格子数

设 $g[i][j][k]$ 为第 $i$ 条木板粉刷 $j$ 次且只涂了前 $k$ 个格子的情况下能正确粉刷的最大格子数

设 $S[i][j]$ 为第 $i$ 条木板前 $j$ 个格子的蓝色格子数

不难发现

时间复杂度 $O(n^4+n^2T)$ ( $n,m$ 同阶)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(55)#define T (int)(3e3+15)char s[N];int n,m,t;int f[N][T],sum[N][T],g[N][T][N];void Max(int &a,int b){    if(b>a)a=b;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> t;    for(int i=1; i<=n; i++)    {        cin >> s;        for(int j=1; j<=m; j++)            sum[i][j]=sum[i][j-1]+(s[j-1]=='1');    }    for(int i=1; i<=n; i++)    for(int j=1; j<=m; j++)    for(int k=1; k<=m; k++)    for(int l=j-1; l<k; l++)        Max(g[i][j][k],g[i][j-1][l]+max(sum[i][k]-sum[i][l],k-l-sum[i][k]+sum[i][l]));    for(int i=1; i<=n; i++)        for(int j=1; j<=t; j++)            for(int k=0; k<=min(j,m); k++)                Max(f[i][j],f[i-1][j-k]+g[i][k][m]);    cout << *max_element(f[n]+1,f[n]+1+t) << endl;    return 0;}
]]>
+ 洛谷P4158 [SCOI2009]粉刷匠题解

题目链接:P4158[SCOI2009]粉刷匠

题意:windy有 N 条木板需要被粉刷。 每条木板被分为 M个格子。 每个格子要被刷成红色或蓝色。

windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。每个格子最多只能被粉刷一次。

如果windy只能粉刷 T 次,他最多能正确粉刷多少格子?

一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。

比较简单的dp

\(f[i][j]\) 为前 \(i\) 块木板粉刷 \(j\) 次的情况下能正确粉刷的最大格子数

\(g[i][j][k]\) 为第 \(i\) 条木板粉刷 \(j\) 次且只涂了前 \(k\)个格子的情况下能正确粉刷的最大格子数

\(S[i][j]\) 为第 \(i\) 条木板前 \(j\) 个格子的蓝色格子数

不难发现 \[f[i][j]=\max(f[i][j],f[i-1][j-k]+g[i][k][m])\\g[i][j][k]=\max(g[i][j][k],g[i][j-1][l]+\max(S[i][k]-S[i][l],k-l-(S[i][k]-S[i][l])))\] 时间复杂度 \(O(n^4+n^2T)\)\(n,m\) 同阶)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(55)#define T (int)(3e3+15)char s[N];int n,m,t;int f[N][T],sum[N][T],g[N][T][N];void Max(int &a,int b){    if(b>a)a=b;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m >> t;    for(int i=1; i<=n; i++)    {        cin >> s;        for(int j=1; j<=m; j++)            sum[i][j]=sum[i][j-1]+(s[j-1]=='1');    }    for(int i=1; i<=n; i++)    for(int j=1; j<=m; j++)    for(int k=1; k<=m; k++)    for(int l=j-1; l<k; l++)        Max(g[i][j][k],g[i][j-1][l]+max(sum[i][k]-sum[i][l],k-l-sum[i][k]+sum[i][l]));    for(int i=1; i<=n; i++)        for(int j=1; j<=t; j++)            for(int k=0; k<=min(j,m); k++)                Max(f[i][j],f[i-1][j-k]+g[i][k][m]);    cout << *max_element(f[n]+1,f[n]+1+t) << endl;    return 0;}
]]>
@@ -6597,7 +6597,7 @@ /2022/05/25/luo-gu-p4310-jue-shi-hao-ti-ti-jie/ - 洛谷P4310 绝世好题 题解

题目链接:P4310 绝世好题

题意:给定一个长度为 $n$ 的数列 $a_i$ ,求 $a_i$ 的子序列 $b_i$ 的最长长度 $k$,满足 $b_i \& b_{i-1} \ne 0$,其中 $2\leq i\leq k$, $\&$ 表示位运算取与。

设 $dp[i][j]$ 为前 $i$ 个数的每个子序列的最后一个数第 $j$ 位为 $1$ 的最大长度

然后对于每个数枚举一遍dp,再转移一遍dp即可

注意到这个递推式可以滚动数组,然后搞一搞就好了

答案就是 $\max(dp[i][j])$

时间复杂度 $O(n \log \max(a_i))$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n,res,dp[35];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,x; i<=n; i++)    {        cin >> x;        int mx=1;        for(int j=0; j<=33; j++)            if((1<<j)&x)mx=max(mx,dp[j]+1);        for(int j=0; j<=33; j++)            if((1<<j)&x)dp[j]=max(dp[j],mx);        res=max(res,mx);    }    cout << res << endl;    return 0;}
]]>
+ 洛谷P4310 绝世好题 题解

题目链接:P4310绝世好题

题意:给定一个长度为 \(n\) 的数列 \(a_i\) ,求 \(a_i\) 的子序列 \(b_i\) 的最长长度 \(k\),满足 \(b_i\& b_{i-1} \ne 0\),其中 \(2\leqi\leq k\)\(\&\)表示位运算取与。

\(dp[i][j]\) 为前 \(i\) 个数的每个子序列的最后一个数第 \(j\) 位为 \(1\) 的最大长度

然后对于每个数枚举一遍dp,再转移一遍dp即可

注意到这个递推式可以滚动数组,然后搞一搞就好了

答案就是 \(\max(dp[i][j])\)

时间复杂度 \(O(n \log\max(a_i))\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)()int n,res,dp[35];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,x; i<=n; i++)    {        cin >> x;        int mx=1;        for(int j=0; j<=33; j++)            if((1<<j)&x)mx=max(mx,dp[j]+1);        for(int j=0; j<=33; j++)            if((1<<j)&x)dp[j]=max(dp[j],mx);        res=max(res,mx);    }    cout << res << endl;    return 0;}
]]>
@@ -6624,7 +6624,7 @@ /2022/05/25/luo-gu-p4342-ioi1998-polygon-ti-jie/ - 洛谷P4342 [IOI1998]Polygon 题解

题目链接:P4342 [IOI1998]Polygon

题意:多边形是一个玩家在一个有 $n$ 个顶点的多边形上的游戏,如图所示,其中 $n=4$ 。每个顶点用整数标记,每个边用符号+(加)或符号*(乘积)标记。

第一步,删除其中一条边。随后每一步:

选择一条边连接的两个顶点V1和V2,用边上的运算符计算V1和V2得到的结果来替换这两个顶点。

游戏结束时,只有一个顶点,没有多余的边。

如图所示,玩家先移除编号为3的边。之后,玩家选择计算编号为1的边,然后计算编号为4的边,最后,计算编号为2的边。结果是0。

这里每条边的运算符旁边的数字为边的编号,不拿来计算

编写一个程序,给定一个多边形,计算最高可能的分数。

$3 \le n\le 50$

对于任何一系列的操作,顶点数字都在 $[-32768,32767]$ 的范围内。

容易发现我们需要断环为链,然后区间dp

注意到存在负数,而负负得正,说明不是简单的区间dp

不难发现两个负的极小值之乘积对答案有更大贡献

考虑维护区间最大值的同时维护区间最小值

即,设 $f[i][j]$ 为区间 $[i,j]$ 的最大值,$g[i][j]$ 为区间 $[i,j]$ 的最小值

当符号为”加”时,显然有

当符号为”乘”时

因为我们并不知道最大值是否非负,直接把所有情况都列出即可

时间复杂度 $O(n^3)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f#define N (int)(120)int n,a[N],f[N][N],g[N][N];char op[N];int max5(int a,int b,int c,int d,int e){return max(a,max(b,max(c,max(d,e))));}int min5(int a,int b,int c,int d,int e){return min(a,min(b,min(c,min(d,e))));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> op[i] >> a[i];        op[i+n]=op[i];a[i+n]=a[i];    }    for(int i=1; i<=2*n; i++)        for(int j=1; j<=2*n; j++)            f[i][j]=-INF,g[i][j]=INF;    for(int i=1; i<=2*n; i++)        f[i][i]=g[i][i]=a[i];    for(int len=1; len<=n; ++len)        for(int i=1; i+len-1<=2*n; i++)        {            int j=i+len-1;            for(int k=i; k<j; k++)            {                if(op[k+1]=='x')                {                    f[i][j]=max5(f[i][j],f[i][k]*f[k+1][j],g[i][k]*g[k+1][j],f[i][k]*g[k+1][j],g[i][k]*f[k+1][j]);                    g[i][j]=min5(g[i][j],f[i][k]*f[k+1][j],g[i][k]*g[k+1][j],f[i][k]*g[k+1][j],g[i][k]*f[k+1][j]);                }else                {                    f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);                    g[i][j]=min(g[i][j],g[i][k]+g[k+1][j]);                }            }        }    int ans=-INF,ok=0;    for(int i=1; i<=n; i++)        ans=max(ans,f[i][i+n-1]);    cout << ans << endl;    for(int i=1; i<=n; i++)        if(f[i][i+n-1]==ans)        {            if(!ok)ok=1,cout << i;            else cout << " " << i;        }    cout << endl;    return 0;}
]]>
+ 洛谷P4342 [IOI1998]Polygon题解

题目链接:P4342[IOI1998]Polygon

题意:多边形是一个玩家在一个有 \(n\) 个顶点的多边形上的游戏,如图所示,其中\(n=4\)。每个顶点用整数标记,每个边用符号+(加)或符号*(乘积)标记。

第一步,删除其中一条边。随后每一步:

选择一条边连接的两个顶点V1和V2,用边上的运算符计算V1和V2得到的结果来替换这两个顶点。

游戏结束时,只有一个顶点,没有多余的边。

如图所示,玩家先移除编号为3的边。之后,玩家选择计算编号为1的边,然后计算编号为4的边,最后,计算编号为2的边。结果是0。

这里每条边的运算符旁边的数字为边的编号,不拿来计算

编写一个程序,给定一个多边形,计算最高可能的分数。

\(3 \le n\le 50\)

对于任何一系列的操作,顶点数字都在 \([-32768,32767]\) 的范围内。

容易发现我们需要断环为链,然后区间dp

注意到存在负数,而负负得正,说明不是简单的区间dp

不难发现两个负的极小值之乘积对答案有更大贡献

考虑维护区间最大值的同时维护区间最小值

即,设 \(f[i][j]\) 为区间 \([i,j]\) 的最大值,\(g[i][j]\) 为区间 \([i,j]\) 的最小值

当符号为"加"时,显然有 \[\begin{aligned}f[i][j]=&\max_{i\le k < j}(f[i][j],f[i][k]+f[k+1][j])\\g[i][j]=&\min_{i\le k < j}(g[i][j],g[i][k]+g[k+1][j])\end{aligned}\]

当符号为"乘"时

因为我们并不知道最大值是否非负,直接把所有情况都列出即可 \[\begin{aligned}f[i][j]=&\max_{i\le k < j}(f[i][j],f[i][k]\timesf[k+1][j],g[i][k]\times g[k+1][j],f[i][k]\times g[k+1][j],g[i][k]\timesf[k+1][j])\\g[i][j]=&\min_{i\le k < j}(g[i][j],f[i][k]\timesf[k+1][j],g[i][k]\times g[k+1][j],f[i][k]\times g[k+1][j],g[i][k]\timesf[k+1][j])\end{aligned}\] 时间复杂度 \(O(n^3)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f#define N (int)(120)int n,a[N],f[N][N],g[N][N];char op[N];int max5(int a,int b,int c,int d,int e){return max(a,max(b,max(c,max(d,e))));}int min5(int a,int b,int c,int d,int e){return min(a,min(b,min(c,min(d,e))));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> op[i] >> a[i];        op[i+n]=op[i];a[i+n]=a[i];    }    for(int i=1; i<=2*n; i++)        for(int j=1; j<=2*n; j++)            f[i][j]=-INF,g[i][j]=INF;    for(int i=1; i<=2*n; i++)        f[i][i]=g[i][i]=a[i];    for(int len=1; len<=n; ++len)        for(int i=1; i+len-1<=2*n; i++)        {            int j=i+len-1;            for(int k=i; k<j; k++)            {                if(op[k+1]=='x')                {                    f[i][j]=max5(f[i][j],f[i][k]*f[k+1][j],g[i][k]*g[k+1][j],f[i][k]*g[k+1][j],g[i][k]*f[k+1][j]);                    g[i][j]=min5(g[i][j],f[i][k]*f[k+1][j],g[i][k]*g[k+1][j],f[i][k]*g[k+1][j],g[i][k]*f[k+1][j]);                }else                {                    f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);                    g[i][j]=min(g[i][j],g[i][k]+g[k+1][j]);                }            }        }    int ans=-INF,ok=0;    for(int i=1; i<=n; i++)        ans=max(ans,f[i][i+n-1]);    cout << ans << endl;    for(int i=1; i<=n; i++)        if(f[i][i+n-1]==ans)        {            if(!ok)ok=1,cout << i;            else cout << " " << i;        }    cout << endl;    return 0;}
]]>
@@ -6647,11 +6647,11 @@ - 洛谷P4395 [BOI2003]Gem 气垫车 题解 - - /2022/05/25/luo-gu-p4395-boi2003-gem-qi-dian-che-ti-jie/ + 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 + + /2022/05/25/luo-gu-p4390-boi2007-mokia-mo-ji-ya-ti-jie/ - 洛谷P4395 [BOI2003]Gem 气垫车 题解

题目链接:P4395 [BOI2003]Gem 气垫车

题意:给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数

唯一的限制条件是相临的两个结点不能标上相同的权值,要求一种方案,使得整棵树的总价值最小。

$N \le 10000$

考虑树形dp

设 $dp[u][i]$ 表示结点 $u$ 的权值为 $i$ 时其所在子树的最小总权值

则有

那么这个权值最大有多少呢

不难发现最多为 $\left\lceil\log n\right\rceil + 1$

那么就很简单了,时间复杂度 $O(n\log n)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f#define N (int)(2e4+15)struct Edge{    int u,v,next;}e[N];int n,pos=1,dp[N][17],head[N];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int f){    for(int i=1; i<=15; i++)        dp[u][i]=i;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u);        for(int j=1; j<=15; j++)        {            int mn=INF;            for(int k=1; k<=15; k++)                if(j!=k)mn=min(mn,dp[v][k]);            dp[u][j]+=mn;        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u,v);        addEdge(v,u);    }    dfs(1,0);    int res=INF;    for(int i=1; i<=15; i++)        res=min(res,dp[1][i]);    cout << res << endl;    return 0;}
]]>
+ 洛谷P4390 [BOI2007]Mokia摩基亚 题解

题目链接:P4390[BOI2007]Mokia 摩基亚

题意:摩尔瓦多的移动电话公司摩基亚(Mokia)设计出了一种新的用户定位系统。和其他的定位系统一样,它能够迅速回答任何形如“用户C的位置在哪?”的问题,精确到毫米。但其真正高科技之处在于,它能够回答形如“给定区域内有多少名用户?”的问题。

在定位系统中,世界被认为是一个 \(w×w\) 的正方形区域,由 \(1\times 1\)的方格组成。每个方格都有一个坐标 \((x,y)\),\(1\leqx,y\leq w\)。坐标的编号从 \(1\)开始。对于一个 \(4\times 4\)的正方形,就有 \(1\leq x\leq 4\)\(1\leq y\leq 4\)(如图):

请帮助 Mokia 公司编写一个程序来计算在某个矩形区域内有多少名用户。

这道题的题解区一片互相抄袭的烂货,实在受不了了就来水一篇题解

可以发现这道题目就是个二维数点问题,不知道的推荐看看这篇博客

然后这道题目,我们不能用归并排序来做

为什么呢?因为这个题目它还有一个维度:时间顺序

因此我们可以使用CDQ分治(第一维是时间,第二维是 \(x\) ,第三维是 \(y\) )

这里我们甚至不用按时间排序,它本来就是顺序的

注意到 \(w\le 2\times 10^6\)有一丢丢大,所以可以不必要地离散化一下 \(y\)

因此时间复杂度 \(O(n\log^2 n)\)

然后就跑到了最优解第一页,嘿嘿树状数组跑的飞快🤤(逃~

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)#define lb lower_boundchar buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){pc('-');k=-k;}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e6+15)struct node{    int x,y,typ,add,id,ans;}t[N],b[N];int w,n,Q,pos,tcnt;int tree[N],ans[N],tmp[N];void addQ(int a,int b,int c,int d=0,int e=0){    t[++pos]={a,b,c,d,e};}#define lowbit(x) (x&(-x))void add(int x,int v){    while(x&&x<=n)    {        tree[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=tree[x];        x-=lowbit(x);    }    return res;}void init(){    for(int i=1; i<=pos; i++)        tmp[i]=t[i].y;    sort(tmp+1,tmp+1+pos);    n=unique(tmp+1,tmp+1+pos)-tmp-1;    for(int i=1; i<=pos; i++)        t[i].y=lb(tmp+1,tmp+1+n,t[i].y)-tmp;}void cdq(int l,int r){    if(l==r)return;    int mid=(l+r)>>1;    cdq(l,mid);cdq(mid+1,r);    int i=mid+1,j=l,cnt=0;    for(;i<=r; i++)    {        while(t[j].x<=t[i].x&&j<=mid)        {            if(t[j].typ==1)add(t[j].y,t[j].add);            b[++cnt]=t[j++];        }        if(t[i].typ==2)t[i].ans+=sum(t[i].y);        b[++cnt]=t[i];    }    for(int i=l; i<j; i++)        if(t[i].typ==1)add(t[i].y,-t[i].add);    while(j<=mid)        b[++cnt]=t[j++];    for(int i=1; i<=cnt; i++)        t[l+i-1]=b[i];}signed main(){    read(w);read(w);    int op;    while(read(op),op!=3)    {        int a,b,c,d;        if(op==1)        {            read(a);read(b);read(c);            addQ(a,b,1,c);        }else if(op==2)        {            read(a);read(b);read(c);read(d);++Q;            addQ(c,d,2,1,Q);            addQ(a-1,d,2,-1,Q);            addQ(c,b-1,2,-1,Q);            addQ(a-1,b-1,2,1,Q);        }    }    init();    cdq(1,pos);    for(int i=1; i<=pos; i++)    {        if(t[i].typ==2)            ans[t[i].id]+=t[i].add*t[i].ans;    }    for(int i=1; i<=Q; i++)        write(ans[i]),pc('\n');    return 0;}

这里还有非离散化版本的

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)#define lb lower_boundchar buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){pc('-');k=-k;}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e6+15)struct node{    int x,y,typ,add,id,ans;}t[N],b[N];int w,n,Q,pos,tcnt;int tree[N],ans[N],tmp[N];void addQ(int a,int b,int c,int d=0,int e=0){    t[++pos]={a,b,c,d,e};}#define lowbit(x) (x&(-x))void add(int x,int v){    while(x&&x<=w)    {        tree[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=tree[x];        x-=lowbit(x);    }    return res;}void cdq(int l,int r){    if(l==r)return;    int mid=(l+r)>>1;    cdq(l,mid);cdq(mid+1,r);    int i=mid+1,j=l,cnt=0;    for(;i<=r; i++)    {        while(t[j].x<=t[i].x&&j<=mid)        {            if(t[j].typ==1)add(t[j].y,t[j].add);            b[++cnt]=t[j++];        }        if(t[i].typ==2)t[i].ans+=sum(t[i].y);        b[++cnt]=t[i];    }    for(int i=l; i<j; i++)        if(t[i].typ==1)add(t[i].y,-t[i].add);    while(j<=mid)        b[++cnt]=t[j++];    for(int i=1; i<=cnt; i++)        t[l+i-1]=b[i];}signed main(){    read(w);read(w);    int op;    while(read(op),op!=3)    {        int a,b,c,d;        if(op==1)        {            read(a);read(b);read(c);            addQ(a,b,1,c);        }else if(op==2)        {            read(a);read(b);read(c);read(d);++Q;            addQ(c,d,2,1,Q);            addQ(a-1,d,2,-1,Q);            addQ(c,b-1,2,-1,Q);            addQ(a-1,b-1,2,1,Q);        }    }    cdq(1,pos);    for(int i=1; i<=pos; i++)    {        if(t[i].typ==2)            ans[t[i].id]+=t[i].add*t[i].ans;    }    for(int i=1; i<=Q; i++)        write(ans[i]),pc('\n');    return 0;}
]]>
@@ -6665,9 +6665,7 @@ 算法 - DP - - 图论 + 数据结构 @@ -6676,11 +6674,11 @@ - 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 - - /2022/05/25/luo-gu-p4390-boi2007-mokia-mo-ji-ya-ti-jie/ + 洛谷P4395 [BOI2003]Gem 气垫车 题解 + + /2022/05/25/luo-gu-p4395-boi2003-gem-qi-dian-che-ti-jie/ - 洛谷P4390 [BOI2007]Mokia 摩基亚 题解

题目链接:P4390 [BOI2007]Mokia 摩基亚

题意:摩尔瓦多的移动电话公司摩基亚(Mokia)设计出了一种新的用户定位系统。和其他的定位系统一样,它能够迅速回答任何形如“用户 C 的位置在哪?”的问题,精确到毫米。但其真正高科技之处在于,它能够回答形如“给定区域内有多少名用户?”的问题。

在定位系统中,世界被认为是一个 $w×w$ 的正方形区域,由 $1\times 1$ 的方格组成。每个方格都有一个坐标 $(x,y)$,$1\leq x,y\leq w$。坐标的编号从 $1$ 开始。对于一个 $4\times 4$ 的正方形,就有 $1\leq x\leq 4$,$1\leq y\leq 4$(如图):

img

请帮助 Mokia 公司编写一个程序来计算在某个矩形区域内有多少名用户。

这道题的题解区一片互相抄袭的烂货,实在受不了了就来水一篇题解

可以发现这道题目就是个二维数点问题,不知道的推荐看看 这篇博客

然后这道题目,我们不能用归并排序来做

为什么呢?因为这个题目它还有一个维度:时间顺序

因此我们可以使用CDQ分治(第一维是时间,第二维是 $x$ ,第三维是 $y$ )

这里我们甚至不用按时间排序,它本来就是顺序的

注意到 $w\le 2\times 10^6$ ,有一丢丢大,所以可以不必要地离散化一下 $y$

因此时间复杂度 $O(n\log^2 n)$

然后就跑到了最优解第一页,嘿嘿树状数组跑的飞快🤤(逃~

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)#define lb lower_boundchar buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){pc('-');k=-k;}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e6+15)struct node{    int x,y,typ,add,id,ans;}t[N],b[N];int w,n,Q,pos,tcnt;int tree[N],ans[N],tmp[N];void addQ(int a,int b,int c,int d=0,int e=0){    t[++pos]={a,b,c,d,e};}#define lowbit(x) (x&(-x))void add(int x,int v){    while(x&&x<=n)    {        tree[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=tree[x];        x-=lowbit(x);    }    return res;}void init(){    for(int i=1; i<=pos; i++)        tmp[i]=t[i].y;    sort(tmp+1,tmp+1+pos);    n=unique(tmp+1,tmp+1+pos)-tmp-1;    for(int i=1; i<=pos; i++)        t[i].y=lb(tmp+1,tmp+1+n,t[i].y)-tmp;}void cdq(int l,int r){    if(l==r)return;    int mid=(l+r)>>1;    cdq(l,mid);cdq(mid+1,r);    int i=mid+1,j=l,cnt=0;    for(;i<=r; i++)    {        while(t[j].x<=t[i].x&&j<=mid)        {            if(t[j].typ==1)add(t[j].y,t[j].add);            b[++cnt]=t[j++];        }        if(t[i].typ==2)t[i].ans+=sum(t[i].y);        b[++cnt]=t[i];    }    for(int i=l; i<j; i++)        if(t[i].typ==1)add(t[i].y,-t[i].add);    while(j<=mid)        b[++cnt]=t[j++];    for(int i=1; i<=cnt; i++)        t[l+i-1]=b[i];}signed main(){    read(w);read(w);    int op;    while(read(op),op!=3)    {        int a,b,c,d;        if(op==1)        {            read(a);read(b);read(c);            addQ(a,b,1,c);        }else if(op==2)        {            read(a);read(b);read(c);read(d);++Q;            addQ(c,d,2,1,Q);            addQ(a-1,d,2,-1,Q);            addQ(c,b-1,2,-1,Q);            addQ(a-1,b-1,2,1,Q);        }    }    init();    cdq(1,pos);    for(int i=1; i<=pos; i++)    {        if(t[i].typ==2)            ans[t[i].id]+=t[i].add*t[i].ans;    }    for(int i=1; i<=Q; i++)        write(ans[i]),pc('\n');    return 0;}

这里还有非离散化版本的

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)#define lb lower_boundchar buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){pc('-');k=-k;}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e6+15)struct node{    int x,y,typ,add,id,ans;}t[N],b[N];int w,n,Q,pos,tcnt;int tree[N],ans[N],tmp[N];void addQ(int a,int b,int c,int d=0,int e=0){    t[++pos]={a,b,c,d,e};}#define lowbit(x) (x&(-x))void add(int x,int v){    while(x&&x<=w)    {        tree[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=tree[x];        x-=lowbit(x);    }    return res;}void cdq(int l,int r){    if(l==r)return;    int mid=(l+r)>>1;    cdq(l,mid);cdq(mid+1,r);    int i=mid+1,j=l,cnt=0;    for(;i<=r; i++)    {        while(t[j].x<=t[i].x&&j<=mid)        {            if(t[j].typ==1)add(t[j].y,t[j].add);            b[++cnt]=t[j++];        }        if(t[i].typ==2)t[i].ans+=sum(t[i].y);        b[++cnt]=t[i];    }    for(int i=l; i<j; i++)        if(t[i].typ==1)add(t[i].y,-t[i].add);    while(j<=mid)        b[++cnt]=t[j++];    for(int i=1; i<=cnt; i++)        t[l+i-1]=b[i];}signed main(){    read(w);read(w);    int op;    while(read(op),op!=3)    {        int a,b,c,d;        if(op==1)        {            read(a);read(b);read(c);            addQ(a,b,1,c);        }else if(op==2)        {            read(a);read(b);read(c);read(d);++Q;            addQ(c,d,2,1,Q);            addQ(a-1,d,2,-1,Q);            addQ(c,b-1,2,-1,Q);            addQ(a-1,b-1,2,1,Q);        }    }    cdq(1,pos);    for(int i=1; i<=pos; i++)    {        if(t[i].typ==2)            ans[t[i].id]+=t[i].add*t[i].ans;    }    for(int i=1; i<=Q; i++)        write(ans[i]),pc('\n');    return 0;}
]]>
+ 洛谷P4395 [BOI2003]Gem 气垫车题解

题目链接:P4395[BOI2003]Gem 气垫车

题意:给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数

唯一的限制条件是相临的两个结点不能标上相同的权值,要求一种方案,使得整棵树的总价值最小。

\(N \le 10000\)

考虑树形dp

\(dp[u][i]\) 表示结点 \(u\) 的权值为 \(i\) 时其所在子树的最小总权值

则有 \[dp[u][i]=\min_{k \ne i}(dp[v][k])+i\] 那么这个权值最大有多少呢

不难发现最多为 \(\left\lceil\logn\right\rceil + 1\)

那么就很简单了,时间复杂度 \(O(n\logn)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f#define N (int)(2e4+15)struct Edge{    int u,v,next;}e[N];int n,pos=1,dp[N][17],head[N];void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int f){    for(int i=1; i<=15; i++)        dp[u][i]=i;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u);        for(int j=1; j<=15; j++)        {            int mn=INF;            for(int k=1; k<=15; k++)                if(j!=k)mn=min(mn,dp[v][k]);            dp[u][j]+=mn;        }    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n;    for(int i=1,u,v; i<n; i++)    {        cin >> u >> v;        addEdge(u,v);        addEdge(v,u);    }    dfs(1,0);    int res=INF;    for(int i=1; i<=15; i++)        res=min(res,dp[1][i]);    cout << res << endl;    return 0;}
]]>
@@ -6694,7 +6692,9 @@ 算法 - 数据结构 + 图论 + + DP @@ -6707,7 +6707,7 @@ /2022/05/25/luo-gu-p4556-vani-you-yue-hui-yu-tian-de-wei-ba-mo-ban-xian-duan-shu-he-bing-ti-jie/ - 洛谷P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 题解

题目链接:P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并

题意:村落里的一共有 $n$ 座房屋,并形成一个树状结构。然后救济粮分 $m$ 次发放,每次选择两个房屋 $(x,y)$,然后对于 $x$ 到 $y$ 的路径上(含 $x$ 和 $y$)每座房子里发放一袋 $z$ 类型的救济粮。

然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮

显然这是板子题

我们利用树上差分的思想,自底向上统计答案

同时一路合并每个节点的线段树,即可

本文主要讲一讲为什么数组要开 1e5*70 左右

首先 $z \le 10^5$ ,取最大值 $Z=10^5$ (权值线段树嘛)

其次,动态开点线段树一次从根节点到叶子节点的修改操作

modify() 新建的点数为 $O(\log Z)$ 的,在本题中 $\log Z \approx \log_2 10^5 \approx 17$

而观察代码可以发现,我们做树上差分的时候,一次差分会修改至多 $4$ 个节点

因此开的空间大小就是 $Z\log Z\times 4$ ,稍微取个整就是 1e5*70

贴个代码,方便各位食用

#include <bits/stdc++.h>using namespace std;// #define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)int n,m,ans[N];struct Edge{    int u,v,next;}e[N<<1];int pos=1,head[N];int fa[N],son[N],sz[N],top[N],dep[N];int val[N*70],ch[N*70][2],rt[N],tot,Z,typ[N*70];#define ls(at) ch[at][0]#define rs(at) ch[at][1]void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int f,int d){    fa[u]=f;    sz[u]=1;    dep[u]=d;    int mx=-1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u,d+1);        sz[u]+=sz[v];        if(sz[v]>mx)mx=sz[v],son[u]=v;    }}void dfs(int u,int ftop){    top[u]=ftop;    if(!son[u])return;    dfs(son[u],ftop);    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==fa[u]||v==son[u])continue;        dfs(v,v);    }}int lca(int x,int y){    while(top[x]!=top[y])    {        if(dep[top[x]]<dep[top[y]])swap(x,y);        x=fa[top[x]];    }    return dep[x]<dep[y]?x:y;}void push_up(int at){    if(val[ls(at)]>=val[rs(at)])        val[at]=val[ls(at)],typ[at]=typ[ls(at)];    else val[at]=val[rs(at)],typ[at]=typ[rs(at)];}void modify(int l,int r,int pos,int v,int &at){    if(!at)at=++tot;    if(l==r)    {        val[at]+=v;        typ[at]=l;        return;    }    int mid=(l+r)>>1;    if(pos<=mid)modify(l,mid,pos,v,ls(at));    else modify(mid+1,r,pos,v,rs(at));    push_up(at);}int merge(int x,int y,int l,int r){    if(!x||!y)return x|y;    if(l==r)    {        val[x]+=val[y];        typ[x]=l;        return x;    }    int mid=(l+r)>>1;    ls(x)=merge(ls(x),ls(y),l,mid);    rs(x)=merge(rs(x),rs(y),mid+1,r);    push_up(x);    return x;}void calc(int u){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==fa[u])continue;        calc(v);        rt[u]=merge(rt[u],rt[v],1,Z);    }    if(val[rt[u]])ans[u]=typ[rt[u]];}int u[N],v[N],z[N];signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    read(n);read(m);    for(int i=1,u,v; i<n; i++)    {        read(u);read(v);        addEdge(u,v);addEdge(v,u);    }    dfs(1,0,1);dfs(1,1);    for(int i=1; i<=m; i++)        read(u[i]),read(v[i]),read(z[i]),Z=max(Z,z[i]);    for(int i=1; i<=m; i++)    {        int d=lca(u[i],v[i]);        modify(1,Z,z[i],1,rt[u[i]]);        modify(1,Z,z[i],1,rt[v[i]]);        modify(1,Z,z[i],-1,rt[d]);        modify(1,Z,z[i],-1,rt[fa[d]]);    }    calc(1);    for(int i=1; i<=n; i++)        write(ans[i]),pc('\n');    return 0;}
]]>
+ 洛谷P4556[Vani有约会]雨天的尾巴 /【模板】线段树合并 题解

题目链接:P4556[Vani有约会]雨天的尾巴 /【模板】线段树合并

题意:村落里的一共有 \(n\)座房屋,并形成一个树状结构。然后救济粮分 \(m\) 次发放,每次选择两个房屋 \((x,y)\),然后对于 \(x\) 到 \(y\) 的路径上(含 \(x\) 和 \(y\))每座房子里发放一袋 \(z\) 类型的救济粮。

然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮

显然这是板子题

我们利用树上差分的思想,自底向上统计答案

同时一路合并每个节点的线段树,即可

本文主要讲一讲为什么数组要开 1e5*70 左右

首先 \(z \le 10^5\) ,取最大值 \(Z=10^5\) (权值线段树嘛)

其次,动态开点线段树一次从根节点到叶子节点的修改操作

modify() 新建的点数为 \(O(\log Z)\) 的,在本题中 \(\log Z \approx \log_2 10^5 \approx 17\)

而观察代码可以发现,我们做树上差分的时候,一次差分会修改至多 \(4\) 个节点

因此开的空间大小就是 \(Z\log Z\times4\) ,稍微取个整就是 1e5*70

贴个代码,方便各位食用

#include <bits/stdc++.h>using namespace std;// #define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(1e5+15)int n,m,ans[N];struct Edge{    int u,v,next;}e[N<<1];int pos=1,head[N];int fa[N],son[N],sz[N],top[N],dep[N];int val[N*70],ch[N*70][2],rt[N],tot,Z,typ[N*70];#define ls(at) ch[at][0]#define rs(at) ch[at][1]void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u,int f,int d){    fa[u]=f;    sz[u]=1;    dep[u]=d;    int mx=-1;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f)continue;        dfs(v,u,d+1);        sz[u]+=sz[v];        if(sz[v]>mx)mx=sz[v],son[u]=v;    }}void dfs(int u,int ftop){    top[u]=ftop;    if(!son[u])return;    dfs(son[u],ftop);    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==fa[u]||v==son[u])continue;        dfs(v,v);    }}int lca(int x,int y){    while(top[x]!=top[y])    {        if(dep[top[x]]<dep[top[y]])swap(x,y);        x=fa[top[x]];    }    return dep[x]<dep[y]?x:y;}void push_up(int at){    if(val[ls(at)]>=val[rs(at)])        val[at]=val[ls(at)],typ[at]=typ[ls(at)];    else val[at]=val[rs(at)],typ[at]=typ[rs(at)];}void modify(int l,int r,int pos,int v,int &at){    if(!at)at=++tot;    if(l==r)    {        val[at]+=v;        typ[at]=l;        return;    }    int mid=(l+r)>>1;    if(pos<=mid)modify(l,mid,pos,v,ls(at));    else modify(mid+1,r,pos,v,rs(at));    push_up(at);}int merge(int x,int y,int l,int r){    if(!x||!y)return x|y;    if(l==r)    {        val[x]+=val[y];        typ[x]=l;        return x;    }    int mid=(l+r)>>1;    ls(x)=merge(ls(x),ls(y),l,mid);    rs(x)=merge(rs(x),rs(y),mid+1,r);    push_up(x);    return x;}void calc(int u){    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==fa[u])continue;        calc(v);        rt[u]=merge(rt[u],rt[v],1,Z);    }    if(val[rt[u]])ans[u]=typ[rt[u]];}int u[N],v[N],z[N];signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    read(n);read(m);    for(int i=1,u,v; i<n; i++)    {        read(u);read(v);        addEdge(u,v);addEdge(v,u);    }    dfs(1,0,1);dfs(1,1);    for(int i=1; i<=m; i++)        read(u[i]),read(v[i]),read(z[i]),Z=max(Z,z[i]);    for(int i=1; i<=m; i++)    {        int d=lca(u[i],v[i]);        modify(1,Z,z[i],1,rt[u[i]]);        modify(1,Z,z[i],1,rt[v[i]]);        modify(1,Z,z[i],-1,rt[d]);        modify(1,Z,z[i],-1,rt[fa[d]]);    }    calc(1);    for(int i=1; i<=n; i++)        write(ans[i]),pc('\n');    return 0;}
]]>
@@ -6732,7 +6732,7 @@ /2022/05/25/luo-gu-p5110-kuai-su-di-tui-ti-jie/ - 洛谷P5110 块速递推 题解

题目链接:P5110 块速递推

题意:给定一个数列 $a$ 满足递推式

求 $a_n \bmod (10^9+7)$

多组询问

这个题是有个循环节的,正好是 $10^9+6$ ,据出题人说是凑好的

关于循环节怎么求的我也不太清楚,先留个坑,研究好了就补上来

upd.20220721 现在知道怎么求循环节了,详见本文最后

也就是,如果数列 $a_n$ 在模 $M$ 意义下存在循环节 $p$ ,则有 $a_n \equiv a_{n\,\bmod\, p} \bmod M$

本题的解法就是手推通项公式

具体方法如下

对于二阶线性递推数列

考虑使用特征方程求解

$a_n=pa_{n-1}+qa_{n-2}$ 的特征方程为

可以求出两个特解(不一定是实数) $x_1,x_2$ ,则

注意,如果数列从 $a_1$ 开始,这里就是 $a_n=\alpha x_1^{n-1} + \beta x_2^{n-1}$

然后将 $a_0=A,a_1=B$ 代入可得

解出 $\alpha,\beta$ 即可

本题的通项公式为

注意到这里有个 $\sqrt{56953}$ ,而我们要求它模意义下的值

也就是求出所有的 $x$

考虑二次剩余求解

什么?不会二次剩余? 那么就打个暴力好了,最多几秒钟就跑出来了

#include <bits/stdc++.h>using namespace std;#define int long longsigned main(){    for(int i=1; i<=1000000000; i++)        if(i*i%1000000007==56953)cout << i << endl;    return 0;}

然后有两个解 $188305837,811694170$ 取个小点的代入就好了

则有

注意到询问有 $10^7$ 个,快速幂过不了

考虑光速幂,$O(\sqrt{n})$ 预处理 $f(x)=x^{65536t},g(x)=x^t$

询问直接查询 $f(x/65536)\times g(n\%65536)$ 即可

这里可以用位运算加速

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define uint unsigned long long#define INF 0x3f3f3f3f3f3f3f3fnamespace Mker{unsigned long long SA,SB,SC;void init(){scanf("%llu%llu%llu",&SA,&SB,&SC);}unsigned long long rand(){    SA^=SA<<32,SA^=SA>>13,SA^=SA<<1;    unsigned long long t=SA;SA=SB,SB=SC,SC^=t^SA;return SC;}}#define N (int)(7e4+15)const int p=(int)(1e9+7);int Q;uint ans;uint pw1[2][N],pw2[2][N];const int a=94153035;const int b=905847205;void init(){    pw1[0][0]=pw2[0][0]=1;    for(int i=1; i<1<<16; i++)        pw1[0][i]=pw1[0][i-1]*a%p;    pw2[0][1]=pw1[0][(1<<16)-1]*a%p;    for(int i=2; i<1<<16; i++)        pw2[0][i]=pw2[0][i-1]*pw2[0][1]%p;    pw1[1][0]=pw2[1][0]=1;    for(int i=1; i<1<<16; i++)        pw1[1][i]=pw1[1][i-1]*b%p;    pw2[1][1]=pw1[1][(1<<16)-1]*b%p;    for(int i=2; i<=1<<16; i++)        pw2[1][i]=pw2[1][i-1]*pw2[1][1]%p;}int pow1(int n){    return pw1[0][n&65535]%p*pw2[0][n>>16]%p;}int pow2(int n){    return pw1[1][n&65535]%p*pw2[1][n>>16]%p;}uint solve(int n){    return 233230706*(pow1(n)-pow2(n)+p)%p;}signed main(){    init();    scanf("%lld",&Q);    Mker::init();    while(Q--)        ans^=solve(Mker::rand()%(p-1));    printf("%llu\n",ans);    return 0;}//233230706×(94153035^n−905847205^n) 

如何求解循环节?

如果数列 $a_n$ 在模 $M$ 意义下存在循环节 $p$ ,则有 $a_n \equiv a_{n\,\bmod\, p} \bmod M$

问了 @zx2017 老师 CCOrz

这个东西其实求起来非常简单,

在已知 $a_n$ 递推式和 $M$ 的情况下

直接暴力去枚举就好了

具体的,这道题

注意到 $a_n$ 是从 $a_{n-1}$ 和 $a_{n-2}$ 推来的

那我们枚举 $n$ ,直到出现了这样的情况

那么 $i-2$ 就是循环节

是不是很神奇?我太菜了根本没想到 QAQ

当然可能会有更优秀的做法(比起暴力枚举)

不过起码知道怎么简单求就很好了 qwq

]]>
+ 洛谷P5110 块速递推 题解

题目链接:P5110块速递推

题意:给定一个数列 \(a\) 满足递推式 \[a_0=0,a_1=1\\a_n = 233a_{n-1}+666a_{n-2}\] 求 \(a_n \bmod (10^9+7)\)

多组询问

这个题是有个循环节的,正好是 \(10^9+6\) ,据出题人说是凑好的

关于循环节怎么求的我也不太清楚,先留个坑,研究好了就补上来

upd.20220721现在知道怎么求循环节了,详见本文最后

也就是,如果数列 \(a_n\) 在模 \(M\) 意义下存在循环节 \(p\) ,则有 \(a_n\equiv a_{n\,\bmod\, p} \bmod M\)

本题的解法就是手推通项公式

具体方法如下

对于二阶线性递推数列 \[a_0=A,a_1=B\\a_n = pa_{n-1}+qa_{n-2} ,n\ge 2\] 考虑使用特征方程求解

\(a_n=pa_{n-1}+qa_{n-2}\)的特征方程为 \[x^2=px+q\] 可以求出两个特解(不一定是实数) \(x_1,x_2\) ,则 \[a_n=\alpha x_1^{n} + \beta x_2^{n}\]

注意,如果数列从 \(a_1\) 开始,这里就是 \(a_n=\alpha x_1^{n-1} + \betax_2^{n-1}\)

然后将 \(a_0=A,a_1=B\) 代入可得\[\begin{cases}\alpha + \beta = A\\\alpha x_1+\beta x_2=B\end{cases}\] 解出 \(\alpha,\beta\)即可

本题的通项公式为 \[a_n=\dfrac{1}{\sqrt{56953}}\left(\left(\dfrac{233+\sqrt{56953}}{2}\right)^n-\left(\dfrac{233-\sqrt{56953}}{2}\right)^n\right)\] 注意到这里有个 \(\sqrt{56953}\),而我们要求它模意义下的值

也就是求出所有的 \(x\) \[x^2\equiv 56953 \bmod(10^9+7)\] 考虑二次剩余求解

什么?不会二次剩余?那么就打个暴力好了,最多几秒钟就跑出来了

#include <bits/stdc++.h>using namespace std;#define int long longsigned main(){    for(int i=1; i<=1000000000; i++)        if(i*i%1000000007==56953)cout << i << endl;    return 0;}

然后有两个解 \(188305837,811694170\)取个小点的代入就好了

则有 \[a_n \equiv 233230706 \times(94153035^n-905847205^n)\] 注意到询问有 \(10^7\)个,快速幂过不了

考虑光速幂,\(O(\sqrt{n})\) 预处理\(f(x)=x^{65536t},g(x)=x^t\)

询问直接查询 \(f(x/65536)\timesg(n\%65536)\) 即可

这里可以用位运算加速

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define uint unsigned long long#define INF 0x3f3f3f3f3f3f3f3fnamespace Mker{unsigned long long SA,SB,SC;void init(){scanf("%llu%llu%llu",&SA,&SB,&SC);}unsigned long long rand(){    SA^=SA<<32,SA^=SA>>13,SA^=SA<<1;    unsigned long long t=SA;SA=SB,SB=SC,SC^=t^SA;return SC;}}#define N (int)(7e4+15)const int p=(int)(1e9+7);int Q;uint ans;uint pw1[2][N],pw2[2][N];const int a=94153035;const int b=905847205;void init(){    pw1[0][0]=pw2[0][0]=1;    for(int i=1; i<1<<16; i++)        pw1[0][i]=pw1[0][i-1]*a%p;    pw2[0][1]=pw1[0][(1<<16)-1]*a%p;    for(int i=2; i<1<<16; i++)        pw2[0][i]=pw2[0][i-1]*pw2[0][1]%p;    pw1[1][0]=pw2[1][0]=1;    for(int i=1; i<1<<16; i++)        pw1[1][i]=pw1[1][i-1]*b%p;    pw2[1][1]=pw1[1][(1<<16)-1]*b%p;    for(int i=2; i<=1<<16; i++)        pw2[1][i]=pw2[1][i-1]*pw2[1][1]%p;}int pow1(int n){    return pw1[0][n&65535]%p*pw2[0][n>>16]%p;}int pow2(int n){    return pw1[1][n&65535]%p*pw2[1][n>>16]%p;}uint solve(int n){    return 233230706*(pow1(n)-pow2(n)+p)%p;}signed main(){    init();    scanf("%lld",&Q);    Mker::init();    while(Q--)        ans^=solve(Mker::rand()%(p-1));    printf("%llu\n",ans);    return 0;}//233230706×(94153035^n−905847205^n) 

如何求解循环节?

如果数列 \(a_n\) 在模 \(M\) 意义下存在循环节 \(p\) ,则有 \(a_n\equiv a_{n\,\bmod\, p} \bmod M\)

问了 @zx2017 老师 CCOrz

这个东西其实求起来非常简单,

在已知 \(a_n\) 递推式和 \(M\) 的情况下

直接暴力去枚举就好了

具体的,这道题 \[a_0=0,a_1=1\\a_n = 233a_{n-1}+666a_{n-2}\] 注意到 \(a_n\) 是从 \(a_{n-1}\) 和 \(a_{n-2}\) 推来的

那我们枚举 \(n\),直到出现了这样的情况 \[\begin{cases}a_i\equiv a_2\\\\a_{i-1}\equiv a_1\\\\a_{i-2}\equiv a_0\end{cases}\mod M\] 那么 \(i-2\) 就是循环节

是不是很神奇?我太菜了根本没想到 QAQ

当然可能会有更优秀的做法(比起暴力枚举)

不过起码知道怎么简单求就很好了 qwq

]]>
@@ -6759,7 +6759,7 @@ /2022/05/25/luo-gu-p5020-noip2018-ti-gao-zu-huo-bi-xi-tong-ti-jie/ - 洛谷P5020 [NOIP2018 提高组] 货币系统 题解

题目链接:P5020 [NOIP2018 提高组] 货币系统

题意:在网友的国度中共有 $n$ 种不同面额的货币,第 $i$ 种货币的面额为 $a[i]$,你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为 $n$ 、面额数组为 $a[1..n]$ 的货币系统记作 $(n,a)$ 。

在一个完善的货币系统中,每一个非负整数的金额 $x$ 都应该可以被表示出,即对每一个非负整数 $x$ ,都存在 $n$ 个非负整数 $t[i]$ 满足 $a[i] \times t[i]$ 的和为 $x$ 。然而, 在网友的国度中,货币系统可能是不完善的,即可能存在金额 $x$ 不能被该货币系统表示出。例如在货币系统 $n=3 ,a=[2,5,9]$ 中,金额 $1,3$ 就无法被表示出来。

两个货币系统 $(n,a)$ 和 $(m,b)$ 是等价的,当且仅当对于任意非负整数 $x$ ,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。

现在网友们打算简化一下货币系统。他们希望找到一个货币系统 $(m,b)$ ,满足 $(m,b)$ 与原来的货币系统 $(n,a)$ 等价,且 $m$ 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 $m$ 。

输入输出样例

输入 #1

2 4 3 19 10 6 5 11 29 13 19 17 

输出 #1

2   5  

观察第一组数据,可以发现其等价于 3 10

因为 $19$ 和 $6$ 均可以用 $3$ 和 $10$ 替代

而第二组,每一种都是有价值的,不可替代

考虑 $dp$ ,

设 $dp[i]$ 表示凑出 $i$ 最多需要花费的金额种类

显然这个是个完全背包。

$dp[a[i]]=1$ 则说明 $a[i]$ 是有价值的面额

然后就没了,代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(3e4+15)int Q,n,mx;int a[115],dp[N];signed main(){    read(Q);    while(Q--)    {        memset(dp,0xc0,sizeof(dp));        read(n);dp[0]=0;mx=-INF;        for(int i=1; i<=n; i++)        {            read(a[i]);            mx=max(mx,a[i]);        }        for(int i=1; i<=n; i++)            for(int j=a[i]; j<=mx; j++)                dp[j]=max(dp[j],dp[j-a[i]]+1);        int res=0;        for(int i=1; i<=n; i++)            if(dp[a[i]]==1)++res;        write(res);pc('\n');    }    return 0;}

其实这个 $b$ 是可以证明有 $b \subseteq a $ 的

但是,OI不需要严谨证明。OI靠得是OI直觉。

]]>
+ 洛谷P5020 [NOIP2018提高组] 货币系统 题解

题目链接:P5020[NOIP2018 提高组] 货币系统

题意:在网友的国度中共有 \(n\) 种不同面额的货币,第 \(i\) 种货币的面额为 \(a[i]\),你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为\(n\) 、面额数组为 \(a[1..n]\) 的货币系统记作 \((n,a)\) 。

在一个完善的货币系统中,每一个非负整数的金额 \(x\) 都应该可以被表示出,即对每一个非负整数\(x\) ,都存在 \(n\) 个非负整数 \(t[i]\) 满足 \(a[i] \times t[i]\) 的和为 \(x\) 。然而,在网友的国度中,货币系统可能是不完善的,即可能存在金额 \(x\) 不能被该货币系统表示出。例如在货币系统\(n=3 ,a=[2,5,9]\) 中,金额 \(1,3\) 就无法被表示出来。

两个货币系统 \((n,a)\)\((m,b)\) 是等价的,当且仅当对于任意非负整数\(x\),它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。

现在网友们打算简化一下货币系统。他们希望找到一个货币系统 \((m,b)\) ,满足 \((m,b)\) 与原来的货币系统 \((n,a)\) 等价,且 \(m\)尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 \(m\) 。

输入输出样例

输入 #1

2 4 3 19 10 6 5 11 29 13 19 17 

输出 #1

2   5  

观察第一组数据,可以发现其等价于 3 10

因为 \(19\)\(6\) 均可以用 \(3\) 和 \(10\) 替代

而第二组,每一种都是有价值的,不可替代

考虑 \(dp\)

\(dp[i]\) 表示凑出 \(i\) 最多需要花费的金额种类

显然这个是个完全背包。

\(dp[a[i]]=1\) 则说明 \(a[i]\) 是有价值的面额

然后就没了,代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3fnamespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;#define N (int)(3e4+15)int Q,n,mx;int a[115],dp[N];signed main(){    read(Q);    while(Q--)    {        memset(dp,0xc0,sizeof(dp));        read(n);dp[0]=0;mx=-INF;        for(int i=1; i<=n; i++)        {            read(a[i]);            mx=max(mx,a[i]);        }        for(int i=1; i<=n; i++)            for(int j=a[i]; j<=mx; j++)                dp[j]=max(dp[j],dp[j-a[i]]+1);        int res=0;        for(int i=1; i<=n; i++)            if(dp[a[i]]==1)++res;        write(res);pc('\n');    }    return 0;}

其实这个 \(b\) 是可以证明有 $b a $的

但是,OI不需要严谨证明。OI靠得是OI直觉。

]]>
@@ -6786,7 +6786,7 @@ /2022/05/25/luo-gu-p5322-bjoi2019-pai-bing-bu-zhen-ti-jie/ - 洛谷P5322 [BJOI2019] 排兵布阵 题解

题意:小 C 正在玩一款排兵布阵的游戏。在游戏中有 $n$ 座城堡,每局对战由两名玩家来争夺这些城堡。每名玩家有 $m$ 名士兵,可以向第 $i$ 座城堡派遣 $a_i$ 名士兵去争夺这个城堡,使得总士兵数不超过 $m$。

如果一名玩家向第 $i$ 座城堡派遣的士兵数严格大于对手派遣士兵数的两倍,那么这名玩家就占领了这座城堡,获得 $i$ 分。

现在小 C 即将和其他 $s$ 名玩家两两对战,这 $s$ 场对决的派遣士兵方案必须相同。小 C 通过某些途径得知了其他 $s$ 名玩家即将使用的策略,他想知道他应该使用什么策略来最大化自己的总分。

由于答案可能不唯一,你只需要输出小 C 总分的最大值。

设 $dp[i][j]$ 表示第 $i$ 个城堡放 $j$ 个士兵可以获得的最大分数

显然我们会选择恰好为某名玩家放的兵数 $x$ 的 $2x+1$ ,不然多的都是浪费

因此这就是个类似背包的问题

没什么难度,直接看代码就能明白了吧(逃

时间复杂度 $O(nms)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define S (int)(105)#define N (int)(115)#define M (int)(2e4+15)int s,n,m;int val[N][M],dp[M],b[N][S],mx[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> s >> n >> m;    for(int i=1; i<=s; i++)        for(int j=1; j<=n; j++)            cin >> b[j][i];    for(int i=1; i<=n; i++)        sort(b[i]+1,b[i]+1+s);    for(int i=1; i<=n; i++)        for(int j=m; j>=1; j--)            for(int k=1; k<=s; k++)                if(j>2*b[i][k]) dp[j]=max(dp[j],dp[j-2*b[i][k]-1]+k*i);    cout << dp[m] << endl;    return 0;}
]]>
+ 洛谷P5322 [BJOI2019] 排兵布阵题解

题意:小 C 正在玩一款排兵布阵的游戏。在游戏中有\(n\)座城堡,每局对战由两名玩家来争夺这些城堡。每名玩家有 \(m\) 名士兵,可以向第 \(i\) 座城堡派遣 \(a_i\)名士兵去争夺这个城堡,使得总士兵数不超过 \(m\)。

如果一名玩家向第 \(i\)座城堡派遣的士兵数严格大于对手派遣士兵数的两倍,那么这名玩家就占领了这座城堡,获得\(i\) 分。

现在小 C 即将和其他 \(s\)名玩家两两对战,这 \(s\)场对决的派遣士兵方案必须相同。小 C 通过某些途径得知了其他 \(s\)名玩家即将使用的策略,他想知道他应该使用什么策略来最大化自己的总分。

由于答案可能不唯一,你只需要输出小 C 总分的最大值。

\(dp[i][j]\) 表示第 \(i\) 个城堡放 \(j\) 个士兵可以获得的最大分数

显然我们会选择恰好为某名玩家放的兵数 \(x\) 的 \(2x+1\) ,不然多的都是浪费

因此这就是个类似背包的问题

没什么难度,直接看代码就能明白了吧(逃

时间复杂度 \(O(nms)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define S (int)(105)#define N (int)(115)#define M (int)(2e4+15)int s,n,m;int val[N][M],dp[M],b[N][S],mx[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> s >> n >> m;    for(int i=1; i<=s; i++)        for(int j=1; j<=n; j++)            cin >> b[j][i];    for(int i=1; i<=n; i++)        sort(b[i]+1,b[i]+1+s);    for(int i=1; i<=n; i++)        for(int j=m; j>=1; j--)            for(int k=1; k<=s; k++)                if(j>2*b[i][k]) dp[j]=max(dp[j],dp[j-2*b[i][k]-1]+k*i);    cout << dp[m] << endl;    return 0;}
]]>
@@ -6813,7 +6813,7 @@ /2022/05/25/luo-gu-p5365-snoi2017-ying-xiong-lian-meng-ti-jie/ - 洛谷P5365 [SNOI2017] 英雄联盟 题解

题目链接:P5365 [SNOI2017] 英雄联盟

题意:正在上大学的小皮球热爱英雄联盟这款游戏,而且打的很菜,被网友们戏称为「小学生」。

现在,小皮球终于受不了网友们的嘲讽,决定变强了,他变强的方法就是:买皮肤!

小皮球只会玩 $\text{N}$ 个英雄,因此,他也只准备给这 $\text{N}$ 个英雄买皮肤,并且决定,以后只玩有皮肤的英雄。

这 $\text{N}$ 个英雄中,第 $\text{i}$ 个英雄有 $K_i$ 款皮肤,价格是每款 $C_i$ Q 币(同一个英雄的皮肤价格相同)。

为了让自己看起来高大上一些,小皮球决定给同学们展示一下自己的皮肤,展示的思路是这样的:对于有皮肤的每一个英雄,随便选一个皮肤给同学看。

比如,小皮球共有 5 个英雄,这 5 个英雄分别有 $\text{0,0,3,2,4}$ 款皮肤,那么,小皮球就有 $3 \times 2 \times 4 = 24$ 种展示的策略。

现在,小皮球希望自己的展示策略能够至少达到 $\text{M}$ 种,请问,小皮球至少要花多少钱呢?

题目要求的就是数量要超过 $m$ ,并要求价格最少

如果直接dp会带上一些乘法除法的转移,不太好

考虑转化问题,求出每种价格可以获得的最多的数量

那么这就变成了一个类似于多重背包的dp问题

其中价格是费用,即 $w[i]$

状态转移方程:

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(205)#define M (int)(3e5+15)int n,m,sum;int w[N],v[N],dp[M];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> v[i];    for(int i=1; i<=n; i++)    {        cin >> w[i];        sum+=w[i]*v[i];    }    dp[0]=1;    for(int i=1; i<=n; i++)        for(int j=sum; j>=0; j--)            for(int k=1; k*w[i]<=j&&k<=v[i]; k++)                dp[j]=max(dp[j],dp[j-k*w[i]]*k);    int mn=INF;    for(int i=1; i<=sum; i++)        if(dp[i]>m){cout << i;return 0;}        return 0;}
]]>
+ 洛谷P5365 [SNOI2017] 英雄联盟题解

题目链接:P5365[SNOI2017] 英雄联盟

题意:正在上大学的小皮球热爱英雄联盟这款游戏,而且打的很菜,被网友们戏称为「小学生」。

现在,小皮球终于受不了网友们的嘲讽,决定变强了,他变强的方法就是:买皮肤!

小皮球只会玩 \(\text{N}\)个英雄,因此,他也只准备给这 \(\text{N}\)个英雄买皮肤,并且决定,以后只玩有皮肤的英雄。

\(\text{N}\) 个英雄中,第 \(\text{i}\) 个英雄有 \(K_i\) 款皮肤,价格是每款 \(C_i\) Q币(同一个英雄的皮肤价格相同)。

为了让自己看起来高大上一些,小皮球决定给同学们展示一下自己的皮肤,展示的思路是这样的:对于有皮肤的每一个英雄,随便选一个皮肤给同学看。

比如,小皮球共有 5 个英雄,这 5 个英雄分别有 \(\text{0,0,3,2,4}\) 款皮肤,那么,小皮球就有\(3 \times 2 \times 4 = 24\)种展示的策略。

现在,小皮球希望自己的展示策略能够至少达到 \(\text{M}\)种,请问,小皮球至少要花多少钱呢?

题目要求的就是数量要超过 \(m\),并要求价格最少

如果直接dp会带上一些乘法除法的转移,不太好

考虑转化问题,求出每种价格可以获得的最多的数量

那么这就变成了一个类似于多重背包的dp问题

其中价格是费用,即 \(w[i]\)

状态转移方程: \[dp[j]=\max_{0 \le k \le\min\left(v[i],\left\lfloor\frac{j}{w[i]}\right\rfloor\right)}(dp[j],dp[j-k\timesw[i]]\times k)\] 代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(205)#define M (int)(3e5+15)int n,m,sum;int w[N],v[N],dp[M];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> v[i];    for(int i=1; i<=n; i++)    {        cin >> w[i];        sum+=w[i]*v[i];    }    dp[0]=1;    for(int i=1; i<=n; i++)        for(int j=sum; j>=0; j--)            for(int k=1; k*w[i]<=j&&k<=v[i]; k++)                dp[j]=max(dp[j],dp[j-k*w[i]]*k);    int mn=INF;    for(int i=1; i<=sum; i++)        if(dp[i]>m){cout << i;return 0;}        return 0;}
]]>
@@ -6840,7 +6840,7 @@ /2022/05/25/luo-gu-p5656-mo-ban-er-yuan-yi-ci-bu-ding-fang-cheng-exgcd-ti-jie/ - 洛谷P5656 【模板】二元一次不定方程 (exgcd) 题解

题目链接:P5656 【模板】二元一次不定方程 (exgcd)

题意:给定不定方程

若该方程无整数解,输出 $-1$。
若该方程有整数解,且有正整数解,则输出其正整数解的数量,所有正整数解中 $x$ 的最小值,所有正整数解中 $y$ 的最小值,所有正整数解中 $x$ 的最大值,以及所有正整数解中 $y$ 的最大值。
若方程有整数解,但没有正整数解,你需要输出所有整数解中 $x$ 的最小正整数值, $y$ 的最小正整数值。

正整数解即为 $x, y$ 均为正整数的解,$\boldsymbol{0}$ 不是正整数
整数解即为 $x,y$ 均为整数的解。
$x$ 的最小正整数值即所有 $x$ 为正整数的整数解中 $x$ 的最小值,$y$ 同理。

$1\le a,b,c\le 10^9$

根据裴蜀定理

对于 $x,y$ 的二元一次不定方程 $ax+by=c$ ,其有解的充要条件为 $\gcd(a,b) \mid c$

可知方程无解当且仅当 $\gcd(a,b) \nmid c$

那么有解的时候,我们可以用扩展欧几里德算法求出一组特解

即 $x^{\prime},y^{\prime}$ 满足

考虑转化为原方程的一组特解。令 $d=\gcd(a,b)$

则特解 $x_0=x^{\prime}\times \dfrac{c}{d},y_0 = y’ \times \dfrac{c}{d}$ 满足

此时的 $x_0,y_0$ 就是一个平凡的解,考虑转化为更有价值的解

考虑增大 $x_0$ 为 $x_0+p$ ,则 $y_0$ 需减小至 $y_0-q$

可得 $p =\dfrac{bq}{a}$

可以发现这样的解有无数个,我们只要找到一个最小正整数解即可(即 $p,q\in \mathbb{Z}_+$ 且 $p,q$ 尽可能小)

$\because a\mid bq,b\mid bq$

$\therefore (bq)_{\min}=\operatorname{lcm}(a,b) = \dfrac{ab}{\gcd(a,b)}$

于是我们尝试将 $x_0$ 调至最小正整数( $y_0$ 同时也会改变 )

即找到一个最小的整数 $k$ 使得 $x_0 + kp \ge 1$

然后将 $x_0$ 变为 $x_0 + kp$ 即可,此时 $y_0$ 变化为 $y_0-qk$

若此时 $y_0 > 0$ ,则存在正整数解

  1. 正整数解的个数:使 $y_0$ 不停地减 $q$ 就是所有可行正整数解,故答案为 $\left\lfloor{\dfrac{y_0-1}{q}}\right\rfloor+1$
  2. $x$ 的最小正整数值:$x_0$
  3. $y$ 的最小正整数值:使 $y$ 不停地减 $q$ ,最小的那个正整数就是答案,即 $(y_0-1)\bmod q + 1$

  4. $x$ 的最大正整数值:$y$ 最小时 $x$ 最大

  5. $y$ 的最大正整数值: $y_0$

若此时 $y_0 \le 0$ ,则仅存在整数解

  1. $x$ 的最小正整数值: $x_0$
  2. $y$ 的最小正整数值:构造 $y_0+k’q \ge 1$ ,易知 $k^{\prime} = \left\lceil{\dfrac{1-y_0}{q}}\right\rceil$ ,故答案为 $y_0+q\times \left\lceil{\dfrac{1-y_0}{q}}\right\rceil$

据说要开 $\text{long long}$ ,反正我#define int long long丝毫不慌(逃

主要代码如下(省略了快读和多组数据)

#define int long longint exgcd(int a,int b,int &x,int &y){    int d=a;    if(!b)x=1,y=0;    else d=exgcd(b,a%b,y,x),y-=a/b*x;    return d;}void solve(){    int a,b,c,x,y;    read(a);read(b);read(c);    int d=exgcd(a,b,x,y);    if(c%d!=0){puts("-1");return;}    x*=c/d;y*=c/d;    int p=b/d,q=a/d,k;    k=ceil((1.0-x)/p),x+=p*k,y-=q*k;    if(y>0)    {        write((y-1)/q+1);  pc(' ');        write(x);          pc(' ');        write((y-1)%q+1);  pc(' ');        write(x+(y-1)/q*p);pc(' ');        write(y);          pc(' ');    }else    {        write(x);                       pc(' ');        write(y+q*(int)ceil((1.0-y)/q));pc(' ');    }    pc('\n');}

参考文献

[1] https://www.luogu.com.cn/blog/McHf/p5656-exgcd

]]>
+ 洛谷P5656【模板】二元一次不定方程 (exgcd) 题解

题目链接:P5656【模板】二元一次不定方程 (exgcd)

题意:给定不定方程 \[ax+by=c\] 若该方程无整数解,输出 \(-1\)。若该方程有整数解,且有正整数解,则输出其正整数解的数量,所有正整数解中\(x\)的最小值,所有正整数解中 \(y\)的最小值,所有正整数解中 \(x\)的最大值,以及所有正整数解中 \(y\) 的最大值。若方程有整数解,但没有正整数解,你需要输出所有整数解\(x\) 的最小正整数值, \(y\) 的最小正整数值。

正整数解即为 \(x, y\)均为正整数的解,\(\boldsymbol{0}\)不是正整数。 整数解即为 \(x,y\) 均为整数的解。 \(x\) 的最小正整数值即所有 \(x\) 为正整数的整数解中 \(x\) 的最小值,\(y\) 同理。

\(1\le a,b,c\le 10^9\)

根据裴蜀定理

对于 \(x,y\)的二元一次不定方程 \(ax+by=c\) ,其有解的充要条件为 \(\gcd(a,b) \mid c\)

可知方程无解当且仅当 \(\gcd(a,b) \nmidc\)

那么有解的时候,我们可以用扩展欧几里德算法求出一组特解

\(x^{\prime},y^{\prime}\) 满足\[ax^{\prime}+by^{\prime}=\gcd(a,b)\]

考虑转化为原方程的一组特解。令 \(d=\gcd(a,b)\)

则特解 \(x_0=x^{\prime}\times\dfrac{c}{d},y_0 = y' \times \dfrac{c}{d}\) 满足 \[ax_0 + by_0 = c\] 此时的 \(x_0,y_0\)就是一个平凡的解,考虑转化为更有价值的解

考虑增大 \(x_0\)\(x_0+p\) ,则 \(y_0\) 需减小至 \(y_0-q\)

\[\begin{cases}a\times(x_0+p)+b\times(y_0-q) = c\\\\ax_0 + b y_0 = c\end{cases}\] 可得 \(p =\dfrac{bq}{a}\)

可以发现这样的解有无数个,我们只要找到一个最小正整数解即可(即\(p,q\in \mathbb{Z}_+\)\(p,q\) 尽可能小)

\(\because a\mid bq,b\mid bq\)

\(\therefore(bq)_{\min}=\operatorname{lcm}(a,b) = \dfrac{ab}{\gcd(a,b)}\)

\[\begin{aligned}\begin{cases}p_{\min}&=\dfrac{b}{d}\\\\q_{\min}&=\dfrac{a}{d}\end{cases}\end{aligned}\] 于是我们尝试将 \(x_0\)调至最小正整数( \(y_0\) 同时也会改变)

即找到一个最小的整数 \(k\) 使得\(x_0 + kp \ge 1\)

\[k= \left\lceil{\dfrac{1-x_0}{p}}\right\rceil\] 然后将 \(x_0\) 变为 \(x_0 + kp\) 即可,此时 \(y_0\) 变化为 \(y_0-qk\)

若此时 \(y_0 > 0\),则存在正整数解

  1. 正整数解的个数:使 \(y_0\) 不停地减 \(q\) 就是所有可行正整数解,故答案为 \(\left\lfloor{\dfrac{y_0-1}{q}}\right\rfloor+1\)

  2. \(x\)的最小正整数值\(x_0\)

  3. \(y\)的最小正整数值:使 \(y\) 不停地减 \(q\) ,最小的那个正整数就是答案,即 \((y_0-1)\bmod q + 1\)

  4. \(x\)的最大正整数值\(y\)最小时 \(x\) 最大

  5. \(y\)的最大正整数值\(y_0\)

若此时 \(y_0 \le 0\),则仅存在整数解

  1. \(x\)的最小正整数值\(x_0\)
  2. \(y\)的最小正整数值:构造 \(y_0+k'q \ge 1\) ,易知 \(k^{\prime} =\left\lceil{\dfrac{1-y_0}{q}}\right\rceil\) ,故答案为 \(y_0+q\times\left\lceil{\dfrac{1-y_0}{q}}\right\rceil\)

据说要开 \(\text{long long}\)反正我#define int long long丝毫不慌(逃

主要代码如下(省略了快读和多组数据)

#define int long longint exgcd(int a,int b,int &x,int &y){    int d=a;    if(!b)x=1,y=0;    else d=exgcd(b,a%b,y,x),y-=a/b*x;    return d;}void solve(){    int a,b,c,x,y;    read(a);read(b);read(c);    int d=exgcd(a,b,x,y);    if(c%d!=0){puts("-1");return;}    x*=c/d;y*=c/d;    int p=b/d,q=a/d,k;    k=ceil((1.0-x)/p),x+=p*k,y-=q*k;    if(y>0)    {        write((y-1)/q+1);  pc(' ');        write(x);          pc(' ');        write((y-1)%q+1);  pc(' ');        write(x+(y-1)/q*p);pc(' ');        write(y);          pc(' ');    }else    {        write(x);                       pc(' ');        write(y+q*(int)ceil((1.0-y)/q));pc(' ');    }    pc('\n');}

参考文献

[1] https://www.luogu.com.cn/blog/McHf/p5656-exgcd

]]>
@@ -6867,7 +6867,7 @@ /2022/05/25/luo-gu-p5658-csp-s2019-gua-hao-shu-ti-jie/ - 洛谷P5658 [CSP-S2019] 括号树 题解

题目链接:P5658 [CSP-S2019] 括号树

题意:略。

注意到本题中,链的部分分很高,就先考虑这种情况

不难发现,每个与 $l$ 配对的右括号 $r$ 的贡献为 $dp[r]=dp[l-1]+1 = dp[fa[l]]+1$

则 $k_i = dp[i] + \sum dp[j]$ ,$j$ 是 $i$ 的祖先结点

显然这个性质可以推广到树上

这样维护一个括号栈就好了,怎么维护呢?

为了防止栈在回溯过程中被破坏,考虑回溯时执行相反操作

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)int n,fa[N],sum[N];char val[N];struct Edge{    int u,v,next;}e[N];int pos=1,head[N],dp[N],stk[N],top;void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u){    int t=0;    if(val[u]==')')    {        if(top)        {            t=stk[top--];            dp[u]=dp[fa[t]]+1;        }    }else stk[++top]=u;    sum[u]=sum[fa[u]]+dp[u];    for(int i=head[u]; i; i=e[i].next)        dfs(e[i].v);    if(t)stk[++top]=t;    else if(top)--top;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> (val+1);    for(int i=2; i<=n; i++)    {        cin >> fa[i];        addEdge(fa[i],i);    }    dfs(1);int res=0;    for(int i=1; i<=n; i++)        res^=i*sum[i];    cout << res;    return 0;}
]]>
+ 洛谷P5658 [CSP-S2019] 括号树题解

题目链接:P5658[CSP-S2019] 括号树

题意:略。

注意到本题中,链的部分分很高,就先考虑这种情况

不难发现,每个与 \(l\) 配对的右括号\(r\) 的贡献为 \(dp[r]=dp[l-1]+1 = dp[fa[l]]+1\)

\(k_i = dp[i] + \sum dp[j]\)\(j\)\(i\) 的祖先结点

显然这个性质可以推广到树上

这样维护一个括号栈就好了,怎么维护呢?

为了防止栈在回溯过程中被破坏,考虑回溯时执行相反操作

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(5e5+15)int n,fa[N],sum[N];char val[N];struct Edge{    int u,v,next;}e[N];int pos=1,head[N],dp[N],stk[N],top;void addEdge(int u,int v){    e[++pos]={u,v,head[u]};    head[u]=pos;}void dfs(int u){    int t=0;    if(val[u]==')')    {        if(top)        {            t=stk[top--];            dp[u]=dp[fa[t]]+1;        }    }else stk[++top]=u;    sum[u]=sum[fa[u]]+dp[u];    for(int i=head[u]; i; i=e[i].next)        dfs(e[i].v);    if(t)stk[++top]=t;    else if(top)--top;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> (val+1);    for(int i=2; i<=n; i++)    {        cin >> fa[i];        addEdge(fa[i],i);    }    dfs(1);int res=0;    for(int i=1; i<=n; i++)        res^=i*sum[i];    cout << res;    return 0;}
]]>
@@ -6881,10 +6881,10 @@ 算法 - DP - 图论 + DP + 数据结构 @@ -6898,7 +6898,7 @@ /2022/05/25/luo-gu-p5851-usaco19dec-greedy-pie-eaters-p-ti-jie/ - 洛谷P5851 [USACO19DEC]Greedy Pie Eaters P 题解

题目链接:P5851 [USACO19DEC]Greedy Pie Eaters P

题意

Farmer John 有 $M$ 头奶牛,为了方便,编号为 $1,\dots,M$。这些奶牛平时都吃青草,但是喜欢偶尔换换口味。Farmer John 一天烤了 $N$ 个派请奶牛吃,这 $N$ 个派编号为 $1,\dots,N$。第 $i$ 头奶牛喜欢吃编号在 $\left[ l_i,r_i \right]$ 中的派(包括两端),并且没有两头奶牛喜欢吃相同范围的派。第 $i$ 头奶牛有一个体重 $w_i$,这是一个在 $\left[ 1,10^6 \right]$ 中的正整数。

Farmer John 可以选择一个奶牛序列 $c_1,c_2,\dots,c_K$,并让这些奶牛按这个顺序轮流吃派。不幸的是,这些奶牛不知道分享!当奶牛 吃派时,她会把她喜欢吃的派都吃掉——也就是说,她会吃掉编号在 $[l_{c_i},r_{c_i}]$ 中所有剩余的派。Farmer John 想要避免当轮到一头奶牛吃派时,她所有喜欢的派在之前都被吃掉了这样尴尬的情况。因此,他想让你计算,要使奶牛按 $c_1,c_2,\dots,c_K$ 的顺序吃派,轮到这头奶牛时她喜欢的派至少剩余一个的情况下,这些奶牛的最大可能体重($w_{c_1}+w_{c_2}+\ldots+w_{c_K}$)是多少。

显然是一个区间dp

设 $f[i][j]$ 表示吃完 $[i,j]$ 能获得的最大 $\sum w$

设 $g[k][i][j]$ 表示 $k$ 还没吃,准备吃掉 $k$ 时 $(i \le l_d \le k \le r_d \le j)$ 的最大 $w$

首先不尝试增加奶牛 $d$ ,即不考虑吃 $k$ ,有

考虑吃 $k$ ,尝试增加奶牛 $d$ ,有

  • 关于 $g[k][i][j]$ 如何限制 $(l_d \le k \le r_d)$

    考虑读入时处理每头奶牛 $g[t][l][r]=w,l \le t \le r$

  • 如何更新 $g[k][i][j]$

    显然当 $[i,j]=[l_d,r_d]$ 时,已经在读入时更新了

    当 $i \le l_d$ 或 $j \ge r_d$ 时,我们可以通过

    进行更新

时间复杂度 $O(n^3)$

代码:

#include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(305)#define M (int)(5e4+15)int n,m;int f[N][N],g[N][N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,w,l,r; i<=m; i++)    {        cin >> w >> l >> r;        for(int j=l; j<=r; j++)            g[j][l][r]=w;    }    for(int len=1; len<=n; len++)        for(int i=1,j=i+len-1; j<=n; i++,j++)            for(int k=i; k<=j; k++)            {                if(i!=1)g[k][i-1][j]=max(g[k][i-1][j],g[k][i][j]);                if(j!=n)g[k][i][j+1]=max(g[k][i][j+1],g[k][i][j]);            }    for(int len=1; len<=n; len++)        for(int i=1,j=i+len-1; j<=n; i++,j++)        {            for(int k=i; k<j; k++)                f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);            for(int k=i; k<=j; k++)                f[i][j]=max(f[i][j],(k!=i?f[i][k-1]:0)+(k!=j?f[k+1][j]:0)+g[k][i][j]);        }    cout << f[1][n];    return 0;}
]]>
+ 洛谷P5851[USACO19DEC]Greedy Pie Eaters P 题解

题目链接:P5851[USACO19DEC]Greedy Pie Eaters P

题意

Farmer John 有 \(M\)头奶牛,为了方便,编号为 \(1,\dots,M\)。这些奶牛平时都吃青草,但是喜欢偶尔换换口味。FarmerJohn 一天烤了 \(N\) 个派请奶牛吃,这\(N\) 个派编号为 \(1,\dots,N\)。第 \(i\) 头奶牛喜欢吃编号在 \(\left[ l_i,r_i \right]\)中的派(包括两端),并且没有两头奶牛喜欢吃相同范围的派。第 \(i\) 头奶牛有一个体重 \(w_i\),这是一个在 \(\left[ 1,10^6 \right]\) 中的正整数。

Farmer John 可以选择一个奶牛序列 \(c_1,c_2,\dots,c_K\),并让这些奶牛按这个顺序轮流吃派。不幸的是,这些奶牛不知道分享!当奶牛吃派时,她会把她喜欢吃的派都吃掉——也就是说,她会吃掉编号在 \([l_{c_i},r_{c_i}]\) 中所有剩余的派。FarmerJohn想要避免当轮到一头奶牛吃派时,她所有喜欢的派在之前都被吃掉了这样尴尬的情况。因此,他想让你计算,要使奶牛按\(c_1,c_2,\dots,c_K\)的顺序吃派,轮到这头奶牛时她喜欢的派至少剩余一个的情况下,这些奶牛的最大可能体重(\(w_{c_1}+w_{c_2}+\ldots+w_{c_K}\))是多少。

显然是一个区间dp

\(f[i][j]\) 表示吃完 \([i,j]\) 能获得的最大 \(\sum w\)

\(g[k][i][j]\) 表示 \(k\) 还没吃,准备吃掉 \(k\) 时 \((i \lel_d \le k \le r_d \le j)\) 的最大 \(w\)

首先不尝试增加奶牛 \(d\),即不考虑吃 \(k\) ,有 \[f[i][j]=\max(f[i][j],f[i][k]+f[k+1][j])\]

考虑吃 \(k\) ,尝试增加奶牛 \(d\) ,有 \[f[i][j]=\max(f[i][j],f[i][k-1]+f[k+1][j]+g[k][i][j])\]

  • 关于 \(g[k][i][j]\) 如何限制\((l_d \le k \le r_d)\)

    考虑读入时处理每头奶牛 \(g[t][l][r]=w,l \let \le r\)

  • 如何更新 \(g[k][i][j]\)

    显然当 \([i,j]=[l_d,r_d]\)时,已经在读入时更新了

    \(i \le l_d\)\(j \ge r_d\) 时,我们可以通过 \[g[k][i-1][j]=\max(g[k][i-1][j],g[k][i][j])\\g[k][i][j+1]=\max(g[k][i][j+1],g[k][i][j])\] 进行更新

时间复杂度 \(O(n^3)\)

代码:

#include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define N (int)(305)#define M (int)(5e4+15)int n,m;int f[N][N],g[N][N][N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    cin >> n >> m;    for(int i=1,w,l,r; i<=m; i++)    {        cin >> w >> l >> r;        for(int j=l; j<=r; j++)            g[j][l][r]=w;    }    for(int len=1; len<=n; len++)        for(int i=1,j=i+len-1; j<=n; i++,j++)            for(int k=i; k<=j; k++)            {                if(i!=1)g[k][i-1][j]=max(g[k][i-1][j],g[k][i][j]);                if(j!=n)g[k][i][j+1]=max(g[k][i][j+1],g[k][i][j]);            }    for(int len=1; len<=n; len++)        for(int i=1,j=i+len-1; j<=n; i++,j++)        {            for(int k=i; k<j; k++)                f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);            for(int k=i; k<=j; k++)                f[i][j]=max(f[i][j],(k!=i?f[i][k-1]:0)+(k!=j?f[k+1][j]:0)+g[k][i][j]);        }    cout << f[1][n];    return 0;}
]]>
@@ -6921,11 +6921,11 @@ - 洛谷P6327 区间加区间sin和 题解 - - /2022/05/25/luo-gu-p6327-qu-jian-jia-qu-jian-sin-he-ti-jie/ + 洛谷P6216 回文匹配 题解 + + /2022/05/25/luo-gu-p6216-hui-wen-pi-pei-ti-jie/ - 洛谷P6327 区间加区间sin和 题解

题目链接:洛谷P6327 区间加区间sin和 题解

题意:维护一个数据结构,支持

  1. 区间加 $v$
  2. 询问区间 $\sum\limits_{i=1}^{r}\sin a_i$

注意到

直接维护即可两个量即可

注意点

  1. 更新的时候拿之前更新过的sin去更新cos,爆零。
  2. 不开long long,爆零。
  3. 不下传标记,爆零。
  4. 可以适当卡卡常(逃

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)#define ls(at) (at<<1)#define rs(at) (at<<1|1)namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;// #define double long double// const double eps=1e-15;double sinx[N<<2],cosx[N<<2];int tag[N<<2];int n,m,a[N];void push_up(int at){    sinx[at]=sinx[ls(at)]+sinx[rs(at)];    cosx[at]=cosx[ls(at)]+cosx[rs(at)];}void build(int l,int r,int at){    if(l==r)    {        sinx[at]=sin(a[l]);        cosx[at]=cos(a[l]);        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void proc(int at,int k){    double sink=sin(k),cosk=cos(k);    double sina=sinx[at],cosa=cosx[at];    sinx[at]=sina*cosk+cosa*sink;    cosx[at]=cosa*cosk-sina*sink;    tag[at]+=k;}void push_down(int at){    int k=tag[at];    if(k)    {        proc(ls(at),k);        proc(rs(at),k);        tag[at]=0;    }}void update(int nl,int nr,int l,int r,int k,int at){    if(nl<=l&&r<=nr)    {        proc(at,k);        return;    }    int mid=(l+r)>>1;    push_down(at);    if(nl<=mid)update(nl,nr,l,mid,k,ls(at));    if(nr>mid)update(nl,nr,mid+1,r,k,rs(at));    push_up(at);}double query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)return sinx[at];    if(nl>r||nr<l)return 0;    push_down(at);    int mid=(l+r)>>1;    return query(nl,nr,l,mid,ls(at))+query(nl,nr,mid+1,r,rs(at));}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // cout << fixed << setprecision(1);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    int Q;read(Q);    int op,l,r,v;    while(Q--)    {        read(op);read(l);read(r);        if(op==1){read(v);update(l,r,1,n,v,1);}        else printf("%.1lf\n",query(l,r,1,n,1));    }    return 0;}
]]>
+ 洛谷P6216 回文匹配 题解

题目链接:P6216回文匹配

题意:对于一对字符串 \((s_1,s_2)\),若 \(s_1\) 的长度为奇数的子串\((l,r)\) 满足 \((l,r)\) 是回文的,那么 \(s_1\) 的“分数”会增加 \(s_2\) 在 \((l,r)\) 中出现的次数。

现在给出一对 \((s_1,s_2)\),请计算出\(s_1\) 的“分数”。

答案对 \(2 ^ {32}\) 取模。

容易发现这就是个Manacher+KMP

Manacher用于找出奇回文串,KMP用于找出 \(s_2\) 在 \((l,r)\) 中的出现位置

难点在于如何计算这个次数

对于每个极大回文串(长度小于 \(|s_2|\) 的不考虑)

它一定包含了在同一回文中心的更小的回文串

abbba还包含了bbb,b

如果暴力去求解,时间复杂度为 \(O(n^2)\)

于是想到前缀和来加快运算

我们可以在匹配成功时将 \(s_2\)左端点位置标记为 \(1\)

考虑一个回文串x为任意字符

str:xxxxxxxpos:1234567

它对答案的贡献为sum_6-sum_0+sum_5-sum_1+sum_4-sum_2+sum_3-sum_3

即,sum_6+sum_5+sum4+sum_3-(sum3+sum_2+sum_1+sum_0)

注:要从r-m+1开始,否则会出现右边界超出范围的情况

还是个前缀和!

于是我们可以再做一次前缀和(即二次前缀和)

这样时间复杂度就压到 \(O(n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(3e6+25)int n,m,r,mid;int fail[MAXN],sum[MAXN],len,p[MAXN];char a[MAXN],b[MAXN];unsigned ans;signed main(){    scanf("%lld%lld",&n,&m);    scanf("%s %s",a+1,b+1);    a[n+1]=b[m+1]='~'; // 本题的数据有问题所以要手动加个分隔符 QAQ    for(int i=2,j=0; i<=m; i++)    {        while(j&&b[i]!=b[j+1])j=fail[j];        if(b[i]==b[j+1])++j;        fail[i]=j;    }    for(int i=1,j=0; i<=n; i++)    {        while(j&&a[i]!=b[j+1])j=fail[j];        if(a[i]==b[j+1])++j;        if(j==m)++sum[i-m+1];    }    for(int i=1; i<=n; i++)        sum[i]+=sum[i-1];    for(int i=1; i<=n; i++)        sum[i]+=sum[i-1];    for(int i=1; i<=n; i++)    {        if(i<=r)p[i]=min(p[(mid<<1)-i],r-i+1);        while(a[i-p[i]]==a[i+p[i]])++p[i];        if(i+p[i]-1>=r)r=i+p[i]-1,mid=i;    }    // ------------------------------------    for(int i=1; i<=n; i++)    {        if(2*p[i]-1<m)continue;        int r=i+p[i]-1,l=i-p[i]+1;        l--;r=r-m+1;        int mid=(l+r)>>1;        ans+=sum[r]-sum[mid]-sum[((l+r)&1)?mid:mid-1]+sum[l-1];    }    printf("%u\n",ans);    return 0;}

参考文献

[1] ZCETHAN'SBLOGS - P6216 回文匹配

]]>
@@ -6937,7 +6937,9 @@ - 数据结构 + 算法 + + 字符串 @@ -6946,11 +6948,11 @@ - 等差数列&等比数列小结 - - /2022/05/25/deng-chai-shu-lie-deng-bi-shu-lie-xiao-jie/ + 洛谷P6327 区间加区间sin和 题解 + + /2022/05/25/luo-gu-p6327-qu-jian-jia-qu-jian-sin-he-ti-jie/ - 等差数列&等比数列小结

高一自学的时候瞎总结写的(好吧我现在还是高一 2022.5.7)

感觉丢在文件夹里吃灰没啥用,就放上来了

欢迎各位指出我的错误(我数学真的烂 $😓$


等差数列

等差数列通项公式

$S_n$ 表示等差数列 $\{a_n\}$ 的 前 $n$ 项和

易知

$\{a_n\}$ 为等差数列当且仅当

命题1:若 $S_m = S_p \land m\ne p$ 则有 $S_{m+p}=0$

证:

命题2:若 $S_m=p,S_p=m\land m\ne p$ 则有 $S_{m+p}=-(m+p)$

证:

等比数列

等比数列通项公式

有性质

等比数列前 $n$ 项和公式

易知

可知性质

当 $q\ne -1$ 时,有等比数列 $\left\{S_{(k+1)m}-S_{km}\right\}$

  1. 有递推式

其中,$c\ne1,cd\ne 0$

构造等比数列求通项公式

即利用 $d=\dfrac{(1-c)d}{1-c}$ ,得

则当 $a_n-\dfrac{d}{1-c}\ne 0$ 时,数列 $\left\{a_n-\dfrac{d}{1-c}\right\}$ 为等比数列

  1. 有递推式

    其中,$c\ne1, cd\ne 0, n\ge 2$

    消常数项求通项公式

    则当 $a_2\ne a_1$ 时,数列 $\left\{a_{n+1}-a_n\right\}$ 为等比数列

  1. 有递推式

    其中,$c\ne d,cd\ne0$

    化归求通项公式

    则数列 $\left\{a_n-\dfrac{d^n}{d-c}\right\}$ 为等比数列

    或者两侧同除以 $d^{n+1}$ 化为情况1

    或者两侧同除以 $c^{n+1}$ ,累加求通项

  1. 有递推式

    其中,$cdt\ne0,c\ne 1$

    化归求通项公式

    则转化为情况2

  1. 有递推式

    其中,$pqk\ne0,p\ne1,q\ne1$

    可使用待定系数法,后略

    也可以两边同除以 $q^{n+1}$ ,得

    进而化归为等比数列

    还可以两边同除以 $p^{n+1}$ ,得


相信一定也有和我一样的懒人不喜欢对着博客手敲的

我直接把上面那一堆的源码贴上来吧

## 等差数列等差数列通项公式$$a_n = a_1+(n-1)d$$$S_n$ 表示等差数列 $\{a_n\}$ 的 前 $n$ 项和$$\begin{aligned}S_n &= \sum\limits_{i=1}^{n}a_n\\&=na_1+\dfrac{n(n-1)}{2}d\\&=\dfrac{d}{2}n^2+\left(a_1-\dfrac{d}{2}\right)n\end{aligned}$$易知$$\begin{aligned}S_n&=\dfrac{n(a_1+a_n)}{2}\\&=\dfrac{n(a_m+a_{n-m+1})}{2}\end{aligned}$$$$S_{2n-1}=(2n-1)a_n\\S_{2n}=n(a_n+a_{n+1})$$$\{a_n\}$ 为等差数列当且仅当$$S_n=An^2+Bn+C \land C=0$$**命题1**:若 $S_m = S_p \land m\ne p$ 则有 $S_{m+p}=0$ 证:$$\begin{cases}S_m=ma_1+\dfrac{m(m-1)d}{2}\\\\S_p=pa_1+\dfrac{p(p-1)d}{2}\\\\S_m=S_p\end{cases}$$$$\therefore (m-p)a_1+\dfrac{(m+p-1)(m-p)d}{2}=0\\\therefore a_1+\dfrac{(m+p-1)d}{2}=0\\\\\begin{aligned}S_{m+p} &= (m+p)a_1+\dfrac{(m+p)(m+p-1)d}{2}\\\\&=(m+p)\times\left[a_1+\dfrac{(m+p)(m+p-1)d}{2}\right] \\\\&=(m+p) \times 0\\\\ &=0\end{aligned}$$**命题2**:若 $S_m=p,S_p=m\land m\ne p$  则有 $S_{m+p}=-(m+p)$证:$$\begin{cases}S_m=ma_1+\dfrac{m(m-1)d}{2}=p\\\\S_p=pa_1+\dfrac{p(p-1)d}{2}=m\end{cases}$$$$\therefore (m-p)a_1+\dfrac{(m+p-1)(m-p)d}{2}=p-m\\\therefore a_1+\dfrac{(m+p-1)d}{2}=-1\\\\\begin{aligned}S_{m+p} &= (m+p)a_1+\dfrac{(m+p)(m+p-1)d}{2}\\\\&=(m+p)\times\left[a_1+\dfrac{(m+p)(m+p-1)d}{2}\right] \\\\&=(m+p) \times (-1)\\\\ &=-(m+p)\end{aligned}$$## 等比数列等比数列通项公式$$a_n = a_1q^{n-1}$$有性质 $$a_1a_n=a_2a_{n-1}=\dots=a_ma_{n-m+1}\\$$等比数列前 $n$ 项和公式$$\begin{aligned}S_n=\begin{cases}na_1,&q=1\\\\\dfrac{a_1\left(1-q^n\right)}{1-q} = \dfrac{a_1-a_nq}{1-q},&q\ne 1\end{cases}\end{aligned}$$易知$$S_n = -\dfrac{a_1}{1-q}\cdot q^n+ \dfrac{a_1}{1-q}\\$$可知性质$$\begin{aligned}\dfrac{S_n}{S_m}=\begin{cases}\dfrac{n}{m},&q=1\\\\\dfrac{1-q^n}{1-q^m},&q\ne 1\end{cases}\end{aligned}$$$$\begin{aligned}S_{n+m} &= S_m + q^m S_n\\&= S_n + q^n S_m\end{aligned}$$当 $q\ne -1$ 时,有等比数列 $\left\{S_{(k+1)m}-S_{km}\right\}$1. 有递推式   $$   a_{n+1}=ca_{n}+d   $$      其中,$c\ne1,cd\ne 0$   **构造等比数列**求通项公式   即利用 $d=\dfrac{(1-c)d}{1-c}$ ,得   $$   a_{n+1}-\dfrac{d}{1-c} = c\left(a_n-\dfrac{d}{1-c}\right) \\   $$   则当 $a_n-\dfrac{d}{1-c}\ne 0$ 时,数列 $\left\{a_n-\dfrac{d}{1-c}\right\}$ 为等比数列   2. 有递推式   $$   a_{n+1}=ca_{n}+d   $$   其中,$c\ne1, cd\ne 0, n\ge 2$   **消常数项**求通项公式   $$   a_{n}=ca_{n-1}+d\\   a_{n+1}-a_n = c(a_n-a_{n-1})   $$   则当 $a_2\ne a_1$ 时,数列 $\left\{a_{n+1}-a_n\right\}$ 为等比数列         3. 有递推式   $$   a_{n+1}=ca_n+d^n   $$   其中,$c\ne d,cd\ne0$   **化归**求通项公式   $$   a_{n+1}-\dfrac{d^{n+1}}{d-c}=c\left(a_n-\dfrac{d^n}{d-c}\right)   $$   则数列 $\left\{a_n-\dfrac{d^n}{d-c}\right\}$ 为等比数列   或者两侧同除以 $d^{n+1}$ 化为情况1   或者两侧同除以 $c^{n+1}$ ,累加求通项   4. 有递推式    $$    a_{n+1}=ca_n+d^n+t    $$    其中,$cdt\ne0,c\ne 1$    **化归**求通项公式    $$    a_{n+1}-\dfrac{t}{1-c}=c\left(a_n-\dfrac{t}{1-c}\right)+d^n    $$    则转化为情况2    5. 有递推式    $$    a_{n+1}=pa_n+kq^{n+1}    $$    其中,$pqk\ne0,p\ne1,q\ne1$    可使用待定系数法,后略    也可以两边同除以 $q^{n+1}$ ,得    $$    \dfrac{a_{n+1}}{p^{n+1}} = \dfrac{p}{q}\times\dfrac{a_n}{q^n}+k    $$    进而化归为等比数列    还可以两边同除以 $p^{n+1}$ ,得    $$    \dfrac{a_{n+1}}{p^{n+1}} = \dfrac{a_n}{p^n}+k\left(\dfrac{q}{p}\right)^{n+1}    $$
]]>
+ 洛谷P6327 区间加区间sin和题解

题目链接:洛谷P6327区间加区间sin和 题解

题意:维护一个数据结构,支持

  1. 区间加 \(v\)
  2. 询问区间 \(\sum\limits_{i=1}^{r}\sina_i\)

注意到 \[\begin{aligned}\sin(a+x)&=\sin a\cos x+\cos a \sin x\\\cos(a+x)&=\cos a\cos x-\sin a \sin x\end{aligned}\] 直接维护即可两个量即可

注意点

  1. 更新的时候拿之前更新过的sin去更新cos,爆零。
  2. 不开long long,爆零。
  3. 不下传标记,爆零。
  4. 可以适当卡卡常(逃

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e5+15)#define ls(at) (at<<1)#define rs(at) (at<<1|1)namespace FastIO{    #define gc() readchar()    #define pc(a) putchar(a)    #define SIZ (int)(1e6+15)    char buf1[SIZ],*p1,*p2;    char readchar()    {        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);        return p1==p2?EOF:*p1++;    }    template<typename T>void read(T &k)    {        char ch=gc();T x=0,f=1;        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}        k=x*f;    }    template<typename T>void write(T k)    {        if(k<0){k=-k;pc('-');}        static T stk[66];T top=0;        do{stk[top++]=k%10,k/=10;}while(k);        while(top){pc(stk[--top]+'0');}    }}using namespace FastIO;// #define double long double// const double eps=1e-15;double sinx[N<<2],cosx[N<<2];int tag[N<<2];int n,m,a[N];void push_up(int at){    sinx[at]=sinx[ls(at)]+sinx[rs(at)];    cosx[at]=cosx[ls(at)]+cosx[rs(at)];}void build(int l,int r,int at){    if(l==r)    {        sinx[at]=sin(a[l]);        cosx[at]=cos(a[l]);        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void proc(int at,int k){    double sink=sin(k),cosk=cos(k);    double sina=sinx[at],cosa=cosx[at];    sinx[at]=sina*cosk+cosa*sink;    cosx[at]=cosa*cosk-sina*sink;    tag[at]+=k;}void push_down(int at){    int k=tag[at];    if(k)    {        proc(ls(at),k);        proc(rs(at),k);        tag[at]=0;    }}void update(int nl,int nr,int l,int r,int k,int at){    if(nl<=l&&r<=nr)    {        proc(at,k);        return;    }    int mid=(l+r)>>1;    push_down(at);    if(nl<=mid)update(nl,nr,l,mid,k,ls(at));    if(nr>mid)update(nl,nr,mid+1,r,k,rs(at));    push_up(at);}double query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)return sinx[at];    if(nl>r||nr<l)return 0;    push_down(at);    int mid=(l+r)>>1;    return query(nl,nr,l,mid,ls(at))+query(nl,nr,mid+1,r,rs(at));}signed main(){    // ios::sync_with_stdio(0);    // cin.tie(0);cout.tie(0);    // cout << fixed << setprecision(1);    // freopen("check.in","r",stdin);    // freopen("check.out","w",stdout);    read(n);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    int Q;read(Q);    int op,l,r,v;    while(Q--)    {        read(op);read(l);read(r);        if(op==1){read(v);update(l,r,1,n,v,1);}        else printf("%.1lf\n",query(l,r,1,n,1));    }    return 0;}
]]>
@@ -6962,7 +6964,7 @@ - 数学 + 数据结构 @@ -6971,11 +6973,11 @@ - 洛谷P6216 回文匹配 题解 - - /2022/05/25/luo-gu-p6216-hui-wen-pi-pei-ti-jie/ + 等差数列&等比数列小结 + + /2022/05/25/deng-chai-shu-lie-deng-bi-shu-lie-xiao-jie/ - 洛谷P6216 回文匹配 题解

题目链接:P6216 回文匹配

题意:对于一对字符串 $(s_1,s_2)$,若 $s_1$ 的长度为奇数的子串 $(l,r)$ 满足 $(l,r)$ 是回文的,那么 $s_1$ 的“分数”会增加 $s_2$ 在 $(l,r)$ 中出现的次数。

现在给出一对 $(s_1,s_2)$,请计算出 $s_1$ 的“分数”。

答案对 $2 ^ {32}$ 取模。

容易发现这就是个Manacher+KMP

Manacher用于找出奇回文串,KMP用于找出 $s_2$ 在 $(l,r)$ 中的出现位置

难点在于如何计算这个次数

对于每个极大回文串(长度小于 $|s_2|$ 的不考虑)

它一定包含了在同一回文中心的更小的回文串

abbba还包含了bbb,b

如果暴力去求解,时间复杂度为 $O(n^2)$

于是想到前缀和来加快运算

我们可以在匹配成功时将 $s_2$ 左端点位置标记为 $1$

考虑一个回文串x为任意字符

str:xxxxxxxpos:1234567

它对答案的贡献为sum_6-sum_0+sum_5-sum_1+sum_4-sum_2+sum_3-sum_3

即,sum_6+sum_5+sum4+sum_3-(sum3+sum_2+sum_1+sum_0)

注:要从r-m+1开始,否则会出现右边界超出范围的情况

还是个前缀和!

于是我们可以再做一次前缀和(即二次前缀和)

这样时间复杂度就压到 $O(n)$ 了

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(3e6+25)int n,m,r,mid;int fail[MAXN],sum[MAXN],len,p[MAXN];char a[MAXN],b[MAXN];unsigned ans;signed main(){    scanf("%lld%lld",&n,&m);    scanf("%s %s",a+1,b+1);    a[n+1]=b[m+1]='~'; // 本题的数据有问题所以要手动加个分隔符 QAQ    for(int i=2,j=0; i<=m; i++)    {        while(j&&b[i]!=b[j+1])j=fail[j];        if(b[i]==b[j+1])++j;        fail[i]=j;    }    for(int i=1,j=0; i<=n; i++)    {        while(j&&a[i]!=b[j+1])j=fail[j];        if(a[i]==b[j+1])++j;        if(j==m)++sum[i-m+1];    }    for(int i=1; i<=n; i++)        sum[i]+=sum[i-1];    for(int i=1; i<=n; i++)        sum[i]+=sum[i-1];    for(int i=1; i<=n; i++)    {        if(i<=r)p[i]=min(p[(mid<<1)-i],r-i+1);        while(a[i-p[i]]==a[i+p[i]])++p[i];        if(i+p[i]-1>=r)r=i+p[i]-1,mid=i;    }    // ------------------------------------    for(int i=1; i<=n; i++)    {        if(2*p[i]-1<m)continue;        int r=i+p[i]-1,l=i-p[i]+1;        l--;r=r-m+1;        int mid=(l+r)>>1;        ans+=sum[r]-sum[mid]-sum[((l+r)&1)?mid:mid-1]+sum[l-1];    }    printf("%u\n",ans);    return 0;}

参考文献

[1] ZCETHAN’S BLOGS - P6216 回文匹配

]]>
+ 等差数列&等比数列小结

高一自学的时候瞎总结写的(好吧我现在还是高一2022.5.7)

感觉丢在文件夹里吃灰没啥用,就放上来了

欢迎各位指出我的错误(我数学真的烂 \(😓\)


等差数列

等差数列通项公式

\[a_n = a_1+(n-1)d\]

\(S_n\) 表示等差数列 \(\{a_n\}\) 的 前 \(n\) 项和

\[\begin{aligned}S_n &=\sum\limits_{i=1}^{n}a_n\\&=na_1+\dfrac{n(n-1)}{2}d\\&=\dfrac{d}{2}n^2+\left(a_1-\dfrac{d}{2}\right)n\end{aligned}\]

易知 \[\begin{aligned}S_n&=\dfrac{n(a_1+a_n)}{2}\\&=\dfrac{n(a_m+a_{n-m+1})}{2}\end{aligned}\]

\[S_{2n-1}=(2n-1)a_n\\S_{2n}=n(a_n+a_{n+1})\]

\(\{a_n\}\) 为等差数列当且仅当

\[S_n=An^2+Bn+C \land C=0\]

命题1:若 \(S_m = S_p\land m\ne p\) 则有 \(S_{m+p}=0\)

证: \[\begin{cases}S_m=ma_1+\dfrac{m(m-1)d}{2}\\\\S_p=pa_1+\dfrac{p(p-1)d}{2}\\\\S_m=S_p\end{cases}\]

\[\therefore (m-p)a_1+\dfrac{(m+p-1)(m-p)d}{2}=0\\\therefore a_1+\dfrac{(m+p-1)d}{2}=0\\\\\begin{aligned}S_{m+p} &=(m+p)a_1+\dfrac{(m+p)(m+p-1)d}{2}\\\\&=(m+p)\times\left[a_1+\dfrac{(m+p)(m+p-1)d}{2}\right]\\\\&=(m+p) \times 0\\\\ &=0\end{aligned}\]

命题2:若 \(S_m=p,S_p=m\land m\ne p\) 则有 \(S_{m+p}=-(m+p)\)

证: \[\begin{cases}S_m=ma_1+\dfrac{m(m-1)d}{2}=p\\\\S_p=pa_1+\dfrac{p(p-1)d}{2}=m\end{cases}\]

\[\therefore (m-p)a_1+\dfrac{(m+p-1)(m-p)d}{2}=p-m\\\therefore a_1+\dfrac{(m+p-1)d}{2}=-1\\\\\begin{aligned}S_{m+p} &=(m+p)a_1+\dfrac{(m+p)(m+p-1)d}{2}\\\\&=(m+p)\times\left[a_1+\dfrac{(m+p)(m+p-1)d}{2}\right]\\\\&=(m+p) \times (-1)\\\\ &=-(m+p)\end{aligned}\]

等比数列

等比数列通项公式 \[a_n = a_1q^{n-1}\] 有性质 \[a_1a_n=a_2a_{n-1}=\dots=a_ma_{n-m+1}\\\]

等比数列前 \(n\) 项和公式 \[\begin{aligned}S_n=\begin{cases}na_1,&q=1\\\\\dfrac{a_1\left(1-q^n\right)}{1-q} = \dfrac{a_1-a_nq}{1-q},&q\ne 1\end{cases}\end{aligned}\] 易知 \[S_n = -\dfrac{a_1}{1-q}\cdot q^n+ \dfrac{a_1}{1-q}\\\] 可知性质 \[\begin{aligned}\dfrac{S_n}{S_m}=\begin{cases}\dfrac{n}{m},&q=1\\\\\dfrac{1-q^n}{1-q^m},&q\ne 1\end{cases}\end{aligned}\]

\[\begin{aligned}S_{n+m} &= S_m + q^m S_n\\&= S_n + q^n S_m\end{aligned}\]

\(q\ne -1\) 时,有等比数列 \(\left\{S_{(k+1)m}-S_{km}\right\}\)

  1. 有递推式 \[a_{n+1}=ca_{n}+d\]

    其中,\(c\ne1,cd\ne 0\)

    构造等比数列求通项公式

    即利用 \(d=\dfrac{(1-c)d}{1-c}\),得 \[a_{n+1}-\dfrac{d}{1-c} = c\left(a_n-\dfrac{d}{1-c}\right) \\\] 则当 \(a_n-\dfrac{d}{1-c}\ne0\) 时,数列 \(\left\{a_n-\dfrac{d}{1-c}\right\}\)为等比数列

  2. 有递推式 \[a_{n+1}=ca_{n}+d\] 其中,\(c\ne1, cd\ne 0, n\ge2\)

    消常数项求通项公式 \[a_{n}=ca_{n-1}+d\\a_{n+1}-a_n = c(a_n-a_{n-1})\] 则当 \(a_2\ne a_1\) 时,数列\(\left\{a_{n+1}-a_n\right\}\)为等比数列

  3. 有递推式 \[a_{n+1}=ca_n+d^n\] 其中,\(c\ne d,cd\ne0\)

    化归求通项公式 \[a_{n+1}-\dfrac{d^{n+1}}{d-c}=c\left(a_n-\dfrac{d^n}{d-c}\right)\] 则数列 \(\left\{a_n-\dfrac{d^n}{d-c}\right\}\)为等比数列

    或者两侧同除以 \(d^{n+1}\)化为情况1

    或者两侧同除以 \(c^{n+1}\),累加求通项

  4. 有递推式 \[a_{n+1}=ca_n+d^n+t\] 其中,\(cdt\ne0,c\ne 1\)

    化归求通项公式 \[a_{n+1}-\dfrac{t}{1-c}=c\left(a_n-\dfrac{t}{1-c}\right)+d^n\] 则转化为情况2

  5. 有递推式 \[a_{n+1}=pa_n+kq^{n+1}\] 其中,\(pqk\ne0,p\ne1,q\ne1\)

    可使用待定系数法,后略

    也可以两边同除以 \(q^{n+1}\) ,得\[\dfrac{a_{n+1}}{p^{n+1}} = \dfrac{p}{q}\times\dfrac{a_n}{q^n}+k\] 进而化归为等比数列

    还可以两边同除以 \(p^{n+1}\) ,得\[\dfrac{a_{n+1}}{p^{n+1}} =\dfrac{a_n}{p^n}+k\left(\dfrac{q}{p}\right)^{n+1}\]


相信一定也有和我一样的懒人不喜欢对着博客手敲的

我直接把上面那一堆的源码贴上来吧

## 等差数列等差数列通项公式$$a_n = a_1+(n-1)d$$$S_n$ 表示等差数列 $\{a_n\}$ 的 前 $n$ 项和$$\begin{aligned}S_n &= \sum\limits_{i=1}^{n}a_n\\&=na_1+\dfrac{n(n-1)}{2}d\\&=\dfrac{d}{2}n^2+\left(a_1-\dfrac{d}{2}\right)n\end{aligned}$$易知$$\begin{aligned}S_n&=\dfrac{n(a_1+a_n)}{2}\\&=\dfrac{n(a_m+a_{n-m+1})}{2}\end{aligned}$$$$S_{2n-1}=(2n-1)a_n\\S_{2n}=n(a_n+a_{n+1})$$$\{a_n\}$ 为等差数列当且仅当$$S_n=An^2+Bn+C \land C=0$$**命题1**:若 $S_m = S_p \land m\ne p$ 则有 $S_{m+p}=0$ 证:$$\begin{cases}S_m=ma_1+\dfrac{m(m-1)d}{2}\\\\S_p=pa_1+\dfrac{p(p-1)d}{2}\\\\S_m=S_p\end{cases}$$$$\therefore (m-p)a_1+\dfrac{(m+p-1)(m-p)d}{2}=0\\\therefore a_1+\dfrac{(m+p-1)d}{2}=0\\\\\begin{aligned}S_{m+p} &= (m+p)a_1+\dfrac{(m+p)(m+p-1)d}{2}\\\\&=(m+p)\times\left[a_1+\dfrac{(m+p)(m+p-1)d}{2}\right] \\\\&=(m+p) \times 0\\\\ &=0\end{aligned}$$**命题2**:若 $S_m=p,S_p=m\land m\ne p$  则有 $S_{m+p}=-(m+p)$证:$$\begin{cases}S_m=ma_1+\dfrac{m(m-1)d}{2}=p\\\\S_p=pa_1+\dfrac{p(p-1)d}{2}=m\end{cases}$$$$\therefore (m-p)a_1+\dfrac{(m+p-1)(m-p)d}{2}=p-m\\\therefore a_1+\dfrac{(m+p-1)d}{2}=-1\\\\\begin{aligned}S_{m+p} &= (m+p)a_1+\dfrac{(m+p)(m+p-1)d}{2}\\\\&=(m+p)\times\left[a_1+\dfrac{(m+p)(m+p-1)d}{2}\right] \\\\&=(m+p) \times (-1)\\\\ &=-(m+p)\end{aligned}$$## 等比数列等比数列通项公式$$a_n = a_1q^{n-1}$$有性质 $$a_1a_n=a_2a_{n-1}=\dots=a_ma_{n-m+1}\\$$等比数列前 $n$ 项和公式$$\begin{aligned}S_n=\begin{cases}na_1,&q=1\\\\\dfrac{a_1\left(1-q^n\right)}{1-q} = \dfrac{a_1-a_nq}{1-q},&q\ne 1\end{cases}\end{aligned}$$易知$$S_n = -\dfrac{a_1}{1-q}\cdot q^n+ \dfrac{a_1}{1-q}\\$$可知性质$$\begin{aligned}\dfrac{S_n}{S_m}=\begin{cases}\dfrac{n}{m},&q=1\\\\\dfrac{1-q^n}{1-q^m},&q\ne 1\end{cases}\end{aligned}$$$$\begin{aligned}S_{n+m} &= S_m + q^m S_n\\&= S_n + q^n S_m\end{aligned}$$当 $q\ne -1$ 时,有等比数列 $\left\{S_{(k+1)m}-S_{km}\right\}$1. 有递推式   $$   a_{n+1}=ca_{n}+d   $$      其中,$c\ne1,cd\ne 0$   **构造等比数列**求通项公式   即利用 $d=\dfrac{(1-c)d}{1-c}$ ,得   $$   a_{n+1}-\dfrac{d}{1-c} = c\left(a_n-\dfrac{d}{1-c}\right) \\   $$   则当 $a_n-\dfrac{d}{1-c}\ne 0$ 时,数列 $\left\{a_n-\dfrac{d}{1-c}\right\}$ 为等比数列   2. 有递推式   $$   a_{n+1}=ca_{n}+d   $$   其中,$c\ne1, cd\ne 0, n\ge 2$   **消常数项**求通项公式   $$   a_{n}=ca_{n-1}+d\\   a_{n+1}-a_n = c(a_n-a_{n-1})   $$   则当 $a_2\ne a_1$ 时,数列 $\left\{a_{n+1}-a_n\right\}$ 为等比数列         3. 有递推式   $$   a_{n+1}=ca_n+d^n   $$   其中,$c\ne d,cd\ne0$   **化归**求通项公式   $$   a_{n+1}-\dfrac{d^{n+1}}{d-c}=c\left(a_n-\dfrac{d^n}{d-c}\right)   $$   则数列 $\left\{a_n-\dfrac{d^n}{d-c}\right\}$ 为等比数列   或者两侧同除以 $d^{n+1}$ 化为情况1   或者两侧同除以 $c^{n+1}$ ,累加求通项   4. 有递推式    $$    a_{n+1}=ca_n+d^n+t    $$    其中,$cdt\ne0,c\ne 1$    **化归**求通项公式    $$    a_{n+1}-\dfrac{t}{1-c}=c\left(a_n-\dfrac{t}{1-c}\right)+d^n    $$    则转化为情况2    5. 有递推式    $$    a_{n+1}=pa_n+kq^{n+1}    $$    其中,$pqk\ne0,p\ne1,q\ne1$    可使用待定系数法,后略    也可以两边同除以 $q^{n+1}$ ,得    $$    \dfrac{a_{n+1}}{p^{n+1}} = \dfrac{p}{q}\times\dfrac{a_n}{q^n}+k    $$    进而化归为等比数列    还可以两边同除以 $p^{n+1}$ ,得    $$    \dfrac{a_{n+1}}{p^{n+1}} = \dfrac{a_n}{p^n}+k\left(\dfrac{q}{p}\right)^{n+1}    $$
]]>
@@ -6987,9 +6989,7 @@ - 算法 - - 字符串 + 数学 @@ -7002,7 +7002,7 @@ /2022/05/25/xian-duan-shu-kong-jian-kai-4-bei-de-yuan-yin/ - 线段树空间开4倍的原因

如果证明有错欢迎指出。

对于长为 $n$ 的序列,显然以其构建的线段树有 $n$ 个叶子节点

此时线段树的高度为 $k=\left\lceil{\log_2 n}\right\rceil+1$ (第一层的高度为 $1$ )

通过等比数列求和公式

可知

至多有 $\dfrac{1\times(1-2^k)}{1-2} = 2^k-1$ 个结点

即有 $2^{\left\lceil{\log_2 n}\right\rceil+1}-1$ 个结点

根据

可知

  • 当 $\log_2 n$ 为整数时,结点数为 $2n-1$ 个节点

  • 当 $\log_2 n$ 不为整数时,结点数为 $4 \times 2^{\left\lfloor{\log_2 n}\right\rfloor}-1$

    $\because \left\lfloor{\log_2 n}\right\rfloor < \log_2 n$

    $\therefore 4 \times 2^{\left\lfloor{\log_2 n}\right\rfloor}-1 < 4n-1 < 4n$

很多时候 $n$ 并不恰好为 $2^k$ ,因此开 $4$ 倍空间正好

值得注意的是,对于 ls(at) = at*2 的这种写法

一定要避免在叶子结点调用 ls(at)

因为此时有可能会越界到 $8n$ 的位置

这里贴一个线段树1的代码 卡了点常啥的,无视它(逃

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)int n,Q;int a[N];struct node{    int sum,tag;}t[N<<2];#define ls(at) (at<<1)#define rs(at) (at<<1|1)void push_up(int at){    t[at].sum=t[ls(at)].sum+t[rs(at)].sum;}void build(int l,int r,int at){    if(l==r)    {        t[at].sum=a[l];        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void proc(int l,int r,int k,int at){    t[at].sum+=k*(r-l+1);    t[at].tag+=k;}void push_down(int l,int r,int at){    if(t[at].tag)    {        int mid=(l+r)>>1;        proc(l,mid,t[at].tag,ls(at));        proc(mid+1,r,t[at].tag,rs(at));    }    t[at].tag=0;}void update(int nl,int nr,int l,int r,int k,int at){    if(nl<=l&&r<=nr)    {        t[at].sum+=k*(r-l+1);        t[at].tag+=k;        return;    }    int mid=(l+r)>>1;    push_down(l,r,at);    if(nl<=mid)update(nl,nr,l,mid,k,ls(at));    if(nr>mid)update(nl,nr,mid+1,r,k,rs(at));    push_up(at);}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        {return t[at].sum;}    int mid=(l+r)>>1,res=0;    push_down(l,r,at);    if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));    if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));    return res;}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    while(Q--)    {        int op,l,r,k;        read(op);        if(op==1)        {            read(l);read(r);read(k);            update(l,r,1,n,k,1);        }else        {            read(l);read(r);            write(query(l,r,1,n,1));pc('\n');        }    }    return 0;}
]]>
+ 线段树空间开4倍的原因

如果证明有错欢迎指出。

对于长为 \(n\)的序列,显然以其构建的线段树有 \(n\)个叶子节点

此时线段树的高度为 \(k=\left\lceil{\log_2n}\right\rceil+1\) (第一层的高度为 \(1\) )

通过等比数列求和公式 \[\begin{aligned}S_n=\begin{cases}na_1,&q=1\\\\\dfrac{a_1\left(1-q^n\right)}{1-q}= \dfrac{a_1-a_nq}{1-q},&q\ne 1\end{cases}\end{aligned}\] 可知

至多有 \(\dfrac{1\times(1-2^k)}{1-2} =2^k-1\) 个结点

即有 \(2^{\left\lceil{\log_2n}\right\rceil+1}-1\) 个结点

根据 \[\begin{aligned}&\left\lceil{x}\right\rceil-\left\lfloor{x}\right\rfloor=\begin{cases}0,&\text{if } x \in \mathbb{Z},\\\\1,&\text{if }x \notin \mathbb{Z}.\end{cases}\end{aligned}\] 可知

  • \(\log_2 n\)为整数时,结点数为 \(2n-1\)个节点

  • \(\log_2 n\)不为整数时,结点数为 \(4 \times2^{\left\lfloor{\log_2 n}\right\rfloor}-1\)

    \(\because \left\lfloor{\log_2n}\right\rfloor < \log_2 n\)

    \(\therefore 4 \times2^{\left\lfloor{\log_2 n}\right\rfloor}-1 < 4n-1 <4n\)

很多时候 \(n\) 并不恰好为 \(2^k\) ,因此开 \(4\) 倍空间正好

值得注意的是,对于 ls(at) = at*2 的这种写法

一定要避免在叶子结点调用 ls(at)

因为此时有可能会越界到 \(8n\)的位置

这里贴一个线段树1的代码卡了点常啥的,无视它(逃

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)int n,Q;int a[N];struct node{    int sum,tag;}t[N<<2];#define ls(at) (at<<1)#define rs(at) (at<<1|1)void push_up(int at){    t[at].sum=t[ls(at)].sum+t[rs(at)].sum;}void build(int l,int r,int at){    if(l==r)    {        t[at].sum=a[l];        return;    }    int mid=(l+r)>>1;    build(l,mid,ls(at));    build(mid+1,r,rs(at));    push_up(at);}void proc(int l,int r,int k,int at){    t[at].sum+=k*(r-l+1);    t[at].tag+=k;}void push_down(int l,int r,int at){    if(t[at].tag)    {        int mid=(l+r)>>1;        proc(l,mid,t[at].tag,ls(at));        proc(mid+1,r,t[at].tag,rs(at));    }    t[at].tag=0;}void update(int nl,int nr,int l,int r,int k,int at){    if(nl<=l&&r<=nr)    {        t[at].sum+=k*(r-l+1);        t[at].tag+=k;        return;    }    int mid=(l+r)>>1;    push_down(l,r,at);    if(nl<=mid)update(nl,nr,l,mid,k,ls(at));    if(nr>mid)update(nl,nr,mid+1,r,k,rs(at));    push_up(at);}int query(int nl,int nr,int l,int r,int at){    if(nl<=l&&r<=nr)        {return t[at].sum;}    int mid=(l+r)>>1,res=0;    push_down(l,r,at);    if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));    if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));    return res;}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    build(1,n,1);    while(Q--)    {        int op,l,r,k;        read(op);        if(op==1)        {            read(l);read(r);read(k);            update(l,r,1,n,k,1);        }else        {            read(l);read(r);            write(query(l,r,1,n,1));pc('\n');        }    }    return 0;}
]]>
@@ -7029,7 +7029,7 @@ /2022/05/25/zhu-ding-li/ - 主定理

证明先不写

将一个规模为 $n$ 的问题,通过分治得到 $a$ 个规模为 $n/b$ 的子问题,每个递归带来的额外计算为 $f(n)$ ,则有

$T(n)=aT(n/b)+f(n)$

其中 $a,b$ 为常数, $n\in \mathbb{N}^*,a\ge 1,b > 1,n/b$ 解释为 $\left\lfloor{\dfrac{n}{b}}\right\rfloor$ 或 $\left\lceil\dfrac{n}{b}\right\rceil$

  1. 若 $\exists \epsilon > 0$ 使得 $f(n)=O(n^{\log_b{a}-\epsilon})$,则 $T(n)=\Theta(n^{\log_ba})$
  2. 若 $\exists \epsilon \ge 0$ 使得 $f(n)=\Theta(n^{\log_ba}\log^\epsilon n)$,则 $T(n)=\Theta(n^{\log_ba}\log^{\epsilon+1}n)$
  3. 若 $\exists \epsilon>0$ 使得 $f(n)=\Omega(n^{\log_ba+\epsilon})$ ,且 $\exists c<1 , \forall n \gg 1$ 使得 $af(n/b)\le cf(n)$ ,则 $T(n)=\Theta(f(n))$

其中 $\epsilon,c$ 均为常数

常用结论

$T(n) = 2T(\frac{n}{2}) + \Theta(n) = \Theta(n \log n)$

$T(n) = T(\frac{n}{2}) + \Theta (n) = \Theta (n)$

$T(n) = T(\frac{n}{2}) +\Theta (1) = \Theta(\log n)$

$T(n) = 2T(\frac{n}{2}) + \Theta(n \sqrt{n}) = \Theta ( n \sqrt{n} )$

]]>
+ 主定理

证明先不写

将一个规模为 \(n\)的问题,通过分治得到 \(a\) 个规模为\(n/b\)的子问题,每个递归带来的额外计算为 \(f(n)\) ,则有

\(T(n)=aT(n/b)+f(n)\)

其中 \(a,b\) 为常数, \(n\in \mathbb{N}^*,a\ge 1,b > 1,n/b\)解释为 \(\left\lfloor{\dfrac{n}{b}}\right\rfloor\)或 \(\left\lceil\dfrac{n}{b}\right\rceil\)

  1. \(\exists \epsilon > 0\) 使得\(f(n)=O(n^{\log_b{a}-\epsilon})\),则\(T(n)=\Theta(n^{\log_ba})\)
  2. \(\exists \epsilon \ge 0\) 使得\(f(n)=\Theta(n^{\log_ba}\log^\epsilonn)\),则 \(T(n)=\Theta(n^{\log_ba}\log^{\epsilon+1}n)\)
  3. \(\exists \epsilon>0\) 使得\(f(n)=\Omega(n^{\log_ba+\epsilon})\),且 \(\exists c<1 , \forall n \gg1\) 使得 \(af(n/b)\le cf(n)\),则 \(T(n)=\Theta(f(n))\)

其中 \(\epsilon,c\) 均为常数

常用结论

\(T(n) = 2T(\frac{n}{2}) + \Theta(n) =\Theta(n \log n)\)

\(T(n) = T(\frac{n}{2}) + \Theta (n) =\Theta (n)\)

\(T(n) = T(\frac{n}{2}) +\Theta (1) =\Theta(\log n)\)

\(T(n) = 2T(\frac{n}{2}) + \Theta(n\sqrt{n}) = \Theta ( n \sqrt{n} )\)

]]>
@@ -7054,7 +7054,7 @@ /2022/04/29/ji-suan-ji-he-shi-yong-cha-ji-ji-suan-zhi-xian-jiao-dian/ - [计算几何] 使用叉积计算直线交点

使用叉积计算直线交点

已知直线 $AB$ 和 $CD$ ,其交点为 $P$ , 求 $P$ 的坐标

计算几何中一般存储向量 $\vec{p},\vec{v}$ 表示起点的向量表示和方向向量

例如直线 $AB$ 一般存储为 $\overset{\longrightarrow}{OA},\overset{\longrightarrow}{AB}$

const double eps=1e-10;struct vct{double x,y;}p[N];#define pf(x) ((x)*(x))vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}struct line{    vct p,way;    double k;    void mkline(vct a,vct b)    {        p=a;way=b;        k=atan2(b.y,b.x); // 部分题会用到的极角排序,并不是必不可缺的    }    void mk(double x1,double y1,double x2,double y2)    {        mkline({x1,y1},{x2-x1,y2-y1});    }}a[N];

因此,可以得到下图

其中 $v_1,v_2$ 为方向向量

则 $P=p_1 + tv_1$

$\because (p_1+tv_1-p_2) \times v_2 = 0$

$\therefore t = \dfrac{(p_2-p_1)\times v_2}{v_1\times v_2}$

则代入 $P=p_1 + tv_1$ 即可

代码如下

vct intersect(line a,line b){    double x=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*x;}

完整代码摘自这里 其实是懒得再打一遍 qwq

]]>
+ [计算几何]使用叉积计算直线交点

使用叉积计算直线交点

已知直线 \(AB\)\(CD\) ,其交点为 \(P\) , 求 \(P\) 的坐标

计算几何中一般存储向量 \(\vec{p},\vec{v}\)表示起点的向量表示和方向向量

例如直线 \(AB\) 一般存储为 \(\overset{\longrightarrow}{OA},\overset{\longrightarrow}{AB}\)

const double eps=1e-10;struct vct{double x,y;}p[N];#define pf(x) ((x)*(x))vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}struct line{    vct p,way;    double k;    void mkline(vct a,vct b)    {        p=a;way=b;        k=atan2(b.y,b.x); // 部分题会用到的极角排序,并不是必不可缺的    }    void mk(double x1,double y1,double x2,double y2)    {        mkline({x1,y1},{x2-x1,y2-y1});    }}a[N];

因此,可以得到下图

其中 \(v_1,v_2\) 为方向向量

\(P=p_1 + tv_1\)

\(\because (p_1+tv_1-p_2) \times v_2 =0\)

\(\therefore t = \dfrac{(p_2-p_1)\timesv_2}{v_1\times v_2}\)

则代入 \(P=p_1 + tv_1\) 即可

代码如下

vct intersect(line a,line b){    double x=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*x;}

完整代码摘自这里其实是懒得再打一遍 qwq

]]>
@@ -7081,7 +7081,7 @@ /2022/04/28/ban-ping-mian-jiao-de-jiao-ji-wen-ti-poj2451-uyuw-s-concert-ti-jie/ - 半平面交的交集问题&POJ2451 Uyuw’s Concert 题解

前言

初学半平面交,就遇到了这样巨大的坑,也是挺无语…


判断交集为空的情况

poj2451 Uyuw’s Concert

Description

Prince Remmarguts solved the CHESS puzzle successfully. As an award, Uyuw planned to hold a concert in a huge piazza named after its great designer Ihsnayish.

The piazza in UDF - United Delta of Freedom’s downtown was a square of [0, 10000] * [0, 10000]. Some basket chairs had been standing there for years, but in a terrible mess. Look at the following graph.

In this case we have three chairs, and the audiences face the direction as what arrows have pointed out. The chairs were old-aged and too heavy to be moved. Princess Remmarguts told the piazza’s current owner Mr. UW, to build a large stage inside it. The stage must be as large as possible, but he should also make sure the audience in every position of every chair would be able to see the stage without turning aside (that means the stage is in the forward direction of their own).

To make it simple, the stage could be set highly enough to make sure even thousands of chairs were in front of you, as long as you were facing the stage, you would be able to see the singer / pianist – Uyuw.

Being a mad idolater, can you tell them the maximal size of the stage?

Input

In the first line, there’s a single non-negative integer N (N <= 20000), denoting the number of basket chairs. Each of the following lines contains four floating numbers x1, y1, x2, y2, which means there’s a basket chair on the line segment of (x1, y1) – (x2, y2), and facing to its LEFT (That a point (x, y) is at the LEFT side of this segment means that (x – x1) (y – y2) – (x – x2) (y – y1) >= 0).

Output

Output a single floating number, rounded to 1 digit after the decimal point. This is the maximal area of the stage.

Sample Input

310000 10000 0 500010000 5000 5000 100000 5000 5000 0

Sample Output

54166666.7

Hint

Sample input is the same as the graph above, while the correct solution for it is as below:


I suggest that you use Extended in pascal and long double in C / C++ to avoid precision error. But the standard program only uses double.

显然是半平面交的板子题

这里我们不讲半平面交,我们只讲一些特例

下面有几个比较特殊的情况

1.向量共线

hack1:(存在向量相反且共线,且交集不是给出向量围成的多边形)

input:39 8 9 03 3 3 43 2 1 7output:0.0

hack2:(存在向量相反且共线,且交集为空)

input:21 1 2 22 2 1 1output:0.0

至少hack了我之前的那几份代码(逃

对于这种给出向量的题目,首先要注意加入构成外边界的4个向量

注意要逆时针加入(题目说了是向量左侧的平面)

我们寻求适用更为广泛的方法

对于方向相同的共线向量(下面说的极角均指atan2(y,x)

  1. 如果极角大于 $0$ 小于 $\pi$ ,就取较左的那个
  2. 如果极角小于 $0$ 大于 $-\pi$ ,就取较右的那个
  3. 如果极角等于 $0$ 就取较上的那个
  4. 如果极角等于 $\pi$ 就取较下的那个

这么一听好复杂哇,其实只要一个叉积判断下就好了

预处理一下这些烦人的共线向量,就可以放心halfplane()

具体看后面的代码,不急(逃

2.存在零向量

hack:(这种情况会卡极角排序)

input:11 1 1 1output:100000000.0

这种情况一般题目会避免出现,但是不排除有的出题人搞了这种

其实这个情况容易判断,只要在读入的时候特判一下就好了

3.其他hack

目前没有发现什么其他hack,欢迎大家来hack我

4.AC代码

poj请选择C++提交,g++提交的话容易作死(poj的g++要用%f输入输出double,pojC++用%lf)

这个出题人不写spj十分恶心人(无意冒犯),反正我搞了好久(6h+) $😓$

upd.2022-07-25 代码2不知道为什么在POJ上CE了(之前用g++交的)

现在更新了一下原来的代码(现在是下面的代码1),用了相对比较老的语法,可以AC(Run ID:23615953)

代码1:(请使用C++提交)

#include <cstdio>#include <iostream>#include <vector>#include <algorithm>#include <cmath>#include <iomanip>using namespace std;#define int long long// #define double long double#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)const double eps=1e-10;struct vct{double x,y;vct(){}vct(double x,double y):x(x),y(y){}}p[N];#define pf(x) ((x)*(x))vct operator+(vct a,vct b){return vct(a.x+b.x,a.y+b.y);}vct operator-(vct a,vct b){return vct(a.x-b.x,a.y-b.y);}vct operator*(vct a,double b){return vct(a.x*b,a.y*b);}vct operator/(vct a,double b){return vct(a.x/b,a.y/b);}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}struct line{    vct p,way;    double k;    line(){} line(vct p,vct way,double k) : p(p),way(way),k(k){}    void mkline(vct a,vct b)    {        p=a;way=b;        k=atan2(b.y,b.x);    }    void mk(double x1,double y1,double x2,double y2)    {        mkline(vct(x1,y1),vct(x2-x1,y2-y1));    }}a[N];int dcmp(double x){    if(fabs(x)<=eps)return 0;    return x>eps?1:-1;}bool operator<(line a,line b){    int d=dcmp(a.k-b.k);    if(d==0)return cross(b.p-a.p,b.way)>0;    return d<0;}bool onright(vct a,line b){return dcmp(cross(b.way,a-b.p))<0;}vct intersect(line a,line b){    double x=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*x;}int st,en;line que[N];bool halfplane(int n){    que[st=en=1]=a[1];    for(int i=2; i<=n; i++)    {        while(st<en&&onright(p[en],a[i]))--en;        while(st<en&&onright(p[st+1],a[i]))++st;        que[++en]=a[i];        if(st<en)p[en]=intersect(que[en-1],que[en]);    }    while(st<en&&onright(p[en],que[st]))--en;    if(en-st<=1)return 0;    p[st]=intersect(que[st],que[en]);    return 1;}double calc(vct *a,int n){    double res=0;    for(int i=1; i<n; i++)        res+=cross(a[i],a[i+1]);    res+=cross(a[n],a[1]);    if(fabs(res/=2)<=eps)res=0;    return res;}signed main(){    int n,o=0,oo=0;    scanf("%lld",&n);    while(n--)    {        double x1,x2,y1,y2;        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);        if(x1==x2&&y1==y2)continue;        a[++oo].mk(x1,y1,x2,y2);    }    a[++oo].mk(0,0,10000,0);    a[++oo].mk(10000,0,10000,10000);    a[++oo].mk(10000,10000,0,10000);    a[++oo].mk(0,10000,0,0);    sort(a+1,a+1+oo);    a[0].k=a[1].k-1;    for(int i=1; i<=oo; i++)        if(i==1||dcmp(a[i-1].k-a[i].k))            a[++o]=a[i];    if(!halfplane(o))puts("0.0");    else printf("%.1lf\n",calc(p+st-1,en-st+1));    return 0;}

代码2:(原来g++可以AC,现在会在POJ上会CE)

#include <cstdio>#include <iostream>#include <vector>#include <algorithm>#include <cmath>#include <iomanip>using namespace std;#define int long long// #define double long double#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)const double eps=1e-10;struct vct{double x,y;}p[N];#define pf(x) ((x)*(x))vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}struct line{    vct p,way;    double k;    void mkline(vct a,vct b)    {        p=a;way=b;        k=atan2(b.y,b.x);    }    void mk(double x1,double y1,double x2,double y2)    {        mkline({x1,y1},{x2-x1,y2-y1});    }}a[N];int dcmp(double x){    if(fabs(x)<=eps)return 0;    return x>eps?1:-1;}bool operator<(line a,line b){    int d=dcmp(a.k-b.k);    if(d==0)return cross(b.p-a.p,b.way)>0;    return d<0;}bool onright(vct a,line b){return dcmp(cross(b.way,a-b.p))<0;}vct intersect(line a,line b){    double x=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*x;}int st,en;line que[N];bool halfplane(int n){    que[st=en=1]=a[1];    for(int i=2; i<=n; i++)    {        while(st<en&&onright(p[en],a[i]))--en;        while(st<en&&onright(p[st+1],a[i]))++st;        que[++en]=a[i];        if(st<en)p[en]=intersect(que[en-1],que[en]);    }    while(st<en&&onright(p[en],que[st]))--en;    if(en-st<=1)return 0;    p[st]=intersect(que[st],que[en]);    return 1;}double calc(vct *a,int n){    double res=0;    for(int i=1; i<n; i++)        res+=cross(a[i],a[i+1]);    res+=cross(a[n],a[1]);    if(fabs(res/=2)<=eps)res=0;    return res;}signed main(){    int n,o=0,oo=0;    scanf("%lld",&n);    while(n--)    {        double x1,x2,y1,y2;        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);        if(x1==x2&&y1==y2)continue;        a[++oo].mk(x1,y1,x2,y2);    }    a[++oo].mk(0,0,10000,0);    a[++oo].mk(10000,0,10000,10000);    a[++oo].mk(10000,10000,0,10000);    a[++oo].mk(0,10000,0,0);    sort(a+1,a+1+oo);    a[0].k=a[1].k-1;    for(int i=1; i<=oo; i++)        if(i==1||dcmp(a[i-1].k-a[i].k))            a[++o]=a[i];    if(!halfplane(o))puts("0.0");    else printf("%.1f\n",calc(p+st-1,en-st+1));    return 0;}

总结

半平面交、旋转卡壳,这俩绝对是我目前见过最毒瘤的算法了 $😅$

啥?你说仙人掌?这不是毒瘤本瘤吗(逃

]]>
+ 半平面交的交集问题&POJ2451Uyuw's Concert 题解

前言

初学半平面交,就遇到了这样巨大的坑,也是挺无语...


判断交集为空的情况

poj2451 Uyuw'sConcert

Description

Prince Remmarguts solved the CHESS puzzle successfully. As an award,Uyuw planned to hold a concert in a huge piazza named after its greatdesigner Ihsnayish.

The piazza in UDF - United Delta of Freedom’s downtown was a squareof [0, 10000] * [0, 10000]. Some basket chairs had been standing therefor years, but in a terrible mess. Look at the following graph. In this case we have three chairs, and the audiences face the directionas what arrows have pointed out. The chairs were old-aged and too heavyto be moved. Princess Remmarguts told the piazza's current owner Mr. UW,to build a large stage inside it. The stage must be as large aspossible, but he should also make sure the audience in every position ofevery chair would be able to see the stage without turning aside (thatmeans the stage is in the forward direction of their own).

To make it simple, the stage could be set highly enough to make sureeven thousands of chairs were in front of you, as long as you werefacing the stage, you would be able to see the singer / pianist –Uyuw.

Being a mad idolater, can you tell them the maximal size of thestage?

Input

In the first line, there's a single non-negative integer N (N <=20000), denoting the number of basket chairs. Each of the followinglines contains four floating numbers x1, y1, x2, y2, which means there’sa basket chair on the line segment of (x1, y1) – (x2, y2), and facing toits LEFT (That a point (x, y) is at the LEFT side of this segment meansthat (x – x1) * (y – y2) – (x – x2) * (y – y1) >= 0).

Output

Output a single floating number, rounded to 1 digit after the decimalpoint. This is the maximal area of the stage.

Sample Input

310000 10000 0 500010000 5000 5000 100000 5000 5000 0

Sample Output

54166666.7

Hint

Sample input is the same as the graph above, while the correctsolution for it is as below:

I suggest that you use Extended in pascal and long double in C / C++ toavoid precision error. But the standard program only uses double.

显然是半平面交的板子题

这里我们不讲半平面交,我们只讲一些特例

下面有几个比较特殊的情况

1.向量共线

hack1:(存在向量相反且共线,且交集不是给出向量围成的多边形)

input:39 8 9 03 3 3 43 2 1 7output:0.0

hack2:(存在向量相反且共线,且交集为空)

input:21 1 2 22 2 1 1output:0.0

至少hack了我之前的那几份代码(逃

对于这种给出向量的题目,首先要注意加入构成外边界的4个向量

注意要逆时针加入(题目说了是向量左侧的平面)

我们寻求适用更为广泛的方法

对于方向相同的共线向量(下面说的极角均指atan2(y,x)

  1. 如果极角大于 \(0\) 小于 \(\pi\) ,就取较左的那个
  2. 如果极角小于 \(0\) 大于 \(-\pi\) ,就取较右的那个
  3. 如果极角等于 \(0\)就取较上的那个
  4. 如果极角等于 \(\pi\)就取较下的那个

这么一听好复杂哇,其实只要一个叉积判断下就好了

预处理一下这些烦人的共线向量,就可以放心halfplane()

具体看后面的代码,不急(逃

2.存在零向量

hack:(这种情况会卡极角排序)

input:11 1 1 1output:100000000.0

这种情况一般题目会避免出现,但是不排除有的出题人搞了这种

其实这个情况容易判断,只要在读入的时候特判一下就好了

3.其他hack

目前没有发现什么其他hack,欢迎大家来hack我

4.AC代码

poj请选择C++提交,g++提交的话容易作死(poj的g++要用%f输入输出double,pojC++用%lf)

这个出题人不写spj十分恶心人(无意冒犯),反正我搞了好久(6h+) \(😓\)

upd.2022-07-25代码2不知道为什么在POJ上CE了(之前用g++交的)

现在更新了一下原来的代码(现在是下面的代码1),用了相对比较老的语法,可以AC(Run ID:23615953)

代码1:(请使用C++提交)

#include <cstdio>#include <iostream>#include <vector>#include <algorithm>#include <cmath>#include <iomanip>using namespace std;#define int long long// #define double long double#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)const double eps=1e-10;struct vct{double x,y;vct(){}vct(double x,double y):x(x),y(y){}}p[N];#define pf(x) ((x)*(x))vct operator+(vct a,vct b){return vct(a.x+b.x,a.y+b.y);}vct operator-(vct a,vct b){return vct(a.x-b.x,a.y-b.y);}vct operator*(vct a,double b){return vct(a.x*b,a.y*b);}vct operator/(vct a,double b){return vct(a.x/b,a.y/b);}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}struct line{    vct p,way;    double k;    line(){} line(vct p,vct way,double k) : p(p),way(way),k(k){}    void mkline(vct a,vct b)    {        p=a;way=b;        k=atan2(b.y,b.x);    }    void mk(double x1,double y1,double x2,double y2)    {        mkline(vct(x1,y1),vct(x2-x1,y2-y1));    }}a[N];int dcmp(double x){    if(fabs(x)<=eps)return 0;    return x>eps?1:-1;}bool operator<(line a,line b){    int d=dcmp(a.k-b.k);    if(d==0)return cross(b.p-a.p,b.way)>0;    return d<0;}bool onright(vct a,line b){return dcmp(cross(b.way,a-b.p))<0;}vct intersect(line a,line b){    double x=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*x;}int st,en;line que[N];bool halfplane(int n){    que[st=en=1]=a[1];    for(int i=2; i<=n; i++)    {        while(st<en&&onright(p[en],a[i]))--en;        while(st<en&&onright(p[st+1],a[i]))++st;        que[++en]=a[i];        if(st<en)p[en]=intersect(que[en-1],que[en]);    }    while(st<en&&onright(p[en],que[st]))--en;    if(en-st<=1)return 0;    p[st]=intersect(que[st],que[en]);    return 1;}double calc(vct *a,int n){    double res=0;    for(int i=1; i<n; i++)        res+=cross(a[i],a[i+1]);    res+=cross(a[n],a[1]);    if(fabs(res/=2)<=eps)res=0;    return res;}signed main(){    int n,o=0,oo=0;    scanf("%lld",&n);    while(n--)    {        double x1,x2,y1,y2;        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);        if(x1==x2&&y1==y2)continue;        a[++oo].mk(x1,y1,x2,y2);    }    a[++oo].mk(0,0,10000,0);    a[++oo].mk(10000,0,10000,10000);    a[++oo].mk(10000,10000,0,10000);    a[++oo].mk(0,10000,0,0);    sort(a+1,a+1+oo);    a[0].k=a[1].k-1;    for(int i=1; i<=oo; i++)        if(i==1||dcmp(a[i-1].k-a[i].k))            a[++o]=a[i];    if(!halfplane(o))puts("0.0");    else printf("%.1lf\n",calc(p+st-1,en-st+1));    return 0;}

代码2:(原来g++可以AC,现在会在POJ上会CE)

#include <cstdio>#include <iostream>#include <vector>#include <algorithm>#include <cmath>#include <iomanip>using namespace std;#define int long long// #define double long double#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)const double eps=1e-10;struct vct{double x,y;}p[N];#define pf(x) ((x)*(x))vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}struct line{    vct p,way;    double k;    void mkline(vct a,vct b)    {        p=a;way=b;        k=atan2(b.y,b.x);    }    void mk(double x1,double y1,double x2,double y2)    {        mkline({x1,y1},{x2-x1,y2-y1});    }}a[N];int dcmp(double x){    if(fabs(x)<=eps)return 0;    return x>eps?1:-1;}bool operator<(line a,line b){    int d=dcmp(a.k-b.k);    if(d==0)return cross(b.p-a.p,b.way)>0;    return d<0;}bool onright(vct a,line b){return dcmp(cross(b.way,a-b.p))<0;}vct intersect(line a,line b){    double x=cross(b.way,a.p-b.p)/cross(a.way,b.way);    return a.p+a.way*x;}int st,en;line que[N];bool halfplane(int n){    que[st=en=1]=a[1];    for(int i=2; i<=n; i++)    {        while(st<en&&onright(p[en],a[i]))--en;        while(st<en&&onright(p[st+1],a[i]))++st;        que[++en]=a[i];        if(st<en)p[en]=intersect(que[en-1],que[en]);    }    while(st<en&&onright(p[en],que[st]))--en;    if(en-st<=1)return 0;    p[st]=intersect(que[st],que[en]);    return 1;}double calc(vct *a,int n){    double res=0;    for(int i=1; i<n; i++)        res+=cross(a[i],a[i+1]);    res+=cross(a[n],a[1]);    if(fabs(res/=2)<=eps)res=0;    return res;}signed main(){    int n,o=0,oo=0;    scanf("%lld",&n);    while(n--)    {        double x1,x2,y1,y2;        scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);        if(x1==x2&&y1==y2)continue;        a[++oo].mk(x1,y1,x2,y2);    }    a[++oo].mk(0,0,10000,0);    a[++oo].mk(10000,0,10000,10000);    a[++oo].mk(10000,10000,0,10000);    a[++oo].mk(0,10000,0,0);    sort(a+1,a+1+oo);    a[0].k=a[1].k-1;    for(int i=1; i<=oo; i++)        if(i==1||dcmp(a[i-1].k-a[i].k))            a[++o]=a[i];    if(!halfplane(o))puts("0.0");    else printf("%.1f\n",calc(p+st-1,en-st+1));    return 0;}

总结

半平面交、旋转卡壳,这俩绝对是我目前见过最毒瘤的算法了 \(😅\)

啥?你说仙人掌?这不是毒瘤本瘤吗(逃

]]>
@@ -7110,7 +7110,7 @@ /2022/04/27/uva12307-smallest-enclosing-rectangle-ti-jie/ - UVA12307 Smallest Enclosing Rectangle 题解

upd.20220429 由于q779太菜,导致代码出了点锅,已经重新修改

题目链接:UVA12307 Smallest Enclosing Rectangle

题意:多组数据,给定平面上的 $n$ 个点,分别求出周长最小、面积最小的矩形,使得所有的点均被矩形覆盖

数据范围: $3\le n \le 10^5$ ,$-10^5 \le x,y \le 10^5$

看到本题,容易想到 P3187

解法是旋转卡壳

要求的是矩形,因此考虑维护三个点:

$a$ 代表当前枚举直线对面的边

$b$ 代表当前枚举直线右侧最远的点

$c$ 代表当前枚举直线左侧最远的点

如下图所示

在这里插入图片描述

其中,$a$ 使用叉积比较,$b,c$ 则使用点积比较

方便起见,我们称当前的底边长为 $\text{T}$ (即图中的q[1]到q[2])

可以发现 $\text{T = L}+\text{R}-\text{d}$

$\text{L}$ 表示向量 $s_i\to c$ 在 $\text{T}$ 上投影向量的模长

$\text{R}$ 表示向量 $s_{i-1} \to b$ 在 $\text{T}$ 上投影向量的模长

$\text{d}$ 表示向量 $s_{i-1}\to s_i$ 的模长(即图中的绿色段)

那么面积就可以计算出来了

关于周长的计算

通过向量旋转和数乘运算来求出矩形的四个顶点坐标

然后直接用两点距离公式计算即可

因为周长和面积不一定同时最小,所以只要两个都分别fmin一下就好了

时间复杂度 $O(n\log n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)int n;int stk[N],used[N],top;const double eps=1e-10;#define pf(x) ((x)*(x))struct vct{double x,y;}p[N],s[N],q[15];vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double dis(vct a,vct b){return sqrt(pf(a.x-b.x)+pf(a.y-b.y));}double sq1(vct a,vct b,vct c){return fabs(cross(c-a,b-a));}double sq2(vct a,vct b,vct c){return 0.5*sq1(a,b,c);}double angle(vct a,vct b){return acos(dot(a,b)/len(a)/len(b));}vct rot(vct a){return {-a.y,a.x};} // 逆时针旋转90度void gettb(){    sort(p+1,p+1+n,[](vct a,vct b)    {        return a.x==b.x?a.y<b.y:a.x<b.x;    });    stk[++top]=1;    for(int i=2; i<=n; i++)    {        while(top>1&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    int tmp=top;    for(int i=n-1; i>=1; i--)    {        if(used[i])continue;        while(top>tmp&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    for(int i=1; i<=top; i++)        s[i]=p[stk[i]];}void getmn(){    int a,b,c;    double ans1=1e100,ans2=1e100;    a=b=c=2;    for(int i=2; i<=top; i++)    {        while(cross(s[a%top+1]-s[i],s[i-1]-s[i])>=cross(s[a]-s[i],s[i-1]-s[i]))            a=a%top+1;        while(dot(s[b%top+1]-s[i],s[i-1]-s[i])<=dot(s[b]-s[i],s[i-1]-s[i]))            b=b%top+1;        if(i==2)c=a;        while(dot(s[c%top+1]-s[i-1],s[i]-s[i-1])<=dot(s[c]-s[i-1],s[i]-s[i-1]))            c=c%top+1;        double d=dis(s[i],s[i-1]);        double L=fabs(dot(s[c]-s[i],s[i]-s[i-1])/d);        double R=fabs(dot(s[b]-s[i-1],s[i-1]-s[i])/d);        double H=fabs(sq1(s[i],s[i-1],s[a])/len(s[i]-s[i-1]));        double res1=(L+R-d)*H;        double res2=0;        q[1]=s[i]-(s[i]-s[i-1])*(L/d);        q[2]=q[1]+(s[i]-s[i-1])*((R+L-d)/d);        q[3]=q[2]+rot(q[2]-q[1])*(H/(L+R-d));        q[4]=q[3]+rot(q[3]-q[2])*((L+R-d)/H);        for(int k=1; k<=4; k++)            res2+=dis(q[k],q[k%4+1]);        ans1=fmin(ans1,res1);        ans2=fmin(ans2,res2);    }    cout << ans1 << " " << ans2 << endl;}void clear(){    for(int i=1; i<=n; i++)        used[i]=0;    top=0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(2);    // freopen("check.out","w",stdout);    while(cin >> n&&n!=0)    {        clear();           for(int i=1; i<=n; i++)            cin >> p[i].x >> p[i].y;        gettb();getmn();    }    return 0;}

友情提醒:多测不清空,爆零两行泪!

部分参考了 这篇博客 的写法,但是他写的那个挂了,会出现除以0的情况

代码确实蛮难写的,这道题我研究了一整天才搞出来 QAQ

]]>
+ UVA12307 SmallestEnclosing Rectangle 题解

upd.20220429由于q779太菜,导致代码出了点锅,已经重新修改

题目链接:UVA12307 SmallestEnclosing Rectangle

题意:多组数据,给定平面上的 \(n\)个点,分别求出周长最小、面积最小的矩形,使得所有的点均被矩形覆盖

数据范围: \(3\le n \le 10^5\)\(-10^5 \le x,y \le 10^5\)

看到本题,容易想到 P3187

解法是旋转卡壳

要求的是矩形,因此考虑维护三个点:

\(a\) 代表当前枚举直线对面的边

\(b\)代表当前枚举直线右侧最远的点

\(c\)代表当前枚举直线左侧最远的点

如下图所示

其中,\(a\) 使用叉积比较,\(b,c\) 则使用点积比较

方便起见,我们称当前的底边长为 \(\text{T}\) (即图中的q[1]到q[2])

可以发现 \(\text{T =L}+\text{R}-\text{d}\)

\(\text{L}\) 表示向量 \(s_i\to c\) 在 \(\text{T}\) 上投影向量的模长

\(\text{R}\) 表示向量 \(s_{i-1} \to b\) 在 \(\text{T}\) 上投影向量的模长

\(\text{d}\) 表示向量 \(s_{i-1}\to s_i\)的模长(即图中的绿色段)

那么面积就可以计算出来了

关于周长的计算

通过向量旋转和数乘运算来求出矩形的四个顶点坐标

然后直接用两点距离公式计算即可

因为周长和面积不一定同时最小,所以只要两个都分别fmin一下就好了

时间复杂度 \(O(n\log n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e5+15)int n;int stk[N],used[N],top;const double eps=1e-10;#define pf(x) ((x)*(x))struct vct{double x,y;}p[N],s[N],q[15];vct operator+(vct a,vct b){return {a.x+b.x,a.y+b.y};}vct operator-(vct a,vct b){return {a.x-b.x,a.y-b.y};}vct operator*(vct a,double b){return {a.x*b,a.y*b};}vct operator/(vct a,double b){return {a.x/b,a.y/b};}double len(vct a){return sqrt(pf(a.x)+pf(a.y));}double cross(vct a,vct b){return a.x*b.y-a.y*b.x;}double dot(vct a,vct b){return a.x*b.x+a.y*b.y;}double dis(vct a,vct b){return sqrt(pf(a.x-b.x)+pf(a.y-b.y));}double sq1(vct a,vct b,vct c){return fabs(cross(c-a,b-a));}double sq2(vct a,vct b,vct c){return 0.5*sq1(a,b,c);}double angle(vct a,vct b){return acos(dot(a,b)/len(a)/len(b));}vct rot(vct a){return {-a.y,a.x};} // 逆时针旋转90度void gettb(){    sort(p+1,p+1+n,[](vct a,vct b)    {        return a.x==b.x?a.y<b.y:a.x<b.x;    });    stk[++top]=1;    for(int i=2; i<=n; i++)    {        while(top>1&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    int tmp=top;    for(int i=n-1; i>=1; i--)    {        if(used[i])continue;        while(top>tmp&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    for(int i=1; i<=top; i++)        s[i]=p[stk[i]];}void getmn(){    int a,b,c;    double ans1=1e100,ans2=1e100;    a=b=c=2;    for(int i=2; i<=top; i++)    {        while(cross(s[a%top+1]-s[i],s[i-1]-s[i])>=cross(s[a]-s[i],s[i-1]-s[i]))            a=a%top+1;        while(dot(s[b%top+1]-s[i],s[i-1]-s[i])<=dot(s[b]-s[i],s[i-1]-s[i]))            b=b%top+1;        if(i==2)c=a;        while(dot(s[c%top+1]-s[i-1],s[i]-s[i-1])<=dot(s[c]-s[i-1],s[i]-s[i-1]))            c=c%top+1;        double d=dis(s[i],s[i-1]);        double L=fabs(dot(s[c]-s[i],s[i]-s[i-1])/d);        double R=fabs(dot(s[b]-s[i-1],s[i-1]-s[i])/d);        double H=fabs(sq1(s[i],s[i-1],s[a])/len(s[i]-s[i-1]));        double res1=(L+R-d)*H;        double res2=0;        q[1]=s[i]-(s[i]-s[i-1])*(L/d);        q[2]=q[1]+(s[i]-s[i-1])*((R+L-d)/d);        q[3]=q[2]+rot(q[2]-q[1])*(H/(L+R-d));        q[4]=q[3]+rot(q[3]-q[2])*((L+R-d)/H);        for(int k=1; k<=4; k++)            res2+=dis(q[k],q[k%4+1]);        ans1=fmin(ans1,res1);        ans2=fmin(ans2,res2);    }    cout << ans1 << " " << ans2 << endl;}void clear(){    for(int i=1; i<=n; i++)        used[i]=0;    top=0;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(2);    // freopen("check.out","w",stdout);    while(cin >> n&&n!=0)    {        clear();           for(int i=1; i<=n; i++)            cin >> p[i].x >> p[i].y;        gettb();getmn();    }    return 0;}

友情提醒:多测不清空,爆零两行泪!

部分参考了 这篇博客的写法,但是他写的那个挂了,会出现除以0的情况

代码确实蛮难写的,这道题我研究了一整天才搞出来 QAQ

]]>
@@ -7139,7 +7139,7 @@ /2022/04/25/luo-gu-p4159-scoi2009-mi-lu-ti-jie/ - 洛谷P4159 [SCOI2009] 迷路 题解

题目链接:P4159 [SCOI2009] 迷路

题意:该有向图有 $n$ 个节点,节点从 $1$ 至 $n$ 编号,windy 从节点 $1$ 出发,他必须恰好在 $t$ 时刻到达节点 $n$。

现在给出该有向图,你能告诉 windy 总共有多少种不同的路径吗?

答案对 $2009$ 取模。

注意:windy 不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

题目给出了邻接矩阵

首先,根据矩阵乘法的性质,我们可以发现

不考虑题目的限制,当矩阵仅由01构成时

$M^2$ 的意义就是只通过两条边(此时边权均为 $1$ )能到达每个结点的方案数

则此时答案就是 $M^t$

可是题目给出的矩阵,至多有 $10$

怎么办呢?考虑把它转化新的矩阵(仅有01构成的)

对于初始矩阵的每个节点,把它化为 $x_i$ 个结点

其中 $x_i$ 表示出边边权的最大值

如下图

在这里插入图片描述

“拆点”操作

在这里插入图片描述

可以发现这样的话仍然是满足题意的

为什么呢?因为可以更新答案的那条路径

一定走到了 $x_i$ 号节点,正准备走到原来的另一个节点

不是很好解释,自己多画几张图试试就知道了(我知道你们懒,给你们画一个

在这里插入图片描述

“拆点”操作

在这里插入图片描述

那么这题就很简单了

时间复杂度 $O(n^3\log t)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(25)const int p=2009;int n,t;struct mat{    int n,g[205][205];    void clear()    {        memset(g,0,sizeof(g));    }    mat operator*(const mat &o)const    {        static mat tmp;        tmp.n=n;tmp.clear();        for(int i=1; i<=n; i++)            for(int k=1; k<=n; k++)            {                int r=g[i][k]%p;                for(int j=1; j<=n; j++)                    tmp.g[i][j]=(tmp.g[i][j]+r*o.g[k][j]%p)%p;            }        return tmp;    }}M;mat qpow(mat a,int b){    static mat ans,base=a;    ans.n=a.n;ans.clear();    for(int i=1; i<=ans.n; i++)        ans.g[i][i]=1;    while(b)    {        if(b&1)ans=ans*base;        base=base*base;        b>>=1;    }    return ans;}int ck(int i,int j){    return (i-1)*10+j;}signed main(){    scanf("%lld%lld",&n,&t);    M.n=n*10;M.clear();    for(int i=1; i<=n; i++)    {        for(int j=1; j<10; j++)            M.g[ck(i,j)][ck(i,j+1)]=1;        for(int j=1,x; j<=n; j++)        {            scanf("%1lld",&x);            if(x)M.g[ck(i,x)][ck(j,1)]=1;        }     }    // for(int i=1; i<=M.n; i++)    //     for(int j=1; j<=M.n; j++)    //         printf("%lld%c",M.g[i][j]," \n"[j==M.n]);    M=qpow(M,t);    printf("%lld\n",M.g[1][ck(n,1)]);    return 0;}
]]>
+ 洛谷P4159 [SCOI2009] 迷路题解

题目链接:P4159[SCOI2009] 迷路

题意:该有向图有 \(n\) 个节点,节点从 \(1\) 至 \(n\) 编号,windy 从节点 \(1\) 出发,他必须恰好在 \(t\) 时刻到达节点 \(n\)。

现在给出该有向图,你能告诉 windy 总共有多少种不同的路径吗?

答案对 \(2009\) 取模。

注意:windy不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。

题目给出了邻接矩阵

首先,根据矩阵乘法的性质,我们可以发现

不考虑题目的限制,当矩阵仅由01构成时

\(M^2\)的意义就是只通过两条边(此时边权均为 \(1\) )能到达每个结点的方案数

则此时答案就是 \(M^t\)

可是题目给出的矩阵,至多有 \(10\)

怎么办呢?考虑把它转化新的矩阵(仅有01构成的)

对于初始矩阵的每个节点,把它化为 \(x_i\) 个结点

其中 \(x_i\)表示出边边权的最大值

如下图

在这里插入图片描述

“拆点”操作

在这里插入图片描述

可以发现这样的话仍然是满足题意的

为什么呢?因为可以更新答案的那条路径

一定走到了 \(x_i\)号节点,正准备走到原来的另一个节点

不是很好解释,自己多画几张图试试就知道了(我知道你们懒,给你们画一个

在这里插入图片描述

“拆点”操作

在这里插入图片描述

那么这题就很简单了

时间复杂度 \(O(n^3\log t)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(25)const int p=2009;int n,t;struct mat{    int n,g[205][205];    void clear()    {        memset(g,0,sizeof(g));    }    mat operator*(const mat &o)const    {        static mat tmp;        tmp.n=n;tmp.clear();        for(int i=1; i<=n; i++)            for(int k=1; k<=n; k++)            {                int r=g[i][k]%p;                for(int j=1; j<=n; j++)                    tmp.g[i][j]=(tmp.g[i][j]+r*o.g[k][j]%p)%p;            }        return tmp;    }}M;mat qpow(mat a,int b){    static mat ans,base=a;    ans.n=a.n;ans.clear();    for(int i=1; i<=ans.n; i++)        ans.g[i][i]=1;    while(b)    {        if(b&1)ans=ans*base;        base=base*base;        b>>=1;    }    return ans;}int ck(int i,int j){    return (i-1)*10+j;}signed main(){    scanf("%lld%lld",&n,&t);    M.n=n*10;M.clear();    for(int i=1; i<=n; i++)    {        for(int j=1; j<10; j++)            M.g[ck(i,j)][ck(i,j+1)]=1;        for(int j=1,x; j<=n; j++)        {            scanf("%1lld",&x);            if(x)M.g[ck(i,x)][ck(j,1)]=1;        }     }    // for(int i=1; i<=M.n; i++)    //     for(int j=1; j<=M.n; j++)    //         printf("%lld%c",M.g[i][j]," \n"[j==M.n]);    M=qpow(M,t);    printf("%lld\n",M.g[1][ck(n,1)]);    return 0;}
]]>
@@ -7166,7 +7166,7 @@ /2022/04/25/luo-gu-p3829-shoi2012-xin-yong-qia-tu-bao-ti-jie/ - 洛谷P3829 [SHOI2012]信用卡凸包 题解

题目链接:P3829 [SHOI2012]信用卡凸包

题意: 给定若干个“信用卡”,求其“凸包”周长

imgimg

这个题其实看上去很不可做,其实很简单

注意到(搬了一张图,来自link,不过这篇题解的代码好像挂了)

在这里插入图片描述

显然,由图可知,我们求的所谓凸包

就是信用卡上面那四个点组成的点集的凸包加上一个圆的面积

关于如何理解这个图形的周长恰好包括一个圆

大家就想象一只小乌龟从一点开始走,走了一大圈又回到了起点

它旋转的角度正好为 $2\pi$

那么这道题就变成数学题了,如何处理这4个点?

再看图,可以发现这个圆对求凸包真的一点用都没有

直接把那四个点组成的新矩形当作处理对象

然后运用一点初中知识就可以求出来了

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e4+15)struct vct{    double x,y;}p[N<<2];double a,b,r,ans;int n,cnt,stk[N<<2],top,used[N<<2];vct operator-(vct a,vct b){    return (vct){a.x-b.x,a.y-b.y};}int cross(vct a,vct b){    return a.x*b.y-a.y*b.x;}double dis(vct a,vct b){    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(2);    cin >> n >> a >> b >> r;    a-=2*r;b-=2*r;    double l=sqrt(a*a+b*b)/2;    double phi=atan(a/b);    while(n--)    {        double x,y,theta;        cin >> x >> y >> theta;        {            double dx=l*cos(theta+phi);            double dy=l*sin(theta+phi);            p[++cnt]={x+dx,y+dy};            p[++cnt]={x-dx,y-dy};        }        {            double dx=l*cos(theta-phi);            double dy=l*sin(theta-phi);            p[++cnt]={x+dx,y+dy};            p[++cnt]={x-dx,y-dy};        }    }    sort(p+1,p+1+cnt,[](vct a,vct b)    {        return a.x==b.x?a.y<b.y:a.x<b.x;    });    stk[++top]=1;    for(int i=2; i<=cnt; i++)    {        while(top>1&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    int tmp=top;    for(int i=cnt-1; i>=1; i--)    {        if(used[i])continue;        while(top>tmp&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    for(int i=1; i<top; i++)        ans+=dis(p[stk[i]],p[stk[i+1]]);    ans+=3.141592653589793*2*r;    cout << ans << endl;    return 0;}
]]>
+ 洛谷P3829[SHOI2012]信用卡凸包 题解

题目链接:P3829[SHOI2012]信用卡凸包

题意: 给定若干个“信用卡”,求其“凸包”周长

这个题其实看上去很不可做,其实很简单

注意到(搬了一张图,来自link,不过这篇题解的代码好像挂了)

显然,由图可知,我们求的所谓凸包

就是信用卡上面那四个点组成的点集的凸包加上一个圆的面积

关于如何理解这个图形的周长恰好包括一个圆

大家就想象一只小乌龟从一点开始走,走了一大圈又回到了起点

它旋转的角度正好为 \(2\pi\)

那么这道题就变成数学题了,如何处理这4个点?

再看图,可以发现这个圆对求凸包真的一点用都没有

直接把那四个点组成的新矩形当作处理对象

然后运用一点初中知识就可以求出来了

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF ((int)0x3f3f3f3f3f3f3f3f)#define inf ((int)0xc0c0c0c0c0c0c0c0)#define N (int)(1e4+15)struct vct{    double x,y;}p[N<<2];double a,b,r,ans;int n,cnt,stk[N<<2],top,used[N<<2];vct operator-(vct a,vct b){    return (vct){a.x-b.x,a.y-b.y};}int cross(vct a,vct b){    return a.x*b.y-a.y*b.x;}double dis(vct a,vct b){    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cout << fixed << setprecision(2);    cin >> n >> a >> b >> r;    a-=2*r;b-=2*r;    double l=sqrt(a*a+b*b)/2;    double phi=atan(a/b);    while(n--)    {        double x,y,theta;        cin >> x >> y >> theta;        {            double dx=l*cos(theta+phi);            double dy=l*sin(theta+phi);            p[++cnt]={x+dx,y+dy};            p[++cnt]={x-dx,y-dy};        }        {            double dx=l*cos(theta-phi);            double dy=l*sin(theta-phi);            p[++cnt]={x+dx,y+dy};            p[++cnt]={x-dx,y-dy};        }    }    sort(p+1,p+1+cnt,[](vct a,vct b)    {        return a.x==b.x?a.y<b.y:a.x<b.x;    });    stk[++top]=1;    for(int i=2; i<=cnt; i++)    {        while(top>1&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    int tmp=top;    for(int i=cnt-1; i>=1; i--)    {        if(used[i])continue;        while(top>tmp&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            used[stk[top--]]=0;        used[i]=1;        stk[++top]=i;    }    for(int i=1; i<top; i++)        ans+=dis(p[stk[i]],p[stk[i+1]]);    ans+=3.141592653589793*2*r;    cout << ans << endl;    return 0;}
]]>
@@ -7193,7 +7193,7 @@ /2022/04/23/luo-gu-p3194-hnoi2008-shui-ping-ke-jian-zhi-xian-ti-jie/ - 洛谷P3194 [HNOI2008]水平可见直线 题解

题目链接:P3194 [HNOI2008]水平可见直线

题意:在$ x-y$ 直角坐标平面上有 $n$ 条直线 $L_1,L_2,…L_n$,若在 $y$ 值为正无穷大处往下看,能见到 $L_i$ 的某个子线段,则称 $L_i$ 为可见的,否则 $L_i$ 为被覆盖的。 例如,对于直线: $L_1:y=x$; $L_2:y=-x$; $L_3:y=0$; 则 $L_1$ 和 $L_2$ 是可见的,$L_3$ 是被覆盖的。给出 $n$ 条直线,表示成 $y=Ax+B$ 的形式($|A|,|B| \le 500000$),且 $n$ 条直线两两不重合,求出所有可见的直线。

一条直线C被另外两条直线A和B所覆盖

它的充分必要条件为直线C在A,B交点处的值更小一些

反之,直线C也可能对答案有贡献

那么交点的坐标是什么呢?

可以发现这个式子很像斜率

如果记 $P_i(A_i,B_i)$

那么答案就是点集 $\{P_i\}$ 构成的上凸壳

为什么是上凸壳?显然我们选定的是斜率大且尽可能截距大

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)struct vct{    double x,y;int id;    vct operator-(const vct &o)const    {        return (vct){x-o.x,y-o.y};    }}p[N];int n,stk[N],top;double cross(vct a,vct b){    return a.x*b.y-a.y*b.x;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> p[i].x >> p[i].y;        p[i].id=i;    }    sort(p+1,p+1+n,[](vct a,vct b)    {        return a.x==b.x?a.y>b.y:a.x>b.x;    });    stk[++top]=1;    for(int i=2; i<=n; i++)    {        if(p[i-1].x==p[i].x)continue;        while(top>1&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            --top;        stk[++top]=i;    }    sort(stk+1,stk+1+top,[](int a,int b)    {        return p[a].id<p[b].id;    });    for(int i=1; i<=top; i++)        cout << p[stk[i]].id << " ";    return 0;}
]]>
+ 洛谷P3194[HNOI2008]水平可见直线 题解

题目链接:P3194[HNOI2008]水平可见直线

题意:在$ x-y$ 直角坐标平面上有 \(n\) 条直线 \(L_1,L_2,…L_n\),若在 \(y\) 值为正无穷大处往下看,能见到 \(L_i\) 的某个子线段,则称 \(L_i\) 为可见的,否则 \(L_i\) 为被覆盖的。 例如,对于直线: \(L_1:y=x\); \(L_2:y=-x\); \(L_3:y=0\); 则 \(L_1\) 和 \(L_2\) 是可见的,\(L_3\) 是被覆盖的。给出 \(n\) 条直线,表示成 \(y=Ax+B\) 的形式(\(|A|,|B| \le 500000\)),且 \(n\)条直线两两不重合,求出所有可见的直线。

一条直线C被另外两条直线A和B所覆盖

它的充分必要条件为直线C在A,B交点处的值更小一些

反之,直线C也可能对答案有贡献

那么交点的坐标是什么呢? \[A_1x+B_1=A_2x+B_2\\x=\dfrac{B_2-B_1}{A_1-A_2}\]\[A_1\dfrac{B_2-B_1}{A_1-A_2} + B_1 \ge A_3\dfrac{B_2-B_1}{A_1-A_2} +B_3\\\color{red}{\dfrac{B_2-B_1}{A_2-A_1}\ge\dfrac{B_3-B_1}{A_3-A_1}}\] 可以发现这个式子很像斜率

如果记 \(P_i(A_i,B_i)\)

那么答案就是点集 \(\{P_i\}\)构成的上凸壳

为什么是上凸壳?显然我们选定的是斜率大且尽可能截距大

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)struct vct{    double x,y;int id;    vct operator-(const vct &o)const    {        return (vct){x-o.x,y-o.y};    }}p[N];int n,stk[N],top;double cross(vct a,vct b){    return a.x*b.y-a.y*b.x;}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> p[i].x >> p[i].y;        p[i].id=i;    }    sort(p+1,p+1+n,[](vct a,vct b)    {        return a.x==b.x?a.y>b.y:a.x>b.x;    });    stk[++top]=1;    for(int i=2; i<=n; i++)    {        if(p[i-1].x==p[i].x)continue;        while(top>1&&cross(p[stk[top]]-p[stk[top-1]],p[i]-p[stk[top]])<=0)            --top;        stk[++top]=i;    }    sort(stk+1,stk+1+top,[](int a,int b)    {        return p[a].id<p[b].id;    });    for(int i=1; i<=top; i++)        cout << p[stk[i]].id << " ";    return 0;}
]]>
@@ -7222,7 +7222,7 @@ /2022/04/22/luo-gu-p3236-hnoi2014-hua-kuang-ti-jie/ - 洛谷P3236 [HNOI2014]画框 题解

题目链接:P3236 [HNOI2014]画框

题意:小 T 准备在家里摆放几幅画,为此他买来了 $N$ 幅画和 $N$ 个画框。为了体现他的品味,小 T 希望能合理地搭配画与画框,使得其显得既不过于平庸也不太违和。

对于第 $i$ 幅画与第 $j$ 个画框的配对,小 T 都给出了这个配对的平凡度 $A_{i, j}$ 与违和度 $B_{i, j}$ 。整个搭配方案的总体不和谐度为每对画与画框平凡度之和与每对画与画框违和度的乘积。具体来说,设搭配方案中第 $i$ 幅画与第 $P_i$ 个画框配对,则总体不和谐度为

小 T 希望知道通过搭配能得到的最小的总体不和谐度是多少。

q779在水文章$🐒$

这道题和P5540 [BalkanOI2011] timeismoney | 最小乘积生成树 几乎就是一道题

可以先看下这篇题解

变化不是很大,基本上就是改一下邻接矩阵啥的

只不过算法换成了KM求完全二分图最大权完美匹配

时间复杂度大概在 $O(kn^4)$ 左右,$k$ 为一个小常数

多测不清空,爆零两行泪!!!!

说句题外话,dfs版本好像是 $O(n^4)$ 的,建议不要写

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(75)int n,g[N][N],a[N][N],b[N][N],Q;struct vct{    int x,y;}ans;int slack[N],lx[N],ly[N],px[N],py[N],pre[N],d,vx[N],vy[N];vct operator-(vct a,vct b){return (vct){a.x-b.x,a.y-b.y};}int cross(vct a,vct b){return a.x*b.y-a.y*b.x;}void aug(int v){    int t;    while(v)    {        t=px[pre[v]];        px[pre[v]]=v;        py[v]=pre[v];        v=t;    }}queue<int> q;void bfs(int s){    for(int i=1; i<=n; i++)        slack[i]=INF,vx[i]=vy[i]=0;    while(!q.empty())q.pop();    q.push(s);    while(1)    {        while(!q.empty())        {            int u=q.front();q.pop();            vx[u]=1;            for(int i=1; i<=n; i++)            {                if(!vy[i]&&lx[u]+ly[i]-g[u][i]<slack[i])                {                    slack[i]=lx[u]+ly[i]-g[u][i];                    pre[i]=u;                    if(!slack[i])                    {                        vy[i]=1;                        if(!py[i]){aug(i);return;}                        else q.push(py[i]);                    }                }            }        }        int d=INF;        for(int i=1; i<=n; i++)            if(!vy[i])d=min(d,slack[i]);        for(int i=1; i<=n; i++)        {            if(vx[i])lx[i]-=d;            if(vy[i])ly[i]+=d;            else slack[i]-=d;        }        for(int i=1; i<=n; i++)        {            if(!vy[i]&&!slack[i])            {                vy[i]=1;                if(!py[i]){aug(i);return;}                else q.push(py[i]);            }        }    }}vct KM(){    vct res={0,0};    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            lx[i]=max(lx[i],g[i][j]);    for(int i=1; i<=n; i++)bfs(i);    for(int i=1; i<=n; i++)    {        res.x+=a[py[i]][i];        res.y+=b[py[i]][i];    }    if(res.x*res.y<ans.x*ans.y)ans=res;    for(int i=1; i<=n; i++)        px[i]=py[i]=lx[i]=ly[i]=pre[i]=0;            return res;}void solve(vct A,vct B){    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            g[i][j]=-b[i][j]*(B.x-A.x)-a[i][j]*(A.y-B.y);    vct C=KM();    if(cross(B-A,C-A)>=0)return;    solve(A,C);solve(C,B);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> Q;    while(Q--)    {        cin >> n;        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                cin >> a[i][j];        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                cin >> b[i][j];        for(int i=1; i<=n; i++)             for(int j=1; j<=n; j++)                g[i][j]=-a[i][j];        vct A=KM();        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                g[i][j]=-b[i][j];        vct B=KM();        ans=(A.x*A.y<B.x*B.y)?A:B;        solve(A,B);        cout << ans.x*ans.y << endl;    }    return 0;}
]]>
+ 洛谷P3236 [HNOI2014]画框 题解

题目链接:P3236[HNOI2014]画框

题意:小 T 准备在家里摆放几幅画,为此他买来了 \(N\) 幅画和 \(N\) 个画框。为了体现他的品味,小 T希望能合理地搭配画与画框,使得其显得既不过于平庸也不太违和。

对于第 \(i\) 幅画与第 \(j\) 个画框的配对,小 T都给出了这个配对的平凡度 \(A_{i, j}\)与违和度 \(B_{i, j}\)。整个搭配方案的总体不和谐度为每对画与画框平凡度之和与每对画与画框违和度的乘积。具体来说,设搭配方案中第\(i\) 幅画与第 \(P_i\) 个画框配对,则总体不和谐度为 \[\mathrm{disharmony}=\sum_{i=1}^{N}A_{i,p_i}\times\sum_{i=1}^{N}B_{i,p_i}\] 小 T 希望知道通过搭配能得到的最小的总体不和谐度是多少。

q779在水文章\(🐒\)

这道题和P5540[BalkanOI2011] timeismoney | 最小乘积生成树 几乎就是一道题

可以先看下这篇题解(

变化不是很大,基本上就是改一下邻接矩阵啥的

只不过算法换成了KM求完全二分图最大权完美匹配

时间复杂度大概在 \(O(kn^4)\)左右,\(k\) 为一个小常数

多测不清空,爆零两行泪!!!!

说句题外话,dfs版本好像是 \(O(n^4)\)的,建议不要写

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(75)int n,g[N][N],a[N][N],b[N][N],Q;struct vct{    int x,y;}ans;int slack[N],lx[N],ly[N],px[N],py[N],pre[N],d,vx[N],vy[N];vct operator-(vct a,vct b){return (vct){a.x-b.x,a.y-b.y};}int cross(vct a,vct b){return a.x*b.y-a.y*b.x;}void aug(int v){    int t;    while(v)    {        t=px[pre[v]];        px[pre[v]]=v;        py[v]=pre[v];        v=t;    }}queue<int> q;void bfs(int s){    for(int i=1; i<=n; i++)        slack[i]=INF,vx[i]=vy[i]=0;    while(!q.empty())q.pop();    q.push(s);    while(1)    {        while(!q.empty())        {            int u=q.front();q.pop();            vx[u]=1;            for(int i=1; i<=n; i++)            {                if(!vy[i]&&lx[u]+ly[i]-g[u][i]<slack[i])                {                    slack[i]=lx[u]+ly[i]-g[u][i];                    pre[i]=u;                    if(!slack[i])                    {                        vy[i]=1;                        if(!py[i]){aug(i);return;}                        else q.push(py[i]);                    }                }            }        }        int d=INF;        for(int i=1; i<=n; i++)            if(!vy[i])d=min(d,slack[i]);        for(int i=1; i<=n; i++)        {            if(vx[i])lx[i]-=d;            if(vy[i])ly[i]+=d;            else slack[i]-=d;        }        for(int i=1; i<=n; i++)        {            if(!vy[i]&&!slack[i])            {                vy[i]=1;                if(!py[i]){aug(i);return;}                else q.push(py[i]);            }        }    }}vct KM(){    vct res={0,0};    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            lx[i]=max(lx[i],g[i][j]);    for(int i=1; i<=n; i++)bfs(i);    for(int i=1; i<=n; i++)    {        res.x+=a[py[i]][i];        res.y+=b[py[i]][i];    }    if(res.x*res.y<ans.x*ans.y)ans=res;    for(int i=1; i<=n; i++)        px[i]=py[i]=lx[i]=ly[i]=pre[i]=0;            return res;}void solve(vct A,vct B){    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            g[i][j]=-b[i][j]*(B.x-A.x)-a[i][j]*(A.y-B.y);    vct C=KM();    if(cross(B-A,C-A)>=0)return;    solve(A,C);solve(C,B);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> Q;    while(Q--)    {        cin >> n;        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                cin >> a[i][j];        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                cin >> b[i][j];        for(int i=1; i<=n; i++)             for(int j=1; j<=n; j++)                g[i][j]=-a[i][j];        vct A=KM();        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                g[i][j]=-b[i][j];        vct B=KM();        ans=(A.x*A.y<B.x*B.y)?A:B;        solve(A,B);        cout << ans.x*ans.y << endl;    }    return 0;}
]]>
@@ -7255,7 +7255,7 @@ /2022/04/22/luo-gu-p5540-balkanoi2011-timeismoney-zui-xiao-cheng-ji-sheng-cheng-shu-ti-jie/ - 洛谷P5540 [BalkanOI2011] timeismoney | 最小乘积生成树 题解

题目链接:P5540 [BalkanOI2011] timeismoney | 最小乘积生成树

题意:给出一个 $n$ 个点 $m$ 条边的无向图,第 $i$ 条边有两个权值 $a_i$ 和 $b_i$ 。

求该图的一棵生成树 $T$ ,使得

最小

注意到对于一棵生成树,式子中的左右两边均为常数

考虑将生成树映射到平面上的点 $(x,y)$

其中 $x=\sum\limits_{e\in T}a_e,y=\sum\limits_{e\in T}b_e$

那么问题就转化为了求一个点 $(x,y)$ 使得 $x\times y$ 最小

可以发现这些点的分布存在边界

即 $x$ 特别大 $y$ 特别小的时候以及 $y$ 特别大 $x$ 特别小的时候

这两种情况分别对应点 $A$ 和 $B$

则有 $A$ 为距离 $x$ 轴最近的点, $B$ 为距离 $y$ 轴最近的点

对于直线 $AB$ ,显然在 $AB$ 上方的不是优解

根据反比例函数的性质,可以发现这个解一定在以 $A,B$ 为端点的下凸包上

由于我们不可能枚举出所有的生成树然后求二维凸包

考虑递归求解,不知道大家知不知道割圆术,不知道也没关系

这个解法有点像割圆术

首先找出距离直线 $AB$ 最远的点 $C$ (在 $AB$ 下方)

然后递归 $AC$ 和 $CB$ ,即可求出解

如何寻找 $C$ 点呢?可以发现此时 $S_{\triangle ABC}$ 取到了最大值

而 $S_{\triangle ABC} = -\dfrac{1}{2}\overset{\longrightarrow}{AB}\times \overset{\longrightarrow}{AC}$

(注:这里写的不是很规范,其实应该除以一个单位向量 $\boldsymbol{z}$ 的,不重要不管了

推柿子环节 qwq

可以发现后面两项是定值

因此我们将边权设为 $(x_B-x_A)b_i+(y_A-y_B)a_i$ ,然后跑最小生成树即可

由于 $n$ 很小,且凸包上的期望点数分析极其复杂

本题复杂度可以近似看作 $O(km\log m)$,其中 $k$ 为一个小常数,据说是 $\sqrt{\ln n}$ ,不太清楚

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e2+15)#define M (int)(1e4+15)int n,m;struct Edge{    int u,v,a,b,w,next;}e[M];struct vct{    int x,y;}ans;int f[N];vct operator-(vct a,vct b){return (vct){a.x-b.x,a.y-b.y};}int cross(vct a,vct b){return a.x*b.y-a.y*b.x;}void init(){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){u=find(u);v=find(v);f[u]=v;}vct kruskal(){    sort(e+1,e+1+m,[](Edge a,Edge b){        return a.w<b.w;    });    init();    vct res={0,0};    int cnt=0;    for(int i=1; i<=m; i++)    {        int u=find(e[i].u),v=find(e[i].v);        if(u!=v)        {            merge(u,v);            res.x+=e[i].a;            res.y+=e[i].b;            if(++cnt==n-1)break;        }    }    int Ans=ans.x*ans.y,Res=res.x*res.y;    if(Ans>Res||Ans==Res&&ans.x>res.x)ans=res;    return res;}void solve(vct A,vct B){    for(int i=1; i<=m; i++)        e[i].w=e[i].b*(B.x-A.x)+e[i].a*(A.y-B.y);    vct C=kruskal();    if(cross(B-A,C-A)>=0)return;    solve(A,C);solve(C,B);}signed main(){    read(n);read(m);    ans={0x3f3f3f3f,0x3f3f3f3f};    for(int i=1; i<=m; i++)    {        read(e[i].u);read(e[i].v);        read(e[i].a);read(e[i].b);        e[i].u++;e[i].v++;    }    for(int i=1; i<=m; i++)        e[i].w=e[i].a;    vct A=kruskal();    for(int i=1; i<=m; i++)        e[i].w=e[i].b;    vct B=kruskal();    solve(A,B);    printf("%lld %lld\n",ans.x,ans.y);    return 0;}
]]>
+ 洛谷P5540[BalkanOI2011] timeismoney | 最小乘积生成树 题解

题目链接:P5540[BalkanOI2011] timeismoney | 最小乘积生成树

题意:给出一个 \(n\) 个点 \(m\) 条边的无向图,第 \(i\) 条边有两个权值 \(a_i\) 和 \(b_i\) 。

求该图的一棵生成树 \(T\) ,使得\[\left(\sum_{e\in T}a_e\right)\times\left(\sum_{e\in T}b_e\right)\]

最小

注意到对于一棵生成树,式子中的左右两边均为常数

考虑将生成树映射到平面上的点 \((x,y)\)

其中 \(x=\sum\limits_{e\inT}a_e,y=\sum\limits_{e\in T}b_e\)

那么问题就转化为了求一个点 \((x,y)\)使得 \(x\times y\) 最小

可以发现这些点的分布存在边界

\(x\) 特别大 \(y\) 特别小的时候以及 \(y\) 特别大 \(x\) 特别小的时候

这两种情况分别对应点 \(A\)\(B\)

则有 \(A\) 为距离 \(x\) 轴最近的点, \(B\) 为距离 \(y\) 轴最近的点

对于直线 \(AB\) ,显然在 \(AB\) 上方的不是优解

根据反比例函数的性质,可以发现这个解一定在以 \(A,B\) 为端点的下凸包上

由于我们不可能枚举出所有的生成树然后求二维凸包

考虑递归求解,不知道大家知不知道割圆术,不知道也没关系

这个解法有点像割圆术

首先找出距离直线 \(AB\) 最远的点\(C\) (在 \(AB\) 下方)

然后递归 \(AC\)\(CB\) ,即可求出解

如何寻找 \(C\) 点呢?可以发现此时\(S_{\triangle ABC}\) 取到了最大值

\(S_{\triangle ABC} =-\dfrac{1}{2}\overset{\longrightarrow}{AB}\times\overset{\longrightarrow}{AC}\)

(注:这里写的不是很规范,其实应该除以一个单位向量 \(\boldsymbol{z}\)的,不重要不管了

推柿子环节 qwq \[\begin{aligned}\overset{\longrightarrow}{AB}\times \overset{\longrightarrow}{AC}&= (x_B-x_A)(y_C-y_A)-(y_B-y_A)(x_C-x_A)\\&=(x_B-x_A)\times y_C + (y_A-y_B)\times x_C + y_Bx_A-x_By_A\end{aligned}\] 可以发现后面两项是定值

因此我们将边权设为 \((x_B-x_A)b_i+(y_A-y_B)a_i\),然后跑最小生成树即可

由于 \(n\)很小,且凸包上的期望点数分析极其复杂

本题复杂度可以近似看作 \(O(km\logm)\),其中 \(k\)为一个小常数,据说是 \(\sqrt{\ln n}\),不太清楚

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1,*p2;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e2+15)#define M (int)(1e4+15)int n,m;struct Edge{    int u,v,a,b,w,next;}e[M];struct vct{    int x,y;}ans;int f[N];vct operator-(vct a,vct b){return (vct){a.x-b.x,a.y-b.y};}int cross(vct a,vct b){return a.x*b.y-a.y*b.x;}void init(){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){u=find(u);v=find(v);f[u]=v;}vct kruskal(){    sort(e+1,e+1+m,[](Edge a,Edge b){        return a.w<b.w;    });    init();    vct res={0,0};    int cnt=0;    for(int i=1; i<=m; i++)    {        int u=find(e[i].u),v=find(e[i].v);        if(u!=v)        {            merge(u,v);            res.x+=e[i].a;            res.y+=e[i].b;            if(++cnt==n-1)break;        }    }    int Ans=ans.x*ans.y,Res=res.x*res.y;    if(Ans>Res||Ans==Res&&ans.x>res.x)ans=res;    return res;}void solve(vct A,vct B){    for(int i=1; i<=m; i++)        e[i].w=e[i].b*(B.x-A.x)+e[i].a*(A.y-B.y);    vct C=kruskal();    if(cross(B-A,C-A)>=0)return;    solve(A,C);solve(C,B);}signed main(){    read(n);read(m);    ans={0x3f3f3f3f,0x3f3f3f3f};    for(int i=1; i<=m; i++)    {        read(e[i].u);read(e[i].v);        read(e[i].a);read(e[i].b);        e[i].u++;e[i].v++;    }    for(int i=1; i<=m; i++)        e[i].w=e[i].a;    vct A=kruskal();    for(int i=1; i<=m; i++)        e[i].w=e[i].b;    vct B=kruskal();    solve(A,B);    printf("%lld %lld\n",ans.x,ans.y);    return 0;}
]]>
@@ -7284,7 +7284,7 @@ /2022/04/19/luo-gu-p2491-sdoi2011-xiao-fang-ti-jie/ - 洛谷P2491 [SDOI2011] 消防 题解

题目链接:P2491 [SDOI2011] 消防

题意:某个国家有 $n$ 个城市,这 $n$ 个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为 $z_i$ 。

这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的行业是消防业。由于政府对国民的热情忍无可忍(大量的消防经费开销)可是却又无可奈何(总统竞选的国民支持率),所以只能想尽方法提高消防能力。

现在这个国家的经费足以在一条边长度和不超过 $s$ 的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。

你受命监管这个项目,你当然需要知道应该把枢纽建立在什么位置上。

看到这是一棵树,而且还要求路径的最大值

很容易想到这个枢纽要建在直径上

当 $s$ 大于等于直径时显然成立

考虑 $s$ 小于直径的情况,枢纽全部建在直径上仍是最优解

以下为证明:

假设只有一个枢纽结点(在直径上),我们以它为中心扩展出一条路径

离这个点最远的结点一定在直径上,而且是直径的端点

此时的最大值就是这条路径的长度

那么如果扩展枢纽结点,只有在直径上扩展,才可能更新这个最大值

因此枢纽一定在直径上

证毕。

于是,我们可以想到个暴力做法

先找出直径,然后枚举每一个直径上的点 $u$ 作为枢纽的起点进行扩展

找到满足条件且最远的 $v$ ,并对每组 $(u,v)$ 计算出直径的两个端点到枢纽的最大距离

再计算出非直径的结点到枢纽的最大距离即可,时间复杂度 $O(n^2)$

注:因为两端到枢纽的距离不一定是最大值,可以参考下图

那么怎么优化呢

注意到选择的枢纽长度随结点数量的增加单调递增

因此我们可以考虑使用尺取法

这样,算法的流程就变成了

  1. 找出直径
  2. 对于每个极大枢纽,统计两端到其距离的最大值的最小值
  3. 统计非直径结点到枢纽距离的最大值

时间复杂度 $O(n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e5+15)struct Edge{    int u,v,w,next;}e[N<<1];int n,s,ans=INF;int pos=1,head[N],x,y,dis[N],pre[N],pd[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dfs(int u,int f){    pre[u]=f;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f||pd[v])continue;        dis[v]=dis[u]+e[i].w;        dfs(v,u);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> s;    for(int i=1,u,v,w; i<n; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);        addEdge(v,u,w);    }    dis[1]=1;dfs(1,0);    x=max_element(dis+1,dis+1+n)-dis;    dis[x]=0;dfs(x,0);    y=max_element(dis+1,dis+1+n)-dis;    for(int i=y,j=y; i; i=pre[i])    {        pd[i]=1;        while(dis[j]-dis[i]>s)j=pre[j];        ans=min(ans,max(dis[i],dis[y]-dis[j]));    }    for(int i=y; i; i=pre[i])    {        dis[i]=0;        dfs(i,pre[i]);    }    for(int i=1; i<=n; i++)        ans=max(ans,dis[i]);    cout << ans << endl;    return 0;}
]]>
+ 洛谷P2491 [SDOI2011] 消防题解

题目链接:P2491[SDOI2011] 消防

题意:某个国家有 \(n\) 个城市,这 \(n\)个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为\(z_i\)

这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的行业是消防业。由于政府对国民的热情忍无可忍(大量的消防经费开销)可是却又无可奈何(总统竞选的国民支持率),所以只能想尽方法提高消防能力。

现在这个国家的经费足以在一条边长度和不超过 \(s\)的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。

你受命监管这个项目,你当然需要知道应该把枢纽建立在什么位置上。

看到这是一棵树,而且还要求路径的最大值

很容易想到这个枢纽要建在直径上

\(s\) 大于等于直径时显然成立

考虑 \(s\)小于直径的情况,枢纽全部建在直径上仍是最优解

以下为证明:

假设只有一个枢纽结点(在直径上),我们以它为中心扩展出一条路径

离这个点最远的结点一定在直径上,而且是直径的端点

此时的最大值就是这条路径的长度

那么如果扩展枢纽结点,只有在直径上扩展,才可能更新这个最大值

因此枢纽一定在直径上

证毕。

于是,我们可以想到个暴力做法

先找出直径,然后枚举每一个直径上的点 \(u\) 作为枢纽的起点进行扩展

找到满足条件且最远的 \(v\),并对每组 \((u,v)\)计算出直径的两个端点到枢纽的最大距离

再计算出非直径的结点到枢纽的最大距离即可,时间复杂度 \(O(n^2)\)

注:因为两端到枢纽的距离不一定是最大值,可以参考下图

那么怎么优化呢

注意到选择的枢纽长度随结点数量的增加单调递增

因此我们可以考虑使用尺取法

这样,算法的流程就变成了

  1. 找出直径
  2. 对于每个极大枢纽,统计两端到其距离的最大值的最小值
  3. 统计非直径结点到枢纽距离的最大值

时间复杂度 \(O(n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(3e5+15)struct Edge{    int u,v,w,next;}e[N<<1];int n,s,ans=INF;int pos=1,head[N],x,y,dis[N],pre[N],pd[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}void dfs(int u,int f){    pre[u]=f;    for(int i=head[u]; i; i=e[i].next)    {        int v=e[i].v;        if(v==f||pd[v])continue;        dis[v]=dis[u]+e[i].w;        dfs(v,u);    }}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> s;    for(int i=1,u,v,w; i<n; i++)    {        cin >> u >> v >> w;        addEdge(u,v,w);        addEdge(v,u,w);    }    dis[1]=1;dfs(1,0);    x=max_element(dis+1,dis+1+n)-dis;    dis[x]=0;dfs(x,0);    y=max_element(dis+1,dis+1+n)-dis;    for(int i=y,j=y; i; i=pre[i])    {        pd[i]=1;        while(dis[j]-dis[i]>s)j=pre[j];        ans=min(ans,max(dis[i],dis[y]-dis[j]));    }    for(int i=y; i; i=pre[i])    {        dis[i]=0;        dfs(i,pre[i]);    }    for(int i=1; i<=n; i++)        ans=max(ans,dis[i]);    cout << ans << endl;    return 0;}
]]>
@@ -7313,7 +7313,7 @@ /2022/04/17/luo-gu-p3299-sdoi2013-bao-hu-chu-ti-ren-ti-jie/ - 洛谷P3299 [SDOI2013]保护出题人 题解

题目链接:P3299 [SDOI2013]保护出题人

题意:出题人铭铭认为给SDOI2012出题太可怕了,因为总要被骂,于是他又给SDOI2013出题了。

参加SDOI2012的小朋友们释放出大量的僵尸,企图攻击铭铭的家。而你作为SDOI2013的参赛者,你需要保护出题人铭铭。

僵尸从唯一一条笔直道路接近,你们需要在铭铭的房门前放置植物攻击僵尸,避免僵尸碰到房子。

第一关,一只血量为 $a_1$ 点的墦尸从距离房子 $x_1$ 米处速接近,你们放置了攻击力为 $y_1$ 点/秒的植物进行防御;第二关,在上一关基础上,僵尸队列排头增加一只血量为 $a_2$ 点的僵尸,与后一只僵尸距离 $d$ 米,从距离房 $x_2$ 米处匀速接近,你们重新放置攻击力为 $y_2$ 点/秒的植物;……;第 $n$ 关,僵尸队列共有 $n$ 只僵尸,相邻两只僵尸距离 $d$ 米,排头僵尸血量 $a_n$ 点,排第二的 僵尸血量 $a_{n-1}$ ,以此类推,排头僵尸从距离房子 $x_n$ 米处匀速接近,其余僵尸跟随排头同时接近,你们重新放置攻击力为 $y_n$ 点/秒的植物。

每只僵尸直线移动速度均为 $1$ 米/秒,由于植物射击速度远大于僵尸移动速度,可忽略植物子弹在空中的时间。所有僵尸同时出现并接近,因此当一只僵尸死亡后,下一只僵尸立刻开始受到植物子弹的伤害。

游戏得分取决于你们放置的植物攻击力的总和 $\sum \limits _{i=1} ^{n} y_i$,和越小分数越高,为了追求分数上界,你们每关都要放置攻击力尽量小的植物。

作为SDOI2013的参赛选手,你们能保护出题人么?

对于第 $i$ 轮的第 $j$ 只僵尸,打死它的充要条件为它前面的僵尸全部被打死

它需要走过的距离为 $x_i+d\times (i-j+1)$ ,则最小的攻击为

则第 $i$ 轮进攻的最小攻击

其中,$S_i = \sum_{k=1}^{i} a_k$

稍微观察一下式子,可以发现

即点 $(x_i+d\times i,i)$ 和 $(d\times(j-1),j-1)$ 的直线斜率

而前者在仅与 $i$ 有关,可以看作定点;后者则与 $i$ 无关,仅与每只僵尸有关

考虑对 $(d\times(j-1),j-1)$ 维护一个下凸壳

每一轮二分一个点使得其和定点所成直线斜率最大

把这些斜率加起来就是答案了

注意本题是四舍五入,而不是向下取整

时间复杂度 $O(n\log n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n;struct vct{    int x,y;    vct operator-(const vct o)const    {        return {x-o.x,y-o.y};    }}stk[N];int d,sum[N],top;double cross(vct a,vct b){    return a.x*b.y-a.y*b.x;}double slope(vct a,vct b){    return 1.0*(a.y-b.y)/(a.x-b.x);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> d;    stk[0]={0,0};    double res=0;    for(int i=1,a,x; i<=n; i++)    {        cin >> a >> x;        sum[i]=sum[i-1]+a;        vct tmp={i*d,sum[i-1]};        while(top&&cross(stk[top]-stk[top-1],tmp-stk[top])<=0)            --top;        stk[++top]=tmp;        tmp={x+i*d,sum[i]};        int l=1,r=top;        while(l<r)        {            int mid=(l+r+1)>>1;            if(slope(stk[mid],tmp)>slope(stk[mid-1],tmp))                l=mid;            else r=mid-1;        }        res+=slope(stk[l],tmp);    }    cout << fixed << setprecision(0);    cout << res << endl;    return 0;}
]]>
+ 洛谷P3299[SDOI2013]保护出题人 题解

题目链接:P3299[SDOI2013]保护出题人

题意:出题人铭铭认为给SDOI2012出题太可怕了,因为总要被骂,于是他又给SDOI2013出题了。

参加SDOI2012的小朋友们释放出大量的僵尸,企图攻击铭铭的家。而你作为SDOI2013的参赛者,你需要保护出题人铭铭。

僵尸从唯一一条笔直道路接近,你们需要在铭铭的房门前放置植物攻击僵尸,避免僵尸碰到房子。

第一关,一只血量为 \(a_1\)点的墦尸从距离房子 \(x_1\)米处速接近,你们放置了攻击力为 \(y_1\)点/秒的植物进行防御;第二关,在上一关基础上,僵尸队列排头增加一只血量为\(a_2\) 点的僵尸,与后一只僵尸距离\(d\) 米,从距离房 \(x_2\) 米处匀速接近,你们重新放置攻击力为\(y_2\) 点/秒的植物;……;第 \(n\) 关,僵尸队列共有 \(n\) 只僵尸,相邻两只僵尸距离 \(d\) 米,排头僵尸血量 \(a_n\) 点,排第二的 僵尸血量 \(a_{n-1}\) ,以此类推,排头僵尸从距离房子\(x_n\)米处匀速接近,其余僵尸跟随排头同时接近,你们重新放置攻击力为 \(y_n\) 点/秒的植物。

每只僵尸直线移动速度均为 \(1\)米/秒,由于植物射击速度远大于僵尸移动速度,可忽略植物子弹在空中的时间。所有僵尸同时出现并接近,因此当一只僵尸死亡后,下一只僵尸立刻开始受到植物子弹的伤害。

游戏得分取决于你们放置的植物攻击力的总和 \(\sum \limits _{i=1} ^{n}y_i\),和越小分数越高,为了追求分数上界,你们每关都要放置攻击力尽量小的植物。

作为SDOI2013的参赛选手,你们能保护出题人么?

对于第 \(i\) 轮的第 \(j\)只僵尸,打死它的充要条件为它前面的僵尸全部被打死

它需要走过的距离为 \(x_i+d\times(i-j+1)\) ,则最小的攻击为 \[\dfrac{\sum_{k=j}^{i}a_k}{x_i+d\times(i-j+1)}\] 则第 \(i\) 轮进攻的最小攻击\[y_i = \max\left(\dfrac{S_i-S_{j-1}}{x_i+d\times(i-j+1)}\right)\] 其中,\(S_i = \sum_{k=1}^{i}a_k\)

稍微观察一下式子,可以发现 \[y_i = \max\left(\dfrac{S_i-S_{j-1}}{(x_i+d\times i)-d\times(j-1)}\right)\] 即点 \((x_i+d\times i,i)\)\((d\times(j-1),j-1)\) 的直线斜率

而前者在仅与 \(i\)有关,可以看作定点;后者则与 \(i\)无关,仅与每只僵尸有关

考虑对 \((d\times(j-1),j-1)\)维护一个下凸壳

每一轮二分一个点使得其和定点所成直线斜率最大

把这些斜率加起来就是答案了

注意本题是四舍五入,而不是向下取整

时间复杂度 \(O(n\log n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e5+15)int n;struct vct{    int x,y;    vct operator-(const vct o)const    {        return {x-o.x,y-o.y};    }}stk[N];int d,sum[N],top;double cross(vct a,vct b){    return a.x*b.y-a.y*b.x;}double slope(vct a,vct b){    return 1.0*(a.y-b.y)/(a.x-b.x);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> d;    stk[0]={0,0};    double res=0;    for(int i=1,a,x; i<=n; i++)    {        cin >> a >> x;        sum[i]=sum[i-1]+a;        vct tmp={i*d,sum[i-1]};        while(top&&cross(stk[top]-stk[top-1],tmp-stk[top])<=0)            --top;        stk[++top]=tmp;        tmp={x+i*d,sum[i]};        int l=1,r=top;        while(l<r)        {            int mid=(l+r+1)>>1;            if(slope(stk[mid],tmp)>slope(stk[mid-1],tmp))                l=mid;            else r=mid-1;        }        res+=slope(stk[l],tmp);    }    cout << fixed << setprecision(0);    cout << res << endl;    return 0;}
]]>
@@ -7340,7 +7340,7 @@ /2022/03/28/dijkstra-ji-qi-fu-za-du-zheng-ming/ - Dijkstra及其复杂度证明

前言

本文主要围绕易混淆的复杂度分析进行讨论


Dijkstra

其实这个不叫迪杰斯特拉,这个叫/ˈdɛɪkstra/ qwq

一、小概念

先放几个简单概念

无向图:图中所有的边都是两端可达的,也就是可以从任意一端通过这条边

有向图:图中所有的边都是单向的,有一个起点和一个终点,方向为起点指向终点

重边:两个结点间多条存在完全一样的边,通常它们可能拥有不同的权重或属性

自环:起点和终点相同的边

简单图:图中不存在自环和重边,显然一定存在两个结点的度数相同

多重图:存在自环或重边

更多内容可以参考这里 ,这里只介绍了一些对本文有用的概念


二、时间复杂度分析

给定一张具有非负权重的图和一个源点 $s$ (起点),问结点 $t$ 与 $s$ 的最短距离是多少

设图中的结点数为 $n$ ,边数为 $m$ ,$n\le m$

算法流程:

  1. $\text{dis[s]=0} \land \forall u \in V\backslash S , \text{dis[}u\text{]}=+\infty$

  2. 从未确定最短路的点集 $T$ 中找到一个结点满足其最短路长度最小

  3. 将已确定最短路的点集 $S$ 的所有出边进行松弛操作
  4. 如果 $T\ne \varnothing$ ,重复1,2操作

1. 朴素dijkstra

操作1复杂度为 $O(n)$ ,操作2复杂度为 $O(n)$ ,每条都会被松弛一次

因此总时间复杂度为 $O(m+n^2)$

2.优先队列优化

显然操作1可以优化

考虑一般多重图,使用优先队列优化存在弊端

对于队首的元素,至多遍历 $d_i$ 条边 ,$\sum d_i = m$

注意到一次操作2中,同一结点可能入队多次,则时间复杂度为 $O(m)$

假设按某种固定顺序遍历出边,每次松弛操作都会使被松弛结点入队

因为存在重边,考虑构造重边以权重大小降序排列,即 $w(e_{k-1})\ge w(e_k),k\in\mathbb{Z}\land k\in[1,d_i]$

这样最坏可以做到 $O(m)$

因此找最短路最小的结点时间为 $O(\log m)$

每个结点至少被遍历一次

则总时间复杂度为 $O\left((n+m)\log m\right)$

优先队列优化的代码

priority_queue<node> q;void dijkstra(int st){memset(d,0x3f,(n+1)*sizeof(int));memset(vis,0,(n+1)*sizeof(int));d[st]=0;q.push({st,0});while(!q.empty()){int u=q.top().u;q.pop();if(vis[u])continue;vis[u]=1;for(int i=head[u]; i; i=e[i].next){int v=e[i].v,u=e[i].u;if(d[v]>d[u]+e[i].w){d[v]=d[u]+e[i].w;q.push({v,d[v]});}}}}

3.二叉堆优化

对于堆顶的元素,至多遍历 $d_i$ 条边 ,$\sum d_i = m$

由于二叉堆支持修改,我们每次遇到重复入队的情况只要修改就好了

因此找最短路最小的结点时间为 $O(\log n)$

每个结点至少被遍历一次

则总时间复杂度为 $O\left((n+m)\log n\right)$

4.斐波那契堆优化

原理同二叉堆,这个更快但是更难写,一般不使用

理论时间复杂度 $O(m + n\log n)$


总结

本文简单讨论了一下复杂度的问题

]]>
+ Dijkstra及其复杂度证明

前言

本文主要围绕易混淆的复杂度分析进行讨论


Dijkstra

其实这个不叫迪杰斯特拉,这个叫/ˈdɛɪkstra/ qwq

一、小概念

先放几个简单概念

无向图:图中所有的边都是两端可达的,也就是可以从任意一端通过这条边

有向图:图中所有的边都是单向的,有一个起点和一个终点,方向为起点指向终点

重边:两个结点间多条存在完全一样的边,通常它们可能拥有不同的权重或属性

自环:起点和终点相同的边

简单图:图中不存在自环和重边,显然一定存在两个结点的度数相同

多重图:存在自环或重边

更多内容可以参考这里,这里只介绍了一些对本文有用的概念


二、时间复杂度分析

给定一张具有非负权重的图和一个源点 \(s\) (起点),问结点 \(t\) 与 \(s\) 的最短距离是多少

设图中的结点数为 \(n\) ,边数为\(m\)\(n\le m\)

算法流程:

  1. \(\text{dis[s]=0} \land \forall u \inV\backslash S , \text{dis[}u\text{]}=+\infty\)

  2. 从未确定最短路的点集 \(T\)中找到一个结点满足其最短路长度最小

  3. 将已确定最短路的点集 \(S\)的所有出边进行松弛操作

  4. 如果 \(T\ne \varnothing\),重复1,2操作

1. 朴素dijkstra

操作1复杂度为 \(O(n)\),操作2复杂度为 \(O(n)\),每条都会被松弛一次

因此总时间复杂度为 \(O(m+n^2)\)

2.优先队列优化

显然操作1可以优化

考虑一般多重图,使用优先队列优化存在弊端

对于队首的元素,至多遍历 \(d_i\)条边 ,\(\sum d_i = m\)

注意到一次操作2中,同一结点可能入队多次,则时间复杂度为 \(O(m)\)

假设按某种固定顺序遍历出边,每次松弛操作都会使被松弛结点入队

因为存在重边,考虑构造重边以权重大小降序排列,即 \(w(e_{k-1})\ge w(e_k),k\in\mathbb{Z}\landk\in[1,d_i]\)

这样最坏可以做到 \(O(m)\)

因此找最短路最小的结点时间为 \(O(\logm)\)

每个结点至少被遍历一次

则总时间复杂度为 \(O\left((n+m)\logm\right)\)

优先队列优化的代码

priority_queue<node> q;void dijkstra(int st){memset(d,0x3f,(n+1)*sizeof(int));memset(vis,0,(n+1)*sizeof(int));d[st]=0;q.push({st,0});while(!q.empty()){int u=q.top().u;q.pop();if(vis[u])continue;vis[u]=1;for(int i=head[u]; i; i=e[i].next){int v=e[i].v,u=e[i].u;if(d[v]>d[u]+e[i].w){d[v]=d[u]+e[i].w;q.push({v,d[v]});}}}}

3.二叉堆优化

对于堆顶的元素,至多遍历 \(d_i\)条边 ,\(\sum d_i = m\)

由于二叉堆支持修改,我们每次遇到重复入队的情况只要修改就好了

因此找最短路最小的结点时间为 \(O(\logn)\)

每个结点至少被遍历一次

则总时间复杂度为 \(O\left((n+m)\logn\right)\)

4.斐波那契堆优化

原理同二叉堆,这个更快但是更难写,一般不使用

理论时间复杂度 \(O(m + n\logn)\)


总结

本文简单讨论了一下复杂度的问题

]]>
@@ -7367,7 +7367,7 @@ /2022/03/27/zui-xiao-shu-xing-tu-tarjan-de-dmst-suan-fa/ - 最小树形图 Tarjan的DMST算法

前言

网上怎么都是朱刘算法啊?

那我来写一篇 TarjanDMST 算法吧 qwq

注:本文的DMST采用左偏树+并查集实现

时间复杂度为 $O(E+V\log E)$

如果采用斐波那契堆则为 $O(E+V\log V)$

由于差别不大(其实是q779不会斐波那契堆),因此本文不会提及

如果需要模板题数据的可以私信我( qwq


最小树形图

模板题:P4716 【模板】最小树形图

题目描述

给定包含 $n$ 个结点, $m$ 条有向边的一个图。试求一棵以结点 $r$ 为根的最小树形图,并输出最小树形图每条边的权值之和,如果没有以 $r$ 为根的最小树形图,输出 $-1$。

1.朱刘算法(Edmonds算法)

有向图上的最小生成树(Directed Minimum Spanning Tree)称为最小树形图。

常用的算法是朱刘算法(也称 Edmonds 算法),可以在 $O(nm)$ 时间内解决最小树形图问题。

注意到有根最小树形图满足从根结点可以到达任意结点的性质

算法流程:

  1. 求出图中的最短弧集合 $E_0$
  2. 若 $E_0$ 中存在环,则对图进行缩点和重构,包括边权的修改和点的处理,转到步骤1
  3. 若 $E_0$ 中不存在环,则展开收缩点,即求得最小树形图,算法结束。

对于环上结点 $v$ 的入边,因为选择了一条这样的边相当于删除了一条环边

所以将边权设为 $w-\text{ine[v]}$,其中 $\text{ine[v]}$ 表示结点 $v$ 的最短弧(权值最小的入边)

本文不过多叙述朱刘算法,仅给出代码

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define M (int)(1e4+15)#define N (int)(1e3+5)struct Edge{    int u,v,w,next;}e[M];int n,m,rt;int pre[N],ine[N];int vis[N],id[N];int pos=1,head[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}int solve(){    int ans=0;    while(1)    {        for(int i=1; i<=n; i++)            ine[i]=INF;        for(int i=2; i<=pos; i++)        {            int u=e[i].u,v=e[i].v;            if(u!=v&&e[i].w<ine[v])                ine[v]=e[i].w,pre[v]=u;        }        for(int i=1; i<=n; i++)            if(i!=rt&&ine[i]==INF)return INF;        int cnt=0;        for(int i=1; i<=n; i++)            vis[i]=id[i]=0;        for(int i=1; i<=n; i++)        {            if(i==rt)continue;            ans+=ine[i];            int v=i;            while(vis[v]!=i&&!id[v]&&v!=rt)            {                vis[v]=i;                v=pre[v];            }            if(!id[v]&&v!=rt)            {                id[v]=++cnt;                for(int u=pre[v]; u!=v; u=pre[u])                    id[u]=cnt;            }        }        if(!cnt)break;        for(int i=1; i<=n; i++)            if(!id[i])id[i]=++cnt;        for(int i=2; i<=pos; i++)        {            int u=e[i].u,v=e[i].v;            e[i].u=id[u];e[i].v=id[v];            if(id[u]!=id[v])e[i].w-=ine[v];        }        rt=id[rt];        n=cnt;    }    return ans;}signed main(){    read(n);read(m);read(rt);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);    }    int res=solve();    write(res==INF?-1:res);pc('\n');    return 0;}

可以发现这就是个暴力 qwq


2.Tarjan的DMST算法(Tarjan的优化算法)

观察朱刘算法的实现

算法流程:

  1. 求出图中的最短弧集合 $E_0$
  2. 若 $E_0$ 中存在环,则对图进行缩点和重构,包括边权的修改和点的处理,转到步骤1
  3. 若 $E_0$ 中不存在环,则展开收缩点,即求得最小树形图,算法结束。

可以发现这是三种操作

  1. 查询最小值
  2. 整体减去一个数
  3. 合并若干集合

1,3操作可以通过左偏树(可并堆)直接维护

2操作可以通过在左偏树上设懒标记实现

朱刘算法每次需要判断是否存在环,很慢

于是我们尝试直接将原图缩成一个点(尽管实现的时候并不完全如此)

然而原图不一定是强连通图,不过我们只要通过添加 $n$ 条边权为 $+\infty$ 就可以让它强连通了

形象地描述,如果我们最后“迫不得已”选择了这些额外的边,则原图不存在最小树形图

本质上该算法就是不断将最短弧加入当前的最小树形图

当树形图中出现环时将环进行缩点处理

算法实现:

我们可以维护一个栈,每次将栈顶元素 $v$ 的最短弧的起点 $u$ 入栈

如果 $u$ 已经在栈中,则出现了环,将环进行缩点即可

注意如果计算答案时存在根节点 $r$ 的入边,显然不可计入答案

引理:下文代码中,建树时间复杂度为 $O(m)$

证明:

对于结点 $k$ ,设其共有 $d_k$ 条入边的

对于第 $i$ 轮合并,需合并 $\dfrac{d_k}{2^i}$ 次,且该轮合并左偏树上各有 $i$ 个结点

则复杂度为

$\because \sum d_k = m$

$\therefore$ 总复杂度为

证毕 。

时间复杂度为 $O(m+n\log m)$

证明:

由引理可知,建树复杂度为 $O(m)$

因为每个结点至多入栈一次,每次查询最短弧复杂度为 $O(\log m)$

则总复杂度为 $O(m+n\log m)$

证毕。

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e3+15)#define M (int)(2e4+15)// 全部两倍#define ls(x) t[x].ch[0]#define rs(x) t[x].ch[1]int n,m,r,cnt,f[N];int vis[N];int stk[N],top;queue<int> q[N];namespace leftist{    struct node    {        int ch[2],u,v,w,dist,tag;    }t[M];    int tot,rt[M];    int New(int u,int v,int w)    {        t[++tot]={0,0,u,v,w};        return tot;    }    void push_down(int x)    {        t[ls(x)].tag+=t[x].tag;        t[ls(x)].w+=t[x].tag;        t[rs(x)].tag+=t[x].tag;        t[rs(x)].w+=t[x].tag;        t[x].tag=0;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        push_down(x);push_down(y);        if(t[x].w>t[y].w)swap(x,y);        rs(x)=merge(rs(x),y);        if(t[rs(x)].dist>t[ls(x)].dist)            swap(ls(x),rs(x));        t[x].dist=t[rs(x)].dist+1;        return x;    }    int remove(int x)    {        push_down(x);        return merge(ls(x),rs(x));    }    void build()    {        for(int i=1; i<=n; i++)        {            if(q[i].empty())continue;            while(q[i].size()!=1)            {                int p1=q[i].front();q[i].pop();                int p2=q[i].front();q[i].pop();                p1=merge(p1,p2);                q[i].push(p1);            }            if(!q[i].empty())                rt[i]=merge(rt[i],q[i].front()),                q[i].pop();        }    }}int find(int x){    return f[x]==x?x:f[x]=find(f[x]);}signed main(){    using namespace leftist;    read(n);read(m);read(r);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        int p=New(u,v,w);        q[v].push(p);        // rt[v]=merge(rt[v],p);    }    for(int i=1; i<=n; i++)    {        int p=New(i>1?i-1:n,i,INF);        q[i].push(p);        // rt[i]=merge(rt[i],p);    }    build();    for(int i=1; i<=2*n; i++) f[i]=i;    stk[++top]=r;vis[r]=1;    int ans=0,cnt=n;    while(rt[stk[top]])    {        int &p=rt[stk[top]];        int u=find(t[p].u);        if(u==stk[top])        {            p=remove(p);            continue;        }        if(!vis[u])        {            stk[++top]=u;            vis[u]=1;            continue;        }        int q=++cnt;        while(vis[u])        {            int v=stk[top--];            vis[v]=0;f[v]=q;            node *tmp=&t[rt[v]];            tmp->tag-=tmp->w;            if(find(tmp->v)!=find(r))ans+=tmp->w;            rt[v]=remove(rt[v]);            rt[q]=merge(rt[q],rt[v]);        }        stk[++top]=q;        vis[q]=1;    }    ans=ans>=INF?-1:ans;    write(ans);pc('\n');    return 0;}


3.加强版模板题

这里有一道我写的模板题

可以卡掉暴力朱刘算法

大家可以在这里调试代码

题目链接U210116 【模板】最小树形图(加强版)

这里是std

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e5+15)#define M (int)(2e6+15)#define ls(x) t[x].ch[0]#define rs(x) t[x].ch[1]int n,m,r,cnt,f[N];int vis[N],stk[N],top;queue<int> q[N];namespace leftist{    struct node    {        int ch[2],u,v,w,dist,tag;    }t[M];    int tot,rt[M];    int New(int u,int v,int w)    {        t[++tot]={0,0,u,v,w};        return tot;    }    void push_down(int x)    {        t[ls(x)].tag+=t[x].tag;        t[ls(x)].w+=t[x].tag;        t[rs(x)].tag+=t[x].tag;        t[rs(x)].w+=t[x].tag;        t[x].tag=0;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        push_down(x);push_down(y);        if(t[x].w>t[y].w)swap(x,y);        rs(x)=merge(rs(x),y);        if(t[rs(x)].dist>t[ls(x)].dist)            swap(ls(x),rs(x));        t[x].dist=t[rs(x)].dist+1;        return x;    }    int remove(int x)    {        push_down(x);        return merge(ls(x),rs(x));    }    void build()    {        for(int i=1; i<=n; i++)        {            if(q[i].empty())continue;            while(q[i].size()!=1)            {                int p1=q[i].front();q[i].pop();                int p2=q[i].front();q[i].pop();                p1=merge(p1,p2);                q[i].push(p1);            }            if(!q[i].empty())            {                rt[i]=merge(rt[i],q[i].front());                q[i].pop();            }        }    }}int find(int x){return f[x]==x?f[x]:f[x]=find(f[x]);}// double BEGIN_CLOCK,END_CLOCK;signed main(){// freopen("2.in","r",stdin);// freopen("2.out","w",stdout);// ofstream outfile;    // outfile << fixed << setprecision(4);    // outfile.open("check.txt");    // // outfile << fixed;    // BEGIN_CLOCK=clock();    using namespace leftist;    read(n);read(m);read(r);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        int p=New(u,v,w);        q[v].push(p);        // rt[v]=merge(rt[v],p);    }    for(int i=1; i<=n; i++)    {        int p=New(i>1?i-1:n,i,INF);        q[i].push(p);        // rt[i]=merge(rt[i],p);    }    build();    for(int i=1; i<=2*n; i++)f[i]=i;    stk[++top]=r;vis[r]=1;    int ans=0,cnt=n;    while(rt[stk[top]])    {if(ans>=INF)return puts("-1"),0;        int &p=rt[stk[top]];        int u=find(t[p].u);        if(u==stk[top])        {            p=remove(p);            continue;        }        if(!vis[u])        {            stk[++top]=u;            vis[u]=1;            continue;        }        int q=++cnt;        while(vis[u])        {            int v=stk[top--];            vis[v]=0;f[v]=q;            node *tmp=&t[rt[v]];            tmp->tag-=tmp->w;            if(find(tmp->v)!=find(r))                ans+=tmp->w;            rt[v]=remove(rt[v]);            rt[q]=merge(rt[q],rt[v]);        }        stk[++top]=q;        vis[q]=1;    }    write(ans);pc('\n');// END_CLOCK=clock();    // outfile << "运行时间: " << (double)((END_CLOCK-BEGIN_CLOCK)/CLOCKS_PER_SEC) << "s" << endl;    // outfile.close();    return 0;}


总结

本文简单介绍了Tarjan的DMST算法及左偏树实现方法



参考文献

[1] OI Wiki 最小树形图

[2] CHiCO的博客 题解 P4716

[3] 左偏树简介

]]>
+ 最小树形图 Tarjan的DMST算法

前言

网上怎么都是朱刘算法啊?

那我来写一篇 TarjanDMST 算法吧qwq

注:本文的DMST采用左偏树+并查集实现

时间复杂度为 \(O(E+V\log E)\)

如果采用斐波那契堆则为 \(O(E+V\logV)\)

由于差别不大(其实是q779不会斐波那契堆),因此本文不会提及

如果需要模板题数据的可以私信我( qwq


最小树形图

模板题:P4716【模板】最小树形图

题目描述

给定包含 \(n\) 个结点, \(m\) 条有向边的一个图。试求一棵以结点 \(r\)为根的最小树形图,并输出最小树形图每条边的权值之和,如果没有以 \(r\) 为根的最小树形图,输出 \(-1\)。

1.朱刘算法(Edmonds算法)

有向图上的最小生成树(Directed Minimum SpanningTree)称为最小树形图。

常用的算法是朱刘算法(也称 Edmonds 算法),可以在 \(O(nm)\) 时间内解决最小树形图问题。

注意到有根最小树形图满足从根结点可以到达任意结点的性质

算法流程:

  1. 求出图中的最短弧集合 \(E_0\)
  2. \(E_0\)中存在环,则对图进行缩点和重构,包括边权的修改和点的处理,转到步骤1
  3. \(E_0\)中不存在环,则展开收缩点,即求得最小树形图,算法结束。

对于环上结点 \(v\)的入边,因为选择了一条这样的边相当于删除了一条环边

所以将边权设为 \(w-\text{ine[v]}\),其中 \(\text{ine[v]}\) 表示结点 \(v\) 的最短弧(权值最小的入边)

本文不过多叙述朱刘算法,仅给出代码

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define M (int)(1e4+15)#define N (int)(1e3+5)struct Edge{    int u,v,w,next;}e[M];int n,m,rt;int pre[N],ine[N];int vis[N],id[N];int pos=1,head[N];void addEdge(int u,int v,int w){    e[++pos]={u,v,w,head[u]};    head[u]=pos;}int solve(){    int ans=0;    while(1)    {        for(int i=1; i<=n; i++)            ine[i]=INF;        for(int i=2; i<=pos; i++)        {            int u=e[i].u,v=e[i].v;            if(u!=v&&e[i].w<ine[v])                ine[v]=e[i].w,pre[v]=u;        }        for(int i=1; i<=n; i++)            if(i!=rt&&ine[i]==INF)return INF;        int cnt=0;        for(int i=1; i<=n; i++)            vis[i]=id[i]=0;        for(int i=1; i<=n; i++)        {            if(i==rt)continue;            ans+=ine[i];            int v=i;            while(vis[v]!=i&&!id[v]&&v!=rt)            {                vis[v]=i;                v=pre[v];            }            if(!id[v]&&v!=rt)            {                id[v]=++cnt;                for(int u=pre[v]; u!=v; u=pre[u])                    id[u]=cnt;            }        }        if(!cnt)break;        for(int i=1; i<=n; i++)            if(!id[i])id[i]=++cnt;        for(int i=2; i<=pos; i++)        {            int u=e[i].u,v=e[i].v;            e[i].u=id[u];e[i].v=id[v];            if(id[u]!=id[v])e[i].w-=ine[v];        }        rt=id[rt];        n=cnt;    }    return ans;}signed main(){    read(n);read(m);read(rt);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);    }    int res=solve();    write(res==INF?-1:res);pc('\n');    return 0;}

可以发现这就是个暴力 qwq


2.Tarjan的DMST算法(Tarjan的优化算法)

观察朱刘算法的实现

算法流程:

  1. 求出图中的最短弧集合 \(E_0\)
  2. \(E_0\)中存在环,则对图进行缩点和重构,包括边权的修改和点的处理,转到步骤1
  3. \(E_0\)中不存在环,则展开收缩点,即求得最小树形图,算法结束。

可以发现这是三种操作

  1. 查询最小值
  2. 整体减去一个数
  3. 合并若干集合

1,3操作可以通过左偏树(可并堆)直接维护

2操作可以通过在左偏树上设懒标记实现

朱刘算法每次需要判断是否存在环,很慢

于是我们尝试直接将原图缩成一个点(尽管实现的时候并不完全如此)

然而原图不一定是强连通图,不过我们只要通过添加 \(n\) 条边权为 \(+\infty\) 就可以让它强连通了

形象地描述,如果我们最后“迫不得已”选择了这些额外的边,则原图不存在最小树形图

本质上该算法就是不断将最短弧加入当前的最小树形图

当树形图中出现环时将环进行缩点处理

算法实现:

我们可以维护一个栈,每次将栈顶元素 \(v\) 的最短弧的起点 \(u\) 入栈

如果 \(u\)已经在栈中,则出现了环,将环进行缩点即可

注意如果计算答案时存在根节点 \(r\)的入边,显然不可计入答案

引理:下文代码中,建树时间复杂度为 \(O(m)\)

证明:

对于结点 \(k\) ,设其共有 \(d_k\) 条入边的

对于第 \(i\) 轮合并,需合并 \(\dfrac{d_k}{2^i}\)次,且该轮合并左偏树上各有 \(i\)个结点

则复杂度为 \[O\left(\sum\limits_{i=1}^{\log_2 d_k}{\dfrac{d_k}{2^i}\times\left(2\log_2{i} + 1\right)}\right) = O(d_k)\] \(\because \sum d_k = m\)

\(\therefore\) 总复杂度为 \[O\left(\sum_{d_k}\sum\limits_{i=1}^{\log_2 d_k}{\dfrac{d_k}{2^i}\times\left(2\log_2{i} + 1\right)}\right) = O(m)\] 证毕 。

时间复杂度为 \(O(m+n\log m)\)

证明:

由引理可知,建树复杂度为 \(O(m)\)

因为每个结点至多入栈一次,每次查询最短弧复杂度为 \(O(\log m)\)

则总复杂度为 \(O(m+n\log m)\)

证毕。

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e3+15)#define M (int)(2e4+15)// 全部两倍#define ls(x) t[x].ch[0]#define rs(x) t[x].ch[1]int n,m,r,cnt,f[N];int vis[N];int stk[N],top;queue<int> q[N];namespace leftist{    struct node    {        int ch[2],u,v,w,dist,tag;    }t[M];    int tot,rt[M];    int New(int u,int v,int w)    {        t[++tot]={0,0,u,v,w};        return tot;    }    void push_down(int x)    {        t[ls(x)].tag+=t[x].tag;        t[ls(x)].w+=t[x].tag;        t[rs(x)].tag+=t[x].tag;        t[rs(x)].w+=t[x].tag;        t[x].tag=0;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        push_down(x);push_down(y);        if(t[x].w>t[y].w)swap(x,y);        rs(x)=merge(rs(x),y);        if(t[rs(x)].dist>t[ls(x)].dist)            swap(ls(x),rs(x));        t[x].dist=t[rs(x)].dist+1;        return x;    }    int remove(int x)    {        push_down(x);        return merge(ls(x),rs(x));    }    void build()    {        for(int i=1; i<=n; i++)        {            if(q[i].empty())continue;            while(q[i].size()!=1)            {                int p1=q[i].front();q[i].pop();                int p2=q[i].front();q[i].pop();                p1=merge(p1,p2);                q[i].push(p1);            }            if(!q[i].empty())                rt[i]=merge(rt[i],q[i].front()),                q[i].pop();        }    }}int find(int x){    return f[x]==x?x:f[x]=find(f[x]);}signed main(){    using namespace leftist;    read(n);read(m);read(r);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        int p=New(u,v,w);        q[v].push(p);        // rt[v]=merge(rt[v],p);    }    for(int i=1; i<=n; i++)    {        int p=New(i>1?i-1:n,i,INF);        q[i].push(p);        // rt[i]=merge(rt[i],p);    }    build();    for(int i=1; i<=2*n; i++) f[i]=i;    stk[++top]=r;vis[r]=1;    int ans=0,cnt=n;    while(rt[stk[top]])    {        int &p=rt[stk[top]];        int u=find(t[p].u);        if(u==stk[top])        {            p=remove(p);            continue;        }        if(!vis[u])        {            stk[++top]=u;            vis[u]=1;            continue;        }        int q=++cnt;        while(vis[u])        {            int v=stk[top--];            vis[v]=0;f[v]=q;            node *tmp=&t[rt[v]];            tmp->tag-=tmp->w;            if(find(tmp->v)!=find(r))ans+=tmp->w;            rt[v]=remove(rt[v]);            rt[q]=merge(rt[q],rt[v]);        }        stk[++top]=q;        vis[q]=1;    }    ans=ans>=INF?-1:ans;    write(ans);pc('\n');    return 0;}

3.加强版模板题

这里有一道我写的模板题

可以卡掉暴力朱刘算法

大家可以在这里调试代码

题目链接U210116【模板】最小树形图(加强版)

这里是std

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(2e5+15)#define M (int)(2e6+15)#define ls(x) t[x].ch[0]#define rs(x) t[x].ch[1]int n,m,r,cnt,f[N];int vis[N],stk[N],top;queue<int> q[N];namespace leftist{    struct node    {        int ch[2],u,v,w,dist,tag;    }t[M];    int tot,rt[M];    int New(int u,int v,int w)    {        t[++tot]={0,0,u,v,w};        return tot;    }    void push_down(int x)    {        t[ls(x)].tag+=t[x].tag;        t[ls(x)].w+=t[x].tag;        t[rs(x)].tag+=t[x].tag;        t[rs(x)].w+=t[x].tag;        t[x].tag=0;    }    int merge(int x,int y)    {        if(!x||!y)return x|y;        push_down(x);push_down(y);        if(t[x].w>t[y].w)swap(x,y);        rs(x)=merge(rs(x),y);        if(t[rs(x)].dist>t[ls(x)].dist)            swap(ls(x),rs(x));        t[x].dist=t[rs(x)].dist+1;        return x;    }    int remove(int x)    {        push_down(x);        return merge(ls(x),rs(x));    }    void build()    {        for(int i=1; i<=n; i++)        {            if(q[i].empty())continue;            while(q[i].size()!=1)            {                int p1=q[i].front();q[i].pop();                int p2=q[i].front();q[i].pop();                p1=merge(p1,p2);                q[i].push(p1);            }            if(!q[i].empty())            {                rt[i]=merge(rt[i],q[i].front());                q[i].pop();            }        }    }}int find(int x){return f[x]==x?f[x]:f[x]=find(f[x]);}// double BEGIN_CLOCK,END_CLOCK;signed main(){// freopen("2.in","r",stdin);// freopen("2.out","w",stdout);// ofstream outfile;    // outfile << fixed << setprecision(4);    // outfile.open("check.txt");    // // outfile << fixed;    // BEGIN_CLOCK=clock();    using namespace leftist;    read(n);read(m);read(r);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        int p=New(u,v,w);        q[v].push(p);        // rt[v]=merge(rt[v],p);    }    for(int i=1; i<=n; i++)    {        int p=New(i>1?i-1:n,i,INF);        q[i].push(p);        // rt[i]=merge(rt[i],p);    }    build();    for(int i=1; i<=2*n; i++)f[i]=i;    stk[++top]=r;vis[r]=1;    int ans=0,cnt=n;    while(rt[stk[top]])    {if(ans>=INF)return puts("-1"),0;        int &p=rt[stk[top]];        int u=find(t[p].u);        if(u==stk[top])        {            p=remove(p);            continue;        }        if(!vis[u])        {            stk[++top]=u;            vis[u]=1;            continue;        }        int q=++cnt;        while(vis[u])        {            int v=stk[top--];            vis[v]=0;f[v]=q;            node *tmp=&t[rt[v]];            tmp->tag-=tmp->w;            if(find(tmp->v)!=find(r))                ans+=tmp->w;            rt[v]=remove(rt[v]);            rt[q]=merge(rt[q],rt[v]);        }        stk[++top]=q;        vis[q]=1;    }    write(ans);pc('\n');// END_CLOCK=clock();    // outfile << "运行时间: " << (double)((END_CLOCK-BEGIN_CLOCK)/CLOCKS_PER_SEC) << "s" << endl;    // outfile.close();    return 0;}

总结

本文简单介绍了Tarjan的DMST算法及左偏树实现方法


参考文献

[1] OI Wiki最小树形图

[2] CHiCO的博客题解 P4716

[3] 左偏树简介

]]>
@@ -7394,7 +7394,7 @@ /2022/03/26/luo-gu-p5826-mo-ban-zi-xu-lie-zi-dong-ji-ti-jie/ - 洛谷P5826 【模板】子序列自动机

题目链接:P5826 【模板】子序列自动机

题意:给定一个主序列,每次给出一个序列,判断是否为其子序列

我们可以把每个字符的出现位置用vector维护

然后对于每个询问,直接二分离当前位置最近的那个位置

使其变为当前位置,如果不存在则输出No

为什么选择最近的位置?贪心可知,选择尽可能近的位置可以产生更多的子序列

这个算法也叫子序列自动机

时间复杂度 $O(|S| + \sum|s_i|\log|S|)$

代码如下(稍微卡了卡常)

#include <bits/stdc++.h>using namespace std;#define int long long#define SIZ (int)(1e5+15)#define gc() readchar()#define pc(a) putchar(a)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)vector<int>pos[N];int n,Q,_,m,a[N],b[N],lstpos,ok;signed main(){    read(_);read(n);read(Q);read(_);    for(int i=1; i<=n; i++)    {        read(a[i]);        pos[a[i]].push_back(i);    }    while(Q--)    {        read(m);ok=1;lstpos=0;        for(int i=1; i<=m; i++)            read(b[i]);        for(int i=1; i<=m; i++)        {            auto p=upper_bound(pos[b[i]].begin(),            pos[b[i]].end(),lstpos);            if(p==pos[b[i]].end()){ok=0;break;}            lstpos=*p;        }        puts(ok?"Yes":"No");    }    return 0;}
]]>
+ 洛谷P5826 【模板】子序列自动机

题目链接:P5826【模板】子序列自动机

题意:给定一个主序列,每次给出一个序列,判断是否为其子序列

我们可以把每个字符的出现位置用vector维护

然后对于每个询问,直接二分离当前位置最近的那个位置

使其变为当前位置,如果不存在则输出No

为什么选择最近的位置?贪心可知,选择尽可能近的位置可以产生更多的子序列

这个算法也叫子序列自动机

时间复杂度 \(O(|S| +\sum|s_i|\log|S|)\)

代码如下(稍微卡了卡常)

#include <bits/stdc++.h>using namespace std;#define int long long#define SIZ (int)(1e5+15)#define gc() readchar()#define pc(a) putchar(a)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e5+15)vector<int>pos[N];int n,Q,_,m,a[N],b[N],lstpos,ok;signed main(){    read(_);read(n);read(Q);read(_);    for(int i=1; i<=n; i++)    {        read(a[i]);        pos[a[i]].push_back(i);    }    while(Q--)    {        read(m);ok=1;lstpos=0;        for(int i=1; i<=m; i++)            read(b[i]);        for(int i=1; i<=m; i++)        {            auto p=upper_bound(pos[b[i]].begin(),            pos[b[i]].end(),lstpos);            if(p==pos[b[i]].end()){ok=0;break;}            lstpos=*p;        }        puts(ok?"Yes":"No");    }    return 0;}
]]>
@@ -7419,7 +7419,7 @@ /2022/03/14/luo-gu-p3919-mo-ban-ke-chi-jiu-hua-xian-duan-shu-1-ke-chi-jiu-hua-shu-zu-ti-jie/ - 洛谷P3919 【模板】可持久化线段树 1(可持久化数组) 题解

题目链接:P3919 【模板】可持久化线段树 1(可持久化数组)

题意:如题,你需要维护这样的一个长度为 NN 的数组,支持如下几种操作

  1. 在某个历史版本上修改某一个位置上的值
  2. 访问某个历史版本上的某一位置的值

此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)

解法一、主席树

注意到历史版本+单点修改,容易想到可持久化线段树

和区间 $k$ 小值稍微有些不同

时间复杂度 $O(n\log n)$

本题的数据有点极限,我的写法开 long long会挂 😓

代码如下

#include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+25)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e6+5)int n,Q;int a[N],T[N],tot;struct node{    int ch[2],num;}t[N<<5];#define ls(at) t[at].ch[0]#define rs(at) t[at].ch[1]int build(int l,int r){    int at=++tot,mid=(l+r)>>1;    if(l==r){t[at].num=a[l];return at;}    ls(at)=build(l,mid);    rs(at)=build(mid+1,r);    return at;}int update(int pre,int l,int r,int x,int v){    int at=++tot,mid=(l+r)>>1;    t[at]=t[pre];    if(l==r)    {        t[at].num=v;        return at;    }    if(x<=mid)ls(at)=update(ls(pre),l,mid,x,v);    else rs(at)=update(rs(pre),mid+1,r,x,v);    return at;}int query(int at,int l,int r,int x){    if(l==r)return t[at].num;    int mid=(l+r)>>1;    if(x<=mid)return query(ls(at),l,mid,x);    else return query(rs(at),mid+1,r,x);}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    T[0]=build(1,n);    for(int i=1; i<=Q; i++)    {        int rt,op,x,y;        read(rt);read(op);read(x);        if(op==1)        {            read(y);            T[i]=update(T[rt],1,n,x,y);        }else        {            write(query(T[rt],1,n,x));            pc('\n');T[i]=T[rt];        }    }    return 0;}

解法二、建树

这个解法是luogu题解区的Elegia神仙提出的

具体地,注意到每次修改都依赖于先前的版本

考虑建立关系树,离线处理修改操作

对于每个询问,直接记录答案,对于每个修改,则临时修改,然后dfs完恢复

不难看出这个解法可以看出有两个前提

  1. 不强制在线
  2. 修改操作可逆

显然这题满足

时间复杂度 $O(n)$

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+25)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e6+5)int n,Q;int a[N],op[N],k[N],x[N],ans[N];vector<int>vec[N];void dfs(int u){    if(op[u]==2)    {        ans[u]=a[k[u]];        for(int v:vec[u])dfs(v);    }else    {        swap(a[k[u]],x[u]);        for(int v:vec[u])dfs(v);        swap(a[k[u]],x[u]);    }}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    op[0]=2;    for(int i=1; i<=Q; i++)    {        int rt;        read(rt);read(op[i]);read(k[i]);        if(op[i]==1)            read(x[i]);        vec[rt].push_back(i);    }    dfs(0);    for(int i=1; i<=Q; i++)        if(op[i]==2)            write(ans[i]),pc('\n');    return 0;}
]]>
+ 洛谷P3919【模板】可持久化线段树 1(可持久化数组) 题解

题目链接:P3919【模板】可持久化线段树 1(可持久化数组)

题意:如题,你需要维护这样的一个长度为 NN的数组,支持如下几种操作

  1. 在某个历史版本上修改某一个位置上的值
  2. 访问某个历史版本上的某一位置的值

此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)

解法一、主席树

注意到历史版本+单点修改,容易想到可持久化线段树

和区间 \(k\) 小值稍微有些不同

时间复杂度 \(O(n\log n)\)

本题的数据有点极限,我的写法开 long long会挂 😓

代码如下

#include <bits/stdc++.h>using namespace std;// #define int long long// #define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+25)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e6+5)int n,Q;int a[N],T[N],tot;struct node{    int ch[2],num;}t[N<<5];#define ls(at) t[at].ch[0]#define rs(at) t[at].ch[1]int build(int l,int r){    int at=++tot,mid=(l+r)>>1;    if(l==r){t[at].num=a[l];return at;}    ls(at)=build(l,mid);    rs(at)=build(mid+1,r);    return at;}int update(int pre,int l,int r,int x,int v){    int at=++tot,mid=(l+r)>>1;    t[at]=t[pre];    if(l==r)    {        t[at].num=v;        return at;    }    if(x<=mid)ls(at)=update(ls(pre),l,mid,x,v);    else rs(at)=update(rs(pre),mid+1,r,x,v);    return at;}int query(int at,int l,int r,int x){    if(l==r)return t[at].num;    int mid=(l+r)>>1;    if(x<=mid)return query(ls(at),l,mid,x);    else return query(rs(at),mid+1,r,x);}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    T[0]=build(1,n);    for(int i=1; i<=Q; i++)    {        int rt,op,x,y;        read(rt);read(op);read(x);        if(op==1)        {            read(y);            T[i]=update(T[rt],1,n,x,y);        }else        {            write(query(T[rt],1,n,x));            pc('\n');T[i]=T[rt];        }    }    return 0;}

解法二、建树

这个解法是luogu题解区的Elegia神仙提出的

具体地,注意到每次修改都依赖于先前的版本

考虑建立关系树,离线处理修改操作

对于每个询问,直接记录答案,对于每个修改,则临时修改,然后dfs完恢复

不难看出这个解法可以看出有两个前提

  1. 不强制在线
  2. 修改操作可逆

显然这题满足

时间复杂度 \(O(n)\)

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+25)char buf1[SIZ];char *p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}#define N (int)(1e6+5)int n,Q;int a[N],op[N],k[N],x[N],ans[N];vector<int>vec[N];void dfs(int u){    if(op[u]==2)    {        ans[u]=a[k[u]];        for(int v:vec[u])dfs(v);    }else    {        swap(a[k[u]],x[u]);        for(int v:vec[u])dfs(v);        swap(a[k[u]],x[u]);    }}signed main(){    read(n);read(Q);    for(int i=1; i<=n; i++)        read(a[i]);    op[0]=2;    for(int i=1; i<=Q; i++)    {        int rt;        read(rt);read(op[i]);read(k[i]);        if(op[i]==1)            read(x[i]);        vec[rt].push_back(i);    }    dfs(0);    for(int i=1; i<=Q; i++)        if(op[i]==2)            write(ans[i]),pc('\n');    return 0;}
]]>
@@ -7446,7 +7446,7 @@ /2022/03/12/luo-gu-p1129-zjoi2007-ju-zhen-you-xi-ti-jie/ - 洛谷P1129 [ZJOI2007] 矩阵游戏 题解

题目链接:P1129 [ZJOI2007] 矩阵游戏

题意:给定一张有黑白棋子的正方形棋盘,问存不存在解法使得经过若干次交换行或列的操作后,左上角至右下角的对角线上所有的点放着黑色棋子

说实话第一眼看到这个题我连暴力都不知道咋打

这道题的建模挺有意思的

对于 $\forall i \in \mathbb{Z}, 1\le i \le n$ ,要求第 $i$ 行第 $i$ 列为黑色

不妨可以看作行结点 $i$ 与列结点 $i$ 之间有一条无向边(显然没有权重)

则交换行 $u,v$ 的操作,就可以看作重命名的操作

问题就可以转化为行结点集 $V_a$ 与列结点集 $V_b$ 求二分图最大匹配

当最大匹配值为 $n$ 时,存在解

时间复杂度 $O(Qn^2)$ ,$Q$ 为数据的组数

代码如下(忽略那个快读,其实没有什么大用处 qwq)

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}// ---------------------------------------------------- qwq#define N (int)(205)int n,Q,vis[N],mch[N],d[N][N];int dfs(int u,int now){    if(vis[u]==now)        return 0;    vis[u]=now;    for(int i=1; i<=n; i++)        if(d[u][i]&&(!mch[i]||dfs(mch[i],now)))            {mch[i]=u;return 1;}    return 0;}int hgn(){    memset(vis,0,sizeof(vis));    memset(mch,0,sizeof(mch));    int ans=0;    for(int i=1; i<=n; i++)        ans+=dfs(i,i);    return ans;}signed main(){    read(Q);    while(Q--)    {        read(n);        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                read(d[i][j]);        puts((hgn()==n)?"Yes":"No");    }    return 0;}
]]>
+ 洛谷P1129 [ZJOI2007] 矩阵游戏题解

题目链接:P1129[ZJOI2007] 矩阵游戏

题意:给定一张有黑白棋子的正方形棋盘,问存不存在解法使得经过若干次交换行或列的操作后,左上角至右下角的对角线上所有的点放着黑色棋子

说实话第一眼看到这个题我连暴力都不知道咋打

这道题的建模挺有意思的

对于 \(\forall i \in \mathbb{Z}, 1\le i \len\) ,要求第 \(i\) 行第 \(i\) 列为黑色

不妨可以看作行结点 \(i\) 与列结点\(i\)之间有一条无向边(显然没有权重)

则交换行 \(u,v\)的操作,就可以看作重命名的操作

问题就可以转化为行结点集 \(V_a\)与列结点集 \(V_b\) 求二分图最大匹配

当最大匹配值为 \(n\) 时,存在解

时间复杂度 \(O(Qn^2)\)\(Q\) 为数据的组数

代码如下(忽略那个快读,其实没有什么大用处 qwq)

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() readchar()#define pc(a) putchar(a)#define SIZ (int)(1e5+15)char buf1[SIZ],*p1=buf1,*p2=buf1;char readchar(){    if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);    return p1==p2?EOF:*p1++;}template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    static T stk[66];T top=0;    do{stk[top++]=k%10,k/=10;}while(k);    while(top){pc(stk[--top]+'0');}}// ---------------------------------------------------- qwq#define N (int)(205)int n,Q,vis[N],mch[N],d[N][N];int dfs(int u,int now){    if(vis[u]==now)        return 0;    vis[u]=now;    for(int i=1; i<=n; i++)        if(d[u][i]&&(!mch[i]||dfs(mch[i],now)))            {mch[i]=u;return 1;}    return 0;}int hgn(){    memset(vis,0,sizeof(vis));    memset(mch,0,sizeof(mch));    int ans=0;    for(int i=1; i<=n; i++)        ans+=dfs(i,i);    return ans;}signed main(){    read(Q);    while(Q--)    {        read(n);        for(int i=1; i<=n; i++)            for(int j=1; j<=n; j++)                read(d[i][j]);        puts((hgn()==n)?"Yes":"No");    }    return 0;}
]]>
@@ -7471,7 +7471,7 @@ /2022/03/02/kd-tree-kdt-shi-jian-fu-za-du-zheng-ming/ - kd-tree(KDT) 时间复杂度证明

kd-tree 是一种可以高效处理 $k$ 维空间的数据结构

在算法竞赛类的题目中一般有 $k=2$

还有个比较有趣的结论,当 $k=1$ 时其实它就是一棵线段树

下文中的 $n$ 为kd-tree中的结点数量, $k$ 为kd-tree的维度


一、建树

一般建树有三种

  1. 随机划分(玄学)
  2. 轮换划分:每个维度轮着划分
  3. 方差划分:优先划分方差较大的维度

我可不想分析玄学的划分

1. 轮换划分

显然有递推式 $T(n) = 2T(n/2) + O(n)$

根据主定理,有 $a=2,b=2,\log_b{a}=1,f(n)=O(n)$

$\because \exist \epsilon \ge 0$ 使得 $f(n) = \Theta(n^{\log_b{a}}\log^{\epsilon}n)$ ,此时 $\epsilon = 0$

$\therefore T(n) = \Theta(n\log n)$

2. 方差划分

注意到 $T(n) = 2T(n/2) + O(kn)$

因此时间复杂度为 $T(n) = \Theta(nk\log n)$

一般在算法竞赛中,由于 $k$ 很小(一般 $k=2$ ),因此有

$T(n) = \Theta(n\log n)$

本划分方法能较好保证树高,且不易被卡


二、插入&删除

一般采用替罪羊树的插入&删除操作

利用重构子树的方式维持平衡

均摊复杂度为 $O(\log n)$

证明可以去看替罪羊树的,这里先留个坑以后补上


三、Range Query

个人感觉算kd-tree的核心操作吧

支持将一个超长方体区域内的点划分为 $O(\sqrt{n})$ 个点所管辖的区域

称 $R$ 为待查询的超长方体,则对于树上结点所管辖的超长方体分类,存在以下三种情况

  1. 与 $R$ 的交集为空
  2. 全部包含于 $R$ 内

  3. 与 $R$ 有交集且不包含于 $R$

算法在查询过程中,碰到第1,2类点不会继续递归其子树

因此算法的时间复杂度就与第3类点的数量有关

我们以轮换划分来分析,则在相邻的 $k$ 轮中,分别对每一维进行了划分

显然会产生 $2^k$ 个部分,每个部分的大小均为原来的 $\dfrac{1}{2^k}$

由于一个用于划分的超平面至多跨越 $2^{k-1}$ 个部分(可以由归纳法证明,此处略)

则有 $T(n) = 2^{k-1}T(n/2^k)+O(1)$

根据主定理,有 $a=2^{k-1},b=2^k,\log_b{a} = \dfrac{k-1}{k},f(n)=O(1)$

$\because \exist \epsilon > 0$ 使得 $f(n) = O(n^{\log_b{a}-\epsilon})$ ,此时 $\epsilon = \dfrac{k-1}{k}$

$\therefore T(n) = \Theta\left(n^{1-\frac{1}{k}}\right)$

当 $k=2$ 时, $T(n) = \Theta(\sqrt{n})$



参考文献

[1] KDT小记

]]>
+ kd-tree(KDT) 时间复杂度证明

kd-tree 是一种可以高效处理 \(k\)维空间的数据结构

在算法竞赛类的题目中一般有 \(k=2\)

还有个比较有趣的结论,当 \(k=1\)时其实它就是一棵线段树

下文中的 \(n\)为kd-tree中的结点数量, \(k\)为kd-tree的维度


一、建树

一般建树有三种

  1. 随机划分(玄学)
  2. 轮换划分:每个维度轮着划分
  3. 方差划分:优先划分方差较大的维度

我可不想分析玄学的划分

1. 轮换划分

显然有递推式 \(T(n) = 2T(n/2) +O(n)\)

根据主定理,有 \(a=2,b=2,\log_b{a}=1,f(n)=O(n)\)

\(\because \exist \epsilon \ge 0\)使得 \(f(n) =\Theta(n^{\log_b{a}}\log^{\epsilon}n)\) ,此时 \(\epsilon = 0\)

\(\therefore T(n) = \Theta(n\logn)\)

2. 方差划分

注意到 \(T(n) = 2T(n/2) +O(kn)\)

因此时间复杂度为 \(T(n) = \Theta(nk\logn)\)

一般在算法竞赛中,由于 \(k\)很小(一般 \(k=2\) ),因此有

\(T(n) = \Theta(n\log n)\)

本划分方法能较好保证树高,且不易被卡


二、插入&删除

一般采用替罪羊树的插入&删除操作

利用重构子树的方式维持平衡

均摊复杂度为 \(O(\log n)\)

证明可以去看替罪羊树的,这里先留个坑以后补上


三、Range Query

个人感觉算kd-tree的核心操作吧

支持将一个超长方体区域内的点划分为 \(O(\sqrt{n})\) 个点所管辖的区域

\(R\)为待查询的超长方体,则对于树上结点所管辖的超长方体分类,存在以下三种情况

  1. \(R\) 的交集为空

  2. 全部包含于 \(R\)

  3. \(R\) 有交集且不包含于 \(R\)

算法在查询过程中,碰到第1,2类点不会继续递归其子树

因此算法的时间复杂度就与第3类点的数量有关

我们以轮换划分来分析,则在相邻的 \(k\) 轮中,分别对每一维进行了划分

显然会产生 \(2^k\)个部分,每个部分的大小均为原来的 \(\dfrac{1}{2^k}\)

由于一个用于划分的超平面至多跨越 \(2^{k-1}\)个部分(可以由归纳法证明,此处略)

则有 \(T(n) =2^{k-1}T(n/2^k)+O(1)\)

根据主定理,有 \(a=2^{k-1},b=2^k,\log_b{a}= \dfrac{k-1}{k},f(n)=O(1)\)

\(\because \exist \epsilon > 0\)使得 \(f(n) =O(n^{\log_b{a}-\epsilon})\) ,此时 \(\epsilon = \dfrac{k-1}{k}\)

\(\therefore T(n) =\Theta\left(n^{1-\frac{1}{k}}\right)\)

\(k=2\) 时, \(T(n) = \Theta(\sqrt{n})\)


参考文献

[1] KDT小记

]]>
@@ -7496,7 +7496,7 @@ /2022/03/01/luo-gu-p4357-cqoi2016-k-yuan-dian-dui-ti-jie/ - 洛谷P4357 [CQOI2016]K 远点对 题解

题目链接:P4357 [CQOI2016]K 远点对

题意:给定平面内 $n$ 个点的坐标,求欧几里德距离第 $k$ 远的点对

本题的正解应该是旋转卡壳、分治等算法,不会

而kd-tree上搜索在本题中相当于搜索+天然剪枝

因此假装这就是个kd-tree的模板题

为什么使用方差呢?因为方差很好描述了数据的离散程度

每次选择方差较大的进行划分,是因为选择较小的很可能导致另一维的方差过高

因此这样就很难保证树高

对于本题中的 $k$ 大值怎么去维护呢?

我们可以弄一个有 $k$ 个结点的小根堆,这些结点默认为一个极小值(本题中设为 $0$ 就可以了)

这样在堆首的就是第 $k$ 大值了

这样我们只要枚举每个点,分别搜它们所对应的点,统计它们的贡献

容易发现,因为是无序的点,因此这些点之间的距离会被计算 $2$ 次,那我们只要把 $k$ 变成 $2k$ 就好了

时间复杂度 比较玄学,可以分析的是建树 $O(n\log n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(1e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('\n');}    if(k>9)write(k/10);    pc(k%10+'0');}priority_queue< int,vector<int>,greater<int> >q;struct Point{    int x,y;}s[N];int n,k;namespace KDT{    bool cmp1(Point a,Point b){return a.x<b.x;}    bool cmp2(Point a,Point b){return a.y<b.y;}    struct node    {        int ch[2],L,R,D,U;    }t[N];    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    void push_up(int x)    {        t[x].L=t[x].R=s[x].x;        t[x].D=t[x].U=s[x].y;        if(ls(x))        {            t[x].L=min(t[x].L,t[ls(x)].L);            t[x].R=max(t[x].R,t[ls(x)].R);            t[x].D=min(t[x].D,t[ls(x)].D);            t[x].U=max(t[x].U,t[ls(x)].U);        }        if(rs(x))        {            t[x].L=min(t[x].L,t[rs(x)].L);            t[x].R=max(t[x].R,t[rs(x)].R);            t[x].D=min(t[x].D,t[rs(x)].D);            t[x].U=max(t[x].U,t[rs(x)].U);        }    }    #define sq(x) ((x)*(x))    int dist(int a,int b)    {        return max(sq(s[a].x-t[b].L),sq(s[a].x-t[b].R))        +max(sq(s[a].y-t[b].D),sq(s[a].y-t[b].U));    }     int build(int l,int r)    {        if(l>r)return 0;        int mid=(l+r)>>1;        double av1=0,av2=0,va1=0,va2=0;        for(int i=l; i<=r; i++)            av1+=s[i].x,av2+=s[i].y;        av1/=(r-l+1);        av2/=(r-l+1);        for(int i=l; i<=r; i++)            va1+=sq(av1-s[i].x),va2+=sq(av2-s[i].y);        if(va1>va2)            nth_element(s+l,s+mid,s+r+1,cmp1);        else nth_element(s+l,s+mid,s+r+1,cmp2);        ls(mid)=build(l,mid-1);        rs(mid)=build(mid+1,r);        push_up(mid);        return mid;    }    void query(int l,int r,int x)    {        if(l>r)return;        int mid=(l+r)>>1;        int d=sq(s[mid].x-s[x].x)+sq(s[mid].y-s[x].y);        if(d>q.top())q.pop(),q.push(d);        int distl=dist(x,ls(mid)),distr=dist(x,rs(mid));        if(distl>q.top()&&distr>q.top())        {            if(distl>distr)            {                query(l,mid-1,x);                if(distr>q.top())query(mid+1,r,x);            }else            {                query(mid+1,r,x);                if(distl>q.top())query(l,mid-1,x);            }        }else        {            if(distl>q.top())query(l,mid-1,x);            if(distr>q.top())query(mid+1,r,x);        }    }}signed main(){    using namespace KDT;    read(n);read(k);    k*=2; // 无序点对,每个有序点对会被计算两次    for(int i=1; i<=k; i++) q.push(0);    for(int i=1; i<=n; i++)        read(s[i].x),read(s[i].y);    build(1,n);    for(int i=1; i<=n; i++)        query(1,n,i);    write(q.top());pc('\n');    return 0;}
]]>
+ 洛谷P4357 [CQOI2016]K 远点对题解

题目链接:P4357[CQOI2016]K 远点对

题意:给定平面内 \(n\) 个点的坐标,求欧几里德距离第 \(k\) 远的点对

本题的正解应该是旋转卡壳、分治等算法,不会

而kd-tree上搜索在本题中相当于搜索+天然剪枝

因此假装这就是个kd-tree的模板题

为什么使用方差呢?因为方差很好描述了数据的离散程度

每次选择方差较大的进行划分,是因为选择较小的很可能导致另一维的方差过高

因此这样就很难保证树高

对于本题中的 \(k\)大值怎么去维护呢?

我们可以弄一个有 \(k\)个结点的小根堆,这些结点默认为一个极小值(本题中设为 \(0\) 就可以了)

这样在堆首的就是第 \(k\) 大值了

这样我们只要枚举每个点,分别搜它们所对应的点,统计它们的贡献

容易发现,因为是无序的点,因此这些点之间的距离会被计算 \(2\) 次,那我们只要把 \(k\) 变成 \(2k\) 就好了

时间复杂度 比较玄学,可以分析的是建树 \(O(n\log n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(1e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('\n');}    if(k>9)write(k/10);    pc(k%10+'0');}priority_queue< int,vector<int>,greater<int> >q;struct Point{    int x,y;}s[N];int n,k;namespace KDT{    bool cmp1(Point a,Point b){return a.x<b.x;}    bool cmp2(Point a,Point b){return a.y<b.y;}    struct node    {        int ch[2],L,R,D,U;    }t[N];    #define ls(x) t[x].ch[0]    #define rs(x) t[x].ch[1]    void push_up(int x)    {        t[x].L=t[x].R=s[x].x;        t[x].D=t[x].U=s[x].y;        if(ls(x))        {            t[x].L=min(t[x].L,t[ls(x)].L);            t[x].R=max(t[x].R,t[ls(x)].R);            t[x].D=min(t[x].D,t[ls(x)].D);            t[x].U=max(t[x].U,t[ls(x)].U);        }        if(rs(x))        {            t[x].L=min(t[x].L,t[rs(x)].L);            t[x].R=max(t[x].R,t[rs(x)].R);            t[x].D=min(t[x].D,t[rs(x)].D);            t[x].U=max(t[x].U,t[rs(x)].U);        }    }    #define sq(x) ((x)*(x))    int dist(int a,int b)    {        return max(sq(s[a].x-t[b].L),sq(s[a].x-t[b].R))        +max(sq(s[a].y-t[b].D),sq(s[a].y-t[b].U));    }     int build(int l,int r)    {        if(l>r)return 0;        int mid=(l+r)>>1;        double av1=0,av2=0,va1=0,va2=0;        for(int i=l; i<=r; i++)            av1+=s[i].x,av2+=s[i].y;        av1/=(r-l+1);        av2/=(r-l+1);        for(int i=l; i<=r; i++)            va1+=sq(av1-s[i].x),va2+=sq(av2-s[i].y);        if(va1>va2)            nth_element(s+l,s+mid,s+r+1,cmp1);        else nth_element(s+l,s+mid,s+r+1,cmp2);        ls(mid)=build(l,mid-1);        rs(mid)=build(mid+1,r);        push_up(mid);        return mid;    }    void query(int l,int r,int x)    {        if(l>r)return;        int mid=(l+r)>>1;        int d=sq(s[mid].x-s[x].x)+sq(s[mid].y-s[x].y);        if(d>q.top())q.pop(),q.push(d);        int distl=dist(x,ls(mid)),distr=dist(x,rs(mid));        if(distl>q.top()&&distr>q.top())        {            if(distl>distr)            {                query(l,mid-1,x);                if(distr>q.top())query(mid+1,r,x);            }else            {                query(mid+1,r,x);                if(distl>q.top())query(l,mid-1,x);            }        }else        {            if(distl>q.top())query(l,mid-1,x);            if(distr>q.top())query(mid+1,r,x);        }    }}signed main(){    using namespace KDT;    read(n);read(k);    k*=2; // 无序点对,每个有序点对会被计算两次    for(int i=1; i<=k; i++) q.push(0);    for(int i=1; i<=n; i++)        read(s[i].x),read(s[i].y);    build(1,n);    for(int i=1; i<=n; i++)        query(1,n,i);    write(q.top());pc('\n');    return 0;}
]]>
@@ -7525,7 +7525,7 @@ /2022/03/01/cf1200e-compress-words-ti-jie/ - CF1200E Compress Words 题解

题目链接:CF1200E Compress Words

题意:给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀 与 待插入串的前缀的最大匹配串

解法一 KMP

这个解法常数比较大

可以想到,暂时把待插入串放到答案串前,然后跑一遍KMP

最后一个值就是新串的最长前后缀,也就是我们要求的最大匹配串的长度

由于直接拼接新串会导致某些时候答案超出原长

因此我们要在它们拼接的地方加上一些非字符集的字符或者乱七八糟的字符

注意不能自带border

而这样的算法还是不行的,因为答案串中有很多字符其实用不到

因此我们只要截取答案串后面的拼接即可

时间复杂度 $O(\sum |s_i|) = O(n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e6+5)int n,fail[N],len1,len2;char ans[N],s[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> s+1;len2=strlen(s+1);        int minlen=min(len1,len2);        string str="0";        for(int i=1; i<=minlen; i++)            str+=s[i];        str+="!@#$%^&*";        for(int i=len1-minlen+1; i<=len1; i++)            str+=ans[i];        for(int i=2,j=0; i<=minlen*2+8; i++)        {            while(j&&str[i]!=str[j+1])j=fail[j];            if(str[i]==str[j+1])++j;            fail[i]=j;        }        for(int i=fail[minlen*2+8]+1; i<=len2; i++)            ans[++len1]=s[i];    }    cout << ans+1 << endl;    return 0;}

解法二 字符串哈希

这里主要扯扯字符串哈希的东西

虽然之前没怎么研究,一直用的unordered_map<string,int>mp

但是这次就手写个字符串哈希吧

一般字符串哈希有两种写法,这里就按照OI WIKI默认的写法来讲了

定义哈希函数 $f(s) = \sum\limits_{i=1}^{l}s[i]\times b^{l-i} \mod M$

一般来讲我们不会只用一个大质数 $M$ ,而是一堆 qwq

因为这样它的 $n$ 次比较的错误率会降到 $1-\left(1-\dfrac{1}{\prod M_i}\right)^n$

这个数有多小呢?我们假设只有两个大质数 1e9+7,998244353,比较1e6

那么它的值约为 $0.00000000000100175873$

这错误的概率还没计算机出故障的几率高吧

因此我们直接忽略不计

那么对于一个字符串 $\tt{xyz}$,它的值就等于 $(xb^2+yb+z) \mod M$

类比于一个 $b$ 进制的数,应该还算比较好理解的

根据这个式子,如果我们要求它的一个子串 $s[l\dots r]$ 的哈希值怎么办

注意到 $f(s[l\dots r]) = \sum\limits_{i=l}^{r}s[i]\times b^{r-i} \mod M$

因此可以得到结论 $f(s[l\dots r]) = f(s[1\dots r]))-f(s[1\dots l-1])\times b^{r-l+1} \mod M$

注意这里,由于做了取模操作,$f(s[l\dots r])$ 的值可能为负数,要注意把它转成正的

这样我们就可以 $O(n)$ 预处理 $b$ , $O(1)$ 查询了(也可以就快速幂 $O(\log n)$ 查询)

好的相信你已经理解了字符串哈希的一些基本东西,下面来做个简单的小例题

好吧就是这题,看它题意

很简单,直接暴力枚举+哈希就OK了,

而朴素的解法,语句 str1==str2 ,它实质上是 $O(n)$ 级别的

因此我们把朴素解法的 $O(n^2)$ 就可以压到 $O(n)$ 了

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+5)#define hash_cnt 2const int hash_base[hash_cnt]={29,31};const int hash_mod[hash_cnt]={1000000007,998244353};struct hash_str{    char s[N];    int len,hsh[hash_cnt][N];    int pwd[hash_cnt][N];        void init()    {        len=0;        for(int i=0; i<hash_cnt; i++)        {            hsh[i][0]=0;            pwd[i][0]=1;        }    }    hash_str(){init();}    void add(char ch)    {        s[++len]=ch;        for(int i=0; i<hash_cnt; i++)        {            pwd[i][len]=pwd[i][len-1]*hash_base[i]%hash_mod[i];            hsh[i][len]=(hsh[i][len-1]*hash_base[i]+ch)%hash_mod[i];        }    }    vector<int> gethash(int l,int r)    {        vector<int> res(hash_cnt,0);        for(int i=0; i<hash_cnt; i++)        {            int t=(hsh[i][r]-hsh[i][l-1]*pwd[i][r-l+1])%hash_mod[i];            t=(t+hash_mod[i])%hash_mod[i];            res[i]=t;        }        return res;    }}s,t;bool equal(const vector<int> &vec1,const vector<int> &vec2){    assert(vec1.size()==vec2.size());    for(int i=0; i<vec1.size(); i++)        if(vec1[i]!=vec2[i])return 0;    return 1;}int n;char str[N];void solve(char *str){    int len=strlen(str+1);    t.init();    for(int i=1; i<=len; i++)        t.add(str[i]);    int d=1;    for(int j=min(len,s.len); j>=1; j--)        if(equal(t.gethash(1,j),s.gethash(s.len-j+1,s.len)))            {d=j+1;break;}    for(int i=d; i<=len; i++)        s.add(str[i]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> str+1;        solve(str);    }    cout << s.s+1 << endl;    return 0;}
]]>
+ CF1200E Compress Words 题解

题目链接:CF1200ECompress Words

题意:给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀与 待插入串的前缀的最大匹配串

解法一 KMP

这个解法常数比较大

可以想到,暂时把待插入串放到答案串前,然后跑一遍KMP

最后一个值就是新串的最长前后缀,也就是我们要求的最大匹配串的长度

由于直接拼接新串会导致某些时候答案超出原长

因此我们要在它们拼接的地方加上一些非字符集的字符或者乱七八糟的字符

注意不能自带border

而这样的算法还是不行的,因为答案串中有很多字符其实用不到

因此我们只要截取答案串后面的拼接即可

时间复杂度 \(O(\sum |s_i|) =O(n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(2e6+5)int n,fail[N],len1,len2;char ans[N],s[N];signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> s+1;len2=strlen(s+1);        int minlen=min(len1,len2);        string str="0";        for(int i=1; i<=minlen; i++)            str+=s[i];        str+="!@#$%^&*";        for(int i=len1-minlen+1; i<=len1; i++)            str+=ans[i];        for(int i=2,j=0; i<=minlen*2+8; i++)        {            while(j&&str[i]!=str[j+1])j=fail[j];            if(str[i]==str[j+1])++j;            fail[i]=j;        }        for(int i=fail[minlen*2+8]+1; i<=len2; i++)            ans[++len1]=s[i];    }    cout << ans+1 << endl;    return 0;}

解法二 字符串哈希

这里主要扯扯字符串哈希的东西

虽然之前没怎么研究,一直用的unordered_map<string,int>mp

但是这次就手写个字符串哈希吧

一般字符串哈希有两种写法,这里就按照OI WIKI默认的写法来讲了

定义哈希函数 \(f(s) =\sum\limits_{i=1}^{l}s[i]\times b^{l-i} \mod M\)

一般来讲我们不会只用一个大质数 \(M\),而是一堆 qwq

因为这样它的 \(n\)次比较的错误率会降到 \(1-\left(1-\dfrac{1}{\prodM_i}\right)^n\)

这个数有多小呢?我们假设只有两个大质数1e9+7,998244353,比较1e6

那么它的值约为 \(0.00000000000100175873\)

这错误的概率还没计算机出故障的几率高吧

因此我们直接忽略不计

那么对于一个字符串 \(\tt{xyz}\),它的值就等于 \((xb^2+yb+z) \mod M\)

类比于一个 \(b\)进制的数,应该还算比较好理解的

根据这个式子,如果我们要求它的一个子串 \(s[l\dots r]\) 的哈希值怎么办

注意到 \(f(s[l\dots r]) =\sum\limits_{i=l}^{r}s[i]\times b^{r-i} \mod M\)

因此可以得到结论 \(f(s[l\dots r]) =f(s[1\dots r]))-f(s[1\dots l-1])\times b^{r-l+1} \mod M\)

注意这里,由于做了取模操作,\(f(s[l\dotsr])\) 的值可能为负数,要注意把它转成正的

这样我们就可以 \(O(n)\) 预处理 \(b\) , \(O(1)\) 查询了(也可以就快速幂 \(O(\log n)\) 查询)

好的相信你已经理解了字符串哈希的一些基本东西,下面来做个简单的小例题

好吧就是这题,看它题意

很简单,直接暴力枚举+哈希就OK了,

而朴素的解法,语句 str1==str2 ,它实质上是 \(O(n)\) 级别的

因此我们把朴素解法的 \(O(n^2)\)就可以压到 \(O(n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e6+5)#define hash_cnt 2const int hash_base[hash_cnt]={29,31};const int hash_mod[hash_cnt]={1000000007,998244353};struct hash_str{    char s[N];    int len,hsh[hash_cnt][N];    int pwd[hash_cnt][N];        void init()    {        len=0;        for(int i=0; i<hash_cnt; i++)        {            hsh[i][0]=0;            pwd[i][0]=1;        }    }    hash_str(){init();}    void add(char ch)    {        s[++len]=ch;        for(int i=0; i<hash_cnt; i++)        {            pwd[i][len]=pwd[i][len-1]*hash_base[i]%hash_mod[i];            hsh[i][len]=(hsh[i][len-1]*hash_base[i]+ch)%hash_mod[i];        }    }    vector<int> gethash(int l,int r)    {        vector<int> res(hash_cnt,0);        for(int i=0; i<hash_cnt; i++)        {            int t=(hsh[i][r]-hsh[i][l-1]*pwd[i][r-l+1])%hash_mod[i];            t=(t+hash_mod[i])%hash_mod[i];            res[i]=t;        }        return res;    }}s,t;bool equal(const vector<int> &vec1,const vector<int> &vec2){    assert(vec1.size()==vec2.size());    for(int i=0; i<vec1.size(); i++)        if(vec1[i]!=vec2[i])return 0;    return 1;}int n;char str[N];void solve(char *str){    int len=strlen(str+1);    t.init();    for(int i=1; i<=len; i++)        t.add(str[i]);    int d=1;    for(int j=min(len,s.len); j>=1; j--)        if(equal(t.gethash(1,j),s.gethash(s.len-j+1,s.len)))            {d=j+1;break;}    for(int i=d; i<=len; i++)        s.add(str[i]);}signed main(){    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> str+1;        solve(str);    }    cout << s.s+1 << endl;    return 0;}
]]>
@@ -7552,7 +7552,7 @@ /2022/02/25/luo-gu-p2387-noi2014-mo-fa-sen-lin-ti-jie/ - 洛谷P2387 [NOI2014] 魔法森林 题解

题目链接:P2387 [NOI2014] 魔法森林

题意:每条边有边权 $a,b$ 两个,求 $1$ 到 $n$ 的路径使得所经过的边中 $\max\{a\}+\max\{b\}$ 尽可能小

由于下面写了很多解题思路上的东西,导致有些冗长,因此这里先放简化版题解

如果不理解可以跳过这一段简化版,直接看下面的完整版

简化版

因为不方便同时处理 $a,b$ 两维,我们可以枚举 $a$ 然后尽可能的选择较小的 $b$

这个 $b$ 的一定在连接 $1$ 和 $n$ 的最小生成树上

因此这道题就是个动态加边的LCT裸题了

代码在后面,时间复杂度 $O(m\log m)$


完整版

这种“二维”的问题常见的解法就是枚举第一维,然后优化第二维

不知道这个套路也没关系

首先看到这个题意,最大值的和最小,显然我们无法确定这是个多大的值

似乎可以二分?但是再一想,二分 $a+b$ 肯定是不对的

那先二分 $a$ 再二分 $b$ (二分套二分)呢?

看上去似乎也不行,这个 $b$ 不是很好二分,而且复杂度似乎是 $O(n\log^3n)$ 的

那二分 $a$ 呢?好像要对 $a$ 这一维从小到大排个序,然后在 $a$ 相等时,第二维也从小到大排序

而每次我们检验 $a$ 时,都要把在 $a$ 所在位置(数组中)左边的边都去连连看有没有用什么的

那还不如直接从小到大枚举 $a$ 呢!

于是我们可以枚举 $a$ ,然后发现如果要保证 $b$ 尽可能的小

我们需要在所有 $a’<a$ 的边中找出一棵生成树

也就是在用 $a$ 所在位置(数组中)左边的边建出的图中建最小生成森林(因为这个子图不一定连通)

其实我们并不是在为了维护最小生成森林而维护它的

而是在维护包括结点 $1$ 和 $n$ 最小生成树的过程中所必需的 qwq

当这个包括结点 $1$ 和 $n$ 的最小生成树建出来的时候,我们就可以更新答案了

注意这里是更新答案,不是最终结果,因为最终结果它和 $a$ 的大小没有直接的关系

举个例子就知道了,a=1,b=998244353a=2,b=1,显然是后者更小

那么怎么动态的维护一棵最小生成树呢?我们可以使用LCT

如果不会LCT动态维护最小生成树,可以看看这篇

那么,其实这道题就解决了

也就是枚举 $a$ ,动态维护最小生成树

时间复杂度 $O(m\log m)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(3e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,ans=INF;namespace LCT{    struct Edge{int u,v,a,b;}e[N];    int cmp(Edge a,Edge b){return a.a==b.a?a.b<b.b:a.a<b.a;}    struct node    {        int ch[2],id,mx,w,fa,tag;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void push_up(int x)    {        t[x].id=x;t[x].mx=t[x].w;        if(t[x].ch[0]&&t[t[x].ch[0]].mx>t[x].mx)            t[x].mx=t[t[x].ch[0]].mx,t[x].id=t[t[x].ch[0]].id;        if(t[x].ch[1]&&t[t[x].ch[1]].mx>t[x].mx)            t[x].mx=t[t[x].ch[1]].mx,t[x].id=t[t[x].ch[1]].id;    }    void push_down(int x)    {        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag^=1;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0;x;y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y,push_up(x);    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void split(int x,int y)    {        make_root(x);        access(y);splay(y);    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])        {            t[x].ch[1]=t[y].fa=0;            push_up(x);        }    }    int ck(int x,int y)    {        make_root(x);        return find_root(y)!=x;    }}signed main(){    using namespace LCT;    read(n);read(m);    for(int i=1; i<=m; i++)    {        read(e[i].u);read(e[i].v);        read(e[i].a);read(e[i].b);    }    sort(e+1,e+1+m,cmp);    for(int i=1; i<=m; i++)    {        int idx=n+i,x=e[i].u,y=e[i].v;        t[idx].w=e[i].b;        if(x==y)continue;        if(ck(x,y))            link(x,idx),link(idx,y);        else        {            split(x,y);int now=t[y].id;            if(t[now].mx<=e[i].b)continue;splay(now);            t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0;            link(x,idx);link(idx,y);        }        if(!ck(1,n))        {            split(1,n);            ans=min(ans,t[n].mx+e[i].a);        }    }    if(ans==INF)write(-1);    else write(ans);pc('\n');    return 0;}
]]>
+ 洛谷P2387 [NOI2014] 魔法森林题解

题目链接:P2387[NOI2014] 魔法森林

题意:每条边有边权 \(a,b\) 两个,求 \(1\) 到 \(n\) 的路径使得所经过的边中 \(\max\{a\}+\max\{b\}\) 尽可能小

由于下面写了很多解题思路上的东西,导致有些冗长,因此这里先放简化版题解

如果不理解可以跳过这一段简化版,直接看下面的完整版### 简化版 因为不方便同时处理 \(a,b\)两维,我们可以枚举 \(a\)然后尽可能的选择较小的 \(b\)

这个 \(b\) 的一定在连接 \(1\) 和 \(n\) 的最小生成树上

因此这道题就是个动态加边的LCT裸题了

代码在后面,时间复杂度 \(O(m\logm)\)


完整版

这种“二维”的问题常见的解法就是枚举第一维,然后优化第二维

不知道这个套路也没关系

首先看到这个题意,最大值的和最小,显然我们无法确定这是个多大的值

似乎可以二分?但是再一想,二分 \(a+b\) 肯定是不对的

那先二分 \(a\) 再二分 \(b\) (二分套二分)呢?

看上去似乎也不行,这个 \(b\)不是很好二分,而且复杂度似乎是 \(O(n\log^3n)\) 的

那二分 \(a\) 呢?好像要对 \(a\) 这一维从小到大排个序,然后在 \(a\) 相等时,第二维也从小到大排序

而每次我们检验 \(a\) 时,都要把在\(a\)所在位置(数组中)左边的边都去连连看有没有用什么的

那还不如直接从小到大枚举 \(a\)呢!

于是我们可以枚举 \(a\),然后发现如果要保证 \(b\)尽可能的小

我们需要在所有 \(a’<a\)的边中找出一棵生成树

也就是在用 \(a\)所在位置(数组中)左边的边建出的图中建最小生成森林(因为这个子图不一定连通)

其实我们并不是在为了维护最小生成森林而维护它的

而是在维护包括结点 \(1\)\(n\) 最小生成树的过程中所必需的 qwq

当这个包括结点 \(1\)\(n\)的最小生成树建出来的时候,我们就可以更新答案了

注意这里是更新答案,不是最终结果,因为最终结果它和 \(a\) 的大小没有直接的关系

举个例子就知道了,a=1,b=998244353a=2,b=1,显然是后者更小

那么怎么动态的维护一棵最小生成树呢?我们可以使用LCT

如果不会LCT动态维护最小生成树,可以看看这篇

那么,其实这道题就解决了

也就是枚举 \(a\),动态维护最小生成树

时间复杂度 \(O(m\log m)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(3e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,ans=INF;namespace LCT{    struct Edge{int u,v,a,b;}e[N];    int cmp(Edge a,Edge b){return a.a==b.a?a.b<b.b:a.a<b.a;}    struct node    {        int ch[2],id,mx,w,fa,tag;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void push_up(int x)    {        t[x].id=x;t[x].mx=t[x].w;        if(t[x].ch[0]&&t[t[x].ch[0]].mx>t[x].mx)            t[x].mx=t[t[x].ch[0]].mx,t[x].id=t[t[x].ch[0]].id;        if(t[x].ch[1]&&t[t[x].ch[1]].mx>t[x].mx)            t[x].mx=t[t[x].ch[1]].mx,t[x].id=t[t[x].ch[1]].id;    }    void push_down(int x)    {        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag^=1;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0;x;y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y,push_up(x);    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void split(int x,int y)    {        make_root(x);        access(y);splay(y);    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])        {            t[x].ch[1]=t[y].fa=0;            push_up(x);        }    }    int ck(int x,int y)    {        make_root(x);        return find_root(y)!=x;    }}signed main(){    using namespace LCT;    read(n);read(m);    for(int i=1; i<=m; i++)    {        read(e[i].u);read(e[i].v);        read(e[i].a);read(e[i].b);    }    sort(e+1,e+1+m,cmp);    for(int i=1; i<=m; i++)    {        int idx=n+i,x=e[i].u,y=e[i].v;        t[idx].w=e[i].b;        if(x==y)continue;        if(ck(x,y))            link(x,idx),link(idx,y);        else        {            split(x,y);int now=t[y].id;            if(t[now].mx<=e[i].b)continue;splay(now);            t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0;            link(x,idx);link(idx,y);        }        if(!ck(1,n))        {            split(1,n);            ans=min(ans,t[n].mx+e[i].a);        }    }    if(ans==INF)write(-1);    else write(ans);pc('\n');    return 0;}
]]>
@@ -7581,7 +7581,7 @@ /2022/02/25/luo-gu-p4234-zui-xiao-chai-zhi-sheng-cheng-shu-ti-jie/ - 洛谷P4234 最小差值生成树 题解

题目链接:P4234 最小差值生成树

题意:给定一个点标号从 $1$ 到 $n$ 的、有 $m$ 条边的无向图,求边权最大值与最小值的差值最小的生成树,图可能存在自环

这个题不太好利用kruskal来维护(其实kruskal来做就是暴力枚举)

考虑使用LCT动态维护生成树

可以先回顾一下如何用LCT来求解最小生成树 link

因为边之间没有什么本质区别,我们可以先把边按大小排序,以保证编号越小的边点,权值越小

然后我们可以从小到大枚举生成树中的最大值(从大到小其实是类似的)

那么我们只要保证生成树中边权的最小值尽可能的大即可

也就是说,在枚举最大边的时候,如果两结点未连通,我们可以直接将这两点连接

如果已经连通,那么就将当前生成树中x,y所成的链中的最小边扔掉,然后用最大边连接这两点

显然这个最小值就是生成树中编号最小的那一个,而且随着枚举会单调不降,那我们可以整个变量记录它,而由于它的单调不降性质,寻找这个最小值是不会影响到复杂度的

对于已经不在生成树的边(包括构成自环和被更新掉的边),我们直接让那个维护最小值的变量跳过它就行了,具体可以看下面代码

时间复杂度 $O(m\log m)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(4e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,idx,ans=INF,cnt,d;namespace LCT{    struct Edge{int u,v,w;}e[N];    int cmp(Edge a,Edge b){return a.w<b.w;}    struct node    {        int ch[2],id,fa,tag,vis;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void push_up(int x)    {        t[x].id=x;        if(t[t[x].ch[0]].id>n&&(t[x].id<=n||t[x].id>t[t[x].ch[0]].id))            t[x].id=t[t[x].ch[0]].id;        if(t[t[x].ch[1]].id>n&&(t[x].id<=n||t[x].id>t[t[x].ch[1]].id))            t[x].id=t[t[x].ch[1]].id;    }    void push_down(int x)    {        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag=0;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0;x;y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y,push_up(x);    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void split(int x,int y)    {        make_root(x);        access(y);splay(y);    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    /*    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])        {            t[x].ch[1]=t[y].fa=0;            push_up(x);        }    }    */    int ck(int x,int y)    {        make_root(x);        return find_root(y)!=x;    }}signed main(){    using namespace LCT;    read(n);read(m);    for(int i=1; i<=m; i++)    {        read(e[i].u);read(e[i].v);        read(e[i].w);    }    sort(e+1,e+1+m,cmp);d=1;    for(int i=1; i<=m; i++)    {        int idx=n+i,w=e[i].w,x=e[i].u,y=e[i].v;        if(x==y){t[idx].vis=1;continue;}        if(ck(x,y))            link(x,idx),link(idx,y),++cnt;        else        {            split(x,y);int now=t[y].id;            t[now].vis=1;splay(now);            t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0;            link(x,idx);link(idx,y);        }        while(t[d+n].vis&&d<i)++d;        if(cnt>=n-1)ans=min(ans,w-e[d].w);    }    write(ans);pc('\n');    return 0;}

顺便贴一下kruskal的暴力代码吧 时间复杂度是 $O(m^2)$ 的

这道题数据水过头了所以暴力也能过(已经反馈了

注:这个暴力是我去年(2021.2.6)写的,所以码风区别较大((

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint n,m,t,head[50005],in[50005],ans=INT_MAX,pos=1,cnt;int f[50005],a[50005];struct Edge{    int u,v,w,next;    const bool operator<(const Edge &o)const    {        return w<o.w;    }}e[500005];void init(){    for(R int i=1; i<=n; i++)        f[i]=i;    cnt=0;}void add(R int u,R int v,R int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}int find(R int x){    return f[x]==x?x:f[x]=find(f[x]);}signed main(){    scanf("%lld%lld",&n,&m);    for(R int i=1,u,v,w; i<=m; i++)    {        scanf("%lld%lld%lld",&u,&v,&w);        add(u,v,w);    }    if(m==0)return puts("-1"),0;    sort(e+1,e+pos+1);    for(R int k=1; k<=pos; k++)    {        if(k>1&&e[k].w==e[k-1].w)continue;        init();        for(R int i=k;i<=pos; i++)        {            R int u=find(e[i].u),v=find(e[i].v),w=e[i].w;            if(u==v)continue;            f[u]=v;            if(++cnt==n-1)            {                if(ans>e[i].w-e[k].w)ans=e[i].w-e[k].w;                break;            }        }        if(cnt!=n-1)break;    }    printf("%lld\n",ans);    return 0;}
]]>
+ 洛谷P4234 最小差值生成树题解

题目链接:P4234最小差值生成树

题意:给定一个点标号从 \(1\) 到 \(n\) 的、有 \(m\)条边的无向图,求边权最大值与最小值的差值最小的生成树,图可能存在自环

这个题不太好利用kruskal来维护(其实kruskal来做就是暴力枚举)

考虑使用LCT动态维护生成树

可以先回顾一下如何用LCT来求解最小生成树 link

因为边之间没有什么本质区别,我们可以先把边按大小排序,以保证编号越小的边点,权值越小

然后我们可以从小到大枚举生成树中的最大值(从大到小其实是类似的)

那么我们只要保证生成树中边权的最小值尽可能的大即可

也就是说,在枚举最大边的时候,如果两结点未连通,我们可以直接将这两点连接

如果已经连通,那么就将当前生成树中x,y所成的链中的最小边扔掉,然后用最大边连接这两点

显然这个最小值就是生成树中编号最小的那一个,而且随着枚举会单调不降,那我们可以整个变量记录它,而由于它的单调不降性质,寻找这个最小值是不会影响到复杂度的

对于已经不在生成树的边(包括构成自环和被更新掉的边),我们直接让那个维护最小值的变量跳过它就行了,具体可以看下面代码

时间复杂度 \(O(m\log m)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(4e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,idx,ans=INF,cnt,d;namespace LCT{    struct Edge{int u,v,w;}e[N];    int cmp(Edge a,Edge b){return a.w<b.w;}    struct node    {        int ch[2],id,fa,tag,vis;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void push_up(int x)    {        t[x].id=x;        if(t[t[x].ch[0]].id>n&&(t[x].id<=n||t[x].id>t[t[x].ch[0]].id))            t[x].id=t[t[x].ch[0]].id;        if(t[t[x].ch[1]].id>n&&(t[x].id<=n||t[x].id>t[t[x].ch[1]].id))            t[x].id=t[t[x].ch[1]].id;    }    void push_down(int x)    {        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag=0;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0;x;y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y,push_up(x);    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void split(int x,int y)    {        make_root(x);        access(y);splay(y);    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    /*    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])        {            t[x].ch[1]=t[y].fa=0;            push_up(x);        }    }    */    int ck(int x,int y)    {        make_root(x);        return find_root(y)!=x;    }}signed main(){    using namespace LCT;    read(n);read(m);    for(int i=1; i<=m; i++)    {        read(e[i].u);read(e[i].v);        read(e[i].w);    }    sort(e+1,e+1+m,cmp);d=1;    for(int i=1; i<=m; i++)    {        int idx=n+i,w=e[i].w,x=e[i].u,y=e[i].v;        if(x==y){t[idx].vis=1;continue;}        if(ck(x,y))            link(x,idx),link(idx,y),++cnt;        else        {            split(x,y);int now=t[y].id;            t[now].vis=1;splay(now);            t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0;            link(x,idx);link(idx,y);        }        while(t[d+n].vis&&d<i)++d;        if(cnt>=n-1)ans=min(ans,w-e[d].w);    }    write(ans);pc('\n');    return 0;}

顺便贴一下kruskal的暴力代码吧 时间复杂度是 \(O(m^2)\) 的

这道题数据水过头了所以暴力也能过(已经反馈了

注:这个暴力是我去年(2021.2.6)写的,所以码风区别较大((

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint n,m,t,head[50005],in[50005],ans=INT_MAX,pos=1,cnt;int f[50005],a[50005];struct Edge{    int u,v,w,next;    const bool operator<(const Edge &o)const    {        return w<o.w;    }}e[500005];void init(){    for(R int i=1; i<=n; i++)        f[i]=i;    cnt=0;}void add(R int u,R int v,R int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}int find(R int x){    return f[x]==x?x:f[x]=find(f[x]);}signed main(){    scanf("%lld%lld",&n,&m);    for(R int i=1,u,v,w; i<=m; i++)    {        scanf("%lld%lld%lld",&u,&v,&w);        add(u,v,w);    }    if(m==0)return puts("-1"),0;    sort(e+1,e+pos+1);    for(R int k=1; k<=pos; k++)    {        if(k>1&&e[k].w==e[k-1].w)continue;        init();        for(R int i=k;i<=pos; i++)        {            R int u=find(e[i].u),v=find(e[i].v),w=e[i].w;            if(u==v)continue;            f[u]=v;            if(++cnt==n-1)            {                if(ans>e[i].w-e[k].w)ans=e[i].w-e[k].w;                break;            }        }        if(cnt!=n-1)break;    }    printf("%lld\n",ans);    return 0;}
]]>
@@ -7610,7 +7610,7 @@ /2022/02/25/lct-qiu-jie-zui-xiao-sheng-cheng-shu/ - LCT求解最小生成树

前言

最小生成树模板: P3366 【模板】最小生成树

朴素的kruskal为主流最小生成树算法

而LCT(link cut tree)也是可以维护最小生成树的

由于LCT动态维护最小生成树,加上常数较大

在实际测试中比kruskal慢了很多倍


最小生成树

维护结点的连通性 可以看看这个

显然我们不是把它和kruskal连用,那样反倒变成了 $\log^2$ 的劣解了

而LCT它是动态的,因此难以使用树链剖分的做法,将边权映射到点权上

那么我们可以把通过建“边点”将边权转化为点权

所谓的边点指直接将每条边当做点,那么连边的操作就变成了

w[idx+n]=z; // 记录边权 ,idx = 1 to mlink(x,idx+n),link(idx+n,y); // 和边所连接的两结点与该边所建结点相连

因为没有对边权排序,那如果 x,y 已经连通怎么办呢?

由于x,y都在LCT上,因此我们可以维护它们所成的链中最大的那一条边

如果当前处理到的边比那条边边权更小,就可以将那条极大边断掉

如何断边?直接t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(4e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,idx,ans,cnt;namespace LCT{    struct node    {        int ch[2],w,id,fa,sz,mx,tag;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void push_up(int x)    {        t[x].id=x;t[x].mx=t[x].w;        if(t[t[x].ch[0]].mx>t[x].mx)            t[x].mx=t[t[x].ch[0]].mx,t[x].id=t[t[x].ch[0]].id;        if(t[t[x].ch[1]].mx>t[x].mx)            t[x].mx=t[t[x].ch[1]].mx,t[x].id=t[t[x].ch[1]].id;    }    void push_down(int x)    {        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag=0;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0; x; y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y,push_up(x);    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void split(int x,int y)    {        make_root(x);        access(y);splay(y);    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    int ck(int x,int y)    {        make_root(x);        return find_root(y)!=x;    }    /*    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])        {            t[x].ch[1]=t[y].fa=0;            push_up(x);        }    }    */}signed main(){    using namespace LCT;    read(n);read(m);    idx=n;    for(int i=1,x,y,z; i<=m; i++)    {        read(x);read(y);read(z);        t[++idx].w=z;        if(x!=y&&ck(x,y))link(x,idx),link(idx,y),ans+=z,++cnt;        else        {            split(x,y);int now=t[y].id;            if(t[now].mx<=z)continue;            ans-=t[now].mx-z;splay(now);            t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0;            link(x,idx);link(idx,y);        }    }    if(cnt<n-1)puts("orz");    else write(ans),pc('\n');    return 0;}

这敲代码多是一件美逝啊

]]>
+ LCT求解最小生成树

前言

最小生成树模板: P3366【模板】最小生成树

朴素的kruskal为主流最小生成树算法

而LCT(link cut tree)也是可以维护最小生成树的

由于LCT动态维护最小生成树,加上常数较大

在实际测试中比kruskal慢了很多倍


最小生成树

维护结点的连通性 可以看看这个

显然我们不是把它和kruskal连用,那样反倒变成了 \(\log^2\) 的劣解了

而LCT它是动态的,因此难以使用树链剖分的做法,将边权映射到点权上

那么我们可以把通过建“边点”将边权转化为点权

所谓的边点指直接将每条边当做点,那么连边的操作就变成了

w[idx+n]=z; // 记录边权 ,idx = 1 to mlink(x,idx+n),link(idx+n,y); // 和边所连接的两结点与该边所建结点相连

因为没有对边权排序,那如果 x,y 已经连通怎么办呢?

由于x,y都在LCT上,因此我们可以维护它们所成的链中最大的那一条边

如果当前处理到的边比那条边边权更小,就可以将那条极大边断掉

如何断边?直接t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(4e5+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,idx,ans,cnt;namespace LCT{    struct node    {        int ch[2],w,id,fa,sz,mx,tag;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void push_up(int x)    {        t[x].id=x;t[x].mx=t[x].w;        if(t[t[x].ch[0]].mx>t[x].mx)            t[x].mx=t[t[x].ch[0]].mx,t[x].id=t[t[x].ch[0]].id;        if(t[t[x].ch[1]].mx>t[x].mx)            t[x].mx=t[t[x].ch[1]].mx,t[x].id=t[t[x].ch[1]].id;    }    void push_down(int x)    {        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag=0;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0; x; y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y,push_up(x);    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void split(int x,int y)    {        make_root(x);        access(y);splay(y);    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    int ck(int x,int y)    {        make_root(x);        return find_root(y)!=x;    }    /*    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])        {            t[x].ch[1]=t[y].fa=0;            push_up(x);        }    }    */}signed main(){    using namespace LCT;    read(n);read(m);    idx=n;    for(int i=1,x,y,z; i<=m; i++)    {        read(x);read(y);read(z);        t[++idx].w=z;        if(x!=y&&ck(x,y))link(x,idx),link(idx,y),ans+=z,++cnt;        else        {            split(x,y);int now=t[y].id;            if(t[now].mx<=z)continue;            ans-=t[now].mx-z;splay(now);            t[t[now].ch[0]].fa=t[t[now].ch[1]].fa=0;            link(x,idx);link(idx,y);        }    }    if(cnt<n-1)puts("orz");    else write(ans),pc('\n');    return 0;}

这敲代码多是一件美逝啊

]]>
@@ -7639,7 +7639,7 @@ /2022/02/24/luo-gu-p2147-sdoi2008-dong-xue-kan-ce-ti-jie/ - 洛谷P2147 [SDOI2008] 洞穴勘测 题解

题目链接:P2147 [SDOI2008] 洞穴勘测

题意:给定若干个点,动态连接(无向边),询问连通性

由于它有删边的操作,因此用并查集并不可行

于是想到LCT(?

由于LCT有 find_root() 操作

说明LCT确实可以动态判定连通性

那么就变成简单的模板题了(

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+5)int n,Q;namespace LCT{    struct node    {        int ch[2],fa,tag;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void push_down(int x)    {        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag=0;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0; x; y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y;    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])            t[y].fa=t[x].ch[1]=0;    }}signed main(){    using namespace  LCT;    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> Q;    while(Q--)    {        string str;int x,y;        cin >> str >> x >> y;        if(str[0]=='Q')        {            make_root(x);            access(y);            if(find_root(y)!=x)cout << "No" << endl;            else cout << "Yes" << endl;        }        if(str[0]=='C') link(x,y);        if(str[0]=='D') cut(x,y);    }    return 0;}
]]>
+ 洛谷P2147 [SDOI2008] 洞穴勘测题解

题目链接:P2147[SDOI2008] 洞穴勘测

题意:给定若干个点,动态连接(无向边),询问连通性

由于它有删边的操作,因此用并查集并不可行

于是想到LCT(?

由于LCT有 find_root() 操作

说明LCT确实可以动态判定连通性

那么就变成简单的模板题了(

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define N (int)(1e4+5)int n,Q;namespace LCT{    struct node    {        int ch[2],fa,tag;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void push_down(int x)    {        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag=0;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0; x; y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y;    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])            t[y].fa=t[x].ch[1]=0;    }}signed main(){    using namespace  LCT;    ios::sync_with_stdio(0);    cin.tie(0);cout.tie(0);    cin >> n >> Q;    while(Q--)    {        string str;int x,y;        cin >> str >> x >> y;        if(str[0]=='Q')        {            make_root(x);            access(y);            if(find_root(y)!=x)cout << "No" << endl;            else cout << "Yes" << endl;        }        if(str[0]=='C') link(x,y);        if(str[0]=='D') cut(x,y);    }    return 0;}
]]>
@@ -7666,7 +7666,7 @@ /2022/02/24/luo-gu-p1501-guo-jia-ji-xun-dui-tree-ii-ti-jie/ - 洛谷P1501 [国家集训队]Tree II 题解

题目链接:P1501 [国家集训队]Tree II

题意:树上区间加&乘&link&cut

显然LCT模板题,因为树链剖分并不能维护动态连边

考虑如何维护区间乘

不知道大家有没有做过洛谷的线段树2,没做过也没关系

对于每个结点,维护一个 $kx+b$ 的懒标记

每次做乘法就整体(包括两个部分)乘上 $c$ ,每次做加法就直接加上 $c$

就可以维护标记了

由于加法的维护需要知道这条链的长,那我们只要维护每个结点的子树大小即可

那么这题就很容易解决了(就是函数有点多)

时间复杂度 $O(n\log n)$

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(1e5+5)const int mod=51061;template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,Q;namespace LCT{    struct node    {        int ch[2],num,fa;        int ans,tag,a,m,sz;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void pusha(int x,int c)    {        t[x].ans=(t[x].ans+c*t[x].sz%mod)%mod;        t[x].a=(t[x].a+c)%mod;t[x].num=(t[x].num+c)%mod;    }    void pushm(int x,int c)    {        t[x].ans=t[x].ans*c%mod;        t[x].num=t[x].num*c%mod;        t[x].m=t[x].m*c%mod;        t[x].a=t[x].a*c%mod;    }    void push_up(int x)    {        t[x].ans=(t[t[x].ch[0]].ans        +t[t[x].ch[1]].ans+t[x].num)%mod;        t[x].sz=t[t[x].ch[0]].sz+t[t[x].ch[1]].sz+1;    }    void push_down(int x)    {        if(t[x].m!=1)        {            pushm(t[x].ch[0],t[x].m);            pushm(t[x].ch[1],t[x].m);            t[x].m=1;        }        if(t[x].a)        {            pusha(t[x].ch[0],t[x].a);            pusha(t[x].ch[1],t[x].a);            t[x].a=0;        }        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag=0;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0;x;y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y,push_up(x);    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void split(int x,int y)    {        make_root(x);        access(y);splay(y);    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])        {            t[x].ch[1]=t[y].fa=0;            push_up(x);        }    }}signed main(){    using namespace LCT;    read(n);read(Q);    for(int i=1; i<=n; i++)        t[i].num=t[i].m=t[i].sz=1;    for(int i=1,u,v; i<n; i++)    {        read(u);read(v);        link(u,v);    }    while(Q--)    {        char op;int x,y,c,d;        scanf("%c",&op);        read(x);read(y);        if(op=='+')        {            read(c);            split(x,y);pusha(y,c);        }        if(op=='-')        {            read(c);read(d);            cut(x,y);link(c,d);        }        if(op=='*')        {            read(c);            split(x,y);pushm(y,c);        }        if(op=='/')        {            split(x,y);            write(t[y].ans);pc('\n');        }    }    return 0;}
]]>
+ 洛谷P1501 [国家集训队]Tree II题解

题目链接:P1501[国家集训队]Tree II

题意:树上区间加&乘&link&cut

显然LCT模板题,因为树链剖分并不能维护动态连边

考虑如何维护区间乘

不知道大家有没有做过洛谷的线段树2,没做过也没关系

对于每个结点,维护一个 \(kx+b\)的懒标记

每次做乘法就整体(包括两个部分)乘上 \(c\) ,每次做加法就直接加上 \(c\)

就可以维护标记了

由于加法的维护需要知道这条链的长,那我们只要维护每个结点的子树大小即可

那么这题就很容易解决了(就是函数有点多)

时间复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define N (int)(1e5+5)const int mod=51061;template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,Q;namespace LCT{    struct node    {        int ch[2],num,fa;        int ans,tag,a,m,sz;    }t[N];    #define isroot(x) ((t[t[x].fa].ch[0]!=x)&&(t[t[x].fa].ch[1]!=x))    void pushr(int x)    {        swap(t[x].ch[0],t[x].ch[1]);        t[x].tag^=1;    }    void pusha(int x,int c)    {        t[x].ans=(t[x].ans+c*t[x].sz%mod)%mod;        t[x].a=(t[x].a+c)%mod;t[x].num=(t[x].num+c)%mod;    }    void pushm(int x,int c)    {        t[x].ans=t[x].ans*c%mod;        t[x].num=t[x].num*c%mod;        t[x].m=t[x].m*c%mod;        t[x].a=t[x].a*c%mod;    }    void push_up(int x)    {        t[x].ans=(t[t[x].ch[0]].ans        +t[t[x].ch[1]].ans+t[x].num)%mod;        t[x].sz=t[t[x].ch[0]].sz+t[t[x].ch[1]].sz+1;    }    void push_down(int x)    {        if(t[x].m!=1)        {            pushm(t[x].ch[0],t[x].m);            pushm(t[x].ch[1],t[x].m);            t[x].m=1;        }        if(t[x].a)        {            pusha(t[x].ch[0],t[x].a);            pusha(t[x].ch[1],t[x].a);            t[x].a=0;        }        if(t[x].tag)        {            if(t[x].ch[0])pushr(t[x].ch[0]);            if(t[x].ch[1])pushr(t[x].ch[1]);            t[x].tag=0;        }    }    void push_all(int x)    {        if(!isroot(x))push_all(t[x].fa);        push_down(x);    }    void rotate(int x)    {        int y=t[x].fa;        int z=t[y].fa;        int k=t[y].ch[1]==x;        if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;        t[x].fa=z;        t[y].ch[k]=t[x].ch[k^1];        t[t[x].ch[k^1]].fa=y;        t[x].ch[k^1]=y;        t[y].fa=x;        push_up(y);        push_up(x);    }    void splay(int x)    {        push_all(x);        while(!isroot(x))        {            int y=t[x].fa;            int z=t[y].fa;            if(!isroot(y))            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);            rotate(x);        }    }    void access(int x)    {        for(int y=0;x;y=x,x=t[x].fa)            splay(x),t[x].ch[1]=y,push_up(x);    }    void make_root(int x)    {        access(x);splay(x);        pushr(x);    }    int find_root(int x)    {        access(x);splay(x);        while(t[x].ch[0])push_down(x),x=t[x].ch[0];        splay(x);        return x;    }    void split(int x,int y)    {        make_root(x);        access(y);splay(y);    }    void link(int x,int y)    {        make_root(x);        if(find_root(y)!=x)t[x].fa=y;    }    void cut(int x,int y)    {        make_root(x);        if(find_root(y)==x&&t[y].fa==x&&!t[y].ch[0])        {            t[x].ch[1]=t[y].fa=0;            push_up(x);        }    }}signed main(){    using namespace LCT;    read(n);read(Q);    for(int i=1; i<=n; i++)        t[i].num=t[i].m=t[i].sz=1;    for(int i=1,u,v; i<n; i++)    {        read(u);read(v);        link(u,v);    }    while(Q--)    {        char op;int x,y,c,d;        scanf("%c",&op);        read(x);read(y);        if(op=='+')        {            read(c);            split(x,y);pusha(y,c);        }        if(op=='-')        {            read(c);read(d);            cut(x,y);link(c,d);        }        if(op=='*')        {            read(c);            split(x,y);pushm(y,c);        }        if(op=='/')        {            split(x,y);            write(t[y].ans);pc('\n');        }    }    return 0;}
]]>
@@ -7693,7 +7693,7 @@ /2022/02/13/qian-tan-kuai-su-cheng/ - 浅谈快速乘

前言

想必大家都听说过快速幂

那快速乘是个什么东西呢?

考虑取模操作a*b%p1^10 ≤ a,b,p ≤ 2^10

可以发现在 long long情况下,我们直接取模会溢出

那么怎么办呢?

题目链接:https://www.luogu.com.cn/problem/U203580


快速乘

注:本文根据q779本人的习惯,所有的int都宏定义为了long long

标准快速乘

时间复杂度 $O(\log b)$,且正确性完备

类似于快速幂,具体见代码

#define int long longint ksc(int a,int b,int p){    int ans=0;    while(b)    {        if(b&1)ans=(ans+a)%p;        a=(a<<1)%p;b>>=1;    }    return ans;}

__int128_t

这是个自带的数据类型,而且根据我查阅到的资料来看,有一点点不太靠谱的感觉

它能存到 $2^{128}$ 的数量级,且NOIP等赛事中是明确可以使用的

而且本机编译通过了 (g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

#define int long longcout << (int)((__int128_t)a*b%p) << endl;cout << (int)((__int128)a*b%p) << endl; // 这样写也是可以的

据说win上不能用,因此建议还是使用标准快速乘吧


O(1)快速乘

一个正确性不太靠谱但是在这个数据范围下能算出正确答案的神奇算法

好像出自 2009年全国信息学奥林匹克冬令营论文 《论程序底层优化的一些方法与技巧》骆可强

它利用了long double的范围来算的

#define int long longint ksc(int a,int b,int p){    int t=(long double)a/p*b;    int ans=a*b-t*p;    if(ans<0)ans+=p;    if(ans>=p)ans-=p;    return ans;}

由于$ab-p\left\lfloor\dfrac{ab}{p}\right\rfloor$的值是“固定”的

即这两部分都会溢出,但long long保证了它们的差值基本不变

因此溢出也不会影响计算

但是因为使用了除法操作,会存在精度问题,不建议在模数大于1e+12时使用(即$10^{12}$)


Montgomery算法

目前还没有学会,先留个坑以后补

估计得到大学才会去研究吧。


总结

介绍了一些快速算乘法取模的算法

本文目前还没有彻底完工,有待更新

参考文献

[1] 快速乘总结

]]>
+ 浅谈快速乘

前言

想必大家都听说过快速幂

那快速乘是个什么东西呢?

考虑取模操作a*b%p1^10 ≤ a,b,p ≤ 2^10

可以发现在 long long情况下,我们直接取模会溢出

那么怎么办呢?

题目链接:https://www.luogu.com.cn/problem/U203580


快速乘

注:本文根据q779本人的习惯,所有的int都宏定义为了long long

标准快速乘

时间复杂度 \(O(\logb)\),且正确性完备

类似于快速幂,具体见代码

#define int long longint ksc(int a,int b,int p){    int ans=0;    while(b)    {        if(b&1)ans=(ans+a)%p;        a=(a<<1)%p;b>>=1;    }    return ans;}

__int128_t

这是个自带的数据类型,而且根据我查阅到的资料来看,有一点点不太靠谱的感觉

它能存到 \(2^{128}\)的数量级,且NOIP等赛事中是明确可以使用的

而且本机编译通过了(g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

#define int long longcout << (int)((__int128_t)a*b%p) << endl;cout << (int)((__int128)a*b%p) << endl; // 这样写也是可以的
据说win上不能用,因此建议还是使用标准快速乘吧

O(1)快速乘

一个正确性不太靠谱但是在这个数据范围下能算出正确答案的神奇算法

好像出自 2009年全国信息学奥林匹克冬令营论文《论程序底层优化的一些方法与技巧》骆可强

它利用了long double的范围来算的

#define int long longint ksc(int a,int b,int p){    int t=(long double)a/p*b;    int ans=a*b-t*p;    if(ans<0)ans+=p;    if(ans>=p)ans-=p;    return ans;}

由于\(ab-p\left\lfloor\dfrac{ab}{p}\right\rfloor\)的值是“固定”的

即这两部分都会溢出,但long long保证了它们的差值基本不变

因此溢出也不会影响计算

但是因为使用了除法操作,会存在精度问题,不建议在模数大于1e+12时使用(即\(10^{12}\))

Montgomery算法

目前还没有学会,先留个坑以后补

估计得到大学才会去研究吧。


总结

介绍了一些快速算乘法取模的算法

本文目前还没有彻底完工,有待更新

参考文献

[1] 快速乘总结

]]>
@@ -7720,7 +7720,7 @@ /2022/02/07/at4284-luo-gu-p1969-p3078-p5019-ti-jie/ - AT4284 & 洛谷 P1969 P3078 P5019 题解

题目链接:AT4284 P1969 P3078 P5019

题意:若干次区间减一,使所有数相等,求最小次数

这几道题就是一个std编出来的吧 $😅$

对于相邻两数 $a_i>a_{i-1}$ ,显然在一次操作中将它们同时减去是最优的

对答案的贡献为 $a_i-a_{i-1}$

推广一下其实是等价的,故时间复杂度 $O(n)$ 即可解决

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)int n;int a[MAXN],ans;signed main(){    ios::sync_with_stdio(0);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> a[i];        if(a[i]>a[i-1])ans+=a[i]-a[i-1];    }    cout << ans << endl;    return 0;}
]]>
+ AT4284 & 洛谷 P1969P3078 P5019 题解

题目链接:AT4284 P1969 P3078 P5019

题意:若干次区间减一,使所有数相等,求最小次数

这几道题就是一个std编出来的吧 \(😅\)

对于相邻两数 \(a_i>a_{i-1}\),显然在一次操作中将它们同时减去是最优的

对答案的贡献为 \(a_i-a_{i-1}\)

推广一下其实是等价的,故时间复杂度 \(O(n)\) 即可解决

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)int n;int a[MAXN],ans;signed main(){    ios::sync_with_stdio(0);    cin >> n;    for(int i=1; i<=n; i++)    {        cin >> a[i];        if(a[i]>a[i-1])ans+=a[i]-a[i-1];    }    cout << ans << endl;    return 0;}
]]>
@@ -7745,7 +7745,7 @@ /2022/02/05/luo-gu-p1234-xiao-a-de-kou-tou-shan-ti-jie/ - 洛谷P1234 小A的口头禅 题解

题目链接:P1234 小A的口头禅

给出了一个矩形,让你求出里面有几个hehe(方向无所谓,斜着不算)

数据范围很良心,嗯~

所以暴力枚举即可

值得注意的是 $\tt{eheh}$ 这种也算

顺便说一句,关了同步流的cin,cout还是很快的 qwq

鬼畜的代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1005)#define A (j+3<=m&&a[i][j]=='h'&&a[i][j+1]=='e'&&a[i][j+2]=='h'&&a[i][j+3]=='e')#define B (j-3>=1&&a[i][j-3]=='e'&&a[i][j-2]=='h'&&a[i][j-1]=='e'&&a[i][j]=='h')#define C (i+3<=n&&a[i][j]=='h'&&a[i+1][j]=='e'&&a[i+2][j]=='h'&&a[i+3][j]=='e')#define D (i-3>=1&&a[i-3][j]=='e'&&a[i-2][j]=='h'&&a[i-1][j]=='e'&&a[i][j]=='h')#define qwq ans+=A+B+C+Dint n,m,ans;char a[MAXN][MAXN];signed main(){    ios::sync_with_stdio(0);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> a[i]+1;    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)            qwq; // 代码的核心! qwq!    cout << ans;    return 0;}

为什么我要写这么简单的题的题解呢?因为这题只要qwq就能过了 qwq qqqqqqwqqqqq

]]>
+ 洛谷P1234 小A的口头禅 题解

题目链接:P1234小A的口头禅

给出了一个矩形,让你求出里面有几个hehe(方向无所谓,斜着不算)

数据范围很良心,嗯~

所以暴力枚举即可

值得注意的是 \(\tt{eheh}\)这种也算

顺便说一句,关了同步流的cin,cout还是很快的 qwq

鬼畜的代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1005)#define A (j+3<=m&&a[i][j]=='h'&&a[i][j+1]=='e'&&a[i][j+2]=='h'&&a[i][j+3]=='e')#define B (j-3>=1&&a[i][j-3]=='e'&&a[i][j-2]=='h'&&a[i][j-1]=='e'&&a[i][j]=='h')#define C (i+3<=n&&a[i][j]=='h'&&a[i+1][j]=='e'&&a[i+2][j]=='h'&&a[i+3][j]=='e')#define D (i-3>=1&&a[i-3][j]=='e'&&a[i-2][j]=='h'&&a[i-1][j]=='e'&&a[i][j]=='h')#define qwq ans+=A+B+C+Dint n,m,ans;char a[MAXN][MAXN];signed main(){    ios::sync_with_stdio(0);    cin >> n >> m;    for(int i=1; i<=n; i++)        cin >> a[i]+1;    for(int i=1; i<=n; i++)        for(int j=1; j<=m; j++)            qwq; // 代码的核心! qwq!    cout << ans;    return 0;}

为什么我要写这么简单的题的题解呢?因为这题只要qwq就能过了 qwqqqqqqqwqqqqq

]]>
@@ -7770,7 +7770,7 @@ /2022/02/03/luo-gu-p2804-shen-mi-shu-zi-ti-jie/ - 洛谷P2804 神秘数字 题解

题目链接:P2804 神秘数字

题意:询问有多少段连续区间的平均值大于 $m$

可以发现将每个数都减去 $m$ 后任意和大于 $0$ 的连续区间都满足题意

区间和可以用前缀和优化,记为 $s$

问题转化为了求 $s_j-s_i>0,0\le i <j$

移项可得 $s_i<s_j,0\le i <j$

问题又转化为了求顺序对(与逆序对相反)

于是可以用树状数组解决 更多解法?

注意当 $s_j>0$ 时 $i=0$ 也是一个解

时间复杂度 $O(n\log n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(2e5+5)#define mod 92084931int n,m,ans;int a[MAXN],bit[MAXN],b[MAXN];int lowbit(int x){return x&(-x);}void add(int x,int v){    while(x&&x<=n)    {        bit[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=bit[x];        x-=lowbit(x);    }    return res;}bool cmp(int x,int y){    return a[x]==a[y]?    x>y:a[x]<a[y];}signed main(){    ios::sync_with_stdio(0);    cin >> n >> m;    for(int i=1,x; i<=n; i++)    {        cin >> x;b[i]=i;        a[i]=a[i-1]+x-m;    }    sort(b+1,b+1+n,cmp);    for(int i=1; i<=n; i++)    {        add(b[i],1);        ans=(ans+sum(b[i]-1)+(a[b[i]]>0))%mod;    }    cout << ans;    return 0;}
]]>
+ 洛谷P2804 神秘数字 题解

题目链接:P2804神秘数字

题意:询问有多少段连续区间的平均值大于 \(m\)

可以发现将每个数都减去 \(m\)后任意和大于 \(0\)的连续区间都满足题意

区间和可以用前缀和优化,记为 \(s\)

问题转化为了求 \(s_j-s_i>0,0\le i<j\)

移项可得 \(s_i<s_j,0\le i<j\)

问题又转化为了求顺序对(与逆序对相反)

于是可以用树状数组解决 更多解法?

注意当 \(s_j>0\)\(i=0\) 也是一个解

时间复杂度 \(O(n\log n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(2e5+5)#define mod 92084931int n,m,ans;int a[MAXN],bit[MAXN],b[MAXN];int lowbit(int x){return x&(-x);}void add(int x,int v){    while(x&&x<=n)    {        bit[x]+=v;        x+=lowbit(x);    }}int sum(int x){    int res=0;    while(x>=1)    {        res+=bit[x];        x-=lowbit(x);    }    return res;}bool cmp(int x,int y){    return a[x]==a[y]?    x>y:a[x]<a[y];}signed main(){    ios::sync_with_stdio(0);    cin >> n >> m;    for(int i=1,x; i<=n; i++)    {        cin >> x;b[i]=i;        a[i]=a[i-1]+x-m;    }    sort(b+1,b+1+n,cmp);    for(int i=1; i<=n; i++)    {        add(b[i],1);        ans=(ans+sum(b[i]-1)+(a[b[i]]>0))%mod;    }    cout << ans;    return 0;}
]]>
@@ -7797,7 +7797,7 @@ /2022/02/03/luo-gu-p1522-usaco2.4-niu-de-lu-xing-cow-tours-ti-jie/ - 洛谷P1522 [USACO2.4]牛的旅行 Cow Tours 题解

题目链接:P1522 [USACO2.4]牛的旅行 Cow Tours

题意:给定一张无向图,有至少 $2$ 个连通分量,定义直径每个连通分量中任意两结点的最短路径的最大值,其中相邻结点边权为它们的欧几里德距离,现在要求添加一条边使得连通分量的数量减少 $1$ ,并使得这个新连通分量的直径尽可能地小,求这个最小值

题意已经简化过了 应该很好懂

由于 $N\le 150$ 显然我们可以用 Floyd 来求这个最短路径

任意两个连通块 $A,B$ 连边,可能出现以下三种情况

  1. $A$ 的直径最大
  2. $B$ 的直径最大
  3. $A$ 和 $B$ 相连后的新直径最大,即 $w(i,j) + d(i) + d(j)$ , 其中 $d(i)$ 指从结点 $i$ 出发的最长路径的长度

那我们求一下就好了 qwq 就这么简单

注意这个最大值不能在Floyd的时候更新,因为那个时候还不是最短路径…

时间复杂度 $O(n^3)$

代码如下(无视我的垃圾卡常

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(255)#define INF 1e18+5#define Max(a,b) a=fmax(a,b)#define Min(a,b) a=fmin(a,b)#define max3(a,b,c) fmax(a,fmax(b,c))int n;char s[MAXN];int x[MAXN],y[MAXN],f[MAXN];double ans=INF,fmx[MAXN],pmx[MAXN],g[MAXN][MAXN],A,B,C;double dis(int i,int j){return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));}void init(){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}signed main(){    scanf("%lld",&n);    for(int i=1; i<=n; i++)        scanf("%lld%lld",&x[i],&y[i]);    init();for(int i=1; i<=n; i++)    {        scanf("%s",s+1);        for(int j=1; j<=n; j++)            if(s[j]=='1')            {                merge(i,j);                g[i][j]=dis(i,j);            }else g[i][j]=(i!=j)?INF:0;    }    for(int k=1; k<=n; k++)        for(int i=1; i<=n; i++)            if(g[i][k]!=INF)            for(int j=1; j<=n; j++)                Min(g[i][j],g[i][k]+g[k][j]);    for(int i=1; i<=n; i++)    {        for(int j=1; j<=n; j++)            if(g[i][j]<INF)                Max(pmx[i],g[i][j]);        Max(fmx[find(i)],pmx[i]);    }    for(int i=1; i<=n; i++)        for(int j=i+1; j<=n; j++)            if(find(i)!=find(j))            {                A=fmx[find(i)],B=fmx[find(j)];                C=pmx[i]+pmx[j]+dis(i,j);                Min(ans,max3(A,B,C));            }    printf("%.6lf",ans);    return 0;}
]]>
+ 洛谷P1522[USACO2.4]牛的旅行 Cow Tours 题解

题目链接:P1522[USACO2.4]牛的旅行 Cow Tours

题意:给定一张无向图,有至少 \(2\)个连通分量,定义直径每个连通分量中任意两结点的最短路径的最大值,其中相邻结点边权为它们的欧几里德距离,现在要求添加一条边使得连通分量的数量减少\(1\),并使得这个新连通分量的直径尽可能地小,求这个最小值

题意已经简化过了 应该很好懂

由于 \(N\le 150\) 显然我们可以用Floyd 来求这个最短路径

任意两个连通块 \(A,B\)连边,可能出现以下三种情况

  1. \(A\) 的直径最大
  2. \(B\) 的直径最大
  3. \(A\)\(B\) 相连后的新直径最大,即 \(w(i,j) + d(i) + d(j)\) , 其中 \(d(i)\) 指从结点 \(i\) 出发的最长路径的长度

那我们求一下就好了 qwq 就这么简单

注意这个最大值不能在Floyd的时候更新,因为那个时候还不是最短路径...

时间复杂度 \(O(n^3)\)

代码如下(无视我的垃圾卡常

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(255)#define INF 1e18+5#define Max(a,b) a=fmax(a,b)#define Min(a,b) a=fmin(a,b)#define max3(a,b,c) fmax(a,fmax(b,c))int n;char s[MAXN];int x[MAXN],y[MAXN],f[MAXN];double ans=INF,fmx[MAXN],pmx[MAXN],g[MAXN][MAXN],A,B,C;double dis(int i,int j){return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));}void init(){for(int i=1; i<=n; i++) f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}signed main(){    scanf("%lld",&n);    for(int i=1; i<=n; i++)        scanf("%lld%lld",&x[i],&y[i]);    init();for(int i=1; i<=n; i++)    {        scanf("%s",s+1);        for(int j=1; j<=n; j++)            if(s[j]=='1')            {                merge(i,j);                g[i][j]=dis(i,j);            }else g[i][j]=(i!=j)?INF:0;    }    for(int k=1; k<=n; k++)        for(int i=1; i<=n; i++)            if(g[i][k]!=INF)            for(int j=1; j<=n; j++)                Min(g[i][j],g[i][k]+g[k][j]);    for(int i=1; i<=n; i++)    {        for(int j=1; j<=n; j++)            if(g[i][j]<INF)                Max(pmx[i],g[i][j]);        Max(fmx[find(i)],pmx[i]);    }    for(int i=1; i<=n; i++)        for(int j=i+1; j<=n; j++)            if(find(i)!=find(j))            {                A=fmx[find(i)],B=fmx[find(j)];                C=pmx[i]+pmx[j]+dis(i,j);                Min(ans,max3(A,B,C));            }    printf("%.6lf",ans);    return 0;}
]]>
@@ -7824,7 +7824,7 @@ /2022/02/03/luo-gu-p5764-cqoi2005-xin-nian-hao-ti-jie/ - 洛谷P5764 [CQOI2005]新年好 题解

题目链接:P5764 [CQOI2005]新年好

题意:从 $1$ 号结点出发,要访问其他 $5$ 个结点,顺序随意,访问一个结点后不用返回

注意到 $5! = O(1)$

那我们可以在 $1$ 和其他 $5$ 个点跑一下dijkstra,然后枚举所有可能的顺序即可

这里给出了两种枚举方式,分别为dfsnext_permutation()

实测(C++14 O2)后者快30ms左右,不过均可轻松通过此题

我才不会说我是因为先写后者没调出来才有的这篇题解呢 QAQ

dfs

#include <bits/stdc++.h>using namespace std;#define int long long#define INF (int)(5e10+233)#define gc() getchar()#define pc(a) putchar(a)#define MAXN (int)(5e4+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,w,next;}e[MAXN<<2];struct node{    int u,dis;    bool operator<(const node &o)const    {        return dis>o.dis;    }};int n,m,a[15],d[8][MAXN],vis[MAXN];int head[MAXN],pos=1,ans=INF,used[MAXN];void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}priority_queue<node> q;void dijkstra(int st,int idx){    for(int i=1; i<=n; i++)        d[idx][i]=INF,vis[i]=0;    q.push({st,0});d[idx][st]=0;    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])            continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[idx][v]>d[idx][u]+e[i].w)            {                d[idx][v]=d[idx][u]+e[i].w;                if(!vis[v])                    q.push({v,d[idx][v]});            }        }    }}void dfs(int dep,int now,int last){    if(now>ans)return;    if(dep==5)    {        ans=min(ans,now);        return;    }    for(int i=1; i<=5; i++)        if(!used[i])        {            used[i]=1;            dfs(dep+1,now+d[last][a[i]],i);            used[i]=0;        }}signed main(){    read(n);read(m);    for(int i=1; i<=5; i++)        read(a[i]);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,w);    }    dijkstra(1,0);    for(int i=1; i<=5; i++)        dijkstra(a[i],i);    dfs(0,0,0);    write(ans);pc('\n');    return 0;}

next_permutation()

#include <bits/stdc++.h>using namespace std;#define int long long#define INF (int)(5e10+233)#define gc() getchar()#define pc(a) putchar(a)#define MAXN (int)(5e4+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,w,next;}e[MAXN<<2];struct node{    int u,dis;    bool operator<(const node &o)const    {        return dis>o.dis;    }};int n,m,a[15],b[15],d[8][MAXN],vis[MAXN];int head[MAXN],pos=1,ans=INF;unordered_map<int,int>id;void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}priority_queue<node> q;void dijkstra(int st,int idx){    for(int i=1; i<=n; i++)        d[idx][i]=INF,vis[i]=0;    q.push({st,0});d[idx][st]=0;    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])            continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[idx][v]>d[idx][u]+e[i].w)            {                d[idx][v]=d[idx][u]+e[i].w;                if(!vis[v])                    q.push({v,d[idx][v]});            }        }    }}int f(int x,int y){return d[id[x]][y];}int sum(){    int res=f(1,a[b[1]]);    for(int i=2; i<=5; i++)        res+=f(a[b[i-1]],a[b[i]]);    return res;}signed main(){    read(n);read(m);    for(int i=1; i<=5; i++)        read(a[i]);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,w);    }    dijkstra(1,id[1]=0);    for(int i=1; i<=5; i++)        dijkstra(a[i],id[a[i]]=i);    for(int i=1; i<=5; i++)b[i]=i;    do    {        ans=min(ans,sum());    }while(next_permutation(b+1,b+1+5));    write(ans);pc('\n');    return 0;}
]]>
+ 洛谷P5764 [CQOI2005]新年好题解

题目链接:P5764[CQOI2005]新年好

题意:从 \(1\)号结点出发,要访问其他 \(5\)个结点,顺序随意,访问一个结点后不用返回

注意到 \(5! = O(1)\)

那我们可以在 \(1\) 和其他 \(5\)个点跑一下dijkstra,然后枚举所有可能的顺序即可

这里给出了两种枚举方式,分别为dfsnext_permutation()

实测(C++14 O2)后者快30ms左右,不过均可轻松通过此题

我才不会说我是因为先写后者没调出来才有的这篇题解呢 QAQ

dfs

#include <bits/stdc++.h>using namespace std;#define int long long#define INF (int)(5e10+233)#define gc() getchar()#define pc(a) putchar(a)#define MAXN (int)(5e4+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,w,next;}e[MAXN<<2];struct node{    int u,dis;    bool operator<(const node &o)const    {        return dis>o.dis;    }};int n,m,a[15],d[8][MAXN],vis[MAXN];int head[MAXN],pos=1,ans=INF,used[MAXN];void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}priority_queue<node> q;void dijkstra(int st,int idx){    for(int i=1; i<=n; i++)        d[idx][i]=INF,vis[i]=0;    q.push({st,0});d[idx][st]=0;    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])            continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[idx][v]>d[idx][u]+e[i].w)            {                d[idx][v]=d[idx][u]+e[i].w;                if(!vis[v])                    q.push({v,d[idx][v]});            }        }    }}void dfs(int dep,int now,int last){    if(now>ans)return;    if(dep==5)    {        ans=min(ans,now);        return;    }    for(int i=1; i<=5; i++)        if(!used[i])        {            used[i]=1;            dfs(dep+1,now+d[last][a[i]],i);            used[i]=0;        }}signed main(){    read(n);read(m);    for(int i=1; i<=5; i++)        read(a[i]);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,w);    }    dijkstra(1,0);    for(int i=1; i<=5; i++)        dijkstra(a[i],i);    dfs(0,0,0);    write(ans);pc('\n');    return 0;}

next_permutation()

#include <bits/stdc++.h>using namespace std;#define int long long#define INF (int)(5e10+233)#define gc() getchar()#define pc(a) putchar(a)#define MAXN (int)(5e4+5)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}struct Edge{    int u,v,w,next;}e[MAXN<<2];struct node{    int u,dis;    bool operator<(const node &o)const    {        return dis>o.dis;    }};int n,m,a[15],b[15],d[8][MAXN],vis[MAXN];int head[MAXN],pos=1,ans=INF;unordered_map<int,int>id;void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}priority_queue<node> q;void dijkstra(int st,int idx){    for(int i=1; i<=n; i++)        d[idx][i]=INF,vis[i]=0;    q.push({st,0});d[idx][st]=0;    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])            continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[idx][v]>d[idx][u]+e[i].w)            {                d[idx][v]=d[idx][u]+e[i].w;                if(!vis[v])                    q.push({v,d[idx][v]});            }        }    }}int f(int x,int y){return d[id[x]][y];}int sum(){    int res=f(1,a[b[1]]);    for(int i=2; i<=5; i++)        res+=f(a[b[i-1]],a[b[i]]);    return res;}signed main(){    read(n);read(m);    for(int i=1; i<=5; i++)        read(a[i]);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,w);    }    dijkstra(1,id[1]=0);    for(int i=1; i<=5; i++)        dijkstra(a[i],id[a[i]]=i);    for(int i=1; i<=5; i++)b[i]=i;    do    {        ans=min(ans,sum());    }while(next_permutation(b+1,b+1+5));    write(ans);pc('\n');    return 0;}
]]>
@@ -7851,7 +7851,7 @@ /2022/02/02/luo-gu-p1462-tong-wang-ao-ge-rui-ma-de-dao-lu-ti-jie/ - 洛谷P1462 通往奥格瑞玛的道路 题解

题目链接:P1462 通往奥格瑞玛的道路

题意:在艾泽拉斯,有 $n$ 个城市。编号为 $1,2,3,\ldots,n$ 。

城市之间有 $m$ 条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。

每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。

假设 $1$ 为暴风城,$n$ 为奥格瑞玛,而他的血量最多为 $b$ ,出发时他的血量是满的。

歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。

本题的数据是真的水,而且也没说清楚 $b$ 最后可不可以为 $0$ ,瞎搞的做法都能写 81pts qwq

看到最大值最小,首先可以想到二分

事实上,由于我们不清楚血量和最小值究竟有何关系(没关系),也只能用二分

注:二分的是这个最小值

在二分了一个值后,我们把伤害看作边权,然后跑个最短路,找一下最少伤害的路径,判断一下能不能到达终点即可

细节还是有点多的 qwq 懒得讲了看代码吧

Dijkstra

关于SPFA,它死了

实测(C++14 O2)SPFA 755ms,dijkstra 208ms

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(1e4+5)template<typename T>void read(T &k){    char ch=gc(); T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,b,l,r;struct Edge{    int u,v,w,next;}e[MAXN*10];struct node{    int u,dis;    bool operator<(const node &o)const        {return dis>o.dis;}};int head[MAXN],pos=1,a[MAXN],t[MAXN],d[MAXN],vis[MAXN];void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}priority_queue<node> q;int dijkstra(int bd){    if(bd<a[1]||bd<a[n])return 0;    memset(d,0x3f,sizeof(d));    q.push({1,0});d[1]=0;    for(int i=1; i<=n; i++)        vis[i]=a[i]>bd;    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])            continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[v]>d[u]+e[i].w)            {                d[v]=d[u]+e[i].w;                q.push({v,d[v]});            }        }    }    return d[n]<b;}signed main(){    read(n);read(m);read(b);    for(int i=1; i<=n; i++)        read(a[i]),t[i]=a[i];    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,w);    }    sort(t+1,t+1+n);l=1;r=n;    if(!dijkstra(t[n]))return puts("AFK"),0;    while(l<r)    {        int mid=(l+r)>>1;        if(dijkstra(t[mid]))r=mid;        else l=mid+1;    }    write(t[l]);pc('\n');    return 0;   }

SPFA

好吧我知道有写SPFA的

贴下代码吧…

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(1e4+5)template<typename T>void read(T &k){    char ch=gc(); T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,b,l,r;struct Edge{    int u,v,w,next;}e[MAXN*10];int head[MAXN],pos=1,a[MAXN],t[MAXN],d[MAXN],vis[MAXN];void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}queue<int> q;int spfa(int bd){    if(bd<a[1]||bd<a[n])return 0;    memset(d,0x3f,sizeof(d));    q.push(1);d[1]=0;vis[1]=1;    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[v]>d[u]+e[i].w&&a[v]<=bd)            {                d[v]=d[u]+e[i].w;                if(!vis[v])                    q.push(v),vis[v]=1;            }        }    }    return d[n]<b;}signed main(){    read(n);read(m);read(b);    for(int i=1; i<=n; i++)        read(a[i]),t[i]=a[i];    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,w);    }    sort(t+1,t+1+n);l=1;r=n;    if(!spfa(t[n]))return puts("AFK"),0;    while(l<r)    {        int mid=(l+r)>>1;        if(spfa(t[mid]))r=mid;        else l=mid+1;    }    write(t[l]);pc('\n');    return 0;   }
]]>
+ 洛谷P1462 通往奥格瑞玛的道路题解

题目链接:P1462通往奥格瑞玛的道路

题意:在艾泽拉斯,有 \(n\) 个城市。编号为 \(1,2,3,\ldots,n\) 。

城市之间有 \(m\)条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联盟的攻击,进而损失一定的血量。

每次经过一个城市,都会被收取一定的过路费(包括起点和终点)。路上并没有收费站。

假设 \(1\) 为暴风城,\(n\) 为奥格瑞玛,而他的血量最多为 \(b\) ,出发时他的血量是满的。

歪嘴哦不希望花很多钱,他想知道,在可以到达奥格瑞玛的情况下,他所经过的所有城市中最多的一次收取的费用的最小值是多少。

本题的数据是真的水,而且也没说清楚 \(b\) 最后可不可以为 \(0\) ,瞎搞的做法都能写 81pts qwq

看到最大值最小,首先可以想到二分

事实上,由于我们不清楚血量和最小值究竟有何关系(没关系),也只能用二分

注:二分的是这个最小值

在二分了一个值后,我们把伤害看作边权,然后跑个最短路,找一下最少伤害的路径,判断一下能不能到达终点即可

细节还是有点多的 qwq 懒得讲了看代码吧

Dijkstra

关于SPFA,它死了

实测(C++14 O2)SPFA 755ms,dijkstra 208ms

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(1e4+5)template<typename T>void read(T &k){    char ch=gc(); T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,b,l,r;struct Edge{    int u,v,w,next;}e[MAXN*10];struct node{    int u,dis;    bool operator<(const node &o)const        {return dis>o.dis;}};int head[MAXN],pos=1,a[MAXN],t[MAXN],d[MAXN],vis[MAXN];void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}priority_queue<node> q;int dijkstra(int bd){    if(bd<a[1]||bd<a[n])return 0;    memset(d,0x3f,sizeof(d));    q.push({1,0});d[1]=0;    for(int i=1; i<=n; i++)        vis[i]=a[i]>bd;    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])            continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[v]>d[u]+e[i].w)            {                d[v]=d[u]+e[i].w;                q.push({v,d[v]});            }        }    }    return d[n]<b;}signed main(){    read(n);read(m);read(b);    for(int i=1; i<=n; i++)        read(a[i]),t[i]=a[i];    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,w);    }    sort(t+1,t+1+n);l=1;r=n;    if(!dijkstra(t[n]))return puts("AFK"),0;    while(l<r)    {        int mid=(l+r)>>1;        if(dijkstra(t[mid]))r=mid;        else l=mid+1;    }    write(t[l]);pc('\n');    return 0;   }

SPFA

好吧我知道有写SPFA的

贴下代码吧...

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(1e4+5)template<typename T>void read(T &k){    char ch=gc(); T x=0,f=1;    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){k=-k;pc('-');}    if(k>9)write(k/10);    pc(k%10+'0');}int n,m,b,l,r;struct Edge{    int u,v,w,next;}e[MAXN*10];int head[MAXN],pos=1,a[MAXN],t[MAXN],d[MAXN],vis[MAXN];void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}queue<int> q;int spfa(int bd){    if(bd<a[1]||bd<a[n])return 0;    memset(d,0x3f,sizeof(d));    q.push(1);d[1]=0;vis[1]=1;    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[v]>d[u]+e[i].w&&a[v]<=bd)            {                d[v]=d[u]+e[i].w;                if(!vis[v])                    q.push(v),vis[v]=1;            }        }    }    return d[n]<b;}signed main(){    read(n);read(m);read(b);    for(int i=1; i<=n; i++)        read(a[i]),t[i]=a[i];    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);addEdge(v,u,w);    }    sort(t+1,t+1+n);l=1;r=n;    if(!spfa(t[n]))return puts("AFK"),0;    while(l<r)    {        int mid=(l+r)>>1;        if(spfa(t[mid]))r=mid;        else l=mid+1;    }    write(t[l]);pc('\n');    return 0;   }
]]>
@@ -7876,7 +7876,7 @@ /2022/01/29/uva11475-extend-to-palindrome-ti-jie/ - UVA11475 Extend to Palindrome 题解

题目链接:UVA11475 Extend to Palindrome

题意

输入多个字符串。

对于每个字符串 $S^{}$ ,求出一个字符串$S^{}$, $S^{*}$ 需要满足:

  1. $S^$为 $S^{}$ 的前缀;
  2. $S^*$ 是一个回文字符串;
  3. $|S^{*}|$应尽可能小;

对于每个 $S$ ,输出 $S^{*}$ ,每行输出以换行符结尾。

可以发现我们似乎要从原字符串中找到一个最长的回文串

然后以该回文串的中心为对称轴对称过去

amanaplanacanal要变换成amanaplanacanalpanama

但是还有一个问题,比如下面这个例子

axxxxxxxxxxxxxxdyyy

由于 $S$ 为 $S^{}$ 的前缀,所以我们要找的是*最长的后缀回文串

使用Manacher算法即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)char a[MAXN<<1],s[MAXN];int x,mx,n,m,p[MAXN<<1],mid=1,r=1;void init(){    mx=1;mid=1;r=1;    memset(p,0,(m+1)*sizeof(int));}signed main(){    while(~scanf("%s",s+1))    {                n=strlen(s+1);a[0]='$';        for(int i=1; i<=n; i++)            a[i*2-1]='#',a[i*2]=s[i];        m=strlen(a+1);        a[++m]='#';        init();        for(int i=1; i<=m; i++)        {            if(i<=r)p[i]=min(p[(mid<<1)-i],r-i+1);            while(a[i-p[i]]==a[i+p[i]])++p[i];            if(i+p[i]-1>=r)r=i+p[i]-1,mid=i;            if(i/2+(p[i]-1)/2==n)mx=max(mx,p[i]-1);        }        printf("%s",s+1);        for(int i=n-mx; i>=1; i--)            putchar(s[i]);        puts("");    }    return 0;}
]]>
+ UVA11475 Extend toPalindrome 题解

题目链接:UVA11475 Extend toPalindrome

题意

输入多个字符串。

对于每个字符串 \(S^{*}\),求出一个字符串\(S^{*}\)\(S^{*}\) 需要满足:

  1. \(S^*\)\(S^{*}\) 的前缀;
  2. \(S^*\) 是一个回文字符串;
  3. \(|S^{*}|\)应尽可能小;

对于每个 \(S\) ,输出 \(S^{*}\) ,每行输出以换行符结尾。

可以发现我们似乎要从原字符串中找到一个最长的回文串

然后以该回文串的中心为对称轴对称过去

amanaplanacanal要变换成amanaplanacanalpanama

但是还有一个问题,比如下面这个例子

axxxxxxxxxxxxxxdyyy

由于 \(S\)\(S^{*}\)的前缀,所以我们要找的是最长的后缀回文串

使用Manacher算法即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)char a[MAXN<<1],s[MAXN];int x,mx,n,m,p[MAXN<<1],mid=1,r=1;void init(){    mx=1;mid=1;r=1;    memset(p,0,(m+1)*sizeof(int));}signed main(){    while(~scanf("%s",s+1))    {                n=strlen(s+1);a[0]='$';        for(int i=1; i<=n; i++)            a[i*2-1]='#',a[i*2]=s[i];        m=strlen(a+1);        a[++m]='#';        init();        for(int i=1; i<=m; i++)        {            if(i<=r)p[i]=min(p[(mid<<1)-i],r-i+1);            while(a[i-p[i]]==a[i+p[i]])++p[i];            if(i+p[i]-1>=r)r=i+p[i]-1,mid=i;            if(i/2+(p[i]-1)/2==n)mx=max(mx,p[i]-1);        }        printf("%s",s+1);        for(int i=n-mx; i>=1; i--)            putchar(s[i]);        puts("");    }    return 0;}
]]>
@@ -7903,7 +7903,7 @@ /2022/01/28/luo-gu-p3121-usaco15feb-censoring-g-ti-jie/ - 洛谷P3121 [USACO15FEB] Censoring G 题解

题目链接:P3121 [USACO15FEB] Censoring G

题意:给出文本串 $t$ 和$n$ 个模式串 $s$ ,删除在 $t$ 中第一次出现的子串 $s_i$ ,并重复这个过程(在产生的新串 $t’$ 上继续删除操作),求最后的结果(所有模式串都要删掉)

多模式串匹配很容易想到是AC自动机

那删除操作怎么处理呢?

注意到删除子串后,在删除的子段后继续匹配可能需要该子段前的已经算出(显然)的匹配值

那么我们可以用两个栈分别维护匹配时的经过的点和答案

在每次删除后我们只要跳回原来的点即可

时间复杂度 $O(|t| + \sum |s_i|)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)int n,tot,top,trie[MAXN][32];char ans[MAXN];char t[MAXN],s[MAXN];int fail[MAXN],e[MAXN],dep[MAXN],stk[MAXN];void insert(int l,char *s){int u=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[u][c])trie[u][c]=++tot;u=trie[u][c];}e[u]=1;}queue<int> q;void build(){for(int i=0; i<26; i++)if(trie[0][i]){q.push(trie[0][i]);dep[trie[0][i]]=1;}while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];q.push(trie[u][i]);dep[trie[u][i]]=dep[u]+1;}elsetrie[u][i]=trie[fail[u]][i];}}}void AC(int l,char *t){int u=0;stk[0]=u; // 根节点不要忘了for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];stk[++top]=u;ans[top]=t[i];if(e[u])top-=dep[u],u=stk[top];}ans[top+1]='\0'; // 不要忘了哦!}signed main(){scanf("%s\n",t+1);scanf("%lld",&n);for(int i=1; i<=n; i++)scanf("%s",s+1),insert(strlen(s+1),s);build();AC(strlen(t+1),t);printf("%s",ans+1);return 0;}
]]>
+ 洛谷P3121 [USACO15FEB]Censoring G 题解

题目链接:P3121[USACO15FEB] Censoring G

题意:给出文本串 \(t\) 和\(n\) 个模式串 \(s\) ,删除在 \(t\) 中第一次出现的子串 \(s_i\) ,并重复这个过程(在产生的新串 \(t’\)上继续删除操作),求最后的结果(所有模式串都要删掉)

多模式串匹配很容易想到是AC自动机

那删除操作怎么处理呢?

注意到删除子串后,在删除的子段后继续匹配可能需要该子段前的已经算出(显然)的匹配值

那么我们可以用两个栈分别维护匹配时的经过的点和答案

在每次删除后我们只要跳回原来的点即可

时间复杂度 \(O(|t| + \sum|s_i|)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)int n,tot,top,trie[MAXN][32];char ans[MAXN];char t[MAXN],s[MAXN];int fail[MAXN],e[MAXN],dep[MAXN],stk[MAXN];void insert(int l,char *s){int u=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[u][c])trie[u][c]=++tot;u=trie[u][c];}e[u]=1;}queue<int> q;void build(){for(int i=0; i<26; i++)if(trie[0][i]){q.push(trie[0][i]);dep[trie[0][i]]=1;}while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];q.push(trie[u][i]);dep[trie[u][i]]=dep[u]+1;}elsetrie[u][i]=trie[fail[u]][i];}}}void AC(int l,char *t){int u=0;stk[0]=u; // 根节点不要忘了for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];stk[++top]=u;ans[top]=t[i];if(e[u])top-=dep[u],u=stk[top];}ans[top+1]='\0'; // 不要忘了哦!}signed main(){scanf("%s\n",t+1);scanf("%lld",&n);for(int i=1; i<=n; i++)scanf("%s",s+1),insert(strlen(s+1),s);build();AC(strlen(t+1),t);printf("%s",ans+1);return 0;}
]]>
@@ -7932,7 +7932,7 @@ /2022/01/28/luo-gu-p4824-usaco15feb-censoring-s-ti-jie/ - 洛谷P4824 [USACO15FEB] Censoring S 题解

题目链接:P4824 [USACO15FEB] Censoring S

题意:给出字符串 $t$ 和 $s$ ,删除在 $t$ 中第一次出现的子串 $s$ ,并重复这个过程(在产生的新串 $t’$ 上继续删除操作),求最后的结果

匹配问题可以用KMP来解决

怎么处理删除操作呢?

可以观察下样例

注意到删除子串后,在删除的子段后继续匹配可能需要该子段前的已经算出(显然)的匹配值

那么我们可以用两个栈分别维护KMP的匹配值和答案

在每次删除后我们只要让top-=m即可,其中m为 $s$ 的长度

时间复杂度 $O(|t|+|s|)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e6+5)int n,m,fail[MAXN],top,stk[MAXN];char t[MAXN],s[MAXN],ans[MAXN];signed main(){scanf("%s\n%s\n",t+1,s+1);n=strlen(t+1);m=strlen(s+1);for(int i=2,j=0; i<=m; i++){while(j&&s[i]!=s[j+1])j=fail[j];if(s[i]==s[j+1])++j;fail[i]=j;}for(int i=1,j=0; i<=n; i++){while(j&&t[i]!=s[j+1])j=fail[j];if(t[i]==s[j+1])++j;stk[++top]=j;ans[top]=t[i];if(j==m)top-=m,j=stk[top];}ans[top+1]='\0'; // 不要忘了这个哦!printf("%s",ans+1);return 0;}

当然这个题还有个加强版

做法类似,只不过变成多模式串匹配了而已,题解

]]>
+ 洛谷P4824 [USACO15FEB]Censoring S 题解

题目链接:P4824[USACO15FEB] Censoring S

题意:给出字符串 \(t\) 和 \(s\) ,删除在 \(t\) 中第一次出现的子串 \(s\) ,并重复这个过程(在产生的新串 \(t’\) 上继续删除操作),求最后的结果

匹配问题可以用KMP来解决

怎么处理删除操作呢?

可以观察下样例

注意到删除子串后,在删除的子段后继续匹配可能需要该子段前的已经算出(显然)的匹配值

那么我们可以用两个栈分别维护KMP的匹配值和答案

在每次删除后我们只要让top-=m即可,其中m\(s\) 的长度

时间复杂度 \(O(|t|+|s|)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e6+5)int n,m,fail[MAXN],top,stk[MAXN];char t[MAXN],s[MAXN],ans[MAXN];signed main(){scanf("%s\n%s\n",t+1,s+1);n=strlen(t+1);m=strlen(s+1);for(int i=2,j=0; i<=m; i++){while(j&&s[i]!=s[j+1])j=fail[j];if(s[i]==s[j+1])++j;fail[i]=j;}for(int i=1,j=0; i<=n; i++){while(j&&t[i]!=s[j+1])j=fail[j];if(t[i]==s[j+1])++j;stk[++top]=j;ans[top]=t[i];if(j==m)top-=m,j=stk[top];}ans[top+1]='\0'; // 不要忘了这个哦!printf("%s",ans+1);return 0;}

当然这个题还有个加强版

做法类似,只不过变成多模式串匹配了而已,题解。

]]>
@@ -7961,7 +7961,7 @@ /2022/01/28/luo-gu-p5357-mo-ban-ac-zi-dong-ji-er-ci-jia-qiang-ban-ti-jie/ - 洛谷P5357 【模板】AC 自动机(二次加强版)题解

题目链接:P5357 【模板】AC 自动机(二次加强版)

题意:$n$ 个模式串 $s_i$(不保证互异),要求输出这些模式串在文本串 $S$ 中出现的次数

建议大家先去做下加强版的,题解在此

我们已经在加强版初步解决了次数统计的问题

可以发现本题的数据范围 $n\le2\times10^5,\sum |s_i|\le2\times10^5,|S|\le2\times10^5$

而原来算法的时间复杂度是 $O\left(|S|\left|\max\{s_i\}\right|\right)$,T飞了

那么考虑怎么优化暴力跳fail的问题

注意到所有fail连出的有向边构成了一个DAG(有向无环图)

证明很简单,最长后缀一定是单调递减的

我们把这个DAG看作一棵树

那么所有的儿子结点一定会跳到父亲结点,并使父亲结点权值增加1

解法一:直接树形dp统计答案

这个我没写代码 qwq

解法二:拓扑排序

我们只要在拓扑排序的过程中统计答案即可

这样我们就可以把时间复杂度压到 $O\left(\sum|s_i|+|S|\right)$ 了!

其他注意点:

由于可能存在相同的模式串,显然它们的出现次数相同

那我们原来的e[u]=id就不可用了

咋办?并查集啊!

而在本题中较为特殊,合并产生的图一定是个菊花图

所以不用并查集,直接用数组也可(这样常数小一点)

但是我一开始写的并查集懒地改,就这样吧 qwq 反正影响很小

代码如下:

#include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(2e5+5)#define L (int)(2e6+5)char t[L],s[N];int n,ans[N],e[N],val[N],in[N];int trie[N][32],tot,fail[N],f[N];void init(){for(int i=1; i<=n; i++)f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}void insert(int l,char *s,int id){int u=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[u][c])trie[u][c]=++tot;u=trie[u][c];}if(!e[u])e[u]=id;else merge(id,e[u]);}queue<int>q;void build(){for(int i=0; i<26; i++)if(trie[0][i])q.push(trie[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];++in[trie[fail[u]][i]];q.push(trie[u][i]);}else trie[u][i]=trie[fail[u]][i];}}}void AC(int l,char *t){int u=0;for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];++val[u];}for(int i=1; i<=tot; i++)if(!in[i])q.push(i);while(!q.empty()){int u=q.front();q.pop();if(e[u])ans[e[u]]=val[u];val[fail[u]]+=val[u];if(!--in[fail[u]])q.push(fail[u]);}}signed main(){scanf("%lld",&n); init();for(int i=1; i<=n; i++){scanf("%s\n",s+1);insert(strlen(s+1),s,i);}scanf("%s\n",t+1);build();AC(strlen(t+1),t);for(int i=1; i<=n; i++)printf("%lld\n",ans[find(i)]);return 0;}
]]>
+ 洛谷P5357 【模板】AC自动机(二次加强版)题解

题目链接:P5357【模板】AC 自动机(二次加强版)

题意\(n\)个模式串 \(s_i\)(不保证互异),要求输出这些模式串在文本串\(S\) 中出现的次数

建议大家先去做下加强版的,题解在此

我们已经在加强版初步解决了次数统计的问题

可以发现本题的数据范围 \(n\le2\times10^5,\sum|s_i|\le2\times10^5,|S|\le2\times10^5\)

而原来算法的时间复杂度是 \(O\left(|S|\left|\max\{s_i\}\right|\right)\),T飞了

那么考虑怎么优化暴力跳fail的问题

注意到所有fail连出的有向边构成了一个DAG(有向无环图)

证明很简单,最长后缀一定是单调递减的

我们把这个DAG看作一棵树

那么所有的儿子结点一定会跳到父亲结点,并使父亲结点权值增加1

解法一:直接树形dp统计答案

这个我没写代码 qwq

解法二:拓扑排序

我们只要在拓扑排序的过程中统计答案即可

这样我们就可以把时间复杂度压到 \(O\left(\sum|s_i|+|S|\right)\) 了!

其他注意点:

由于可能存在相同的模式串,显然它们的出现次数相同

那我们原来的e[u]=id就不可用了

咋办?并查集啊!

而在本题中较为特殊,合并产生的图一定是个菊花图

所以不用并查集,直接用数组也可(这样常数小一点)

但是我一开始写的并查集懒地改,就这样吧 qwq反正影响很小

代码如下:

#include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(2e5+5)#define L (int)(2e6+5)char t[L],s[N];int n,ans[N],e[N],val[N],in[N];int trie[N][32],tot,fail[N],f[N];void init(){for(int i=1; i<=n; i++)f[i]=i;}int find(int x){return f[x]==x?x:f[x]=find(f[x]);}void merge(int u,int v){f[find(u)]=find(v);}void insert(int l,char *s,int id){int u=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[u][c])trie[u][c]=++tot;u=trie[u][c];}if(!e[u])e[u]=id;else merge(id,e[u]);}queue<int>q;void build(){for(int i=0; i<26; i++)if(trie[0][i])q.push(trie[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];++in[trie[fail[u]][i]];q.push(trie[u][i]);}else trie[u][i]=trie[fail[u]][i];}}}void AC(int l,char *t){int u=0;for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];++val[u];}for(int i=1; i<=tot; i++)if(!in[i])q.push(i);while(!q.empty()){int u=q.front();q.pop();if(e[u])ans[e[u]]=val[u];val[fail[u]]+=val[u];if(!--in[fail[u]])q.push(fail[u]);}}signed main(){scanf("%lld",&n); init();for(int i=1; i<=n; i++){scanf("%s\n",s+1);insert(strlen(s+1),s,i);}scanf("%s\n",t+1);build();AC(strlen(t+1),t);for(int i=1; i<=n; i++)printf("%lld\n",ans[find(i)]);return 0;}
]]>
@@ -7992,7 +7992,7 @@ /2022/01/28/luo-gu-p3796-mo-ban-ac-zi-dong-ji-jia-qiang-ban-ti-jie/ - 洛谷P3796 【模板】AC 自动机(加强版) 题解

题目链接:P3796 【模板】AC 自动机(加强版)

题意: $T$ 组数据,每组 $N$ 个模式串 $s_i$ ,要求输出这些模式串在文本串 $S$ 中出现次数最多的模式串以及它们的出现次数,按输入顺序输出

可以发现这是个AC自动机的裸题

那怎么处理出现次数呢?

我们只需要把匹配过程中所有可能产生的字符串都次数+1

例如在匹配到she的时候,可以发现它的任意后缀都可能成为其他模式串的前缀

那我们只要将(如果有的话)she,he,e这三个都加上1

这个过程不就是沿着当前结点的fail指针往上跳吗?(上指结点深度小的,根节点深度为0)

还有,多组数据一定要清空哦!

char数组不一定要,因为在读取的时候会在最后加一个\0的,一般不影响

到此为止,一些题解就直接贴代码了

我们来分析一下单次询问的时间复杂度

AC自动机的时间复杂度 $O(\sum|s_i|+|S|)$

而我们统计的时候是暴力跳fail指针,那么最坏时间复杂度 $O(S\times \max\{|s_i|\})$

所以整个算法的时间复杂度 $O(T|S|\max\{|s_i|\})$

然后我们发现它的数据范围 $T\le50,S\le10^6,s_i\le70$ ,时间有 $3$ 秒

而且这道题也没有故意卡这个,那我们就可以水过去了(别急着关掉这个网页啊!)

先贴一下代码:qwq

#include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(155)#define S (int)(75)#define SZ (int)(155*75)#define L (int)(1e6+5)char t[L],s[N][S];int n,cnt[N],e[SZ],val[SZ];int trie[SZ][32],tot,fail[SZ];void init(){memset(fail,0,sizeof(fail));memset(cnt,0,sizeof(cnt));memset(e,0,sizeof(e));memset(trie,0,sizeof(trie));memset(val,0,sizeof(val));tot=0;}void insert(int l,char *s,int id){int u=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[u][c])trie[u][c]=++tot;u=trie[u][c];}e[u]=id;}queue<int>q;void build(){for(int i=0; i<26; i++)if(trie[0][i])q.push(trie[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];q.push(trie[u][i]);}else trie[u][i]=trie[fail[u]][i];}}}int AC(int l,char *t){int u=0,res=0;for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];for(int j=u; j; j=fail[j])++val[j];}for(int i=1; i<=tot; i++) if(e[i]){res=max(res,val[i]);cnt[e[i]]=val[i];}return res;}signed main(){while(scanf("%lld",&n)&&n){init();for(int i=1; i<=n; i++){scanf("%s\n",s[i]+1);insert(strlen(s[i]+1),s[i],i);}scanf("%s\n",t+1);build();int mx=AC(strlen(t+1),t);printf("%lld\n",mx);for(int i=1; i<=n; i++)if(cnt[i]==mx)printf("%s\n",s[i]+1);}return 0;}

那么问题来了,这题为什么不卡呢?

因为还有个 P5357 【模板】AC 自动机(二次加强版)

题解在此

]]>
+ 洛谷P3796 【模板】AC自动机(加强版) 题解

题目链接:P3796【模板】AC 自动机(加强版)

题意\(T\)组数据,每组 \(N\) 个模式串 \(s_i\) ,要求输出这些模式串在文本串 \(S\)中出现次数最多的模式串以及它们的出现次数,按输入顺序输出

可以发现这是个AC自动机的裸题

那怎么处理出现次数呢?

我们只需要把匹配过程中所有可能产生的字符串都次数+1

例如在匹配到she的时候,可以发现它的任意后缀都可能成为其他模式串的前缀

那我们只要将(如果有的话)she,he,e这三个都加上1

这个过程不就是沿着当前结点的fail指针往上跳吗?(上指结点深度小的,根节点深度为0)

还有,多组数据一定要清空哦!

char数组不一定要,因为在读取的时候会在最后加一个\0的,一般不影响

到此为止,一些题解就直接贴代码了

我们来分析一下单次询问的时间复杂度

AC自动机的时间复杂度 \(O(\sum|s_i|+|S|)\)

而我们统计的时候是暴力跳fail指针,那么最坏时间复杂度 \(O(S\times \max\{|s_i|\})\)

所以整个算法的时间复杂度 \(O(T|S|\max\{|s_i|\})\)

然后我们发现它的数据范围 \(T\le50,S\le10^6,s_i\le70\) ,时间有 \(3\) 秒

而且这道题也没有故意卡这个,那我们就可以水过去了(别急着关掉这个网页啊!)

先贴一下代码:qwq

#include <bits/stdc++.h>using namespace std;#define int long long#define N (int)(155)#define S (int)(75)#define SZ (int)(155*75)#define L (int)(1e6+5)char t[L],s[N][S];int n,cnt[N],e[SZ],val[SZ];int trie[SZ][32],tot,fail[SZ];void init(){memset(fail,0,sizeof(fail));memset(cnt,0,sizeof(cnt));memset(e,0,sizeof(e));memset(trie,0,sizeof(trie));memset(val,0,sizeof(val));tot=0;}void insert(int l,char *s,int id){int u=0;for(int i=1; i<=l; i++){int c=s[i]-'a';if(!trie[u][c])trie[u][c]=++tot;u=trie[u][c];}e[u]=id;}queue<int>q;void build(){for(int i=0; i<26; i++)if(trie[0][i])q.push(trie[0][i]);while(!q.empty()){int u=q.front();q.pop();for(int i=0; i<26; i++){if(trie[u][i]){fail[trie[u][i]]=trie[fail[u]][i];q.push(trie[u][i]);}else trie[u][i]=trie[fail[u]][i];}}}int AC(int l,char *t){int u=0,res=0;for(int i=1; i<=l; i++){u=trie[u][t[i]-'a'];for(int j=u; j; j=fail[j])++val[j];}for(int i=1; i<=tot; i++) if(e[i]){res=max(res,val[i]);cnt[e[i]]=val[i];}return res;}signed main(){while(scanf("%lld",&n)&&n){init();for(int i=1; i<=n; i++){scanf("%s\n",s[i]+1);insert(strlen(s[i]+1),s[i],i);}scanf("%s\n",t+1);build();int mx=AC(strlen(t+1),t);printf("%lld\n",mx);for(int i=1; i<=n; i++)if(cnt[i]==mx)printf("%s\n",s[i]+1);}return 0;}

那么问题来了,这题为什么不卡呢?

因为还有个 P5357【模板】AC 自动机(二次加强版)

题解在此

]]>
@@ -8019,7 +8019,7 @@ /2022/01/16/rmb-zhao-ling-wen-ti/ - RMB找零问题

来自某次研究性学习的作业

前言

可以先考虑这样的问题

给定 $n$ 种足量多的纸币,每种面额为 $a_i$ 元 $(0<a[i]≤10000,a[i]\in \Z,1≤i≤n≤100)$

给出需要找零的金额 $m (0≤m≤10000)$

请找到一种方法使得找零所需的纸币数量尽可能地少,输出最小数量

例如:$68=50+10+5+2+1,12=10+2$

尽管 $8=10-2$ ,但是收银员是不能支付 $-2$ 元的

可以看出这就是一道完全背包模板题

代码如下

#include <bits/stdc++.h>using namespace std;#define int long longint n,m;int a[105],dp[100005];signed main(){ios::sync_with_stdio(0);cin >> n >> m;memset(dp,0x3f,sizeof(dp));dp[0]=0;for(int i=1; i<=n; i++)cin >> a[i];for(int i=1; i<=n; i++)dp[a[i]]=1;for(int i=1; i<=n; i++)for(int j=a[i]; j<=m; j++)dp[j]=min(dp[j],dp[j-a[i]]+1);cout << dp[m] << endl;return 0;}

而本文讨论的是关于RMB(人民币)的找零问题


特殊的找零问题

考虑这样的问题

给定足量多的纸币,面额为 $1,2,5,10,20,50,100$ 元

给出需要找零的金额 $m(0≤m≤10^9)$

请找到一种方法使得找零所需的纸币数量尽可能地少,输出最小数量

例如:$68=50+10+5+2+1,12=10+2$

尽管 $8=10-2$ ,但是收银员是不能支付 $-2$ 元的

根据生活常识,我们可以发现这其实是个贪心问题

那我们来尝试证明

先把概念放一下

首先能用贪心解决的问题需要具备贪心选择最优子结构的性质


贪心选择

所谓贪心选择是指应用同一规则,将原问题转变成一个相似的但规模更小的子问题,而后的每一步
都是当前看似最佳的选择,且这种选择只依赖于已做出的选择,不依赖于未做出的选择

也就是说所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到

这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划的主要区别。

在动态规划中,每步所做的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才能做出选择。

而在贪心算法中,仅在当前状态下做出最好选择,即局部最优选择。然后再去解出做出这个选择后产生的相应的子问题。贪心算法所做的贪心选择可以依赖于以往所做过的选择,但决不依赖于将来所做的选择,也不依赖于子问题的解。正是由于这种差别,动态规划通常以自底向上的方式解各个问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式做出相继的贪心选择,每做一次贪心选择就将所求问题简化为规模更小的子问题


最优子结构

执行算法时,每一次得到的结果虽然都是当前问题的最优解(即局部最优解),但只有满足全局最

优解包含局部最优解时,才能保证最终得到的结果是最优解


证明

设 $c_i$ 为第 $i$ 种纸币的面额, $S_i$ 为总金额为 $i$ 时的最小花费

设总金额为 $n$ ,则原问题的最优解即为 $S_n=k$

现在将某一面值的纸币 $c_j$ 减少 $1$ ,则有 $S_{n-c_j} = k-1$ 为总金额 $n-c_j$ 时的最优解

否则设 $T_{n-c_j} = m$ 为总金额 $n-c_j$ 时的最优解,即 $m<k-1$

则 $T_{n-c_j} + 1$ 应为原问题的最优解,即 $m+1<k-1+1=k$ ,与最小花费为 $k$ 相矛盾

又由于纸币的数量足够多,因此问题间相互独立

故问题满足最优子结构性质

设 $c_i$ 为第 $i$ 种纸币的面额,$x_i$ 为贪心得到的最优解 $\sum x_i$ 中使用第 $i$ 种纸币的数量,

$y_i$ 为原问题最优解 $\sum y_i$ 中使用第 $i$ 种纸币的数量,令 $x_i<x_{i+1},y_i<y_{i+1}$

假设贪心的得到的最优解不是原问题的最优解,则存在一个最大的 $k$ ,使得 $x_k\ne y_k$

可以发现 $k\ne 1$

情况1:若 $x_k<y_k$ ,由于贪心算法每次选择的都是最大面额的纸币,因此不存在

情况2:若 $x_k>y_k$ ,此时一定使用了较小面额的纸币来替代 $c_k$ ,即 $\sum{c_i}=c_k$

显然对于任意一个币值,任意 $2$ 张小于它的相加都小于它,例如 $1+2<5$

因此此时 $\sum x_k < \sum y_k$ ,与 $\sum y_k$ 是最优解相矛盾,因此不成立

故问题满足贪心选择性质

因此我们就可以 $O(1)$ 求解了

代码不放了比较简单


其他情况

可以猜测,可能存在其他的情况也满足贪心的性质

还没研究 qwq 但是找到了一篇论文

https://arxiv.org/pdf/0809.0400v1.pdf

是关于怎样的面额才能使用贪心算法的


总结

简单证明了一下某些特定找零问题贪心算法的正确性


参考文献

[1] 人民币找零的贪婪算法最有解的证明

]]>
+ RMB找零问题

来自某次研究性学习的作业

前言

可以先考虑这样的问题

给定 \(n\)种足量多的纸币,每种面额为 \(a_i\)\((0<a[i]≤10000,a[i]\in\Z,1≤i≤n≤100)\)

给出需要找零的金额 \(m(0≤m≤10000)\)

请找到一种方法使得找零所需的纸币数量尽可能地少,输出最小数量

例如:\(68=50+10+5+2+1,12=10+2\)

尽管 \(8=10-2\),但是收银员是不能支付 \(-2\) 元的

可以看出这就是一道完全背包模板题

代码如下

#include <bits/stdc++.h>using namespace std;#define int long longint n,m;int a[105],dp[100005];signed main(){ios::sync_with_stdio(0);cin >> n >> m;memset(dp,0x3f,sizeof(dp));dp[0]=0;for(int i=1; i<=n; i++)cin >> a[i];for(int i=1; i<=n; i++)dp[a[i]]=1;for(int i=1; i<=n; i++)for(int j=a[i]; j<=m; j++)dp[j]=min(dp[j],dp[j-a[i]]+1);cout << dp[m] << endl;return 0;}

而本文讨论的是关于RMB(人民币)的找零问题


特殊的找零问题

考虑这样的问题

给定足量多的纸币,面额为 \(1,2,5,10,20,50,100\) 元

给出需要找零的金额 \(m(0≤m≤10^9)\)

请找到一种方法使得找零所需的纸币数量尽可能地少,输出最小数量

例如:\(68=50+10+5+2+1,12=10+2\)

尽管 \(8=10-2\),但是收银员是不能支付 \(-2\) 元的

根据生活常识,我们可以发现这其实是个贪心问题

那我们来尝试证明

先把概念放一下

首先能用贪心解决的问题需要具备贪心选择最优子结构的性质

贪心选择

所谓贪心选择是指应用同一规则,将原问题转变成一个相似的但规模更小的子问题,而后的每一步都是当前看似最佳的选择,且这种选择只依赖于已做出的选择,不依赖于未做出的选择

也就是说所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到

这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划的主要区别。

在动态规划中,每步所做的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才能做出选择。

而在贪心算法中,仅在当前状态下做出最好选择,即局部最优选择。然后再去解出做出这个选择后产生的相应的子问题。贪心算法所做的贪心选择可以依赖于以往所做过的选择,但决不依赖于将来所做的选择,也不依赖于子问题的解。正是由于这种差别,动态规划通常以自底向上的方式解各个问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式做出相继的贪心选择,每做一次贪心选择就将所求问题简化为规模更小的子问题


最优子结构

执行算法时,每一次得到的结果虽然都是当前问题的最优解(即局部最优解),但只有满足全局最

优解包含局部最优解时,才能保证最终得到的结果是最优解

证明

\(c_i\) 为第 \(i\) 种纸币的面额, \(S_i\) 为总金额为 \(i\) 时的最小花费

设总金额为 \(n\),则原问题的最优解即为 \(S_n=k\)

现在将某一面值的纸币 \(c_j\) 减少\(1\) ,则有 \(S_{n-c_j} = k-1\) 为总金额 \(n-c_j\) 时的最优解

否则设 \(T_{n-c_j} = m\) 为总金额\(n-c_j\) 时的最优解,即 \(m<k-1\)

\(T_{n-c_j} + 1\)应为原问题的最优解,即 \(m+1<k-1+1=k\) ,与最小花费为 \(k\) 相矛盾

又由于纸币的数量足够多,因此问题间相互独立

故问题满足最优子结构性质

\(c_i\) 为第 \(i\) 种纸币的面额,\(x_i\) 为贪心得到的最优解 \(\sum x_i\) 中使用第 \(i\) 种纸币的数量,

\(y_i\) 为原问题最优解 \(\sum y_i\) 中使用第 \(i\) 种纸币的数量,令 \(x_i<x_{i+1},y_i<y_{i+1}\)

假设贪心的得到的最优解不是原问题的最优解,则存在一个最大的 \(k\) ,使得 \(x_k\ne y_k\)

可以发现 \(k\ne 1\)

情况1:若 \(x_k<y_k\),由于贪心算法每次选择的都是最大面额的纸币,因此不存在

情况2:若 \(x_k>y_k\),此时一定使用了较小面额的纸币来替代 \(c_k\) ,即 \(\sum{c_i}=c_k\)

显然对于任意一个币值,任意 \(2\)张小于它的相加都小于它,例如 \(1+2<5\)

因此此时 \(\sum x_k < \sum y_k\),与 \(\sum y_k\)是最优解相矛盾,因此不成立

故问题满足贪心选择性质

因此我们就可以 \(O(1)\) 求解了

代码不放了比较简单

其他情况

可以猜测,可能存在其他的情况也满足贪心的性质

还没研究 qwq 但是找到了一篇论文

https://arxiv.org/pdf/0809.0400v1.pdf

是关于怎样的面额才能使用贪心算法的

总结

简单证明了一下某些特定找零问题贪心算法的正确性


参考文献

[1] 人民币找零的贪婪算法最有解的证明

]]>
@@ -8048,7 +8048,7 @@ /2022/01/01/luo-gu-p5116-usaco18dec-mixing-milk-b-ti-jie/ - 洛谷P5116 [USACO18DEC]Mixing Milk B 题解

题目链接:P5116 [USACO18DEC]Mixing Milk B

题意:三个桶有一定牛奶和容积,要循环倒100次

模拟,要注意一些细节

代码:

#include <bits/stdc++.h>using namespace std;#define int long longint a[4],b[4];signed main(){for(int i=1; i<=3; i++)cin >> b[i] >> a[i];for(int i=1; i<=100; i++){int x=(i%3)?(i%3):3,y=((i+1)%3)?((i+1)%3):3;if(a[x]>=b[y]-a[y])a[x]-=b[y]-a[y],a[y]=b[y];else a[y]+=a[x],a[x]=0;}for(int i=1; i<=3; i++)cout << a[i] << endl;return 0;}

本来不想发的,但是感觉自己写的比一些题解简洁 qwq

]]>
+ 洛谷P5116[USACO18DEC]Mixing Milk B 题解

题目链接:P5116[USACO18DEC]Mixing Milk B

题意:三个桶有一定牛奶和容积,要循环倒100次

模拟,要注意一些细节

代码:

#include <bits/stdc++.h>using namespace std;#define int long longint a[4],b[4];signed main(){for(int i=1; i<=3; i++)cin >> b[i] >> a[i];for(int i=1; i<=100; i++){int x=(i%3)?(i%3):3,y=((i+1)%3)?((i+1)%3):3;if(a[x]>=b[y]-a[y])a[x]-=b[y]-a[y],a[y]=b[y];else a[y]+=a[x],a[x]=0;}for(int i=1; i<=3; i++)cout << a[i] << endl;return 0;}

本来不想发的,但是感觉自己写的比一些题解简洁 qwq

]]>
@@ -8073,7 +8073,7 @@ /2022/01/01/luo-gu-p1204-usaco1.2-ji-niu-nai-milking-cows-ti-jie/ - 洛谷P1204 [USACO1.2]挤牛奶Milking Cows 题解

题目链接:P1204 [USACO1.2]挤牛奶Milking Cows

题意:给定 $n$ 个区间,求这些区间覆盖后最长的区间和未被覆盖的最长区间(未被覆盖区间的范围在最左端到最右端内,否则就没有意义了)

正如简化的题意,我们可以使用类似于差分的思想,如图

我们可以保存左右端点,然后从左向右遍历(排序一下就行,如果左右端点相同左端点优先)

每次碰到端点加一下就行,和为0时更新答案即可

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5005)int n,pos,ans1,ans2,sum,last;struct node{int isleft,x;const bool operator <(const node &o)const{if(x==o.x)return isleft>o.isleft;return x<o.x;}}a[MAXN<<1]; // 记得要乘2signed main(){scanf("%lld",&n);for(int i=1,l,r; i<=n; i++){scanf("%lld%lld",&l,&r);a[++pos]={1,l};a[++pos]={-1,r};}sort(a+1,a+1+pos);last=a[1].x;sum=1;for(int i=2; i<=pos; i++){sum+=a[i].isleft;if(!sum){ans1=max(ans1,a[i].x-last);ans2=max(ans2,a[i+1].x-a[i].x);last=a[i+1].x;}}printf("%lld %lld\n",ans1,ans2);return 0;}

刷点水题养生qwq

]]>
+ 洛谷P1204[USACO1.2]挤牛奶Milking Cows 题解

题目链接:P1204[USACO1.2]挤牛奶Milking Cows

题意:给定 \(n\)个区间,求这些区间覆盖后最长的区间和未被覆盖的最长区间(未被覆盖区间的范围在最左端到最右端内,否则就没有意义了)

正如简化的题意,我们可以使用类似于差分的思想,如图

我们可以保存左右端点,然后从左向右遍历(排序一下就行,如果左右端点相同左端点优先)

每次碰到端点加一下就行,和为0时更新答案即可

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5005)int n,pos,ans1,ans2,sum,last;struct node{int isleft,x;const bool operator <(const node &o)const{if(x==o.x)return isleft>o.isleft;return x<o.x;}}a[MAXN<<1]; // 记得要乘2signed main(){scanf("%lld",&n);for(int i=1,l,r; i<=n; i++){scanf("%lld%lld",&l,&r);a[++pos]={1,l};a[++pos]={-1,r};}sort(a+1,a+1+pos);last=a[1].x;sum=1;for(int i=2; i<=pos; i++){sum+=a[i].isleft;if(!sum){ans1=max(ans1,a[i].x-last);ans2=max(ans2,a[i+1].x-a[i].x);last=a[i+1].x;}}printf("%lld %lld\n",ans1,ans2);return 0;}

刷点水题养生qwq

]]>
@@ -8098,7 +8098,7 @@ /2022/01/01/chrome-zi-dong-cao-zong-gu-ge-xiao-kong-long/ - chrome 自动操纵谷歌小恐龙

全自动谷歌小恐龙 qwq

代码来自网络(链接

function TrexRunnerBot() {  const makeKeyArgs = (keyCode) => {    const preventDefault = () => void 0;    return {keyCode, preventDefault};  };  const upKeyArgs = makeKeyArgs(38);  const downKeyArgs = makeKeyArgs(40);  const startArgs = makeKeyArgs(32);  if (!Runner().playing) {    Runner().onKeyDown(startArgs);    setTimeout(() => {      Runner().onKeyUp(startArgs);    }, 500);  }  function conquerTheGame() {    if (!Runner || !Runner().horizon.obstacles[0]) return;    const obstacle = Runner().horizon.obstacles[0];    if (obstacle.typeConfig && obstacle.typeConfig.type === 'SNACK') return;    if (needsToTackle(obstacle) && closeEnoughToTackle(obstacle)) tackle(obstacle);  }  function needsToTackle(obstacle) {    return obstacle.yPos !== 50;  }  function closeEnoughToTackle(obstacle) {    return obstacle.xPos <= Runner().currentSpeed * 18;  }  function tackle(obstacle) {    if (isDuckable(obstacle)) {      duck();    } else {      jumpOver(obstacle);    }  }  function isDuckable(obstacle) {    return obstacle.yPos === 50;  }  function duck() {    Runner().onKeyDown(downKeyArgs);    setTimeout(() => {      Runner().onKeyUp(downKeyArgs);    }, 500);  }  function jumpOver(obstacle) {    if (isNextObstacleCloseTo(obstacle))      jumpFast();    else      Runner().onKeyDown(upKeyArgs);  }  function isNextObstacleCloseTo(currentObstacle) {    const nextObstacle = Runner().horizon.obstacles[1];    return nextObstacle && nextObstacle.xPos - currentObstacle.xPos <= Runner().currentSpeed * 42;  }  function jumpFast() {    Runner().onKeyDown(upKeyArgs);    Runner().onKeyUp(upKeyArgs);  }  return {conquerTheGame: conquerTheGame};}let bot = TrexRunnerBot();let botInterval = setInterval(bot.conquerTheGame, 2);

使用方法

小恐龙在chrome://dino(谷歌浏览器才行哦 qwq)

然后按下F12,在Console中粘贴以上代码,然后按Enter即可

在这里插入图片描述

q779太弱了才玩到2364分 QAQ

]]>
+ chrome 自动操纵谷歌小恐龙

全自动谷歌小恐龙 qwq

代码来自网络(链接)

function TrexRunnerBot() {  const makeKeyArgs = (keyCode) => {    const preventDefault = () => void 0;    return {keyCode, preventDefault};  };  const upKeyArgs = makeKeyArgs(38);  const downKeyArgs = makeKeyArgs(40);  const startArgs = makeKeyArgs(32);  if (!Runner().playing) {    Runner().onKeyDown(startArgs);    setTimeout(() => {      Runner().onKeyUp(startArgs);    }, 500);  }  function conquerTheGame() {    if (!Runner || !Runner().horizon.obstacles[0]) return;    const obstacle = Runner().horizon.obstacles[0];    if (obstacle.typeConfig && obstacle.typeConfig.type === 'SNACK') return;    if (needsToTackle(obstacle) && closeEnoughToTackle(obstacle)) tackle(obstacle);  }  function needsToTackle(obstacle) {    return obstacle.yPos !== 50;  }  function closeEnoughToTackle(obstacle) {    return obstacle.xPos <= Runner().currentSpeed * 18;  }  function tackle(obstacle) {    if (isDuckable(obstacle)) {      duck();    } else {      jumpOver(obstacle);    }  }  function isDuckable(obstacle) {    return obstacle.yPos === 50;  }  function duck() {    Runner().onKeyDown(downKeyArgs);    setTimeout(() => {      Runner().onKeyUp(downKeyArgs);    }, 500);  }  function jumpOver(obstacle) {    if (isNextObstacleCloseTo(obstacle))      jumpFast();    else      Runner().onKeyDown(upKeyArgs);  }  function isNextObstacleCloseTo(currentObstacle) {    const nextObstacle = Runner().horizon.obstacles[1];    return nextObstacle && nextObstacle.xPos - currentObstacle.xPos <= Runner().currentSpeed * 42;  }  function jumpFast() {    Runner().onKeyDown(upKeyArgs);    Runner().onKeyUp(upKeyArgs);  }  return {conquerTheGame: conquerTheGame};}let bot = TrexRunnerBot();let botInterval = setInterval(bot.conquerTheGame, 2);

使用方法

小恐龙在chrome://dino(谷歌浏览器才行哦 qwq)

然后按下F12,在Console中粘贴以上代码,然后按Enter即可

q779太弱了才玩到2364分 QAQ

]]>
@@ -8123,7 +8123,7 @@ /2021/12/26/san-jiao-han-shu-chang-yong-gong-shi-zong-jie/ - 三角函数常用公式总结

基本公式

诱导公式

$\sin ^{2} \alpha+\cos ^{2} \alpha=1$

$\tan \alpha=\dfrac{\sin \alpha}{\cos \alpha}$

$\tan\alpha=\dfrac{1}{\cot \alpha}$

$\sin (\alpha+2 \pi)=\sin \alpha$

$\cos (\alpha+2 \pi)=\cos \alpha$

$\tan (\alpha+2 \pi)=\tan \alpha$

$\sin (\pi+\alpha)=-\sin \alpha$

$\cos (\pi+\alpha)=-\cos \alpha$

$\tan (\pi+\alpha)=\tan \alpha$

$\sin (-\alpha)=-\sin \alpha$

$\cos (-\alpha)=\cos \alpha$

$\tan (-\alpha)=-\tan \alpha$

$\sin (\pi-\alpha)=\sin \alpha$

$\cos (\pi-\alpha)=-\cos \alpha$

$\tan (\pi -\alpha)=-\tan \alpha$

$\sin \left(\dfrac{\pi}{2}-\alpha\right)=\cos \alpha$

$\cos \left(\dfrac{\pi}{2}-\alpha\right)=\sin \alpha$

$\sin \left(\dfrac{\pi}{2}+\alpha\right)=\cos \alpha$

$\cos \left(\dfrac{\pi}{2}+\alpha\right)=-\sin \alpha$

$\sin\left(\dfrac{3\pi}{2}+\alpha\right)=-\cos\alpha$

$\cos\left(\dfrac{3\pi}{2}+\alpha\right)=\sin\alpha$

$\tan\left(\dfrac{3\pi}{2}+\alpha\right)=-\cot\alpha$

$\sin\left(\dfrac{3\pi}{2}-\alpha\right)=-\cos\alpha$

$\cos\left(\dfrac{3\pi}{2}-\alpha\right)=-\sin\alpha$

$\tan\left(\dfrac{3\pi}{2}-\alpha\right)=\cot\alpha$

总结:

$\sin\left(\dfrac{k\pi}{2}\pm \alpha\right),k\in\Z$

“奇变偶不变,符号看象限”


和差角公式

$\sin (\alpha\pm\beta)=\sin \alpha \cos \beta \pm \cos \alpha \sin \beta$

$\cos (\alpha \pm \beta)=\cos \alpha \cos \beta \mp \sin \alpha \sin \beta$

$\tan (\alpha\pm\beta)=\dfrac{\tan \alpha \pm \tan \beta}{1 \mp \tan \alpha \tan \beta}$


辅助角公式

$a \sin x+b \cos x=\sqrt{a^{2}+b^{2}} \sin \left(x+\arctan \dfrac{b}{a}\right)$

推广

$\sin x \pm \cos x=\sqrt{2} \sin \left(x \pm \dfrac{\pi}{4}\right)$

$\cos x \pm \sin x=\sqrt{2} \cos \left(x \mp \dfrac{\pi}{4}\right)$


推广公式

基本

$\sin \alpha=\pm \sqrt{1-\cos ^{2} \alpha}$

$\cos \alpha=\pm \sqrt{1-\sin ^{2} \alpha}$

$(\sin \alpha+\cos \alpha)^{2}+(\sin \alpha-\cos \alpha)^{2}=2$

$(\sin \alpha \pm \cos \alpha)^{2}=1 \pm \sin 2 \alpha$


二倍角公式

$\sin 2 \alpha=2 \sin \alpha \cos \alpha$

$\cos 2 \alpha=\cos ^{2} \alpha-\sin ^{2} \alpha=1-2 \sin ^{2} \alpha=2 \cos ^{2} \alpha-1$

$\tan 2 \alpha=\dfrac{2 \tan \alpha}{1-\tan ^{2} \alpha}$


半角公式

$\sin \dfrac{\alpha}{2}=\pm \sqrt{\dfrac{1-\cos \alpha}{2}}$

$\cos \dfrac{\alpha}{2}=\pm \sqrt{\dfrac{1+\cos \alpha}{2}}$

$\tan \dfrac{\alpha}{2}=\pm \sqrt{\dfrac{1-\cos \alpha}{1+\cos \alpha}}$

推广

$\sin ^{2} \alpha=\dfrac{1-\cos 2 \alpha}{2}$

$\cos ^{2} \alpha=\dfrac{1+\cos 2 \alpha}{2}$

$1+\cos \alpha=2 \cos ^{2} \dfrac{\alpha}{2}$

$1-\cos\alpha = 2\sin^2\dfrac{\alpha}{2}$

$\tan\dfrac{\alpha}{2} = \dfrac{1-\cos\alpha}{\sin\alpha}$


积化和差公式

$\sin x \cos y = \dfrac{1}{2}\left[\sin(x+y) + \sin(x-y)\right]$

$\cos x \sin y = \dfrac{1}{2}\left[\sin(x+y) - \sin(x-y)\right]$

$\cos x\cos y = \dfrac{1}{2}\left[\cos (x+y)+\cos(x-y)\right]$

$\sin x \sin y = -\dfrac{1}{2}\left[\cos(x+y) - \cos(x-y)\right]$


和差化积公式

$\sin x + \sin y = 2\sin\dfrac{x+y}{2}\cos\dfrac{x-y}{2}$

$\sin x -\sin y = 2\cos\dfrac{x+y}{2}\sin\dfrac{x-y}{2}$

$\cos x + \cos y = 2\cos\dfrac{x+y}{2}\cos\dfrac{x-y}{2}$

$\cos x-\cos y = -2\sin\dfrac{x+y}{2}\sin\dfrac{x-y}{2}$


其他

$\dfrac{\cos \alpha}{1-\sin \alpha}=\dfrac{1+\sin \alpha}{\cos \alpha}$

$\sin \alpha=\dfrac{\sin 2 \alpha}{2 \cos \alpha}$

$\cos \alpha=\dfrac{\sin 2 \alpha}{2 \sin \alpha}$

$\tan \alpha+\tan \beta=\tan (\alpha+\beta)(1-\tan \alpha \tan \beta)$


常用角的变换

$\alpha=(\alpha+\beta)-\beta$

$\alpha=\beta-(\beta-\alpha)$

$\alpha=\dfrac{1}{2}\left[(\alpha+\beta)+(\alpha-\beta)\right]$

$\alpha=\dfrac{1}{2}\left[(\alpha+\beta)-(\beta-\alpha)\right]$

$\dfrac{\alpha+\beta}{2}=\left(\alpha-\dfrac{\beta}{2}\right)-\left(\dfrac{\alpha}{2}-\beta\right)$

$\alpha-\gamma=(\alpha-\beta)+(\beta-\gamma)$

]]>
+ 三角函数常用公式总结

基本公式

诱导公式

\(\sin ^{2} \alpha+\cos ^{2}\alpha=1\)

\(\tan \alpha=\dfrac{\sin \alpha}{\cos\alpha}\)

\(\tan\alpha=\dfrac{1}{\cot\alpha}\)

\(\sin (\alpha+2 \pi)=\sin\alpha\)

\(\cos (\alpha+2 \pi)=\cos\alpha\)

\(\tan (\alpha+2 \pi)=\tan\alpha\)

\(\sin (\pi+\alpha)=-\sin\alpha\)

\(\cos (\pi+\alpha)=-\cos\alpha\)

\(\tan (\pi+\alpha)=\tan\alpha\)

\(\sin (-\alpha)=-\sin \alpha\)

\(\cos (-\alpha)=\cos \alpha\)

\(\tan (-\alpha)=-\tan \alpha\)

\(\sin (\pi-\alpha)=\sin\alpha\)

\(\cos (\pi-\alpha)=-\cos\alpha\)

\(\tan (\pi -\alpha)=-\tan\alpha\)

\(\sin\left(\dfrac{\pi}{2}-\alpha\right)=\cos \alpha\)

\(\cos\left(\dfrac{\pi}{2}-\alpha\right)=\sin \alpha\)

\(\sin\left(\dfrac{\pi}{2}+\alpha\right)=\cos \alpha\)

\(\cos\left(\dfrac{\pi}{2}+\alpha\right)=-\sin \alpha\)

\(\sin\left(\dfrac{3\pi}{2}+\alpha\right)=-\cos\alpha\)

\(\cos\left(\dfrac{3\pi}{2}+\alpha\right)=\sin\alpha\)

\(\tan\left(\dfrac{3\pi}{2}+\alpha\right)=-\cot\alpha\)

\(\sin\left(\dfrac{3\pi}{2}-\alpha\right)=-\cos\alpha\)

\(\cos\left(\dfrac{3\pi}{2}-\alpha\right)=-\sin\alpha\)

\(\tan\left(\dfrac{3\pi}{2}-\alpha\right)=\cot\alpha\)

总结:

\(\sin\left(\dfrac{k\pi}{2}\pm\alpha\right),k\in\Z\)

“奇变偶不变,符号看象限”


和差角公式

\(\sin (\alpha\pm\beta)=\sin \alpha \cos\beta \pm \cos \alpha \sin \beta\)

\(\cos (\alpha \pm \beta)=\cos \alpha \cos\beta \mp \sin \alpha \sin \beta\)

\(\tan (\alpha\pm\beta)=\dfrac{\tan \alpha\pm \tan \beta}{1 \mp \tan \alpha \tan \beta}\)


辅助角公式

\(a \sin x+b \cos x=\sqrt{a^{2}+b^{2}} \sin\left(x+\arctan \dfrac{b}{a}\right)\)

推广

\(\sin x \pm \cos x=\sqrt{2} \sin \left(x\pm \dfrac{\pi}{4}\right)\)

\(\cos x \pm \sin x=\sqrt{2} \cos \left(x\mp \dfrac{\pi}{4}\right)\)


推广公式

基本

\(\sin \alpha=\pm \sqrt{1-\cos ^{2}\alpha}\)

\(\cos \alpha=\pm \sqrt{1-\sin ^{2}\alpha}\)

\((\sin \alpha+\cos \alpha)^{2}+(\sin\alpha-\cos \alpha)^{2}=2\)

\((\sin \alpha \pm \cos \alpha)^{2}=1 \pm\sin 2 \alpha\)


二倍角公式

\(\sin 2 \alpha=2 \sin \alpha \cos\alpha\)

\(\cos 2 \alpha=\cos ^{2} \alpha-\sin ^{2}\alpha=1-2 \sin ^{2} \alpha=2 \cos ^{2} \alpha-1\)

\(\tan 2 \alpha=\dfrac{2 \tan\alpha}{1-\tan ^{2} \alpha}\)


半角公式

\(\sin \dfrac{\alpha}{2}=\pm\sqrt{\dfrac{1-\cos \alpha}{2}}\)

\(\cos \dfrac{\alpha}{2}=\pm\sqrt{\dfrac{1+\cos \alpha}{2}}\)

\(\tan \dfrac{\alpha}{2}=\pm\sqrt{\dfrac{1-\cos \alpha}{1+\cos \alpha}}\)

推广

\(\sin ^{2} \alpha=\dfrac{1-\cos 2\alpha}{2}\)

\(\cos ^{2} \alpha=\dfrac{1+\cos 2\alpha}{2}\)

\(1+\cos \alpha=2 \cos ^{2}\dfrac{\alpha}{2}\)

\(1-\cos\alpha =2\sin^2\dfrac{\alpha}{2}\)

\(\tan\dfrac{\alpha}{2} =\dfrac{1-\cos\alpha}{\sin\alpha}\)


积化和差公式

\(\sin x \cos y =\dfrac{1}{2}\left[\sin(x+y) + \sin(x-y)\right]\)

\(\cos x \sin y =\dfrac{1}{2}\left[\sin(x+y) - \sin(x-y)\right]\)

\(\cos x\cos y = \dfrac{1}{2}\left[\cos(x+y)+\cos(x-y)\right]\)

\(\sin x \sin y =-\dfrac{1}{2}\left[\cos(x+y) - \cos(x-y)\right]\)


和差化积公式

\(\sin x + \sin y =2\sin\dfrac{x+y}{2}\cos\dfrac{x-y}{2}\)

\(\sin x -\sin y =2\cos\dfrac{x+y}{2}\sin\dfrac{x-y}{2}\)

\(\cos x + \cos y =2\cos\dfrac{x+y}{2}\cos\dfrac{x-y}{2}\)

\(\cos x-\cos y =-2\sin\dfrac{x+y}{2}\sin\dfrac{x-y}{2}\)


其他

\(\dfrac{\cos \alpha}{1-\sin\alpha}=\dfrac{1+\sin \alpha}{\cos \alpha}\)

\(\sin \alpha=\dfrac{\sin 2 \alpha}{2 \cos\alpha}\)

\(\cos \alpha=\dfrac{\sin 2 \alpha}{2 \sin\alpha}\)

\(\tan \alpha+\tan \beta=\tan(\alpha+\beta)(1-\tan \alpha \tan \beta)\)


常用角的变换

\(\alpha=(\alpha+\beta)-\beta\)

\(\alpha=\beta-(\beta-\alpha)\)

\(\alpha=\dfrac{1}{2}\left[(\alpha+\beta)+(\alpha-\beta)\right]\)

\(\alpha=\dfrac{1}{2}\left[(\alpha+\beta)-(\beta-\alpha)\right]\)

\(\dfrac{\alpha+\beta}{2}=\left(\alpha-\dfrac{\beta}{2}\right)-\left(\dfrac{\alpha}{2}-\beta\right)\)

\(\alpha-\gamma=(\alpha-\beta)+(\beta-\gamma)\)

]]>
@@ -8148,7 +8148,7 @@ /2021/12/19/ubuntu-shi-fang-huan-cun-jiao-ben/ - ubuntu 释放缓存脚本

注意:缓存$\ne$内存

内存占用过高解决办法

首先在主目录新建一个free.sh,输入

echo 3 > /proc/sys/vm/drop_caches

保存后打开终端

sudo sucrontab -e

在crontab里输入(/home/qry是我的主目录)

*/5 * * * * /home/qry/free.sh

ctrl+x保存并退出即可

意思是每隔5分钟释放一下缓存,具体参数配置不说了

]]>
+ ubuntu 释放缓存脚本

注意:缓存\(\ne\)内存

内存占用过高解决办法

首先在主目录新建一个free.sh,输入

echo 3 > /proc/sys/vm/drop_caches

保存后打开终端

sudo sucrontab -e

在crontab里输入(/home/qry是我的主目录)

*/5 * * * * /home/qry/free.sh

ctrl+x保存并退出即可

意思是每隔5分钟释放一下缓存,具体参数配置不说了

]]>
@@ -8173,7 +8173,7 @@ /2021/12/04/luo-gu-p4315-yue-xia-mao-jing-shu-ti-jie/ - 洛谷P4315 月下“毛景树” 题解

题目链接:P4315 月下“毛景树”

题意:请维护一个数据结构,支持

  1. 改第 $k$ 条边的边权
  2. 结点 $u$ 到 $v$ 路径上的边权改为 $k$
  3. 结点 $u$ 到 $v$ 路径上的边权增加 $k$
  4. 询问结点 $u$ 到 $v$ 路径上的边权最大值

一看树链剖分,敲了个板子发现给的是边权

那么就只要把边权转化为点权就可以用线段树维护了

显然把父亲到儿子的边权存在儿子上更好维护

也就是把边权存在深度大的结点

在跳出top[x]!=top[y]循环时

id[x],dep[x]<dep[y]其实是原来 $u$ 和 $v$ 的lca,这个lca的边权显然不是 $u$ 和 $v$ 路径上的边

所以维护id[x]+1

然后就是少见的区间赋值操作,别跟我说ODT,怕被卡

主要难在代码实现上,直接贴了(3.79KB)

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}#define MAXN (int)(1e5+5)#define Max(a,b) a=max(a,b) struct Edge{int u,v,w,next;}e[MAXN<<1];int head[MAXN],pos=1;int n;char tmp[233];int a[MAXN],t[MAXN],dep[MAXN],id[MAXN],idx;int fa[MAXN],son[MAXN],sz[MAXN],top[MAXN];int ans[MAXN<<2],tag1[MAXN<<2],tag2[MAXN<<2];void addEdge(int u,int v,int w){e[pos]={u,v,w,head[u]};head[u]=pos++;}void dfs(int u,int f,int d){fa[u]=f;dep[u]=d;sz[u]=1;int mx=-1;for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==f)continue;dfs(v,u,d+1);sz[u]+=sz[v];t[v]=e[i].w;if(sz[v]>mx)mx=sz[v],son[u]=v;}}void dfs(int u,int ftop){id[u]=++idx;a[idx]=t[u];top[u]=ftop;if(!son[u])return;dfs(son[u],ftop);for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==fa[u]||v==son[u])continue;dfs(v,v);}}#define ls(x) (x<<1)#define rs(x) (x<<1|1)void push_up(int at){ans[at]=max(ans[ls(at)],ans[rs(at)]);}void build(int l,int r,int at){if(l==r){ans[at]=a[l];return;}int mid=(l+r)>>1;build(l,mid,ls(at));build(mid+1,r,rs(at));push_up(at);tag1[at]=-1;tag2[at]=0;}void proc(int l,int r,int at){int u=at>>1;if(tag1[u]>=0){tag1[at]=tag1[u];tag2[at]=0;ans[at]=tag1[u];}if(tag2[u]>=0){ans[at]+=tag2[u];tag2[at]+=tag2[u];}}void push_down(int l,int r,int at){int mid=(l+r)>>1;proc(l,mid,ls(at));proc(mid+1,r,rs(at));tag1[at]=-1;tag2[at]=0;}void update_cover(int nl,int nr,int l,int r,int k,int at){if(nl<=l&&r<=nr){ans[at]=k;tag1[at]=k;tag2[at]=0;return;}push_down(l,r,at);int mid=(l+r)>>1;if(nl<=mid)update_cover(nl,nr,l,mid,k,ls(at));if(nr>mid)update_cover(nl,nr,mid+1,r,k,rs(at));push_up(at);}void update_add(int nl,int nr,int l,int r,int k,int at){if(nl<=l&&r<=nr){ans[at]+=k;tag2[at]+=k;return;}push_down(l,r,at);int mid=(l+r)>>1;if(nl<=mid)update_add(nl,nr,l,mid,k,ls(at));if(nr>mid)update_add(nl,nr,mid+1,r,k,rs(at));push_up(at);}int query(int nl,int nr,int l,int r,int at){int res=-1;if(nl<=l&&r<=nr){return ans[at];}push_down(l,r,at);int mid=(l+r)>>1;if(nl<=mid)Max(res,query(nl,nr,l,mid,ls(at)));if(nr>mid)Max(res,query(nl,nr,mid+1,r,rs(at)));return res;}void addRange(int x,int y,int k){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);update_add(id[top[x]],id[x],1,n,k,1);x=fa[top[x]];}if(id[x]>id[y])swap(x,y);update_add(id[x]+1,id[y],1,n,k,1);}void coverRange(int x,int y,int k){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);update_cover(id[top[x]],id[x],1,n,k,1);x=fa[top[x]];}if(id[x]>id[y])swap(x,y);update_cover(id[x]+1,id[y],1,n,k,1);}int qRange(int x,int y){int res=-1;while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);Max(res,query(id[top[x]],id[x],1,n,1));x=fa[top[x]];}if(id[x]>id[y])swap(x,y);Max(res,query(id[x]+1,id[y],1,n,1));return res;}signed main(){read(n);for(int i=1,u,v,w; i<n; i++){read(u);read(v);read(w);addEdge(u,v,w);addEdge(v,u,w);}dfs(1,0,1);dfs(1,1);build(1,n,1);int l,r,x,k;while(~scanf("%s%lld%lld",tmp,&l,&r)){if(tmp[0]=='S')break;if(tmp[0]=='M')write(qRange(l,r)),pc('\n');if(tmp[0]=='A'){read(k);addRange(l,r,k);}if(tmp[1]=='h'){x=l,k=r;x=dep[e[2*x-1].v]<dep[e[2*x].v]?e[x*2].v:e[x*2-1].v;update_cover(id[x],id[x],1,n,k,1);}if(tmp[1]=='o'){read(k);coverRange(l,r,k);}}return 0;}
]]>
+ 洛谷P4315 月下“毛景树” 题解

题目链接:P4315月下“毛景树”

题意:请维护一个数据结构,支持

  1. 改第 \(k\) 条边的边权
  2. 结点 \(u\)\(v\) 路径上的边权改为 \(k\)
  3. 结点 \(u\)\(v\) 路径上的边权增加 \(k\)
  4. 询问结点 \(u\)\(v\) 路径上的边权最大值

一看树链剖分,敲了个板子发现给的是边权

那么就只要把边权转化为点权就可以用线段树维护了

显然把父亲到儿子的边权存在儿子上更好维护

也就是把边权存在深度大的结点

在跳出top[x]!=top[y]循环时

id[x],dep[x]<dep[y]其实是原来 \(u\) 和 \(v\)的lca,这个lca的边权显然不是 \(u\) 和 \(v\) 路径上的边

所以维护id[x]+1

然后就是少见的区间赋值操作,别跟我说ODT,怕被卡

主要难在代码实现上,直接贴了(3.79KB)

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}#define MAXN (int)(1e5+5)#define Max(a,b) a=max(a,b) struct Edge{int u,v,w,next;}e[MAXN<<1];int head[MAXN],pos=1;int n;char tmp[233];int a[MAXN],t[MAXN],dep[MAXN],id[MAXN],idx;int fa[MAXN],son[MAXN],sz[MAXN],top[MAXN];int ans[MAXN<<2],tag1[MAXN<<2],tag2[MAXN<<2];void addEdge(int u,int v,int w){e[pos]={u,v,w,head[u]};head[u]=pos++;}void dfs(int u,int f,int d){fa[u]=f;dep[u]=d;sz[u]=1;int mx=-1;for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==f)continue;dfs(v,u,d+1);sz[u]+=sz[v];t[v]=e[i].w;if(sz[v]>mx)mx=sz[v],son[u]=v;}}void dfs(int u,int ftop){id[u]=++idx;a[idx]=t[u];top[u]=ftop;if(!son[u])return;dfs(son[u],ftop);for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==fa[u]||v==son[u])continue;dfs(v,v);}}#define ls(x) (x<<1)#define rs(x) (x<<1|1)void push_up(int at){ans[at]=max(ans[ls(at)],ans[rs(at)]);}void build(int l,int r,int at){if(l==r){ans[at]=a[l];return;}int mid=(l+r)>>1;build(l,mid,ls(at));build(mid+1,r,rs(at));push_up(at);tag1[at]=-1;tag2[at]=0;}void proc(int l,int r,int at){int u=at>>1;if(tag1[u]>=0){tag1[at]=tag1[u];tag2[at]=0;ans[at]=tag1[u];}if(tag2[u]>=0){ans[at]+=tag2[u];tag2[at]+=tag2[u];}}void push_down(int l,int r,int at){int mid=(l+r)>>1;proc(l,mid,ls(at));proc(mid+1,r,rs(at));tag1[at]=-1;tag2[at]=0;}void update_cover(int nl,int nr,int l,int r,int k,int at){if(nl<=l&&r<=nr){ans[at]=k;tag1[at]=k;tag2[at]=0;return;}push_down(l,r,at);int mid=(l+r)>>1;if(nl<=mid)update_cover(nl,nr,l,mid,k,ls(at));if(nr>mid)update_cover(nl,nr,mid+1,r,k,rs(at));push_up(at);}void update_add(int nl,int nr,int l,int r,int k,int at){if(nl<=l&&r<=nr){ans[at]+=k;tag2[at]+=k;return;}push_down(l,r,at);int mid=(l+r)>>1;if(nl<=mid)update_add(nl,nr,l,mid,k,ls(at));if(nr>mid)update_add(nl,nr,mid+1,r,k,rs(at));push_up(at);}int query(int nl,int nr,int l,int r,int at){int res=-1;if(nl<=l&&r<=nr){return ans[at];}push_down(l,r,at);int mid=(l+r)>>1;if(nl<=mid)Max(res,query(nl,nr,l,mid,ls(at)));if(nr>mid)Max(res,query(nl,nr,mid+1,r,rs(at)));return res;}void addRange(int x,int y,int k){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);update_add(id[top[x]],id[x],1,n,k,1);x=fa[top[x]];}if(id[x]>id[y])swap(x,y);update_add(id[x]+1,id[y],1,n,k,1);}void coverRange(int x,int y,int k){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);update_cover(id[top[x]],id[x],1,n,k,1);x=fa[top[x]];}if(id[x]>id[y])swap(x,y);update_cover(id[x]+1,id[y],1,n,k,1);}int qRange(int x,int y){int res=-1;while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);Max(res,query(id[top[x]],id[x],1,n,1));x=fa[top[x]];}if(id[x]>id[y])swap(x,y);Max(res,query(id[x]+1,id[y],1,n,1));return res;}signed main(){read(n);for(int i=1,u,v,w; i<n; i++){read(u);read(v);read(w);addEdge(u,v,w);addEdge(v,u,w);}dfs(1,0,1);dfs(1,1);build(1,n,1);int l,r,x,k;while(~scanf("%s%lld%lld",tmp,&l,&r)){if(tmp[0]=='S')break;if(tmp[0]=='M')write(qRange(l,r)),pc('\n');if(tmp[0]=='A'){read(k);addRange(l,r,k);}if(tmp[1]=='h'){x=l,k=r;x=dep[e[2*x-1].v]<dep[e[2*x].v]?e[x*2].v:e[x*2-1].v;update_cover(id[x],id[x],1,n,k,1);}if(tmp[1]=='o'){read(k);coverRange(l,r,k);}}return 0;}
]]>
@@ -8198,7 +8198,7 @@ /2021/12/03/qian-tan-la-ge-lang-ri-cha-zhi-fa/ - 浅谈拉格朗日插值法

模板题链接P4781 【模板】拉格朗日插值

题意:给定 $n$ 个点 $P_i(x_i,y_i)$ ,将过该 $n$ 个点的最多 $n-1$ 次多项式记为 $f(x)$

给出 $k$ ,求 $f(k) \mod 998244353$

$f(k) = \sum\limits_{i=1}^{n}y_i\prod_{j\ne i}{ \dfrac{k-x_j}{x_i-x_j} }$

证明:过 $P_i$ 作垂线交 $x$ 轴于 $H_i(x_i,0)$

构造 $g_i(x),i=1,2,\cdots ,n$ ,使得 $g_i(x)$ 的图像过 $\begin{cases}P_i(x_i,y_i) \\ H_j(x_j,0),j\ne i\end{cases}$

可以发现,$g_i(x) = a \prod_{j\ne i}{(x-x_j)}$ ,其中 $a$ 是未知数

因为 $g_i(x)$ 过 $P_i(x_i,y_i)$ ,代入并整理可得

$a = \dfrac{y_i}{ \prod_{j\ne i}{(x_i-x_j)} }$

把 $a$ 代入 $g_i(x)$ 得 $g_i(x) = y_i\times\dfrac{ \prod_{j\ne i}{(x-x_j)} }{ \prod_{j\ne i}{(x_i-x_j)} } = y_i\prod_{j\ne i}\dfrac{x-x_j}{x_i-x_j}$

$\therefore f(x) = \sum\limits_{i=1}^{n}g_i(x) = \sum\limits_{i=1}^{n}y_i\prod_{j\ne i}\dfrac{x-x_j}{x_i-x_j}$

把 $k$ 代入即可得

$f(k) = \sum\limits_{i=1}^{n}y_i\prod_{j\ne i}{ \dfrac{k-x_j}{x_i-x_j}}$

求解时可以先分别求出分子和分母,最后分子乘上分母的逆元即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define mod (int)(998244353)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}#define MAXN (int)(2e3+5)int qpow(int a,int b){int ans=1,base=a;while(b){if(b&1)ans=ans*base%mod;base=base*base%mod;b>>=1;}return ans;}int inv(int x){return qpow(x,mod-2);} // 费马小定理求逆元int n,k,a,b,ans;int x[MAXN],y[MAXN];signed main(){read(n);read(k);for(int i=1; i<=n; i++)read(x[i]),read(y[i]);for(int i=1; i<=n; i++){a=y[i]%mod,b=1;for(int j=1; j<=n; j++) if(i!=j){b=(x[i]-x[j]+mod)%mod*b%mod;a=(k-x[j]+mod)%mod*a%mod;}ans=(ans+a*inv(b)%mod)%mod;}write(ans);pc('\n');return 0;}
]]>
+ 浅谈拉格朗日插值法

模板题链接P4781【模板】拉格朗日插值

题意:给定 \(n\)个点 \(P_i(x_i,y_i)\) ,将过该 \(n\) 个点的最多 \(n-1\) 次多项式记为 \(f(x)\)

给出 \(k\) ,求 \(f(k) \mod 998244353\)

\(f(k) =\sum\limits_{i=1}^{n}y_i\prod_{j\ne i}{ \dfrac{k-x_j}{x_i-x_j}}\)

证明:过 \(P_i\) 作垂线交 \(x\) 轴于 \(H_i(x_i,0)\)

构造 \(g_i(x),i=1,2,\cdots ,n\),使得 \(g_i(x)\) 的图像过 \(\begin{cases}P_i(x_i,y_i) \\ H_j(x_j,0),j\nei\end{cases}\)

可以发现,\(g_i(x) = a \prod_{j\nei}{(x-x_j)}\) ,其中 \(a\)是未知数

因为 \(g_i(x)\)\(P_i(x_i,y_i)\) ,代入并整理可得

\(a = \dfrac{y_i}{ \prod_{j\nei}{(x_i-x_j)} }\)

\(a\) 代入 \(g_i(x)\) 得 \(g_i(x) = y_i\times\dfrac{ \prod_{j\ne i}{(x-x_j)}}{ \prod_{j\ne i}{(x_i-x_j)} } = y_i\prod_{j\nei}\dfrac{x-x_j}{x_i-x_j}\)

\(\therefore f(x) =\sum\limits_{i=1}^{n}g_i(x) = \sum\limits_{i=1}^{n}y_i\prod_{j\nei}\dfrac{x-x_j}{x_i-x_j}\)

\(k\) 代入即可得

\(f(k) =\sum\limits_{i=1}^{n}y_i\prod_{j\ne i}{\dfrac{k-x_j}{x_i-x_j}}\)

求解时可以先分别求出分子和分母,最后分子乘上分母的逆元即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)#define mod (int)(998244353)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}#define MAXN (int)(2e3+5)int qpow(int a,int b){int ans=1,base=a;while(b){if(b&1)ans=ans*base%mod;base=base*base%mod;b>>=1;}return ans;}int inv(int x){return qpow(x,mod-2);} // 费马小定理求逆元int n,k,a,b,ans;int x[MAXN],y[MAXN];signed main(){read(n);read(k);for(int i=1; i<=n; i++)read(x[i]),read(y[i]);for(int i=1; i<=n; i++){a=y[i]%mod,b=1;for(int j=1; j<=n; j++) if(i!=j){b=(x[i]-x[j]+mod)%mod*b%mod;a=(k-x[j]+mod)%mod*a%mod;}ans=(ans+a*inv(b)%mod)%mod;}write(ans);pc('\n');return 0;}
]]>
@@ -8225,7 +8225,7 @@ /2021/11/24/luo-gu-p2633-count-on-a-tree-ti-jie/ - 洛谷P2633 Count on a tree 题解

题目链接:P2633 Count on a tree

题意:给定一棵树和 $u,v,k$ ,求 $u,v$ 结点间的第 $k$ 小点权

本来以为是个树链剖分+主席树,结果发现并不可做

观察到主席树本质上利用了前缀和来计算线性的区间和

现在就是把求和搬到了树上

可以立即想到树上差分

不过这道题不是树上差分,可能应该叫树上前缀和更恰当一些

对于一条根结点(无根树就假定是 $1$ 好了)到叶子结点的简单路径,就是一个线性区间上的主席树

也就是在树上建立了类似前缀和体系的主席树群,每一个主席树维护了一条根结点到叶子结点的路径上的结点的信息

感性理解一下,有点“前缀主席树”的感觉

那么 $u$ 到 $v$ 的信息就是

sum[u]+sum[v]-sum[lca(u,v)]-sum[fa[lca(u,v)][0]]

是不是很像树上差分了

其他的比如离散化什么的都是基本操作了,这里不再赘述了 qwq

时间复杂度 $O(n\log n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}#define MAXN (int)(1e5+5)struct node{int u,v,next;}e[MAXN<<1];int n,m,Q,last,a[MAXN],b[MAXN],tot;int head[MAXN],pos=1,fa[MAXN][22],lg[MAXN];int L[MAXN<<5],R[MAXN<<5],sum[MAXN<<5],T[MAXN<<5],dep[MAXN];void addEdge(int u,int v){e[pos]={u,v,head[u]};head[u]=pos++;}int build(int l,int r){int at=++tot;if(l<r){int mid=(l+r)>>1;L[at]=build(l,mid);R[at]=build(mid+1,r);}return at;}int insert(int pre,int l,int r,int k){int at=++tot;L[at]=L[pre];R[at]=R[pre];sum[at]=sum[pre]+1;if(l<r){int mid=(l+r)>>1;if(k<=mid)L[at]=insert(L[pre],l,mid,k);else R[at]=insert(R[pre],mid+1,r,k);}return at;}void dfs(int u,int f){fa[u][0]=f;dep[u]=dep[f]+1;T[u]=insert(T[f],1,m,a[u]);for(int i=1; i<=lg[dep[u]]; i++)fa[u][i]=fa[fa[u][i-1]][i-1];for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==f)continue;dfs(v,u);}}int lca(int x,int y){if(dep[x]<dep[y])swap(x,y);while(dep[x]>dep[y])x=fa[x][lg[dep[x]-dep[y]]-1];if(x==y)return x;for(int i=lg[dep[x]]; i>=0; i--){if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];}return fa[x][0];} // 倍增求LCAint query(int x,int y,int z,int t,int l,int r,int k){if(l==r)return l;int mid=(l+r)>>1;int res=sum[L[x]]+sum[L[y]]-sum[L[t]]-sum[L[z]];if(k<=res)return query(L[x],L[y],L[z],L[t],l,mid,k);else return query(R[x],R[y],R[z],R[t],mid+1,r,k-res);}void init(){sort(b+1,b+1+n);m=unique(b+1,b+1+n)-b-1;for(int i=1; i<=n; i++)a[i]=lower_bound(b+1,b+1+m,a[i])-b;for(int i=1; i<=n; i++)lg[i]=lg[i-1]+(1<<lg[i-1]==i);build(1,m);dfs(1,0);}signed main(){read(n);read(Q);for(int i=1; i<=n; i++)read(a[i]),b[i]=a[i];for(int i=1,u,v; i<n; i++){read(u);read(v);addEdge(u,v);addEdge(v,u);}init();while(Q--){int x,y,k;read(x);read(y);read(k);x^=last; // 强制在线int t=lca(x,y);write(last=b[query(T[x],T[y],T[fa[t][0]],T[t],1,m,k)]);pc('\n');}return 0;}
]]>
+ 洛谷P2633 Count on a tree题解

题目链接:P2633Count on a tree

题意:给定一棵树和 \(u,v,k\) ,求 \(u,v\) 结点间的第 \(k\) 小点权

本来以为是个树链剖分+主席树,结果发现并不可做

观察到主席树本质上利用了前缀和来计算线性的区间和

现在就是把求和搬到了树上

可以立即想到树上差分

不过这道题不是树上差分,可能应该叫树上前缀和更恰当一些

对于一条根结点(无根树就假定是 \(1\)好了)到叶子结点的简单路径,就是一个线性区间上的主席树

也就是在树上建立了类似前缀和体系的主席树群,每一个主席树维护了一条根结点到叶子结点的路径上的结点的信息

感性理解一下,有点“前缀主席树”的感觉

那么 \(u\)\(v\) 的信息就是

sum[u]+sum[v]-sum[lca(u,v)]-sum[fa[lca(u,v)][0]]

是不是很像树上差分了

其他的比如离散化什么的都是基本操作了,这里不再赘述了 qwq

时间复杂度 \(O(n\log n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define gc() getchar()#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}#define MAXN (int)(1e5+5)struct node{int u,v,next;}e[MAXN<<1];int n,m,Q,last,a[MAXN],b[MAXN],tot;int head[MAXN],pos=1,fa[MAXN][22],lg[MAXN];int L[MAXN<<5],R[MAXN<<5],sum[MAXN<<5],T[MAXN<<5],dep[MAXN];void addEdge(int u,int v){e[pos]={u,v,head[u]};head[u]=pos++;}int build(int l,int r){int at=++tot;if(l<r){int mid=(l+r)>>1;L[at]=build(l,mid);R[at]=build(mid+1,r);}return at;}int insert(int pre,int l,int r,int k){int at=++tot;L[at]=L[pre];R[at]=R[pre];sum[at]=sum[pre]+1;if(l<r){int mid=(l+r)>>1;if(k<=mid)L[at]=insert(L[pre],l,mid,k);else R[at]=insert(R[pre],mid+1,r,k);}return at;}void dfs(int u,int f){fa[u][0]=f;dep[u]=dep[f]+1;T[u]=insert(T[f],1,m,a[u]);for(int i=1; i<=lg[dep[u]]; i++)fa[u][i]=fa[fa[u][i-1]][i-1];for(int i=head[u]; i; i=e[i].next){int v=e[i].v;if(v==f)continue;dfs(v,u);}}int lca(int x,int y){if(dep[x]<dep[y])swap(x,y);while(dep[x]>dep[y])x=fa[x][lg[dep[x]-dep[y]]-1];if(x==y)return x;for(int i=lg[dep[x]]; i>=0; i--){if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];}return fa[x][0];} // 倍增求LCAint query(int x,int y,int z,int t,int l,int r,int k){if(l==r)return l;int mid=(l+r)>>1;int res=sum[L[x]]+sum[L[y]]-sum[L[t]]-sum[L[z]];if(k<=res)return query(L[x],L[y],L[z],L[t],l,mid,k);else return query(R[x],R[y],R[z],R[t],mid+1,r,k-res);}void init(){sort(b+1,b+1+n);m=unique(b+1,b+1+n)-b-1;for(int i=1; i<=n; i++)a[i]=lower_bound(b+1,b+1+m,a[i])-b;for(int i=1; i<=n; i++)lg[i]=lg[i-1]+(1<<lg[i-1]==i);build(1,m);dfs(1,0);}signed main(){read(n);read(Q);for(int i=1; i<=n; i++)read(a[i]),b[i]=a[i];for(int i=1,u,v; i<n; i++){read(u);read(v);addEdge(u,v);addEdge(v,u);}init();while(Q--){int x,y,k;read(x);read(y);read(k);x^=last; // 强制在线int t=lca(x,y);write(last=b[query(T[x],T[y],T[fa[t][0]],T[t],1,m,k)]);pc('\n');}return 0;}
]]>
@@ -8250,7 +8250,7 @@ /2021/11/21/ubuntu-he-windows-chrome-liu-lan-qi-guan-bi-what-s-new-ye-mian/ - ubuntu和windows chrome浏览器 关闭what‘s new页面

在搜索栏里输入chrome://flags

修改操作如下

]]>
+ ubuntu和windowschrome浏览器 关闭what‘s new页面

在搜索栏里输入chrome://flags

修改操作如下

]]>
@@ -8277,7 +8277,7 @@ /2021/11/20/windows-matlab-r2020b-an-zhuang-jiao-cheng/ - windows Matlab R2020b 安装教程

前言

本文其实是我自己在安装过程中截图的,现在搞定了整理出来给大家参考一下

使用的是matlab R2020b

如果本文有什么错误或对本文有什么问题,欢迎留言 qwq

要是图挂了可以直接私信我,只要我还在


一、下载文件

下载好文件,我就放张图

要尽量支持正版哦,这是正版软件下载链接

https://ww2.mathworks.cn/products/matlab.html

在这里插入图片描述


二、解压文件

选中上面的那些.rar文件,然后解压(我建议用360压缩,挺好用的,不是打广告qwq

在这里插入图片描述

然后稍微整理一下,留下有用的,如下

注:第二个文件夹就是解压后的东西,我不放心就先留着了

在这里插入图片描述


三、开始安装

打开.iso的文件,然后双击setup文件

在这里插入图片描述
在这里插入图片描述


四、安装细节

在这里插入图片描述

安装密钥是

09806-07443-53955-64350-21751-41297

在这里插入图片描述

许可证就选择刚刚下载的文件夹里...\crack\license_standalone.lic

在这里插入图片描述

强烈建议安装在其他的盘,比如D盘

在这里插入图片描述

跳过几个不用设置的步骤….

开始安装,安装时间会比较长

在这里插入图片描述


五、激活

安装好的不可以直接用,要先激活

就两个步骤,很简单

首先把crack文件夹下的license.lic复制到安装目录下的license文件夹中

然后把那个dll文件复制下,直接看图吧…

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


六、运行

它默认不会把快捷方式放桌面上,手动弄一下就好了

位置在R2020b\bin

运行后可以看到下图所示

在这里插入图片描述


总结

安装安装安装,没了qwq



参考文献

[1] 《最新MATLAB R2020b超详细安装教程(附完整安装文件)》

]]>
+ windows Matlab R2020b安装教程

前言

本文其实是我自己在安装过程中截图的,现在搞定了整理出来给大家参考一下

使用的是matlab R2020b

如果本文有什么错误或对本文有什么问题,欢迎留言 qwq

要是图挂了可以直接私信我,只要我还在


一、下载文件

下载好文件,我就放张图

要尽量支持正版哦,这是正版软件下载链接

https://ww2.mathworks.cn/products/matlab.html


二、解压文件

选中上面的那些.rar文件,然后解压(我建议用360压缩,挺好用的,不是打广告qwq

然后稍微整理一下,留下有用的,如下

注:第二个文件夹就是解压后的东西,我不放心就先留着了


三、开始安装

打开.iso的文件,然后双击setup文件


四、安装细节

安装密钥是

09806-07443-53955-64350-21751-41297

许可证就选择刚刚下载的文件夹里...\crack\license_standalone.lic

强烈建议安装在其他的盘,比如D盘

跳过几个不用设置的步骤....

开始安装,安装时间会比较长


五、激活

安装好的不可以直接用,要先激活

就两个步骤,很简单

首先把crack文件夹下的license.lic复制到安装目录下的license文件夹中

然后把那个dll文件复制下,直接看图吧...


六、运行

它默认不会把快捷方式放桌面上,手动弄一下就好了

位置在R2020b\bin

运行后可以看到下图所示


总结

安装安装安装,没了qwq


参考文献

[1] 《最新MATLABR2020b超详细安装教程(附完整安装文件)》

]]>
@@ -8302,7 +8302,7 @@ /2021/11/19/windows-she-zhi-xu-yao-guan-li-yuan-quan-xian-shi-shu-ru-mi-ma-cai-neng-cao-zuo/ - windows 设置需要管理员权限时输入密码才能操作

步骤如下

首先 win+r,输入regedit打开注册表编辑器

然后找到

计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorAdmin

将值修改为 3 (十六进制)

不明白的话可以看看下面的图

然后尝试打开一下任务管理器

就发现需要PIN啦!

]]>
+ windows设置需要管理员权限时输入密码才能操作

步骤如下

首先 win+r,输入regedit打开注册表编辑器

然后找到

计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ConsentPromptBehaviorAdmin

将值修改为 3 (十六进制)

不明白的话可以看看下面的图

然后尝试打开一下任务管理器

就发现需要PIN啦!

]]>
@@ -8327,7 +8327,7 @@ /2021/11/13/ubuntu-gimp-zi-ti-xian-shi-yi-chang-jie-jue-fang-fa/ - ubuntu GIMP 字体显示异常 解决方法

今天用GIMP的时候发现字体出了问题

报错如下

Fontconfig warning: FcPattern object width does not accept value [75 100)/snap/gimp/380/usr/bin/gimp: Gimp-Text-严重: gimp_font_factory_load_names: assertion 'fontset' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_scale_simple: assertion 'dest_width > 0' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_width: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_height: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_width: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_height: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_pixels_with_length: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_rowstride: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_n_channels: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GLib-ERROR **: 12:21:22.032: ../../../../glib/gmem.c:333: overflow allocating 18446744073709551615*18446744073709551615 bytes(script-fu:25464): LibGimpBase-WARNING **: 12:21:22.168: script-fu: gimp_wire_read(): error追踪与中断点陷阱 (核心已转储)

主要的问题是Fontconfig warning: FcPattern object width does not accept value [75 100)

找到了一个解决方法,在终端输入以下内容

sudo rm -rf ~/.cache/fontconfig

最近好像没啥东西写就水一点文章吧 qwq

]]>
+ ubuntu GIMP 字体显示异常解决方法

今天用GIMP的时候发现字体出了问题

报错如下

Fontconfig warning: FcPattern object width does not accept value [75 100)/snap/gimp/380/usr/bin/gimp: Gimp-Text-严重: gimp_font_factory_load_names: assertion 'fontset' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_scale_simple: assertion 'dest_width > 0' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_width: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_height: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_width: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_height: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_pixels_with_length: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_rowstride: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GdkPixbuf-CRITICAL **: 12:21:22.032: gdk_pixbuf_get_n_channels: assertion 'GDK_IS_PIXBUF (pixbuf)' failed(gimp:25382): GLib-ERROR **: 12:21:22.032: ../../../../glib/gmem.c:333: overflow allocating 18446744073709551615*18446744073709551615 bytes(script-fu:25464): LibGimpBase-WARNING **: 12:21:22.168: script-fu: gimp_wire_read(): error追踪与中断点陷阱 (核心已转储)

主要的问题是Fontconfig warning: FcPattern object width does not accept value [75 100)

找到了一个解决方法,在终端输入以下内容

sudo rm -rf ~/.cache/fontconfig

最近好像没啥东西写就水一点文章吧 qwq

]]>
@@ -8352,7 +8352,7 @@ /2021/11/12/ubuntu-wps-zi-ti-que-shi-jie-jue-fang-fa/ - ubuntu WPS字体缺失 解决方法

前言

请保证您还有一台windows


一、在windows复制字体

首先在windows下载好WPS,然后找到字体,复制

在这里插入图片描述

二、复制到ubuntu

我的ubuntu上装了WPS2019,目录在/usr/share/fonts/wps-office

因此只要在终端输入

sudo cp ./* /usr/share/fonts/wps-office

就好了

三、重启WPS

关掉再打开


总结

从windows上复制到ubuntu

]]>
+ ubuntu WPS字体缺失 解决方法

前言

请保证您还有一台windows


一、在windows复制字体

首先在windows下载好WPS,然后找到字体,复制

二、复制到ubuntu

我的ubuntu上装了WPS2019,目录在/usr/share/fonts/wps-office

因此只要在终端输入

sudo cp ./* /usr/share/fonts/wps-office

就好了

三、重启WPS

关掉再打开


总结

从windows上复制到ubuntu

]]>
@@ -8379,7 +8379,7 @@ /2021/10/10/luo-gu-p1486-noi2004-yu-men-de-chu-na-yuan-ti-jie/ - 洛谷P1486 [NOI2004] 郁闷的出纳员 题解

题目链接:P1486 [NOI2004] 郁闷的出纳员

题意:维护一个数据结构,支持

  1. 插入一个大小为 $k$ 的值,小于下界时不插入
  2. 所有元素加上 $k$
  3. 所有元素减去 $k$ ,小于下界的删除
  4. 查询所有元素中的 $k$ 大值

最后输出删除的元素个数(两元素相等算两个)

可以用平衡树来解决本题

听说正解是splay,不过我来讲一种替罪羊树的写法

我们可以维护一个 $d$ ,表示每次加减的变化

这样每个元素就可以用 $x+d$ 的形式表示了

在平衡树里放原来的数减去插入时的 $d$ ,即 $k-d$

这样输出的时候就是正常的数了

那小于下界的怎么搞呢?

根据替罪羊树的性质,我们可以直接把整课树重构

Flatten操作时判断当前元素 $x$ 加上 $d$ 是否超过下界,且存在,否则就直接不管它了

$k$ 大值直接查 $num1-k+1$ 小值就好了

这个 $num1$ 表示每次操作后的剩余结点数(两元素相等算两个)

因为它最后还有个询问删除的,再记录一下总插入结点数(两元素相等算两个) $num2$ ,答案就是 $num2-num1$

这样整个算法的时间复杂度为 $O(n\log n)$

本题在luogu上的数据是windows下造的,所以还要注意换行符是\r\n,不是linux下的\n

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(3e5+5)#define gc() getchar()#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}namespace BT{#define ls(at) a[at].l#define rs(at) a[at].rint n,m;int d,num1,num2;struct node{int l,r,num,cnt,s,sz,sd;}a[MAXN];double alpha=0.75;int tot,rt;int tmp[MAXN];int New(int x){a[++tot]={0,0,x,1,1,1,1};return tot;}void push_up(int at){a[at].s=a[ls(at)].s+a[rs(at)].s+1;a[at].sz=a[ls(at)].sz+a[rs(at)].sz+a[at].cnt;a[at].sd=a[ls(at)].sd+a[rs(at)].sd+(a[at].cnt!=0);}bool CanR(int at){double x=a[at].s*alpha;return a[at].cnt&&(x<=(double)max(a[ls(at)].s,a[rs(at)].s)||(double)a[at].sd<=x);}void CanR_flatten(int &idx,int at){if(!at)return;CanR_flatten(idx,ls(at));if(a[at].cnt){if(a[at].num+d>=m)tmp[++idx]=at; //在这里就筛掉不满足下界的else num1-=a[at].cnt; // 注意不是减1,因为可能有相同的元素}CanR_flatten(idx,rs(at));}int CanR_build(int l,int r){if(l>=r)return 0;int mid=(l+r)>>1;int &at=tmp[mid];ls(at)=CanR_build(l,mid);rs(at)=CanR_build(mid+1,r);push_up(at);return at;}void rebuild(int &at){int idx=0;CanR_flatten(idx,at);at=CanR_build(1,idx+1);}void insert(int x,int &at){if(!at){at=New(x);return;}if(a[at].num==x)++a[at].cnt;else if(x<a[at].num)insert(x,ls(at));else insert(x,rs(at));push_up(at);if(CanR(at))rebuild(at);}void remove(int x,int &at){if(!at)return;if(x==a[at].num){if(a[at].cnt)--a[at].cnt;}else if(x<a[at].num)remove(x,ls(at));else remove(x,rs(at));push_up(at);if(CanR(at))rebuild(at);}int uprbd(int x,int at){if(!at)return 1;if(x==a[at].num&&a[at].cnt)return a[ls(at)].sz+a[at].cnt+1;else if(x<a[at].num)return uprbd(x,ls(at));else return uprbd(x,rs(at))+a[ls(at)].sz+a[at].cnt;}int uprbd_gt(int x,int at){if(!at)return 0;if(x==a[at].num&&a[at].cnt)return a[ls(at)].sz;else if(x<a[at].num)return uprbd_gt(x,ls(at));else return uprbd_gt(x,rs(at))+a[ls(at)].sz+a[at].cnt;}int getval(int x,int at){if(!at)return INF;if(x<=a[ls(at)].sz)return getval(x,ls(at));if(x<=a[ls(at)].sz+a[at].cnt)return a[at].num;else return getval(x-a[ls(at)].sz-a[at].cnt,rs(at));}int getpre(int x,int at){return getval(uprbd_gt(x,at),at);}int getnext(int x,int at){return getval(uprbd(x,at),at);}int getrank(int x,int at){return uprbd_gt(x,at)+1;}}signed main(){using namespace BT; // 替罪羊树 qwqread(n);read(m);for(int i=1; i<=n; i++){char op;int x;char t;//gc(); //win下要用op=gc();read(x);if(op=='I'&&x>=m) // 不满下界的直接不管了{insert(x-d,rt);++num1;++num2;}if(op=='A')d+=x;if(op=='F'){if(num1<x)puts("-1"); // 不满k个数else {write(getval(num1-x+1,rt)+d);pc('\n');}}if(op=='S'){d-=x;rebuild(rt); // 直接重构}}write(num2-num1);pc('\n');return 0;}
]]>
+ 洛谷P1486 [NOI2004]郁闷的出纳员 题解

题目链接:P1486[NOI2004] 郁闷的出纳员

题意:维护一个数据结构,支持

  1. 插入一个大小为 \(k\)的值,小于下界时不插入
  2. 所有元素加上 \(k\)
  3. 所有元素减去 \(k\),小于下界的删除
  4. 查询所有元素中的 \(k\) 大值

最后输出删除的元素个数(两元素相等算两个)

可以用平衡树来解决本题

听说正解是splay,不过我来讲一种替罪羊树的写法

我们可以维护一个 \(d\),表示每次加减的变化

这样每个元素就可以用 \(x+d\)的形式表示了

在平衡树里放原来的数减去插入时的 \(d\) ,即 \(k-d\)

这样输出的时候就是正常的数了

那小于下界的怎么搞呢?

根据替罪羊树的性质,我们可以直接把整课树重构

Flatten操作时判断当前元素 \(x\) 加上 \(d\)是否超过下界,且存在,否则就直接不管它了

\(k\) 大值直接查 \(num1-k+1\) 小值就好了

这个 \(num1\)表示每次操作后的剩余结点数(两元素相等算两个)

因为它最后还有个询问删除的,再记录一下总插入结点数(两元素相等算两个)\(num2\) ,答案就是 \(num2-num1\)

这样整个算法的时间复杂度为 \(O(n\logn)\)

本题在luogu上的数据是windows下造的,所以还要注意换行符是\r\n,不是linux下的\n

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(3e5+5)#define gc() getchar()#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=gc();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}k=x*f;}template<typename T>inline void write(T k){if(k<0){k=-k;pc('-');}if(k>9)write(k/10);pc(k%10+'0');}namespace BT{#define ls(at) a[at].l#define rs(at) a[at].rint n,m;int d,num1,num2;struct node{int l,r,num,cnt,s,sz,sd;}a[MAXN];double alpha=0.75;int tot,rt;int tmp[MAXN];int New(int x){a[++tot]={0,0,x,1,1,1,1};return tot;}void push_up(int at){a[at].s=a[ls(at)].s+a[rs(at)].s+1;a[at].sz=a[ls(at)].sz+a[rs(at)].sz+a[at].cnt;a[at].sd=a[ls(at)].sd+a[rs(at)].sd+(a[at].cnt!=0);}bool CanR(int at){double x=a[at].s*alpha;return a[at].cnt&&(x<=(double)max(a[ls(at)].s,a[rs(at)].s)||(double)a[at].sd<=x);}void CanR_flatten(int &idx,int at){if(!at)return;CanR_flatten(idx,ls(at));if(a[at].cnt){if(a[at].num+d>=m)tmp[++idx]=at; //在这里就筛掉不满足下界的else num1-=a[at].cnt; // 注意不是减1,因为可能有相同的元素}CanR_flatten(idx,rs(at));}int CanR_build(int l,int r){if(l>=r)return 0;int mid=(l+r)>>1;int &at=tmp[mid];ls(at)=CanR_build(l,mid);rs(at)=CanR_build(mid+1,r);push_up(at);return at;}void rebuild(int &at){int idx=0;CanR_flatten(idx,at);at=CanR_build(1,idx+1);}void insert(int x,int &at){if(!at){at=New(x);return;}if(a[at].num==x)++a[at].cnt;else if(x<a[at].num)insert(x,ls(at));else insert(x,rs(at));push_up(at);if(CanR(at))rebuild(at);}void remove(int x,int &at){if(!at)return;if(x==a[at].num){if(a[at].cnt)--a[at].cnt;}else if(x<a[at].num)remove(x,ls(at));else remove(x,rs(at));push_up(at);if(CanR(at))rebuild(at);}int uprbd(int x,int at){if(!at)return 1;if(x==a[at].num&&a[at].cnt)return a[ls(at)].sz+a[at].cnt+1;else if(x<a[at].num)return uprbd(x,ls(at));else return uprbd(x,rs(at))+a[ls(at)].sz+a[at].cnt;}int uprbd_gt(int x,int at){if(!at)return 0;if(x==a[at].num&&a[at].cnt)return a[ls(at)].sz;else if(x<a[at].num)return uprbd_gt(x,ls(at));else return uprbd_gt(x,rs(at))+a[ls(at)].sz+a[at].cnt;}int getval(int x,int at){if(!at)return INF;if(x<=a[ls(at)].sz)return getval(x,ls(at));if(x<=a[ls(at)].sz+a[at].cnt)return a[at].num;else return getval(x-a[ls(at)].sz-a[at].cnt,rs(at));}int getpre(int x,int at){return getval(uprbd_gt(x,at),at);}int getnext(int x,int at){return getval(uprbd(x,at),at);}int getrank(int x,int at){return uprbd_gt(x,at)+1;}}signed main(){using namespace BT; // 替罪羊树 qwqread(n);read(m);for(int i=1; i<=n; i++){char op;int x;char t;//gc(); //win下要用op=gc();read(x);if(op=='I'&&x>=m) // 不满下界的直接不管了{insert(x-d,rt);++num1;++num2;}if(op=='A')d+=x;if(op=='F'){if(num1<x)puts("-1"); // 不满k个数else {write(getval(num1-x+1,rt)+d);pc('\n');}}if(op=='S'){d-=x;rebuild(rt); // 直接重构}}write(num2-num1);pc('\n');return 0;}
]]>
@@ -8404,7 +8404,7 @@ /2021/10/03/ubuntu-xie-zai-fcitx-shu-ru-fa/ - ubuntu 卸载fcitx输入法

我又来水文章了

打开终端

输入

sudo apt-get remove fcitxsudo apt-get remove fcitx-module*sudo apt-get remove fcitx-frontend*sudo apt-get purge fcitx*

如果您原来使用的就是fcitx输入法,那再输入一句

sudo reboot

重启以后默认就是ibus输入法啦!

ibus大法好!

]]>
+ ubuntu 卸载fcitx输入法

我又来水文章了

打开终端

输入

sudo apt-get remove fcitxsudo apt-get remove fcitx-module*sudo apt-get remove fcitx-frontend*sudo apt-get purge fcitx*

如果您原来使用的就是fcitx输入法,那再输入一句

sudo reboot

重启以后默认就是ibus输入法啦!

ibus大法好!

]]>
@@ -8429,7 +8429,7 @@ /2021/09/23/jun-zhi-bu-deng-shi-ji-qi-zheng-ming/ - 均值不等式及其证明

前言

还有很多证明方法,等我学了再写 qwq


均值不等式

引理1:若 $a\ge 0,b\ge 0$ ,则 $(a+b)^n\ge a^n+na^{n-1}b,n\in \Z_+$

直接二项式展开,证明略


命题1:求证 $\dfrac{\sum_{i=1}^{n}a_i}{n}\ge \sqrt[n]{\prod_{i=1}^na_i}$

数学归纳法证明:原命题等价于 $\left(\dfrac{\sum_{i=1}^{n}a_i}{n}\right)^n \ge \prod\limits_{i=1}^{n}a_i$

设 $n=k$ 时原不等式成立

不妨假设 $\forall i<j,a_i<a_j$

记 $S_n = \sum\limits_{i=1}^{n}a_i$

当 $n=k+1$ 时,原不等式为

$\left(\dfrac{S_k+a_{k+1}}{k+1}\right)^{k+1} \ge \prod\limits_{i=1}^{k+1}a_i$

$\left(\dfrac{S_k}{k}+\dfrac{ka_{k+1}-S_k}{k(k+1)}\right)^{k+1}\ge \prod\limits_{i=1}^{k+1}a_i$

由引理1可知 $\text{LHS}\ge \left(\dfrac{S_k}{k}\right)^{k+1}+(k+1)\left(\dfrac{S_k}{k}\right)^k\dfrac{ka_{k+1}-S_k}{k(k+1)}$

$\because a_{k+1}=\max\limits_{1\le i \le k+1}\{a_i\}$

$\therefore ka_{k+1}-S_k\ge 0$

故 $\text{LHS} \ge \left(\dfrac{S_k}{k}\right)^{k}\times a_{k+1} \ge \text{RHS}$

故原命题得证


命题2:求证 $\sqrt{\dfrac{\sum_{i=1}^{n}a_i^2}{n}} \ge \dfrac{\sum_{i=1}^{n}a_i}{n}$

证明:令 $c = \dfrac{\sum_{i=1}^{n}a_i}{n},x_i=a_i-c$

则 $\sum_{i=1}^{n}a_i = \sum_{i=1}^{n}x_i+nc = \sum_{i=1}^{n}x_i+\sum_{i=1}^{n}a_i$

$\therefore \sum_{i=1}^{n}x_i = 0$

$\therefore \text{LHS}=\sqrt{\dfrac{\sum_{i=1}^{n}\left(x_i+c\right)^2}{n}}$

$= \sqrt{c^2+\dfrac{\sum_{i=1}^{n}x_i^2}{n}} \ge \sqrt{c^2} = \text{RHS}$

故原命题得证


命题3:求证 $\sqrt[n]{\prod_{i=1}^na_i} \ge \dfrac{n}{\sum_{i=1}^{n}a_i^{-1}}$

证明:由命题1可知

$\dfrac{\sum_{i=1}^{n}a_i^{-1}}{n} \ge \sqrt[n]{\prod_{i=1}^{n}a_i^{-1}} = \dfrac{1}{\sqrt[n]{\prod_{i=1}^{n}a_i}}$

$\therefore \sqrt[n]{\prod_{i=1}^{n}a_i} \ge \dfrac{n}{\sum_{i=1}^{n}a_i^{-1}}$

故原命题得证


$\therefore$ 综上所述,可得结论(均值不等式)

$\dfrac{n}{\sum_{i=1}^{n}a_i^{-1}} \le \sqrt[n]{\prod_{i=1}^{n}a_i} \le \dfrac{\sum_{i=1}^{n}a_i}{n} \le \sqrt{\dfrac{\sum_{i=1}^{n}a_i^2}{n}}$

当且仅当 $a_1=a_2=…=a_n$ 时等号成立

也就是

调和平均数不超过几何平均数不超过算术平均数不超过平方平均数

]]>
+ 均值不等式及其证明

前言

还有很多证明方法,等我学了再写 qwq


均值不等式

引理1:若 \(a\ge 0,b\ge0\) ,则 \((a+b)^n\gea^n+na^{n-1}b,n\in \Z_+\)

直接二项式展开,证明略


命题1:求证 \(\dfrac{\sum_{i=1}^{n}a_i}{n}\ge\sqrt[n]{\prod_{i=1}^na_i}\)

数学归纳法证明:原命题等价于 \(\left(\dfrac{\sum_{i=1}^{n}a_i}{n}\right)^n \ge\prod\limits_{i=1}^{n}a_i\)

\(n=k\) 时原不等式成立

不妨假设 \(\foralli<j,a_i<a_j\)

\(S_n =\sum\limits_{i=1}^{n}a_i\)

\(n=k+1\) 时,原不等式为

\(\left(\dfrac{S_k+a_{k+1}}{k+1}\right)^{k+1} \ge\prod\limits_{i=1}^{k+1}a_i\)

\(\left(\dfrac{S_k}{k}+\dfrac{ka_{k+1}-S_k}{k(k+1)}\right)^{k+1}\ge\prod\limits_{i=1}^{k+1}a_i\)

由引理1可知 \(\text{LHS}\ge\left(\dfrac{S_k}{k}\right)^{k+1}+(k+1)\left(\dfrac{S_k}{k}\right)^k\dfrac{ka_{k+1}-S_k}{k(k+1)}\)

\(\because a_{k+1}=\max\limits_{1\le i \lek+1}\{a_i\}\)

\(\therefore ka_{k+1}-S_k\ge 0\)

\(\text{LHS} \ge\left(\dfrac{S_k}{k}\right)^{k}\times a_{k+1} \ge\text{RHS}\)

故原命题得证


命题2:求证 \(\sqrt{\dfrac{\sum_{i=1}^{n}a_i^2}{n}} \ge\dfrac{\sum_{i=1}^{n}a_i}{n}\)

证明:令 \(c =\dfrac{\sum_{i=1}^{n}a_i}{n},x_i=a_i-c\)

\(\sum_{i=1}^{n}a_i =\sum_{i=1}^{n}x_i+nc = \sum_{i=1}^{n}x_i+\sum_{i=1}^{n}a_i\)

\(\therefore \sum_{i=1}^{n}x_i =0\)

\(\therefore\text{LHS}=\sqrt{\dfrac{\sum_{i=1}^{n}\left(x_i+c\right)^2}{n}}\)

\(=\sqrt{c^2+\dfrac{\sum_{i=1}^{n}x_i^2}{n}} \ge \sqrt{c^2} =\text{RHS}\)

故原命题得证


命题3:求证 \(\sqrt[n]{\prod_{i=1}^na_i} \ge\dfrac{n}{\sum_{i=1}^{n}a_i^{-1}}\)

证明:由命题1可知

\(\dfrac{\sum_{i=1}^{n}a_i^{-1}}{n} \ge\sqrt[n]{\prod_{i=1}^{n}a_i^{-1}} =\dfrac{1}{\sqrt[n]{\prod_{i=1}^{n}a_i}}\)

\(\therefore \sqrt[n]{\prod_{i=1}^{n}a_i}\ge \dfrac{n}{\sum_{i=1}^{n}a_i^{-1}}\)

故原命题得证


\(\therefore\)综上所述,可得结论(均值不等式)

\(\dfrac{n}{\sum_{i=1}^{n}a_i^{-1}} \le\sqrt[n]{\prod_{i=1}^{n}a_i} \le \dfrac{\sum_{i=1}^{n}a_i}{n} \le\sqrt{\dfrac{\sum_{i=1}^{n}a_i^2}{n}}\)

当且仅当 \(a_1=a_2=...=a_n\)时等号成立

也就是

调和平均数不超过几何平均数不超过算术平均数不超过平方平均数

]]>
@@ -8454,7 +8454,7 @@ /2021/09/18/qian-tan-fen-kuai-qu-jian-zhong-shu/ - 浅谈分块 区间众数

前言

分块大法好(

本文直接讲例题了 qwq


P4168 [Violet]蒲公英

题目链接:P4168 [Violet]蒲公英

题意: 找到区间内编号最小的众数,强制在线

解法一

直接分块

设块长为 $len$ ,块的总数为 $t$ 个

我们先预处理出所有以块的端点为端点的区间众数和众数的出现次数

或者说就是把每个连续块组成的区间都算出编号最小众数和它的出现次数

别忘了离散化,不然MLE咯

总共有 $t^2$ 个这样的区间,每次处理 $O(n)$ ,则预处理时间复杂度为 $O(nt^2)$

暴力查询时可以在预处理过的数组基础上临时更新数组,以统计答案,再复原数组

总时间复杂度 $O(nt^2+mn/t)$

假设 $m$ 和 $n$ 数量级相同,解 $nt^2=mn/t$,$t \approx \sqrt[3]{n}$

则时间复杂度可以控制在 $O(n^{5/3})$ 内

空间复杂度比较大,$O(nt^2)$ (注:预处理所需)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5e4+5)#define MAXM (int)(45)#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>void write(T k){if(k<0){pc('-');k=-k;}if(k>9)write(k/10);pc(k%10+'0');}int n,m,Q,len,last,t;int id[MAXN],L[MAXN],R[MAXN],c[MAXM][MAXM][MAXN]; // c[i][j][a[k]] 表示块i~j中a[k]的出现次数int b[MAXN],f[MAXM][MAXM][2],now[2],a[MAXN]; // f[i][j][0/1]分别记录次数和众数void init(){    // 预处理sort(b+1,b+1+n);m=unique(b+1,b+1+n)-b-1;for(int i=1; i<=n; i++)a[i]=lower_bound(b+1,b+1+m,a[i])-b; // 离散化t=pow((double)n,(double)1/3); // 块的数量len=t?n/t:n; // 块的长度for(int i=1; i<=t; i++){L[i]=(i-1)*len+1;R[i]=i*len;}if(R[t]<n){L[t+1]=R[t]+1;R[++t]=n;}for(int i=1; i<=t; i++)for(int j=L[i]; j<=R[i]; j++)id[j]=i;for(int i=1; i<=t; i++)for(int j=i; j<=t; j++){for(int k=L[i]; k<=R[j]; k++)++c[i][j][a[k]]; // 统计答案for(int k=1; k<=m; k++){if(c[i][j][k]>f[i][j][0]) // 更新答案{f[i][j][0]=c[i][j][k];f[i][j][1]=k;}}}}void proc(int x,int y,int k){++c[x][y][k]; // 临时修改if(c[x][y][k]>now[0]||c[x][y][k]==now[0]&&k时间<now[1]){now[0]=c[x][y][k]; // 更新答案now[1]=k;}}void solve(int l,int r){l=(l+last-1)%n+1; // 题目要求r=(r+last-1)%n+1;if(l>r)swap(l,r); // 题目要求int p=id[l],q=id[r],x=0,y=0;if(p+1<=q-1)x=p+1,y=q-1;memcpy(now,f[x][y],sizeof(now));if(p==q){for(int i=l; i<=r; i++)proc(x,y,a[i]); // 临时修改+更新答案for(int i=l; i<=r; i++)--c[x][y][a[i]]; // 改回来}else{for(int i=l; i<=R[p]; i++)proc(x,y,a[i]);for(int i=L[q]; i<=r; i++)proc(x,y,a[i]);for(int i=l; i<=R[p]; i++)--c[x][y][a[i]];for(int i=L[q]; i<=r; i++)--c[x][y][a[i]];}write(last=b[now[1]]);pc('\n');}signed main(){read(n);read(Q);for(int i=1; i<=n; i++)read(a[i]),b[i]=a[i];init();while(Q--){int l,r;read(l);read(r);solve(l,r);}return 0;}

解法二

还是分块,只不过维护的东西不一样

我们发现,解法一的空间就多在了次数统计这个东西上

次数统计?不是可以二分吗?

对每个数值建个vector保存它出现的下标,出现次数就是upper_bd-lower_bd

这样我们只要预处理众数是谁就行了

暴力查询结果和众数的结果比一下就行了

别忘了离散化哦

有点时间换空间的感觉,但是还是很快

时间复杂度 $O(nt+mn/t\times \log n)$

假设 $m$ 和 $n$ 数量级相同,还是解方程,$t \approx \sqrt{n\log n}$

则时间复杂度可控制在 $O(n\sqrt{n\log n})$

空间复杂度 $O(t^2)$ ,不错

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5e4+5)#define MAXM (int)(888)#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>void write(T k){if(k<0){pc('-');k=-k;}if(k>9)write(k/10);pc(k%10+'0');}int n,m,Q,last,t,len,c[MAXN],id[MAXN];int a[MAXN],b[MAXN],L[MAXM],R[MAXM],f[MAXM][MAXM];vector<int> vec[MAXN];int find(int k,int l,int r){return upper_bound(vec[k].begin(),vec[k].end(),r)-lower_bound(vec[k].begin(),vec[k].end(),l);} // 出现次数void proc(int k,int l,int r,int &ans,int &cnt){int res=find(k,l,r);if(res>cnt||res==cnt&&k<ans){cnt=res;ans=k;} // 更新答案}void init(){sort(b+1,b+1+n);m=unique(b+1,b+1+n)-b-1;for(int i=1; i<=n; i++){a[i]=lower_bound(b+1,b+1+m,a[i])-b;vec[a[i]].push_back(i); // 记录下标}t=sqrt(log(n)/log(2)*n);len=t?n/t:n;for(int i=1; i<=t; i++){L[i]=(i-1)*len+1;R[i]=i*len;}if(R[t]<n){L[t+1]=R[t]+1;R[++t]=n;}for(int i=1; i<=t; i++)for(int j=L[i]; j<=R[i]; j++)id[j]=i;for(int i=1; i<=t; i++){memset(c,0,sizeof(c)); // 统计次数int cnt=0,ans=0;for(int j=L[i]; j<=n; j++){++c[a[j]];if(c[a[j]]>cnt||c[a[j]]==cnt&&a[j]<ans){cnt=c[a[j]];ans=a[j];}f[i][id[j]]=ans; // 众数}}}void solve(int l,int r){l=(l+last-1)%n+1;r=(r+last-1)%n+1;if(l>r)swap(l,r);int p=id[l],q=id[r],x=0,y=0,ans=0,cnt=0;if(p+1<=q-1)x=p+1,y=q-1;if(p==q){for(int i=l; i<=r; i++)proc(a[i],l,r,ans,cnt);}else{for(int i=l; i<=R[p]; i++)proc(a[i],l,r,ans,cnt);for(int i=L[q]; i<=r; i++)proc(a[i],l,r,ans,cnt);}if(f[x][y])proc(f[x][y],l,r,ans,cnt);write(last=b[ans]);pc('\n');}signed main(){read(n);read(Q);for(int i=1; i<=n; i++)read(a[i]),b[i]=a[i];init();while(Q--){int l,r;read(l);read(r);solve(l,r);}return 0;}

然后我们就想到

如果只需要知道众数的出现次数,有没有什么好的办法呢?


[Ynoi2019 模拟赛] Yuno loves sqrt technology III

题目链接:[Ynoi2019 模拟赛] Yuno loves sqrt technology III

题意:区间众数出现次数,强制在线

类似于上一题的解法二,这题 $t=\sqrt{n}$ 即可

不同的是,我们要预处理出连续块的众数出现次数

然后还是每个数值建一个vector,统计出现的下标

再记录每个存的下标在vector中的下标 $ax_i$

显然非暴力查询的区域,答案 $res=f[x][y]$

因为边界数最多对答案有 $2\sqrt{n}$ 的贡献

所以暴力查询时,对于每个左侧元素 $k$ ,我们只要找到 $k$ 的vector中 $ax_k+res$ 的位置的元素,看它是不是小于 $r$ ,如果小于 $r$ ,说明它一定更接近最终答案且在该区间内,更新 $res$

对于每个右侧元素 $k$ ,我们只要找到 $k$ 的vector中 $ax_k-res$ 的位置的元素,看它是不是大于 $l$ ,原理同上

最终答案即为 $res$

时间复杂度 $O((n+m)\sqrt{n})$

空间复杂度 $O(n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5e5+5)#define MAXM (int)(888)#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>void write(T k){if(k<0){pc('-');k=-k;}if(k>9)write(k/10);pc(k%10+'0');}int n,m,Q,last,t,len,c[MAXN],id[MAXN],cnt[MAXN];int a[MAXN],b[MAXN],L[MAXM],R[MAXM],f[MAXM][MAXM],ax[MAXN];vector<int> vec[MAXN];void init(){sort(b+1,b+1+n);m=unique(b+1,b+1+n)-b-1;for(int i=1; i<=n; i++){a[i]=lower_bound(b+1,b+1+m,a[i])-b; // 离散化vec[a[i]].push_back(i); // 下标ax[i]=vec[a[i]].size()-1; // 每个下标在vector中的下标}t=sqrt(n);len=t?n/t:n;for(int i=1; i<=t; i++){L[i]=(i-1)*len+1;R[i]=i*len;}if(R[t]<n){L[t+1]=R[t]+1;R[++t]=n;}for(int i=1; i<=t; i++)for(int j=L[i]; j<=R[i]; j++)id[j]=i;for(int i=1; i<=t; i++){memset(c,0,sizeof(c));for(int j=i; j<=t; j++){f[i][j]=f[i][j-1];for(int k=L[j]; k<=R[j]; k++)f[i][j]=max(++c[a[k]],f[i][j]); // 统计次数即可(它又不要知道众数是谁咯 qwq)}}}void solve(int l,int r){l^=last;r^=last; // 题目要求if(l>r)swap(l,r);int p=id[l],q=id[r],x=0,y=0,res=0;if(p+1<=q-1)x=p+1,y=q-1;if(p==q){for(int i=l; i<=r; i++)cnt[a[i]]=0;for(int i=l; i<=r; i++)res=max(++cnt[a[i]],res);}else{res=f[x][y];for(int i=l; i<=R[p]; i++){int it=ax[i]+res;while(it<vec[a[i]].size()&&vec[a[i]][it]<=r){++res;++it;} // 暴力扩展+更新答案}for(int i=L[q]; i<=r; i++){int it=ax[i]-res;while(it>=0&&vec[a[i]][it]>=l){++res;--it;}}}write(last=res);pc('\n');}signed main(){read(n);read(Q);for(int i=1; i<=n; i++)read(a[i]),b[i]=a[i];init();while(Q--){int l,r;read(l);read(r);solve(l,r);}return 0;}

总结

本文简单讲了下区间众数的分块解法

]]>
+ 浅谈分块 区间众数

前言

分块大法好(

本文直接讲例题了 qwq


P4168 [Violet]蒲公英

题目链接:P4168[Violet]蒲公英

题意: 找到区间内编号最小的众数,强制在线

解法一

直接分块

设块长为 \(len\) ,块的总数为 \(t\) 个

我们先预处理出所有以块的端点为端点的区间众数和众数的出现次数

或者说就是把每个连续块组成的区间都算出编号最小众数和它的出现次数

别忘了离散化,不然MLE咯

总共有 \(t^2\)个这样的区间,每次处理 \(O(n)\),则预处理时间复杂度为 \(O(nt^2)\)

暴力查询时可以在预处理过的数组基础上临时更新数组,以统计答案,再复原数组

总时间复杂度 \(O(nt^2+mn/t)\)

假设 \(m\)\(n\) 数量级相同,解 \(nt^2=mn/t\),\(t\approx \sqrt[3]{n}\)

则时间复杂度可以控制在 \(O(n^{5/3})\) 内

空间复杂度比较大,\(O(nt^2)\)(注:预处理所需)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5e4+5)#define MAXM (int)(45)#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>void write(T k){if(k<0){pc('-');k=-k;}if(k>9)write(k/10);pc(k%10+'0');}int n,m,Q,len,last,t;int id[MAXN],L[MAXN],R[MAXN],c[MAXM][MAXM][MAXN]; // c[i][j][a[k]] 表示块i~j中a[k]的出现次数int b[MAXN],f[MAXM][MAXM][2],now[2],a[MAXN]; // f[i][j][0/1]分别记录次数和众数void init(){    // 预处理sort(b+1,b+1+n);m=unique(b+1,b+1+n)-b-1;for(int i=1; i<=n; i++)a[i]=lower_bound(b+1,b+1+m,a[i])-b; // 离散化t=pow((double)n,(double)1/3); // 块的数量len=t?n/t:n; // 块的长度for(int i=1; i<=t; i++){L[i]=(i-1)*len+1;R[i]=i*len;}if(R[t]<n){L[t+1]=R[t]+1;R[++t]=n;}for(int i=1; i<=t; i++)for(int j=L[i]; j<=R[i]; j++)id[j]=i;for(int i=1; i<=t; i++)for(int j=i; j<=t; j++){for(int k=L[i]; k<=R[j]; k++)++c[i][j][a[k]]; // 统计答案for(int k=1; k<=m; k++){if(c[i][j][k]>f[i][j][0]) // 更新答案{f[i][j][0]=c[i][j][k];f[i][j][1]=k;}}}}void proc(int x,int y,int k){++c[x][y][k]; // 临时修改if(c[x][y][k]>now[0]||c[x][y][k]==now[0]&&k时间<now[1]){now[0]=c[x][y][k]; // 更新答案now[1]=k;}}void solve(int l,int r){l=(l+last-1)%n+1; // 题目要求r=(r+last-1)%n+1;if(l>r)swap(l,r); // 题目要求int p=id[l],q=id[r],x=0,y=0;if(p+1<=q-1)x=p+1,y=q-1;memcpy(now,f[x][y],sizeof(now));if(p==q){for(int i=l; i<=r; i++)proc(x,y,a[i]); // 临时修改+更新答案for(int i=l; i<=r; i++)--c[x][y][a[i]]; // 改回来}else{for(int i=l; i<=R[p]; i++)proc(x,y,a[i]);for(int i=L[q]; i<=r; i++)proc(x,y,a[i]);for(int i=l; i<=R[p]; i++)--c[x][y][a[i]];for(int i=L[q]; i<=r; i++)--c[x][y][a[i]];}write(last=b[now[1]]);pc('\n');}signed main(){read(n);read(Q);for(int i=1; i<=n; i++)read(a[i]),b[i]=a[i];init();while(Q--){int l,r;read(l);read(r);solve(l,r);}return 0;}

解法二

还是分块,只不过维护的东西不一样

我们发现,解法一的空间就多在了次数统计这个东西上

次数统计?不是可以二分吗?

对每个数值建个vector保存它出现的下标,出现次数就是upper_bd-lower_bd

这样我们只要预处理众数是谁就行了

暴力查询结果和众数的结果比一下就行了

别忘了离散化哦

有点时间换空间的感觉,但是还是很快

时间复杂度 \(O(nt+mn/t\times \logn)\)

假设 \(m\)\(n\) 数量级相同,还是解方程,\(t \approx \sqrt{n\log n}\)

则时间复杂度可控制在 \(O(n\sqrt{n\logn})\)

空间复杂度 \(O(t^2)\) ,不错

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5e4+5)#define MAXM (int)(888)#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>void write(T k){if(k<0){pc('-');k=-k;}if(k>9)write(k/10);pc(k%10+'0');}int n,m,Q,last,t,len,c[MAXN],id[MAXN];int a[MAXN],b[MAXN],L[MAXM],R[MAXM],f[MAXM][MAXM];vector<int> vec[MAXN];int find(int k,int l,int r){return upper_bound(vec[k].begin(),vec[k].end(),r)-lower_bound(vec[k].begin(),vec[k].end(),l);} // 出现次数void proc(int k,int l,int r,int &ans,int &cnt){int res=find(k,l,r);if(res>cnt||res==cnt&&k<ans){cnt=res;ans=k;} // 更新答案}void init(){sort(b+1,b+1+n);m=unique(b+1,b+1+n)-b-1;for(int i=1; i<=n; i++){a[i]=lower_bound(b+1,b+1+m,a[i])-b;vec[a[i]].push_back(i); // 记录下标}t=sqrt(log(n)/log(2)*n);len=t?n/t:n;for(int i=1; i<=t; i++){L[i]=(i-1)*len+1;R[i]=i*len;}if(R[t]<n){L[t+1]=R[t]+1;R[++t]=n;}for(int i=1; i<=t; i++)for(int j=L[i]; j<=R[i]; j++)id[j]=i;for(int i=1; i<=t; i++){memset(c,0,sizeof(c)); // 统计次数int cnt=0,ans=0;for(int j=L[i]; j<=n; j++){++c[a[j]];if(c[a[j]]>cnt||c[a[j]]==cnt&&a[j]<ans){cnt=c[a[j]];ans=a[j];}f[i][id[j]]=ans; // 众数}}}void solve(int l,int r){l=(l+last-1)%n+1;r=(r+last-1)%n+1;if(l>r)swap(l,r);int p=id[l],q=id[r],x=0,y=0,ans=0,cnt=0;if(p+1<=q-1)x=p+1,y=q-1;if(p==q){for(int i=l; i<=r; i++)proc(a[i],l,r,ans,cnt);}else{for(int i=l; i<=R[p]; i++)proc(a[i],l,r,ans,cnt);for(int i=L[q]; i<=r; i++)proc(a[i],l,r,ans,cnt);}if(f[x][y])proc(f[x][y],l,r,ans,cnt);write(last=b[ans]);pc('\n');}signed main(){read(n);read(Q);for(int i=1; i<=n; i++)read(a[i]),b[i]=a[i];init();while(Q--){int l,r;read(l);read(r);solve(l,r);}return 0;}

然后我们就想到

如果只需要知道众数的出现次数,有没有什么好的办法呢?


[Ynoi2019模拟赛] Yuno loves sqrt technology III

题目链接:[Ynoi2019模拟赛] Yuno loves sqrt technology III

题意:区间众数出现次数,强制在线

类似于上一题的解法二,这题 \(t=\sqrt{n}\) 即可

不同的是,我们要预处理出连续块的众数出现次数

然后还是每个数值建一个vector,统计出现的下标

再记录每个存的下标在vector中的下标 \(ax_i\)

显然非暴力查询的区域,答案 \(res=f[x][y]\)

因为边界数最多对答案有 \(2\sqrt{n}\)的贡献

所以暴力查询时,对于每个左侧元素 \(k\) ,我们只要找到 \(k\) 的vector中 \(ax_k+res\) 的位置的元素,看它是不是小于\(r\) ,如果小于 \(r\),说明它一定更接近最终答案且在该区间内,更新 \(res\)

对于每个右侧元素 \(k\),我们只要找到 \(k\) 的vector中 \(ax_k-res\) 的位置的元素,看它是不是大于\(l\) ,原理同上

最终答案即为 \(res\)

时间复杂度 \(O((n+m)\sqrt{n})\)

空间复杂度 \(O(n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5e5+5)#define MAXM (int)(888)#define pc(a) putchar(a)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>void write(T k){if(k<0){pc('-');k=-k;}if(k>9)write(k/10);pc(k%10+'0');}int n,m,Q,last,t,len,c[MAXN],id[MAXN],cnt[MAXN];int a[MAXN],b[MAXN],L[MAXM],R[MAXM],f[MAXM][MAXM],ax[MAXN];vector<int> vec[MAXN];void init(){sort(b+1,b+1+n);m=unique(b+1,b+1+n)-b-1;for(int i=1; i<=n; i++){a[i]=lower_bound(b+1,b+1+m,a[i])-b; // 离散化vec[a[i]].push_back(i); // 下标ax[i]=vec[a[i]].size()-1; // 每个下标在vector中的下标}t=sqrt(n);len=t?n/t:n;for(int i=1; i<=t; i++){L[i]=(i-1)*len+1;R[i]=i*len;}if(R[t]<n){L[t+1]=R[t]+1;R[++t]=n;}for(int i=1; i<=t; i++)for(int j=L[i]; j<=R[i]; j++)id[j]=i;for(int i=1; i<=t; i++){memset(c,0,sizeof(c));for(int j=i; j<=t; j++){f[i][j]=f[i][j-1];for(int k=L[j]; k<=R[j]; k++)f[i][j]=max(++c[a[k]],f[i][j]); // 统计次数即可(它又不要知道众数是谁咯 qwq)}}}void solve(int l,int r){l^=last;r^=last; // 题目要求if(l>r)swap(l,r);int p=id[l],q=id[r],x=0,y=0,res=0;if(p+1<=q-1)x=p+1,y=q-1;if(p==q){for(int i=l; i<=r; i++)cnt[a[i]]=0;for(int i=l; i<=r; i++)res=max(++cnt[a[i]],res);}else{res=f[x][y];for(int i=l; i<=R[p]; i++){int it=ax[i]+res;while(it<vec[a[i]].size()&&vec[a[i]][it]<=r){++res;++it;} // 暴力扩展+更新答案}for(int i=L[q]; i<=r; i++){int it=ax[i]-res;while(it>=0&&vec[a[i]][it]>=l){++res;--it;}}}write(last=res);pc('\n');}signed main(){read(n);read(Q);for(int i=1; i<=n; i++)read(a[i]),b[i]=a[i];init();while(Q--){int l,r;read(l);read(r);solve(l,r);}return 0;}

总结

本文简单讲了下区间众数的分块解法

]]>
@@ -8481,7 +8481,7 @@ /2021/09/11/loj10050-the-xor-largest-pair-ti-jie/ - LOJ10050 The XOR Largest Pair

题目链接:LOJ10050 The XOR Largest Pair

题意:给定 $n$ 个非负整数,任取两个数,使得这两个数的异或结果最大,求这个最大值

肯定不是朴素枚举啊qwq

直接用Trie存每个数的二进制就可以了(注:把二进制看作字符串)

根据异或的定义,对于每个 $a_j$ ,我们从最高位开始,尽可能取与 $a_i$ 这一位相反的那一条路,一定可以得到最大值

至于最高位,我们只要不够的补 $0$ 即可

因为它题目中说了每个数最大不大于 $2^{31}-1$ ,那么我们直接保存为30位二进制即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>void write(T k){if(k<0){putchar('-');k=-k;}if(k>9)write(k/10);putchar(k%10+'0');}int n,ans=0;int a[MAXN];struct Trie{int trie[MAXN*32][3],tot=1; // trie的大小要注意void insert(int x){int p=0;for(int i=31; i>=0; --i) // 其实只要30就可以了{int k=(x>>i)&1;if(!trie[p][k])trie[p][k]=++tot;p=trie[p][k];}}int qry(int x){int ans=0,p=0;for(int i=31; i>=0; --i){int k=(x>>i)&1;if(trie[p][k^1])p=trie[p][k^1],ans|=(1<<i); // 尽可能选择这一位相反的else p=trie[p][k];}return ans;}}t[1]; // 这个[1]并没有什么用 qwqsigned main(){read(n);for(int i=1; i<=n; i++){read(a[i]);t[0].insert(a[i]);ans=max(ans,t[0].qry(a[i]));}write(ans);putchar('\n');return 0;}
]]>
+ LOJ10050 The XOR LargestPair

题目链接:LOJ10050 The XOR LargestPair

题意:给定 \(n\)个非负整数,任取两个数,使得这两个数的异或结果最大,求这个最大值

肯定不是朴素枚举啊qwq

直接用Trie存每个数的二进制就可以了(注:把二进制看作字符串)

根据异或的定义,对于每个 \(a_j\),我们从最高位开始,尽可能取与 \(a_i\)这一位相反的那一条路,一定可以得到最大值

至于最高位,我们只要不够的补 \(0\)即可

因为它题目中说了每个数最大不大于 \(2^{31}-1\),那么我们直接保存为30位二进制即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(1e5+5)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>void write(T k){if(k<0){putchar('-');k=-k;}if(k>9)write(k/10);putchar(k%10+'0');}int n,ans=0;int a[MAXN];struct Trie{int trie[MAXN*32][3],tot=1; // trie的大小要注意void insert(int x){int p=0;for(int i=31; i>=0; --i) // 其实只要30就可以了{int k=(x>>i)&1;if(!trie[p][k])trie[p][k]=++tot;p=trie[p][k];}}int qry(int x){int ans=0,p=0;for(int i=31; i>=0; --i){int k=(x>>i)&1;if(trie[p][k^1])p=trie[p][k^1],ans|=(1<<i); // 尽可能选择这一位相反的else p=trie[p][k];}return ans;}}t[1]; // 这个[1]并没有什么用 qwqsigned main(){read(n);for(int i=1; i<=n; i++){read(a[i]);t[0].insert(a[i]);ans=max(ans,t[0].qry(a[i]));}write(ans);putchar('\n');return 0;}
]]>
@@ -8510,7 +8510,7 @@ /2021/09/05/luo-gu-p6186-noi-online-1-ti-gao-zu-mou-pao-pai-xu-ti-jie/ - 洛谷P6186 [NOI Online #1 提高组] 冒泡排序 题解

题目链接:P6186 [NOI Online #1 提高组] 冒泡排序

题意:支持交换 $a_x$ 和 $a_{x+1}$ 和查询 $k$ 轮冒泡排序后的逆序对个数, $a$ 数组是长度为 $n$ 的排列

首先逆序对这个东西可以用树状数组搞定

题目给的是排列,因此求逆序对的伪代码如下

for i=1~n:    f[i]=i-sum(a[i])-1,add(a[i],1)

当然如果不是排列会稍微烦一点,可以看下我的这篇文章

设 $f[i]$ 表示在 $i$ 左侧比 $i$ 大的数的个数,那么逆序对就是 $\sum\limits_{i=1}^{n}{f[i]}$

(注:这里的 $f[i]$ 就是上面伪代码中的 qwq)

我们看看冒泡排序了 $k$ 次以后会发生什么事

首先每次冒泡排序所有的 $f[i]$ 都会减少 $1$ 当且仅当 $f[i]>0$

那么 $k$ 次之后所有小于等于 $k$ 的 $f[i]$ 都会变成 $0$

因此我们只要每次就查询 $[k+1,n]$ 中 $f[i]$ 的个数就是答案了

这个我们可以用两个树状数组来维护答案

再看看这个交换操作,可能会影响到其他的答案

那么怎么办呢?根据刚才我们发现的性质,我们可以先把它们两个的贡献减掉 $1$ ,然后按 $a_x$ 和 $a_{x+1}$ 的大小关系分别处理答案的变化,再把原先的贡献补回来就好了

代码实现有点烦,主要是有点乱

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(2e5+5)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(T k){if(!k){putchar('0');return;}if(k<0){k=-k;putchar('-');}if(k>9)write(k/10);putchar(k%10+'0');}int n,m;int a[MAXN],f[MAXN];struct BIT{int bit[MAXN];void clear(){memset(bit,0,sizeof(bit));}inline int lowbit(int x){return x&(-x);}void add(int x,int v){while(x&&x<=n){bit[x]+=v;x+=lowbit(x);}} // 这里是坑点,f[]可能为0int sum(int x){int res=0;while(x>=1){res+=bit[x];x-=lowbit(x);}return res;}int qry(int l,int r){return sum(r)-sum(l-1);}}bt[2];// 两个树状数组维护的东西不一样哦 qwqvoid upd(int x,int v){bt[0].add(x,v);bt[1].add(x,v*x);} int qry(int l,int r){return bt[1].qry(l,r)-bt[0].qry(l,r)*(l-1);}inline void Swap(int x){upd(f[x],-1);upd(f[x+1],-1); if(a[x]>a[x+1])--f[x+1]; // 分情况讨论else ++f[x];swap(a[x],a[x+1]);swap(f[x],f[x+1]);upd(f[x],1);upd(f[x+1],1);}signed main(){read(n);read(m);for(int i=1; i<=n; i++){read(a[i]);f[i]=i-bt[0].sum(a[i])-1;bt[0].add(a[i],1);} // 先拿一个树状数组临时用一下bt[0].clear();for(int i=1; i<=n; i++)upd(f[i],1);while(m--){int op,k;read(op);read(k);if(op==1)Swap(k);if(op==2)write(k+1>n?0:qry(k+1,n)),putchar('\n'); // 特判勿忘}return 0;}
]]>
+ 洛谷P6186 [NOIOnline #1 提高组] 冒泡排序 题解

题目链接:P6186 [NOIOnline #1 提高组] 冒泡排序

题意:支持交换 \(a_x\) 和 \(a_{x+1}\) 和查询 \(k\) 轮冒泡排序后的逆序对个数, \(a\) 数组是长度为 \(n\) 的排列

首先逆序对这个东西可以用树状数组搞定

题目给的是排列,因此求逆序对的伪代码如下

for i=1~n:    f[i]=i-sum(a[i])-1,add(a[i],1)

当然如果不是排列会稍微烦一点,可以看下我的这篇文章

\(f[i]\) 表示在 \(i\) 左侧比 \(i\) 大的数的个数,那么逆序对就是 \(\sum\limits_{i=1}^{n}{f[i]}\)

(注:这里的 \(f[i]\)就是上面伪代码中的 qwq)

我们看看冒泡排序了 \(k\)次以后会发生什么事

首先每次冒泡排序所有的 \(f[i]\)都会减少 \(1\) 当且仅当 \(f[i]>0\)

那么 \(k\) 次之后所有小于等于 \(k\) 的 \(f[i]\) 都会变成 \(0\)

因此我们只要每次就查询 \([k+1,n]\)\(f[i]\) 的个数就是答案了

这个我们可以用两个树状数组来维护答案

再看看这个交换操作,可能会影响到其他的答案

那么怎么办呢?根据刚才我们发现的性质,我们可以先把它们两个的贡献减掉\(1\) ,然后按 \(a_x\) 和 \(a_{x+1}\)的大小关系分别处理答案的变化,再把原先的贡献补回来就好了

代码实现有点烦,主要是有点乱

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(2e5+5)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(T k){if(!k){putchar('0');return;}if(k<0){k=-k;putchar('-');}if(k>9)write(k/10);putchar(k%10+'0');}int n,m;int a[MAXN],f[MAXN];struct BIT{int bit[MAXN];void clear(){memset(bit,0,sizeof(bit));}inline int lowbit(int x){return x&(-x);}void add(int x,int v){while(x&&x<=n){bit[x]+=v;x+=lowbit(x);}} // 这里是坑点,f[]可能为0int sum(int x){int res=0;while(x>=1){res+=bit[x];x-=lowbit(x);}return res;}int qry(int l,int r){return sum(r)-sum(l-1);}}bt[2];// 两个树状数组维护的东西不一样哦 qwqvoid upd(int x,int v){bt[0].add(x,v);bt[1].add(x,v*x);} int qry(int l,int r){return bt[1].qry(l,r)-bt[0].qry(l,r)*(l-1);}inline void Swap(int x){upd(f[x],-1);upd(f[x+1],-1); if(a[x]>a[x+1])--f[x+1]; // 分情况讨论else ++f[x];swap(a[x],a[x+1]);swap(f[x],f[x+1]);upd(f[x],1);upd(f[x+1],1);}signed main(){read(n);read(m);for(int i=1; i<=n; i++){read(a[i]);f[i]=i-bt[0].sum(a[i])-1;bt[0].add(a[i],1);} // 先拿一个树状数组临时用一下bt[0].clear();for(int i=1; i<=n; i++)upd(f[i],1);while(m--){int op,k;read(op);read(k);if(op==1)Swap(k);if(op==2)write(k+1>n?0:qry(k+1,n)),putchar('\n'); // 特判勿忘}return 0;}
]]>
@@ -8537,7 +8537,7 @@ /2021/09/05/ni-xu-dui-de-san-chong-qiu-fa/ - 逆序对的三种求法

一、什么是逆序对?

对于给定的一段正整数序列,逆序对就是序列中 $a_i>a_j$ 且 $i<j$ 的有序对


二、怎么求逆序对

1.归并排序解法

归并排序可以很好的解决逆序对问题

我们只需要计算跨越分界线的贡献,并保证分界线两端的答案已被统计,显然根据归并的性质是可以保证的

对于跨分界线的贡献计算很简单,只要在归并排序上加一句就行了

这个东西请您自己理解,没什么好讲的 qwq

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5e5+5)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(T k){if(!k){putchar('0');return;}if(k<0){putchar('-');k=-k;}if(k>9)write(k/10);putchar(k%10+'0');}int n,ans;int a[MAXN],b[MAXN];void solve(int l,int r){if(l==r)return;int mid=(l+r)>>1;solve(l,mid);solve(mid+1,r);int i=l,j=mid+1,k=l;while(i<=mid&&j<=r){if(a[i]<=a[j])b[k++]=a[i++];else{ans+=mid-i+1; // i<j && a[i]>a[j]b[k++]=a[j++];}}while(i<=mid)b[k++]=a[i++];while(j<=r)b[k++]=a[j++];for(int i=l; i<=r; i++)a[i]=b[i];}signed main(){read(n);for(int i=1; i<=n; i++)read(a[i]);solve(1,n);write(ans);putchar('\n');return 0;}

2.树状数组解法

有人要说了,你这文章写的太烂了 好吧我承认我只是想讲这个方法,稍微铺垫一下…

那么树状数组跟逆序对有什么关系?

我们可以观察每一个元素,假设它有属性idx[i]表示在原数组中的位置,b[i]表示它的大小

我们把元素按照b[i]降序排序,然后从大到小取出这些元素并++f[idx[i]]

这样有什么性质呢?显然越小的数越晚标记

或者说,$\sum\limits_{j=1}^{\text{idx[i]-1}}{f[j]}$ 就是 $ia_j$ 的个数

这个东西一看就是树状数组处理

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5e5+5)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(T k){if(!k){putchar('0');return;}if(k<0){putchar('-');k=-k;}if(k>9)write(k/10);putchar(k%10+'0');}int n,ans;int a[MAXN],b[MAXN];struct BIT{int bit[MAXN];inline int lowbit(int x){return x&(-x);}void add(int x,int v){while(x<=n){bit[x]+=v;x+=lowbit(x);}}int sum(int x){int res=0;while(x>=1){res+=bit[x];x-=lowbit(x);}return res;}}bt[1];bool cmp(int x,int y){return b[x]==b[y]?x>y:b[x]>b[y];} // 相等的情况要注意signed main(){read(n);for(int i=1; i<=n; i++)read(b[i]),a[i]=i; // 记录下标sort(a+1,a+1+n,cmp);for(int i=1; i<=n; i++){bt[0].add(a[i],1);ans+=bt[0].sum(a[i]-1);}write(ans);putchar('\n');return 0;}

3.动态开点权值线段树解法

有点类似树状数组的解法,维护区间和

我们可以基于值域 $[1,1e9]$ 建一棵线段树

显然不能把所有的值都建结点,不然就MLE了

怎么办呢?我们会发现,其实每次要查的其实就几个点

那么就可以使用动态开点的思想,开一棵残缺的线段树,只保留我们会用到的结点

其他的结点就是虚拟结点啦!根本不用管,查到的时候返回0就行了

这题用这个解法有点容易被卡(? 而且有点大材小用

因此写的权值线段树可能有点小区别(因为实在太简单了…)

代码如下

#include <bits/stdc++.h>using namespace std;//#define int long long#define MAXN (int)(1e7+5)#define ls(at) a[at].l#define rs(at) a[at].rtemplate<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(T k){if(!k){putchar('0');return;}if(k<0){putchar('-');k=-k;}if(k>9)write(k/10);putchar(k%10+'0');}struct node{int l,r,cnt;}a[MAXN];int n,rt,tot;long long ans;void push_up(int at){a[at].cnt=a[ls(at)].cnt+a[rs(at)].cnt;}void add(int l,int r,int x,int k,int &at){if(!at)at=++tot; // 没有就新建if(l==r){a[at].cnt+=k;return;} // 叶子结点int mid=(l+r)>>1;if(x<=mid)add(l,mid,x,k,ls(at));else add(mid+1,r,x,k,rs(at));push_up(at);}int query(int nl,int nr,int l,int r,int at){if(!at)return 0;if(nl<=l&&r<=nr)return a[at].cnt;int mid=(l+r)>>1,res=0;if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));return res;}signed main(){read(n);for(int i=1; i<=n; i++){int x;read(x);ans+=query(x+1,1e9,1,1e9,rt);add(1,1e9,x,1,rt);}write(ans);putchar('\n');return 0;}
]]>
+ 逆序对的三种求法

一、什么是逆序对?

对于给定的一段正整数序列,逆序对就是序列中 \(a_i>a_j\) 且 \(i<j\) 的有序对


二、怎么求逆序对

1.归并排序解法

归并排序可以很好的解决逆序对问题

我们只需要计算跨越分界线的贡献,并保证分界线两端的答案已被统计,显然根据归并的性质是可以保证的

对于跨分界线的贡献计算很简单,只要在归并排序上加一句就行了

这个东西请您自己理解,没什么好讲的 qwq

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5e5+5)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(T k){if(!k){putchar('0');return;}if(k<0){putchar('-');k=-k;}if(k>9)write(k/10);putchar(k%10+'0');}int n,ans;int a[MAXN],b[MAXN];void solve(int l,int r){if(l==r)return;int mid=(l+r)>>1;solve(l,mid);solve(mid+1,r);int i=l,j=mid+1,k=l;while(i<=mid&&j<=r){if(a[i]<=a[j])b[k++]=a[i++];else{ans+=mid-i+1; // i<j && a[i]>a[j]b[k++]=a[j++];}}while(i<=mid)b[k++]=a[i++];while(j<=r)b[k++]=a[j++];for(int i=l; i<=r; i++)a[i]=b[i];}signed main(){read(n);for(int i=1; i<=n; i++)read(a[i]);solve(1,n);write(ans);putchar('\n');return 0;}

2.树状数组解法

有人要说了,你这文章写的太烂了好吧我承认我只是想讲这个方法,稍微铺垫一下...

那么树状数组跟逆序对有什么关系?

我们可以观察每一个元素,假设它有属性idx[i]表示在原数组中的位置,b[i]表示它的大小

我们把元素按照b[i]降序排序,然后从大到小取出这些元素并++f[idx[i]]

这样有什么性质呢?显然越小的数越晚标记

或者说,\(\sum\limits_{j=1}^{\text{idx[i]-1}}{f[j]}\)就是 \(i<j\)\(a_i>a_j\) 的个数

这个东西一看就是树状数组处理

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define MAXN (int)(5e5+5)template<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(T k){if(!k){putchar('0');return;}if(k<0){putchar('-');k=-k;}if(k>9)write(k/10);putchar(k%10+'0');}int n,ans;int a[MAXN],b[MAXN];struct BIT{int bit[MAXN];inline int lowbit(int x){return x&(-x);}void add(int x,int v){while(x<=n){bit[x]+=v;x+=lowbit(x);}}int sum(int x){int res=0;while(x>=1){res+=bit[x];x-=lowbit(x);}return res;}}bt[1];bool cmp(int x,int y){return b[x]==b[y]?x>y:b[x]>b[y];} // 相等的情况要注意signed main(){read(n);for(int i=1; i<=n; i++)read(b[i]),a[i]=i; // 记录下标sort(a+1,a+1+n,cmp);for(int i=1; i<=n; i++){bt[0].add(a[i],1);ans+=bt[0].sum(a[i]-1);}write(ans);putchar('\n');return 0;}

3.动态开点权值线段树解法

有点类似树状数组的解法,维护区间和

我们可以基于值域 \([1,1e9]\)建一棵线段树

显然不能把所有的值都建结点,不然就MLE了

怎么办呢?我们会发现,其实每次要查的其实就几个点

那么就可以使用动态开点的思想,开一棵残缺的线段树,只保留我们会用到的结点

其他的结点就是虚拟结点啦!根本不用管,查到的时候返回0就行了

这题用这个解法有点容易被卡(? 而且有点大材小用

因此写的权值线段树可能有点小区别(因为实在太简单了...)

代码如下

#include <bits/stdc++.h>using namespace std;//#define int long long#define MAXN (int)(1e7+5)#define ls(at) a[at].l#define rs(at) a[at].rtemplate<typename T>inline void read(T &k){char ch=getchar();T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(T k){if(!k){putchar('0');return;}if(k<0){putchar('-');k=-k;}if(k>9)write(k/10);putchar(k%10+'0');}struct node{int l,r,cnt;}a[MAXN];int n,rt,tot;long long ans;void push_up(int at){a[at].cnt=a[ls(at)].cnt+a[rs(at)].cnt;}void add(int l,int r,int x,int k,int &at){if(!at)at=++tot; // 没有就新建if(l==r){a[at].cnt+=k;return;} // 叶子结点int mid=(l+r)>>1;if(x<=mid)add(l,mid,x,k,ls(at));else add(mid+1,r,x,k,rs(at));push_up(at);}int query(int nl,int nr,int l,int r,int at){if(!at)return 0;if(nl<=l&&r<=nr)return a[at].cnt;int mid=(l+r)>>1,res=0;if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));return res;}signed main(){read(n);for(int i=1; i<=n; i++){int x;read(x);ans+=query(x+1,1e9,1,1e9,rt);add(1,1e9,x,1,rt);}write(ans);putchar('\n');return 0;}
]]>
@@ -8564,7 +8564,7 @@ /2021/09/05/qian-tan-shu-zhuang-shu-zu-qu-jian-xiu-gai-qu-jian-cha-xun/ - 浅谈树状数组 区间修改&区间查询

一、区间修改,单点查询

首先我们可以先来想一下,树状数组的区间修改,单点查询怎么弄

我们可以维护一个关于原数组的差分数组

很容易知道 $a_i=\sum\limits_{j=1}^{i}b_j$,其中 $b$ 为差分数组

那么区间修改时和差分的差不多,就是add(l,x),add(r+1,-x)表示 $[l,r]$ 加上 $x$

单点查询根据上面的结论,就是sum(i)

区间修改,单点查询的代码如下(注:这个不是LOJ132的代码 qwq)

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e5+5)template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m;int a[MAXN],ans[MAXN];inline int lowbit(R int x){return x&(-x);}void add(R int x,R int v){while(x<=n){ans[x]+=v;x+=lowbit(x);}}int sum(R int x){R int res=0;while(x>0){res+=ans[x];x-=lowbit(x);}return res;}signed main(){read(n);read(m);read(a[1]);add(1,a[1]);for(R int i=2; i<=n; i++)read(a[i]),add(i,a[i]-a[i-1]);while(m--){R int op,x,y,k;read(op);read(x);if(op==1){read(y);read(k);add(x,k);add(y+1,-k);}else printf("%lld\n",sum(x));}return 0;}

二、区间修改,区间查询

模板题LOJ132

那么区间修改,区间查询怎么搞呢?

我们先假设查询的区间为 $[1,r]$ ,因为 $[l,r]$ 的区间和就是 $[1,r]$ 的和减去 $[1,l]$ 的和

那么

那么我们只需要用两个树状数组分别维护左右两边的式子就行了

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e6+5)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(R T k){if(!k){putchar('0');return;}if(k<0)k=-k,putchar('-');if(k>9)write(k/10);putchar(k%10+'0');}int n,m;int a[MAXN];struct BIT{int bit1[MAXN],bit2[MAXN];int lowbit(R int x){return x&(-x);}void add(R int x,R int v){R int k=v*x;while(x<=n){bit1[x]+=v,bit2[x]+=k; // 维护的重点部分x+=lowbit(x);}}int sum(R int *b,R int x){R int res=0;while(x>=1){res+=b[x];x-=lowbit(x);}return res;}int qry(R int l,R int r){return sum(bit1,r)*(r+1)-sum(bit1,l-1)*(l)-(sum(bit2,r)-sum(bit2,l-1));}}bt[1]; // 这个[1]没什么用 qwqsigned main(){read(n);read(m);for(R int i=1; i<=n; i++)read(a[i]),bt[0].add(i,a[i]-a[i-1]);while(m--){R int op,l,r,x;read(op);read(l);read(r);if(op==1){read(x);bt[0].add(l,x),bt[0].add(r+1,-x);}if(op==2){printf("%lld\n",bt[0].qry(l,r));}}return 0;}
]]>
+ 浅谈树状数组区间修改&区间查询

一、区间修改,单点查询

首先我们可以先来想一下,树状数组的区间修改,单点查询怎么弄

我们可以维护一个关于原数组的差分数组

很容易知道 \(a_i=\sum\limits_{j=1}^{i}b_j\),其中 \(b\) 为差分数组

那么区间修改时和差分的差不多,就是add(l,x),add(r+1,-x)表示\([l,r]\) 加上 \(x\)

单点查询根据上面的结论,就是sum(i)

区间修改,单点查询的代码如下(注:这个不是LOJ132的代码 qwq)

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e5+5)template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m;int a[MAXN],ans[MAXN];inline int lowbit(R int x){return x&(-x);}void add(R int x,R int v){while(x<=n){ans[x]+=v;x+=lowbit(x);}}int sum(R int x){R int res=0;while(x>0){res+=ans[x];x-=lowbit(x);}return res;}signed main(){read(n);read(m);read(a[1]);add(1,a[1]);for(R int i=2; i<=n; i++)read(a[i]),add(i,a[i]-a[i-1]);while(m--){R int op,x,y,k;read(op);read(x);if(op==1){read(y);read(k);add(x,k);add(y+1,-k);}else printf("%lld\n",sum(x));}return 0;}

二、区间修改,区间查询

模板题LOJ132

那么区间修改,区间查询怎么搞呢?

我们先假设查询的区间为 \([1,r]\),因为 \([l,r]\) 的区间和就是 \([1,r]\) 的和减去 \([1,l]\) 的和

那么 \[\begin{aligned}\text{ans} &= \sum\limits_{i=1}^{r}a_i\\&= \sum\limits_{i=1}^{r}\sum\limits_{j=1}^{i}b_j\\&=\sum\limits_{i=1}^{r}{b_i \times (r-i+1)}\\&=\sum\limits_{i=1}^{r}{b_i\times (r+1)} -\sum\limits_{i=1}^{r}{b_i\times i}\end{aligned}\] 那么我们只需要用两个树状数组分别维护左右两边的式子就行了

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e6+5)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(R T k){if(!k){putchar('0');return;}if(k<0)k=-k,putchar('-');if(k>9)write(k/10);putchar(k%10+'0');}int n,m;int a[MAXN];struct BIT{int bit1[MAXN],bit2[MAXN];int lowbit(R int x){return x&(-x);}void add(R int x,R int v){R int k=v*x;while(x<=n){bit1[x]+=v,bit2[x]+=k; // 维护的重点部分x+=lowbit(x);}}int sum(R int *b,R int x){R int res=0;while(x>=1){res+=b[x];x-=lowbit(x);}return res;}int qry(R int l,R int r){return sum(bit1,r)*(r+1)-sum(bit1,l-1)*(l)-(sum(bit2,r)-sum(bit2,l-1));}}bt[1]; // 这个[1]没什么用 qwqsigned main(){read(n);read(m);for(R int i=1; i<=n; i++)read(a[i]),bt[0].add(i,a[i]-a[i-1]);while(m--){R int op,l,r,x;read(op);read(l);read(r);if(op==1){read(x);bt[0].add(l,x),bt[0].add(r+1,-x);}if(op==2){printf("%lld\n",bt[0].qry(l,r));}}return 0;}
]]>
@@ -8589,7 +8589,7 @@ /2021/08/31/uva11524-values-whose-sum-is-0-ti-jie/ - UVA11524 Values whose Sum is 0 题解

题目链接:UVA1152 4 Values whose Sum is 0

题意:给定四个数组,求 $a_i+b_j+c_k+d_l = 0$ 的个数,多组数据+奇怪的输出格式

解法一

$O(n^4)$ 枚举肯定过不去

想一想,我们有必要在枚举 $i,j$ 的时候再去枚举 $k,l$ 吗?

没有,因为 $k,l$ 的组合是有限的,只有这么多

我们可以先 $O(n^2)$ 把所有 $c_k+d_l$ 枚举出来,然后排个序,就可以二分找出所有的 $-(a_i+b_j)$ 了

时间复杂度 $O(n^2\log n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(4e3+5)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(R T k){if(k<0)putchar('-'),k=-k;if(!k){putchar('0');return;}if(k>9)write(k/10);putchar(k%10+'0');}int Q,n,ans;int a[MAXN],b[MAXN],c[MAXN],d[MAXN],t[MAXN*MAXN+MAXN];void solve(){ans=0;read(n);for(R int i=1; i<=n; i++){read(a[i]);read(b[i]);read(c[i]);read(d[i]);}for(R int i=1; i<=n; i++)for(R int j=1; j<=n; j++)t[(i-1)*n+j]=c[i]+d[j];sort(t+1,t+n*n+1);for(R int i=1; i<=n; i++)for(R int j=1; j<=n; j++){R int tmp=a[i]+b[j];ans+=upper_bound(t+1,t+n*n+1,-tmp)-lower_bound(t+1,t+n*n+1,-tmp);}write(ans);puts("");if(Q)puts("");}signed main(){read(Q);while(Q--)solve();return 0;}

解法二

通过刚才的分析,我们会惊奇的发现这个 $i,j$ 的组合其实是有限的!

那么我们就可以预处理所有的 $a_i+b_j$ 和 $c_k+d_l$

然后用哈希表搞一搞,每次去暴力查表(注:查有没有相反数)

时间复杂度 $O(n^2)$

真的有这么简单吗?

我们来重新分析一下时间复杂度

首先题目中说明给的数小于等于 $2^{28}$

那么每个组合(和)小于$2^{29}$

如果用哈希表搞,理论上来说可以构造hack数据,特别是简单的哈希函数很容易hack

为什么?因为如果哈希函数不是很好,那么至多在一个位置放了 $n^2$ 个数

好耶时间复杂度退化为 $O(n^4)$ 直接TLE

什么你要表内排序? $O(n^2\log n^2)$ + 可能较大常数 (来自本文作者的奇思妙想)

所以其实理论上来说这个解法是行不通的

如果数据生成器能够分析提交的代码原理并构造hack数据,那么直接玩完

好在没有那么恶心这题,数据比较简单,手写哈希表也能过

代码就不贴了,个人感觉实现很简单而且这解法不是很好 qwq

]]>
+ UVA11524 Values whose Sumis 0 题解

题目链接:UVA11524 Values whose Sum is 0

题意:给定四个数组,求 \(a_i+b_j+c_k+d_l = 0\)的个数,多组数据+奇怪的输出格式

解法一

\(O(n^4)\) 枚举肯定过不去

想一想,我们有必要在枚举 \(i,j\)的时候再去枚举 \(k,l\) 吗?

没有,因为 \(k,l\)的组合是有限的,只有这么多

我们可以先 \(O(n^2)\) 把所有 \(c_k+d_l\)枚举出来,然后排个序,就可以二分找出所有的 \(-(a_i+b_j)\) 了

时间复杂度 \(O(n^2\log n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(4e3+5)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}template<typename T>inline void write(R T k){if(k<0)putchar('-'),k=-k;if(!k){putchar('0');return;}if(k>9)write(k/10);putchar(k%10+'0');}int Q,n,ans;int a[MAXN],b[MAXN],c[MAXN],d[MAXN],t[MAXN*MAXN+MAXN];void solve(){ans=0;read(n);for(R int i=1; i<=n; i++){read(a[i]);read(b[i]);read(c[i]);read(d[i]);}for(R int i=1; i<=n; i++)for(R int j=1; j<=n; j++)t[(i-1)*n+j]=c[i]+d[j];sort(t+1,t+n*n+1);for(R int i=1; i<=n; i++)for(R int j=1; j<=n; j++){R int tmp=a[i]+b[j];ans+=upper_bound(t+1,t+n*n+1,-tmp)-lower_bound(t+1,t+n*n+1,-tmp);}write(ans);puts("");if(Q)puts("");}signed main(){read(Q);while(Q--)solve();return 0;}

解法二

通过刚才的分析,我们会惊奇的发现这个 \(i,j\) 的组合其实是有限的!

那么我们就可以预处理所有的 \(a_i+b_j\) 和 \(c_k+d_l\)

然后用哈希表搞一搞,每次去暴力查表(注:查有没有相反数)

时间复杂度 \(O(n^2)\)

真的有这么简单吗?

我们来重新分析一下时间复杂度

首先题目中说明给的数小于等于 \(2^{28}\)

那么每个组合(和)小于\(2^{29}\)

如果用哈希表搞,理论上来说可以构造hack数据,特别是简单的哈希函数很容易hack

为什么?因为如果哈希函数不是很好,那么至多在一个位置放了 \(n^2\) 个数

好耶时间复杂度退化为 \(O(n^4)\) 直接TLE

什么你要表内排序? \(O(n^2\logn^2)\) + 可能较大常数 (来自本文作者的奇思妙想)

所以其实理论上来说这个解法是行不通的

如果数据生成器能够分析提交的代码原理并构造hack数据,那么直接玩完

好在没有那么恶心这题,数据比较简单,手写哈希表也能过

代码就不贴了,个人感觉实现很简单而且这解法不是很好 qwq

]]>
@@ -8614,7 +8614,7 @@ /2021/08/29/luo-gu-p1985-usaco07open-fliptile-s-ti-jie/ - 洛谷P1985 [USACO07OPEN] Fliptile S 题解

题目链接:P1985 [USACO07OPEN] Fliptile S

题意:二维的开关问题,一次改变该格和四连通格

注:原题是 $(m,n)$ ,但本人不习惯这种表示,所以就用 $(n,m)$ 替代(即 $n$ 行 $m$ 列)!

首先看到题意马上想到搜索,发现状态数 $2^{nm}$ ,直接起飞

显然我们可以发现对于一个位置,翻转次数至多 $2$ 次,否则是没有意义的

且翻转的顺序不影响结果,这个性质类似于这题题解

不同于链接中那题,本题的四连通会有一定影响,不可直接确定

如果您没做过那题没有关系 qwq

我们考虑 $(1,1)$ ,如果它要翻转,那么一定会影响到 $(1,2)$ 和 $(2,1)$

这样我们又要考虑它右边的,又要考虑它下面的,很麻烦

于是提出设想,如果第一行已经确定了,那是不是可以推出第二行?

答案是可以的

那么我们就可以枚举第一行的翻转状态,然后再逐行推结果

时间复杂度 $O(nm2^m)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(25)#define INF 0x3f3f3f3f3f3f3f3fint n,m;template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int a[MAXN][MAXN]; // 原数组int f[MAXN][MAXN]; // 表示是否翻转该格子及其四连通格int dx[]={1,-1,0,0,0};int dy[]={0,0,1,-1,0};int ans[MAXN][MAXN],mn=INF; // ans[][]记录答案,mn记录最小操作数int get(R int x,R int y) // 获取格子的状态{R int res=a[x][y];for(R int i=0; i<5; i++) // 自己也要算上 qwq{R int tx=x+dx[i];R int ty=y+dy[i];if(tx>=1&&tx<=n&&ty>=1&&ty<=m)res+=f[tx][ty];}return res&1; // 显然}int proc(){R int res=0;for(R int i=2; i<=n; i++)for(R int j=1; j<=m; j++)if(get(i-1,j))f[i][j]=1;for(R int i=1; i<=n; i++)for(R int j=1; j<=m; j++)if(get(i,j))return -1145141919810; // 如果操作完还有没搞定的格子,无解,返回负无穷 qwqfor(R int i=1; i<=n; i++)for(R int j=1; j<=m; j++)res+=f[i][j]; // 统计答案return res;}signed main(){read(n);read(m);for(R int i=1; i<=n; i++)for(R int j=1; j<=m; j++)read(a[i][j]);for(R int i=0; i<(1<<m); i++) // 枚举第一行的状态{memset(f,0,sizeof(f)); // 别忘了清空for(R int j=0; j<m; j++)f[1][j+1]=(i>>j)&1;R int res=proc();if(mn>res&&res>=0){mn=res;memcpy(ans,f,sizeof(f));}}if(mn==INF)return puts("IMPOSSIBLE"),0;for(R int i=1; i<=n; i++)for(R int j=1; j<=m; j++)printf("%lld%c",ans[i][j]," \n"[j==m]);return 0;}
]]>
+ 洛谷P1985 [USACO07OPEN]Fliptile S 题解

题目链接:P1985[USACO07OPEN] Fliptile S

题意:二维的开关问题,一次改变该格和四连通格

注:原题是 \((m,n)\),但本人不习惯这种表示,所以就用 \((n,m)\) 替代(即 \(n\) 行 \(m\) 列)!

首先看到题意马上想到搜索,发现状态数 \(2^{nm}\) ,直接起飞

显然我们可以发现对于一个位置,翻转次数至多 \(2\) 次,否则是没有意义的

且翻转的顺序不影响结果,这个性质类似于这题,题解

不同于链接中那题,本题的四连通会有一定影响,不可直接确定

如果您没做过那题没有关系 qwq

我们考虑 \((1,1)\),如果它要翻转,那么一定会影响到 \((1,2)\) 和 \((2,1)\)

这样我们又要考虑它右边的,又要考虑它下面的,很麻烦

于是提出设想,如果第一行已经确定了,那是不是可以推出第二行?

答案是可以的

那么我们就可以枚举第一行的翻转状态,然后再逐行推结果

时间复杂度 \(O(nm2^m)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(25)#define INF 0x3f3f3f3f3f3f3f3fint n,m;template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int a[MAXN][MAXN]; // 原数组int f[MAXN][MAXN]; // 表示是否翻转该格子及其四连通格int dx[]={1,-1,0,0,0};int dy[]={0,0,1,-1,0};int ans[MAXN][MAXN],mn=INF; // ans[][]记录答案,mn记录最小操作数int get(R int x,R int y) // 获取格子的状态{R int res=a[x][y];for(R int i=0; i<5; i++) // 自己也要算上 qwq{R int tx=x+dx[i];R int ty=y+dy[i];if(tx>=1&&tx<=n&&ty>=1&&ty<=m)res+=f[tx][ty];}return res&1; // 显然}int proc(){R int res=0;for(R int i=2; i<=n; i++)for(R int j=1; j<=m; j++)if(get(i-1,j))f[i][j]=1;for(R int i=1; i<=n; i++)for(R int j=1; j<=m; j++)if(get(i,j))return -1145141919810; // 如果操作完还有没搞定的格子,无解,返回负无穷 qwqfor(R int i=1; i<=n; i++)for(R int j=1; j<=m; j++)res+=f[i][j]; // 统计答案return res;}signed main(){read(n);read(m);for(R int i=1; i<=n; i++)for(R int j=1; j<=m; j++)read(a[i][j]);for(R int i=0; i<(1<<m); i++) // 枚举第一行的状态{memset(f,0,sizeof(f)); // 别忘了清空for(R int j=0; j<m; j++)f[1][j+1]=(i>>j)&1;R int res=proc();if(mn>res&&res>=0){mn=res;memcpy(ans,f,sizeof(f));}}if(mn==INF)return puts("IMPOSSIBLE"),0;for(R int i=1; i<=n; i++)for(R int j=1; j<=m; j++)printf("%lld%c",ans[i][j]," \n"[j==m]);return 0;}
]]>
@@ -8639,7 +8639,7 @@ /2021/08/29/luo-gu-p2882-usaco07mar-face-the-right-way-g-ti-jie/ - 洛谷P2882 [USACO07MAR]Face The Right Way G 题解

题目链接:P2882 [USACO07MAR]Face The Right Way G

题意: 固定长度转向,求最小次数和相应的长度

(注:下文中的转向操作均以翻转一词替代 qwq)

首先可以想到搜索,状态数 $2^n$ ,直接起飞

那么我们来分析一下,可以看出对于一个位置,如果它被翻转了大于 $2$ 次,那这个一定不是必要的操作

也就是说,一个位置只可能被翻转至多 $2$ 次

而且通过观察样例,我们可以发现其实翻转的顺序和答案无关

很容易想到我们要枚举 $k$ (转向的区间长度),然后进行检查

怎么检查呢?我们要从左开始扫一遍,则至多翻转 $n-k+1$ 次,而每次翻转要 $k$ 个位置

时间复杂度$O(n^3)$ ,显然 $n\le 5000$ 是过不了的

考虑优化翻转操作

为了方便起见,我们可以把 B的位置记为 1,表示要翻转这个位置

记 $f[i]$ 表示 $[i,i+k-1]$ 是否被翻转

那么如果从左往右扫到一个位置 $i$ ,怎么判断这个位置要不要再翻转呢?

我们可以发现,如果 $\sum_{j=i-k+1}^{i-1}f[j]$ 为奇数则该点需要翻转(这个东西自己想一想就知道了

那么我们怎么把这个东西求出来呢?

我们看下一个位置 $i+1$ ,它要求的就是 $\sum_{j=i-k+2}^{i}f[j]$

我们来拆一下可以得到 $\sum_{j=i-k+2}^{i}f[j] = \sum_{j=i-k+1}^{i-1}f[j]+f[i-1]-f[i-k+1]$

有没有发现它可以从 $i$ 的位置转移过来?

那么我们就可以 $O(1)$ 求解这个东西了

来算一下时间复杂度

枚举 $k$ ,从左往右扫一遍,时间复杂度 $O(n^2)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e3+5)int n,m,k;int d[MAXN],f[MAXN];int check(R int K){memset(f,0,sizeof(f)); // 多测(枚举了k)不清空,爆零两行泪 qwqR int M=0,sum=0; // sum就是那个f[j]的和for(R int i=1; i+K-1<=n; i++){if((d[i]+sum)&1)f[i]=1,++M; // 要翻转sum+=f[i]; // 不要忘了加上if(i-K+1>=1)sum-=f[i-K+1]; // 数组不要越界了}for(R int i=n-K+2; i<=n; i++) // 检查一下翻的对不对,因为这里一段没法翻了{if((d[i]+sum)&1) return -1145141919810; // 有没翻成功的则无解,返回负无穷 qwqif(i-K+1>=1)sum-=f[i-K+1]; // 不要忘了减}return M;}signed main(){ios::sync_with_stdio(0); // 卡常cin >> n;for(R int i=1; i<=n; i++){char ch; cin >> ch;d[i]=ch=='B'?1:0; // 表示方向}m=n,k=1;for(R int i=1; i<=n; i++){int j=check(i); // 枚举k并算出mif(j>=0&&m>j)m=j,k=i;}cout << k << " " << m << endl;return 0;}
]]>
+ 洛谷P2882[USACO07MAR]Face The Right Way G 题解

题目链接:P2882[USACO07MAR]Face The Right Way G

题意: 固定长度转向,求最小次数和相应的长度

(注:下文中的转向操作均以翻转一词替代 qwq)

首先可以想到搜索,状态数 \(2^n\)直接起飞

那么我们来分析一下,可以看出对于一个位置,如果它被翻转了大于 \(2\) 次,那这个一定不是必要的操作

也就是说,一个位置只可能被翻转至多 \(2\) 次

而且通过观察样例,我们可以发现其实翻转的顺序和答案无关

很容易想到我们要枚举 \(k\)(转向的区间长度),然后进行检查

怎么检查呢?我们要从左开始扫一遍,则至多翻转 \(n-k+1\) 次,而每次翻转要 \(k\) 个位置

时间复杂度\(O(n^3)\) ,显然 \(n\le 5000\) 是过不了的

考虑优化翻转操作

为了方便起见,我们可以把 B的位置记为1,表示要翻转这个位置

\(f[i]\) 表示 \([i,i+k-1]\) 是否被翻转

那么如果从左往右扫到一个位置 \(i\),怎么判断这个位置要不要再翻转呢?

我们可以发现,如果 \(\sum_{j=i-k+1}^{i-1}f[j]\)为奇数则该点需要翻转(这个东西自己想一想就知道了

那么我们怎么把这个东西求出来呢?

我们看下一个位置 \(i+1\),它要求的就是 \(\sum_{j=i-k+2}^{i}f[j]\)

我们来拆一下可以得到 \(\sum_{j=i-k+2}^{i}f[j] =\sum_{j=i-k+1}^{i-1}f[j]+f[i-1]-f[i-k+1]\)

有没有发现它可以从 \(i\)的位置转移过来?

那么我们就可以 \(O(1)\)求解这个东西了

来算一下时间复杂度

枚举 \(k\),从左往右扫一遍,时间复杂度 \(O(n^2)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e3+5)int n,m,k;int d[MAXN],f[MAXN];int check(R int K){memset(f,0,sizeof(f)); // 多测(枚举了k)不清空,爆零两行泪 qwqR int M=0,sum=0; // sum就是那个f[j]的和for(R int i=1; i+K-1<=n; i++){if((d[i]+sum)&1)f[i]=1,++M; // 要翻转sum+=f[i]; // 不要忘了加上if(i-K+1>=1)sum-=f[i-K+1]; // 数组不要越界了}for(R int i=n-K+2; i<=n; i++) // 检查一下翻的对不对,因为这里一段没法翻了{if((d[i]+sum)&1) return -1145141919810; // 有没翻成功的则无解,返回负无穷 qwqif(i-K+1>=1)sum-=f[i-K+1]; // 不要忘了减}return M;}signed main(){ios::sync_with_stdio(0); // 卡常cin >> n;for(R int i=1; i<=n; i++){char ch; cin >> ch;d[i]=ch=='B'?1:0; // 表示方向}m=n,k=1;for(R int i=1; i<=n; i++){int j=check(i); // 枚举k并算出mif(j>=0&&m>j)m=j,k=i;}cout << k << " " << m << endl;return 0;}
]]>
@@ -8664,7 +8664,7 @@ /2021/08/28/luo-gu-p4085-usaco17dec-haybale-feast-g-ti-jie/ - 洛谷P4085 [USACO17DEC]Haybale Feast G 题解

题目链接:P4085 [USACO17DEC]Haybale Feast G

题意:给定 $2$ 个由 $N$ 个数字组成的数列 $F,S$ ,需要找到使得 $\sum_{k=i}^{j}F_k\ge M$ 的 $i,j$ 并输出在所有满足条件的 $i,j$ 中,$\max(S_i,S_{i+1},…S_{j-1},S_{j}$)的最小值,数列里每个数非负

解法一

由非负这个条件我们可以得到一个重要结论

对于区间 $A,B$ ,若 $A \subsetneqq B$ ,则 $A$ 的最大值小于等于 $B$ 的最大值

因此具有单调性

那么我们可以二分答案解决此题

每次判断就 $O(n)$ 扫一遍看看有没有满足条件的区间即可

时间复杂度 $O(n\log \max \{S_i\})$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e5+5)int n,k,inf;int a[MAXN],b[MAXN];bool check(R int mx){R int res=0;for(R int i=1; i<=n; i++){if(b[i]>mx)res=0; // 该区间不合法else res+=a[i]; // 否则加上if(res>=k)return 1; // 有答案}return 0;}void proc(R int l,R int r){while(l<=r){R int mid=(l+r)>>1;if(check(mid))r=mid-1;else l=mid+1;}printf("%lld\n",l);}signed main(){scanf("%lld%lld",&n,&k);for(R int i=1; i<=n; i++){scanf("%lld%lld",&a[i],&b[i]);inf=max(inf,b[i]);}proc(0,inf);return 0;}

解法二

显然第一维限制我们可以用尺取法搞定

因为我们要找到的 $i,j$ 一定是尽可能短的区间

那么第二维限制我们可以用单调队列搞定

为什么?因为尺取法的端点是单调不下降的

时间复杂度 $O(n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e5+5)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int n,k,mn=INF;int a[MAXN],b[MAXN];deque<int> q; // 我懒 qwqvoid push(R int idx){while(!q.empty()&&b[q.back()]<=b[idx])q.pop_back();q.push_back(idx);}void pop(R int idx){while(!q.empty()&&q.front()<=idx)q.pop_front();}void proc(R int l,R int r){R int now=0,mn=INF;while(1){while(now<k&&r<n){now+=a[++r];push(r); // 拓展右端点时往单调队列里塞下标}if(now>=k)mn=min(mn,b[q.front()]);else break;pop(l); // 收缩左端点时弹单调队列队头now-=a[l++];}printf("%lld\n",mn);}signed main(){read(n);read(k);for(R int i=1; i<=n; i++)read(a[i]),read(b[i]);proc(1,0);return 0;}
]]>
+ 洛谷P4085[USACO17DEC]Haybale Feast G 题解

题目链接:P4085[USACO17DEC]Haybale Feast G

题意:给定 \(2\)个由 \(N\) 个数字组成的数列 \(F,S\) ,需要找到使得 \(\sum_{k=i}^{j}F_k\ge M\) 的 \(i,j\) 并输出在所有满足条件的 \(i,j\) 中,\(\max(S_i,S_{i+1},...S_{j-1},S_{j}\))的最小值,数列里每个数非负

解法一

由非负这个条件我们可以得到一个重要结论

对于区间 \(A,B\) ,若 \(A \subsetneqq B\) ,则 \(A\) 的最大值小于等于 \(B\) 的最大值

因此具有单调性

那么我们可以二分答案解决此题

每次判断就 \(O(n)\)扫一遍看看有没有满足条件的区间即可

时间复杂度 \(O(n\log \max\{S_i\})\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e5+5)int n,k,inf;int a[MAXN],b[MAXN];bool check(R int mx){R int res=0;for(R int i=1; i<=n; i++){if(b[i]>mx)res=0; // 该区间不合法else res+=a[i]; // 否则加上if(res>=k)return 1; // 有答案}return 0;}void proc(R int l,R int r){while(l<=r){R int mid=(l+r)>>1;if(check(mid))r=mid-1;else l=mid+1;}printf("%lld\n",l);}signed main(){scanf("%lld%lld",&n,&k);for(R int i=1; i<=n; i++){scanf("%lld%lld",&a[i],&b[i]);inf=max(inf,b[i]);}proc(0,inf);return 0;}

解法二

显然第一维限制我们可以用尺取法搞定

因为我们要找到的 \(i,j\)一定是尽可能短的区间

那么第二维限制我们可以用单调队列搞定

为什么?因为尺取法的端点是单调不下降的

时间复杂度 \(O(n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e5+5)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int n,k,mn=INF;int a[MAXN],b[MAXN];deque<int> q; // 我懒 qwqvoid push(R int idx){while(!q.empty()&&b[q.back()]<=b[idx])q.pop_back();q.push_back(idx);}void pop(R int idx){while(!q.empty()&&q.front()<=idx)q.pop_front();}void proc(R int l,R int r){R int now=0,mn=INF;while(1){while(now<k&&r<n){now+=a[++r];push(r); // 拓展右端点时往单调队列里塞下标}if(now>=k)mn=min(mn,b[q.front()]);else break;pop(l); // 收缩左端点时弹单调队列队头now-=a[l++];}printf("%lld\n",mn);}signed main(){read(n);read(k);for(R int i=1; i<=n; i++)read(a[i]),read(b[i]);proc(1,0);return 0;}
]]>
@@ -8691,7 +8691,7 @@ /2021/08/28/vijos1659-he-xie-wang-guo-ti-jie/ - Vijos1659 河蟹王国 题解

题目链接:Vijos1659 河蟹王国

题意:维护一个数据结构,支持区间最大值查询、区间加操作

一看就线段树水题

我们在建树时将最大值搞好查询就好了

那么区间加怎么办?

显然区间加操作会将影响到的最大值增加同一个值,而改变后的最大值在上传时进行更新即可

等于就把线段树板子搬过来改一改就过了… 因为基本原理差不多

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(1e5+5)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int n,m;int a[MAXN];int mx[MAXN<<2],tag[MAXN<<2];inline int ls(R int x){return x<<1;}inline int rs(R int x){return x<<1|1;}inline void proc(R int l,R int r,R int at){R int u=at>>1;mx[at]+=tag[u]; // 最大值更新tag[at]+=tag[u];}inline void push_up(R int at){mx[at]=max(mx[ls(at)],mx[rs(at)]);} // 上传inline void push_down(R int l,R int r,R int at){R int mid=(l+r)>>1;proc(l,mid,ls(at));proc(mid+1,r,rs(at));tag[at]=0;}void build(R int l,R int r,R int at){if(l==r){mx[at]=a[l];return;}R int mid=(l+r)>>1;build(l,mid,ls(at));build(mid+1,r,rs(at));push_up(at);}void update(R int nl,R int nr,R int l,R int r,R int k,R int at){if(nl<=l&&r<=nr){mx[at]+=k;tag[at]+=k;return;}push_down(l,r,at);R int mid=(l+r)>>1;if(nl<=mid)update(nl,nr,l,mid,k,ls(at));if(nr>mid)update(nl,nr,mid+1,r,k,rs(at));push_up(at);}int query(R int nl,R int nr,R int l,R int r,R int at){if(nl<=l&&r<=nr)return mx[at];R int res=-INF;R int mid=(l+r)>>1;push_down(l,r,at);if(nl<=mid)res=max(res,query(nl,nr,l,mid,ls(at)));if(nr>mid)res=max(res,query(nl,nr,mid+1,r,rs(at)));return res;}signed main(){read(n);for(R int i=1; i<=n; i++)read(a[i]);build(1,n,1);read(m);while(m--){R int op,l,r,k;read(op);read(l);read(r);if(op==1){read(k);update(l,r,1,n,k,1);}if(op==2){printf("%lld\n",query(l,r,1,n,1));}}return 0;}
]]>
+ Vijos1659 河蟹王国 题解

题目链接:Vijos1659河蟹王国

题意:维护一个数据结构,支持区间最大值查询、区间加操作

一看就线段树水题

我们在建树时将最大值搞好查询就好了

那么区间加怎么办?

显然区间加操作会将影响到的最大值增加同一个值,而改变后的最大值在上传时进行更新即可

等于就把线段树板子搬过来改一改就过了... 因为基本原理差不多

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define INF 0x3f3f3f3f3f3f3f3f#define MAXN (int)(1e5+5)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int n,m;int a[MAXN];int mx[MAXN<<2],tag[MAXN<<2];inline int ls(R int x){return x<<1;}inline int rs(R int x){return x<<1|1;}inline void proc(R int l,R int r,R int at){R int u=at>>1;mx[at]+=tag[u]; // 最大值更新tag[at]+=tag[u];}inline void push_up(R int at){mx[at]=max(mx[ls(at)],mx[rs(at)]);} // 上传inline void push_down(R int l,R int r,R int at){R int mid=(l+r)>>1;proc(l,mid,ls(at));proc(mid+1,r,rs(at));tag[at]=0;}void build(R int l,R int r,R int at){if(l==r){mx[at]=a[l];return;}R int mid=(l+r)>>1;build(l,mid,ls(at));build(mid+1,r,rs(at));push_up(at);}void update(R int nl,R int nr,R int l,R int r,R int k,R int at){if(nl<=l&&r<=nr){mx[at]+=k;tag[at]+=k;return;}push_down(l,r,at);R int mid=(l+r)>>1;if(nl<=mid)update(nl,nr,l,mid,k,ls(at));if(nr>mid)update(nl,nr,mid+1,r,k,rs(at));push_up(at);}int query(R int nl,R int nr,R int l,R int r,R int at){if(nl<=l&&r<=nr)return mx[at];R int res=-INF;R int mid=(l+r)>>1;push_down(l,r,at);if(nl<=mid)res=max(res,query(nl,nr,l,mid,ls(at)));if(nr>mid)res=max(res,query(nl,nr,mid+1,r,rs(at)));return res;}signed main(){read(n);for(R int i=1; i<=n; i++)read(a[i]);build(1,n,1);read(m);while(m--){R int op,l,r,k;read(op);read(l);read(r);if(op==1){read(k);update(l,r,1,n,k,1);}if(op==2){printf("%lld\n",query(l,r,1,n,1));}}return 0;}
]]>
@@ -8716,7 +8716,7 @@ /2021/08/28/uva1121-subsequence-ti-jie/ - UVA1121 Subsequence 题解

题目链接:UVA1121 Subsequence

题意:给定数组,找最短连续子序列使其和大于 $S$ ,多组数据

解法一

对于区间 $[l,r]$ ,若 $\sum_{i=l}^{r}a[i]\ge S$ ,那么 $\forall r’>r (r’ \le n)$ 该条件均成立

那么我们只要枚举左端点,二分查找满足条件的最左的右端点,然后找个最小值就行了

区间和用前缀和优化即可

时间复杂度 $O(n\log n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e5+5)#define INF 0x3f3f3f3f3f3f3f3fint n,k;int sum[MAXN];void proc(){R int mn=INF;for(R int l=1; sum[l]+k<=sum[n]; l++){R int r=lower_bound(sum+l,sum+n,sum[l]+k)-sum-1;mn=min(mn,r-l+1);}printf("%lld\n",mn==INF?0:mn);}bool Input(){if(!(~scanf("%lld%lld",&n,&k)))return 0;for(R int i=1,t; i<=n; i++){scanf("%lld",&t);sum[i]=sum[i-1]+t;}return 1;}signed main(){while(Input())proc();return 0;}

解法二

本解法就是尺取法

根据上面的结论,我们再想一想,有没有必要枚举左端点呢?

对于每个左端点,当且仅当右端点在尽可能左的位置时对答案有贡献

或者说对答案有贡献的每个左端点所对应的右端点是唯一确定

显然右端点也是单调不降

我们可以设 $l$ 表示当前左端点的位置, $r$ 不断自增 $1$ 直到 $[l,r]$ 的和大于等于 $S$

这时 $l,r$ 一定是对答案有贡献的一组,和最小值取个 $\min$

那么怎么继续呢?

我们把左端点 $l$ 不断自增 $1$ 直到此时 $[l,r]$ 的和小于 $S$ ,然后再继续拓展右端点

不断重复以上过程直到右端点无法拓展且左端点无解时结束

由于每个点只遍历至多 $2$ 次,因此

时间复杂度 $O(n)$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e5+5)#define INF 0x3f3f3f3f3f3f3f3fint n,k;int a[MAXN];void proc(R int l,R int r){R int now=0,mn=INF;while(1){while(now<k&&r<n)now+=a[++r]; // 拓展右端点if(now>=k)mn=min(mn,r-l+1);else break; // 无解退出now-=a[l++]; // 收缩左端点}printf("%lld\n",mn==INF?0:mn);}bool Input(){if(!(~scanf("%lld%lld",&n,&k)))return 0;for(R int i=1; i<=n; i++)scanf("%lld",&a[i]);return 1;}signed main(){while(Input())proc(1,0); // 个人传参习惯 qwqreturn 0;}
]]>
+ UVA1121 Subsequence 题解

题目链接:UVA1121Subsequence

题意:给定数组,找最短连续子序列使其和大于 \(S\) ,多组数据

解法一

对于区间 \([l,r]\) ,若 \(\sum_{i=l}^{r}a[i]\ge S\) ,那么 \(\forall r'>r (r' \le n)\)该条件均成立

那么我们只要枚举左端点,二分查找满足条件的最左的右端点,然后找个最小值就行了

区间和用前缀和优化即可

时间复杂度 \(O(n\log n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e5+5)#define INF 0x3f3f3f3f3f3f3f3fint n,k;int sum[MAXN];void proc(){R int mn=INF;for(R int l=1; sum[l]+k<=sum[n]; l++){R int r=lower_bound(sum+l,sum+n,sum[l]+k)-sum-1;mn=min(mn,r-l+1);}printf("%lld\n",mn==INF?0:mn);}bool Input(){if(!(~scanf("%lld%lld",&n,&k)))return 0;for(R int i=1,t; i<=n; i++){scanf("%lld",&t);sum[i]=sum[i-1]+t;}return 1;}signed main(){while(Input())proc();return 0;}

解法二

本解法就是尺取法

根据上面的结论,我们再想一想,有没有必要枚举左端点呢?

对于每个左端点,当且仅当右端点在尽可能左的位置时对答案有贡献

或者说对答案有贡献的每个左端点所对应的右端点是唯一确定

显然右端点也是单调不降

我们可以设 \(l\)表示当前左端点的位置, \(r\) 不断自增\(1\) 直到 \([l,r]\) 的和大于等于 \(S\)

这时 \(l,r\)一定是对答案有贡献的一组,和最小值取个 \(\min\)

那么怎么继续呢?

我们把左端点 \(l\) 不断自增 \(1\) 直到此时 \([l,r]\) 的和小于 \(S\) ,然后再继续拓展右端点

不断重复以上过程直到右端点无法拓展且左端点无解时结束

由于每个点只遍历至多 \(2\)次,因此

时间复杂度 \(O(n)\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e5+5)#define INF 0x3f3f3f3f3f3f3f3fint n,k;int a[MAXN];void proc(R int l,R int r){R int now=0,mn=INF;while(1){while(now<k&&r<n)now+=a[++r]; // 拓展右端点if(now>=k)mn=min(mn,r-l+1);else break; // 无解退出now-=a[l++]; // 收缩左端点}printf("%lld\n",mn==INF?0:mn);}bool Input(){if(!(~scanf("%lld%lld",&n,&k)))return 0;for(R int i=1; i<=n; i++)scanf("%lld",&a[i]);return 1;}signed main(){while(Input())proc(1,0); // 个人传参习惯 qwqreturn 0;}
]]>
@@ -8741,7 +8741,7 @@ /2021/08/28/ubuntu-wu-fa-lian-jie-zhi-ibus-jie-jue-fang-fa/ - ubuntu 无法连接至ibus 解决方法

在终端中输入以下指令

ibus-daemon -r -d -x

大概的意思就是把原来的进程杀掉然后启动ibus

然后ibus它就回来了

这篇文章好短啊(

]]>
+ ubuntu 无法连接至ibus解决方法

在终端中输入以下指令

ibus-daemon -r -d -x

大概的意思就是把原来的进程杀掉然后启动ibus

然后ibus它就回来了

这篇文章好短啊(

]]>
@@ -8766,7 +8766,7 @@ /2021/08/28/luo-gu-p1006-noip2008-ti-gao-zu-chuan-zhi-tiao-ti-jie/ - 洛谷P1006 [NOIP2008 提高组] 传纸条

题目链接:P1006 [NOIP2008 提高组] 传纸条

题意:网格图, $(1,1)$ 到 $(n,m)$ 找两条不重合的路径,最大价值
注:原题是 $(m,n)$ ,但我不习惯,所以就用 $(n,m)$ 替代(即 $n$ 行 $m$ 列)!

解法一

最烂解法 ,但是能通过此题

设 $dp[i][j][k][l]$ 表示第一条路径到 $(i,j)$ 的位置,第二条路径到 $(k,l)$ 的位置,能获得的最大价值

由于两条路径正着走反着走都一样,所以我们可以看作从 $(1,1)$ 到 $(n,m)$ 的两条不重合的路径

那么显然

$dp[i][j][k][l] = \\ \max\{dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]\}+a[i][j]+a[k][l]$

因为不重复,所以当 $i=k,j=l$ 时要减去 $a[i][j]$

时间复杂度 $O(n^4)$

空间复杂度 $O(n^4)$

伪代码如下(懒的写了…)

for i=1 to n:for j=1 to m:for k=1 to n:for l=1 to m:dp[i][j][k][l]=max{dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]}+a[i][j]+!((i==k&&j==l))*a[k][l];

答案就是 $dp[n][m][n][m]$

呕~这代码够烂的

解法二

我们观察每个路径,由于它一定是一个斜线方向的(即只能右移下移)

我们知道一个点的纵坐标,那么它的横坐标就是横纵坐标之和减去纵坐标

而两条路径的长度一定是相等的

那么就不用记录点的坐标了

设 $dp[k][i][j]$ 表示横纵坐标和为 $k$ ,第一条路径终点纵坐标为 $i$ ,第二条路径终点纵坐标为 $j$ 时能获得的最大价值

那么显然

$dp[k][i][j]=\\\max\{dp[k-1][i-1][j],dp[k-1][i][j-1],dp[k-1][i][j],dp[k-1][i-1][j-1]\}+a[k-i][i]+a[k-j][j]$

我们发现 $k$ 和 $k-1$ 的答案有关

那么我们可以弄个滚动数组,注意顺序即可通过此题

时间复杂度 $O(n^3)$

空间复杂度 $O(n^2)$

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define max4(a,b,c,d) max(max((a),(b)),max((c),(d)))template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int n,m;int a[55][55];int dp[55][55];signed main(){read(n);read(m);for(R int i=1; i<=n; i++)for(R int j=1; j<=m; j++)read(a[i][j]);for(R int k=3; k<=m+n-1; k++)for(R int i=min(k-2,m-1); i>=1&&k-i<=n; i--) // 注意不要越界了for(R int j=min(k-1,m); j>i&&k-j<=n; j--) // 两条路径不重合dp[i][j]=max4(dp[i-1][j],dp[i][j-1],dp[i][j],dp[i-1][j-1])+a[k-i][i]+a[k-j][j];printf("%lld\n",dp[m-1][m]); // 两条路径不重合return 0;}
]]>
+ 洛谷P1006 [NOIP2008 提高组]传纸条

题目链接:P1006[NOIP2008 提高组] 传纸条

题意:网格图, \((1,1)\) 到 \((n,m)\) 找两条不重合的路径,最大价值注:原题是 \((m,n)\),但我不习惯,所以就用 \((n,m)\)替代(即 \(n\)\(m\) 列)!

解法一

最烂解法 ,但是能通过此题

\(dp[i][j][k][l]\)表示第一条路径到 \((i,j)\)的位置,第二条路径到 \((k,l)\)的位置,能获得的最大价值

由于两条路径正着走反着走都一样,所以我们可以看作从 \((1,1)\) 到 \((n,m)\) 的两条不重合的路径

那么显然

\(dp[i][j][k][l] = \\\max\{dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]\}+a[i][j]+a[k][l]\)

因为不重复,所以当 \(i=k,j=l\)时要减去 \(a[i][j]\)

时间复杂度 \(O(n^4)\)

空间复杂度 \(O(n^4)\)

伪代码如下(懒的写了...)

for i=1 to n:for j=1 to m:for k=1 to n:for l=1 to m:dp[i][j][k][l]=max{dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]}+a[i][j]+!((i==k&&j==l))*a[k][l];

答案就是 \(dp[n][m][n][m]\)

呕~这代码够烂的

解法二

我们观察每个路径,由于它一定是一个斜线方向的(即只能右移下移)

我们知道一个点的纵坐标,那么它的横坐标就是横纵坐标之和减去纵坐标

而两条路径的长度一定是相等的

那么就不用记录点的坐标了

\(dp[k][i][j]\) 表示横纵坐标和为\(k\) ,第一条路径终点纵坐标为 \(i\) ,第二条路径终点纵坐标为 \(j\) 时能获得的最大价值

那么显然

\(dp[k][i][j]=\\\max\{dp[k-1][i-1][j],dp[k-1][i][j-1],dp[k-1][i][j],dp[k-1][i-1][j-1]\}+a[k-i][i]+a[k-j][j]\)

我们发现 \(k\)\(k-1\) 的答案有关

那么我们可以弄个滚动数组,注意顺序即可通过此题

时间复杂度 \(O(n^3)\)

空间复杂度 \(O(n^2)\)

代码:

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define max4(a,b,c,d) max(max((a),(b)),max((c),(d)))template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int n,m;int a[55][55];int dp[55][55];signed main(){read(n);read(m);for(R int i=1; i<=n; i++)for(R int j=1; j<=m; j++)read(a[i][j]);for(R int k=3; k<=m+n-1; k++)for(R int i=min(k-2,m-1); i>=1&&k-i<=n; i--) // 注意不要越界了for(R int j=min(k-1,m); j>i&&k-j<=n; j--) // 两条路径不重合dp[i][j]=max4(dp[i-1][j],dp[i][j-1],dp[i][j],dp[i-1][j-1])+a[k-i][i]+a[k-j][j];printf("%lld\n",dp[m-1][m]); // 两条路径不重合return 0;}
]]>
@@ -8793,7 +8793,7 @@ /2021/08/27/uva10006-carmichael-numbers-ti-jie/ - UVA10006 Carmichael Numbers

题目链接:UVA10006 Carmichael Numbers

题意:若 $\forall x (1<x<n) ,x^n\equiv x \mod n$ ,且 $n$ 为合数,则称 $n$ 为Carmichael Numbers,多组数据判断 $n$ 是否是这种数

数据范围 $2<n<65000$

合数好处理,直接 $O(\sqrt{n})$ 暴力判断即可

那么 $x^n$ 怎么处理呢?

如果我们去一个一个乘,时间复杂度 $O(n^2)$ ,直接TLE

因此,我们要用到快速幂

对于 $x^k$ ,根据整数的唯一分解定理和常识,$k$ 可以写成唯一的二进制形式

则 $x^k$ 可以被分解为它二进制位上每一个 $1$ 的幂的乘积

例如: $x^{(5)_{10}} = x^{(101)_2} = x^{(100)_2} \times x^{(1)_2} = x^{(4)_{10}} \times x^{(1)_{10}}$

那么我们只需要把 $k$ 不断右移一位,看它最后一位是否为 $1$ ,是就乘上对应的数即可

因此我们就可以在 $O(n\log{n})$的时间搞定这题了

代码如下:

#include <bits/stdc++.h>using namespace std;#define int long long#define R registerint n;bool prime(R int x){if(x<2)return 0;for(R int i=2; i*i<=x; i++)if(!(x%i))return 0;return 1;}int qpow(R int a,R int b){R int ans=1,base=a%n;while(b){if(b&1)ans=ans*base%n;base=base*base%n; // 要取模,不然中间就溢出了b>>=1;}return ans;}void proc(){if(prime(n)){printf("%lld is normal.\n",n);return;}for(R int x=2; x<n; x++)if(qpow(x,n)%n!=x){printf("%lld is normal.\n",n);return;}printf("The number %lld is a Carmichael number.\n",n);}signed main(){while(~scanf("%lld",&n)&&n)proc();return 0;}
]]>
+ UVA10006 Carmichael Numbers

题目链接:UVA10006 CarmichaelNumbers

题意:若 \(\forall x(1<x<n) ,x^n\equiv x \mod n\) ,且 \(n\) 为合数,则称 \(n\)为Carmichael Numbers,多组数据判断 \(n\) 是否是这种数

数据范围 \(2<n<65000\)

合数好处理,直接 \(O(\sqrt{n})\)暴力判断即可

那么 \(x^n\) 怎么处理呢?

如果我们去一个一个乘,时间复杂度 \(O(n^2)\) ,直接TLE

因此,我们要用到快速幂

对于 \(x^k\),根据整数的唯一分解定理和常识,\(k\)可以写成唯一的二进制形式

\(x^k\)可以被分解为它二进制位上每一个 \(1\)的幂的乘积

例如: \(x^{(5)_{10}} = x^{(101)_2} =x^{(100)_2} \times x^{(1)_2} = x^{(4)_{10}} \timesx^{(1)_{10}}\)

那么我们只需要把 \(k\)不断右移一位,看它最后一位是否为 \(1\),是就乘上对应的数即可

因此我们就可以在 \(O(n\log{n})\)的时间搞定这题了

代码如下:

#include <bits/stdc++.h>using namespace std;#define int long long#define R registerint n;bool prime(R int x){if(x<2)return 0;for(R int i=2; i*i<=x; i++)if(!(x%i))return 0;return 1;}int qpow(R int a,R int b){R int ans=1,base=a%n;while(b){if(b&1)ans=ans*base%n;base=base*base%n; // 要取模,不然中间就溢出了b>>=1;}return ans;}void proc(){if(prime(n)){printf("%lld is normal.\n",n);return;}for(R int x=2; x<n; x++)if(qpow(x,n)%n!=x){printf("%lld is normal.\n",n);return;}printf("The number %lld is a Carmichael number.\n",n);}signed main(){while(~scanf("%lld",&n)&&n)proc();return 0;}
]]>
@@ -8820,7 +8820,7 @@ /2021/08/27/shu-lun-ti-xia-zuo-1/ - 数论题瞎做[1]

某个学MO的朋友给我看的题

这题似乎是1994年国家数学集训队选拔考试D1T1

怪不得我做了好久(

upd.20220513 害,他半途而废退役了


题面

求四个所有的由四个自然数 $a,b,c,d$ 组成的数组,使数组中任意三个数的乘积除以剩下的一个数余数为 $1$

题解

(注:我写的可能有点不太正式,毕竟我只是个OIer)

由题意可得

$abc \equiv 1 \mod d$

$abd \equiv 1 \mod c$

$acd \equiv 1 \mod b$

$bcd \equiv 1 \mod a$

可化为

$d\mid (abc-1)$

$c\mid (abd-1)$

$b\mid (acd-1)$

$a\mid (bcd-1)$

$\therefore ab\mid (acd-1)(bcd-1) \Rightarrow ab\mid (abc^2d^2-acd-bcd+1)$

$\therefore ab\mid (acd+bcd-1)$

同理 $cd\mid (abc+abd-1)$

$\therefore abcd\mid (a^2bc^2d+ab^2c^2d+a^2bc^2d+ab^2cd^2-abc-abd-acd-bcd+1)$

$\therefore abcd\mid (abc+abd+acd+bcd-1)$

设 $t=\dfrac{abc+abd+acd+bcd-1}{abcd}$

$\therefore t=\dfrac{1}{a}+\dfrac{1}{b}+\dfrac{1}{c}+\dfrac{1}{d}-\dfrac{1}{abcd}$

显然 $a,b,c,d$ 两两互质,且 $a \ge 2$

由于不考虑顺序,则假设 $2\le a < b < c < d$

$\therefore t< \dfrac{4}{a} \le 2$

$\because t\in \mathbb{Z}^+$

$\therefore a=2,3,t=1$

当 $a=3$ 时,$t_{max} = \dfrac{1}{3}+\dfrac{1}{4}+\dfrac{1}{5}+\dfrac{1}{6}-\dfrac{1}{360} = \dfrac{341}{360}<1$$\quad\therefore$舍去

当 $a=2$ 时,$t_{max} = \dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{5}+\dfrac{1}{7}-\dfrac{1}{210} = \dfrac{246}{210}>1$

$\therefore a =2$

$\therefore \dfrac{1}{2}<\dfrac{3}{b}$

$\therefore b=3,5$

当 $b=5$ 时,$t_{max} = \dfrac{1}{2}+\dfrac{1}{5}+\dfrac{1}{7}+\dfrac{1}{9}-\dfrac{1}{630} = \dfrac{600}{630}<1$$\quad\therefore$舍去

当 $b=3$ 时,$t_{max} = \dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{5}+\dfrac{1}{7}-\dfrac{1}{210} = \dfrac{246}{210}>1$

$\therefore b=3$

$\therefore \dfrac{1}{6} < \dfrac{2}{c}$

$\therefore c=7,11$

当 $c=7$ 时,$t_{max} = \dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{7}+\dfrac{1}{11}-\dfrac{1}{462} = \dfrac{492}{462}>1$

当 $c=11$ 时,$t_{max} = \dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{11}+\dfrac{1}{13}-\dfrac{1}{858} = \dfrac{858}{858}=1$

显然当 $c=11$ 时 $d=13$

则一组解为 $2,3,11,13$

当 $c=7$ 时

$\dfrac{1}{42}<\dfrac{1}{d}$

$\therefore d=41$

则另一组解为 $2,3,7,41$

综上所述,答案为 $2,3,11,13$ 或 $2,3,7,41$


为了验算结果我直接敲了个 $O(n^4)$ 的暴力(傻)

]]>
+ 数论题瞎做[1]

某个学MO的朋友给我看的题

这题似乎是1994年国家数学集训队选拔考试D1T1

怪不得我做了好久(

upd.20220513 害,他半途而废退役了


题面

求四个所有的由四个自然数 \(a,b,c,d\)组成的数组,使数组中任意三个数的乘积除以剩下的一个数余数为 \(1\)

题解

(注:我写的可能有点不太正式,毕竟我只是个OIer)

由题意可得

\(abc \equiv 1 \mod d\)

\(abd \equiv 1 \mod c\)

\(acd \equiv 1 \mod b\)

\(bcd \equiv 1 \mod a\)

可化为

\(d\mid (abc-1)\)

\(c\mid (abd-1)\)

\(b\mid (acd-1)\)

\(a\mid (bcd-1)\)

\(\therefore ab\mid (acd-1)(bcd-1)\Rightarrow ab\mid (abc^2d^2-acd-bcd+1)\)

\(\therefore ab\mid(acd+bcd-1)\)

同理 \(cd\mid (abc+abd-1)\)

\(\therefore abcd\mid(a^2bc^2d+ab^2c^2d+a^2bc^2d+ab^2cd^2-abc-abd-acd-bcd+1)\)

\(\therefore abcd\mid(abc+abd+acd+bcd-1)\)

\(t=\dfrac{abc+abd+acd+bcd-1}{abcd}\)

\(\thereforet=\dfrac{1}{a}+\dfrac{1}{b}+\dfrac{1}{c}+\dfrac{1}{d}-\dfrac{1}{abcd}\)

显然 \(a,b,c,d\) 两两互质,且 \(a \ge 2\)

由于不考虑顺序,则假设 \(2\le a < b <c < d\)

\(\therefore t< \dfrac{4}{a} \le2\)

\(\because t\in \mathbb{Z}^+\)

\(\therefore a=2,3,t=1\)

\(a=3\) 时,\(t_{max} =\dfrac{1}{3}+\dfrac{1}{4}+\dfrac{1}{5}+\dfrac{1}{6}-\dfrac{1}{360} =\dfrac{341}{360}<1\)\(\quad\therefore\)舍去

\(a=2\) 时,\(t_{max} =\dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{5}+\dfrac{1}{7}-\dfrac{1}{210} =\dfrac{246}{210}>1\)

\(\therefore a =2\)

\(\therefore\dfrac{1}{2}<\dfrac{3}{b}\)

\(\therefore b=3,5\)

\(b=5\) 时,\(t_{max} =\dfrac{1}{2}+\dfrac{1}{5}+\dfrac{1}{7}+\dfrac{1}{9}-\dfrac{1}{630} =\dfrac{600}{630}<1\)\(\quad\therefore\)舍去

\(b=3\) 时,\(t_{max} =\dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{5}+\dfrac{1}{7}-\dfrac{1}{210} =\dfrac{246}{210}>1\)

\(\therefore b=3\)

\(\therefore \dfrac{1}{6} <\dfrac{2}{c}\)

\(\therefore c=7,11\)

\(c=7\) 时,\(t_{max} =\dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{7}+\dfrac{1}{11}-\dfrac{1}{462} =\dfrac{492}{462}>1\)

\(c=11\) 时,\(t_{max} =\dfrac{1}{2}+\dfrac{1}{3}+\dfrac{1}{11}+\dfrac{1}{13}-\dfrac{1}{858} =\dfrac{858}{858}=1\)

显然当 \(c=11\)\(d=13\)

则一组解为 \(2,3,11,13\)

\(c=7\)

\(\dfrac{1}{42}<\dfrac{1}{d}\)

\(\therefore d=41\)

则另一组解为 \(2,3,7,41\)

综上所述,答案为 \(2,3,11,13\)\(2,3,7,41\)


为了验算结果我直接敲了个 \(O(n^4)\)的暴力(傻)

]]>
@@ -8845,7 +8845,7 @@ /2021/08/26/luo-gu-p4878-usaco05dec-layout-g-ti-jie/ - 洛谷P4878 [USACO05DEC]Layout G 题解

题目链接:P4878 [USACO05DEC]Layout G

题意:按编号排了 $n$ 只奶牛,有的奶牛间必须相距小于等于一个距离,有的奶牛间必须相距大于等于一个距离,问 $1$ 到 $n$ 的距离最大值

我们可以发现题目给的数据本质上就是 $v-u\ge d_i$ 或 $v-u \le d_i$

这是什么?差分约束!

差分约束:根据三角不等式 $d_u + w(u,v) \ge d_v$ 的构成,我们可以把形如 $v-u\ge d_i$ 的不等式转化为

$v-d_i\ge u$ (可以看作 $v$ 向 $u$ 连了一条边权为 $-d_i$ 的有向边),同理 $v-u \le d_i$ 转化为 $u+d_i \ge v$

这里不过多讲解差分约束了

那么为什么差分约束后求最短路就是最大值呢?

因为求最短路是由无穷大向下不断约束得到的,因此得到的是最大值(同理求最长路就是最小值)

那我们只要按题意建图就行了

要注意的是,每个奶牛 $i$ 满足 $d_{i-1} \le d_{i}$ ,其中 $d$ 表示所在位置,因此相邻的也要建边

为什么要强调这一点呢?

如下图:

如果没有建边,会发现答案为 $5$ (如上图所示)

如果建了边,答案才正确(该情况无解)

那现在只要从 $1$ 开始跑SPFA就好了

但是还有问题, $1$ 并不能保证与所有结点连通,而我们知道差分约束无解的情况就是图中有负环

这个问题好解决,我们先在原图上建一个超级结点 $0$ 与所有结点相连(边权为 $0$ ),在 $0$ 跑一次SPFA判断即可判断解的情况

说了这么多,是不是有点晕(

理一下思路:

  1. 差分约束建图
  2. 相邻编号建图
  3. 建 $0$ 结点判断解的情况
  4. 有解则从 $1$ 开始跑SPFA

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e3+5)#define MAXM (int)(2e4+5)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int n,ml,md,m,d[MAXN],vis[MAXN],cnt[MAXN];struct Edge{int u,v,w,next;}e[MAXM<<1];int head[MAXN],pos=1;void add(R int u,R int v,R int w){e[pos]={u,v,w,head[u]};head[u]=pos++;}bool spfa(R int st){queue<int> q;q.push(st);memset(d,0x3f,sizeof(d));vis[st]=1;d[st]=0;cnt[st]=1;while(!q.empty()){R int u=q.front();q.pop();vis[u]=0;for(R int i=head[u]; i; i=e[i].next){R int v=e[i].v,w=e[i].w;if(d[v]>d[u]+w){d[v]=d[u]+w;if(!vis[v]){if(++cnt[v]>n) return 0; // 有0结点,结点数增加1,判负环略有区别 vis[v]=1;q.push(v);}}}}return 1;}signed main(){read(n);read(ml);read(md);for(R int i=1,u,v,w; i<=ml; i++){read(u);read(v);read(w); // d_v-d_u<=w -> d_v<=d_u+wadd(u,v,w);}for(R int i=1,u,v,w; i<=md; i++){read(u);read(v);read(w); // d_v-d_u>=w -> d_v-w>=d_uadd(v,u,-w);}for(R int i=1; i<n; i++)add(i+1,i,0); // d_i<=d_{i+1}+0for(R int i=1; i<=n; i++)add(0,i,0);if(!spfa(0))return puts("-1"),0;spfa(1);if(d[n]==INF)puts("-2");else printf("%lld\n",d[n]);return 0;}
]]>
+ 洛谷P4878 [USACO05DEC]LayoutG 题解

题目链接:P4878[USACO05DEC]Layout G

题意:按编号排了 \(n\)只奶牛,有的奶牛间必须相距小于等于一个距离,有的奶牛间必须相距大于等于一个距离,问\(1\)\(n\) 的距离最大值

我们可以发现题目给的数据本质上就是 \(v-u\ged_i\)\(v-u \le d_i\)

这是什么?差分约束!

差分约束:根据三角不等式 \(d_u + w(u,v) \ge d_v\)的构成,我们可以把形如 \(v-u\ge d_i\)的不等式转化为

\(v-d_i\ge u\) (可以看作 \(v\) 向 \(u\) 连了一条边权为 \(-d_i\) 的有向边),同理 \(v-u \le d_i\) 转化为 \(u+d_i \ge v\)

这里不过多讲解差分约束了

那么为什么差分约束后求最短路就是最大值呢?

因为求最短路是由无穷大向下不断约束得到的,因此得到的是最大值(同理求最长路就是最小值)

那我们只要按题意建图就行了

要注意的是,每个奶牛 \(i\) 满足\(d_{i-1} \le d_{i}\) ,其中 \(d\) 表示所在位置,因此相邻的也要建边

为什么要强调这一点呢?

如下图:

如果没有建边,会发现答案为 \(5\)(如上图所示)

如果建了边,答案才正确(该情况无解)

那现在只要从 \(1\)开始跑SPFA就好了

但是还有问题, \(1\)并不能保证与所有结点连通,而我们知道差分约束无解的情况就是图中有负环

这个问题好解决,我们先在原图上建一个超级结点 \(0\) 与所有结点相连(边权为 \(0\) ),在 \(0\) 跑一次SPFA判断即可判断解的情况

说了这么多,是不是有点晕(

理一下思路:

  1. 差分约束建图
  2. 相邻编号建图
  3. \(0\) 结点判断解的情况
  4. 有解则从 \(1\) 开始跑SPFA

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e3+5)#define MAXM (int)(2e4+5)#define INF 0x3f3f3f3f3f3f3f3ftemplate<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int n,ml,md,m,d[MAXN],vis[MAXN],cnt[MAXN];struct Edge{int u,v,w,next;}e[MAXM<<1];int head[MAXN],pos=1;void add(R int u,R int v,R int w){e[pos]={u,v,w,head[u]};head[u]=pos++;}bool spfa(R int st){queue<int> q;q.push(st);memset(d,0x3f,sizeof(d));vis[st]=1;d[st]=0;cnt[st]=1;while(!q.empty()){R int u=q.front();q.pop();vis[u]=0;for(R int i=head[u]; i; i=e[i].next){R int v=e[i].v,w=e[i].w;if(d[v]>d[u]+w){d[v]=d[u]+w;if(!vis[v]){if(++cnt[v]>n) return 0; // 有0结点,结点数增加1,判负环略有区别 vis[v]=1;q.push(v);}}}}return 1;}signed main(){read(n);read(ml);read(md);for(R int i=1,u,v,w; i<=ml; i++){read(u);read(v);read(w); // d_v-d_u<=w -> d_v<=d_u+wadd(u,v,w);}for(R int i=1,u,v,w; i<=md; i++){read(u);read(v);read(w); // d_v-d_u>=w -> d_v-w>=d_uadd(v,u,-w);}for(R int i=1; i<n; i++)add(i+1,i,0); // d_i<=d_{i+1}+0for(R int i=1; i<=n; i++)add(0,i,0);if(!spfa(0))return puts("-1"),0;spfa(1);if(d[n]==INF)puts("-2");else printf("%lld\n",d[n]);return 0;}
]]>
@@ -8872,7 +8872,7 @@ /2021/08/26/poj3723-conscription-ti-jie/ - POJ3723 Conscription 题解

题目链接:POJ3723 Conscription

题意:要招 $n$ 个女的, $m$ 个男的,原价 $10000$,如果招了关系亲密的(男女)人可以降价,求最小花费

首先,题目给出的格式 $x_i\ y_i\ d_i$ 很像图论题

我们可以把每个人看作一个结点,为了防止男女编号相同的人搞混了,我们可以让女的编号为 $1 \sim n$ ,男的编号为 $n+1 \sim m$ ,显然这题要我们选出所有的结点且花费最小

我们可以发现答案如果存在环则会产生矛盾,例如:a招了b,b招了c,c招了a,这说不通

那是最小生成树吗?不一定

我们可以发现给定的这张图不一定是连通图,而我们要求的是花费最小的树型结构(感性理解下),那么这就是最大权森林问题,同样可以用最小生成树来求解(注:边权设为 $-d_i$)

由于不一定是树,所以不需要记录连了多少边

代码如下

#include <cstdio>#include <vector>#include <iostream>#include <algorithm>// POJ不给用万能头文件 qwqusing namespace std;#define int long long#define R register#define MAXN (int)(2e4+50)#define MAXM (int)(5e4+50)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int Q,n,m,r;int f[MAXN];int find(R int x){return f[x]==x?x:f[x]=find(f[x]);}inline void merge(R int u,R int v){f[find(u)]=find(v);} // 并查集struct Edge{int u,v,w,next;void clear(){u=v=w=next=0;} // 清空bool operator<(const Edge &o)const{return w<o.w;}}e[MAXM];int head[MAXN],pos=1;void add(R int u,R int v,R int w){e[pos]={u,v,w,head[u]};head[u]=pos++;}int kruskal(){R int res=0;sort(e+1,e+r+1);for(R int i=1; i<=r; i++){R int u=e[i].u,v=e[i].v,w=e[i].w;if(find(u)!=find(v)){merge(u,v);res+=w;}}return res;}void proc(){read(n);read(m);read(r);pos=1;for(R int i=1; i<=n+m; i++)f[i]=i;for(R int i=1; i<=r; i++)e[i].clear();for(R int i=1,u,v,w; i<=r; i++){read(u);read(v);read(w);v+=n;++u;++v; // 题目是从0开始编号的,我不喜欢QwQadd(u,v,-w);}printf("%lld\n",10000ll*(n+m)+kruskal()); // 别忘了加上每个人10000$的原价}signed main(){read(Q);while(Q--)proc(); // 多组数据return 0;}
]]>
+ POJ3723 Conscription 题解

题目链接:POJ3723Conscription

题意:要招 \(n\)个女的, \(m\) 个男的,原价 \(10000\),如果招了关系亲密的(男女)人可以降价,求最小花费

首先,题目给出的格式 \(x_i\ y_i\d_i\) 很像图论题

我们可以把每个人看作一个结点,为了防止男女编号相同的人搞混了,我们可以让女的编号为\(1 \sim n\) ,男的编号为 \(n+1 \sim m\),显然这题要我们选出所有的结点且花费最小

我们可以发现答案如果存在环则会产生矛盾,例如:a招了b,b招了c,c招了a,这说不通

那是最小生成树吗?不一定

我们可以发现给定的这张图不一定是连通图,而我们要求的是花费最小的树型结构(感性理解下),那么这就是最大权森林问题,同样可以用最小生成树来求解(注:边权设为\(-d_i\)

由于不一定是树,所以不需要记录连了多少边

代码如下

#include <cstdio>#include <vector>#include <iostream>#include <algorithm>// POJ不给用万能头文件 qwqusing namespace std;#define int long long#define R register#define MAXN (int)(2e4+50)#define MAXM (int)(5e4+50)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}int Q,n,m,r;int f[MAXN];int find(R int x){return f[x]==x?x:f[x]=find(f[x]);}inline void merge(R int u,R int v){f[find(u)]=find(v);} // 并查集struct Edge{int u,v,w,next;void clear(){u=v=w=next=0;} // 清空bool operator<(const Edge &o)const{return w<o.w;}}e[MAXM];int head[MAXN],pos=1;void add(R int u,R int v,R int w){e[pos]={u,v,w,head[u]};head[u]=pos++;}int kruskal(){R int res=0;sort(e+1,e+r+1);for(R int i=1; i<=r; i++){R int u=e[i].u,v=e[i].v,w=e[i].w;if(find(u)!=find(v)){merge(u,v);res+=w;}}return res;}void proc(){read(n);read(m);read(r);pos=1;for(R int i=1; i<=n+m; i++)f[i]=i;for(R int i=1; i<=r; i++)e[i].clear();for(R int i=1,u,v,w; i<=r; i++){read(u);read(v);read(w);v+=n;++u;++v; // 题目是从0开始编号的,我不喜欢QwQadd(u,v,-w);}printf("%lld\n",10000ll*(n+m)+kruskal()); // 别忘了加上每个人10000$的原价}signed main(){read(Q);while(Q--)proc(); // 多组数据return 0;}
]]>
@@ -8899,7 +8899,7 @@ /2021/08/26/luo-gu-p2865-usaco06nov-roadblocks-g-ti-jie/ - 洛谷P2865 [USACO06NOV]Roadblocks G 题解

题目链接:P2865 [USACO06NOV]Roadblocks G

题意:求结点 $1$ 到结点 $n$ 的次短路,所有边有非负权重,边可以重复经过,无向图

如果是求最短路,那么一个dijkstra直接搞定

但是这题要求的是次短路,同样也可以dijkstra解决

我们观察 $1$ 到 $v$ 的一条次短路 $p$

要么存在 $1$ 到 $u$ 的一条最短路 $k_1$ ,使得 $k_1$ 加上 $(u,v)$ 等于 $p$

要么存在 $1$ 到 $u$ 的一条次短路 $k_2$ ,使得 $k_2$ 加上 $(u,v)$ 等于 $p$

因此我们只需要记录每个结点的最短距离次短距离即可

(注:由于最短路径可能有多条,但是并不影响结果,因此我们假设只有一条路径)

由于每个结点都可以访问多次,那么怎么防止死循环呢?

显然对于一个结点,如果在我们的优先队列中将它取出时,我们只要判断它的距离是否大于它的结点的次短距离,如果大于,一定不是次短路

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e3+5)#define MAXM (int)(2e5+10)#define INF (int)(0x3f3f3f3f3f3f3f3f)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}struct Edge{int u,v,w,next;}e[MAXM];struct node{int v,dis;bool operator<(const node &o)const{return dis>o.dis;}};int n,m;int head[MAXN],pos=1;int d1[MAXN],d2[MAXN];void add(R int u,R int v,R int w){e[pos]={u,v,w,head[u]};head[u]=pos++;}void dijkstra(){memset(d1,0x3f,sizeof(d1)); // 最短路memset(d2,0x3f,sizeof(d2)); // 次短路priority_queue<node> q;d1[1]=0;q.push({1,0}); // 次短路不用将结点1的距离设为0while(!q.empty()){R int u=q.top().v,tmp=q.top().dis;q.pop();if(tmp>d2[u])continue; // 防止死循环for(R int i=head[u]; i; i=e[i].next){R int v=e[i].v,w=e[i].w;R int dis=tmp+w;if(dis<d1[v]){swap(d1[v],dis); // 这里要注意不是直接赋值,而是交换,这很显然吧..q.push({v,d1[v]});}if(dis<d2[v]&&d1[v]<dis){d2[v]=dis; // 这里就是直接赋值了q.push({v,d2[v]});}}}}signed main(){read(n);read(m);for(R int i=1,u,v,w; i<=m; i++){read(u);read(v);read(w);add(u,v,w);add(v,u,w); // 无向边}dijkstra();printf("%lld\n",d2[n]);return 0;}
]]>
+ 洛谷P2865[USACO06NOV]Roadblocks G 题解

题目链接:P2865[USACO06NOV]Roadblocks G

题意:求结点 \(1\)到结点 \(n\)的次短路,所有边有非负权重,边可以重复经过,无向图

如果是求最短路,那么一个dijkstra直接搞定

但是这题要求的是次短路,同样也可以dijkstra解决

我们观察 \(1\)\(v\) 的一条次短路 \(p\)

要么存在 \(1\)\(u\) 的一条最短路 \(k_1\) ,使得 \(k_1\) 加上 \((u,v)\) 等于 \(p\)

要么存在 \(1\)\(u\) 的一条次短路 \(k_2\) ,使得 \(k_2\) 加上 \((u,v)\) 等于 \(p\)

因此我们只需要记录每个结点的最短距离次短距离即可

(注:由于最短路径可能有多条,但是并不影响结果,因此我们假设只有一条路径)

由于每个结点都可以访问多次,那么怎么防止死循环呢?

显然对于一个结点,如果在我们的优先队列中将它取出时,我们只要判断它的距离是否大于它的结点的次短距离,如果大于,一定不是次短路

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e3+5)#define MAXM (int)(2e5+10)#define INF (int)(0x3f3f3f3f3f3f3f3f)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}k=x*f;}struct Edge{int u,v,w,next;}e[MAXM];struct node{int v,dis;bool operator<(const node &o)const{return dis>o.dis;}};int n,m;int head[MAXN],pos=1;int d1[MAXN],d2[MAXN];void add(R int u,R int v,R int w){e[pos]={u,v,w,head[u]};head[u]=pos++;}void dijkstra(){memset(d1,0x3f,sizeof(d1)); // 最短路memset(d2,0x3f,sizeof(d2)); // 次短路priority_queue<node> q;d1[1]=0;q.push({1,0}); // 次短路不用将结点1的距离设为0while(!q.empty()){R int u=q.top().v,tmp=q.top().dis;q.pop();if(tmp>d2[u])continue; // 防止死循环for(R int i=head[u]; i; i=e[i].next){R int v=e[i].v,w=e[i].w;R int dis=tmp+w;if(dis<d1[v]){swap(d1[v],dis); // 这里要注意不是直接赋值,而是交换,这很显然吧..q.push({v,d1[v]});}if(dis<d2[v]&&d1[v]<dis){d2[v]=dis; // 这里就是直接赋值了q.push({v,d2[v]});}}}}signed main(){read(n);read(m);for(R int i=1,u,v,w; i<=m; i++){read(u);read(v);read(w);add(u,v,w);add(v,u,w); // 无向边}dijkstra();printf("%lld\n",d2[n]);return 0;}
]]>
@@ -8926,7 +8926,7 @@ /2021/08/26/luo-gu-p1581-a-b-problem-sheng-ji-ban-ti-jie/ - 洛谷P1581 A+B Problem(升级版)题解

题目链接:P1581 A+B Problem(升级版)

题意:每一位进制不同,第一位进制为 $2$ ,第二位进制为 $3$ ,第 $i$ 位进制为第 $i$ 个质数,求A+B

本题就是以普通的高精度加法为基础,改了一下进位的方式而已

这里我用的vector<int>写的高精度

我们可以把每一位的进制打个表,在进位的时候取模就行了

代码如下

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint mod[]={2,3,5,7,11,13,17,19,22};int t,f=0,len1,len2;vector<int> num1,num2;char ch;signed main(){    while(~scanf("%lld%c",&t,&ch))    {        if(!f)num1.push_back(t);        else num2.push_back(t);        if(ch=='+')f=1;//f是用于标记当前读的是哪一个数        if(ch=='\n')break;    }    // for(R int i=0; i<len1; i++)cout<<num1[i]<<" ";cout<<endl;    // for(R int i=0; i<len2; i++)cout<<num2[i]<<" ";cout<<endl;    len1=num1.size();    len2=num2.size();    if(len1<len2)//把两个数的长度补成一样的    {        for(R int i=1; i<=len2-len1; i++)            num1.insert(num1.begin(),0);    }else for(R int i=1; i<=len1-len2; i++)            num2.insert(num2.begin(),0);    vector<int>num;//答案    int len=num1.size();    int jw=0;//进位    for(R int i=len-1; i>=0; i--)    {        int md=mod[len-1-i];//查表,找进制        int tmp=(num1[i]+num2[i]+jw);//先把这一位的全部加起来        jw=tmp/md;//进位就是这一位上所有的加起来除以进制        tmp%=md;//留下的余数就是这一位最后的结果        num.push_back(tmp);    }if(jw!=0)num.push_back(jw);//别忘了最后可能有一个进位    reverse(num.begin(),num.end());//由于我们是一个一个从后放进去的,所以结果需要翻转一下    for(R int i=0; i<num.size(); i++)//注意,这里一定要是num.size(),因为最后可能有一个进位        printf("%lld%c",num[i],",\n"[i==num.size()-1]);    return 0;}

]]>
+ 洛谷P1581 A+BProblem(升级版)题解

题目链接:P1581 A+BProblem(升级版)

题意:每一位进制不同,第一位进制为 \(2\) ,第二位进制为 \(3\) ,第 \(i\) 位进制为第 \(i\) 个质数,求A+B

本题就是以普通的高精度加法为基础,改了一下进位的方式而已

这里我用的vector<int>写的高精度

我们可以把每一位的进制打个表,在进位的时候取模就行了

代码如下

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint mod[]={2,3,5,7,11,13,17,19,22};int t,f=0,len1,len2;vector<int> num1,num2;char ch;signed main(){    while(~scanf("%lld%c",&t,&ch))    {        if(!f)num1.push_back(t);        else num2.push_back(t);        if(ch=='+')f=1;//f是用于标记当前读的是哪一个数        if(ch=='\n')break;    }    // for(R int i=0; i<len1; i++)cout<<num1[i]<<" ";cout<<endl;    // for(R int i=0; i<len2; i++)cout<<num2[i]<<" ";cout<<endl;    len1=num1.size();    len2=num2.size();    if(len1<len2)//把两个数的长度补成一样的    {        for(R int i=1; i<=len2-len1; i++)            num1.insert(num1.begin(),0);    }else for(R int i=1; i<=len1-len2; i++)            num2.insert(num2.begin(),0);    vector<int>num;//答案    int len=num1.size();    int jw=0;//进位    for(R int i=len-1; i>=0; i--)    {        int md=mod[len-1-i];//查表,找进制        int tmp=(num1[i]+num2[i]+jw);//先把这一位的全部加起来        jw=tmp/md;//进位就是这一位上所有的加起来除以进制        tmp%=md;//留下的余数就是这一位最后的结果        num.push_back(tmp);    }if(jw!=0)num.push_back(jw);//别忘了最后可能有一个进位    reverse(num.begin(),num.end());//由于我们是一个一个从后放进去的,所以结果需要翻转一下    for(R int i=0; i<num.size(); i++)//注意,这里一定要是num.size(),因为最后可能有一个进位        printf("%lld%c",num[i],",\n"[i==num.size()-1]);    return 0;}

]]>
@@ -8953,7 +8953,7 @@ /2021/08/18/luo-gu-p1047-noip2005-pu-ji-zu-xiao-men-wai-de-shu-ti-jie/ - 洛谷P1047 [NOIP2005 普及组] 校门外的树 题解

前言

如何把一道入门题写成省选题?(手动滑稽)

本题解是我在练习分块时突发奇想写的,真就把入门题写成省选题的感觉(

才发现原来这些简单题这么有趣(


题目链接: P1047 [NOIP2005 普及组] 校门外的树

题意:马路上砍树,问砍了 $m$ 次还有几棵树

一、模拟解法(正常解法)

我们只要把每次砍掉的树标记一下,最后统计未标记的数量即可

注意下标从 $0$ 开始,共 $n+1$ 个数

代码如下:

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e4+5)int n,m,ans;bool a[MAXN];signed main(){scanf("%lld%lld",&n,&m);for(R int i=1,l,r; i<=m; i++){scanf("%lld%lld",&l,&r);for(R int j=l; j<=r; j++)a[j]=1;}for(R int i=0; i<=n; i++)ans+=(a[i]==0);printf("%lld\n",ans);return 0;}

二、线段树解法(开始奇怪起来)

直接用线段树维护区间,没什么特别的

代码如下:

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e4+5)template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2];inline int ls(R int x){return x<<1;}inline int rs(R int x){return x<<1|1;}void proc(R int l,R int r,R int at){R int u=at>>1;if(tag[u])ans[at]=0,tag[at]=1;}void push_up(R int at){ans[at]=ans[ls(at)]+ans[rs(at)];}void push_down(R int l,R int r,R int at){R int mid=(l+r)>>1;proc(l,mid,ls(at));proc(mid+1,r,rs(at));tag[at]=0;}void build(R int l,R int r,R int at){tag[at]=0;if(l==r){ans[at]=a[l];return;}R int mid=(l+r)>>1;build(l,mid,ls(at));build(mid+1,r,rs(at));push_up(at);}void update(R int nl,R int nr,R int l,R int r,R int at){if(nl<=l&&r<=nr){ans[at]=0;tag[at]=1;return;}R int mid=(l+r)>>1;push_down(l,r,at);if(nl<=mid)update(nl,nr,l,mid,ls(at));if(nr>mid)update(nl,nr,mid+1,r,rs(at));push_up(at);}int query(R int nl,R int nr,R int l,R int r,R int at){R int res=0;if(nl<=l&&r<=nr)return ans[at];R int mid=(l+r)>>1;push_down(l,r,at);if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));return res;}signed main(){read(n);read(m);++n;for(R int i=1; i<=n; i++)a[i]=1;build(1,n,1);while(m--){R int l,r;read(l);read(r);update(l+1,r+1,1,n,1);}printf("%lld\n",query(1,n,1,n,1));return 0;}

三、分块解法 (开始毒瘤起来)

只有一个要注意的,就是每次暴力减的时候要防止减为负数
代码如下:

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e4+5)template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m,len;int a[MAXN],ans[MAXN],id[MAXN],tag[MAXN];void add(R int l,R int r){R int lid=id[l],rid=id[r];    if(lid==rid)    {        for(R int i=l; i<=r; i++)            ans[id[i]]-=a[i],a[i]=0;        return;    }    for(R int i=l; i<=lid*len&&!tag[lid]; i++)        ans[id[i]]-=a[i],a[i]=0;    for(R int i=lid+1;i<rid; i++)        tag[i]=1,ans[i]=0;    for(R int i=(rid-1)*len+1; i<=r&&!tag[rid]; i++)        ans[id[i]]-=a[i],a[i]=0;}int sum(R int l,R int r){    R int res=0;    R int lid=id[l],rid=id[r];    if(tag[lid])    {        for(R int i=(lid-1)*len+1; i<=len*lid; i++)            ans[id[i]]-=a[i],a[i]=0;    }    if(tag[rid])    {        for(R int i=(rid-1)*len+1; i<=len*rid; i++)            ans[id[i]]-=a[i],a[i]=0;    }    if(lid==rid)    {        for(R int i=l; i<=r; i++)            res+=a[i];        return res;    }    for(R int i=l; i<=lid*len; i++)        res+=a[i];    for(R int i=lid+1; i<rid; i++)        res+=ans[i];    for(R int i=(rid-1)*len+1; i<=r; i++)        res+=a[i];    return res;}signed main(){read(n);read(m);++n;len=sqrt(n);for(R int i=1; i<=n; i++){a[i]=1;id[i]=(i-1)/len+1;ans[id[i]]++;}while(m--){R int x,y;read(x);read(y);add(x+1,y+1);}printf("%lld\n",sum(1,n));return 0;}

四、珂朵莉树解法 (非常珂学)

一看这个推平操作,立马就想到了珂朵莉树

如果不知道珂朵莉树的可以看看我写的这篇文章

代码如下:

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e4+5)template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m;int a[MAXN];struct node{int l,r;mutable int v;bool operator<(const node &o)const{return l<o.l;}};set<node>s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it;--it;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}void assign(R int l,R int r){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert({l,r,0});}int sum(R int l,R int r){R int res=0;set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)res+=it->v*(it->r-it->l+1);return res;}signed main(){read(n);read(m);++n;s.insert({1,n,1});while(m--){R int x,y;read(x);read(y);assign(x+1,y+1);}printf("%lld\n",sum(1,n));return 0;}


总结

本文简单介绍了几种解法来解决此题


题外话

数据太水了,所以这几种做法的速度都很快

附上提交记录叭…

模拟解法 线段树解法 分块解法 珂朵莉树解法

]]>
+ 洛谷P1047 [NOIP2005普及组] 校门外的树 题解

前言

如何把一道入门题写成省选题?(手动滑稽)

本题解是我在练习分块时突发奇想写的,真就把入门题写成省选题的感觉(

才发现原来这些简单题这么有趣(


题目链接: P1047[NOIP2005 普及组] 校门外的树

题意:马路上砍树,问砍了 \(m\) 次还有几棵树

一、模拟解法(正常解法)

我们只要把每次砍掉的树标记一下,最后统计未标记的数量即可

注意下标从 \(0\) 开始,共 \(n+1\) 个数

代码如下:

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e4+5)int n,m,ans;bool a[MAXN];signed main(){scanf("%lld%lld",&n,&m);for(R int i=1,l,r; i<=m; i++){scanf("%lld%lld",&l,&r);for(R int j=l; j<=r; j++)a[j]=1;}for(R int i=0; i<=n; i++)ans+=(a[i]==0);printf("%lld\n",ans);return 0;}

二、线段树解法(开始奇怪起来)

直接用线段树维护区间,没什么特别的

代码如下:

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e4+5)template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2];inline int ls(R int x){return x<<1;}inline int rs(R int x){return x<<1|1;}void proc(R int l,R int r,R int at){R int u=at>>1;if(tag[u])ans[at]=0,tag[at]=1;}void push_up(R int at){ans[at]=ans[ls(at)]+ans[rs(at)];}void push_down(R int l,R int r,R int at){R int mid=(l+r)>>1;proc(l,mid,ls(at));proc(mid+1,r,rs(at));tag[at]=0;}void build(R int l,R int r,R int at){tag[at]=0;if(l==r){ans[at]=a[l];return;}R int mid=(l+r)>>1;build(l,mid,ls(at));build(mid+1,r,rs(at));push_up(at);}void update(R int nl,R int nr,R int l,R int r,R int at){if(nl<=l&&r<=nr){ans[at]=0;tag[at]=1;return;}R int mid=(l+r)>>1;push_down(l,r,at);if(nl<=mid)update(nl,nr,l,mid,ls(at));if(nr>mid)update(nl,nr,mid+1,r,rs(at));push_up(at);}int query(R int nl,R int nr,R int l,R int r,R int at){R int res=0;if(nl<=l&&r<=nr)return ans[at];R int mid=(l+r)>>1;push_down(l,r,at);if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));return res;}signed main(){read(n);read(m);++n;for(R int i=1; i<=n; i++)a[i]=1;build(1,n,1);while(m--){R int l,r;read(l);read(r);update(l+1,r+1,1,n,1);}printf("%lld\n",query(1,n,1,n,1));return 0;}

三、分块解法 (开始毒瘤起来)

只有一个要注意的,就是每次暴力减的时候要防止减为负数 代码如下:

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e4+5)template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m,len;int a[MAXN],ans[MAXN],id[MAXN],tag[MAXN];void add(R int l,R int r){R int lid=id[l],rid=id[r];    if(lid==rid)    {        for(R int i=l; i<=r; i++)            ans[id[i]]-=a[i],a[i]=0;        return;    }    for(R int i=l; i<=lid*len&&!tag[lid]; i++)        ans[id[i]]-=a[i],a[i]=0;    for(R int i=lid+1;i<rid; i++)        tag[i]=1,ans[i]=0;    for(R int i=(rid-1)*len+1; i<=r&&!tag[rid]; i++)        ans[id[i]]-=a[i],a[i]=0;}int sum(R int l,R int r){    R int res=0;    R int lid=id[l],rid=id[r];    if(tag[lid])    {        for(R int i=(lid-1)*len+1; i<=len*lid; i++)            ans[id[i]]-=a[i],a[i]=0;    }    if(tag[rid])    {        for(R int i=(rid-1)*len+1; i<=len*rid; i++)            ans[id[i]]-=a[i],a[i]=0;    }    if(lid==rid)    {        for(R int i=l; i<=r; i++)            res+=a[i];        return res;    }    for(R int i=l; i<=lid*len; i++)        res+=a[i];    for(R int i=lid+1; i<rid; i++)        res+=ans[i];    for(R int i=(rid-1)*len+1; i<=r; i++)        res+=a[i];    return res;}signed main(){read(n);read(m);++n;len=sqrt(n);for(R int i=1; i<=n; i++){a[i]=1;id[i]=(i-1)/len+1;ans[id[i]]++;}while(m--){R int x,y;read(x);read(y);add(x+1,y+1);}printf("%lld\n",sum(1,n));return 0;}

四、珂朵莉树解法 (非常珂学)

一看这个推平操作,立马就想到了珂朵莉树

如果不知道珂朵莉树的可以看看我写的这篇文章

代码如下:
#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(1e4+5)template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m;int a[MAXN];struct node{int l,r;mutable int v;bool operator<(const node &o)const{return l<o.l;}};set<node>s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it;--it;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}void assign(R int l,R int r){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert({l,r,0});}int sum(R int l,R int r){R int res=0;set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)res+=it->v*(it->r-it->l+1);return res;}signed main(){read(n);read(m);++n;s.insert({1,n,1});while(m--){R int x,y;read(x);read(y);assign(x+1,y+1);}printf("%lld\n",sum(1,n));return 0;}

总结

本文简单介绍了几种解法来解决此题


题外话

数据太水了,所以这几种做法的速度都很快

附上提交记录叭...

模拟解法 线段树解法 分块解法 珂朵莉树解法

]]>
@@ -8984,7 +8984,7 @@ /2021/08/15/qian-tan-ke-duo-li-shu-odt/ - 浅谈珂朵莉树(ODT)

前言

珂学家狂喜(


一、珂朵莉树来源

珂朵莉树,原名老司机树(Old Driver Tree),在某场CF比赛中提出

因为题目背景是《末日时在做什么?有没有空?可以来拯救吗?》中的珂朵莉,所以就叫珂朵莉树了


二、珂朵莉树

题目链接:CF896C Willem, Chtholly and Seniorious

题目要求维护一种数据结构,支持以下操作

  1. 将 $[l,r]$ 区间内所有数加上 $x$
  2. 将 $[l,r]$ 区间内所有数改成 $x$
  3. 求 $[l,r]$ 区间第 $k$ 小的数
  4. 求 $[l,r]$ 区间内所有数的 $x$ 次方的和取模 $y$

值得注意的是,数据为随机数据,说明没有故意构造卡的数据

1.珂朵莉树有什么用?

最主要的就是区间内的推平操作了,即本题中的操作 $2$

当然还有别的操作,那就不是最主要的了

2.原理是什么?

写在前面:这个数据结构唯一前置知识就只有set

a.存储

我们可以把区间看作若干个结点,每个结点都有自己的左端点 $l$ 、右端点 $r$ 以及值 $v$

显然,一开始的时候每个结点的 $l=r=idx$ , $idx$ 指该元素在数组中的下标

我们可以把这些结点按左端点顺序存储在一个set

struct node{int l,r; // 左、右端点mutable int v; // 值const bool operator<(const node &o)const{return l<o.l;}};

注意 int v前的关键字 mutable,这个关键字和const恰好相反,意思是使v始终允许修改,即使它是个常量

那么我们为什么要多次一举呢?过会再说(

b.分割结点

显然每次推平操作并不能保证区间左、右端点恰好就在一个结点上,因此我们还需要对结点进行分割

考虑查找一个结点,使得其左端点恰好为 $pos$ ,$pos$ 指某次操作中的一个分割点

我们可以用set中的lower_bound()函数来找到第一个左端点大于等于 $pos$ 的结点

(注:因为我们是按左端点顺序排序的)

这个lower_bound()会返回一个set常量迭代器

现在我们找到了一个结点,那么会出现以下三种情况

第一种情况: $pos$ 恰好为一个结点的左端点,直接返回这个端点的迭代器(显然前提是这个结点不是s.end()

那么其他情况得到的这个结点的左端点一定比 $pos$ 大

因此可以尝试分割前一个结点,即把迭代器it--

第二种情况:这个结点的右端点小于 $pos$ ,由于结点的端点一定是连续的,说明 $pos$ 是新加入的结点,直接 return s.end()

第三种情况:最普遍的情况,找到了一个结点恰好包含 $pos$ ,因为我们要的是以 $pos$ 为左端点的结点,显然我们要把这个结点分割成 $node\{l,pos-1,v\}$ 和 $node\{pos,r,v\}$

set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it; // 情况1it--;if(it->r<pos)return s.end(); // 情况2R int l=it->l,r=it->r,v=it->v; // 情况3s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;     // insert函数的返回值是pair类型的,而它的first恰好使我们需要的(新插入结点的指针)}

c.推平

解决了区间的端点问题,我们只要获取要求修改区间左、右端点的结点,把这一段删除,再插入要求赋的值和修改区间的左、右端点作为新的结点

注意一定要先找右端点所在结点,再找左端点所在结点

为什么?因为我们再分割结点时大概率删除了部分结点,并加入新的结点,如果我们先找左端点所在结点,再找右端点所在结点,很有左端点所在结点的迭代器已经失效了

例如有一个结点 $node\{l=1,r=5\}$ ,修改区间的左端点为 $1$ ,右端点为 $3$

按先左再右的顺序,我们先会得到左端点所在结点

$node\{l=1,r=5\}$

显然如果我们找右端点所在结点,会将左端点所在结点进行分割,那么原来的结点就没了,迭代器失效,然后RE

inline void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l); // 找结点s.erase(itl,itr); // 删掉[itl,itr)中所有结点s.insert({l,r,k}); // 插入新结点}

d.剩余操作

区间加,十分暴力,十分简单,我们只要找到左、右端点所在结点,然后直接把每个结点修改就行

现在知道为什么要写mutable了吧!因为split()返回的是常量迭代器

inline void add(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);for(R set<node>::iterator it=itl; it!=itr; it++)it->v+=k; // 直接加}

区间第 $k$ 小数,我们只要把区间内的所有结点取出来从大到小排序即可

注意每个结点指代的可能是一段区间,而我们要求的是第 $k$ 小的数,因此每遍历一个结点,如果 $k$ 大于该结点指代的区间长,则让 $k$ 减去该结点指代的区间长,否则第 $k$ 小的数就在该结点区间内,直接输出即可

struct Rank{int num,cnt;const bool operator<(const Rank &o)const{return num<o.num;}};inline int rnk(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);vector<Rank> v;for(R set<node>::iterator it=itl; it!=itr; it++)v.push_back({it->v,it->r - it->l +1}); // 记录结点sort(v.begin(),v.end());for(R int i=0; i<v.size(); i++)if(v[i].cnt<k)k-=v[i].cnt; // 减去区间长else return v[i].num;return -1; // 这句显然在数据合法时没有任何用}

区间求和,只要遍历一下,加一下乘一下取模一下就好

inline int sum(R int l,R int r,R int x,R int y){R int ans=0;set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)ans=(ans%y+qpow(it->v,x,y)%y*(it->r-it->l+1)%y)%y; // 快速幂qpow()就不贴了return ans;}

3.复杂度分析

set实现的珂朵莉树复杂度为 $O(n\log^2n)$

不过本人不是很会分析,这篇文章分析证明的很好,大家可以看看

完整代码 (注:原题的随机数据是给定 $seed$ 等自行生成)

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define mod (int)(1e9+7)#define MAXN (int)(1e5+5)int n,m,seed,vmax;int qpow(R int a,R int b,R int p){R int ans=1,base=a;while(b){if(b&1)ans=(ans%p*base%p)%p;base=(base%p*base%p)%p;b>>=1;}return ans%p;}struct node{int l,r;mutable int v;const bool operator<(const node &o)const{return l<o.l;}};int rnd(){R int ret=seed;seed=(seed*7+13)%mod;return ret;}int a[MAXN];set<node> s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos) return it;it--;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}inline void add(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);for(R set<node>::iterator it=itl; it!=itr; it++)it->v+=k;}inline void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);//[itl,itr)s.insert({l,r,k});}struct Rank{int num,cnt;const bool operator<(const Rank &o)const{return num<o.num;}};inline int rnk(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);vector<Rank> v;for(R set<node>::iterator it=itl; it!=itr; it++)v.push_back({it->v,it->r - it->l +1});sort(v.begin(),v.end());for(R int i=0; i<v.size(); i++)if(v[i].cnt<k)k-=v[i].cnt;else return v[i].num;return -1;}inline int sum(R int l,R int r,R int x,R int y){R int ans=0;set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)ans=(ans%y+qpow(it->v,x,y)%y*(it->r-it->l+1)%y)%y;return ans;}signed main(){scanf("%lld%lld%lld%lld",&n,&m,&seed,&vmax);for(R int i=1; i<=n; i++){a[i]=(rnd()%vmax)+1;s.insert({i,i,a[i]});}for(R int i=1; i<=m; i++){R int op,l,r,x,y;op=(rnd()%4)+1;l=(rnd()%n)+1;r=(rnd()%n)+1;if(l>r)swap(l,r);if(op==3){x=(rnd()%(r-l+1))+1;}else {x=(rnd()%vmax)+1;}if(op==4){y=(rnd()%vmax)+1;}if(op==1)add(l,r,x);else if(op==2)assign(l,r,x);else if(op==3)printf("%lld\n",rnk(l,r,x));else printf("%lld\n",sum(l,r,x,y));}return 0;}/**在太阳西斜的这个世界里,置身天上之森,*等这场战争结束之后,不归之人与望眼欲穿的人们,*人人本着正义之名,长存不灭的过去,逐渐消逝的未来,*我回来了,纵使日薄西山,即便看不到未来,*此时此刻的光辉,盼君勿忘**————世上最幸福的女孩*/

三、珂朵莉树例题

(注:都是洛谷上的题~)

1.P4979 矿洞:坍塌

题目链接:P4979 矿洞:坍塌

题意:要求维护一个数据结构,支持对给定字符串进行如下操作

A x y op表示替换材料,将 $x$ 到 $y(1\le x\le y\le N)$
区间内的材料替换为opop为$A,B,C$ 三种材料字符中的一个

B x y表示是否询问,即询问 $x$ 到 $y(1\le x\le y\le
N)$区间内的材料是否合法,合法输出Yes,不合法输出No

合法指该区间连续且材料相等,并且该区间前一个和后一个材料不相同

几乎是板子题,没什么特别的,只要跟题目意思写查询操作就行

不过要注意的是,出题人卡了无优化的ODT

那么怎么优化呢?我们只要在每次查询时将相同值的相邻结点合并即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e5+5)int n,Q;char a[MAXN];struct node{int l,r;mutable char v;const bool operator<(const node &o)const{return l<o.l;}};set<node>s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it;it--;if(it->r<pos)return s.end();R int l=it->l,r=it->r;R char v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}inline void assign(R int l,R int r,R char k){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert({l,r,k});}inline void query(R int l,R int r){set<node>::iterator itr=split(r+1),itl=split(l);if(l!=r){set<node>::iterator it=itl,last=itl;it++;for(; it!=itr; it++,last++)if(it->v!=last->v){puts("No");R int _l=itl->l,_r=last->r;R char _v=last->v;s.erase(itl,it);s.insert({_l,_r,_v});return;}}R char _v=itl->v;s.erase(itl,itr);s.insert({l,r,_v});if(l==1||r==n){puts("Yes");return;}set<node>::iterator ib=split(r+1),ia=split(l-1);puts((ia->v!=ib->v)?"Yes":"No");}signed main(){scanf("%lld %s\n",&n,a+1);for(R int i=1; i<=n; i++){R int l=i,r=i;while(a[l]==a[i])r=++i;s.insert({l,r,a[--i]});}scanf("%lld\n",&Q);while(Q--){R int l,r;R char op,k;scanf("%c",&op);if(op=='A'){scanf("%lld %lld %c\n",&l,&r,&k);assign(l,r,k);}else{scanf("%lld %lld\n",&l,&r);query(l,r);}}return 0;}

2.P5350 序列

题目链接:P5350 序列

题意:要求维护一个数据结构,支持对给定数组进行以下操作

1 l r求 $[l,r]$ 的区间和

2 l r v将 $[l,r]$ 赋值为 $v$

3 l r v将 $[l,r]$ 加上 $v$

4 l1 r1 l2 r2将 $[l_1,r_1]$ 复制到 $[l_2,r_2]$

5 l1 r1 l2 r2将 $[l_1,r_1]$ 和 $[l_2,r_2]$ 交换

6 l r将 $[l,r]$翻转

我直呼神仙题

本题前三个操作就是基本操作

后三个操作值得探讨

首先是将 $[l_1,r_1]$ 中所有数复制到 $[l_2,r_2]$

我们可以把 $[l_1,r_1]$ 中所有结点记录,然后直接用这些结点推平 $[l_2,r_2]$

struct THR{int l,r,v;};void copy(R int l1,R int r1,R int l2,R int r2){set<node>::iterator itr=split(r1+1),itl=split(l1);vector<THR>vec;R int tmp=l2-l1;for(set<node>::iterator it=itl; it!=itr; it++)vec.push_back({it->l+tmp,it->r+tmp,it->v});for(R int i=0; i<vec.size(); i++)assign(vec[i].l,vec[i].r,vec[i].v);}

其次是将 $[l_1,r_1]$ 和 $[l_2,r_2]$ 交换

我比较懒,直接把 $[l_1,r_1]$ 复制到 $[n+1,n+r_1-l_1+1]$

然后就像当年 int c=a;a=b;b=c;一样交换就行了

void Swap(R int l1,R int r1,R int l2,R int r2){copy(l1,r1,n+1,n+r1-l1+1);copy(l2,r2,l1,r1);copy(n+1,n+r1-l1+1,l2,r2);}

最后是区间翻转操作

这个不难,记录每个结点,然后改下左右端点插入就好

void rev(R int l,R int r){set<node>::iterator itr=split(r+1),itl=split(l);vector<THR>vec;for(set<node>::iterator it=itl; it!=itr; it++)vec.push_back({it->l,it->r,it->v});R int cnt=r;s.erase(itl,itr);for(R int i=0; i<vec.size(); i++){s.insert({cnt-(vec[i].r-vec[i].l),cnt,vec[i].v});cnt-=(vec[i].r-vec[i].l+1);}}

完整代码

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(3e5+5)#define mod (int)(1e9+7)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m;int a[MAXN];struct node{int l,r;mutable int v;const bool operator<(const node &o)const{return l<o.l;}};set<node> s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it;it--;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert({l,r,k});}int sum(R int l,R int r){R int ans=0;set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)ans=(ans+(it->v*(it->r-it->l+1)%mod)%mod)%mod;return ans;}struct THR{int l,r,v;};void add(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)it->v=(it->v+k)%mod;}void copy(R int l1,R int r1,R int l2,R int r2){set<node>::iterator itr=split(r1+1),itl=split(l1);vector<THR>vec;R int tmp=l2-l1;for(set<node>::iterator it=itl; it!=itr; it++)vec.push_back({it->l+tmp,it->r+tmp,it->v});for(R int i=0; i<vec.size(); i++)assign(vec[i].l,vec[i].r,vec[i].v);}void Swap(R int l1,R int r1,R int l2,R int r2){copy(l1,r1,n+1,n+r1-l1+1);copy(l2,r2,l1,r1);copy(n+1,n+r1-l1+1,l2,r2);}void rev(R int l,R int r){set<node>::iterator itr=split(r+1),itl=split(l);vector<THR>vec;for(set<node>::iterator it=itl; it!=itr; it++)vec.push_back({it->l,it->r,it->v});R int cnt=r;s.erase(itl,itr);for(R int i=0; i<vec.size(); i++){s.insert({cnt-(vec[i].r-vec[i].l),cnt,vec[i].v});cnt-=(vec[i].r-vec[i].l+1);}}signed main(){read(n);read(m);for(R int i=1; i<=n; i++)read(a[i]),s.insert({i,i,a[i]});while(m--){R int op,l1,l2,r1,r2,v;read(op);read(l1);read(r1);if(op==1)printf("%lld\n",sum(l1,r1)); // 求和else if(op==2){read(v);assign(l1,r1,v);} // 赋值else if(op==3){read(v);add(l1,r1,v);} // 区间加else if(op==4){read(l2);read(r2);copy(l1,r1,l2,r2);} // 复制else if(op==5){read(l2);read(r2);Swap(l1,r1,l2,r2);} // 交换else {rev(l1,r1);} // 翻转}set<node>::iterator it=s.begin();R int idx=0;for(;it!=s.end();it++){for(R int i=it->l; i<=it->r&&idx<n; idx++,i++)printf("%lld%c",it->v," \n"[idx==n-1]);}return 0;}

3.CF343D Water Tree

题目链接:CF343D Water Tree

题意:给出一棵以 $1$ 为根节点的 $n$ 个节点的有根树。每个点有一个权值,初始为 $0$ ,支持以下操作

1 u将点 $u$ 和其子树上的所有节点的权值改为 $1$

2 u将点 $u$ 到 $1$ 的路径上的所有节点的权值改为 $0$

3 u询问 $u$ 的权值

简单的树链剖分+珂朵莉树

单点查询只要lower_bound()就行了

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e5+5)int n,m;int a[MAXN];int fa[MAXN],son[MAXN],siz[MAXN];int dep[MAXN],idx,top[MAXN],id[MAXN];template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}struct Edge{int u,v,next;}e[MAXN<<1];struct node{int l,r;mutable int v;const bool operator<(const node &o)const{return l<o.l;}};set<node> s;int head[MAXN],pos=1;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it;--it;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert({l,r,k});}void add(R int u,R int v){e[pos]={u,v,head[u]};head[u]=pos++;}void dfs(R int u,R int f,R int d){dep[u]=d;fa[u]=f;siz[u]=1;R int mx=-1;for(R int i=head[u]; i; i=e[i].next){R int v=e[i].v;if(v==f)continue;dfs(v,u,d+1);siz[u]+=siz[v];if(siz[v]>mx)mx=siz[v],son[u]=v;}}void dfs(R int u,R int ftop){top[u]=ftop;id[u]=++idx;a[idx]=0;if(!son[u])return;dfs(son[u],ftop);for(R int i=head[u]; i; i=e[i].next){R int v=e[i].v;if(v==fa[u]||v==son[u])continue;dfs(v,v);}}void upRange(R int x,R int y){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);assign(id[top[x]],id[x],0);x=fa[x];}if(dep[x]>dep[y])swap(x,y);assign(id[x],id[y],0);}void upSon(R int u){assign(id[u],id[u]+siz[u]-1,1);}int query(R int u){set<node>::iterator pos=s.lower_bound({id[u]});if(pos==s.end()||pos->l>id[u])--pos;return pos->v;}signed main(){read(n);for(R int i=1,u,v; i<=n-1; i++){read(u);read(v);add(u,v);add(v,u);}dfs(1,0,1);dfs(1,1);for(R int i=1; i<=n; i++)s.insert({i,i,0});read(m);while(m--){R int op,k;read(op);read(k);if(op==1)upSon(k);if(op==2)upRange(1,k);if(op==3)printf("%lld\n",query(k));}return 0;}

4.CF915E Physical Education Lessons

题目链接:CF915E Physical Education Lessons

题意:区间赋值为 $1$ 或 $0$ ,求 $1$ 个数 (我用 $1$ 表示题目中的工作日)

这题比板子题还要简单

唯一要注意的是每次输出结果不能去扫一遍(复杂度爆炸),而是在每次修改时统计

直接贴代码了(

#include <bits/stdc++.h>using namespace std;#define int long long#define R registerint n,Q,ans;template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-');f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}struct node{int l,r;mutable int v;const bool operator<(const node &o)const{return l<o.l;}};set<node>s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it;--it;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)ans-=it->v*(it->r-it->l+1);s.erase(itl,itr);s.insert({l,r,k});printf("%lld\n",ans+=k*(r-l+1));}signed main(){read(n);read(Q);ans=n;s.insert({1,n,1});while(Q--){R int op,l,r;read(l);read(r);read(op);assign(l,r,op-1);}return 0;}

总结

本文简单介绍了珂朵莉树

顺便讲了几道简单的例题


题外话

作为珂学家,怎么能少了这环节呢(

送上本人找到的高清无水印壁纸一份(


再来一个(


如果幸福有颜色的话,那一定是终末之红染尽的蓝色!

]]>
+ 浅谈珂朵莉树(ODT)

前言

珂学家狂喜(


一、珂朵莉树来源

珂朵莉树,原名老司机树(Old DriverTree),在某场CF比赛中提出

因为题目背景是《末日时在做什么?有没有空?可以来拯救吗?》中的珂朵莉,所以就叫珂朵莉树了


二、珂朵莉树

题目链接:CF896CWillem, Chtholly and Seniorious

题目要求维护一种数据结构,支持以下操作

  1. \([l,r]\) 区间内所有数加上 \(x\)
  2. \([l,r]\)区间内所有数改成 \(x\)
  3. \([l,r]\) 区间第 \(k\) 小的数
  4. \([l,r]\) 区间内所有数的 \(x\) 次方的和取模 \(y\)

值得注意的是,数据为随机数据,说明没有故意构造卡的数据

1.珂朵莉树有什么用?

最主要的就是区间内的推平操作了,即本题中的操作 \(2\)

当然还有别的操作,那就不是最主要的了

2.原理是什么?

写在前面:这个数据结构唯一前置知识就只有set

a.存储

我们可以把区间看作若干个结点,每个结点都有自己的左端点 \(l\) 、右端点 \(r\) 以及值 \(v\)

显然,一开始的时候每个结点的 \(l=r=idx\) , \(idx\) 指该元素在数组中的下标

我们可以把这些结点按左端点顺序存储在一个set

struct node{int l,r; // 左、右端点mutable int v; // 值const bool operator<(const node &o)const{return l<o.l;}};

注意 int v前的关键字mutable,这个关键字和const恰好相反,意思是使v始终允许修改,即使它是个常量

那么我们为什么要多次一举呢?过会再说(

b.分割结点

显然每次推平操作并不能保证区间左、右端点恰好就在一个结点上,因此我们还需要对结点进行分割

考虑查找一个结点,使得其左端点恰好为 \(pos\) ,\(pos\) 指某次操作中的一个分割点

我们可以用set中的lower_bound()函数来找到第一个左端点大于等于\(pos\) 的结点

(注:因为我们是按左端点顺序排序的)

这个lower_bound()会返回一个set常量迭代器

现在我们找到了一个结点,那么会出现以下三种情况

第一种情况\(pos\)恰好为一个结点的左端点,直接返回这个端点的迭代器(显然前提是这个结点不是s.end()

那么其他情况得到的这个结点的左端点一定\(pos\) 大

因此可以尝试分割前一个结点,即把迭代器it--

第二种情况:这个结点的右端点小于 \(pos\) ,由于结点的端点一定是连续的,说明\(pos\) 是新加入的结点,直接return s.end()

第三种情况:最普遍的情况,找到了一个结点恰好包含\(pos\) ,因为我们要的是以 \(pos\)为左端点的结点,显然我们要把这个结点分割成 \(node\{l,pos-1,v\}\) 和 \(node\{pos,r,v\}\)

set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it; // 情况1it--;if(it->r<pos)return s.end(); // 情况2R int l=it->l,r=it->r,v=it->v; // 情况3s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;     // insert函数的返回值是pair类型的,而它的first恰好使我们需要的(新插入结点的指针)}

c.推平

解决了区间的端点问题,我们只要获取要求修改区间左、右端点的结点,把这一段删除,再插入要求赋的值和修改区间的左、右端点作为新的结点

注意一定要先找右端点所在结点,再找左端点所在结点

为什么?因为我们再分割结点时大概率删除了部分结点,并加入新的结点,如果我们先找左端点所在结点,再找右端点所在结点,很有左端点所在结点的迭代器已经失效了

例如有一个结点 \(node\{l=1,r=5\}\),修改区间的左端点为 \(1\) ,右端点为\(3\)

按先左再右的顺序,我们先会得到左端点所在结点

\(node\{l=1,r=5\}\)

显然如果我们找右端点所在结点,会将左端点所在结点进行分割,那么原来的结点就没了,迭代器失效,然后RE

inline void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l); // 找结点s.erase(itl,itr); // 删掉[itl,itr)中所有结点s.insert({l,r,k}); // 插入新结点}

d.剩余操作

区间加,十分暴力,十分简单,我们只要找到左、右端点所在结点,然后直接把每个结点修改就行

现在知道为什么要写mutable了吧!因为split()返回的是常量迭代器

inline void add(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);for(R set<node>::iterator it=itl; it!=itr; it++)it->v+=k; // 直接加}

区间第 \(k\)小数,我们只要把区间内的所有结点取出来从大到小排序即可

注意每个结点指代的可能是一段区间,而我们要求的是第 \(k\) 小的数,因此每遍历一个结点,如果 \(k\) 大于该结点指代的区间长,则让 \(k\) 减去该结点指代的区间长,否则第 \(k\)小的数就在该结点区间内,直接输出即可

struct Rank{int num,cnt;const bool operator<(const Rank &o)const{return num<o.num;}};inline int rnk(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);vector<Rank> v;for(R set<node>::iterator it=itl; it!=itr; it++)v.push_back({it->v,it->r - it->l +1}); // 记录结点sort(v.begin(),v.end());for(R int i=0; i<v.size(); i++)if(v[i].cnt<k)k-=v[i].cnt; // 减去区间长else return v[i].num;return -1; // 这句显然在数据合法时没有任何用}

区间求和,只要遍历一下,加一下乘一下取模一下就好

inline int sum(R int l,R int r,R int x,R int y){R int ans=0;set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)ans=(ans%y+qpow(it->v,x,y)%y*(it->r-it->l+1)%y)%y; // 快速幂qpow()就不贴了return ans;}

3.复杂度分析

set实现的珂朵莉树复杂度为 \(O(n\log^2n)\)

不过本人不是很会分析,这篇文章分析证明的很好,大家可以看看

完整代码 (注:原题的随机数据是给定 \(seed\) 等自行生成)

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define mod (int)(1e9+7)#define MAXN (int)(1e5+5)int n,m,seed,vmax;int qpow(R int a,R int b,R int p){R int ans=1,base=a;while(b){if(b&1)ans=(ans%p*base%p)%p;base=(base%p*base%p)%p;b>>=1;}return ans%p;}struct node{int l,r;mutable int v;const bool operator<(const node &o)const{return l<o.l;}};int rnd(){R int ret=seed;seed=(seed*7+13)%mod;return ret;}int a[MAXN];set<node> s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos) return it;it--;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}inline void add(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);for(R set<node>::iterator it=itl; it!=itr; it++)it->v+=k;}inline void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);//[itl,itr)s.insert({l,r,k});}struct Rank{int num,cnt;const bool operator<(const Rank &o)const{return num<o.num;}};inline int rnk(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);vector<Rank> v;for(R set<node>::iterator it=itl; it!=itr; it++)v.push_back({it->v,it->r - it->l +1});sort(v.begin(),v.end());for(R int i=0; i<v.size(); i++)if(v[i].cnt<k)k-=v[i].cnt;else return v[i].num;return -1;}inline int sum(R int l,R int r,R int x,R int y){R int ans=0;set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)ans=(ans%y+qpow(it->v,x,y)%y*(it->r-it->l+1)%y)%y;return ans;}signed main(){scanf("%lld%lld%lld%lld",&n,&m,&seed,&vmax);for(R int i=1; i<=n; i++){a[i]=(rnd()%vmax)+1;s.insert({i,i,a[i]});}for(R int i=1; i<=m; i++){R int op,l,r,x,y;op=(rnd()%4)+1;l=(rnd()%n)+1;r=(rnd()%n)+1;if(l>r)swap(l,r);if(op==3){x=(rnd()%(r-l+1))+1;}else {x=(rnd()%vmax)+1;}if(op==4){y=(rnd()%vmax)+1;}if(op==1)add(l,r,x);else if(op==2)assign(l,r,x);else if(op==3)printf("%lld\n",rnk(l,r,x));else printf("%lld\n",sum(l,r,x,y));}return 0;}/**在太阳西斜的这个世界里,置身天上之森,*等这场战争结束之后,不归之人与望眼欲穿的人们,*人人本着正义之名,长存不灭的过去,逐渐消逝的未来,*我回来了,纵使日薄西山,即便看不到未来,*此时此刻的光辉,盼君勿忘**————世上最幸福的女孩*/

三、珂朵莉树例题

(注:都是洛谷上的题~)

1.P4979 矿洞:坍塌

题目链接:P4979矿洞:坍塌

题意:要求维护一个数据结构,支持对给定字符串进行如下操作

A x y op表示替换材料,将 \(x\) 到 \(y(1\lex\le y\le N)\)区间内的材料替换为opop\(A,B,C\) 三种材料字符中的一个

B x y表示是否询问,即询问 \(x\) 到 \(y(1\lex\le y\leN)\)区间内的材料是否合法,合法输出Yes,不合法输出No

合法指该区间连续且材料相等,并且该区间前一个和后一个材料不相同

几乎是板子题,没什么特别的,只要跟题目意思写查询操作就行

不过要注意的是,出题人卡了无优化的ODT

那么怎么优化呢?我们只要在每次查询时将相同值的相邻结点合并即可

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e5+5)int n,Q;char a[MAXN];struct node{int l,r;mutable char v;const bool operator<(const node &o)const{return l<o.l;}};set<node>s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it;it--;if(it->r<pos)return s.end();R int l=it->l,r=it->r;R char v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}inline void assign(R int l,R int r,R char k){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert({l,r,k});}inline void query(R int l,R int r){set<node>::iterator itr=split(r+1),itl=split(l);if(l!=r){set<node>::iterator it=itl,last=itl;it++;for(; it!=itr; it++,last++)if(it->v!=last->v){puts("No");R int _l=itl->l,_r=last->r;R char _v=last->v;s.erase(itl,it);s.insert({_l,_r,_v});return;}}R char _v=itl->v;s.erase(itl,itr);s.insert({l,r,_v});if(l==1||r==n){puts("Yes");return;}set<node>::iterator ib=split(r+1),ia=split(l-1);puts((ia->v!=ib->v)?"Yes":"No");}signed main(){scanf("%lld %s\n",&n,a+1);for(R int i=1; i<=n; i++){R int l=i,r=i;while(a[l]==a[i])r=++i;s.insert({l,r,a[--i]});}scanf("%lld\n",&Q);while(Q--){R int l,r;R char op,k;scanf("%c",&op);if(op=='A'){scanf("%lld %lld %c\n",&l,&r,&k);assign(l,r,k);}else{scanf("%lld %lld\n",&l,&r);query(l,r);}}return 0;}

2.P5350 序列

题目链接:P5350序列

题意:要求维护一个数据结构,支持对给定数组进行以下操作

1 l r\([l,r]\)的区间和

2 l r v\([l,r]\)赋值为 \(v\)

3 l r v\([l,r]\)加上 \(v\)

4 l1 r1 l2 r2\([l_1,r_1]\) 复制到 \([l_2,r_2]\)

5 l1 r1 l2 r2\([l_1,r_1]\) 和 \([l_2,r_2]\) 交换

6 l r\([l,r]\)翻转

我直呼神仙题

本题前三个操作就是基本操作

后三个操作值得探讨

首先是将 \([l_1,r_1]\)中所有数复制到 \([l_2,r_2]\)

我们可以把 \([l_1,r_1]\)中所有结点记录,然后直接用这些结点推平 \([l_2,r_2]\)

struct THR{int l,r,v;};void copy(R int l1,R int r1,R int l2,R int r2){set<node>::iterator itr=split(r1+1),itl=split(l1);vector<THR>vec;R int tmp=l2-l1;for(set<node>::iterator it=itl; it!=itr; it++)vec.push_back({it->l+tmp,it->r+tmp,it->v});for(R int i=0; i<vec.size(); i++)assign(vec[i].l,vec[i].r,vec[i].v);}

其次是将 \([l_1,r_1]\)\([l_2,r_2]\) 交换

我比较懒,直接把 \([l_1,r_1]\)复制到 \([n+1,n+r_1-l_1+1]\)

然后就像当年 int c=a;a=b;b=c;一样交换就行了

void Swap(R int l1,R int r1,R int l2,R int r2){copy(l1,r1,n+1,n+r1-l1+1);copy(l2,r2,l1,r1);copy(n+1,n+r1-l1+1,l2,r2);}

最后是区间翻转操作

这个不难,记录每个结点,然后改下左右端点插入就好

void rev(R int l,R int r){set<node>::iterator itr=split(r+1),itl=split(l);vector<THR>vec;for(set<node>::iterator it=itl; it!=itr; it++)vec.push_back({it->l,it->r,it->v});R int cnt=r;s.erase(itl,itr);for(R int i=0; i<vec.size(); i++){s.insert({cnt-(vec[i].r-vec[i].l),cnt,vec[i].v});cnt-=(vec[i].r-vec[i].l+1);}}

完整代码

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(3e5+5)#define mod (int)(1e9+7)template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m;int a[MAXN];struct node{int l,r;mutable int v;const bool operator<(const node &o)const{return l<o.l;}};set<node> s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it;it--;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert({l,r,k});}int sum(R int l,R int r){R int ans=0;set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)ans=(ans+(it->v*(it->r-it->l+1)%mod)%mod)%mod;return ans;}struct THR{int l,r,v;};void add(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)it->v=(it->v+k)%mod;}void copy(R int l1,R int r1,R int l2,R int r2){set<node>::iterator itr=split(r1+1),itl=split(l1);vector<THR>vec;R int tmp=l2-l1;for(set<node>::iterator it=itl; it!=itr; it++)vec.push_back({it->l+tmp,it->r+tmp,it->v});for(R int i=0; i<vec.size(); i++)assign(vec[i].l,vec[i].r,vec[i].v);}void Swap(R int l1,R int r1,R int l2,R int r2){copy(l1,r1,n+1,n+r1-l1+1);copy(l2,r2,l1,r1);copy(n+1,n+r1-l1+1,l2,r2);}void rev(R int l,R int r){set<node>::iterator itr=split(r+1),itl=split(l);vector<THR>vec;for(set<node>::iterator it=itl; it!=itr; it++)vec.push_back({it->l,it->r,it->v});R int cnt=r;s.erase(itl,itr);for(R int i=0; i<vec.size(); i++){s.insert({cnt-(vec[i].r-vec[i].l),cnt,vec[i].v});cnt-=(vec[i].r-vec[i].l+1);}}signed main(){read(n);read(m);for(R int i=1; i<=n; i++)read(a[i]),s.insert({i,i,a[i]});while(m--){R int op,l1,l2,r1,r2,v;read(op);read(l1);read(r1);if(op==1)printf("%lld\n",sum(l1,r1)); // 求和else if(op==2){read(v);assign(l1,r1,v);} // 赋值else if(op==3){read(v);add(l1,r1,v);} // 区间加else if(op==4){read(l2);read(r2);copy(l1,r1,l2,r2);} // 复制else if(op==5){read(l2);read(r2);Swap(l1,r1,l2,r2);} // 交换else {rev(l1,r1);} // 翻转}set<node>::iterator it=s.begin();R int idx=0;for(;it!=s.end();it++){for(R int i=it->l; i<=it->r&&idx<n; idx++,i++)printf("%lld%c",it->v," \n"[idx==n-1]);}return 0;}

3.CF343D Water Tree

题目链接:CF343DWater Tree

题意:给出一棵以 \(1\) 为根节点的 \(n\)个节点的有根树。每个点有一个权值,初始为 \(0\) ,支持以下操作

1 u将点 \(u\)和其子树上的所有节点的权值改为 \(1\)

2 u将点 \(u\)\(1\) 的路径上的所有节点的权值改为 \(0\)

3 u询问 \(u\)的权值

简单的树链剖分+珂朵莉树

单点查询只要lower_bound()就行了

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (int)(5e5+5)int n,m;int a[MAXN];int fa[MAXN],son[MAXN],siz[MAXN];int dep[MAXN],idx,top[MAXN],id[MAXN];template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}struct Edge{int u,v,next;}e[MAXN<<1];struct node{int l,r;mutable int v;const bool operator<(const node &o)const{return l<o.l;}};set<node> s;int head[MAXN],pos=1;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it;--it;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);s.erase(itl,itr);s.insert({l,r,k});}void add(R int u,R int v){e[pos]={u,v,head[u]};head[u]=pos++;}void dfs(R int u,R int f,R int d){dep[u]=d;fa[u]=f;siz[u]=1;R int mx=-1;for(R int i=head[u]; i; i=e[i].next){R int v=e[i].v;if(v==f)continue;dfs(v,u,d+1);siz[u]+=siz[v];if(siz[v]>mx)mx=siz[v],son[u]=v;}}void dfs(R int u,R int ftop){top[u]=ftop;id[u]=++idx;a[idx]=0;if(!son[u])return;dfs(son[u],ftop);for(R int i=head[u]; i; i=e[i].next){R int v=e[i].v;if(v==fa[u]||v==son[u])continue;dfs(v,v);}}void upRange(R int x,R int y){while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);assign(id[top[x]],id[x],0);x=fa[x];}if(dep[x]>dep[y])swap(x,y);assign(id[x],id[y],0);}void upSon(R int u){assign(id[u],id[u]+siz[u]-1,1);}int query(R int u){set<node>::iterator pos=s.lower_bound({id[u]});if(pos==s.end()||pos->l>id[u])--pos;return pos->v;}signed main(){read(n);for(R int i=1,u,v; i<=n-1; i++){read(u);read(v);add(u,v);add(v,u);}dfs(1,0,1);dfs(1,1);for(R int i=1; i<=n; i++)s.insert({i,i,0});read(m);while(m--){R int op,k;read(op);read(k);if(op==1)upSon(k);if(op==2)upRange(1,k);if(op==3)printf("%lld\n",query(k));}return 0;}

4.CF915E Physical EducationLessons

题目链接:CF915EPhysical Education Lessons

题意:区间赋值为 \(1\) 或 \(0\) ,求 \(1\) 个数 (我用 \(1\) 表示题目中的工作日)

这题比板子题还要简单

唯一要注意的是每次输出结果不能去扫一遍(复杂度爆炸),而是在每次修改时统计

直接贴代码了(

#include <bits/stdc++.h>using namespace std;#define int long long#define R registerint n,Q,ans;template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-');f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}struct node{int l,r;mutable int v;const bool operator<(const node &o)const{return l<o.l;}};set<node>s;set<node>::iterator split(R int pos){set<node>::iterator it=s.lower_bound({pos});if(it!=s.end()&&it->l==pos)return it;--it;if(it->r<pos)return s.end();R int l=it->l,r=it->r,v=it->v;s.erase(it);s.insert({l,pos-1,v});return s.insert({pos,r,v}).first;}void assign(R int l,R int r,R int k){set<node>::iterator itr=split(r+1),itl=split(l);for(set<node>::iterator it=itl; it!=itr; it++)ans-=it->v*(it->r-it->l+1);s.erase(itl,itr);s.insert({l,r,k});printf("%lld\n",ans+=k*(r-l+1));}signed main(){read(n);read(Q);ans=n;s.insert({1,n,1});while(Q--){R int op,l,r;read(l);read(r);read(op);assign(l,r,op-1);}return 0;}

总结

本文简单介绍了珂朵莉树

顺便讲了几道简单的例题


题外话

作为珂学家,怎么能少了这环节呢(

送上本人找到的高清无水印壁纸一份(

再来一个(

如果幸福有颜色的话,那一定是终末之红染尽的蓝色!

]]>
@@ -9005,7 +9005,7 @@ /2021/08/13/pei-shu-ding-li-ji-qi-zheng-ming/ - 裴蜀定理及其证明

前言

原来裴蜀是法国数学家QwQ


一、裴蜀定理

对于 $x,y$ 的二元一次不定方程 $ax+by=c$ ,其有解的充要条件为 $\gcd(a,b)\mid c$

1.充分性证明

充分性:若 $\gcd(a,b)\mid c$ ,则 $ax+by=c$ 有解

设 $k$ 为 $a,b$ 线性组合的最小非负解

令 $q=\left\lfloor\dfrac{a}{k}\right\rfloor$ ,则有 $r=a \mod k = a-qk = a-q(ax+by) = a(1-qx)+b(-qy)$

显然 $r$ 也为 $a,b$ 线性组合的解,且 $0\le r \le k$

$\because k$ 为最小非负解

$\therefore r=0$

$\therefore k\mid a$

同理 $k\mid b$

令 $d=\gcd(a,b)$ ,则 $s\mid d$ 且 $d \ge s$

$\because d\mid a,d\mid b$ 且 $s$ 为 $a,b$ 线性组合的解

$\therefore d\mid s$

$\because s>0$

$\therefore d=s$

则 $ax+by=c$ 的最小非负解为 $\gcd(a,b)$

显然 $\forall c=k\gcd(a,b),k\in \mathbb{Z}^+$ 是原方程的解

2.必要性证明

必要性:若 $ax+by=c$ 有解,则 $\gcd(a,b)\mid c$
证明:
令 $d=\gcd(a,b)$ ,则 $d\mid a,d\mid b$

$\because ax+by=c$ 有解

$\therefore d\mid ax,d\mid by$

$\therefore d\mid ax+by=c$

3.推广

对于不定方程 $x_1y_1+x_2y_2+…+x_ny_n=k, \forall y_i \in \mathbb{Z}$ ,其有解的充要条件为 $\gcd\{x_i\}\mid k$

证明略


二、裴蜀定理模板题

题目链接:P4549 【模板】裴蜀定理

即推广结论裸题

要注意负数要转成正数再算 $\gcd$

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R registerint n,ans,a[25];int gcd(R int a,R int b){return b==0?a:gcd(b,a%b);}signed main(){scanf("%lld",&n);for(R int i=1; i<=n; i++){scanf("%lld",&a[i]);a[i]=a[i]>0?a[i]:-a[i];ans=gcd((i==1?a[1]:ans),a[i]);}printf("%lld\n",ans);return 0;}

总结

本文简单介绍了裴蜀定理

]]>
+ 裴蜀定理及其证明

前言

原来裴蜀是法国数学家QwQ


一、裴蜀定理

对于 \(x,y\)的二元一次不定方程 \(ax+by=c\) ,其有解的充要条件为 \(\gcd(a,b)\mid c\)

1.充分性证明

充分性:若 \(\gcd(a,b)\mid c\) ,则\(ax+by=c\) 有解

\(k\)\(a,b\) 线性组合的最小非负解

\(q=\left\lfloor\dfrac{a}{k}\right\rfloor\),则有 \(r=a \mod k = a-qk = a-q(ax+by) =a(1-qx)+b(-qy)\)

显然 \(r\) 也为 \(a,b\) 线性组合的解,且 \(0\le r \le k\)

\(\because k\) 为最小非负解

\(\therefore r=0\)

\(\therefore k\mid a\)

同理 \(k\mid b\)

\(d=\gcd(a,b)\) ,则 \(s\mid d\) 且 \(d\ge s\)

\(\because d\mid a,d\mid b\)\(s\)\(a,b\) 线性组合的解

\(\therefore d\mid s\)

\(\because s>0\)

\(\therefore d=s\)

\(ax+by=c\) 的最小非负解为 \(\gcd(a,b)\)

显然 \(\forall c=k\gcd(a,b),k\in\mathbb{Z}^+\) 是原方程的解

2.必要性证明

必要性:若 \(ax+by=c\) 有解,则\(\gcd(a,b)\mid c\) 证明: 令 \(d=\gcd(a,b)\) ,则 \(d\mid a,d\mid b\)

\(\because ax+by=c\) 有解

\(\therefore d\mid ax,d\mid by\)

\(\therefore d\mid ax+by=c\)

3.推广

对于不定方程 \(x_1y_1+x_2y_2+...+x_ny_n=k,\forall y_i \in \mathbb{Z}\) ,其有解的充要条件为 \(\gcd\{x_i\}\mid k\)

证明略


二、裴蜀定理模板题

题目链接:P4549【模板】裴蜀定理

即推广结论裸题

要注意负数要转成正数再算 \(\gcd\)

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R registerint n,ans,a[25];int gcd(R int a,R int b){return b==0?a:gcd(b,a%b);}signed main(){scanf("%lld",&n);for(R int i=1; i<=n; i++){scanf("%lld",&a[i]);a[i]=a[i]>0?a[i]:-a[i];ans=gcd((i==1?a[1]:ans),a[i]);}printf("%lld\n",ans);return 0;}

总结

本文简单介绍了裴蜀定理

]]>
@@ -9030,7 +9030,7 @@ /2021/08/10/ubuntu-qiang-zhi-xie-zai-vmware-player/ - ubuntu 强制卸载vmware player

不知道什么时候下了vmware-player,然后怎么都删不掉

解决方法

打开终端,输入以下指令

locate vmware-player

然后会出现一大堆vmware player的文件

复制一下,再把每一行都加上 sudo rmsudo rmdir

(注:这里注意一下删除顺序,rmdir似乎不能直接删除非空文件夹)

非常简单,非常暴力,直接删除这些文件

当然如果能正常卸载的话还是这个更好一点

sudo vmware-installer -u vmware-player

]]>
+ ubuntu 强制卸载vmware player

不知道什么时候下了vmware-player,然后怎么都删不掉

解决方法

打开终端,输入以下指令

locate vmware-player
然后会出现一大堆vmwareplayer的文件

复制一下,再把每一行都加上sudo rmsudo rmdir

(注:这里注意一下删除顺序,rmdir似乎不能直接删除非空文件夹)

非常简单,非常暴力,直接删除这些文件

当然如果能正常卸载的话还是这个更好一点

sudo vmware-installer -u vmware-player

]]>
@@ -9055,7 +9055,7 @@ /2021/08/09/qian-tan-wu-dao-lian-dlx/ - 浅谈舞蹈链(DLX)

前言

舞蹈链的名字真好玩…


一、舞蹈链概述

舞蹈链 (Dancing links),也叫 DLX ,是由 Donald Knuth 提出的数据结构,目的是快速实现他提出的X算法。X算法是一种递归算法,时间复杂度不确定,深度优先,通过回溯寻找精确覆盖问题所有可能的解

(以上摘自维基百科)

舞蹈链的主要思想来源于双向链表

我们设 $l[x]$ 表示元素 $x$ 的左指针, $r[x]$ 表示元素 $x$ 的右指针

显然,如果想要删除元素 $x$ ,我们可以做以下操作

r[l[x]]=r[x]; // x左侧的元素的右指针指向x右侧的元素l[r[x]]=l[x]; // x右侧的元素的左指针指向x左侧的元素

那恢复元素 $x$ 呢? 我们可以发现删除 $x$ 的时候, $x$ 的左右指针并没有改变,即 $l[x]$ 和 $r[x]$ 并没有改变,于是我们可以做以下操作

r[l[x]]=x; // x左侧的元素的右指针重新指向xl[r[x]]=x; // x右侧的元素的左指针重新指向x

这样如果 $x$ 左右两侧没有改变,我们就可以恢复 $x$ 所在的位置

那么精确覆盖问题又是什么呢?

给定矩阵,要求选出一个由若干行组成的集合,使得每一列上都有且仅有一个 $1$

例如该矩阵选出的行为 $1,4,5$ 行

我们来模拟一下朴素X算法求解的过程

以下过程用红色表示选择了这一行,绿色表示存在冲突的元素,灰色表示删除的行

1.选择第一行

2.标记所有和第一行冲突的元素

3.删除存在冲突的行

4.接着选择第二行

5.标记与第二行冲突的元素

6.删除存在冲突的行

7.发现没有可以选择的行了,而已选的不满足要求,回溯,选择第四行

8.接下来的同理,不断执行,直到找到答案

我们会发现, $X$ 算法花了大量的时间在找 $1$ ,而且删改很不方便

为了解决这个问题,舞蹈链就产生了

模板题 $\to$ P4929 【模板】舞蹈链(DLX)

(注:为了方便讲述,以下引用这篇博客中的图片(感谢图片的作者!))

舞蹈链的结构即交叉十字循环双向链,本文中以数组形式实现链表

int n,m; // 行、列数int u[MAXN],d[MAXN],l[MAXN],r[MAXN],h[MAXN]; // 每个结点的上下左右指针;每一行的头指针int row[MAXN],col[MAXN],s[MAXN],ansk[MAXN],pos; // 每个结点原先所在的行、列;每一列的结点个数;ansk记录搜索信息;结点总数

如下图所示
在这里插入图片描述
别急!我们一步一步来实现这个复杂的数据结构

首先初始化上方的列头结点

我们可以称列头结点为限制,行头结点为决策 (注:这个做题的时候有用)

void init(){for(R int i=0; i<=m; i++){l[i]=i-1;r[i]=i+1;u[i]=d[i]=i; }l[0]=m;r[m]=0; //循环链表memset(h,-1,sizeof(h)); //每一行的头结点都为空memset(s,0,sizeof(s)); //每一列的结点数都为0pos=m+1; //已经搭建好了m个列头结点,下一个加入的结点从m+1开始编号}

接下来,我们来把插入结点的功能完成(注:这里比较复杂,可以感性理解一下)
void link(R int x,R int y){s[y]++; //所在的列结点数加1row[pos]=x;col[pos]=y; //记录编号为pos的结点(即新加入的结点)的行和列u[pos]=y; //pos结点的上指针指向插入的列yd[pos]=d[y]; //pos结点的下指针指向插入位置下方的元素u[d[y]]=pos; //插入位置下方的元素的上指针指向pos结点d[y]=pos; //插入位置上方的下指针指向pos结点if(h[x]<0)h[x]=l[pos]=r[pos]=pos;//如果pos结点所在的这一行没有头结点,就自己当else //不然就插入头结点一侧 (下面的就不注释了,和上面插入的方法类似){l[pos]=l[h[x]];r[pos]=h[x];r[l[h[x]]]=pos;l[h[x]]=pos;}pos++; //下一个结点不要标错号了}

现在我们来完成删除和恢复操作(差不多的)
inline void rm(R int y){l[r[y]]=l[y];r[l[y]]=r[y];//删除y列的结点(这个位置已经填满了)for(R int i=d[y]; i!=y; i=d[i])for(R int j=r[i]; j!=i; j=r[j])//删除这一行(冲突的行){d[u[j]]=d[j];u[d[j]]=u[j];s[col[j]]--;//别忘了减1}}inline void rv(R int y){for(R int i=u[y]; i!=y; i=u[i])for(R int j=l[i]; j!=i; j=l[j])//恢复也是一样的{d[u[j]]=j;u[d[j]]=j;s[col[j]]++;}r[l[y]]=y;l[r[y]]=y;}

然后可以开始跳舞了
主体部分,就是深度优先搜索
bool dance(R int dep){if(!r[0])//所有的列头结点都被选了,说明成功了{for(R int i=0; i<dep; i++)printf("%lld%c",ansk[i]," \n"[i==dep-1]);return 1;}R int y=r[0];for(R int i=r[0]; i; i=r[i])if(s[i]<s[y])y=i;//这里是一个剪枝,每次选择结点最少的列能在一定情况下提高性能rm(y);//删掉这一列for(R int i=d[y]; i!=y; i=d[i])//每次选择这列中的一行{ansk[dep]=row[i];for(R int j=r[i]; j!=i; j=r[j])rm(col[j]);//删掉这一行中所有结点(这一行冲突)if(dance(dep+1))return 1;//成功就返回for(R int j=l[i]; j!=i; j=l[j])rv(col[j]);//恢复}rv(y);return 0;}

如果您不太理解的话,可以看看下面的动图(注:其实和之前模拟的有些相似)

算法执行过程 (注:图片是这篇博客的)

在这里插入图片描述

最终的答案即下图 (选择 $1,4,5$ )
在这里插入图片描述
最后贴上完整代码

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN 250505template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m;int u[MAXN],d[MAXN],l[MAXN],r[MAXN],h[MAXN];int row[MAXN],col[MAXN],s[MAXN],ansk[MAXN],pos;void init(){for(R int i=0; i<=m; i++){l[i]=i-1;r[i]=i+1;u[i]=d[i]=i;}l[0]=m;r[m]=0;memset(h,-1,sizeof(h));memset(s,0,sizeof(s));pos=m+1;}void link(R int x,R int y){s[y]++;row[pos]=x;col[pos]=y;u[pos]=y;d[pos]=d[y];u[d[y]]=pos;d[y]=pos;if(h[x]<0)h[x]=l[pos]=r[pos]=pos;else{l[pos]=l[h[x]];r[pos]=h[x];r[l[h[x]]]=pos;l[h[x]]=pos;}pos++;}inline void rm(R int y){l[r[y]]=l[y];r[l[y]]=r[y];for(R int i=d[y]; i!=y; i=d[i])for(R int j=r[i]; j!=i; j=r[j]){d[u[j]]=d[j];u[d[j]]=u[j];s[col[j]]--;}}inline void rv(R int y){for(R int i=u[y]; i!=y; i=u[i])for(R int j=l[i]; j!=i; j=l[j]){d[u[j]]=j;u[d[j]]=j;s[col[j]]++;}r[l[y]]=y;l[r[y]]=y;}bool dance(R int dep){if(!r[0]){for(R int i=0; i<dep; i++)printf("%lld%c",ansk[i]," \n"[i==dep-1]);return 1;}R int y=r[0];for(R int i=r[0]; i; i=r[i])if(s[i]<s[y])y=i;rm(y);for(R int i=d[y]; i!=y; i=d[i]){ansk[dep]=row[i];for(R int j=r[i]; j!=i; j=r[j])rm(col[j]);if(dance(dep+1))return 1;for(R int j=l[i]; j!=i; j=l[j])rv(col[j]);}rv(y);return 0;}signed main(){read(n);read(m);init();for(R int i=1; i<=n; i++)for(R int j=1,t; j<=m; j++){read(t);if(t)link(i,j);}if(!dance(0))puts("No Solution!");return 0;}


二、舞蹈链例题

如果您看到这里,还是很明白的话,那么我们来讲个例题

题目链接:SP13980 SUDOGOB - Sudoku goblin

题意:给定一个 $9 \times 9$ 的数独,输出可填的方案数,多组数据

选择这个例题当然不是让你写暴搜的

首先考虑决策

每个格子上填数字,至多有 $9\times 9\times 9 = 729$ 种决策

再考虑限制

  1. 每个点只能填一个数
  2. 每行一个数只能填一次
  3. 每列一个数只能填一次
  4. 每个九宫格一个数只能填一次

限制数为 $9\times 9 \times 4 = 324$

对于精准覆盖问题,我们本质上是在选择若干决策,使其恰好满足所有限制条件

因此这题可以用 DLX 求解

那么 MAXN只要开到 729*324 就行了(注:不过开大点保险)

这题唯一的细节是插入结点的行、列,还是很简单的题目

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (729*324+6666)int Q;bool suc;int a[25][25];int ans[25][25],Ans;int h[MAXN],l[MAXN],r[MAXN],u[MAXN],d[MAXN];int row[MAXN],col[MAXN],pos,s[MAXN],ansk[MAXN];template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}inline void init(){int m=324;Ans=0;suc=0;for(R int i=0; i<=m; i++){l[i]=i-1;r[i]=i+1;u[i]=d[i]=i;}l[0]=m;r[m]=0;memset(s,0,sizeof(s));memset(h,-1,sizeof(h));pos=m+1;}inline void link(R int x,R int y){s[y]++;row[pos]=x;col[pos]=y;u[pos]=y;d[pos]=d[y];u[d[y]]=pos;d[y]=pos;if(h[x]<0)h[x]=l[pos]=r[pos]=pos;else{l[pos]=l[h[x]];r[pos]=h[x];r[l[h[x]]]=pos;l[h[x]]=pos;}++pos;}inline void rm(R int y){r[l[y]]=r[y];l[r[y]]=l[y];for(R int i=d[y]; i!=y; i=d[i])for(R int j=r[i]; j!=i; j=r[j]){u[d[j]]=u[j];d[u[j]]=d[j];s[col[j]]--;}}inline void rv(R int y){for(R int i=u[y]; i!=y; i=u[i])for(R int j=l[i]; j!=i; j=l[j]){u[d[j]]=j;d[u[j]]=j;s[col[j]]++;}l[r[y]]=y;r[l[y]]=y;}void dance(R int dep){if(!r[0]){Ans++;if(Ans>1)suc=1;for(R int i=0; i<dep&&!suc; i++){R int x=(ansk[i]-1)/9/9+1;R int y=(ansk[i]-1)/9%9+1;R int z=(ansk[i]-1)%9+1;ans[x][y]=z;}return;}R int y=r[0];for(R int i=r[0]; i!=0; i=r[i])if(s[i]<s[y])y=i;rm(y);for(R int i=d[y]; i!=y; i=d[i]){ansk[dep]=row[i];for(R int j=r[i]; j!=i; j=r[j])rm(col[j]);dance(dep+1);for(R int j=l[i]; j!=i; j=l[j])rv(col[j]);}rv(y);}signed main(){read(Q);while(Q--){init();for(R int i=1; i<=9; i++)for(R int j=1; j<=9; j++){read(a[i][j]);R int &t=a[i][j];for(R int k=1; k<=9; k++){if(t!=k&&t!=0)continue;R int o=(i-1)*9*9+(j-1)*9+k;R int c1=81*0 + (i-1)*9+(j-1)+1;R int c2=81*1 + (i-1)*9+k;R int c3=81*2 + (j-1)*9+k;R int c4=81*3 + ((i-1)/3*3+(j-1)/3)*9+k;link(o,c1);link(o,c2);link(o,c3);link(o,c4);}}dance(0);if(!Ans){puts("0");continue;}printf("%lld\n",Ans);for(R int i=1; i<=9&&!suc; i++)for(R int j=1; j<=9; j++)printf("%lld%c",ans[i][j]," \n"[j==9]);}return 0;}

当然,如果您有兴趣的话,可以去做下这道题

题目链接:SP1110 SUDOKU - Sudoku

这道题就是上一题的加强版,其实没什么区别,如果您理解了例题的话,这题就很简单了


总结

本文介绍了舞蹈链

]]>
+ 浅谈舞蹈链(DLX)

前言

舞蹈链的名字真好玩...


一、舞蹈链概述

舞蹈链 (Dancing links),也叫 DLX,是由 Donald Knuth提出的数据结构,目的是快速实现他提出的X算法。X算法是一种递归算法,时间复杂度不确定,深度优先,通过回溯寻找精确覆盖问题所有可能的解

(以上摘自维基百科)

舞蹈链的主要思想来源于双向链表

我们设 \(l[x]\) 表示元素 \(x\) 的左指针, \(r[x]\) 表示元素 \(x\) 的右指针

显然,如果想要删除元素 \(x\),我们可以做以下操作

r[l[x]]=r[x]; // x左侧的元素的右指针指向x右侧的元素l[r[x]]=l[x]; // x右侧的元素的左指针指向x左侧的元素

那恢复元素 \(x\) 呢?我们可以发现删除 \(x\) 的时候, \(x\) 的左右指针并没有改变,即 \(l[x]\) 和 \(r[x]\)并没有改变,于是我们可以做以下操作

r[l[x]]=x; // x左侧的元素的右指针重新指向xl[r[x]]=x; // x右侧的元素的左指针重新指向x

这样如果 \(x\)左右两侧没有改变,我们就可以恢复 \(x\)所在的位置

那么精确覆盖问题又是什么呢?

给定矩阵,要求选出一个由若干行组成的集合,使得每一列上都有且仅有一个\(1\)

\[\begin{pmatrix}0&0&1&0&1&1&0\\ 1&0&0&1&0&0&1 \\0&1&1&0&0&1&0 \\1&0&0&1&0&0&0 \\0&1&0&0&0&0&1 \\0&0&0&1&1&0&1\end{pmatrix}\]

例如该矩阵选出的行为 \(1,4,5\)

我们来模拟一下朴素X算法求解的过程

以下过程用红色表示选择了这一行,绿色表示存在冲突的元素,灰色表示删除的行

1.选择第一行

\[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0\\ 1&0&0&1&0&0&1 \\0&1&1&0&0&1&0 \\1&0&0&1&0&0&0 \\0&1&0&0&0&0&1 \\0&0&0&1&1&0&1\end{pmatrix}\]

2.标记所有和第一行冲突的元素

\[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0\\ 1&0&0&1&0&0&1 \\0&1&\color{green}1&0&0&\color{green}1&0 \\1&0&0&1&0&0&0 \\0&1&0&0&0&0&1 \\0&0&0&1&\color{green}1&0&1\end{pmatrix}\]

3.删除存在冲突的行

\[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0\\ 1&0&0&1&0&0&1 \\\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0\\ 1&0&0&1&0&0&0 \\0&1&0&0&0&0&1 \\\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}1\end{pmatrix}\]

4.接着选择第二行

\[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0\\\color{red}1&\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}0&\color{red}1\\\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0\\ 1&0&0&1&0&0&0 \\0&1&0&0&0&0&1 \\\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}1\end{pmatrix}\]

5.标记与第二行冲突的元素

\[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0\\\color{red}1&\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}0&\color{red}1\\\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0\\ \color{green}1&0&0&\color{green}1&0&0&0 \\0&1&0&0&0&0&\color{green}1 \\\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}1\end{pmatrix}\]

6.删除存在冲突的行

\[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0\\\color{red}1&\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}0&\color{red}1\\\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0\\\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}0\\\color{grey}0&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1\\\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}1\end{pmatrix}\]

7.发现没有可以选择的行了,而已选的不满足要求,回溯,选择第四行

\[\begin{pmatrix}\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}1&\color{red}1&\color{red}0\\ 1&0&0&1&0&0&1 \\\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}0\\\color{red}1&\color{red}0&\color{red}0&\color{red}1&\color{red}0&\color{red}0&\color{red}0\\ 0&1&0&0&0&0&1 \\\color{grey}0&\color{grey}0&\color{grey}0&\color{grey}1&\color{grey}1&\color{grey}0&\color{grey}1\end{pmatrix}\]8.接下来的同理,不断执行,直到找到答案

我们会发现, \(X\)算法花了大量的时间在找 \(1\),而且删改很不方便

为了解决这个问题,舞蹈链就产生了

模板题 \(\to\) P4929【模板】舞蹈链(DLX)

(注:为了方便讲述,以下引用这篇博客中的图片(感谢图片的作者!))

舞蹈链的结构即交叉十字循环双向链,本文中以数组形式实现链表

int n,m; // 行、列数int u[MAXN],d[MAXN],l[MAXN],r[MAXN],h[MAXN]; // 每个结点的上下左右指针;每一行的头指针int row[MAXN],col[MAXN],s[MAXN],ansk[MAXN],pos; // 每个结点原先所在的行、列;每一列的结点个数;ansk记录搜索信息;结点总数

如下图所示 别急!我们一步一步来实现这个复杂的数据结构

首先初始化上方的列头结点

我们可以称列头结点为限制,行头结点为决策 (注:这个做题的时候有用)

void init(){for(R int i=0; i<=m; i++){l[i]=i-1;r[i]=i+1;u[i]=d[i]=i; }l[0]=m;r[m]=0; //循环链表memset(h,-1,sizeof(h)); //每一行的头结点都为空memset(s,0,sizeof(s)); //每一列的结点数都为0pos=m+1; //已经搭建好了m个列头结点,下一个加入的结点从m+1开始编号}
接下来,我们来把插入结点的功能完成(注:这里比较复杂,可以感性理解一下)
void link(R int x,R int y){s[y]++; //所在的列结点数加1row[pos]=x;col[pos]=y; //记录编号为pos的结点(即新加入的结点)的行和列u[pos]=y; //pos结点的上指针指向插入的列yd[pos]=d[y]; //pos结点的下指针指向插入位置下方的元素u[d[y]]=pos; //插入位置下方的元素的上指针指向pos结点d[y]=pos; //插入位置上方的下指针指向pos结点if(h[x]<0)h[x]=l[pos]=r[pos]=pos;//如果pos结点所在的这一行没有头结点,就自己当else //不然就插入头结点一侧 (下面的就不注释了,和上面插入的方法类似){l[pos]=l[h[x]];r[pos]=h[x];r[l[h[x]]]=pos;l[h[x]]=pos;}pos++; //下一个结点不要标错号了}
现在我们来完成删除和恢复操作(差不多的)
inline void rm(R int y){l[r[y]]=l[y];r[l[y]]=r[y];//删除y列的结点(这个位置已经填满了)for(R int i=d[y]; i!=y; i=d[i])for(R int j=r[i]; j!=i; j=r[j])//删除这一行(冲突的行){d[u[j]]=d[j];u[d[j]]=u[j];s[col[j]]--;//别忘了减1}}inline void rv(R int y){for(R int i=u[y]; i!=y; i=u[i])for(R int j=l[i]; j!=i; j=l[j])//恢复也是一样的{d[u[j]]=j;u[d[j]]=j;s[col[j]]++;}r[l[y]]=y;l[r[y]]=y;}
然后可以开始跳舞了 主体部分,就是深度优先搜索
bool dance(R int dep){if(!r[0])//所有的列头结点都被选了,说明成功了{for(R int i=0; i<dep; i++)printf("%lld%c",ansk[i]," \n"[i==dep-1]);return 1;}R int y=r[0];for(R int i=r[0]; i; i=r[i])if(s[i]<s[y])y=i;//这里是一个剪枝,每次选择结点最少的列能在一定情况下提高性能rm(y);//删掉这一列for(R int i=d[y]; i!=y; i=d[i])//每次选择这列中的一行{ansk[dep]=row[i];for(R int j=r[i]; j!=i; j=r[j])rm(col[j]);//删掉这一行中所有结点(这一行冲突)if(dance(dep+1))return 1;//成功就返回for(R int j=l[i]; j!=i; j=l[j])rv(col[j]);//恢复}rv(y);return 0;}
如果您不太理解的话,可以看看下面的动图(注:其实和之前模拟的有些相似)

算法执行过程 (注:图片是这篇博客的)

最终的答案即下图 (选择 \(1,4,5\) 最后贴上完整代码

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN 250505template<typename T>inline void read(R T &k){R char ch=getchar(); R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}int n,m;int u[MAXN],d[MAXN],l[MAXN],r[MAXN],h[MAXN];int row[MAXN],col[MAXN],s[MAXN],ansk[MAXN],pos;void init(){for(R int i=0; i<=m; i++){l[i]=i-1;r[i]=i+1;u[i]=d[i]=i;}l[0]=m;r[m]=0;memset(h,-1,sizeof(h));memset(s,0,sizeof(s));pos=m+1;}void link(R int x,R int y){s[y]++;row[pos]=x;col[pos]=y;u[pos]=y;d[pos]=d[y];u[d[y]]=pos;d[y]=pos;if(h[x]<0)h[x]=l[pos]=r[pos]=pos;else{l[pos]=l[h[x]];r[pos]=h[x];r[l[h[x]]]=pos;l[h[x]]=pos;}pos++;}inline void rm(R int y){l[r[y]]=l[y];r[l[y]]=r[y];for(R int i=d[y]; i!=y; i=d[i])for(R int j=r[i]; j!=i; j=r[j]){d[u[j]]=d[j];u[d[j]]=u[j];s[col[j]]--;}}inline void rv(R int y){for(R int i=u[y]; i!=y; i=u[i])for(R int j=l[i]; j!=i; j=l[j]){d[u[j]]=j;u[d[j]]=j;s[col[j]]++;}r[l[y]]=y;l[r[y]]=y;}bool dance(R int dep){if(!r[0]){for(R int i=0; i<dep; i++)printf("%lld%c",ansk[i]," \n"[i==dep-1]);return 1;}R int y=r[0];for(R int i=r[0]; i; i=r[i])if(s[i]<s[y])y=i;rm(y);for(R int i=d[y]; i!=y; i=d[i]){ansk[dep]=row[i];for(R int j=r[i]; j!=i; j=r[j])rm(col[j]);if(dance(dep+1))return 1;for(R int j=l[i]; j!=i; j=l[j])rv(col[j]);}rv(y);return 0;}signed main(){read(n);read(m);init();for(R int i=1; i<=n; i++)for(R int j=1,t; j<=m; j++){read(t);if(t)link(i,j);}if(!dance(0))puts("No Solution!");return 0;}


二、舞蹈链例题

如果您看到这里,还是很明白的话,那么我们来讲个例题

题目链接:SP13980SUDOGOB - Sudoku goblin

题意:给定一个 \(9 \times9\) 的数独,输出可填的方案数,多组数据

选择这个例题当然不是让你写暴搜的

首先考虑决策

每个格子上填数字,至多有 \(9\times 9\times9 = 729\) 种决策

再考虑限制

  1. 每个点只能填一个数
  2. 每行一个数只能填一次
  3. 每列一个数只能填一次
  4. 每个九宫格一个数只能填一次

限制数为 \(9\times 9 \times 4 =324\)

对于精准覆盖问题,我们本质上是在选择若干决策,使其恰好满足所有限制条件

因此这题可以用 DLX 求解

那么 MAXN只要开到 729*324就行了(注:不过开大点保险)

这题唯一的细节是插入结点的行、列,还是很简单的题目

代码如下

#include <bits/stdc++.h>using namespace std;#define int long long#define R register#define MAXN (729*324+6666)int Q;bool suc;int a[25][25];int ans[25][25],Ans;int h[MAXN],l[MAXN],r[MAXN],u[MAXN],d[MAXN];int row[MAXN],col[MAXN],pos,s[MAXN],ansk[MAXN];template<typename T>inline void read(R T &k){R char ch=getchar();R T x=0,f=1;while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}k=x*f;}inline void init(){int m=324;Ans=0;suc=0;for(R int i=0; i<=m; i++){l[i]=i-1;r[i]=i+1;u[i]=d[i]=i;}l[0]=m;r[m]=0;memset(s,0,sizeof(s));memset(h,-1,sizeof(h));pos=m+1;}inline void link(R int x,R int y){s[y]++;row[pos]=x;col[pos]=y;u[pos]=y;d[pos]=d[y];u[d[y]]=pos;d[y]=pos;if(h[x]<0)h[x]=l[pos]=r[pos]=pos;else{l[pos]=l[h[x]];r[pos]=h[x];r[l[h[x]]]=pos;l[h[x]]=pos;}++pos;}inline void rm(R int y){r[l[y]]=r[y];l[r[y]]=l[y];for(R int i=d[y]; i!=y; i=d[i])for(R int j=r[i]; j!=i; j=r[j]){u[d[j]]=u[j];d[u[j]]=d[j];s[col[j]]--;}}inline void rv(R int y){for(R int i=u[y]; i!=y; i=u[i])for(R int j=l[i]; j!=i; j=l[j]){u[d[j]]=j;d[u[j]]=j;s[col[j]]++;}l[r[y]]=y;r[l[y]]=y;}void dance(R int dep){if(!r[0]){Ans++;if(Ans>1)suc=1;for(R int i=0; i<dep&&!suc; i++){R int x=(ansk[i]-1)/9/9+1;R int y=(ansk[i]-1)/9%9+1;R int z=(ansk[i]-1)%9+1;ans[x][y]=z;}return;}R int y=r[0];for(R int i=r[0]; i!=0; i=r[i])if(s[i]<s[y])y=i;rm(y);for(R int i=d[y]; i!=y; i=d[i]){ansk[dep]=row[i];for(R int j=r[i]; j!=i; j=r[j])rm(col[j]);dance(dep+1);for(R int j=l[i]; j!=i; j=l[j])rv(col[j]);}rv(y);}signed main(){read(Q);while(Q--){init();for(R int i=1; i<=9; i++)for(R int j=1; j<=9; j++){read(a[i][j]);R int &t=a[i][j];for(R int k=1; k<=9; k++){if(t!=k&&t!=0)continue;R int o=(i-1)*9*9+(j-1)*9+k;R int c1=81*0 + (i-1)*9+(j-1)+1;R int c2=81*1 + (i-1)*9+k;R int c3=81*2 + (j-1)*9+k;R int c4=81*3 + ((i-1)/3*3+(j-1)/3)*9+k;link(o,c1);link(o,c2);link(o,c3);link(o,c4);}}dance(0);if(!Ans){puts("0");continue;}printf("%lld\n",Ans);for(R int i=1; i<=9&&!suc; i++)for(R int j=1; j<=9; j++)printf("%lld%c",ans[i][j]," \n"[j==9]);}return 0;}

当然,如果您有兴趣的话,可以去做下这道题

题目链接:SP1110SUDOKU - Sudoku

这道题就是上一题的加强版,其实没什么区别,如果您理解了例题的话,这题就很简单了


总结

本文介绍了舞蹈链

]]>
@@ -9082,7 +9082,7 @@ /2021/07/22/dao-shu-de-ji-ben-gong-shi-tui-dao/ - 导数的基本公式推导

主要推导了人教版A版数学选择性必修二上直接给出的基本的导数公式

本文写于作者初三暑假,更新于高一暑假

可能含有很多不足,如果您方便的话可以联系我修改 awa

大概率会在高二暑假再更新一次吧


一、导数的四则运算法则

设 $f(x),~g(x)$ 均为可导函数

命题:${[f(x) \pm g(x)]}^{\prime} = f^{\prime}(x) \pm g^{\prime}(x)$

证明


命题:$[f(x)g(x)]^{\prime}=f^{\prime}(x)g(x)+f(x)g^{\prime}(x)$

证明


命题:$\left[\dfrac{f(x)}{g(x)}\right]^{\prime} = \dfrac{f^{\prime}(x)g(x)-f(x)g^{\prime}(x)}{\left[g(x)\right]^2}$

证明


二、复合函数的求导法则(链式法则)

命题:设函数 $y=f(u)$ 在 $u=g(x)$ 处可导,而 $u=g(x)$ 在 $x$ 处可导,

则函数 $y=f(g(x))$ 在 $x$ 处可导,且

证明

因为 $u=g(x)$ 在 $x$ 处可导,所以它在这一点也是连续的

也就是 $\Delta u \to 0$ 当 $\Delta x \to 0$ ,故


三、基本初等函数的导数公式

命题:若 $f(x)=x^a(a\in \mathbb{N})$ ,则 $f^{\prime}(x) = a x^{a-1}$

证明

可以推广到 $a\in \mathbb{R}$ 的情况,即

命题:若 $f(x)=x^a(a\in \mathbb{R})$ ,则 $f^{\prime}(x) = a x^{a-1}$

证明

令 $t=(1+\frac{\Delta x}{x})^a-1$ ,则


命题:若 $f(x)=\sin x$ ,则 $f^{\prime}(x)=\cos x$

证明


命题:若 $f(x)=\cos x$ ,则 $f^{\prime}(x) = -\sin x$

证明


命题:若 $f(x)=a^x (a>0,\text{且}a \ne 1)$ ,则 $f^{\prime}(x)=a^x\ln a$

证明

令 $t=a^{\Delta x}-1$ ,则$\Delta x = \log_a(t+1)$

则原极限可化为

特别地,当 $a=e$ 时,即 $f(x)=e^x$ ,有 $f^{\prime}(x)=e^x$


命题:若 $f(x)=\log_ax(a>0,\text{且}a \ne 1)$ ,则 $f^{\prime}(x) = \dfrac{1}{x\ln a}$

证明

令 $t=\dfrac{\Delta x}{x}$ ,则 $\dfrac{1}{\Delta x} = \dfrac{1}{tx}$

$\therefore$ 原极限可化为

特别地,当 $a=e$ 时,即 $f(x)=\ln x$ ,有 $f^{\prime}(x)=\dfrac{1}{x}$


命题:若 $f(x)=\varphi ( \varphi \texttt{为常数})$ ,则 $f^{\prime}(x)=0$

证明


参考文献

[1] 复合函数的导数(链式法则)

[2] 基本初等函数的导数

]]>
+ 导数的基本公式推导

主要推导了人教版A版数学选择性必修二上直接给出的基本的导数公式

本文写于作者初三暑假,更新于高一暑假

可能含有很多不足,如果您方便的话可以联系我修改 awa

大概率会在高二暑假再更新一次吧


一、导数的四则运算法则

\(f(x),~g(x)\)均为可导函数

命题\({[f(x) \pmg(x)]}^{\prime} = f^{\prime}(x) \pm g^{\prime}(x)\)

证明

\[\begin{aligned}{[f(x) \pm g(x)]}^{\prime}&=\lim\limits_{\Delta x\to0}{\dfrac{f(x+\Delta x)-f(x) \pm\left[g(x+\Delta x)-g(x)\right]}{\Deltax}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{f(x+\Delta x)-f(x)}{\Deltax}} \pm \lim\limits_{\Delta x\to 0}{\dfrac{g(x+\Delta x)-g(x)}{\Deltax}}\\\\&=f^{\prime}(x)\pm g^{\prime}(x)\end{aligned}\]


命题\([f(x)g(x)]^{\prime}=f^{\prime}(x)g(x)+f(x)g^{\prime}(x)\)

证明

\[\begin{aligned}{[f(x)g(x)]}^{\prime}&=\lim\limits_{\Delta x \to 0}{\dfrac{f(x+\Delta x)g(x+\Deltax)-f(x)g(x)}{\Delta x}}\\\\&=\lim\limits_{\Delta x \to 0}{\dfrac{f(x+\Delta x)g(x+\Deltax)-f(x)g(x+\Delta x)+f(x)g(x+\Delta x)-f(x)g(x)}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{f(x+\Delta x)-f(x)}{\Deltax}}\lim\limits_{\Delta x\to 0}{g(x+\Delta x)}+f(x)\lim\limits_{\Deltax\to 0}{\dfrac{g(x+\Delta x)-g(x)}{\Delta x}}\\\\&=f^{\prime}(x)g(x)+f(x)g^{\prime}(x)\end{aligned}\]


命题\(\left[\dfrac{f(x)}{g(x)}\right]^{\prime} =\dfrac{f^{\prime}(x)g(x)-f(x)g^{\prime}(x)}{\left[g(x)\right]^2}\)

证明

\[\begin{aligned}{\left[\dfrac{f(x)}{g(x)}\right]}^{\prime}&=\lim\limits_{\Delta x\to0}{\dfrac{\frac{f(x+\Delta x)}{g(x+\Delta x)}-\frac{f(x)}{g(x)}}{\Deltax}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{f(x+\Deltax)g(x)-g(x+\Delta x)f(x)}{\Delta xg(x+\Delta x)g(x)}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{f(x+\Deltax)g(x)-f(x)g(x)}{\Delta xg(x+\Delta x)g(x)}}-\lim\limits_{\Delta x \to0}{\dfrac{g(x+\Delta x)f(x)-f(x)g(x)}{\Delta xg(x+\Delta x)g(x)}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{f(x+\Delta x)-f(x)}{\Deltax}}\lim\limits_{\Delta x\to 0}{\dfrac{g(x)}{g(x+\Delta x)g(x)}} -\lim\limits_{\Delta x\to 0}{\dfrac{g(x+\Delta x)-g(x)}{\Deltax}}\lim\limits_{\Delta x\to 0}{\dfrac{f(x)}{g(x+\Delta x)g(x)}}\\\\&=f^{\prime}(x)\dfrac{g(x)}{\left[g(x)\right]^2} -g^{\prime}(x)\dfrac{f(x)}{\left[g(x)\right]^2}\\\\&=\dfrac{f^{\prime}(x)g(x)-f(x)g^{\prime}(x)}{\left[g(x)\right]^2}\end{aligned}\]


二、复合函数的求导法则(链式法则)

命题:设函数 \(y=f(u)\) 在 \(u=g(x)\) 处可导,而 \(u=g(x)\) 在 \(x\) 处可导,

则函数 \(y=f(g(x))\)\(x\) 处可导,且 \[{\left[f(g(x))\right]}^{\prime}=f^{\prime}(g(x))\cdot g^{\prime}(x)\]

证明

\[\begin{aligned}{\left[f(g(x))\right]}^{\prime}&=\lim\limits_{\Delta x \to0}\dfrac{\Delta y}{\Delta g(x)}\\\\&=\lim\limits_{\Delta x \to 0}\dfrac{\Delta y}{\Deltau}\cdot\dfrac{\Delta u}{\Delta x}\end{aligned}\] 因为 \(u=g(x)\)\(x\) 处可导,所以它在这一点也是连续的

也就是 \(\Delta u \to 0\)\(\Delta x \to 0\) ,故 \[\begin{aligned}{\left[f(g(x))\right]}^{\prime}&=\lim\limits_{\Delta u \to0}\dfrac{\Delta y}{\Delta u}\lim\limits_{\Delta x \to 0}\dfrac{\Deltau}{\Delta x}\\\\&=f^{\prime}(u)g^{\prime}(x)\\\\&=f^{\prime}(g(x))\cdot g^{\prime}(x)\end{aligned}\]


三、基本初等函数的导数公式

命题:若 \(f(x)=x^a(a\in\mathbb{N})\) ,则 \(f^{\prime}(x) = ax^{a-1}\)

证明

\[\begin{aligned}f^{\prime}(x)&=\lim\limits_{\Delta x\to 0}{\dfrac{(x+\Deltax)^{a}-(x)^{a}}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to0}{\dfrac{\sum\limits_{r=0}^{a}{C_{a}^{r}x^{a-r}\Deltax^{r}}-C_{a}^{0}x^{a}}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to0}{\dfrac{\sum\limits_{r=1}^{a}{C_{a}^{r}x^{a-r}\Delta x^{r}}}{\Deltax}}\\\\&=\lim\limits_{\Delta x\to0}{\sum\limits_{r=2}^{a}{C_{a}^{r}x^{a-r}\Deltax^{r-1}+C_{a}^{1}x^{a-1}}}\\\\&=C_a^{1}x^{a-1}\\\\&=a x^{a-1}\end{aligned}\]

可以推广到 \(a\in \mathbb{R}\)的情况,即

命题:若 \(f(x)=x^a(a\in\mathbb{R})\) ,则 \(f^{\prime}(x) = ax^{a-1}\)

证明

\[\begin{aligned}f^{\prime}(x)&=\lim\limits_{\Delta x \to 0}\dfrac{(x+\Deltax)^a-x^a}{\Delta x}\\\\&=\lim\limits_{\Delta x \to 0}x^a\dfrac{\left(1+\frac{\Deltax}{x}\right)^a-1}{\Delta x}\\\\&=x^a\lim\limits_{\Delta x \to 0}\dfrac{\left(1+\frac{\Deltax}{x}\right)^a-1}{\ln\left(1+\frac{\Deltax}{x}\right)^a}\cdot\dfrac{\ln\left(1+\frac{\Deltax}{x}\right)^a}{\Delta x}\end{aligned}\]\(t=(1+\frac{\Deltax}{x})^a-1\) ,则 \[\begin{aligned}f^{\prime}(x)&=x^a\lim\limits_{\Delta x \to0}\dfrac{t}{\ln\left(1+t\right)} \cdot \dfrac{a\ln\left(1+\frac{\Deltax}{x}\right)}{\Delta x}\\\\&=x^a\lim\limits_{\Delta x \to 0}\dfrac{t}{\ln\left(1+t\right)}\cdot \dfrac{\ln\left(1+\frac{\Delta x}{x}\right)}{\frac{\Delta x}{x}}\cdot\dfrac{a}{x}\\\\&=ax^{a-1}\end{aligned}\]


命题:若 \(f(x)=\sinx\) ,则 \(f^{\prime}(x)=\cosx\)

证明

\[\begin{aligned}f^{\prime}(x)&=\lim\limits_{\Delta x\to 0}{\dfrac{\sin (x+\Deltax)-\sin x}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{\sin x \cos\Delta x+\sin\Delta x \cos x-\sin x}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{\sin x(\cos\Deltax-1)+\sin\Delta x\cos x}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{\sinx\left[\left(1-2\sin^2\frac{\Delta x}{2}\right)-1\right]+\cosx\left(2\sin\frac{\Delta x}{2}\cos \frac{\Delta x}{2}\right)}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{2\sin\frac{\Deltax}{2}\cos\frac{\Delta x}{2}\cos x-2\sin^2\frac{\Delta x}{2}\sinx}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{2\sin\frac{\Deltax}{2}\left(\cos\frac{\Delta x}{2}\cos x-\sin x\sin \frac{\Deltax}{2}\right)}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{2\sin\frac{\Deltax}{2}\cos\left(\frac{\Delta x}{2}+x\right)}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\cos\left(\dfrac{\Deltax}{2}+x\right)\dfrac{\sin\frac{\Delta x}{2}}{\frac{\Delta x}{2}}}\\\\&=\lim\limits_{\Delta x\to 0}\cos\left(\dfrac{\Deltax}{2}+x\right)\\\\&=\cos x\end{aligned}\]


命题:若 \(f(x)=\cosx\) ,则 \(f^{\prime}(x) = -\sinx\)

证明

\[\begin{aligned}f^{\prime}(x)&=\lim\limits_{\Delta x\to 0}{\dfrac{\cos(x+\Deltax)-\cos x}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}\dfrac{\cos x\cos \Delta x- \sin\Delta x\sin x-\cos x}{\Delta x}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{\cos x(\cos\Delta x-1)-\sin\Delta x\sin x}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{\cos x\left[\left(1-2\sin^2\frac{\Delta x}{2}\right)-1\right]-\sinx\left(2\sin\frac{\Delta x}{2}\cos\frac{\Delta x}{2}\right)}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{-2\sin^2\frac{\Deltax}{2}\cos x-2\sin\frac{\Delta x}{2}\sin x \cos\frac{\Delta x}{2}}{\Deltax}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{-2\sin\frac{\Deltax}{2}\left(\sin \frac{\Delta x}{2}\cos x+\sin x \cos \frac{\Deltax}{2}\right)}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{-2\sin \frac{\Deltax}{2}\sin\left(x+\frac{\Delta x}{2}\right)}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{-\sin\left(x+\dfrac{\Deltax}{2}\right)\dfrac{\sin\frac{\Delta x}{2}}{\frac{\Delta x}{2}}}\\\\&=\lim\limits_{\Delta x\to 0}{-\sin\left(x+\dfrac{\Deltax}{2}\right)}\\\\&=-\sin x\end{aligned}\]


命题:若 \(f(x)=a^x(a>0,\text{且}a \ne 1)\) ,则 \(f^{\prime}(x)=a^x\ln a\)

证明

\[\begin{aligned}f^{\prime}(x)&=\lim\limits_{\Delta x\to 0}{\dfrac{a^{x+\Deltax}-a^{x}}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{a^x(a^{\Delta x}-1)}{\Deltax}}\end{aligned}\]\(t=a^{\Delta x}-1\),则\(\Delta x = \log_a(t+1)\) \[\begin{aligned}&\because\Delta x\to 0\\\\&\therefore a^{\Delta x}\to 1\\\\&\therefore t+1\to1\end{aligned}\] 即 \[t \to 0\]

则原极限可化为 \[\begin{aligned}f^{\prime}(x)&=\lim\limits_{t\to 0}{\dfrac{a^xt}{\log_a(t+1)}}\\\\&=\lim\limits_{t\to 0}{\dfrac{a^x}{\frac{1}{t}\log_a(t+1)}}\\\\&=\lim\limits_{t\to 0}{\dfrac{a^x}{\log_a(1+t)^{\frac{1}{t}}}}\\\\&=a^x\dfrac{1}{\log_ae}\\\\&=a^x\dfrac{\ln a}{\ln e}\\\\&=a^x\ln a\end{aligned}\]

特别地,当 \(a=e\) 时,即 \(f(x)=e^x\) ,有 \(f^{\prime}(x)=e^x\)


命题:若 \(f(x)=\log_ax(a>0,\text{且}a \ne 1)\),则 \(f^{\prime}(x) = \dfrac{1}{x\lna}\)

证明\[\begin{aligned}f^{\prime}(x)&=\lim\limits_{\Delta x\to 0}{\dfrac{\log_a(x+\Deltax)-\log_ax}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\dfrac{\log_a\left(1+\frac{\Deltax}{x}\right)}{\Delta x}}\\\\&=\lim\limits_{\Delta x\to 0}{\log_a{\left(1+\dfrac{\Deltax}{x}\right)^{\frac{1}{\Delta x}}}}\end{aligned}\]\(t=\dfrac{\Delta x}{x}\),则 \(\dfrac{1}{\Delta x} =\dfrac{1}{tx}\) \[\begin{aligned}&\because \Delta x\to 0\\\\&\therefore \dfrac{\Delta x}{x}\to 0\\\\&\therefore t\to 0\end{aligned}\] \(\therefore\) 原极限可化为\[\begin{aligned}f^{\prime}(x)&=\lim\limits_{t\to0}{\log_a\left(1+t\right)^{\frac{1}{tx}}}\\\\&=\lim\limits_{t\to0}{\dfrac{1}{x}\log_a\left(1+t\right)^{\frac{1}{t}}}\\\\&=\dfrac{1}{x}\log_ae\\\\&=\dfrac{1\ln e}{x\ln a}\\\\&=\dfrac{1}{x\ln a}\end{aligned}\]

特别地,当 \(a=e\) 时,即 \(f(x)=\ln x\) ,有 \(f^{\prime}(x)=\dfrac{1}{x}\)


命题:若 \(f(x)=\varphi (\varphi \texttt{为常数})\) ,则 \(f^{\prime}(x)=0\)

证明\[\begin{aligned}f^{\prime}(x) &=\lim\limits_{\Delta x\to0}{\dfrac{\varphi-\varphi}{\Delta x}}\\&=0\end{aligned}\]


参考文献

[1] 复合函数的导数(链式法则)

[2] 基本初等函数的导数

]]>
@@ -9107,7 +9107,7 @@ /2021/07/21/meng-ti-huo-er-wen-ti-ji-qi-tui-guang/ - 蒙提霍尔问题及其推广

前言

蒙提霍尔问题在《人教版A版数学选择性必修三》上作为阅读与思考的材料出现

本文会提供一种简单的解法并推广这个著名的问题


蒙提霍尔问题

一、背景

三门问题(Monty Hall problem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目Let’s Make a Deal。问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)


二、简介

参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门是否会增加参赛者赢得汽车的几率


(注:以上摘自百度)


三、分析

题面没什么好说的,我们直接来讨论概率问题

根据直觉,我们可能会有以下两种判断

  1. 三扇门中有车的概率都是 $\dfrac{1}{3}$ ,因此不必换门
  2. 既然打开了一扇山羊门,那车在剩下两扇门中的概率都为 $\dfrac{1}{2}$ ,因此不必换门

那么到底哪个是对的呢?(其实都不对)

首先我们先来明确概率是什么

我们称第一次选择后并在剩余的门中开启了一扇有山羊的门后可以选择的门的数量为可选门

(注:说的有一些绕,但是这样的思路对于该问题的推广很有帮助)

可选门中换到车门的概率就是 可选门中车门的数量除以可选门的数量

接下来我们来分类讨论选择换门后得到车的概率(注:车门指门后是汽车,山羊门指门后是山羊)

  1. 第一次选择的是车门(概率为 $\dfrac{1}{3}$ ),可选门有 $1$ 扇,其中还有 $0$ 扇车门,因此 $P_1=\dfrac{1}{3}\times\dfrac{0}{1} = 0$
  2. 第一次选择的是山羊门(概率为 $\dfrac{2}{3}$ ),可选门有 $1$ 扇,其中还有 $1$ 扇车门,因此 $P_2=\dfrac{2}{3}\times\dfrac{1}{1} = \dfrac{2}{3}$

加起来就是选择换门后得到车的概率 $\dfrac{2}{3}$

而不换门得到车的概率为 $\dfrac{1}{3}$

是不是很反直觉?


四、推广

现在我们来讨论蒙提霍尔问题的推广(uva10491)

设有 $a$ 扇山羊门, $b$ 扇车门,主持人打开 $c$ 扇山羊门( $a,b,c\in \mathbb{Z},1\le a,b \le 10000 , 0\le c < a$ )

显然不换门得到车的概率为 $\dfrac{b}{a+b}$

那么选择换门后得到车的概率是多少呢?

第一次选择后剩余的门为 $(a+b-1)$ 扇,可选门的数量为 $(a+b-c-1)$

继续来分类讨论

  1. 第一次选择的是车门(概率为 $\dfrac{b}{a+b}$),可选门中车门的数量为 $(b-1)$ 扇,因此 $P_1 = \dfrac{b}{a+b}\times\dfrac{b-1}{a+b-c-1}$
  2. 第一次选择的是山羊门(概率为 $\dfrac{a}{a+b}$ ),可选门中车门的数量为 $b$ 扇,因此 $P_2 = \dfrac{a}{a+b}\times\dfrac{b}{a+b-c-1}$

加起来就是选择换门后得到车的概率为 $\dfrac{ab+b(b-1)}{(a+b)(a+b-c-1)}$

很容易证明换门后得到车的概率一定更大(除了c=0时概率相等)


总结

本文介绍并推广了蒙提霍尔问题

]]>
+ 蒙提霍尔问题及其推广

前言

蒙提霍尔问题在《人教版A版数学选择性必修三》上作为阅读与思考的材料出现

本文会提供一种简单的解法并推广这个著名的问题


蒙提霍尔问题

一、背景

三门问题(Monty Hallproblem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目Let'sMake a Deal。问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)

二、简介

参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门是否会增加参赛者赢得汽车的几率

(注:以上摘自百度)


三、分析

题面没什么好说的,我们直接来讨论概率问题

根据直觉,我们可能会有以下两种判断

  1. 三扇门中有车的概率都是 \(\dfrac{1}{3}\) ,因此不必换门
  2. 既然打开了一扇山羊门,那车在剩下两扇门中的概率都为 \(\dfrac{1}{2}\) ,因此不必换门

那么到底哪个是对的呢?(其实都不对)

首先我们先来明确概率是什么

我们称第一次选择后并在剩余的门中开启了一扇有山羊的门后可以选择的门的数量为可选门

(注:说的有一些绕,但是这样的思路对于该问题的推广很有帮助)

可选门中换到车门的概率就是可选门中车门的数量除以可选门的数量

接下来我们来分类讨论选择换门后得到车的概率(注:车门指门后是汽车,山羊门指门后是山羊)

  1. 第一次选择的是车门(概率为 \(\dfrac{1}{3}\) ),可选门有 \(1\) 扇,其中还有 \(0\) 扇车门,因此 \(P_1=\dfrac{1}{3}\times\dfrac{0}{1} =0\)
  2. 第一次选择的是山羊门(概率为 \(\dfrac{2}{3}\) ),可选门有 \(1\) 扇,其中还有 \(1\) 扇车门,因此 \(P_2=\dfrac{2}{3}\times\dfrac{1}{1} =\dfrac{2}{3}\)

加起来就是选择换门后得到车的概率 \(\dfrac{2}{3}\)

而不换门得到车的概率为 \(\dfrac{1}{3}\)

是不是很反直觉?

四、推广

现在我们来讨论蒙提霍尔问题的推广(uva10491)

设有 \(a\) 扇山羊门, \(b\) 扇车门,主持人打开 \(c\) 扇山羊门( \(a,b,c\in \mathbb{Z},1\le a,b \le 10000 , 0\le c< a\) )

显然不换门得到车的概率为 \(\dfrac{b}{a+b}\)

那么选择换门后得到车的概率是多少呢?

第一次选择后剩余的门为 \((a+b-1)\)扇,可选门的数量为 \((a+b-c-1)\)

继续来分类讨论

  1. 第一次选择的是车门(概率为 \(\dfrac{b}{a+b}\)),可选门中车门的数量为\((b-1)\) 扇,因此 \(P_1 =\dfrac{b}{a+b}\times\dfrac{b-1}{a+b-c-1}\)
  2. 第一次选择的是山羊门(概率为 \(\dfrac{a}{a+b}\) ),可选门中车门的数量为\(b\) 扇,因此 \(P_2 =\dfrac{a}{a+b}\times\dfrac{b}{a+b-c-1}\)

加起来就是选择换门后得到车的概率为 \(\dfrac{ab+b(b-1)}{(a+b)(a+b-c-1)}\)

很容易证明换门后得到车的概率一定更大(除了c=0时概率相等)

总结

本文介绍并推广了蒙提霍尔问题

]]>
@@ -9132,7 +9132,7 @@ /2021/07/10/quan-yuan-zui-duan-lu-johnson-suan-fa/ - 全源最短路 Johnson算法

本文写于较早时期,之前对Dijkstra的理解不是很透彻

已经修改了部分显然错误的内容,有空会再仔细检查的

模板题P5905 【模板】Johnson 全源最短路

题意简述:给定一个包含 $n$ 个结点和 $m$ 条带权边的有向图,求全源最短路,可能有负权重的边重边自环,部分数据卡 SPFA 算法(还特地针对了SLF优化)

题意很明确,就是卡SPFA、Dijkstra、Floyd的

这题用SPFA可以被卡成$O(n^2m)$,Dijkstra不能处理负权重的边,Floyd$O(n^3)$肯定超时

说了这么多,就是为了引出我们要讲的 Johnson 算法

Johnson算法可以判断给定的图中有无负环,在无负环时能给出全源最短路时间复杂度 $O(n^2\log m)$ (Dijkstra采用优先队列优化,详细的分析可以看这边link

Johnson算法的核心为重新赋予权值,使得边权变成非负的新边权,而在运行过程中以Bellman-Ford和Dijkstra(以下提到的Dijkstra都用优先队列优化)作为自己的子程序

一、算法原理

以下推导过程部分参照《算法导论》

对于给定有向图 $G=(V,E)$ ,权重函数为 $w:E \rightarrow R$ ,如果所有的边权重都为非负值,则只需在每一个结点上跑Dijkstra即可,时间复杂度$O(V^2\log E)$;如果存在边权重为负值的边,但没有负环,则我们只需要预处理出一种新的权重函数 $w^{\prime}:E \rightarrow R$ 使得所有的边权重为非负值再跑Dijkstra即可

则新的权重函数 $w^{\prime}$ 一定满足以下两个条件:

  1. 对于结点 $u,v \in V$ ,路径 $p$ 为使用权重函数 $w^{\prime}$ 时 $u$ 到 $v$ 的一条最短路径,当且仅当 $p$ 为使用权重函数 $w$ 时 $u$ 到 $v$ 的一条最短路径
  2. 对于任何边 $(u,v) \in E$ ,$w^{\prime}(u,v)$ 为非负值 (Dijkstra不可以处理负权重的边)

设函数 $h:V\rightarrow \mathbb{R}$ 将结点映射到实数上

对于任何边$(u,v) \in E$,定义 $w^{\prime}(u,v) = w(u,v) + h(u) - h(v)$

我们先假设图 $G$ 没有负环,设路径 $p$ 为使用权重函数 $w^{\prime}$ 时 $v_0$ 到 $v_k$的一条最短路径$(v_0,v_k \in V)$ ,当且仅当 $p$ 为使用权重函数 $w$ 时 $v_0$ 到 $v_k$ 的一条最短路径

则可以证明 $w^{\prime}(p) = w(p) + h(v_0)-h(v_k)$

因为 $h$ 为将结点映射到实数上的函数,函数的值不受边权重的影响,所以对于一条为使用权重函数 $w$ 时 $v_0$ 到 $v_k$ 比 $p$ 要长的路径 $q$ ,在使用权重函数 $w^{\prime}$ 时 $q$ 一定比 $p$ 长

我们再来看看负环的问题,对于环$c = \langle v_0, v_1, … v_k\rangle$ ,其中 $v_0 = v_k$ ,则有 $w^{\prime}(c) = w(c) + h(v_0) -h(v_k) = w(c)$

则对于 $c$ 使用权重函数 $w$ 时为负环,当且仅当使用权重函数 $w$ 时为负环

那么 $h$ 函数该如何处理呢?

我们可以新建一个虚拟结点 $s$ ,并将 $s$ 和其他所有结点建边权重为 $0$ 的边,在 $s$ 结点跑一次Bellman-Ford(SPFA当然可以),求出 $s$ 到其他结点的最短路,则对于任何 $v \in V$ , $h(v)$ 的值即为 $s$ 到 $v$ 的最短路径长,在跑的过程中如果一条路径中某个结点被松弛超过 $n$ 次,则一定存在负环,并返回存在负环的信息(SPFA判负环的方法就不延伸了)

因为 $s$ 结点入度为 $0$ ,所以其他的路径中不会包含 $s$ 结点

那么为什么无负环时使用权重函数 $w^{\prime}$ ,边权重一定为非负呢?

设现在的图为 $G^{\prime} = (V^{\prime},E^{\prime})$ ,$V^{\prime} = V \cup \{s\}$ ,$E^{\prime} = E \cup \{(s,v),v\in E\}$

根据三角形不等式,可知对于任意结点 $u,v \in V^{\prime}$ ,若 $\exists (u,v) \in E^{\prime}$,则有

现在我们只需要在每个结点上跑一次Dijkstra即可

时间复杂度 $O(V^2 \log E)$ (SPFA时间复杂度最坏 $O(VE)$ ,预处理时间复杂度 $O(VE)$ )

二、参考代码

模板题代码如下 (注:输出按该题要求写的)

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 1000000000#define MAXN (int)(3e3+15)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch==^{\prime}-^{\prime})f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){pc(^{\prime}-^{\prime});k=-k;}    if(k>9)write(k/10);    pc(k%10+^{\prime}0^{\prime});}struct Edge{    int u,v,w,next;}e[MAXN<<2];struct node{    int u,dis;    bool operator<(const node &o)const        {return dis>o.dis;}};int n,m,pos=1,head[MAXN];int h[MAXN],d[MAXN],vis[MAXN],cnt[MAXN],ans;void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}bool spfa(){    queue<int> q;    fill(h+1,h+1+n,INF);    q.push(0);    vis[0]=1;h[0]=0;    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(h[v]>h[u]+e[i].w)            {                h[v]=h[u]+e[i].w;                if(!vis[v])                {                    q.push(v);                    vis[v]=1;                    if(++cnt[v]>n) // n+1结点                        return 0;                }            }        }    }    return 1;}void dijkstra(int st){    priority_queue<node> q;    fill(d+1,d+1+n,INF);    memset(vis,0,sizeof(vis));    q.push({st,0});    d[st]=0;    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])            continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[v]>d[u]+e[i].w)            {                d[v]=d[u]+e[i].w;                q.push({v,d[v]});            }        }    }}signed main(){    read(n);read(m);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);    }    for(int i=1; i<=n; i++)        addEdge(0,i,0);    if(!spfa())return puts("-1"),0;    for(int i=1; i<pos; i++)        e[i].w+=h[e[i].u]-h[e[i].v];    for(int i=1; i<=n; i++)    {        dijkstra(i);        int ans=0;        for(int j=1; j<=n; j++)        {            if(d[j]==INF)                ans+=j*INF;            else                ans+=j*(d[j]-(h[i]-h[j]));        }        write(ans);pc(^{\prime}\n^{\prime});    }    return 0;}
]]>
+ 全源最短路 Johnson算法

本文写于较早时期,之前对Dijkstra的理解不是很透彻

已经修改了部分显然错误的内容,有空会再仔细检查的

模板题P5905 【模板】Johnson全源最短路

题意简述:给定一个包含 \(n\) 个结点和 \(m\)条带权边的有向图,求全源最短路,可能有负权重的边重边自环,部分数据卡SPFA 算法(还特地针对了SLF优化)

题意很明确,就是卡SPFA、Dijkstra、Floyd的

这题用SPFA可以被卡成\(O(n^2m)\),Dijkstra不能处理负权重的边,Floyd\(O(n^3)\)肯定超时

说了这么多,就是为了引出我们要讲的 Johnson 算法

Johnson算法可以判断给定的图中有无负环,在无负环时能给出全源最短路时间复杂度\(O(n^2\log m)\)(Dijkstra采用优先队列优化,详细的分析可以看这边link)

Johnson算法的核心为重新赋予权值,使得边权变成非负的新边权,而在运行过程中以Bellman-Ford和Dijkstra(以下提到的Dijkstra都用优先队列优化)作为自己的子程序

一、算法原理

以下推导过程部分参照《算法导论》

对于给定有向图 \(G=(V,E)\),权重函数为 \(w:E \rightarrow R\),如果所有的边权重都为非负值,则只需在每一个结点上跑Dijkstra即可,时间复杂度\(O(V^2\logE)\);如果存在边权重为负值的边,但没有负环,则我们只需要预处理出一种新的权重函数\(w^{\prime}:E \rightarrow R\)使得所有的边权重为非负值再跑Dijkstra即可

则新的权重函数 \(w^{\prime}\)一定满足以下两个条件:

  1. 对于结点 \(u,v \in V\) ,路径 \(p\) 为使用权重函数 \(w^{\prime}\) 时 \(u\) 到 \(v\) 的一条最短路径,当且仅当 \(p\) 为使用权重函数 \(w\) 时 \(u\) 到 \(v\) 的一条最短路径
  2. 对于任何边 \((u,v) \in E\)\(w^{\prime}(u,v)\) 为非负值(Dijkstra不可以处理负权重的边)

设函数 \(h:V\rightarrow \mathbb{R}\)将结点映射到实数上

对于任何边\((u,v) \in E\),定义\(w^{\prime}(u,v) = w(u,v) + h(u) -h(v)\)

我们先假设图 \(G\)没有负环,设路径 \(p\)为使用权重函数 \(w^{\prime}\)\(v_0\) 到 \(v_k\)的一条最短路径\((v_0,v_k \in V)\) ,当且仅当 \(p\) 为使用权重函数 \(w\) 时 \(v_0\) 到 \(v_k\) 的一条最短路径

则可以证明 \(w^{\prime}(p) = w(p) +h(v_0)-h(v_k)\) \[\begin{aligned}w^{\prime}(p)&=\sum\limits_{i=1}^{k}{w^{\prime}(v_{i-1},v_i)}\\\\&= \sum\limits_{i=1}^{k}{(w(v_{i-1},v_i)+h(v_{i-1})-h(v_i))}\\\\&=\sum\limits_{i=1}^{k}{w(v_{i-1},v_i)}+h(v_0)-h(v_k)\\\\&= w(p)+h(v_0)-h(v_k)\end{aligned}\] 因为 \(h\)为将结点映射到实数上的函数,函数的值不受边权重的影响,所以对于一条为使用权重函数\(w\)\(v_0\) 到 \(v_k\) 比 \(p\) 要长的路径 \(q\) ,在使用权重函数 \(w^{\prime}\) 时 \(q\) 一定比 \(p\) 长

我们再来看看负环的问题,对于环\(c = \langlev_0, v_1, ... v_k\rangle\) ,其中 \(v_0= v_k\) ,则有 \(w^{\prime}(c) = w(c) +h(v_0) -h(v_k) = w(c)\)

则对于 \(c\) 使用权重函数 \(w\) 时为负环,当且仅当使用权重函数 \(w\) 时为负环

那么 \(h\) 函数该如何处理呢?

我们可以新建一个虚拟结点 \(s\) ,并将 \(s\) 和其他所有结点建边权重为 \(0\) 的边,在 \(s\)结点跑一次Bellman-Ford(SPFA当然可以),求出 \(s\) 到其他结点的最短路,则对于任何 \(v \in V\) , \(h(v)\) 的值即为 \(s\) 到 \(v\)的最短路径长,在跑的过程中如果一条路径中某个结点被松弛超过 \(n\)次,则一定存在负环,并返回存在负环的信息(SPFA判负环的方法就不延伸了)

因为 \(s\) 结点入度为 \(0\) ,所以其他的路径中不会包含 \(s\) 结点

那么为什么无负环时使用权重函数 \(w^{\prime}\) ,边权重一定为非负呢?

设现在的图为 \(G^{\prime} =(V^{\prime},E^{\prime})\)\(V^{\prime} = V \cup \{s\}\) ,\(E^{\prime} = E \cup \{(s,v),v\in E\}\)

根据三角形不等式,可知对于任意结点 \(u,v\in V^{\prime}\) ,若 \(\exists (u,v)\in E^{\prime}\),则有 \[\begin{aligned}h(u) + w(u,v) \ge h(v)&\Rightarrow w(u,v) + h(u) - h(v) \ge 0\\\\&\Rightarrow w^{\prime}(u,v) \ge 0\end{aligned}\] 现在我们只需要在每个结点上跑一次Dijkstra即可

时间复杂度 \(O(V^2 \logE)\) (SPFA时间复杂度最坏 \(O(VE)\) ,预处理时间复杂度 \(O(VE)\) )

二、参考代码

模板题代码如下 (注:输出按该题要求写的)

#include <bits/stdc++.h>using namespace std;#define int long long#define gc() getchar()#define pc(a) putchar(a)#define INF 1000000000#define MAXN (int)(3e3+15)template<typename T>void read(T &k){    char ch=gc();T x=0,f=1;    while(!isdigit(ch)){if(ch==^{\prime}-^{\prime})f=-1;ch=gc();}    while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}    k=x*f;}template<typename T>void write(T k){    if(k<0){pc(^{\prime}-^{\prime});k=-k;}    if(k>9)write(k/10);    pc(k%10+^{\prime}0^{\prime});}struct Edge{    int u,v,w,next;}e[MAXN<<2];struct node{    int u,dis;    bool operator<(const node &o)const        {return dis>o.dis;}};int n,m,pos=1,head[MAXN];int h[MAXN],d[MAXN],vis[MAXN],cnt[MAXN],ans;void addEdge(int u,int v,int w){    e[pos]={u,v,w,head[u]};    head[u]=pos++;}bool spfa(){    queue<int> q;    fill(h+1,h+1+n,INF);    q.push(0);    vis[0]=1;h[0]=0;    while(!q.empty())    {        int u=q.front();q.pop();        vis[u]=0;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(h[v]>h[u]+e[i].w)            {                h[v]=h[u]+e[i].w;                if(!vis[v])                {                    q.push(v);                    vis[v]=1;                    if(++cnt[v]>n) // n+1结点                        return 0;                }            }        }    }    return 1;}void dijkstra(int st){    priority_queue<node> q;    fill(d+1,d+1+n,INF);    memset(vis,0,sizeof(vis));    q.push({st,0});    d[st]=0;    while(!q.empty())    {        int u=q.top().u;q.pop();        if(vis[u])            continue;        vis[u]=1;        for(int i=head[u]; i; i=e[i].next)        {            int v=e[i].v;            if(d[v]>d[u]+e[i].w)            {                d[v]=d[u]+e[i].w;                q.push({v,d[v]});            }        }    }}signed main(){    read(n);read(m);    for(int i=1,u,v,w; i<=m; i++)    {        read(u);read(v);read(w);        addEdge(u,v,w);    }    for(int i=1; i<=n; i++)        addEdge(0,i,0);    if(!spfa())return puts("-1"),0;    for(int i=1; i<pos; i++)        e[i].w+=h[e[i].u]-h[e[i].v];    for(int i=1; i<=n; i++)    {        dijkstra(i);        int ans=0;        for(int j=1; j<=n; j++)        {            if(d[j]==INF)                ans+=j*INF;            else                ans+=j*(d[j]-(h[i]-h[j]));        }        write(ans);pc(^{\prime}\n^{\prime});    }    return 0;}
]]>
@@ -9157,7 +9157,7 @@ /2021/07/09/wu-xu-shu-zu-jiao-huan-ren-yi-liang-ge-yuan-su-zui-shao-jiao-huan-ci-shu/ - 无序数组交换任意两个元素 最少交换次数

题目描述

给定长度为 $n$ 的无序数组,将数组中的元素按从小到大的顺序排列,每次可以交换任意两个元素,最少要交换几次?


解题方法

解法一(较繁琐)

我们可以遍历一遍原数组,如果当前元素不在正确位置上,将该元素和此时在它正确位置上的元素交换

这里有一个要注意的,某次交换后我们可能没有将交换和被交换数都放在正确位置上,且有可能在接下来的遍历中不会再次遍历到被交换数,因此交换后还要再判断,直到当前位置的数正确,显然不这么做不会影响正确结果

很容易证明这是最少的。

设当前元素为 $\alpha$ (不在正确位置上),它与非 $\alpha$ 的正确位置上的元素 $\beta$ 交换,会有两种情况:第一种, $\beta$ 在正确位置上。由于最后 $\beta$ 一定会在正确位置上,且一定是 $\beta$ 和在 $\beta$ 正确位置上的元素交换(或 $\beta$ 本来就在正确位置),所以交换 $\alpha$ 和 $\beta$ 就是多余的;第二种, $\beta$ 不在正确位置上。显然对于每次必要交换,我们不在乎正确位置上的元素是什么,我们只需要将正确的元素与其交换就可以了,至于被交换的元素,我们会接下来处理它,所以交换 $\alpha$ 和 $\beta$ 是多余的

由于我们要知道正确位置,所以要排序好的数组记录正确位置,时间复杂度 $O(n\log n)$

主要代码如下

int fun(vector<int> a){    vector<int>tmp=a;//排序后的数组    map<int,int>mp;    int ans=0;    sort(tmp.begin(),tmp.end());    for(int i=0; i<tmp.size(); i++)        mp[tmp[i]]=i;//记录每个对应的正确位置    for(int i=0; i<a.size(); i++)        while(i!=mp[a[i]])        {            ans++;            swap(a[i],a[mp[a[i]]]);        }            return ans;}

解法二

对于每个元素,我们将该元素和它的正确位置建边

最后一定是 $1\sim n$ 个环(自环也算)

对于有 $k$ 个元素的环,最少交换次数为 $k-1$

假设共有 $p$ 个环,对于第 $i$ 个环,有 $k_i$ 个元素,则它的最少交换次数为 $k_i - 1$

因此 $ans = \sum\limits_{i=1}^{p}{(k_i-1)} = \sum\limits_{i=1}^{p}{k_i} - p$

显然 $n = \sum\limits_{i=1}^{p}{k_i}$

所以答案就是 $n-p$ ,即元素个数 - 环的个数

由于我们要知道正确位置,所以要排序好的数组记录正确位置,时间复杂度 $O(n\log n)$

主要代码如下(注:其实可以不用递归)

vector<int> a;map<int,int>mp;map<int,bool>vis;void dfs(int x){    vis[x]=1;    if(!vis.count(mp[a[x]]))dfs(mp[a[x]]);}int fun(){    vector<int>tmp=a;//排序后的数组    int p=0;    sort(tmp.begin(),tmp.end());    for(int i=0; i<tmp.size(); i++)        mp[tmp[i]]=i;//记录每个对应的正确位置    for(int i=0; i<a.size(); i++)        if(!vis.count(i))p++,dfs(i);    return n-p;}
]]>
+ 无序数组交换任意两个元素最少交换次数

题目描述

给定长度为 \(n\)的无序数组,将数组中的元素按从小到大的顺序排列,每次可以交换任意两个元素,最少要交换几次?

解题方法

解法一(较繁琐)

我们可以遍历一遍原数组,如果当前元素不在正确位置上,将该元素和此时在它正确位置上的元素交换

这里有一个要注意的,某次交换后我们可能没有将交换和被交换数都放在正确位置上,且有可能在接下来的遍历中不会再次遍历到被交换数,因此交换后还要再判断,直到当前位置的数正确,显然不这么做不会影响正确结果

很容易证明这是最少的。

设当前元素为 \(\alpha\)(不在正确位置上),它与非 \(\alpha\)的正确位置上的元素 \(\beta\)交换,会有两种情况:第一种, \(\beta\)在正确位置上。由于最后 \(\beta\)一定会在正确位置上,且一定是 \(\beta\)和在 \(\beta\) 正确位置上的元素交换(或\(\beta\) 本来就在正确位置),所以交换\(\alpha\)\(\beta\) 就是多余的;第二种, \(\beta\)不在正确位置上。显然对于每次必要交换,我们不在乎正确位置上的元素是什么,我们只需要将正确的元素与其交换就可以了,至于被交换的元素,我们会接下来处理它,所以交换\(\alpha\)\(\beta\) 是多余的

由于我们要知道正确位置,所以要排序好的数组记录正确位置,时间复杂度\(O(n\log n)\)

主要代码如下

int fun(vector<int> a){    vector<int>tmp=a;//排序后的数组    map<int,int>mp;    int ans=0;    sort(tmp.begin(),tmp.end());    for(int i=0; i<tmp.size(); i++)        mp[tmp[i]]=i;//记录每个对应的正确位置    for(int i=0; i<a.size(); i++)        while(i!=mp[a[i]])        {            ans++;            swap(a[i],a[mp[a[i]]]);        }            return ans;}

解法二

对于每个元素,我们将该元素和它的正确位置建边

最后一定是 \(1\sim n\)个环(自环也算)

对于有 \(k\)个元素的环,最少交换次数为 \(k-1\)

假设共有 \(p\) 个环,对于第 \(i\) 个环,有 \(k_i\) 个元素,则它的最少交换次数为 \(k_i - 1\)

因此 \(ans = \sum\limits_{i=1}^{p}{(k_i-1)}= \sum\limits_{i=1}^{p}{k_i} - p\)

显然 \(n =\sum\limits_{i=1}^{p}{k_i}\)

所以答案就是 \(n-p\) ,即元素个数 -环的个数

由于我们要知道正确位置,所以要排序好的数组记录正确位置,时间复杂度\(O(n\log n)\)

主要代码如下(注:其实可以不用递归)

vector<int> a;map<int,int>mp;map<int,bool>vis;void dfs(int x){    vis[x]=1;    if(!vis.count(mp[a[x]]))dfs(mp[a[x]]);}int fun(){    vector<int>tmp=a;//排序后的数组    int p=0;    sort(tmp.begin(),tmp.end());    for(int i=0; i<tmp.size(); i++)        mp[tmp[i]]=i;//记录每个对应的正确位置    for(int i=0; i<a.size(); i++)        if(!vis.count(i))p++,dfs(i);    return n-p;}
]]>
@@ -9184,7 +9184,7 @@ /2021/07/08/qian-tan-bu-ma-de-yuan-li-he-zheng-que-xing/ - 浅谈补码的原理和正确性

前言

upd 2022.2.14 我就该早点看《计算机组成原理》,补码的定义就是

一个 $n$ 位二进制数 $N$ 的二进制补码定义为 $2^n-N$

不过本文还是严谨证明了它的正确性,以下为原文

补码是怎么来的?

负数的补码为什么是按位取反(除了符号位)再加 $1$ ?

补码的正确性能保证吗?

补码背后的数学原理是什么?

本文围绕原理和正确性介绍了补码


一、我们为什么用补码?

我们以 $8$ 位二进制数为例

$8$ 位无符号数,可以表示 $0\sim255$ 以内的数(即 $00000000 \sim 11111111$ ),那么如果我们要表示负数呢?

我们都知道正、负号能够表示正、负数,可是计算机“看不懂“,它只能识别 $0,1$

于是,就出现了原码

最高位表示数的正负,$0$ 表示 ,$1$ 表示

$8$ 位有符号数,以原码形式存储则可以表示 $0 \sim 127$(即 $00000000 \sim 01111111$ ) 和 $-127 \sim 0$ (即 $11111111 \sim 10000000$ )

相信看到这里,你应该发现了:以原码形式存储时,$0$ 的值不唯一

我们把 $\pm 0$ 的问题放一边,先来讨论一下编码的正确性

计算 $1+(-1)$

$00000001 + 10000001 = 10000010$

然而 $10000010$ 转成十进制是 $-2$ !

由此可知原码不可以直接进行运算,或者可以认为以原码的编码方式运算是错误

那么问题来了,正数的表示都符合我们的习惯,那么问题一定出在负数上


二、补码是怎么来的?

各大教科书上都会告诉你,正数的补码就是它的原码,负数的补码就是原码按位取反(除了符号位)再加 $1$

你是不是也很迷惑?补码到底为什么是这么算出来的?

我们还是以 $8$ 位有符号数为例

我们知道,两个数如果互为相反数则和为$0$ ,例如 $1+(-1)=0$

那么正确的编码方式中 $1+(-1)$ 一定等于 $0$

我们设 $-1$ 的编码为 $\phi$ ,可得 $00000001 + \phi = 0$

$\therefore \phi = 11111111$(注:计算过程将在下文中讲述,这里只需要知道结果)

那么就很清楚了,如果我们已知编码 $\phi$ ,它的相反数的编码就是 $0 - \phi$

现在我们来讨论一下 $\pm 0$ 的问题,显而易见,$10000000$ 肯定是不符合常理的( $00000000 + 10000000 \ne 00000000$ )

那么 $10000000$ 对应的相反数的编码是什么呢?

设 $10000000 + \phi = 0$ 解得 $\phi = 100000000$ (注意位数!)

我们会发现 $00000000 \sim 11111111$ 已经无法表示了

那 $0$ 呢?$0$ 的相反数还是 $0$ ,就是它本身

事已至此,干脆就让它们单身着吧。 既然 $10000000$ 的第一位是 $1$ ,表示负数,那就规定它为负数,因此 $10000000$ 就代替了 $-128$

因为 $10000000$ 没有对应的相反数的编码,所以没有 $+128$

然后每个数都有了自己对应的编码,$1 \sim 127$ 对应 $-1 \sim -127$ ,加上两个单身汉 $0$ 和 $-128$

这种编码就叫补码

那么为什么负数的补码是原码按位取反再加 $1$ 呢?

我们再看 $1+(-1)=0$ 的例子

设 $00000001 + \phi = 00000000$

$\therefore \phi = 00000000-00000001$

一个小的数减一个大的数,怎么办?

首先我们应该都知道溢出这一概念

那么 $00000000$ 就可以看作 $11111111 + 00000001$

$\therefore\ \phi = (11111111 + 00000001) - 00000001$

$\qquad= (11111111-00000001) + 00000001$

注:这个就是简单的结合律

我们看前面一部分,不就是按位取反嘛(反码)!

相信你现在应该理解了,如果我们要求正数 $\lambda$ 的相反数 $\mu$ 的补码

$\mu = (11111111-\lambda) + 00000001$ ,

即 $\lambda$)按位取反(包括符号位)再加 $1$ ,当然也是 $\mu$ 按位取反(除了符号位)再加 $1$ ,一样的


三、补码的正确性能保证吗?

1.符号位能保证本身的值、运算结果的值正确吗?

本身的值肯定正确,运算结果的值也正确

运算都是从最低位运算到最高位,这个符号位又加在最高位,不可能影响运算,只可能被影响(注:接下来我们就来证明这个影响不会对运算结果的符号正确性产生影响)

2.数学运算结果的符号能保证正确吗?

为了方便理解和证明,我们以 $4$ 位有符号数为例(即 $-8 \sim 7$ )

注意:正确性指在数学运算结果不溢出的情况下位运算的结果和数学运算的结果正确

首先,易知任何合法的数与 $0$ 作运算结果一定正确

1) 值的位运算结果不溢出时

1.正数+正数

数学运算结果:一定为正数,符号为

位运算结果:值的位运算不溢出,符号位 $0+0=0$,仍为

设这里两个数补码分别为$[0a_2a_1a_0], [0b_2b_1b_0]$

令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

原式可化为 $A+B$

且 $0 < A+B \le 7$

值的位运算结果不溢出即 $-8 \le A+B \le 7$

$\{x|0 < x \le 7\}\subsetneqq\{x|-8 \le x \le 7\}$

故该情况$\color{red}{正确}$

2.负数+负数

数学运算结果:一定为负数,符号为

位运算结果:值的位运算不溢出,符号位 $1+1=0$ ,变成了

设这里两个数补码分别为$[1a_2a_1a_0], [1b_2b_1b_0]$

令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

原式可化为 $-8+A+(-8)+B = -16+A+B$

且 $-8 \le A+B-16 < 0 \Rightarrow 8 \le A+B < 16$

值的位运算结果不溢出即 $-8 \le A+B \le 7$

$\{x|-8 \le x \le 7\}\cap\{x|8 \le x < 16\} = \varnothing$

故该情况$\color{red}{不存在}$

3.一正一负
a.正数绝对值大于负数绝对值

数学运算结果:一定为正数,符号为

位运算结果:值的位运算结果不溢出,符号位 $0+1=1$ ,变成了

设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

原式可化为$A+(-8)+B = A+B-8$

且 $0 < A+B-8 \le 7 \Rightarrow 8 < A+B \le 15$

值的位运算的结果不溢出即 $-8 \le A+B \le 7$

$\{x|-8 \le x \le 7\}\cap\{x|8 < x \le 15\} = \varnothing$

故该情况$\color{red}{不存在}$

b.正数绝对值等于负数绝对值

数学运算结果:$0$ ,符号为

位运算结果:值的位运算结果不溢出,符号位 $0+1=1$ ,变成了

设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

原式可化为$A+(-8)+B = A+B-8 = 0 \Rightarrow A+B=8$

值的位运算的结果不溢出即 $-8 \le A+B \le 7$

$\{x|-8 \le x \le 7\}\cap\{x|x=8\} = \varnothing$

故该情况$\color{red}{不存在}$

c.正数绝对值小于负数绝对值

数学运算结果:一定为负数,符号为

位运算结果:值的位运算结果不溢出,符号为 $0 + 1 = 1$ ,仍为

设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

原式可化为 $A+(-8)+B = A+B-8$

且 $-8 \le A+B-8 < 0 \Rightarrow 0 \le A+B < 8$

值的位运算结果不溢出即 $-8 \le A+B \le 7$

$\{x|0 \le x < 8\}\subsetneqq\{x|-8 \le x \le 7\}$

故该情况$\color{red}{正确}$

2) 值的位运算结果溢出时

1.正数+正数

数学运算结果:一定为正数,符号为

位运算结果:值的位运算结果溢出,符号位 $0+0+1=1$ ,变成了

设这里两个数补码分别为$[0a_2a_1a_0], [0b_2b_1b_0]$

令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

原式可化为 $A+B$

且 $0 < A+B \le 7$

值的位运算结果溢出即 $A+B>7$

$\{x|0 < x \le 7\}\cap\{x|x>7\} = \varnothing$

故该情况$\color{red}{不存在}$

2.负数+负数

数学运算结果:一定为负数,符号为

位运算结果:值的位运算结果溢出,符号位 $1+1+1=1$ ,仍为

设这里两个数补码分别为$[1a_2a_1a_0], [1b_2b_1b_0]$

令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

原式可化为 $-8+A+(-8)+B = A+B-16$

且 $-8 \le A+B -16< 0 \Rightarrow 8 \le A+B < 16$

值的位运算结果溢出即 $A+B>7$

$\{x|8\le x < 16\}\subsetneqq\{x|x>7\}$

故该情况$\color{red}{正确}$

3.一正一负
a.正数绝对值大于负数绝对值

数学运算结果:一定为正数,符号为

位运算结果:值的位运算结果溢出,符号位 $0+1+1=0$ ,仍为

设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

原式可化为 $A+(-8)+B = A+B-8$

且 $0<A+B-8\le 7 \Rightarrow 8<A+B\le15$

值的位运算结果溢出即 $A+B>7$

$\{x|8< x \le 15\}\subsetneqq\{x|x>7\}$

故该情况$\color{red}{正确}$

b.正数绝对值等于负数绝对值

数学运算结果:$0$ ,符号为

位运算结果:值的位运算结果溢出,符号位 $0+1+1=0$ ,仍为

设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

原式可化为 $A+(-8)+B = A+B-8 = 0 \Rightarrow A+B=8$

值的位运算结果溢出即$A+B>7$

$\{x|x=8\}\subsetneqq\{x|x>7\}$

故该情况$\color{red}{正确}$

c.正数绝对值小于负数绝对值

数学运算结果:一定为负数 ,符号为

位运算结果:值的位运算结果溢出,符号位 $0+1+1=0$ ,变成了

设这里两个数补码分别为$[0a_2a_1a_0], [1b_2b_1b_0]$

令 $A = \sum\limits_{i=0}^{2}{a_i\times 2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}$,易知 $A,B \in \N$

原式可化为 $A+(-8)+B = A+B-8$

且 $-8\le A+B - 8<0 \Rightarrow 0 \le A+B <8$

值的位运算结果溢出即 $A+B>7$

$\{x|0 < x < 8\}\cap\{x|x>7\} = \varnothing$

故该情况$\color{red}{不存在}$

综上所述,所有存在的情况中,符号都是正确的,因此补码的正确性是可以保证的


总结

本文简单解释了补码的原理

并证明了补码的正确性



参考文献

[1] 《补码正确性的证明》

[2] 《补码(为什么按位取反再加一):告诉你一个其实很简单的问题》

]]>
+ 浅谈补码的原理和正确性

前言

upd 2022.2.14我就该早点看《计算机组成原理》,补码的定义就是

一个 \(n\) 位二进制数 \(N\) 的二进制补码定义为 \(2^n-N\)

不过本文还是严谨证明了它的正确性,以下为原文

补码是怎么来的?

负数的补码为什么是按位取反(除了符号位)再加 \(1\) ?

补码的正确性能保证吗?

补码背后的数学原理是什么?

本文围绕原理和正确性介绍了补码


一、我们为什么用补码?

我们以 \(8\) 位二进制数为例

\(8\)无符号数,可以表示 \(0\sim255\) 以内的数(即 \(00000000 \sim 11111111\)),那么如果我们要表示负数呢?

我们都知道正、负号能够表示正、负数,可是计算机“看不懂“,它只能识别\(0,1\)

于是,就出现了原码

最高位表示数的正负,\(0\) 表示\(1\)表示

\(8\)有符号数,以原码形式存储则可以表示 \(0 \sim 127\)(即 \(00000000 \sim 01111111\) ) 和 \(-127 \sim 0\) (即 \(11111111 \sim 10000000\) )

相信看到这里,你应该发现了:以原码形式存储时,\(0\) 的值不唯一

我们把 \(\pm 0\)的问题放一边,先来讨论一下编码的正确性

计算 \(1+(-1)\)

\(00000001 + 10000001 =10000010\)

然而 \(10000010\) 转成十进制是 \(-2\) !

由此可知原码不可以直接进行运算,或者可以认为以原码的编码方式运算是错误

那么问题来了,正数的表示都符合我们的习惯,那么问题一定出在负数上


二、补码是怎么来的?

各大教科书上都会告诉你,正数的补码就是它的原码,负数的补码就是原码按位取反(除了符号位)再加\(1\)

你是不是也很迷惑?补码到底为什么是这么算出来的?

我们还是以 \(8\)位有符号数为例

我们知道,两个数如果互为相反数则和为\(0\) ,例如 \(1+(-1)=0\)

那么正确的编码方式中 \(1+(-1)\)一定等于 \(0\)

我们设 \(-1\) 的编码为 \(\phi\) ,可得 \(00000001 + \phi = 0\)

\(\therefore \phi =11111111\)(注:计算过程将在下文中讲述,这里只需要知道结果)

那么就很清楚了,如果我们已知编码 \(\phi\) ,它的相反数的编码就是 \(0 - \phi\)

现在我们来讨论一下 \(\pm 0\)的问题,显而易见,\(10000000\)肯定是不符合常理的( \(00000000 + 10000000 \ne00000000\)

那么 \(10000000\)对应的相反数的编码是什么呢?

\(10000000 + \phi = 0\) 解得\(\phi = 100000000\) (注意位数!)

我们会发现 \(00000000 \sim11111111\) 已经无法表示了

\(0\) 呢?\(0\) 的相反数还是 \(0\) ,就是它本身

事已至此,干脆就让它们单身着吧。 既然 \(10000000\) 的第一位是 \(1\) ,表示负数,那就规定它为负数,因此\(10000000\) 就代替了 \(-128\)

因为 \(10000000\)没有对应的相反数的编码,所以没有 \(+128\)

然后每个数都有了自己对应的编码,\(1 \sim127\) 对应 \(-1 \sim -127\),加上两个单身汉 \(0\)\(-128\)

这种编码就叫补码

那么为什么负数的补码是原码按位取反再加 \(1\) 呢?

我们再看 \(1+(-1)=0\) 的例子

\(00000001 + \phi =00000000\)

\(\therefore \phi =00000000-00000001\)

一个小的数减一个大的数,怎么办?

首先我们应该都知道溢出这一概念

那么 \(00000000\) 就可以看作 \(11111111 + 00000001\)

\(\therefore\ \phi = (11111111 + 00000001)- 00000001\)

\(\qquad= (11111111-00000001) +00000001\)

注:这个就是简单的结合律

我们看前面一部分,不就是按位取反嘛(反码)!

相信你现在应该理解了,如果我们要求正数 \(\lambda\) 的相反数 \(\mu\) 的补码

\(\mu = (11111111-\lambda) +00000001\)

\(\lambda\))按位取反(包括符号位)再加 \(1\) ,当然也是 \(\mu\) 按位取反(除了符号位)再加 \(1\) ,一样的


三、补码的正确性能保证吗?

1.符号位能保证本身的值、运算结果的值正确吗?

本身的值肯定正确,运算结果的值也正确

运算都是从最低位运算到最高位,这个符号位又加在最高位,不可能影响运算,只可能被影响(注:接下来我们就来证明这个影响不会对运算结果的符号正确性产生影响)

2.数学运算结果的符号能保证正确吗?

为了方便理解和证明,我们以 \(4\)位有符号数为例(即 \(-8 \sim7\)

注意:正确性指在数学运算结果不溢出的情况下位运算的结果和数学运算的结果正确

首先,易知任何合法的数\(0\) 作运算结果一定正确

1) 值的位运算结果不溢出时

1.正数+正数

数学运算结果:一定为正数,符号为

位运算结果:值的位运算不溢出,符号位 \(0+0=0\),仍为

设这里两个数补码分别为\([0a_2a_1a_0],[0b_2b_1b_0]\)

\(A = \sum\limits_{i=0}^{2}{a_i\times2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

原式可化为 \(A+B\)

\(0 < A+B \le 7\)

值的位运算结果不溢出即 \(-8 \le A+B \le7\)

\(\{x|0 < x \le 7\}\subsetneqq\{x|-8 \lex \le 7\}\)

故该情况\(\color{red}{正确}\)

2.负数+负数

数学运算结果:一定为负数,符号为

位运算结果:值的位运算不溢出,符号位 \(1+1=0\) ,变成了

设这里两个数补码分别为\([1a_2a_1a_0],[1b_2b_1b_0]\)

\(A = \sum\limits_{i=0}^{2}{a_i\times2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

原式可化为 \(-8+A+(-8)+B =-16+A+B\)

\(-8 \le A+B-16 < 0 \Rightarrow 8 \leA+B < 16\)

值的位运算结果不溢出即 \(-8 \le A+B \le7\)

\(\{x|-8 \le x \le 7\}\cap\{x|8 \le x <16\} = \varnothing\)

故该情况\(\color{red}{不存在}\)

3.一正一负
a.正数绝对值大于负数绝对值

数学运算结果:一定为正数,符号为

位运算结果:值的位运算结果不溢出,符号位 \(0+1=1\) ,变成了

设这里两个数补码分别为\([0a_2a_1a_0],[1b_2b_1b_0]\)

\(A = \sum\limits_{i=0}^{2}{a_i\times2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

原式可化为\(A+(-8)+B = A+B-8\)

\(0 < A+B-8 \le 7 \Rightarrow 8 <A+B \le 15\)

值的位运算的结果不溢出即 \(-8 \le A+B \le7\)

\(\{x|-8 \le x \le 7\}\cap\{x|8 < x \le15\} = \varnothing\)

故该情况\(\color{red}{不存在}\)

b.正数绝对值等于负数绝对值

数学运算结果:\(0\),符号为

位运算结果:值的位运算结果不溢出,符号位 \(0+1=1\) ,变成了

设这里两个数补码分别为\([0a_2a_1a_0],[1b_2b_1b_0]\)

\(A = \sum\limits_{i=0}^{2}{a_i\times2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

原式可化为\(A+(-8)+B = A+B-8 = 0\Rightarrow A+B=8\)

值的位运算的结果不溢出即 \(-8 \le A+B \le7\)

\(\{x|-8 \le x \le 7\}\cap\{x|x=8\} =\varnothing\)

故该情况\(\color{red}{不存在}\)

c.正数绝对值小于负数绝对值

数学运算结果:一定为负数,符号为

位运算结果:值的位运算结果不溢出,符号为 \(0 + 1 = 1\) ,仍为

设这里两个数补码分别为\([0a_2a_1a_0],[1b_2b_1b_0]\)

\(A = \sum\limits_{i=0}^{2}{a_i\times2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

原式可化为 \(A+(-8)+B = A+B-8\)

\(-8 \le A+B-8 < 0 \Rightarrow 0 \leA+B < 8\)

值的位运算结果不溢出即 \(-8 \le A+B \le7\)

\(\{x|0 \le x < 8\}\subsetneqq\{x|-8 \lex \le 7\}\)

故该情况\(\color{red}{正确}\)

2) 值的位运算结果溢出时

1.正数+正数

数学运算结果:一定为正数,符号为

位运算结果:值的位运算结果溢出,符号位 \(0+0+1=1\) ,变成了

设这里两个数补码分别为\([0a_2a_1a_0],[0b_2b_1b_0]\)

\(A = \sum\limits_{i=0}^{2}{a_i\times2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

原式可化为 \(A+B\)

\(0 < A+B \le 7\)

值的位运算结果溢出即 \(A+B>7\)

\(\{x|0 < x \le 7\}\cap\{x|x>7\} =\varnothing\)

故该情况\(\color{red}{不存在}\)

2.负数+负数

数学运算结果:一定为负数,符号为

位运算结果:值的位运算结果溢出,符号位 \(1+1+1=1\) ,仍为

设这里两个数补码分别为\([1a_2a_1a_0],[1b_2b_1b_0]\)

\(A = \sum\limits_{i=0}^{2}{a_i\times2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

原式可化为 \(-8+A+(-8)+B =A+B-16\)

\(-8 \le A+B -16< 0 \Rightarrow 8 \leA+B < 16\)

值的位运算结果溢出即 \(A+B>7\)

\(\{x|8\le x <16\}\subsetneqq\{x|x>7\}\)

故该情况\(\color{red}{正确}\)

3.一正一负
a.正数绝对值大于负数绝对值

数学运算结果:一定为正数,符号为

位运算结果:值的位运算结果溢出,符号位 \(0+1+1=0\) ,仍为

设这里两个数补码分别为\([0a_2a_1a_0],[1b_2b_1b_0]\)

\(A = \sum\limits_{i=0}^{2}{a_i\times2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

原式可化为 \(A+(-8)+B = A+B-8\)

\(0<A+B-8\le 7 \Rightarrow8<A+B\le15\)

值的位运算结果溢出即 \(A+B>7\)

\(\{x|8< x \le15\}\subsetneqq\{x|x>7\}\)

故该情况\(\color{red}{正确}\)

b.正数绝对值等于负数绝对值

数学运算结果:\(0\),符号为

位运算结果:值的位运算结果溢出,符号位 \(0+1+1=0\) ,仍为

设这里两个数补码分别为\([0a_2a_1a_0],[1b_2b_1b_0]\)

\(A = \sum\limits_{i=0}^{2}{a_i\times2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

原式可化为 \(A+(-8)+B = A+B-8 = 0\Rightarrow A+B=8\)

值的位运算结果溢出即\(A+B>7\)

\(\{x|x=8\}\subsetneqq\{x|x>7\}\)

故该情况\(\color{red}{正确}\)

c.正数绝对值小于负数绝对值

数学运算结果:一定为负数 ,符号为

位运算结果:值的位运算结果溢出,符号位 \(0+1+1=0\) ,变成了

设这里两个数补码分别为\([0a_2a_1a_0],[1b_2b_1b_0]\)

\(A = \sum\limits_{i=0}^{2}{a_i\times2^i}, B = \sum\limits_{i=0}^{2}{b_i \times 2^i}\),易知 \(A,B \in \N\)

原式可化为 \(A+(-8)+B = A+B-8\)

\(-8\le A+B - 8<0 \Rightarrow 0 \leA+B <8\)

值的位运算结果溢出即 \(A+B>7\)

\(\{x|0 < x < 8\}\cap\{x|x>7\} =\varnothing\)

故该情况\(\color{red}{不存在}\)

综上所述,所有存在的情况中,符号都是正确的,因此补码的正确性是可以保证的

总结

本文简单解释了补码的原理

并证明了补码的正确性


参考文献

[1] 《补码正确性的证明》

[2] 《补码(为什么按位取反再加一):告诉你一个其实很简单的问题》

]]>
@@ -9209,7 +9209,7 @@ /2021/05/15/ubuntu-ibus-shu-ru-fa-tu-ran-wu-fa-shu-ru-yan-chi-guo-gao-jie-jue-fang-fa/ - ubuntu ibus输入法 突然无法输入 (延迟过高) 解决方法

前言

ibus拼音输入法最近不知道为何出现了一些异常问题(经常卡死)

被困扰了很久后,终于找到了解决办法

upd.20220423

在中文维基上查到了这样的资料

由于读取sqlite词库时有大量的IO操作,ibus-pinyin在系统高负载时输入时有卡住的现象。

upd.20220423

fcitx差点把你们可爱的q779给整没了,现在他继续用ibus了(重装了一遍)


一、具体表现

写代码时,突然键盘像失灵了一样打不出字,过了好一会才慢慢显示出来

可以说延迟太高了

在此期间终端却可以打开,鼠标也可以正常移动

关闭窗口什么的都没问题,就是打不出字


二、解决方法

如果您的ubuntu中除了ibus拼音输入还有英文输入,那这个方法必定有效

在这里插入图片描述
首先切换到en,即英语输入(右上角wifi图标旁边)

然后ctrl+alt+t打开终端

输入

ibus restart

重启ibus拼音输入法

切换回ibus输入法,然后你原先输入的就出现了!

而且可以正常使用了!

注意:如果重启时显示无法连接至ibus,可以看这里


总结

本文介绍了ibus输入法突然无法输入(卡死)的解决方法

众所周知,重启能解决大部分问题

]]>
+ ubuntuibus输入法 突然无法输入 (延迟过高) 解决方法

前言

ibus拼音输入法最近不知道为何出现了一些异常问题(经常卡死)

被困扰了很久后,终于找到了解决办法

upd.20220423

在中文维基上查到了这样的资料

由于读取sqlite词库时有大量的IO操作,ibus-pinyin在系统高负载时输入时有卡住的现象。

upd.20220423

fcitx差点把你们可爱的q779给整没了,现在他继续用ibus了(重装了一遍)


一、具体表现

写代码时,突然键盘像失灵了一样打不出字,过了好一会才慢慢显示出来

可以说延迟太高了

在此期间终端却可以打开,鼠标也可以正常移动

关闭窗口什么的都没问题,就是打不出字


二、解决方法

如果您的ubuntu中除了ibus拼音输入还有英文输入,那这个方法必定有效

首先切换到en,即英语输入(右上角wifi图标旁边)

然后ctrl+alt+t打开终端

输入

ibus restart
重启ibus拼音输入法

切换回ibus输入法,然后你原先输入的就出现了!

而且可以正常使用了!

注意:如果重启时显示无法连接至ibus,可以看这里


总结

本文介绍了ibus输入法突然无法输入(卡死)的解决方法

众所周知,重启能解决大部分问题

]]>
@@ -9234,7 +9234,7 @@ /2021/05/07/ubuntu-vmware-bao-cuo-could-not-open...jie-jue-fang-fa/ - ubuntu VMware报错could not open…解决方法

前言

本人在使用VMware时报了这个错

could not open /dev/vmmon:??????

经过多方查阅资料,为大家提供一个有效的解决办法


一、具体表现

安装好VMware,虚拟机也创建完了

一运行结果就报错了

在这里插入图片描述
一开始以为权限问题,用root打开也一样(显然没用)


二、解决方法

需要关闭安全启动 ($\mathrm{\color{black}{secure \ boot}}$)

安全启动设计之初作用是防止恶意软件侵入

事实上它能够做到的仅仅是当电脑引导器被病毒修改之后,它会给出提醒并拒绝启动

因此基本上没什么用

什么?在哪里关闭?? 当然是BIOS

进入BIOS (开机后出现电脑品牌的图标时狂按F12)

下面看我拍的照片就懂了吧…

注:我的电脑是DELL的,其他品牌的我不知道

在这里插入图片描述
在这里插入图片描述

图片可能有点糊,还请谅解

改完记得保存哦!

然后打开ubuntu,虚拟机就可以正常运行了


总结

本文介绍了因安全启动导致的报错的解决方法

]]>
+ ubuntuVMware报错could not open...解决方法

前言

本人在使用VMware时报了这个错

could not open /dev/vmmon:??????

经过多方查阅资料,为大家提供一个有效的解决办法


一、具体表现

安装好VMware,虚拟机也创建完了

一运行结果就报错了

一开始以为权限问题,用root打开也一样(显然没用)


二、解决方法

需要关闭安全启动 (\(\mathrm{\color{black}{secure \boot}}\))

安全启动设计之初作用是防止恶意软件侵入

事实上它能够做到的仅仅是当电脑引导器被病毒修改之后,它会给出提醒并拒绝启动

因此基本上没什么用

什么?在哪里关闭?? 当然是BIOS

进入BIOS(开机后出现电脑品牌的图标时狂按F12)

下面看我拍的照片就懂了吧...

注:我的电脑是DELL的,其他品牌的我不知道

图片可能有点糊,还请谅解

改完记得保存哦!

然后打开ubuntu,虚拟机就可以正常运行了


总结

本文介绍了因安全启动导致的报错的解决方法

]]>
@@ -9259,7 +9259,7 @@ /2021/03/27/ubuntu-zhui-zhu-shu-biao-zhi-zhen-de-xiao-mao-oneko/ - ubuntu 追逐鼠标指针的小猫~Oneko

前言

最近发现了一个有趣的软件 Oneko

可以让一只小猫追着鼠标指针跑

是不是很有趣?

一、下载Oneko

打开终端

sudo apt install oneko

安装即可

然后它就会在应用程序中了


二、使用Oneko

只要输入这个指令就行

oneko

然后就会有以下效果

Oneko

或者也可以添加到收藏夹后直接单击

不过关闭需要右键,选择Oneko STOP


三、自定义Oneko

官方给的参数如下

Usage: oneko [<options>]Options are:-display <display>: Neko appears on specified display.-fg <color>: Foreground color-bg <color>: Background color-speed <dots>-time <microseconds>-idle <dots>-name <name>: set window name of neko.-towindow       : Neko chases selected window.-toname <name>: Neko chases specified window.-tofocus      : Neko runs on top of focus window-rv: Reverse video. (effects monochrome display only)-position <geometry>   : adjust position relative to mouse pointer.-debug                 : puts you in synchronous mode.-patchlevel            : print out your current patchlevel.-cursor <cursornumber> : cursor number to set when quitting.-neko Use neko bitmaps-tora Use tora bitmaps-dog Use dog bitmaps-sakura Use sakura bitmaps-tomoyo Use tomoyo bitmaps

1.常用指令

其他指令使用较少,您可以自行研究

-fg <color>设置前景色

-bg <color>设置背景色

-speed <dots>设置跑步速度

-time <microseconds>设置帧速

-neko设置为默认猫咪~

-tora设置为另一只猫咪~

-dog设置为小狗

-sakura设置为一位猫耳少女sakura

-tomoyo设置为一位少女tomoyo

2.实际效果

例如:

oneko -fg redoneko -bg greenoneko -toraoneko -dogoneko -sakuraoneko -tomoyo

效果如下
oneko -fg redoneko -bg greenoneko -toraoneko -dogoneko -sakuraoneko -tomoyo


总结

本文仅介绍了常用功能

Oneko真的很好玩呢!

安装和使用也挺简单的

]]>
+ ubuntu追逐鼠标指针的小猫~Oneko

前言

最近发现了一个有趣的软件 Oneko

可以让一只小猫追着鼠标指针跑

是不是很有趣?

一、下载Oneko

打开终端

sudo apt install oneko
安装即可

然后它就会在应用程序中了


二、使用Oneko

只要输入这个指令就行

oneko
然后就会有以下效果

Oneko

或者也可以添加到收藏夹后直接单击

不过关闭需要右键,选择Oneko STOP


三、自定义Oneko

官方给的参数如下

Usage: oneko [<options>]Options are:-display <display>: Neko appears on specified display.-fg <color>: Foreground color-bg <color>: Background color-speed <dots>-time <microseconds>-idle <dots>-name <name>: set window name of neko.-towindow       : Neko chases selected window.-toname <name>: Neko chases specified window.-tofocus      : Neko runs on top of focus window-rv: Reverse video. (effects monochrome display only)-position <geometry>   : adjust position relative to mouse pointer.-debug                 : puts you in synchronous mode.-patchlevel            : print out your current patchlevel.-cursor <cursornumber> : cursor number to set when quitting.-neko Use neko bitmaps-tora Use tora bitmaps-dog Use dog bitmaps-sakura Use sakura bitmaps-tomoyo Use tomoyo bitmaps

1.常用指令

其他指令使用较少,您可以自行研究

-fg <color>设置前景色

-bg <color>设置背景色

-speed <dots>设置跑步速度

-time <microseconds>设置帧速

-neko设置为默认猫咪~

-tora设置为另一只猫咪~

-dog设置为小狗

-sakura设置为一位猫耳少女sakura

-tomoyo设置为一位少女tomoyo

2.实际效果

例如:

oneko -fg redoneko -bg greenoneko -toraoneko -dogoneko -sakuraoneko -tomoyo

效果如下 oneko -fg red


总结

本文仅介绍了常用功能

Oneko真的很好玩呢!

安装和使用也挺简单的

]]>
@@ -9284,7 +9284,7 @@ /2021/03/20/ubuntu-xian-shi-jian-pan-an-jian/ - ubuntu 显示键盘按键

前言

在看一些主播玩游戏时,他们屏幕上会有一个虚拟键盘,可以显示按键

当时觉得很神奇,就想着给ubuntu也弄一个

庆幸的是,ubuntu的确有这种软件


一、KeyMon简介

这个软件叫key-mon,全称Keyboard Status Monitor,即键盘状态监视器

在这里插入图片描述
注:截图时我按住了shfit键

不过相对于别的软件,这个就显得有些简单

但不可否认的确有效果


二、安装步骤

打开终端

sudo apt install key-mon

安装即可

适用于ubuntu18.04,目前ubuntu20.04也可以用


三、自定义设置

希望更好的体验?

右键选择Settings...设置就可以了


总结

本文介绍了KeyMon的安装方法

显示键盘按键就这么简单

]]>
+ ubuntu 显示键盘按键

前言

在看一些主播玩游戏时,他们屏幕上会有一个虚拟键盘,可以显示按键

当时觉得很神奇,就想着给ubuntu也弄一个

庆幸的是,ubuntu的确有这种软件


一、KeyMon简介

这个软件叫key-mon,全称Keyboard StatusMonitor,即键盘状态监视器

在这里插入图片描述 注:截图时我按住了shfit键

不过相对于别的软件,这个就显得有些简单

但不可否认的确有效果


二、安装步骤

打开终端

sudo apt install key-mon

安装即可

适用于ubuntu18.04,目前ubuntu20.04也可以用


三、自定义设置

希望更好的体验?

右键选择Settings...设置就可以了


总结

本文介绍了KeyMon的安装方法

显示键盘按键就这么简单

]]>
@@ -9309,7 +9309,7 @@ /2021/03/11/linux-ji-windows-dui-pai-cheng-xu-c/ - linux及windows对拍程序 C++

前言

OI赛制的比赛中,选手不能看到自己的成绩,那么如何保证代码正确呢?

1.水品高 秒切
2.暴力+对拍 尝试调正解

本文给出了linux和windows的对拍程序


一、什么是对拍?

在比赛中,某道题已经写出了暴力解法(须保证正确),开始尝试写正解

我们就可以用到对拍程序,用于对比暴力解法和尝试解法的输出结果,以判断该解法的是否是正解

注:暴力解法通常跑不了很大的点,因此对拍时数据不强,即使正确也未必是正解

那么对拍程序怎么写呢?


二、怎么写对拍程序?

对拍程序的写法有很多种,但结构基本一致

while(1)//不一定要用死循环{生成随机数据暴力解法跑一遍尝试解法跑一遍比对}

接下来,我将以A+B Problem为例

首先,写暴力程序std.cpp

#include<bits/stdc++.h>using namespace std;#define int long long#define R register#define FILEsigned main(){#ifdef FILE    freopen("data.in","r",stdin);    freopen("std.out","w",stdout);#endif    int a,b;    scanf("%lld%lld",&a,&b);    while(b){a++,b--;}    printf("%lld\n",a);    return 0;}

接下来,尝试写正解my.cpp
#include<bits/stdc++.h>using namespace std;#define int long long#define R register#define FILEsigned main(){#ifdef FILE    freopen("data.in","r",stdin);    freopen("my.out","w",stdout);#endif    int a,b;    scanf("%lld%lld",&a,&b);    printf("%lld\n",a+b);    return 0;}

然后,随机数生成器rand.cpp

#include<bits/stdc++.h>using namespace std;#define int long long#define R register#define mod (int)1e5inline int Rand(){    //rand()随机数函数    return ((rand()%mod*rand()%mod)%mod+rand())%mod;//这里可以随便些,反正生成随机数}signed main(){    srand(time(0));    freopen("data.in","w",stdout);    printf("%lld %lld\n",Rand(),Rand());    return 0;}

最后,记得要把这些文件都编译哦!

注:需要有可执行文件才能对拍,因此需要编译

1.linux下的对拍程序

linux下的比对命令是diff
./std的意思可以简单地认为是运行当前目录下std.cpp编译后生成的可执行文件std

本人使用的是vscode,编译后生成的可执行文件就是文件名

注:如果您没有用类似的软件,您可以手动编译

cd code 注:该文件所在目录,我的叫codeg++ std.cpp -o std

C++对拍程序

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint i=0;signed main(){    while(1)    {        system("./rand");//这些文件要和checker.cpp放在一个目录下        system("./std");//直接运行,因为已经写了freopen        system("./my");        printf("Running on test %lld\n",++i);        if(system("diff std.out my.out"))//如果相同(即正确)返回0        {            printf("Wrong Answer on test %lld\n",i);            return 0;        }    }    return 0;}

Bash脚本

#!/bin/bashi=0while truedo    i=$(($i+1))    ./rand    ./std    ./my    echo "Running on test $i"    if !(diff std.out my.out);then        echo "Wrong answer on test $i"        break    fidone

注:可以用以下指令编辑.sh文件

cd code 注:该文件所在目录gedit checker.sh 注:新建文件sh checker.sh 注:运行

2.windows下的对拍程序

本人使用的Dev C++

C++对拍程序

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint i=0;signed main(){    while(1)    {        system("rand.exe");//直接运行,因为已经写了freopen        system("std.exe");        system("my.exe");        printf("Running on test %lld\n",++i);        if(system("fc std.out my.out"))//windows下的比对命令是fc        {            printf("Wrong Answer on test %lld\n",i);            return 0;        }    }    return 0;}

bat脚本

@echo off::指不显示命令set /a i=0:loop::设置一个标签rand.exestd.exemy.exeset /a i+=1echo Running on test %i%fc std.out my.outif %errorlevel%==0 goto loop::没有问题回到loop那echo Wrong answer on test %i%::否则退出pause^C

总结

本文介绍了linux和windows下对拍程序的写法(共4种)

对拍程序的思想简单,实现多样

在竞赛中能有所帮助

]]>
+ linux及windows对拍程序 C++

前言

OI赛制的比赛中,选手不能看到自己的成绩,那么如何保证代码正确呢?

1.水品高 秒切 2.暴力+对拍 尝试调正解

本文给出了linux和windows的对拍程序


一、什么是对拍?

在比赛中,某道题已经写出了暴力解法(须保证正确),开始尝试写正解

我们就可以用到对拍程序,用于对比暴力解法和尝试解法的输出结果,以判断该解法的是否是正解

注:暴力解法通常跑不了很大的点,因此对拍时数据不强,即使正确也未必是正解

那么对拍程序怎么写呢?


二、怎么写对拍程序?

对拍程序的写法有很多种,但结构基本一致

while(1)//不一定要用死循环{生成随机数据暴力解法跑一遍尝试解法跑一遍比对}

接下来,我将以A+B Problem为例

首先,写暴力程序std.cpp

#include<bits/stdc++.h>using namespace std;#define int long long#define R register#define FILEsigned main(){#ifdef FILE    freopen("data.in","r",stdin);    freopen("std.out","w",stdout);#endif    int a,b;    scanf("%lld%lld",&a,&b);    while(b){a++,b--;}    printf("%lld\n",a);    return 0;}
接下来,尝试写正解my.cpp
#include<bits/stdc++.h>using namespace std;#define int long long#define R register#define FILEsigned main(){#ifdef FILE    freopen("data.in","r",stdin);    freopen("my.out","w",stdout);#endif    int a,b;    scanf("%lld%lld",&a,&b);    printf("%lld\n",a+b);    return 0;}

然后,随机数生成器rand.cpp

#include<bits/stdc++.h>using namespace std;#define int long long#define R register#define mod (int)1e5inline int Rand(){    //rand()随机数函数    return ((rand()%mod*rand()%mod)%mod+rand())%mod;//这里可以随便些,反正生成随机数}signed main(){    srand(time(0));    freopen("data.in","w",stdout);    printf("%lld %lld\n",Rand(),Rand());    return 0;}

最后,记得要把这些文件都编译哦!

注:需要有可执行文件才能对拍,因此需要编译

1.linux下的对拍程序

linux下的比对命令是diff./std的意思可以简单地认为是运行当前目录下std.cpp编译后生成的可执行文件std

本人使用的是vscode,编译后生成的可执行文件就是文件名

注:如果您没有用类似的软件,您可以手动编译

cd code 注:该文件所在目录,我的叫codeg++ std.cpp -o std
####C++对拍程序
#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint i=0;signed main(){    while(1)    {        system("./rand");//这些文件要和checker.cpp放在一个目录下        system("./std");//直接运行,因为已经写了freopen        system("./my");        printf("Running on test %lld\n",++i);        if(system("diff std.out my.out"))//如果相同(即正确)返回0        {            printf("Wrong Answer on test %lld\n",i);            return 0;        }    }    return 0;}
#### Bash脚本
#!/bin/bashi=0while truedo    i=$(($i+1))    ./rand    ./std    ./my    echo "Running on test $i"    if !(diff std.out my.out);then        echo "Wrong answer on test $i"        break    fidone
注:可以用以下指令编辑.sh文件
cd code 注:该文件所在目录gedit checker.sh 注:新建文件sh checker.sh 注:运行

2.windows下的对拍程序

本人使用的Dev C++

C++对拍程序

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint i=0;signed main(){    while(1)    {        system("rand.exe");//直接运行,因为已经写了freopen        system("std.exe");        system("my.exe");        printf("Running on test %lld\n",++i);        if(system("fc std.out my.out"))//windows下的比对命令是fc        {            printf("Wrong Answer on test %lld\n",i);            return 0;        }    }    return 0;}

bat脚本

@echo off::指不显示命令set /a i=0:loop::设置一个标签rand.exestd.exemy.exeset /a i+=1echo Running on test %i%fc std.out my.outif %errorlevel%==0 goto loop::没有问题回到loop那echo Wrong answer on test %i%::否则退出pause^C

总结

本文介绍了linux和windows下对拍程序的写法(共4种)

对拍程序的思想简单,实现多样

在竞赛中能有所帮助

]]>
@@ -9334,7 +9334,7 @@ /2021/03/02/ubuntu-nei-cun-zhan-yong-guo-gao-dao-zhi-qia-si-jie-jue-ban-fa/ - ubuntu 内存占用过高导致卡死 解决办法

一、具体表现

例如下图

在这里插入图片描述

注:图示版本为ubuntu18.04,现在我用的是ubuntu20.04


二、原因

查阅到了一些资料

在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然。这是Linux内存管理的一个优秀特性,在这方 面,区别于 Windows的内存管理。主要特点是,无论物理内存有多大,Linux 都将其充份利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能。

Linux 的这一特性,主要是利用空闲的物理内存,划分出一部份空间,做为 cache 和 buffers ,以此提高数据访问性能。

Linux 优先使用物理内存,当物理内存还有空闲时,linux是不会施放内存的,即时占用内存的程序已经被关闭了(这部分内存就用来做缓存了)。也就是说,即使你有很多内存,用过一段时间后,也会被占满。这样做的好处是,启动那些刚开启过的程序、或是读取刚存取过得数据会比较快,对于服务器很有好处。

总结一下,我的这种情况就是swap空间开的太小了


三、解决方案

把swap空间调大(建议在物理内存的两倍以上)

首先用gparted改一下磁盘分区,然后再配置(这两步缺一不可)

可以参考一下这篇博客,写的很好(感谢)

顺便提醒一下,每个人电脑都存在差异,我在参考那篇博客时就出现了一些不同之处

因为我写本文的时候买的16GB内存条还没到,所以提前调成60GB的swap空间了 (好像大了点)

经过实验证明,目前该方法完全解决了之前的问题(再次道歉 qwq)

如果要释放swap空间的话,可以用以下指令

sudo suswapoff -aswapon swapfile 注:也有用 swapon -a的,不过我这不行

放几张图吧…(开了一堆窗口,然后不会卡死了)

在这里插入图片描述

全部关掉以后,一切正常!!成功!!


四、其他优化

有时候缓存会过高,可以写个脚本及时释放缓存

释放缓存脚本


总结

调大swap空间

]]>
+ ubuntu内存占用过高导致卡死 解决办法

一、具体表现

例如下图

注:图示版本为ubuntu18.04,现在我用的是ubuntu20.04


二、原因

查阅到了一些资料

在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然。这是Linux内存管理的一个优秀特性,在这方面,区别于 Windows的内存管理。主要特点是,无论物理内存有多大,Linux都将其充份利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能。

Linux 的这一特性,主要是利用空闲的物理内存,划分出一部份空间,做为cache 和 buffers ,以此提高数据访问性能。

Linux优先使用物理内存,当物理内存还有空闲时,linux是不会施放内存的,即时占用内存的程序已经被关闭了(这部分内存就用来做缓存了)。也就是说,即使你有很多内存,用过一段时间后,也会被占满。这样做的好处是,启动那些刚开启过的程序、或是读取刚存取过得数据会比较快,对于服务器很有好处。

总结一下,我的这种情况就是swap空间开的太小了


三、解决方案

把swap空间调大(建议在物理内存的两倍以上)

首先用gparted改一下磁盘分区,然后再配置(这两步缺一不可)

可以参考一下这篇博客,写的很好(感谢)

顺便提醒一下,每个人电脑都存在差异,我在参考那篇博客时就出现了一些不同之处

因为我写本文的时候买的16GB内存条还没到,所以提前调成60GB的swap空间了(好像大了点)

经过实验证明,目前该方法完全解决了之前的问题(再次道歉 qwq)

如果要释放swap空间的话,可以用以下指令

sudo suswapoff -aswapon swapfile 注:也有用 swapon -a的,不过我这不行

放几张图吧...(开了一堆窗口,然后不会卡死了)

全部关掉以后,一切正常!!成功!!


四、其他优化

有时候缓存会过高,可以写个脚本及时释放缓存

释放缓存脚本


总结

调大swap空间

]]>
@@ -9359,7 +9359,7 @@ /2021/02/23/zheng-shu-de-hua-fen-dong-tai-gui-hua/ - 整数的划分 动态规划

题目描述

每个非负整数都可以被拆分,比如说

2 = 22 = 1+1
3 = 33 = 2+13 = 1+1+1

输入格式
一个非负整数$n(0 \leq n \leq 100)$

输出格式
输出可以被拆分的方案数

输入样例

4

输出样例

5

样例解释

4 = 44 = 3+14 = 2+24 = 2+1+14 = 1+1+1+1共5种

本题是我在学 $dp$ 时候做的一道题

看到题目首先来一发暴力搜索


40分解法

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint n,ans;int vis[500005];void dfs(R int x,R int y){    if(x<0)return;//拆过头了    if(!x)//没法拆了    {        ++ans;        return;    }    for(R int i=vis[y-1]; i<=x; i++)//要比上次拆的大    {    //这种拆法是反过来拆的,本质上一样    //每一个拆分出的数字都记录在vis数组中    //例如 4 = 1+1+2        if(i<n)        {            vis[y]=i;            dfs(x-i,y+1);        }    }}signed main(){    scanf("%lld",&n);    if(!n)return puts("1"),0;//0就一种    vis[0]=1;//最小拆个1出来    dfs(n,1);    printf("%lld\n",ans+1);//不拆也是一种    return 0;}

如果认为暴力能过,那请看以下数据

输入: 100输出: 190569292

综上所述,这道题正解是$dp$


100分 解法一

设 $dp[i][j]$ 表示 $i$ 拆分成 $j$ 个数的方案数

我们可以把 $j$ 看作 $j$ 个桶,$i$ 就是 $i$ 个$1$

1.$dp[i-1][j-1]$
一个桶里已经放了一个 $1$ 了,还有 $i-1$ 个 $1$ 要划分到 $j-1$ 个桶考虑$i-1$的$j-1$划分($1$本身作为一个划分)保证划分中包含$1$

  1. $dp[i-j][j]$
    先在每一个桶里都放个 $1$,还有 $i-j$ 个 $1$ 要划分到这 $j$ 个桶里($i-j$不作为单独作为划分,因此还是划分为$j$组),考虑 $i-j$ 的 $j$ 划分,在合法的情况下保证划分中不包含$1$
    $[$ 对于 $i$ 的 $j$ 划分$a_k$( $\sum\limits_{k=1}^{j}{a_k}=i,a_k\in \mathbb{Z}_+$ ),都有 $a_k>0$ ,因此 $a_k-1$ 对应了 $i-j$ 的 $j$ 划分 $]$

所以状态转移方程就是

$dp[i][j]=dp[i-j][j]+dp[i-1][j-1]$

注:对于当前状态的划分中包含 $1$ ($dp[i-1][j-1]$),不保证状态转移后仍有 $1$

例如 $dp[7][3]$ 的一种划分方式 $1+2+4$

$dp[7][3]$ $\xrightarrow{}$ $1+dp[6][2]$ $\xrightarrow{}$ $1+\frac{dp[4][2]}{+1}$ $\xrightarrow{}$ $1+\frac{1+dp[3][1]}{+1}$ $\xrightarrow{}$ $1+\frac{1}{+1}+\frac{3}{+1}$ $\xrightarrow{}$ $1+2+4$

边界:

1.$0$ 的总方案数为 $1$
2.$j=1$ ,方案数就是 $1$
3.$i<j$ 时无法划分成 $j$ 个数,方案数为 $0$
4.$i=j$ 时方案数为 $1$

最后答案就是 $\sum\limits_{i=1}^{n}{dp[n][i]}$

时间复杂度 $O(n^2)$

空间复杂度 $O(n^2)$

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint n,ans;int dp[105][105];signed main(){    scanf("%lld",&n);    if(!n)return printf("1\n"),0;    for(R int i=1; i<=n; i++)        for(R int j=1; j<=n; j++)            if(i==j||j==1)dp[i][j]=1;            else if(i>j) dp[i][j]=dp[i-j][j]+dp[i-1][j-1];    for(R int i=1; i<=n; i++)        ans+=dp[n][i];    printf("%lld\n",ans);    return 0;}

100分 解法二

设 $dp[j]$ 为 $j$ 的所有划分方案数

对于 $\forall j \in \mathbb{Z}_+$,$dp[j]=\sum\limits_{i=1}^{j}{dp[j-i]}$

边界: $0$ 的方案数为 $1$

时间复杂度 $O(n^2)$

空间复杂度 $O(n)$

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint n,dp[105]={1};signed main(){    scanf("%lld",&n);    for(R int i=1; i<=n; i++)        for(R int j=i; j<=n; j++)            dp[j]+=dp[j-i];    printf("%lld\n",dp[n]);    return 0;}

]]>
+ 整数的划分 动态规划

题目描述

每个非负整数都可以被拆分,比如说

2 = 22 = 1+1
3 = 33 = 2+13 = 1+1+1

输入格式 一个非负整数\(n(0\leq n \leq 100)\)

输出格式 输出可以被拆分的方案数

输入样例

4

输出样例

5

样例解释

4 = 44 = 3+14 = 2+24 = 2+1+14 = 1+1+1+1共5种

本题是我在学 \(dp\)时候做的一道题

看到题目首先来一发暴力搜索

40分解法

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint n,ans;int vis[500005];void dfs(R int x,R int y){    if(x<0)return;//拆过头了    if(!x)//没法拆了    {        ++ans;        return;    }    for(R int i=vis[y-1]; i<=x; i++)//要比上次拆的大    {    //这种拆法是反过来拆的,本质上一样    //每一个拆分出的数字都记录在vis数组中    //例如 4 = 1+1+2        if(i<n)        {            vis[y]=i;            dfs(x-i,y+1);        }    }}signed main(){    scanf("%lld",&n);    if(!n)return puts("1"),0;//0就一种    vis[0]=1;//最小拆个1出来    dfs(n,1);    printf("%lld\n",ans+1);//不拆也是一种    return 0;}

如果认为暴力能过,那请看以下数据

输入: 100输出: 190569292

综上所述,这道题正解是\(dp\)


100分 解法一

\(dp[i][j]\) 表示 \(i\) 拆分成 \(j\) 个数的方案数

我们可以把 \(j\) 看作 \(j\) 个桶,\(i\) 就是 \(i\) 个\(1\)

1.\(dp[i-1][j-1]\)一个桶里已经放了一个 \(1\) 了,还有\(i-1\)\(1\) 要划分到 \(j-1\) 个桶考虑\(i-1\)的\(j-1\)划分(\(1\)本身作为一个划分)保证划分中包含\(1\)

  1. \(dp[i-j][j]\) 先在每一个桶里都放个\(1\),还有 \(i-j\) 个 \(1\) 要划分到这 \(j\) 个桶里(\(i-j\)不作为单独作为划分,因此还是划分为\(j\)组),考虑 \(i-j\) 的 \(j\)划分,在合法的情况下保证划分中不包含\(1\) \([\)对于 \(i\)\(j\) 划分\(a_k\)( \(\sum\limits_{k=1}^{j}{a_k}=i,a_k\in\mathbb{Z}_+\) ),都有 \(a_k>0\) ,因此 \(a_k-1\) 对应了 \(i-j\) 的 \(j\) 划分 \(]\)

所以状态转移方程就是

\(dp[i][j]=dp[i-j][j]+dp[i-1][j-1]\)

注:对于当前状态的划分中包含 \(1\) (\(dp[i-1][j-1]\)),不保证状态转移后仍有\(1\)

例如 \(dp[7][3]\) 的一种划分方式\(1+2+4\)

\(dp[7][3]\) \(\xrightarrow{}\) \(1+dp[6][2]\) \(\xrightarrow{}\) \(1+\frac{dp[4][2]}{+1}\) \(\xrightarrow{}\) \(1+\frac{1+dp[3][1]}{+1}\) \(\xrightarrow{}\) \(1+\frac{1}{+1}+\frac{3}{+1}\) \(\xrightarrow{}\) \(1+2+4\)

边界:

1.\(0\) 的总方案数为 \(1\) 2.\(j=1\) ,方案数就是 \(1\) 3.\(i<j\) 时无法划分成 \(j\) 个数,方案数为 \(0\) 4.\(i=j\) 时方案数为 \(1\)

最后答案就是 \(\sum\limits_{i=1}^{n}{dp[n][i]}\)

时间复杂度 \(O(n^2)\)

空间复杂度 \(O(n^2)\)

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint n,ans;int dp[105][105];signed main(){    scanf("%lld",&n);    if(!n)return printf("1\n"),0;    for(R int i=1; i<=n; i++)        for(R int j=1; j<=n; j++)            if(i==j||j==1)dp[i][j]=1;            else if(i>j) dp[i][j]=dp[i-j][j]+dp[i-1][j-1];    for(R int i=1; i<=n; i++)        ans+=dp[n][i];    printf("%lld\n",ans);    return 0;}

100分 解法二

\(dp[j]\)\(j\) 的所有划分方案数

对于 \(\forall j \in\mathbb{Z}_+\)\(dp[j]=\sum\limits_{i=1}^{j}{dp[j-i]}\)

边界: \(0\)的方案数为 \(1\)

时间复杂度 \(O(n^2)\)

空间复杂度 \(O(n)\)

#include<bits/stdc++.h>using namespace std;#define int long long#define R registerint n,dp[105]={1};signed main(){    scanf("%lld",&n);    for(R int i=1; i<=n; i++)        for(R int j=i; j<=n; j++)            dp[j]+=dp[j-i];    printf("%lld\n",dp[n]);    return 0;}

]]>
@@ -9384,7 +9384,7 @@ /2021/02/17/ubuntu20.04-zhuo-mian-tu-biao-xian-shi-yi-chang-ji-jie-jue-fang-fa/ - ubuntu20.04 桌面图标显示异常及解决方法

前言

更新至ubuntu20.04后,出现了一些以前没有的问题

桌面上有些图标不显示


一、具体表现

例如有一次我在做备忘录时

我习惯地打开终端

cd 桌面gedit 账号.txt

桌面效果


然后我写了一些东西,保存后,桌面变成了这样 (注:并非每次都会出现这种问题)


打开文件夹,显示我桌面上的文件都存在,但是桌面上不显示,双击文件原来的位置也没有用


二、原因

这种情况是$\text{gnome\ shell}$出现了异常

众所周知重启电脑能解决大部分问题,但是总不能动不动就重启吧?


三、解决方法

按下alt+F2,会跳出一个窗口,然后输入一个r(重启gnome shell)

然后再看一眼桌面,就恢复了


总结

碰到gnome shell问题,重启它一般能解决

]]>
+ ubuntu20.04桌面图标显示异常及解决方法

前言

更新至ubuntu20.04后,出现了一些以前没有的问题

桌面上有些图标不显示


一、具体表现

例如有一次我在做备忘录时

我习惯地打开终端

cd 桌面gedit 账号.txt

桌面效果

然后我写了一些东西,保存后,桌面变成了这样(注:并非每次都会出现这种问题)

打开文件夹,显示我桌面上的文件都存在,但是桌面上不显示,双击文件原来的位置也没有用


二、原因

这种情况是\(\text{gnome\ shell}\)出现了异常

众所周知重启电脑能解决大部分问题,但是总不能动不动就重启吧?


三、解决方法

按下alt+F2,会跳出一个窗口,然后输入一个r(重启gnomeshell)

然后再看一眼桌面,就恢复了


总结

碰到gnome shell问题,重启它一般能解决

]]>
@@ -9409,7 +9409,7 @@ /2021/02/11/cf652b-z-sort-ti-jie/ - CF652B z-sort 题解

题目链接:CF652B z-sort

题意:一种叫Z排序的方法,奇数位递增,偶数位递减,给定数组请用此方法排序

题意要求奇数位递增,偶数位递减

那每次只要输出最小值和最大值就可以了

这里给出了优先队列的写法

#include<bits/stdc++.h>using namespace std;#define R register#define int long longpriority_queue<int> q1;//大根堆priority_queue< int,vector<int>,greater<int> > q2;//小根堆int n;signed main(){    scanf("%lld",&n);    for(R int i=0,t; i<n; i++)    {        scanf("%lld",&t);        q1.push(t);        q2.push(t);    }    for(R int i=1; i<=n/2; i++)    {        printf("%lld %lld ",q2.top(),q1.top());        q1.pop();q2.pop();    }    if(n&1)printf("%lld",q1.top());//n为奇数    return 0;}
]]>
+ CF652B z-sort 题解

题目链接:CF652Bz-sort

题意:一种叫Z排序的方法,奇数位递增,偶数位递减,给定数组请用此方法排序

题意要求奇数位递增,偶数位递减

那每次只要输出最小值和最大值就可以了

这里给出了优先队列的写法

#include<bits/stdc++.h>using namespace std;#define R register#define int long longpriority_queue<int> q1;//大根堆priority_queue< int,vector<int>,greater<int> > q2;//小根堆int n;signed main(){    scanf("%lld",&n);    for(R int i=0,t; i<n; i++)    {        scanf("%lld",&t);        q1.push(t);        q2.push(t);    }    for(R int i=1; i<=n/2; i++)    {        printf("%lld %lld ",q2.top(),q1.top());        q1.pop();q2.pop();    }    if(n&1)printf("%lld",q1.top());//n为奇数    return 0;}
]]>
@@ -9436,7 +9436,7 @@ /2021/02/11/cf708a-letters-cyclic-shift-ti-jie/ - CF708A Letters Cyclic Shift 题解

题目链接:CF708A Letters Cyclic Shift

题意:一次变换指将字母变为它前面一个字母,例如a变成zb变成a,给定字符串,找出一个非空子串进行变换使得改变后字典序尽可能小

要求字典序最小,首先想到从最左端开始改变

那么这样做一定是最小的吗?例如下面这种

aabcdefabb

如果改变了第一个字符a,它会变成z,字典序不降反升

我们可以初步得到结论,从第一个不为a的位置开始改变,能得到最小结果

再看题目,要求改变的是非空子串,因此只能改变到下一个不为a的位置

因此aabcdefabb改变后变为aaabcdeabb

还有一种情况要特判 例如aaaaa

题目要求你必须选择一个非空子串,这种情况只要把最后一个字符改变就行了

代码如下

#include<bits/stdc++.h>using namespace std;#define R registerstring s;bool flag;signed main(){    cin>>s;    for(R int i=0; i<s.size(); i++)        if(s[i]^97) //s[i]!='a'        {            s[i]--;flag=1;        }        else if(flag) return cout<<s,0;    if(!flag)s[s.size()-1]='z';//特判    cout<<s;    return 0;}
]]>
+ CF708A Letters Cyclic Shift题解

题目链接:CF708ALetters Cyclic Shift

题意:一次变换指将字母变为它前面一个字母,例如a变成zb变成a,给定字符串,找出一个非空子串进行变换使得改变后字典序尽可能小

要求字典序最小,首先想到从最左端开始改变

那么这样做一定是最小的吗?例如下面这种

aabcdefabb

如果改变了第一个字符a,它会变成z,字典序不降反升

我们可以初步得到结论,从第一个不为a的位置开始改变,能得到最小结果

再看题目,要求改变的是非空子串,因此只能改变到下一个不为a的位置

因此aabcdefabb改变后变为aaabcdeabb

还有一种情况要特判 例如aaaaa

题目要求你必须选择一个非空子串,这种情况只要把最后一个字符改变就行了

代码如下

#include<bits/stdc++.h>using namespace std;#define R registerstring s;bool flag;signed main(){    cin>>s;    for(R int i=0; i<s.size(); i++)        if(s[i]^97) //s[i]!='a'        {            s[i]--;flag=1;        }        else if(flag) return cout<<s,0;    if(!flag)s[s.size()-1]='z';//特判    cout<<s;    return 0;}
]]>
@@ -9463,7 +9463,7 @@ /2021/02/11/cf676a-nicholas-and-permutation-ti-jie/ - CF676A Nicholas and Permutation 题解

题目链接:CF676A Nicholas and Permutation

题意:给定数组,可以让两个数的位置交换,让最大值和最小值的位置的差的绝对值最大

先用$c$记录最大值位置,$d$记录最小值位置

然后取$4$种情况中绝对值最大的

第$1$种,$d$与第$1$个元素交换

第$2$种,$d$与第$n$个元素交换

第$3$种,$c$与第$1$个元素交换

第$4$种,$c$与第$n$个元素交换

代码实现还是比较简单的

#include<bits/stdc++.h>using namespace std;#define R registerint n,k,a=-1,b=INT_MAX,c,d;//最大值初始化为-1,最小值初始化为很大的数(2147483647)signed main(){    scanf("%d",&n);    for(R int i=0; i<n; i++)    {        scanf("%d",&k);//无需数组存        if(k>a)a=k,c=i;        if(k<b)b=k,d=i;    }    printf    (        "%d\n",        max        (            max(d,abs(d-(n-1))),            max(c,abs(c-(n-1)))        )    );//此处是为了方便阅读    return 0;}

]]>
+ CF676A Nicholas andPermutation 题解

题目链接:CF676ANicholas and Permutation

题意:给定数组,可以让两个数的位置交换,让最大值和最小值的位置的差的绝对值最大

先用\(c\)记录最大值位置,\(d\)记录最小值位置

然后取\(4\)种情况中绝对值最大的

\(1\)种,\(d\)与第\(1\)个元素交换

\(2\)种,\(d\)与第\(n\)个元素交换

\(3\)种,\(c\)与第\(1\)个元素交换

\(4\)种,\(c\)与第\(n\)个元素交换

代码实现还是比较简单的

#include<bits/stdc++.h>using namespace std;#define R registerint n,k,a=-1,b=INT_MAX,c,d;//最大值初始化为-1,最小值初始化为很大的数(2147483647)signed main(){    scanf("%d",&n);    for(R int i=0; i<n; i++)    {        scanf("%d",&k);//无需数组存        if(k>a)a=k,c=i;        if(k<b)b=k,d=i;    }    printf    (        "%d\n",        max        (            max(d,abs(d-(n-1))),            max(c,abs(c-(n-1)))        )    );//此处是为了方便阅读    return 0;}

]]>
@@ -9488,7 +9488,7 @@ /2021/02/11/at1899-hua-xiang-chu-li-gao-qiao-jun-ti-jie/ - AT1899 画像処理高橋君 题解

题目链接:AT1899 画像処理高橋君

原题是日文的,我就不翻译了(

题意:给出压缩后的图像,求压缩前的图像
压缩是指对于各个像素,在其周围8个方向的像素中,只要有一个黑色像素,其像素就会变黑的处理

从题意中第二句话可以初步推断出,只要是周围8个方向上都是黑色的像素就是压缩前存在的黑色像素
例如

###..###.......

压缩前的图像就是

##.............

_这么说来只要扫一遍,把和白色像素相接的黑色像素全部变为白色不就好了?_

但是如果是下面这种情况

###.##.#..##..##

用刚才的思路做,会得到这样的图像
#..............#

这样就出现了问题

如果把得到的这个图像压缩,得到的应该是

#...          ##......    ->    ##......          ..##...#          ..##

这样就还得再检查一遍得到的图像是否合法

代码如下

#include<bits/stdc++.h>using namespace std;int n,m;char a[205][205];//记录压缩后的图像(即输入的图像)char b[205][205];//用于输出压缩前的图像int dx[9]={1,1,1,0,0,0,-1,-1,-1};//八个方向+原地int dy[9]={1,0,-1,1,0,-1,1,0,-1};signed main(){    cin>>n>>m;    for(int i=0; i<n; i++)        cin>>a[i];//输入    for(int i=0; i<n; i++)    {        for(int j=0; j<m; j++)        {            int flag=0;            for(int k=0; k<9; k++)            {                int tx = i+dx[k];                int ty = j+dy[k];                if(tx>=0&&ty>=0&&tx<n&&ty<m&&a[tx][ty]=='.')flag=1;            }            if(flag)b[i][j]='.';//如果这个像素本来就是白的,或者这是个和白色像素相连的黑色像素            else b[i][j]='#';//不和白色像素相连的黑色像素        }    }            for(int i=0; i<n; i++)    {        for(int j=0; j<m; j++)        {            if(a[i][j]=='#')//检查压缩后的图像            {                int flag=0;                for(int k=0; k<9; k++)                {                    int tx = i+dx[k];                    int ty = j+dy[k];                    if(tx>=0&&ty>=0&&tx<n&&ty<m&&b[tx][ty]=='#')flag=1;//该像素是压缩出来的                }                if(!flag)//得到的压缩前的图像 无法压缩为 压缩后的图像                {                    cout<<"impossible"<<endl;//不是合法压缩出来的图像                    return 0;                }            }        }    }    cout<<"possible"<<endl;//合法    for(int i=0; i<n; i++)    {            for(int j=0; j<m; j++)        {            cout<<b[i][j];        }        cout<<endl;    }    return 0;}

]]>
+ AT1899 画像処理高橋君 题解

题目链接:AT1899画像処理高橋君

原题是日文的,我就不翻译了(

题意:给出压缩后的图像,求压缩前的图像压缩是指对于各个像素,在其周围8个方向的像素中,只要有一个黑色像素,其像素就会变黑的处理

从题意中第二句话可以初步推断出,只要是周围8个方向上都是黑色的像素就是压缩前存在的黑色像素例如

###..###.......

压缩前的图像就是

##.............
这么说来只要扫一遍,把和白色像素相接的黑色像素全部变为白色不就好了?

但是如果是下面这种情况

###.##.#..##..##
用刚才的思路做,会得到这样的图像
#..............#
这样就出现了问题

如果把得到的这个图像压缩,得到的应该是

#...          ##......    ->    ##......          ..##...#          ..##
这样就还得再检查一遍得到的图像是否合法

代码如下

#include<bits/stdc++.h>using namespace std;int n,m;char a[205][205];//记录压缩后的图像(即输入的图像)char b[205][205];//用于输出压缩前的图像int dx[9]={1,1,1,0,0,0,-1,-1,-1};//八个方向+原地int dy[9]={1,0,-1,1,0,-1,1,0,-1};signed main(){    cin>>n>>m;    for(int i=0; i<n; i++)        cin>>a[i];//输入    for(int i=0; i<n; i++)    {        for(int j=0; j<m; j++)        {            int flag=0;            for(int k=0; k<9; k++)            {                int tx = i+dx[k];                int ty = j+dy[k];                if(tx>=0&&ty>=0&&tx<n&&ty<m&&a[tx][ty]=='.')flag=1;            }            if(flag)b[i][j]='.';//如果这个像素本来就是白的,或者这是个和白色像素相连的黑色像素            else b[i][j]='#';//不和白色像素相连的黑色像素        }    }            for(int i=0; i<n; i++)    {        for(int j=0; j<m; j++)        {            if(a[i][j]=='#')//检查压缩后的图像            {                int flag=0;                for(int k=0; k<9; k++)                {                    int tx = i+dx[k];                    int ty = j+dy[k];                    if(tx>=0&&ty>=0&&tx<n&&ty<m&&b[tx][ty]=='#')flag=1;//该像素是压缩出来的                }                if(!flag)//得到的压缩前的图像 无法压缩为 压缩后的图像                {                    cout<<"impossible"<<endl;//不是合法压缩出来的图像                    return 0;                }            }        }    }    cout<<"possible"<<endl;//合法    for(int i=0; i<n; i++)    {            for(int j=0; j<m; j++)        {            cout<<b[i][j];        }        cout<<endl;    }    return 0;}

]]>
diff --git "a/tags/01\345\210\206\346\225\260\350\247\204\345\210\222/index.html" "b/tags/01\345\210\206\346\225\260\350\247\204\345\210\222/index.html" index 42ead17dd9..6d075fd707 100644 --- "a/tags/01\345\210\206\346\225\260\350\247\204\345\210\222/index.html" +++ "b/tags/01\345\210\206\346\225\260\350\247\204\345\210\222/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + chip-active " + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + chip-default " + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -505,7 +505,7 @@
-
+
@@ -622,7 +622,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/DP/index.html b/tags/DP/index.html index 533ae51daf..e4b7e4e0f1 100644 --- a/tags/DP/index.html +++ b/tags/DP/index.html @@ -360,20 +360,20 @@
- + DP - 97 + chip-default " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-active " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -934,7 +934,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/DP/page/2/index.html b/tags/DP/page/2/index.html index 4df1897c42..559ae83ac5 100644 --- a/tags/DP/page/2/index.html +++ b/tags/DP/page/2/index.html @@ -360,20 +360,20 @@ - + DP - 97 + chip-default " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-active " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/DP/page/3/index.html b/tags/DP/page/3/index.html index 1d7e3fa3de..f57eaafe8b 100644 --- a/tags/DP/page/3/index.html +++ b/tags/DP/page/3/index.html @@ -360,20 +360,20 @@ - + DP - 97 + chip-default " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-active " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -633,7 +633,7 @@
-
+
-
+
@@ -697,7 +697,7 @@
-
+
@@ -761,7 +761,7 @@
-
+
@@ -825,7 +825,7 @@
-
+
@@ -857,7 +857,7 @@
-
+
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/DP/page/5/index.html b/tags/DP/page/5/index.html index 7b6ac83537..2113cf7259 100644 --- a/tags/DP/page/5/index.html +++ b/tags/DP/page/5/index.html @@ -360,20 +360,20 @@
- + DP - 97 + chip-default " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-active " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -505,7 +505,7 @@
-
+
@@ -537,7 +537,7 @@
-
+
@@ -601,7 +601,7 @@
-
+
@@ -633,7 +633,7 @@
-
+
@@ -665,7 +665,7 @@
-
+
@@ -697,7 +697,7 @@
-
+
@@ -729,7 +729,7 @@
-
+
@@ -793,7 +793,7 @@
-
+
@@ -825,7 +825,7 @@
-
+
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/DP/page/6/index.html b/tags/DP/page/6/index.html index db4dba1616..beb330ffe5 100644 --- a/tags/DP/page/6/index.html +++ b/tags/DP/page/6/index.html @@ -360,20 +360,20 @@
- + DP - 97 + chip-default " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-active " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -667,13 +667,13 @@
- +
- CF346B Lucky Common Subsequence 题解 + CF41D Pawn 题解 - CF346B Lucky Common Subsequence 题解 + CF41D Pawn 题解
@@ -699,13 +699,13 @@
- +
- CF41D Pawn 题解 + CF374C Inna and Dima 题解 - CF41D Pawn 题解 + CF374C Inna and Dima 题解
@@ -731,13 +731,13 @@
- +
- CF219D Choosing Capital for Treeland 题解 + CF346B Lucky Common Subsequence 题解 - CF219D Choosing Capital for Treeland 题解 + CF346B Lucky Common Subsequence 题解
@@ -763,13 +763,13 @@
- +
- CF374C Inna and Dima 题解 + CF219D Choosing Capital for Treeland 题解 - CF374C Inna and Dima 题解 + CF219D Choosing Capital for Treeland 题解
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/DP/page/7/index.html b/tags/DP/page/7/index.html index 8bf5f62770..c415fc9ae3 100644 --- a/tags/DP/page/7/index.html +++ b/tags/DP/page/7/index.html @@ -360,20 +360,20 @@ - + DP - 97 + chip-default " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-active " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -665,15 +665,15 @@
-
+
- +
- 洛谷P1772 [ZJOI2006]物流运输 题解 + 洛谷P1858 多人背包 题解 - 洛谷P1772 [ZJOI2006]物流运输 题解 + 洛谷P1858 多人背包 题解
@@ -697,15 +697,15 @@
-
+
- +
- 洛谷P1858 多人背包 题解 + 洛谷P1772 [ZJOI2006]物流运输 题解 - 洛谷P1858 多人背包 题解 + 洛谷P1772 [ZJOI2006]物流运输 题解
@@ -763,13 +763,13 @@
- +
- 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + 洛谷P1282 多米诺骨牌 题解 - 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + 洛谷P1282 多米诺骨牌 题解
@@ -795,13 +795,13 @@
- +
- 洛谷P1282 多米诺骨牌 题解 + 洛谷P1171 售货员的难题 题解 - 洛谷P1282 多米诺骨牌 题解 + 洛谷P1171 售货员的难题 题解
@@ -827,13 +827,13 @@
- +
- 洛谷P1171 售货员的难题 题解 + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 - 洛谷P1171 售货员的难题 题解 + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/DP/page/8/index.html b/tags/DP/page/8/index.html index e7248201e2..60f3b466f5 100644 --- a/tags/DP/page/8/index.html +++ b/tags/DP/page/8/index.html @@ -360,20 +360,20 @@ - + DP - 97 + chip-default " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-active " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -537,7 +537,7 @@
-
+
@@ -665,7 +665,7 @@
-
+
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/DP/page/9/index.html b/tags/DP/page/9/index.html index 7497652e0d..dadcb50313 100644 --- a/tags/DP/page/9/index.html +++ b/tags/DP/page/9/index.html @@ -360,20 +360,20 @@
- + DP - 97 + chip-default " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-active " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -582,7 +582,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/index.html b/tags/index.html index e1cbb135ae..b8e4390176 100644 --- a/tags/index.html +++ b/tags/index.html @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -518,7 +518,7 @@ @@ -543,7 +543,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/ubuntu/index.html b/tags/ubuntu/index.html index 269e411c0e..9327eb81a2 100644 --- a/tags/ubuntu/index.html +++ b/tags/ubuntu/index.html @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -934,7 +934,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/ubuntu/page/2/index.html b/tags/ubuntu/page/2/index.html index 6f4f9ef3a3..599acbf6f6 100644 --- a/tags/ubuntu/page/2/index.html +++ b/tags/ubuntu/page/2/index.html @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -582,7 +582,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git a/tags/windows/index.html b/tags/windows/index.html index 3ccd98cda8..bb34bc35fb 100644 --- a/tags/windows/index.html +++ b/tags/windows/index.html @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -654,7 +654,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\344\272\244\344\272\222\351\242\230/index.html" "b/tags/\344\272\244\344\272\222\351\242\230/index.html" index 3982104b44..dfed3e1a24 100644 --- "a/tags/\344\272\244\344\272\222\351\242\230/index.html" +++ "b/tags/\344\272\244\344\272\222\351\242\230/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -558,7 +558,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\345\210\206\345\235\227/index.html" "b/tags/\345\210\206\345\235\227/index.html" index 64281e25e8..2945e2976c 100644 --- "a/tags/\345\210\206\345\235\227/index.html" +++ "b/tags/\345\210\206\345\235\227/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -558,7 +558,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\345\233\276\350\256\272/index.html" "b/tags/\345\233\276\350\256\272/index.html" index 33ae6815e3..317e7a4d90 100644 --- "a/tags/\345\233\276\350\256\272/index.html" +++ "b/tags/\345\233\276\350\256\272/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + chip-active " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-default " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -934,7 +934,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\345\233\276\350\256\272/page/2/index.html" "b/tags/\345\233\276\350\256\272/page/2/index.html" index b98bd4848d..678be723fa 100644 --- "a/tags/\345\233\276\350\256\272/page/2/index.html" +++ "b/tags/\345\233\276\350\256\272/page/2/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + chip-active " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-default " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\345\233\276\350\256\272/page/3/index.html" "b/tags/\345\233\276\350\256\272/page/3/index.html" index f3110d20ac..59a3b8a1a9 100644 --- "a/tags/\345\233\276\350\256\272/page/3/index.html" +++ "b/tags/\345\233\276\350\256\272/page/3/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + chip-active " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-default " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -601,7 +601,7 @@
-
+
@@ -825,7 +825,7 @@
-
+
@@ -857,7 +857,7 @@
-
+
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\345\233\276\350\256\272/page/4/index.html" "b/tags/\345\233\276\350\256\272/page/4/index.html" index b703e53335..e9f6b4e484 100644 --- "a/tags/\345\233\276\350\256\272/page/4/index.html" +++ "b/tags/\345\233\276\350\256\272/page/4/index.html" @@ -360,20 +360,20 @@
- + DP - 97 + chip-active " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-default " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -505,7 +505,7 @@
-
+
@@ -537,7 +537,7 @@
-
+
@@ -601,7 +601,7 @@
-
+
@@ -633,7 +633,7 @@
-
+
@@ -697,7 +697,7 @@
-
+
@@ -729,7 +729,7 @@
-
+
@@ -761,7 +761,7 @@
-
+
@@ -793,7 +793,7 @@
-
+
@@ -825,7 +825,7 @@
-
+
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\345\233\276\350\256\272/page/5/index.html" "b/tags/\345\233\276\350\256\272/page/5/index.html" index 33d57103dd..1014ca67f5 100644 --- "a/tags/\345\233\276\350\256\272/page/5/index.html" +++ "b/tags/\345\233\276\350\256\272/page/5/index.html" @@ -360,20 +360,20 @@
- + DP - 97 + chip-active " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-default " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -505,7 +505,7 @@
-
+
@@ -537,7 +537,7 @@
-
+
@@ -569,7 +569,7 @@
-
+
@@ -601,7 +601,7 @@
-
+
@@ -633,7 +633,7 @@
-
+
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\345\233\276\350\256\272/page/6/index.html" "b/tags/\345\233\276\350\256\272/page/6/index.html" index ac2d5fc770..ef340b3f8e 100644 --- "a/tags/\345\233\276\350\256\272/page/6/index.html" +++ "b/tags/\345\233\276\350\256\272/page/6/index.html" @@ -360,20 +360,20 @@
- + DP - 97 + chip-active " + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + chip-default " + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -838,7 +838,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\345\255\227\347\254\246\344\270\262/index.html" "b/tags/\345\255\227\347\254\246\344\270\262/index.html" index d386833d13..3c55263c6e 100644 --- "a/tags/\345\255\227\347\254\246\344\270\262/index.html" +++ "b/tags/\345\255\227\347\254\246\344\270\262/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -934,7 +934,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\345\255\227\347\254\246\344\270\262/page/2/index.html" "b/tags/\345\255\227\347\254\246\344\270\262/page/2/index.html" index 1e53097c59..7111011cd8 100644 --- "a/tags/\345\255\227\347\254\246\344\270\262/page/2/index.html" +++ "b/tags/\345\255\227\347\254\246\344\270\262/page/2/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\345\255\227\347\254\246\344\270\262/page/3/index.html" "b/tags/\345\255\227\347\254\246\344\270\262/page/3/index.html" index b81fddd818..f823576e44 100644 --- "a/tags/\345\255\227\347\254\246\344\270\262/page/3/index.html" +++ "b/tags/\345\255\227\347\254\246\344\270\262/page/3/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -838,7 +838,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\200\273\347\273\223/index.html" "b/tags/\346\200\273\347\273\223/index.html" index 2604cc6fe0..39185e0446 100644 --- "a/tags/\346\200\273\347\273\223/index.html" +++ "b/tags/\346\200\273\347\273\223/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -932,7 +932,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\200\273\347\273\223/page/2/index.html" "b/tags/\346\200\273\347\273\223/page/2/index.html" index 40ecc03472..6f749fffad 100644 --- "a/tags/\346\200\273\347\273\223/page/2/index.html" +++ "b/tags/\346\200\273\347\273\223/page/2/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -742,7 +742,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\211\223\350\241\250/index.html" "b/tags/\346\211\223\350\241\250/index.html" index 24e4ad516d..d609b489db 100644 --- "a/tags/\346\211\223\350\241\250/index.html" +++ "b/tags/\346\211\223\350\241\250/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -558,7 +558,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\221\206\347\203\202/index.html" "b/tags/\346\221\206\347\203\202/index.html" index b91969101e..84949e19ab 100644 --- "a/tags/\346\221\206\347\203\202/index.html" +++ "b/tags/\346\221\206\347\203\202/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + chip-default " + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + chip-active " + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -590,7 +590,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\345\255\246/index.html" "b/tags/\346\225\260\345\255\246/index.html" index 254a4c45d7..43b2f5f609 100644 --- "a/tags/\346\225\260\345\255\246/index.html" +++ "b/tags/\346\225\260\345\255\246/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -934,7 +934,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\345\255\246/page/2/index.html" "b/tags/\346\225\260\345\255\246/page/2/index.html" index eec35ce758..72f5d57ba7 100644 --- "a/tags/\346\225\260\345\255\246/page/2/index.html" +++ "b/tags/\346\225\260\345\255\246/page/2/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\345\255\246/page/3/index.html" "b/tags/\346\225\260\345\255\246/page/3/index.html" index 69918ccf2f..b0ebb4b95d 100644 --- "a/tags/\346\225\260\345\255\246/page/3/index.html" +++ "b/tags/\346\225\260\345\255\246/page/3/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\345\255\246/page/4/index.html" "b/tags/\346\225\260\345\255\246/page/4/index.html" index 65e7349940..41a3b9efce 100644 --- "a/tags/\346\225\260\345\255\246/page/4/index.html" +++ "b/tags/\346\225\260\345\255\246/page/4/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\345\255\246/page/5/index.html" "b/tags/\346\225\260\345\255\246/page/5/index.html" index 7e67c383c9..81163edcb7 100644 --- "a/tags/\346\225\260\345\255\246/page/5/index.html" +++ "b/tags/\346\225\260\345\255\246/page/5/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -763,13 +763,13 @@
- +
- 洛谷P3336 [ZJOI2013]话旧 题解 + 洛谷P3327 [SDOI2015]约数个数和 题解 - 洛谷P3336 [ZJOI2013]话旧 题解 + 洛谷P3327 [SDOI2015]约数个数和 题解
@@ -795,13 +795,13 @@
- +
- 洛谷P3327 [SDOI2015]约数个数和 题解 + 洛谷P3336 [ZJOI2013]话旧 题解 - 洛谷P3327 [SDOI2015]约数个数和 题解 + 洛谷P3336 [ZJOI2013]话旧 题解
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\345\255\246/page/6/index.html" "b/tags/\346\225\260\345\255\246/page/6/index.html" index ed930f2fa5..df244be6ef 100644 --- "a/tags/\346\225\260\345\255\246/page/6/index.html" +++ "b/tags/\346\225\260\345\255\246/page/6/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\345\255\246/page/7/index.html" "b/tags/\346\225\260\345\255\246/page/7/index.html" index 2f25ecfe27..33d2177350 100644 --- "a/tags/\346\225\260\345\255\246/page/7/index.html" +++ "b/tags/\346\225\260\345\255\246/page/7/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -741,7 +741,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/index.html" "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/index.html" index fb889918b9..9626099dab 100644 --- "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/index.html" +++ "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -934,7 +934,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/2/index.html" "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/2/index.html" index abb0d2f9ad..74410bb06e 100644 --- "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/2/index.html" +++ "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/2/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/3/index.html" "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/3/index.html" index b4b49af987..9404743035 100644 --- "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/3/index.html" +++ "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/3/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/4/index.html" "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/4/index.html" index 13cdf3f069..3d5a4696b7 100644 --- "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/4/index.html" +++ "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/4/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -729,7 +729,7 @@
-
+
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/5/index.html" "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/5/index.html" index e990ec067d..26d62cbbe5 100644 --- "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/5/index.html" +++ "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/5/index.html" @@ -360,20 +360,20 @@
- + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -505,7 +505,7 @@
-
+
@@ -665,7 +665,7 @@
-
+
@@ -729,7 +729,7 @@
-
+
@@ -761,7 +761,7 @@
-
+
@@ -793,7 +793,7 @@
-
+
@@ -825,7 +825,7 @@
-
+
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/6/index.html" "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/6/index.html" index ae914463fb..92d2a46e3a 100644 --- "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/6/index.html" +++ "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/6/index.html" @@ -360,20 +360,20 @@
- + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -505,7 +505,7 @@
-
+
@@ -569,7 +569,7 @@
-
+
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/7/index.html" "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/7/index.html" index 9524d3e8c8..4bc426a5bb 100644 --- "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/7/index.html" +++ "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/7/index.html" @@ -360,20 +360,20 @@
- + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -934,7 +934,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/8/index.html" "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/8/index.html" index ab270b0eb8..ba4ac1f2a2 100644 --- "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/8/index.html" +++ "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/8/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/9/index.html" "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/9/index.html" index cd902a7fcb..f1f590636a 100644 --- "a/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/9/index.html" +++ "b/tags/\346\225\260\346\215\256\347\273\223\346\236\204/page/9/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -833,7 +833,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\216\204\345\255\246/index.html" "b/tags/\347\216\204\345\255\246/index.html" index 80e4ad2070..eba5f6f77d 100644 --- "a/tags/\347\216\204\345\255\246/index.html" +++ "b/tags/\347\216\204\345\255\246/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -558,7 +558,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\236\216\346\220\236/index.html" "b/tags/\347\236\216\346\220\236/index.html" index 0eb630755e..0331a9af12 100644 --- "a/tags/\347\236\216\346\220\236/index.html" +++ "b/tags/\347\236\216\346\220\236/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + chip-default " + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + chip-active " + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -622,7 +622,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/index.html" "b/tags/\347\256\227\346\263\225/index.html" index 5b2cc779c6..82522b6b24 100644 --- "a/tags/\347\256\227\346\263\225/index.html" +++ "b/tags/\347\256\227\346\263\225/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -934,7 +934,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/10/index.html" "b/tags/\347\256\227\346\263\225/page/10/index.html" index 4fb94e9fa2..a5ab66a8d2 100644 --- "a/tags/\347\256\227\346\263\225/page/10/index.html" +++ "b/tags/\347\256\227\346\263\225/page/10/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/11/index.html" "b/tags/\347\256\227\346\263\225/page/11/index.html" index 9524dd95c3..fc96b1f6bf 100644 --- "a/tags/\347\256\227\346\263\225/page/11/index.html" +++ "b/tags/\347\256\227\346\263\225/page/11/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -793,7 +793,7 @@
-
+
-
+
@@ -761,7 +761,7 @@
-
+
@@ -825,7 +825,7 @@
-
+
-
+
@@ -569,7 +569,7 @@
-
+
@@ -633,7 +633,7 @@
-
+
@@ -697,7 +697,7 @@
-
+
@@ -729,7 +729,7 @@
-
+
@@ -761,7 +761,7 @@
-
+
@@ -793,7 +793,7 @@
-
+
@@ -857,7 +857,7 @@
-
+
-
+
@@ -569,7 +569,7 @@
-
+
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/15/index.html" "b/tags/\347\256\227\346\263\225/page/15/index.html" index eae6e853aa..986abd3544 100644 --- "a/tags/\347\256\227\346\263\225/page/15/index.html" +++ "b/tags/\347\256\227\346\263\225/page/15/index.html" @@ -360,20 +360,20 @@
- + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -571,13 +571,13 @@
- +
- 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + 洛谷P1282 多米诺骨牌 题解 - 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 + 洛谷P1282 多米诺骨牌 题解
@@ -603,13 +603,13 @@
- +
- 洛谷P1282 多米诺骨牌 题解 + 洛谷P1171 售货员的难题 题解 - 洛谷P1282 多米诺骨牌 题解 + 洛谷P1171 售货员的难题 题解
@@ -635,13 +635,13 @@
- +
- 洛谷P1171 售货员的难题 题解 + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法 - 洛谷P1171 售货员的难题 题解 + 洛谷P1156 垃圾陷阱 题解&浅谈刷表法与填表法
@@ -731,13 +731,13 @@
- +
- CF346B Lucky Common Subsequence 题解 + CF41D Pawn 题解 - CF346B Lucky Common Subsequence 题解 + CF41D Pawn 题解
@@ -761,15 +761,15 @@
-
+
- +
- CF41D Pawn 题解 + CF294B Shaass and Bookshelf 题解 - CF41D Pawn 题解 + CF294B Shaass and Bookshelf 题解
@@ -795,13 +795,13 @@
- +
- CF219D Choosing Capital for Treeland 题解 + CF374C Inna and Dima 题解 - CF219D Choosing Capital for Treeland 题解 + CF374C Inna and Dima 题解
@@ -827,13 +827,13 @@
- +
- CF374C Inna and Dima 题解 + CF346B Lucky Common Subsequence 题解 - CF374C Inna and Dima 题解 + CF346B Lucky Common Subsequence 题解
@@ -857,15 +857,15 @@
-
+
- +
- CF294B Shaass and Bookshelf 题解 + CF219D Choosing Capital for Treeland 题解 - CF294B Shaass and Bookshelf 题解 + CF219D Choosing Capital for Treeland 题解
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/16/index.html" "b/tags/\347\256\227\346\263\225/page/16/index.html" index 336bd3eafa..578599714c 100644 --- "a/tags/\347\256\227\346\263\225/page/16/index.html" +++ "b/tags/\347\256\227\346\263\225/page/16/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -633,15 +633,15 @@
-
+
- +
- 洛谷P2216 [HAOI2007]理想的正方形 题解 + 洛谷P2257 YY的GCD 题解 - 洛谷P2216 [HAOI2007]理想的正方形 题解 + 洛谷P2257 YY的GCD 题解
@@ -697,15 +697,15 @@
-
+
- +
- 洛谷P2257 YY的GCD 题解 + 洛谷P2216 [HAOI2007]理想的正方形 题解 - 洛谷P2257 YY的GCD 题解 + 洛谷P2216 [HAOI2007]理想的正方形 题解
@@ -729,15 +729,15 @@
-
+
- +
- 洛谷P2158 [SDOI2008] 仪仗队 题解 + 洛谷P2170 选学霸 题解 - 洛谷P2158 [SDOI2008] 仪仗队 题解 + 洛谷P2170 选学霸 题解
@@ -761,15 +761,15 @@
-
+
- +
- 洛谷P2170 选学霸 题解 + 洛谷P2158 [SDOI2008] 仪仗队 题解 - 洛谷P2170 选学霸 题解 + 洛谷P2158 [SDOI2008] 仪仗队 题解
@@ -825,15 +825,15 @@
-
+
- +
- 洛谷P1772 [ZJOI2006]物流运输 题解 + 洛谷P1858 多人背包 题解 - 洛谷P1772 [ZJOI2006]物流运输 题解 + 洛谷P1858 多人背包 题解
@@ -857,15 +857,15 @@
-
+
- +
- 洛谷P1858 多人背包 题解 + 洛谷P1772 [ZJOI2006]物流运输 题解 - 洛谷P1858 多人背包 题解 + 洛谷P1772 [ZJOI2006]物流运输 题解
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/17/index.html" "b/tags/\347\256\227\346\263\225/page/17/index.html" index 910ad67cf4..0a64c4415d 100644 --- "a/tags/\347\256\227\346\263\225/page/17/index.html" +++ "b/tags/\347\256\227\346\263\225/page/17/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -635,13 +635,13 @@
- +
- 洛谷P3336 [ZJOI2013]话旧 题解 + 洛谷P3327 [SDOI2015]约数个数和 题解 - 洛谷P3336 [ZJOI2013]话旧 题解 + 洛谷P3327 [SDOI2015]约数个数和 题解
@@ -667,13 +667,13 @@
- +
- 洛谷P3327 [SDOI2015]约数个数和 题解 + 洛谷P3336 [ZJOI2013]话旧 题解 - 洛谷P3327 [SDOI2015]约数个数和 题解 + 洛谷P3336 [ZJOI2013]话旧 题解
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/18/index.html" "b/tags/\347\256\227\346\263\225/page/18/index.html" index be52c2ec76..95354585d1 100644 --- "a/tags/\347\256\227\346\263\225/page/18/index.html" +++ "b/tags/\347\256\227\346\263\225/page/18/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -633,15 +633,15 @@
-
+
- +
- 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 + 洛谷P4395 [BOI2003]Gem 气垫车 题解 - 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 + 洛谷P4395 [BOI2003]Gem 气垫车 题解
@@ -665,15 +665,15 @@
-
+
- +
- 洛谷P4395 [BOI2003]Gem 气垫车 题解 + 洛谷P4390 [BOI2007]Mokia 摩基亚 题解 - 洛谷P4395 [BOI2003]Gem 气垫车 题解 + 洛谷P4390 [BOI2007]Mokia 摩基亚 题解
@@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/19/index.html" "b/tags/\347\256\227\346\263\225/page/19/index.html" index b53a845d3a..1d15087fe2 100644 --- "a/tags/\347\256\227\346\263\225/page/19/index.html" +++ "b/tags/\347\256\227\346\263\225/page/19/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -569,7 +569,7 @@
-
+
@@ -934,7 +934,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/2/index.html" "b/tags/\347\256\227\346\263\225/page/2/index.html" index 9bcfebb9a6..5de0efef14 100644 --- "a/tags/\347\256\227\346\263\225/page/2/index.html" +++ "b/tags/\347\256\227\346\263\225/page/2/index.html" @@ -360,20 +360,20 @@
- + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/20/index.html" "b/tags/\347\256\227\346\263\225/page/20/index.html" index 1e8c16c49b..6c26ffdd73 100644 --- "a/tags/\347\256\227\346\263\225/page/20/index.html" +++ "b/tags/\347\256\227\346\263\225/page/20/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/21/index.html" "b/tags/\347\256\227\346\263\225/page/21/index.html" index d0e455fa49..eb10f6a095 100644 --- "a/tags/\347\256\227\346\263\225/page/21/index.html" +++ "b/tags/\347\256\227\346\263\225/page/21/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/22/index.html" "b/tags/\347\256\227\346\263\225/page/22/index.html" index 56968dd78d..3df6a00a2d 100644 --- "a/tags/\347\256\227\346\263\225/page/22/index.html" +++ "b/tags/\347\256\227\346\263\225/page/22/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/23/index.html" "b/tags/\347\256\227\346\263\225/page/23/index.html" index 43c54b70ae..7d97569aa1 100644 --- "a/tags/\347\256\227\346\263\225/page/23/index.html" +++ "b/tags/\347\256\227\346\263\225/page/23/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -930,7 +930,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/24/index.html" "b/tags/\347\256\227\346\263\225/page/24/index.html" index b89ba025ac..83ba3480b7 100644 --- "a/tags/\347\256\227\346\263\225/page/24/index.html" +++ "b/tags/\347\256\227\346\263\225/page/24/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -710,7 +710,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/3/index.html" "b/tags/\347\256\227\346\263\225/page/3/index.html" index 5acb98e64c..c946b8405b 100644 --- "a/tags/\347\256\227\346\263\225/page/3/index.html" +++ "b/tags/\347\256\227\346\263\225/page/3/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/4/index.html" "b/tags/\347\256\227\346\263\225/page/4/index.html" index bb46380fe4..e1f5f96085 100644 --- "a/tags/\347\256\227\346\263\225/page/4/index.html" +++ "b/tags/\347\256\227\346\263\225/page/4/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/5/index.html" "b/tags/\347\256\227\346\263\225/page/5/index.html" index 24f3784629..116e64cb77 100644 --- "a/tags/\347\256\227\346\263\225/page/5/index.html" +++ "b/tags/\347\256\227\346\263\225/page/5/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/6/index.html" "b/tags/\347\256\227\346\263\225/page/6/index.html" index f77487afe7..23147ba8e4 100644 --- "a/tags/\347\256\227\346\263\225/page/6/index.html" +++ "b/tags/\347\256\227\346\263\225/page/6/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/7/index.html" "b/tags/\347\256\227\346\263\225/page/7/index.html" index 69dc005716..dae9f1cdab 100644 --- "a/tags/\347\256\227\346\263\225/page/7/index.html" +++ "b/tags/\347\256\227\346\263\225/page/7/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/8/index.html" "b/tags/\347\256\227\346\263\225/page/8/index.html" index a7acb38a1f..2b1797a50f 100644 --- "a/tags/\347\256\227\346\263\225/page/8/index.html" +++ "b/tags/\347\256\227\346\263\225/page/8/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -935,7 +935,7 @@
  站点总字数: 320.5k 字 + class="white-color">322.9k 字 diff --git "a/tags/\347\256\227\346\263\225/page/9/index.html" "b/tags/\347\256\227\346\263\225/page/9/index.html" index eac3716013..9057a2ff97 100644 --- "a/tags/\347\256\227\346\263\225/page/9/index.html" +++ "b/tags/\347\256\227\346\263\225/page/9/index.html" @@ -360,20 +360,20 @@ - + DP - 97 + data-tagname="图论" style="background-color: #D5F5E3;">图论 + 69 - + 图论 - 69 + data-tagname="DP" style="background-color: #E8F8F5;">DP + 97 @@ -414,19 +414,19 @@ - + 瞎搞 + data-tagname="01分数规划" style="background-color: #A3E4D7;">01分数规划 3 - + 01分数规划 + data-tagname="瞎搞" style="background-color: #85C1E9;">瞎搞 3 @@ -441,20 +441,20 @@ - + 摆烂 - 2 + data-tagname="计算几何" style="background-color: #F9E79F;">计算几何 + 9 - + 计算几何 - 9 + data-tagname="摆烂" style="background-color: #D7BDE2;">摆烂 + 2 @@ -761,7 +761,7 @@
-
+