多项式的一些操作
大多数东西只有代码啦
主要是因为数学公式太烦了
代码中\(vc\)指的是\(vector<int>\)
树套树每套一层多一个\(log\)
多项式每套一层不多\(log\).
\(nlog^2n\)的树套树能跑\(2*10^5\),\(nlogn\)的多项式多套几层只能跑\(10^5\)
所以log越多越快
细思极恐
快速傅里叶变换 FFT
经过一番精妙的数学推导得出的
void FFT(Complex *a,int tp){
for(int i=0;i<Lim;i++)
if(i<rev[i])swap(a[i],a[rev[i]]);
for(int pos=1;pos<Lim;pos<<=1){
Complex w; w.a=cos(pi/pos),w.b=tp*sin(pi/pos);
for(int R=pos<<1,j=0;j<Lim;j+=R){
Complex p; p.a=1,p.b=0;
for(int k=j;k<pos+j;k++,p=p*w){
Complex x=a[k],y=p*a[k+pos];
a[k]=x+y,a[k+pos]=x-y;
}
}
}
}
快速数论变换 NTT
void NTT(LL *a,int tp){
for(int i=0;i<Lim;i++)
if(i<rev[i])swap(a[i],a[rev[i]]);
for(int pos=1;pos<Lim;pos<<=1){
LL w=ksm(3,(P-1)/(pos<<1));
if(tp==-1)w=ksm(w,P-2);
for(int R=pos<<1,j=0;j<Lim;j+=R){
LL p=1;
for(int k=j;k<j+pos;p=(p*w)%P,k++){
LL x=a[k],y=(p*a[k+pos])%P;
a[k]=(x+y)%P,a[k+pos]=(x-y+P)%P;
}
}
}
if(tp==-1)
for(int i=0;i<Lim;i++)a[i]=(a[i]*inv)%P;
}
快速沃尔什变换 FWT
\(OR\)卷积
这个其实是\(FMT\)
void FWT_or(LL *a,int tp){
for(int i=1;i<Lim;i<<=1)
for(int R=i<<1,j=0;j<Lim;j+=R)
for(int k=j;k<j+i;k++)
(a[k+i]+=a[k]*(LL)tp+P)%=P;
}
\(AND\)卷积
江道理应该把每一位反过来然后做\(FMT\)的
void FWT_and(LL *a,int tp){
for(int i=1;i<Lim;i<<=1)
for(int R=i<<1,j=0;j<Lim;j+=R)
for(int k=j;k<j+i;k++)
(a[k]+=a[k+i]*(LL)tp+P)%=P;
}
\(XOR\)卷积
\(This~is~true~FWT!\)
void FWT_xor(LL *a,int tp){
for(int i=1;i<Lim;i<<=1)
for(int R=i<<1,j=0;j<Lim;j+=R)
for(int k=j;k<j+i;k++){
LL x=a[k],y=a[k+i];
a[k]=(x+y)%P,a[k+i]=(x-y+P)%P;
if(tp==-1)(a[k]=a[k]*inv2%P),(a[k+i]=a[k+i]*inv2%P);
}
}
快速莫比乌斯变换 FMT
本质是高维前缀和
void FMT(int *a){
for(int i=0;i<Lim;i++)
for(int j=0;j<n;j++)
if(j>>i&1)a[j]=(a[j]+a[j^(1<<i)])%P;
}
多项式求逆
直接倍增就好了
void solve(){
bin[0]=n;
for(cnt=1;;cnt++){
bin[cnt]=(bin[cnt-1]+1)/2;
if(bin[cnt]==1)break;
}
g[0]=ksm(f[0],P-2),NTT(f,1);
for(rg i=cnt-1;~i;i--){
for(rg j=bin[i+1];j<Lim;j++)g[j]=0;
for(rg j=0;j<bin[i+1];j++){
h[j]=(g[j]<<1);
if(h[j]>P)h[j]-=P;
}
NTT(g,1);
for(rg j=0;j<Lim;j++)
g[j]=(g[j]*g[j]%P*f[j])%P;
NTT(g,-1);
for(rg j=0;j<bin[i];j++)
g[j]=(h[j]-g[j]+P)%P;
}
}
多项式微分
void diff(vc &a){
int len=a.size()-1;
for(int i=0;i<len-1;i++)a[i]=1ll*a[i+1]*(i+1)%P;
a[len]=0,a.resize(len);
}
多项式积分
void inte(vc &a){
int len=a.size();
a.resize(len+1);
for(int i=len;i;i--)
a[i]=1ll*a[i-1]*ksm(i,P-2)%P;
a[0]=0;
}
这两个是基本操作
多项式求\(ln\)
牛顿迭代就行了.
vc getln(vc a,int n){
vc g=getinv(a,n);
diff(a);
mul(g,a),inte(g);
return g;
}
多项式\(exp\)
常数巨大还在卡常
如果你那么想看代码板子里有.
板子
这里默认对998244353取模
即拉即用造福社会
namespace poly{
typedef vector<int> vc; vc h;
int mi[41],iv[41],bin[41],inv,len,Lim,rev[N],cnt;
int ksm(int a,int p){
int res=1;
while(p){
if(p&1)res=1ll*res*a%P;
a=1ll*a*a%P,p>>=1;
}
return res;
}
void init(){
for(int i=0;(1<<i)<=P-1;i++)mi[i]=ksm(3,(P-1)/(1<<i)),iv[i]=ksm(mi[i],P-2);
}
void init_p(int S){
len=0;
for(Lim=1;Lim<=S;Lim<<=1)len++; inv=ksm(Lim,P-2);
for(int i=0;i<Lim;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
}
void NTT(vc &a,int tp){
a.resize(Lim);
for(rg i=0;i<Lim;i++)
if(i<rev[i])swap(a[i],a[rev[i]]);
for(rg pos=1,s=1;pos<Lim;pos<<=1,s++){
int w=(tp==1)?mi[s]:iv[s];
for(rg R=pos<<1,j=0;j<Lim;j+=R){
int p=1;
for(rg k=j;k<j+pos;p=1ll*p*w%P,k++){
int x=a[k],y=1ll*p*a[k+pos]%P;
a[k]=(x+y)%P,a[k+pos]=(x-y+P)%P;
}
}
}
if(tp==-1)
for(int i=0;i<Lim;i++)a[i]=(1ll*a[i]*inv)%P;
}
void mul(vc &a,vc b){
init_p(a.size()+b.size()+1);
a.resize(Lim),b.resize(Lim);
NTT(a,1),NTT(b,1);
for(int i=0;i<Lim;i++)a[i]=1ll*a[i]*b[i]%P;
NTT(a,-1);
}
void mul2(vc &a,vc b){
init_p(2*a.size()+b.size()+1);
NTT(a,1),NTT(b,1);
for(int i=0;i<Lim;i++)a[i]=1ll*a[i]*a[i]%P*b[i]%P;
NTT(a,-1);
}
vc getinv(vc f,int n){
bin[0]=n;
for(cnt=1;;cnt++){
bin[cnt]=(bin[cnt-1]+1)/2;
if(bin[cnt]==1)break;
}
vc tmp,g; g.resize(1);
g[0]=ksm(f[0],P-2);
for(rg i=cnt-1;~i;i--){
h.resize(bin[i]);
for(rg j=0;j<bin[i+1];j++){
h[j]=(g[j]<<1);
if(h[j]>P)h[j]-=P;
}
tmp.resize(bin[i]);
for(rg j=0;j<bin[i];j++)tmp[j]=f[j];
mul2(g,tmp),g.resize(bin[i]);
for(rg j=0;j<bin[i];j++)
g[j]=(h[j]-g[j]+P)%P;
}
return g;
}
void diff(vc &a){
int len=a.size()-1;
for(int i=0;i<len-1;i++)a[i]=1ll*a[i+1]*(i+1)%P;
a[len]=0,a.resize(len);
}
void inte(vc &a){
int len=a.size();
a.resize(len+1);
for(int i=len;i;i--)
a[i]=1ll*a[i-1]*ksm(i,P-2)%P;
a[0]=0;
}
vc getln(vc a,int n){
a.resize(n+1);
vc g=getinv(a,n);
diff(a);
mul(g,a),inte(g);
g.resize(n+1);
return g;
}
vc getexp(vc f,int n){
int bn[41],cnt=0;
bn[0]=n*2;
for(cnt=1;;cnt++){
bn[cnt]=(bn[cnt-1]-1)/2+1;
if(bn[cnt]==1)break;
}
vc g; g.resize(1),g[0]=1;
for(int i=cnt-1;~i;i--){
vc tmp=getln(g,bn[i]);
for(int j=0;j<bn[i];j++)
tmp[j]=((f[j]-tmp[j])%P+P)%P;
tmp[0]=(tmp[0]+1)%P;
mul(g,tmp);
g.resize(bn[i]);
}
return g;
}
}