網上的求逆方法普遍長這樣
a[1]=1;
for(int i=2;i<=n;++i) a[i]=(mod-(mod/i))%mod*a[mod%i]%mod;
下面介紹一種簡單且萬能的
pr[1]=1;
for(int i=2;i<=n;++i) pr[i]=pr[i-1]i%mod;
b[n]=qpow(pr[n],mod-2);
for(int i=n-1;i;–i) b[i]=b[i+1](i+1)%mod;
b[1]=1;
for(int i=2;i<=n;++i) b[i]=b[i]*pr[i-1]%mod;
講解開始
逆元的定義
在(modp)意義下,i×i−1≡1,則i與i−1互爲逆元
使用逆元的意義:爲了保證精度,維護整數問題(即數論問題)的過程中,不能除,於是利用與原數積在模意義下爲1
的數,來替代除法
an=n的逆元求法
常用,但是常數有點大
推導:
模p意義下,求i的逆元p=ki+r(k=⌊ip⌋,r=pmodi)ki+r≡0(modp)i−1r−1(ki+r)≡0(modp)i−1+kr−1≡0(modp)i−1≡−kr−1(modp)由於r=pmodi,則r<i,求i時r已求出,可以遞歸求解邊界1−1=1
Code
a[1]=1;
for(int i=2;i<=n;++i) a[i]=(mod-(mod/i))%mod*a[mod%i]%mod;
任意數列的逆元求法
常數又小,適用範圍又廣
不學他學誰?
推導
對於{an}(通常是an=n的數列),記pri=j=1∏iaj,第一遍,invn=prnp−2(即prn的逆元)≡∏i=1nai1invi=invi+1×ai+1,遞推下去第二遍,invi=invi×pri−1時間復雜度O(N)
Code
pr[1]=1;
for(int i=2;i<=n;++i) pr[i]=pr[i-1]*i%mod;
b[n]=qpow(pr[n],mod-2);
for(int i=n-1;i;--i) b[i]=b[i+1]*(i+1)%mod;
b[1]=1;
for(int i=2;i<=n;++i) b[i]=b[i]*pr[i-1]%mod;
Tips
- 注意邊界:
法一中inv[1]
要賦初值
法二中最後一層循環由於pr[0]=0
,無法更新inv[1]
,那麼手動賦1
- 注意求逆元的範圍
想清楚就行了