排列组合,高精度

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;
}
posted @ 2021-04-07 07:12  WindZR  阅读(100)  评论(0编辑  收藏  举报