题解:P12594 炽声音节旋

· · 题解

可以发现所有音符的时长都是 96 分音的倍数,因此可以一个 96 分音的时长作为单位长度构建一个时间轴。只有在轴的刻度上的时刻需要判断。然后依照题意解析输入的字符串,在音符对应的时间区间上标记当前区间内按下按键编号。最后扫描一遍时间轴,检查每个时间点上维护的按键有没有重复或超过三个即可。

更具体的,对每一个时刻维护一个 vector 存放该时刻被按下的按键编号。对于某一个时刻的 tap,直接把该 tap 扔到相应时刻的 vector 里。对于一个 yx 分音的 hold,其长度是 y \times 96 / x96 分音。如果当前时刻是 t,其对应的按压区间是 [t, t + 96y/x]。在这个区间内的时刻的 vector 里都加入相应的按键编号即可。注意 hold 按压时间是闭区间,首尾时刻的 vector 都要加入。

#include <bits/stdc++.h>

const int maxn = 100'000;
const int base = 96;

int T;
std::vector<int> timeLine[maxn];

int main() {
  std::cin >> T;
  for (auto _ : std::views::iota(0, T)) {
    int n;
    std::cin >> n;
    for (auto &i : timeLine) i.clear();
    int curT = 0;
    for (auto _ : std::views::iota(0, n)) {
      std::string line;
      std::cin >> line;
      auto split = [](const std::string &s, char ch) -> std::vector<std::string> {
        std::vector<std::string> ret;
        std::string cur;
        for (auto i : s) {
          if (i == ch) {
            ret.push_back(cur);
            cur.clear();
          } else {
            cur += i;
          }
        }
        if (!cur.empty()) ret.push_back(cur);
        return ret;
      };
      auto splited = split(line, ',');
      int notevalue = std::stoi(splited[0].substr(1));
      int noteTime = base / notevalue;
      splited.erase(splited.begin());
      for (auto notes : splited) {
        auto note = split(notes, '/');
        for (auto i : note) {
          auto button = std::stoi(i);
          if (i.size() == 1) {
            timeLine[curT].push_back(button);
          } else {
            auto body = i.substr(3, int(i.size() - 2));
            auto splitedHold = split(body, ':');
            int x = std::stoi(splitedHold[0]), y = std::stoi(splitedHold[1]);
            int holdTime = y * (base / x);
            for (int t = 0; t <= holdTime; ++t) {
              timeLine[curT + t].push_back(button);
            }
          }
        }
        curT += noteTime;
      }
    }
    bool ans = true;
    for (auto &i : timeLine) if (i.size() > 2) {
      ans = false;
      break;
    } else if (i.size() == 2 && i[0] == i[1]) {
      ans = false;
    }
    std::cout << (ans ? "Yes" : "No") << std::endl;
  }
}

验题人 @NotEvenANeko 写了一份 400 行的 R:https://www.luogu.com.cn/paste/gr6xtbky