一、定义
1.第一类斯特林数:
表示方法:S1(n,m)或
[
n
m
]
n \brack m
[mn]
组合意义:指n个点组成m个圆排列的方案数。
递推求法:S1(n,m)=S1(n-1,m-1)+(n-1)*S1(n-1,m)
快速求法:
∏
i
=
0
n
−
1
(
x
+
i
)
\prod_{i=0}^{n-1}(x+i)
∏i=0n−1(x+i)
的第k次项系数就是S1(n,k),所以可用分治fft做到n*log^2或者再推下用倍增fft做到n*log
2.第二类斯特林数
表示方法:S2(n,m)或
\{
n
m
\}
n \brace m
{mn}
组合意义:指n个点划分成m个非空集合的方案数。
递推求法:S2(n,m)=S2(n-1,m-1)+m*S2(n-1,m)
快速求法:考虑枚举至少i个集合是空的,容斥所求的0个集合非空得:
S
2
(
n
,
m
)
=
1
m
!
∑
i
=
0
m
(
−
1
)
i
∗
(
m
i
)
∗
(
m
−
i
)
n
S2(n,m)=\frac{1}{m!}\sum_{i=0}^m(-1)^i*\binom m i*(m-i)^n
S2(n,m)=m!1i=0∑m(−1)i∗(im)∗(m−i)n
其中之前乘的阶乘分之一是因为后面式子求出的是在盒子有标号(有序)情况下,这样才能变成无序集合。
显然搞一搞就是卷积形式可以直接fft来n*log求
二、应用
参考:(Orz)
https://www.cnblogs.com/acha/p/6444944.html
https://www.cnblogs.com/hchhch233/p/10016543.html
http://yyy.is-programmer.com/posts/202122.html
x
k
=
∑
i
=
0
k
(
x
i
)
∗
\{
k
i
\}
∗
i
!
=
∑
i
=
0
k
x
i
‾
∗
\{
k
i
\}
x^k=\sum_{i=0}^{k}\binom x i*{k\brace i}*i!=\sum_{i=0}^{k}x^{\underline i}*{k\brace i}
xk=i=0∑k(ix)∗{ik}∗i!=i=0∑kxi∗{ik}
理解:x种颜色给k个点染色,直接考虑是等式左边,复杂地考虑用上k种颜色的方案数求和就是等式右边了。枚举到k是因为用的颜色数不会多于点数,而且推式子题目k也一般比x范围小。
x
k
‾
=
∑
i
=
0
k
[
k
i
]
∗
x
i
x^{\overline k}=\sum_{i=0}^{k}{k\brack i}*x^i
xk=i=0∑k[ik]∗xi
理解:k个点组成圆排列,在为每个圆排列里的所有点染一种颜色。等式右边意义为暴枚组成了几个圆排列计算,左边意义为:考虑一个个加入点,对于当前点可以染x种颜色任意一种或者放到某个点左边并和这个点颜色相同,那么显然就是
x
k
‾
x^{\overline k}
xk
而下降幂与上升幂有如下转化:
x
n
‾
=
(
−
1
)
n
(
−
x
)
n
‾
x
n
‾
=
(
−
1
)
n
(
−
x
)
n
‾
x^{\underline{n}}=(-1)^n(-x)^{\overline{n}}\\ x^{\overline{n}}=(-1)^n(-x)^{\underline{n}}
xn=(−1)n(−x)nxn=(−1)n(−x)n
所以之前两个东西可以互相带来带去得出一堆等式…(推详见参考的大佬博客)
斯特林反演:
f
(
n
)
=
∑
k
=
0
n
{
n
k
}
g
(
k
)
⟺
g
(
n
)
=
∑
k
=
0
n
(
−
1
)
n
−
k
[
n
k
]
f
(
k
)
\displaystyle f(n)=\sum_{k=0}^n \begin{Bmatrix}n\\k \end{Bmatrix}g(k) \Longleftrightarrow g(n)=\sum_{k=0}^n(-1)^{n-k}\begin {bmatrix} n\\k \end{bmatrix}f(k)
f(n)=k=0∑n{nk}g(k)⟺g(n)=k=0∑n(−1)n−k[nk]f(k)
之后是一个很重要的东西(推式子方法很有用):
首先要理解"差分"这个东西的真正意义(原来以为就是一个数组后一项减前一项…),它的别名叫离散微分,就是说把微积分里的无限小变成1,求导的
lim
d
x
→
0
f
(
x
+
d
x
)
−
f
(
x
)
d
x
\lim_{dx \rightarrow 0} \frac{f(x+dx)-f(x)}{dx}
dx→0limdxf(x+dx)−f(x)
这个式子里的
d
x
→
0
dx \rightarrow 0
dx→0变成
d
x
=
1
dx =1
dx=1
那么这样可导的条件也不是函数连续,而是离散意义下的连续了。
还有一些积分之类符号的变化,可以参见参考的第三篇大佬博客
可以发现这样能得到
Δ
x
k
‾
=
k
∗
x
k
−
1
‾
\Delta x^{\underline k}=k*x^{\underline {k-1}}
Δxk=k∗xk−1
几乎就等于原来对
x
k
x^k
xk求导了.
把k用k+1代可以得到
x
k
‾
x^{\underline k}
xk原函数为:
x
k
+
1
‾
k
+
1
\frac {x^{\underline {k+1}}}{k+1}
k+1xk+1(ps:这里分母的k+1要看成一个常数而不是关于k的变量)
而根据前面得到幂级数和下降幂之间的通过斯特林数的关系式,发现
x
k
x^k
xk差分也能得到了。
来推下一个较常出现的东西–自然数幂和(先推n-1好化简一点)
f
(
n
−
1
)
=
∑
i
=
0
n
−
1
i
k
=
∑
i
=
0
n
−
1
∑
j
=
0
k
\{
k
j
\}
i
j
‾
=
∑
j
=
0
k
\{
k
j
\}
∑
i
=
0
n
−
1
i
j
‾
=
∑
j
=
0
k
\{
k
j
\}
∑
i
=
0
n
−
1
Δ
i
j
+
1
‾
j
+
1
=
∑
j
=
0
k
\{
k
j
\}
n
j
+
1
‾
j
+
1
f
(
n
)
=
∑
j
=
0
k
\{
k
j
\}
(
n
+
1
)
j
+
1
‾
j
+
1
=
(
n
+
1
)
∑
j
=
0
k
\{
k
j
\}
n
j
‾
j
+
1
f(n-1)=\sum_{i=0}^{n-1}i^k\\ =\sum_{i=0}^{n-1}\sum_{j=0}^{k}{k \brace j}i^{\underline j}\\ =\sum_{j=0}^{k}{k \brace j}\sum_{i=0}^{n-1}i^{\underline j}\\ =\sum_{j=0}^{k}{k \brace j}\frac{\sum_{i=0}^{n-1}\Delta i^{\underline {j+1}}}{j+1}\\ =\sum_{j=0}^{k}{k \brace j}\frac{n^{\underline {j+1}}}{j+1}\\ \\ f(n)=\sum_{j=0}^{k}{k \brace j}\frac{(n+1)^{\underline {j+1}}}{j+1}\\ =(n+1)\sum_{j=0}^{k}{k \brace j}\frac{n^{\underline j}}{j+1}\\
f(n−1)=i=0∑n−1ik=i=0∑n−1j=0∑k{jk}ij=j=0∑k{jk}i=0∑n−1ij=j=0∑k{jk}j+1∑i=0n−1Δij+1=j=0∑k{jk}j+1nj+1f(n)=j=0∑k{jk}j+1(n+1)j+1=(n+1)j=0∑k{jk}j+1nj
复杂度就是瓶颈就在求斯特林数上了。
再来推道cf的题目:
http://codeforces.com/problemset/problem/932/E
∑
i
=
0
n
(
n
i
)
i
k
=
∑
i
=
0
n
(
n
i
)
∑
j
=
0
k
\{
k
j
\}
(
i
j
)
j
!
=
∑
i
=
0
n
n
!
(
n
−
i
)
!
∑
j
=
0
k
\{
k
j
\}
1
i
−
j
=
∑
j
=
0
k
\{
k
j
\}
∑
i
=
0
n
n
!
(
n
−
i
)
!
∗
1
(
i
−
j
)
!
=
∑
j
=
0
k
\{
k
j
\}
∗
n
!
(
n
−
j
)
!
∑
i
=
0
n
(
n
−
j
i
−
j
)
=
∑
j
=
0
k
\{
k
j
\}
∗
n
j
‾
∗
2
n
−
j
\sum_{i=0}^n\binom {n} {i} i^k\\ =\sum_{i=0}^n\binom {n}{i} \sum_{j=0}^k{k\brace j}\binom i j j! \\ =\sum_{i=0}^n\frac{n!}{(n-i)!}\sum_{j=0}^k{k\brace j}\frac{1}{i-j} \\ =\sum_{j=0}^k{k\brace j}\sum_{i=0}^n\frac{n!}{(n-i)!}*\frac{1}{(i-j)!}\\ =\sum_{j=0}^k{k\brace j}*\frac{n!}{(n-j)!}\sum_{i=0}^n\binom {n-j}{i-j}\\ =\sum_{j=0}^k{k\brace j}*n^{\underline j}*2^{n-j}\\
i=0∑n(in)ik=i=0∑n(in)j=0∑k{jk}(ji)j!=i=0∑n(n−i)!n!j=0∑k{jk}i−j1=j=0∑k{jk}i=0∑n(n−i)!n!∗(i−j)!1=j=0∑k{jk}∗(n−j)!n!i=0∑n(i−jn−j)=j=0∑k{jk}∗nj∗2n−j
个人觉得刚开始推这种题目难想的就是如何把有关i的项强行化成一个可以快速求的东西。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5050;
const ll mod=1e9+7;
ll S2[N][N],jm[N];
ll n,k;
void Ad(ll &x,ll y)
{if((x+=y)>=mod)x-=mod;}
ll qpow(ll x,ll y)
{
ll res=1;
while(y)
{
if(y&1)res=res*x%mod;
x=x*x%mod,y>>=1;
}
return res;
}
int main()
{
S2[0][0]=1;
for(int i=1;i<N;i++)
{
for(int j=1;j<=i;j++)
{
S2[i][j]=(S2[i-1][j-1]+1LL*j*S2[i-1][j])%mod;
}
}
cin>>n>>k;
jm[0]=1;
for(int i=1;i<=k;i++)
jm[i]=jm[i-1]*(n-i+1)%mod;
ll ans=0;
for(int j=0;j<=min(n,k);j++)
Ad(ans,S2[k][j]*jm[j]%mod*qpow(2,n-j)%mod);
printf("%lld\n",ans);
}