省选前学习记录

2021.11.24

CF1612G Max Sum Array

算法 : 构造,贪心,计数

难度 : 2500

code

发现一个贪心结论,从两边向中间放数,每次放的数一定是剩余最多的数。证明:

考虑一对数的贡献,对于外部的数,贡献为 00 , 这是显然的。对于内部的数,设一共有 kk 个, 位置分别为 aia_i,则贡献为 i=1k(rai)+(ail)=i=1krl=k(rl)\sum_{i=1}^{k}(r - a_i) + (a_i - l) = \sum_{i=1}^{k}r-l = k(r-l). kk 越大越优。

然后考虑模拟这个过程。从大到小枚举剩余出现次数。对于出现次数为 ii 的数,把它们各取一对从两端(没有数的区间的两端)放入序列中。设剩余出现次数为 ii 的数有 numnum 个, 空区间大小为 nn 那么放置方案数为 (num!)2(num!)^2 , 放置的贡献为 (i1)((n1)+(n2num+21))num2=(i1)(nnum)num(i - 1)\frac{((n - 1)+(n - 2num + 2 - 1))\cdot num}{2} = (i - 1)(n- num)\cdot num

然后把答案累积起来就行

CF1610D Not Quite Lee

算法 : 数论,计数,结论

难度 : 2000

code

考虑什么样的子序列满足条件。显然当且仅当 gcd(a1,a2...an)(i=1nai2[2ai])\gcd(a_1, a_2...a_n)|(\sum_{i=1}^{n}\frac{a_i}{2}[2|a_i])​ , 显然如果 gcd\gcd​ 是奇数则一定满足条件。那么如果不是奇数,就考虑删去原序列里所有的奇数。那么满足条件当且仅当gcd(a1,a2...an)i=1nai4[4ai]\gcd(a_1, a_2...a_n)|\sum_{i=1}^{n}\frac{a_i}{4}[4|a_i]​ , 即 gcd\gcd​ 是 22​ 的倍数却不是 44​ 的倍数。这样按照 2,4,8,16,32...2, 4, 8, 16, 32 ...​ 不断重复下去。可以在 O(nlog109)O(nlog10^9)​​ 内求出答案


2021.11.25

[SDOI2011]拦截导弹

算法 : cdq + lis + dp

难度 : 紫

code

模板题,把二维的 lislis 通过 cdqcdq 分治转化为三维的 lislis

[TJOI2010]分金币

算法 : 模拟退火

难度 : 紫

code

模拟退火模板,模拟退火太好用啦,可惜 noipnoip​​ 没有写


2021.11.26

CF1610E AmShZ and G.O.A.T.

算法 : 贪心 + 二分

难度 : 2300

code

考虑 bad 序列的性质, 一个序列是 bad 序列,当且仅当存在两个相邻数 axa_x , ax+1a_{x+1} 。满足 ax+1ax<axa1a_{x + 1} - a_x < a_x - a_1 。证明挺显然的,但是不知道怎么描述。

然后考虑如何删数,先枚举 a1a_1 , 然后对于第 ii 个数。 二分得到右侧第一个满足 ax+1axaxa1a_{x + 1} - a_x \geq a_x - a_1 的数 aja_j , 而 aia_iaja_j 之间的数和 aia_i 是不能共存的, 可以简单的证明最优策略一定是删去 aia_iaja_j 之间的数。不知道怎么解释证明。感性的想,每次删除较大数显然比删除较小数更优,把这些操作累积起来,就是删除 aia_iaja_j 之间的数。

然后操作流程为,枚举 a1a_1 , 对于第 ii 个数, 二分得到右侧第一个满足 ax+1axaxa1a_{x + 1} - a_x \geq a_x - a_1 的数 aja_j 。 另 i=ji = j, 不断重复操作,复杂度 O(nlog109logn)O(nlog10^9logn) 关于复杂度的证明如下 :

考虑二分的到 jj 因为满足 ajaiaia1a_{j} - a_i \geq a_i - a_1 , 所以 aja12(aia1)a_j - a_1 \geq 2(a_i - a_1) 每次二分之后 aja1a_j - a_1 会变为原来的 2 倍,所以 log109log10^9 次以内可以二分出答案

CF1614D1 Divan and Kostomuksha (easy version)

算法 : 数论 + dp

难度 : 2100

code

fif_i​ 表示最后一段的前缀 gcdgcd 都为 ii 时, 前缀 gcdgcd 的前缀和。考虑如何进行 dpdp

当前段的前缀 gcdgcd 都为 ii 那么上一段的前缀 gcdgcd 一定是 ii 的倍数。所以 fif_i 可以有 fj[ij]f_j[i|j] 转移过来。方程为 fi=maxijfj+(cnticntj)if_i = \max_{i|j} f_j +(cnt_i - cnt_j)i , cnticnt_i 表示 aa 序列中有多少个数是 ii 的倍数。那么 cnticntjcnt_i - cnt_j 表示的就是是 ii 的倍数却不是 jj 的倍数的数。

考虑为什么这样 dpdp 是对的。首先 ii 的倍数一定不会在前缀 gcdgcdii 的段之后出现,因为如果在之前出现,贡献至少为 ii , 但是如果在之后出现,贡献一定小于 ii 。同理 jj 的倍数会出现在前缀 gcdgcdjj 的倍数的段。即不会出现在前缀 gcdgcdii 的段。所以出现在前缀 gcdgcdii 的段的数为是 ii 的倍数却不是 jj​ 的倍数的数。

所以只需把 cntcnt​​ 求出来,再 dpdp​​ 一遍就行, 复杂度为 O(na+aloga)O(n\sqrt{a}+a\log a)


2021.11.27

CF1614D2 Divan and Kostomuksha (hard version)

算法 : 数论 + dp

难度 : 2300

code

考虑优化之前的做法,瓶颈在于 dpdp​​ 的转移, 发现每次转移其实并不用枚举所有的倍数,只需要从相邻的倍数(即这个数乘上一个质数)转移过来就行了。复杂度就可以变为 O(alogloga)O(a\log \log a)