组合数学的实质还是DP,但是从通式角度处理的话有利于FFT等的实现。

首先推荐$Candy?$的球划分问题集合:

http://www.cnblogs.com/candy99/p/6400735.html

以下部分节选自

http://blog.csdn.net/sr_19930829/article/details/40888349

第一类Stirling数

  定理:第一类Stirling数$s(p,k)$计数的是把p个对象排成k个非空循环排列的方法数。

证明:把上述定理叙述中的循环排列叫做圆圈。递推公式为:

$s(p,p)=1 (p>=0)$    有p个人和p个圆圈,每个圆圈就只有一个人

$s(p,0)=0 (p>=1)$    如果至少有1个人,那么任何的安排都至少包含一个圆圈$$s(p,k)=(p-1)*s(p-1,k)+s(p-1,k-1)$$       设人被标上$1,2,\ldots,p$。将这p个人排成k个圆圈有两种情况。第一种排法是在一个圆圈里只有标号为$p$的人自己,排法有$s(p-1,k-1)$个。第二种排法中,$p$至少和另一个人在一个圆圈里。这些排法可以通过把$1,2,\ldots,p-1$排成k个圆圈再把p放在$1,2,\ldots,p-1$任何一人的左边得到,因此第二种类型的排法共有$(p-1)*s(p-1,k)$种排法。

在证明中我们所做的就是把$\{1,2,\ldots,p\}$划分到k个非空且不可区分的盒子,然后将每个盒子中的元素排成一个循环排列。

   组合数通式:$$s(k,i)=\frac{C_n^k\ k!}{\sum_{i=0}^{k}(-1)^{i+k}n^i}$$

   第一类斯特林数快速求法:

    首先我们有:$n^{\underline{k}}=\sum\limits_{i=0}^{k}(-1)^{k-i}s(k,i)\cdot n^i$,$n^{\overline{k}}=\sum\limits_{i=0}^{k}s(k,i)\cdot n^i$

    也就是$s(n,k)=[x^k]\prod\limits_{i=0}^{n-1}(x+i)$

    于是一种显然的做法就是分治NTT,复杂度$O(n\log^2 n)$。

 #include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
