組合公式
c(n,m)=p(n,m)/m!=n!/((n-m)!*m!)
c(n,m)=c(n,n-m)
c(n,m)=c(n-1,m)+c(n-1,m-1)
歐拉定理
歐拉定理,(也稱費馬-歐拉定理)是一個關於同余的性質。歐拉定理表明,若n,a為正整數,且n,a互質,則:

φ(n)表示1~n中與n互質的數的個數
看一個基本的例子。令a = 3,n = 5,這兩個數是互素的。比5小的正整數中與5互素的數有1、2、3和4,所以φ(5)=4(詳情見[歐拉函數])。計算:a^{φ(n)} = 3^4 =81,而81= 80 + 1 Ξ 1 (mod 5)
這個定理可以用來簡化冪的模運算。比如計算7^{222}的個位數,實際是求7^{222}被10除的余數。7和10[[互素]],且φ(10)=4。由歐拉定理知7^4Ξ1(mod 10)。所以7^{222}=(7^4)^55*(7^2)Ξ1^{55}*7^2Ξ49Ξ9 (mod 10)。
后話:歐拉定理其實是費馬小定理的推廣
先看費馬小定理: 假如p是質數,且(a,p)=1,那么 a^(p-1)
p是質數時,φ(p)=p-1,那么a^φ(p)≡1(mod p)
乘法逆元
(a/b) mod p=(a*b^(p-2)) mod p
條件:p是素數,gcd(b,p)=1,a%b=0
定義: 滿足a*k≡1 (mod p)的k值就是a關於p的乘法逆元。
(PS:p一定是個素數才能對a有乘法逆元(除1),特別注意:當p是1時,對於任意a,k都為1)
為什么要有乘法逆元呢?
當我們要求(a/b) mod p的值(a/b一定是個整數),且a很大,無法直接求得a/b的值時,我們就要用到乘法逆元。
我們可以通過求b關於p的乘法逆元k,將a乘上k再模p,即(a*k) mod p。
其結果與(a/b) mod p等價。
怎樣求乘法逆元?
目前只會歐拉定理,留個坑,待填
根據歐拉定理: 假如p是質數,且(b,p)=1,則φ(p)=(p-1),那么 b^(p-1) ≡1(mod p) 。
b*b^(p-2) ≡1(mod p),所以b^(p-2)就是b的逆元,即k=b^(p-2)
所以(a/b) mod p=(a*b^(p-2)) mod p
這里是博主瞎比寫的,正版的在下面↓↓↓
歐拉定理 (證明+在求逆元上的應用)
歐拉定理(又稱費馬-歐拉定理):已知a和n為正整數,並且a和p互素,則a^phi(n) ≡ 1(mod n)。
證明:
設集合Z = {X1, X2, X3, .... , Xphi(n)},其中Xi (i = 1, 2, .. phi(n))表示第i個不大於n與n互質的數。
考慮集合S = {a*X1(mod n), a*X2(mod n), ... ,a*Xphi(n) (mod n) },則集合Z = S;
1) 因為a和n互質,Xi和n也互質,所以a*Xi 也與n互質。所以對任意一個Xi,a*Xi (mod n)一定是Z里面的元素;
2)對於任意Xi, Xj, 如果Xi != Xj,則a*Xi(mod n) != a*Xj(mod n);
所以S = Z;
那么 (a*X1*a*X2*...*a*Xphi(n))(mod n) ---------------------------------------------------- (1)
= (a*X1(mod n)* a*X2(mod n)* ... *a*Xphi(n) (mod n)) (mod n)
= (X1* X2* X3* .... * Xphi(n)) (mod n) ------------------------------------------------------ (2)
式(1)整理得 [a^phi(x) * (X1* X2* X3* .... * Xphi(n))] (mod n)
與(2)式一同消去 (X1* X2* X3* .... * Xphi(n)),即得 a^phi(x) ≡ 1 (mod n);
逆元 :(b/a) (mod n) = (b * x) (mod n)。 x表示a的逆元。並且 a*x ≡ 1 (mod n)
因為a^phi(x) ≡ 1 (mod n),所以x可以表示為a^(phi(n) - 1)。
當n是素數時 phi(n)=n-1, x表示為a^(n-2)。
Lucas定理
A、B是非負整數,p是質數。AB寫成p進制:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]。
則組合數C(A,B)與C(a[n],b[n])*C(a[n-1],b[n-1])*...*C(a[0],b[0]) modp同余
即:Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p)
For non-negative integers m and n and a prime p, the following congruence relationholds:
-
表示的是同余,表示兩個數對p取余,余數相同。
where
and
are the base p expansions of m and n respectively.
將m划分成m0個1,m1個p,m2個p^2,……
將n划分成n0個1,n1個p,n2個p^2……
C(m,n)的總共的取法就是( 在m0個里面取n0個的方案數 * m1個里面取n1個的方案數 * m2個里面取n2個的方案數 ……)
fzu2020

