用户工具

站点工具


2020-2021:teams:legal_string:jxm2001:多项式_4

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
2020-2021:teams:legal_string:jxm2001:多项式_4 [2020/08/25 12:44]
jxm2001
2020-2021:teams:legal_string:jxm2001:多项式_4 [2020/08/25 17:43] (当前版本)
jxm2001
行 456: 行 456:
 namespace Poly{ namespace Poly{
  const int G=3;  const int G=3;
- int rev[MAXN<<​2],​Wn[30][2];+ int rev[MAXN<<​2],​Pool[MAXN<<​3],*Wn[30];
  void init(){  void init(){
- int m=Mod-1,lg2=0; + int lg2=0,​*pos=Pool,​n,​w
- while(m%2==0)m>>​=1,​lg2++; + while((1<<​lg2)<​MAXN*2)lg2++; 
- Wn[lg2][1]=quick_pow(G,​m); + n=1<<lg2,w=quick_pow(G,​(Mod-1)/​(1<<​lg2)); 
- Wn[lg2][0]=quick_pow(Wn[lg2][1],Mod-2); + while(~lg2){ 
- while(lg2){ + Wn[lg2]=pos,pos+=n
- m<<=1,lg2--+ Wn[lg2][0]=1; 
- Wn[lg2][0]=1LL*Wn[lg2+1][0]*Wn[lg2+1][0]%Mod; + _for(i,​1,​n)Wn[lg2][i]=1LL*Wn[lg2][i-1]*w%Mod; 
- Wn[lg2][1]=1LL*Wn[lg2+1][1]*Wn[lg2+1][1]%Mod;+ w=1LL*w*w%Mod; 
 + lg2--;​n>>​=1;
  }  }
  }  }
行 479: 行 480:
  swap(f[i],​f[rev[i]]);​  swap(f[i],​f[rev[i]]);​
  int t1,t2;  int t1,t2;
- for(int i=1,lg2=0;​i<​n;​i<<​=1,​lg2++){ + for(int i=1,lg2=1;​i<​n;​i<<​=1,​lg2++){
- int w=Wn[lg2+1][type];​+
  for(int j=0;​j<​n;​j+=(i<<​1)){  for(int j=0;​j<​n;​j+=(i<<​1)){
- int cur=1; 
  _for(k,​j,​j+i){  _for(k,​j,​j+i){
- t1=f[k],​t2=1LL*cur*f[k+i]%Mod;​+ t1=f[k],​t2=1LL*Wn[lg2][k-j]*f[k+i]%Mod;​
  f[k]=(t1+t2)%Mod,​f[k+i]=(t1-t2)%Mod;​  f[k]=(t1+t2)%Mod,​f[k+i]=(t1-t2)%Mod;​
- cur=1LL*cur*w%Mod;​ 
  }  }
  }  }
  }  }
  if(!type){  if(!type){
 + reverse(f+1,​f+n);​
  int div=quick_pow(n,​Mod-2);​  int div=quick_pow(n,​Mod-2);​
  _for(i,​0,​n)f[i]=(1LL*f[i]*div%Mod+Mod)%Mod;​  _for(i,​0,​n)f[i]=(1LL*f[i]*div%Mod+Mod)%Mod;​
行 745: 行 744:
  
 ===== 多项式快速插值 ===== ===== 多项式快速插值 =====
 +
 +==== 算法简介 ====
 +
 +给定 $n$ 个点 $(x_i,​y_i)$,求次数不超过 $n-1$ 次的多项式 $f(x)$ 满足 $f(x_i)=y_i$。时间复杂度 $O(n\log^2 n)$,空间复杂度 $O(m\log m)$。
 +
 +==== 算法实现 ====
 +
 +[[https://​www.luogu.com.cn/​problem/​P5158|洛谷p5158]]
 +
 +根据拉格朗日插值法,有
 +
 +$$f(x)=\sum_{i=1}^n y_i\prod_{j=1,​j\neq i}^n \frac {x-x_j}{x_i-x_j}=\sum_{i=1}^n \cfrac {y_i}{\prod_{j=1,​j\neq i}(x_i-x_j)}\prod_{j=1,​j\neq i}^n x-x_j$$
 +
 +设 $g(x)=\prod_{i=1}^n x-x_i$,根据洛必达法则,有
 +
 +$$\prod_{j=1,​j\neq i}^n x_i-x_j=\lim_{x\to x_i}\frac {g(x)}{x-x_i}=g'​(x_i)$$
 +
 +于是有 $f(x)=\sum_{i=1}^n \cfrac {y_i}{g'​(x_i)}\prod_{j=1,​j\neq i}^n x-x_j$。
 +
 +考虑 $O(n\log^2 n)$ 分治乘法计算出 $g(x)$,再通过 $O(n\log^2 n)$ 多项式多点求值计算出所有 $g'​(x_i)$。
 +
 +设 $f_{l,​r}(x)=\sum_{i=l}^r \cfrac {y_i}{g'​(x_i)}\prod_{j=l,​j\neq i}^r x-x_j$。考虑分治,有
 +
 +$$
 +\begin{equation}\begin{split} ​
 +f_{l,​r}(x)&​=\left(\sum_{i=l}^m \cfrac {y_i}{g'​(x_i)}\prod_{j=l,​j\neq i}^m x-x_j\right)\left(\prod_{j=m+1}^r x-x_j\right)+\left(\sum_{i=m+1}^r \cfrac {y_i}{g'​(x_i)}\prod_{j=m+1,​j\neq i}^r x-x_j\right)\left(\prod_{j=l}^m x-x_j\right)\\ ​
 +&​=f_{l,​m}(x)\left(\prod_{j=m+1}^r x-x_j\right)+f_{m+1,​r}(x)\left(\prod_{j=l}^m x-x_j\right)
 +\end{split}\end{equation}
 +$$
 +
 +于是可以 $O(n\log^2 n)$ 完成分治,总时间复杂度 $O(n\log^2 n)$。
 +
 +<hidden 查看代码>​
 +<code cpp>
 +const int MAXN=1e5+5,​MINL=640,​Mod=998244353;​
 +int quick_pow(int a,int b){
 + int ans=1;
 + while(b){
 + if(b&​1)
 + ans=1LL*ans*a%Mod;​
 + a=1LL*a*a%Mod;​
 + b>>​=1;​
 + }
 + return ans;
 +}
 +namespace Poly{
 + const int G=3;
 + int rev[MAXN<<​2],​Pool[MAXN<<​3],​*Wn[30];​
 + void init(){
 + int lg2=0,​*pos=Pool,​n,​w;​
 + while((1<<​lg2)<​MAXN*2)lg2++;​
 + n=1<<​lg2,​w=quick_pow(G,​(Mod-1)/​(1<<​lg2));​
 + while(~lg2){
 + Wn[lg2]=pos,​pos+=n;​
 + Wn[lg2][0]=1;​
 + _for(i,​1,​n)Wn[lg2][i]=1LL*Wn[lg2][i-1]*w%Mod;​
 + w=1LL*w*w%Mod;​
 + lg2--;​n>>​=1;​
 + }
 + }
 + int build(int k){
 + int n,pos=0;
 + while((1<<​pos)<​=k)pos++;​
 + n=1<<​pos;​
 + _for(i,​0,​n)rev[i]=(rev[i>>​1]>>​1)|((i&​1)<<​(pos-1));​
 + return n;
 + }
 + void NTT(int *f,int n,bool type){
 + _for(i,​0,​n)if(i<​rev[i])
 + swap(f[i],​f[rev[i]]);​
 + int t1,t2;
 + for(int i=1,​lg2=1;​i<​n;​i<<​=1,​lg2++){
 + for(int j=0;​j<​n;​j+=(i<<​1)){
 + _for(k,​j,​j+i){
 + t1=f[k],​t2=1LL*Wn[lg2][k-j]*f[k+i]%Mod;​
 + f[k]=(t1+t2)%Mod,​f[k+i]=(t1-t2)%Mod;​
 + }
 + }
 + }
 + if(!type){
 + reverse(f+1,​f+n);​
 + int div=quick_pow(n,​Mod-2);​
 + _for(i,​0,​n)f[i]=(1LL*f[i]*div%Mod+Mod)%Mod;​
 + }
 + }
 + void Mul(int *f,int _n,int *g,int _m,int xmod=0){
 + int n=build(_n+_m-2);​
 + _for(i,​_n,​n)f[i]=0;​_for(i,​_m,​n)g[i]=0;​
 + NTT(f,​n,​true);​NTT(g,​n,​true);​
 + _for(i,​0,​n)f[i]=1LL*f[i]*g[i]%Mod;​
 + NTT(f,​n,​false);​
 + if(xmod)_for(i,​xmod,​n)f[i]=0;​
 + }
 + void Mul2(const int *f,int _n,const int *g,int _m,int *h){
 + static int temp1[MAXN<<​2],​temp2[MAXN<<​2];​
 + int n=build(_n+_m-2);​
 + memcpy(temp1,​f,​sizeof(int)*_n);​memcpy(temp2,​g,​sizeof(int)*_m);​
 + _for(i,​_n,​n)temp1[i]=0;​_for(i,​_m,​n)temp2[i]=0;​
 + NTT(temp1,​n,​true);​NTT(temp2,​n,​true);​
 + _for(i,​0,​n)temp1[i]=1LL*temp1[i]*temp2[i]%Mod;​
 + NTT(temp1,​n,​false);​
 + n=_n+_m-1;​
 + _for(i,​0,​n)h[i]=temp1[i];​
 + }
 + void Mul3(int *f,int _n,int *g,int _m){
 + static int temp1[MAXN<<​2],​temp2[MAXN<<​2];​
 + int n=build(_n+_m-2);​
 + memcpy(temp1,​f,​sizeof(int)*_n);​memcpy(temp2,​g,​sizeof(int)*_m);​
 + _for(i,​_n,​n)temp1[i]=0;​_for(i,​_m,​n)temp2[i]=0;​
 + NTT(temp1,​n,​true);​NTT(temp2,​n,​true);​
 + _for(i,​0,​n)temp1[i]=1LL*temp1[i]*temp2[i]%Mod;​
 + NTT(temp1,​n,​false);​
 + n=_n+_m-1;​
 + _for(i,​0,​n)f[i]=temp1[i];​
 + }
 + void Inv(const int *f,int *g,int _n){
 + static int temp[MAXN<<​2];​
 + if(_n==1)return g[0]=quick_pow(f[0],​Mod-2),​void();​
 + Inv(f,​g,​(_n+1)>>​1);​
 + int n=build((_n-1)<<​1);​
 + _for(i,​(_n+1)>>​1,​n)g[i]=0;​
 + _for(i,​0,​_n)temp[i]=f[i];​_for(i,​_n,​n)temp[i]=0;​
 + NTT(g,​n,​true);​NTT(temp,​n,​true);​
 + _for(i,​0,​n)g[i]=(2-1LL*temp[i]*g[i]%Mod)*g[i]%Mod;​
 + NTT(g,​n,​false);​
 + _for(i,​_n,​n)g[i]=0;​
 + }
 + void Div(const int *f,int _n,const int *g,int _m,int *r){
 + static int temp1[MAXN<<​2],​temp2[MAXN<<​2];​
 + if(_n<​_m){
 + _rep(i,​0,​_n)r[i]=f[i];​
 + return;
 + }
 + _rep(i,​0,​_m)temp1[i]=g[_m-i];​
 + Inv(temp1,​temp2,​_n-_m+1);​
 + _rep(i,​0,​_n)temp1[i]=f[_n-i];​
 + Mul(temp2,​_n-_m+1,​temp1,​_n+1,​_n-_m+1);​
 + for(int i=0,​j=_n-_m;​i<​j;​i++,​j--)swap(temp2[i],​temp2[j]);​
 + int __m=min(_n-_m+1,​_m);​
 + _for(i,​0,​_m)temp1[i]=g[i];​
 + Mul(temp1,​_m,​temp2,​__m);​
 + _for(i,​0,​_m)r[i]=(f[i]+Mod-temp1[i])%Mod;​
 + }
 +}
 +int x[MAXN],​y[MAXN],​gy[MAXN],​pool[MAXN*50],​*pos=pool,​*t[MAXN<<​2];​
 +void build(int k,int L,int R){
 + t[k]=pos,​pos+=R-L+2;​
 + int M=L+R>>​1;​
 + if(L==R)return t[k][0]=Mod-x[M],​t[k][1]=1,​void();​
 + build(k<<​1,​L,​M);​build(k<<​1|1,​M+1,​R);​
 + Poly::​Mul2(t[k<<​1],​M-L+2,​t[k<<​1|1],​R-M+1,​t[k]);​
 +}
 +void query(int k,int L,int R,int *f){
 + if(R-L<​MINL){
 + _rep(i,​L,​R){
 + gy[i]=0;
 + for(int j=R-L;​~j;​j--)
 + gy[i]=(1LL*gy[i]*x[i]+f[j])%Mod;​
 + }
 + return;
 + }
 + int *temp=pos,​M=L+R>>​1;​pos+=R-L+1;​
 + Poly::​Div(f,​R-L,​t[k<<​1],​M-L+1,​temp);​
 + query(k<<​1,​L,​M,​temp);​
 + Poly::​Div(f,​R-L,​t[k<<​1|1],​R-M,​temp);​
 + query(k<<​1|1,​M+1,​R,​temp);​
 +}
 +void query2(int k,int L,int R,int *f){
 + int M=L+R>>​1;​
 + if(L==R)return f[0]=1LL*y[M]*quick_pow(gy[M],​Mod-2)%Mod,​void();​
 + int *temp=pos;​pos+=R-L+1;​
 + query2(k<<​1,​L,​M,​f);​query2(k<<​1|1,​M+1,​R,​temp);​
 + Poly::​Mul3(f,​M-L+1,​t[k<<​1|1],​R-M+1);​Poly::​Mul3(temp,​R-M,​t[k<<​1],​M-L+2);​
 + int n=R-L+1;
 + _for(i,​0,​n)f[i]=(f[i]+temp[i])%Mod;​
 +}
 +int main()
 +{
 + Poly::​init();​
 + int n=read_int();​
 + _for(i,​0,​n)x[i]=read_int(),​y[i]=read_int();​
 + build(1,​0,​n-1);​
 + int *g=pos;​pos+=n;​
 + memcpy(g,​t[1],​sizeof(int)*(n+1));​
 + _for(i,​0,​n)g[i]=1LL*g[i+1]*(i+1)%Mod;​
 + query(1,​0,​n-1,​g);​
 + int *f=pos;​pos+=n;​
 + query2(1,​0,​n-1,​f);​
 + _for(i,​0,​n)space(f[i]);​
 + return 0;
 +}
 +</​code>​
 +</​hidden>​
2020-2021/teams/legal_string/jxm2001/多项式_4.1598330660.txt.gz · 最后更改: 2020/08/25 12:44 由 jxm2001