题目描述

✅ 1010. 总持续时间可被 60 整除的歌曲

image-20260929070952754

题意分析

统计满足 i < j 的歌曲下标对,使两首歌的总时长能够被 60 整除。总时长可以是任意 60 的倍数,不要求恰好等于 60。

歌曲按位置区分,即使时长相同也是不同元素。同一对只统计一次,一首歌不能与自己组成一对。

解法:余数计数

核心思路

[!blue]

一个数能否被 60 整除只取决于它的余数。两首歌总时长的余数等于它们余数之和再对 60 取模,因此可以把所有时长归到 0..59 这 60 类,而不必逐对检查原时长。

当前歌曲余数为 r 时,需要另一首余数为 (60 - r) % 60。当 r = 0,需要的仍是 0,所以外层取模不能省略;当 r = 30,它与同类余数配对,但仍必须来自另一个位置。

用 count 保存此前歌曲各余数的出现次数。把当前歌曲当作一对中的右端点,历史互补余数有多少首,就新增多少对:answer += count[need]。然后才增加当前余数计数,让它参与后续歌曲的配对。

每一组合法下标对都只会在较大下标被扫描时计入一次。先查询再登记也保证查到的都是更早的歌曲,不会与当前歌曲自身配对;即使同一余数出现很多次,次数累加仍会保留所有不同下标组合。

解题步骤

  1. 创建长度为 60 的全零计数数组,答案初始化为 0。
  2. 依次读取歌曲时长,计算当前余数 remainder 和互补余数 need。
  3. 将历史计数 count[need] 加入答案。
  4. 令 count[remainder]++,再继续处理下一首,最后返回总数。

代码实现

class Solution {
    public int numPairsDivisibleBy60(int[] time) {
        int[] count = new int[60];
        int answer = 0;

        for (int t : time) {
            int remainder = t % 60;
            // 再次取模让余数 0 的互补余数仍为 0。
            int need = (60 - remainder) % 60;

            // 先查询历史歌曲,再记录当前歌曲,避免与自己配对。
            answer += count[need];
            count[remainder]++;
        }

        return answer;
    }
}
func numPairsDivisibleBy60(time []int) int {
    count := make([]int, 60)
    answer := 0

    for _, t := range time {
        remainder := t % 60
        // 再次取模让余数 0 的互补余数仍为 0。
        need := (60 - remainder) % 60
        // 先查询历史歌曲,再记录当前歌曲,避免与自己配对。
        answer += count[need]
        count[remainder]++
    }
    return answer
}

复杂度分析

  • 时间复杂度:$O(n)$,每首歌曲只处理一次。
  • 空间复杂度:$O(1)$,计数数组长度固定为 60。

关键点总结

[!green]

  • 同余关系把所有可能时长压缩成固定的余数类别。
  • 当前歌曲固定作右端点,每个历史互补元素对应一个不同下标对。
  • 先查后记同时保证不与自身配对、不重复统计。
  • 题目最多 60000 首歌,全部配对也只有 1,799,970,000 对,仍在 32 位有符号整数范围内。

易错点总结

[!yellow]

  • 互补余数直接写成 60 - r,余数为零时会得到数组外的下标 60。
  • 先增加计数再查找,会在余数 0 或 30 时把当前歌曲与自己配对。
  • 用集合只记录某个余数是否出现过,会漏掉这个类别中不同歌曲对应的多组配对。
  • 只接受余数之和为 60,会漏掉两首余数都为 0 的合法情况。
  • 把时长相同的歌曲去重,会改变题目按下标区分元素的计数方式。

相似题目

题目 难度 关联与区别
1. 两数之和 简单 同样在扫描时查互补值,本题先对60取模,查找余数和为0模60的历史歌曲。
974. 和可被 K 整除的子数组 中等 同样利用余数消除大数值,本题两元素余数相加,原题两个前缀余数相减。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2023/24202267
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!