排列组合,高精度
A. 排队
内存限制:128 MiB 时间限制:1000 ms 标准输入输出
题目类型:传统 评测方式:文本比较
题目描述
某中学有 n 名男同学,m 名女同学和两名老师要排队参加体检。他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻,那么一共有多少种排法呢?(注意:任意两个人都是不同的)
输入格式
只有一行且为用空格隔开的两个非负整数 n 和 m,其含义如上所述。
对于 30%的数据 n≤100,m≤100
对于 100%的数据 n≤2000,m≤2000
输出格式
输出文件 output.txt 仅包含一个非负整数,表示不同的排法个数。注意答案可能很大。
样例
样例输入
1 1
样例输出
12
析:经过数奥大佬MXD的开导,想到了本题一种较清晰的思路。
老师之前要么是男生,要么是女生,所以分两种情况:
1.老师之间是男生,所以组合共有 A(n,n)*A(n+1,2)*A(n+3,m)
2.老师之间是女生,则将两个老和一个女生看成一个人,则组合有 A(n,n)*A(n+1,1)*A(n+2,m-1)*A(2,2)*C(m,1)
将结果相加即可
注意,本题是高精度乘低精度,可以用更简单的方式计算
而不用将每个数变成高精度,会超时!!
void workk(int x,int f[],int l)
{
for(re i=1;i<=l;i++)
f[i]*=x;
for(re i=1;i<=l;i++)
{
if(f[i]>=10)
{
f[i+1]+=f[i]/10;
f[i]%=10;
}
}
while(f[l+1])
{
++l;
f[l+1]+=f[l]/10;
f[l]%=10;
}
f[0]=l;
}
#include<bits/stdc++.h>
#define N 1000010
#define re register int
using namespace std;
int l1,l2,n,m;
int a[N],b[N],c[N];
void get_n()
{
for(re i=2; i<=n; i++)
{
for(re j=1; j<=l1; j++)
a[j]=a[j]*i;
for(re j=1; j<=l1; j++)
if(a[j]>=10)
{
a[j+1]+=a[j]/10;
a[j]=a[j]%10;
}
while(a[l1+1])
{
l1++;
a[l1+1]+=a[l1]/10;
a[l1]=a[l1]%10;
}
}
a[0]=l1;
for(re i=0;i<=a[0];i++)
b[i]=a[i];
}
void workk(int x,int f[],int l)
{
for(re i=1;i<=l;i++)
f[i]*=x;
for(re i=1;i<=l;i++)
{
if(f[i]>=10)
{
f[i+1]+=f[i]/10;
f[i]%=10;
}
}
while(f[l+1])
{
++l;
f[l+1]+=f[l]/10;
f[l]%=10;
}
f[0]=l;
}
void in1()//A(n,n)*A(n+1,2)*A(n+3,m);
{
workk(n,a,a[0]);
workk(n+1,a,a[0]);
for(re i=n+3;i>=n+3-m+1;i--)
workk(i,a,a[0]);
/*for(re i=a[0];i>0;i--)
cout<<a[i];
cout<<endl;*/
}
void in2()//A(n,n)*A(n+1,1)*A(n+2,m-1)*A(2,2)*m
{
workk(n+1,b,b[0]);
workk(2,b,b[0]);
workk(m,b,b[0]);
for(re i=n+2;i>=n+2-m+2;i--)
workk(i,b,b[0]);
}
void add()
{
c[0]=max(a[0],b[0]);
for(re i=1;i<=c[0];i++)
c[i]+=a[i]+b[i];
for(re i=1;i<=c[0];i++)
if(c[i]>=10)
{
c[i+1]+=c[i]/10;
c[i]%=10;
}
while(c[c[0]])
++c[0];
--c[0];
}
int main()
{
scanf("%d%d",&n,&m);
if(n==0&&m==0)
{
printf("0\n");
return 0;
}
a[1]=1;
a[0]=1;
l1=1;
b[1]=1;
a[0]=1;
get_n();
in1();
//cout<<"YES"<<endl;
in2();
add();
for(re i=c[0];i>0;i--)
printf("%d",c[i]);
return 0;
}