第二类斯特林数小结

第二类斯特林数

s 2 n m s2_n^m s2nm表示将 n n n个数放进 m m m个集合的方案数。
有一个显然的递推式:
s 2 n m = s 2 n − 1 m − 1 + m ∗ s 2 n − 1 m s2_n^m=s2_{n-1}^{m-1}+m*s2_{n-1}^m s2nm=s2n1m1+ms2n1m,对应的意义:要么第 n n n个单独构成一个新的集合,要么放在之前某个集合中。
然而第二类斯特林数并没有什么生成函数。
但它可以花 O ( n l o g n ) O(nlogn) O(nlogn)的时间预处理出来。
我们考虑用容斥原理来推第二类斯特林数的通项公式,假设现在求 s 2 i j s2_i^j s2ij强制枚举 k k k个集合为空来计算贡献。
于是 s 2 i j = ∑ k = 0 j C j k ( j − k ) i ( − 1 ) k j ! s2_i^j=\frac{\sum_{k=0}^jC_j^k(j-k)^i(-1)^k}{j!} s2ij=j!k=0jCjk(jk)i(1)k
然后整理一下右边变成 ∑ k = 0 j ( − 1 ) k k ! ( j − k ) i ( j − k ) ! \sum_{k=0}^j\frac{(-1)^k}{k!}\frac{(j-k)^i}{(j-k)!} k=0jk!(1)k(jk)!(jk)i
于是就可以上 n t t ntt ntt优化了。

一道板题:bzoj4555: [Tjoi2016&Heoi2016]求和
题意:要求计算

在这里插入图片描述
S(i, j)表示第二类斯特林数。


思路:
先改变枚举顺序:
⇒ ∑ j = 0 n 2 j j ! ∑ i = j n s 2 i j = ∑ j = 0 n 2 j j ! ∑ i = 0 n s 2 i j \Rightarrow \sum_{j=0}^n2^jj!\sum_{i=j}^ns2_i^j=\sum_{j=0}^n2^jj!\sum_{i=0}^ns2_i^j j=0n2jj!i=jns2ij=j=0n2jj!i=0ns2ij
然后带入斯特林数的通项公式:
= ∑ j = 0 n 2 j j ! ∑ i = 0 n ∑ k = 0 j ( − 1 ) k k ! ( j − k ) i ( j − k ) ! =\sum_{j=0}^n2^jj!\sum_{i=0}^n\sum_{k=0}^j\frac{(-1)^k}{k!}\frac{(j-k)^i}{(j-k)!} =j=0n2jj!i=0nk=0jk!(1)k(jk)!(jk)i
= ∑ j = 0 n 2 j j ! ∑ k = 0 j ( − 1 ) k k ! ∑ i = 0 n ( j − k ) i ( j − k ) ! =\sum_{j=0}^n2^jj!\sum_{k=0}^j\frac{(-1)^k}{k!}\frac{\sum_{i=0}^n(j-k)^i}{(j-k)!} =j=0n2jj!k=0jk!(1)k(jk)!i=0n(jk)i
于是构造 f ( x ) = ( − 1 ) x x ! , g ( x ) = ∑ i = 0 n x i x ! = x n + 1 − 1 x ! ( x − 1 ) f(x)=\frac{(-1)^x}{x!},g(x)=\frac{\sum_{i=0}^nx^i}{x!}=\frac{x^{n+1}-1}{x!(x-1)} f(x)=x!(1)x,g(x)=x!i=0nxi=x!(x1)xn+11
然后 A n s = ∑ j = 0 n 2 j j ! ( f ∗ g ) ( j ) Ans=\sum_{j=0}^n2^jj!(f*g)(j) Ans=j=0n2jj!(fg)(j)
做完啦!
代码:

#include
#define ri register int
#define add(a,b) ((a)+(b)>=mod?(a)+(b)-mod:(a)+(b))
#define dec(a,b) ((a)>=(b)?(a)-(b):(a)-(b)+mod)
#define mul(a,b) ((ll)(a)*(b)%mod)
using namespace std;
const int mod=998244353;
int lim,tim;
typedef long long ll;
vector<int>A,B,pos,fac,ifac,inv,pow2;
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
inline void init(const int&k){
    lim=1,tim=0;
    while(lim<=k)lim<<=1,++tim;
    A.resize(lim),B.resize(lim),pos.resize(lim),pos[0]=0;
    for(ri i=0;i<lim;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(tim-1));
}
inline void ntt(vector<int>&a,const int&type){
    for(ri i=0;i<lim;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
    for(ri mid=1,mult=(mod-1)/2,typ=type==1?3:(mod+1)/3,wn;mid<lim;mid<<=1,mult>>=1){
        wn=ksm(typ,mult);
        for(ri j=0,len=mid<<1;j<lim;j+=len)for(ri a0,a1,w=1,k=0;k<mid;++k,w=mul(w,wn)){
            a0=a[j+k],a1=mul(a[j+k+mid],w);
            a[j+k]=add(a0,a1),a[j+k+mid]=dec(a0,a1);
        }
    }
    if(type==-1)for(ri i=0,inv=ksm(lim,mod-2);i<lim;++i)a[i]=mul(a[i],inv);
}
struct poly{
    vector<int>a;
    poly(int k=0,int x=0){a.resize(k+1),a[k]=x;}
    inline int deg()const{return a.size()-1;}
    inline poly extend(const int&k){poly ret=*this;return ret.a.resize(k+1),ret;}
    inline int&operator[](const int&k){return a[k];}
    inline const int&operator[](const int&k)const{return a[k];}
    friend inline poly operator*(const poly&a,const poly&b){
        int n=a.deg(),m=b.deg();
        init(n+m);
        poly ret(lim);
        for(ri i=0;i<=n;++i)A[i]=a[i];
        for(ri i=0;i<=m;++i)B[i]=b[i];
        for(ri i=n+1;i<lim;++i)A[i]=0;
        for(ri i=m+1;i<lim;++i)B[i]=0;
        ntt(A,1),ntt(B,1);
        for(ri i=0;i<lim;++i)A[i]=mul(A[i],B[i]);
        return ntt(A,-1),ret.a=A,ret;
    }
};
inline void Init(const int&up){
    fac.resize(up+1),ifac.resize(up+1),inv.resize(up+1),pow2.resize(up+1);
    fac[0]=fac[1]=ifac[0]=ifac[1]=1,inv[1]=1,pow2[0]=1,pow2[1]=2;
    for(ri i=2;i<=up;++i){
        pow2[i]=mul(pow2[i-1],2),fac[i]=mul(fac[i-1],i);
        inv[i]=mul(inv[mod%i],mod-mod/i),ifac[i]=mul(ifac[i-1],inv[i]);
    }
}
int main(){
    int n,ans=0;
    scanf("%d",&n),Init(n+1);
    poly a(n),b(n);
    b[0]=1,b[1]=n+1;
    for(ri i=0;i<=n;++i)a[i]=(i&1)?dec(0,ifac[i]):ifac[i];
    for(ri i=2;i<=n;++i)b[i]=mul(mul(dec(ksm(i,n+1),1),inv[i-1]),ifac[i]);
    a=a*b;
    for(ri i=0;i<=n;++i)ans=add(ans,mul(mul(pow2[i],fac[i]),a[i]));
    cout<<ans;
    return 0;
}

你可能感兴趣的:(#,组合数学,#,数学,#,ntt,#,生成函数)