多项式孤儿桶

巨佬制作人们大家好,我是练习多项式两周半的个人练习生lgl。这里总结一下多项式基本操作。


 

1.多项式加、减、输出

不说了。

时间复杂度$O(n)$。

2.多项式取模

已知多项式$F(x)$,求它对$x^n$取模。

人话:把$n$次及以上的系数清零。

时间复杂度$O(n)$。

3.多项式乘法/卷积

洛谷传送门。

(1)$FFT$

依靠复平面上瞎转以及强大的$double$。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 2000050
#define ll long long
const double Pi = acos(-1.0);
template<typename T>
inline void read(T&x)
{T f=1,c=0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();}x = f*c;
}
struct cp
{double x,y;cp(){}cp(double x,double y):x(x),y(y){}cp operator + (const cp&a)const{return cp(x+a.x,y+a.y);}cp operator - (const cp&a)const{return cp(x-a.x,y-a.y);}cp operator * (const cp&a)const{return cp(x*a.x-y*a.y,x*a.y+y*a.x);}
};
int n,m,mx,to[2*N],lim=1,L;
void fft(cp *a,int len,int k)
{for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);for(int i=1;i<len;i<<=1){cp w0(cos(Pi/i),k*sin(Pi/i));for(int j=0;j<len;j+=(i<<1)){cp w(1,0);for(int o=0;o<i;o++,w=w*w0){cp w1 = a[j+o],w2 = a[j+o+i]*w;a[j+o] = w1+w2;a[j+o+i] = w1-w2;}}}
}
cp a[2*N],b[2*N],c[2*N];
int main()
{read(n),read(m);mx = max(n,m);for(int i=0;i<=n;i++)read(a[i].x);for(int i=0;i<=m;i++)read(b[i].x);while(lim<=2*mx)lim<<=1,L++;for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(L-1)));fft(a,lim,1),fft(b,lim,1);for(int i=0;i<lim;i++)c[i]=a[i]*b[i];fft(c,lim,-1);for(int i=0;i<=n+m;i++)printf("%lld ",(ll)(c[i].x/lim+0.5));puts("");return 0;
}
FFT

注意那个重载运算符,写在里面是可以连加连乘的。

(2)$NTT$

需要一个好模数,还要依靠原根的优秀性质。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 2000050
#define ll long long
#define MOD 998244353
template<typename T>
inline void read(T&x)
{T f=1,c=0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();}x = f*c;
}
ll fastpow(ll x,int y)
{ll ret = 1;while(y){if(y&1)ret=ret*x%MOD;x=x*x%MOD;y>>=1;}return ret;
}
int n,m,mx,to[2*N],lim=1,l;
void ntt(ll *a,int len,int k)
{for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);for(int i=1;i<len;i<<=1){ll w0 = fastpow(3,(MOD-1)/(i<<1));for(int j=0;j<len;j+=(i<<1)){ll w = 1;for(int o=0;o<i;o++,w=w*w0%MOD){ll w1 = a[j+o],w2 = a[j+o+i]*w%MOD;a[j+o] = (w1+w2)%MOD;a[j+o+i] = ((w1-w2)%MOD+MOD)%MOD;}}}if(k==-1)for(int i=1;i<(lim>>1);i++)swap(c[i],c[lim-i]);
}
ll a[2*N],b[2*N],c[2*N];
int main()
{read(n),read(m);mx = max(n,m);for(int i=0;i<=n;i++)read(a[i]);for(int i=0;i<=m;i++)read(b[i]);while(lim<=2*mx)lim<<=1,l++;for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(l-1)));ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=a[i]*b[i]%MOD;ntt(c,lim,-1);ll inv = fastpow(lim,MOD-2);for(int i=0;i<lim;i++)c[i]=c[i]*inv%MOD;for(int i=0;i<=n+m;i++)printf("%lld ",c[i]);puts("");return 0;
}
NTT

自测不开$O2$时$NTT$较快,开了$O2$之后$FFT$更快。

(3)任意模数$NTT$

