斯特林数(Educational Codeforces Round 86 (Rated for Div. 2) E. Placing Rooks)

第一类斯特林数:
将n个不同元素分为m个环(圆排列)的方案数。(细分还有带符号和不带符号的第一类斯特林数)
推导: dp(m,n)表示n个不同元素组成m个环的方案数,这边求方案数,有点计数dp的味道,事实也是,最后一个元素可以分两个状态,一个自己成环,另一个加入别人,如果n个元素构成了m-1个圆排列,第n+1个元素独自构成一个圆排列;如果n个元素构成了m个圆排列,将第n+1个元素插入到任意元素的左边;n个元素构成的x圆排列,x小于m-1时就根本构成不了m个圆排列, 总方案数:,dp(m,n+1)=dp(m-1,n)+dp(m,n)*(n)。
在这里插入图片描述

∑ k = 0 n s ( n , k ) \sum_{k=0}^ns(n,k) k=0ns(n,k)=n! (s(n,k)表示n个不同元素k个圆排列)
注意s(0,0)=1。

第二类斯特林数:
将n个不同的元素分为m个非空集合的方案数。
推导: 和第一类Stirling数不同的是,集合内是不考虑次序的,而圆排列是有序的。常常用于解决组合数学中几类放球模型。描述为:将n个不同的球放入m个无差别的盒子中,要求盒子非空,有几种方案?
推导的切入点和第一类基本一致。
如果n个元素构成了m-1个集合,那么第n+1个元素单独构成一个集合。
如果n个元素已经构成了m个集合,将第n+1个元素插入到任意一个集合
dp(m,n+1)=dp(m-1,n)+dp(m,n)*(m)。
在这里插入图片描述

∑ k = 0 n S ( n , k ) \sum_{k=0}^nS(n,k) k=0nS(n,k)= B n B_n Bn B n B_n Bn是贝尔数。(S(n,k)表示n个不同元素拆分为k个集合的方案数)

第二类斯特林数的通项公式: S(n,m)= 1 m ! \frac{1}{m!} m!1 ∑ k = 0 m C m k ( − 1 ) k ( m − k ) n \sum_{k=0}^mC_m^k(-1)^k(m-k)^n k=0mCmk(1)k(mk)n
这里多说一句组合的恒等式C(n,m)=C(n-1,m-1)+C(n-1,m),定义推理就能得证。
推导:
这边为了区分集合,先标号,最后除以 m ! m! m!即可。
从空盒子入手,先不考虑空盒子一共有 m n m^n mn的方案数,其中包含了空盒子的方案数,设空盒子数量是h时的方案数是T(h)种,即 m ! ∗ S ( n , m ) = T ( 0 ) m!*S(n,m)=T(0) m!S(n,m)=T(0) m n = ∑ h = 0 m T ( h ) m^n=\sum_{h=0}^mT(h) mn=h=0mT(h)
需要解决空盒数大于0的情况。先选定一个空盒子剩余的随便放置,则: C m 1 ∗ ( m − 1 ) n C_m^1*(m-1)^n Cm1(m1)n。那么空盒数为h的情况被计算了几次?由于空盒必须要有一个出现在被选定的位置,重复次数是 C h 1 C_h^1 Ch1。之后看一般的情况,计算空盒数不小于k的情况有 C m k ∗ ( m − k ) n C_m^k*(m-k)^n Cmk(mk)n,可得 C m k ∗ ( m − k ) n = ∑ h = 0 m C h k T ( h ) C_m^k*(m-k)^n=\sum_{h=0}^mC_h^kT(h) Cmk(mk)n=h=0mChkT(h)
继续为了消除系数 ∑ k = 0 h ( − 1 ) k C h k = ( 1 − 1 ) h = 0 h \sum_{k=0}^h(-1)^kC_h^k=(1-1)^h=0^h k=0h(1)kChk=(11)h=0h;那么继续 ∑ k = 0 m ( − 1 ) k C m k ( m − k ) n = ∑ k = 0 m ( − 1 ) k ∑ h = 0 m C h k T ( h ) = ∑ h = 0 m T ( h ) ∑ k = 0 m ( − 1 ) k C h k = ∑ h = 0 m T ( h ) 0 h = T ( 0 ) = m ! S ( n , m ) \sum_{k=0}^m(-1)^kC_m^k(m-k)^n=\sum_{k=0}^m(-1)^k\sum_{h=0}^mC_h^kT(h)=\sum_{h=0}^mT(h)\sum_{k=0}^m(-1)^kC_h^k=\sum_{h=0}^mT(h)0^h=T(0)=m!S(n,m) k=0m(1)kCmk(mk)n=k=0m(1)kh=0mChkT(h)=h=0mT(h)k=0m(1)kChk=h=0mT(h)0h=T(0)=m!S(n,m)
解得
S(n,m)= 1 m ! ∑ k = 0 m C m k ( − 1 ) k ( m − k ) n \frac{1}{m!}\sum_{k=0}^mC_m^k(-1)^k(m-k)^n m!1k=0mCmk(1)k(mk)n

