浅谈多项式全家桶与实现

  • 2026/2/12 upd:重写本文,将每个函数单独开在一个 namespace 里,添加了多项式复合逆
  • 2021/5/8 upd:修补了一些锅,增加了大量技术,多项式全家桶基本完成

封装模式和代码习惯

本文与(我发布的)其他介绍知识点的博客不同,更侧重于具体实现和封装细节,也是提供一种多项式模板的封装实现方式。有关更多多项式和生成函数的技巧请参考 Re:从零开始的生成函数 by me

本文模板的封装较为严密和安全,因此需要牺牲轻微的常数以及灵活性,但使用更加方便。请根据自己的需要进行修改和使用。具体代码封装模式为:

  • 每个模板开一个独立的 namespace,内含一个主函数形如 void solve(int *s, int *input, int arg),其中 s 是输出数组,input 是输入数组,arg 是一些额外的参数(如多项式次数等),因具体问题而异。
  • 传入的数组指针均可以相同,solve 函数会保证不破坏 input 数组的内容,并且不访问超出传入次数的数组元素。
  • namespace 内的大写字母 A B C 等数组表示的是临时数组。

0. 前置知识

[Under Construction]

1. 多项式复合简单函数

一些常见的多项式复合简单函数,可以认为是多项式工业内的“四则运算”。复杂度均为 \(\Theta(n \log n)\)

顺便一提,多项式复合任意函数已经可以做到 \(\Theta(n \log^2 n)\)

1.1 NTT

最基础的多项式操作,其它运算都以此为基础,正因此这个模块对常数的影响很大。这里的实现比较基础,没有用到一些优化技巧,若对常数要求较高可以替换为其他实现。

const int N=3e6+5,mod=998244353,g=3; 
int ksm(int a,int x){
    int tot=1;
    while(x){
        if(x & 1ll) tot=1ll*tot*a%mod;
        a=1ll*a*a%mod;
        x>>=1ll;
    }
    return tot;
}
void gmodn(int &x){ x+=x>>31 & mod; }
void gmod(int &x) { x%=mod; }

namespace NTT{
    int A[N],B[N],C[N],rev[N];
    int init(int n){
		int lim=0;
		while((1<<lim)<=n) lim++;
		for(int i=0;i<=(1<<lim)-1;i++)
			rev[i]=(rev[i>>1] >> 1) | ((i & 1)<<(lim-1));
		return lim;
	}
	void ntt(int *f,int n,int opt){
		for(int i=0;i<=n-1;i++)
			if(i<rev[i]) swap(f[i],f[rev[i]]);
		for(int l=1;l<n;l<<=1ll){
			int tmp=ksm(g,(mod-1)/(l*2));
			if(opt==-1) tmp=ksm(tmp,mod-2);
			for(int i=0;i<=n-1;i+=l<<1ll){
				int omegf=1;
				for(int j=0;j<l;j++){
					int x=f[i+j],y=1ll*omegf*f[i+j+l]%mod;
					gmodn(f[i+j]=x+y-mod);
                    gmodn(f[i+j+l]=x-y);
					omegf=1ll*omegf*tmp%mod;
				}
			}
		}
		if(opt==-1){
			int t=ksm(n,mod-2);
			for(int i=0;i<=n-1;i++) f[i]=1ll*f[i]*t%mod;
		}
	}
    void solve(int *s,int* f,int* g,int n,int m){
        if(n+m<=150){
            for(int i=0;i<=n+m;i++) C[i]=0;
            for(int i=0;i<=n;i++)
                for(int j=0;j<=m;j++)
                    gmodn(C[i+j]+=1ll*f[i]*g[j]%mod-mod);
            for(int i=0;i<=n+m;i++) s[i]=C[i];
            return ;
        }
        int lim=init(n+m);
        for(int i=0;i<(1ll<<lim);i++) A[i]=B[i]=0;
        for(int i=0;i<=n;i++) A[i]=f[i];
        for(int i=0;i<=m;i++) B[i]=g[i];
        ntt(A,(1<<lim),1);
        ntt(B,(1<<lim),1);
        for(int i=0;i<(1<<lim);i++) C[i]=1ll*A[i]*B[i]%mod;
        ntt(C,(1<<lim),-1);
        for(int i=0;i<=n+m;i++) s[i]=C[i];
    }
}

1.1A 任意模数 NTT

三模 NTT

用三个模数 \(998244353\) / \(1004535809\) / \(469762049\) (原根都是 \(3\))分别做一遍再 CRT 合并起来即可。常数较大。

namespace EXCRT{
	int solve(int x1,int x2,int x3,int mod){
		int k1=(x2+mod2-x1)%mod2*ksm(mod1,mod2-2,mod2)%mod2;
		int x4=x1+k1*mod1;
		int k4=(x3+mod3-x4%mod3)%mod3*ksm(mod1*mod2%mod3,mod3-2,mod3)%mod3;
		return (x4%mod+k4%mod*mod1%mod*mod2%mod)%mod;
	}
}

int st1[N],st2[N],st3[N];
void MTT(int *s,int *a,int *b,int n,int m,int p){
	NTT::solve(st1,a,b,n,m,mod1);
	NTT::solve(st2,a,b,n,m,mod2);
	NTT::solve(st3,a,b,n,m,mod3);
	for(int i=0;i<=n+m;i++){
		s[i]=EXCRT::solve(st1[i],st2[i],st3[i],p);
	}
}

FFT

直接进行 FFT 会爆精度。这里实现的是拆系数 5 次 FFT 的做法,具体推导可见 题解 P4245 【【模板】任意模数NTT】 by command_block

const double PI=acos(-1);
int mod;

struct cpl{
	double A,B;
};
inline cpl operator +(cpl X,cpl Y){
	return (cpl){X.A+Y.A,X.B+Y.B};
}
inline cpl operator -(cpl X,cpl Y){
	return (cpl){X.A-Y.A,X.B-Y.B};
}
inline cpl operator *(cpl X,cpl Y){
	return (cpl){X.A*Y.A-X.B*Y.B,X.A*Y.B+X.B*Y.A};
}