我的版本是用$FFT$+$long\;double$把系数拆成$x/M$和$x\;mod\;M$,之后再合并。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define double long double
typedef long long ll;
const int N = 500050;
const double Pi = acos(-1.0);
template<typename T>
inline void read(T&x)
{T f = 1,c = 0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}x = f*c;
}
int n,m,MOD;
struct cp
{double x,y;cp(){}cp(double x,double y):x(x),y(y){}cp operator + (const cp&a)const{return cp(x+a.x,y+a.y);}cp operator - (const cp&a)const{return cp(x-a.x,y-a.y);}cp operator * (const cp&a)const{return cp(x*a.x-y*a.y,x*a.y+y*a.x);}
};
int to[N],lim,L;
void init()
{lim = 1,L = 0;while(lim<=2*max(n,m))lim<<=1,L++;for(int i=1;i<lim;i++)to[i] = ((to[i>>1]>>1)|((i&1)<<(L-1)));
}
ll A[N],B[N],C[N];
void fft(cp*a,int len,int k)
{for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);for(int i=1;i<len;i<<=1){cp w0(cos(Pi/i),k*sin(Pi/i));for(int j=0;j<len;j+=(i<<1)){cp w(1,0);for(int o=0;o<i;o++,w=w*w0){cp w1 = a[j+o],w2 = a[j+o+i]*w;a[j+o] = w1+w2;a[j+o+i] = w1-w2;}}}if(k==-1)for(int i=0;i<len;i++)a[i].x/=len;
}
cp a[N],b[N],c[N],d[N],e[N],f[N],g[N],h[N];
void mtt()
{int M = 32768;for(int i=0;i<max(n,m);i++){a[i].x = A[i]/M,b[i].x = A[i]%M;c[i].x = B[i]/M,d[i].x = B[i]%M;}fft(a,lim,1),fft(b,lim,1),fft(c,lim,1),fft(d,lim,1);for(int i=0;i<lim;i++){e[i] = a[i]*c[i],f[i] = a[i]*d[i];g[i] = b[i]*c[i],h[i] = b[i]*d[i];}fft(e,lim,-1),fft(f,lim,-1),fft(g,lim,-1),fft(h,lim,-1);for(int i=0;i<lim;i++)C[i] = (((ll)(e[i].x+0.1))%MOD*M%MOD*M%MOD+((ll)(f[i].x+0.1))%MOD*M%MOD+((ll)(g[i].x+0.1))%MOD*M%MOD+((ll)(h[i].x+0.1))%MOD)%MOD;
}
int main()
{read(n),read(m),read(MOD);n++,m++;init();for(int i=0;i<n;i++)read(A[i]);for(int i=0;i<m;i++)read(B[i]);mtt();for(int i=0;i<n+m-1;i++)printf("%lld ",C[i]);puts("");return 0;
}
MTT

时间复杂度$O(nlogn)$。大常数。

(4)更强的$MTT$

参见myy论文。

void mtt(int*F,int*G,int*H,int len)
{get_lim(len<<1);for(register int i=0;i<lim;++i)a[i]=b[i]=cp(0,0);for(register int i=0;i<len;++i)a[i]=cp(F[i]&(M-1),F[i]>>15),b[i]=cp(G[i]&(M-1),G[i]>>15);fft(a,lim,1),fft(b,lim,1);for(register int i=0;i<lim;++i){int j = (lim-i)&(lim-1);c[j]=cp(0.5*(a[i].x+a[j].x),0.5*(a[i].y-a[j].y))*b[i];d[j]=cp(0.5*(a[i].y+a[j].y),0.5*(a[j].x-a[i].x))*b[i];}fft(c,lim,1),fft(d,lim,1);for(register int i=0;i<lim;++i){int kaa = (ll)(c[i].x/lim+0.5)%MOD;int kab = (ll)(c[i].y/lim+0.5)%MOD;int kba = (ll)(d[i].x/lim+0.5)%MOD;int kbb = (ll)(d[i].y/lim+0.5)%MOD;Mod(H[i] = ((ll)kaa+((ll)(kab+kba)<<15)%MOD+((ll)kbb<<30)%MOD)%MOD+MOD);}
}
更强的MTT

4.多项式求导、积分

不会求导积分建议出门左转高中数学教材。

void down(ll*a,int len)
{for(int i=0;i+1<len;i++)a[i]=a[i+1]*(i+1)%MOD;a[len-1]=0;
}
void up(ll*a,int len)
{for(int i=len-1;i>0;i--)a[i]=a[i-1]*inv(i)%MOD;a[0] = 0;
}
求导积分

求导时间复杂度$O(n)$,积分$O(n)$或者$O(nlog)$。

5.多项式求逆

洛谷传送门。

洛谷加强传送门。

已知多项式$F(x)$,求$G(x)$满足$$F(x)*G(x)≡1\;(mod\;x^n)$$

一个多项式对应的逆是一个唯一的无穷极的多项式,我们要求的是它的前$n$项。

一般都是倍增法求解。

我们现在知道$$F(x)*G0(x)≡1\;(mod\;x^{\frac{n}{2}})$$

移项:$$F(x)*G0(x)-1≡0\;(mod\;x^{\frac{n}{2}})$$

搞模的次数,整体平方:$$(F(x)*G0(x)-1)^2≡0\;(mod\;x^n)$$

打开:$$F^2*G0^2-2*F*G0+1≡0\;(mod\;x^n)$$

两边乘上$G$,得$$F*G0^2-2*G0+G≡0\;(mod\;x^n)$$

再移项:$$G≡2*G0-F*G0^2$$

这个式子减号左边是$\frac{n}{2}$次的,减号右边是$n$次的。

 注意右边乘的话先$G*G$再乘$F$,注意长度。

void get_inv(int len,int*F,int*G)
{if(len==1){G[0]=fastpow(F[0],MOD-2);return ;}get_inv(len>>1,F,G),mtt(G,G,T,len>>1),mtt(T,F,H,len);for(register int i=0;i<len;++i)Mod(G[i]=G[i]*2%MOD-H[i]+MOD);
}
多项式求逆

