快速乘法

在乘法计算 (a×b)%p(a\times b) \% p 的结果时, a×b264a\times b\ge2^{64}(即unsigned long long范围) ,如果直接算,显然会溢出,这时,就可以用上快速乘法了 .

普通版

类似于快速幂,原理与快速幂类似,复杂度为 O(log2b)O(\log_2 b) .

原理:
b=(bnbn1...b2b1b0)2b = (b_nb_{n-1}...b_2b_1b_0)_2
ans=a×b=a×(2nbn+2n1bn1+...+21b1+20b0)ans=a\times b=a\times(2^nb_n+2^{n-1}b_{n-1}+...+2^1b_1+2^0b_0)
bkb_k 为1时,aa 才会乘上 2k2^k ,结果 ansans 加上 a×2ka\times 2^ka×2k=a<<ka\times 2^k=a<<k .

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进行运算,时间复杂度为 O(1)O(1),不过有时会使用不了,出现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中定义,只能进行运算,而不能进行输入输出 .

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章