今天在教室看了一上午的白書,發現其中的這一章很有意思,用各種神奇的解法來做一個沒有任何用的階乘取模。
首先我們直到如果p<n的話,取模的結果肯定是0對吧,如果p>n他又叫我們直接預處理出來,真的搞不懂。
然后就是他給的神奇方法:
“
在計數問題中,經常需要用到n!。在學完前面的介紹之后,有必要了解n!在mod p下的一些性質,下面我們假設p是素數,n!=a pe(a無法被p整除),並試圖求解a mod p和e。
e是n!能夠迭代整除p的次數,因此可以使用下面的式子計算。
n/p+n/p2+n/p3+......
這個結論很顯然,因為n/d和不超過n的能被d整除的正整數的個數相等。由於只需要
對於pt<=n的t進行計算,因此復雜度是O(logpn)。
接下來計算a mod p。首先計算n!=1*2*...*n的因數中不能被p整除的項的積。假設n=10,p=3,則
n!=1*2*3*4*5*6*7*8*9&10
=1*2*4*5*7*8*10≡1*2*1*2*1*2 *1(mod p)
從這個例子中可以看出,不能被p整除的項的積等於(p-1)!(n/p)*(n mod p)!。事實上,根據威爾遜定理,我們有(p-1)!≡-1(mod p)。因為除了1和p-1之外的其余的項都可以和各自的逆元相稱等於1。所以該項可以簡化成-1(n/p)*(n mod p)!。
接下來,計算可以被p整除的項的積。很簡單,就是1,2,3....n/p。因此,問題的范圍就由n縮小到了n/p。如果預處理出0<=n<p范圍中n! mod p的表,就可以在O(logpn)時間內算出答案。
int fact[MAX];//預處理階乘表 //分解n!=ape,返回a mod p。 int mod_fact(int n,int p,int& e) { e=0; if(n==0) return 1; int res=mod_fact(n/p,p,e); e+=n/p; if(n/p&2!=0) return res*(p-fact[n%p])%p; return res*fact[n%p]%p; }
”