[BZOJ2729][HNOI2012]排队(组合数学+高精度)

211 篇文章 0 订阅
35 篇文章 0 订阅

题目描述

传送门

题解

各种分类讨论狗屎题
先任务两个人相同,最后再乘全排
先放好个女生(m+1个空),然后将老师插进去(m+3个空),再将男生插进去

  • 两个老师相邻
    • 放在女生中间m-1种,有m-1个空必须放男生,其余随意
    • 放在女生旁边2种,有m个空必须放男生,其余随意
  • 两个老师不相邻
    • 都放在女生中间 C2m1 种,有m-3个空必须放男生,其余随意
    • 都放在女生旁边1种,有m-1个空必须放男生,其余随意
    • 一个中间一个旁边2*(m-1)种,有m-2个空必须放男生,其余随意

两个模型
1.n个球,放在m个盒子里,k个盒子必须放
先将k个球放在k个盒子里,剩n-k个球,再随意放在m个盒子里
2.n个球,放在m个盒子里,盒子允许为空
插板法
Cm1n+m1

每一次算组合数,质因数分解然后写一个高精乘单精就行了
果然什么东西一扯上高精就恶心了…

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;

int n,m;
int prime[350],p[2010],cnt[350],num[2010];
struct data{int a[5005];};
data one,zero,now,ans;

void get_p()
{
    for (int i=2;i<=2005;++i)
    {
        if (!p[i]) prime[++prime[0]]=i,num[i]=prime[0];
        for (int j=1;j<=prime[0]&&i*prime[j]<=2005;++j)
        {
            p[i*prime[j]]=1;
            if (i%prime[j]==0) break;
        }
    }
}
data jia(data a,data b)
{
    data ans=zero;
    int len=max(a.a[0],b.a[0]);
    for (int i=1;i<=len;++i) ans.a[i]=a.a[i]+b.a[i];
    for (int i=1;i<=len;++i)
    {
        ans.a[i+1]+=ans.a[i]/1000;
        ans.a[i]%=1000;
    }
    if (ans.a[len+1]) ++len;
    ans.a[0]=len;
    return ans;
}
data cheng(data a,int b)
{
    data ans=zero;
    int len=a.a[0];
    for (int i=1;i<=len;++i) ans.a[i]=a.a[i]*b;
    for (int i=1;i<=len;++i)
    {
        ans.a[i+1]+=ans.a[i]/1000;
        ans.a[i]%=1000;
    }
    while (ans.a[len+1])
    {
        ++len;
        ans.a[len+1]+=ans.a[len]/1000;
        ans.a[len]%=1000;
    }
    while (len>1&&!ans.a[len]) --len;
    ans.a[0]=len;
    return ans;
}
void calc(int x,int add)
{
    for (int i=2;x>1&&i*i<=x;++i)
        while (x%i==0) cnt[num[i]]+=add,x/=i;
    if (x>1) cnt[num[x]]+=add;
}
data C(int n,int m)
{
    if (n<0||m<0||m>n) return zero;
    memset(cnt,0,sizeof(cnt));
    data ans=one;
    for (int i=n-m+1;i<=n;++i) calc(i,1);
    for (int i=1;i<=m;++i) calc(i,-1);
    for (int i=1;i<=prime[0];++i)
    {
        while (cnt[i]>0)
        {
            ans=cheng(ans,prime[i]);
            --cnt[i];
        }
    }
    return ans;
}
data choose(int n,int m,int k)
{
    if (n<k) return zero;
    data ans=C(n-k+m-1,m-1);
    return ans;
}
void print(data a)
{
    for (int i=a.a[0];i>=1;--i)
    {
        if (i==a.a[0]) {printf("%d",a.a[i]);continue;}
        if (a.a[i]>=100) printf("%d",a.a[i]);
        else if (a.a[i]>=10) printf("0%d",a.a[i]);
        else printf("00%d",a.a[i]);
    }
    puts("");
}
int main()
{
    scanf("%d%d",&n,&m);get_p();
    memset(zero.a,0,sizeof(zero.a));zero.a[0]=1;
    memset(one.a,0,sizeof(one.a));one.a[0]=one.a[1]=1;

    ans=zero;
    // 1. 两个老师放在一起
    // 1.1 两个老师在里面 
    now=choose(n,m+3,m-1);
    now=cheng(now,m-1);
    ans=jia(ans,now);
    // 1.2 两个老师在边上
    now=choose(n,m+3,m);
    now=cheng(now,2);
    ans=jia(ans,now);

    // 2. 两个老师分开放
    // 2.1 两个老师都在里面
    now=choose(n,m+3,m-3);
    now=cheng(now,(m-1)*(m-2)/2);
    ans=jia(ans,now);
    // 2.2 两个老师都在边上
    now=choose(n,m+3,m-1);
    ans=jia(ans,now);
    // 2.3 一个老师在里面 一个老师在边上
    now=choose(n,m+3,m-2);
    now=cheng(now,2*(m-1));
    ans=jia(ans,now);

    memset(cnt,0,sizeof(cnt));
    for (int i=1;i<=n;++i) calc(i,1);
    for (int i=1;i<=m;++i) calc(i,1);
    for (int i=1;i<=2;++i) calc(i,1);
    for (int i=1;i<=prime[0];++i)
        while (cnt[i])
        {
            ans=cheng(ans,prime[i]);
            --cnt[i];
        }

    print(ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值