我是靠谱客的博主 无奈电灯胆,最近开发中收集的这篇文章主要介绍多项式求逆(倍增或分治FFT),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

传送门

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 (modx2n)F(x)G(x)1(modx2n)F(x)[G(x)H(x)]0(modx2n)G(x)H(x)0(modx2n)G(x)2+H(x)22G(x)H(x)0(modxn)xn,f(x)=G(x)H(x),f(x)2f(x)f(x)fi2=j=0ifjfijiij2n,0F(x),F(x)G(x)1G(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==kFiGj=0

考虑求出了前 x − 1 x-1 x1项的 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=1xFiGxi++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=1xFiGxiF01

可以发现这个 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....rl作卷积

其实就是多了个系数套模板了

#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)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部