边界条件$G_0= \frac{1}{F_0}$,时间复杂度$O(nlogn)$。

6.多项式整除、取余

洛谷传送门。

给出$F(x),G(x)$,其中$F(x)$是$n$次的,$G(x)$是$m$次的。

求$$F(x)=Q(x)*G(x)+R(x)$$

$R(x)$次数$m-1$,$Q(x)$次数$n-m$。

神奇变换:$$F(\frac{1}{x})=Q(\frac{1}{x})*G(\frac{1}{x})+R(\frac{1}{x})$$

然后同乘$x^n$,发现$$F(\frac{1}{x})*x^n=Q(\frac{1}{x})*x^{n-m}*G(\frac{1}{x})*x^{m}+R(\frac{1}{x})*x^n$$

$$F_R(x)=Q_R(x)*G_R(x)+R_R(x)$$

其中$F_R(x)$表示将$F$系数反转得到的多项式,$Q_R(x)$、$G_R(x)$同理。

但是我们发现$R(x)$是$m-1$次的,乘完之后后面会有一堆0。所以$$F_R(x)≡Q_R(x)*G_R(x)\;(mod\;x^{n-m})$$

反过来求逆就好了。

把$Q(x)$求出来之后求$R(x)$就是多项式乘法、多项式减法。

时间复杂度$O(nlogn)$。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 600050;
const int MOD = 998244353;
template<typename T>
inline void read(T&x)
{T f = 1,c = 0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}x = f*c;
}
ll fastpow(ll x,int y)
{ll ret = 1;while(y){if(y&1)ret=ret*x%MOD;x=x*x%MOD;y>>=1;}return ret;
}
int n,m,to[N],lim,L;
void ntt(ll*a,int len,int k)
{for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);for(int i=1;i<len;i<<=1){ll w0 = fastpow(3,(MOD-1)/(i<<1));for(int j=0;j<len;j+=(i<<1)){ll w = 1;for(int o=0;o<i;o++,w=w*w0%MOD){ll w1 = a[j+o],w2 = a[j+o+i]*w%MOD;a[j+o] = (w1+w2)%MOD;a[j+o+i] = (w1-w2+MOD)%MOD;}}}if(k==-1){for(int i=1;i<len>>1;i++)swap(a[i],a[len-i]);ll inv = fastpow(len,MOD-2);for(int i=0;i<len;i++)a[i]=a[i]*inv%MOD;}
}
ll f[N],g[N],a[N],b[N],c[N];
void sol(int len)
{if(len==1){g[0] = fastpow(f[0],MOD-2);return ;}int mid = (len+1)>>1;sol(mid);lim = 1,L = 0;while(lim<=2*len)lim<<=1,L++;for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(L-1)));for(int i=0;i<lim;i++)a[i]=b[i]=0;for(int i=0;i<len;i++)a[i]=f[i];for(int i=0;i<mid;i++)b[i]=g[i];ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=a[i]*b[i]%MOD*b[i]%MOD;ntt(c,lim,-1);for(int i=0;i<len;i++)g[i]=(2*g[i]%MOD-c[i]+MOD)%MOD;
}
void mul(ll*A,ll*B,int len)
{lim = 1,L = 0;while(lim<=2*len)lim<<=1,L++;for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(L-1)));for(int i=0;i<lim;i++)a[i]=b[i]=0;for(int i=0;i<len;i++)a[i]=A[i],b[i]=B[i];ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=a[i]*b[i]%MOD;ntt(c,lim,-1);
}
ll F[N],G[N],H[N],R[N],F0[N],G0[N];
int main()
{read(n),read(m),n++,m++;for(int i=0;i<n;i++)read(F[i]);for(int i=0;i<m;i++)read(G[i]);memcpy(F0,F,sizeof(F0));memcpy(G0,G,sizeof(G0));reverse(F,F+n);reverse(G,G+m);for(int i=0;i<m;i++)f[i]=G[i];sol(n-m+1);mul(F,g,n-m+1);for(int i=0;i<n-m+1;i++)H[i]=c[i];reverse(H,H+n-m+1);mul(H,G0,n);for(int i=0;i<n-m+1;i++)printf("%lld ",H[i]);puts("");for(int i=0;i<m-1;i++)printf("%lld ",(F0[i]-c[i]+MOD)%MOD);puts("");return 0;
}
多项式除法

7.多项式开根

洛谷传送门。

这个黑科技是小朋友与二叉树之后大佬们搞出来的。

已知$F(x)$,求$G(x)$满足$$G(x)*G(x)≡F(x)\;(mod\;x^n)$$

和多项式的逆一样,一个多项式的根是惟一的。

依然考虑倍增求解。

假设我们已知$$H(x)*H(x)≡F(x)\;(mod\;x^{\frac{n}{2}})$$,要求$G(x)$。

