多项式小结

Preface

  • 蒟蒻的我最近才会多项式exp……
  • 在这里总结一下多项式的各种芝士。顺便挂个模板,以备未来之需。

万恶之源——FFT/NTT

  • 我一个图像分析算法,怎么跑到OI来了
  • 一次dft,要求解\(A(x)\)\(\omega_n^i(i\in[0,n))\)的点值;idft要根据这些点值插出一个\(n-1\)次多项式。
  • FFT核心思想:记\(A(x)=\sum_i a_ix^i,A^0(x)=\sum_i a_{2i}x^{2i},A^1(x)=\sum_i a_{2i+1}x^{2i+1}\)。则有:

\[A(\omega_n^i)=A^0(\omega_n^{2i})+A^1(\omega_n^{2i})*\omega_n^i \]

\[A(\omega_n^{i+\frac n2})=A^0(\omega_n^{2i})+A^1(\omega_n^{2i})*\omega_n^{i+\frac n2}=A^0(\omega_n^{2i})-A^1(\omega_n^{2i})*\omega_n^i \]

  • 普通FFT的时候,\(\omega_n^j=e^{2\pi ij/n}=\cos\frac{2\pi j}n+i\sin\frac{2\pi j}n\);NTT的时候,\(\omega_n^i=g^{i(mo-1)/n}\)

求逆

  • \(B(x)\equiv A^{-1}(x)(mod\ x^n)\)
  • 已知\(B_0(x)\equiv A^{-1}(x)(mod\ x^{n/2})\),则\(B(x)-B_0(x)\equiv 0(mod\ x^{n/2})\)
  • 两边平方、移项,再乘上个\(A(x)\),得到\(B(x)\equiv 2B_0(x)-A(x)B_0^2(x)(mod\ x^n)\)

除法、取模

  • 已知\(n\)\(A(x)\)\(m(m≤n)\)\(B(x)\),求\(D(x),R(x)\),使\(A(x)=D(x)B(x)+R(x)\)
  • \(x^nA(\frac 1x)=x^{n-m}D(\frac 1x)x^mB(\frac 1x)+x^{n-m+1}x^{m-1}R(\frac 1x)\)
  • \(A^R(x)=x^nA(\frac 1x)\),可以发现这个操作就是将其系数reverse。把\(R(x)\)看成\(m-1\)次(不足补0),则\(A^R(x)=D^R(x)B^R(x)+x^{n-m+1}R^R(x)\)
  • \(A^R(x)\equiv D^R(x)B^R(x)(mod\ x^{n-m+1})\)

开根

  • 已知\(B_0^2\equiv A(mod\ x^{n/2})\),求\(B^2\equiv A(mod\ x^n)\)
  • 移项、平方,得到\((B_0-B)^2\equiv 0(mod\ x^n)\)
  • 拆开来、移项,得到\(B_0^2+B^2\equiv 2B_0B(mod\ x^n)\)
  • \(B\equiv B_0/2+A/(2B_0)(mod\ x^n)\)
  • 注意如果常数项不为1,可能要打个二次剩余。(坑点:\(x^2\equiv n(mod\ 998244353)\),同时\((998244353-x)^2\equiv n(mod\ 998244353)\))

