把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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

点此看题面

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

前言

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

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

推式子

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

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

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

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

\[H(x)^2\equiv F(x)(mod\ x^{\lfloor\frac n2\rfloor}) \]

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

\[G(x)^2\equiv F(x)(mod\ x^{\lfloor\frac n2\rfloor}) \]

则,显然就可以得到:

\[G(x)-H(x)\equiv0(mod\ x^{\lfloor\frac n2\rfloor}) \]

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

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

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

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

用完全平方公式拆平方:

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

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

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

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

\[G(x)\equiv\frac{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]," \n"[i==n]);return 0;
}
posted @ 2020-06-04 10:54  TheLostWeak  阅读(200)  评论(0编辑  收藏  举报