在乘法计算 的结果时, (即unsigned long long范围) ,如果直接算,显然会溢出,这时,就可以用上快速乘法了 .
普通版
类似于快速幂,原理与快速幂类似,复杂度为 .
原理:
设
则
当 为1时, 才会乘上 ,结果 加上 , .
LL fast_mul(LL a, LL b, LL p)
{
LL ans = 0;
a %= p;
while (b)
{
if (b&1)
ans = (ans+a) % p;
b >>= 1;
a <<= 1;
a %= p;
}
return ans;
}
dls版
先说一句dlsnb.
可以使用long double进行运算,时间复杂度为 ,不过有时会使用不了,出现CE .
大佬解释long double的输出情况. 也可以直接看下面copy的结论 .
MSVC下long double为8字节,而GCC编译后的程序不论在Linux下还是在Windows下都为12字节。不过仔细看可以发现,虽然GCC生成的程序占用了12字节,但其只用到了前10字节(后2字节不论怎样赋值其内容都不会发生改变),也就是说GCC的long double实际上是10字节(80bit)的。
除此之外,还可以发现,当使用scanf("%lf")时,不论变量是什么类型的,都是按8字节存储的(即按double类型存储的),而使用scanf("%Lf"),则是按10字节存储的(即按long double类型存储的)。由于在MSVC下double = long double,所以不论怎么混用,结果都是正确的。而在Linux下,我们发现,当存储的long double为真正的long double时(使用scanf("%Lf")),只能使用%Lf输出结果,而long double内存储的内容为double时,只能使用输出double的格式化字符串输出。
所以猜想在GCC MinGW下,可能就像在Linux下存储的double而强制输出long double那样会输出为0一样,存储的内容为double,而MSVC将其认定为long double输出,所以最终结果为0。
LL fast_mul(LL a, LL b, LL p)
{
if (p <= 1e9)
return a*b % p;
if (p <= 1000000000000LL)
return (((a*(b>>20)%p) << 20) + (a*(b&((1<<20)-1)))) % p;
LL d = (LL)floor(a * (long double)b/p+0.5);
LL res = (a*b - d*p) % p;
if (res < 0)
res += p;
return res;
}
__int128版
这时,你只需要在你的宏定义里加上下面一句 :
#define LL __int128
不过,由于__int128不在C/C++的IO中定义,只能进行运算,而不能进行输入输出 .