排座位
题目链接: http://exercise.acmcoder.com/online/online_judge_ques?ques_id=3370&konwledgeId=40
解题思路: 我们可以先把m个人排好,方案数m!,然后考虑在两个人之间插入空格,假设在第一个人前面插入x0个空格,在第1个人和第2个人之间插入x1个空格。。。
那么我们有: x0+x1+x2+....+xm=n-m (除去m个人后,只剩下n-m个位置)。若要符合题意的要求使得,任何两个人都不相邻,则,xi>=1 i=1,2,3,...,m-1。
由于第一个人和最后一个人是可以环形相邻的,所以需要分别考虑:
- x0==0,那么需要xm>=1
- xm==0,那么需要x0>=1
- x0>=1 && xm>=1
先考虑第三种情况,即x0+x1+x2+...+xm=n-m,这个可以用隔板法求出排列数。例如x1+x2+x3=5,可以把5写成5=1+1+1+1+1,现在需要在4个"+"号中选择两个,使得最后是三个数相加,所以是C(4-1, 3-1)。对于我们的问题有ans=C(n-m-1, m) (因为共m+1个数)
对于第1,2这两种情况,相当于x1+x2+...+m=n-m,解法同上,ans=C(n-m-1, m-1)
最后把所有的结果加一起就好。最终的结果就是:m!(C(n-m-1,m) + 2*C(n-m-1, m-1))
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 const int MAXN = 100005; 6 const LL MOD7 = 1e9+7; 7 8 LL n,m; 9 LL pow(LL a, LL b, LL MOD) 10 { 11 LL ans = a%MOD; 12 LL res=1LL; 13 while (b) 14 { 15 if (b&1) res=res*ans%MOD; 16 ans=ans*ans%MOD; 17 b>>=1; 18 } 19 return res; 20 } 21 22 LL inverse(LL a, LL p) 23 { 24 return pow(a,p,p-2); 25 } 26 27 void solve(LL n, LL m) 28 { 29 LL ans=1LL; 30 for (int i=1;i<=m-1;++i) ans=ans*(n-m-1-i+1)%MOD7; 31 LL res=0LL; 32 res=(ans*m*2%MOD7+ans*(n-m*2)%MOD7)%MOD7; 33 printf("%lld\n",res); 34 } 35 36 int main() 37 { 38 #ifndef ONLINE_JUDGE 39 freopen("test.txt","r",stdin); 40 #endif // ONLINE_JUDGE 41 int Case; 42 scanf("%d",&Case); 43 while (Case--) 44 { 45 scanf("%lld%lld",&n,&m); 46 if (n<2*m) printf("0\n"); 47 else if (m==1LL) printf("%lld\n",n); 48 else solve(n,m); 49 } 50 return 0; 51 }