首页 > 其他分享 >多项式

多项式

时间:2024-04-20 19:58:39浏览次数:24  
标签:frac 多项式 sum cpx aligned omega

快速傅里叶变换

模板题:【模板】多项式乘法(FFT)

对于两个多项式 \(f,g\),\(f\) 的项数为 \(n\),\(g\) 的项数为 \(m\),FFT 可以 \(O((n+m)\log(n+m))\) 地求它们的卷积。

多项式点值表示法

引理:给定 \(n\) 个点值 \(f(x_0),f(x_1),f(x_2),\dots,f(x_{n-1})\)(\(x_i\) 互不相同),可确定唯一一个多项式 \(f(x)=a_0+a_1x+a_2x^2+a_3x^3+\dots+a_{n-1}x^{n-1}\)。

证明:

对于给定 \(n\) 个点值 \(f(x_0),f(x_1),f(x_2),\dots,f(x_{n-1})\),可得方程:

\[\begin{cases} a_0+a_1x_0+a_2x_0^2+a_3x_0^3+\dots+a_{n-1}x_0^{n-1}=f(x_0)\\ a_0+a_1x_1+a_2x_1^2+a_3x_1^3+\dots+a_{n-1}x_1^{n-1}=f(x_1)\\ a_0+a_1x_2+a_2x_2^2+a_3x_2^3+\dots+a_{n-1}x_2^{n-1}=f(x_2)\\ \dots\\ a_0+a_1x_{n-1}+a_2x_{n-1}^2+a_3x_{n-1}^3+\dots+a_{n-1}x_{n-1}^{n-1}=f(x_{n-1}) \end{cases}\]

这是一个由 \(n\) 个本质不同的 \(n-1\) 元 \(n-1\) 次方程组成的方程组,有唯一解。

多项式转点值

考虑用 \(n\) 个点值 \(f(x_0),f(x_1),f(x_2),\dots,f(x_{n-1})\) 表示多项式 \(f\),现在需要快速求出这 \(n\) 个点值。

假设 \(f(x)=a_0+a_1x+a_2x^2+a_3x^3+a_4x^4+a_5x^5+a_6x^6+a_7x^7\),有:

\[\begin{aligned} f(x)&=(a_0+a_2x^2+a_4x^4+a_6x^6)+(a_1x+a_3x^3+a_5x^5+a_7x^7)\\ &=(a_0+a_2x^2+a_4x^4+a_6x^6)+x(a_1+a_3x^2+a_5x^4+a_7x^6) \end{aligned}\]

令 \(f_1(x)=a_0+a_2x+a_4x^2+a_6x^3,f_2(x)=a_1+a_3x+a_5x^2+a_7x^3\),有 \(f(x)=f_1(x^2)+x\times f_2(x^2)\)。

上式仍只能每次求解一个 \(f(x_i)\),考虑用单位根作为点值,有:

\[\begin{aligned} &\begin{aligned} f(\omega_n^k)&=f_1((\omega_n^k)^2)+\omega_n^k\times f_2((\omega_n^k)^2)\\ &=f_1(\omega_n^{2k})+\omega_n^k\times f_2(\omega_n^{2k})\\ &=f_1(\omega_{\frac{n}{2}}^k)+\omega_n^k\times f_2(\omega_{\frac{n}{2}}^k) \end{aligned}\\ &\begin{aligned} f(\omega_n^{k+\frac{n}{2}})&=f_1((\omega_n^{k+\frac{n}{2}})^2)+\omega_n^{k+\frac{n}{2}}\times f_2((\omega_n^{k+\frac{n}{2}})^2)\\ &=f_1((-\omega_n^k)^2)-\omega_n^k\times f_2((-\omega_n^k)^2)\\ &=f_1(\omega_n^{2k})-\omega_n^k\times f_2(\omega_n^{2k})\\ &=f_1(\omega_{\frac{n}{2}}^k)-\omega_n^k\times f_2(\omega_{\frac{n}{2}}^k) \end{aligned} \end{aligned} \]

这样就把所求的点值联系了起来,可以递归求解:

\(f(\omega_1^0)\to f(\omega_2^{0\sim 1})\to\dots\to f(\omega_{\frac{n}{4}}^{{0\sim\frac{n}{4}-1}})\to f(\omega_{\frac{n}{2}}^{{0\sim\frac{n}{2}-1}})\to f(\omega_n^{{0\sim n-1}})\)

时间复杂度 \(O(n\log n)\)。

该算法要求 \(n\) 为 \(2\) 的幂次,对于不是 \(2\) 的幂次的情况需要找到一个 \(2^k\ge n\),把 \(n+1\sim 2^k\) 的系数补为 \(0\)。

但递归常数巨大,不实用。

考虑先求出底层的 \(f(\omega_1^0)\),再一层一层向上计算,就省去了递归的大常数。

于是需要求出底层 \(a\) 的排列。