常规移项平方:$$(H^2(x)-F(x))^2≡0\;(mod\;x^n)$$

然后是一个神奇操作:$$(H^2(x)+F(x))^2≡4F(x)H^2(x)\;(mod\;x^n)$$

然后再移项:$$\frac{(H^2(x)+F(x))^2}{4H^2(x)} ≡ F(x) \; (mod\;x^n)$$

然后惊奇的发现左边是平方形式,所以$$G(x) ≡ \frac{H^2(x)+F(x)}{2H(x)} \; (mod\;x^n)$$

递归的时候套一个多项式求逆即可(反正才四行),注意清数组不然可能会死

时间复杂度$T(n)=T(n/2)+O(nlogn)=O(nlogn)$,大常数。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 600050;
const int MOD = 998244353;
template<typename T>
inline void read(T&x)
{T f = 1,c = 0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}x = f*c;
}
ll fastpow(ll x,int y)
{ll ret = 1;while(y){if(y&1)ret=ret*x%MOD;x=x*x%MOD;y>>=1;}return ret;
}
int to[N],lim,L;
void ntt(ll*a,int len,int k)
{for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);for(int i=1;i<len;i<<=1){ll w0 = fastpow(3,(MOD-1)/(i<<1));for(int j=0;j<len;j+=(i<<1)){ll w = 1;for(int o=0;o<i;o++,w=w*w0%MOD){ll w1 = a[j+o],w2 = a[j+o+i]*w%MOD;a[j+o] = (w1+w2)%MOD;a[j+o+i] = (w1-w2+MOD)%MOD;}}}if(k==-1){for(int i=1;i<len>>1;i++)swap(a[i],a[len-i]);ll inv = fastpow(len,MOD-2);for(int i=0;i<len;i++)a[i]=a[i]*inv%MOD;}
}
int n;
ll a[N],b[N],c[N];
void get_lim(int len)
{lim = 1,L = 0;while(lim<=len)lim<<=1,L++;for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(L-1)));
}
void mul(ll*A,int Alen,ll*B,int Blen)
{get_lim(Alen+Blen);for(int i=0;i<lim;i++)a[i]=b[i]=0;for(int i=0;i<Alen;i++)a[i]=A[i];for(int i=0;i<Blen;i++)b[i]=B[i];ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=a[i]*b[i]%MOD;ntt(c,lim,-1);
}
ll F[N],G[N],H[N];
void get_inv(int len)
{if(len==1){H[0] = fastpow(G[0],MOD-2);return ;}int mid = (len+1)>>1;get_inv(mid);get_lim(len<<1);for(int i=0;i<lim;i++)a[i]=b[i]=0;for(int i=0;i<len;i++)a[i]=G[i];for(int i=0;i<mid;i++)b[i]=H[i];ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=a[i]*b[i]%MOD*b[i]%MOD;ntt(c,lim,-1);for(int i=0;i<len;i++)H[i]=(2*H[i]%MOD-c[i]+MOD)%MOD;
}
void get_sqrt(int len)
{if(len==1){G[0] = 1;return ;}int mid = (len+1)>>1;get_sqrt(mid);for(int i=0;i<len;i++)H[i]=0;get_inv(len);mul(G,mid,G,mid);for(int i=0;i<len;i++)G[i]=(c[i]+F[i])%MOD;mul(G,len,H,len);for(int i=0;i<len;i++)G[i]=c[i]*((MOD+1)/2)%MOD;
}
int main()
{read(n);for(int i=0;i<n;i++)read(F[i]);get_sqrt(n);for(int i=0;i<n;i++)printf("%lld ",G[i]);puts("");return 0;
}
多项式开根

8.多项式对数函数(ln)

洛谷传送门。

已知$F(x)$,求$$G(x)≡ln(F(x))\;(mod\;x^n)$$

直接求导即可:$$G'(x)≡F'(x)*\frac{1}{F(x)}$$

所以说求导求逆积分即可。

