1. 程式人生 > >數論知識點1——快速冪取模-LightOJ 1282

數論知識點1——快速冪取模-LightOJ 1282

數論知識點1——快速冪取模

1.快速冪的思路

普通的冪運算操作是時間複雜度是O(n),這個速度的確很快,但是當n比較大的時候,普通的寫法就會超時,我們考慮將ab這種形式,我們把b(也就是指數),看成二進位制的,比如a22次方,22代表的是10110,那麼我們可以把a22 => 轉換為 a16  * a4  *  a2 ,很明兩者是等價的,因為16 + 4 + 2 == 22。這個表示式的意思就是我們在計算a22的時候,可以使用二進位制的形式來運算,遇到1則對結果進行累乘,始終對指數進行累乘,具體看程式碼

LL pow_mod(LL x, LL n)
{
    LL res = 1;
    while(n > 0)
    {
        if(n & 1)
            res = res * x;   //遇到1對結果進行累乘
        x = x * x;    //指數累乘
        n >>= 1;
    }
    return res;
}

這個程式碼的問題就是,會溢位,不管速度是否加快但是結果都是一樣的,所以才有了冪取模這麼一個說法,但是我們如果在最後取模還是要溢位,,所以一邊運算一邊取模,這裡用到的知識點是(a * b) % c = (a % c * b % c) % c,所以有了下面這個完整的程式碼

LL pow_mod(LL x, LL n, LL mod)
{
    LL res = 1;
    while(n > 0)
    {
        if(n & 1)
            res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

這個題目不僅要求ab的後3位,還有就是前3位,後3位就是剛剛的快速冪取模運算,把mod寫成1000,即可。

前三位,需要用到一個知識點,那就是給定一個數n,這個數可以表示成10a,這個a一般是小數,那麼nk就等於10ak,這裡把ak分為兩部分,整數和小數部分,即x和y,那麼nk = 10x * 10y,由於x是整數,那麼很明顯他是用來指定位數的,因為10x肯定是一個10, 100, 10000...之類的數字,也就是說10y才是這個數字的有效部分,我們只要求出10y,然後乘上100最終結果就是我們想要的。

因為n = 10a  所以 a = log10(n),10y就是小數部分,我們用函式fmod(x, 1),返回x的小數部分

,然後乘上100即可

fmod返回的是y的值,所以必須計算10y才是真實值,所以直接使用102 * 10y 即pow(10, 2 + y);

程式碼如下:

LL pow_mod(LL x, LL n, LL mod)
{
    LL res = 1;
    while(n > 0)
    {
        if(n & 1)
            res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}

int main()
{
    int t;
    int n, k;
    int Case = 1;
    cin >> t;
    while(t--)
    {
        cin >> n >> k;
        int res = pow(10.0, 2.0 + fmod((double)k * log10((double)n), 1)); 
        cout << "Case " << Case++  << ": " << res << " " << setfill('0') << setw(3) << pow_mod(n, k, 1000) << endl;
    }
    return 0;
}

感謝_大太陽_提供的關於前三位計算的思路