一.逆元(inv)
当求解公式:(a/b)%m 时,因b可能会过大,会出现爆精度的情况,所以需变除法为乘法:
设c是b的逆元,则有b*c≡1(mod m);则(a/b)%m = (a/b)*1%m = (a/b)*b*c%m = a*c(mod m);
即a/b的模等于a*b的逆元的模,逆元就是这样应用的;
二.求逆元的方法:
1.扩展欧几里得算法
2.费马小定理inv(a)=pow(2,mod-2)%mod;
3.逆元线性筛:
const int mod = 1e; const int maxn = 10005; int inv[maxn]; inv[1] = 1; for(int i = 2; i < 10000; i++) inv[i] = inv[mod % i] * (mod - mod / i) % mod;
4.阶乘逆元线性筛
inv[N]=qpow(fac[N],mod-2); for(ll i=N-1;i>=0;i--) inv[i]=(inv[i+1]*(i+1))%mod;
三.组合数的求法
1.o(n^2)递推 (适用于n较小的情况的快速写法)
C[n][m]=C[n-1][m-1]+C[n-1][m];
2.o(n)处理阶乘和逆元,运用公式求解:o(n)
C(n,m)=n!/(m!*(n-m)!)
typedef long long ll; const int mod=1e9+7; const int N=1e6+10; ll fac[N],inv_fac[N]; ll qpow(ll a,ll b){ ll res=1,tmp=a; while(b){ if(b&1) (res*=tmp)%=mod; (tmp*=tmp)%=mod; b>>=1; } return res; } void inin(){ fac[1]=fac[0]=1; for(int i=2;i<N;i++) fac[i]=1ll*fac[i-1]*i%mod; inv_fac[N-1]=qpow(N-1,mod-2); for(int i=N-2;i>=0;i--) inv_fac[i]=(inv_fac[i+1]*(i+1))%mod; } ll C(int n,int m){//0<=m<=n if(m==0||m==n) return 1; ll res=((fac[n]*inv_fac[m]%mod)*inv_fac[n-m])%mod; return res; }
3.递推求解组合数,适用于n较大的情况,同时在递推的时候,相邻两项有递推关系
即运用公式来进行一系列变换:C(n,m)=n!/(m!*(n-m)!)
例如:
C[n+1][m+1]=C[n][m]*(n+1)/(m+1); C[n+1][m+1]=C[n][m]*(n+1)*inv[m+1];