快速乘法

在乘法計算 (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中定義,只能進行運算,而不能進行輸入輸出 .

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