首页 > 其他分享 >普通有限多项式笔记

普通有限多项式笔记

时间:2024-04-25 09:01:18浏览次数:27  
标签:__ int 多项式 len pnm 笔记 有限 inline mod

普通多项式笔记

\(\textrm{Newton's Method}\) (牛顿迭代)

应用于解决已知 \(g(x)\) 的情况下,求出 \(g(f(x))\equiv 0\mod x^n\)。

首先通过列出方程显然,\(f(x) \mod x^n\) 在此时是唯一的。

那么我们假设已知 \(g(f_0(x))\equiv 0\mod x^{n/2}\),显然此时 \(f_0(x)\mod x^{n/2}\) 也是唯一的,且 \(\deg f_0(x)=\frac{n}{2},f(x)\equiv f_0(x) \mod x^{n/2}\)。

这时我们把 \(g(f(x))\) 在 \(f_0(x)\) 处展开,可以得到:

\[g(f(x))= \sum_{k=0}^{+\infty}\frac{g^{(k)}(f_0(x))}{k!}(f(x)-f_0(x))^k \]

因此有:

\[\sum_{k=0}^{+\infty}\frac{g^{(k)}(f_0(x))}{k!}(f(x)-f_0(x))^k\equiv 0 \mod x^n \]

因为 \(f(x)-f_0(x)\equiv 0 \mod x^{n/2}\),所以有: \((f(x)-f_0(x))^2 \equiv 0 \mod x^n\)。

当然 \(n=1\) 时应该要特殊地求出 \(f(x)\)。

所以:

\[\sum_{k=0}^{+\infty}\frac{g^{(k)}(f_0(x))}{k!}(f(x)-f_0(x))^k\equiv g(f_0(x))+g'(f_0(x))(f(x)-f_0(x))\equiv 0 \mod x^n \\ f(x)\equiv f_0(x)-\frac{g(f_0(x))}{g'(f_0(x))} \mod x^n \]

最终的结果看起来没什么令人惊奇的地方,但是实际上应用却是非常多的。

多项式 \(\textrm{inverse}\),逆

已知 \(h(x)\),求 \(f(x)=h^{-1}(x)\)。

考虑使用 \(\textrm{Newton's Method}\) 解决这个问题。

构造 \(g(f(x))=f^{-1}(x)-h(x)\),此时我们把 \(h(x)\) 当作一个常数。

因此有:

\[\begin{aligned} f(x)&\equiv f_0(x)-\frac{f^{-1}_0(x)-h(x)}{-f_0^{-2}(x)} &\mod x^n \\ &\equiv f_0(x)_+f_0^2(x)\left(f_0^{-1}(x)-h(x)\right) &\mod x^n \end{aligned} \]

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

多项式 \(\textrm{division}\),带余除法

已知 \(A(x),B(x)\),求 \(Q(x),R(x)\) 使得 \(A(x)=B(x)\cdot Q(x)+R(x)\)。

记 \(\deg A(x)=n,\deg B(x)=m,n\ge m\),那么钦定 \(\deg Q(x)=n-m,\deg R(x)=m-1\)。

记对多项式 \(f(x)\) 的 \(\textrm{Reverse}\) 操作为 \(f^R(x)=x^{\deg f(x)}f\left(\frac1 x\right)\)。

因此有:

\[A(\frac{1}{x})=B(\frac{1}{x})\cdot Q(\frac{1}{x}) +R(\frac{1}{x}) \\ x^nA(\frac{1}{x})=x^{m}B(\frac{1}{x})\cdot x^{n-m}Q(\frac{1}{x}) +x^{n-m+1}\cdot x^{m-1}R(\frac{1}{x}) \\ A^R(x)=B^R(x)\cdot Q^R(x)+x^{n-m+1} R^R(x) \\ A^R(x)\equiv B^R(x)\cdot Q^R(x) \mod x^{n-m+1} \]

可以求出 \(Q^R(x)\) 进而求出 \(Q(x),R(x)\)。

多项式 \(\textrm{sqrt}\),开方

已知 \(h(x)\),求 \(f(x)=\sqrt {h(x)}\)。

考虑使用 \(\textrm{Newton's Method}\) 解决这个问题。

构造 \(g(f(x))=f^2(x)-h(x)\),此时我们把 \(h(x)\) 当作一个常数。

因此有:

\[\begin{aligned} f(x)\equiv f_0(x)-\frac{f_0^2(x)-h(x)}{2f_0(x)} \mod x^n \end{aligned} \]

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

多项式 \(\ln\),自然对数

已知 \(f(x)\),求 \(\ln f(x)\)。

考虑求导:

\[(\ln f(x))'=\frac{f'(x)}{f(x)} \]

积分还原后:

\[\ln f(x)=\int \frac{f'(x)}{f(x)} \]

有一个问题是最终答案的常数项。

但是我们可以证明若 \(\ln f(x)\) 存在,其充要条件是 \(f(x)\) 常数项为 \(1\)。

令 \(g(x) = 1-f(x)\)

考虑:

\[\ln f(x)=\ln (1-g(x))=-\sum_{k=0}^{+\infty} \frac{g^k(x)}{k} \]

若 \([x^0]g(x)\ne 0\),则 \(\ln(1-g(x))=-\sum_{k=0}^{+\infty}\frac{g^k(x)}{k}\) 的常数项可能不收敛,如果仅考虑整数的话则一定不收敛。可以求出当且仅当 \([x^0]g(x)\in (0,2)\) 时才收敛。

由此我们可以推得 \([x^0]\ln f(x)=0\)。

多项式 \(\exp\),自然常数的幂

已知 \(h(x)\),求 \(f(x)=\exp h(x)\)。

考虑使用 \(\textrm{Newton's Method}\) 解决这个问题。

构造 \(g(f(x))=\ln f(x)-h(x)\),此时我们把 \(h(x)\) 当作一个常数。

因此有:

\[\begin{aligned} f(x)&\equiv f_0(x)-\frac{\ln f_0(x)-h(x)}{f_0^{-1}(x)} &\mod x^n \\ &\equiv f_0(x)(1-\ln f_0(x)+h(x)) &\mod x^n \end{aligned} \]

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

需要注意的是 \(h(x)\) 的常数项一定为 \(0\),否则如果使用快速数论变换时会出现问题,因为我们无法得到 \(\exp [x^0]h(x)\) 在取模意义下的取值。

多项式 \(\textrm{power}\),幂

已知 \(h(x)\),求 \(h^m(x)\)。

显然有:

\[h^m(x)=\exp (m \ln h(x)) \]

当然,如果出现 \([x^0]h(x)=0\) 或者 \([x^0]h(x)\ne 1,0\) 的情况要先对 \(h(x)\) 做处理,最后再乘回来。

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

全家桶代码

// Not afraid to dark.

#include <bits/stdc++.h>
using namespace std;

clock_t start_time, end_time;
#define GET_START start_time = clock ();
#define GET_END end_time = clock (); fprintf (stderr, "TIME COSSEMED : %0.3lf\n", 1.0 * (end_time - start_time) / CLOCKS_PER_SEC);

#define int long long

namespace io {
    int read_pos, read_dt; char read_char;
    __inline__ __attribute__ ((always_inline)) int read (int &p = read_pos){
        p = 0, read_dt = 1; read_char = getchar ();
        while (! isdigit (read_char)){
            if (read_char == '-')
                read_dt = - 1;
            read_char = getchar ();
        }
        while (isdigit (read_char)){
            p = (p << 1) + (p << 3) + read_char - 48;
            read_char = getchar ();
        }
        return p = p * read_dt;
    }
    int write_sta[65], write_top;
    __inline__ __attribute__ ((always_inline)) void write (int x){
        if (x < 0)
            putchar ('-'), x = - x;
        write_top = 0;
        do
            write_sta[write_top ++] = x % 10, x /= 10;
        while (x);
        while (write_top)
            putchar (write_sta[-- write_top] + 48);
    }
    int llen;
    __inline__ __attribute__ ((always_inline)) int get_string (char c[], int &len = llen){
        len = 0;
        read_char = getchar ();
        while (read_char == ' ' || read_char == '\n' || read_char == '\r')
            read_char = getchar ();
        while (read_char != ' ' && read_char != '\n' && read_char != '\r'){
            c[++ len] = read_char;
            read_char = getchar ();
        }
        return len;
    }
}

#define log2_(x) ((x) ? (31 ^ __builtin_clz (x)) : 0)

namespace Region {
    const int mod = 998244353, G = 3, Gi = 332748118;
    __inline__ __attribute__ ((always_inline)) int power (int a, int p){
        int res = 1;
        while (p > 0){
            if (p & 1)
                res = res * a % mod;
            a = a * a % mod;
            p >>= 1;
        }
        return res;
    }
    __inline__ __attribute__ ((always_inline)) int Inverse (int x){
        return power (x, mod - 2);
    }
}

namespace Polynomial {
    // ordinary Number-Theoretic Tranform

    using namespace Region;

    const int N = 5e5;

    __inline__ __attribute__ ((always_inline)) void Reverse (vector < int > &a, int len){
        static int rev[N + 5];
        rev[0] = 0;
        for (int i = 1;i < len;++ i){
            rev[i] = rev[i >> 1] >> 1;
            if (i & 1)
                rev[i] |= len >> 1;
            if (i < rev[i])
                swap (a[i], a[rev[i]]);
        }
    }

    struct pnm {
        int len;
        vector < int > a;
        pnm (int _len_ = 0, int _c_ = 0){
            len = _len_;
            a.resize (len, 0);
            if (len)
                a[0] = _c_;
        }
        __inline__ __attribute__ ((always_inline)) int & operator [] (const int x) {
            return a[x];
        }
        __inline__ __attribute__ ((always_inline)) int operator [] (const int x) const {
            return a[x];
        }
        __inline__ __attribute__ ((always_inline)) int deg (){
            for (int i = len - 1;i > 0;-- i)
                if (a[i] != 0)
                    return i;
            return 0;
        }
        __inline__ __attribute__ ((always_inline)) void flush (int x = 0){
            if (x < len)
                x = len;
            for (int i = 2 << log2_ (x - 1);i > len;-- i)
                a.push_back (0);
            len = (int) a.size ();
        }
        __inline__ __attribute__ ((always_inline)) void release (){
            len = 0, a.clear ();
        }
        __inline__ __attribute__ ((always_inline)) pnm Cut (int l) const {
            pnm res (l);
            for (int i = min (l, len) - 1;i >= 0;-- i)
                res[i] = a[i];
            return res;
        }
        __inline__ __attribute__ ((always_inline)) void Delete (int l){
            while (len > l){
                -- len;
                a.pop_back ();
            }
        }
        __inline__ __attribute__ ((always_inline)) void NTT (int swi){
            Reverse (a, len);
            int om, mid, x, y;
            for (int l = 2;l <= len;l <<= 1){
                om = power ((swi == - 1) ? Gi : G, (mod - 1) / l), mid = l >> 1;
                for (int i = 0;i < len;i += l)
                    for (int j = i, pw = 1;j < i + mid;++ j, pw = pw * om % mod){
                        x = a[j], y = a[j + mid] * pw % mod;
                        a[j] = (x + y) % mod;
                        a[j + mid] = (x - y + mod) % mod;
                    }
            }
            if (swi == - 1){
                int iv = Inverse (len);
                for (int i = 0;i < len;++ i)
                    a[i] = a[i] * iv % mod;
            }
        }
    };

    __inline__ __attribute__ ((always_inline)) pnm operator + (const pnm a, const pnm b){
        pnm res (max (a.len, b.len));
        for (int i = 0;i < a.len;++ i)
            res[i] = a[i];
        for (int i = 0;i < b.len;++ i)
            res[i] = (res[i] + b[i]) % mod;
        return res;
    }
    __inline__ __attribute__ ((always_inline)) pnm operator - (const pnm a, const pnm b){
        pnm res (max (a.len, b.len));
        for (int i = 0;i < a.len;++ i)
            res[i] = a[i];
        for (int i = 0;i < b.len;++ i)
            res[i] = (res[i] - b[i] + mod) % mod;
        return res;
    }
    __inline__ __attribute__ ((always_inline)) pnm operator ^ (const pnm a, const pnm b){
        // assert (a.len == b.len);
        pnm res = a;
        for (int i = 0;i < b.len;++ i)
            res[i] = res[i] * b[i];
        return res;
    }
    __inline__ __attribute__ ((always_inline)) pnm operator * (pnm a, pnm b){
        static int l = 0;
        l = a.len + b.len - 1;
        a.flush (l), b.flush (l);
        l = a.len;
        a.NTT (1), b.NTT (1);
        a = a ^ b;
        a.NTT (- 1);
        return a;
    }
    __inline__ __attribute__ ((always_inline)) pnm inv (pnm f, int le = 0){
        // assert (f[0] != 0);
        f.flush ();
        if (! le)
            le = f.len;
        pnm iv (1, Inverse (f[0]));
        for (int l = 2;l <= le;l <<= 1)
            iv = iv * (pnm (1, 2) - (f.Cut (l) * iv).Cut (l)), iv.Delete (l);
        return iv;
    }
    __inline__ __attribute__ ((always_inline)) pnm operator / (const pnm a, pnm b){
        // assert (b[0] != 0);
        return a * inv (b);
    }
    __inline__ __attribute__ ((always_inline)) void Rev (pnm &f, int l = 0){
        if (! l)
            l = f.len;
        f.flush (l);
        for (int i = 0;i < l - i - 1;++ i)
            swap (f[i], f[l - i - 1]);
    }
    __inline__ __attribute__ ((always_inline)) pnm operator | (pnm a, pnm b){
        int nn = a.deg (), mm = b.deg ();
        Rev (a, nn + 1), Rev (b, mm + 1);
        pnm c = a.Cut (nn - mm + 1) * inv (b.Cut (nn - mm + 1));
        c.Delete (nn - mm + 1);
        Rev (c, nn - mm + 1);
        return c;
    }
    __inline__ __attribute__ ((always_inline)) pnm operator % (const pnm a, const pnm b){
        return a - ((a | b) * b);
    }

    __inline__ __attribute__ ((always_inline)) pnm inte (pnm f){
        if (f.len == 0)
            return f;
        for (int i = 1;i < f.len;++ i)
            f[i - 1] = f[i] * i % mod;
        -- f.len, f.a.pop_back ();
        return f;
    }
    __inline__ __attribute__ ((always_inline)) pnm deri (pnm f, int _c = 0){
        static int jc[N + 5], inj;
        jc[0] = 1;
        for (int i = 1;i <= f.len;++ i)
            jc[i] = jc[i - 1] * i % mod;
        inj = Inverse (jc[f.len]);
        ++ f.len;
        f.a.push_back (0);
        for (int i = f.len - 1;i > 0;inj = inj * (i --) % mod)
            f[i] = f[i - 1] * jc[i - 1] % mod * inj % mod;
        f[0] = _c;
        return f;
    }
    __inline__ __attribute__ ((always_inline)) pnm LN (pnm f, int m = 0){
        // assert (f[0] == 1);
        f.flush ();
        if (! m)
            m = f.len;
        return deri (inte (f) * inv (f, m)).Cut (m);
    }
    __inline__ __attribute__ ((always_inline)) pnm EXP (pnm f, int m = 0){
        // assert (f[0] == 0);
        f.flush ();
        static int p;
        pnm g (1, 1);
        p = 1;
        if (! m)
            m = f.len;
        while (p < m){
            p <<= 1;
            g = g * (pnm (1, 1) - LN (g, p) + f.Cut (p));
            g.Delete (p);
        }
        g.Delete (m);
        return g;
    }
    __inline__ __attribute__ ((always_inline)) pnm SQRT (pnm f, int m = 0){
        // assert (f[0] == 1)
        f.flush ();
        static int p;
        pnm g (1, 1);
        p = 1;
        if (! m)
            m = f.len;
        while (p < m){
            p <<= 1;
            g = g - ((g * g).Cut (p) - f.Cut (p)) * pnm (1, Inverse (2)) * inv (g, p);
            g.Delete (p);
        }
        g.Delete (m);
        return g;
    }
    __inline__ __attribute__ ((always_inline)) pnm POWER (pnm f, int m, int n = 0){
        f.flush ();
        if (! n)
            n = f.len;
        int lowx = 0;
        while (lowx < f.len && ! f[lowx])
            ++ lowx;
        if (lowx == f.len)
            return pnm (n);
        if (lowx && (m >= mod || m * lowx > n))
            return pnm (n);
        int v = f[lowx];
        int iv = Inverse (v);
        for (int i = 0;i < f.len;++ i){
            if (i + lowx < f.len)
                f[i] = f[i + lowx] * iv % mod;
            else
                f[i] = 0;
        }
        f = EXP (pnm (1, m % mod) * LN (f), n).Cut (n);
        if (lowx){
            pnm g (m % mod * lowx + 1);
            g[m % mod * lowx] = power (v, m % (mod - 1));
            return f * g;
        }
        return f * pnm (1, power (v, m % (mod - 1)));
    }
}

using Polynomial::EXP;
using Polynomial::LN;
using Polynomial::POWER;
using Polynomial::SQRT;

signed main (){
    // GET_START

    

    // GET_END
    return 0;
}

标签:__,int,多项式,len,pnm,笔记,有限,inline,mod
From: https://www.cnblogs.com/imcaigou/p/18156794

相关文章

  • [算法学习笔记] 并查集
    提示:本文并非并查集模板讲解,是在模板基础上的进一步理解以及拓展。Review并查集可以用来维护集合问题。例如,已知\(a,b\)同属一个集合,\(b,c\)同属一个集合。那么\(a,b,c\)都属一个集合。并查集分为合并,查询操作。定义\(fa_i\)表示点\(i\)的父亲。为了降低复杂度,在fi......
  • Fast Möbius Transform 学习笔记
    小Tips:在计算机语言中\(\cup\)=&/and,\(\cap\)=|/orFirstStep.定义定义长度为\(2^n\)的序列的and卷积\(A=B*C\)为\(A_i=\sum_{j\cupk=i}{B_j*C_k}\)考虑快速计算SecondStep.变换定义长度为\(2^n\)的序列的Zeta变换为\[\hat{A}_i=\sum......
  • 笔记/C++中的数组排序
    在C++中,std::sort函数是一个用于对容器(如数组、向量等)进行排序的通用算法。它定义在<algorithm>头文件中,并接受两个迭代器参数,分别指向要排序的范围的开始和结束位置。此外,std::sort还可以接受一个可选的比较函数或lambda表达式,用于自定义排序规则。以下是std::sort函数的基本用......
  • SQL学习笔记
    --creattable--auto-generateddefinitioncreatetableemp(idintnullcomment'编号',worknovarchar(10)nullcomment'工号',namevarchar(10)nullcomment'名字',genderchar(1)......
  • 论文笔记-Two-phase flow regime identification based on the liquid-phase velocity
    对象:液相速度信息方法:CNN、LSTM、SVM目标:实现了水平管道内两相流态识别关注特征:从速度时间序列数据中提取的统计特征:均值、均方根和功率谱密度、最大速度比和最大速度差比结果:SVM-93.1%,CNN-94%,LSTM-不佳73.3%LSTM:总共使用了300秒的速度数据,然后将其分为180秒用于训练和......
  • PM 的基本技术训练 – 案例分析 在PM 带领下, 每个团队深入分析下面行业的软件, 找到行
    英语学习/词典App英语学习/词典App评级牛津高阶英汉双解词典app优点:权威的词汇分类,适合专业英语词汇学习,查词功能强大,支持通配符搜索。缺点:可能需要在特定区域的Appstore购买,价格较高。网易有道词典优点:用户评分高,专为iPad设计,提供多种语言翻译,适合学生使用。缺点:可......
  • 2024.4.22(周一)构建之法阅读笔记3
    第六章敏捷流程敏捷开发的原则是:1.尽早并持续地交付有价值的软件以满足顾客需求  2.敏捷流程欢迎需求的变化  3.经常发布可用的软件,发布间隔可以从几周到几个月,能短则短 4.业务人员和开发人员在项目开发过程中应该每天共同工作 5.以有进取心的人为项目核心,充分支持信......
  • 2024.4.18(周四)构建之法阅读笔记1
    第一章概论软件=程序+软件工程  软件企业=软件+商业模式  一个复杂的软件不但要有合理的软件架构、软件设计与实现,还要有各种文件和数据来描述各个程序文件之间的依赖关系、编译参数等等,这些都是软件构建的过程。软件开发的不同阶段:1.玩具阶段 2.业余爱好阶段 3.探索......
  • 2024.4.19(周五)构建之法阅读笔记2
    第四章两人合作在代码规范方面,可以分为两个部分:代码风格规范和代码设计规范。代码风格规范主要是缩进、行宽、括号、断行与空白的{}行、分行、命名、下划线、大小写、注释等;建民老师上课主要强调的是缩进、命名和注释。在代码设计规范方面,主要是函数、goto错误处理、类处理等。......
  • CDQ分治学习笔记
    1.简介CDQ分治是一种思想,类似于动态规划,依照原理和写法的不同,大致分为3类:解决与点对相关的问题1D动态规划的优化及转移通过CDQ分治,将一些动态问题转化为静态问题2.解决与点对相关的问题2.1.流程1.找到序列的中点mid2.将所有的点对(i,j)划分为三类a.\(i\lemi......