以后还会补充
第一类斯特林数
S ( n , k ) S(n,k) S(n,k): n n n个不同的人围着 m m m张相同的桌子,每个桌子非空。
第一类斯特林数的求法
S
(
n
,
k
)
=
S
(
n
−
1
,
k
−
1
)
+
(
n
−
1
)
S
(
n
−
1
,
k
)
S(n,k)=S(n-1,k-1)+(n-1)S(n-1,k)
S(n,k)=S(n−1,k−1)+(n−1)S(n−1,k)
即讨论第一个人是否单独坐一桌。
性质
S
(
n
,
k
)
S(n,k)
S(n,k)为
x
x
x的
n
n
n次上升幂的第
k
k
k项系数。
(
S
(
n
,
k
)
S(n,k)
S(n,k)指无符号的第一类斯特林数)
S
s
(
n
,
k
)
S_s(n,k)
Ss(n,k)为
x
x
x的
n
n
n次下降幂的第k项系数。
(
S
s
(
n
,
k
)
S_s(n,k)
Ss(n,k)指有符号的第一类斯特林数)
这个东西可以用分治NTT简单求出。
然而,在一次考试中
l
o
g
2
log^2
log2被卡了,
于是来补一下单
l
o
g
log
log做法:
你考虑倍增,每次遇到一个一直接暴力做。
然后每次扩大两倍,就相当于
F
2
n
=
F
n
(
x
)
F
n
(
x
+
n
)
F_{2n}=F_n(x)F_n(x+n)
F2n=Fn(x)Fn(x+n)
那你考虑求出
F
n
(
x
+
n
)
F_n(x+n)
Fn(x+n)。
设
F
n
(
x
)
=
∑
i
=
0
n
a
i
∗
x
i
F_n(x)=\sum_{i=0}^{n}ai*x^i
Fn(x)=∑i=0nai∗xi
F
n
(
x
+
n
)
=
F_n(x+n)=
Fn(x+n)=
∑
i
=
0
n
a
i
(
x
+
n
)
i
\sum_{i=0}^{n}ai(x+n)^i
i=0∑nai(x+n)i
∑
i
=
0
n
a
i
∑
j
=
0
i
C
(
i
,
j
)
n
i
−
j
x
j
\sum_{i=0}^{n}ai\sum_{j=0}^iC(i,j)n^{i-j}x^j
i=0∑naij=0∑iC(i,j)ni−jxj
∑
i
=
0
n
(
∑
j
=
i
n
C
(
j
,
i
)
n
j
−
i
a
j
)
x
i
\sum_{i=0}^{n}(\sum_{j=i}^nC(j,i)n^{j-i}aj)x^i
i=0∑n(j=i∑nC(j,i)nj−iaj)xi
注意到中间的东西你翻转一下又是个卷积,直接处理。
然后两个多项式直接乘。
因为:
T
(
n
)
=
T
(
n
/
2
)
+
n
l
o
g
n
T(n)=T(n/2)+nlog n
T(n)=T(n/2)+nlogn
可得复杂度为:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
放个这样写的板子,cf960G:
#include <ctime>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const int N = 100001;
const LL mod = 998244353;
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
void put(int x) {
if(x >= 10) put(x / 10);
putchar(x % 10 + '0');
}
int bin[21];
int R[N * 4];
LL ans[N * 4], A[N * 4], B[N * 4], jc[N], inv[N];
LL c1[N * 4], c2[N * 4];
LL pow_mod(LL a, LL k) {
LL ans = 1;
while(k) {
if(k & 1) (ans *= a) %= mod;
(a *= a) %= mod; k /= 2;
} return ans;
}
LL C(int n, int m) {return jc[n] * inv[m] % mod * inv[n - m] % mod;}
void pre(int n) {
for(int i = 1; i < N * 4; i <<= 1) {
c1[i] = pow_mod(3, (mod - 1) / (i * 2));
c2[i] = pow_mod(c1[i], mod - 2);
}
jc[0] = inv[0] = 1; for(int i = 1; i <= n; i++) jc[i] = jc[i - 1] * i % mod;
inv[n] = pow_mod(jc[n], mod - 2); for(int i = n - 1; i >= 1; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
bin[0] = 1; for(int i = 1; i <= 20; i++) bin[i] = bin[i - 1] * 2;
}
void NTT(LL y[], int len, int on) {
for(int i = 0; i < len; i++) R[i] = (R[i >> 1] >> 1) | ((i & 1) * (len >> 1));
for(int i = 0; i < len; i++) if(i < R[i]) swap(y[i], y[R[i]]);
for(int i = 1; i < len; i *= 2) {
LL wn = c1[i]; if(on == -1) wn = c2[i];
for(int j = 0; j < len; j += i * 2) {
LL w = 1;
for(int k = 0; k < i; k++) {
LL u = y[j + k], v = y[j + k + i] * w % mod;
y[j + k] = (u + v) % mod, y[j + k + i] = (u - v + mod) % mod;
w = w * wn % mod;
}
}
} if(on == -1) {
LL tmp = pow_mod(len, mod - 2);
for(int i = 0; i < len; i++) y[i] = y[i] * tmp % mod;
}
}
void solve(int n) {
int now = 0; ans[0] = 1;
for(int i = 20; i >= 0; i--) {
int len = 1; for(; len <= now * 2; len <<= 1);
for(int j = 0; j <= now; j++) A[j] = ans[j] * jc[j] % mod;
LL hh = 1;
for(int j = 0; j <= now; j++) B[now - j] = hh * inv[j] % mod, (hh *= now) %= mod;
for(int j = now + 1; j < len; j++) A[j] = B[j] = 0;
NTT(A, len, 1), NTT(B, len, 1);
for(int j = 0; j < len; j++) A[j] = A[j] * B[j] % mod;
NTT(A, len, -1);
for(int j = 0; j < len - now; j++) (A[j] = A[j + now] * inv[j]) %= mod;
for(int j = len - now; j < len; j++) A[j] = 0;
NTT(A, len, 1), NTT(ans, len, 1);
for(int j = 0; j < len; j++) ans[j] = ans[j] * A[j] % mod;
NTT(ans, len, -1);
now *= 2;
if(n >= bin[i]) {
n -= bin[i];
for(int j = now; j >= 0; j--) ans[j + 1] = (ans[j] + ans[j + 1] * now % mod) % mod;
ans[0] = 0;
now++;
}
}
}
int main() {
int n = read(), a = read(), b = read(); pre(n);
if(a + b - 1 > n || !a || !b) {puts("0"); return 0;}
if(n == 1) {puts("1"); return 0;}
solve(n - 1);
printf("%lld\n", ans[a + b - 2] * C(a + b - 2, a - 1) % mod);
return 0;
}
第二类斯特林数
S ( n , k ) S(n,k) S(n,k):把 n n n个不同的小球放在 k k k个相同的格子里的方案数,每个盒子至少放一个球。
第二类斯特林数的求法:
S
(
n
,
k
)
=
S
(
n
−
1
,
k
−
1
)
+
k
S
(
n
−
1
,
k
)
S(n,k)=S(n-1,k-1)+kS(n-1,k)
S(n,k)=S(n−1,k−1)+kS(n−1,k)
即讨论编号为1的球是否单独一个盒子。
S
(
n
,
k
)
=
1
k
!
∑
j
=
0
k
(
−
1
)
j
C
(
k
,
j
)
(
k
−
j
)
n
=
∑
j
=
0
k
(
−
1
)
j
(
k
−
j
)
n
j
!
(
k
−
j
)
!
S(n,k)=\frac 1{k!}\sum_{j=0}^k(-1)^jC(k,j)(k-j)^n=\sum_{j=0}^k(-1)^j\frac {(k-j)^n}{j!(k-j)!}
S(n,k)=k!1∑j=0k(−1)jC(k,j)(k−j)n=∑j=0k(−1)jj!(k−j)!(k−j)n
即枚举空盒的个数,剩下随意放,盒子是相同的,所以要除
k
!
k!
k!。
注意这个式子可以卷积。
性质
n
k
n^k
nk:
k
k
k个有标号球放在
n
n
n个有标号盒子里的方案数。
n
k
n^k
nk=
∑
j
=
1
k
S
(
k
,
j
)
∗
C
(
n
,
j
)
∗
j
!
=
∑
j
=
1
k
S
(
k
,
j
)
∗
n
!
(
n
−
j
)
!
\sum_{j=1}^kS(k,j)*C(n,j)*j!=\sum_{j=1}^kS(k,j)*\frac {n!}{(n-j)!}
∑j=1kS(k,j)∗C(n,j)∗j!=∑j=1kS(k,j)∗(n−j)!n!
相当于枚举放多少个盒子,求方案。
upd:
斯特林反演:学习资料。