<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 */