模板:基于NTT的多项式操作

基于NTT的多项式操作

因为实在是太多东西啦,所以就全部整理了以下,持续更新ing~
前置知识:NTT
顺便说一句,代码采用重载vector封装的形式(因为懒得自己开结构体)
下面是几个已经封装的基础代码,为了方便浏览,先贴出来:

typedef std::vector<int> VI;
int fix(int x) {
   
   return (x >> 31 & P) + x;}
int Pow(int x, int k) {
   
   
    int r = 1;
    for(;k; k >>= 1, x = 1LL * x * x % P)
        if(k & 1)
            r = 1LL * r * x % P;
    return r;
}
int Inv(int x) {
   
   return Pow(x, P - 2);}
void Pre(int m) {
   
   
    int x = 0; L = 1;
    for(;(L <<= 1) < m; ++x) ;
    for(int i = 1;i < L; ++i)
        R[i] = R[i >> 1] >> 1 | (i & 1) << x;
    int wn = Pow(3, (P - 1) / L); w[0] = 1;
    for(int i = 1;i < L; ++i)
        w[i] = 1LL * w[i - 1] * wn % P;
    InvL = Inv(L);
}
void NTT(int *F) {
   
   
    for(int i = 0;i < L; ++i)
        if(R[i] > i)
            std::swap(F[i], F[R[i]]);
    for(int i = 1, d = L >> 1; i < L; i <<= 1, d >>= 1)
        for(int j = 0;j < L; j += i << 1) {
   
   
            int *l = F + j, *r = F + i + j, *p = w, tp;
            for(int k = i; k--; ++l, ++r, p += d)
                tp = 1LL * *p * *r % P, *r = (*l - tp) % P, *l = (*l + tp) % P;
        }
}
void Fill(const VI &a, int *A, int m) {
   
   
    m = std::min(m, (int)a.size());
    for(int i = 0;i < m; ++i)
        A[i] = a[i];
    for(int i = m; i < L; ++i)
        A[i] = 0;
}
void Fill(int *A, int *B, int m) {
   
   
    for(int i = 0;i < m; ++i)
        B[i] = A[i];
    for(int i = m; i < L; ++i)
        B[i] = 0;
}
VI operator * (const VI &a, const VI &b) {
   
   
	const int Lim = 3000;
    int m = a.size() + b.size() - 1;
    static VI c; static int A[N], B[N];
    c.resize(m, 0);
    if(a.size() * b.size() <= Lim) {
   
    //小范围暴力
		for(int i = 0;i < m; ++i) 
			c[i] = 0;
    	for(int i = 0;i < a.size(); ++i)
    		for(int j = 0;j < b.size(); ++j)
    			c[i + j] = (c[i + j] + 1LL * a[i] * b[j]) % P;
    	return c;
	}
	Pre(m);
    Fill(a, A, a.size());
    Fill(b, B, b.size());
    NTT(A); NTT(B);
    for(int i = 0;i < L; ++i)
        A[i] = 1LL * A[i] * B[i] % P;
    NTT(A);
    for(int i = 0;i < m; ++i)
        c[i] = fix(1LL * A[L - i & L - 1] * InvL % P);
    return c;
}
VI operator - (const VI &a, const VI &b) {
   
   
    int n = std::max(a.size(), b.size());
    static VI c; c.resize(n, 0);
    for(int i = 0;i < a.size(); ++i)
        c[i] = a[i];
    for(int i = 0;i < b.size(); ++i)
        c[i] = fix(c[i] - b[i]);
    return c;
}
VI operator + (const VI &a, const VI &b) {
   
   
    int n = std::max(a.size(), b.size());
    static VI c; c.resize(n, 0);
    for(int i = 0;i < a.size(); ++i)
        c[i] = a[i];
    for(int i = 0;i < b.size(); ++i)
    	c[i] = (c[i] + b[i]) % P;
    return c;
}

迭代相关:多项式求逆,开根,求exp

这些我们熟悉的多项式操作都是采用迭代法解决,既然都是迭代法,必有其共性,因此下面便脱离传统的证明方法,采用牛顿迭代法来进行一波推导

知识点:泰勒展开

