题目链接
题目描述
Alice 和 Bob 玩一个取石子游戏,初始有 颗石子。玩家轮流操作,Alice 先手。
操作规则如下:
- 设当前石子数为
。找出
的所有真因数
中满足
的因数集合
。
- 如果集合
为空,则当前玩家直接获胜。
- 否则,从
中任选一个数
,将石子数量变为
。
双方都采取最优策略,判断谁将获胜。
解题思路
这是一个规则新颖的公平组合游戏。由于 的范围高达
,我们无法使用动态规划表,必须直接分析得出规律。
首先,我们分析游戏的终止和获胜条件:当轮到某个玩家时,如果当前的石子数 的真因数集合
(不含1和x本身)为空,则该玩家获胜。一个数没有这类真因数,当且仅当这个数是质数或1。因此,游戏状态为质数或1时,轮到的玩家直接获胜,这是一个必胜态。
基于此,我们可以推导其他状态的胜负情况:
- 一个状态是必胜态 (W),如果它能转移到一个必败态 (L)。
- 一个状态是必败态 (L),如果它的所有转移都只能到达必胜态 (W)。
我们来分析不同类型的数:
-
是质数或1: 根据规则,这是必胜态。Alice 赢。
-
(p是质数):
唯一的合法操作是转移到状态
。因为
是质数,是必胜态。由于所有(唯一的)转移都通向必胜态,所以
是一个必败态。Bob 赢。
-
(p, q是不同质数):
的合法操作是转移到状态
或
。两者都是质数,都是必胜态。因此,
也是一个必败态。Bob 赢。
-
有3个或更多质因数 (计算重复,例如
): 这样的数
总可以找到一个真因数
,而这个
恰好由两个质因数构成(例如,从
可以转移到
,从
可以转移到
)。根据我们刚才的分析,任何由两个质因数构成的数(
或
)都是必败态。因此,任何有3个或更多质因数的数,都可以一步转移到必败态,所以它本身是一个必胜态。Alice 赢。
最终结论:
- Bob 赢 (必败态): 当且仅当初始石子数
的质因数分解中,质因数的总个数(计入重复)恰好为 2。
- Alice 赢 (必胜态): 其他所有情况(质因数个数为 0, 1, 或
)。
因此,解题算法就是对 进行质因数分解,并统计质因数的总个数。
代码
#include <iostream>
using namespace std;
// 计算 n 的质因数个数 (包括重复)
int count_prime_factors(long long n) {
if (n == 1) return 0;
int count = 0;
// 优化:处理因子2
while (n % 2 == 0) {
count++;
n /= 2;
}
for (long long i = 3; i * i <= n; i += 2) {
while (n % i == 0) {
count++;
n /= i;
}
}
if (n > 1) {
count++;
}
return count;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
long long n;
cin >> n;
int factors_count = count_prime_factors(n);
if (factors_count == 2) {
cout << "Bob" << endl;
} else {
cout << "Alice" << endl;
}
return 0;
}
import java.util.Scanner;
public class Main {
// 计算 n 的质因数个数 (包括重复)
public static int countPrimeFactors(long n) {
if (n == 1) return 0;
int count = 0;
while (n % 2 == 0) {
count++;
n /= 2;
}
for (long i = 3; i * i <= n; i += 2) {
while (n % i == 0) {
count++;
n /= i;
}
}
if (n > 1) {
count++;
}
return count;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
long n = sc.nextLong();
int factorsCount = countPrimeFactors(n);
if (factorsCount == 2) {
System.out.println("Bob");
} else {
System.out.println("Alice");
}
}
}
import sys
def count_prime_factors(n):
if n == 1:
return 0
count = 0
while n % 2 == 0:
count += 1
n //= 2
d = 3
while d * d <= n:
while n % d == 0:
count += 1
n //= d
d += 2
if n > 1:
count += 1
return count
def main():
try:
n = int(sys.stdin.readline())
factors_count = count_prime_factors(n)
if factors_count == 2:
print("Bob")
else:
print("Alice")
except (IOError, ValueError):
return
if __name__ == "__main__":
main()
算法及复杂度
- 算法:数论、博弈论
- 时间复杂度:算法的核心是计算质因数的个数。通过试除法,我们只需要检查到
。因此,对于每个输入
,时间复杂度为
。
- 空间复杂度:只需要常数个变量来存储中间结果,空间复杂度为
。