时间复杂度$O(nlogn)$。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 600050;
const int MOD = 998244353;
template<typename T>
inline void read(T&x)
{T f = 1,c = 0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}x = f*c;
}
ll fastpow(ll x,int y)
{ll ret = 1;while(y){if(y&1)ret=ret*x%MOD;x=x*x%MOD;y>>=1;}return ret;
}
ll inv(ll x){return fastpow(x,MOD-2);}
int to[N],lim,L;
void down(ll*a,int len)
{for(int i=0;i+1<len;i++)a[i]=a[i+1]*(i+1)%MOD;a[len-1]=0;
}
void up(ll*a,int len)
{for(int i=len-1;i>0;i--)a[i]=a[i-1]*inv(i)%MOD;a[0] = 0;
}
void ntt(ll*a,int len,int k)
{for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);for(int i=1;i<len;i<<=1){ll w0 = fastpow(3,(MOD-1)/(i<<1));for(int j=0;j<len;j+=(i<<1)){ll w = 1;for(int o=0;o<i;o++,w=w*w0%MOD){ll w1 = a[j+o],w2 = a[j+o+i]*w%MOD;a[j+o] = (w1+w2)%MOD;a[j+o+i] = (w1-w2+MOD)%MOD;}}}if(k==-1){for(int i=1;i<len>>1;i++)swap(a[i],a[len-i]);ll inv = fastpow(len,MOD-2);for(int i=0;i<len;i++)a[i]=a[i]*inv%MOD;}
}
int n;
ll a[N],b[N],c[N];
ll F[N],G[N];
void get_inv(int len)
{if(len==1){G[0] = inv(F[0]);return ;}int mid = (len+1)>>1;get_inv(mid);lim = 1,L = 0;while(lim<2*len)lim<<=1,L++;for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(L-1)));for(int i=0;i<lim;i++)a[i]=b[i]=0;for(int i=0;i<len;i++)a[i]=F[i];for(int i=0;i<mid;i++)b[i]=G[i];ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=a[i]*b[i]%MOD*b[i]%MOD;ntt(c,lim,-1);for(int i=0;i<len;i++)G[i]=(2*G[i]%MOD-c[i]+MOD)%MOD;
}
int main()
{read(n);for(int i=0;i<n;i++)read(F[i]);get_inv(n);down(F,n);lim = 1,L = 0;while(lim<=2*n)lim<<=1,L++;for(int i=1;i<lim;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(L-1)));for(int i=0;i<lim;i++)a[i]=b[i]=0;for(int i=0;i<n;i++)a[i]=F[i];for(int i=0;i<n;i++)b[i]=G[i];ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=a[i]*b[i]%MOD;ntt(c,lim,-1);for(int i=0;i<n;i++)F[i]=c[i];up(F,n);for(int i=0;i<n;i++)printf("%lld ",F[i]);puts("");return 0;
}
多项式ln

9.多项式指数函数(exp)

洛谷传送门。

已知$F(x)$,求$$G(x)≡e^{F(x)}\;(mod\;x^n)$$

附加牛顿迭代:$x=x'-\frac{f(x')}{f(x')'}$

牛迭原理?一张图:

回到问题,依然倍增求解,假设已知$$H(x)≡e^{F(x)}\;(mod\;x^{\frac{n}{2}})$$

先对式子取ln再移项:$$ln(H(x))-F(x)≡0\;(mod\;x^{\frac{n}{2}})$$

然后呢……

我们把它当做$ln(x)-k=0$,问题转化为式子求零点,那么把牛迭套进去有这样一个式子:$$G(x)≡H(x)-\frac{ln(H(x))-F(x)}{\frac{1}{H(x)}}\;(mod\;x^n)$$

分数太难看了,变一下:$$G(x)≡H(x)*(1-ln(H(x))+F(x))\;(mod\;x^n)$$

左转把多项式ln板子带过来即可。

复杂度?$T(n)=T(n/2)+O(nlogn)=O(nlogn)$?

常数?卡死人。

我的$ntt$跑$1e6$的数据:

我的$exp$跑$1e5$的数据:

卡常好啊

// luogu-judger-enable-o2
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ll;
const int N = 600050;
const int MOD = 998244353;
template<typename T>
inline void read(T&x)
{T f = 1,c = 0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}x = f*c;
}
ll fastpow(ll x,int y)
{ll ret = 1;while(y){if(y&1)ret=ret*x%MOD;x=x*x%MOD;y>>=1;}return ret;
}
int to[N],lim,L;
ll W[N],Inv;
void Mod(ll&x){if(x>=MOD)x-=MOD;}
void ntt(ll*a,int len,int k)
{for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);for(int i=1;i<len;i<<=1){ll w0 = W[i];for(int j=0;j<len;j+=(i<<1)){ll w = 1;for(int o=0;o<i;o++,w=w*w0%MOD){ll w1 = a[j+o],w2 = a[j+o+i]*w%MOD;Mod(a[j+o] = w1+w2);Mod(a[j+o+i] = w1-w2+MOD);}}}if(k==-1){for(int i=1;i<len>>1;i++)swap(a[i],a[len-i]);for(int i=0;i<len;i++)a[i]=a[i]*Inv%MOD;}
}
ll a[N],b[N],c[N];
void get_lim(int len)
{lim = 1,L = 0;while(lim<=len)lim<<=1,L++;for(int i=1;i<lim;i++)to[i]=((to[i>>1])>>1|((i&1)<<(L-1)));for(int i=1;i<lim;i<<=1)W[i]=fastpow(3,(MOD-1)/(i<<1));Inv = fastpow(lim,MOD-2);
}
int n;
ll A[N],F[N],G[N],H[N];//F,lnF,1/G
void get_inv(int len)
{if(len==1){H[0] = fastpow(G[0],MOD-2);return ;}int mid = (len+1)>>1;get_inv(mid);get_lim(len<<1);for(int i=0;i<=lim;i++)a[i]=b[i]=0;for(int i=0;i<len;i++)a[i]=G[i];for(int i=0;i<mid;i++)b[i]=H[i];ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=a[i]*b[i]%MOD*b[i]%MOD;ntt(c,lim,-1);for(int i=0;i<len;i++)Mod(H[i]=2*H[i]%MOD-c[i]+MOD);
}
void down(ll*a,int len)
{for(int i=0;i<len;i++)a[i]=a[i+1]*(i+1)%MOD;a[len-1]=0;
}
void up(ll*a,int len)
{for(int i=len-1;i;i--)a[i]=a[i-1]*fastpow(i,MOD-2)%MOD;a[0] = 0;
}
void get_ln(int len)
{for(int i=0;i<len;i++)G[i]=F[i],H[i]=0;get_inv(len);down(G,len);get_lim(len<<1);for(int i=0;i<lim;i++)a[i]=b[i]=0;for(int i=0;i<len;i++)a[i]=G[i],b[i]=H[i];ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=a[i]*b[i]%MOD;ntt(c,lim,-1);for(int i=0;i<len;i++)G[i]=c[i];up(G,len);
}
void get_exp(int len)
{if(len==1){F[0] = 1;return ;}int mid = (len+1)>>1;get_exp(mid);for(int i=0;i<len;i++)G[i]=0;get_ln(len);get_lim(len<<1);for(int i=0;i<lim;i++)a[i]=b[i]=0;for(int i=0;i<len;i++)a[i]=F[i],b[i]=(A[i]-G[i]+MOD)%MOD;Mod(++b[0]);ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=a[i]*b[i]%MOD;ntt(c,lim,-1);for(int i=0;i<len;i++)F[i]=c[i];
}
int main()
{read(n);for(int i=0;i<n;i++)read(A[i]);get_exp(n);for(int i=0;i<n;i++)printf("%llu ",F[i]);puts("");return 0;
}
多项式exp