下面首先介绍一下泰勒展开:
对于一个函数 f ( x ) f(x) f(x),其在 x = x 0 x=x_0 x=x0处的泰勒公式如下:
f ( x ) = f ( x 0 ) 0 ! + f ′ ( x 0 ) 1 ! ( x − x 0 ) + f ′ ′ ( x 0 ) 2 ! ( x − x 0 ) 2 ⋯ f ( n ) ( x 0 ) n ! ( x − x 0 ) n f(x)=\frac{f(x_0)}{0!}+\frac{f&#x27;(x_0)}{1!}(x-x_0)+\frac{f&#x27;&#x27;(x_0)}{2!}(x-x_0)^2\cdots\frac{f^{(n)}(x_0)}{n!}(x-x_0)^n f(x)=0!f(x0)+1!f(x0)(xx0)+2!f(x0)(xx0)2n!f(n)(x0)(xx0)n

知识点:牛顿迭代

这是一种逼近零点的科技。(高中数学课本上有,文化课选手应该了解)
对于一个函数 f ( x ) f(x) f(x),我们考虑以 x i x_i xi作为起点逼近其零点。
方法就是,作 ( x i , y i ) (x_i,y_i) (xi,yi)处的切线,设其交 x x x轴于 x i + 1 x_{i+1} xi+1,以 x i + 1 x_{i+1} xi+1作为下一次的起点重复上述过程不断逼近零点。
推一推式子:
y − f ( x i ) = f ′ ( x i ) ( x − x i ) y-f(x_i)=f&#x27;(x_i)(x-x_i) yf(xi)=f(xi)(xxi)
y = 0 y=0 y=0,得到 x i + 1 = x i − f ( x i ) f ′ ( x i ) x_{i+1}=x_i-\frac{f(x_i)}{f&#x27;(x_i)} xi+1=xif(xi)f(xi)
有没有发现式子其实挺熟悉的?
考虑直线方程: y = f ( x i ) + f ′ ( x i ) ( x − x i ) y=f(x_i)+f&#x27;(x_i)(x-x_i) y=f(xi)+f(xi)(xxi)
发现直线方程就是 f ( x ) f(x) f(x) x i x_i xi处的一阶泰勒展开。
形式化地,我们可以这样描述牛顿迭代法:
用函数 f ( x ) f(x) f(x) f ( x i ) f(x_i) f(xi)处的一阶展开方程的零点作为下一次迭代的起点 x i x_i xi,这样的迭代法就是牛顿迭代法。
这个东西对于多项式有什么好处呢?
我们不难发现定义一个以多项式为自变量的函数 G ( x ) G(x) G(x),泰勒公式仍然成立。
我们首先将题目的形式转化为求 F ( x ) ∣ G ( F ( x ) ) ≡ 0 ( m o d &ThinSpace;&ThinSpace; x n ) F(x)|G(F(x))\equiv 0(\mod x^n) F(x)G(F(x))0(modxn)
迭代法的一般考虑步骤是:
已知 G ( F 0 ( x ) ) ≡ 0 ( m o d &ThinSpace;&ThinSpace; x n ) G(F_0(x))\equiv 0(\mod x^n) G(F0(x))0(modxn)
G ( F ( x ) ) ≡ 0 ( m o d &ThinSpace;&ThinSpace; x 2 n ) G(F(x))\equiv 0 (\mod x^{2n}) G(F(x))0(modx2n)
考虑 G ( x ) G(x) G(x) F 0 ( x ) F_0(x) F0(x)处的泰勒展开逼近 F ( x ) F(x) F(x)
G ( F ( x ) ) ≡ G ( F 0 ( x ) ) + G ′ ( F 0 ( x ) ) ( F ( x ) − F 0 ( x ) ) + G ′ ′ ( F 0 ( x ) ) ( F ( x ) − F 0 ( x ) ) 2 ⋯ ( m o d &ThinSpace;&ThinSpace; x n ) G(F(x))\equiv G(F_0(x))+G&#x27;(F_0(x))(F(x)-F_0(x))+G&#x27;&#x27;(F_0(x))(F(x)-F_0(x))^2\cdots(\mod x^n) G(F(x))G(F0(x))+G(F0(x))(F(x)F0(x))+G(F0(x))(F(x)F0(x))2(modxn)
事实上,我们有 F ( x ) ≡ F 0 ( x ) ( m o d &ThinSpace;&ThinSpace; x n ) F(x)\equiv F_0(x) (\mod x^n) F(x)F0(x)(modxn)
那么 F ( x ) − F 0 ( x ) ≡ 0 ( m o d &ThinSpace;&ThinSpace; x n ) ⇒ ( F ( x ) − F 0 ( x ) ) 2 ≡ 0 ( m o d &ThinSpace;&ThinSpace; x 2 n ) F(x)-F_0(x)\equiv 0 (\mod x^n)\Rightarrow (F(x)-F_0(x))^2\equiv 0 (\mod x^{2n}) F(x)F0(x)0(modxn)(F(x)F0(x))20(modx2n)
这一步推导是因为 F ( x ) − F 0 ( x ) F(x)-F_0(x) F(x)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值