组合数 —— 全

一、 一般组合数计算

若对 a n s ans ans取模,则无法保证下一步是否能被整除,所以只能对最后的结果取模

时间复杂度: O ( m ) O(m) O(m)
m o d mod mod的要求:无
有效范围: 1 < = n , m < = 60 1<=n,m<=60 1<=n,m<=60

//一般方法求C(n,m)最后取模。C(62,28)溢出。有效范围1<n,m<=60
ll C(int n, int m) {
	if(n<0 || m<0 || n<m) return 0;
	ll ans = 1;
	for (int i=1; i<=m; i++) {
		ans *= (n - m + i);
		ans /= i;
	}
	return ans % mod;
}

二、 杨辉三角

可以在过程中取模,但时间复杂度较高

公式: C ( n , m ) = C ( n − 1 , m ) + C ( n − 1 , m − 1 ) C(n,m)=C(n-1,m)+C(n-1,m-1) C(n,m)=C(n1,m)+C(n1,m1)
时间复杂度: O ( n m ) O(nm) O(nm)
m o d mod mod的要求:无
有效范围: 1 < = n , m < = 1000 1<=n,m<=1000 1<=n,m<=1000

//杨辉三角求C(n,m)
ll matrix[110][110];
ll C(int n, int m) {
	if(n<0 || m<0 || n<m) return 0;
	ll ans = 0;
	matrix[1][0] = matrix[1][1] = 1;
	for (int i=2; i<=n; i++) {
		int k = min(i, m);
		for(int j=0; j<=k; j++) {
			if (j==0 || j==i) matrix[i][j] = 1;
			else matrix[i][j] = (matrix[i-1][j] + matrix[i-1][j-1]) % mod;
		}
	}
	return matrix[n][m];
}

三、 逆元

公式: C ( n , m ) = n ! m ! × ( n − m ) ! C(n,m)=\cfrac{n!}{m!×(n-m)!} C(n,m)=m!×(nm)!n!
时间复杂度: O ( l o g p + n ) O(logp+n) O(logp+n)
m o d mod mod要求: p p p为质数,且 m < p m<p m<p G C D ( p , m ) = 1 GCD(p, m)=1 GCD(p,m)=1
有效范围: 1 < = n , m < = 1 0 6 1<=n,m<=10^6 1<=n,m<=106

ll fac[1<<21];
ll qpow(ll a, ll b, ll c) {	
	ll ret = 1; 
	a = a % c;
	while (b) {
		if (b & 1) ret = ret * a % c;
		a = a * a % c;
		b >>= 1;
	}
	return ret;
}
void init() { //先打出阶乘,节省时间
	fac[1] = fac[0] = 1;
	for (int i=2; i<=(1<<20); i++) {
		fac[i] = fac[i - 1] * i % mod;
	}
}
ll C(ll n, ll m) {	//逆元求组合数 
	return fac[n] * qpow(fac[m] * fac[n - m] % mod, mod - 2, mod) % mod;
}

线性预处理逆元:

const int maxf = 1e7 + 5;
ll fac[maxf], inv[maxf], invf[maxf];
void init(){
	fac[0] = inv[0] = invf[0] = 1;
	fac[1] = inv[1] = invf[1] = 1;
	for(int i=2; i<maxf; i++){
		fac[i] = fac[i-1] * i % mod;
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
		invf[i] = inv[i] * invf[i-1] % mod;	// 可用 fac[maxf] 反向求出 
	}
}
ll C(ll n, ll m){
	if(n<0 || m<0 || n<m) return 0;
	return fac[n] * invf[m] % mod * invf[n-m] % mod;
}

四、 分解因子

时间复杂度: O ( n ) O(n) O(n)
m o d mod mod的要求:无(可为合数)
有效范围: 1 < = n , m , p < = 1 0 6 1<=n,m,p<=10^6 1<=n,m,p<=106

const int maxp = 1e6 + 5;
int pnum = 0, ispri[maxp], pri[maxp];
inline void pri_table() {
	for(int i=2; i<maxp; i++) ispri[i] = 1;
	for(int i=2; i<maxp; i++) {
		if(ispri[i]) pri[pnum++] = i;	//	注意略有修改
		for(int j=0; i*pri[j]<maxp && j<pnum; j++) {
			ispri[i*pri[j]] = 0;
			if(i%pri[j]==0) break;
		}
	}
}
ll qpow(ll a, ll b, ll c) {
	ll ans = 1;
	a = a % c;
	while (b) {
		if(b & 1) ans = (ans * a) % c;
		b >>= 1;
		a = (a * a) % c;
	}
	return ans;
}
//获得n!中因子p的个数
ll getPNum(ll n, ll p) {
	ll ans = 0;
	while (n) {
		ans += n / p;
		n /= p;
	}
	return ans;
}

//利用因子求C(n,m)
ll CFactor(ll n, ll m) {
	if (m > n) return 0;
	ll ans = 1;
	for (int i=0; pri[i]!=0 && pri[i]<=n; i++) {
		ll a = getPNum(n, pri[i]);
		ll b = getPNum(m, pri[i]);
		ll c = getPNum(n - m, pri[i]);
		a -= (b + c);
		ans *= qpow(pri[i], a, mod);
		ans %= mod;
	}
	return ans;
}

