第一类斯特林数求自然数幂和学习小记

本文介绍了第一类斯特林数的概念、递推公式,并探讨了其在求解自然数幂和问题中的应用。通过递推关系,得出求自然数幂和的算法,最终给出实现该算法的代码片段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目标

∑i=0nik\sum_{i=0}^ni^ki=0nik

前置技能

第一类斯特林数

第一类斯特林数s(n,m)s(n,m)s(n,m)定义为有n个人,编号分别为1-n,排成m个循环排列的方案数。
递推式:s(n,m)=s(n−1,m−1)+(n−1)∗s(n−1,m)s(n,m)=s(n-1,m-1)+(n-1)*s(n-1,m)s(n,m)=s(n1,m1)+(n1)s(n1,m)
证明:若第n个人自己排成一个循环排列,则方案数为s(n−1,m)s(n-1,m)s(n1,m);若第n个人加入到原来的循环排列中,则可以放在任意一个人的左边,方案数为(n−1)∗s(n−1,m)(n-1)*s(n-1,m)(n1)s(n1,m)

另一种形式

众所周知,Cnk表示从n件物品中选出k件的方案数,众所周知,C_n^k表示从n件物品中选出k件的方案数,CnknkP_n^k表示从n件物品中选出k个排列的方案数表示从n件物品中选出k个排列的方案数nk
显然有Cnk=Pnkk!C_n^k=\frac{P_n^k}{k!}Cnk=k!Pnk
根据定义我们可以得到Pnk=n∗(n−1)∗...∗(n−k+1)P_n^k=n*(n-1)*...*(n-k+1)Pnk=n(n1)...(nk+1)
展开后可得Pnk=S(k,k)∗nk−S(k,k−1)∗nk−1...P_n^k=S(k,k)*n^k-S(k,k-1)*n^{k-1}...Pnk=S(k,k)nkS(k,k1)nk1...
所以有Pnk=∑i=0k(−1)k−i∗s(k,i)∗niP_n^k=\sum_{i=0}^k(-1)^{k-i}*s(k,i)*n^iPnk=i=0k(1)kis(k,i)ni
这个才是第一类斯特林数的定义。
所以第一类斯特林数S就是排列数公式的展开式的系数,也是如上所述的那个东西。

第一类斯特林数的递推

边界条件:s(p,0)=0(p>0),s(p,p)=1(p>=0)s(p,0)=0(p>0),s(p,p)=1(p>=0)s(p,0)=0(p>0)s(p,p)=1(p>=0)

一个小结论

∑i=0nCik=Cn+1k+1\sum_{i=0}^nC_i^k=C_{n+1}^{k+1}i=0nCik=Cn+1k+1
证明:因为Cn+1k+1=Cnk+Cnk+1C_{n+1}^{k+1}=C_n^{k}+C_n^{k+1}Cn+1k+1=Cnk+Cnk+1,等式两边可以同时把CnkC_n^kCnk消掉,然后就又得到了一个相同形式的式子。所以通过归纳法便可以证明这个结论。

求自然数幂和

我们设Sk(n)=∑i=0nikS_k(n)=\sum_{i=0}^ni^kSk(n)=i=0nik
根据第一类斯特林数的定义,有Cnk=Pnkk!=∑i=0k(−1)k−i∗s(k,i)∗nik!C_n^k=\frac{P_n^k}{k!}=\frac{\sum_{i=0}^k(-1)^{k-i}*s(k,i)*n^i}{k!}Cnk=k!Pnk=k!i=0k(1)kis(k,i)ni
把式子中的nkn^knk提出来,得nk=Cnk∗k!−∑i=0k−1(−1)k−i∗s(k,i)∗nin^k=C_n^k*k!-\sum_{i=0}^{k-1}(-1)^{k-i}*s(k,i)*n^ink=Cnkk!i=0k1(1)kis(k,i)ni
那么Sk(n)=∑i=0nikS_k(n)=\sum_{i=0}^ni^kSk(n)=i=0nik
=∑i=0n(Cik∗k!−∑j=0k−1(−1)k−j∗s(k,j)∗ij)=\sum_{i=0}^n(C_i^k*k!-\sum_{j=0}^{k-1}(-1)^{k-j}*s(k,j)*i^j)=i=0n(Cikk!j=0k1(1)kjs(k,j)ij)
=k!∗∑i=0nCik−∑i=0n∑j=0k−1(−1)k−j∗s(k,j)∗ij=k!*\sum_{i=0}^nC_i^k-\sum_{i=0}^n\sum_{j=0}^{k-1}(-1)^{k-j}*s(k,j)*i^j=k!i=0nCiki=0nj=0k1(1)kjs(k,j)ij
=k!∗Cn+1k+1−∑j=0k−1(−1)k−j∗s(k,j)∗Sj(n)=k!*C_{n+1}^{k+1}-\sum_{j=0}^{k-1}(-1)^{k-j}*s(k,j)*S_j(n)=k!Cn+1k+1j=0k1(1)kjs(k,j)Sj(n)
=Pn+1k+1k+1−∑j=0k−1(−1)k−j∗s(k,j)∗Sj(n)=\frac{P_{n+1}^{k+1}}{k+1}-\sum_{j=0}^{k-1}(-1)^{k-j}*s(k,j)*S_j(n)=k+1Pn+1k+1j=0k1(1)kjs(k,j)Sj(n)

于是就可以愉快地O(k2)O(k^2)O(k2)递推Sk(n)S_k(n)Sk(n)啦。边界条件S1(n)=n∗(n+1)2S_1(n)=\frac{n*(n+1)}{2}S1(n)=2n(n+1)

因为Pn+1k+1=(n+1)∗n∗(n−1)∗...∗(n−k+1)P_{n+1}^{k+1}=(n+1)*n*(n-1)*...*(n-k+1)Pn+1k+1=(n+1)n(n1)...(nk+1),而连续k+1个数中必然有一个是k+1的倍数,所以在用这种方法求自然数幂和时,由于不存在除法,所以并不用考虑模数是什么。

代码

int get_sum(int n)
{
	sum[1]=(LL)n*(n+1)/2%MOD;
	for (int i=2;i<=k;i++)
	{
		sum[i]=1;
		for (int j=0;j<i+1;j++)
			if ((LL)(n-j+1)%(i+1)==0) sum[i]=(LL)(n-j+1)/(i+1)*sum[i]%MOD;
			else sum[i]=(LL)(n-j+1)*sum[i]%MOD;
		for (int j=1;j<i;j++)
			if ((i-j)%2==0) (sum[i]-=(LL)s[i][j]*sum[j]%MOD)%=MOD;
			else (sum[i]+=(LL)s[i][j]*sum[j]%MOD)%=MOD;
	}
	return (sum[k]+MOD)%MOD;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值