hdu 5894 hannnnah_j’s Biological Test 排列组合

题目


题意:

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


解法:

首先必须有n>=m+k*m,否则答案是0。

现在先考虑m个人的相对关系(即不考虑座位编号和人对应,先只考虑人与人间的间隔,即位置分配),
先在n个座位中抽走k*m个,每个人和k个座位绑在一起,一旦这个人有了座位,他后面自动添加这k个座位。
现在因为是确定相对关系,那么第一个人必有一个座位,那么剩下n-1-k*m个座位,在这之中选m-1个作为给其余人座,即C(n-1-k*m,m-1),现在相对位置就形成了。下面开始对号入座。

由于这第一个人有n中选择,选择[1,n]这n个座位编号,所以将答案先乘以n,即n*C(n-1-k*m,m-1)。不过这样做绝对比真实答案要大,因为是个环形,如果第一个人坐到靠后的位置,那么后面的人就会坐到编号较小的位置,所以就算的有重复,因为当初相对位置为第一的人实际不是总是对应最小编号。

想想 ,已经计算中的所有方法中,相对位置中的每个人实际成为其座位编号最小的人的机会是相等的,故将答案除以m,即n*C(n-1-k*m,m-1)/m。

另 :对于m=1,特判,因为1个人没有间隔。


代码:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;

#define all(x) (x).begin(), (x).end()
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
#define mes(a,x,s)  memset(a,x,(s)*sizeof a[0])
#define mem(a,x)  memset(a,x,sizeof a)
#define ysk(x)  (1<<(x))
typedef long long ll;
typedef pair<int, int> pii;
const int INF =0x3f3f3f3f;
const ll mod=1e9+7;
const int maxn=1e6+0.5;
ll n,m,K,S[maxn+5];

ll inv(ll a,ll m)
{
    if(a == 1)  return 1;
    return inv(m%a,m)*(m-m/a)%m;
}

void pre()
{
    S[0]=1;
    for1(i,maxn)
    {
        S[i]=S[i-1]*i%mod;
    }
}

int main()
{
   std::ios::sync_with_stdio(false);
   pre();
   int T;cin>>T;
   while(T--)
   {
       cin>>n>>m>>K;
       if(m==1)
       {
           cout<<n<<endl;
           continue;
       }
       if(  n<m+K*m)
       {
           cout<<0<<endl;
           continue;
       }
       //cal :  n*C(n-1-k*m,m-1)/m;
       ll ret=S[n-1-K*m]*inv(S[m-1]*S[n-K*m-m]%mod,mod)%mod;
       ret=ret*n%mod *inv(m,mod)%mod;

       cout<<ret<<endl;
   }
   return 0;
}
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;

#define all(x) (x).begin(), (x).end()
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
#define mes(a,x,s)  memset(a,x,(s)*sizeof a[0])
#define mem(a,x)  memset(a,x,sizeof a)
#define ysk(x)  (1<<(x))
typedef long long ll;
typedef pair<int, int> pii;
const int INF =0x3f3f3f3f;
const ll mod=1e9+7;
const int maxn=1e6+0.5;
ll n,m,K,S[maxn+5];



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

ll inv(ll a,ll m)
{
   return qb(a,m-2);
}

void pre()
{
    S[0]=1;
    for1(i,maxn)
    {
        S[i]=S[i-1]*i%mod;
    }
}

int main()
{
   std::ios::sync_with_stdio(false);
   pre();
   int T;cin>>T;
   while(T--)
   {
       cin>>n>>m>>K;
       if(m==1)
       {
           cout<<n<<endl;
           continue;
       }
       if(  n<m+K*m)
       {
           cout<<0<<endl;
           continue;
       }
       //cal :  n*C(n-1-k*m,m-1)/m;
       ll ret=S[n-1-K*m]*inv( S[n-K*m-m]*S[m-1]%mod,mod)%mod;
       ret=ret*n%mod *inv(m,mod)%mod;

       cout<<ret<<endl;
   }
   return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值