CodeVS1994 排队

链接

  http://codevs.cn/problem/1994/

题解

  高中排列组合题
  

ans=An+2n+2AmmCmn+32An+1n+2Cmn+1

  稍微解释一下,就是先把老师看做男生,男生先全排列,然后形成 n+3 个空,女生来插,同样女生也要全排列。
  再捆绑法减去老师相邻的情况。
  然后就可以开始打恶心的高精度了。

代码

//组合数+高精 
#include <cstdio>
#include <algorithm>
#define maxn 2100
#define ll long long
#define ya 1000000000
using namespace std;
ll prime[maxn], q[maxn], mark[maxn], N, M, A[maxn], B[maxn];
inline void shai()
{
    ll i, j;
    for(i=2;i<maxn;i++)
    {
        if(!mark[i])prime[++prime[0]]=i;
        for(j=1;j<=prime[0] and i*prime[j]<maxn;j++)
        {
            mark[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
inline void mult(ll *num, ll x)
{
    ll i;
    for(i=1;i<=num[0];i++)num[i]*=x;
    for(i=1;i<=num[0];i++)num[i+1]+=num[i]/ya, num[i]%=ya;
    if(num[num[0]+1])num[0]++;
}
inline void minu(ll *a, ll *b)
{
    ll i;
    for(i=1;i<=b[0];i++)a[i]-=b[i];
    for(i=1;i<=a[0];i++)if(a[i]<0)a[i+1]--,a[i]+=ya;
    for(;a[a[0]]==0;a[0]--);
}
inline void show(ll *num)
{
    ll i, j;
    printf("%lld",num[num[0]]);
    for(i=num[0]-1;i>0;i--)
    {
        for(j=ya/10;j>1;j/=10)if(num[i]<j)putchar(48);
        printf("%lld",num[i]);
    }
}
inline void resolve(ll x, ll d)
{
    ll t, i;
    for(i=1,t=x;prime[i]<=t;i++)
        for(;t%prime[i]==0;t/=prime[i])q[i]+=d;
}
void calc(ll *num)
{
    ll i, j, t;
    for(i=1;i<=prime[0];i++)
    {
        for(j=1,t=1;j<=q[i];j++)
        {
            if(t*prime[i]>ya)mult(num,t),t=1;
            t*=prime[i];
        }
        if(t>1)mult(num,t);
    }
}
void solve()
{
    ll i;
    if(M>N+3)return;
    for(i=1;i<=N+2;i++)resolve(i,1);
    for(i=1;i<=M;i++)resolve(i,1);
    for(i=N+3;i>=N+3-M+1;i--)resolve(i,1);
    for(i=1;i<=M;i++)resolve(i,-1);
    A[0]=A[1]=1;calc(A);
    for(i=1;i<=prime[0];i++)q[i]=0;
    resolve(2,1);
    for(i=1;i<=N+1;i++)resolve(i,1);
    for(i=1;i<=M;i++)resolve(i,1);
    for(i=N+2;i>=N+2-M+1;i--)resolve(i,1);
    for(i=1;i<=M;i++)resolve(i,-1);
    if(M>N+2)return;
    B[0]=B[1]=1;calc(B);
    minu(A,B);
}
int main()
{
    shai();
    scanf("%lld%lld",&N,&M);
    solve();
    if(N==0)putchar(48);
    else show(A);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值