#P12594. 炽声音节旋
炽声音节旋
题目背景
扶苏是一个舞萌吃。她今天学习了如何编写舞萌的谱面!现在,她要来教一下你了。
舞萌是一款街机游戏,游戏框体共有两个机位,玩家可以单人游玩,亦可以双人组队,其玩法与大多数音乐游戏相似,音符则从屏幕中心向四周移动,玩家需要在合适的时机拍击按键或者触摸屏幕外侧,并达到一定分数才可通关。
图片来源:wikipedia
题目描述
舞萌框体共有 8 个可供拍打的外键,写谱时以右上角为 1 号键,顺时针进行编号,如下所示:
8 1
7 2
6 3
5 4
舞萌谱面使用一种名为 s-simai (simplified-simai) 的语言进行编写。为了方便处理,我们对 s-simai 做进一步简化。
节拍与时值
一拍是谱面的一个基本时间单位,被称为一个四分音或一个四分音符。在本题中,可以认为一拍对应一个固定的时间长度,其具体数值并不重要,你无需区分接下来表述中音符的「时值」和「时长」,可以认为二者是统一的。如果你愿意,可以认为一拍是 秒,即一个四分音符占 秒的时间。
除了四分音符外,还有其他时长的音符。一个 分音符的时长是一拍的 倍。例如,一个 分音符的时长是一拍的一半,即半拍。如果一拍是 秒,则一个八分音占 秒。一个 分音符(虽然乐理中一般不采用「十二分音」这样的表述,但这里为了方便描述做了统一)的时长是一拍的 。我们保证 是 六个数字之一。
简化的 s-simai 语言一行表示若干个时长相同的音符,以 {x},
开头,表示本行音符的时值(时长)。音符与音符之间以 ,
分隔。例如,{16},A,B,C,D,
表示了接下来连续 个十六分音的音符分别是 A
,B
,C
,D
。多行 s-simai 表示的时间是连续的。例如:
{16},A,B,C,D,
{8},E,F,
就表示了连续的四个 分音和两个 分音的谱面,谱面的总长度是两拍。
接下来我们介绍音符的具体表示法。
休止符
如果在某一个时值内什么都不做,则谱面中当前音符为空,例如 {16},,,C,D,
表示前两个十六分音什么都不做(没有音符),后两个十六分音分别是 C
,D
两个音符。
Tap 音符
Tap 音符需要在一个音符开始的瞬间拍击某一个标号的按键,然后瞬间松开。在谱面中直接写出需要拍击的按键编号。例如,{16},1,,2,,
表示接下来一拍(四个 分音)内,在第一个和第三个 分音的开始时刻分别拍击 号和 号按键,第二个和第四个 分音里什么都不做。
如果同一个时刻要拍击多个按键,可以把按键的编号用 /
隔开。例如,{16},1/8,,2/7,,
表示在第一个 分音的开始时刻同时拍击 和 号键,在第三个 分音的开始时刻同时拍击 和 号键。
Hold 音符
这是一种需要持续按压的音符,用 ph[x:y]
表示在 号键上出现一个 Hold 音符,在当前分音开始的瞬间开始按压,持续按压 个 分音符的长度, 保证 是 六个数字之一。例如,{16},1h[8:1],,2h[8:1],,
表示在第一个 分音的开始时刻起按压 号键,持续时间为 个八分音符,即在第二个 分音的结束时刻(也是第三个 分音的开始时刻)松开按压;然后在第三个 分音的开始时刻类似地按压 号按键。同样的,在同一时刻拍击的多个 Hold 音符也可以用 /
分隔;/
也可以用于分隔 Tap 音符和 Hold
音符。例如,下面的谱面都是合法的:
{4},1h[4:1]/2h[4:1],
{4},1h[4:1]/2,
{4},1h[4:1]/2/3,
{4},1/2/3,
{4},1h[4:1]/2h[8:1],
我们保证 /
分隔开的按键编号是不同的,即不会出现 {4},1/1
或 {4},1/1h[8:1]
这样的情况。
无理检测
因为你只有两只手,所以谱面必须保证在任何时刻,都只有至多两个按键被按下(含拍击)。类似这样的谱面被称为多押无理:{16},1h[8:1]/2h[8:1],3,
,因为在第二个 分音开始时,你的两只手分别正在按压 号和 号按键,没有多余的手来按压 号按键。特别的,{16},1h[8:1]/2h[8:1],,3,,
也是多押无理,因为 Hold 音符结束时刻(第二个 分音结束时)和 Tap 拍击时刻(第三个 分音开始时)是同一个时刻。
在 Hold 音符持续过程内,按键持续处于按下状态,因此无法拍击同一位置上的 Tap 音符。因此这样的谱面被称为嵌套无理:{16},1h[8:1],1,
,因为第二个 分音开始时, 号键处于按下状态,无法拍击。在一个 Hold 音符的结束时刻于同一按键出现一个 Tap 音符也属于嵌套无理,如 {16},1h[16:1],1
。
给定一个 s-simai 谱面,你需要判定该谱面是否含有上述两种无理。
输入格式
本题单个测试点内有多组测试数据。输入的第一行为一个整数 ,表示接下来将给出 个 s-simai 谱面。对每个谱面,按如下格式给出:
第一行是一个整数 ,表示谱面文件的行数。
接下来 行,每行是一个字符串,表示 s-simai 谱面的一行。
输出格式
对每组数据依次输出一行一个字符串表示答案。
如果谱面没有无理,输出 Yes
,否则输出 No
。
9
1
{4},1h[4:1]/2h[4:1],
1
{4},1h[4:1]/2,
1
{4},1h[4:1]/2/3,
1
{4},1/2/3,
1
{4},1h[4:1]/2h[8:1],
1
{16},1h[8:1]/2h[8:1],3,
1
{16},1h[8:1]/2h[8:1],,3,,
1
{16},1h[8:1],1,
1
{16},1h[16:1],1,
Yes
Yes
No
No
Yes
No
No
No
No
2
2
{16},1,2,3,
{12},1,2,3,
2
{16},1h[4:1],2h[4:1],
{8},,3,4,
Yes
No
提示
数据规模与约定
测试点编号 | 特殊约定 | |
---|---|---|
AB | ||
A | ||
B | ||
无 | ||
AB | ||
A | ||
B | ||
无 |
特殊约定中,A 表示谱面中不含 Hold 音符,B 表示谱面中不含用 /
分隔的音符,AB 表示同时满足上述约定。
对于全部的测试数据,保证 ,,输入的谱面是合法的 s-simai 谱面,保证一个测试点内输入谱面的总行数不超过 。
一行合法的 s-simai 谱面做如下保证:
- 以
{x},
开头,其中 - 以
,
结尾,且每行至少包含两个,
。 - 一行的相邻两个
,
之间要么是空串(没有字符),要么是若干个 Tap 或 Hold。如果是多个音符,用/
隔开。 - 一个 Tap 是一个整数 ,保证 。
- 一个 Hold 表示为
ph[x:y]
,其中 是整数表示按键编号, 和 也都是整数,h
是字符 。满足 ,,。 - 保证用
/
隔开的音符不会要求按压或拍击同一个按键。 - 保证一行里字符
,
的数量不会超过 个。
形式化地,一行 s-simai 谱面满足如下 BNF 范式:
<LINE> = \{<NOTEVALUE>\},<BODY>,
<BODY> = <NOTES> | ε | <BODY>,<BODY>
<NOTES> = <NOTE> | <NOTE>/<NOTES>
<NOTE> = <TAP> | <HOLD>
<TAP> = <BUTTON>
<HOLD> = <BUTTON>h\[<NOTEVALUE>:<DIGIT>\]
<BUTTON> = [1-8]
<DIGIT> = [1-9]
<NOTEVALUE> = 4 | 8 | 12 | 16 | 24 | 32
特别地,保证一个 <NOTES>
里 <BUTTON>
互不相同,<BODY>
展开不会超过 次。
一个合法的 s-simai 谱面是若干行合法的 s-simai 谱面。