來到數論王國,一切都得重新開始啦
模運算,顧名思義,對一個數進行取模運算,在大數運算中,模運算是常客
如果一個數太大無法直接輸出,或者是不需要直接輸出,可以對他進行取模縮小數值在輸出
我們習慣這樣寫:a%b=c
取模的結果一般滿足於0<=c<=m-1,m一般是題目給的資料範圍
而對於取模操作,滿足一下的性質:
(a+b)%c=((a%c)+(b%c))%c;
(a-b)%c=((a%c)-(b%c))%c;
(a*b)%c=((a%c)*(b%c))%c;
而除法就不行了,除法如果進行上面的取模操作是錯誤的,
那怎麼辦呢?對於除法取模操作,在同餘和逆元中會展開講解,這裡我李某人先講講所謂的基本數論操作
那取模運算我們就算講完了,接下來我們來講講所謂的快速冪運算
冪運算我們老生常談,而快速冪就為了快速解決冪次運算的,當一個數很大的時候進行冪次運算的時候,一般兩種情況:要麼炸,要麼超時
我們可以採取一下兩種辦法來解決這個問題:
一、
我們可以很容易想到,先算a^2,在a^2^2,一直算到結束,我們可以用遞迴分治的思想來解決
long long fastpow(long long a,long long b) { if(b==0) return 1; long long temp=(fastpow(a,b/2));//分治遞迴了 if(b%2==1)//如果是奇數次,還要多乘一個 return (temp*temp*a); else //偶數次正好乘完 return (temp*temp); }
這種方法是較為好想到的;
我們再來介紹另外一種方法--其實也是老生常談了,只不過換了一個形式--我們在多重揹包中所見的二進位制優化
將a^11分解成a^8,a^2,a^1的乘積
而a^2=a^1*a^1,a^4=a^2*a^2;
a^8同理,都是二的倍數,產生的a^i都是倍數關係,一步一步遞迴就可以了
另一方面,我們在分解像n這樣的數字的時候,我們也可以採取二進位制的思想,我們很清楚 ,對於二進位制來說,二進位制的前一位數字的值都比低一位的數字的值多2倍;
舉個例子來講;
我們用10進位制來表示11;
那對應的2進位制就是1011;
就可以表示成2^3+2^1+2^0
還有一個需要考慮的是,對於二進位制中的0,我們可以採用二進位制中的位運算的方法來跳過:
(1)n&1,如果不是1,就直接跳了就?了;
(2)n>>=1,和n<<=1的功能是相反的,那個是向左位運算,相當於每次進行二次方,而這次是向右運算,相當於每次縮小2次方
long long fastpow(long long a,long long b) { long long base=a;//底數 long long res=1;//結果 while(b) { if(b&1)//如果這一位是,就要對結果進行累乘了 res=(base*res); base=(base*base);//遞推運算,a^2->a^4->a^8->a^16 b=b>>=1;//進行位移運算 } return res; }
而對於快速冪取模
在快速冪進行取模操作,直接對a^n取模,和先對a取模在做冪次操作的效果是相同的;
所以快速冪取模有這樣的性質:a^n%b=(a%b)^n%b;
講了這麼多,拿個題練練手
題目連結:https://www.luogu.com.cn/problem/P1226
題目的兩種解法:
一、遞迴分治法:
1 #include<bits/stdc++.h>//快速冪分治遞迴法 2 using namespace std; 3 long long a,b,p; 4 long long fastpow(long long a,long long b) 5 { 6 if(b==0) 7 return 1; 8 long long temp=(fastpow(a,b/2)); 9 if(b%2==1) 10 return (temp%p*temp%p*a%p)%p; 11 else 12 return (temp%p*temp%p)%p; 13 } 14 int main() 15 { 16 std::ios::sync_with_stdio(false); 17 cin>>a>>b>>p; 18 long long ans=fastpow(a,b); 19 ans=ans%p; 20 printf("%lld^%lld mod %lld=%lld\n",a,b,p,ans); 21 return 0; 22 }
快速冪二進位制優化:
1 #include<bits/stdc++.h> 2 using namespace std; 3 long long a,b,p; 4 long long ans; 5 long long fastpow(long long a,long long b) 6 { 7 long long base=a; 8 long long res=1; 9 while(b) 10 { 11 if(b&1) 12 res=(base%p*res%p)%p; 13 base=(base%p*base%p)%p; 14 b=b>>=1; 15 } 16 return res; 17 } 18 int main() 19 { 20 std::ios::sync_with_stdio(false); 21 cin>>a>>b>>p; 22 long long ans=fastpow(a,b); 23 ans=ans%p; 24 printf("%lld^%lld mod %lld=%lld\n",a,b,p,ans); 25 return 0; 26 27 }
而對於快速冪來講,還有一個很重要的應用,就是快速冪矩陣運算,這個嘛,賣個關子,明天再講,哈哈哈