多項式求逆
給定一個 \(n-1\) 次的多項式 \(A(x)(a_0 \neq 0)\) ,要求一個多項式 \(F(x)\) 滿足 \(F(x)A(x) \equiv 1 \pmod x^n\) 。
假設求出了 \(F_0(x)\) 滿足 \(F_0(x)A(x) \equiv 1 \pmod{x^{\lceil \frac{n}{2} \rceil}}\) ,那麼有
由於 \(A(x) \neq 0\) ,所以
又因爲 \(F(x)A(x) \equiv 1 \pmod{x^n}\) ,兩邊都乘個 \(A(x)\) ,
所以我們可以遞歸求解,時間複雜度 \(T(n) = T(n/2)+n\log n = n\log n\) 。
void get_inv(int *a, int *f, int n){
if(n == 1)return f[0] = fpow(a[0], mod-2), void();
get_inv(a, f, n+1>>1), init(2*n-1);//init()處理數組長度和蝴蝶變換
fp(i, 0, n-1)inv_ary[i] = a[i];
ntt(f, 0), ntt(inv_ary, 0);
fp(i, 0, lim-1)f[i] = 1ll*f[i]*(2-1ll*f[i]*inv_ary[i]%mod+mod)%mod;
ntt(f, 1);
fp(i, n, lim-1)f[i] = 0;
fp(i, 0, lim-1)inv_ary[i] = 0;
}
多項式求 \(\rm{ln}\) 。
給定一個 \(n-1\) 次的多項式 \(A(x)(a_0 = 1)\) ,要求一個多項式 \(F(x)\) 滿足 \(F(x) \equiv \ln A(x) \pmod{x^n}\) 。
兩邊求個導得到:
所以只需要對 \(A(x)\) 求個導和逆就能求出 \(F'(x)\) 了。最後再對 \(F'(x)\) 求個積分,由於 \(a_0=1\) ,所以常數項爲 \(0\) 。時間複雜度爲 \(\mathcal{O} (n\log n)\) 。
void get_ln(int *a, int *f, int n){
get_inv(a, ln_ary, n), init(2*n-2);
fp(i, 1, n-1)f[i-1] = 1ll*a[i]*i%mod;
ntt(f, 0), ntt(ln_ary, 0);
fp(i, 0, lim-1)f[i] = 1ll*f[i]*ln_ary[i]%mod;
ntt(f, 1);
fp(i, n-1, lim)f[i] = 0;
fp(i, 0, lim)ln_ary[i] = 0;
fb(i, n-1, 1)f[i] = 1ll*f[i-1]*inv[i]%mod;
f[0] = 0;
}
牛頓迭代
給定一個函數 \(G(x)\) ,要求一個多項式 \(F(x)\) 滿足 \(G(F(x)) \equiv 0 \pmod{x^n}\) 。
假設求出了 \(F_0(x)\) 滿足 \(G(F_0(x)) \equiv 0 \pmod{x^{\lceil \frac{n}{2}\rceil}}\) ,那麼把 \(G(x)\) 在 \(F_0(x)\) 處泰勒展開可以得到:
因爲 \(F(x)-F_0(x)\) 最低次是 \(x^{\lceil \frac{n}{2}\rceil}\) ,所以從第三項開始模 \(x^n\) 就爲 \(0\) 了。 注意這裏的求導是求 \(F_0(x)\) 的偏導 。然後就可以遞歸計算了。
多項式 \(\rm{exp}\)
給定一個 \(n-1\) 次多項式 \(A(x)(a_0=0)\) ,要求一個多項式 \(F(x)\) 滿足 \(F(x) \equiv e^{A(x)} \pmod{x^n}\) 。
兩邊求個 \(\ln\) : \(\ln F(x) \equiv A(x) \pmod{x^n}\) 。設 \(G(F(x))=\ln F(x)-A(x)\) ,那就相當於求 \(F(x)\) 滿足 \(G(F(x)) \equiv 0 \pmod{x^n}\) 。
根據牛頓迭代的式子, \(F(x) \equiv F_0(x)-F_0(x)(\ln F_0(x)-A(x)) \equiv F_0(x)(1-\ln F_0(x)+A(x))\) 。遞歸求解即可,時間複雜度 \(\mathcal{O} (n\log n)\) 。
void get_exp(int *a, int *f, int n){
if(n == 1)return f[0] = 1, void();
get_exp(a, f, n+1>>1), get_ln(f, exp_ary, n), init(2*n-1);
fp(i, 0, n-1)exp_ary[i] = (a[i]-exp_ary[i]+mod)%mod;
if((++exp_ary[0]) == mod)exp_ary[0] = 0;
ntt(f, 0), ntt(exp_ary, 0);
fp(i, 0, lim-1)f[i] = 1ll*f[i]*exp_ary[i]%mod;
ntt(f, 1);
fp(i, n, lim-1)f[i] = 0;
fp(i, 0, lim-1)exp_ary[i] = 0;
}
分治 \(\rm{FFT/NTT}\)
求出 \(\prod_{i=1}^n(a_{i,0}+a_{i,1}x+a_{i,2}x^2+ \cdots +a_{i,k+1}x^{k+1})\) ,要求複雜度爲 \(\mathcal{O} (nk \log^2 nk)\) 。一般情況下 \(k\) 都等於 \(1\) 。
顯然不能從左到右乘過去,這樣的話複雜度高達 \(\mathcal{O} (n^2)\) 。
考慮分治,每次做完左右兩邊的乘法,這樣左右兩邊的長度都是 \(\frac{len}{2}\) 的, \(len\) 是當前的分治區間長度。然後把兩邊的乘起來即可。顯然複雜度降到了 \(\mathcal{O} (nk \log^2 nk)\) 。當 \(k=1\) 時就是 \(\mathcal{O} (n \log^2 n)\) 。
貼個 \(k=1\) 時的代碼(\(a_0 = 1\))。
int div_ary[20][maxn<<2];
void divntt(int d, int l, int r){
if(l == r)return div_ary[d][0] = 1, div_ary[d][1] = mod-a[l], void();
int md = l+r>>1, len = r-l+2;
divntt(d, l, md), divntt(d+1, md+1, r), init(len), ntt(div_ary[d], 0), ntt(div_ary[d+1], 0);
fp(i, 0, lim-1)div_ary[d][i] = 1ll*div_ary[d][i]*div_ary[d+1][i]%mod;
ntt(div_ary[d], 1);
fp(i, len, lim-1)div_ary[d][i] = 0;
fp(i, 0, lim-1)div_ary[d+1][i] = 0;
}
常見的數
第一類斯特林數
定義 :\(\begin{bmatrix} n\\m \end{bmatrix}\) 表示把 \(n\) 個有標號元素劃分成 \(m\) 個圓排列的方案數。
遞推公式 :
考慮加入第 \(n\) 個元素,它要麼新成一個環,要麼排在前 \(n-1\) 個元素中某一個的後面。
組合意義 : \(n! = \sum_{i=0}^n\begin{bmatrix} n\\i \end{bmatrix}\)
可以理解爲一個排列對應爲若干個置換,每個置換又對應一個圓排列。
生成函數 :\(\sum_{i=0}^{\infty} \begin{bmatrix} n\\i \end{bmatrix}x^i=x^{\overline{n}}\)
考慮歸納法,首先有 \(\begin{bmatrix} 0\\0 \end{bmatrix}x^0=x^{\overline{0}}\) 。
所以 \(\sum_{i=0}^{\infty} \begin{bmatrix} n+1\\i \end{bmatrix}x^i=x^{\overline{n+1}}\)
考慮求這個生成函數。直接的方法可以用分治 \(\rm{NTT}\) 求,複雜度 \(\mathcal{O} (n\log^2 n)\)。還有種倍增的方法。
設 \(F_n(x) = x^{\overline{n}}\) ,考慮怎麼通過它求出 \(F_{2n}(x)\) 。顯然有 \(F_{2n}=F_n(x)F_n(x+n)\) 。
很容易看出來後面的式子可以卷積。設 \(a_i=i!\begin{bmatrix} n\\i \end{bmatrix},b_i=\frac{n^i}{i!}\),將 \(a\) 的下標反轉一下,卷積的第 \(n-j\) 次項係數除以 \(j!\) 就是 \(F_n(x+n)\) 的第 \(j\) 次項係數。現在就可以遞歸求解了,當 \(n\) 爲奇數時就將 \(n\) 減一,求出多項式後乘一個 \(x+n-1\) 即可。複雜度 \(\mathcal{O} (n\log n)\) 。
第二類斯特林數
定義 : \(\begin{Bmatrix} n\\m \end{Bmatrix}\) 表示將 \(n\) 個有標號元素劃分爲 \(m\) 個無標號非空集的方案數。
遞推公式 :
考慮加入第 \(n\) 個元素,它要麼新成一個集合,要麼被划進已有的元素。
組合意義 : \(m^n = \sum_{i=0}^m{m\choose i}\begin{Bmatrix} n\\i \end{Bmatrix}i!\)
因爲左邊是 \(n\) 個有標號元素劃分爲任意個有標號集合的方案數,那麼枚舉有幾個非空集,很自然能得到右邊的式子。
通項公式 :
有兩種方法理解這個式子:
由於不能選空集,那就容斥掉空集的情況。所以枚舉選了 \(i\) 個空集,然後將所有 \(n\) 個元素放入 \(m-i\) 個集合中(可以爲空集)。容易看出來容斥係數爲 \((-1)^i{m\choose i}\) 。
或者考慮二項式反演:
有了這個通項之後就可以 \(\mathcal{O} (n\log n)\) 求解一行第二類斯特林數了,因爲把組合數打開可以發現是卷積的形式。
貝爾數
定義 : \(B_n\) 表示將 \(n\) 個有標號元素劃分爲任意個無標號非空集合的方案數。
遞推公式 :
考慮加入第 \(n+1\) 個元素,枚舉它與多少個元素划進同一個集合,剩下的元素任意劃分。
組合意義 :
這樣的話後面的 \(\sum\) 就只與上界有關了,先給後面的 \(\sum\) 求個前綴和,再線篩 \(i^n\) ,就可以 \(\mathcal{O}(n)\) 求解一個貝爾數了。
生成函數 : \(\sum_{n=0}^{\infty}B_nx^n = e^{e^x-1}\) 。
寫出 \(n\) 個數劃分成一個集合的方案數 \(f_n\) ,顯然 \(f_n=1\) 。考慮劃分成任意個集合的方案數就是 \(e^{F(x)}\) ,其中 \(F(x) = \sum_{n=1}^{\infty}\frac{f_n}{n!}x^n = e^x-1\) 。所以寫一個多項式 \(\rm{exp}\) 就可以 \(\mathcal{O} (n\log n)\) 求解前 \(n\) 個貝爾數了。
通項公式 :
所以通項爲 \(\frac{1}{e}\sum_{n=0}^{\infty}\frac{n^i}{n!}\) 。
\(\rm Touchard\) 同餘
\(B_{n+p}\equiv B_n+B_{n+1}\pmod{p}\)。
引理 \(1\) :\(B_{n+m}=\sum_{i=0}^n\sum_{j=0}^m j^{n-i}\begin{Bmatrix}m\\j\end{Bmatrix}{n\choose i}B_i\)
把n+m個元素分成n個元素和m個元素。首先枚舉m個元素劃分成了j個集合,就有了\(\begin{Bmatrix}m\\j\end{Bmatrix}\);再枚舉n個元素中選i個出來任意劃分,就有了\({n\choose i}B_i\);再把剩下的n-i個元素隨意放進m個元素的那j個集合裏,就有了\(j^{n-i}\) 。根據乘法原理乘起來,就是\(B_{n+m}\)。正確性是因爲每種劃分方案都可以像這樣描述。
引理 \(2\) : \(\begin{Bmatrix}n\\m\end{Bmatrix}\equiv0\pmod n(1<m<n),n\)爲質數 。
展開左邊的式子:\(\begin{Bmatrix}n\\m\end{Bmatrix}=\frac{1}{m!}\sum_{i=0}^m(-1)^i{m\choose i}(m-i)^n\),可以發現組合數的m!可以跟前面約了:
根據費馬小定理,可以把m-i的指數消一下:\(\equiv\sum_{i=0}^m\frac{(-1)^i}{i!}\frac{m-i}{(m-i)!}\pmod n\)
當\(i=m\)的時候後面就=0,所以\(i\)最多等於\(m-1\),那麼根據二項式定理就可得:
所以當\(1<m<n\)的時候\(\begin{Bmatrix}n\\m\end{Bmatrix}\)就同餘0。
證明 \(\rm Touchard\) 同餘:
最後我們寫出引理1的式子:
根據引理\(2\),當\(1<j<p\)時,後邊的式子值爲\(0\)。又根據第二類斯特林數的定義,當\(j=0\)時,後邊也爲\(0\),當\(j=1\)或者\(p\)時,\(\begin{Bmatrix}p\\j\end{Bmatrix}=1\),。所以這個式子可以進一步寫成:
根據最開始的\(n^2\)的遞推式,加號左邊的就是\(B_{n+1}\);由於是模意義下的,所以當\(i\neq n\)時,\(p^{n-i}{n\choose i}B_i\equiv 0\pmod{p}\)。。當i=n時,這個式子等於\(B_n\)。
所以\(B_{n+p}\equiv B_n+B_{n+1}\pmod{p}\)
劃分數
定義 : \(p_{n,m}\) 表示將 \(n\) 劃分成 \(m\) 個無標號 自然數 和的方案數。
遞推公式 : \(p_{n,m} = p_{n-m,m}+p_{n,m-1}\) 。
要麼給已有的 \(m\) 個數都加 \(1\) ,要麼加入一個 \(0\) 。
生成函數 : \(P_n(x) = \sum_{i=0}^{\infty}p_{i,n}x^i\) 。
根據遞推公式,
又有 \(P_1(x) = \sum_{i=0}^{\infty}x^i=\frac{1}{1-x}\) ,不難發現
直接的方法就是分治 \(\rm NTT\) ,複雜度 \(\mathcal{O} (n\log^2 n)\) 。
觀察到 \(x^i\) 的係數都是 \(-1\) ,考慮繼續優化:
設 \(F_i(x) = \ln(1-x^i)\) ,
所以暴力確定 \(\ln P_n(x)\) 的係數是 \(\mathcal{O} (n\ln n)\) 的,再做個 \(\rm exp\) 就可以求得 \(P_n(x)\) 。
一些奇技淫巧
\(\rm EularTransform\)
給定長度爲 \(n\) 的數列 \(a\) ,要求 \(F(x) = \sum_{i=1}^n\frac{1}{1-a_ix}\) 。
當然可以通分+分治 \(\rm NTT\) ,但那樣常數有點大。
\(\rm EularTransform\) 的思想是通過 \(1-x(\ln (1-a_ix))' = 1+\frac{a_i}{1-a_ix}=\frac{1}{1-a_ix}\) ,得到
\(\prod\) 就用分治 \(\rm NTT\) 算,複雜度 \(\mathcal{O} (n\log^2 n)\) 。
不知道叫啥
給定長度爲 \(n\) 的數列 \(a\) 和多項式 \(F(x)\) ,要求 \(\sum_{i=1}^nF(a_ix)\) 。
暴力做顯然是 \(\mathcal{O} (n^2)\) 的。
注意到最後的多項式的第 \(m\) 次項係數肯定是 \(\sum_{i=1}^na_i^m\) 。考慮寫出這玩意兒的 \(\rm OGF\) :
剩下的就是上面的 \(\rm EularTransform\) 了。
自然數冪和
對於所有 \(n\in [0,N]\) 求出 \(\sum_{i=0}^mi^n\) ,也就是剛纔那個東西的特殊形式。
這次我們寫出 \(\rm EGF\) :
給分母展開求個逆再乘上分母即可。注意到分母的常數項爲 \(0\) 不能直接求逆,但是分子的常數項也爲 \(0\) ,同時除以 \(x\) 即可。複雜度 \(\mathcal{O} (n\log n)\) 。
任意模數 \(\rm NTT\)
求 \(A(x)B(x)\) 的係數對\(10^9+7\) 取模的結果,最高次項是 \(10^5\) 級別,係數是 \(10^9\) 級別。
分析可得最後係數的值域應是 \(10^23\) 以內。那麼對 \(998244353,1004535809,469762049\) 三個模數分別做 \(\rm NTT\) ,就能用 \(\rm CRT\) 解出真實值(因爲三個模數乘起來大於 \(10^23\) ),再對 \(10^9+7\) 取模即可。選這三個因爲原根都是 \(3\) 。
咕咕咕