• 预备知识:FFT/NTT

  • 多项式的逆

    给定一个多项式 F(x)F(x)F(x),请求出一个多项式 G(x)G(x)G(x),满足 F(x)∗G(x)≡1(mod xn)F(x)*G(x) \equiv 1(mod\ x^n)F(x)∗G(x)≡1(mod xn)。

    系数对 998244353998244353998244353 取模,1≤n≤1051≤n≤10^51≤n≤105

    首先将多项式的长度拓展至222的次幂,然后我们要求的是

    G(x)∗F(x)≡1 (mod xn)G(x)*F(x) \equiv 1\ (mod\ x^n)G(x)∗F(x)≡1 (mod xn)假设已经求出了

    H(x)∗F(x)≡1 (mod x⌈n2⌉)H(x)*F(x) \equiv 1\ (mod\ x^{⌈\frac n2⌉})H(x)∗F(x)≡1 (mod x⌈2n​⌉)又因为有

    G(x)∗F(x)≡1 (mod x⌈n2⌉)G(x)*F(x) \equiv 1\ (mod\ x^{⌈\frac n2⌉})G(x)∗F(x)≡1 (mod x⌈2n​⌉)两式相减有

    (G(x)−H(x))∗F(x)≡0 (mod x⌈n2⌉)(G(x)-H(x))*F(x) \equiv 0\ (mod\ x^{⌈\frac n2⌉})(G(x)−H(x))∗F(x)≡0 (mod x⌈2n​⌉)G(x)−H(x)≡0 (mod x⌈n2⌉)G(x)-H(x) \equiv 0\ (mod\ x^{⌈\frac n2⌉})G(x)−H(x)≡0 (mod x⌈2n​⌉)因为左边得到的多项式前⌈n2⌉{⌈\frac n2⌉}⌈2n​⌉项都是000,平方前nnn项都是000,所以有

    (G(x)−H(x))2≡0 (mod xn)(G(x)-H(x))^2 \equiv 0\ (mod\ x^{n})(G(x)−H(x))2≡0 (mod xn)G2(x)−2G(x)H(x)+H2(x)≡0 (mod xn)G^2(x)-2G(x)H(x)+H^2(x)\equiv 0\ (mod\ x^n)G2(x)−2G(x)H(x)+H2(x)≡0 (mod xn)两边同时乘以F(x)F(x)F(x)

    G(x)−2H(x)+H2(x)F(x)≡0 (mod xn)G(x)-2H(x)+H^2(x)F(x)\equiv 0\ (mod\ x^n)G(x)−2H(x)+H2(x)F(x)≡0 (mod xn)G(x)≡2H(x)−H2(x)F(x) (mod xn)G(x)\equiv2H(x)-H^2(x)F(x)\ (mod\ x^n)G(x)≡2H(x)−H2(x)F(x) (mod xn)

    我们就得到了递推式,时间复杂度如下T(n)=T(n/2)+O(nlog2n)=O(nlog2n)T(n)=T(n/2)+O(nlog_2n)=O(nlog_2n)T(n)=T(n/2)+O(nlog2​n)=O(nlog2​n)

  • 多项式开根

    给定一个 n−1n−1n−1 次多项式 F(x)F(x)F(x),求一个在 mod xnmod\ x^nmod xn 意义下的多项式 G(x)G(x)G(x),使得 G2(x)≡F(x) (mod xn)G^2(x) \equiv F(x) \ (mod\ x^n)G2(x)≡F(x) (mod xn)(多项式的系数在 mod 998244353mod\ 998244353mod 998244353 的意义下进行运算,1≤n≤1051≤n≤10^51≤n≤105)

    同样假设求出了H2(x)≡F(x) (mod x⌈n2⌉)H^2(x)\equiv F(x)\ (mod\ x^{⌈\frac n2⌉})H2(x)≡F(x) (mod x⌈2n​⌉)且有

    G2(x)≡F(x) (mod x⌈n2⌉)G^2(x)\equiv F(x)\ (mod\ x^{⌈\frac n2⌉})G2(x)≡F(x) (mod x⌈2n​⌉)所以

    G(x)−H(x)≡0 (mod x⌈n2⌉)G(x)-H(x) \equiv 0\ (mod\ x^{⌈\frac n2⌉})G(x)−H(x)≡0 (mod x⌈2n​⌉)(G(x)−H(x))2≡0 (mod xn)(G(x)-H(x))^2 \equiv 0\ (mod\ x^{n})(G(x)−H(x))2≡0 (mod xn)G2(x)−2G(x)H(x)+H2(x)≡0 (mod xn)G^2(x)-2G(x)H(x)+H^2(x)\equiv 0\ (mod\ x^n)G2(x)−2G(x)H(x)+H2(x)≡0 (mod xn)F(x)−2G(x)H(x)+H2(x)≡0 (mod xn)F(x)-2G(x)H(x)+H^2(x)\equiv 0\ (mod\ x^n)F(x)−2G(x)H(x)+H2(x)≡0 (mod xn)

    所以2G(x)≡F(x)H(x)+H(x) (mod xn)2G(x)\equiv \frac{F(x)}{H(x)}+H(x)\ (mod\ x^n)2G(x)≡H(x)F(x)​+H(x) (mod xn)

    此处除法转化为乘上多项式的逆,就能递推了,时间复杂度也是O(nlog2n)O(nlog_2n)O(nlog2​n)

  • 多项式求自然对数

    给出 n−1n−1n−1 次多项式 F(x)F(x)F(x),求一个 mod xnmod\ x^nmod xn 下的多项式 G(x)G(x)G(x),满足 G(x)≡ln⁡F(x) (mod xn)G(x) \equiv \ln F(x)\ (mod\ x^n)G(x)≡lnF(x) (mod xn).

    多项式的系数在 mod 998244353mod\ 998244353mod 998244353 的意义下进行运算,1≤n≤1051≤n≤10^51≤n≤105

    G(x)=ln F(x)=∫F′(x)F(x)dx=∫F′(x)F−1(x)dx\begin{aligned}
    G(x)&=ln\ F(x)\\
    &=\int \frac{F'(x)}{F(x)}dx\\
    &=\int F'(x)F^{-1}(x)dx
    \end{aligned}G(x)​=ln F(x)=∫F(x)F′(x)​dx=∫F′(x)F−1(x)dx​所以G=∫F′F−1G=\int F'F^{-1}G=∫F′F−1,直接多项式求逆+多项式求导积分就行了

    求逆复杂度O(nlog2n)O(nlog_2n)O(nlog2​n),求导积分复杂度O(n)O(n)O(n),总时间复杂度仍是O(nlog2n)O(nlog_2n)O(nlog2​n)

  • 多项式exp

    多项式exp是用牛顿迭代做的,我也不会证明牛顿迭代,只会背公式.

    本来牛顿迭代公式是这个样子的xn+1=xn−f(xn)f′(xn)x_{n+1}=x_n-\frac{f(x_n)}{f'(x_n)}xn+1​=xn​−f′(xn​)f(xn​)​

    对于多项式的牛顿迭代长这样un+1=un−f(u,v)fu(u,v)u_{n+1}=u_n-\frac{f(u,v)}{f_u(u,v)}un+1​=un​−fu​(u,v)f(u,v)​

    • 其中f(u,v)f(u,v)f(u,v)表示已知多项式为vvv,未知多项式为uuu的多项式方程,就拿exp为例,原方程为u≡ev (mod xn)u\equiv e^v\ (mod\ x^n)u≡ev (mod xn)两边取对数为ln u≡v (mod xn)ln u−v≡0 (mod xn)\begin{aligned}ln\ u\equiv v\ (mod\ x^n)\\ln\ u-v\equiv 0\ (mod\ x^n)\end{aligned}ln u≡v (mod xn)ln u−v≡0 (mod xn)​那么f(u,v)=ln u−vf(u,v)=ln\ u-vf(u,v)=ln u−v.
    • fu(u,v)f_u(u,v)fu​(u,v)表示的是f(u,v)f(u,v)f(u,v)对uuu取导.注意是对uuu,不是对多项式里的xxx.

      举个栗子,ln vln\ vln v对vvv求导是1v\frac 1vv1​,但是对xxx求导是v′v\frac{v'}{v}vv′​(此处v′v'v′是对xxx求导)

    那么我们就可以推推柿子un+1≡un−(ln un−v(ln un−v)′) (mod xn)≡un−(ln un−v1un) (mod xn)≡un−(ln un−v)un (mod xn)≡un(1−ln un+v) (mod xn)\begin{aligned}u_{n+1}&\equiv u_n-\left(\frac{ln\ u_n-v}{(ln\ u_n-v)'}\right)\ &(mod\ x^n)\\&\equiv u_n-\left(\frac{ln\ u_n-v}{\frac1{u_n}}\right)\ &(mod\ x^n)\\&\equiv u_n-(ln\ u_n-v)u_n\ &(mod\ x^n)\\&\equiv u_n(1-ln\ u_n+v)\ &(mod\ x^n)\end{aligned}un+1​​≡un​−((ln un​−v)′ln un​−v​) ≡un​−(un​1​ln un​−v​) ≡un​−(ln un​−v)un​ ≡un​(1−ln un​+v) ​(mod xn)(mod xn)(mod xn)(mod xn)​然后就套用前面的模板迭代做了

    TIP:TIP:TIP:其实多项式开根也可以用这个方法推出来,有兴趣可以去试试看.

代码
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
namespace READ {
inline void read(int &num) {
char ch; int flg=1;
while((ch=getchar())<'0'||ch>'9')if(ch=='-')flg=-flg;
for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar());
num*=flg;
}
}
using namespace READ;
const int mod = 998244353, G = 3, inv2 = 499122177;
const int MAXN = 1<<18; // 注意MAXN要大于2*n
int rev[MAXN], Wn[MAXN+1][2], inv[MAXN+1];
inline void reset(int len) { for(int i = 0; i < len; ++i) rev[i] = (rev[i>>1]>>1)|((i&1)?(len>>1):0); }
inline int qmul(int a, int b) {
int res = 1;
while(b) {
if(b&1) res = 1ll * res * a % mod;
a = 1ll * a * a % mod; b>>=1;
}
return res;
}
inline int add(int x, int y) { return x+y>mod ? x+y-mod : x+y; }
namespace Poly {
int A[MAXN],B[MAXN],C[MAXN],D[MAXN],E[MAXN],F[MAXN],GG[MAXN],H[MAXN];//每个不同函数开不同的临时数组,否则可能重复使用
inline void clear(int *b, int n) { for(int i = 0; i < n; ++i) b[i] = 0; } //多清零
inline void NTT(int *arr, int n, int flg) {
int wn, w, A0, wA1, i, j, k;
for(i = 0; i < n; ++i) if(i < rev[i]) swap(arr[i], arr[rev[i]]);
for(i = 2; i <= n; i<<=1) {
wn = Wn[i][(~flg)?0:1];
for(j = 0, w = 1; j < n; j += i, w = 1)
for(k = j; k < j + i/2; ++k, w = 1ll * w * wn % mod) {
A0 = arr[k], wA1 = 1ll * w * arr[k + i/2] % mod;
arr[k] = add(A0, wA1);
arr[k + i/2] = add(A0, -wA1+mod);
}
}
if(!(~flg)) for(i = 0; i < n; ++i) arr[i] = 1ll * arr[i] * inv[n] % mod;
} inline void INV(int *a, int *b, int n) {
clear(b, n<<1), b[0] = qmul(a[0], mod-2);
clear(A, n<<1), clear(B, n<<1);
int len, lim;
for(len = 2; len < (n<<1); len<<=1) {
lim = len<<1;
for(int i = 0; i < len; ++i) A[i] = a[i], B[i] = b[i];
reset(lim); NTT(A, lim, 1); NTT(B, lim, 1);
for(int i = 0; i < lim; ++i) b[i] = ((2ll - 1ll * A[i] * B[i] % mod) * B[i] % mod + mod) % mod;
NTT(b, lim, -1);
for(int i = len; i < lim; ++i) b[i] = 0;
}
for(int i = 0; i < len; ++i) A[i] = B[i] = 0;
for(int i = n; i < len; ++i) b[i] = 0;
} inline void SQRT(int *a, int *b, int n) {
clear(b, n<<1); b[0] = int(sqrt(a[0])+0.5);
int len, lim, *A = GG, *B = H;
clear(A, n<<1), clear(B, n<<1);
for(len = 2; len < (n<<1); len<<=1) {
lim = len<<1;
for(int i = 0; i < len; ++i) A[i] = a[i];
INV(b, B, len);
reset(lim); NTT(A, lim, 1); NTT(B, lim, 1);
for(int i = 0; i < lim; ++i) A[i] = 1ll * A[i] * B[i] % mod;
NTT(A, lim, -1);
for(int i = 0; i < len; ++i) b[i] = 1ll * (b[i] + A[i]) % mod * inv2 % mod;
for(int i = len; i < lim; ++i) b[i] = 0;
}
for(int i = 0; i < len; ++i) A[i] = B[i] = 0;
for(int i = n; i < len; ++i) b[i] = 0;
} inline void INT(int *a, int *b, int n) {
clear(b, n<<1);
for(int i = n-1; i; --i)
b[i] = 1ll * a[i-1] * qmul(i, mod-2) % mod;
b[0] = 0;
} inline void DIF(int *a, int *b, int n) {
clear(b, n<<1);
for(int i = 1; i < n; ++i)
b[i-1] = 1ll * a[i] * i % mod;
b[n-1] = 0;
} inline void LN(int *a, int *b, int n) {
int *A = E, *B = F, len = 1;
clear(A, n<<1), clear(B, n<<1);
INV(a, A, n); DIF(a, B, n);
while(len < (n<<1)) len<<=1;
reset(len); NTT(A, len, 1); NTT(B, len, 1);
for(int i = 0; i < len; ++i) A[i] = 1ll * A[i] * B[i] % mod;
NTT(A, len, -1); INT(A, b, n);
} inline void EXP(int *a, int *b, int n) {
clear(b, n<<1), b[0] = 1;
int len, lim, *A = C, *B = D;
clear(A, n<<1), clear(B, n<<1);
for(len = 2; len < (n<<1); len<<=1) {
lim = len<<1;
LN(b, A, len);
for(int i = 0; i < len; ++i) A[i] = ((i==0)-A[i]+a[i]+mod) % mod, B[i] = b[i];
for(int i = len; i < lim; ++i) A[i] = B[i] = 0;
reset(lim); NTT(A, lim, 1); NTT(B, lim, 1);
for(int i = 0; i < lim; ++i) A[i] = 1ll * A[i] * B[i] % mod;
NTT(A, lim, -1);
for(int i = 0; i < len; ++i) b[i] = A[i];
}
for(int i = n; i < len; ++i) b[i] = 0;
}
}
using namespace Poly;
inline void Pre() { //预处理原根及逆元
for(int i = 1; i <= MAXN; i<<=1)
Wn[i][0] = qmul(G, (mod-1)/i),
Wn[i][1] = qmul(G, (mod-1)-(mod-1)/i),
inv[i] = qmul(i, mod-2);
}
int main () {
Pre();
}

Upd:Upd:Upd:现在看,发现我以前sb了…不用开不同的数组来取地址,直接static定义就行了…

  • 带余除法(待更…)

  • 多项式多点求值(待更…)

多项式的各类计算(多项式的逆/开根/对数/exp/带余除法/多点求值)的更多相关文章

  1. [模板]多项式全家桶小记(求逆,开根,ln,exp)

    前言 这里的全家桶目前只包括了\(ln,exp,sqrt\).还有一些类似于带余数模,快速幂之类用的比较少的有时间再更,\(NTT\)这种前置知识这里不多说. 还有一些基本的导数和微积分内容要了解,建 ...

  2. 洛谷P5282 【模板】快速阶乘算法(多项式多点求值+MTT)

    题面 传送门 前置芝士 \(MTT\),多项式多点求值 题解 这题法老当初好像讲过--而且他还说这种题目如果模数已经给定可以直接分段打表艹过去 以下是题解 我们设 \[F(x)=\prod_{i=0} ...

  3. luogu P5667 拉格朗日插值2 拉格朗日插值 多项式多点求值 NTT

    LINK:P5667 拉格朗日插值2 给出了n个连续的取值的自变量的点值 求 f(m+1),f(m+2),...f(m+n). 如果我们直接把f这个函数给插值出来就变成了了多项式多点求值 这个难度好像 ...

  4. 洛谷P5050 【模板】多项式多点求值

    传送门 人傻常数大.jpg 因为求逆的时候没清零结果调了几个小时-- 前置芝士 多项式除法,多项式求逆 什么?你不会?左转你谷模板区,包教包会 题解 首先我们要知道一个结论\[f(x_0)\equiv ...

  5. 【洛谷P5050】 【模板】多项式多点求值

    code: #include <bits/stdc++.h> #define ll long long #define ull unsigned long long #define set ...

  6. 【BZOJ3625】【CF438E】小朋友和二叉树 NTT 生成函数 多项式开根 多项式求逆

    题目大意 考虑一个含有\(n\)个互异正整数的序列\(c_1,c_2,\ldots ,c_n\).如果一棵带点权的有根二叉树满足其所有顶点的权值都在集合\(\{c_1,c_2,\ldots ,c_n\ ...

  7. 【learning】多项式相关(求逆、开根、除法、取模)

    (首先要%miskcoo,这位dalao写的博客(这里)实在是太强啦qwq大部分多项式相关的知识都是从这位dalao博客里面学的,下面这篇东西是自己对其博客学习后的一些总结和想法,大部分是按照其博客里 ...

  8. 【BZOJ3625】【codeforces438E】小朋友和二叉树 生成函数+多项式求逆+多项式开根

    首先,我们构造一个函数$G(x)$,若存在$k∈C$,则$[x^k]G(x)=1$. 不妨设$F(x)$为最终答案的生成函数,则$[x^n]F(x)$即为权值为$n$的神犇二叉树个数. 不难推导出,$ ...

  9. FFT模板 生成函数 原根 多项式求逆 多项式开根

    FFT #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> ...

随机推荐

  1. Linux Docker Introduction

    Setup on Ubuntu. 前提条件: Docker需要两个重要的安装要求: 它仅适用于64位Linux安装,注意:是64位的Linux系统. 它需要Linux内核版本3.10或更高版本. 要查 ...

  2. Flume和 Sqoop

    Sqoop简介 Sqoop是一种旨在有效地在Apache Hadoop和诸如关系数据库等结构化数据存储之间传输大量数据的工具 原理: 将导入或导出命令翻译成Mapreduce程序来实现. 在翻译出的M ...

  3. LInux基础(04)项目设计一(理解链表管理协议的代码架构)

    要设计好一个项目必须要有一个健全的代码框架 一个结构体内有数据域和处理数据的函数指针, 先实现管理链表的函数 增加节点  删除节点  清空链表  遍历节点对每个节点进行操作 再实现协议的注册 把对象s ...

  4. java网站架构演变过程

    网站架构演变过程. .传统架构.传统的SSH架构,分为三层架构web控制层.业务逻辑层.数据库访问层..传统架构也就是单点应用,就是大家在刚开始初学JavaEE技术的时候SSH架构或者SSM架构,业务 ...

  5. .net Dapper 学习系列(2) ---Dapper进阶

    目录 写在前面 前期准备 Dapper 单表批量添加 在Dapper 多表查询 在Dapper 调用存储过程 在Dapper 使用QueryMultiple进行多表查询 在Dapper 使用事务进行多 ...

  6. Tornado + Bootstrap 快速搭建自己的web应用

    前言 最近用 python tordado 框架, 整了一个模板页面, 用于接入与发布数据的展示, tornado 简单易用, bootstrap 比较流行, 用起来也省事, 配合起来做些小案例非常迅 ...

  7. HDU2476 String painter(DP)

    题目 String painter 给出两个字符串s1,s2.对于每次操作可以将 s1 串中的任意一个子段变成另一个字符.问最少需要多少步操作能将s1串变为s2串. 解析 太妙了这个题,mark一下. ...

  8. Weak Session IDs

    工具的使用 首先github上下载火狐插件(正版收费),按F12调用 服务器生成sessionID通过response返回给浏览器,sessionID存放在浏览器cookie中,然后再通过cookie ...

  9. kali之使用sqlmap进行sql注入

    sqlmap简介 sqlmap支持五种不同的注入模式: 1.基于布尔的盲注,即可以根据返回页面判断条件真假的注入. 2.基于时间的盲注,即不能根据页面返回内容判断任何信息,用条件语句查看时间延迟语句是 ...

  10. Linq Left Join;linq左连接 (转载)

    来源 https://www.cnblogs.com/xinjian/archive/2010/11/17/1879959.html 准备一些测试数据,如下: use Test Create tabl ...