魔法棒
思路
拿到这道题,先理清操作到底在做什么。小坤一开始有 1 根魔法棒,每次操作可以选一根魔法棒,把它"分裂"成 根(
是正整数)。注意:原来那根消失了,变成了
根新的,所以每次操作净增
根。
时净增 0,没意义。
时,能增加的数量依次是
那问题就变成了: 能不能表示成若干个
中元素的和?
关键推导
最小的两个增量是 3 和 8,它们互质()。根据 Chicken McNugget 定理(也叫 Frobenius 定理),用 3 和 8 做非负整数组合,不能表示的最大整数是
。也就是说,所有
的非负整数都能用若干个 3 和若干个 8 凑出来。
那小于 14 的情况呢?手动列一下 能凑出的值:
$$
凑不出的值是 。
所以 不可达当且仅当
,即
。
除了这 7 个数以外,其他所有正整数都输出 Yes。
样例验证
:
,凑不出,输出
No:
,先分裂出 9 根(
),再分裂一根出 4 根(
),输出
Yes
代码
#include <bits/stdc++.h>
using namespace std;
int main(){
// 不可达的 n 只有这 7 个
set<int> bad = {2,3,5,6,8,11,14};
int t;
scanf("%d",&t);
while(t--){
long long n;
scanf("%lld",&n);
puts(bad.count(n) ? "No" : "Yes");
}
return 0;
}
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int t = Integer.parseInt(br.readLine().trim());
Set<Long> bad = new HashSet<>(Arrays.asList(2L,3L,5L,6L,8L,11L,14L));
StringBuilder sb = new StringBuilder();
while (t-- > 0) {
long n = Long.parseLong(br.readLine().trim());
sb.append(bad.contains(n) ? "No" : "Yes").append('\n');
}
System.out.print(sb);
}
}
import sys
input = sys.stdin.readline
bad = {2, 3, 5, 6, 8, 11, 14}
t = int(input())
out = []
for _ in range(t):
n = int(input())
out.append("No" if n in bad else "Yes")
print('\n'.join(out))
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line));
rl.on('close', () => {
const bad = new Set([2, 3, 5, 6, 8, 11, 14]);
const t = parseInt(lines[0]);
const out = [];
for (let i = 1; i <= t; i++) {
const n = parseInt(lines[i]);
out.push(bad.has(n) ? "No" : "Yes");
}
console.log(out.join('\n'));
});
复杂度分析
- 时间复杂度:
,每组测试数据
查表
- 空间复杂度:
,只存了 7 个常量
总结
这题乍一看像搜索或 DP,但仔细一分析,每次操作的净增量集合是 。既然 3 和 8 已经互质了,后面的大数其实不影响结论——Frobenius 定理直接告诉你,大于 13 的数都能凑出来。小于等于 13 的手动列举一下就行,最终就是 7 个不可达的数,打个表完事。数论题最怕的就是想复杂了,这题的核心就是一个 Chicken McNugget 定理。



京公网安备 11010502036488号