快速冪-競賽演算法用法-和-例題詳解---Hayden's Blog.
在ACM競賽中經常會遇到各種的求冪問題,這種問題一般情況下對應的數字都會很大,所以快速冪就會經常用到,快速冪很好用,就算沒有弄清楚快速冪的原理,只要有模板很快就可以解題。
先說幾個比較重要的公式:
模運算與基本四則運算有些相似,但是除法例外。其規則如下:
(a + b) % p = (a % p + b % p) % p (1) // 這個公式等下寫題目就會用到的
(a – b) % p = (a % p – b % p) % p (2)
(a * b) % p = (a % p * b % p) % p (3)
(a^b) % p = ((a % p)^b) % p(4) //快速冪的公式
結合律:
((a+b) % p + c) % p = (a + (b+c) % p) % p (5)
((a*b) % p * c)% p = (a * (b*c) % p) % p (6)
交換律:
(a + b) % p = (b+a) % p (7)
(a * b) % p = (b * a) % p (8)
分配律:
((a +b)% p * c) % p = ((a * c) % p + (b * c) % p) % p (9)
重要定理:
若a≡b (% p),則對於任意的c,都有(a + c) ≡ (b + c) (%p);(10)
若a≡b (% p),則對於任意的c,都有(a * c) ≡ (b * c) (%p);(11)
若a≡b (% p),c≡d (% p),則 (a + c) ≡ (b + d) (%p),(a – c) ≡ (b – d) (%p),
(a * c) ≡ (b * d) (%p),(a / c) ≡ (b / d) (%p); (12)
先上幾個最常用的模板:
1.快速乘法 : a*b
int qmul(int a,int b){// 根據資料範圍可選擇long long
int ans=0;
while(b){
if( b&1)ans+=a;//按位與完成位數為1的判斷
b>>=1;a<<=1;//位運算代替/2和*2
}
return ans;
}
2.快速乘法取模 (a*b)%mod
int qmul_mod(int a,int b,int mod){
int ans=0;
while(b){
if ((b%=mod)&1)ans+=a%=mod;//這裡需要b%=mod 以及a%=mod
b>>=1;a<<=1;
}
return ans%mod; //ans也需要對mod取模
}
3.快速冪 a^b
int qpow(int a,int b){
if(a==0)return 0;//這是個坑,校賽被坑過,很多網上的實現都沒寫這一點
int ans=1;
while(b){
if(b&1)ans*=a;//和快速乘法的區別
b>>=1;a*=a;//區別,同上
}
return ans;
}
4.快速冪取模 (a^b)%mod
int qpow_mod(int a,int b,int mod){
if(a==0)return 0;
int ans=1;
while(b){
if(b&1)ans=(ans%mod)*(a%mod);//如果確定資料不會爆的話,可寫成 ans*=a%=mod;
b>>=1;a*=a%=mod;//等價於a=(a%mod)*(a%mod),且將一個模運算通過賦值代替,提高了效率
}
return ans%mod;//資料不會爆的話,這裡的%運算會等價於第5中不斷重複的 ans%mod
}
例題 POJ 1995
我是在VJ上直接拉過去的 就不放連結了
題目意思:
前面的廢話省略,現在有一個遊戲,每個人可以有兩個數字Ai 和 Bi
現在要你把所以人的Ai^Bi求出來 然後全部加起來 再取模mod 結果輸出就可以了
就是:(A1^B1+A2^B2+A3^B3+……………………An^Bn)%mod;
把上面那個式子的結果輸出就可以了 資料範圍不大 快速冪一遍過
但是這裡用到了前面說的公式
(a + b) % p = (a % p + b % p) % p
這個對於多項也是成立的,然後利用上面的公式
將每個人的Ai 和 Bi快速冪取模 然後相加 全部加起來之後再取一次模 就拿到結果了
ok
上一下我的程式碼:
// POJ 1995
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
ll qpow_mod(ll a,ll b,ll mod){
if(a==0) return 0;
ll ans = 1;
while(b){
if(b&1) ans = (ans%mod)*(a%mod);
b>>=1;
a*=a%=mod;
}
return ans%mod;
}
int main(int argc, char const *argv[])
{
int T;
int m,n;
ll sum;
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
scanf("%d",&T);
while(T--){
sum = 0;
scanf("%d",&m);
scanf("%d",&n);
for(int i=0;i<n;i++){
int a,b;
scanf("%d %d",&a,&b);
sum = sum + qpow_mod(a,b,m);
}
printf("%lld\n",sum%m);
}
return 0;
}
很尷尬 用了141ms 雖然不是特別快 但是也不慢了至少是Ac了
好了 快速冪就講到這裡
然後我覺得 畢竟是在搞競賽的嘛,我認為準備模板啊這些的很正常,
而且在我現在實力不是特別的雄厚的時候,準備一些模板我覺得是可取的,雖然說不能完全的理解這個模板,但是多少懂一點,然後會用,在慢慢用的過程中自己在去領悟這個演算法的真諦
之前打杭電的多校聯賽,清華大學的team090這隻隊伍四次比賽三次AK,有一個很難的一題沒有A出來,很佩服他們,和大神之間有很大差距,我們可以向他們學習,至少比昨天的自己更加優秀
遭遇了不理解,枯燥,TLE,WA………這些之後還在堅持的走下去,等看到了Ac,內心的成就感,我想這就是我們學演算法的一個小意義所在吧 !好了,要去吃飯了。一起加油!