多项式
模板
跳过定义与基础性质与操作。
默认 \(n\) 为 \(2\) 的幂。
\(1.FFT \text{与} NTT\)
设多项式 \(F(x)=\sum_{i=0}f_ix^i\) 和 \(G(x)=\sum_{i=0}g_ix^i\)。
首先有多项式卷积 \(H=F \times G\)。
那么有 \(h_i=\sum_{j=0}^if_jg_{i-j}\)。
直接计算是 \(O(n^2)\) 的。
考虑转化为点值表示,并引入复数。
在单位圆上,有单位根 \(\omega_n^k=\cos k\dfrac{2\pi}{n}+\sin k\dfrac{2\pi}{n}i\)。
单位根有一些性质:
\((1).DFT\)
我们尝试把多项式 \(F\) 搞成点值表示。
有 \(F(\omega_n^k)=\sum_{j=0}f_j\omega_n^{kj}\)
首先,将 \(F\) 的各项按照下表奇偶分类:
于是 \(F(x)=F_1(x^2)+xF_2(x^2)\)
现在我们尝试求出 \(F(\omega_n^k)(k\leq \dfrac{n}{2})\)。
代入得 \(F(\omega_n^k)=F_1(\omega_{\frac{n}{2}}^{k})+\omega_n^kF_2(\omega_{\frac{n}{2}}^{k})\)
同时代入 \(\omega_{n}^{k+\frac{n}{2}}\):
\(F(\omega_{n}^{k+\frac{n}{2}})=F_1(\omega_{\frac{n}{2}}^{k})-\omega_n^kF_2(\omega_{\frac{n}{2}}^{k})\)
两式仅有符号不同,可以递归处理。
\(O(nlogn)\)。
\((2).IDFT\)
设 \(F\) 的点值表达为 \(\{(\omega_n^0,y_0),(\omega_n^1,y_1)\dots\}\)。
那么有 \(f_i=\dfrac{1}{n}\sum_{j=0}y_j\omega_n^{-ji}\)
我们发现这和 \(F(\omega_n^k)=\sum_{j=0}f_j\omega_n^{kj}\) 长得很像。
于是相似处理。
然后就好了。
最后有一个东西叫蝴蝶操作,可以把递归变成迭代。
\((3).NTT\)
我们发现 \(FFT\) 有精度问题。
对于质数 \(p\) 及其原根 \(g\),有这样一个结论:
常用的是 \(p=998244353,g=3\)。
然后全部换掉就行了。
2.多项式乘法逆
有了多项式乘法,没有除法怎么行呢?
定义:多项式 \(F\) 的乘法逆 \(G\),满足 \(F(x)G(x)=1\)。
\((1)\) 递推
由定义得:
\(n=0\) 时化为 \(f[0]g[0]=1\)。
所以 \(g[0]=\dfrac{1}{f[0]}\)。
\(n>0\) 时有:
所以得到\(g[n]=-\dfrac{1}{f[0]}\sum_{i=1}^{n}f[i]g[n-i]\)
于是我们能得到 \(F\) 存在逆的充要条件是 \(f[0]≠0\)。
可惜这个方法是 \(O(n^2)\) 的。
\((2)\) 倍增
设 \(R(x) = F^{-1}(x)\ (mod\ x^{2n}),S(x) = F^{-1}(x)\ (mod\ x^{n})\)
那么有 \(R(x)-S(x)=0\ (mod\ x^n)\)
两边平方:\(R(x)^2-2*R(x)*S(x)+S(x)^2=0\ (mod\ x^{2n})\)
同乘 \(F(x),R(x)-2*S(x)+F(x)S(x)^2=0\ (mod\ x^{2n})\)
所以有 \(R(x)=2*S(x)-F(x)S(x)^2\)
3.导数和积分
由于
对于多项式来说,每一项分别求导/积分,再加起来就行。
所以
4.对数
求导。求逆。积分。
5. \(\text{exp}\)
两边求导得:
积回去:
这长的就很分治 NTT。
5.5
接下来就能做 \(F^k\) 的东西了,包括开根。
不过常数项不为 1 的时候不能直接用板子。
设常数项为 \(c\)。
则可以设 \(G=\dfrac{F}{c}\),求 \(G^k=\dfrac{F^k}{c^k}\)。
则 \(F^k=c^kG^k\)。
对于 \(k\) 是整数的时候还可以搞,但是比如开根的时候, \(c^k\) 就得是二次剩余了。
另外,当心常数项等于 0,这时候要整体位移以后做。
6.取模
设 \(n\) 次多项式 \(F\),\(m\) 次多项式 \(G\),要求找到 \(n-m\) 次多项式 \(Q\) 与 \(m-1\) 次多项式 \(R\),使得:
设多项式 \(F\) 系数翻转后的多项式为 \(F_R\),则有关系 \(F(\dfrac{1}{x})x^n=F_R(x)\)
那么,
于是就求出了 \(Q_R\),简单变换可得 \(Q\)。
然后简单变换可得 \(R=F-Q\times G\)。
6.5 常齐线推
设 \(f_n=\sum_{k=1}^mc_kf_{n-k}\),给定 \(c_1\dots c_m,f_0\dots f_{m-1}\),求 \(f_N\ (N=10^9)\)。
经过一些手膜可以发现,可以通过对 \(F(x)=x^N\) 取模来得到 \(f_N=\sum_{i=0}^{m-1}C_if_i\) 中的系数。
作为模数的多项式是 \(G=\sum_{i=0}^{m-1}-c_{m-i}x^i+x^m\)。
具体实现上可以对 \(x^N\) 的计算使用快速幂,只不过将整数取模换成了多项式取模。
复杂度 \(O(m\log m\log n)\)。
应用
1.斯特林数全家桶
斯二林行
\(\begin{Bmatrix}n\\m\end{Bmatrix}=\sum_{i=0}^m\dfrac{(-1)^{m-i}}{(m-i)!}\dfrac{i^n}{i!}\),直接卷。
斯二林列
\((e^z-1)^m=m!\sum_{n\ge 0}\begin{Bmatrix}n\\m\end{Bmatrix}\dfrac{z^n}{n!}\)
斯一林行
\(z^{\overline{n}}\)(上升幂)\(=\sum_{m=0}^n\begin{bmatrix}n\\m\end{bmatrix}z^m\),用倍增做。
斯一林列
\((\ln\dfrac{1}{1-z})^m=m!\sum_{n\ge 0}\begin{bmatrix}n\\m\end{bmatrix}\dfrac{z^n}{n!}\)
斯二行求和
就是贝尔数, \(\text{EGF}=\exp(e^z-1)\).
斯二列求和
注意到上面的式子,\(S_n=F\times G_n\),其中 \([z^m]S_n=\begin{Bmatrix}n\\m\end{Bmatrix},[z^m]F=\dfrac{(-1)^m}{m!},[z^m]G_n=\dfrac{m^n}{m!}\).
那么列求和 \(Sum_n=\sum_{i=0}^n S_i=F\times \sum_{i=0}^nG_n\).
后面那个和式的计算十分的显然,直接等比数列求和就好了。(真不知道 P4091 咋评的黑)
斯一行求和
这不是搞笑吗? \(ans=n!\).
斯一行的部分和
把 \(m!\) 除到左边去,左边变成 \(e^{-\ln(1-x)}\) 的前 \(m\) 项展开,多项式复合一下未尝不可?
斯一列求和
不会。
2.欧拉数
\(\langle{n \atop m}\rangle=\sum_{i=0}^m\dbinom{n+1}{i}(-1)^i(m-i+1)^n\)。
又是直接卷。
3.三角函数
有 \(e^{ix}=\cos(x)+i\sin(x)\).
同理,\(e^{-ix}=\cos(x)-i\sin(x)\).
那么,\(\cos(x)=\dfrac{e^{ix}+e^{-ix}}{2},\sin(x)=\dfrac{e^{ix}-e^{-ix}}{2i}\).
模意义下 \(i\equiv g^{\frac{p-1}{4}} \pmod p\).
一些结论
1.\(\ln(1-x^k)\)
2.\(f(x+c)\)(可用于斯一林行的倍增)
设 \(F=\sum_{i=0}^nf_{n-i}(n-i)!x^i,G=\sum_{i=0}^n\dfrac{c^i}{i!}x^i\)
那么就有 \(f(x+c)=\sum_{i=0}^n[x^{n-i}](F\times G)\dfrac{x^i}{i!}\)
3.\(\sum_{i=0}^{m}i^n\)
1. 伯努利数
伯努利数的 \(\text{EGF}\) 是 \(B=\dfrac{x}{e^x-1}\)。用它求自然数幂和的式子是:
展开组合数后可以轻松卷积。
2. \(\text{EGF}\)
直接求 \(\sum_{i=0}^ni^k\) 的 \(\text{EGF}\):
多项式模板(更新至 取模):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define f(i,a,b) for(int i=a;i<=b;++i)
#define wt int tt=d;while(tt--)
#define py puts("Yes")
#define pn puts("No")
#define fe(i,e) for(int i=0;i<e.size();i++)
#define vi vector<ll>
inline ll rd() {
ll x=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
return x*f;
}
ll dx[4]={0,1,0,-1};
ll dy[4]={1,0,-1,0};
#define d rd()
#define pb push_back
const ll Bit=21;
const ll PoL=(1<<Bit)+5;
ll but[PoL],pw[2][PoL],Curl=-1;
void out(ll *a,ll n){f(i,0,n)printf("%lld ",a[i]);puts("");}
const ll mod=998244353,G=3,Gi=332748118;
ll qp(ll a,ll b=mod-2){ll ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;b>>=1;
}return ans%mod;
}
namespace poly{
ll jc[PoL+10],inv[PoL+10],inc[PoL+10];
ll w[PoL]={1},qwq=0,Curl=-1;
// ll usla[PoL],uslb[PoL];
bool hpre=0;
void pre(){
if(hpre)return;hpre=1;
jc[0]=jc[1]=inc[0]=inc[1]=inv[0]=inv[1]=1;
f(i,2,PoL-1)jc[i]=jc[i-1]*i%mod,inv[i]=(mod-mod/i)*inv[mod%i]%mod,
inc[i]=inc[i-1]*inv[i]%mod;
}
#define ck(x) ((x)>=mod?(x)-mod:(x))
void revbit(ll k){
if(Curl==k)return;Curl=k;
f(i,0,k-1)but[i]=but[i>>1]>>1|((i&1)?(k>>1):0);
}
ll NTT(ll *a,ll *to,ll n,ll o){
ll _n=1;while(_n<=n)_n<<=1;n=_n;
revbit(n);qwq=0;
ll inv=qp(n);
// memcpy(to,a,sizeof(to));
f(i,0,n-1)to[i]=a[i];
f(i,0,n-1)if(i<but[i])swap(to[i],to[but[i]]);
for(int l=1;l<n;l<<=1){qwq++;
ll Wn=qp((o==1?G:Gi),((mod-1)/(l<<1)));
for(int i=1;i<l;i++)w[i]=w[i-1]*Wn%mod;
for(int j=0;j<n;j+=l+l){
for(int i=0;i<l;i++){
int tt=w[i]*to[i|j|l]%mod;
to[i|j|l]=mod+to[i|j]-tt;
to[i|j]=to[i|j]+tt;
}
}if(qwq%9==0)f(i,0,n-1)to[i]%=mod;
}if(o==1)inv=1;
f(i,0,n-1)to[i]=to[i]*inv%mod;
return n-1;
}
void Mul(ll *f,ll *g,ll *to,ll n,ll m){
static ll _f[PoL],_g[PoL],_n,_m;_n=n,_m=m;
f(i,0,_n)_f[i]=f[i];
f(i,0,_m)_g[i]=g[i];
_m+=_n,_n=1;
_n=NTT(_f,_f,_m,1);NTT(_g,_g,_m,1);
f(i,0,_n)to[i]=_f[i]*_g[i]%mod;
NTT(to,to,_m,-1);
f(i,_m+1,_n)to[i]=0;
f(i,0,_n)_f[i]=_g[i]=0;
}
void Inv(ll *a,ll *to,ll *usl,ll n){//a!=to
ll k=1;to[0]=qp(a[0]);
while(k<=n){k<<=1;
f(i,0,k-1)usl[i]=a[i];
NTT(to,to,k*2-1,1),NTT(usl,usl,k*2-1,1);
f(i,0,k*2-1)to[i]=(2-usl[i]*to[i]%mod+mod)%mod*to[i]%mod;
NTT(to,to,k*2-1,-1);f(i,k,k*2-1)to[i]=0;
}
}
void Dev(ll *a,ll *to,ll n){
f(i,0,n-1)to[i]=a[i+1]*(i+1)%mod;to[n]=0;
}
void Int(ll *a,ll *to,ll n){pre();
for(int i=n+1;i>=1;i--)to[i]=a[i-1]*inv[i]%mod;
to[0]=0;//to[0]=原函数的常数项
}
void Ln(ll *a,ll *to,ll *usl,ll n){//a!=to
f(i,1,4*n)usl[i]=0;
Inv(a,to,usl,n);
f(i,0,n)usl[i]=a[i];Dev(a,a,n);
ll len=NTT(to,to,2*n,1);NTT(a,a,2*n,1);
f(i,0,len)to[i]=to[i]*a[i]%mod;
NTT(to,to,2*n,-1);f(i,0,n)a[i]=usl[i];
Int(to,to,len);
f(i,1,4*n)usl[i]=0;
}
void work(ll *a,ll *to,ll *f,ll *g,ll l,ll r){
if(l==r){
to[l]=to[l]*inv[l]%mod;
return;
}ll mid=l+r>>1;
work(a,to,f,g,l,mid);ll lf=-1,lg=-1;
f(i,l,mid)f[++lf]=to[i];f(i,0,r-1-l)g[++lg]=a[i];
ll len=NTT(f,f,lf+lg,1);NTT(g,g,lf+lg,1);
f(i,0,len)f[i]=f[i]*g[i]%mod;
NTT(f,f,lf+lg,-1);
f(i,mid+1,r)to[i]=(to[i]+f[i-1-l])%mod;
f(i,0,len)f[i]=g[i]=0;
work(a,to,f,g,mid+1,r);
}void exp(ll *a,ll *to,ll *f,ll *g,ll n){//a!=to
f(i,1,4*n)f[i]=g[i]=0;
pre();
Dev(a,a,n);to[0]=1;
work(a,to,f,g,0,n);
}
ll lshift(ll *a,ll n,ll t){//a[t]->a[0]
f(i,0,n-t)a[i]=a[i+t];
return n-t;
}ll rshift(ll *a,ll n,ll t){//a[0]->a[t]
for(int i=n+t;i>=t;i--)a[i]=a[i-t];
f(i,0,t-1)a[i]=0;
return n+t;
}void mul(ll *a,ll n,ll k){
f(i,0,n)a[i]=a[i]*k%mod;
}
void pwr(ll *a,ll *to,ll *f,ll *g,ll *h,ll n,ll m){//a!=to
f(i,1,4*n)to[i]=f[i]=g[i]=h[i]=0;
ll t=0;
if(a[0]==0){
f(i,0,n)if(a[i]!=0){t=i;break;}
if(t==0||m*t>n)return;
}n=lshift(a,n,t);
ll C=a[0],iv=qp(C);
mul(a,n,iv);Ln(a,h,f,n);
mul(h,n,m);exp(h,to,f,g,n);
iv=qp(C,m);mul(to,n,iv);
n=rshift(to,n,t*m);n=n-t*m+t;
}
void div(ll *f,ll *g,ll *gr,ll *gg,ll *ff,ll *Q,ll *R,ll n,ll m){
f(i,0,4*n)gr[i]=ff[i]=0;
reverse(f,f+n+1);reverse(g,g+m+1);
Inv(g,gr,ff,n-m+1);
ll len=NTT(f,ff,2*n,1);NTT(gr,gg,len,1);
f(i,0,len)ff[i]=ff[i]*gg[i]%mod;
NTT(ff,Q,len,-1);reverse(Q,Q+n-m+1);
f(i,n-m+1,len)Q[i]=0;
reverse(g,g+m+1);
len=NTT(Q,ff,n*2,1);NTT(g,gr,n*2,1);
f(i,0,len)ff[i]=ff[i]*gr[i]%mod;
NTT(ff,ff,len,-1);
reverse(f,f+n+1);
f(i,0,n)R[i]=(f[i]-ff[i]+mod)%mod;
}
}
using namespace poly;
ll f[PoL],g[PoL],ff[PoL],gg[PoL],hh[PoL];
ll n,m,mm,lim;
int main(){
return 0;
}

浙公网安备 33010602011771号