HDU 5894 hannnnah_j’s Biological Test(组合数学 逆元)

题意:现在 m个考生人需要坐在有n个座位的圆桌上。你需要安排位置,使得任意两个考生之间相距至少k个位置。桌子有编号,考生a和b交换位置视作一种方案,问有多少方案,mod 1e9+7。(0 < m < n < 1e6, 0 < k < 1000)


思路:

定好m个人 相邻人之间k个座位 剩下就剩n-(m+1)*k个座位
剩下座位去插m个不同的盒子==就等价n个相同的球放m个不同的盒子
然后组合数出来了
乘n的话是枚举座位,除m是去掉枚举第一个座位的时候,剩下人相邻的座位相对不变的情况


除法取模要用到逆元 
费马小定理(p为素数):有a^(p - 2) ≡ 1/a (mod p)


当p较小n和m较大时,可以用Lucas定理:

p是素数,那么:C(n, m) % p = C(n / p, m / p) * C(n % p, m % p) % p; 


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;

ll qmod(ll x, ll p)
{
	ll ans = 1;
	while(p)
	{
		if(p%2) ans = ans*x%mod;
		x = x*x%mod;
		p /= 2;
	}	
	return ans;
}

ll C(ll n, ll m)
{
	if(m > n) return 0;	
	ll ans = 1;
	for(int i = 1; i <= m; i++)	//可以预处理阶乘直接求逆元
		ans = ans*(n+i-m)%mod*qmod(i, mod-2)%mod;
	return ans;
}

int main(void)
{
    int t;
    cin >> t;
    while(t--)
    {
    	ll n, m, k;
    	scanf("%lld%lld%lld", &n, &m, &k);
    	if(m == 1) printf("%lld\n", n);
    	else printf("%lld\n", C(n-k*m-1, m-1)*n%mod*qmod(m, mod-2)%mod);
	}
    return 0;
}


Lucas模板:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1000000 + 5;
LL a[maxn], n, m, k;
LL mod = 1e9 + 7;

void init() {
    a[0] = 1;
    for (LL i = 1; i < maxn; i++) {
        a[i] = a[i - 1] * i;
        a[i] %= mod;
    }
}

LL qpower(LL x, LL p) {
    LL ans = 1;
    x %= mod;
    while (p) {
        if (p & 1) (ans *= x) %= mod;
        (x *= x) %= mod;
        p >>= 1;
    }
    return ans;
}

LL C(int n, int m) {
    if (m > n) return 0;
    return a[n] * qpower(a[m] * a[n - m] % mod, mod - 2) % mod;
}

LL Lucas(LL n, LL m) {
    if (m == 0) return 1;
    return Lucas(n / mod, m / mod) * C(n % mod, m % mod) % mod;
}

int main() {
    int T;
    cin >> T;
    init();
    while (T--) {
        scanf("%lld%lld", &n, &m);
        LL res = Lucas(n, m);
        printf("%lld\n", res);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值