用處
一個能快速求\(n^{k}\%p\)的算法,在數論題目里可以用來模擬多次重復的運算,是數論的基礎算法。
模板題
【題目描述】
求\(n^{k}\%p\)的值。
【輸入格式】
一行,包含三個數\(n,k,p\)
【輸出格式】
一行,輸出\(n^{k}\%p\)的值。
樣例
樣例輸入
2 6 23
樣例輸出
18
樣例說明
\(2^{6}=64,64\%23=18\)
數據范圍與提示
對於全部數據,\(1\le n,k\le 10^{15},1 \leq p \leq 10^{9}\)
解析
這是一道基本的數論題,不用數論算法就會超時(簡單來說,不用數論思想就是暴力模擬)。
暴力模擬的時間復雜度為\(O(k),\)但\(k\)最大可以取\(10^{15},\)肯定會超時。
快速冪是一種時間復雜度為\(O(log2(k))\)的算法,這代表\(k\)甚至可以取到\(\LARGE2^{\normalsize2\times10^{8}}(2^{200000000})\)。
可以說是無限了,C++中很難有這樣的時間和內存夠這種數運算了\((FFT\)也做不到呀\()\)。
為什么說快速冪時間復雜度為為\(O(log2(k)),\)這因為它的算法模式。
利用\(2\)進制來分治,將運算次數縮小到二分的次數。
比如數據
2 29 657
\(n=2,k=29,p=657\)
將\(k\)轉化成\(2\)進制得\(11101,ans=1\)
每次\(n\)都會平方並進行取模,然后將\(k \div 2\)
因為\(n^{k}=(n\times2)^{k \div 2}\)
但是\(C\)語言中默認向下取整,所以\(5\div 2=2,\)但是還有個\(1\)該怎么辦呢?
所以為什么轉化為\(2\)進制,當\(k\%2=1,\)代表有一個\(1\)是多出來的,要先把這\(1\)個\(a\)先乘上。
具體代碼實現如下
#include<cstdio>
#include<cstring>
using namespace std;
int n,k,p;
int ksm(int a,int b,int inf)
{
int ans=1;a%=inf;
while(b)
{
if(b&1/*b%2==1*/)ans=ans*a%inf;
a=a*a%inf;b>>=1;/*b=b/2;*/
}
return 0;
}
int main()
{
scanf("%d%d%d",&n,&k,&p);
printf("%d\n",ksm(n,k,p));
}
練習題 NOIP2013提高組day1【轉圈游戲】
題目描述
\(n\)個小伙伴\((\)編號從\(0\) 到\(n-1)\)圍坐一圈玩游戲。按照順時針方向給\(n\)個位置編號,從\(0\)到\(n-1\)。最初,第\(0\)號小伙伴在第\(0\)號位置,第\(1\)號小伙伴在第\(1\)號位置\(,……,\)依此類推。
游戲規則如下:每一輪第\(0\)號位置上的小伙伴順時針走到第\(m\)號位置,第\(1\)號位置小伙伴走到第\(m+1\)號位置\(,……,\)依此類推,第\(n−m\)號位置上的小伙伴走到第\(0\)號位置,第\(n-m+1\)號位置上的小伙伴走到第\(1\)號位置\(,……,\)第\(n-1\)號位置上的小伙伴順時針走到第\(m-1\)號位置。
現在,一共進行了\(10^{k}\)輪,請問\(x\)號小伙伴最后走到了第幾號位置。
輸入格式
輸入共\(1\)行,包含\(4\)個整數\(n,m,k,x,\)每兩個整數之間用一個空格隔開。
輸出格式
輸出共\(1\)行,包含\(1\)個整數,表示\(10^{k}\)輪后\(x\)號小伙伴所在的位置編號。
樣例
樣例輸入1
10 3 4 5
樣例輸出1
5
數據范圍與提示
對於\(30\%\)的數據\(,0 < k < 7;\)
對於\(80\%\)的數據\(,0 < k < 10^{7};\)
對於\(100\%\)的數據\(,1 < n < 10^{6},0 < m < n,1 \leq x \leq n,0 < k < 10^9。\)