如果说,生命的脚步终有一天被时间的尘埃掩埋.|

Sonnety

园龄:2年粉丝:80关注:96

【学习笔记】【数学】斯特林数基础

斯特林数基础

高等的我不会

点击查看目录

前置知识:

  • 组合数学

  • 容斥原理(证明第二类斯特林数的通项公式)

  • 二项式反演(证明第二类斯特林数的通项公式)

  • 圆排列相关(第一类斯特林数)

圆排列相关

循环排列,又称圆排列,环排列,轮换。

m 个数中选 n 个不同的元素排列成一个无头无尾的环形,两个圆排列相同当且仅当索取元素个数相同,取法一致,环上排列顺序相同。

如:abcde,bcdea,cdeab,deabc,eabcd 是五个元素的一种圆排列。

acbde 则又是另一种圆排列,这是圆排列与直线排列的主要区别。

而根据刚刚我们知道,一种圆排列可以拆成五种不同的直线排列,而五个元素的直线排列有 5! 种,设圆排列个数为 x,易得:

x=5!5=(51)!=4!

即,对于 n 个不同的元素,其圆排列个数为 (n1)!

而我们知道,m 个相异元素里,选出 n 个数的方案数是 (mn)

因此,从 m 个相异元素选出 n 个可以组成圆排列个数:

x=(mn)×(n1)!=Amnn

  • 上升幂与下降幂(通过斯特林数转换普通幂)
上升幂、下降幂

上升幂与下降幂怎么来的我就不知道了,好像是微积分?

来自微积分

定义下降幂xm_,上升幂 xm¯,有:

xm_=x(x1)(x2)...(xm+1)xm¯=x(x+1)(x+2)...(x+m1)

他们之间有关联:

(x)m_=(1)m×xm¯(x)m¯=(1)m×xm_