using namespace std; const int N=,mod=;
int n,A,B,top,stk[],rev[N],fac[N],inv[N],S[N],tmp[][N]; int ksm(int a,int b){
int res=;
for (; b; a=1ll*a*a%mod,b>>=)
if (b & ) res=1ll*res*a%mod;
return res;
} void NTT(int a[],int n,int f){
for (int i=; i<n; i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
for (int i=; i<n; i<<=){
int wn=ksm(,f ? (mod-)/(i<<) : (mod-)-(mod-)/(i<<));
for (int p=i<<,j=; j<n; j+=p){
int w=;
for (int k=; k<i; k++,w=1ll*w*wn%mod){
int x=a[j+k],y=1ll*w*a[i+j+k]%mod;
a[j+k]=(x+y)%mod; a[i+j+k]=(x-y+mod)%mod;
}
}
}
if (f) return;
int inv=ksm(n,mod-);
for (int i=; i<n; i++) a[i]=1ll*a[i]*inv%mod;
} int solve(int l,int r,int a[]){
if (l==r){ a[]=l; a[]=; return ; }
int ls=stk[top--],rs=stk[top--],mid=(l+r)>>;
int l1=solve(l,mid,tmp[ls]),l2=solve(mid+,r,tmp[rs]),n=,L=;
for (; n<=l1+l2; n<<=) L++;
for (int i=; i<n; i++) rev[i]=(rev[i>>]>>)|((i&)<<(L-));
NTT(tmp[ls],n,); NTT(tmp[rs],n,);
for (int i=; i<n; i++) a[i]=1ll*tmp[ls][i]*tmp[rs][i]%mod;
NTT(a,n,); stk[++top]=ls; stk[++top]=rs;
for (int i=; i<n; i++) tmp[ls][i]=tmp[rs][i]=;
return l1+l2;
} int main(){
freopen("960g.in","r",stdin);
freopen("960g.out","w",stdout);
scanf("%d%d%d",&n,&A,&B);
if (n==){ if (A== && B==) puts(""); else puts(""); return ; }
rep(i,,) stk[i]=i; top=;
solve(,n-,S);
fac[]=; rep(i,,A+B) fac[i]=1ll*fac[i-]*i%mod;
inv[A+B]=ksm(fac[A+B],mod-);
for (int i=A+B-; ~i; i--) inv[i]=1ll*inv[i+]*(i+)%mod;
printf("%lld\n",1ll*S[A+B-]*fac[A+B-]%mod*inv[A-]%mod*inv[B-]%mod);
return ;
}

O(nlog^2n)

    另一种做法是:令$F_n(x)=\prod\limits_{i=0}^{n-1}(x+i)$,则有$F_{2n}(x)=F_n(x)F_{n}(x+n)$。
    $F_n(x)$我们递归求得其答案,现在考虑如何利用$F_n(x)$快速求出$F_n(x+n)$。
    在这里我们假设$F_n(x)=\sum_{i=0}^{n-1} a_i x^i$
       $\begin{aligned} F_n(x+n)&=\sum_{i=0}^{n-1}a_i (x+n)^i\\ &=\sum_{i=0}^{n-1}a_i\sum_{j=0}^i{i\choose j}n^{i-j}x^j\\&=\sum_{i=0}^{n-1}(\sum_{j=i}^n {j\choose i}n^{j-i}a_j)x^i \end{aligned}$
    括号内的部分拆开之后,可以分成差相等的两个部分,意味着可以翻转之后卷积。
    那么每次递归前一半,后面一半卷积求解即可得到答案。
    时间复杂度为一个log。(来自这里

 #include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
using namespace std; const int N=,mod=;
int n,A,B,top,rev[N],fac[N],inv[N],pw[N],S[N],a[N],b[N]; int ksm(int a,int b){
int res=;
for (; b; a=1ll*a*a%mod,b>>=)
if (b & ) res=1ll*res*a%mod;
return res;
} void NTT(int a[],int n,int f){
for (int i=; i<n; i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
for (int i=; i<n; i<<=){
int wn=ksm(,f ? (mod-)/(i<<) : (mod-)-(mod-)/(i<<));
for (int p=i<<,j=; j<n; j+=p){
int w=;
for (int k=; k<i; k++,w=1ll*w*wn%mod){
int x=a[j+k],y=1ll*w*a[i+j+k]%mod;
a[j+k]=(x+y)%mod; a[i+j+k]=(x-y+mod)%mod;
}
}
}
if (f) return;
int inv=ksm(n,mod-);
for (int i=; i<n; i++) a[i]=1ll*a[i]*inv%mod;
} void solve(int l){
if (l<=){ S[l]=; return; }
if (l&){
solve(l-);
for (int i=l; i; i--) S[i]=(S[i-]+1ll*S[i]*(l-))%mod;
}else{
int m=l>>,n=,L=; solve(m);
for (; n<=l; n<<=) L++;
pw[]=; rep(i,,m) pw[i]=1ll*pw[i-]*m%mod;
rep(i,,m) a[i]=1ll*S[i]*fac[i]%mod;
rep(i,,m) b[i]=1ll*pw[m-i]*inv[m-i]%mod;
for (int i=; i<n; i++) rev[i]=(rev[i>>]>>)|((i&)<<(L-));
NTT(a,n,); NTT(b,n,);
for (int i=; i<n; i++) a[i]=1ll*a[i]*b[i]%mod;
NTT(a,n,);
rep(i,,m) a[i]=1ll*a[i+m]*inv[i]%mod,b[i]=S[i];
rep(i,m+,n) a[i]=b[i]=;
NTT(a,n,); NTT(b,n,);
for (int i=; i<n; i++) a[i]=1ll*a[i]*b[i]%mod;
NTT(a,n,);
rep(i,,l) S[i]=a[i];
for (int i=; i<n; i++) a[i]=b[i]=;
}
} int main(){
freopen("960g.in","r",stdin);
freopen("960g.out","w",stdout);
scanf("%d%d%d",&n,&A,&B); int m=max(A+B,n);
fac[]=; rep(i,,m) fac[i]=1ll*fac[i-]*i%mod;
inv[m]=ksm(fac[m],mod-);
for (int i=m-; ~i; i--) inv[i]=1ll*inv[i+]*(i+)%mod;
solve(n-); int ans=1ll*fac[A+B-]*inv[A-]%mod*inv[B-]%mod*S[A+B-]%mod;
printf("%d\n",ans);
return ;
}

O(nlogn)

第二类Stirling数

   定理:第二类Stirling数$S(p,k)$计数的是把p元素集合划分到k个不可区分的盒子里且没有空盒子的划分个数。

证明:元素在拿些盒子并不重要,唯一重要的是各个盒子里装的是什么,而不管哪个盒子装了什么。

递推公式有:$$S(p,p)=1 (p>=0) $$ $$ S(p,0)=0 (p>=1) $$ $$S(p,k)=k*S(p-1,k)+S(p-1,k-1)$$考虑将前p个正整数,$1,2,\ldots,p$的集合作为要被划分的集合,把$\{1,2,\ldots,p\}$分到k个非空且不可区分的盒子的划分有两种情况:

(1)那些使得p自己单独在一个盒子的划分,存在有$S(p-1,k-1)$种划分个数

(2)那些使得p不单独自己在一个盒子的划分,存在有 $k*S(p-1,k)$种划分个数

考虑第二种情况,p不单独自己在一个盒子,也就是p和其他元素在一个集合里面,也就是说在没有放p之前,有p-1个元素已经分到了k个非空且不可区分的盒子里面(划分个数为$S(p-1,k)$,那么现在问题是把p放在哪个盒子里面呢,有k种选择,所以存在有$k*S(p-1,k)$。

  第二类斯特林数组合数通式求法:

    先考虑每个盒子都可以区分的方案数:$S'(n,i)=S(n,i)*i!$

    首先,原问题不允许有空盒子,通过容斥解除这个限制:$S'(n,i)=\sum\limits_{k=0}^{i} (-1)^k \binom{i}{k}(i-k)^n$

    显然就有$$S(n,i)=\sum\limits_{k=0}^{i} (-1)^k\binom{i}{k}(i-k)^n *\frac{1}{i!}$$适合FFT的写法:$$S(n,m)=\sum_{i=0}^{m}\frac{(-1)^i}{i!}\frac{(m-i)^n}{(m-i)!}$$

    应用:http://www.cnblogs.com/candy99/p/6648754.html

Bell数

定理:Bell数$B(p)$是将p元素集合分到非空且不可区分盒子的划分个数(没有说分到几个盒子里面)。$$B(p)=S(p,0)+S(p,1)+.....+S(p,k)$$

所以要求Bell数就要先求出第二类Stiring数。

  $B_0=1$     $B_{n+1}=\sum_{k=0}^n\binom{n}{k}B_k$

Catalan数

$$C_n=\frac{1}{n+1}\binom{2n}{n}=\frac{(2n)!}{(n+1)!n!}=\binom{2n}{n}-\binom{2n}{n+1}=\frac{1}{n+1} \sum\limits_{i=0}^{n}\binom{n}{i}^2$$$$C_n=\frac{4n-2}{n+1}C_{n-1}$$$$C_n=\sum_{i=0}^{n-1}C_i C_{n-i-1}\quad C_{n+1}=\sum_{i=0}^{n}C_i C{n-i}$$

Bernoulli数

$$B_0=1\quad\quad\sum_{k=0}^{n}C_{n+1}^k B_k =0$$$$B_n=-\frac{1}{n+1}(C_{n+1}^0 B_0+C_{n+1}^1 B_1+ \cdots +C_{n+1}^{n-1}B_{n-1})$$$$\sum_{i=1}^{n} i^k=\frac{1}{k+1} \sum_{i=1}^{k+1} C_{k+1}^{i}B_{k+1-i} (n+1)^i$$

自然数幂和

  http://blog.csdn.net/doyouseeman/article/details/50826293

  http://blog.csdn.net/a_crazy_czy/article/details/50926374

Stirling数,Bell数,Catalan数,Bernoulli数的更多相关文章

  1. 回文数 第N个回文数

    判断回文数还是不难,如果能转为字符串就更简单了. 如果是求第N个回文数呢. 12321是一个回文数,这里先考虑一半的情况. 回文数的个数其实是有规律的.如: 1位回文数: 9个 2位回文数: 9个 3 ...

  2. c++描述将一个2进制数转化成10进制数(用到初始化栈,进栈,入栈)

    /* c++描述将2进制数转化成10进制数 问题,1.初始化栈后,用new,不知道delete是否要再写一个函数释放内存, 还是在哪里可以加上delete 2.如果栈满了,我要分配多点空间,我想的办法 ...

  3. 编程实现将一个N进制数转换成M进制数

    问题:编程实现将一个N进制数转换成M进制数.(c/c++.Java.Javascript.C#.Python) 1.Python 手写算法版 def conversion_num(num, src, ...

  4. 作业:WordCount--实现字符数,单词数,行数的统计

    1. Gitee 地址 https://gitee.com/fyxiaobai/wordcount 2. PSP表格 PSP2.1 PSP阶段 预估耗时 (分钟) 实际耗时 (分钟) Planning ...

  5. WordCount C语言实现求文本的字符数,单词数,行数

    1.码云地址: https://gitee.com/miaomiaobobo/WordCount 2.psp表格 PSP2.1表格 PSP2.1 PSP阶段 预估耗时 (分钟) 实际耗时 (分钟) P ...

  6. 根据CPU核心数确定线程池并发线程数(转)

    一.抛出问题 关于如何计算并发线程数,一般分两派,来自两本书,且都是好书,到底哪个是对的?问题追踪后,整理如下: 第一派:<Java Concurrency in Practice>即&l ...

  7. 「日志」Navicat统计的行数竟然和表实际行数不一致

    背景 近期为了保障线上数据库的稳定性,我决定针对一些大表的历史数据有计划地进行备份迁移,但是呢,发现一个奇特的现象,Navicat统计行数和表自身count统计数竟然不一致!?0.0 Navicat ...

  8. leetcode 264. 丑数 II 及 313. 超级丑数

    264. 丑数 II 题目描述 编写一个程序,找出第 n 个丑数. 丑数就是只包含质因数 2, 3, 5 的正整数. 示例: 输入: n = 10 输出: 12 解释: 1, 2, 3, 4, 5, ...

  9. Catalan Number 卡特兰数

    内容部分来自以下博客: Cyberspace_TechNode 邀月独斟 一个大叔 表示感谢! Catalan数的引入: 一个长度为2N的序列,里面有N个+1,N个-1 它的任意前缀和均非负,给定N, ...

随机推荐

  1. 【CodeForces】901 B. GCD of Polynomials

    [题目]B. GCD of Polynomials [题意]给定n,要求两个最高次项不超过n的多项式(第一个>第二个),使得到它们GCD的辗转次数为n.n<=150. [算法]构造 [题解 ...

  2. 【洛谷 P4166】 [SCOI2007]最大土地面积(凸包,旋转卡壳)

    题目链接 又调了我两个多小时巨亏 直接\(O(n^4)\)枚举4个点显然不行. 数据范围提示我们需要一个\(O(n^2)\)的算法. 于是\(O(n^2)\)枚举对角线,然后在这两个点两边各找一个点使 ...

  3. Hie with the Pie(POJ3311+floyd+状压dp+TSP问题dp解法)

    题目链接:http://poj.org/problem?id=3311 题目: 题意:n个城市,每两个城市间都存在距离,问你恰好经过所有城市一遍,最后回到起点(0)的最短距离. 思路:我们首先用flo ...

  4. python笔记之BytesIO

    1. 什么是BytesIO BytesIO与StringIO类似,不同的是StringIO只能存放string,BytesIO是用来存放bytes的,它提供了在内存中读写字节的能力. 即在内存中读写字 ...

  5. Vue 传递

    今天刷了一遍Vue的API,做个小笔记 父子传递数据时,父组件里标记要传的数据,子组件里用props获取,子组件用$emit('func',args)发布事件,父组件用@func接收. 方法一 par ...

  6. ACM-ICPC北京赛区2018重现赛 A题

    题目链接:http://hihocoder.com/contest/icpcbeijing2018/problem/1 具体思路:dfs,判断矛盾就可以了. AC代码: #include<ios ...

  7. linux下route命令--说的比较清楚!

    linux下route命令     route命令感觉很不容易.一般开机后在命令行中使用route命令,会得到下面的信息   Kernel IP routing table   Destination ...

  8. Apache+jboss群集部署

    Jboss default方式上的Cluster配置[二] - 操作系统http://www.myexception.cn/operating-system/862858.html Jboss def ...

  9. caffe Python API 之中值转换

    # 编写一个函数,将二进制的均值转换为python的均值 def convert_mean(binMean,npyMean): blob = caffe.proto.caffe_pb2.BlobPro ...

  10. Tomcat: Connector中HTTP与AJP差别与整合

    apache tomcat 整合(ajp proxy, http proxy) 1.软件: apache: httpd-2.2.17-win32-x86-openssl-0.9.8o.msi tomc ...