斯特林数学习笔记

以后还会补充


第一类斯特林数

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(n1,k1)+(n1)S(n1,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=0naixi
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=0nai(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=0naij=0iC(i,j)nijxj
∑ 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=0n(j=inC(j,i)njiaj)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(n1,k1)+kS(n1,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!1j=0k(1)jC(k,j)(kj)n=j=0k(1)jj!(kj)!(kj)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)(nj)!n!
相当于枚举放多少个盒子,求方案。


upd:
斯特林反演:学习资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值