从前有个多项式

版权声明:既然是蒟蒻写的文,那么各位大爷就将就着看吧~ https://blog.csdn.net/alan_cty/article/details/81909453

想写一个多项式全家桶就开了这个坑
注意所有运算均是在模x^2n的域中进行的

Part A:多项式ln,exp,求幂
多项式ln:假设我们要求 ln ( f ( x ) )
我们不妨求导之后再积分,那么就是 f ( x ) f ( x )
只需要求逆

多项式exp:
先介绍牛顿迭代
假设我们知道了一个函数g(x),我需要求出一个多项式f(x),使得g(f(x))=0
为了方便我们令f(x)有2n项,设前n项是f0(x)
对g(f(x))在f0(x)处泰勒展开,我们有

g ( f ( x ) ) = g ( f 0 ( x ) ) + g ( f 0 ( x ) ) ( f ( x ) f 0 ( x ) ) + g ( f 0 ( x ) ) ( f ( x ) f 0 ( x ) ) 2 2 . . . . . .

注意到我们所有的运算都是在模x^2n的意义下进行的,所以这个式子只有前两项有用
g ( f ( x ) ) = g ( f 0 ( x ) ) + g ( f 0 ( x ) ) ( f ( x ) f 0 ( x ) )

考虑倍增,我们已经求出了f0(x),又g(f(x))=0,那么
g ( f 0 ( x ) ) + g ( f 0 ( x ) ) f ( x ) g ( f 0 ( x ) ) f 0 ( x ) = 0

f ( x ) = f 0 ( x ) g ( f 0 ( x ) ) g ( f 0 ( x ) )

回到原问题,我们要求 B ( x ) = e A ( x )
考虑构造牛顿迭代的形式,设函数 g ( x ) = l n ( x ) A ( x ) ,则g(B(x))=0
根据上述式子

B ( x ) = B 0 ( x ) B 0 ( l n ( B 0 ( x ) ) A ( x ) )

原因是
g ( x ) = 1 x

然后我们只需要求对数即可

多项式求幂:
先求ln,再乘幂次,然后exp回去
注意因为能求ln的多项式一定满足常数项为1,所以这个做法有很大的局限性
不过还是比一般的暴力快速幂快多了

Code

[LOJ6268]分拆数
我知道可以用五边形数做,当个板子写写
Ps:

l n ( 1 1 x ) = l n ( 1 x ) = i >= 1 x i i

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long ll;

const int N=4e5+5,Mo=998244353;

int pwr(int x,int y) {
    int z=1;
    for(;y;y>>=1,x=(ll)x*x%Mo)
        if (y&1) z=(ll)z*x%Mo;
    return z;
}

ll t[N],W[N],inv[N];

void pre(int N) {
    inv[1]=1;
    fo(i,2,N) inv[i]=(Mo-Mo/i)*inv[Mo%i]%Mo;
}

void DFT(ll *a,int len,int flag) {
    int lg=0;for(;(1<<lg)<len;lg++);
    W[0]=1;W[1]=pwr(3,(Mo-1)/len);
    fo(i,2,len) W[i]=W[i-1]*W[1]%Mo;
    for(int i=0;i<len;i++) {
        int p=0;
        for(int j=i,k=0;k<lg;k++,j>>=1) p=(p<<1)+(j&1);
        t[p]=a[i];
    }
    for(int m=2;m<=len;m<<=1) {
        int half=m/2,times=len/m;
        for(int i=0;i<half;i++) {
            ll w=(flag>0)?W[i*times]:W[len-i*times];
            for(int j=i;j<len;j+=m) {
                ll u=t[j],v=t[j+half]*w;
                t[j]=(u+v)%Mo;t[j+half]=(u-v)%Mo;
            }
        }
    }
    for(int i=0;i<len;i++) a[i]=t[i];
    if (flag==-1) {
        int inv=pwr(len,Mo-2);
        for(int i=0;i<len;i++) a[i]=a[i]*inv%Mo;
    }
}

ll c[N];

void get_Inv(ll *a,ll *b,int n) {
    if (n==1) {b[0]=pwr(a[0],Mo-2);return;}
    get_Inv(a,b,n>>1);
    int len=n<<1;
    fo(i,0,n-1) c[i]=a[i];fo(i,n,len-1) c[i]=0;
    fo(i,(n>>1),len-1) b[i]=0;
    DFT(c,len,1);DFT(b,len,1);
    fo(i,0,len-1) b[i]=(2*b[i]-b[i]*b[i]%Mo*c[i])%Mo;
    DFT(b,len,-1);
    fo(i,n,len-1) b[i]=0;
}

ll f[N],g[N];

void get_ln(ll *a,ll *b,int n) {
    fo(i,0,n-2) f[i]=a[i+1]*(i+1)%Mo;f[n-1]=0;
    get_Inv(a,g,n);
    int len=n<<1;
    fo(i,n,len-1) f[i]=g[i]=0;
    DFT(f,len,1);DFT(g,len,1);
    fo(i,0,len-1) f[i]=f[i]*g[i]%Mo;
    DFT(f,len,-1);
    fo(i,1,n-1) b[i]=f[i-1]*pwr(i,Mo-2)%Mo;
    b[0]=0;
}

