今天在教室看了一上午的白书,发现其中的这一章很有意思,用各种神奇的解法来做一个没有任何用的阶乘取模。
首先我们直到如果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; }
”