Dù được gọi là “loại hai”, số Stirling loại hai lại được mô tả trước trong các tác phẩm của Stirling và trong toán học cụ thể, đồng thời được dùng phổ biến hơn loại một.
Số Stirling loại hai (số phân hoạch con) \(\begin{Bmatrix}n\\ k\end{Bmatrix}\), cũng ký hiệu \(S(n,k)\), là số cách chia \(n\) phần tử phân biệt thành \(k\) tập con không rỗng, không phân biệt.
Dùng nguyên lý bao hàm – loại trừ. Gọi số cách chia \(n\) phần tử phân biệt vào \(i\) hộp phân biệt (cho phép rỗng) là \(G_i\), và vào \(i\) hộp phân biệt không rỗng là \(F_i\).
“Cùng hàng” nghĩa là cùng \(n\) và \(i\) chạy \(0..n\). Tính toàn bộ số Stirling loại hai cùng hàng tức là tính số cách chia \(n\) phần tử thành \(i\) tập con không rỗng.
Theo công thức tường minh, có thể dùng tích chập, đạt \(O(n \log n)\).
Đoạn code dưới dùng lớp đa thức poly, chỉ để tham khảo.
#ifndef _FEISTDLIB_POLY_#define _FEISTDLIB_POLY_/* * This file is part of the fstdlib project. * Version: Build v0.0.2 * You can check for details at https://github.com/FNatsuka/fstdlib */#include<algorithm>#include<cmath>#include<cstdio>#include<vector>namespacefstdlib{usingll=longlong;intmod=998244353,grt=3;classpoly{private:std::vector<int>data;voidout(void){for(inti=0;i<(int)data.size();++i)printf("%d ",data[i]);puts("");}public:poly(std::size_tlen=std::size_t(0)){data=std::vector<int>(len);}poly(conststd::vector<int>&b){data=b;}poly(constpoly&b){data=b.data;}voidresize(std::size_tlen,intval=0){data.resize(len,val);}std::size_tsize(void)const{returndata.size();}voidclear(void){data.clear();}#if __cplusplus >= 201103Lvoidshrink_to_fit(void){data.shrink_to_fit();}#endifint&operator[](std::size_tb){returndata[b];}constint&operator[](std::size_tb)const{returndata[b];}polyoperator*(constpoly&h)const;polyoperator*=(constpoly&h);polyoperator*(constint&h)const;polyoperator*=(constint&h);polyoperator+(constpoly&h)const;polyoperator+=(constpoly&h);polyoperator-(constpoly&h)const;polyoperator-=(constpoly&h);polyoperator<<(conststd::size_t&b)const;polyoperator<<=(conststd::size_t&b);polyoperator>>(conststd::size_t&b)const;polyoperator>>=(conststd::size_t&b);polyoperator/(constint&h)const;polyoperator/=(constint&h);polyoperator==(constpoly&h)const;polyoperator!=(constpoly&h)const;polyoperator+(constint&h)const;polyoperator+=(constint&h);polyinv(void)const;polyinv(constint&h)const;friendpolysqrt(constpoly&h);friendpolylog(constpoly&h);friendpolyexp(constpoly&h);};intqpow(inta,intb,intp=mod){intres=1;while(b){if(b&1)res=(ll)res*a%p;a=(ll)a*a%p,b>>=1;}returnres;}std::vector<int>rev;voiddft_for_module(std::vector<int>&f,intn,intb){staticstd::vector<int>w;w.resize(n);for(inti=0;i<n;++i)if(i<rev[i])std::swap(f[i],f[rev[i]]);for(inti=2;i<=n;i<<=1){w[0]=1,w[1]=qpow(grt,(mod-1)/i);if(b==-1)w[1]=qpow(w[1],mod-2);for(intj=2;j<i/2;++j)w[j]=(ll)w[j-1]*w[1]%mod;for(intj=0;j<n;j+=i)for(intk=0;k<i/2;++k){intp=f[j+k],q=(ll)f[j+k+i/2]*w[k]%mod;f[j+k]=(p+q)%mod,f[j+k+i/2]=(p-q+mod)%mod;}}}polypoly::operator*(constpoly&h)const{intN=1;while(N<(int)(size()+h.size()-1))N<<=1;std::vector<int>f(this->data),g(h.data);f.resize(N),g.resize(N);rev.resize(N);for(inti=0;i<N;++i)rev[i]=(rev[i>>1]>>1)|(i&1?N>>1:0);dft_for_module(f,N,1),dft_for_module(g,N,1);for(inti=0;i<N;++i)f[i]=(ll)f[i]*g[i]%mod;dft_for_module(f,N,-1),f.resize(size()+h.size()-1);for(inti=0,inv=qpow(N,mod-2);i<(int)f.size();++i)f[i]=(ll)f[i]*inv%mod;returnf;}polypoly::operator*=(constpoly&h){return*this=*this*h;}polypoly::operator*(constint&h)const{std::vector<int>f(this->data);for(inti=0;i<(int)f.size();++i)f[i]=(ll)f[i]*h%mod;returnf;}polypoly::operator*=(constint&h){for(inti=0;i<(int)size();++i)data[i]=(ll)data[i]*h%mod;return*this;}polypoly::operator+(constpoly&h)const{std::vector<int>f(this->data);if(f.size()<h.size())f.resize(h.size());for(inti=0;i<(int)h.size();++i)f[i]=(f[i]+h[i])%mod;returnf;}polypoly::operator+=(constpoly&h){std::vector<int>&f=this->data;if(f.size()<h.size())f.resize(h.size());for(inti=0;i<(int)h.size();++i)f[i]=(f[i]+h[i])%mod;returnf;}polypoly::operator-(constpoly&h)const{std::vector<int>f(this->data);if(f.size()<h.size())f.resize(h.size());for(inti=0;i<(int)h.size();++i)f[i]=(f[i]-h[i]+mod)%mod;returnf;}polypoly::operator-=(constpoly&h){std::vector<int>&f=this->data;if(f.size()<h.size())f.resize(h.size());for(inti=0;i<(int)h.size();++i)f[i]=(f[i]-h[i]+mod)%mod;returnf;}polypoly::operator<<(conststd::size_t&b)const{std::vector<int>f(size()+b);for(inti=0;i<(int)size();++i)f[i+b]=data[i];returnf;}polypoly::operator<<=(conststd::size_t&b){return*this=(*this)<<b;}polypoly::operator>>(conststd::size_t&b)const{std::vector<int>f(size()-b);for(inti=0;i<(int)f.size();++i)f[i]=data[i+b];returnf;}polypoly::operator>>=(conststd::size_t&b){return*this=(*this)>>b;}polypoly::operator/(constint&h)const{std::vector<int>f(this->data);intinv=qpow(h,mod-2);for(inti=0;i<(int)f.size();++i)f[i]=(ll)f[i]*inv%mod;returnf;}polypoly::operator/=(constint&h){intinv=qpow(h,mod-2);for(inti=0;i<(int)data.size();++i)data[i]=(ll)data[i]*inv%mod;return*this;}polypoly::inv(void)const{intN=1;while(N<(int)(size()+size()-1))N<<=1;std::vector<int>f(N),g(N),d(this->data);d.resize(N),f[0]=qpow(d[0],mod-2);for(intw=2;w<N;w<<=1){for(inti=0;i<w;++i)g[i]=d[i];rev.resize(w<<1);for(inti=0;i<w*2;++i)rev[i]=(rev[i>>1]>>1)|(i&1?w:0);dft_for_module(f,w<<1,1),dft_for_module(g,w<<1,1);for(inti=0;i<w*2;++i)f[i]=(ll)f[i]*(2+mod-(ll)f[i]*g[i]%mod)%mod;dft_for_module(f,w<<1,-1);for(inti=0,inv=qpow(w<<1,mod-2);i<w;++i)f[i]=(ll)f[i]*inv%mod;for(inti=w;i<w*2;++i)f[i]=0;}f.resize(size());returnf;}polypoly::operator==(constpoly&h)const{if(size()!=h.size())return0;for(inti=0;i<(int)size();++i)if(data[i]!=h[i])return0;return1;}polypoly::operator!=(constpoly&h)const{if(size()!=h.size())return1;for(inti=0;i<(int)size();++i)if(data[i]!=h[i])return1;return0;}polypoly::operator+(constint&h)const{polyf(this->data);f[0]=(f[0]+h)%mod;returnf;}polypoly::operator+=(constint&h){return*this=(*this)+h;}polypoly::inv(constint&h)const{polyf(*this);f.resize(h);returnf.inv();}intmodsqrt(inth,intp=mod){return1;}polysqrt(constpoly&h){intN=1;while(N<(int)(h.size()+h.size()-1))N<<=1;polyf(N),g(N),d(h);d.resize(N),f[0]=modsqrt(d[0]);for(intw=2;w<N;w<<=1){g.resize(w);for(inti=0;i<w;++i)g[i]=d[i];f=(f+f.inv(w)*g)/2;f.resize(w);}f.resize(h.size());returnf;}polylog(constpoly&h){polyf(h);for(inti=1;i<(int)f.size();++i)f[i-1]=(ll)f[i]*i%mod;f[f.size()-1]=0,f=f*h.inv(),f.resize(h.size());for(inti=(int)f.size()-1;i>0;--i)f[i]=(ll)f[i-1]*qpow(i,mod-2)%mod;f[0]=0;returnf;}polyexp(constpoly&h){intN=1;while(N<(int)(h.size()+h.size()-1))N<<=1;polyf(N),g(N),d(h);f[0]=1,d.resize(N);for(intw=2;w<N;w<<=1){f.resize(w),g.resize(w);for(inti=0;i<w;++i)g[i]=d[i];f=f*(g+1-log(f));f.resize(w);}f.resize(h.size());returnf;}structcomp{longdoublex,y;comp(longdouble_x=0,longdouble_y=0):x(_x),y(_y){}compoperator*(constcomp&b)const{returncomp(x*b.x-y*b.y,x*b.y+y*b.x);}compoperator+(constcomp&b)const{returncomp(x+b.x,y+b.y);}compoperator-(constcomp&b)const{returncomp(x-b.x,y-b.y);}compconj(void){returncomp(x,-y);}};constintEPS=1e-9;template<typenameFLOAT_T>FLOAT_Tfabs(constFLOAT_T&x){returnx>0?x:-x;}template<typenameFLOAT_T>FLOAT_Tsin(constFLOAT_T&x,constlongdouble&EPS=fstdlib::EPS){FLOAT_Tres=0,delt=x;intd=0;while(fabs(delt)>EPS){res+=delt,++d;delt*=-x*x/((2*d)*(2*d+1));}returnres;}template<typenameFLOAT_T>FLOAT_Tcos(constFLOAT_T&x,constlongdouble&EPS=fstdlib::EPS){FLOAT_Tres=0,delt=1;intd=0;while(fabs(delt)>EPS){res+=delt,++d;delt*=-x*x/((2*d)*(2*d-1));}returnres;}constlongdoublePI=std::acos((longdouble)(-1));voiddft_for_complex(std::vector<comp>&f,intn,intb){staticstd::vector<comp>w;w.resize(n);for(inti=0;i<n;++i)if(i<rev[i])std::swap(f[i],f[rev[i]]);for(inti=2;i<=n;i<<=1){w[0]=comp(1,0),w[1]=comp(cos(2*PI/i),b*sin(2*PI/i));for(intj=2;j<i/2;++j)w[j]=w[j-1]*w[1];for(intj=0;j<n;j+=i)for(intk=0;k<i/2;++k){compp=f[j+k],q=f[j+k+i/2]*w[k];f[j+k]=p+q,f[j+k+i/2]=p-q;}}}classarbitrary_module_poly{private:std::vector<int>data;intconstruct_element(intD,llx,lly,llz)const{x%=mod,y%=mod,z%=mod;return((ll)D*D*x%mod+(ll)D*y%mod+z)%mod;}public:intmod;arbitrary_module_poly(std::size_tlen=std::size_t(0),intmodule_value=1e9+7){mod=module_value;data=std::vector<int>(len);}arbitrary_module_poly(conststd::vector<int>&b,intmodule_value=1e9+7){mod=module_value;data=b;}arbitrary_module_poly(constarbitrary_module_poly&b){mod=b.mod;data=b.data;}voidresize(std::size_tlen,constint&val=0){data.resize(len,val);}std::size_tsize(void)const{returndata.size();}voidclear(void){data.clear();}#if __cplusplus >= 201103Lvoidshrink_to_fit(void){data.shrink_to_fit();}#endifint&operator[](std::size_tb){returndata[b];}constint&operator[](std::size_tb)const{returndata[b];}arbitrary_module_polyoperator*(constarbitrary_module_poly&h)const;arbitrary_module_polyoperator*=(constarbitrary_module_poly&h);arbitrary_module_polyoperator*(constint&h)const;arbitrary_module_polyoperator*=(constint&h);arbitrary_module_polyoperator+(constarbitrary_module_poly&h)const;arbitrary_module_polyoperator+=(constarbitrary_module_poly&h);arbitrary_module_polyoperator-(constarbitrary_module_poly&h)const;arbitrary_module_polyoperator-=(constarbitrary_module_poly&h);arbitrary_module_polyoperator<<(conststd::size_t&b)const;arbitrary_module_polyoperator<<=(conststd::size_t&b);arbitrary_module_polyoperator>>(conststd::size_t&b)const;arbitrary_module_polyoperator>>=(conststd::size_t&b);arbitrary_module_polyoperator/(constint&h)const;arbitrary_module_polyoperator/=(constint&h);arbitrary_module_polyoperator==(constarbitrary_module_poly&h)const;arbitrary_module_polyoperator!=(constarbitrary_module_poly&h)const;arbitrary_module_polyinv(void)const;arbitrary_module_polyinv(constint&h)const;friendarbitrary_module_polysqrt(constarbitrary_module_poly&h);friendarbitrary_module_polylog(constarbitrary_module_poly&h);};arbitrary_module_polyarbitrary_module_poly::operator*(constarbitrary_module_poly&h)const{intN=1;while(N<(int)(size()+h.size()-1))N<<=1;std::vector<comp>f(N),g(N),p(N),q(N);constintD=std::sqrt(mod);for(inti=0;i<(int)size();++i)f[i].x=data[i]/D,f[i].y=data[i]%D;for(inti=0;i<(int)h.size();++i)g[i].x=h[i]/D,g[i].y=h[i]%D;rev.resize(N);for(inti=0;i<N;++i)rev[i]=(rev[i>>1]>>1)|(i&1?N>>1:0);dft_for_complex(f,N,1),dft_for_complex(g,N,1);for(inti=0;i<N;++i){p[i]=(f[i]+f[(N-i)%N].conj())*comp(0.50,0)*g[i];q[i]=(f[i]-f[(N-i)%N].conj())*comp(0,-0.5)*g[i];}dft_for_complex(p,N,-1),dft_for_complex(q,N,-1);std::vector<int>r(size()+h.size()-1);for(inti=0;i<(int)r.size();++i)r[i]=construct_element(D,p[i].x/N+0.5,(p[i].y+q[i].x)/N+0.5,q[i].y/N+0.5);returnarbitrary_module_poly(r,mod);}arbitrary_module_polyarbitrary_module_poly::operator*=(constarbitrary_module_poly&h){return*this=*this*h;}arbitrary_module_polyarbitrary_module_poly::operator*(constint&h)const{std::vector<int>f(this->data);for(inti=0;i<(int)f.size();++i)f[i]=(ll)f[i]*h%mod;returnarbitrary_module_poly(f,mod);}arbitrary_module_polyarbitrary_module_poly::operator*=(constint&h){for(inti=0;i<(int)size();++i)data[i]=(ll)data[i]*h%mod;return*this;}arbitrary_module_polyarbitrary_module_poly::operator+(constarbitrary_module_poly&h)const{std::vector<int>f(this->data);if(f.size()<h.size())f.resize(h.size());for(inti=0;i<(int)h.size();++i)f[i]=(f[i]+h[i])%mod;returnarbitrary_module_poly(f,mod);}arbitrary_module_polyarbitrary_module_poly::operator+=(constarbitrary_module_poly&h){if(size()<h.size())resize(h.size());for(inti=0;i<(int)h.size();++i)data[i]=(data[i]+h[i])%mod;return*this;}arbitrary_module_polyarbitrary_module_poly::operator-(constarbitrary_module_poly&h)const{std::vector<int>f(this->data);if(f.size()<h.size())f.resize(h.size());for(inti=0;i<(int)h.size();++i)f[i]=(f[i]+mod-h[i])%mod;returnarbitrary_module_poly(f,mod);}arbitrary_module_polyarbitrary_module_poly::operator-=(constarbitrary_module_poly&h){if(size()<h.size())resize(h.size());for(inti=0;i<(int)h.size();++i)data[i]=(data[i]+mod-h[i])%mod;return*this;}arbitrary_module_polyarbitrary_module_poly::operator<<(conststd::size_t&b)const{std::vector<int>f(size()+b);for(inti=0;i<(int)size();++i)f[i+b]=data[i];returnarbitrary_module_poly(f,mod);}arbitrary_module_polyarbitrary_module_poly::operator<<=(conststd::size_t&b){return*this=(*this)<<b;}arbitrary_module_polyarbitrary_module_poly::operator>>(conststd::size_t&b)const{std::vector<int>f(size()-b);for(inti=0;i<(int)f.size();++i)f[i]=data[i+b];returnarbitrary_module_poly(f,mod);}arbitrary_module_polyarbitrary_module_poly::operator>>=(conststd::size_t&b){return*this=(*this)>>b;}arbitrary_module_polyarbitrary_module_poly::inv(void)const{intN=1;while(N<(int)(size()+size()-1))N<<=1;arbitrary_module_polyf(1,mod),g(N,mod),h(*this),f2(1,mod);f[0]=qpow(data[0],mod-2,mod),h.resize(N),f2[0]=2;for(intw=2;w<N;w<<=1){g.resize(w);for(inti=0;i<w;++i)g[i]=h[i];f=f*(f*g-f2)*(mod-1);f.resize(w);}f.resize(size());returnf;}arbitrary_module_polyarbitrary_module_poly::inv(constint&h)const{arbitrary_module_polyf(*this);f.resize(h);returnf.inv();}arbitrary_module_polyarbitrary_module_poly::operator/(constint&h)const{intinv=qpow(h,mod-2,mod);std::vector<int>f(this->data);for(inti=0;i<(int)f.size();++i)f[i]=(ll)f[i]*inv%mod;returnarbitrary_module_poly(f,mod);}arbitrary_module_polyarbitrary_module_poly::operator/=(constint&h){intinv=qpow(h,mod-2,mod);for(inti=0;i<(int)size();++i)data[i]=(ll)data[i]*inv%mod;return*this;}arbitrary_module_polyarbitrary_module_poly::operator==(constarbitrary_module_poly&h)const{if(size()!=h.size()||mod!=h.mod)return0;for(inti=0;i<(int)size();++i)if(data[i]!=h[i])return0;return1;}arbitrary_module_polyarbitrary_module_poly::operator!=(constarbitrary_module_poly&h)const{if(size()!=h.size()||mod!=h.mod)return1;for(inti=0;i<(int)size();++i)if(data[i]!=h[i])return1;return0;}arbitrary_module_polysqrt(constarbitrary_module_poly&h){intN=1;while(N<(int)(h.size()+h.size()-1))N<<=1;arbitrary_module_polyf(1,mod),g(N,mod),d(h);f[0]=modsqrt(h[0],mod),d.resize(N);for(intw=2;w<N;w<<=1){g.resize(w);for(inti=0;i<w;++i)g[i]=d[i];f=(f+f.inv(w)*g)/2;f.resize(w);}f.resize(h.size());returnf;}arbitrary_module_polylog(constarbitrary_module_poly&h){arbitrary_module_polyf(h);for(inti=1;i<(int)f.size();++i)f[i-1]=(ll)f[i]*i%f.mod;f[f.size()-1]=0,f=f*h.inv(),f.resize(h.size());for(inti=(int)f.size()-1;i>0;--i)f[i]=(ll)f[i-1]*qpow(i,f.mod-2,f.mod)%f.mod;f[0]=0;returnf;}usingm_poly=arbitrary_module_poly;}// namespace fstdlib#endif
“Cùng cột” nghĩa là cùng \(k\) và \(i\) chạy \(0..n\). Tính toàn bộ các số \(\begin{Bmatrix}i\\k\end{Bmatrix}\).
Dùng hàm sinh mũ.
Một hộp chứa \(i\) vật và không rỗng có số cách là \([i>0]\). Hàm sinh mũ là \(F(x)=\sum\limits_{i=1}^{+\infty}\dfrac{x^i}{i!} = \mathrm{e}^x-1\). Khi đó \(F^k(x)\) là EGF của việc phân \(i\) vật có nhãn vào \(k\) hộp có nhãn, chia cho \(k!\) để bỏ nhãn hộp.
\(\begin{Bmatrix}i\\k\end{Bmatrix}=\dfrac{\left[\dfrac{x^i}{i!}\right]F^k(x)}{k!}\), tính lũy thừa đa thức \(O(n\log n)\).
Ngoài ra, \(\exp F(x)=\sum\limits_{i=0}^{+\infty}\dfrac{F^i(x)}{i!}\) là EGF của việc phân \(i\) vật có nhãn vào số hộp không nhãn bất kỳ (EXP chia mỗi hạng cho \(i!\) để bỏ nhãn hộp). Đây chính là hàm sinh của số Bell.
Số Stirling loại một (số hoán vị vòng) \(\begin{bmatrix}n\\ k\end{bmatrix}\), cũng ký hiệu \(s(n,k)\), là số cách chia \(n\) phần tử phân biệt thành \(k\) vòng không phân biệt, không rỗng.
Một vòng là một hoán vị vòng. Ví dụ một vòng \([A,B,C,D]\) và ta coi \([A,B,C,D]=[B,C,D,A]=[C,D,A,B]=[D,A,B,C]\), tức các vòng quay là tương đương. Không coi đối xứng gương là tương đương: \([A,B,C,D]\neq[D,C,B,A]\).
Đây là lũy thừa tăng \(x^{\overline n}\). Có thể chia để trị \(O(n\log^2 n)\) hoặc dùng kỹ thuật liên quan đến lũy thừa tăng \(O(n\log n)\), xem Dời đa thức | Dời điểm liên tiếp.
Tính các số Stirling loại một cùng cột
Tương tự loại hai, dùng hàm sinh mũ. Do truy hồi theo hàng nên không dùng truy hồi để tính theo cột.
Last updated on this page:, Update history Found an error? Want to help improve? Edit this page on GitHub! Contributors to this page:OI-wiki All content on this page is provided under the terms of the CC BY-SA 4.0 and SATA license, additional terms may apply