问题描述
因为长期钻研算法, 无暇顾及个人问题,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,2,3} }
- { {1}, {2,3} }
- { {2}, {1,3} }
- { {3}, {1,2} }
- { {1}, {2}, {3} }
一共 5 种划分方案。因此。
这里说到划分就想到离散数学中的划分与覆盖的相关概念:
A:一个集合**;**
,S是由 A 的各个子集
构成的集合(叫子集族)
覆盖:
(即子集不能为空集);
(即所有子集并起来要等于全集A)。
划分:
(即子集不能为空集);
(即所有子集并起来要等于全集A);
(即任意两个子集不相交)。
第三点还说明了:在划分中,集合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]);
}
}
}