多项式牛顿迭代和多项式开根

orz picks

前置skill

基础求导

(全世界应该只有我不会求导了吧……=。=,但考虑到可能有小学生在看这篇文章,所以还是讲一下吧
导数,是微积分中的重要基础概念,关于它的具体内容就不讲了,在本文中,你只要知道,求导是用来拟合一个函数在各处的斜率的东西,并且有:
f(x)=xa f ( x ) = x a ,则 f(x)=axa1 f ′ ( x ) = a x a − 1 f(x) f ′ ( x ) :一阶导数)。由此可知,还有 f(x)=a(a1)xa2 f ″ ( x ) = a ( a − 1 ) x a − 2 f(x) f ″ ( x ) :二阶导数)等等。

泰勒展开

将一个函数在 x0 x 0 处泰勒展开可得:

f(x)=f(x0)+f(x0)1!(xx0)+f(x0)2!(xx0)2+...+fn(x0)n!(xx0)n f ( x ) = f ( x 0 ) + f ′ ( x 0 ) 1 ! ( x − x 0 ) + f ″ ( x 0 ) 2 ! ( x − x 0 ) 2 + . . . + f n ( x 0 ) n ! ( x − x 0 ) n

多项式求逆

戳这里

多项式牛顿迭代

已知 G(x) G ( x ) ,求解 G(F(x))0(modxn) G ( F ( x ) ) ≡ 0 ( mod x n )

假设已经存在 G(H(x))0(modxn2) G ( H ( x ) ) ≡ 0 ( mod x n 2 )
我们把 G(F(x)) G ( F ( x ) ) H(x) H ( x ) 处泰勒展开一下。
又因为 G(H(x))0(modxn2) G ( H ( x ) ) ≡ 0 ( mod x n 2 ) ,而 G(F(x))0(modxn) G ( F ( x ) ) ≡ 0 ( mod x n ) G(F(x)) G ( F ( x ) ) F(x) F ( x ) 次数较小的 n2 n 2 项在模 xn x n 意义下能够产生的贡献是0,而 G G 是一个多项式,所以次数较大的n2项不会影响次数较小的这些项,因此 F(x) F ( x ) 的前 n2 n 2 项就是 H(x) H ( x ) ,则 F(x)H(x) F ( x ) − H ( x ) 的前 n2 n 2 项为0,则 (F(x)H(x))2(modxn)0 ( F ( x ) − H ( x ) ) 2 ( mod x n ) ≡ 0
则有:

G(F(x))G(H(x))+G(H(x))(F(x)H(x)) 0(modxn) G ( F ( x ) ) ≡ G ( H ( x ) ) + G ′ ( H ( x ) ) ( F ( x ) − H ( x ) )   ≡ 0 ( mod x n )

也就是:
F(x)H(x)G(H(x))G(H(x))(modxn) F ( x ) ≡ H ( x ) − G ( H ( x ) ) G ′ ( H ( x ) ) ( mod x n )

于是我们可以不断缩小 n n 的规模,同时利用多项式求逆来搞这些东西了。

多项式开根

即求F(x)2P(x)(modxn) P(x) P ( x ) 已知,就是一个常数。
那么我们设 G(A)=A2P G ( A ) = A 2 − P ,于是我们就可以利用多项式牛顿迭代的式子来求解 F(x) F ( x ) 辣。
很容易知道:

F(x)=H(x)H(x)2P(x)2H(x) F ( x ) = H ( x ) − H ( x ) 2 − P ( x ) 2 H ( x )

例题

codeforces 438E/bzoj3625 小朋友和二叉树
我们设 f(x) f ( x ) 表示权值和为 x x 的二叉树形态个数,设a(x)表示 c c 集合中是否存在权值x,则通过枚举根节点权值和根节点两个叉上子树的权值和,可得一个递推式:

f(t)=f(x)f(y)a(txy) f ( t ) = f ( x ) f ( y ) a ( t − x − y )

