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;
}
posted @ 2020-11-16 11:21  When_C  阅读(103)  评论(0编辑  收藏  举报