假设 \(f\) 的各项系数为 \(a_0,a_1,a_2,a_3,a_4,a_5,a_6,a_7\),递归法会这样划分:

  • \((a_0,a_1,a_2,a_3,a_4,a_5,a_6,a_7)\)

  • \((a_0,a_2,a_4,a_6),(a_1,a_3,a_5,a_7)\)

  • \((a_0,a_4),(a_2,a_6),(a_1,a_5),(a_3,a_7)\)

  • \((a_0),(a_4),(a_2),(a_6),(a_1),(a_5),(a_3),(a_7)\)

假设一开始每个数都在位置 \(0\),若 \(i\) 的二进制下第 \(i\) 位为 \(1\)(所有位为 \(1\sim k\)),那么 \(i\) 在排列中会被往后移 \(2^{k-i}\) 位。

因此,在二进制下将 \(i\) 翻转,就得到了它在底层排列的位置。

点值转多项式

令 \(F(x)=f(x_0)+f(x_1)x+f(x_2)x^2+\dots+f(x_{n-1})x^{n-1}\),有:

\[\begin{aligned} F(\omega_n^{-k})&=\sum_{i=0}^{n-1}f(\omega_n^i)(\omega_n^{-k})^i\\ &=\sum_{i=0}^{n-1}\Big(\sum_{j=0}^{n-1}a_j(\omega_n^i)^j\Big)(\omega_n^{-k})^i\\ &=\sum_{i=0}^{n-1}\Big(\sum_{j=0}^{n-1}a_j\omega_n^{ij}\Big)\omega_n^{-ik}\\ &=\sum_{i=0}^{n-1}\sum_{j=0}^{n-1}a_j\omega_n^{i(j-k)}\\ &=\sum_{j=0}^{n-1}a_j\sum_{i=0}^{n-1}(\omega_n^{j-k})^i \end{aligned}\]

令 \(g(\omega_n^t)=\displaystyle\sum_{i=0}^{n-1}(\omega_n^t)^i\)

分类讨论 \(g(\omega_n^t)\) 的值:

  1. \(t=0\)

    \(g(\omega_n^0)=\displaystyle\sum_{i=0}^{n-1}(\omega_n^0)^i=\sum_{i=0}^{n-1}1^i=n\)

  2. \(t\ne 0\)

    根据等比数列求和公式,\(g(\omega_n^t)=\dfrac{(\omega_n^t)^n-1}{\omega_n^t-1}=\dfrac{1-1}{\omega_n^t-1}=0\)

故有:

\[g(\omega_n^t)=\begin{cases} n,&\text{}t=0\\ 0,&\text{}t\ne 0 \end{cases}\]

带入原式:

\[\begin{aligned} F(\omega_n^{-k})&=\sum_{j=0}^{n-1}a_j\sum_{i=0}^{n-1}(\omega_n^{j-k})^i\\ &=\sum_{j=0}^{n-1}a_jg(\omega_n^{j-k})\\ &=a_k\times n \end{aligned}\]

也就是说 \(F(\omega_n^{-k})=\dfrac{f(\omega_n^k)}{n}\)。

因此,将点值作为 \(F\) 的系数,再求一遍 \(F\) 的点值,除以 \(n\),就得到了 \(f\) 的各项系数。

多项式运算转点值运算

显然,两个多项式运算可以转化为点值运算。

例如两个多项式相乘(卷积),就相当于它们的对应点值相乘。其他运算同理。

因此,利用 FFT 将难以直接运算的多项式转为极好运算的点值,就可以方便地进行多项式运算。

代码

#include <bits/stdc++.h>
using namespace std;
const double PI = acos(-1);
int n, m, rev[4000005];
struct cpx {
    double x, c;
};
cpx operator+(cpx x1, cpx x2) {
    return {x1.x + x2.x, x1.c + x2.c};
}
cpx operator-(cpx x1, cpx x2) {
    return {x1.x - x2.x, x1.c - x2.c};
}
cpx operator*(cpx x1, cpx x2) {
    return {x1.x *x2.x - x1.c * x2.c, x1.x *x2.c + x2.x * x1.c};
}
void change(int len, cpx *f) {
    for (int i = 0; i < len; i++) {
        rev[i] = rev[i >> 1] >> 1;

        if (i & 1)
            rev[i] |= len >> 1;
    }

    for (int i = 0; i < len; i++)
        if (i < rev[i])
            swap(f[i], f[rev[i]]);
}
void FFT(int op, int len, cpx *f) {
    cpx w, tw, t1, t2;
    change(len, f);

    for (int t = 2; t <= len; t <<= 1) {
        if (op)
            tw = {cos(PI * 2 / t), sin(PI * 2 / t)};
        else
            tw = {cos(PI * 2 / t), sin(-PI * 2 / t)};

        for (int i = 0; i < len; i += t) {
            w = {1, 0};

            for (int j = i; j < i + t / 2; j++) {
                t1 = f[j], t2 = f[j + t / 2];
                f[j] = t1 + w * t2, f[j + t / 2] = t1 - w * t2;
                w = w * tw;
            }
        }
    }

    if (!op) {
        for (int i = 0; i < len; i++)
            f[i].x /= len;
    }
}
cpx f[4000005], g[4000005];
int main() {
    cin >> n >> m;

    for (int i = 0; i <= n; i++)
        cin >> f[i].x;

    for (int i = 0; i <= m; i++)
        cin >> g[i].x;

    int len = 1;

    while (len < n + m + 1)
        len <<= 1;

    FFT(1, len, f);
    FFT(1, len, g);

    for (int i = 0; i < len; i++)
        f[i] = f[i] * g[i];

    FFT(0, len, f);

    for (int i = 0; i < n + m + 1; i++)
        cout << (int)(f[i].x + 0.5) << ' ';
      //四舍五入,避免精度误差

    return 0;
}