ll h[N];

void get_exp(ll *a,ll *b,int n) {
    if (n==1) {b[0]=1;return;}
    get_exp(a,b,n>>1);
    get_ln(b,h,n);
    fo(i,0,n-1) h[i]=(a[i]-h[i]+Mo)%Mo;
    (h[0]=h[0]+1)%=Mo;
    int len=n<<1;
    fo(i,n,len-1) h[i]=b[i]=0;
    DFT(h,len,1);DFT(b,len,1);
    fo(i,0,len-1) b[i]=b[i]*h[i]%Mo;
    DFT(b,len,-1);
    fo(i,n,len-1) b[i]=0;
}

ll H[N];

void get_pow(ll *a,int m,int n) {
    get_ln(a,H,n);
    fo(i,0,n-1) H[i]=H[i]*m%Mo;
    get_exp(H,a,n);
}

int n;
ll F[N],G[N];

int main() {
    scanf("%d",&n);int len;
    for(len=1;len<=n;len<<=1);
    pre(len);
    fo(i,1,len-1)
        for(int j=1;i*j<len;j++)
            (F[i*j]+=inv[j])%=Mo;
    get_exp(F,G,len);
    fo(i,1,n) (G[i]+=Mo)%=Mo;
    fo(i,1,n) printf("%lld\n",G[i]);
    return 0;
}

Part B:多项式取模,多点求值
之所以没有快速插值是因为我懒_ (:з」∠) _
定义

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

其中A(x)是一个n次多项式,B(x)是一个m次多项式,m<=n,D(x)的次数界是n-m+1,R(x)的次数界严格小于m,则称R(x)是A(x)模B(x)之后的结果
考虑令等式两边的x取1/x,然后乘上x^n,那么我们会得到
x n A ( 1 x ) = x m B ( 1 x ) x n m D ( 1 x ) + x n R ( 1 x )

我们都知道对于一个次数为n的多项式F(x),x^nF(1/x)表示将F(x)的系数翻转
那么
A R ( x ) = B R ( x ) D R ( x ) + x n m + 1 R R ( x )

考虑在模x^(n-m+1)的意义下计算上式,那么R(x)就被消去了
那么就有
A R ( x ) = B R ( x ) D R ( x ) ( mod x n m + 1 )

直接求逆即可,注意这里求逆的次数界 必须为x^(n-m+1)

多点求值:根据因式定理F(x)在点xi处的点值为F(x) mod (x-xi)
我们想要求出所有的F(x) mod (x-xi),显然可以分治

F ( x ) mod A 0 ( x ) = F ( x ) mod ( A 0 ( x ) A 1 ( x ) ) mod A 0 ( x )

