Atcoder - 156D 組合數 / Lucas定理

<Atcoder - 156D>

https://atcoder.jp/contests/abc156/tasks/abc156_d

題意:

有n束花,需要從其中挑出若干束,但是不能挑a束或b束,問多少種選法。

即求:ans = (2 ^ n) - C(n, a) - C(n, b) - 1

思路:

<法一> 組合數

AC代碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL Mod = 1e9 + 7;
LL n, a, b, ans;

LL qwe(LL a, LL n) { //快速冪
	LL res = 1LL;
	while(n) {
		if(n & 1) res = res * a % Mod;
		a = a * a % Mod;
		n >>= 1LL;
	}
	return res % Mod;
}

LL C(LL n, LL m) { //組合數
	if(m < n - m) m = n - m;
	LL x = 1LL;
	for(LL i = m + 1; i <= n; i++) x = x * i % Mod;
	LL y = 1LL;
	for(LL i = 1; i <= n - m; i++) y = y * i % Mod;
	return x * qwe(y, Mod - 2) % Mod;
	//除法取模只需將除法變乘法即可, 也就是用被除數去乘以除數的乘法逆元
	//求乘法逆元用費馬小定理: 當模Mod爲素數, x的逆元爲qwe(x, Mod - 2)
}

int main() {
	cin >> n >> a >> b;
	ans = ((qwe(2LL, n) - C(n, a) - C(n, b) - 1) % Mod + Mod) % Mod;
	cout << ans << endl;
    return 0;
}

/*
c(n, 1) + ... + c(n, n)
c(n, 0) + ... + c(n, n) = 2 ^ n
=> ans = (2 ^ n) - C(n, a) - C(n, b) - 1
減去一束也不選的情況
*/

<法二> Lucas定理

AC代碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL p = 1e9 + 7;
LL n, a, b, ans;

LL qwe(LL a, LL n) { //快速冪
	LL res = 1LL;
	while(n) {
		if(n & 1) res = res * a % p;
		a = a * a % p;
		n >>= 1LL;
	}
	return res % p;
}

LL inv(LL x) { //逆元
	return qwe(x, p - 2) % p;
}

LL C(LL n, LL m) { //組合數
	LL res = 1LL;
	for(LL i = 1, j = n; i <= m; i++, j--) {
		res = res * j % p;
		res = res * inv(i) % p;
	}
	return res % p;
}

LL lucas(LL a, LL b) {
	if(a < p && b < p) return C(a, b) % p;
	return C(a % p, b % p) * lucas(a / p, b / p) % p;
}

int main() {
	cin >> n >> a >> b;
	ans = (qwe(2LL, n) - (lucas(n, a) % p + lucas(n, b) % p) % p - 1 + p) % p;
	cout << ans << endl;
    return 0;
}

/*
n和m較大, 且p爲素數時
Lucas定理作用: c(n, m) mod p
C(n, m) % p = C(n / p, m / p) * C(n % p, m % p) % p
=> Lucas(n, m) % p = Lucas(n / p, m / p) * C(n % p, m % p) % p
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章