10.多项式快速幂

洛谷传送门。

已知$A(x)$,求$B(x)≡A^k(x)\;(mod\;x^n)$。

直接取$ln$,乘完再$exp$回去……

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 500050;
const int MOD = 998244353;
template<typename T>
inline void read(T&x)
{T f = 1,c = 0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}x = f*c;
}
template<typename T>
inline void Mod(T&x){if(x>=MOD)x-=MOD;}
int modread()
{int ret = 0;char ch=getchar();while(ch<'0'||ch>'9'){ch=getchar();}while(ch>='0'&&ch<='9'){Mod(ret=10ll*ret%MOD+ch-'0');ch=getchar();}return ret;
}
int fastpow(int x,int y)
{int ret = 1;while(y){if(y&1)ret=1ll*ret*x%MOD;x=1ll*x*x%MOD;y>>=1;}return ret;
}
int inv(int x){return fastpow(x,MOD-2);}
int n,k,to[N],lim,L,LL[N],ny[N],jc[N],jny[N];
int init(int n)
{lim = LL[2] = 1;while(lim<=n)lim<<=1,LL[lim<<1]=LL[lim]+1;ny[1] = jc[0] = jc[1] = jny[0] = jny[1] = 1;for(int i=2;i<=lim;i++){ny[i] = 1ll*(MOD-MOD/i)*ny[MOD%i]%MOD;jc[i] = 1ll*jc[i-1]*i%MOD;jny[i] = 1ll*jny[i-1]*ny[i]%MOD;}return lim;
}
void get_lim(int len)
{lim = len,L = LL[len];for(int i=1;i<len;i++)to[i]=((to[i>>1]>>1)|((i&1)<<(L-1)));
}
void ntt(int*a,int len,int k)
{for(int i=0;i<len;i++)if(i<to[i])swap(a[i],a[to[i]]);for(int i=1;i<len;i<<=1){int w0 = fastpow(3,(MOD-1)/(i<<1));for(int j=0;j<len;j+=(i<<1)){int w = 1;for(int o=0;o<i;o++,w=1ll*w*w0%MOD){int w1 = a[j+o],w2 = 1ll*a[j+o+i]*w%MOD;Mod(a[j+o] = w1+w2);Mod(a[j+o+i] = w1+MOD-w2);}}}if(k==-1){for(int i=1;i<len>>1;i++)swap(a[i],a[len-i]);int Inv = inv(len);for(int i=0;i<len;i++)a[i]=1ll*a[i]*Inv%MOD;}
}
int a[N],b[N],c[N],I[N],T[N],Ln[N];
void mul(int*F,int*G,int len)
{get_lim(len<<1);for(int i=0;i<lim;i++)a[i]=b[i]=0;for(int i=0;i<len;i++)a[i]=F[i],b[i]=G[i];ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=1ll*a[i]*b[i]%MOD;ntt(c,lim,-1);
}
void get_d(int*F,int*G,int len)
{for(int i=1;i<len;i++)G[i-1]=1ll*F[i]*i%MOD;G[len-1]=0;
}
void get_j(int*F,int*G,int len)
{for(int i=1;i<len;i++)G[i]=1ll*F[i-1]*ny[i]%MOD;G[0] = 0;
}
void get_inv(int*F,int*G,int len)
{if(len==1){G[0]=inv(F[0]);return ;}get_inv(F,G,len>>1);get_lim(len<<1);for(int i=0;i<lim;i++)a[i]=b[i]=0;for(int i=0;i<len;i++)a[i]=F[i];for(int i=0;i<len>>1;i++)b[i]=G[i];ntt(a,lim,1),ntt(b,lim,1);for(int i=0;i<lim;i++)c[i]=1ll*a[i]*b[i]%MOD*b[i]%MOD;ntt(c,lim,-1);for(int i=0;i<len;i++)G[i]=(2ll*G[i]%MOD+MOD-c[i])%MOD;
}
void get_ln(int*F,int*G,int len)
{for(int i=0;i<len;i++)I[i]=0;get_inv(F,I,len),get_d(F,T,len);mul(I,T,len);for(int i=0;i<len;i++)T[i]=c[i];get_j(T,G,len);
}
void get_exp(int*F,int*G,int len)
{if(len==1){G[0]=1;return ;}get_exp(F,G,len>>1);get_ln(G,Ln,len);for(int i=0;i<len;i++)Mod(Ln[i]=F[i]+MOD-Ln[i]);Mod(++Ln[0]);mul(Ln,G,len);for(int i=0;i<len;i++)G[i]=c[i];
}
void fastpow(int*F,int*G,int len,int k)
{get_ln(F,F,len);for(int i=0;i<len;i++)F[i]=1ll*F[i]*k%MOD;get_exp(F,G,len);
}
int F[N],G[N];
int main()
{
//    freopen("tt.in","r",stdin);read(n),k=modread();for(int i=0;i<n;i++)read(F[i]);int mx = init(n);fastpow(F,G,mx,k);for(int i=0;i<n;i++)printf("%d ",G[i]);puts("");return 0;
}
多项式快速幂

 

