Gym 101775A

题意

n个人,大于等于k个人可以建一个群,问能建成多少个不同的群。
结果对1000000007取模

思路

公式很简单

ans=C(n,k)+C(n,k+1)+......+C(n,n)

由于1N109. 3K105.
可以将公式转化为
ans=2n(C(n,0)+C(n,1)+......+C(n,k1))

在算组合数的时候,一开始考虑用卢卡斯求每次的组合数相加,但是这样的话复杂度会非常高。
考虑换组合数递推式,一边递推一边就可以累加

C(n,k+1)=C(n,k)(nk)/(k+1)

而其中的除法,取余等等操作会爆精度,这里就要采用逆元的方法变除法为乘法:
C(n,k+1)=C(n,k)(nk)inv(k+1)

乘法每一步取模
C(n,k+1)=C(n,k)(nk)modminv(k+1)modm

什么是逆元
当求解公式:(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;
}
原文地址:https://www.cnblogs.com/JinxiSui/p/9740516.html