zoukankan      html  css  js  c++  java
  • 【洛谷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;
    }
    
  • 相关阅读:
    URL编码及解码
    Javascript解析URL
    为什么在JavaScript中0.1+0.2不等于0.3?
    void 0 与 undefined
    Windows7、Windows10下把Git Bash Here 添加到右键菜单(ContextMenu)
    [菜鸟]C++创建类对象时(无参)后不加括号与加括号的区别
    git常用命令
    git 出错及解决
    Vim升华之树形目录插件NERDTree安装图解
    rails.vim环境安装(ubuntu)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/PolySqrt.html
Copyright © 2011-2022 走看看