题目1: a b % p a^b\%p ab%p
输入3个数字a, b, p 求ab%p
数据范围很大,最大到1018
这个题目核心的就是用到了递推来防止溢出
数字b可以表示成
b
=
∑
k
=
0
k
=
n
d
k
∗
2
k
b = \sum_{k = 0}^{k=n}d_k*2^k
b=∑k=0k=ndk∗2k,其中
d
k
d_k
dk是二进制表示中第k位的值,
d
k
d_k
dk只有两个取值,0或者1。那么
a
b
=
a
∑
k
=
0
k
=
n
d
k
∗
2
k
=
∏
k
=
0
k
=
n
a
d
k
∗
2
k
=
a
d
0
∗
2
0
∗
a
d
1
∗
2
1
∗
a
d
2
∗
2
2
∗
.
.
.
∗
a
d
n
∗
2
n
a^b=a^{ \sum_{k = 0}^{k=n}d_k*2^k}=\prod_{k=0}^{k=n}a^{d_k*2^k}=a^{d_0*2^0}*a^{d_1*2^1}*a^{d_2*2^2}*...*a^{d_n*2^n}
ab=a∑k=0k=ndk∗2k=k=0∏k=nadk∗2k=ad0∗20∗ad1∗21∗ad2∗22∗...∗adn∗2n
需要用到的递推公式如下 a 2 k + 1 = a 2 ∗ 2 k = ( a 2 k ) 2 a^{2^{k+1}}=a^{2*2^{k}}={(a^{2^{k}})}^2 a2k+1=a2∗2k=(a2k)2这样就可以通过 a 2 k a^{2^{k}} a2k一步求出 a 2 k + 1 a^{2^{k+1}} a2k+1(求个平方即可)
因为
d
k
d_k
dk只有0或者1两种可能,当取0的时候对最终的结果没有任何影响。
利用这个递推公式就可以逐项求出
a
2
0
,
a
2
1
,
a
2
2
.
.
.
,
a
2
n
a^{2^{0}},a^{2^{1}},a^{2^{2}}...,a^{2^{n}}
a20,a21,a22...,a2n
a
2
0
a^{2^0}
a20就是a,其他项依次递推即可。
结算结果的时候,我们只关系 d k d_k dk是1的情况,如果是0,那相当于在结果上乘了一个 a 0 ∗ 2 k = a 0 = 1 a^{0*2^k}=a^0=1 a0∗2k=a0=1可以直接跳过。
代码如下
#include<iostream>
int a, b, p;
using namespace std;
int main() {
cin >> a >> b >> p;
int res = 1 % p;
while ( b ) {
if ( b & 1 ) res = res * 1ll * a % p;
a = a * 1ll * a % p;
b >>= 1;
}
cout << res << endl;
return 0;
}
题目2: a ∗ b % p a*b\%p a∗b%p
输入3个数字a, b, p 求a*b%p
思路和上一题一样,只不过这次是
a
∗
b
=
a
∗
∑
k
=
0
k
=
n
d
k
∗
2
k
=
∑
k
=
0
k
=
n
a
∗
d
k
∗
2
k
=
a
∗
d
0
∗
2
0
+
a
∗
d
1
∗
2
1
+
a
∗
d
2
∗
2
2
+
.
.
.
+
a
∗
d
n
∗
2
n
a*b=a*{ \sum_{k = 0}^{k=n}d_k*2^k}=\sum_{k=0}^{k=n}a*{d_k*2^k}=a*{d_0*2^0}+a*{d_1*2^1}+a*{d_2*2^2}+...+a*{d_n*2^n}
a∗b=a∗k=0∑k=ndk∗2k=k=0∑k=na∗dk∗2k=a∗d0∗20+a∗d1∗21+a∗d2∗22+...+a∗dn∗2n
递推式
a
∗
2
k
+
1
=
2
∗
(
a
∗
2
k
)
a*{2^{k+1}}=2*(a*{2^{k}})
a∗2k+1=2∗(a∗2k)
#include<iostream>
using namespace std;
typedef unsigned long long ull;
ull a, b, p;
int main() {
cin >> a >> b >> p;
ull res = 0;
while ( b ) {
if ( b & 1 ) res = (res + a) % p;
a = a * 2 % p;
b >>= 1;
}
cout << res << endl;
return 0;
}
代码中变量a存的是递推式算出来的值,即 ( a ∗ 2 k ) % p (a*{2^{k}}) \%p (a∗2k)%p(这里a是题目给的数字)