【数学】斯特林子集数 | 斯特林轮换数 | 欧拉数
斯特林子集数/第二类斯特林数
从把n个不同的小球放到k个相同的盒子里,且每个盒子至少要有一个小球的选法。
快速计算一行斯特林数:
使用这个式子
把上式变形
是一个卷积的形式
设
那么
https://www.luogu.com.cn/problem/P5395
vector<int> A, B;
A.clear(), B.clear();
A.eb(1), B.eb(0);
for(int i = 1, fac = 1; i <= n; ++i) {
fac = 1LL * fac * i % MOD;
A.eb(1LL * qpow(MOD - 1, i)*qpow(fac, MOD - 2) % MOD);
B.eb(1LL * qpow(i, n)*qpow(fac, MOD - 2) % MOD);
}
FNTT::Convolution(A, B);
A.resize(n + 1);
把n个元素分为k个非空子集的选法。
斯特林轮换数/第一类斯特林数
从把n个不同的小球放到k个相同的转盘上,且每个转盘至少要有一个小球,转盘在旋转直接相同视为同一种方案的选法。
把n个元素分为k个非空轮换的选法。
快速计算一行斯特林轮换数:
设生成函数 \(E_n(x)=\sum\limits_{i=0}^n {n \brack i}x^i\) ,同时可以写作 \(E_n(x)=\prod\limits_{i=0}^{n-1} (x+i)\)
这个式子直接分治FNTT O(nlog^2n)
for(int i = 1; i <= n; ++i) {
vec[i].clear();
vec[i].eb(i - 1);
vec[i].eb(1);
}
DCFNTT(1, n);
pr(vec[1]);
(常数问题自己想办法解决)。
\(O(n\log n)\) 做法
\(E_n(x)=\prod\limits_{i=1}^{n} (x+i-1)\)
当n为奇数时: \(E_n(x)=\prod\limits_{i=1}^{n} (x+i-1)=(x+n-1)E_{n-1}(x)\)
当n为偶数时: \(E_n(x)=\prod\limits_{i=1}^{\frac{n}{2}} (x+i-1)\prod\limits_{i=\frac{n}{2}+1}^{n}(x+i-1)=\prod\limits_{i=1}^{\frac{n}{2}} (x+i-1)\prod\limits_{i=1}^{\frac{n}{2}} (x+i-1+\frac{n}{2})=E_{\frac{n}{2}}(x)E_{\frac{n}{2}}(x+\frac{n}{2})\)
问题转化为,已知形式幂级数 \(f(x)=\sum\limits_{i=0}^n a_ix^i\) ,求 \(g(x)=f(x+c)\) 。
设 \(A(i)=i!\cdot a_i,B(i)=\frac{c^{i}}{i!}\)
发现A和B中,j都是+1次,那么翻转其中一边就变成卷积。记 \(A(i+j)=A(n-i-j)\)
设 \(C(i)=\sum\limits_{j=0}^{i} A'(i-j)B(j)\) ,这个就是一个卷积。
\(g(x)=\sum\limits_{i=0}^n \frac{x^i}{i!} C(n-i)\) 再卷积一次。
vector<int> Calc(int n) {
if(n == 1) {
vector<int> f;
f.eb(0), f.eb(1);
return f;
}
if(n % 2 == 1) {
vector<int> f = Calc(n - 1);
f.eb(0);
for(int i = n; i >= 1; --i)
f[i] = (f[i - 1] + 1LL * f[i] * (n - 1)) % MOD;
f[0] = 1LL * f[0] * (n - 1) % MOD;
return f;
} else {
int m = n / 2;
vector<int> f = Calc(m), g(m + 1), a(m + 1), b(m + 1);
for(int i = 0, res = 1; i <= m; ++i) {
a[i] = 1LL * f[i] * fac[i] % MOD;
b[i] = 1LL * res * invfac[i] % MOD;
res = 1LL * res * m % MOD;
}
reverse(a.begin(), a.end());
FNTT::Convolution(a, b);
for(int i = 0; i <= m; ++i)
g[i] = 1LL * invfac[i] * a[m - i] % MOD;
FNTT::Convolution(f, g);
f.resize(n + 1);
return f;
}
}
欧拉数
把n个元素排成一个序列,相邻两个标上符号,总共n-1个符号,恰好有k个小于号的选法。