斯特林数学习笔记
第一类斯特林数
定义
将 个互不相同的元素,划分为 个互不区分的非空轮换的方案数,记为 ,或 。
一个轮换就是一个首尾相接的环形排列。如轮换 ,我们认为 。即,两个可以通过旋转而相互得到的轮换是等价的。需要注意,通过翻转得到的轮换不等价,如:。
第一类斯特林数也可以形象理解为: 个人坐 张圆桌(没有空桌)的方案数。
递推式
。
其中边界为 。
递推式的证明可以考虑组合意义,新加入一个元素时,有两种方案:
-
将该元素单独至于一个轮换中,那么共有 种方案。
-
将该元素加入当任意一个现有的轮换中,共有 种方案。
第二类斯特林数
将 个互不相同的元素,划分为 个互不区分的非空子集的方案数。记为 ,也可记为
递推式
其中边界为 。
递推式的证明可以考虑组合意义,新加入一个元素时,有两种方案:
-
将该元素单独至于一个新集合中,那么共有 种方案。
-
将该元素加入当任意一个现有的轮换中,共有 种方案。
通项公式
利用二项式反演,可以得到第二类斯特林数的通项公式:
建筑师
对于一个 的排列,设有 个数的左边都比它小, 个数的右边都比它小,求满足的排列个数。
组询问,答案对 取模。
。
思路:
不难发现, 一定满足题意,考虑以 为界限,在其左侧的属于 ,在其右侧的属于 。
设第 个位置上的数为 ,。
那么对于一个在 中的数字,一定满足 ,在 中的同理。
设 为前后两个在 中的数,那么对于 ,一定满足 。考虑将 划分为一个区间,那么最终可以得到 个区间,如下图所示:
其中黑色的方块表示该点在 或 中,其中 单独成一个区间。
不难发现,划分为 个区间以后,区间内最大的数一定在区间的最左侧或最右侧。而对于其他的数,则可以全排列。
那么一个区间就等价于一个固定了开头的轮换,于是就可以套用第一类斯特林数,总的划分方案数为 。
而一个区间既可以放在 中,也可以放在 中,将区间分组的方案数就为 。
于是对于每一组询问,答案就是 。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=50010,M=210,mod=1e9+7;
int n,A,B,c[N][M],s[N][M];
int main()
{
s[0][0]=1;for(int i=1;i<N;i++) for(int j=1;j<M;j++) s[i][j]=(s[i-1][j-1]+1ll*(i-1)*s[i-1][j]%mod)%mod;
for(int i=0;i<M;i++) for(int j=0;j<=i;j++) if(!j) c[i][j]=1;else c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
int T;scanf("%d",&T);while(T--) scanf("%d%d%d",&n,&A,&B),printf("%d\n",1ll*c[A+B-2][A-1]*s[n-1][A+B-2]%mod);
return 0;
}
组合数问题
计算
的值。其中 , , 为给定的整数, 为给定的一个 次多项式 。 为组合数,其值为 。
。
思路:
令 。称为 的 次下降幂。
而对于单项式下降幂,与组合数存在以下联系:
考虑将多项式 转换为 的形式。
对于一个单项式 ,与第二类斯特林数存在以下等式:
形象地理解一下, 可以看成是将 个小球分别放到 个盒子当中,允许有空盒子的方案数。那么就可以考虑枚举哪些盒子不为空,然后就可以套用第二类斯特林数。由于盒子是不同的,所以最后还要乘上一个阶层。
于是就可以将 变形:
得到 的表达式:
于是 就可以 预处理出来。
接下来考虑对原式进行变形:
利用上面的组合公式,可以得到:
注意到当 时,后面的组合数没有意义,考虑内层枚举 ,得到:
此时不难发现,内层循环就是一个二项式的展开形式,利用二项式定理,可以得到:
由于题目中满足 ,此时就可以直接枚举得到答案。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=2023;
int n,x,ans,p,m,a[N],b[N],s[N][N];
void Add(int &a,int b){a+=b;if(a>=p) a-=p;}
void Mul(int &a,int b){a=1ll*a*b%p;}
int mul(int a,int b){int res=1;while(b) ((b&1)&&(res=1ll*res*a%p,0)),a=1ll*a*a%p,b>>=1;return res;}
int main()
{
scanf("%d%d%d%d",&n,&x,&p,&m);for(int i=0;i<=m;i++) scanf("%d",&a[i]);s[0][0]=1;
for(int i=1;i<=m;i++) for(int j=1;j<=i;j++) s[i][j]=(s[i-1][j-1]+1ll*j*s[i-1][j]%p)%p;
for(int i=0;i<=m;i++) for(int j=i;j<=m;j++) Add(b[i],1ll*a[j]*s[j][i]%p);int n_i=1,x_i=1;
for(int i=0;i<=m;i++) Add(ans,1ll*b[i]*n_i%p*x_i%p*mul(x+1,n-i)%p),Mul(n_i,n-i),Mul(x_i,x);printf("%d\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」