快速幂:快速求$a^b$ % p的问题,时间复杂度:$O(logb)$,若对于n组数据,那么时间复杂度为$O(n \ast logb)$
一.暴力解法 $\quad O(n \ast b) \quad 会\color{red}{TLE}$
基本思路:对于n组数据,分别循环b次求出$a^b \quad mod \quad p$
#include<iostream>
using namespace std;
int main()
{
int n;
cin>>n;
while(n--)
{
int a,b,p;
long long res=1;
cin>>a>>b>>p;
while(b--)
res = res * a %p;
cout<<res<<endl;
}
}
二.快速幂解法 $\quad O(n \ast logb)$
基本思路:
- 预处理出$a^{2^0},a^{2^1},a^{2^2},\ldots,a^{2^{logb}}.这b个数$
- 将$a^b用a^{2^0},a^{2^1},a^{2^2},\ldots,a^{2^{logb}}这b种数来组合,即组合成a^b=a^{2^{x_1}}\times a^{2^{x_2}}\times \ldots \times a^{2^{x_t}}=a^{2^{x_1}+2^{x_2}+\ldots+2^{x_t}}$即用二进制表示
为什么b可用$a^{2^0},a^{2^1},a^{2^2},\ldots,a^{2^{logb}}这b个数来表示?$∵$\color{brown}{二进制可以表示所有数,且用单一用二进制表示时,b单一表示最大可表示为二进制形式的2^{logb} }$
注意:
- b&1就是判断b的二进制表示中第0位上的数是否为1,若为1,b&1=true,反之b&1=false 还不理解?进传送门
b&1也可以用来判断奇数和偶数,b&1=true时为奇数,反之b&1=false时为偶数
快速幂之迭代版 $\quad O(n \ast logb)$
#include<iostream>
using namespace std;
long long qmi(long long a,int b,int p)
{
long long res=1;
while(b)//对b进行二进制化,从低位到高位
{
//如果b的二进制表示的第0位为1,则乘上当前的a
if(b&1) res = res *a %p;
//b右移一位
b>>=1;
//更新a,a依次为a^{2^0},a^{2^1},a^{2^2},....,a^{2^logb}
a=a*a%p;
}
return res;
}
int main()
{
int n;
cin>>n;
while(n--)
{
cin.tie(0);
ios::sync_with_stdio(false);
int a,b,p;
long long res=1;
cin>>a>>b>>p;
res = qmi(a,b,p);
cout<<res<<endl;
}
return 0;
}
快速幂之递归版 $\quad O(n \ast logb)$
#include<iostream>
using namespace std;
#define ull unsigned long long
ull quick_pow(ull a,ull b,ull p)
{
if(b==0) return 1;
a%=p;
ull res=quick_pow(a,b>>1,p);
if(b&1) return res*res%p*a%p;
return res*res%p;
}
int main()
{
int n;
cin>>n;
while(n--)
{
int a,b,p;
cin.tie(0);
ios::sync_with_stdio(false);
cin>>a>>b>>p;
cout<<quick_pow(a,b,p)<<endl;
}
return 0;
}
$\color{brown}{喜欢题解的话,欢迎点赞、收藏、关注(三连)喔,如有什么疑问,欢迎评论下方指出}$
任何数都可以用任何进制的数表示出来吧
不对的兄弟
十进制的整数是可以精确的转化为二进制的,小数部分转化为二进制不一定能精确转化为二进制,有时候会有精度丢失。
人家说的是对的,精度丢失只是因为进制不同,10进制也有无限位的小数
为什么不能用pow呢
pow直接溢出了
收到
你好,请问一下,这里 a=a*a%p为什么也要%p呢?
防止溢出long long范围
但是取模了不是会改变结果?
题目要的就是取模后的
这个对a*a取模为啥不会影响最后的结果,大佬
(a + b) % p = (a % p + b % p) % p
(a - b) % p = (a % p - b % p) % p
(a * b) % p = (a % p * b % p) % p
a ^ b % p = ((a % p)^b) % p
搜了一下取模的运算性质
但是推了好长时间没推出来,可能是我太菜了www
我也没推出来
用数学归纳法推导的
如果还有疑问的话可以看看我的题解 https://www.acwing.com/solution/content/225928/
大佬,快速幂第二点第三行好像写错了,应该是b可用2^0 + 2^1 ……这些数表示吧,而且也不是b个数,比如b是9的话那应该是两个数2^0 + 2^3
应该是酱紫吧:
为什么 $k$ 可以用 ${2^{0}},{2^{1}},{2^{2}},…{2^{logk}}$ 中的数凑出来?
$2^{{log_{2}}k} = k$ 所以一定可以凑出k
思路更清晰了2333
第二种方法为什么写出来会超时呢?
请问一下,为什么用while(b)再b>>=1可以,而for(int i=b;i;i>>=1)就不行呢
好的我现在知道是我没用1ll可能溢出了吧(逃)
%%%
好像现在 a = (LL) a * a % p才行
就这种加(long long)的 a定义的时候就是long long 如果不加long long 为啥会错呢
因为a*a有可能溢出
int 范围是10^10,而 long long 是10^19
qmi的返回值好像改成int也可以
因为 p 的范围是int
问一下为什么每次都要取余啊,最后求完幂直接取个余为什么不行呐
怕数值溢出
哦哦是怕a溢出是吗
res和a都可能溢出,所以都模下
感谢:)
都模下不会对结果造成影响吗
$你去模拟一下 最开始模和最后模效果是一样的$
这种字怎么打的啊
LATEX
# 谢谢
您好,这ios::sync_with_stdio(false)是什么意思啊?
优化cin和cout的输入输出
那这句话实际上用处大不大呀?
随便找个刷过的图论题,图论一般输入量大,你带上这句交两次,去掉再交两次,能看到效果还是挺明显的
kksk