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
题解
简单的组合题+高精度(能用python多好)。
先考虑n个男生站好之后用插板法插入老师和女生:
Ann×A2n+1×Amn+3
发现漏算了一种情况,就是老师中间夹着一个女生,那么把这三个人打包:
A22×A1m×An+1n+1×Am−1n+2
加起来就是总的方案数了。
注意边界的特判。
#include<bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for(int i = (l); i <= (r); i++)
#define per(i, r, l) for(int i = (r); i >= (l); i--)
const int N = 10000 + 10, P = 10000;
struct Bigint{
int dat[N], len;
Bigint(){len = 1; memset(dat, 0, sizeof(dat));}
void cl(){while(len > 1 && !dat[len])len--;}
Bigint operator + (const Bigint &a) const{
Bigint ret; ret.len = max(len, a.len) + 1;
rep(i, 1, ret.len){
ret.dat[i] += dat[i] + a.dat[i];
if(ret.dat[i] >= P) ret.dat[i+1]++, ret.dat[i] -= P;
}
ret.cl();
return ret;
}
Bigint operator * (const Bigint &a) const{
Bigint ret; ret.len = len + a.len + 1;
rep(i, 1, len) rep(j, 1, a.len){
ret.dat[i+j-1] += dat[i] * a.dat[j];
if(ret.dat[i+j-1] >= P) ret.dat[i+j] += ret.dat[i+j-1] / P, ret.dat[i+j-1] %= P;
}
ret.cl();
return ret;
}
void out(){
printf("%d", dat[len]);
per(i, len-1, 1) printf("%04d", dat[i]);
puts("");
}
};
int n, m;
Bigint ans;
Bigint mul(int x, int y){
Bigint a, b;
if(x > y) return a;
a.dat[1] = 1;
rep(i, x, y){
b.dat[1] = i;
a = a * b;
}
return a;
}
Bigint A(int n, int m){
if(m == 0){
Bigint ret; ret.dat[1] = 1;
return ret;
}
if(m > n){
Bigint ret;
return ret;
}
return mul(n-m+1, n);
}
int main(){
scanf("%d%d", &n, &m);
ans = (A(2, 1) * A(m, 1) * A(n+1, 1) * A(n+2, m-1) + A(n+1, 2) * A(n+3, m)) * A(n,n);
ans.out();
}