zoukankan      html  css  js  c++  java
  • 关于斯特林数

    第一类斯特林数

    意义:将n个不同的元素划分成m个环的方案数。

    递推式:

    (通常第一类斯特林数用小写 $s$ 表示,而第二类斯特林数用大写 $S$ 表示)

    $$
    s(n,m)=s(n-1,m-1)+s(n-1,m) imes (n-1)
    $$

    关于定义:

    $P_{n}^{k} $ 表示从n个为物品中选出k个排列的方案数,显然有:
    $$
    C_{n}^{k}=frac{P_{n}^{k}}{k!}
    $$

    $$
    P_{n}^{k}=n imes (n-1) imes (n-2) imes...... imes (n-k+1)
    $$

    考虑把式子展开,

    对于第 $i$ 项(即 $n^{i}$ 项)的系数表示成有 $i$ 项选择 $n$ ,剩余的 $k-i$ 项选择 $k$ 个常数,所以对于第 $i$ 项系数是选择的 $i$ 个常数的乘积的和。

    我们考虑把斯特林数左右反过来,因为每一行数的个数都差 $1$ ,这里的定义是向左看齐。
    $$
    s_{t}(n,m)=s_{t}(n-1,m-1) imes (n-1)+s_{t}(i-1,j)
    $$
    这样就这个递推式就可以理解为前 $i$ 个数我选了 $m$ 个数的乘积的和,因为可以选择的常数其实是从 $0$ ~ $k-1$ ,所以乘的系数是 $(n-1)$ 。

    所以呢
    $$
    P_{n}^{k}=s(k,k) imes n^{k}-s(k,k-1) imes n^{k-1}...
    $$

    $$
    P_{n}^{k}=sum _{i=0}^{k}(-1)^{k-i} imes s(k,i) imes n^{i}
    $$

    所以第一类斯特林数是排列数公式的展开系数。

    同时也得到第一类有符号斯特林数的多项式形式
    $$
    n(n-1)(n-2)...(n-k+1)=sum_{i=0}^{k}(-1)^{k-i}S(k,i)n^{i}
    $$
    也可以推出第一类无符号斯特林数的多项式形式
    $$
    n(n+1)(n+2)...(n+k-1)=sum_{i=0}^{k}S(k,i)n^{i}
    $$
    当前得到的式子,左边用分治 $NTT$ 求出第 $i$ 项系数即为 $S(k,i)$ ,这个求法效率是 $O(nlog^{2}n)$

    接下来 $O(nlogn)$ 的倍增做法,

    考虑当我们已经知道 $S_{n}$ 的生成函数为
    $$
    prod_{i=0}^{n-1}(x+i)=sum f_{i}x^{i}
    $$
    以此来研究 $S_{2n}$ 的生成函数
    $$
    prod_{i=n}^{2n-1}(x+i)\
    =prod_{i=0}^{n-1}(x+i+n)\
    =sum_{i=0}^{n}f_{i}(x+n)^{i}
    $$
    再用二项式定理展开:
    $$
    =sum_{i=0}^{n}f_{i}sum C_{i}^{j}x^{j}n^{i-j}\
    =sum_{j=0}^{n}sum_{i=j}^{n}f_{i}frac{i!}{j!(i-j)!}x^{j}n^{i}n^{-j}\
    =sum_{j=0}^{n}frac{1}{j!n^{j}}(sum_{i-j}^{n}frac{i!}{(i-j)!}n^{i})x^{j}
    $$
    那么对于括号内的多项式 $NTT$ $O(nlogn)$ 得到,对于奇数的情况先求出 $2n$ 的答案,再单独乘最后一项。

    以下代码:

    #include<bits/stdc++.h>
    #define il inline
    #define LL long long
    #define _(d) while(d(isdigit(ch=getchar())))
    using namespace std;
    const int N=3e5+5,p=998244353;
    int t1[N],t2[N],t3[N],t4[N];
    int n,a,b,jc[N],ny[N],t=1,l,f[N],v[N],g[2][40];
    il int read(){
        int x,f=1;char ch;
        _(!)ch=='-'?f=-1:f;x=ch^48;
        _()x=(x<<1)+(x<<3)+(ch^48);
        return f*x;
    }
    il int ksm(LL a,int y){
        LL b=1;
        while(y){
            if(y&1)b=b*a%p;
            a=a*a%p;y>>=1;
        }
        return b;
    }
    il int mu(int x,int y){
        return (x+y>=p)?x+y-p:x+y;
    }
    il int C(int n,int m){
        return 1ll*jc[n]*ny[m]%p*ny[n-m]%p;
    }
    il void init(int n){
        t=1;l=0;
        while(t<=n)t<<=1,l++;
        for(int i=0;i<t;i++)v[i]=(v[i>>1]>>1)|((i&1)<<l-1);
    }
    il void dft(int *x,int op){
        for(int i=0;i<t;i++)if(i<v[i])swap(x[i],x[v[i]]);
        for(int i=1,z=1;i<t;i<<=1,z++){
            int wn=g[op][z];
            for(int j=0;j<t;j+=i<<1){
                for(int w=1,A,B,k=0;k<i;k++,w=1ll*w*wn%p){
                    A=x[j+k],B=1ll*x[i+j+k]*w%p;
                    x[j+k]=mu(A,B);x[i+j+k]=mu(A,p-B);
                }
            }
        }
        int kk=ksm(t,p-2);
        if(op)for(int i=0;i<t;i++)x[i]=1ll*x[i]*kk%p;
    }
    il void solve(int *x,int n){
        if(n==1){
            x[0]=0;x[1]=1;
            return;
        }
        if(n&1){
            solve(x,n-1);x[n]=0;
            for(int i=n-1;i>=0;i--)x[i+1]=mu(1ll*(n-1)*x[i+1]%p,x[i]);
            return;
        }
        solve(x,n>>1);
        init(n);
        for(int i=(n>>1)+1;i<=n;i++)
            t1[i]=t2[i]=t3[i]=t4[i]=0;
        for(int i=0;i<=(n>>1);i++){
            t1[i]=1ll*x[i]*ksm(n>>1,i)%p*jc[i]%p;
            t2[i]=ny[i];
        }
        reverse(t1,t1+(n>>1)+1);
        dft(t1,0);dft(t2,0);
        for(int i=0;i<t;i++)t3[i]=1ll*t1[i]*t2[i]%p;
        dft(t3,1);reverse(t3,t3+(n>>1)+1);
        for(int i=0;i<=(n>>1);i++)t4[i]=1ll*t3[i]*ksm(ksm(n>>1,i),p-2)%p*ny[i]%p;
        dft(t4,0);dft(x,0);
        for(int i=0;i<t;i++)x[i]=1ll*x[i]*t4[i]%p;
        dft(x,1);
    }
    int main()
    {
        n=read();a=read();b=read();
        if(n==1&&a==1&&b==1)return puts("1"),0;
        if(a+b-1>n||a<1||b<1)return puts("0"),0;
        g[0][0]=3;g[1][0]=ksm(3,p-2);
        while(t<=n)t<<=1,l++;
        for(int i=1,z=1;i<t;i<<=1,z++){
            g[0][z]=ksm(g[0][0],(p-1)/(i<<1));
            g[1][z]=ksm(g[1][0],(p-1)/(i<<1));
        }
        jc[0]=1;for(int i=1;i<=t;i++)jc[i]=1ll*i*jc[i-1]%p;
        ny[t]=ksm(jc[t],p-2);for(int i=t;i;i--)ny[i-1]=1ll*i*ny[i]%p;
        solve(f,n-1);
        printf("%d
    ",1ll*f[a+b-2]*C(a+b-2,a-1)%p);
        return 0;
    }
    View Code

    一些关于第一类斯特林数的性质:

    1. $s(0,0)=1​$

    2. $s(n,0)=0 (n>0)$
    3. $s(n,1)=(n-1)!$
    4. $s(n,n-1)=C_{n}^{2}​$
    5. $s(n,2)=(n-1)! imes sum_{i=1}^{n-1} frac{i}{1}$
    6. $s(n,n-2)=2 imes C_{n}^{3}+3 imes C_{n}^{4}$
    7. $sum_{k=0}^{n}s(n,k)=n!​$

    第二类斯特林数

    意义:将 $n$ 个不同的元素拆成 $m$ 个集合的方案数(不存在空集)。

    (与第一类不同于,第二类球在集合中无序的)

    递推式:

    $$
    S(n,m)=S(n-1,m-1)+m imes S(n-1,m)
    $$
    容斥式子:

    枚举空盒子的个数,求出至少有 $i$ 个空盒的方案数容斥得到恰好有 $m$ 个盒子的方案数。
    $$
    S(n,m)=frac{1}{m!}sum_{k=0}^{m}(-1)^{k} imes C_{m}^{k} imes (m-k)^{n}
    $$
    $ps:$ 因为集合是相同的,所以最后要 $ imes frac{1}{m!}$

    观察到式子是卷积的形式,所以可以 $NTT$ 在 $O(nlogn)$ 内求出 $S(n,0),S(n,1)......$

    性质:
    $$
    n^{k}=sum_{i=0}^{k}S(k,i) imes i! imes C_{n}^{i}
    $$
    左边相当于把 $k$ 个球随便放在 $i$ 个不同的盒子里

    右边表示枚举 $k$ 个球装在 $i$ 个盒子里,(因为左边盒子是不同的,为了一致 $ imes i!$ 表示不同的盒子),再组合数 $C_{n}^{k}$ 选出 $k$ 个非空盒子。

    继续变化
    $$
    n^{k}=sum_{i=0}^{k}S(k,i)n^{underline{k}}
    $$
    $n^{underline{k}}$ 表示 $n$ 的 $k$ 次下降幂,等于 $n imes (n-1) imes (n-2) imes ...... imes (n-k+1)​$

    斯特林反演后续填坑...

  • 相关阅读:
    设计模式之抽象工厂模式
    MQ任意延时消息(三)基于服务端实现
    MQ任意延时消息(二)基于客户端实现
    MQ任意延时消息(一)实现原理概述
    sqlyog报错2058
    base标签的作用
    相对路径和绝对路径的解释
    自定义Tomcat部署目录
    常用正则表达式
    接口的结构定义
  • 原文地址:https://www.cnblogs.com/Jessie-/p/10499775.html
Copyright © 2011-2022 走看看