zoukankan      html  css  js  c++  java
  • 【learning】多项式相关(求逆、开根、除法、取模)

    (首先要%miskcoo,这位dalao写的博客实在是太强啦qwq大部分多项式相关的知识都是从这位dalao博客里面学的,作为一只蒟蒻还是疯狂膜拜后自己理下思路吧qwq)


    多项式求逆(元)

    • 定义

      对于一个多项式(A(x)),如果存在一个多项式(B(x)),满足(B(x))的次数小于等于(A(x))(A(x)B(x)equiv 1(mod x^n)),那么我们称(B(x))(A(x))在模(x^n)意义下的逆元,简单记作(A^{-1}(x))

    • 求解

      从最简单的情况开始考虑,当(n=1)的时候(A(x)equiv c (mod x))(c)(A(x))的常数项,此时(A^{-1}(x))(c)的逆元

      在这个基础上我们继续考虑一般情况

      对于(n>1)的情况,不妨设(B(x)=A^{-1}(x)),那么我们可以根据定义列出下面的式子:

    [A(x)B(x)equiv 1(mod x^n) ]

      这里的话考虑用倍增的方式求解(算倍增吧),这里假设我们已经知道了(A(x))(mod x^{lceil frac{n}{2} ceil})下的逆元(G(x)),那么有:

    [A(x)G(x)equiv 1(mod x^{lceil frac{n}{2} ceil}) ]

      我们把(A(x))(B(x))的式子写成(mod x^{lceil frac{n}{2} ceil})下的:

      (可以这么写是因为(mod x^n)相当于将乘积中(x)次数大于等于(n)的忽略掉了,而(mod x^{lceil frac{n}{2} ceil})则相当于忽略了更多的项,既然前者满足,那么后者肯定也满足)

    [A(x)B(x)equiv 1 (mod x^{lceil frac{n}{2} ceil}) ]

      把这两条式子相减,就可以搞事情了:

    [egin{aligned} A(x)[B(x)-G(x)]&equiv 0 (mod x^{lceil frac{n}{2} ceil})\ B(x)-G(x)&equiv 0 (mod x^{lceil frac{n}{2} ceil})\ end{aligned} ]

      然后我们两边平方一下:

    [B^2(x)-2B(x)G(x)+G^2(x)equiv 0 (mod x^{lceil frac{n}{2} ceil}) ]

      然后这里有个很神奇的事情,(B(x)-G(x))(mod x^{lceil frac{n}{2} ceil})下为0,说明这个式子最后的结果的(0)(lceil frac{n}{2} ceil -1)次项系数都为(0),平方了之后,对于结果的(i)次项系数,((0<=i<=2*lceil frac{n}{2} ceil -1) ),其系数(a_i = sumlimits_{j=0}^{i}a_j a_{i-j}),而(a_j)(a_{i-j})中必定有一项为(0)(因为(j)(i-j)中必定有一个值小于(lceil frac{n}{2} ceil)),所以我们可以得到一个结论,这个式子在平方了之后在(mod x^n)下也是(0)

      那么我们就可以写成:

    [egin{aligned} B^2(x)-2B(x)G(x)+G^2(x)&equiv 0 (mod x^n)\ A(x)B(x)*B(x)-2*A(x)B(x)*G(x)+A(x)G^2(x)&equiv 0 (mod x^n)\ end{aligned} ]

      两边同时乘上(A(x)),由逆元的定义我们可以将上面的式子化简成下面这样:

    [B(x)-2G(x)+A(x)G^2(x)equiv 0 (mod x^n) ]

      最后得到:

    [B(x)equiv 2G(x)-A(x)G^2(x) (mod x^n) ]

      也就是说,如果我们知道(G(x)),我们就可以推出(B(x))

      具体的实现可以用递归的方式实现,中间的多项式乘法可以用fft加速一下,那么最终的时间复杂度就是

    [T(n)=T(frac{n}{2})+O(n log n)=O(n log n) ]

      然而为啥这样搞完了还是一个log呢?因为每次递归下去多项式的最高次数都会减半,稍微算一下就会发现最后总的时间复杂度合起来还是一个log而不是两个

      注意,后面这一堆推式子的过程是建立在(n=1)的时候有解的前提下的,所以我们还可以得到一个结论:一个多项式在(mod x^n)下是否有逆元取决于其常数项在(mod x^n)下是否有逆元

    • 实现

      首先先实现一个namespace NTT,然后除了基础的函数外主要供外部调用的过程是这个:

    void Ntt_getinv(vct &a,vct &b,int n,int m){
    	prework(a,b,n,2*m);//这里注意因为后面是A*B*B,所以m要*2
    	ntt(A,1);
    	ntt(B,1);
    	for (int i=0;i<len;++i) 
          	B[i]=(2LL-1LL*A[i]*B[i]%MOD+MOD)*1LL*B[i]%MOD;
    	ntt(B,-1);
    }
    

      然后求逆的过程大概是这样(这里用vector来写了):

    vct Inv(vct a){
    	int N=a.size();
    	if (N==1){
    		a[0]=ksm(a[0],MOD-2);
    		return a;
    	}
    	vct b=a; b.resize((N+1)>>1);
    	b=Inv(b); b.resize(N);
    	NTT::Ntt_getinv(a,b,N,N);
    	b.resize(NTT::len);
    	for (int i=0;i<NTT::len;++i) b[i]=NTT::B[i];
    	b.resize(N);
    	return b;
    }
    

      求逆大概就是这样吧ovo

      

    多项式开根

    • 定义

      对于一个多项式(A(x)),如果存在一个多项式(B(x)),满足(B^2(x)equiv A(x) (mod x^n)),则称(B(x))(A(x))(mod x^n)下的平方根

    • 求解

      同样是考虑最简单的情况,当(n=0)的时候,(B(x))的常数项就是(1)

      然后考虑一般情况,同样的思路,考虑用倍增的方式来求

      假设我们已经知道了(A(x))(mod x^{n})下的平方根(G(x)),现在要求在(mod x^{2n})下的平方根(B(x)),根据定义我们可以列出式子:

    [egin{aligned} B^2(x)&equiv A(x)(mod x^{2n})\ G^2(x)&equiv A(x)(mod x^n) end{aligned} ]

      我们对这个式子进行一些处理:

    [egin{aligned} G^2(x)&equiv A(x)(mod x^n)\ G^2(x)-A(x)&equiv 0(mod x^n)\ end{aligned} ]

      那么可以得到(因为右边是(0)所以可以这么搞):

    [egin{aligned} (G^2(x)-A(x))^2&equiv 0 (mod x^{2n})\ G^4(x)-2G^2(x)A(x)+A^2(x)&equiv 0(mod x^{2n})\ end{aligned} ]

      然后两边加上(4G^2(x)A(x))

    [egin{aligned} G^4(x)+2G^2(x)A(x)+A^2(x)&equiv 4G^2(x)A(x)(mod x^{2n})\ (G^2(x)+A(x))^2&equiv 4G^2(x)A(x)(mod x^{2n})\ (G^2(x)+A(x))^2&equiv (2G(x))^2A(x)(mod x^{2n})\ end{aligned} ]

      我们将((2G^2(x))^2)移到左边去,将左边写成一个平方的形式,得到:

    [(frac{G^2(x)+A(x)}{2G(x)})^2equiv A(x)(mod x^{2n}) ]

      等式左边的东西就是我们要求的(B(x))

      所以如果说我们知道了(G(x)),我们也就可以得出(B(x))啦,分母可以用多项式求逆搞一下,其他的多项式乘法fft搞一下,问题不大

      总的复杂度是:

    [T(n)=T(frac{n}{2})+求逆复杂度+O(n log n)=O(n log n) ]

      

    • 实现

      namespace NTT中主要需要调用的过程长这个样子

    void Ntt_getsqrt(vct &a,vct &invb,int n,int m){
    	prework(a,invb,n,m);
    	ntt(A,1);
    	ntt(B,1);
    	for (int i=0;i<len;++i) 
    		B[i]=1LL*B[i]*inv2%MOD*A[i]%MOD;
    	ntt(B,-1);
    }
    

      开根的话大概长这个样子

    vct Sqrt(vct a){
    	int N=a.size(),M,M1;
    	if (N==1){
    		a[0]=1;
    		return a;
    	}
    	vct b=a,invb;
    	b.resize((N+1)>>1);
    	b=Sqrt(b);
    	invb=b; invb.resize(N);//resize!!!
    	invb=Inv(invb);
    	NTT::Ntt_getsqrt(a,invb,N,N);
    	b.resize(NTT::len);
    	for (int i=0;i<NTT::len;++i) b[i]=(1LL*b[i]*inv2%MOD+NTT::B[i])%MOD;
    	b.resize(N);
    	return b;
    }
    

      
      
      

    多项式除法

    • 问题

      给出一个(n)次多项式(A(x)),以及一个((m(m<=n))次多项式(B(x))

      要求出(D(x))满足(A(x)=D(x)B(x)+R(x)),且(D(x))的次数(<=n-m)(R(x))的次数(<m)

      简单来说就是类比整数的除法,(D(x))就是商,(R(x))就是余数,我们现在考虑求商

    • 求解

      为了方便接下来的表述,先定义一些操作,我们记:

    [rev(A(x))=x^nA(frac{1}{n}) ]

      也就是系数反转,举个简单的例子:

    [egin{aligned} A(x)&=4x^4+3x^3+2x^2+1\ rev(A(x))&=x^4+2x^3+3x^2+4 end{aligned} ]

      那么现在我们把上面那条式子搬下来:

    [A(x)=D(x)B(x)+R(x) ]

      (接下来的步骤均将(D(x))看成(n-m)次多项式,(R(x))看成(m-1)次多项式,对于那些不存在的高次项我们就把系数看成(0)就好了)

      后面的余数看起来十分不友善,所以我们要想个办法把它去掉,于是我们可以进行以下的操作:

      我们将上面式子中的所有(x)换成(frac{1}{x}),然后等式两边同时乘上(x^n),得到:

    [egin{aligned} x^nA(frac{1}{x})&=x^{n-m}D(frac{1}{x})x^mB(frac{1}{x})+x^{n-m+1}x^{m-1}R(frac{1}{x})\ rev(A(x))&=rev(D(x))rev(B(x))+x^{n-m+1}rev(R(x))\ end{aligned} ]

      现在再来看一下各个项的最高次项,首先是我们要求的元素(D(x)),由于这个多项式原来是(n-m)次,所以在系数反转之后肯定不会超过(n-m)次,而我们要“消掉”的(R(x))原来是(m-1)次多项式,所以(x^{n-m+1}R(x))最低次项应该是大于(n-m)

      那么考虑将上面的式子放到(mod x^{n-m+1})下,(x^{n-m+1}R(x))的影响就可以十分愉快滴被消掉啦,同时我们也不会影响到(D(x))的求解,因为(D(x))(n-m)次的(疯狂%miskcoo太强了qwq)

      于是我们就得到了这样一个式子:

    [rev(A(x))equiv rev(D(x))rev(B(x)) (mod x^{n-m+1}) ]

      那所以,我们只要求一个(rev(B(x)))(mod x^{n-m+1})意义下的逆元然后跟(rev(A(x)))乘一下,得到(rev(D(x))),然后再把系数反转回来就得到(D(x))

    • 实现

      除法大概是长这个样子

    vct operator / (vct a,vct b){
    	int N=a.size()-1,M=b.size()-1;
    	if (N<M){
    		d.resize(1);d[0]=0;
    		return d;
    	}
    	reverse(a.begin(),a.end());
    	reverse(b.begin(),b.end());
    	b.resize(N-M+1);
    	d=Inv_p(b)*a;
    	d.resize(N-M+1);
    	reverse(d.begin(),d.end());
    	return d;
    }
    

    多项式取模

    • 问题

       这个。。其实就是求上面那个(R(x))

    • 求解

      有了多项式除法(也就是求商)之后,求余数就变得比较简单了

      类比整数的取模,我们可以得到这样的一个式子:
    $$
    R(x)=A(x)-D(x)B(x)
    $$
      那就除法求出(D(x))之后直接减一下就好了,(D(x)B(x))这个多项式乘法也是直接用(fft)求就好了

    • 实现

      假装非常短的样子 (然而前面的东西都是要写的qwq醒醒)

    void mod(vct &a,vct b){
    	int N=a.size()-1,M=b.size()-1;
    	if (N<M) return;
    	t=a/b;
    	a=a-(t*b);
    	a.resize(M);
    }
    

     
      大概。。就先写这么多吧ovo

  • 相关阅读:
    一元2次方程求解
    react-native 相对项目路径导入组件
    appium页面元素封装(十一)
    pytest高级用法,跳过、失败函数,函数数据参数化(十)
    python之自定义分页代码模板
    pytest高级用法,参数、函数、自动、返回值引用(九)
    pytest.ini文件配置
    因安装pytest最新版本导致,运行pytest命令出错:pluggy.manager.PluginValidationError: unknown hook 'pytest_namespace' in plugin <module 'allure.pytest_plugin' from 'd:\anaconda3\lib\site-packages\allure\pytest_plug
    pytest的使用(八)
    运行pytest,提示编码错误
  • 原文地址:https://www.cnblogs.com/yoyoball/p/8724115.html
Copyright © 2011-2022 走看看