大概就这些了吧。

(是时候总结一下常用卡常技巧了)

转载于:https://www.cnblogs.com/LiGuanlin1124/p/11024466.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/364186.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python亲密度_Python OpenCV 图像2D直方图,取经之旅第 25 天

Python OpenCV 365 天学习计划&#xff0c;与橡皮擦一起进入图像领域吧。基础知识铺垫在之前的博客中&#xff0c;我们获取图像直方图的方式都是获取一维直方图&#xff0c;简单说就是只获取一个通道的特征&#xff0c;例如灰度&#xff0c;B 通道&#xff0c;R 通道。今天要学…

清除浮动的方式

1、父级div定义伪类&#xff1a;after和zoom <style type"text/css"> .div1{background:#000080;border:1px solid red;}.div2{background:#800080;border:1px solid red;height:100px;margin-top:10px}.left{float:left;width:20%;height:200px;background:#D…

py函数两个返回值_Python 函数为什么会默认返回 None?

Python 有一项默认的做法&#xff0c;很多编程语言都没有——它的所有函数都会有一个返回值&#xff0c;不管你有没有写 return 语句。本文出自“Python为什么”系列&#xff0c;在正式开始之前&#xff0c;我们就用之前讨论过的 pass语句和 …对象 作为例子&#xff0c;看看 P…

行高 line-height

一、行高的定义line-height(行高)&#xff1a;两行文字基线之间的距离1、什么是基线&#xff1f;2、为何是基线&#xff1f;3、需要两行吗&#xff1f;1、什么是基线&#xff1f;我们上学的时候都用过&#xff0c;抄写英文字母的时候。其中有一条红线&#xff0c;这个红线就是基…

实验七报告

一、实验结论 part1&#xff1a;验证性实验 1.验证性实验2 如果事先不知道学生人数&#xff0c;尝试对line29做如下修改后&#xff0c;程序运行结果是否正确&#xff1f;回答问题&#xff0c;并给出运行结果截图。 运行结果正确// 将file1.txt中小写字母转换成大写后&#xff…

XPath语法规则及实例

XPath语法规则及实例 XPath语法规则一、XPath术语&#xff1a; 1.节点&#xff1a;在XPath中&#xff0c;有七种类型的节点&#xff1a;元素、属性、文本、命名空间、处理指令、注释以及文档&#xff08;根&#xff09;节点。 XML文档是被作为节点树来对待的。树的根被称为文档…

WorkPlus超级APP助力企业节省IT人力成本,实现快速移动化

在信息化时代&#xff0c;移动应用已经成为企业发展的重要组成部分。然而&#xff0c;开发和维护原生客户端的成本却相对较高&#xff0c;需要大量的iOS、安卓和桌面端工程师。为了解决这一问题&#xff0c;WorkPlus作为一个功能完备的超级APP&#xff0c;为企业节约了大量的IT…

addEventListener的click和onclick的区别

前两节都和addEventListener的click有关&#xff0c;于是在想它与onclick有什么区别呢&#xff0c;自己调试了一下&#xff0c;网上也有相关资料 事件绑定 onclick绑定方式 优点&#xff1a; - 简洁 - 处理事件的this关键字指向当前元素 缺点&#xff1a; - 不能对事件捕获或…

