zoukankan      html  css  js  c++  java
  • 【模板】多项式求逆

    蒟蒻写题解实在不易

    前置芝士

    多项式最高次数为度,多项式(A)的度记为:(deg(A))

    多项式取模的意义:将多项式(A)记作余式(A(x)=Q(x)B(x)+R(x)),则(A(x)equiv R(x)(mod~B(x)))

    在有模数(mod)的情况下,(F(x)G(x)equiv 1 (mod~x^n))的具体情况:(F(x)G(x))后次数(≥n)的不管,其他除常数项为(1)外其他项系数除(mod)后为0

    推式前置

    • (F(x)G(x)equiv 1 (mod~x^n))(F(x)G(x)=Q(x)*x^n+1)

    • 求逆的关键在于(G(x))(F(x)H(x)equiv 1(mod~ x^{lceil frac{n}{2} ceil}))(H(x))推过来

    • (F(x)G(x)equiv 1 (mod~x))则除常数项其他是会消掉的,(F(x)equiv f[0] (mod~x))( herefore G(x)=f[0]^{-1})

    • 那么(G(x))(H(x))有什么关系呢?先看一下((mod~x^n))((mod~ x^{lceil frac{n}{2} ceil})(n>1))的关系:

    [egin{aligned}\ F(x)G(x)&=Q*x^n+1\ F(x)G(x)&=Q'*x^{lceil frac{n}{2} ceil}+?\ ?=1:Q'&可以通过与Q的相乘次数差转换过来,所以余数为1\ end{aligned}]

    • (F(x)G(x)equiv 1(mod~x)longrightarrow F(x)G(x)equiv 1(mod~ x^{lceil frac{n}{2} ceil}))

    • 事实上,我们能得到:$$A(x)equiv R(x)(mod~x^n)longrightarrow A(x)equiv R(x)(mod ~x^{lceil frac{n}{2} ceil})$$

    推式

    [egin{aligned}\ F(x)G(x)&equiv 1(mod~x^n)\ F(x)H(x)&equiv 1(mod~ x^{lceil frac{n}{2} ceil}),F(x)G(x)equiv 1(mod~ x^{lceil frac{n}{2} ceil})\ F(x)(G(x)-H(x))&equiv 0(mod~ x^{lceil frac{n}{2} ceil})\ G(x)-H(x)&equiv 0(mod~ x^{lceil frac{n}{2} ceil})\ (G(x)-H(x))^2&equiv 0(mod~x^n)\ G(x)^2-2G(x)H(x)+H(x)^2&equiv 0(mod~x^n)\ F(x)(G(x)^2-2G(x)H(x)+H(x)^2)&equiv 0 (mod~x^n)\ F(x)G(x)cdot G(x)-(F(x)G(x)cdot 2H(x)+(F(x)H(x)^2&equiv 0(mod~x^n)\ G(x)-2H(x)+F(x)H(x)^2&equiv 0(mod~x^n)\ G(x)&equiv 2H(x)-F(x)H(x)^2(mod~x^n)\ end{aligned}]

    具体实现

    我们可以至底(x^1)向上求出(H(x))在更新至(G(x)),反复操作

    由于次数在模意义的下次数较大时会消掉,所以我们并不需要每次都(NTT(x))

    忽略常数及栈空间,递归求解感觉更舒服

    code

    #include<bits/stdc++.h>
    typedef long long LL;
    const LL mod=998244353,maxn=1e6+9,g=3;
    inline LL Read(){
    	LL x(0),f(1); char c=getchar();
    	while(c<'0' || c>'9'){
    		if(c=='-') f=-1; c=getchar();
    	}
    	while(c>='0' && c<='9'){
    		x=(x<<3)+(x<<1)+c-'0'; c=getchar();
    	}
    	return x*f;
    }
    inline LL Pow(LL base,LL b){
    	LL ret(1ll);
    	while(b){
    		if(b&1) ret=ret*base%mod;
    		base=base*base%mod; b>>=1;
    	}
    	return ret;
    }
    LL r[maxn];
    inline LL NTT(LL *a,LL n,LL type){
    	for(LL i=0;i<n;++i) if(i<r[i]) std::swap(a[i],a[r[i]]);
    	for(LL mid=1;mid<n;mid<<=1){
    		LL wn(Pow(g,(mod-1)/(mid<<1)));
    		if(type==-1) wn=Pow(wn,mod-2);
    		for(LL R=mid<<1,j=0;j<n;j+=R){
    			LL w(1);
    			for(LL k=0;k<mid;++k,w=w*wn%mod){
                    LL x(a[j+k]),y(w*a[j+mid+k]%mod);
    				a[j+k]=(x+y)%mod; a[j+mid+k]=(x-y+mod)%mod;
    			}
    		}
    	}
    }
    LL a[maxn],F[maxn],b[maxn];
    void Solve(LL n,LL *G){
    	if(n==1){
    		G[0]=Pow(a[0],mod-2); return;
    	}
    	Solve(n+1>>1,G);
    	LL limit(1),len(0);
    	while(limit<(n<<1)){
    		limit<<=1; ++len;
    	}
    	for(LL i=1;i<limit;++i) r[i]=(r[i>>1]>>1)|((i&1)<<len-1);
    	for(LL i=0;i<n;++i) F[i]=a[i];
    	for(LL i=n;i<limit;++i) F[i]=0;
        NTT(G,limit,1); NTT(F,limit,1);
    	for(LL i=0;i<limit;++i)
    		G[i]=(2ll-F[i]*G[i]%mod+mod)%mod*G[i]%mod;
    	NTT(G,limit,-1);
    	for(LL i=0;i<limit;++i)
    		G[i]=G[i]*Pow(limit,mod-2)%mod;
    	for(LL i=n;i<limit;++i) G[i]=0;
    }
    int main(){
        LL n(Read());
    	for(LL i=0;i<n;++i) a[i]=Read();
    	Solve(n,b);
    	for(LL i=0;i<n;++i) printf("%lld ",b[i]);
    	return 0;
    }
    
  • 相关阅读:
    架构之路(六):把框架拉出来
    读取mdb文件
    基类、子类之间的类型转换
    WPF Trigger
    WPF 打开txt文件
    C# 匿名方法
    自定义显隐式类型转换
    枚举获得Description扩展方法
    IFormattable和IFormatProvider
    WPF DataGrid下滑动态加载数据
  • 原文地址:https://www.cnblogs.com/y2823774827y/p/10682143.html
Copyright © 2011-2022 走看看