在 C\C++ 中,%
符号为求模运算符,即 a%b
表示 a
除以 b
的余数。
周期问题
我们在奥数中经常遇到这样的问题:
- 给你一串数字 4 5 6 4 5 6 4 5 6 ...,求这里的第 1000 个数字是几?
我们发现这是一个周期数列,周期为 3。
a. 如果将4看作第 1 个数,第 1000 个数一定是周期中的第 1 个数 4,因为前面 999 个数刚好是 333 组 456 构成的循环。
b. 如果将4看作第 0 个数,那么第 1000 个数此时就对应为第 999 个数,因为 999 模 3 余数为 0,同样对应的是 4。
- 给你一串数字 0 1 2 3 4 5 6 4 5 6 4 5 6...,求这里的第 1000 个数字是几?
这还是一个周期数列,周期为 3,但是它是从第 5 个数开始循环的,因此我们应该用 (1000-4) % 3
,算出来是 0,这该怎么办呢?
针对这个问题,我们可以有两种处理方法:
(1)特殊处理:遇到 0 时,取周期中的最后一个数 6;
(2)统一处理:我们不要从 1 开始数,由于 %
运算会出现 0,因此我们就从 0 开始数。我们将第一个循环的数字 4 (目前是第 5 个数)变成第 0 个数,重新写算式 (1000-5) % 3
,结果为 2,则 456 中,4 作为周期的第 0 个数,那么 6 就是第 2 个数。
练习题: 凯撒密码https://www.mfstem.org/p/368
这一题里,每个字母都被变成它后面的第 3 个字母,因此直接将其 ASCII 码值加 3 即可。
但是,最后 3 个字母却遇到了问题,x→a,y→b,z→c,这个不是加 3 能够实现的。如何解决呢?
我们可以想象一下,把 26 个字母看成上面那样的循环:a,b,c,d,…,z,a,b,c,d,…,这样的话,当我们遇到x→a 的问题时,x 是第 23 个字母(从 0 开始数的),就相当于求第 26 个字母(计算23+3得到)是什么。那很容易想到,第 26 个字母就是 a。
那么现在还有一个问题,就是在 C\C++ 中,'a'
是用 ASCII 值 97 存储的,其余字母的 ASCII 值依次是 98,99,…98,99,…,我们需要先将 'a'
看成第 0 个字母,因此,如果我们要对一个字母变量 alpha 进行凯撒加密,计算其后面第 3 个字母时,需要先执行 alpha-'a'
,然后再加 3,除以 26 取模,完整的算式应该是 (alpha - 'a' + 3) % 26 + 'a'
。
#include <iostream>
using namespace std;
int main() {
char alpha;
cin >> alpha;
//这里 'a' 就是97,尽量不要写 97,而是直接用 'a' 表示,这样可读性更强。
alpha = (alpha - 'a' + 3) % 26 + 'a';
cout << alpha;
return 0;
}
获取一个整数的个位、十位
我们可以通过 a%10
来求得整数 a
的个位,通过 a/10%10
来获得它的十位。
所以,我们要获取一个位,需要分两步:
(1)将这个位用除以一次或多次 10 的方法变成个位;
(2)用 %10
的方法获取现在的这个个位即可。
例如,659 % 10
得到个位的 9; 659/10%10
得到十位的 5;659/10/10%10
得到百位的 6。虽然可以通过 /100
直接获得 6,但是如果遇到更多位数的时候,我们后面会通过循环来实现这个操作,那么每次循环就只除以 10,即可依次获取从个位到最高位的所有位。
要获得某一位的值,就先把这个位通过除以 10 的整数次方,移动到个位,再模 10 即可。
求模运算的公式
(1).(a+b+c)%M=(a%M+b%M+C%M)%M
(2).(a−b−c)%M=((a%M−b%M−C%M)%M+M)%M
(3).(a∗b∗c)%M=a%M∗b%M∗C%M
快速幂
#include <iostream>
typedef long long LL;
LL qpow(LL a, int b, int M) {
LL ans = 1;
while (b) {
if (b & 1) ans = ans * a % M;
a = a * a % M, b >>= 1;
}
return ans;
}
int main() {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
printf("%lld", qpow(a, b, c));
return 0;
}