ApiCloud利用NVTabBar模块快速搭建起APP的框架

废话不说&#xff0c;直接上代码 模块地址&#xff1a;https://docs.apicloud.com/Client-API/Nav-Menu/NVTabBar 代码实例&#xff1a; <!doctype html> <html><head><meta charset"utf-8"><meta name"viewport" content"…

Java 8中新的并行API:Glitz和Glamour的背后

我是一个出色的多任务处理者。 即使我在写这篇文章&#xff0c;我仍然可以为昨天在一个大家都对我陌生的聚会上发表的言论感到尴尬。 好消息是&#xff0c;我并不孤单– Java 8在多任务处理方面也相当出色。 让我们看看如何。 Java 8中引入的关键新功能之一是并行数组操作。 这…

ASP.NET 中执行 URL 重写

作者&#xff1a;overred 来源&#xff1a;原创URL 重写就是把URL地址重新改写&#xff08;汗^_^&#xff09;。详情&#xff1a;http://www.microsoft.com/china/msdn/library/webservices/asp.net/URLRewriting.mspx优点&#xff1a;把url缩短等用法&#xff1a;1.下载ms的…

nine

nine Scarpy爬虫框架https://www.cnblogs.com/kermitjam/articles/10147261.html?tdsourcetags_pctim_aiomsg 高并发的爬虫框架 Runspider只能执行某个 爬虫程序.py文件 将项目根目录导入 Xpath语法 获取内容&#xff08;‘./text()’) 发送请求 ---> 获取响应数据 --->…

使用ActiveMQ –具有故障转移协议的“主/从”配置

介绍 ActiveMQ代理往往是企业中消息传递基础结构的核心部分。 此消息传递基础结构的高度可用性和可伸缩性至关重要。 请阅读此链接 &#xff0c;以了解有关创建经纪人网络以支持各种用例的更多信息。 ActiveMQ的流行用例之一是带有共享数据库的主/从配置。 使用此配置时&#x…

页面那些位置是投放广告的最佳位置

通常好的广告位置并非页面最上方的通栏广告&#xff0c;而是页面第一屏导航条下面中央和左侧的位置&#xff0c;此处放置广告会取得较好的效果。除此之外&#xff0c;页面中屏主要内容附件的位置为左侧和下侧较好&#xff0c;用户会第一时间注意到并关注这些广告。 转载于:http…

如何在Java中将字节数组转换为InputStream和OutputStream

您是否坚持使用编码&#xff0c;因为您有字节数组&#xff0c;并且链中的下一个方法需要InputStream&#xff1f; 不用担心Java有解决方案&#xff0c;您可以使用 ByteArrayInputStream 在Java中将字节数组转换为InputStream 。 此类使用字节数组作为源&#xff0c;并且由于它…

mysql 笔记打包下载_mysql 5.7压缩包安装笔记

重装系统之后准备安装mysql,看到官网上有mysql 5.7.10可以下载就点了,然后就开始了漫长的安装路程,总共折腾差不多一个多小时,最后终于安装成功了,这里把安装过程写下来,给自己做个笔记,也给后来人一个安装提示.1.下载安装包直接点击或者复制之后就可以下载了,不嫌麻烦或者想体…

Java 8 LongAdders:管理并发计数器的正确方法

我只是喜欢新玩具&#xff0c;而Java 8有很多 。 这次我想谈谈我的最爱之一-并发加法器。 这是一组用于管理由多个线程编写和读取的计数器的新类。 新的API有望显着提高性能&#xff0c;同时仍然使事情变得简单明了。 自从多核架构问世以来人们一直在管理并发计数器&#xff0…

JS中ptototype和__proto__的关系

学到原型的时候感觉头都大了/(ㄒoㄒ)/~~ 尤其是ptototype和__proto__ 傻傻分不清 通过多番查找资料&#xff0c;根据自己的理解&#xff0c;总结如下&#xff1a; 一、构造函数&#xff1a; 构造函数&#xff1a;通过new关键字可以用来创建特定类型的对象的函数。比如像Obje…

[最短路]飞行

题目描述 WFYZ的校园很大&#xff0c;这里生活着很多生物&#xff0c;比如住在钟楼上的的鸽子&#xff0c;其中鸽子冉冉和她的妹妹凝凝白天在不同的地方吃虫&#xff0c;而在晚上她们都回到钟楼休息。她俩是两只懒鸟&#xff0c;于是提出了一个计划&#xff0c;尽量减少她们在飞…

Java状态和策略设计模式之间的差异

为了在Core Java应用程序中正确使用状态和策略设计模式&#xff0c;对于Java开发人员清楚地了解它们之间的区别很重要。 尽管状态和策略设计模式的结构相似&#xff0c;并且都基于开放式封闭设计原则&#xff0c;从SOLID设计原则表示为“ O”&#xff0c;但它们在意图上完全不同…