钾肥喵的窝

我在 CODING 部署的 Hexo 博客

0%

SEERC 2017 E.Looping Playlist

题面

E.Looping Playlist

题意

播放列表从任意位置开始循环播放一次, 给定音高, 求最少歌曲数.

每首歌曲有两个(含)以上音符组成, 均遵循某种大调音阶(major scale).

下面给出题目中的12种大调音阶:

major scale Root +0 Root +2 Root +4 Root +5 Root +7 Root +9 Root +11 Root +12
Do Do Re Mi Fa Sol La Si Do
Do# Do# Re# Fa Fa# Sol# La# Do Do#
Re Re Mi Fa# Sol La Si Do# Re
Re# Re# Fa Sol Sol# La# Do Re Re#
Mi Mi Fa# Sol# La Si Do# Re# Mi
Fa Fa Sol La La# Do Re Mi Fa
Fa# Fa# Sol# La# Si Do# Re# Fa Fa#
Sol Sol La Si Do Re Mi Fa# Sol
Sol# Sol# La# Do Do# Re# Fa Sol Sol#
La La Si Do# Re Mi Fa# Sol# La
La# La# Do Re Re# Fa Sol La La#
Si Si Do# Re# Mi Fa# Sol# La# Si

基本思路

首先研究一组大调音阶, 显然, 对给定的一组大调音阶, 可以存在的音符是确定的, 而在音高序列中某种音符只有两种状态(存在和不存在), 很自然我们想到用二进制来处理.

接下来很重要的一步就是将音高序列转换为二进制数, 赋予每种音符一定的权值, 然后移位 + 按位或即可, 不过多赘述.

那么, 如何确定音高序列和大调音阶是否匹配呢? 很显然, 如果匹配, 二者按位或的结果中为1的bit数必然等于7(想一想, 为什么?)

于是我们就可以使用贪心来处理了, 遍历每个音符, 然后判断是否有匹配的大调音阶, 一旦加入某个音符后无法匹配, 说明这个音符属于另一个大调音阶, 也就是另一首歌曲.

因为是循环播放, 所以我们枚举开始音符(长度为12的区间就可以了, 自行证明)来计算歌曲数的最小值, 最后还需要考虑结尾和开始(长度可以压缩到14, 或者更小)是否是同一首歌.

打表

有了上面的性质, 我们就可以打表了, 逐一判断是否有匹配的序列就可以了, 然而这样朴素的打表会TLE, 于是我们还需要优化.

我们至多用到了 12 bit, 所以最多有 4095 种情况, 打表是可以接受的, 于是只要遍历然后判断是否符合某个大调音阶就可以了.

打表的相关代码如下(如有需要请自行修改):

完整AC代码

事实证明, 优化过的cin读入比scanf()读入要快, 前者勉强AC(1647ms ~ 2s), 后者TLE.