学习笔记:生成函数II(集合分拆、置换、整数分拆、它们的递推公式、生成函数 和快速计算)

形式幂级数的更多运算#

形式幂级数与幂级数的比较#

  • 形式幂级数本质是序列(xi 无意义),幂级数本质是极限。
  • 形式幂级数通过带入 x 还原成幂级数。
  • 假设系数在 C 上,可以证明形式幂级数与具有正收敛半径的幂级数在 '通常' 的所有运算上服从同样规律(加减乘除求导积分……)。

形式幂级数的更多运算#

假设 f(x)=a0+a1x+a2x2++anxn+

求导#

定义 f(x) 的导数为 a1+2a2x++(n+1)an+1xn+,记做 f(x)df(x)d(x)

对于形式幂级数而言,

求导是序列上的一个变换,即 {a0,a1,,an,}{a1,2a2,,(n+1)an+1,}

积分#

定义 f(x) 的积分为 a0x+a12x2++an1nxn+,记做 f(x)dx0xf(y)dy

{a0,a1,,an,}{0,a0,a12,,an1n,}

推论:如果 [x0]f(x)=0,令 g(x)=f(x),则 g(x)dx=f(x)

复合#

假设 f(x)=a1x+a2x2++anxn+, g(x)=b0+b1x+b2x2++bnxn+

g 复合 f 定义为 c0+c1x+cnxn+,满足 c0=b0, cn=k=1nbki1+i2++ik=naij

记做 gfg(f(x))

如果 f 的常数项不为 0

那么 c0=b0+b0a0+b0a02++bna0n+

c0 本身就是一个极限的形式,涉及到收不收敛的问题,这是在形式幂级数中不愿看到的。

因此在定义复合时,一般令 [x0]f(x)=0

再考虑 cn=k=1nbki1+i2++ik=naij

