题意:现在 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;
}