多项式求逆与多项式除法/取模

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/83098562

多项式求逆

Procedure

多项式求逆是多项式模块中的一个重要操作(“操作”这个词看出如今多项式题是多么的工业化,犹如毒瘤8操作LCT),在做生成函数/多项式除法、多项式取模/多项式多点求值等中均有应用

对于一个n次多项式 F ( x ) F(x) ,我们希望求出一个m-1次多项式 G ( x ) G(x) ,满足 F ( x ) G ( x ) 1 ( m o d x m ) F(x)G(x)\equiv 1\pmod {x^m}
也就是说, G ( x ) G(x) F ( x ) F(x) 在模 x m x^m 意义下的逆(即 x m x^m 以后的项我们都不管了,前面的项乘起来只有常数项系数为1,其他系数都是0)

多项式求逆运用了倍增的思想
假设我们已经求出了 B ( x ) , B ( x ) F ( x ) 1 ( m o d x m 2 ) B(x),B(x)F(x)\equiv 1\pmod {x^{m\over 2}}
考虑如何求 G ( x ) G(x)

移项
B ( x ) F ( x ) 1 0 ( m o d x m 2 ) B(x)F(x)-1\equiv 0\pmod {x^{m\over 2}}
此时意味着 B ( x ) F ( x ) 1 B(x)F(x)-1 的0 ~ m/2-1次项的系数全都是0,那么将同余式两边平方,就变成0 ~ m-1次项的系数都为0
因此
( B ( x ) F ( x ) 1 ) 2 0 ( m o d x m ) \left(B(x)F(x)-1\right)^2\equiv 0 \pmod {x^m}
展开
F 2 ( x ) B 2 ( x ) 2 F ( x ) B ( x ) + 1 0 ( m o d x m ) F^2(x)B^2(x)-2F(x)B(x)+1\equiv 0\pmod {x^m}
提一个 F ( x ) F(x) 出来
F ( x ) ( F ( x ) B 2 ( x ) 2 B ( x ) ) 1 ( m o d x m ) F(x)\left(F(x)B^2(x)-2B(x)\right)\equiv -1\pmod {x^m}

此时容易看出 G ( x ) 2 B ( x ) F ( x ) B 2 ( x ) ( m o d x m ) G(x)\equiv 2B(x)-F(x)B^2(x) \pmod {x^m}
这样就求出了 F ( x ) F(x) x m x^m 意义下的乘法逆元
我们从m=1开始做,此时直接求常数项的乘法逆元即可,
然后可以推出m=2,4,8,16…
这样一直倍增下去,直到m>=我们所需要的次数,此时直接取前面我们需要的项,后面多出来的直接设为0即可(模掉了没有影响)

注意,由于我们求 G ( x ) G(x) 是在模 x m x^m 意义下进行的,而不是 x m 2 x^{m\over 2} ,因此即使 B ( x ) B(x) 的次数为m/2-1,此时的 F ( x ) F(x) 的次数仍然要取m-1

分析时间复杂度
T ( n ) = T ( n / 2 ) + O ( n log n ) = O ( n log n ) T(n)=T(n/2)+O(n\log n)=O(n\log n) (常数相当大)

Code

void make(int l,LL *a,LL *b)//l为我们需要的次数,a为待求逆多项式,用b来存储结果
{
	b[0]=ksm(a[0],mo-2);//求常数项逆元
	for(int m=1,t=2,num=4,cnt=2;m<l;m=t,t=num,num<<=1,cnt++)
	//由于乘法有平方操作,因此NTT的范围需要开到2倍。
	//t为当前的模数次数,m=t/2
	{
		prp(num,cnt);//预处理单位根、反位等
		fo(i,0,m-1) c[i]=a[i],d[i]=b[i];
		fo(i,m,t-1) c[i]=a[i];
		fo(i,t,num-1) c[i]=0;
		NTT(c,0,num),NTT(b,0,num);
		fo(i,0,num-1) b[i]=b[i]*b[i]%mo*c[i]%mo;
		NTT(b,1,num);
		fo(i,0,t-1) b[i]=((LL)2*d[i]-b[i]+mo)%mo;
		fo(i,t,num-1) b[i]=0;
	}		
}

多项式除法(多项式取模)

Procedure

