Note/Solution -「洛谷 P5158」「模板」多项式快速插值
\(\mathcal{Description}\)
Link.
给定 \(n\) 个点 \((x_i,y_i)\),求一个不超过 \(n-1\) 次的多项式 \(f(x)\),使得 \(f(x_i)\equiv y_i\pmod{998244353}\)。
\(n\le10^5\)。
\(\mathcal{Solution}\)
摆出 Lagrange 插值的式子:
\]
现在的问题是分母上的 \(\prod_{j\neq i}(x_i-x_j)\) 不好求。若令
\]
则
\]
中间的 \(\lim\) 可以直接洛出来啊,也可以构造 \(\lim_{x\rightarrow x_i}\frac{g(x)}{x-x_i}=\lim_{x\rightarrow x_i}\frac{g(x)-g(x_i)}{x-x_i}\),整理得到
\]
先分治求出 \(g\),然后多点求值求得 \(g'(x_i)\),再分治求出 \(f\) 即可。注意求 \(g\) 的过程量 \(\prod_{i=l}^r(z-x_i)\) 翻转系数就得到多点求值要用的 \(\prod_{i=l}^r(1-x_iz)\),可以节约一点常数。最终复杂度 \(\mathcal O(n\log^2 n)\)。
\(\mathcal{Code}\)
/*+Rainybunny+*/
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
typedef std::vector<int> Poly;
const int MAXN = 1 << 18, MOD = 998244353;
int n, x[MAXN + 5], y[MAXN + 5];
Poly X[MAXN << 2];
inline int mul(const int u, const int v) { return 1ll * u * v % MOD; }
inline int sub(int u, const int v) { return (u -= v) < 0 ? u + MOD : u; }
inline int add(int u, const int v) { return (u += v) < MOD ? u : u - MOD; }
inline int mpow(int u, int v) {
int ret = 1;
for (; v; u = mul(u, u), v >>= 1) ret = mul(ret, v & 1 ? u : 1);
return ret;
}
namespace PolyOper {
const int G = 3;
int omega[19][MAXN + 5];
inline void init() {
rep (i, 1, 18) {
int* wi = omega[i];
wi[0] = 1, wi[1] = mpow(G, MOD - 1 >> i);
rep (j, 2, (1 << i) - 1) wi[j] = mul(wi[j - 1], wi[1]);
}
}
inline void ntt(Poly& u, const int tp) {
static int rev[MAXN + 5]; int n = u.size();
rep (i, 0, n - 1) rev[i] = rev[i >> 1] >> 1 | (i & 1) * n >> 1;
rep (i, 0, n - 1) if (i < rev[i]) std::swap(u[i], u[rev[i]]);
for (int i = 1, stp = 1; stp < n; ++i, stp <<= 1) {
int* wi = omega[i];
for (int j = 0; j < n; j += stp << 1) {
rep (k, j, j + stp - 1) {
int ev = u[k], ov = mul(wi[k - j], u[k + stp]);
u[k] = add(ev, ov), u[k + stp] = sub(ev, ov);
}
}
}
if (!~tp) {
int inv = mpow(n, MOD - 2);
std::reverse(u.begin() + 1, u.end());
for (int& a: u) a = mul(a, inv);
}
}
inline Poly padd(Poly u, Poly v) {
if (u.size() < v.size()) u.swap(v);
rep (i, 0, int(v.size()) - 1) u[i] = add(u[i], v[i]);
return u;
}
inline Poly pmul(Poly u, Poly v) {
int res = u.size() + v.size() - 1, len = 1;
while (len < res) len <<= 1;
u.resize(len), v.resize(len);
ntt(u, 1), ntt(v, 1);
rep (i, 0, len - 1) u[i] = mul(u[i], v[i]);
ntt(u, -1);
return u.resize(res), u;
}
inline Poly pmulT(Poly u, Poly v) {
int n = u.size(), m = v.size();
std::reverse(v.begin(), v.end()), v = pmul(u, v);
rep (i, 0, n - 1) u[i] = v[i + m - 1];
return u;
}
inline void pinv(const int n, const Poly& u, Poly& r) {
if (n == 1) return void(r = { { mpow(u[0], MOD - 2) } });
static Poly tmp; pinv(n >> 1, u, r);
tmp.resize(n << 1), r.resize(n << 1);
rep (i, 0, n - 1) tmp[i] = i < u.size() ? u[i] : 0;
rep (i, n, (n << 1) - 1) tmp[i] = 0;
ntt(r, 1), ntt(tmp, 1);
rep (i, 0, (n << 1) - 1) r[i] = mul(r[i], sub(2, mul(tmp[i], r[i])));
ntt(r, -1), r.resize(n);
}
} // namespace PolyOper.
inline void init(const int u, const int l, const int r) {
if (l == r) return void(X[u] = { { 1, sub(0, x[l]) } });
int mid = l + r >> 1;
init(u << 1, l, mid), init(u << 1 | 1, mid + 1, r);
X[u] = PolyOper::pmul(X[u << 1], X[u << 1 | 1]);
}
inline void calcG(const int u, const int l, const int r, Poly F) {
F.resize(r - l + 1);
if (l == r) return void(y[l] = mul(y[l], mpow(F[0], MOD - 2)));
int mid = l + r >> 1;
calcG(u << 1, l, mid, PolyOper::pmulT(F, X[u << 1 | 1]));
calcG(u << 1 | 1, mid + 1, r, PolyOper::pmulT(F, X[u << 1]));
}
inline Poly calcF(const int u, const int l, const int r) {
std::reverse(X[u].begin(), X[u].end());
if (l == r) return { { y[l] } };
int mid = l + r >> 1;
Poly &&p(calcF(u << 1, l, mid)), &&q(calcF(u << 1 | 1, mid + 1, r));
return PolyOper::padd(PolyOper::pmul(p, X[u << 1 | 1]),
PolyOper::pmul(q, X[u << 1]));
}
int main() {
scanf("%d", &n);
rep (i, 0, n - 1) scanf("%d %d", &x[i], &y[i]);
PolyOper::init(), init(1, 0, n - 1);
int len = 1; while (len < n) len <<= 1;
Poly T; PolyOper::pinv(len, X[1], T);
Poly Q(X[1]); std::reverse(Q.begin(), Q.end());
rep (i, 0, n - 1) Q[i] = mul(i + 1, Q[i + 1]);
Q.resize(n);
calcG(1, 0, n - 1, PolyOper::pmulT(Q, T));
Poly&& ans = calcF(1, 0, n - 1);
for (int u: ans) printf("%d ", u);
putchar('\n');
return 0;
}
Note/Solution -「洛谷 P5158」「模板」多项式快速插值的更多相关文章
- 【洛谷P5158】 【模板】多项式快速插值
卡常严重,可有采用如下优化方案: 1.预处理单位根 2.少取几次模 3.复制数组时用 memcpy 4.进行多项式乘法项数少的时候直接暴力乘 5.进行多项式多点求值时如果项数小于500的话直接秦九昭展 ...
- 洛谷P5158 【模板】多项式快速插值
题面 传送门 前置芝士 拉格朗日插值,多项式多点求值 题解 首先根据拉格朗日插值公式我们可以暴力\(O(n^2)\)插出这个多项式,然而这显然是\(gg\)的 那么看看怎么优化,先来看一看拉格朗日插值 ...
- 【总结】对FFT的理解 / 【洛谷 P3803】 【模板】多项式乘法(FFT)
题目链接 \(\Huge\text{无图,慎入}\) \(FFT\)即快速傅里叶变换,用于加速多项式乘法. 如果暴力做卷积的话就是一个多项式的每个单项式去乘另一个多项式然后加起来,时间复杂度为\(O( ...
- 模板【洛谷P3390】 【模板】矩阵快速幂
P3390 [模板]矩阵快速幂 题目描述 给定n*n的矩阵A,求A^k 矩阵A的大小为n×m,B的大小为n×k,设C=A×B 则\(C_{i,j}=\sum\limits_{k=1}^{n}A_{i, ...
- 【洛谷P5050】 【模板】多项式多点求值
code: #include <bits/stdc++.h> #define ll long long #define ull unsigned long long #define set ...
- 「区间DP」「洛谷P1043」数字游戏
「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...
- 「 洛谷 」P2768 珍珠项链
珍珠项链 题目限制 内存限制:125.00MB 时间限制:1.00s 标准输入输出 题目知识点 动态规划 \(dp\) 矩阵 矩阵乘法 矩阵加速 矩阵快速幂 题目来源 「 洛谷 」P2768 珍珠项链 ...
- 「 洛谷 」P4539 [SCOI2006]zh_tree
小兔的话 推荐 小兔的CSDN [SCOI2006]zh_tree 题目限制 内存限制:250.00MB 时间限制:1.00s 标准输入输出 题目知识点 思维 动态规划 \(dp\) 区间\(dp\) ...
- 「 洛谷 」P2151 [SDOI2009]HH去散步
小兔的话 欢迎大家在评论区留言哦~ HH去散步 题目限制 内存限制:125.00MB 时间限制:1.00s 标准输入 标准输出 题目知识点 动态规划 \(dp\) 矩阵 矩阵乘法 矩阵加速 矩阵快速幂 ...
随机推荐
- Spring循环依赖原理
Spring循环依赖的原理解析 1.什么是循环依赖? 我们使用Spring的时候,在一个对象中注入另一个对象,但是另外的一个对象中也包含该对象.如图: 在Student中包含了teacher的一个 ...
- 封装OCX
封装OCX的办法有2种: 1. 使用C++的MFC activex项目生成OCX 2. 使用C#的用户控件生成OCX(.net core好像不支持) 注意:以管理员身份运行Visual Studio ...
- 【C++】STL容器
STL容器 标签:c++ 目录 STL容器 容器的成员函数 所有容器都有的 顺序容器和关联容器 顺序容器(vector/string/list/deque) 容器 vector 构造函数 操作 set ...
- 【记录一个问题】opencv中使用UMat类,在析构的时候出现refcount == 0的断言错误
发生coredump时的错误信息如下: terminate called after throwing an instance of 'cv::Exception' what(): OpenCV(4. ...
- 从带Per-Building数据的KML/COLLADA中创建3D Tiles
Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 许多Cesium的使用者经常需要将整个城市的数十万个三维建筑可视 ...
- 理解cpu load
三种命令 1. w 2. uptime 3. top CPU负载和CPU利用率的区别 1)CPU利用率:显示的是程序在运行期间实时占用的CPU百分比 2)CPU负载:显示的是一段时间内正在使用和等待使 ...
- python列表删除和排序
1.del message[0]---知道位置不知道值 2.message.remove('a')----知道值不知道位置 3.message.sort()--正向永久排序 4.message.sor ...
- Linux 总结
查看端口 lsof -i:8000 查看进程 ps -ef | grep python netstat -tunlp |grep 端口号 拷贝 cp 文件 生成文件名 做软连接找到目标文件目录 ...
- C++的set重载运算符
转载: https://www.cnblogs.com/zhihaospace/p/12843802.html set 容器模版需要3个泛型参数,如下: template<class T, cl ...
- Lesson8——Pandas reindex重置索引
pandas目录 1 简介 重置索引(reindex)可以更改原 DataFrame 的行标签或列标签,并使更改后的行.列标签与 DataFrame 中的数据逐一匹配.通过重置索引操作,您可以完成对现 ...