背景:
好久没有更
blog
\text{blog}
blog,最近都在准备模拟(水 )赛。
第一类斯特林数:
第一类斯特林数第 n n n行 m m m列,记做 [   n m ] \LARGE[_{\,n}^m] [nm]。
que \text{que} que:
题目传送门:https://www.luogu.org/problem/P5408。
现在你有
n
n
n个珠子,每一个珠子的颜色各不相同,求这些珠子组成
m
m
m个项链的方案数。
现在求
1
,
2
,
3
,
.
.
.
m
1,2,3,...m
1,2,3,...m时方案数。
sol \text{sol} sol:
设
S
n
,
m
S_{n,m}
Sn,m表示上面的方案数。
那么有:
S
n
,
m
=
S
n
−
1
,
m
−
1
+
(
n
−
1
)
S
n
−
1
,
m
S_{n,m}=S_{n-1,m-1}+(n-1)S_{n-1,m}
Sn,m=Sn−1,m−1+(n−1)Sn−1,m
解释一下:对于当前的珠子来说,有两种选择:
[
1
]
[1]
[1]:新开一个项链,那么方案数就要继承
S
n
−
1
,
m
−
1
S_{n-1,m-1}
Sn−1,m−1;
[
2
]
[2]
[2]:在原来的项链里加入,考虑它会加在某一个珠子的后面,由于之前已经有
(
n
−
1
)
(n-1)
(n−1)个珠子,可以加在任意一个后面,因此方案数为
(
n
−
1
)
S
n
−
1
,
m
(n-1)S_{n-1,m}
(n−1)Sn−1,m。
可是这是
Θ
(
n
m
)
\Theta(nm)
Θ(nm)的大暴力啊。
第一类斯特林数有一个性质就是它可以表示下面的形式:
S
n
,
m
=
[
x
m
]
x
(
x
+
1
)
(
x
+
2
)
+
.
.
.
+
(
x
+
n
−
1
)
S_{n,m}=[x^m]x(x+1)(x+2)+...+(x+n-1)
Sn,m=[xm]x(x+1)(x+2)+...+(x+n−1)
现在这个式子就可以分治
NTT
\text{NTT}
NTT解决了。
时间复杂度:
Θ
(
n
log
2
n
)
\Theta(n\log^2n)
Θ(nlog2n)。
而模板题卡这种做法,因此不能通过。
有
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn)的做法,可我太菜了,不想学。
code \text{code} code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
const LL mod=167772161,G=3,inv_G=55924054;
using namespace std;
int r[800010],f[800010],a[20][800010],b[20][800010];
int n,A,B,l,limit;
int dg(int x,int k)
{
if(!k) return 1;
int op=dg(x,k>>1);
if(k&1) return (LL)op*op%mod*x%mod; else return (LL)op*op%mod;
}
int get_inv(int x)
{
return dg(x,mod-2);
}
void init(int n)
{
limit=1,l=0;
while(limit<(n<<1))
limit<<=1,l++;
for(int i=1;i<limit;i++)
r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
}
void NTT(int *now,int limit,int op)
{
for(int i=0;i<limit;i++)
if(i<r[i]) swap(now[i],now[r[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
int wn=dg(op==1?G:inv_G,(mod-1)/(mid<<1));
for(int j=0;j<limit;j+=(mid<<1))
{
int w=1;
for(int k=0;k<mid;k++,w=((LL)w*wn)%mod)
{
int x=now[j+k],y=(LL)w*now[j+k+mid]%mod;
now[j+k]=(x+y)%mod;
now[j+k+mid]=(x-y+mod)%mod;
}
}
}
if(op==1) return;
int INV=get_inv(limit);
for(int i=0;i<limit;i++)
now[i]=(LL)now[i]*INV%mod;
}
void poly_CDQ(int *f,int dep,int l,int r)
{
if(l==r)
{
f[0]=l;f[1]=1;
return;
}
int mid=(l+r)>>1;
init(r-l+1+1);
for(int i=0;i<limit;i++)
a[dep][i]=b[dep][i]=0;
poly_CDQ(a[dep],dep+1,l,mid),poly_CDQ(b[dep],dep+1,mid+1,r);
init(r-l+1+1);
NTT(a[dep],limit,1),NTT(b[dep],limit,1);
for(int i=0;i<limit;i++)
f[i]=(LL)a[dep][i]*b[dep][i]%mod;
NTT(f,limit,-1);
}
int main()
{
scanf("%d",&n);
poly_CDQ(f,0,0,n-1);
for(int i=0;i<=n;i++)
printf("%d ",f[i]);
}
第二类斯特林数:
第二类斯特林数第
n
n
n行
m
m
m列,记做
{
 
n
m
}
\left\{{\LARGE_{\,n}^m}\right\}
{nm}。
这一个更加广泛应用。
que \text{que} que:
题目传送门:https://www.luogu.org/problem/P5395 。
现在你有
n
n
n个珠子,每一个珠子的颜色各不相同,求这些珠子放进
m
m
m个盒子的方案数。
现在求
1
,
2
,
3
,
.
.
.
m
1,2,3,...m
1,2,3,...m时方案数。
sol \text{sol} sol:
设
S
n
,
m
S_{n,m}
Sn,m表示上面的方案数。
那么有:
S
n
,
m
=
S
n
−
1
,
m
−
1
+
m
S
n
−
1
,
m
S_{n,m}=S_{n-1,m-1}+mS_{n-1,m}
Sn,m=Sn−1,m−1+mSn−1,m
解释一下:对于当前的珠子来说,有两种选择:
[
1
]
[1]
[1]:新开一个盒子,那么方案数就要继承
S
n
−
1
,
m
−
1
S_{n-1,m-1}
Sn−1,m−1;
[
2
]
[2]
[2]:在原来的盒子里加入,考虑它会加在某一个盒子里,由于之前已经有
m
m
m个盒子,可以加在任意一个里面,因此方案数为
(
n
−
1
)
S
n
−
1
,
m
(n-1)S_{n-1,m}
(n−1)Sn−1,m。
可是这是
n
m
nm
nm的大暴力啊。
第二类斯特林数有一个性质就是它可以表示下面的形式:
S
n
,
m
=
1
m
!
∑
k
=
0
m
(
−
1
)
k
C
m
k
(
m
−
k
)
n
S_{n,m}=\frac{1}{m!}\sum_{k=0}^{m}(-1)^kC_{m}^{k}(m-k)^n
Sn,m=m!1k=0∑m(−1)kCmk(m−k)n
S n , m = 1 m ! ∑ k = 0 m ( − 1 ) k m ! k ! ( m − k ) ! ( m − k ) n S_{n,m}=\frac{1}{m!}\sum_{k=0}^{m}(-1)^k\frac{m!}{k!(m-k)!}(m-k)^n Sn,m=m!1k=0∑m(−1)kk!(m−k)!m!(m−k)n
S n , m = ∑ k = 0 m ( − 1 ) k 1 k ! ( m − k ) ! ( m − k ) n S_{n,m}=\sum_{k=0}^{m}(-1)^k\frac{1}{k!(m-k)!}(m-k)^n Sn,m=k=0∑m(−1)kk!(m−k)!1(m−k)n
S n , m = ∑ k = 0 m ( − 1 ) k k ! ( m − k ) n ( m − k ) ! S_{n,m}=\sum_{k=0}^{m}\frac{(-1)^k}{k!}\frac{(m-k)^n}{(m-k)!} Sn,m=k=0∑mk!(−1)k(m−k)!(m−k)n
为什么呢?
假设每一个盒子都不同,并且我们允许空盒的存在。
但是我们当然就是不允许空盒存在啊。
所以考虑容斥。
枚举我当前有
k
k
k个空盒子。
那么先把这
k
k
k个盒子选出来,也就是
C
m
k
C_{m}^{k}
Cmk。
然后剩下
m
−
k
m−k
m−k个盒子,
n
n
n个球可以随便放,也就是
(
m
−
k
)
n
(m−k)^n
(m−k)n。
考虑到我们盒子是没有区别的,所以除以
m
!
m!
m!。
现在这个式子是一个卷积的形式,就可以
NTT
\text{NTT}
NTT解决了。
时间复杂度:
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn)。
code \text{code} code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
const int mod=167772161,G=3,inv_G=55924054;
using namespace std;
int a[1000010],b[1000010],f[1000010],g[1000010],inv[1000010],Inv[1000010];
int limit,n,l,r[1000010];
int dg(int x,int k)
{
if(!k) return 1;
int op=dg(x,k>>1);
if(k&1) return (LL)op*op%mod*x%mod; else return (LL)op*op%mod;
}
int get_inv(int x)
{
return dg(x,mod-2);
}
void init(int n)
{
limit=1,l=0;
while(limit<(n<<1))
limit<<=1,l++;
for(int i=1;i<limit;i++)
r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
}
void NTT(int *now,int limit,int op)
{
for(int i=0;i<limit;i++)
if(i<r[i]) swap(now[i],now[r[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
int wn=dg(op==1?G:inv_G,(mod-1)/(mid<<1));
for(int j=0;j<limit;j+=(mid<<1))
{
int w=1;
for(int k=0;k<mid;k++,w=((LL)w*wn)%mod)
{
int x=now[j+k],y=(LL)w*now[j+k+mid]%mod;
now[j+k]=(x+y)%mod;
now[j+k+mid]=(x-y+mod)%mod;
}
}
}
}
void dft(int *f,int n,int limit)
{
NTT(f,limit,-1);
int INV=get_inv(limit);
for(int i=0;i<n;i++)
f[i]=(LL)f[i]*INV%mod;
}
void Init()
{
inv[0]=inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=((LL)mod-mod/i)*inv[mod%i]%mod;
Inv[0]=Inv[1]=1;
for(int i=2;i<=n;i++)
Inv[i]=(LL)Inv[i-1]*inv[i]%mod;
}
int main()
{
scanf("%d",&n);
Init();
for(int i=0;i<=n;i++)
{
f[i]=(((i&1)?-1ll:1ll)*Inv[i]+mod)%mod;
g[i]=(LL)dg(i,n)*Inv[i]%mod;
}
init(n+1);
NTT(f,limit,1),NTT(g,limit,1);
for(int i=0;i<limit;i++)
f[i]=(LL)f[i]*g[i]%mod;
dft(f,n+1,limit);
for(int i=0;i<=n;i++)
printf("%d ",f[i]);
}
贝尔数:
que \text{que} que:
现在你有
n
n
n个珠子,每一个珠子的颜色各不相同,求这些珠子放进若干个盒子的方案数。
现在求
1
,
2
,
3
,
.
.
.
m
1,2,3,...m
1,2,3,...m时方案数。
sol \text{sol} sol:
枚举盒子数
i
i
i,显然我们有:
B
n
,
m
=
∑
i
=
1
m
{
n
 
i
}
B_{n,m}=\sum_{i=1}^{m}\left\{{\LARGE_{n}^{\,i}}\right\}
Bn,m=i=1∑m{ni}
可是这样的时间复杂度是
Θ
(
n
m
)
\Theta(nm)
Θ(nm)的,而且还要预处理第二类斯特林数,极其复杂。
考虑它的递推式:
B
n
=
∑
k
=
1
n
−
1
C
n
−
1
k
B
k
B_{n}=\sum_{k=1}^{n-1}C_{n-1}^{k}B_k
Bn=k=1∑n−1Cn−1kBk
理解起来也不难,枚举珠子数
k
k
k,考虑从
n
−
1
n-1
n−1个珠子里选出
k
k
k个珠子放在一个盒子里,那么
未完待续。
后记:
有什么好的套路或性质以后再补充吧,毕竟还没有做题。
欢迎大佬指出错误。
一些性质:
n
k
=
∑
i
=
0
k
{
k
 
i
}
C
n
i
i
!
n^k=\sum_{i=0}^{k}\left\{{\LARGE_{k}^{\,i}}\right\}C_{n}^{i}i!
nk=i=0∑k{ki}Cnii!
持续更哦。