Carmichael Numbers
我们把对任意的
1
<
x
<
n
1 < x < n
1<x<n 都有
x
n
≡
x
(
m
o
d
n
)
x^n\equiv x(mod\quad n)
xn≡x(modn)成立的合数
n
n
n称为Carmichael Number。对于给定的整数
n
n
n,请判断它是不是Carmichael Number。
限制条件
-
2
<
n
<
65000
2 < n < 65000
2<n<65000
样例输入
样例1
输入 |
---|
17 17 17 |
输出 |
---|
No(17是素数) |
样例2
输入 |
---|
561 561 561 |
输出 |
---|
Yes |
样例3
输入 |
---|
4 4 4 |
输出 |
---|
No( 2 4 ≡ 0 ( m o d 4 2^4 \equiv0 (\quad mod4 24≡0(mod4) ) |
题解
此题中,有
n
n
n 个待检查的书,如果每个数都按定义
O
(
n
)
O(n)
O(n)复杂度来计算幂,则总的负责的为
O
(
n
3
)
O(n^3)
O(n3),时间复杂度太高,过不了。我们来介绍快速幂的方法。如果
n
=
2
k
n=2^k
n=2k ,可以将其表示为:
x
n
=
(
(
x
2
)
2
)
⋯
x^n=((x^2)^2)\cdots
xn=((x2)2)⋯
只要做 k 次平方运算就可以轻松求得。由此我们可以想到,先将 n 表示为 2 的幂次的和。(可以结合该数的二进制形式来理解)
n
=
2
k
1
+
2
k
2
+
2
k
3
+
⋯
n=2^{k_1}+2^{k_2}+2^{k_3}+\cdots
n=2k1+2k2+2k3+⋯
就有
x
n
=
x
2
k
1
x
2
k
2
x
2
k
3
⋯
x^n=x^{2^{k_1}}x^{2^{k_2}}x^{2^{k_3}}\cdots
xn=x2k1x2k2x2k3⋯
只要在依次求出
x
2
i
x^{2^i}
x2i的同时进行计算就行了,最终得到
O
(
l
o
g
n
)
O(logn)
O(logn)计算快速幂运算的算法。
核心代码:
typedef long long ll;
ll res = 1;
while(n>0)
{
if(n & 1) res = res * x % k; // 如果二进制最低为为1,则乘上 x^(2^i)
x = x * x % k; // 将 x 平方
n >>= 1;
}
printf("%lld",res);
我们通过一个实例来消化一下:
x
22
=
x
16
×
x
4
×
x
2
x^{22}=x^{16}\times x^{4} \times x^2
x22=x16×x4×x2
解释一下式子的来由:
因为,22 的二进制形式是 10110 (从最右边开始,第1位是第0位),我们看到“10110”该数的第1位、第2位、第四位都不为0,即代表,22 可以由
2
1
+
2
2
+
2
4
2^1+2^2+2^4
21+22+24来构成,这也就对应了上面的公式,同时也是对应了代码中的:
if(n & 1) res = res * x % k;
当 n 的值每右移一位(n >>= 1),我们都判断一下,如果它的最低为上面是1,我们就乘上
x
2
i
x^{2^i}
x2i。至于为什么这样分步来对 k 来取模运算得到的答案是和结果相同的,可以去熟悉取模的基本运算规则(运算规则放在了文末)。
上面的程序还没有完全做完(我们只重点介绍快速幂的思想),还有加上一个素数判断的部分,判断素数可以打表也可以直接判断,最后如果不是素数,并且上面代码对 k 的取模运算等于 x 自身对 k 的取模运算,那么输出 Yes。
学完巩固题:P1226 【模板】快速幂||取余运算