用来取模的多项式可以分治NTT预处理
整合?算了没时间(怠惰
代码很丑,抄了一个跑的快的FFT

Code

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long ll;

int read() {
    char ch;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
    int x=ch-'0';
    for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x;
}

const int N=1e6+5,Mo=998244353;

ll pwr(ll x,ll y) {
    ll z=1;
    for(;y;y>>=1,x=x*x%Mo)
        if (y&1) z=z*x%Mo;
    return z;
}

int n;

ll pa[N],pb[N],pc[N],w[N],wc[N];

int len,lg;
ll t[N],W[2][N],suf[N],pre[N];

vector<ll> poly[N];
#define pb(a) push_back(a) 

void init(int b){
    for(int i=1;i<(1<<b);i<<=1){
        ll wn=pwr(3,(Mo-1)/(i<<1));
        for(int j=0;j<i;++j) W[1][i+j]=(j?wn*W[1][i+j-1]%Mo:1);
        wn=pwr(3,Mo-1-(Mo-1)/(i<<1));
        for(int j=0;j<i;++j) W[0][i+j]=(j?wn*W[0][i+j-1]%Mo:1);
    }
}

void DFT(ll *a,int len,int flag) {
    if (flag==-1) flag=0;
    for(int i=0,j=0;i<len;++i){
        if(i<j) swap(a[i],a[j]);
        for(int k=len>>1;(j^=k)<k;k>>=1);
    }
    for(int i=1;i<len;i<<=1)
        for(int j=0;j<len;j+=(i<<1))
            for(int k=0;k<i;++k) {
                ll x=a[j+k],y=a[j+k+i]*W[flag][i+k]%Mo;
                a[j+k]=(x+y)%Mo;
                a[j+k+i]=(x-y)%Mo;
            }
    ll inv=pwr(len,Mo-2);
    if (!flag) for(int i=0;i<len;i++) a[i]=a[i]*inv%Mo;
}

ll f[N],g[N];

void solve(int v,int l,int r) {
    poly[v].clear();
    if (l==r) {
        poly[v].pb(-w[l]);
        poly[v].pb(wc[l]);
        return;
    }
    int mid=l+r>>1,len=1;
    for(;len<=r-l+1;len<<=1);
    solve(v<<1,l,mid);solve(v<<1|1,mid+1,r);
    fo(i,0,len-1) f[i]=g[i]=0;
    fo(i,0,mid-l+1) f[i]=poly[v<<1][i];
    fo(i,0,r-mid) g[i]=poly[v<<1|1][i];

    DFT(f,len,1);DFT(g,len,1);
    fo(i,0,len-1) f[i]=f[i]*g[i]%Mo;
    DFT(f,len,-1);

    fo(i,0,r-l+1) poly[v].pb(f[i]);
}

ll c[N];

void get_Inv(ll *a,ll *b,int n) {
    if (n==1) {b[0]=pwr(a[0],Mo-2);return;}
    get_Inv(a,b,(n+1)>>1);
    int len=1;
    for(;len<n<<1;len<<=1);
    fo(i,0,n-1) c[i]=a[i];fo(i,n,len-1) c[i]=0;
    fo(i,(n+1)>>1,len-1) b[i]=0;
    DFT(c,len,1);DFT(b,len,1);
    fo(i,0,len-1) b[i]=(2*b[i]-b[i]*b[i]%Mo*c[i])%Mo;
    DFT(b,len,-1);
    fo(i,n,len-1) b[i]=0;
}

ll ta[N],tb[N],tc[N];

void get_Mod(ll *a,ll *b,ll *d,ll *r,int n,int m) {
    int t=n-m+1;
    for(len=1;len<t<<1;len<<=1);
    fo(i,0,m) tb[i]=b[m-i];fo(i,m+1,t) tb[i]=0;
    get_Inv(tb,tc,t);
    for(len=1;len<n<<1;len<<=1);
    fo(i,t,len-1) tc[i]=0;
    fo(i,0,n) ta[i]=a[n-i];fo(i,n+1,len-1) ta[i]=0;
    DFT(tc,len,1);DFT(ta,len,1);
    fo(i,0,len-1) tb[i]=ta[i]*tc[i]%Mo;
    DFT(tb,len,-1);
    fo(i,0,t-1) d[i]=tb[t-i-1];
    fo(i,t,len-1) d[i]=0;
    fo(i,0,len-1) ta[i]=tb[i]=0;
    fo(i,0,m) ta[i]=b[i];
    fo(i,0,t-1) tb[i]=d[i];
    DFT(ta,len,1);DFT(tb,len,1);
    fo(i,0,len-1) tc[i]=ta[i]*tb[i]%Mo;
    DFT(tc,len,-1);
    fo(i,0,m-1) r[i]=(a[i]-tc[i])%Mo;
    fo(i,m,len-1) r[i]=0;
}

vector<ll> E[N];
ll b[N],F[N],G[N],P[N],H[N];

void get_Eva(int v,int l,int r) {
    if (l==r) {
        b[l]=E[v][0];
        return;
    }
    int mid=l+r>>1;
    E[v<<1].clear();E[v<<1|1].clear();
    fo(i,0,r-l) H[i]=E[v][i];
    fo(i,0,mid-l+1) F[i]=poly[v<<1][i];
    get_Mod(H,F,G,P,r-l,mid-l+1);
    fo(i,0,mid-l) E[v<<1].pb(P[i]);

    fo(i,0,r-l) H[i]=E[v][i];
    fo(i,0,r-mid) F[i]=poly[v<<1|1][i];
    get_Mod(H,F,G,P,r-l,r-mid);
    fo(i,0,r-mid-1) E[v<<1|1].pb(P[i]);

    get_Eva(v<<1,l,mid);
    get_Eva(v<<1|1,mid+1,r);
}

int main() {
    freopen("guess.in","r",stdin);
    freopen("guess.out","w",stdout);
    n=read();init(18);
    fo(i,0,n-1) pa[i]=read();
    fo(i,0,n-1) pc[i]=read();

    fo(i,0,n-1) w[i]=-1,wc[i]=-pa[i];
    solve(1,0,n-1);
    fo(i,0,n) P[i]=poly[1][i];
    for(len=1;len<n<<1;len<<=1);
    DFT(P,len,1);DFT(pc,len,1);
    fo(i,0,len-1) pc[i]=pc[i]*P[i]%Mo;
    DFT(pc,len,-1);

    fo(i,0,n-1) w[i]=pwr(pa[i],Mo-2),wc[i]=1;
    solve(1,0,n-1);

    ll ret=1;
    fo(i,0,n-1) ret=ret*pa[i]%Mo;
    fo(i,0,n-1) H[i]=(n-i)*ret%Mo*poly[1][i]%Mo;
    if (n&1) fo(i,0,n-1) H[i]=-H[i];
    E[1].clear();fo(i,0,n-1) E[1].pb(H[i]);
    get_Eva(1,0,n-1);

    fo(i,0,n-1) pb[i]=b[i];

    E[1].clear();fo(i,0,n-1) E[1].pb(pc[i]);
    get_Eva(1,0,n-1);

    fo(i,0,n-1) pb[i]=b[i]*pwr(pb[i],Mo-2)%Mo;
    fo(i,0,n-1) printf("%lld ",(pb[i]+Mo)%Mo);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/alan_cty/article/details/81909453