多项式除法/取模也是多项式模块中的一个重要操作,在做生成函数/多项式多点求值等中均有应用。。。
对于一个n次多项式 F ( x ) F(x) ,m次多项式 G ( x ) G(x) (n>=m),我们希望求出一个n-m次多项式 H ( x ) H(x) ,一个至多m-1次的多项式 R ( x ) R(x) ,满足 F ( x ) = G ( x ) H ( x ) + R ( x ) F(x)=G(x)H(x)+R(x) ,并且 R ( x ) R(x) 小于 G ( x ) G(x)

R ( x ) 0 ( m o d x n m + 1 ) R(x)\equiv 0\pmod {x^{n-m+1}} 时,我们就可以直接对 G ( x ) G(x) 求逆了
接下来的方法就比较高妙了:

我们引入多项式的反转操作,即将多项式的系数倒转过来
形式化的,对于n次多项式 F ( x ) F(x) ,反转之后就是 x n F ( 1 x ) x^nF\left({1\over x}\right)

对于上面的等式反转
x n F ( 1 x ) = x n ( G ( 1 x ) H ( 1 x ) + R ( 1 x ) ) x^nF\left({1\over x}\right)=x^n(G\left({1\over x}\right)H\left({1\over x}\right)+R\left({1\over x}\right))

此时相当于将x替换成1/x,等式仍然成立
右边分配x^n
x n F ( 1 x ) = x m G ( 1 x ) x n m H ( 1 x ) + x n R ( 1 x ) x^nF\left({1\over x}\right)=x^mG\left({1\over x}\right)x^{n-m}H\left({1\over x}\right)+x^nR\left({1\over x}\right)

观察各个多项式指数范围的变化。
F ( x ) F(x) 原本的指数范围为 [ 0 , n ] [0,n] ,现在仍然是 [ 0 , n ] [0,n]
G ( x ) G(x) 原本的指数范围为 [ 0 , m ] [0,m] ,现在仍然是 [ 0 , m ] [0,m]
H ( x ) H(x) 原本的指数范围为 [ 0 , n m ] [0,n-m] ,现在仍然是 [ 0 , n m ] [0,n-m]
但余式有变化:
R ( x ) R(x) 原本的指数范围为 [ 0 , m 1 ] [0,m-1] ,现在变成了 [ n m + 1 , n ] [n-m+1,n]

这下好了,如果我们对于等式两边同时模一个 x n m + 1 x^{n-m+1}
F ( x ) , G ( x ) F(x),G(x) 保留次数小于等于n-m的项, H ( x ) H(x) 不变, R ( x ) R(x) 没了!

x n F ( 1 x ) x m G ( 1 x ) x n m H ( 1 x ) ( m o d x n m + 1 ) x^nF\left({1\over x}\right)\equiv x^mG\left({1\over x}\right)x^{n-m}H\left({1\over x}\right)\pmod {x^{n-m+1}}

此时只需要对于 G ( 1 / x ) G(1/x) 求出模意义下的逆,乘一下就得到 H ( 1 / x ) H(1/x) ,反转回来就是 H ( x ) H(x)

H ( x ) H(x) 带回原来的式子,一减就可以得出 R ( x ) R(x)

分析时间复杂度
NTT、多项式求逆的复杂度均为 O ( n log n ) O(n\log n)
因此总的复杂度 O ( n log n ) O(n\log n)
常数更大了…

Code

void rev(int num,LL *a,LL *b)//反转操作
{
    fo(i,0,num-1) b[i]=a[num-i-1];
}
void div(LL *a,LL *b,LL *d,LL *r)
{
    rev(m+1,b,r);
    make(n-m+1,r,d);//求出除数的逆
    int num=cf[l2[n]]*2;
    prp(num);
    fo(i,0,n) f[i]=a[n-i];
    fo(i,n-m+1,num-1) f[i]=0,d[i]=0;
    NTT(f,0,num),NTT(d,0,num);
    fo(i,0,num-1) d[i]=d[i]*f[i]%mo;
    NTT(d,1,num);
    fo(i,n-m+1,num-1) d[i]=0;
    fo(i,0,(n-m)>>1) swap(d[i],d[n-m-i]);//反转回来,得到商
    fo(i,0,num-1) r[i]=d[i],f[i]=b[i];
    num=cf[l2[n+1]];
    prp(num);
    NTT(f,0,num),NTT(r,0,num);
    fo(i,0,num-1) r[i]=r[i]*f[i]%mo;
    NTT(r,1,num);
    fo(i,0,num-1) r[i]=(a[i]-r[i]+mo)%mo;//得到余式
}

猜你喜欢

转载自blog.csdn.net/hzj1054689699/article/details/83098562