namespace FFT{
	cpl A[N],B[N],C[N],S1[N],S2[N],pre[N];
	int S[N];
	int rev[N];
	int init(int n,int m){
		int lim=0;
		while((1ll<<lim)<=n+m) lim++;
		for(int i=0;i<(1<<lim);i++)
			rev[i]=(rev[i>>1ll]>>1ll) | ((i & 1ll)<<(lim-1));
		for(int i=0;i<=(1<<lim);i++)
			pre[i]={cos(2*PI*i/(1<<lim)),sin(2*PI*i/(1<<lim))};
		return lim;
	}
	void fft(cpl *a,int n,int opt){
		for(int i=0;i<n;i++)
			if(i<rev[i]) swap(a[i],a[rev[i]]);
		for(int l=1;l<n;l*=2){
			for(int i=0;i<n;i+=l*2){
				int u=n/l/2,nw=(opt==1)?0:n;
				for(int j=0;j<l;j++){
					cpl x=a[i+j],y=pre[nw]*a[i+j+l];
					a[i+j]=x+y,a[i+j+l]=x-y;
					nw+=opt*u;
				}
			}
		}
		if(opt==-1){
			for(int i=0;i<n;i++) a[i].A=a[i].A/n,a[i].B=a[i].B/n;
		}
	}
	void solve(int *s,int *f,int *g,int n,int m,int mod){
		if(n+m<=200){
			for(int i=0;i<=n+m;i++) S[i]=0;
			for(int i=0;i<=n;i++){
				for(int j=0;j<=m;j++){
					S[i+j]=(S[i+j]+1ll*f[i]*g[j]%mod)%mod;
				}
			}
			for(int i=0;i<=n+m;i++) s[i]=S[i];
			return ;
		}
		int M=sqrt(mod);
		int lim=init(n,m);
		for(int i=0;i<=n;i++) A[i]=(cpl){f[i]%M,f[i]/M},B[i]=(cpl){f[i]%M,-f[i]/M};
		for(int i=0;i<=m;i++) C[i]=(cpl){g[i]%M,g[i]/M};
		for(int i=n+1;i<(1ll<<lim);i++) A[i]=B[i]=(cpl){0,0};
		for(int i=m+1;i<(1ll<<lim);i++) C[i]=(cpl){0,0};
		fft(A,(1<<lim),1);
		fft(B,(1<<lim),1);
		fft(C,(1<<lim),1);
		for(int i=0;i<(1<<lim);i++) S1[i]=A[i]*C[i],S2[i]=B[i]*C[i];
		fft(S1,(1<<lim),-1);
		fft(S2,(1<<lim),-1);
		for(int i=0;i<=n+m;i++){
			long long a=round((S1[i].A+S2[i].A)/2),b=round((S2[i].A-S1[i].A)/2),c=round(S1[i].B);
			s[i]=(a%mod+b%mod*M%mod*M%mod+c%mod*M%mod)%mod;
		}
	}
}

1.2 多项式求逆

  • 给出 \(F(x)\) ,求出 \(G(x)\) 使得 \(F(x)G(x) \equiv 1 \pmod {x^n}\)

\[F(x) \equiv \dfrac{1}{G(x)} \pmod {x^n} \]

\[F(x) - \dfrac{1}{G(x)}\equiv 0 \pmod {x^n} \]

\(H(t)=F(x)- \dfrac{1}{t}\) ,则有 \(H(G(x)) \equiv 0 \pmod{x^n}\)

用牛顿迭代求 \(H(G(x))\) 的零点。假设已经求出了 \(H(G_0(x)) \equiv 0 \pmod{x^\frac{n}{2}}\)

