zoukankan      html  css  js  c++  java
  • Stirling数

    第一类(Stirling)

    (egin{bmatrix} n \ m \ end{bmatrix})表示(n)个元素组成(m)个圆排列的方案数。

    何为圆排列?即通过排列在一个环上,两两不能通过旋转相互得到的排列的个数。

    [egin{bmatrix} n \ m \ end{bmatrix}=egin{bmatrix} n-1 \ m-1 \ end{bmatrix}+(n-1)cdot egin{bmatrix} n-1 \ m \ end{bmatrix} ]

    [egin{bmatrix} 0 \ 0 \ end{bmatrix}=1, egin{bmatrix} n \ 0 \ end{bmatrix}=0(n>0) ]

    递推考虑加入的元素要不重新成一个排列,要不放在已有的任一个元素后。

    性质

    [(n-1)!=egin{bmatrix} n \ 1 \ end{bmatrix} ]

    圆排列性质

    [n!=sum_{i=0}^{n}egin{bmatrix} n \ i \ end{bmatrix} ]

    置换与圆排列关系

    [egin{bmatrix} n \ 2 \ end{bmatrix}=(n-1)!cdot sum_{i=1}^{n-1}frac{1}{i} ]

    [egin{bmatrix} n \ n-1 \ end{bmatrix}=egin{pmatrix} n \ 2 \ end{pmatrix} ]

    [egin{bmatrix} n \ n-2 \ end{bmatrix}=2egin{pmatrix} n \ 3 \ end{pmatrix}+3egin{pmatrix} n \ 4 \ end{pmatrix} ]

    一些其他的性质

    [x^{overline{n}}=sum_{i=0}^{n} egin{bmatrix} n \ i \ end{bmatrix} x^{i} ]

    [x^{underline{n}}=sum_{i=0}^{n}egin{bmatrix} n \ i \ end{bmatrix}(-1)^{n-i}x^i ]

    两个与上升幂与下降幂有关的性质。

    上述性质大多可以用数学归纳法证明,限于篇幅,不给出具体证明。

    求解第一类(Stirling)

    我们有(O(nlog^2n))的分治(NTT)做法

    [sum_{i=0}^{n}egin{bmatrix} n \ i \ end{bmatrix}x^i=prod_{i=0}^{n-1}(x+i) ]

    记生成函数为(f_n=prod_{i=0}^{n-1}(x+i)),可以得到:

    [f_n=(x+n-1)f_{n-1}=xf_{n-1}+(n-1)f_{n-1} ]

    这个和第一类(Stirling)数的递推式等价。

    一道经典例题

    求长度为(n)的排列中从左边能看到(A)个且从右边能看到(B)个的数量。(能看到即为前缀/后缀最大值)

    按最高的楼分为两部分,左边有(A-1)个,右边有(B-1)个要被看见,这(A+B-2)个都要放在靠边的位置,所以方案数是圆排列(固定了一个为第一个),即(egin{bmatrix} n-1 \ A+B-2 \ end{bmatrix})

    然后选(A-1)个放左边,总方案数为(egin{bmatrix} n-1 \ A+B-2 \ end{bmatrix}cdot egin{pmatrix} A+B-2 \ A-1 \ end{pmatrix})

    一个双倍经验题

    和上题除数据范围外都一致,用分治(NTT)的方式求第一类(Stirling)数即可。

    给出后面一题的代码

    //by OIerC
    //Forca Barcelona!
    #include<cstdio>
    #include<algorithm>
    #define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
    #define per(i, a, b) for (register int i=(a); i>=(b); --i)
    using namespace std;
    const int N=400005, P=998244353, G=3;
    inline int add(int x, int y){return x+y>=P?x+y-P:x+y;}
    inline int sub(int x, int y){return x-y<0?x-y+P:x-y;}
    inline int mul(int x, int y){return 1ll*x*y-1ll*x*y/P*P;}
    int S[20][N], rev[N], n, A, B;
    
    inline int read()
    {
     	int x=0,f=1;char ch=getchar();
        for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
        for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
        return x*f;
    }
    
    int Pow(int x, int t)
    {
        int res=1;
        for (; t; t>>=1, x=mul(x, x)) if (t&1) res=mul(res, x);
        return res;
    }
    
    int C(int n, int m)
    {
        int ans=1;
        rep(i, n-m+1, n) ans=mul(ans, i);
        rep(i, 1, m) ans=mul(ans, Pow(i, P-2));
        return ans;
    }
    
    void NTT(int *a, int n, int x)
    {
        for (int i=0; i<n; i++) if (i<rev[i]) swap(a[i], a[rev[i]]);
        for (int mid=1, len=2; mid<n; mid<<=1, len<<=1)
        {
            int Gn=Pow(G, (P-1)/len);
            for (int i=0; i<n; i+=len)
            {
                int Gen=1;
                for (int j=0; j<mid; j++, Gen=mul(Gen, Gn))
                {
                    int A1=a[i+j], A2=mul(Gen, a[i+j+mid]);
                    a[i+j]=add(A1, A2); a[i+j+mid]=sub(A1, A2);
                }
            }
        }
        if (!~x)
        {
            reverse(a+1, a+n);
            int inv=Pow(n, P-2);
            for (int i=0; i<n; i++) a[i]=mul(a[i], inv);
        }
    }
    
    void solve(int l, int r, int deg)
    {
        if (l==r) {S[deg][0]=l; S[deg][1]=1; return;}
        int mid=l+r>>1, lim=1, cnt=0;
        while (lim<=r-l+1) lim<<=1, cnt++;
        solve(l, mid, deg+1);
        rep(i, 0, mid-l+1) S[deg][i]=S[deg+1][i];
        solve(mid+1, r, deg+1);
        rep(i, mid-l+2, lim) S[deg][i]=0;
        rep(i, r-mid+1, lim) S[deg+1][i]=0;
        rep(i, 0, lim-1) rev[i]=(rev[i>>1]>>1)|((i&1)<<(cnt-1));
        NTT(S[deg], lim, 1); NTT(S[deg+1], lim, 1);
        rep(i, 0, lim-1) S[deg][i]=mul(S[deg][i], S[deg+1][i]);
        NTT(S[deg], lim, -1);
    }
    
    int main()
    {
        n=read(); A=read(); B=read();
        if (n-1<A+B-2 || !A || !B) {puts("0"); return 0;}
            else if (n==1) {puts("1"); return 0;}
        solve(0, n-2, 0);
        printf("%d
    ", mul(C(A+B-2, A-1), S[0][A+B-2]));
        return 0;
    }
    
    

    第二类(Stirling)

    (egin{Bmatrix} n \ m \ end{Bmatrix})表示(n)个元素放入(m)个无差别盒子且每个盒子非空的方案数。

    [egin{Bmatrix} n \ m \ end{Bmatrix}=egin{Bmatrix} n-1 \ m-1 \ end{Bmatrix}+megin{Bmatrix} n-1 \ m \ end{Bmatrix} ]

    [egin{Bmatrix} 0 \ 0 \ end{Bmatrix}=1 ]

    性质

    [egin{Bmatrix} n \ m \ end{Bmatrix}=frac{1}{m !} sum_{i=0}^{m}(-1)^{i} left( egin{array}{c}{m} \ {i}end{array} ight)(m-i)^{n} ]

    右边( imes m!)后变为有序。

    容斥,选(i)个盒子为空,剩下(n)个球乱放(m-i)个盒子为((m-i)^n)

    [n^{m}=sum_{i=0}^{n} egin{Bmatrix} m \ i \ end{Bmatrix} * A_n^i ]

    枚举非空盒子个数(i)(egin{Bmatrix} m \ i \ end{Bmatrix})为球放入方案,排列为选盒子方案。

    [egin{align} egin{Bmatrix} n \ m \ end{Bmatrix}& = frac{1}{m !} sum_{i=0}^{m}(-1)^{i} left( egin{array}{c}{m} \ {i}end{array} ight)(m-i)^{n}\ & = sum_{i=0}^{n}frac{(-1)^i(n-i)^n}{i!(m-i)!}\ end{align} ]

    卷积形式,(FFT)即可。时间复杂度(O(nlogn))

    例题

    (HEOI2016/TJOI2016) 求和

    [egin{align} f(n) &=sum_{i=0}^{n}sum_{j=0}^{i}egin{Bmatrix} i \ j \ end{Bmatrix}2^j imes j!\ & =sum_{j=0}^{n}2^j imes j!sum_{i=0}^{n}egin{Bmatrix} i \ j \ end{Bmatrix}\ & = sum_{j=0}^{n}2^j imes j!sum_{i=0}^{n}sum_{k=0}^{j}frac{(-1)^k(j-k)^i}{k!(i-k)!}\ &=sum_{j=0}^{n}2^j imes j!sum_{k=0}^{j}frac{(-1)^k}{k!}frac{sum_{i=0}^{n}(j-k)^i}{(i-k)!} end{align} ]

    (a_i=frac{(-1)^i}{i},b_i=frac{sum_{j=0}^{n}i^j}{i!})(a_i)直接求,等比数列求和求(b_i)

    卷积,(NTT)即可,时间复杂度(O(nlogn))

    //by OIerC
    //Forca Barcelona!
    #include<cstdio>
    #include<algorithm>
    #define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
    #define per(i, a, b) for (register int i=(a); i>=(b); --i)
    using namespace std;
    typedef long long ll;
    const int P=998244353, G=3, N=4000005;
    inline int add(int x, int y){return x+y>=P?x+y-P:x+y;}
    inline int sub(int x, int y){return x-y<0?x-y+P:x-y;}
    inline int mul(int x, int y){return 1ll*x*y-1ll*x*y/P*P;}
    int f[N], g[N], rev[N], fac[N], ifac[N];
    
    inline int read()
    {
    	int x=0,f=1;char ch=getchar();
        for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
        for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
        return x*f;
    }
    
    int Pow(int x, int t)
    {
        int res=1;
        for (; t; t>>=1, x=mul(x, x)) if (t&1) res=mul(res, x);
        return res;
    }
    
    void NTT(int *a, int n, int x)
    {
        for (int i=0; i<n; i++) if (i<rev[i]) swap(a[i], a[rev[i]]);
        for (int mid=1, len=2; mid<n; mid<<=1, len<<=1)
        {
            int Gn=Pow(G, (P-1)/len);
            for (int i=0; i<n; i+=len)
            {
                int Gen=1;
                for (int j=0; j<mid; j++, Gen=mul(Gen, Gn))
                {
                    int A1=a[i+j], A2=mul(Gen, a[i+j+mid]);
                    a[i+j]=add(A1, A2); a[i+j+mid]=sub(A1, A2);
                }
            }
        }
        if (!~x) 
        {
            reverse(a+1, a+n);
            int inv=Pow(n, P-2);
            for (int i=0; i<n; i++) a[i]=mul(a[i], inv);
        }
    }
    
    int main()
    {
        int n=read(), ans=0; fac[0]=1; 
        rep(i, 1, n) fac[i]=mul(fac[i-1], i);
        ifac[n]=Pow(fac[n], P-2); 
        per(i, n-1, 0) ifac[i]=mul(ifac[i+1], i+1); 
        rep(i, 0, n)
        {
            f[i]=mul(add(i&1?-1:1, P), ifac[i]);
            g[i]=i==1?n+1:mul(mul(sub(Pow(i, n+1), 1), Pow(sub(i, 1), P-2)), ifac[i]);
        }
        int lim=1, cnt=0;
        while (lim<=n<<1) lim<<=1, cnt++;
        rep(i, 0, lim) rev[i]=(rev[i>>1]>>1) | ((i&1)<<(cnt-1));
        NTT(f, lim, 1); NTT(g, lim, 1);
        rep(i, 0, lim) f[i]=mul(f[i], g[i]);
        NTT(f, lim, -1);
        rep(i, 0, n)
            ans=add(ans, mul(mul(Pow(2, i), fac[i]), f[i]));
        printf("%d
    ", ans);
        return 0;
    }
    

    (CF932E Team Work)

    [egin{align} Ans&=sum_{i=1}^{n}egin{pmatrix} n \ i \ end{pmatrix}i^k \ &=sum_{i=0}^{n}frac{n!}{(n-i)!}sum_{j=0}^{k}egin{Bmatrix} k \ j \ end{Bmatrix}frac{i!}{(i-j)!}\ &=sum_{i=0}^{n}sum_{j=0}^{k}egin{Bmatrix} k \ j \ end{Bmatrix}frac{n!}{(n-i)!(i-j)!}\ &=sum_{j=0}^{k}egin{Bmatrix} k \ j \ end{Bmatrix}sum_{i=0}^{n}egin{pmatrix} n-j \ n-i \ end{pmatrix}cdot frac{n!}{(n-j)!}\ &=sum_{j=0}^{k}egin{Bmatrix} k \ j \ end{Bmatrix}frac{n!}{(n-j)!}2^{n-j} end{align} ]

    直接递推求第二类(Stirling)数即可,时间复杂度(O(k^2))

    //by OIerC
    //Forca Barcelona!
    #include<cstdio>
    #include<algorithm>
    #define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
    #define per(i, a, b) for (register int i=(a); i>=(b); --i)
    using namespace std;
    const int N=5005, P=1000000007;
    inline int add(int x, int y){return x+y>=P?x+y-P:x+y;}
    inline int sub(int x, int y){return x-y<0?x-y+P:x-y;}
    inline int mul(int x, int y){return 1ll*x*y-1ll*x*y/P*P;}
    int S[N][N];
    
    inline int read()
    {
    	int x=0,f=1;char ch=getchar();
        for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
        for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
        return x*f;
    }
    
    int Pow(int x, int t)
    {
        int res=1;
        for (; t; t>>=1, x=mul(x, x)) if (t&1) res=mul(res, x);
        return res;
    }
    
    int main()
    {
        int n=read(), k=read(); S[0][0]=1;
        rep(i, 1, k) rep(j, 1, k) S[i][j]=add(S[i-1][j-1], mul(S[i-1][j], j));
        int bin=Pow(2, n), inv_2=Pow(2, P-2), down=1, ans=0;
        rep(i, 0, min(n, k))
        {
            ans=add(ans, mul(S[k][i], mul(bin, down)));
            bin=mul(bin, inv_2); down=mul(down, n-i);
        }
        printf("%d
    ", ans);
        return 0;
    }
    

    (CF961G Partitions)

    留给读者自行完成

    别人的题解

  • 相关阅读:
    【笔记】隐式寻址方式(pending...)
    【笔记】有结构文件(pending...)
    【笔记】目录项(pending...)
    【笔记】主存储器
    Ubuntu 16.04 升级OpenSSH至7.7p1
    awk用法笔记
    find命令笔记
    VIM的使用
    Shell
    Linux文本处理三剑客
  • 原文地址:https://www.cnblogs.com/ACMSN/p/10810418.html
Copyright © 2011-2022 走看看