如果 ij>n,那么 ij>n,所以 cn 的定义不需要考虑收敛。

  • exp(x)=n01n!xn
  • ln(1+x)=n1(1)n+1xnn
  • ln(1x)=n1xnn
  • 设形式幂级数 f(x) 满足 [x0]f(x)=0,则可定义 exp(f(x))ln(1+(f(x))。(或 [x0]=1 可定义 ln(f(x)))。
  • g(x)=exp(f(x))f(x)=ln(g(x))

推论:(gf)=(gf)f

假设 f(x)=i1aixi, g(x)=i0bi, g(f(x))=i0cixi

满足

{f(x)=n0(n+1)an+1xna0=0g(x)=n0(n+1)bn+1xng(f(x))=n0(n+1)cn+1xnc0=b0, cn=k=1nbki1+i2++ik=naijg(f(x))=n0dnxndn=k=0n(k+1)bk+1i1+i2++ik=naij

即证 (n+1)cn+1=i=0n(i+1)ai+1dni

左边:

i=0n(i+1)ai+1dni=i=0n(i+1)ai+1k=0ni(k+1)bk+1i1+i2++ik=niaij=k=0n(k+1)bk+1i=0n(i+1)ai+1i1+i2++ik=niaij=k=1n+1kbki=1n+1iaii1+i2++ik1+i=n+1j=1k1aij=k=1n+1kbki1+i2++ik1+i=n+1ai1ai2aik1aii=k=1n+1bk[i1+i2++ik1+ik=n+1ai1aiki1+i1+i2++ik1+ik=n+1ai1aiki2+]把 k 项拆开,每次选出一个特殊的 ij=k=1n+1bki1+i2++ik1+ik=n+1(i1+i2+ik)j=1kaij=k=1n+1bki1+i2++ik1+ik=n+1(n+1)j=1kaij

右边:

(n+1)cn+1=(n+1)k=1n+1bki1+i2++ik=n+1i=1kaij

左边等于右边。

计算 ln(f(x))#

(满足 [x0]f(x)=1)重要观察:

ln(f(x))=1f(x)f(x)

O(n) 求导,O(nlogn) 求逆,O(n) 积分。

P4725 【模板】多项式对数函数(多项式 ln)

计算 exp(f(x))#

(满足 [x0]f(x)=1)如果 g(x)=exp(f(x)),那么 ln(g(x))=f(x)

构造牛顿迭代 h(g(x))=ln(g(x))f(x)

有递推式

g(x)=g0(x)[1ln(g0(x))+f(x)](modx2n)

P4726 【模板】多项式指数函数(多项式 exp)

例题#

三轮#

题意:n 种商品,每种商品体积为 vi,都有 105 件,输出凑成 1m 的体积的总方案数。

对质数 19260817 取模 。(n,m,vi5×104

vi 至少为 1,可认为每种商品都有无穷件。

考虑其生成函数 Fi(x)=1+xvi+x2vi+=11xvi

所以 G(x)=i=1k11xvi

由于 vi 很大,无法分治 ntt 求解。

将积化为和,两边取对数。

ln(G(x))=i=1kln11xvi=i=1kln (1xvi)=i=1kj1xvijj


第二类斯特林数#

第二类斯特林数(斯特林子集数){nk},也可记做 S2(n,k),表示将 n 个两两不同的元素,划分为 k 个互不区分的非空子集的方案数。

递推关系#

我们插入一个新元素时,有两种方案:

  • 将新元素放入一个现有的非空子集,有 k{n1k} 种方案。
  • 将新元素单独放入一个子集,有 {n1k1} 种方案;

{nk}={n1k1}+k{n1k}

边界是 {n0}=[n=0]

通项公式#

不妨先认为 k 个集合互不相同,转化成经典容斥问题,最后乘上 1k!

{nk}=1k!i=0k(1)iCki(ki)n

重要公式#

记下降阶乘幂 xn=x!(xn)!=k=0n1(xk)

则可以利用下面的恒等式将普通幂转化为下降幂:

xn=k=0n{nk}(xk)k!=k=0n{nk}xk

考虑各式组合意义。

  • xn n 个不同的球放入 x 个不同的盒子。
  • (xk) x 个盒子中选出 k 个。
  • {nk} n 个不同的球放入 k 个相同的盒子且都不为空。
  • k! 将选出的 k 个盒子排列。

正确性显然。

指数生成函数#

n0{nk}xnn!=1k!(exp(x)1)k

不妨将盒子染成 k 种不同颜色,每种颜色在长度为 n 的排列中必须出现一次。

对于第一种颜色,F1(x)=x+x22!+x33!+=exp(x)1(没有零次项,因为必须出现)。

全部的合法方案为 [xn]Fi

最后将染色去除,得到 G(x)=1k!(exp(x)1)k

第二类斯特林数·列#

利用公式

n0{nk}xnn!=1k!(exp(x)1)k

其中

f(x)k=exp(lnf(x)k)=exp(klnf(x))

如果 [x0]f(x)1 则不能直接求对。

考虑给每项除以一个公因式 aixi,满足第 i 项是系数非 0 的最低项。

最后再乘上 (aixi)k

P5396 第二类斯特林数·列submission

EGF 求得的系数为 ann!

第二类斯特林数·行#

{nk}=1k!i=0k(1)i(ki)(ki)n=i=0k(1)ii!(ki)n(ki)!

F(x)=i0(1)ii!xi, G(x)=i0ini!xi

于是 S2(n)=FG

P5395 第二类斯特林数·行submission


第一类斯特林数#

k 个轮换的 n 元置换的方案,记为 c(n,k)[nk]

置换向原位置连边,形成若干个环,即轮换。

对于置换 σ=(1234535124),有两个轮换:

递推关系#

[nk]=[n1k1]+(n1)[n1k]

边界:[00]=[11]=1

  • 自己向自己连边,单独成环。

  • 已经有了 k 个环,在 n1 个空隙里选一个插入。

重要公式#

上升幂转普通幂#

xn=k=0n[nk]xk

假设 xn=k=0nf(n,k)xk

联立

{xn=k=0nf(n,k)xkxn=(x+n1)xn1=k=0n1f(n1,k)(x+n1)xk

得到 f 的递推式:f(n,k)=f(n1,k1)+(n1)f(n1,k)

发现 f 与第一类斯特林数递推式相同,初值相同,证毕。

下降幂转普通幂#

xn=k=0n(1)n+k[nk]xk

x 带入 1 中的 x

(x)n=(x)(1x)(n1x)=(1)nx(x1)(xn+1)=(1)nxn=k=0n[nk](x)k

把左式的负号移至右式,即证。

指数生成函数#

等效于 n 个不同球放入 k 个互不区分且非空的盒子,装有 i 个球的盒子有 (i1)! 种不同形态,求方案数。

i 个球只用大小为 i 的盒子的方案为 (i1)!

先将盒子编号,对于每个盒子,有生成函数 F(x)=x+x22!+2!x33!+=ln(1x)

k 个盒子,则 G(x)=1k![ln(1x)]k

第一类斯特林数·列#

[nk]=n![xn]G(x)

P5409 第一类斯特林数·列submission

第一类斯特林数·行#

展开

xn=k=0n[nk]xk

显然可以分治 ntt O(nlog2n) 求(但被出题人卡了)。

考虑倍增。

x2n=xn(x+n)n

f(x)=i=0n1(x+i)=i=0naixi,则 (x+n)n=f(x+n)

问题转化为已知多项式 f(x),快速求 f(x+c)

i=0nai(x+c)i=i=0naij=0i(ij)xjcij=j=0nxji=jn(ij)aicij=j=0nxjj!i=jnaii!cij(ij)!

第二个求和式可以写成 g(x)=i!aixi, h(x)=cii!xni 的卷积。

[xj]f(x+c)=[xj+n]gh

时间复杂度 T(n)=T(n/2)+O(nlogn)=O(nlogn)

submission

有符号的第一类斯特林数#

定义:S1(n,k)=(1)n+k[nk]

重要公式

xn=k=0nS1(n,k)xk

指数生成函数

i0S1(n,k)xnn!=1k![ln(1+x)]k

两类斯特林数的关系及斯特林反演#

k0(1)n+k[nk]{km}=[n=m]k0(1)k+m{nk}[km]=[n=m]

证明:

对于公式 xn=k=0n{nk}xk,可以写成向量之积:

(x0x1xn)=(s2(0,0)s2(1,0)s2(1,1)s2(n,0)s2(n,1)s2(n,2)s2(n,n))(x0x1xn)

对于 有符号 第一类斯特林数:

(x0x1xn)=(s1(0,0)s1(1,0)s1(1,1)s1(n,0)s1(n,1)s1(n,2)s1(n,n))(x0x1xn)

我们有 S1×S2=I

则在 S1 中取一行 n,在 S2 中取一列 m,两个向量之积为 1 当且仅当 m=n

k0(1)n+k[nk]{km}=[n=m]

斯特林反演

fn=i=0n{ni}gign=i=0n(1)n+i[ni]fi

证明:

fn=i=0n[i=n]fi=i=0nk0n(1)k+i{nk}[ki]fi=k0n{nk}i=0n(1)k+i[ki]fi=k0n{nk}gk


整数拆分#

nk 无序拆分#

n 个无标号球分配到 k 个无标号盒子的方案数,满足每个盒子不为空,记为 p(n,k)

递推关系#

p(n,k)=p(n1,k1)+p(nk,k)

假设第 i 个盒子(第 i 行)里有 ai 个球,满足 a1a2ak1

分两部分考虑。

  • k 个盒子有且仅有新加入的一个球,方案为 p(n1,k1)
  • k 个盒子不止一个球,把黄色一列去掉,方案为 p(nk,k)

常生成函数#

考虑上图中有多少有 i 个球的列。

i 个球时,只能有 1 列,一种方案。

2i 个球时,只能有 2 列,一种方案。

因此其常生成函数为 Fi(x)=1+xi+x2i+=11xi

Gk=i=1kFi

由于至少要有一列为 k,所以

Pk(x)=Gk(x)Gk1(x)=xki=1k11xi

  • 快速求 Pk 的前 n 项:对后式先取 lnexp,详见 形式幂级数的更多运算-例题-三轮

    i=1kln11xi=i=1kj1xijj

n 的无序拆分#

n 个无标号球分配到一些无标号盒子的方案数,满足每个盒子不为空,记为 p(n)

可知 p(n)=k=1np(n,k)

常生成函数#

延续上板的思考方向,行的个数没有限制,所以只要一些长度为 i 的凑出 n 个球即可。

那么

P(x)=i111xi=expi1j1xijj=expn1dnxnd

LOJ6268. 分拆数submission

递推关系#

证明繁琐,当结论记。

p(n)=k1(1)k1[p(n3k2k2)+p(n3k2+k2)]

产生贡献的项只有 O(N) 个,复杂度 O(NN)

主要运用于模数不友好的情况。


生成函数模型#

分配问题总结#

n 个球放入 k 个盒子,球方案数。

n 个球k 个盒子盒子可以为空每个盒子至少一个球有标号有标号knk!{nk}有标号无标号i=1k{nk}{nk}无标号有标号(n+k1k1)(n1k1)无标号无标号p(n+k,k)p(n,k)

p(n + k, k):把每行之前都加一个球,则 k 个盒子都不为空

分配问题(加强版1)#

n 个球放入 k 个盒子,装有 i 个球的盒子有 fi 种形态,不同形态算不同方案,有多少种方案?

{fi}i1 的常生成函数 F(x)=i1fixi,其指数生成函数 E(x)=i1fii!xi(有没有常数项取决于需不需要每个盒子都有球)。

n 个球k 个盒子有标号有标号e.g.f=E(x)k有标号无标号e.g.f=1k!E(x)k无标号有标号o.g.f=F(x)k

分配问题(加强版2)#

n 个球放入一些盒子且每个盒子都不为空,装有 i 个球的盒子有 fi 种形态,不同形态算不同方案,有多少种方案?

{fi}i1 的常生成函数 F(x)=i1fixi,其指数生成函数 E(x)=i1fii!xi

即对加强版 1 做个 i0

n 个球k 个盒子有标号有标号e.g.f=11E(x)k有标号无标号e.g.f=expE(x)k无标号有标号o.g.f=11F(x)k无标号无标号o.g.f=i1(11xi)fi=expj11jF(xj)

无标号/无标号:

考虑大小为 i 的盒子的常生成函数(即 [xn] 表示只用大小为 i 的盒子放入 n 个球的方案)。

对于盒子的第一种形态 g(x)=1+xi+x2i+

以此类推,其常生成函数为 gfi1(x)

所以无标号/无标号的常生成函数为

i1(11xi)fi=expi1fij1xijj=expj11ji1fixij=expj11jF(xj)

例题#

CF961G Partitions#

题意:

给出 n 个物品,每个物品有一个权值 wi

定义一个集合 S 的权值 W(S)=|S|xSwx

定义一个划分的权值为 W(R)=SRW(S)

求将 n 个物品划分成 k 个集合的所有方案的权值和。答案对 109+7 取模。

n,k2×105wi109


感性理解,每个数的贡献是相同的,所以答案一定是 Pai 的形式。

枚举当前元素所在集合大小。

P=i=1ni(n1i1){nik1}从剩下 n1 个里选出在当前集合的,再划分其余的 ni个。=i=1ni(n1)!(i1)!(ni)!j=0k1(1)j(kj1)nij!(kj1)!=j=0k1(1)j1j!(kj1)!i=1ni(n1)!(kj1)ni(i1)!(ni)!

单独计算后面一个求和式,把 kj1 当作常数 t

i=1ni(n1)!tni(i1)!(ni)!=i=1n(n1)!tni(i1)!(ni)!(i+11)=i=1n(n1)!tni(i1)!(ni)!(i1+1)=(n1)i=1n(n2)!(i2)!(ni)!tni+i=1n(n1)!(i1)!(ni)!tni=(n1)i=1n(n2ni)tni+i=1n(n1ni)tni=(n1)(t+1)n2+(t+1)n1

时间复杂度 O(nlogn)

还有一种更为精巧的解法:

考虑 wi 会对答案产生多少次贡献?

在一个大小为 |S| 的集合中,wi 贡献 |S| 次,等效于集合中每一个元素都使 wi 产生一次贡献。

分两部分讨论

  • 自己使自己的产生的贡献,显然每一种划分产生一次,共 {nk} 次。

  • 其他元素使自己的产生的贡献,把其余 n1 个球划分好 k 个集合,对于每种划分,当前元素可以任选一个集合放进去,因此 n1 个物品都会有一次贡献,共 (n1){n1k}

综上 P={nk}+(n1){n1k}

submission

CF960G Bandit Blues#

题意:给定 n, a, b,定义 A 为一个排列中是前缀最大值的数的个数,定义 B 为一个排列中是后缀最大值的数的个数,求长度为 n 的排列中满足 A=aB=b 的排列个数。n105,对 998244353 取模。

首先,排列里最大值一定同时是前缀最大和后缀最大,如果存在 a/b=0,无解。

f(i,j) 表示 i 个数的排列有 j 个前缀最大的方案。

不妨从大到小一个一个填。

  • 填到序列的最前面,一定是前缀最大,有 f(i1,j1) 种情况。
  • 由于所有数都比他大,填到任意一个数后面,有 (i1)f(i1,j) 种情况。

所以 f(i,j)=f(i1,j1)+(n1)f(i1,j),恰为第一类斯特林数的递推式,由于边界相同,所以 f(i,j)=[ij]

枚举最大值左边的元素个数。

则答案为 i=0n1(n1i)[ia1][ni1b1]

可以直接求出两列斯特林数,但形式仍不够优美。

考虑式子的组合意义:选出 i 个排成 a1 个环 剩下 n1i 个排成 b1 个环。

也就是 n1 个元素排成 a+b2 个环,选其中 a1 个分给左边。

于是答案化简为 [n1a+b2](a+b2a1)

第一列斯特林数没有实用的通项公式,随便求出一行或一列即可。

submission

附录:模板#

#include<bits/stdc++.h>
using namespace std;

using ll = long long;
constexpr int N = 5e5 + 5, P = 998244353;		// g = 3

ll gg[30], ig[30], fac[N], ifac[N], inv[N];

ll qpow(ll a, ll b) {
	ll ret = 1;
	while(b) {
		if(b & 1) ret = ret * a % P;
		b >>= 1;
		a = a * a % P;
	}
	return ret;
}

void init() {
	gg[0] = ig[0] = 1;
	for(int i = 1; i < 30; ++ i) gg[i] = qpow(3, (P - 1) / (1 << i));
	for(int i = 1; i < 30; ++ i) ig[i] = qpow(gg[i], P - 2);
	fac[0] = 1;
	for(int i = 1; i < N; ++ i) {
		inv[i] = (i == 1) ? 1 : -(P / i) * inv[P % i] % P;
		fac[i] = fac[i - 1] * i % P;
	}
	ifac[N - 1] = qpow(fac[N - 1], P - 2);
	for(int i = N - 1; i >= 1; -- i) {
		ifac[i - 1] = ifac[i] * i % P; 
	}
}

int rev[N];

void ntt(ll *a, int tot, int ty) {
	for(int i = 0; i < tot; ++ i) {
		if(i < rev[i]) {
			swap(a[i], a[rev[i]]);
		}
	}
	int t = 1;
	for(int mid = 1; mid < tot; mid <<= 1, ++ t) {
		ll g1 = gg[t];
		if(ty == -1) {
			g1 = ig[t];
		}
		for(int i = 0; i < tot; i += mid * 2) {
			ll gk = 1;
			for(int j = 0; j < mid; ++ j, gk = gk * g1 % P) {
				ll x = a[i + j], y = a[i + j + mid];
				a[i + j] = (x + gk * y) % P;
				a[i + j + mid] = (x - gk * y) % P;
			}
		}
	}
	if(ty == -1) {
		ll tmp = qpow(tot, P - 2);
		for(int i = 0; i < tot; ++ i) {
			a[i] = a[i] * tmp % P;
		}
	}
}


void polymul(ll *f, const ll *g, const ll *h, int n, int m) {		// 项数,非次数
	static ll a[N], b[N];
	int tot = 1, bit = 0;
	while(tot < n + m - 1) ++ bit, tot <<= 1;
	for(int i = 0; i < tot; ++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << bit - 1);
	memcpy(a, g, n * 8), memset(a + n, 0, (tot - n) * 8);
	memcpy(b, h, m * 8), memset(b + m, 0, (tot - m) * 8);
	ntt(a, tot, 1), ntt(b, tot, 1);
	for(int i = 0; i < tot; ++ i) a[i] = a[i] * b[i] % P;
	ntt(a, tot, -1);
	memcpy(f, a, tot * 8);
}

void polyinv(ll *f, const ll *h, int n){
	static ll d[N], g[N];
	int V = 1; while(V < n + n - 1) V <<= 1;
	memcpy(d, h, n * 8), memset(d + n, 0, (V - n) * 8);
	memset(f, 0, V * 8), memset(g, 0, V * 8);
	f[0] = qpow(h[0], P - 2);
	for(int w = 2; w / 2 < n; w <<= 1){
		memcpy(g, d, w * 8);
		for(int i = 0; i < w * 2; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? w : 0);
		ntt(f, w << 1, 1), ntt(g, w << 1, 1);
		for(int i = 0; i < w * 2; ++i) f[i] = (2 - f[i] * g[i]) % P * f[i] % P;
		ntt(f, w << 1, -1);
		memset(f + w, 0, w * 8);
	}
	memset(f + n, 0, (V - n) * 8);
}


void polysqrt(ll *f, const ll *h, int n){
	static ll d[N], g[N], f_inv[N];
	int V = 1; while(V < n + n - 1) V <<= 1;
	memcpy(d, h, n * 8), memset(d + n, 0, (V - n) * 8);
	memset(f, 0, V * 8), memset(g, 0, V * 8), memset(f_inv, 0, V * 8);
	f[0] = 1;
	constexpr int i2 = 499122177;
	for(int w = 2; w / 2 < n; w <<= 1){
		memcpy(g, d, w * 8);
		for(int i = 0; i < w * 2; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? w : 0);
		polyinv(f_inv, f, w);
		ntt(f, w << 1, 1), ntt(g, w << 1, 1), ntt(f_inv, w << 1, 1);
		for(int i = 0; i < w * 2; ++i) f[i] = (f[i] + f_inv[i] * g[i]) % P * i2 % P;
		ntt(f, w << 1, -1);
		memset(f + w, 0, w * 8);
	}
	memset(f + n, 0, (V - n) * 8);
}


void polyder(ll *f, const ll *h, int n) {							// 项数,非次数
	for(int i = 0; i <= n - 1; ++ i) {
		f[i] = (i + 1) * h[i + 1] % P;
	}
	f[n - 1] = 0;
}


void polyint(ll *f, const ll *h, int n) {							// 项数,非次数
	for(int i = n - 1; i >= 1; -- i) {
		f[i] = h[i - 1] * inv[i] % P; 
	}
	f[0] = 0;
}


void polyln(ll *f, const ll *h, int n) {				// h[0] = 1
	static ll a[N], b[N];
	polyinv(a, h, n);
	polyder(b, h, n);
	polymul(a, a, b, n, n);
	polyint(f, a, n);	
}

void polyexp(ll *f, const ll *h, int n) {				// h[0] = 0
	static ll d[N], g[N], f_ln[N];
	int V = 1; while(V < n + n - 1) V <<= 1;
	memcpy(d, h, n * 8), memset(d + n, 0, (V - n) * 8);
	memset(f, 0, V * 8), memset(g, 0, V * 8), memset(f_ln, 0, V * 8);
	f[0] = 1;	//ln(h[0])
	for(int w = 2; w / 2 < n; w <<= 1){
		memcpy(g, d, w * 8);
		for(int i = 0; i < w * 2; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? w : 0);
		polyln(f_ln, f, w);
		ntt(f, w << 1, 1), ntt(g, w << 1, 1), ntt(f_ln, w << 1, 1);
		for(int i = 0; i < w * 2; ++i) f[i] = (1 - f_ln[i] + g[i]) % P * f[i] % P;
		ntt(f, w << 1, -1);
		memset(f + w, 0, w * 8);
	}
	memset(f + n, 0, (V - n) * 8);
}

void polypow(ll *f, ll *h, string K, int n) {
	int cur = 0;
	while(cur < n && h[cur] == 0) ++ cur;
	ll k1 = 0, k2 = 0;
	for(auto ch : K) {
		if((k1 * 10 + ch - '0') * cur >= n || cur == n) {
			for(int i = 0; i < n; ++ i) f[i] = 0;
			return;
		}
		k1 = (k1 * 10 + ch - '0') % P;
		k2 = (k2 * 10 + ch - '0') % (P - 1);
	}
	
	ll h_cur = h[cur], icur = qpow(h_cur, P - 2);
	for(int i = cur; i < n; ++ i) {
		f[i - cur] = h[i] * icur % P;
	}
	polyln(f, f, n - cur);
	for(int i = 0; i < n - cur; ++ i) f[i] = f[i] * k1 % P;
	polyexp(f, f, n - cur);
	h_cur = qpow(h_cur, k2);
	for(int i = n - 1; i >= k1 * cur; -- i) f[i] = f[i - k1 * cur] * h_cur % P;
	for(int i = 0; i < k1 * cur; ++ i) f[i] = 0;
}

void Stirling2ndCol(ll *f, int n, int k) { //S2(0...n, k)
	for(int i = 1; i <= n; ++ i) {
		f[i] = ifac[i];
	}
	polypow(f, f, to_string(k), n + 1);
	
	for(int i = 0; i <= n; ++ i) {
		f[i] = f[i] * ifac[k] % P * fac[i] % P;
	}
}

void Stirling2ndRow(ll *f, int n) { // S2(n, 0...n)
	static ll g[N];
	for(int i = 0; i <= n; ++ i) {
		f[i] = (i & 1) ? -ifac[i] : ifac[i];
		g[i] = qpow(i, n) * ifac[i] % P;
	}
	polymul(f, f, g, n + 1, n + 1);
}

void Stirling1stCol(ll *f, int n, int k) { //c(0...n, k)
	for(int i = 2; i <= n; ++ i) {
		f[i] = 0;
	}
	f[0] = 1, f[1] = -1;
	polyln(f, f, n + 1);
	for(int i = 0; i <= n; ++ i) f[i] = -f[i];
	
	polypow(f, f, to_string(k), n + 1);
	for(int i = 0; i <= n; ++ i) {
		f[i] = f[i] * ifac[k] % P * fac[i] % P;
	}
}

void Offset(ll *f, ll *h, int n, int c) {		// f <-- h(x + c) 偏移   n次多项式
	static ll g[N], d[N];
	for(int i = 0; i <= n; ++ i) {
		g[i] = fac[i] * h[i] % P;
		d[i] = (i == 0 ? 1 : d[i - 1] * c % P);
	}
	for(int i = 0; i <= n; ++ i) d[i] = d[i] * ifac[i] % P;
	reverse(d, d + n + 1);
	polymul(f, g, d, n + 1, n + 1);
	for(int i = 0; i <= n; ++ i) {
		f[i] = f[i +  n] * ifac[i] % P;
	}
	memset(f + n + 1, 0, n);
}

void Stirling1stRow(ll *f, int n) { // c(n, 0...n)
	static int stk[30], tp;
	static ll g[N];
	tp = 0;
	while(n) {
		stk[++ tp] = n;
		n >>= 1;
	}
	f[0] = 0, f[1] = 1;
	n = 1;
	while(-- tp) {
		Offset(g, f, n, n);
		polymul(f, f, g, n + 1, n + 1);
		n <<= 1;
		if(stk[tp] == n + 1) {
			f[n + 1] = f[n];
			for(int i = n; i >= 1; -- i) {
				f[i] = (f[i] * n + f[i - 1]) % P;
			}
			++ n;
		}
	}
}

void Partition(ll *f, int n) {
	memset(f, 0, (n + 1) * 8);
	for(int i = 1; i <= n; ++ i) {
		for(int j = i; j <= n; j += i) {
			f[j] = (f[j] + inv[i]) % P;
		}
	}
	polyexp(f, f, n + 1);
}

ll n, k, f[N];

int main() {
	cin.tie(0)->sync_with_stdio(0);
	init();
	
	return 0;
}
posted @   Lu_xZ  阅读(72)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示