hdu-5894-hannnnah_j’s Biological Test(Lucas定理+乘法逆+组合数)

hannnnah_j’s Biological Test


Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)

Problem Description
hannnnah_j is a teacher in WL High school who teaches biology.

One day, she wants to test m students, thus she arranges n different seats around a round table.

In order to prevent cheating, she thinks that there should be at least k empty seats between every two students.

hannnnah_j is poor at math, and she wants to know the sum of the solutions.So she turns to you for help.Can you help her? The answer maybe large, and you need to mod 1e9+7.
 
Input
First line is an integer T(T≤1000).
The next T lines were given n, m, k, respectively.
0 < m < n < 1e6, 0 < k < 1000
 
Output
For each test case the output is only one integer number ans in a line.
 

Sample Input
2
4 2 6
5 2 1
 
Sample Output
0

5


题意:

给一个m个学生在长度为n的环形摆放的座位安排座位,要求相邻两个学生座位之间的距离至少是k,交换两个学生后的摆放和原来一样视为同种方案,求总方案数%1e9+7。


题目链接:hannnnah_j’s Biological Test


解题思路:

先从n个座位选一个出来,符合条件的方案,必须有k*m个空座位,那么接下来只需从n-1+k*m个座位中选出m-1个同

学的座位即可,第一个位置有n种选法,但因为学生都是一样的,同种方案会被不同的学生选到,即选了m次,删去重

复答案为n*C(n-k*m-1, m-1)/m % (1e9+7)。

计算组合数取模可以套用Lucas模板,在模mod下,a/b %mod 与 a*(b)的逆 同余数,用inv(m,mod)求解模mod下m的

逆(Lrj 训练指南122)。


代码:

#include<cstdio>

using namespace std;
typedef long long LL;
const int N = 1e6+5;
const LL mod = 1e9+7;
LL fac[N+10];

LL pow_mod(LL x, LL n)//快速幂取模
{
    LL sum = 1;
    while(n) {
        if(n & 1) sum = sum * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return sum;
}

void init()//初始化所需要的阶乘
{
    fac[0] = 1;
    for(int i = 1;i <= N;i++)
        fac[i] =fac[i-1] * i % mod;
}

LL Lucas(LL n, LL m)//Lucas定理,计算C(n,m)%mod
{
    LL ans = 1;
    while(n && m) {
        LL a = n % mod, b = m % mod;
        if(a < b) return 0;
        ans = ans * fac[a] * pow_mod(fac[b]*fac[a-b] % mod, mod-2) % mod;
        n /= mod;
        m /= mod;
    }
    return ans;
}

void exd_gcd(LL a, LL b, LL& d, LL& x, LL& y)//拓展欧几里得解方程ax+by=gcd(a,b);
{
    if(!b) {
        d = a;
        x = 1;
        y = 0;
    }
    else {
        exd_gcd(b, a%b, d, y, x);
        y -= x*(a/b);
    }
}
LL inv(LL a, LL n)//计算模n下a的逆元,如果不存在,返回-1
{
    LL d, x, y;
    exd_gcd(a, n, d, x, y);
    return d == 1? (x+n)%n : -1;
}

int main()
{
    int T;
    LL n, m, k;
    init();
    scanf("%d",&T);
    while(T--) {
        scanf("%I64d%I64d%I64d",&n, &m, &k);
        if(m == 1) {
            printf("%I64d\n",n);
            continue;
        }
        printf("%I64d\n",(n * Lucas(n-k*m-1, m-1) % mod) * inv(m, mod) % mod);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值