居家必备多项式板子

常用的多项式操作都在这。MTT啥的就算了(

NTT +多项式逆+开根+带余除法+求导+积分+ ln + exp +多项式快速幂+分治NTT
+位移+下降幂与点值互转

loj 挑战多项式可以直接 AC。

#include<bits/stdc++.h>
#define int long long
#define pc(x) putchar(x)
#define clr(f,n) memset(f,0,sizeof(int)*n)
#define cpy(f,g,n) memcpy(f,g,sizeof(int)*n)
#define rev(f,n) reverse(f,f+n)
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){f=ch=='-'?-1:f;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
void write(int x)
{
    if(x<0){x=-x;pc('-');}
    if(x>9)write(x/10);
    pc(x%10+48);
}
void polywrite(int *f,int n){for(int i=0;i<n;++i)write(f[i]),pc(' ');pc('\n');}//输出
const int mod=998244353;//模数
const int G=3;//原根
const int maxn=400005;//数组大小,最好算算要开多少,像FDT至少8倍
mt19937 rnd(time(NULL));
int qpow(int x,int y=mod-2)//普通快速幂
{
    int res=1;
    while(y)
    {
        if(y&1)res=res*x%mod;
        x=x*x%mod;y>>=1;
    }return res;
}
const int invG=qpow(G);
namespace exsqrt
{
    int i2;
    struct CP
    {
        int x,y;
        CP(int xx=0,int yy=0){x=xx;y=yy;}
        friend CP operator *(CP x,CP y)
        {return CP((x.x*y.x%mod+i2*x.y%mod*y.y%mod+mod)%mod,(x.x*y.y%mod+x.y*y.x%mod+mod)%mod);}
        friend CP qpowCP(CP x,int y)
        {
            CP res(1,0);
            while(y)
            {
                if(y&1)res=res*x;
                x=x*x;y>>=1;
            }return res;
        }
    };
    bool check(int x){return qpow(x,(mod-1)>>1)==1;}
    int exsqrt(int x)
    {
        if(!x)return 0; if(x==1)return 1;
        int a=rnd()%mod;while(!a||check((a*a-x+mod)%mod))a=rnd()%mod;
        i2=(a*a%mod-x+mod)%mod;int res=qpowCP(CP(a,1),(mod+1)>>1).x;
        return min(res,mod-res);
    }  
}
int n,f[maxn],g[maxn],tr[maxn];
int iv[maxn],fac[maxn],ifac[maxn];
void pref(int m){for(int i=0;i<m;++i)tr[i]=(tr[i>>1]>>1)|((i&1)?m>>1:0);}//初始化
void NTT(int *f,bool op,int m)//逆天天
{
    pref(m);for(int i=0;i<m;++i)if(i<tr[i])swap(f[i],f[tr[i]]);
    for(int p=2;p<=m;p<<=1)
    {
        int len=p>>1;
        int tG=qpow(op?G:invG,(mod-1)/p);
        for(int k=0;k<m;k+=p)
        {
            int buf=1;
            for(int i=k;i<k+len;++i)
            {
                int x=f[i],y=buf*f[i+len]%mod;
                f[i]=(x+y)%mod;f[i+len]=(x-y+mod)%mod;
                buf=buf*tG%mod;
            }
        }
    }if(!op)for(int i=0,invn=qpow(m);i<m;++i)f[i]=f[i]*invn%mod;
}
void px(int *f,int *g,int m){for(int i=0;i<m;++i)f[i]=f[i]*g[i]%mod;}//点乘
void mul(int *f,int *g,int n,int lim)//乘法
{
    static int sav[maxn];
    int m;for(m=1;m<(n<<1);m<<=1);
    clr(sav,m);cpy(sav,g,m);
    NTT(f,1,m);NTT(sav,1,m);
    px(f,sav,m);NTT(f,0,m);
    clr(f+lim,m-lim);clr(sav,m);
}
void invf(int *f,int n)//逆元
{
    int m;for(m=1;m<n;m<<=1);
    static int w[maxn],r[maxn],sav[maxn];
    r[0]=qpow(f[0]);
    for(int p=2;p<=m;p<<=1)
    {
        int len=p>>1;
        cpy(sav,f,p);cpy(w,r,len);
        NTT(sav,1,p);NTT(w,1,p);
        px(sav,w,p);NTT(sav,0,p);
        clr(sav,len);cpy(w,r,p);
        NTT(sav,1,p);NTT(w,1,p);
        px(sav,w,p);NTT(sav,0,p);
        for(int i=len;i<p;++i)r[i]=(mod-sav[i])%mod;
    }cpy(f,r,n);clr(sav,m);clr(w,m);clr(r,m);
}
void sqrtf(int *f,int n)//开根
{
    int m;for(m=1;m<n;m<<=1);
    static int f1[maxn],f2[maxn];
    f1[0]=exsqrt::exsqrt(f[0]);
    for(int p=2;p<=m;p<<=1)
    {
        int len=p>>1;
        for(int i=0;i<len;++i)f2[i]=(f1[i]<<1)%mod;
        invf(f2,p);
        NTT(f1,1,p);px(f1,f1,p);NTT(f1,0,p);
        for(int i=0;i<p;++i)f1[i]=(f1[i]+f[i])%mod;
        mul(f1,f2,p,p);
    }cpy(f,f1,n);clr(f1,m);clr(f2,m);
}
void divf(int *f,int *g,int n,int m)//带余除法,f存商,g存余数
{
    static int q[maxn],r[maxn];
    int len=n-m+1;
    rev(f,n);cpy(r,f,len);rev(f,n);
    rev(g,m);cpy(q,g,len);rev(g,m);
    invf(q,len);mul(q,r,len,len);rev(q,len);mul(g,q,n,n);
    for(int i=0;i<m-1;++i)g[i]=(f[i]-g[i]+mod)%mod;
    cpy(f,q,len);clr(q,n);clr(r,len);
    clr(f+len,n-len);clr(g+m-1,len);
}
void deriv(int *f,int n)//求导
{
    for(int i=1;i<n;++i)
        f[i-1]=f[i]*i%mod;
    f[n-1]=0;
}
void integ(int *f,int n)//积分
{
    iv[1]=1;
    for(int i=2;i<=n;++i)
        iv[i]=(mod-mod/i)*iv[mod%i]%mod;
    for(int i=n-1;i>=1;--i)
        f[i]=f[i-1]*iv[i]%mod;
    f[0]=0;
}
void lnf(int *f,int n)//ln
{
    static int sav[maxn];
    cpy(sav,f,n);invf(sav,n);
    deriv(f,n);mul(f,sav,n,n);
    integ(f,n);clr(sav,n);
}
void expf(int *f,int n)//exp
{
    static int sav[maxn],s[maxn];
    int m;for(m=1;m<n;m<<=1); s[0]=1;
    for(int p=2;p<=m;p<<=1)
    {
        cpy(sav,s,p<<1);lnf(sav,p);
        for(int i=0;i<p;++i)
            sav[i]=(f[i]-sav[i]+mod)%mod;
        (sav[0]+=1)%=mod;mul(s,sav,p,p);
    }cpy(f,s,n);clr(sav,m);clr(s,m);
}
void qpowf(int *f,int n,int k,int pk=-1,int rk=-1)//快速幂,pk是对mod-1取模,rk是真实值
{
    int sft=0;if(!~pk)pk=rk=k;
    for(int i=0;i<n&&!f[i];++i)++sft;
    if(sft*rk>=n||sft==n){fill(f,f+n,0);return;}
    int ivsft=qpow(f[sft]),tmp=qpow(f[sft],pk);
    for(int i=0;i<n;++i)f[i]=f[i+sft]*ivsft%mod;
    lnf(f,n);
    for(int i=0;i<n;++i)f[i]=f[i]*k%mod;
    expf(f,n);sft=min(sft*rk,n);
    for(int i=n-1;i>=sft;--i)f[i]=f[i-sft]*tmp%mod;
    for(int i=0;i<sft;++i)f[i]=0;
}
void shiftf(int *f,int n,int c)//平移
{
    static int s[maxn];fac[0]=1;
    for(int i=1;i<=n;++i)fac[i]=fac[i-1]*i%mod;
    ifac[n]=qpow(fac[n]);
    for(int i=n-1;i>=0;--i)ifac[i]=ifac[i+1]*(i+1)%mod;
    for(int i=0;i<n;++i)s[i]=f[n-i-1]*fac[n-i-1]%mod;
    for(int i=0,buf=1;i<n;++i,buf=buf*c%mod)
        f[i]=buf*ifac[i]%mod;
    mul(s,f,n,n);
    for(int i=0;i<n;++i)f[i]=ifac[i]*s[n-i-1]%mod;
    clr(s+n,n);
}
void FDT(int *f,bool op,int n)//下降幂与点值互转
{
    static int s[maxn];fac[0]=1;
    for(int i=1;i<=n;++i)fac[i]=fac[i-1]*i%mod;
    ifac[n]=qpow(fac[n]);
    for(int i=n-1;i>=0;--i)ifac[i]=ifac[i+1]*(i+1)%mod;
    if(op)for(int i=0;i<n;++i)s[i]=ifac[i];
    else for(int i=0;i<n;++i)if(i&1)s[i]=mod-ifac[i];else s[i]=ifac[i];
    mul(f,s,n,n);clr(s,n);
}
/*
int f1[maxn],f2[maxn];
void precdq()//cdq预处理前缀DFT
{
    g[0]=1;
    for(n=1;(n>>1)<m;n<<=1)
    {
        cpy(f1,f,n);NTT(f1,1,n);
        cpy(f2+n,f1,n);
    }
}
void cdq(int l,int r)//分治逆天天
{
    if(l+1>=r)return;
    int mid=(l+r)>>1,n=r-l;
    cdq(l,mid);
    cpy(f1,g+l,n/2);clr(f1+n/2,n/2);
    NTT(f1,1,n);px(f1,f2+n,n);NTT(f1,0,n);
    for(int i=n/2;i<n;++i)
        g[l+i]=(g[l+i]+f1[i])%mod;
    cdq(mid,r);
}
//这里不能直接用,本模板并没有定义全局变量 m,所以这里需要自行调整变量名称
*/
signed main()
{
    


    //开始你的计数之旅...
    return 0;
}
//ps: n基本上都是多项式真实长度,m是为了NTT的2的幂
//这个模板是复制能用的,函数参数直接用多项式真实长度

posted @ 2022-09-15 19:02  violetctl39  阅读(86)  评论(0编辑  收藏  举报