A - Chat Group ( 乘法逆元 + 组合数递推公式 )

A - Chat Group ( 乘法逆元 )

It is said that a dormitory with 6 persons has 7 chat groups ^_^. But the number can be even larger: since every 3 or more persons could make a chat group, there can be 42 different chat groups.

Given N persons in a dormitory, and every K or more persons could make a chat group, how many different chat groups could there be?

Input

The input starts with one line containing exactly one integer T which is the number of test cases.

Each test case contains one line with two integers N and K indicating the number of persons in a dormitory and the minimum number of persons that could make a chat group.

  • 1 ≤ T ≤ 100.
  • 1 ≤ N ≤ 109.
  • 3 ≤ K ≤ 105.

Output

For each test case, output one line containing "Case #x: y" where x is the test case number (starting from 1) and y is the number of different chat groups modulo 1000000007.

Example Input

1
6 3

Output

Case #1: 42

题意:一个宿舍中又n个人,最少k(k >= 3)个人就可以建一个讨论组,问最多可以建多少个不同的讨论组。

思路:求组合数的和,因为涉及除法取余,所以要求逆元来解题。

虽然之前看到过有关逆元的知识,但是一直没有弄明白逆元的应用。嗯~~挖下的坑终于把自己给坑了。这次认栽!!

最终的结果是:C(n,k)+C(n,k+1)+.......+C(n,n)  =  2^n - ( C(n,0) + C(n,1) + C(n,2) + ......+C(n,k-1) )

(a / b)%mod = a % mod *(b关于模mod的逆元);

扩展知识 : 组合数的递推公式 C_{n}^{k} = \frac{n-k+1}{k}*C_{n}^{k-1} 

注: 一般是很大的数做乘除法再去模一个数的时候需要用乘法逆元。如该模的数是1e9+7, 直接用费马小定理求逆元inv(a) = qpow( a, mod-2 )。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int mod = 1e9+7;
ll exgcd( ll a, ll b, ll &x, ll &y )
{
    if ( b==0 ) {
        x = 1;
        y = 0;
        return a;
    }
    ll re = exgcd(b,a%b,y,x);
    y -= x*(a/b);
    return re;
}

ll getni( ll a )
{
    ll mod = 1e9+7,x,y;
    ll gcd = exgcd( a,mod,x,y );
    if ( gcd==1 ) {
        if ( x>=0 ) return x%mod;
        else return x%mod+mod;
    }
    else {
        return -1;
    }
}

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

int main()
{
    ll listt,n,k,ji=1;
    cin >> listt;
    while ( listt-- ) {
        cin >> n >> k;
        ll ans = qpow(2,n) - 1;
        ll now = 1;
        for ( int i=1; i<k; i++ ) {
            now = (((n-i+1)*now)%mod*qpow(i,mod-2))%mod;
            ans = (ans-now)%mod;
        }
        if ( ans<0 ) ans = ans%mod+mod;
        else ans = ans%mod;
        cout << "Case #" << ji++ << ": " << ans << endl;
    }

    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值