Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine))
D - Up the Strip
题意
给出一个数字 n 表示初始的数字,你可以对当前的数字(比如说是 x)做若干次变化,变化包含下列两种:
-
选择一个数字 y∈[1,x−1],将现在的数字 x 变为 y−x。
-
选择一个数字 z∈[2,x],将现在的数字 x 变为 ⌊zx⌋。
求将初始数字 n 变为 1 的不同的操作有多少种?
答案对 m 取模,每次会给出 m。
数据范围:2⩽n⩽4×106,108<m<109。
思路
求不同操作的个数,可以用dp完成,令 f(n) 为将数字 n 转变为 1 的不同操作个数,则转移为:
f(n)=i=2∑nf(⌊in⌋)+i=1∑n−1f(i)
右侧第一个式子可以使用数论分块在 O(n) 内完成,然后从1到n计算对应的 f(x),右侧用前缀和即可 O(1) 求出。
总复杂度:O(NN),于是 Up the Strip (simplified version) 就可以通过了。
点击显/隐代码
如果要通过 n=4×106 的数据,时间复杂度不能超过 O(NlogN),空间复杂度不能超过 O(N)(128Mb)。
考虑 f 中相邻两项之间的关系,下面列式观察:
f(n)f(n−1)=i=2∑nf(⌊in⌋)+i=1∑n−1f(i)=i=2∑n−1f(⌊in⌋)+i=1∑n−2f(i)+f(1)+f(n−1)=i=2∑n−1f(⌊in−1⌋)+i=1∑n−2f(i)
不难发现 f(n) 和 f(n−1) 中第一项只有分子发生了变化,从 n−1 变为 n。
那么只需要考虑,分母在何时会发生变化即可。
当 i=3 时,那么只有当 n=3,6,9,⋯,3k 时,⌊3n⌋ 和 ⌊3n−1⌋ 才会发生变化
而且只是从 3n−1=3n−1 变为 3n。
所以 f(n) 从 f(n−1) 的转移可以写成:
f(n)=2f(n−1)+f(1)−k∣n,k⩾2∑f(kn−1)+k∣n,k⩾2∑f(kn)
由于 2n+3n+4n+⋯+nn=O(nlogn),所以如果我们直接预处理能整除 n 的 k 时,在空间上会爆掉的。
所以正确的做法有如下两种:
他们时空间复杂度都一样为:时间复杂度 O(nlogn),空间复杂度 O(n)。
法一:
对每个 n 求它所有的因数(需要先用筛法求出素数表,然后求 n 对应的素因数,最后暴力枚举每个素数的个数)。
点击显/隐代码
法二(更优美的做法):
就是考虑倒序求解,从 1∼n 求解,考虑每个数 f(k) 对 2k,3k,4k,… 的贡献。
点击显/隐代码
E - Bottom-Tier Reversals
题意
给出 1 到 n 的排列, a=[a1,a2,…,an],其中 n 为奇数。
你可以进行一次操作,每次操作可以选择当前序列的前 k 项,k必须为任意的奇数,然后反转他们,也就是将 ai 和 ak−i+1 交换位置。
如果你可以在不超过 25n 次操作后完成当前序列的排序,先输出反转的次数,再输出每次反转的长度。
如果不能则输出 −1。
数据范围:3⩽n⩽2021。
思路
性质:如果每次反转的区间长度为奇数,那么反转元素的下标的奇偶性不发生变化,如 [1,4,3,2,5]→[5,2,3,4,1],奇偶性不变。
那么由于每次反转只能反转奇数长度,所以一定有 ai 与 i 的奇偶性相同,也就是 ai≡i(mod2)。
下面用具体操作证明这是一个充要条件。
由于又限制了每次反转只能在开头,开头位置无法改变,但末尾位置如果已经对应好了,则可以改变,所以考虑当前的末尾元素是否满足条件:
-
若 an=n,an−1=n−1,则不需要交换,继续考虑 n−2。
-
若 an=n 或 an−1=n−1,则考虑下面5次交换,使得 an=n,an−1=n−1:
-
设 ak=n(注:k 为奇数),反转 [1,…,k]。
-
设 at=n−1(注:t 为偶数),反转 [1,…,t−1]。
-
设 at=n−1,反转 [1,…,t+1]。
-
反转 [1,2,3]。
-
反转 [1,…,n]。
上面步骤可以在草稿纸上模拟完成,便于理解。
于是总操作步骤一定不会超过 25(n−1) 步。
只需模拟上述操作即可,时间复杂度 O(n2)。
点击显/隐代码