Codeforces Round 894 (Div. 3)
A. Gift Carpet
动态规划 贪心 模拟 字符串
A. 题意
给定一个 n × m n \times m n×m 由小写字母组成的网格
判断是否可以从不同列中各选一个字母,有序地组成 vika
A. 题解
模拟:按列扫描网格。指针一开始指向字母 v
。如果该列中含有指针所指向的字母,则移动指针,开始新一列的扫描。扫描完成后,判断指针是否移动到字符串的末尾。
贪心:选择字母 v
在哪一列时,越靠左越好。因为此时选择字母 i
时可选择的范围最大。其他字母类似。
B. Sequence Game
构造
B. 题意
正整数序列 A = a 1 , a 2 … a n A = a_1,a_2 \dots a_n A=a1,a2…an
序列 B B B 由序列 A A A 产生,规则如下:
- a 1 ∈ B a_1 \in B a1∈B
- 如果 a i ≥ a i − 1 a_i \ge a_{i - 1} ai≥ai−1 则 a i ∈ B a_i \in B ai∈B
现给出序列 B B B ,求一可能的序列 A A A ,要求 ∣ A ∣ ≤ 2 ∣ B ∣ |A| \le 2|B| ∣A∣≤2∣B∣
B. 题解
构造:考虑 b i b_i bi 和 b i + 1 b_{i + 1} bi+1 之间是否要插入新的元素
- 如果 b i + 1 ≥ b i b_{i + 1} \ge b_{i} bi+1≥bi 则 a i = b i , a i + 1 = b i + 1 a_i = b_i, a_{i + 1} = b_{i + 1} ai=bi,ai+1=bi+1 是一个可行的方案,不用往这两个元素之间插入新的元素
- 如果 b i + 1 < b i b_{i + 1} < b_{i} bi+1<bi 则说明其之间存在一个元素,使得 x < b i , b i + 1 > = x x < b_i, b_{i + 1} >= x x<bi,bi+1>=x ,只需令 x = 1 x = 1 x=1 即可
C. Flower City Fence
二分查找 数据结构 模拟 排序
C. 题意
给定一不增序列 A = a 1 , a 2 … a n A = a_1,a_2 \dots a_n A=a1,a2…an
a i a_i ai 表示一个宽为 1 1 1 ,长为 a i a_i ai 的矩形
将其有序排在一起,判断其所构成的图形是否为以对角线为轴的轴对称图形
例子:
A = { 5 , 4 , 3 , 2 , 1 } A = \{5,4,3,2,1\} A={5,4,3,2,1}
C. 题解
如果其为轴对称图形,则下图中红线和黄线部分长度相等
从右往左遍历,依次统计黄线的长度,存入 B B B 中
判断 a i a_i ai 是否等于 b i b_i bi ,如果全部都相等,则为轴对称图形
D. Ice Cream Balls
二分查找 组合 构造 数学
D. 题意
给定一个正整数 n ( 1 ≤ n ≤ 1 0 18 ) n (1 \le n \le 10^{18}) n(1≤n≤1018)
求最少需要多少个数,使得由其组合而来的无序数对的个数恰好等于 n n n
例如: 1 , 1 , 2 {1, 1, 2} 1,1,2 可以组成的无序数对有: ( 1 , 1 ) , ( 1 , 2 ) (1, 1), (1, 2) (1,1),(1,2)
D. 题解
- 如果选择了 n n n 个不同的数,那么无序数对的个数为 n ( n − 1 ) 2 \frac{n(n - 1)}{2} 2n(n−1)
- 如果此时选了一个已有的数,那么无序数对的个数多加 1 1 1 个
- 如果此时再选一个已有的数,那么无序数对的个数不再增加
综上,我们可以先选择 n n n 个不同的数,再从中选择若干个相同的数,使得无序数对的个数等于 n n n
考虑第一步的实现方法:二分。 1 , 3 , 6 , 10 … x ( x − 1 ) 2 1, 3, 6, 10 \dots \frac{x(x-1)}{2} 1,3,6,10…2x(x−1) 上二分第一个小于等于 n n n 的数 x x x
答案为: x + ( n − x ( x − 1 ) 2 ) x + (n - \frac{x(x-1)}{2}) x+(n−2x(x−1))
E. Kolya and Movie Theatre
构造 数据结构 贪心
E. 题意
给定一长度为 n n n 数列 A = a 1 , a 2 … a n A = a_1, a_2 \dots a_n A=a1,a2…an
给定正整数 m , d m, d m,d
你需要从序列 A A A 中至多选择 m m m 个元素,使得下列式子值最大:
∑ a i − ( p i − p i − 1 ) ⋅ d \sum a_i - (p_i - p_{i - 1}) \cdot d ∑ai−(pi−pi−1)⋅d
其中 p i p_i pi 表示你所选择的第 i i i 个元素的下标
E. 题解
注意到,我们可以将式子分成两个部分考虑:
∑ a i \sum a_i ∑ai 和 ∑ p i − p i − 1 ⋅ d \sum p_i - p_{i - 1} \cdot d ∑pi−pi−1⋅d
考虑第二个式子,我们可以将其展开:
∑ p i − p i − 1 ⋅ d = d ( p 1 − 0 + p 2 − p 1 … p n − p n − 1 ) = d ⋅ p n \sum p_i - p_{i - 1} \cdot d = d (p_1 - 0 + p_2 - p1 \dots p_n - p_{n - 1}) = d \cdot p_n ∑pi−pi−1⋅d=d(p1−0+p2−p1…pn−pn−1)=d⋅pn
发现,第二个式子的值只与所选择的最后一个元素的位置有关
于是,我们用小根堆维护第一个式子的最大值,当堆的个数小于 m m m 时,直接插入 a i a_i ai ;当堆顶元素小于 a i a_i ai 时,用 a i a_i ai 替换堆顶元素
每次操作后,用 ∑ − d ⋅ p \sum - d \cdot p ∑−d⋅p 更新答案, ∑ \sum ∑ 表示第一个和式的值, d ⋅ p d \cdot p d⋅p 表示第二个式子的值
F. Magic Will Save the World
二分查找 位运算 暴力 动态规划
F. 题意
有 n n n 个怪兽,血量为 s 1 , s 2 ⋯ s n s_1,s_2 \cdots s_n s1,s2⋯sn
每秒你可以获得 w w w 点水系魔法点和 f f f 点火系魔法点
同时,你可以消耗 x x x 点魔法点释放伤害为 x x x 的法术
当你对某个怪兽造成的伤害大于等于其血量时,其将会被消灭
注意,同一只怪兽只会受到同一种属性的伤害
求消灭所有怪兽的最短时间
F. 题解
发现:我们可以先积攒魔法点,再一次性消灭所有的怪兽,而不是考虑如何一边积攒一边消灭怪兽
设 S = ∑ s i S = \sum s_i S=∑si , x x x 为积攒的水系魔法点, y y y 为积攒的火系魔法点
当 x + y ≥ S x + y \ge S x+y≥S 的时候,我们便可以一次性消灭所有的怪兽
此时所用的时间为 max ( ⌈ x w ⌉ , ⌈ y f ⌉ ) \max(\left\lceil\frac{x}{w}\right\rceil, \left\lceil\frac{y}{f}\right\rceil) max(⌈wx⌉,⌈fy⌉)
首先我们计算 S = ∑ s i S = \sum s_i S=∑si 然后枚举所有合法的 x x x 的值,用 S − x S - x S−x 求出相应的 y y y 的值后不断更新答案
考虑如何求出所有可能的 x x x 的值:
假设怪兽的血量为: 2 , 6 , 7 2, 6, 7 2,6,7
版本1:
0 0 0 ,第一只怪兽的血量为 2 2 2 ,更新: 0 , 0 + 2 = 2 0, 0 + 2 = 2 0,0+2=2
0 , 2 0, 2 0,2 ,第二只怪兽的血量为 6 6 6 ,更新: 0 , 2 , 0 + 6 , 2 + 6 0, 2, 0 + 6, 2 + 6 0,2,0+6,2+6 ,得到 0 , 2 , 6 , 8 0, 2, 6, 8 0,2,6,8
同理,最后我们可以得到所有可能的 x x x 的值为: 0 , 2 , 7 , 9 , 13 , 15 0, 2, 7, 9, 13, 15 0,2,7,9,13,15
考虑这种方法的复杂度,其复杂度为: O ( n S ) O(nS) O(nS) ,估算一下为: 1 0 2 × 1 0 2 × 1 0 4 = 1 0 8 10^2 \times 10^2 \times 10^4 = 10^8 102×102×104=108 可能过不了
版本2:
上述过程,我们可以用 bitset
来优化加速
想象一个 1 0 6 10^6 106 位的二进制数字, 1 1 1 表示是一个合法的 x x x , 0 0 0 表示不是一个合法的 x x x
每次用 s i s_i si 更新时,相当于进行一次左移运算再加上一次或运算
0000 0000 0000 0001 0000~0000~0000~0001 0000 0000 0000 0001 表示 0 0 0 是一个合法的 x x x
用 2 2 2 更新,相当于 0000 0000 0000 0100 0000~0000~0000~0100 0000 0000 0000 0100 (进行一次左移运算)与 0000 0000 0000 0001 0000~0000~0000~0001 0000 0000 0000 0001 (原来)进行一次或运算,其结果为 0000 0000 0000 0101 0000~0000~0000~0101 0000 0000 0000 0101 表示 0 , 2 0,2 0,2 均是一个合法的 x x x
同理我们可以用其他的 s i s_i si 更新,最后遍历,每个二进制位,如果该位为 1 1 1 就更新答案
G. The Great Equalizer
二分查找 数据结构 数学 排序
G. 题意
给定一个长度为 n n n 的序列 A = a 1 , a 2 ⋯ a n A = a_1,a_2 \cdots a_n A=a1,a2⋯an
定义操作1如下:
- 对 A A A 从小到大排序,并删除重复的元素,假设此时 A A A 的长度变为 m m m
- 将序列 A A A 更新为 { a 1 + m , a 2 + m − 1 , a 3 + m − 2 ⋯ a m + 1 } \{a_1 + m, a_2 + m-1, a_3 + m-2 \cdots a_m + 1\} {a1+m,a2+m−1,a3+m−2⋯am+1}
- 不断重复操作1和操作2,直到序列 A A A 中只剩下一个元素,你需要输出该元素的值
定义操作2如下:
- 每次操作给定两个整数 x , y x, y x,y ,其含义为:将 a x a_x ax 修改为 y y y
现依次执行 q q q 次操作2,求每次操纵2后,假如执行操作1后剩下的值
G. 题解
首先考虑给定一个序列 A A A ,经过操作1后剩下的数是什么
发现,操作1中每次更新只会使相邻元素的差减一,从而可以发现, A A A 中最大的元素会更新 A A A 差分的最大值,而 A A A 中最大元素每次更新是加一,从而可以得出 A A A 中最后剩下的元素为 A A A 的最大的元素加上 A A A 差分的最大值( A A A 中最大的元素一直在最右边,从右往左一直合并)
于是,问题变成了:我们需要维护这样的一个东西,可以高效地查询操作2执行后, A A A 中的最大值和 A A A 差分的最大值
我们可以利用两个 multiset
来维护这两个值
考虑如何用 multiset
维护序列
A
A
A 的最大值,显然,直接删除
a
x
a_x
ax 然后加入
y
y
y 即可。
考虑如何用 multiset
维护序列
A
A
A 差分的最大值:
- 当修改
a
x
a_x
ax 时,我们需要:
- 删除 a x a_x ax 与其前继的差分
- 删除 a x a_x ax 的后继与其的差分
- 加入 a x a_x ax 的后继与其前继的差分
- (注意,需要判断 a x a_x ax 是否有前继、后继)
- 让后我们找到插入
y
y
y 的位置,同理:
- 加入 y y y 与其前继的差分
- 加入 y y y 其后继与其的差分
- 删除 y y y 的后继与其前继的差分
- (注意,此时我们也需要判断 y y y 是否有前继、后继)