边界: f(0)=1 f ( 0 ) = 1
也就是说, f(x)=f(x)2a(x)+1 f ( x ) = f ( x ) 2 a ( x ) + 1
解得: f(x)=1±14a(x)2a(x) f ( x ) = 1 ± 1 − 4 a ( x ) 2 a ( x )
分子分母同时乘以 114a(x) 1 ∓ 1 − 4 a ( x ) ,并舍去分母有可能为0的那个解可得:
f(x)=21+14a(x) f ( x ) = 2 1 + 1 − 4 a ( x )

多项式求逆+开根即可完成本题。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return q;
}
const int mod=998244353,N=262150,G=3;
int n,m,inv2;
int A[N],B[N],C[N],rev[N],c[N],tmp[N],len[N];
int ksm(int x,int y) {
    int re=1;
    for(;y;y>>=1,x=1LL*x*x%mod) if(y&1) re=1LL*re*x%mod;
    return re;
}
void NTT(int *a,int n,int x) {
    for(RI i=0;i<n;++i) if(rev[i]>i) swap(a[i],a[rev[i]]);
    for(RI i=1;i<n;i<<=1) {
        int gn=ksm(G,(mod-1)/(i<<1));
        for(RI j=0;j<n;j+=(i<<1)) {
            int t1,t2,g=1;
            for(RI k=0;k<i;++k,g=1LL*g*gn%mod) {
                t1=a[j+k],t2=1LL*g*a[j+i+k]%mod;
                a[j+k]=(t1+t2)%mod,a[j+i+k]=(t1-t2+mod)%mod;
            }
        }
    }
    if(x==1) return;
    reverse(a+1,a+n);int inv=ksm(n,mod-2);
    for(RI i=0;i<n;++i) a[i]=1LL*a[i]*inv%mod;
}
void getrev(int t)
    {for(RI i=0;i<t;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len[t]-1));}
void getinv(int *a,int *b,int n) {
    if(n==1) {b[0]=ksm(a[0],mod-2);return;}
    getinv(a,b,n>>1);
    int kn=n<<1;getrev(kn);
    for(RI i=0;i<n;++i) c[i]=a[i],c[n+i]=0;
    NTT(c,kn,1),NTT(b,kn,1);
    for(RI i=0;i<kn;++i) b[i]=1LL*b[i]*(2-1LL*c[i]*b[i]%mod+mod)%mod;
    NTT(b,kn,-1);
    for(RI i=n;i<kn;++i) b[i]=0;
}
void getsqrt(int *a,int *b,int n) {
    if(n==1) {b[0]=1;return;}
    getsqrt(a,b,n>>1);int kn=n<<1;
    for(RI i=0;i<n;++i) tmp[i]=0;
    getinv(b,tmp,n),getrev(kn);
    for(RI i=0;i<n;++i) c[i]=a[i],c[n+i]=0;
    NTT(c,kn,1),NTT(b,kn,1),NTT(tmp,kn,1);
    for(RI i=0;i<kn;++i) b[i]=1LL*(b[i]+1LL*c[i]*tmp[i]%mod)%mod*inv2%mod;
    NTT(b,kn,-1);
    for(RI i=n;i<kn;++i) b[i]=0;
}
int main()
{
    n=read(),m=read();
    for(RI i=1;i<=n;++i) A[read()]=1;
    int kn=1;while(kn<=m) kn<<=1,len[kn]=len[kn>>1]+1;
    len[kn<<1]=len[kn]+1;
    for(RI i=0;i<kn;++i) A[i]=(mod-4LL*A[i]%mod)%mod;
    A[0]=1,inv2=ksm(2,mod-2);
    getsqrt(A,B,kn),B[0]=(B[0]+1)%mod;
    getinv(B,C,kn);
    for(RI i=1;i<=m;++i) printf("%d\n",(C[i]+C[i])%mod);
    return 0;
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值