第二类斯特林数#
第二类斯特林数,用 S(n,m) 或 {nm} 表示:将 n 个有标号元素分为 m 个 非空 无标号集合的方案数。
递推式#
{nm}={n−1m−1}+m×{n−1m}
通项公式#
{nm}=i=0∑mi!(m−i)!in(−1)m−i
n 固定,m 变。
注意到通项公式很像卷积式,直接设:
[ni]g(n)=i!in,[ni]h(n)=i!(−1)i
由于通项公式中 i+(m−i)=m,直接卷积得到 f=g∗h.
Poly A, B;
A.a.resize(n+1), B.a.resize(n+1);
for(int i = 0; i <= n; i++) A.a[i] = power(i, n) * fac_inv[i] % mod;
for(int i = 0; i <= n; i++) B.a[i] = (i&1) ? mod - fac_inv[i] : fac_inv[i];
A = A * B;
cpp
TODO.
第一类斯特林数#
第一类斯特林数,用 s(n,m) 或 [nm] 表示:将 n 个有标号元素分为 m 个非空无标号圆排列的方案数。
递推式#
[nk]=[n−1k−1]+(n−1)[n−1k]
https://www.luogu.com.cn/blog/oylyer/solution-p5408 ↗
记:
Fn(x)=xn=i=0∏n(x+i)
则可证:
xn=i=1∑n[ni]xi
由于显然有 x2n=xn(x+n)n,考虑分治求解。
Fn(x+n)=i=1∑n[ni](x+n)i=i=1∑n[ni]j=0∑i(ji)xjni−j=i=1∑n[ni]j=0∑ij!(i−j)!i!xjni−j=j=0∑nxjj!1i=j∑n[ni]i!(i−j)!ni−j
设 Ai=[ni]i!,Bi=i!ni,则原式化为:
Fn(x+n)=j=0∑ixjj!1i=1∑n[ni]i!(i−j)!ni−j=j=0∑ixjj!1i=1∑nAiBi−j
要凑卷积的形式,把 B 翻转得到 Bn−i′=Bi,则:
Fn(x+n)=j=0∑ixjj!1i=1∑nAiBi−j=j=0∑ixjj!1i=1∑nAiB′n+j−i
这样 A∗B′ 之后,得到的 n+j 次项系数左移 n 位,再整体 ×j!1,就是 Fn(x+n) 的 j 次项系数。
再把 Fn(x)∗Fn(x+n),即可得到 F2n(x)。
注意分治过程中,F2n+1=Fn(x)∗Fn(x+n)∗(x+2n),遇到奇数就手动乘上去即可。
void solve(int x) { //求f(x)
//x的一次上升幂f(x)=x,零次项系数为0,一次项系数为1
if(x == 1) { F.a.resize(2); F.a[1] = 1; return; }
//递归,通过f(n)求出f(x)
solve(x >> 1);
int n = (x >> 1);
//计算出A、B
A.a.resize(n+1), B.a.resize(n+1);
for(int i = 0; i <= n; i++) A.a[i] = F.a[i] * fac[i] % mod;
for(int i = 0, pw = 1; i <= n; i++, pw = pw * n % mod)
B.a[i] = pw * fac_inv[i] % mod;
reverse(B.a.begin(), B.a.begin() + n + 1);
//A与B卷积,把第n+1~2n位向左平移,之后A的每项系数除以阶乘,F再卷上A
A = A * B;
for(int i = 0; i <= n; i++) A.a[i] = A.a[i + n];
for(int i = 0; i <= n; i++) A.a[i] = A.a[i] * fac_inv[i] % mod;
A.a.resize(n+1);
F = A * F;
//若x是奇数,则2*n=x-1,上升幂需要再卷上一个(x+2*n),左移,每个系数都乘上2*n
if(x & 1) {
F.a.resize(2 * n + 2);
for(int i = 2 * n + 1; i >= 1; i--)
F.a[i] = (F.a[i - 1] + (x - 1) * F.a[i] % mod) % mod;
F.a[0] = F.a[0] * (x - 1) % mod;
}
}
cpp
TODO。