贝尔数求集合划分方案总数

问题描述

因为长期钻研算法, 无暇顾及个人问题,BUAA ACM/ICPC 训练小组的帅哥们大部分都是单身。

某天,他们在机房商量一个绝妙的计划"一卡通大冒险"。

这个计划是由 wf 最先提出来的,计划的内容是,把自己的联系方式写在校园一卡通的背面,然后故意将自己的卡"遗失"在某处(如水房,TD,食堂,主 M。。。。)他们希望能有 MM 看到他们遗失卡,能主动跟他们联系,这样就有机会请 MM 吃饭了。

他们决定将自己的一卡通夹在基本相同的书里,然后再将书遗失到校园的各个角落。正当大家为这个绝妙的计划叫好时,大家想到一个问题。

很明显,如果只有一张一卡通,那么只有一种方法,即,将其夹入一本书中。当有两张一卡通时,就有了两种选择,即,将两张一卡通夹在一本书里,或者分开夹在不同的书里。

当有三张一卡通时,他们就有了 5 种选择,即:{``{A},{B},{C}},{``{A,B},{C}},{``{B,C},{A}},{``{A,C},{B}},{``{A,B,C}},于是,这个邪恶计划的组织者 wf 希望了解,如果 ACM 训练对里有 n 位帅哥(即有 N 张一卡通),那么要把这些一卡通夹到书里有多少种不同的方法。

输入格式

包含多组数据,第一行为 n(1<=n<=5×10^5),表示接下来有 n 组数据,以下每行一个数 x(1<=x<=2000),表示共有 x 张一卡通。

输出格式

对每组数据,输出一行,不同的方法数,因为这个数可能非常大,我们只需要它除以 1000 的余数。

输入样例

复制代码
4
1
2
3
100

输出样例

复制代码
1
2
5
751

前置知识

第二类斯特林数

第二类斯特林数(斯特林子集数),也可记做 𝑆(𝑛,𝑘),表示将 𝑛 个两两不同的元素,划分为 𝑘 个互不区分的非空子集的方案数。

递推式为:;通项公式为:

贝尔数

有限集划分数又称贝尔数,是组合数学中表示基数为n的集合所有划分方式数量的整数数列,记为。它用于求将n个元素划分为若干互不相交非空子集的不同方法总数(即求n个元素总共有多少种划分方式,每种划分方式满足划分定义)是因为空集正好有1种划分方法。

eg:对 {1,2,3},所有划分方案是:

  1. { {1,2,3} }
  2. { {1}, {2,3} }
  3. { {2}, {1,3} }
  4. { {3}, {1,2} }
  5. { {1}, {2}, {3} }

一共 5 种划分方案。因此

这里说到划分就想到离散数学中的划分与覆盖的相关概念:

A:一个集合**;** ,S是由 A 的各个子集构成的集合(叫子集族)

覆盖:

  1. (即子集不能为空集);
  2. (即所有子集并起来要等于全集A)。

划分:

  1. (即子集不能为空集);
  2. (即所有子集并起来要等于全集A);
  3. (即任意两个子集不相交)。

第三点还说明了:在划分中,集合A中的每个元素只能被选择一次。而覆盖没有这个要求,因此,覆盖可以选择同一个元素在不同子集中多次出现。但它们最终都需要满足所有子集并起来能覆盖全集。

无论是覆盖还是划分只要求每个子集不能是空集,并没有要求划分必须至少有一个子集。(是可行的,因为它表示划分的是一个空集;但是则是不可行的,因为它表示的是划分的子集是一个空集。这也是的原因。)

递推公式:。每个贝尔数都是相应的第二类斯特林数的和,因为第二类斯特林数是把基数为 𝑛 的集合划分为正好 𝑘 个非空集的方法数目,因此,贝尔数可以表示为:

贝尔三角形

该三角形的每行的首项是贝尔数,可以利用这个三角形来递推求出贝尔数。

构造方法:

  • 对于 𝑛>=1,第 𝑛 行首项等于上一行的末项,即
  • 对于 𝑚,𝑛>=1,第 𝑛 行第 𝑚 项等于它左边和左上角两个数之和,即

这里给出的递推公式以及通项公式等均不给出证明,如果有感兴趣的可以自行网上查阅。

上述内容引用自:oi-wiki网站

代码实现

java 复制代码
//题目要求结果对1000取模(如果不取模,对于long类型,也可能出现溢出)
import java.util.Scanner;

public class Main{
    public static void main(String[] args) {
        //贝尔三角:用来求贝尔数,第 i 行第 0 列 = 贝尔数 B_i
        long[][] bellTriangle=new long[2001][2001];
        //a_{0,0} = 1
        bellTriangle[0][0]=1;
        for (int n = 1; n < 2001; n++) {
            //第 𝑛 行首项等于上一行的末项,即 a_{n,0}=a_{n-1,n-1}
            bellTriangle[n][0]=bellTriangle[n-1][n-1]%1000;
            for (int m = 1; m <= n; m++) {
                //第 𝑛 行第 𝑚 项等于它左边和左上角两个数之和,即a_{n,m}=a_{n,m-1}+a_{n-1,m-1}
                bellTriangle[n][m]=(bellTriangle[n][m-1]+bellTriangle[n-1][m-1])%1000;
            }
        }

        Scanner sc = new Scanner(System.in);
        int t=sc.nextInt();
        for (int i = 0; i < t; i++) {
            System.out.println(bellTriangle[sc.nextInt()][0]);
        }
    }
}
相关推荐
大G的笔记本10 分钟前
synchronized 的原理(简单版)
笔记
daad7771 小时前
记录一个希尔伯特曲线笔记
笔记
oddsand11 小时前
Redis网络模型
java·数据库·redis
皮卡祺q2 小时前
【redies0-导论】分布式系统的演进-引进redis原因
java·数据库·redis
roman_日积跬步-终至千里2 小时前
如何分析复杂架构:一套真正能落地的方法
java·开发语言·架构
武子康2 小时前
Java-02 深入浅出MyBatis 3 快速入门:环境配置、项目创建与 CRUD 操作
java·后端
Don.TIk2 小时前
ChapterOne-搭建项目骨架
java·spring·spring cloud·mybatis
Don.TIk2 小时前
ChaperTwo-整合 SaToken 实现 JWT 登录功能
java·开发语言
qq_2518364572 小时前
基于java Web汽车销售管理系统设计与实现
java·前端·汽车
南极企鹅2 小时前
事务&@Transactional注解
java·数据库·spring·oracle·mybatis