直接用Lucas
#include<cstdio> #include<iostream> using namespace std; #define ll long long ll n,m,p; ll pow_m(ll a,ll k,ll p) { ll ans=1; ll tmp=a%p; while(k) { if(k&1)ans=ans*tmp%p; tmp=tmp*tmp%p; k>>=1; } return ans; } ll C(ll n,ll m,ll p) { if(m>n)return 0; ll a=1,b=1; for(int i=1;i<=m;i++) { a=a*(n+i-m)%p; b=b*i%p; } return a*pow_m(b,p-2,p)%p; } ll Lucas(ll n,ll m,ll p) { ll ans=1; while(n&&m) { ans=ans*C(n%p,m%p,p)%p; n/=p; m/=p; } return ans; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&p); printf("%lld\n",Lucas(n,m,p)); } return 0; }
hdu3037

考慮加多一顆樹,這樣的話當加的樹放了k(0<=k<=m)個beans時,原本的n顆樹上放的beans數量之和就等於m-k(<=m),滿足題目的要求 ,也降低了計算的難度。
則題目要求的是a1+a2+......an+an+1=m(0<=ai<=m,1<=i<=n+1) 式1
解有多少組。
考慮把問題轉換成,求a1+a2+......an+an+1=m+n+1(1<=ai<=m+1,1<=i<=n+1) 式2
解有多少組。
因為式1的每組解,對於每個ai,都加上1的話,就是式2的一組解。
對於式2的求解:
考慮有m+n+1個Beans排成一列,則它們中恰好有m+n個間隔,在m+n個間隔中選擇n個各插入一塊木板,則把這些Beans分成n+1部分,每部分的值對應到每個ai,就是式2的一組解。而在m+n個間隔中選擇n個,則是求組合數的問題了,p<=10^5且為質數,則可用Lucas定理求。
p<10^5,要進行階乘預處理,用C2的方法來算組合數,否則會超時
#include<cstdio> #include<iostream> using namespace std; #define ll long long #define maxn 100010 ll n,m,p; ll fact[maxn]; ll pow_m(ll a,ll k,ll p) { ll ans=1; ll tmp=a%p; while(k) { if(k&1)ans=ans*tmp%p; tmp=tmp*tmp%p; k>>=1; } return ans; } ll C2(ll n,ll m,ll p)//p<10^5 { if(m>n)return 0; return fact[n]*pow_m(fact[m]*fact[n-m]%p,p-2,p); } ll Lucas(ll n,ll m,ll p) { ll ans=1; while(n&&m) { ans=ans*C2(n%p,m%p,p)%p; n/=p; m/=p; } return ans; } void init(ll p) { fact[0]=1; for(int i=1;i<=p;i++)fact[i]=fact[i-1]*i%p; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%lld%lld%lld",&n,&m,&p); init(p); printf("%lld\n",Lucas(n+m,n,p)); } return 0; }
nefu625

題意:給出一個n*m大的花園,求出從左上角到右下角的路徑數目(路徑單調)。
先采取暴力分解,然后快速冪即可。分解的思想很巧妙。
非降路徑數。從(a,b)到(m,n)共有C(m+n-a-b,m-a)種.
所以本題的解應該是C(m+n-2,m-1) (數學太差,窩也不知道它是怎么出來的,留坑
由於C(x,y)=x!/(y!*(x-y)!),這里我們可以將x!分解素因子,並保存記錄下來,同樣的方法記錄后面兩個,由於x!必然能夠整除(y!*(x-y)!),所以后面兩個數有的因子,x!比然有,只需要將他們的因子的指數相加減,就能得到最后結果的素因子分解的情況,然后最后使用快速冪取模,就能得到最后的結果。
注意:如何進行素因子分解?
首先要打表將所有的素因子求出來,這里有是將n!進行素因子分解,假設想要求出其中有多少個5,這里是有技巧的。
假設n=200,那么因子5的個數=200/5+40/5+8/5=49,怎么得到的呢?200中5的倍數有40個,這40個數中其中是25的倍數的有8個,所以還能分解出8個5,這8個數中還有一個是125的倍數,還能分解出一個5,就這樣一直循環下去,就能求出指數的值。
#include<cstdio> #include<vector> #include<iostream> using namespace std; #define ll long long #define maxn 200010 ll n,m,p; vector<int>pri; bool prime[maxn]; void init() { pri.clear(); prime[0]=prime[1]=false; for(int i=2;i<maxn;i++) { if(!prime[i]) { pri.push_back(i); for(int j=i+i;j<maxn;j+=i) prime[j]=true; } } // printf("%d %d\n",pri.size(),pri[pri.size()-1]);//測試本題數據n<200000,則只需要17000個素數 } ll pow_m(ll a,ll k,ll p) { ll ans=1; ll tmp=a%p; while(k) { if(k&1)ans=ans*tmp%p; tmp=tmp*tmp%p; k>>=1; } return ans; } ll work(ll n,ll su) { ll ans=0; while(n) { ans+=n/su; n/=su; } return ans; } ll C3(ll n,ll m,ll p)// 0<n,m<10^6, 0<p<10^9 { if(m>n)return 0; ll ans=1; for(int i=0;pri[i]<=n;i++)//如果出現數組越界的錯誤,則說明素數不夠用,init中再多開出一點素數來,或者換個姿勢求素數 { ll x=work(n,pri[i]); ll y=work(n-m,pri[i]); ll z=work(m,pri[i]); ans=ans*pow_m(pri[i],x-(y+z),p)%p; } return ans; } int main() { init(); int T; scanf("%d",&T); while(T--) { scanf("%lld%lld%lld",&n,&m,&p); printf("%lld\n",C3(n+m-2,m-1,p)); } return 0; }
