多项式求逆
给定一个 (n-1) 次的多项式 (A(x)(a_0
eq 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}
ceil}}) ,那么有
由于 (A(x) eq 0) ,所以
又因为 (F(x)A(x) equiv 1 pmod{x^n}) ,两边都乘个 (A(x)) ,
所以我们可以递归求解,时间复杂度 (T(n) = T(n/2)+nlog n = nlog 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;
}
多项式求 ( m{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} (nlog 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}
ceil}}) ,那么把 (G(x)) 在 (F_0(x)) 处泰勒展开可以得到:
因为 (F(x)-F_0(x)) 最低次是 (x^{lceil frac{n}{2} ceil}) ,所以从第三项开始模 (x^n) 就为 (0) 了。 注意这里的求导是求 (F_0(x)) 的偏导 。然后就可以递归计算了。
多项式 ( m{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} (nlog 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;
}
分治 ( m{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;
}
常见的数
第一类斯特林数
定义 :(egin{bmatrix} n\m end{bmatrix}) 表示把 (n) 个有标号元素划分成 (m) 个圆排列的方案数。
递推公式 :
考虑加入第 (n) 个元素,它要么新成一个环,要么排在前 (n-1) 个元素中某一个的后面。
组合意义 : (n! = sum_{i=0}^negin{bmatrix} n\i end{bmatrix})
可以理解为一个排列对应为若干个置换,每个置换又对应一个圆排列。
生成函数 :(sum_{i=0}^{infty} egin{bmatrix} n\i end{bmatrix}x^i=x^{overline{n}})
考虑归纳法,首先有 (egin{bmatrix} 0\0 end{bmatrix}x^0=x^{overline{0}}) 。
所以 (sum_{i=0}^{infty} egin{bmatrix} n+1\i end{bmatrix}x^i=x^{overline{n+1}})
考虑求这个生成函数。直接的方法可以用分治 (
m{NTT}) 求,复杂度 (mathcal{O} (nlog^2 n))。还有种倍增的方法。
设 (F_n(x) = x^{overline{n}}) ,考虑怎么通过它求出 (F_{2n}(x)) 。显然有 (F_{2n}=F_n(x)F_n(x+n)) 。
很容易看出来后面的式子可以卷积。设 (a_i=i!egin{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} (nlog n)) 。
第二类斯特林数
定义 : (egin{Bmatrix} n\m end{Bmatrix}) 表示将 (n) 个有标号元素划分为 (m) 个无标号非空集的方案数。
递推公式 :
考虑加入第 (n) 个元素,它要么新成一个集合,要么被划进已有的元素。
组合意义 : (m^n = sum_{i=0}^m{mchoose i}egin{Bmatrix} n\i end{Bmatrix}i!)
因为左边是 (n) 个有标号元素划分为任意个有标号集合的方案数,那么枚举有几个非空集,很自然能得到右边的式子。
通项公式 :
有两种方法理解这个式子:
由于不能选空集,那就容斥掉空集的情况。所以枚举选了 (i) 个空集,然后将所有 (n) 个元素放入 (m-i) 个集合中(可以为空集)。容易看出来容斥系数为 ((-1)^i{mchoose i}) 。
或者考虑二项式反演:
有了这个通项之后就可以 (mathcal{O} (nlog 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) 。所以写一个多项式 (
m{exp}) 就可以 (mathcal{O} (nlog n)) 求解前 (n) 个贝尔数了。
通项公式 :
所以通项为 (frac{1}{e}sum_{n=0}^{infty}frac{n^i}{n!}) 。
(
m Touchard) 同余
(B_{n+p}equiv B_n+B_{n+1}pmod{p})。
引理 (1) :(B_{n+m}=sum_{i=0}^nsum_{j=0}^m j^{n-i}egin{Bmatrix}m\jend{Bmatrix}{nchoose i}B_i)
把n+m个元素分成n个元素和m个元素。首先枚举m个元素划分成了j个集合,就有了(egin{Bmatrix}m\jend{Bmatrix});再枚举n个元素中选i个出来任意划分,就有了({nchoose i}B_i);再把剩下的n-i个元素随意放进m个元素的那j个集合里,就有了(j^{n-i}) 。根据乘法原理乘起来,就是(B_{n+m})。正确性是因为每种划分方案都可以像这样描述。
引理 (2) : (egin{Bmatrix}n\mend{Bmatrix}equiv0pmod n(1<m<n),n)为质数 。
展开左边的式子:(egin{Bmatrix}n\mend{Bmatrix}=frac{1}{m!}sum_{i=0}^m(-1)^i{mchoose i}(m-i)^n),可以发现组合数的m!可以跟前面约了:
根据费马小定理,可以把m-i的指数消一下:(equivsum_{i=0}^mfrac{(-1)^i}{i!}frac{m-i}{(m-i)!}pmod n)
当(i=m)的时候后面就=0,所以(i)最多等于(m-1),那么根据二项式定理就可得:
所以当(1<m<n)的时候(egin{Bmatrix}n\mend{Bmatrix})就同余0。
证明 (
m Touchard) 同余:
最后我们写出引理1的式子:
根据引理(2),当(1<j<p)时,后边的式子值为(0)。又根据第二类斯特林数的定义,当(j=0)时,后边也为(0),当(j=1)或者(p)时,(egin{Bmatrix}p\jend{Bmatrix}=1),。所以这个式子可以进一步写成:
根据最开始的(n^2)的递推式,加号左边的就是(B_{n+1});由于是模意义下的,所以当(i
eq n)时,(p^{n-i}{nchoose i}B_iequiv 0pmod{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}) ,不难发现
直接的方法就是分治 (
m NTT) ,复杂度 (mathcal{O} (nlog^2 n)) 。
观察到 (x^i) 的系数都是 (-1) ,考虑继续优化:
设 (F_i(x) = ln(1-x^i)) ,
所以暴力确定 (ln P_n(x)) 的系数是 (mathcal{O} (nln n)) 的,再做个 ( m exp) 就可以求得 (P_n(x)) 。
一些奇技淫巧
( m EularTransform)
给定长度为 (n) 的数列 (a) ,要求 (F(x) = sum_{i=1}^nfrac{1}{1-a_ix}) 。
当然可以通分+分治 (
m NTT) ,但那样常数有点大。
(
m EularTransform) 的思想是通过 (1-x(ln (1-a_ix))' = 1+frac{a_i}{1-a_ix}=frac{1}{1-a_ix}) ,得到
(prod) 就用分治 ( m NTT) 算,复杂度 (mathcal{O} (nlog^2 n)) 。
不知道叫啥
给定长度为 (n) 的数列 (a) 和多项式 (F(x)) ,要求 (sum_{i=1}^nF(a_ix)) 。
暴力做显然是 (mathcal{O} (n^2)) 的。
注意到最后的多项式的第 (m) 次项系数肯定是 (sum_{i=1}^na_i^m) 。考虑写出这玩意儿的 (
m OGF) :
剩下的就是上面的 ( m EularTransform) 了。
自然数幂和
对于所有 (nin [0,N]) 求出 (sum_{i=0}^mi^n) ,也就是刚才那个东西的特殊形式。
这次我们写出 (
m EGF) :
给分母展开求个逆再乘上分母即可。注意到分母的常数项为 (0) 不能直接求逆,但是分子的常数项也为 (0) ,同时除以 (x) 即可。复杂度 (mathcal{O} (nlog n)) 。
任意模数 ( m NTT)
求 (A(x)B(x)) 的系数对(10^9+7) 取模的结果,最高次项是 (10^5) 级别,系数是 (10^9) 级别。
分析可得最后系数的值域应是 (10^23) 以内。那么对 (998244353,1004535809,469762049) 三个模数分别做 (
m NTT) ,就能用 (
m CRT) 解出真实值(因为三个模数乘起来大于 (10^23) ),再对 (10^9+7) 取模即可。选这三个因为原根都是 (3) 。
咕咕咕