ln

  • \(G(x)=\ln F(x)=\int\frac{F'(x)}{F(x)}dx\)
  • 注意这个当\(A(x)\)的常数项不为1时是求不了的。如果想求多项式幂函数,要先提出一个因式,使得\(A(x)\)的常数项为1。

exp

  • \(B(x)=e^{A(x)}(mod\ x^n)\)
  • \(F(B)=\ln B-A\),我们要求它\(mod\ x^n\)意义下的零点。(注意,这里是以\(B\)为自变量,\(A\)对于\(F(B)\)来说是个常数,因此\(F'(B)=\frac 1B\)
  • 已知\(F(B_0)\equiv 0(mod\ x^{n/2})\)。泰勒展开:\(F(B)=F(B_0)+\sum_i \frac{F^{(i)}(B_0)(B-B_0)^i}{i!}\)
  • \(B-B_0\equiv 0(mod\ x^{n/2})\),二次方后\(mod\ x^n=0\),因此可以不管\(i>1\)的项。于是得到\(F(B)\equiv F'(B_0)(B-B_0)(mod\ x^n)\)
  • 因此\(B\equiv B_0-\frac{F(B_0)}{F'(B_0)}\equiv B_0(1-\ln B_0+A)(mod\ x^n)\)
  • 注意这个当\(A(x)\)的常数项不为0时,应该是求不了的。

三角函数

  • 一个无聊的东西。
  • 根据欧拉公式有:\(e^{ix}=\cos x+i\sin x\),解方程可得\(\sin x=\frac{e^{ix}-e^{-ix}}{2i},\cos x=\frac{e^{ix}+e^{-ix}}2\)
  • 把多项式代入\(x\)即可。在模\(998244353\)意义下,\(i=\sqrt{998244352}=±86583718\)

  • 注意,以下皆为口胡……

反三角函数

  • 一个更无聊的东西。
  • 对于\(y=\arcsin x\),有\(y'=\frac 1{\sqrt{1-x^2}}\)。那么对于\(G(x)=\arcsin F(x)\),有\(G=\int\frac{F'}{\sqrt{1-F^2}}dx\)。这个exp都不用了,直接求逆+开根。
  • 对于\(y=\arctan x\),有\(y'=\frac 1{1+x^2}\)。那么对于\(G(x)=\arctan F(x)\),有\(G=\int\frac {F'}{1+F^2}dx\)。这个开根都不用了。

多点求值

  • 已知多项式\(A(x)\)\(n\)个点\(x_1,x_2,...,x_n\),求\(A(x_1),A(x_2),...,A(x_n)\)
  • 考虑分治,将\(n\)个点分为前后两半。记\(B_0(x)=\prod_{i=1}^{n/2}(x-x_i)\),则有\(\forall i\in[1,n/2]:B_0(x_i)=0\)
  • 若我们将\(A(x)\)表示成\(D(x)B_0(x)+R(x)\)的形式,可得\(\forall i\in[1,n/2]:A(x)=R(x)\)
  • 那么就可以用分治NTT+多项式取模解决。另一半同理。

快速插值

  • 普通的朗格朗日插值长这样:\(F(x)=\sum_{i=1}^n y_i\prod_{j≠i}\frac{x-x_j}{x_i-x_j}\)

  • \(M(x)=\prod_{i=1}^n(x-x_i)\),洛一洛可得:\(\prod_{j\neq i} (x_i - x_j) = \lim_{x\rightarrow x_i} \frac{\prod_{j=1}^n (x - x_j)}{x - x_i} = M'(x_i)\)

  • 因此\(F(x)=\sum_{i = 1}^n \frac{y_i}{M'(x_i)}\prod_{j \neq i}(x - x_j)\)

  • 再考虑分治。令\(F_0(x)=\sum_{i=1}^{n/2}\frac{y_i}{M'(x_i)}\prod_{j \neq i\wedge j≤n/2}(x - x_j),M_0=\prod_{i=1}^{n/2}(x-x_i)\)(另一半同理),则可得\(F(x)=F_0(x)M_1(x)+F_1(x)M_0(x)\)

Code

  • 注意:主程序是多项式除法与取模。
#include
#include
#include
#include
#define P(x,y) x=(x+y)%mo
#define T(x,y) x=x*(y)%mo
#define clr(a,n) memset(a,0,8*n)
#define go(i,a,b) for(int i=a;i< b;i++)
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=1<<21;
const ll mo=998244353,_1=86583718,n2=mo+1>>1;
int n,n1,k;
ll f[N],g[N],q[N];
ll ksm(ll x,ll y) {ll a=1; for(;y;y/=2,T(x,x))if(y&1)T(a,x); return a;}
ll iv(ll x) {return ksm(x,mo-2);}
ll R() {return (rand()*19260817ll+rand())%mo;}
namespace NTT
{
	int tf[N]; ll w[N],c[N],d[N],e[N],b1[N];
	void build(int n)
	{
		for(int i=1;i>1)==1;}
	pl ksm(pl x,ll y) {pl a={1,0}; for(;y;y/=2,x=x*x)if(y&1)a=a*x; return a;}
	ll Sqrt(ll n)
	{
		if(!n) return 0;
		ll a;
		do a=R();
		while(ck(_=(a*a%mo-n+mo)%mo));
		return ksm({a,1},mo+1>>1).x;
	}
	void Sqrt(ll a[N],ll b[N],int n1)
	{
		clr(b,n1); b[0]=Sqrt(a[0]);
		if(b[0]>mo-b[0]) b[0]=mo-b[0];
		for(int m=2;m<=n1;m*=2)
		{
			clr(d,m*2);
			go(i,0,m) d[i]=b[i]*2%mo;
			inv(d,e,m);
			clr(d,m*2); memcpy(d,a,8*m);
			mtp(d,e,m*2);
			go(i,m/2,m) b[i]=(b[i]*n2+d[i])%mo;
		}
	}
}
using namespace NTT;
int main()
{
	scanf("%d%d",&n,&m); n++,m++;
	for(n1=1;n1

你可能感兴趣的:(多项式小结)