排队[HNOI2012][Codevs1994]
2016-06-01 10:37
260 查看
题目描述 Description
某中学有 n 名男同学, m 名女同学和两名老师要排队参加体检。他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻,那么一共有多少种排法呢?(注意:任意两个人都是不同的)输入描述 Input Description
输入文件只有一行且为用空格隔开的两个非负整数 n 和 m ,其含义如上所述。输出描述 Output Description
仅包含一个非负整数,表示不同的排法个数。注意答案可能很大。样例输入 Sample Input
样例输入 11 1
样例输入 2
7 3
样例输出 Sample Output
样例输出 112
样例输出 2
220631040
数据范围及提示 Data Size & Hint
对于 30% 的数据 n≤100,m≤100对于 100% 的数据 n≤2000,m≤2000
分析
首先讨论特殊情况。当 n=0 时,若 m=1 则 ans=2 。若 m=2 则 ans=8。否则 ans=0 。
当 m>n+3 时 ans=0 (必定会有两个女生相邻)
一般情况。
我们把 n 个同学和 1 个老师站成一列,这样有 (n+1)! 种可能的情况,再讨论另一个老师站的地方。
1 :两个老师不站在一起,此时这个老师共有 n 个可能的位置。剩下的 m 个女生将会站在每两个同学(老师)中间的位置或队头,队尾共 n+3 个位置,根据排列我们知道共有 Amn+3 种可能,根据乘法原理我们知道两个老师不站在一起的合法方案数共 n∗Amn+3 种可能
2 :两个老师站在一起,这两个老师的位置共有 2 个可能(老师的前后顺序有两种可能),此时两个老师之间必定站着一个女生,共 m 种可能,剩下的 m−1 个女生将会站在每两个同学(老师)中间的位置或队头,队尾共 n+2 个位置,根据排列我们知道共有 Am−1n+2 种可能,根据乘法原理我们知道两个老师站在一起的合法方案数共 2∗m∗Am−1n+2 种可能
综上,所有的可能情况有 (n+1)!∗(n∗Amn+3+2∗m∗Am−1n+2) 种。因为数据大,所以要打高精度
代码
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; const LL Mod = 100000000LL; struct bignum{ LL a[4000]; int len; bignum(int num=0){ memset(a,0,sizeof a); a[0] = num; len = 1; } bignum operator = (const bignum&h){ memcpy(a,h.a,sizeof h.a); len = h.len; return *this; } bignum operator = (const int h){ memset(a,0,sizeof a); a[0] = h; len = 1; return *this; } bignum operator * (const int h){ bignum c; for(int i=0;i<len;++i) c.a[i] = a[i]*h; for(int i=0;i<len;++i){ c.a[i+1] += c.a[i]/Mod; c.a[i] %= Mod; } if(c.a[len]) c.len = len+1; else c.len = len; return c; } bignum operator * (const bignum h){ bignum c; c.len = len+h.len; for(int i=0;i<len;++i) for(int j=0;j<h.len;++j) c.a[i+j] += a[i]*h.a[j]; for(int i=0;i<c.len;++i){ c.a[i+1] += c.a[i]/Mod; c.a[i] %= Mod; } if(!c.a[c.len-1]) --c.len; return c; } bignum operator + (const bignum h){ bignum c; c.len = max(len,h.len); for(int i=0;i<c.len;++i){ c.a[i] += a[i]+h.a[i]; c.a[i+1] += c.a[i]/Mod; c.a[i] %= Mod; } if(c.a[c.len]) ++c.len; return c; } void display(){ printf("%lld",a[len-1]); for(int i=len-2;i>=0;--i) printf("%08lld",a[i]); } }ans(1); int n,m; int main(){ scanf("%d%d",&n,&m); if(!n){ if(m == 1){printf("2");return 0;} else if(m == 2){printf("8");return 0;} else {printf("0");return 0;} } if(m > n+3){printf("0");return 0;} for(int i=2;i<=n+1;++i) ans = ans*i; bignum tmp1(n); for(int i=1;i<=m;++i) tmp1 = tmp1*(n+4-i); if(!m){ ans = ans*tmp1; ans.display(); return 0; } bignum tmp2(2*m); for(int i=1;i<=m-1;++i) tmp2 = tmp2*(n+3-i); ans = ans*(tmp1+tmp2); ans.display(); return 0; }