题外话:
本来这段时间都在洛谷写博客的…
但是因为洛谷猝不及防的改版了导致洛谷博客的搜索功能一度变的很垃圾..
所以又回来了…
等什么时候变好了再回去吧…
哦对了我去洛谷写博客的唯一理由是自己高兴。嗯,真的。

多项式全家桶系列

本来觉得这些没啥用,但是想学新东西了…那就随便学学叭

多项式求逆

Description

给定多项式 \(F(x)\),请求出一个多项式 \(G(x)\),满足 \(F(x)G(x)\equiv 1\; \operatorname{mod} x^n\)。对 \(998244353\) 取模。

Sol

推导过程挺简单也挺显然的…

结论就是 \[B(x)\equiv 2B'(x)-A(x)B’^2(x)\; \operatorname{mod} x^n\]

其中 \[A(x)B'(x)\equiv 1\; \operatorname{mod} x^{\frac n2}\]

一个多项式有没有逆取决于常数项有没有逆

Code

void solve(int len){
    if(len==1) return b[0]=inv(a[0]),void();
    solve(len>>1);lim=len+len;
    for(int i=1;i<lim;i++) rev[i]=(rev[i>>1]>>1)|(i&1?lim>>1:0);
    for(int i=0;i<len;i++) c[i]=a[i];
    ntt(c,1);ntt(b,1);
    for(int i=0;i<lim;i++) b[i]=(2ll*b[i]%mod-1ll*c[i]*b[i]%mod*b[i]%mod+mod)%mod;
    ntt(b,-1);
    for(int i=0;i<lim;i++) c[i]=0;
    for(int i=len;i<lim;i++) b[i]=0;
}

多项式除法

Description

给出 \(n\) 次多项式 \(F(x)\)\(m\) 次多项式 \(G(x)\),请求出多项式 \(Q(x),R(x)\),满足:

  • \(deg(Q)=n-m,deg(R)<m\)
  • \(F(x)=G(x)Q(x)+R(x)\)

\(998244353\) 取模。

Sol

首先,对于任意多项式

\[P(x)=\sum_{i=0}^na_ix^i\]

显然有

\[P^{rev}(x)=x^nP(\frac 1x)=\sum_{i=0}^na_{n-i}x^i\]

也就是说通过这个可以把多项式的系数翻转。

\(F(x)=G(x)Q(x)+R(x)\) 进行这个操作:

\[F(x)=G(x)Q(x)+R(x)\]

\[F(\frac 1x)=G(\frac 1x)Q(\frac 1x)+R(\frac 1x)\]

\[x^nF(\frac 1x)=x^nG(\frac 1x)Q(\frac 1x)+x^nR(\frac 1x)\]

\[x^nF(\frac 1x)=x^mG(\frac 1x)x^{n-m}Q(\frac 1x)+x^{n-m+1}x^{m-1}R(\frac 1x)\]

\[F^{rev}(x)=G^{rev}(x)Q^{rev}(x)+x^{n-m+1}R^{rev}(x)\]

注意到 \(x^{n-m+1}R^{rev}(x)\) 的最低项次数至少为 \(n-m+1\),所以我们对式子取模:

\[F^{rev}(x)=G^{rev}(x)Q^{rev}(x)\; \operatorname{mod}x^{n-m+1}\]

因为 \(deg(Q)=n-m\),所以这样是不会出问题的。

所以就可以算出 \(Q^{rev}(x)=F^{rev}(x)(G^{rev}(x))^{-1}\)

多项式求逆即可。

整个做法的关键就是消去余数

Code

void solve(){
    n=getint(),m=getint();
    for(int i=0;i<=n;i++) f[i]=getint();
    for(int i=0;i<=m;i++) g[i]=getint();
    std::reverse(f,f+n+1),std::reverse(g,g+m+1); //翻转系数
    for(int i=0;i<=n-m;i++) ing[i]=g[i];  
    //因为对 x^{n-m+1} 取模,所以后边的系数都应该为0
    getni(ing,q,n-m+1);  //求出g的逆q
    for(int i=0;i<=n-m;i++) c[i]=f[i]; //后边的系数都应该为0
    mul(q,c,n-m+1);
    for(int i=0;i<lim;i++) c[i]=0;
    std::reverse(q,q+n-m+1);
    for(int i=0;i<=n-m;i++) printf("%d ",q[i]);puts("");
    for(int i=n-m+1;i<lim;i++) q[i]=0;
    std::reverse(f,f+n+1),std::reverse(g,g+m+1);
    mul(q,g,n);
    for(int i=0;i<m;i++) printf("%d ",(f[i]-q[i]+mod)%mod);
}

多项式开根

Description

给定多项式 \(A(x)\),若存在一个多项式 \(B(x)\),满足:

  • \(deg(B)\leq deg(A)\)
  • \(B^2(x)\equiv A(x)\;\operatorname{mod} x^n\)

则称 \(B(x)\)\(A(x)\) 在模 \(x^n\) 意义下的开根。

Sol

懒得写了…

感谢AlphaINF

三模数NTT

Description

\(F(x)G(x)\),对 \(p\) 取模。\(p\) 不保证有原根。

Sol

拆成对三个模数 \(469762049,998244353,1004535809\) 分别做 \(NTT\),它们的原根都是 \(3\)

先合并前两个式子,得到:

\[ans\equiv C\;\operatorname{mod}M\]

\[ans\equiv c_3\operatorname{mod} m_3\]

设:

\[ans=xM+C=ym_3+c_3\]

求出 \(x\)\(\operatorname{mod}m_3\) 意义下的值:

\[xM\equiv c_3-C\;\operatorname{mod}m_3\]

\(\operatorname{mod} m_3\) 意义下, \(ym_3\) 被消掉了。所以有:

\[x\equiv (c_3-C)M^{-1}\;\operatorname{mod}m_3\]

算出右边的值为 \(q\),令 \(x=km_3+q\),代入\(ans=xM+C\):

\[ans=(km_3+q)M+C=km_1m_2m_3+qM+C\]

因为 \(ans\in[0,m_1m_2m_3)\),所以 \(k=0\)! 所以 \(ans=qM+C\)

Code

主要是合并部分

int get(int x,int y,int z,int mod){
    ll M=p[1]*p[0];
    ll tmp1=mul(1ll*x*p[1]%M,inv(p[1]%p[0],p[0]),M); //快速幂之前先取模
    ll tmp2=mul(1ll*y*p[0]%M,inv(p[0]%p[1],p[1]),M);
    ll tmp=(tmp1+tmp2)%M;
    ll xx=1ll*(z-tmp%p[2]+p[2])%p[2]*inv(M%p[2],p[2])%p[2];
    return ((xx%mod)*(M%mod)%mod+tmp%mod)%mod; //注意这里先对mod取模再乘起来
}

多项式exp,ln等学完高数马上填坑

多项式对数函数

Description

给定 \(n-1\) 次多项式 \(A(x)\),求一个 \(\bmod x^n\) 下的多项式 \(B(x)\),满足 \(B(x)\equiv \ln A(x)\;\bmod x^n\)。对 \(998244353\) 取模。

Sol

求ln还是挺傻逼的吧…

根据微积分基本知识,复合函数 \(y=f(g(x))\) 的导数和函数 \(y=f(u),u=g(x)\) 的导数间的关系为:

\[y_x’=y_u’\cdot u_x’\]

然后这题就是求 \(y=\ln(A(x))\)

根据上边的定义转变成 \(y’=\frac{A'(x)}{A(x)}\)

求导求逆再积分就行了

然后发现自己求逆的一点小bug就是设置长度设置为不小于 \(n\)\(2\) 的整次幂就行了,不用大于 \(2n\)

void ds(int *a,int *b,int n){
    for(int i=1;i<n;i++) b[i-1]=1ll*a[i]*i%mod;b[n-1]=0;
}
void jf(int *a,int *b,int n){
    for(int i=1;i<n;i++) b[i]=1ll*a[i-1]*inv(i)%mod;b[0]=0;
}
void getln(int *f,int *g,int lim){
    getinv(f,d,c);
    for(int i=1;i<lim;i++) rev[i]=(rev[i>>1]>>1)|(i&1?lim>>1:0);
    ds(f,b,lim);
    ntt(b,1),ntt(d,1);
    for(int i=0;i<lim;i++) b[i]=1ll*b[i]*d[i]%mod;
    ntt(b,-1);jf(b,g,lim);
}

版权声明:本文为YoungNeal原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/YoungNeal/p/10209424.html