题目
求 a 乘 b 对 p 取模的值。
输入格式
第一行输入整数a,第二行输入整数b,第三行输入整数p。
输出格式
输出一个整数,表示 a * b mod p 的值。
数据范围
1 ≤ a,b,p ≤ 1018
输入样例:
3
4
5
输出样例:
2
解题思路
暴力
首先最容易想到的是直接计算 a * b mod p,但由于数据范围非常大,这会导致没有数据类型能够装下 a * b,所以需要将中间结果限制在一定范围内。(代码略)
数学优化
由数论中的结论可知:
(A * B) % p = (A % p * B % p) % p
且乘法可以转化成加法,即通过循环每次加 a,共循环 b 次来达到乘 b 的效果
java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
InputReader in = new InputReader(System.in);
PrintWriter out = new PrintWriter(System.out);
long a = in.nextLong();
long b = in.nextLong();
long p = in.nextLong();
long res = 0;
for(int i = 0; i < b; i++) {
res += a;
res %= p;
}
out.print(res);
out.flush();
}
public static class InputReader{
BufferedReader reader;
StringTokenizer tokenizer;
public InputReader(InputStream in) {
reader = new BufferedReader(new InputStreamReader(in));
}
public String next() throws IOException {
while(tokenizer == null || !tokenizer.hasMoreTokens()) {
tokenizer = new StringTokenizer(reader.readLine());
}
return tokenizer.nextToken();
}
public Long nextLong() throws IOException {
return Long.parseLong(next());
}
}
}
c++
#include<bits/stdc++.h>
using namespace std;
int main()
{
long long a, b, p;
scanf("%lld%lld%lld", &a, &b, &p);
long long res = 0;
for(int i = 0; i < b; i++)
{
res += a;
res %= p;
}
printf("%lld", res);
return 0;
}
但此时由于 b 的值范围非常大,而该思想时间复杂度又为O(n),所以在最坏情况下必然 TLE。
二进制优化
例如计算 3 ∗ 5 3 * 5 3∗5
首先将 5 转成二进制数 0101B
5
=
2
2
+
2
0
5 = 2^{2} + 2^{0}
5=22+20
所以原式可以化成下式:
3
∗
5
=
3
∗
2
2
+
3
∗
2
0
3 * 5 = 3 * 2^{2} + 3 * 2^{0}
3∗5=3∗22+3∗20
而根据(A + B) % p = (A % p * B % p) % p
2
n
m
o
d
p
=
2
n
−
1
m
o
d
p
∗
2
m
o
d
p
2^{n} \bmod p = 2^{n-1} \bmod p * 2 \bmod p
2nmodp=2n−1modp∗2modp
这不仅可以将每次加的数控制在允许范围内,还可以将时间复杂度降低到O(logn)。
AC代码
Java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.StringTokenizer;
public class Main90 {
public static void main(String[] args) throws IOException {
InputReader in = new InputReader(System.in);
PrintWriter out = new PrintWriter(System.out);
long a = in.nextLong();
long b = in.nextLong();
long p = in.nextLong();
long res = 0;
while(b != 0) {
if((b & 1) == 1) {
res += a;
res %= p;
}
a %= p;
a = (2 * a) % p;
b >>= 1;
}
out.print(res);
out.flush();
}
public static class InputReader{
BufferedReader reader;
StringTokenizer tokenizer;
public InputReader(InputStream in) {
reader = new BufferedReader(new InputStreamReader(in));
}
public String next() throws IOException {
while(tokenizer == null || !tokenizer.hasMoreTokens()) {
tokenizer = new StringTokenizer(reader.readLine());
}
return tokenizer.nextToken();
}
public Long nextLong() throws IOException {
return Long.parseLong(next());
}
}
}
c++
#include<bits/stdc++.h>
using namespace std;
int main()
{
long long a, b, p;
scanf("%lld%lld%lld", &a, &b, &p);
long long res = 0;
while(b)
{
if(b & 1)
{
res += a;
res %= p;
}
a %= p;
a = (2 * a) % p;
b >>= 1;
}
printf("%lld", res);
return 0;
}