數論知識點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;
}
感謝_大太陽_提供的關於前三位計算的思路