[数论][组合数学]微信群

题目描述

众所周知,一个有着6个人的宿舍可以有7个微信群(^_^,别问我我也不知道为什么),然而事实上这个数字可以更大,因为每3个或者是更多的人都可以组建一个群,所以6个人最多可以组建42个不同的群。
现在,已知一间宿舍有N个人,并且每至少K个人都可以组建一个微信群,那么他们最多可以组建多少个不同的微信群?

输入

一行两个整数N和K,表示宿舍中的人数和最少能够组建微信群的人数

输出

一行一个整数,即最多能组建多少个不同的微信群,由于这个数字很大,请输出对10^9+7求余后的结果

样例输入

6 3

样例输出

42

提示

对于30%的数据,3<=N<=10^3
对于60%的数据,3<=N<=10^6
对于100%的数据,3<=N<=10^9,3<=K<=10^5

思路:要求C(n,k)+C(n,k+1)+...+C(n,n),转化为求2^n-(C(n,0)+C(n,1)+...+C(n,k-1))
求组和数有这么几种方式:
1.C(n,m)=n!/(n-m)!/m! (逆元求组合数——n,m不太大,而T又很大时,预处理)
2.C(n,m)=C(n,m-1)*(n-m+1)/m (递推求组合数——n较大m不太大,而T又不大时)
3.C(n,m)=C(n-1,m)+C(n-1,m-1)(递推求组合数——n,m不太大时,而T又很大时,预处理,不过用处似乎不大?)
组合数的性质有:
1.C(n,m)=C(n,n-m)
2.C(n,0)+C(n,1)+...+C(n,n-1)+C(n,n)=2^n
 
AC代码:
#include <iostream>
#include<cstdio>
#define mod 1000000007
typedef long long ll;
using namespace std;

ll n,k;

ll qpow(ll a,ll b){
  ll ret=1;
  while(b){
    if(b&1) ret=(ret*a)%mod;
    a=(a*a)%mod;
    b>>=1;
  }
  return ret;
}

int main()
{
    scanf("%lld%lld",&n,&k);
    ll ans=qpow(2,n);
    ll sum=1; ll tmp=1;
    for(ll i=1;i<=k-1;i++){
        tmp=tmp*(n-i+1)%mod*qpow(i,mod-2)%mod;
        sum=(sum+tmp)%mod;
    }
    ans=(ans-sum+mod)%mod;//防止出现负数
    printf("%lld
",ans);
    return 0;
}
转载请注明出处:https://www.cnblogs.com/lllxq/
原文地址:https://www.cnblogs.com/lllxq/p/9038243.html