这是一种证明方法,但我个人更倾向公式法的推理证明,过程实在不好打(可能是懒),我写在草稿本上了,可能比较丑,但条理还是比较清晰的。有错误的地方还请提醒一下。

在这里插入图片描述

最后带一条两类Stirling数之间得关系:
∑ k = 0 n S ( n , k ) s ( k , m ) = ∑ k = 0 n s ( n , k ) S ( k , m ) \sum_{k=0}^nS(n,k)s(k,m)=\sum_{k=0}^ns(n,k)S(k,m) k=0nS(n,k)s(k,m)=k=0ns(n,k)S(k,m)
类似于矩阵的对称转置关系。

附上一道例题其他例题待更。
题目:Educational Codeforces Round 86 (Rated for Div. 2) E. Placing Rooks
题意:在一个n*n的棋盘里,放n个车,确保n * n的棋盘里每一个格点都能被车攻击,并且保证k对车能相互攻击(中间没有其他车就是一对可攻击的在同一行或者同一列),车就是象棋里的车能走完一列或者一行,求总的情况数。

先考虑按行放n个就必然全部能攻击到,列本质和行相同乘以2就行。
以按行放的情况我们现在只考虑列方向就可以了。如果要求k的话,存先考虑最大有车的列数,k+1个车在同一列,这样必成立,其余n-(k+1)在其他列,那么会发现一共是n-k列,基于这种情况无论怎么移动车到另一列都是成立的一方+1一方减1,总和k未变,一旦超出n-k,那么无法达到k个相对。
这样就可直接推导了, C n n − k ∗ S ( n , n − k ) ∗ ( n − k ) ! C_n^{n-k}*S(n,n-k)*(n-k)! CnnkS(n,nk)(nk)!,S(n,n-k)就是第二类斯特林数。k=0时,显然是n!。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
const int mod=998244353;
ll num[200100];
ll quickpow(ll a,ll n)
{
    ll res=1;
    while(n)
    {
        if(n&1)
        {
            res=(res*a)%mod;
        }
        a=(a*a)%mod;
        n>>=1;
    }
    return res;
}
ll C(ll x,ll n)
{
    ll temp=num[n]*(quickpow((num[n-x]*num[x])%mod,mod-2));
    temp%=mod;
    return temp;
}
int main()
{
    ll n,k,i,j;
    scanf("%I64d %I64d",&n,&k);
    num[1]=1;
    num[0]=1;
    for(i=2;i<=n;i++)
    {
        num[i]=(num[i-1]*i)%mod;
    }
    if(k>=n)
    {
        printf("0");
        return 0;
    }
    if(k==0)
    {
        printf("%I64d",num[n]);
        return 0;
    }
    ll ans=0;
    for(i=0;i<n-k;i++)
    {
        ans=(ans+(quickpow(n-k-i,n)%mod)*(C(i,n-k)%mod)*(quickpow(-1,i))+mod)%mod;
    }
    ans=ans*C(n-k,n);
    ans%=mod;
    ans=(ans*2)%mod;
    printf("%I64d",ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值