【洛谷5205】【模板】多项式开根

点此看题面

大致题意: 给定多项式(F(x)),求(G(x))满足(G(x)^2equiv F(x)(mod x^n)),向(998244353)取模。

前言

在知道多项式乘法逆的前提下,这道题的推导其实是非常简单,甚至要简单于多项式乘法逆的。

但由于多项式开根最后的求解依然需要用到多项式乘法逆,所以总体难度还是高于多项式乘法逆的?

推式子

按照多项式乘法逆的套路,我们可以递归求解此题。

对于边界,显然当递归到(n=0)的时候,由于题目保证(a_0=1),所以(b_0=1)

(话说如果(a_0)不等于(1),那么可能会有多个(b_0)满足(b_0^2equiv1),我也很想知道这样子该怎么去做)

否则,假设我们当前已知:

[H(x)^2equiv F(x)(mod x^{lfloorfrac n2 floor}) ]

由于(G(x)^2equiv F(x)(mod x^n)),所以显然可知

[G(x)^2equiv F(x)(mod x^{lfloorfrac n2 floor}) ]

则,显然就可以得到:

[G(x)-H(x)equiv0(mod x^{lfloorfrac n2 floor}) ]

考虑我们现在要求出一个多项式,满足与(F(x))卷积模(x^n)(1)

则显然,我们必须要想办法,让此处的模数由(x^{lfloorfrac n2 floor})变为(x^n)

怎么办呢?将式子两边同时平方便可。即:

[(G(x)-H(x))^2equiv0(mod x^n) ]

用完全平方公式拆平方:

[G(x)^2-2G(x)*H(x)+H(x)^2equiv0(mod x^n) ]

然后,由于(G(x)^2equiv F(x)(mod x^n)),所以:

[F(x)-2G(x)*H(x)+H(x)^2equiv0(mod x^n) ]

此时只要通过移项就可以得到:

[G(x)equivfrac{F(x)+H(x)^2}{2H(x)}(mod x^n) ]

关于这个式子,只要用(NTT)+多项式乘法逆就可以求解了。

注意在具体实现中,(G(x))(H(x))完全可以存在一个数组中。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define X 998244353
using namespace std;
int n,a[N+5],b[N<<2];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
class Poly
{
	private:
		int P,L,R[N<<2],t[N<<2],k[N<<2],PR,IPR,I2;
		I void T(int *s,CI p)//NTT
		{
			RI i,j,k,U,S,x,y;for(i=0;i^P;++i) i<R[i]&&(x=s[i],s[i]=s[R[i]],s[R[i]]=x);
			for(i=1;i^P;i<<=1) for(U=QP(p,(X-1)/(i<<1)),j=0;j^P;j+=i<<1) for(S=1,k=0;k^i;
				S=1LL*S*U%X,++k) s[j+k]=((x=s[j+k])+(y=1LL*S*s[i+j+k]%X))%X,s[i+j+k]=(x-y+X)%X;
		}
	public:
		I Poly() {PR=3,IPR=QP(3,X-2),I2=X+1>>1;}
		I void Inv(CI n,int *a,int *b)//多项式乘法逆
		{
			if(!n) return (void)(b[0]=QP(a[0],X-2));Inv(n>>1,a,b);
			RI i;P=1,L=0;W(P<=(n<<1)) P<<=1,++L;for(i=0;i^P;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);
			for(i=0;i<=n;++i) t[i]=a[i];W(i^P) t[i++]=0;
			for(T(b,PR),T(t,PR),i=0;i^P;++i) b[i]=(2LL*b[i]-1LL*t[i]*b[i]%X*b[i]%X+X)%X;
			RI t=QP(P,X-2);for(T(b,IPR),i=0;i<=n;++i) b[i]=1LL*b[i]*t%X;W(i^P) b[i++]=0;
		}
		I void Sqrt(CI n,int *a,int *b)//多项式开根
		{
			if(!n) return (void)(b[0]=1);Sqrt(n>>1,a,b);//递归
			RI i;P=1,L=0;W(P<=(n<<1)) P<<=1,++L;for(i=0;i^P;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);//预处理
			for(i=0;i^P;++i) k[i]=0;for(Inv(n,b,k),i=0;i<=n;++i) t[i]=a[i];W(i^P) t[i++]=0;
			for(T(t,PR),T(b,PR),T(k,PR),i=0;i^P;++i) b[i]=(1LL*b[i]*b[i]+t[i])%X*k[i]%X*I2%X;//计算新多项式
			RI t=QP(P,X-2);for(T(b,IPR),i=0;i<=n;++i) b[i]=1LL*b[i]*t%X;W(i^P) b[i++]=0;//注意清空
		}
}P;
int main()
{
	RI i;for(scanf("%d",&n),--n,i=0;i<=n;++i) scanf("%d",a+i);
	for(P.Sqrt(n,a,b),i=0;i<=n;++i) printf("%d%c",b[i]," 
"[i==n]);return 0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/PolySqrt.html