题解:洛谷 P5834 MooBuzz
【题目来源】
洛谷:P5834 [USACO19DEC] MooBuzz S - 洛谷
【题目描述】
Farmer John 的奶牛们最近成为了一个简单的数字游戏“FizzBuzz”的狂热玩家。这个游戏的规则很简单:奶牛们站成一圈,依次从一开始报数,每头奶牛在轮到她的时候报一个数。如果一头奶牛将要报的数字是 \(3\) 的倍数,她应当报 Fizz 来代替这个数。如果一头奶牛将要报的数字是 \(5\) 的倍数,她应当报 Buzz 来代替这个数。如果一头奶牛将要报的数字是 \(15\) 的倍数,她应当报 FizzBuzz 来代替这个数。于是这个游戏的开始部分的记录为:
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16
由于词汇的匮乏,奶牛们玩的 FizzBuzz 中用Moo 代替了 Fizz、Buzz、FizzBuzz。于是奶牛版的游戏的开始部分的记录为:
1, 2, Moo, 4, Moo, Moo, 7, 8, Moo, Moo, 11, Moo, 13, 14, Moo, 16
给定 \(N\),请求出这个游戏中第 \(N\) 个被报的数。
【输入】
输入包含一个整数 \(N\)。
【输出】
输出游戏中被报出的第 \(N\) 个数。
【输入样例】
4
【输出样例】
7
【核心思想】
-
问题分析:奶牛们依次报数,但跳过所有 \(3\) 或 \(5\) 的倍数(用
Moo代替)。给定 \(N\),求第 \(N\) 个实际被报出的数字(即第 \(N\) 个不是 \(3\) 或 \(5\) 倍数的正整数)。本质是一个周期性的数论计数问题。 -
算法选择:
- 周期规律法:观察发现每 \(15\) 个数为一个周期(\(3\) 和 \(5\) 的最小公倍数),每个周期内有 \(8\) 个有效数字(非 \(3\) 且非 \(5\) 的倍数),直接利用周期性公式计算
-
关键步骤:
- 周期分析:在 \([1, 15]\) 范围内,有效数字为 \(\{1, 2, 4, 7, 8, 11, 13, 14\}\),共 \(8\) 个
- 公式推导:
- 完整周期数:\(\lfloor N / 8 \rfloor\),每个完整周期贡献 \(15\) 个数字跨度
- 剩余位置:\(N \bmod 8\),对应周期内第 \(N \bmod 8\) 个有效数字
- 答案:\(\text{num} = \lfloor N / 8 \rfloor \times 15 + a[N \bmod 8]\),其中 \(a[0..7] = \{-1, 1, 2, 4, 7, 8, 11, 13, 14\}\)(\(a[0] = -1\) 为占位,实际使用 \(a[1..8]\))
- 直接计算:\(O(1)\) 得出结果
-
时间/空间复杂度:
- 时间复杂度:\(O(1)\)
- 空间复杂度:\(O(1)\)
-
周期规律法的核心思想:
- 容斥原理确定周期长度:\(3\) 和 \(5\) 的最小公倍数为 \(15\),因此有效数字的分布以 \(15\) 为周期重复
- 周期内计数:每 \(15\) 个数中,\(3\) 的倍数有 \(5\) 个,\(5\) 的倍数有 \(3\) 个,\(15\) 的倍数有 \(1\) 个,有效数字数为 \(15 - 5 - 3 + 1 = 8\)
- 打表映射余数:将周期内 \(8\) 个有效数字按顺序存入数组,通过余数直接索引,避免实时计算
- 整数除法拆分:将第 \(N\) 个有效数字拆分为"完整周期贡献"加"周期内偏移",类比时钟的"小时 + 分钟"结构
- 适用于模周期计数、筛选数列的第 \(N\) 项、FizzBuzz类数论问题
【解题思路】

【算法标签】
普及- #数学
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, a[10]={-1,1,2,4,7,8,11,13,14}; // 注意a[0]写为-1
int main()
{
cin >> n; // 输入n
int num = n/8 * 15 + a[n%8]; // 根据数字规律得到此公式
cout << num << endl; // 输出结果
return 0;
}
【运行结果】
4
7
浙公网安备 33010602011771号