我认为这是显然的,读者自证不难(

小小的证明一下:

(x)m_=(x)(x1)(x2)(xm+1)=(1)m×x(x+1)(x+2)(x+m1)=(1)m×xm¯

相应的,排列组合数也和二项式有关:

(nm)=n!m!(nm)!=nm_m!Anm=n!(nm)!=nm_

甚至有非常奇妙的有关同余的性质:

xm¯xmx(modm)

但是没什么用好像,也不是很想证明了,留给佬的闲话(什

证了的话可以@我谢谢喵

  • 数学归纳法(通过斯特林数转换普通幂)
有关数学归纳法

数学归纳法是证明某个命题对于所有满足 nn0 的整数 n 的都成立的一种方法,具体步骤如下:

  • 首先在 n 取得最小值 n0 时证明命题,即基础

  • 其次对 n>n0,假设 n0n1 之间(包括它们本身)所有值都已经被证明,证明该命题对 n 成立,即归纳

用有限步可以得到无限的结果,这就是数学归纳!

往往递推式可以用数学归纳法完美地建立。

定义:

百度百科

oi-wiki

斯特林数,多出现在组合枚举问题中。

  • 第一类斯特林数 s(n,m),也被记为 [nm],表示将 n 个不同元素构成 m 个圆排列的方案数。

  • 第二类斯特林数 S(n,m),也被记为 {nm},表示将 n 个不同元素分成 m 个集合的方案数。

由于第一类斯特林数与第二类斯特林数的 s 大小写难以区分,所以本文将采用另一种写法。

通常第二类斯特林数更加常用,因此首先描述第二类斯特林数。

第二类斯特林数

第二类斯特林数(斯特林子集数){nk},表示将 n 个相异元素划分为 k 个互不区分的非空子集的方案数。

(互不区分:不考虑非空子集之间的排列)

递推式:

{nk}={n1k1}+k×{n1k}

边界为 {n0}=[n=0]

([n=0] 返回值是一个 bool 值)

证明:

  • 这是一个递推式,我们每新加入一个新元素,将新元素单独开一个子集,有{n1k1} 种方案。

  • 将新元素放入一个现有的非空子集,有 k×{n1k} 种方案。

加法原理相加。

最后边界是 {n0}=[n=0],毫无疑问如果 n0 是无意义的,否则方案数为 1

于是有递推代码:

递推求第二类斯特林数
#define rg register int
#define il inline
il void pre(){
//递推求至S[n][m]
S[0][0]=1;
for(rg i=1;i<=n;++i){
for(rg j=1;j<=min(i,m);++j){
S[i][j]=S[i-1][j-1]+j*S[i-1][j]%mod;
}
}
}

通项公式

{nm}=i=0m(1)mi×ini!(mi)!

这个公式可以用容斥原理或二项式反演证明,这里使用二项式反演:

n 个互异元素,划分到 i 个互异集合(包括空集)的方案数是 gi=in,而 n 个互异元素,划分至 i 个两两不同的非空集合(不包括空集)的方案数是 fi

根据二项式反演形式一:fn=i=0n(ni)gign=i=0n(1)ni(ni)fi,易得:

gi=j=0i(ij)×fjfi=j=0i(1)ij×(ij)×gj=j=0i(1)ij×(ij)×jn=j=0ii!×(1)ij×jnj!×(ij)!

fi{ni} 的唯一不同点在于:{ni} 不计算非空集合之间的排列,因此 fi=i!×{ni},得证:

{nm}=fmm!=i=0m(1)mi×ini!×(mi)!

至于同一行第二类斯特林数之类的计算我不会,长大再学(

第一类斯特林数

第一类斯特林数(斯特林轮换数),[nk],表示将 n 个相异元素划分为 k 个互不区分的非空的圆排列方案数。

(互不区分:不考虑非空子集之间的排列)

(有关圆排列已经在前置知识解释)

递推式

[nk]=[n1k1]+(n1)×[n1k]

边界是 [n0]=[n=0]

([n=0] 返回值是一个 bool 值)

相似的证明:

当我们插入一个新元素的时候,有两种方案:

  • 新元素单独放入一个圆排列: [n1k1]

  • 新元素插入到任何一个现有的圆排列中:(n1)×[n1k]

加法原理易证。

对于 [n0]=[n=0] 边界,n0 无意义,否则方案为 1

递推求第一类斯特林数
#define rg register int
#define il inline
il void pre(){
//递推求至s[n][m]
s[0][0]=1;
for(rg i=1;i<=n;++i){
for(rg j=1;j<=min(i,m);++j){
s[i][j]=s[i-1][j-1]+(i-1)*s[i-1][j]%mod;
}
}
}

同一行第一类斯特林数我不会,长大再学(

上升幂、下降幂与普通幂的转化

有恒等式:

xn¯=k=0n[nk]×xkxn=k=0n{nk}×(1)nkxk¯xn_=k=0n[nk]×(1)nkxkxn=k=0n{nk}×xk_

如何证明?数学归纳法(前置知识有)。

首先假设我们认为:

xn=k=0n{nk}xk_,nZ,n0

显然本式在 n=0 时成立,故假设本式在 0(n1) 区间成立,证明 n 是否成立。

(我只会这么证明,不要问我先辈是如何找到这个式子的,我真不会)

因为 xk+1_=xk_×(xk),所以 x×xk_=xk+1_+kxk_。(本式在下面第23 行使用)

(ps:在第 45 行中:设 {n1k1}k=0 的情况下等于 0{n1k}k=n 的情况下等于 0。)

因此得到:

xn=x×xn1=x×k=0n1{n1k}×xk_=k=0n1{n1k}×xk+1_+k=0n1{n1k}×kxk_=k=1n{n1k1}×xk_+k=0n1{n1k}×kxk_=k=0n{n1k1}×xk_+k=0n{n1k}×kxk_=k=0n(k×{n1k}+{n1k1})xk_=k=0n{nk}xk_

证毕。

其余同理。

例题:Team Work

Team Work

题目链接

题面翻译

给定 n,k,求:

i=1n(ni)×ik

1k5000,1n109

题目描述

You have a team of N people. For a particular task, you can pick any non-empty subset of people. The cost of having x people for the task is xk .

Output the sum of costs over all non-empty subsets of people.

输入格式

Only line of input contains two integers N (1<=N<=109) representing total number of people and k (1<=k<=5000) .

输出格式

Output the sum of costs for all non empty subsets modulo 109+7 .

样例 #1

样例输入 #1

1 1

样例输出 #1

1

样例 #2

样例输入 #2

3 2

样例输出 #2

24

提示

In the first example, there is only one non-empty subset 1 with cost 11=1 .

In the second example, there are seven non-empty subsets.

- 1 with cost 12=1

- 2 with cost 12=1

- 1,2 with cost 22=4

- 3 with cost 12=1

- 1,3 with cost 22=4

- 2,3 with cost 22=4

- 1,2,3 with cost 32=9

The total cost is 1+1+4+1+4+4+9=24 .

解题:

题意就是给你 n 个元素,你可以选任意几个元素组成的非空子集,选 x 个元素的代价是 xk

要求输出所有非空子集的元素代价总和。

也就是要求输出答案 i=1n(ni)×ik

i=1n(ni)×ik=i=0n(ni)×ik

对于 ik 可以用第二类斯特林数展开:

推柿子:

i=0n(ni)×ik=i=0n(ni)j=1k{kj}×ij_=i=0n(ni)j=1k{kj}×i!(ij)!=i=0n(ni)j=1k{kj}×i!(ij)!j!×j!=i=0n(ni)j=1k{kj}×(ij)×j!=j=1k{kj}j!i=0n(ni)×(ij)=j=1k{kj}j!i=0n(nj)×(njij)=j=1k{kj}j!(nj)i=0nj(nji)=j=1k{kj}j!(nj)i=0nj(nji)=j=1k{kj}j!×(nj)×2nj=j=1k{kj}n!(nj)!×2nj

k 要取 min(n,k)

然后某些 OJ 有一些离谱数据,什么 n2×105,k109 之类的,因为 n 较小 k 过大,所以直接暴力过就行。

Miku's Code
#include<bits/stdc++.h>
using namespace std;
#define rg register int
#define il inline
#define int long long
typedef long double llf;
namespace mystd{
il int Max(int a,int b){
if(a<b) return b;
else return a;
}
il int Min(int a,int b){
if(a>b) return b;
else return a;
}
il int Abs(int a){
if(a<0) return a*(-1);
else return a;
}
}
const int maxn=2e5+50,mod=1e9+7;
int n,k,S[5000][5000];
int fac[maxn],inv[maxn],facinv[maxn];
int ans;
int qpow(int x,int y){
int ans=1;
while(y>0){
if(y&1) ans=(ans*x)%mod;
x=(x*x)%mod;
y=y>>1;
}
return ans%mod;
}
int getc(int y,int x,int m){
if(x<y) return 0;
return fac[x]%m*facinv[y]%m*facinv[x-y]%m;
}
int lucas(int y,int x,int m){
if(y==0) return 1;
return getc(y%m,x%m,m)%m*lucas(y/m,x/m,m)%m;
}
il void input(){
scanf("%lld %lld",&n,&k);
}
il void pre(int w){
S[0][0]=1,fac[0]=fac[1]=1;
for(int i=1;i<=w;++i){
fac[i]=i*fac[i-1]%mod;
for(int j=1;j<=i;++j){
S[i][j]=(S[i-1][j-1]+(int)j*S[i-1][j]%mod)%mod;
}
}
}
il void init(int maxp){
fac[0]=fac[1]=1;
inv[0]=inv[1]=1;
facinv[0]=facinv[1]=1;
for(int i=2;i<=maxp-1;++i){
fac[i]=i*fac[i-1]%mod;
}
for(int i=2;i<=maxp-1;++i){
inv[i]=(mod-mod/i*inv[mod%i]%mod+mod)%mod;
}
for(int i=2;i<=maxp-1;++i){
facinv[i]=facinv[i-1]*inv[i]%mod;
}
}
signed main(){
input();
if(k<=5000){
pre(k);
for(int j=0;j<=min(n,k);++j){
int res=0;
res=(int)S[k][j]*qpow(2,n-j)%mod;
for(int q=n-j+1;q<=n;++q){
res=res*q%mod;
}
ans=(ans+res)%mod;
}
printf("%lld",ans);
}
else{
init(2e5+50);
for(int i=0;i<=n;++i){
int res=0;
res=lucas(i,n,mod)*qpow(i,k)%mod;
ans=(ans+res)%mod;
}
printf("%lld",ans);
return 0;
}
return 0;
}

结束了,所有 latex 手打可能出错,若有错误请@我。

一次卷积以及反演我不会,长大再学(

upd on 10.7:集中修复部分 LATEX 问题。

posted @   Sonnety  阅读(386)  评论(6编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起