五、 Lucas定理*

m o d mod mod的要求:质数
有效范围: 1 < = n , m , p < = 1 0 9 1<=n,m,p<=10^9 1<=n,m,p<=109

p p p较大、不进行预处理

ll qpow(ll a, ll b, ll c) {
	ll ans = 1;
	a %= c;
	while(b) {
		if(b & 1) ans = ans * a % c;
		b >>= 1;
		a = a * a % c;
	}
	return ans;
}
//x关于p的逆元,p为素数
ll inv(ll x, ll p) {
	return qpow(x, p - 2, p);
}
//组合数C(n, m) % p
ll C(ll n, ll m, ll p) {
	if(m > n)	return 0;
	ll up = 1, down = 1;//分子分母;
	for(int i = n-m+1; i <= n; i++)	up = up * i % p;
	for(int i = 1; i <= m; i++)	down = down * i % p;
	return up * inv(down, p) % p;
}
ll Lucas(ll n, ll m) {
	if(m == 0) return 1;
	return C(n % mod, m % mod, mod) * Lucas(n / mod, m / mod) % mod;
}

p p p较小、 p < 1 0 6 p<10^6 p<106、半预处理

ll fac[1<<20];
ll qpow(ll a, ll b, ll c) {	//快速幂
	ll ans = 1;
	a = a % c;
	while (b) {
		if (b & 1) ans = ans * a % c;
		b >>= 1;
		a = a * a % c;
	}
	return ans;
}
void init() { //先打出阶乘,节省时间
	fac[1] = 1;
	for (int i=2; i<=(1<<20); i++) {
		fac[i] = fac[i - 1] * i % mod;
	}
}
ll C(ll n, ll m) {	//逆元求组合数
	return fac[n] * qpow(fac[m] * fac[n - m] % mod, mod - 2, mod) % mod;
}
ll Lucas(ll n, ll m) {
	if(m == 0)	return 1;
	return C(n % mod, m % mod) * Lucas(n / mod, m / mod) % mod;
}

六、 拓展Lucas定理*

m o d mod mod的要求:无
有效范围: 1 < = n , m , p < = 1 0 9 1<=n,m,p<=10^9 1<=n,m,p<=109
n , m n,m n,m都很大且 p p p不为质数的时候,用此定理

ll pow(ll a, ll b, ll p) {
	ll ans = 1;
	a %= p;
	while(b) {
		if(b & 1) ans = ans * a % p;
		b >>= 1;
		a = a * a % p;
	}
	return ans;
}
// 求解ax+by=gcd(a, b),返回值为gcd(a, b)
ll extgcd(ll a, ll b, ll &x, ll &y) {
	ll d = a;
	if(b) {
		d = extgcd(b, a % b, y, x);
		y -= (a / b) * x;
	} else x = 1, y = 0;
	return d;
}
//求解a关于模上m的逆元,返回-1表示逆元不存在
ll mod_inverse(ll a, ll m) {
	ll x, y, d = extgcd(a, m, x, y);
	return d == 1 ? (m + x % m) % m : -1;
}
//计算n! mod pk的部分值  pk为pi的ki次方
//算出的答案不包括pi的幂的那一部分
ll Mul(ll n, ll pi, ll pk) {
	if(!n) return 1;
	ll ans = 1;
	if(n / pk) {
		for(ll i=2; i<=pk; i++) 	//求出循环节乘积
			if(i % pi) ans = ans * i % pk;
		ans = pow(ans, n / pk, pk); //循环节次数为n / pk
	}
	for(ll i=2; i<=n%pk; i++)
		if(i % pi) ans = ans * i % pk;
	return ans * Mul(n / pi, pi, pk) % pk;	//递归求解
}
//计算组合数C(n, m) mod pk的值 pk为pi的ki次方
ll C(ll n, ll m, ll p, ll pi, ll pk) {
	if(m > n) return 0;
	ll a = Mul(n, pi, pk), b = Mul(m, pi, pk), c = Mul(n - m, pi, pk);
	ll k = 0, ans;	//k为pi的幂值
	for(ll i=n; i; i/=pi) k += i / pi;
	for(ll i=m; i; i/=pi) k -= i / pi;
	for(ll i = n - m; i; i /= pi) k -= i / pi;
	ans = a * mod_inverse(b, pk) % pk * mod_inverse(c, pk) % pk * pow(pi, k, pk) % pk; //ans就是n! mod pk的值
	ans = ans * (p / pk) % p * mod_inverse(p / pk, pk) % p;	//此时用剩余定理合并解
	return ans;
}

ll Lucas(ll n, ll m, ll p) {
	ll x = p, ans = 0;
	for(ll i=2; i<=p; i++) {
		if(x % i == 0) {
			ll pk = 1;
			while(x % i == 0) pk *= i, x /= i;
			ans = (ans + C(n, m, p, i, pk)) % p;
		}
	}
	return ans;
}

参考:
http://www.cnblogs.com/fzl194/p/9095177.html
https://blog.csdn.net/u013269631/article/details/43485189

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值