Luogu3223 [HNOI2012]排队(排列组合)题解
数学题
排列组合+高精度
前置芝士
思路
考虑容斥,老师不相邻的方案数\(=\)不考虑老师特殊要求的方案数\(-\)保证老师相邻的方案数
- 不考虑老师限制,即老师与男生相同,则方案数为:\(A(n+3,m)\times A(n+2,n+2)\)
- 保证老师相邻,即把两个老师捆在一起,看做一个男生,则方案数为:\(A(n+2,m)\times A(n+1,n+1)\times A(2,2)\)
由于\(n\)和\(m\)都很小,可以直接高精度暴力计算排列。
注意:压位高精记得要控制输出位数!
代码
#include <cstdio>
#define LL long long
using namespace std;
const int maxn = 1e4 + 10;
const LL p = 10000000000ll;
int len,n,m,len1;
LL ans[maxn],f[maxn];
void tim(int x){
LL tmp = 0, pre = 0;
for(int i = 1; i <= len; ++ i){
tmp = (LL)x * ans[i];
ans[i] = tmp % p + pre;
pre = tmp / p;
}
if(pre) len++, ans[len] = pre;
}
void tim1(int x){
LL tmp = 0, pre(0);
for(int i = 1; i <= len1; ++ i){
tmp = (LL)x * f[i];
f[i] = tmp % p + pre;
pre = tmp / p;
}
if(pre) len1++, f[len1] = pre;
}
int cnt(LL x){
int num = 0;
while(x) num++, x /= 10;
return num;
}
void re(){
LL pre = 0;
for(int i = 1; i <= len; ++ i){
if(!pre && i > len1) break;
ans[i] -= pre; pre = 0;
if(ans[i] < f[i]) ans[i] = ans[i] + (p - f[i]), pre = 1;
else ans[i] = ans[i] - f[i];
}
while(!ans[len] && len){
len--;
if(!len) break;
}
printf("%lld", ans[len]);
for(int i = len - 1; i >= 1; -- i){
printf("%010lld", ans[i]);
}
printf("\n");
}
int main(){
scanf("%d%d", &n, &m);
ans[++len] = 1; f[++len1] = 1;
for(int i = n + 3 - m + 1; i <= n + 3; ++ i) tim(i);
for(int i = n + 2 - m + 1; i <= n + 2; ++ i) tim1(i);
for(int i = 2; i <= n + 2; ++ i){
tim(i);
if(i <= n + 1) tim1(i);
}
tim1(2);
re();
//printf("%lld\n", p);
return 0;
}