二分快速幂与快读
二分快速幂
问题引入
现在要求计算 aⁿ % p 的值,其中a , n , p为整数,且0 < a, p < 10⁹ , 0 < n < 10¹⁸
我们朴素的想法一般都会想到暴力求解,直接循环计算aⁿ,最后再对p取模
long long ans = 1;
for(int i = 1 ; i <= p ; i++)
ans *= n;
ans %= p;
return ans;
这样有两个问题
其一:很可能溢出long long 的范围
其二:时间复杂度太高,进行10¹⁸次的运算,已经超出大部分计算机的能力
这里我们就要引入一种快速计算幂运算的算法--二分快速幂
首先我们要明确取模运算的性质
(用数论来理解其实就是证明a * b 和 [(a % p) * (b % p)]同余)
证明
证明如下:
设 a = k1 * p + q1 , b = k2 * p + q2
a ^ b = C * p + q1 * q2
(a * b) % p = q1 * q2
(a % p) * (b % p) = q1 * q2
证毕
那么这和我们的二分快速幂又有什么关系呢?
假如我们现在要手算3¹⁰
我们可以转化为 9⁵ ,再转化为9 * 81² ,再转化为 9 * (81²)¹ (81²的计算交给计算机就好)
如果我们要求在p范围内,我们每次转化模上p就能保证结果的正确性
那么代码如何实现呢
inline long long quickPower(int a, int b, int p)
{
long long ans = 1;
while (b)
{
if (b & 1)
{
ans = ans * a % p;
}
a = a * a % p;
b /= 2;
}
return ans;
}
快读
在IO型的OJ题中,我们经常要进行数据读入,如果数据量特别大,我们传统的数据读取就会消耗大量的时间
对C++来说,cin不需要格式化控制,非常方便,但是它的效率其实是低于我们C语言中的scanf的
但是就算我们用scanf来代替cin,在面对较多的数据时,也不是一种很好的方法
这个时候我们就要进行优化(快读针对的是int类型)
我们数据方式的效率从低到高为
cin < scanf < getchar < fread
这样我们可以利用getchar或者fread针对int类型进行一种优化
getchar版本
inline int read()
{
int s = 0, w = 1;//s 数值 w符号
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w *= -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
s = (s << 1) + (s << 3) + (ch ^ 48);
//该行代码的解释为 数字字符串转为对应的数字大小 而我们可以用位运算来进行优化
//一般我们写为 s = s * 10 + ch - 48
//而s * 10 = s << 1 + s << 3
//ch - 48 由于48二进制为110000 57 为 111001 ch - 48的值就相当于与48做异或
ch = getchar();
}
return s * w;
}
其实getchar已经很快了,但是我们还可以利用fread进一步优化
注意:fread在某些平台环境下遇到读取stdin遇到换行符不会停止,最好用第一种
代码如下:
char buf[1 << 21], * p1 = buf, * p2 = buf;
inline char gc()
{
if (p1 == p2)//p1 == p2说明数据流中的数据读完了,可以进行下一次读取了
{
int cnt = fread(buf, sizeof(char), 1 << 21, stdin);
p2 = (p1 = buf) + cnt;
}
return *(p1++);
}
inline int read()
{
int s = 0, w = 1;//s 数值 w符号
char ch = gc();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w *= -1;
ch = gc();
}
while (ch >= '0' && ch <= '9')
{
s = (s << 1) + (s << 3) + (ch ^ 48);
ch = gc();
}
return s * w;
}
快写
既然有快读,当然也有快写,同样是按位处理。
inline void write(int x)
{
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar((x % 10) ^ 48);
}