\[G(x) = G_0(x) - \dfrac{H(G_0(x))}{H'(G_0(x))} \]

\[G(x) = G_0(x) - \dfrac{F(x)-\frac{1}{G_0(x)}}{\frac{1}{G_0(x)^2}} \]

\[G(x) = 2G_0(x) - G_0(x)^2F(x) \]

递归边界为 \(G_0(x) = \dfrac{1}{f_0}\)

\(O(n \log n)\)

namespace INV{
	int A[N],B[N],S[N];
	void solve(int *s,int *f,int n){
		S[0]=ksm(f[0],mod-2);
		S[1]=0;
		for(int len=2;len<=(n<<1);len<<=1){
			int lim=len<<1;
			for(int i=0;i<lim;i++) A[i]=B[i]=0;
			for(int i=0;i<=min(len-1,n);i++) A[i]=f[i]; // 避免越界访问
			for(int i=0;i<len;i++) B[i]=S[i];
            
			int t=NTT::init(len);
			NTT::ntt(A,lim);
			NTT::ntt(B,lim);
			for(int j=0;j<lim;j++)
				S[j]=(2*B[j]%mod+mod-1ll*A[j]*B[j]%mod*B[j]%mod)%mod;
			NTT::intt(S,lim);
			for(int j=len;j<lim;j++) S[j]=0;
		}
		for(int i=0;i<=n;i++) s[i]=S[i];
	}
}

1.3 多项式对数函数(ln)

  • 给出 \(F(x)\) ,求 \(G(x)=\ln(F(x)) \pmod{x^n}\)。保证 \(f_0=1\)

对两边求导:

\[G'(x)=\dfrac{F'(x)}{F(x)} \]

\[G(x)=\int \dfrac{F'(x)}{F(x)} \]

求导,求逆,再积分即可。

namespace LN{
	int A[N],B[N];
	void solve(int *s,int *f,int n){
		for(int i=0;i<=n;i++) A[i]=f[i],B[i]=0;
		for(int i=1;i<=n;i++) B[i-1]=1ll*f[i]*i%mod;

		INV::solve(A,A,n);
		NTT::solve(A,A,B,n,n);

		for(int i=n-1;i>=0;i--) A[i+1]=1ll*A[i]*ksm(i+1,mod-2)%mod;
		A[0]=0;

		for(int i=0;i<=n;i++) s[i]=A[i];
	}
}

1.4 多项式指数函数(exp)

  • 给出 \(F(x)\) ,求出 \(G(x)\) 使得 \(G(x) \equiv e^{F(x)} \pmod {x^{n}}\)。保证 \(f_0=0\)

\[F(x) \equiv \ln(G(x)) \pmod {x^n} \]

\[F(x) - \ln(G(x))\equiv 0 \pmod {x^n} \]

\(H(t)=F(x)- ln(t)\) ,则有 \(H(G(x)) \equiv 0 \pmod{x^n}\) 。上牛顿迭代,假设已经求出了 \(H(G_0(x)) \equiv 0 \pmod{x^\frac{n}{2}}\)

\[G(x) = G_0(x) - \dfrac{H(G_0(x))}{H'(G_0(x))} \]

\[G(x) = G_0(x) - \dfrac{F(x)-\ln(G_0(x))}{-G_0(x)^{-1}} \]

\[G(x) = G_0(x)[1+F(x)-\ln(G_0(x))] \]

namespace EXP{
	int A[N],B[N],C[N],S[N];
	void solve(int *s,int *f,int n){
		A[0]=B[0]=C[0]=0; S[0]=1;
		for(int len=2;len/2<=n;len<<=1){
			int lim=len<<1;
			for(int i=0;i<lim;i++) A[i]=B[i]=0;
			for(int i=0;i<=min(len-1,n);i++) A[i]=f[i];
			for(int i=0;i<len;i++) B[i]=S[i];
			
			LN::solve(C,B,len-1);
			for(int i=0;i<len;i++) gmodn(C[i]=A[i]-C[i]);
			C[0]=(C[0]+1)%mod;
			NTT::init(lim-1);
			NTT::ntt(B,lim);
			NTT::ntt(C,lim);
			for(int j=0;j<lim;j++) S[j]=1ll*B[j]*C[j]%mod;
			
			NTT::intt(S,lim);
			for(int j=len;j<lim;j++) S[j]=0;
		}
		for(int i=0;i<=n;i++) s[i]=S[i];
	}
}

1.5 多项式开根

  • 给出多项式 \(F(x)\) ,求 \(G(x)\) 使 \(G(x)^2 \equiv F(x) \pmod{x^{n}}\)

\[F(x) - G(x)^2\equiv 0 \pmod {x^n} \]

\(H(t)=F(x)- t^2\) ,则有 \(H(G(x)) \equiv 0 \pmod{x^n}\)

上牛顿迭代,假设已经求出了 \(H(G_0(x)) \equiv 0 \pmod{x^\frac{n}{2}}\)

\[G(x) = G_0(x) - \dfrac{H(G_0(x))}{H'(G_0(x))} \]

\[G(x) = G_0(x) - \dfrac{F(x)-G_0(x)^2}{-2G_0(x)} \]

\[G(x) = G_0(x)+\dfrac{F(x)}{2G_0(x)}-\dfrac{1}{2} \]

\[G(x) = \dfrac{2G_0(x)+\dfrac{F(x)}{G_0(x)}-1}{2} \]

注意:边界条件对 \(f_0\) 开根可能得到两个结果,按实际情况选择即可。

namespace SQRT{
	int A[N],B[N],C[N],S[N];
	void solve(int *s,int *f,int n){
		int p=BSGS(3,998244353,f[0]),s0=ksm(3,p/2);
		S[0]=min(s0,(mod-s0)%mod);
		S[1]=0;
		
		for(int len=2;len/2<=n;len<<=1){
			int lim=len<<1;
			for(int i=0;i<lim;i++) A[i]=B[i]=C[i]=0;
			for(int i=0;i<=min(len-1,n);i++) A[i]=f[i];
			for(int i=0;i<len;i++) B[i]=S[i];
			
			INV::solve(C,B,len-1);
			NTT::init(lim-1);
			NTT::ntt(A,lim,1);
			NTT::ntt(C,lim,1);
			for(int j=0;j<lim;j++) S[j]=A[j]*C[j]%mod;
			NTT::ntt(S,lim,-1);
			
			for(int i=0;i<len;i++) S[i]=(S[i]+B[i])%mod*inv2%mod;
			for(int j=len;j<lim;j++) S[j]=0;
		}
		for(int i=0;i<=n;i++) s[i]=S[i];
	}
}

1.6 多项式除法(取模)

  • 给出 \(n\) 次多项式 \(F(x)\)\(m\) 次多项式 \(G(x)\) ,求 \(n-m\) 次多项式 \(Q(x)\) 和不超过 \(m-1\) 次多项式 \(R(x)\) 使得 \(F(x)=Q(x)G(x)+R(x)\)

考虑将系数对称:

\[F_R(x)=\sum\limits_{i=0}^n [x^{n-i}]F(x) x^i=x^nF(x^{-1}) \]

\[F(x^{-1})=Q(x^{-1})G(x^{-1})+R(x^{-1}) \]

\[x^nF(x^{-1})=x^nQ(x^{-1})G(x^{-1})+x^nR(x^{-1}) \]

\[x^nF(x^{-1})=x^{n-m}Q(x^{-1})x^mG(x^{-1})+x^{n-m+1}x^{m-1}R(x^{-1}) \]

\[F_R(x)=Q_R(x)G_R(x)+x^{n-m+1}R_R(x) \]

考虑先求出 \(Q(x)\) ,模上 \(x^{n-m+1}\)

\[F_R(x) \equiv Q_R(x)G_R(x) \pmod{x^{n-m+1}} \]

\[Q_R(x) \equiv \dfrac{F_R(x)}{G_R(x)} \pmod{x^{n-m+1}} \]

求出 \(Q(x)\)\(R(x)\) 也很简单了:

\[R(x)=F(x)-Q(x)G(x) \]

namespace DIV{
	int A[N],B[N],C[N],D[N];
	void solve(int *q,int* r,int *f,int *g,int n,int m){
		for(int i=0;i<=n;i++) A[i]=C[i]=f[i];
		for(int i=0;i<=m;i++) B[i]=D[i]=g[i];

		reverse(A,A+n+1);
		reverse(B,B+m+1);

		INV::solve(B,B,n-m);
		NTT::solve(q,A,B,n,n-m);

		for(int i=n-m+1;i<=n+n-m;i++) q[i]=0;
		reverse(q,q+n-m+1);

		NTT::solve(D,D,q,m,n-m);
		for(int i=0;i<=n;i++) r[i]=(C[i]+mod-D[i])%mod;
	}
}

1.7 多项式快速幂

  • 给出多项式 \(F(x)\) ,求 \(F(x)^k \pmod{x^n}\)

\(f_0=1\) 时,可以直接取对数,

\[G(x)=F(x)^k \]

\[\ln(G(x))=k\ln(F(x)) \]

\[G(x)=e^{k\ln(F(x))} \]

这里的 \(k\) 可以对 \(\bmod -1\) 取模。

\(O(n\log n)\)

void Pow(int *s,int *f,int n,int k){
	int lim=NTT::init(n,n);
	for(int i=n+1;i<(1ll<<lim);i++) A[i]=0;
	for(int i=0;i<=n;i++) A[i]=f[i];
	
	Ln(A,A,n);
	Mult(A,A,n,k);
	Exp(A,A,n);
	for(int i=0;i<=n;i++) s[i]=A[i];
}

不保证 \(f_0=1\) 时会有一些 Corner Case。\(f_0 \neq 0\) 时,直接除掉即可;\(f_0 = 0\) 时则找到第一个不为 \(0\) 的系数(记为第 \(t\) 项),然后除掉 \(x^t\) 即可。记 \(p=[x^t]f(x)\)

\[F(x)^k=\dfrac{F(x)}{px^t}^k p^k x^{tk} \]

注意当 \(tk>n\) 时需要特判。

void Pow(int *s,int *f,int n,int k1,int k2,int lenk){
	//k1 是对 mod 取模, k2 是对 mod-1 取模,lenk 是 k 的长度
	static int A[N];
	int lim=NTT::init(n,n);
	
	int t=0;
	while(!f[t]) t++;
	if(lenk>log(n) && t){
		for(int i=0;i<=n;i++) s[i]=0;
		return ;
	}
	
	int p=ksm(f[t],k2),q=ksm(f[t],mod-2);
	n-=t;
	for(int i=0;i<=n;i++) A[i]=f[i+t]*q%mod;
	for(int i=n+1;i<(1ll<<lim);i++) A[i]=0;
	
	Ln(A,A,n);
	Mult(A,A,n,k1);
	Exp(A,A,n);
	for(int i=0;i<=n+t;i++) s[i]=0;
	for(int i=0;i+t*k1<=n+t;i++)
	  s[i+t*k1]=A[i]*p%mod;
}

1.8 多项式三角函数

@warn 该模板不常用

@todo 更新代码为新模板

  • 给出多项式 \(F(x)\) ,求 \(sin(x)\)\(cos(x)\)\(tan(x)\)。保证 \(f_0=0\)

考虑复数的指数形式(欧拉公式):

\[e^{ix}=cosx+isin(x) \]

\[e^{-ix}=cosx-isin(x) \]

\[sin(x)=\dfrac{e^{ix}-e^{-ix}}{2} \]

\[cos(x)=\dfrac{e^{ix}+e^{-ix}}{2} \]

\[tan(x)=\dfrac{sin(x)}{cos(x)}=\dfrac{e^{ix}-e^{-ix}}{e^{ix}+e^{-ix}} \]

void Sin(int *s,int *f,int n){
	int I=mod-ksm(g,(mod-1)/4);
	static int A[N],B[N];
	for(int i=0;i<=n;i++) A[i]=f[i]*I%mod;
	poly::Exp(A,A,n);
	INV::solve(B,A,n);
	int t=ksm(2*I%mod,mod-2);
	for(int i=0;i<=n;i++) s[i]=(A[i]+mod-B[i])%mod*t%mod;
}
void Cos(int *s,int *f,int n){
	int I=mod-ksm(g,(mod-1)/4);
	static int A[N],B[N];
	for(int i=0;i<=n;i++) A[i]=f[i]*I%mod;
	poly::Exp(A,A,n);
	INV::solve(B,A,n);
	int t=inv2;
	for(int i=0;i<=n;i++) s[i]=(A[i]+B[i])%mod*t%mod;
}

1.9 多项式反三角函数

@warn 该模板不常用

@todo 更新代码为新模板

  • 给出多项式 \(F(x)\) ,求 \(\arcsin(x)\)\(\arccos(x)\)\(\arctan(x)\)。保证 \(f_0=0\)

直接求导:

\[\arcsin(x)'=\dfrac{1}{\sqrt{1-x^2}} \]

\[\arccos(x)'=-\dfrac{1}{\sqrt{1-x^2}} \]

\[\arctan(x)'=\dfrac{1}{1+x^2} \]

\[\text{arccot}(x)'=-\dfrac{1}{1+x^2} \]

于是:

\[\arcsin(F(x))'=\dfrac{1}{\sqrt{1-F(x)^2}}F'(x) \]

\[\arcsin(F(x))=\int \dfrac{1}{\sqrt{1-F(x)^2}}F'(x) \]

\[\arccos(F(x))=- \int \dfrac{1}{\sqrt{1-F(x)^2}}F'(x) \]

\[\arctan(F(x))=\int \dfrac{1}{1+F(x)^2}F'(x) \]

\[\text{arccot}(F(x))=- \int \dfrac{1}{1+F(x)^2}F'(x) \]

void Arcsin(int *s,int *f,int n){
	static int A[N],B[N];
	for(int i=0;i<=n;i++) A[i]=f[i],B[i]=f[i];
	NTT::solve(A,A,A,n,n);
	for(int i=0;i<=n;i++) A[i]=(mod-A[i])%mod;
	A[0]=(A[0]+1)%mod;
	poly::Sqrt(A,A,n);
	INV::solve(A,A,n);
	poly::Deriv(B,B,n);
	NTT::solve(A,A,B,n,n);
	poly::Limit(A,A,n); 
	for(int i=0;i<=n;i++) s[i]=A[i];
}
void Arctan(int *s,int *f,int n){
	static int A[N],B[N];
	for(int i=0;i<=n;i++) A[i]=f[i],B[i]=f[i];
	NTT::solve(A,A,A,n,n);
	A[0]=(A[0]+1)%mod;
	INV::solve(A,A,n);
	poly::Deriv(B,B,n);
	NTT::solve(A,A,B,n,n);
	poly::Limit(A,A,n); 
	for(int i=0;i<=n;i++) s[i]=A[i];
}

2. 复杂问题

本章收录一些目前只能做到 \(\Theta(n \log^2 n)\) 的问题。

2.1 多项式多点求值

  • 给出多项式 \(F(x)\) 并给定 \(m\) 处位置 \(a_1,a_2,\cdots,a_m\),对每个给出的位置 \(a_i\) 求点值 \(F(a_i)\)

算法一

\(F(x)=Q(x)(x-x_0)+R(x)\),则 \(F(x_0)=R(x_0)\)。所以我们只要对每个 \(x_i\) 求出取模后的余式 \(R(x)\) 即可。

考虑分治,先分治乘处理出 \(\prod\limits_{i=l}^r (x-x_i)\)。若当前获得了 \(F(x)=Q(x) \prod\limits_{i=l}^r (x-x_i) +R(x)\),那么分治下去只要对两边分别取模就可以了,显然次数会减半。


算法二

多项式除法的常数很大,我们希望用其他方式替代。考虑多项式除法的本质:

\[Q_R(x) \equiv \dfrac{F_R(x)}{G_R(x)} \pmod{x^{n-m+1}} \]

\[R(x)=F(x)-Q(x)G(x) \]

这里 \(Q(x)\) 是一次的,\(R(x)\) 是零次的,所以只要求出 \(Q(x)\) 的常数项即可得到答案。考虑分治:

\[Q_R(x)=F_R(x)G_R^{-1}(x) \]

\[Q_{R_0}(x)=F_R(x)G_{R_0}^{-1}(x) \]

试着推一下 \(G_{R_0}^{-1}(x)\)\(G_R^{-1}(x)\) 的关系:

\[G(x)=G_0(x)G_1(x) \]

\[x^{r-l+1}G(\dfrac{1}{x})=x^{mid-l+1}G_0(\dfrac{1}{x})x^{r-mid}G_1(\dfrac{1}{x}) \]

\[G_R(x)=G_{R_0}(x)G_{R_1}(x) \]

\[G_{R_0}^{-1}(x)=G_R^{-1}(x)G_{R_1}(x) \]

于是:

\[Q_{R_0}(x)=F_R(x)G_{R_0}^{-1}(x)=F_R(x)G_R^{-1}(x)G_{R_1}(x)=Q_R(x)G_{R_1}(x) \]

\[Q_{R_1}(x)=Q_R(x)G_{R_0}(x) \]

所以算到底就相当于 \(F_R(x)\) 乘上若干个 \(G_{R}(x)\)

直接实现复杂度是错的,但注意到我们只需要 \(Q(x)\) 的常数项,即 \([x^{n-1}]Q_R(x)\)。所以乘到某一个区间时,只有最高的 \(r-l+1\) 项有用,因为之后乘的东西不超过 \(r-l+1\) 次。

只维护 \(Q_R(x)\) 的最高 \(r-l+1\) 项,时间复杂度就对了,\(\Theta(n \log^2 n)\)。同时这样每轮只需要做两次多项式乘法,常数很小。

namespace Evaluation{
	int tmp[N];
	vector <int> P[N],Q[N],Qn[N];
	int lenp[N],lenq[N];
	void init(int o,int l,int r){
		int len=r-l+1,mid=(l+r)>>1ll;
		lenp[o]=len;
		lenq[o]=len-1;
		P[o].resize(2*lenp[o]+2);
		Q[o].resize(2*lenq[o]+2);
		if(l==r) return ;
		init(o<<1,l,mid);
		init(o<<1|1,mid+1,r);
	}
	
	void solve1(int *a,int o,int l,int r){	
		if(l==r){
			P[o]={1,(mod-a[l])%mod};
			return ;
		}
		int mid=(l+r)>>1;
		solve1(a,o<<1,l,mid);
		solve1(a,o<<1|1,mid+1,r);
		NTT::solve(P[o].data(),P[o<<1].data(),P[o<<1|1].data(),lenp[o<<1],lenp[o<<1|1]);
	}
	
	void solve2(int *s,int *a,int o,int l,int r){
		if(l==r){
			s[l]=Q[o][0];
			return ;
		}
		int mid=(l+r)>>1;
		int len1=mid-l+1,len2=r-mid;
		
		NTT::solve(tmp,Q[o].data(),P[o<<1|1].data(),lenq[o],lenp[o<<1|1]);
		for(int i=0;i<len1;i++) Q[o<<1][i]=tmp[i+len2];
		
		NTT::solve(tmp,Q[o].data(),P[o<<1].data(),lenq[o],lenp[o<<1]);
		for(int i=0;i<len2;i++) Q[o<<1|1][i]=tmp[i+len1];
		
		solve2(s,a,o<<1,l,mid);
		solve2(s,a,o<<1|1,mid+1,r);
	}
    // f: [0,n]; y: [1,m]
	void solve(int *s,int *f,int *a,int n,int m){
		m=max(n,m); n=max(n,m);
		init(1,1,m);
		solve1(a,1,1,m);
		poly::Rev(f,f,n);
		
		INV::solve(P[1].data(),P[1].data(),lenp[1]);
		
		NTT::solve(tmp,f,P[1].data(),n,lenp[1]);
		for(int i=0;i<=m;i++) Q[1][i]=tmp[i];
		
		solve2(s,a,1,1,m);
		for(int i=1;i<=m;i++){
			s[i]=(f[n]+1ll*s[i]*a[i]%mod)%mod;
		}
	}
}

2.2 多项式快速插值

  • 给出 \(n+1\) 处点值 ,求同时满足这些点值的 \(n\) 次多项式 \(F(x)\)

直接使用拉格朗日插值:

\[F(x)=\sum\limits_{i=1}^ny_i \prod\limits_{j \neq i} \dfrac{x-x_j}{x_i-x_j} \]

\[F(x)=\sum\limits_{i=1}^ny_i \dfrac{1}{\prod_{j \neq i} (x_i-x_j)} \prod\limits_{j \neq i} (x-x_j) \]

考虑算这个系数,设 \(G(x)= \prod\limits_{i=1}^n (x-x_i)\),那么这个系数就是 \(c_i =\Big[ \dfrac{G(x)}{x-x_i} \Big](x_i)\)。当然这个记号并不严谨,因为分子分母都是 \(0\),但我们可以用极限的方式来理解,使用洛必达法则:

\[c_i = \lim\limits_{x \to x_i} \dfrac{G(x)}{x-x_i} = G'(x_i) \]

同样考虑分治:

\[\begin{aligned} f_{l,r} & =\sum\limits_{i=l}^r \dfrac{y_i}{G'(x_i)} \prod\limits_{j=l,i \neq j}^r (x-x_j)\\ & =[\prod\limits_{j=mid+1}^r (x-x_j)][\sum\limits_{i=l}^{mid} \dfrac{y_i}{G'(x_i)} \prod\limits_{j=l,i \neq j}^{mid} (x-x_j)]+[\prod\limits_{j=mid+1}^r (x-x_j)][\sum\limits_{i=mid+1}^r \dfrac{y_i}{G'(x_i)} \prod\limits_{j=l,i \neq j}^mid (x-x_j)]\\ & =f_{l,mid} \prod\limits_{j=mid+1}^r (x-x_j) + f_{mid+1,r} \prod\limits_{j=l}^{mid} (x-x_j)\\ \end{aligned} \]

多项式快速插值问题同样只能做到 \(\Theta(n\log^2n)\)

@remark 多项式快速插值有常数更小的利用转置原理的做法。

namespace Interpolation{
	int tmp[N];
	vector <int> P[N],Q[N];
	int lenp[N],lenq[N];
	int g[N],gv[N];
	void init(int o,int l,int r){
		int len=r-l+1,mid=(l+r)>>1ll;
		lenp[o]=len;
		lenq[o]=len-1;
		P[o].resize(2*lenp[o]+2);
		Q[o].resize(2*lenq[o]+2);
		if(l==r) return ;
		init(o<<1,l,mid);
		init(o<<1|1,mid+1,r);
	}
	
	void solve1(int *x,int o,int l,int r){	
		if(l==r){
			P[o]={(mod-x[l])%mod,1};
			return ;
		}
		int mid=(l+r)>>1;
		solve1(x,o<<1,l,mid);
		solve1(x,o<<1|1,mid+1,r);
		NTT::solve(P[o].data(),P[o<<1].data(),P[o<<1|1].data(),lenp[o<<1],lenp[o<<1|1]);
	}
	
	void solve2(int *x,int *y,int o,int l,int r){
		if(l==r){
			Q[o]={1ll*y[l]*ksm(gv[l],mod-2)%mod};
			return ;
		}
		int mid=(l+r)>>1;
		solve2(x,y,o<<1,l,mid);
		solve2(x,y,o<<1|1,mid+1,r);
		int tmp[N];
		NTT::solve(Q[o].data(),Q[o<<1].data(),P[o<<1|1].data(),lenq[o<<1],lenp[o<<1|1]);
		NTT::solve(tmp,Q[o<<1|1].data(),P[o<<1].data(),lenq[o<<1|1],lenp[o<<1]);
		for(int i=0;i<=lenq[o];i++) Q[o][i]=(Q[o][i]+tmp[i])%mod;
	}
    
    //x: [1,n]; y: [1,n]
	void solve(int *s,int *x,int *y,int n){
		init(1,1,n);
		solve1(x,1,1,n);
		poly::Deriv(g,P[1].data(),lenp[1]);
		Evaluation::solve(gv,g,x,lenp[1]-1,n);
		solve2(x,y,1,1,n);
		for(int i=0;i<=n;i++) s[i]=Q[1][i];
	}
}

2.3 多项式复合逆

  • 给出多项式 \(F(x)\),求 多项式 \(G(x)\) 使得 \(G(F(x)) \equiv x \pmod{x^n}\)。保证 \(f_0=0\)\(f_1 \neq 0\)

@TODO 推导

namespace N_COEF_PROBLEM{
	// MAX: 6n+O(1)
	// Solve S_k = [x^n] f^k
	// Require f_0 = 0
	
	int A[N],B[N],C[N],D[N],P[N];
	void solve(int *s,int *f,int n){
		int sn=n;
		
		int d=1; // deg of y
		for(int i=0;i<=n*3+2;i++) A[i]=B[i]=0;
		A[0]=B[0]=1; // A = 1
		for(int i=0;i<=n;i++) B[i*3+1]=(mod-f[i])%mod; // B = 1-yF
		
		while(n){
			int siz=n*(d*2+1)+d*2;
			for(int i=0;i<=siz;i++) P[i]=0;
			for(int i=0;i<=n;i++){
				for(int j=0;j<=d;j++){
					int id=i*(d*2+1)+j;
					P[id]=1ll*B[id]*ID(i)%mod;
				}
			}
			
			NTT::solve(C,A,P,siz,siz);
			NTT::solve(D,B,P,siz,siz);
			
			int nn=n/2,nd=d*2,nsiz=n*(d*2+1)+d*2;
			for(int i=0;i<=nsiz;i++) A[i]=B[i]=0;
			
			for(int i=(n & 1);i<=nn*2+1;i+=2){
				for(int j=0;j<=d*2;j++){
					int id=i*(d*2+1)+j;
					int nid=(i/2)*(nd*2+1)+j;
					A[nid]=C[id];
				}
			}
			for(int i=0;i<=nn*2;i+=2){
				for(int j=0;j<=d*2;j++){
					int id=i*(d*2+1)+j;
					int nid=(i/2)*(nd*2+1)+j;
					B[nid]=D[id];
				}
			}
			
			n=nn,d=nd;
		}
		
		n=sn;
		INV::solve(P,B,n);
		NTT::solve(A,A,P,n,n);
		for(int i=0;i<=n;i++) s[i]=A[i];
	}
}

namespace COMP_INV{
	// MAX: 6n+O(1)
	
	int A[N],B[N];
	void solve(int *s,int *f,int n){
		int kw=ksm(f[1],mod-2);
		N_COEF_PROBLEM::solve(A,f,n);
		int iw=ksm(A[n],mod-2);
		for(int k=0;k<=n;k++) B[n-k]=1ll*A[k]*n%mod*ksm(k,mod-2)%mod*iw%mod;
		
		LN::solve(B,B,n);
		int val=(mod-ksm(n,mod-2))%mod; // B^{-1/n}
		for(int i=0;i<=n;i++) B[i]=1ll*B[i]*val%mod;
		EXP::solve(B,B,n);
		
		s[0]=0;
		for(int i=1;i<=n;i++) s[i]=1ll*B[i-1]*kw%mod;
	}
}

3. 分块多项式

3.1 快速阶乘算法

  • 给出 \(n\),求 \(n! \pmod{p}\)

\(B = \lfloor \sqrt n \rfloor\)

\[F_d(x) = \prod\limits_{i=1}^d (x+i) \]

考虑维护 \(F_d(0), F_d(B), \cdots, F_d(dB)\),转移至 \(2d\)\(d+1\)

对于转移至 \(2d\)

\[F_{2d}(x) = F_d(x) F_d(x+d) \]

\(H(x) = F_d(x/B)\),那么求 \(F_d(x+d)\) 就是对 \(d+1\) 个连续点值偏移 \(d/B\),可以在 \(\Theta(d \log d)\) 内解决。

对于转移至 \(d+1\)

\[F_{d+1}(x) = F_d(x)(x+d+1) \]

\[F_{d+1}((d+1)B) = \prod_{i=1}^{d+1} [(d+1)B+i] \]

直接计算即可。总时间复杂度 \(\Theta(\sqrt{n} \log n)\)

namespace trans{
	int A[N],B[N],S[N];
	int tmul[N],tinv[N];
	void solve(int *s,int *f,int n,long long m){
		tmul[0]=tinv[0]=1;
		for(int i=1;i<=2*n;i++) tmul[i]=1ll*tmul[i-1]*(m+mod-n+i)%mod;
		tinv[2*n]=ksm(tmul[2*n],mod-2);
		for(int i=2*n-1;i>=1;i--) tinv[i]=1ll*tinv[i+1]*(m+mod-n+i+1)%mod;
		tinv[0]=ksm((m+mod-n)%mod,mod-2);
		for(int i=1;i<=2*n;i++) tinv[i]=1ll*tinv[i]*tmul[i-1]%mod;
		
		for(int i=0;i<=2*n;i++){
			A[i]=1ll*f[i]*inv[i]%mod*inv[n-i]%mod*ID(n-i)%mod;
			B[i]=tinv[i];
		}
		FFT::solve(S,A,B,n,2*n,mod);
		int tmp=1;
		for(int i=m;i>=m-n;i--) tmp=1ll*tmp*i%mod;
		for(int i=0;i<=n;i++){
			s[i]=1ll*S[i+n]*tmp%mod;
			tmp=1ll*tmp*(m+i+1)%mod*tinv[i]%mod;
		}
	}
}
int T,n,B;
int F[N],G[N];

int d;
void shift(){
	trans::solve(G,F,d,d+1);
	for(int i=0;i<=d;i++) F[i+d+1]=G[i];
	trans::solve(G,F,2*d,1ll*d*ksm(B,mod-2)%mod);
	for(int i=0;i<=2*d;i++) F[i]=1ll*F[i]*G[i]%mod;
	d*=2;
}
void setbit(){
	for(int i=0;i<=d;i++) F[i]=1ll*F[i]*(1ll*i*B%mod+d+1)%mod;
	F[d+1]=1;
	for(int i=1;i<=d+1;i++) F[d+1]=1ll*F[d+1]*(1ll*(d+1)*B%mod+i)%mod;
	d++;
}
int solve(int n,int mod){
	bool fl=0;
	if(n>mod/2) n=mod-1-n,fl=1;
	B=sqrt(n);
	int lim=log2(B);
	init(B);
	
	d=1; F[0]=1,F[1]=B+1;
	for(int i=lim-1;i>=0;i--){
		shift();
		if(B>>i & 1) setbit();
	}
	int ans=1;
	for(int i=0;i<d;i++) ans=1ll*ans*F[i]%mod;
	for(int i=B*B+1;i<=n;i++) ans=1ll*ans*i%mod;
	if(!fl) return ans;
	else return 1ll*ID(n+1)*ksm(ans,mod-2)%mod;
}

3.1A 带系数快速阶乘算法

  • 给出 \(n,a,b\),求 \(\prod\limits_{i=1}^{n} (a+bi) \pmod{p}\)
...

int d;
void shift(){
	trans::solve(G,F,d,d+1);
	for(int i=0;i<=d;i++) F[i+d+1]=G[i];
	trans::solve(G,F,2*d,1ll*d*ksm(B,mod-2)%mod);
	for(int i=0;i<=2*d;i++) F[i]=1ll*F[i]*G[i]%mod;
	d*=2;
}

void setbit(){
	for(int i=0;i<=d;i++) F[i]=1ll*F[i]*(1ll*a*B%mod*i%mod+1ll*a*(d+1)%mod+b)%mod;
	F[d+1]=1;
	for(int i=1;i<=d+1;i++) F[d+1]=1ll*F[d+1]*(1ll*a*B%mod*(d+1)%mod+1ll*a*i%mod+b)%mod;
	d++;
}
int solve(int n,int mod){
	B=sqrt(n);
	int lim=log2(B);
	init(B);
	
	d=1; F[0]=(a+b)%mod,F[1]=(1ll*a*B%mod+F[0])%mod;
	for(int i=lim-1;i>=0;i--){
		shift();
		if(B>>i & 1) setbit();
	}
	int ans=1;
	for(int i=0;i<d;i++) ans=1ll*ans*F[i]%mod;
	for(int i=B*B+1;i<=n;i++) ans=1ll*ans*(1ll*a*i%mod+b)%mod;
	return ans;
}

3.1B 快速阶乘和

  • 给出 \(n\),求 \(\sum\limits_{i=1}^{n} i! \pmod{p}\)

\[G_d(x) = \sum\limits_{t=1}^d \prod\limits_{i=1}^t (x+i) \]

对于转移至 \(2d\)

\[G_{2d}(x) = G_d(x) + F_d(x) G_d(x+d) \]

对于转移至 \(d+1\)

\[G_{d+1}(x) = G_d(x) + F_d(x)(x+d+1) \]

int B;
int F[N],G[N];
int P[N],Q[N];

int d;
void shift(){
	trans::solve(P,F,d,d+1);
	trans::solve(Q,G,d,d+1);
	
	for(int i=0;i<=d;i++) F[i+d+1]=P[i];
	for(int i=0;i<=d;i++) G[i+d+1]=Q[i];
	
	trans::solve(P,F,2*d,1ll*d*ksm(B,mod-2)%mod);
	trans::solve(Q,G,2*d,1ll*d*ksm(B,mod-2)%mod);
	
	for(int i=0;i<=2*d;i++){
		G[i]=(G[i]+1ll*F[i]*Q[i]%mod)%mod;
		F[i]=1ll*F[i]*P[i]%mod;
	}
	d*=2;
}

void setbit(){
	for(int i=0;i<=d;i++){
		F[i]=1ll*F[i]*(1ll*B*i%mod+(d+1))%mod;
		gmod(G[i]+=F[i]);
	}
	
	F[d+1]=1; G[d+1]=0;
	for(int i=1;i<=d+1;i++){
		F[d+1]=1ll*F[d+1]*(1ll*B*(d+1)%mod+i)%mod;
		gmod(G[d+1]+=F[d+1]);
	}
	d++;
}

int res0=0,res1=0;
void solve(int n){
	B=sqrt(n);
	int lim=__lg(B);
	init(B);
	
	d=1;
	F[0]=1,F[1]=(B+1)%mod;
	G[0]=1,G[1]=(B+1)%mod;
	for(int i=lim-1;i>=0;i--){
		shift();
		if(B>>i & 1) setbit();
	}
	int ans=0,tot=1;
	for(int i=0;i<d;i++){
		gmod(ans+=1ll*tot*G[i]%mod);
		tot=1ll*tot*F[i]%mod;
	}
	for(int i=B*B+1;i<=n;i++){
		tot=1ll*tot*i%mod;
		gmod(ans+=tot);
	}
	res0=tot,res1=ans;
}

4. 杂项

这一块严格来说并不算模板,只是记录一些常见的模型和处理方式。

4.1 分治乘

  • 给定 \(n\) 个一次多项式 \(a_i+b_ix\),求 \(\prod\limits_{i=1}^n (a_i+b_ix)\)

  • 给定 \(n\) 个一次多项式 \(a_i+b_ix\),求 \(\sum\limits_{i=1}^n \dfrac{1}{a_i+b_ix}\)

用类似分治的办法,尽量先乘小的区间,再合并大的区间。若初始多项式次数不一致,可以用 Huffman 算法的思路,优先队列维护当前的多项式,不断取出两个次数最小的多项式相乘。

时间复杂度是 \(T(n)=2T(n/2)+\Theta(n\log n)=\Theta(n\log^2n)\)

具体的实现可以用 vector 存多项式,空间复杂度是 \(\Theta(n\log n)\)

对于分式求和,对于每个区间同时维护分子和分母,最后再求逆算答案即可。

4.2 分治 FFT(半在线卷积)

离线分治 FFT

  • 已知 \(F(x)\),求 \(G(x)\) 使 \(g_n=\sum\limits_{i=1}^n f_ig_{n-i}\),边界条件为 \(g_0=1\)

如果知道 \(G(x)\) 的前 \(n\) 项,可以推出第 \(n+1\) 项。

类比 CDQ 分治:现在的区间 \(G\) 分治成左右两个区间(记作 \(G_l\)\(G_r\)),考虑左边对右边的贡献。

再记 \(F_l'\)\(F'\)\(F(x)\) 的前缀多项式(不太严谨),且 \(\deg F_l'=\deg G_l\)\(\deg F'=\deg G\),即加上 \('\) 就代表被平移到了开头。

显然贡献只有 \(F' \times G_l\)。单次 NTT 的时间复杂度为 \(\Theta(n \log n)\),套上分治的总复杂度即为 \(\Theta(n \log^2 n)\)

在线分治 FFT

  • \(F(x)\)\(G(x)\) 使 \(f_n=g_1 \oplus g_2 \oplus \cdots \oplus g_n\)\(g_n=\sum\limits_{i=1}^n f_ig_{n-i}\),边界条件为 \(f_0=g_0=0\)\(f_1=1\)

现在必须知道 \(f_1,\cdots,f_n\),才能算出 \(f_{n+1}\),没办法再像刚才那样计算。

具体而言,如果左端点不为 \(1\),那 \(F'\) 已经被算出来了,没有问题。但若左端点为 \(1\),区间 \(G\) 没算出来,\(F'=F\) 自然也不知道。

那现在只考虑左端点为 \(1\) 的情况。这时候 \(F_r'\) 不知道,只能将 \(F_l' * G_l\) 贡献到 \(G_r\) 去。因此 \(G_r\) 还缺了 \(F_r' \times G_l\),我们暂且放在一边。幸运的是,这时候 \(G_r\) 中的第一项系数是正确的,\(F_r\) 的第一项和第二项也计算得出来。

因此现在考虑分治到 \(G_r\) 里面,再分治到区间 \(GG\),可以将 \(FF’ \times GG_l\) 贡献到 \(GG_r\)。但是别忘了,\(G_r\) 还缺了 \(F_r' * G_l\),因此还要再补上 \(FF \times GG_l’\)

每分治到叶子节点就计算一些 \(f_n\),这样就正确了。

时间复杂度仍是 \(\Theta(n \log^2 n)\)

4.3 下降幂多项式乘法

  • 给出下降幂多项式 \(F(x),G(x)\) ,求下降幂多项式 \(H(x) = F(x) G(x)\)

下降幂多项式可以轻松(指单 log)在系数形式与点值形式之间转换。具体推导可参见 Re:从零开始的生成函数魔法 Section 3.4,这里重复一遍:

\(a_i=F(i)\)\(A(x)\)\(a_n\) 的生成函数

\[a_k=\sum\limits_{i=0}^n f_i k^{\underline{i}}=k!\sum\limits_{i=0}^n \dfrac{f_i}{(k-i)!} \]

\[\dfrac{a_k}{k!}=\sum\limits_{i=0}^n \dfrac{f_i}{(k-i)!} \]

\[\sum\limits_{k=0}^n \dfrac{a_k}{k!}=\sum\limits_{k=0}^n \sum\limits_{i=0}^n f_i \dfrac{1}{(k-i)!} \]

\[A(x)=F(x) e^x \]

\[F(x)=A(x)e^{-x} \]

总时间复杂度仅为 \(\Theta(n \log n)\)

void ftt(int *s,int *f,int n,int typ){
	static int A[N];
	if(typ==1){
		A[1]=1;
		NTT::solve(s,inv,f,n,n);
		for(int i=0;i<=n;i++) s[i]=s[i]*mul[i]%mod;
	}
	else{
		for(int i=0;i<=n;i++) f[i]=f[i]*inv[i]%mod;
		for(int i=0;i<=n;i++) A[i]=inv[i]*ID(i)%mod;
		NTT::solve(s,A,f,n,n);
	}
}
void FTT(int *s,int *f,int *g,int n,int m){
	static int A[N],B[N];
	ftt(A,f,n+m,1);
	ftt(B,g,n+m,1);
	for(int i=0;i<=n+m;i++) s[i]=A[i]*B[i]%mod;
	ftt(s,s,n+m,-1);
}

4.4 普通多项式与下降幂多项式互化

  • 给出下降幂/普通多项式 \(F(x)\) ,求它的普通/下降幂多项式形式 \(G(x)\)

这个问题是较为困难的,只能以点值为媒介进行转换,使用插值与求值来实现。时间复杂度为 \(\Theta(n \log^2 n)\) 且常数由插值与求值的常数决定。

void FtoP(int *s,int *f,int n){
	static int A[N],B[N];
	ftt(A,f,n,1);
	for(int i=n+1;i>=1;i--) A[i]=A[i-1];
	for(int i=1;i<=n+1;i++) B[i]=i-1;
	Interpolation::solve(s,B,A,n+1);
}
void PtoF(int *s,int *f,int n){
	static int A[N],B[N];
	for(int i=1;i<=n+1;i++) B[i]=i-1;
	Evaluation::solve(A,f,B,n,n+1);
	for(int i=0;i<=n;i++) A[i]=A[i+1];
	ftt(s,A,n,-1);
}

5. 参考资料

posted @ 2021-02-02 10:46  Appleblue17  阅读(962)  评论(4)    收藏  举报