记录近期训练遇到的 10 道值得记录的 Codeforces 题,平均难度约 2000+。
CF1870E
题意
对长度为 n ( n ≤ 5000 ) n(n\le 5000) n(n≤5000) 的数组 a ( a i ∈ [ 0 , n ] ) a(a_i\in [0,n]) a(ai∈[0,n]),选择若干不重合的子段分别计算 MEX 再异或起来,求该异或值的最大值。
做法
- 很容易想到朴素的 dp 做法:令 d p ( i , j ) dp(i,j) dp(i,j) 表示考虑前 i i i 个元素能否得到异或值 j j j,对于每个状态从前缀所有元素转移,时间复杂度 O ( n 3 ) O(n^3) O(n3)
- 考虑到 MEX 相同的段中只有极小子段用于转移有意义,可以发现极小子段只有 O ( n ) O(n) O(n) 个,于是只要预处理出有意义的极小子段再 O ( n 2 ) O(n^2) O(n2) 转移即可
CF1884D
题意
给定长度为 n ( n ≤ 1 0 6 ) n(n\le 10^6) n(n≤106) 的数组 a ( 1 ≤ a i ≤ n ) a(1\le a_i\le n) a(1≤ai≤n),求不存在 x ∈ a x\in a x∈a 使得 x ∣ a i x|a_i x∣ai 且 x ∣ a j x|a_j x∣aj 且 i < j i<j i<j 的 ( i , j ) (i,j) (i,j) 对数。
做法
- 计算 g c d ( a i , a j ) = x gcd(a_i,a_j)=x gcd(ai,aj)=x 的 ( i , j ) (i,j) (i,j) 对数 d p ( x ) dp(x) dp(x)
- 对于所有没有因子在 a a a 中的数 u,计算 ∑ u d p ( u ) \sum_u dp(u) ∑udp(u),即为答案
CF1861E
题意
定义序列 a a a 最多包含 c o s t a cost_a costa 个不重叠的子段,满足每个子段都是 [ 1 , k ] [1,k] [1,k] 的排列。对于所有长度为 n ( n ≤ 4000 ) n(n\le 4000) n(n≤4000) 且仅包含 [ 1 , k ] ( k ≤ 4000 ) [1,k](k\le 4000) [1,k](k≤4000) 的序列,求它们 c o s t cost cost 的和。
做法
- 计算 cost 的策略是从左到右扫描长度为 k k k 的窗口,将每次最先出现的 k 排列计入答案
- 考虑统计所有序列中每个位置上被计入答案的 k 排列个数。以 i i i 位置结尾的 k 排列被计入答案的前提是以 [ i − k + 1 , i − 1 ] [i-k+1,i-1] [i−k+1,i−1] 结尾的子段都没有被计入答案
- 定义 d p i dp_i dpi 表示在 i 位置被计入答案的 k 排列个数,则按照上述策略, d p i = k i − k ⋅ k ! − ∑ j = i − k + 1 i − 1 d p j ⋅ ( i − j ) ! dp_i=k^{i-k}\cdot k!-\sum_{j=i-k+1}^{i-1} dp_j\cdot (i-j)! dpi=ki−k⋅k!−∑j=i−k+1i−1dpj⋅(i−j)!,即所有 i 位置为 k 排列的序列个数减去以 [ i − k + 1 , i − 1 ] [i-k+1,i-1] [i−k+1,i−1] 结尾的子段被计入答案的序列个数
- 答案为 ∑ i = k n d p i ⋅ k n − i \sum_{i=k}^{n} dp_i\cdot k^{n-i} ∑i=kndpi⋅kn−i
CF895C
题意
给定长度为 n ( n ≤ 1 0 5 ) n(n\le 10^5) n(n≤105) 的序列 a ( 1 ≤ a i ≤ 70 ) a(1\le a_i\le 70) a(1≤ai≤70),求序列有多少个非空子集满足所有元素的积是一个完全平方数。
做法
- 乘积为完全平方数等价于所有质因子出现的次数都为偶数,而 70 70 70 以内的质数非常少,可以进行状态压缩,转换为线性基经典问题,即求一组向量和为 0 的线性组合个数
- n n n 个向量的集合 s s s 中和为 0 的线性组合个数为 2 n − r a n k ( s ) 2^{n-rank(s)} 2n−rank(s),其中包含一个空集,需要去掉
CF1896D
题意
给定一个长度为 n ( n ≤ 1 0 5 ) n(n\le 10^5) n(n≤105) 只包含 1 1 1 和 2 2 2 的数组以及 q ( q ≤ 1 0 5 ) q(q\le 10^5) q(q≤105) 个询问,每个询问要么对数组单点修改,要么求是否存在和为 s s s 的子数组。
做法
- 观察到:对于一个子数组,若其左边或右边为 2 2 2 则可以删去使和减少 2 2 2,否则可以同时去掉两边的 1 1 1 同样使和减少 2 2 2。因此只要和为 s u m sum sum 的子数组存在,和为 s u m − 2 , s u m − 4 , . . . sum-2,sum-4,... sum−2,sum−4,... 的子数组就一定也存在
- 只需要找到与询问的 s u m sum sum 奇偶性相同且和最大的子数组即可判断。这可以通过维护所有为 1 1 1 的位置来完成
CF1913D
题意
给定所有元素互不相同的、长度为 n ( n ≤ 3 ⋅ 1 0 5 ) n(n\le 3\cdot 10^5) n(n≤3⋅105) 的序列 p p p 和一种操作:
- 选择 p p p 中一个子段,删除除了最小元素以外的所有元素
求进行任意次操作可以得到的所有结果序列的种类数。
做法
- 等价于求合法子序列个数,因此考虑动态规划:用 d p i dp_i dpi 表示对前缀 i i i 进行操作能得到且不删除第 i i i 个元素的子序列个数
- 对于 d p i dp_i dpi 考虑从左边哪些位置转移合法:
- 若 p i p_i pi 左边没有更小的数,则 [ 0 , i ) [0,i) [0,i) 都合法
- 否则,记 l ( i ) l(i) l(i) 为 p i p_i pi 左边第一个比 p i p_i pi 小的元素的位置,合法的转移位置有 [ l ( i ) , i ) [l(i),i) [l(i),i) 以及 l ( l ( i ) ) , l ( l ( l ( i ) ) ) , . . . l(l(i)),l(l(l(i))),... l(l(i)),l(l(l(i))),...
- 需要注意:情况 1 中位置 0 0 0 合法,情况 2 中位置 0 0 0 不合法
- 在从左到右扫描的过程中,记录 d p i dp_i dpi 的前缀和以及通过 l l l 关系形成的链上的 dp 值的前缀和,就可以做到 O ( 1 ) O(1) O(1) 转移
- 最终找到所有满足“比自己右边所有元素都大”的 p i p_i pi 的位置 i i i,对这些位置的 dp 值求和即为答案
CF1922D
题意
给定一排 n ( n ≤ 3 ⋅ 1 0 5 ) n(n\le 3\cdot 10^5) n(n≤3⋅105) 个怪物,每个怪物的伤害和一次性最多能接受的伤害分别为 a i a_i ai 和 d i d_i di。每个回合,所有怪物会先对左边和右边第一个存活的怪物造成伤害,然后受到他们的伤害。问前 n n n 个回合中分别有多少个怪物死亡。
做法
- 在某一回合中,若某怪物自己没有死亡,且相邻的两个怪物也没有死亡,则在下一回合中不需要考虑它。换句话说,每个回合只需要检查上个回合死亡的怪物的相邻怪物即可
- 整个过程中最多有 n n n 个怪物死亡,每个怪物死亡时,下回合需要考虑其相邻的两个怪物;同时初始时需要考虑所有 n n n 个怪物。因此总共需要考虑的次数为 O ( 3 n ) O(3n) O(3n)
- 用 set 维护当前活着的怪物可以方便地找到每个怪物的相邻怪物
CF1942D
题意
有一个包含 n ( n ≤ 1 0 3 ) n(n\le 10^3) n(n≤103) 格的条带,可以任选格子涂上颜色。对于 2 n 2^n 2n 种选择中的一种,其对应的得分是 a x 1 , x 2 + a x 3 , x 4 + . . . a_{x_1,x_2}+a_{x_3,x_4}+... ax1,x2+ax3,x4+...,其中 [ x 1 , x 2 ] , [ x 3 , x 4 ] , . . . [x_1,x_2],[x_3,x_4],... [x1,x2],[x3,x4],... 是最大连续涂色段。求得分最大的 k ( k ≤ 5 × 1 0 3 ) k(k\le 5\times 10^3) k(k≤5×103) 种选择的得分。
做法
- 考虑动态规划, d p i dp_i dpi 表示使用了前 i i i 个格子的前 k k k 大列表,则 d p i dp_i dpi 可以从 d p − 1 , d p 0 , . . . , d p i − 2 dp_{-1},dp_0,...,dp_{i-2} dp−1,dp0,...,dpi−2 处转移
- 按照上述想法如果直接暴力转移复杂度高达 O ( n 2 k ) O(n^2k) O(n2k),需要优化。如果每个 d p dp dp 列表都是降序的,新的 d p i dp_i dpi 一定会从所有 d p j dp_j dpj 的前缀转移来,且所有前缀的总长只有 O ( k ) O(k) O(k) 级别。换句话说就是对这些列表进行归并,且只归并前 k k k 个即可。这一过程可以借助优先队列实现,队列中的每个结点记录 ( i d x , p o s ) (idx,pos) (idx,pos) 信息,代表从 d p i d x dp_{idx} dpidx 的第 p o s pos pos 大数进行转移
CF1957E
题意
回答 t ( t ≤ 1 0 5 ) t(t\le 10^5) t(t≤105) 个询问,每次询问给出 n ( n ≤ 1 0 6 ) n(n\le 10^6) n(n≤106),求 ∑ i = 1 n ∑ j = 1 i ( i ! ( i − j ) ! ⋅ j mod j ) \sum_{i=1}^{n}\sum_{j=1}^{i}\left(\frac{i!}{(i-j)!\cdot j} \text{ mod } j\right) ∑i=1n∑j=1i((i−j)!⋅ji! mod j)
做法
- 观察式子得 i ! ( i − j ) ! ⋅ j mod j = i ⋅ ( i − 1 ) ⋅ ( i − 2 ) ⋅ … ⋅ ( i − j + 1 ) j mod j = ( j − 1 ) ! ⋅ ⌊ i j ⌋ mod j \frac{i!}{(i-j)!\cdot j} \text{ mod } j=\frac{i\cdot (i-1)\cdot(i-2)\cdot\ldots\cdot(i-j+1)}{j}\text{ mod } j=(j-1)!\cdot\lfloor\frac{i}{j}\rfloor\text{ mod }j (i−j)!⋅ji! mod j=ji⋅(i−1)⋅(i−2)⋅…⋅(i−j+1) mod j=(j−1)!⋅⌊ji⌋ mod j
- 除了质数和 4 4 4 以外,其他数 j j j 都可以整除 ( j − 1 ) ! (j-1)! (j−1)!,即满足 ( j − 1 ) ! ⋅ ⌊ i j ⌋ mod j = 0 (j-1)!\cdot\lfloor\frac{i}{j}\rfloor\text{ mod }j=0 (j−1)!⋅⌊ji⌋ mod j=0
- 对于质数,由威尔逊定理, ( j − 1 ) ! ⋅ ⌊ i j ⌋ mod j = ( j − 1 ) ⋅ ⌊ i j ⌋ mod j (j-1)!\cdot\lfloor\frac{i}{j}\rfloor\text{ mod }j=(j-1)\cdot\lfloor\frac{i}{j}\rfloor\text{ mod }j (j−1)!⋅⌊ji⌋ mod j=(j−1)⋅⌊ji⌋ mod j
- 用一个数组记录 ∑ j = 1 i ( i ! ( i − j ) ! ⋅ j mod j ) \sum_{j=1}^{i}\left(\frac{i!}{(i-j)!\cdot j} \text{ mod } j\right) ∑j=1i((i−j)!⋅ji! mod j),枚举质数 j j j 对数组的差分进行贡献,最终答案为数组的前缀和
威尔逊定理:对于质数 p p p,有 ( p − 1 ) ! + 1 ≡ 0 mod p (p-1)!+1\equiv 0\text{ mod }p (p−1)!+1≡0 mod p.
CF1954E
题意
已知序列 a ( a i ≤ 1 0 5 ) a(a_i\le10^5) a(ai≤105) 代表 n ( n ≤ 1 0 5 ) n(n\le 10^5) n(n≤105) 个怪物的生命值。每次链式攻击可以选择一个存活的怪物造成 k k k 点伤害并传递:当一个怪物受到伤害时,相邻的存活的怪物也会受到同样的伤害。求对于所有 k ∈ [ 1 , max ( a ) ] k\in[1,\max(a)] k∈[1,max(a)],最少要进行多少次链式攻击才能消灭所有怪物。
做法
- 对于固定的 k k k,显然答案即 ∑ i max ( 0 , ⌈ a i k ⌉ − ⌈ a i − 1 k ⌉ ) \sum_i \max(0,\lceil \frac{a_i}{k}\rceil-\lceil \frac{a_{i-1}}{k}\rceil) ∑imax(0,⌈kai⌉−⌈kai−1⌉)
- 考虑数论分块, ⌈ a i k ⌉ \lceil \frac{a_i}{k}\rceil ⌈kai⌉ 的取值实际上只有 O ( log a i ) O(\log a_i) O(logai) 种。可以枚举 a a a 的相邻元素计算对答案数组区间的贡献,因为 ⌈ a i k ⌉ \lceil \frac{a_i}{k}\rceil ⌈kai⌉ 和 ⌈ a i − 1 k ⌉ \lceil \frac{a_{i-1}}{k}\rceil ⌈kai−1⌉ 的取值一共也只有 O ( log max ( a i − 1 , a i ) ) O(\log{\max(a_{i-1},a_i)}) O(logmax(ai−1,ai)) 种。即将 k k k 的分布区间 [ 1 , max ( a ) ] [1,\max(a)] [1,max(a)] 划分为 O ( log max ( a i − 1 , a i ) ) O(\log{\max(a_{i-1},a_i)}) O(logmax(ai−1,ai)) 段分别计算贡献
- 数论分块通常是向下取整的,即根据 ⌊ x i ⌋ \lfloor\frac{x}{i}\rfloor ⌊ix⌋ 的结果分块,但此处需要向上取整的分块,可以利用 ⌈ x i ⌉ = ⌊ x + i − 1 i ⌋ = ⌊ x − 1 i ⌋ + 1 \lceil \frac{x}{i}\rceil=\lfloor\frac{x+i-1}{i}\rfloor=\lfloor\frac{x-1}{i}\rfloor+1 ⌈ix⌉=⌊ix+i−1⌋=⌊ix−1⌋+1 的性质转换为 x − 1 x-1 x−1 的向下取整分块