推出公式:C(n,s)*2^(n-s)
计算C(n,s)=A(n,s)/s!时,s!会超出范围造成精度损失。又因为除法不能运用同余数定理,故想到了利用逆元来解题。
可把逆元类比于倒数。
费马小定理(Fermat’s little theorem):假如p是指数,且gcd(a,p)=1,那么 a^(p-1)(mod p)≡1。
即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。
由上方的公式推导出:a*a(p-2)≡1(mod p)
a关于p的逆元就是a^(p-2),但注意这个定理要求a,p互质!
所以C(n,s) % p = A(n,s)%p * (s!^p-2)
具体代码如下:
#include <iostream>
using namespace std;
#define ll long long
const int mod = 1e9+7;
ll quick_pow(ll n, ll k)
{
ll res = 1;
while(k)
{
if(k & 1) res = res * n % mod;
n = n * n % mod;
k >>= 1;
}
return res % mod;
}
int main()
{
int n, s, t;
cin >> n >> s;
for(int i = 0; i < n ;i++) cin >> t;
if(s > n) cout << 0 << endl;
else
{
ll ans1 = 1, ans2 = 1, ans3 = 1;
for(int i = n, j = 1; j <= s; i--, j++) ans1 = ans1*i%mod;
for(int i = 1; i <= s; i++) ans2 = ans2*i%mod;
ans2 = quick_pow(ans2, mod-2);
ans3 = quick_pow(2, n-s);
cout << (ans1*ans2%mod*ans3%mod) << endl;
}
return 0;
}