[bzoj2729][HNOI2012]排队 题解 (排列组合 高精)
Description
某中学有 n 名男同学,m 名女同学和两名老师要排队参加体检。他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻,那么一共有多少种排法呢?(注意:任意两个人都是不同的)
Input
只有一行且为用空格隔开的两个非负整数 n 和 m,其含义如上所述。
对于 30%的数据 n≤100,m≤100
对于 100%的数据 n≤2000,m≤2000
Output
输出文件 output.txt 仅包含一个非负整数,表示不同的排法个数。注意答案可能很大。
Sample Input
1 1
Sample Output
12
码题5分钟,推导两小时
对于“不能相邻”,考虑采用插空法
首先对于无限制的男生有$A_n^n$种排列
这时产生了n+1个空档,插入2个老师还要$*A_{n+1}^2$
现在有n+3个空档,放m个女生有$A_{n+3}^m$种
此时的所有情况都满足条件
但只考虑了男生隔开老师的情况
而女生也可以隔开老师
考虑捆绑play(雾)法
让两个老师一个女生卡在一起(卢老爷我错辽)
这种组合放入男生队伍中有$n+1$种位置
选着一个女生有m种
放剩下的$A_{n+2}^{m-1}$
老师排列方式$A_2^2=2$
男生排列方式$A_n^n$
$ANS=A_n^n*A_{n+1}^2*A_{n+3}^m+(n+1)*2*A_n^n*m*A_{n+2}^{m-1}$
连高精乘低精都不会打了真是耻辱
#include<cstdio> #include<iostream> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int N=100005; int n,m,num1[N],num2[N],tot[N]; /*int intlen(int x) { return (int)log10(x)+1; } void turn(int x,int num[]) { int l=0; while(x) { int now=x%10; num[++l]=now; x/=10; } num[0]=l; //reverse(num+1,num+l+1); }*/ void mult(int x,int num[]) { int k=0; for(int i=1;i<=num[0];i++) { int tmp=num[i]*x+k; num[i]=tmp%10; k=tmp/10; } while(k)num[++num[0]]=k%10,k/=10; } void sum(int a1[],int a2[],int res[]) { int j=1,x=0; while(j<=a1[0]||j<=a2[0]) { res[j]=a1[j]+a2[j]+x; x=res[j]/10; res[j]%=10; j++; } res[j]=x; if(!res[j])j--; res[0]=j; } void print(int a[]) { for(int i=a[0];i;i--)printf("%d",a[i]); puts(" "); } int main() { scanf("%d%d",&n,&m); //turn(n,num1);turn(m,num2); num1[0]=num1[1]=1; for(int i=1;i<=n;i++)mult(i,num1); for(int i=n+1;i>=n;i--)mult(i,num1); for(int i=n+3;i>=n-m+4;i--)mult(i,num1); num2[0]=num2[1]=1; mult(n+1,num2); mult(m,num2); mult(2,num2); for(int i=n+2;i>=n+2-(m-1)+1;i--)mult(i,num2); for(int i=1;i<=n;i++)mult(i,num2); //print(num1);print(num2); sum(num1,num2,tot); print(tot); return 0; }
兴许青竹早凋,碧梧已僵,人事本难防。