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