快速幂(乘法~乘方)
(工具 二进制优化的思想)
我们平时实现计算a^n次方通常会想到直接用1乘a循环n次
根据数学知识: 同底数幂相乘 底数不变指数相加 以及数的二进制表示
我们可以进行以下优化
将指数n转换成二进制展开式
n
=
x
n
⋅
2
n
−
1
+
.
.
.
+
x
3
⋅
2
2
+
x
2
⋅
2
1
+
x
1
⋅
2
0
\begin{align*} n &= x_{n} \cdot 2^{n-1} + ... + x_3 \cdot 2^2 + x_2 \cdot 2^1 + x_1 \cdot 2^0 \end{align*}
n=xn⋅2n−1+...+x3⋅22+x2⋅21+x1⋅20
所以快速幂的思想就是:预处理出 底数为a 指数为2的k次方(k从0一直往下取取到指数二进制最后一位减1)
然后取出指数n的每一位 如果该m位是1 那么就把a的2^m次方累乘起来(m从0开始)
位数
=
log
2
(
n
)
位数 = \log_2(n)
位数=log2(n)
上取整
//a^k%p
----------------------------
快速幂就是求在O(logK)求出a^k%p
算法原理就是预处理出a的(2的0~logK次方)就是把a^k拆乘a^(x1*2^0+x2*2^1+...)
用二进制来表示k
原本朴素是O(n) a乘k次
现在 k的二进制拆解 两数相乘 指数相加 这样就可以凑出K次方
然后发现预处理的每一项
a^(2^0) a^(2^1) a(2^2)
a(2^n)=a(2^(n-1))^2
就是每一项等于前一项的平方 所以预处理只需要不断的平方就可以了 别忘了每一步(mod p)
----------------------------
#include <iostream>
using namespace std;
typedef long long LL;
int qmi(int a,int k,int p)
{
int res=1;
while(k)
{
if(k&1)res=(LL)res*a%p;
k>>=1;
a=(LL)a*a%p;
}
return res;
}
int main(void)
{
int n;
scanf("%d",&n);
while(n--)
{
int a,k,p;
scanf("%d%d%d",&a,&k,&p);//n比较大 用scanf和printf快些
printf("%d\n",qmi(a,k,p));
}
return 0;
}
快速加(龟速乘 加法~乘法)
思想和快速幂类似 将大数优化乘二进制 a*b
预处理出 1a 2a 4a 8a…然后相加
细节如代码
acwing.64位整数乘法
#include <cstdio>
typedef long long LL;
/*
---------------
快速加:
a,b 10的18次方 如果直接乘起来会爆long long
回忆快速幂: 我们用乘法来解决乘方的问题
a*b 相当于 b个a相加
到底是多少个呢?
我们预处理出 a%p 2a %p=(a%p+a%p)%p
b*a=(k0*2^0+k1*2^1+...)*a
预处理加上判断b的第k位上是不是1
然后加起来
---------------
*/
LL qadd(LL a,LL b,LL p)
{
LL res=0;
while(b)
{
if(b&1)res=(res+a)%p;
b>>=1;
a=(a+a)%p;
}
return res;
}
int main(void)
{
LL a,b,p;
scanf("%lld%lld%lld",&a,&b,&p);
LL res=qadd(a,b,p);
printf("%lld\n",res);
return 0;
}
``