标签:frac,多项式,sum,cpx,aligned,omega
From: https://www.cnblogs.com/wangxuzhou-blog/p/18148052/polynomial

相关文章

  • 多项式全家桶
    【多项式求逆】【整式取模】定义单项式取模。\[C\cdotx^k\bmodx^n=\begin{cases}0&k\gen\\C\cdotx^k&k<n\end{cases}\]定义多项式取模为它的每一项取模相加。可以看出,模\(x^n\)相当于保留\(0\simn-1\)次项。【问题描述】一般形式:已知多项式\(A(x),C(x)\),求\(B(x......
  • 多项式全家桶
    多项式求逆考虑倍增。若已经求出\(A\timesB'\equiv1\pmod{x^n}\),我们希望求出\(B\)使得\(A\timesB\equiv1\pmod{x^{2n}}\)。有:\[B-B'\equiv0\pmod{x^n}\]\[(B-B')^2\equiv0\pmod{x^{2n}}\]\[B^2-2BB'+B'^2\......
  • 多项式学习笔记
    1.前置知识1.1基础\(f(x)=\sum_{i=0}^na_ix^i\)被称为一个\(n\)次多项式。\(\degf(x)\)表示多项式的次数。\(f(x)g(x)=h(x)\)称为多项式乘法,也叫多项式卷积,满足\(h_n=\sum_{i+j=n}f_ig_j\)。1.2点值给定一个多项式\(f(x)\),再给\(m\)个点\(x_1,\dot......
  • 1010 一元多项式求导
    测试点2应该是只输入1对并且是一个常数,如30这种。应该输出00。#include<bits/stdc++.h>usingnamespacestd;vector<int>a,b;//系数指数intmain(){ intxs,zs; while(cin>>xs>>zs){ a.push_back(xs); b.push_back(zs); } if(a.size()==1&&b[0]==0){ ......
  • 【模板】任意模数多项式乘法:三模 NTT
    前置知识https://www.cnblogs.com/caijianhong/p/template-crt.htmlhttps://www.cnblogs.com/caijianhong/p/template-fft.html题目描述任意模数多项式乘法solution首先我们打开https://blog.miskcoo.com/2014/07/fft-prime-table这篇文章找到\(998244353\)附近的几个质......
  • 【数学】多项式插值
    问题描述给出\(n+1\)个二维平面上的点对\((x_0,y_0),(x_1,y_1),(x_2,y_2),\cdots,(x_{n},y_{n})\),求一个经过这些点的不超过\(n\)次的多项式\(P(x)=p_{n}\cdotx^{n}+p_{n-1}\cdotx^{n-1}+p_{n-2}\cdotx^{n-2}+\cdots+p_{1}\cdotx+......
  • CF156D-Prufer序列、多项式定理
    link:https://codeforces.com/contest/156/problem/D题意:给一张无向简单图\(G\),问有多少种加边的方式,使得图联通,并且需要加的边最小。\(|E|,|V|\leq10^5\),对\(k\)取模前置知识应该是Prufer序列(这题应该是绕不开这个东西)对每个连通分支考虑答案,如果有\(k\)个连通分支,大小......
  • 从向量空间到特征多项式(参考自代数学引论)
    抽象线性空间定义线性空间\((R,V)\),满足:\(R\)是域,\(V\)是加法交换群;给定运算\((R,V)\toV\),即“纯量乘向量”,需要满足:对加法的左分配律(纯量加法和向量加法)和结合律(具体来说是\(a(b{\bfx})=(ab){\bfx}\))和“酉性”(\(1{\bfx}={\bfx}\))。容易定义线性组合。定义一个子集......
  • 数据结构与算法分析实验3 [进阶]通过链表实现多项式加法和乘法
    文章目录大致内容介绍多项式加法代码一览头文件Poly.h内容如下:实现文件Poly.cpp内容如下:初始化增加元素删除元素功能函数遍历函数清除销毁打印多项式向多项式内插入一个元素源文件main.cpp内容如下:实现效果:多项式乘法实现方法:在Poly.h中添加声明:在Poly.cpp中添加实现:在......
  • 【数据结构】一元多项式的表示与相加(无序输入 有序输出)
    一元多项式的表示与运算——课程设计(无序输入有序输出)目录一元多项式的表示与运算——课程设计(无序输入有序输出)一.例题:(输入无序,指数升序排列的一元多项式)1.链表结点定义2.创建单链表存放一元多项式(将无序的输入有序存放于链表)3.输出一元多项式4.一元多项式求值......