第一类\(Stirling\)数
\(\begin{bmatrix} n \\ m \\ \end{bmatrix}\)表示\(n\)个元素组成\(m\)个圆排列的方案数。
何为圆排列?即通过排列在一个环上,两两不能通过旋转相互得到的排列的个数。
\[
\begin{bmatrix} n \\ m \\ \end{bmatrix}=\begin{bmatrix} n-1 \\ m-1 \\ \end{bmatrix}+(n-1)\cdot \begin{bmatrix} n-1 \\ m \\ \end{bmatrix}
\]
\[
\begin{bmatrix} 0 \\ 0 \\ \end{bmatrix}=1, \begin{bmatrix} n \\ 0 \\ \end{bmatrix}=0(n>0)
\]
递推考虑加入的元素要不重新成一个排列,要不放在已有的任一个元素后。
性质
\[
(n-1)!=\begin{bmatrix} n \\ 1 \\ \end{bmatrix}
\]
圆排列性质
\[
n!=\sum_{i=0}^{n}\begin{bmatrix} n \\ i \\ \end{bmatrix}
\]
置换与圆排列关系
\[
\begin{bmatrix} n \\ 2 \\ \end{bmatrix}=(n-1)!\cdot \sum_{i=1}^{n-1}\frac{1}{i}
\]
\[ \begin{bmatrix} n \\ n-1 \\ \end{bmatrix}=\begin{pmatrix} n \\ 2 \\ \end{pmatrix} \]
\[ \begin{bmatrix} n \\ n-2 \\ \end{bmatrix}=2\begin{pmatrix} n \\ 3 \\ \end{pmatrix}+3\begin{pmatrix} n \\ 4 \\ \end{pmatrix} \]
一些其他的性质
\[
x^{\overline{n}}=\sum_{i=0}^{n} \begin{bmatrix} n \\ i \\ \end{bmatrix} x^{i}
\]
\[ x^{\underline{n}}=\sum_{i=0}^{n}\begin{bmatrix} n \\ i \\ \end{bmatrix}(-1)^{n-i}x^i \]
两个与上升幂与下降幂有关的性质。
上述性质大多可以用数学归纳法证明,限于篇幅,不给出具体证明。
求解第一类\(Stirling\)数
我们有\(O(nlog^2n)\)的分治\(NTT\)做法
\[
\sum_{i=0}^{n}\begin{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\)个都要放在靠边的位置,所以方案数是圆排列(固定了一个为第一个),即\(\begin{bmatrix} n-1 \\ A+B-2 \\ \end{bmatrix}\)。
然后选\(A-1\)个放左边,总方案数为\(\begin{bmatrix} n-1 \\ A+B-2 \\ \end{bmatrix}\cdot \begin{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\n", mul(C(A+B-2, A-1), S[0][A+B-2])); return 0; }
第二类\(Stirling\)数
\(\begin{Bmatrix} n \\ m \\ \end{Bmatrix}\)表示\(n\)个元素放入\(m\)个无差别盒子且每个盒子非空的方案数。
\[
\begin{Bmatrix} n \\ m \\ \end{Bmatrix}=\begin{Bmatrix} n-1 \\ m-1 \\ \end{Bmatrix}+m\begin{Bmatrix} n-1 \\ m \\ \end{Bmatrix}
\]
\[ \begin{Bmatrix} 0 \\ 0 \\ \end{Bmatrix}=1 \]
性质
\[ \begin{Bmatrix} n \\ m \\ \end{Bmatrix}=\frac{1}{m !} \sum_{i=0}^{m}(-1)^{i} \left( \begin{array}{c}{m} \\ {i}\end{array}\right)(m-i)^{n} \]
右边\(\times m!\)后变为有序。
容斥,选\(i\)个盒子为空,剩下\(n\)个球乱放\(m-i\)个盒子为\((m-i)^n\)。
\[
n^{m}=\sum_{i=0}^{n} \begin{Bmatrix} m \\ i \\ \end{Bmatrix} * A_n^i
\]
枚举非空盒子个数\(i\),\(\begin{Bmatrix} m \\ i \\ \end{Bmatrix}\)为球放入方案,排列为选盒子方案。
\[
\begin{align} \begin{Bmatrix} n \\ m \\ \end{Bmatrix}& = \frac{1}{m !} \sum_{i=0}^{m}(-1)^{i} \left( \begin{array}{c}{m} \\ {i}\end{array}\right)(m-i)^{n}\\
& = \sum_{i=0}^{n}\frac{(-1)^i(n-i)^n}{i!(m-i)!}\\
\end{align}
\]
卷积形式,\(FFT\)即可。时间复杂度\(O(nlogn)\)
例题
\(HEOI2016/TJOI2016\) 求和
\[
\begin{align}
f(n) &=\sum_{i=0}^{n}\sum_{j=0}^{i}\begin{Bmatrix} i \\ j \\ \end{Bmatrix}2^j\times j!\\
& =\sum_{j=0}^{n}2^j\times j!\sum_{i=0}^{n}\begin{Bmatrix} i \\ j \\ \end{Bmatrix}\\
& = \sum_{j=0}^{n}2^j\times j!\sum_{i=0}^{n}\sum_{k=0}^{j}\frac{(-1)^k(j-k)^i}{k!(i-k)!}\\
&=\sum_{j=0}^{n}2^j\times 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\n", ans); return 0; }
\(CF932E\ Team Work\)
\[
\begin{align}
Ans&=\sum_{i=1}^{n}\begin{pmatrix} n \\ i \\ \end{pmatrix}i^k \\
&=\sum_{i=0}^{n}\frac{n!}{(n-i)!}\sum_{j=0}^{k}\begin{Bmatrix} k \\ j \\ \end{Bmatrix}\frac{i!}{(i-j)!}\\
&=\sum_{i=0}^{n}\sum_{j=0}^{k}\begin{Bmatrix} k \\ j \\ \end{Bmatrix}\frac{n!}{(n-i)!(i-j)!}\\
&=\sum_{j=0}^{k}\begin{Bmatrix} k \\ j \\ \end{Bmatrix}\sum_{i=0}^{n}\begin{pmatrix} n-j \\ n-i \\ \end{pmatrix}\cdot \frac{n!}{(n-j)!}\\
&=\sum_{j=0}^{k}\begin{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\n", ans); return 0; }
留给读者自行完成
来源:https://www.cnblogs.com/ACMSN/p/10810418.html