zoukankan      html  css  js  c++  java
  • 【学习笔记】斯特林数

    第一类斯特林数

    定义:(S_1(n,m))表示(n)个元素,排成(m)个轮换的方案数。

    标记:$egin{bmatrix} n mend{bmatrix} $

    递推式:
    (egin{bmatrix} n \ mend{bmatrix} = (n-1)egin{bmatrix} n-1 \ mend{bmatrix} +egin{bmatrix} n-1 \ m-1end{bmatrix})
    通过组合意义理解就是新加入一个元素可以放入任何一个元素之前或者新开一个轮换

    可能会用到的柿子:(n! = sum_{i=0}^n egin{bmatrix} n \ iend{bmatrix})显然,一个排列唯一对应一组轮换

    可以分治FFT在(O(nlg^2n))的时间里求出固定的(n)对应的第一类斯特林数

    第二类斯特林数

    定义:(S_2(n,m))表示(n)个元素,组成(m)个集合的方案数。

    标记:(egin{Bmatrix} n \ mend{Bmatrix})

    递推式:
    (egin{Bmatrix} n \ mend{Bmatrix} = megin{Bmatrix} n-1 \ mend{Bmatrix}+egin{Bmatrix} n-1 \ m-1end{Bmatrix})
    分别对应加入之前的集合新开一个集合

    可能会用到的柿子:(egin{Bmatrix} n \ mend{Bmatrix} = frac{1}{m!} sum_{k=0}^m (-1)^k inom{m}{k} (m-k)^n)

    对应容斥原理,比之前方便,直接卷积就能求了

    (n^k = sum_{i=0}^k egin{Bmatrix} k \ iend{Bmatrix} i! inom{n}{i})

    貌似要下降幂推导 我还是没学明白下降幂 先鸽着好了/cy

    斯特林数也可以做反演...长这样

    (q_n = sum_{i=1}^n egin{Bmatrix} n \ iend{Bmatrix} p_i Leftrightarrow p_n = sum_{i=0}^n (-1)^{n-i} egin{bmatrix} n \ iend{bmatrix} q_i)

    例题:TJOI/HEOI2016 求和

    我们一起来推柿子啦

    首先由于(i>j)(S(i,j)=0)所以我们的求和式可以直接简化一下

    (sum_{i=0}^n sum_{j=0}^i S(i,j) 2^j j! = sum_{i=0}^n sum_{j=0}^n S(i,j) 2^j j!)

    好看多了 我们直接开始推吧 直接把定义式扔进去

    (sum_{j=0}^n 2^j j! frac{1}{j!}sum_{i=0}^n sum_{k=0}^j inom{j}{k}(-1)^k (j-k)^i frac{1}{k!}\ = sum_{j=0}^n 2^j sum_{i=0}^n frac{j!}{k!(j-k)!} (-1)^k (j-k)^i \ = sum_{j=0}^n 2^j j! sum_{k=0}^j sum_{i=0}^n frac{(-1)^k}{k!} frac{(j-k)^i}{(j-k)!}\= sum_{j=0}^n 2^j j! sum_{k=0}^j frac{(-1)^k}{k!} frac{(j-k)^{n+1} - 1}{(j-k)!(j-k-1)})

    显然后面长得就像个卷积

    然后直接NTT就好辣

    (呜呜明明我是为了分治NTT来做的这个题 它偏偏不用)

    //Love and Freedom.
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define inf 20021225
    #define mdn 998244353
    #define G 3
    #define N 300010
    using namespace std;
    int read()
    {
    	int s=0,f=1; char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    	while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    	return f*s;
    }
    int ksm(int bs,int mi)
    {
    	int ans=1;
    	while(mi)
    	{
    		if(mi&1)	ans=1ll*ans*bs%mdn;
    		bs=1ll*bs*bs%mdn; mi>>=1;
    	}
    	return ans;
    }
    int r[N];
    int init(int n)
    {
    	int lim=1,l=0;
    	while(lim<n)	lim<<=1,l++;
    	for(int i=0;i<lim;i++)	r[i]=(r[i>>1]>>1)|((i&1)<<l-1);
    	return lim;
    }
    void ntt(int *a,int lim,int f)
    {
    	for(int i=0;i<lim;i++)	if(r[i]>i)	swap(a[r[i]],a[i]);
    	for(int k=2,mid=1;k<=lim;mid<<=1,k<<=1)
    	{
    		int Wn=ksm(G,(mdn-1)/k); if(f)	Wn=ksm(Wn,mdn-2);
    		for(int i=0,w=1;i<lim;i+=k,w=1)	for(int j=0;j<mid;j++,w=1ll*w*Wn%mdn)
    		{
    			int x=a[i+j],y=1ll*w*a[i+mid+j]%mdn;
    			a[i+j]=(x+y)%mdn; a[i+mid+j]=(x-y+mdn)%mdn;
    		}
    	}
    	if(f)	for(int i=0,inv=ksm(lim,mdn-2);i<lim;i++)	a[i]=1ll*a[i]*inv%mdn;
    }
    int f[N],g[N],fac[N],inv[N];
    int main()
    {
    	int n=read(); fac[0]=1;
    	for(int i=1;i<=n;i++)	fac[i]=1ll*fac[i-1]*i%mdn;
    	inv[n]=ksm(fac[n],mdn-2);
    	for(int i=n;i;i--)	inv[i-1]=1ll*inv[i]*i%mdn;
    	f[0]=g[0]=1; f[1]=n+1; g[1]=mdn-1;
    	for(int i=2;i<=n;i++)	f[i]=1ll*(ksm(i,n+1)-1+mdn)%mdn*inv[i]%mdn*ksm((i-1+mdn)%mdn,mdn-2)%mdn;
    	for(int i=2;i<=n;i++)	g[i]=i&1?mdn-inv[i]:inv[i];
    	int lim=init(n+1<<1); ntt(g,lim,0); ntt(f,lim,0);
    	for(int i=0;i<lim;i++)	f[i]=1ll*f[i]*g[i]%mdn;
    	ntt(f,lim,1); int ans=0;
    	for(int i=0;i<=n;i++)	(ans+=1ll*ksm(2,i)*fac[i]%mdn*f[i]%mdn)%=mdn;
    	printf("%d
    ",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    51nod 1185 威佐夫游戏 V2
    51nod 1212 无向图最小生成树
    51nod 1242 斐波那契数列的第N项
    51nod 1240 莫比乌斯函数
    51nod 1256 乘法逆元
    51nod 1264 线段相交
    51nod 1265 四点共面
    51nod 1298 圆与三角形
    51nod 2006 飞行员配对
    CGLIB介绍与原理
  • 原文地址:https://www.cnblogs.com/hanyuweining/p/12050992.html
Copyright © 2011-2022 走看看