Codeforces 刷题笔记(2024.4)

记录近期训练遇到的 10 道值得记录的 Codeforces 题,平均难度约 2000+。


CF1870E

题意

对长度为 n ( n ≤ 5000 ) n(n\le 5000) n(n5000) 的数组 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(n106) 的数组 a ( 1 ≤ a i ≤ n ) a(1\le a_i\le n) a(1ain),求不存在 x ∈ a x\in a xa 使得 x ∣ a i x|a_i xai x ∣ a j x|a_j xaj 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(n4000) 且仅包含 [ 1 , k ] ( k ≤ 4000 ) [1,k](k\le 4000) [1,k](k4000) 的序列,求它们 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] [ik+1,i1] 结尾的子段都没有被计入答案
  • 定义 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=kikk!j=ik+1i1dpj(ij)!,即所有 i 位置为 k 排列的序列个数减去以 [ i − k + 1 , i − 1 ] [i-k+1,i-1] [ik+1,i1] 结尾的子段被计入答案的序列个数
  • 答案为 ∑ i = k n d p i ⋅ k n − i \sum_{i=k}^{n} dp_i\cdot k^{n-i} i=kndpikni

CF895C

题意

给定长度为 n ( n ≤ 1 0 5 ) n(n\le 10^5) n(n105) 的序列 a ( 1 ≤ a i ≤ 70 ) a(1\le a_i\le 70) a(1ai70),求序列有多少个非空子集满足所有元素的积是一个完全平方数。

做法
  • 乘积为完全平方数等价于所有质因子出现的次数都为偶数,而 70 70 70 以内的质数非常少,可以进行状态压缩,转换为线性基经典问题,即求一组向量和为 0 的线性组合个数
  • n n n 个向量的集合 s s s 中和为 0 的线性组合个数为 2 n − r a n k ( s ) 2^{n-rank(s)} 2nrank(s),其中包含一个空集,需要去掉

CF1896D

题意

给定一个长度为 n ( n ≤ 1 0 5 ) n(n\le 10^5) n(n105) 只包含 1 1 1 2 2 2 的数组以及 q ( q ≤ 1 0 5 ) q(q\le 10^5) q(q105) 个询问,每个询问要么对数组单点修改,要么求是否存在和为 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,... sum2,sum4,... 的子数组就一定也存在
  • 只需要找到与询问的 s u m sum sum 奇偶性相同且和最大的子数组即可判断。这可以通过维护所有为 1 1 1 的位置来完成

CF1913D

题意

给定所有元素互不相同的、长度为 n ( n ≤ 3 ⋅ 1 0 5 ) n(n\le 3\cdot 10^5) n(n3105) 的序列 p p p 和一种操作:

  • 选择 p p p 中一个子段,删除除了最小元素以外的所有元素

求进行任意次操作可以得到的所有结果序列的种类数。

做法
  • 等价于求合法子序列个数,因此考虑动态规划:用 d p i dp_i dpi 表示对前缀 i i i 进行操作能得到且不删除第 i i i 个元素的子序列个数
  • 对于 d p i dp_i dpi 考虑从左边哪些位置转移合法:
  1. p i p_i pi 左边没有更小的数,则 [ 0 , i ) [0,i) [0,i) 都合法
  2. 否则,记 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))),...
  3. 需要注意:情况 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(n3105) 个怪物,每个怪物的伤害和一次性最多能接受的伤害分别为 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(n103) 格的条带,可以任选格子涂上颜色。对于 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(k5×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} dp1,dp0,...,dpi2 处转移
  • 按照上述想法如果直接暴力转移复杂度高达 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(t105) 个询问,每次询问给出 n ( n ≤ 1 0 6 ) n(n\le 10^6) n(n106),求 ∑ 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=1nj=1i((ij)!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 (ij)!ji! mod j=ji(i1)(i2)(ij+1) mod j=(j1)!ji mod j
  • 除了质数和 4 4 4 以外,其他数 j j j 都可以整除 ( j − 1 ) ! (j-1)! (j1)!,即满足 ( j − 1 ) ! ⋅ ⌊ i j ⌋  mod  j = 0 (j-1)!\cdot\lfloor\frac{i}{j}\rfloor\text{ mod }j=0 (j1)!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 (j1)!ji mod j=(j1)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((ij)!ji! mod j),枚举质数 j j j 对数组的差分进行贡献,最终答案为数组的前缀和

威尔逊定理:对于质数 p p p,有 ( p − 1 ) ! + 1 ≡ 0  mod  p (p-1)!+1\equiv 0\text{ mod }p (p1)!+10 mod p.


CF1954E

题意

已知序列 a ( a i ≤ 1 0 5 ) a(a_i\le10^5) a(ai105) 代表 n ( n ≤ 1 0 5 ) n(n\le 10^5) n(n105) 个怪物的生命值。每次链式攻击可以选择一个存活的怪物造成 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,kaikai1⌉)
  • 考虑数论分块, ⌈ 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 kai1 的取值一共也只有 O ( log ⁡ max ⁡ ( a i − 1 , a i ) ) O(\log{\max(a_{i-1},a_i)}) O(logmax(ai1,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(ai1,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+i1=ix1+1 的性质转换为 x − 1 x-1 x1 的向下取整分块
  • 13
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值