题意
n个人,大于等于k个人可以建一个群,问能建成多少个不同的群。
结果对1000000007取模
思路
公式很简单
由于. .
可以将公式转化为
在算组合数的时候,一开始考虑用卢卡斯求每次的组合数相加,但是这样的话复杂度会非常高。
考虑换组合数递推式,一边递推一边就可以累加
而其中的除法,取余等等操作会爆精度,这里就要采用逆元的方法变除法为乘法:
乘法每一步取模
什么是逆元
当求解公式:(a/b)%m 时,因b可能会过大,会出现爆精度的情况,所以需变除法为乘法:
设c是b的逆元,则有b*c≡1(mod m);
则(a/b)%m = (a/b)*1%m = (a/b)*b*c%m = a*c(mod m);
即a/b的模等于a*b的逆元的模;
逆元就是这样应用的;
逆元:
ll inv(ll a){
return mul(a, mod-2); // mul()是快速幂
}
AC代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
#include <cstdlib>
#define mst(a) memset(a,0,sizeof a);
#define eps 1e-8
#define _sign(x) ((x)>eps?1:((x)<-eps?2:0))
#define FRER() freopen("in.txt","r",stdin)
#define FREW() freopen("out.txt","w",stdout)
#define mod(a,m) ((a)%(m)+(m))%(m)
using namespace std;
typedef unsigned long long ll;
const ll mod = 1000000007;
ll mul(ll a, ll k){
ll ret = 1;
for(;k;k>>=1){
if(k&1) ret = (ret*a)%mod;
a = (a*a)%mod;
}
return ret;
}
ll inv(ll a){
return mul(a, mod-2);
}
int main()
{
ll n, m, k;
int T;
scanf("%d",&T);
int kase = 0;
for(int kase = 1; kase <= T; kase++){
scanf("%lld%lld",&n, &k);
ll sum = 1, pre = 1;
for(ll i = 1; i < k; i++){
pre = pre*(n-i+1)%mod*inv(i)%mod;
sum = (sum + pre)%mod;
}
ll pp = mul(2,n);
printf("Case #%d: %lld
",kase, (pp-sum+mod)%mod);
}
return 0;
}