\(\text{Link}\)
力求把最新技術翻譯地人人都能看懂。
推薦先學習:多項式複合逆、轉置原理。
題意
給出兩個 \(n\) 次多項式 \(F(x),G(x)\),求出 \(H(x)\equiv F(G(x))(\bmod x^{n+1})\)。
\(n\le 2\times 10^5\)。
思路
我們有:
\[h_i=\sum_{j=0}^nf_j[x^i]G^j(x)
\]
那麼這是由 \(f\) 至 \(h\) 的一個線性變化,不妨考慮轉置原理,該問題的轉置為:
\[f_j=\sum_{i=0}^nh_i[x^i]G^j(x)
\]
不難發現 \(F(x)=[x^n]\dfrac{H_R(x)}{1-yG(x)}\),其中 \(H_R(x)\) 表示把 \(H(x)\) 係數翻轉得到的多項式。如果 \(H(x)\) 是輸入,\(F(x)\) 是輸出,那麼我們可以直接使用 Bostan-Mori 演算法得到答案,那麼根據轉置原理,我們寫出該演算法的轉置即可。可以配合程式碼理解。
關於 Bostan-Mori 演算法的過程與複雜度證明,可參見多項式複合逆。
時間複雜度 \(O(n\log^2n)\)。
核心程式碼:
namespace PolyC{
//...
#define PolyY vector<Poly>
inline PolyY operator*(const PolyY &a,const PolyY &b){
int n=a.size(),m=b.size(),p=a[0].size(),q=b[0].size();
Poly P,Q;
P.resize(n*(p+q-1)),Q.resize(m*(p+q-1));
for(int i=0;i<n;i++)
for(int j=0;j<p;j++)
P[i*(p+q-1)+j]=a[i][j];
for(int i=0;i<m;i++)
for(int j=0;j<q;j++)
Q[i*(p+q-1)+j]=b[i][j];
P=P*Q;
PolyY F(n+m-1,Poly(p+q-1,0));
for(int i=0;i<n+m-1;i++)
for(int j=0;j<p+q-1;j++)
F[i][j]=P[i*(p+q-1)+j];
return F;
}
}
using namespace PolyC;
namespace MulTT{
inline Poly MulT(const Poly &a,const Poly &b){
Poly F=a,G=b;
int n=a.size(),m=b.size();
reverse(G.begin(),G.end());
init(n);
F.resize(lim),G.resize(lim);
NTT(F,1),NTT(G,1);
for(int i=0;i<lim;i++)
G[i]=1ll*F[i]*G[i]%mod;
NTT(G,-1);
for(int i=m-1;i<n;i++)
F[i-m+1]=G[i];
F.resize(max(0,n-m+1));
return F;
}
inline PolyY MulT(const PolyY &a,const PolyY &b){
int n=a.size(),m=b.size(),p=a[0].size(),q=b[0].size();
Poly P,Q;
P.resize(n*p),Q.resize(m*p);
for(int i=0;i<n;i++)
for(int j=0;j<p;j++)
P[i*p+j]=a[i][j];
for(int i=0;i<m;i++)
for(int j=0;j<q;j++)
Q[i*p+j]=b[m-1-i][q-1-j];
init(n*p);
P.resize(lim),Q.resize(lim);
NTT(P,1),NTT(Q,1);
for(int i=0;i<lim;i++)
P[i]=1ll*P[i]*Q[i]%mod;
NTT(P,-1);
PolyY F(n-m+1,Poly(p-q+1,0));
for(int i=m-1;i<n;i++)
for(int j=q-1;j<p;j++)
F[i-m+1][j-q+1]=P[i*p+j];
return F;
}
}
using namespace MulTT;
inline PolyY BostanMoriT(int n,Poly P,PolyY G){
if(!n){
int p=G[0].size();
P.resize(p*2-1);
return {MulT(P,Inv(G[0]))};
}
if(n+1<G.size()) G.resize(n+1);
PolyY H=G;
for(int i=1;i<H.size();i+=2)
for(int j=0;j<H[i].size();j++)
H[i][j]=dec(0,H[i][j]);
G=G*H;
PolyY A,B;
for(int i=0;i<G.size();i+=2) B.push_back(G[i]);
PolyY F=BostanMoriT(n/2,P,B);
int p=H.size(),q=F[0].size();
A.resize(p*2);
for(int i=0,j=0;i<p*2;i++){
if((i&1)==(n&1)&&j<F.size()) A[i]=F[j++];
else A[i]=Poly(q,0);
}
F=MulT(A,H);
return F;
}
inline Poly Comp(Poly F,Poly G){
int n=F.size();
G.resize(n);
PolyY Q;
for(int i=0;i<n;i++)
Q.push_back({!i,dec(0,G[i])});
PolyY P=BostanMoriT(n-1,F,Q);
Poly H(n,0);
for(int i=0;i<n;i++)
H[n-1-i]=P[i][0];
return H;
}