居家必备多项式板子
常用的多项式操作都在这。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的幂
//这个模板是复制能用的,函数参数直接用多项式真实长度