概述
传送门
F ( x ) ∗ H ( x ) ≡ 1 ( m o d x ⌈ n 2 ⌉ ) 由 题 意 得 F ( x ) ∗ G ( x ) ≡ 1 ( m o d x ⌈ n 2 ⌉ ) 那 么 F ( x ) ∗ [ G ( x ) − H ( x ) ] ≡ 0 ( m o d x ⌈ n 2 ⌉ ) 就 是 G ( x ) − H ( x ) ≡ 0 ( m o d x ⌈ n 2 ⌉ ) 两 边 同 时 平 方 得 到 G ( x ) 2 + H ( x ) 2 − 2 G ( x ) H ( x ) ≡ 0 ( m o d x n ) 模 数 变 成 x n 是 有 原 因 的 , 我 们 定 义 f ( x ) = G ( x ) − H ( x ) , f ( x ) 2 为 f ( x ) ∗ f ( x ) 因 为 f i 2 = ∑ j = 0 i f j ∗ f i − j 其 中 i 和 i − j 不 可 能 同 时 大 于 n 2 , 所 以 一 定 有 一 项 为 0 得 证 我 们 再 两 边 同 时 乘 上 F ( x ) 得 到 , 结 合 F ( x ) G ( x ) ≡ 1 得 到 G ( x ) ≡ 2 H ( x ) − F ( x ) H ( x ) 2 ( m o d x n ) F(x)*H(x)equiv 1 (mod x^{lceil frac{n}{2}rceil})\ 由题意得F(x)*G(x)equiv1(mod x^{lceil frac{n}{2} rceil})\ 那么F(x)*[G(x)-H(x)]equiv0(mod x^{lceil frac{n}{2} rceil} )\ 就是G(x)-H(x)equiv0(mod x^{lceil frac{n}{2} rceil} )\ 两边同时平方得到 G(x)^2+H(x)^2-2G(x)H(x)equiv0(mod x^n )\ 模数变成x^n是有原因的,我们定义f(x)=G(x)-H(x),f(x)^2为f(x)*f(x)\ 因为f_i^2=sumlimits_{j=0}^if_j*f_{i-j}\ 其中i和i-j不可能同时大于frac{n}{2},所以一定有一项为0得证\ 我们再两边同时乘上F(x)得到,结合F(x)G(x)equiv1得到\ G(x)equiv 2H(x)-F(x)H(x)^2(mod x^n) F(x)∗H(x)≡ 1 (modx⌈2n⌉)由题意得F(x)∗G(x)≡1(modx⌈2n⌉)那么F(x)∗[G(x)−H(x)]≡0(modx⌈2n⌉)就是G(x)−H(x)≡0(modx⌈2n⌉)两边同时平方得到G(x)2+H(x)2−2G(x)H(x)≡0(modxn)模数变成xn是有原因的,我们定义f(x)=G(x)−H(x),f(x)2为f(x)∗f(x)因为fi2=j=0∑ifj∗fi−j其中i和i−j不可能同时大于2n,所以一定有一项为0得证我们再两边同时乘上F(x)得到,结合F(x)G(x)≡1得到G(x)≡2H(x)−F(x)H(x)2(modxn)
得到这个式子我们就可以倍增求解 G ( x ) G(x) G(x)
就是先求模 x 1 x^1 x1下的 G ( x ) G(x) G(x)
然后把这个 G ( x ) G(x) G(x)作为 H ( x ) H(x) H(x),求模 x 2 x^2 x2下的 G ( x ) G(x) G(x)
再求模 x 4 x^4 x4下的 G ( x ) G(x) G(x),问题得到解,乘法使用 N T T NTT NTT求解
新版本
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e6+10;
const int mod = 998244353, G = 3, Gi = (mod+1)/3;
int f[maxn],g[maxn],a[maxn],b[maxn],c[maxn],r[maxn],n;
int quick(int x,int n)
{
int ans = 1;
for( ; n ; n>>=1,x=x*x%mod )
if( n&1 ) ans = ans*x%mod;
return ans;
}
void NTT(int *a,int limit,int type)
{
for(int i=0;i<limit;i++)
if( i<r[i] ) swap( a[i],a[r[i]] );
for(int mid=1;mid<limit;mid<<=1 )
{
int wn = quick( (type==1)?G:Gi,(mod-1)/(mid<<1) );
for(int R=mid<<1,i=0;i<limit;i+=R )
for(int w=1,k=0;k<mid;k++,w=w*wn%mod)
{
int x = a[i+k], y = a[i+mid+k]*w%mod;
a[i+k] = (x+y)%mod, a[i+mid+k] = (x-y+mod)%mod;
}
}
if( type==1 ) return;
int inv = quick(limit,mod-2);
for(int i=0;i<limit;i++) a[i] = a[i]*inv%mod;
}
void solve(int bit,int *f,int *g)
{
if( bit==1 ){ g[0] = quick(f[0],mod-2); return; }//递归边界
solve( (bit+1)>>1,f,g );//开始倍增,注意是向上取整(多了没关系,少了不行)
int limit = 1;
while( limit<(bit<<1) ) limit<<=1;
for(int i=0;i<limit;i++)
r[i] = ( r[i>>1]>>1 ) | ( (i&1)?limit>>1:0 );
for(int i=0;i<limit;i++) a[i] = (i<bit?f[i]:0), b[i] = g[i];
NTT(a,limit,1); NTT(b,limit,1);
for(int i=0;i<limit;i++) g[i] = (2-a[i]*b[i]%mod+mod)%mod*b[i]%mod;
NTT(g,limit,-1);
for(int i=bit;i<limit;i++) g[i] = 0;
}
signed main()
{
cin >> n;
for(int i=0;i<n;i++) cin >> f[i];
solve(n,f,g);
for(int i=0;i<n;i++) printf("%lld ",g[i]);
}
老版本:传送门
分治FFT
因为对于 k ∈ [ 1 , n ] kin[1,n] k∈[1,n]满足 ∑ i + j = = k F i G j = 0 sumlimits_{i+j==k}F_iG_j=0 i+j==k∑FiGj=0
考虑求出了前 x − 1 x-1 x−1项的 G G G如何求出 G x G_x Gx?
因为 ∑ i = 1 x F i G x − i + + F 0 G x = 0 sumlimits_{i=1}^{x}F_iG_{x-i+}+F_0G_x=0 i=1∑xFiGx−i++F0Gx=0
也就是 G x = − ∑ i = 1 x F i G x − i ∗ 1 F 0 G_x=-sumlimits_{i=1}^xF_iG_{x-i}*frac{1}{F_0} Gx=−i=1∑xFiGx−i∗F01
可以发现这个 G G G数组其实就是可以分治 F F T FFT FFT来求解
就是考虑求出了 [ l , m i d ] [l,mid] [l,mid]的 F F F数值,累加贡献到 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]上面去
就是拿 F l . . . . . . m i d F_{l......mid} Fl......mid去和 G 1.... r − l G_{1....r-l} G1....r−l作卷积
其实就是多了个系数套模板了
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 3e6+10;
const int mod = 998244353, G = 3, Gi = (mod+1)/G;
int ans[maxn],f[maxn],g[maxn],a[maxn],r[maxn],n;
int quick(int x,int n)
{
int ans = 1;
for( ; n ; n>>=1,x = x*x%mod )
if( n&1 ) ans = ans*x%mod;
return ans;
}
void init(int limit)
{
for(int i=0;i<limit;i++)
r[i] = ( r[i>>1]>>1 ) | ((i&1)?limit>>1:0 );
}
void NTT(int *a,int limit,int type)
{
for(int i=0;i<limit;i++)
if( i<r[i] ) swap( a[i],a[r[i]] );
for(int mid=1;mid<limit;mid<<=1)//待合并区间长度
{
int wn = quick( type==1?G:Gi,(mod-1)/(mid<<1) );
for(int R=mid<<1,i=0;i<limit;i+=R )//i是待合并区间左端点
for(int k=0,w=1;k<mid;k++,w=w*wn%mod )
{
int x = a[i+k], y = a[i+k+mid]*w%mod;
a[i+k] = ( x+y )%mod, a[i+k+mid] = ( x-y+mod )%mod;
}
}
}
void solve(int l,int r)
{
if( l+1>=r ) return;
int mid = l+r>>1;
solve(l,mid);
int len = r-l; init(len);
for(int i=1;i<len;i++) g[i] = a[i];
for(int i=l;i<mid;i++) f[i-l] = ans[i];
for(int i=mid;i<r;i++) f[i-l] = 0;
NTT(f,len,1); NTT(g,len,1);
for(int i=0;i<len;i++) f[i] = f[i]*g[i]%mod;
NTT(f,len,-1);
int inv = quick(len,mod-2);
int invv = quick( (-a[0]%mod+mod)%mod,mod-2 );
for(int i=mid;i<r;i++) ans[i] = ( ans[i]+f[i-l]*inv%mod*invv%mod )%mod;
solve(mid,r);
}
signed main()
{
scanf("%lld",&n);
for(int i=0;i<n;i++) scanf("%lld",&a[i] );
int limit = 1;
while( limit<n ) limit<<=1;
ans[0] = quick(a[0],mod-2);
solve(0,limit);
for(int i=0;i<n;i++)
printf("%lld ",ans[i] );
}
最后
以上就是无奈电灯胆为你收集整理的多项式求逆(倍增或分治FFT)的全部内容,希望文章能够帮你解决多项式求逆(倍增或分治FFT)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复