\(\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 插值的式子:

\[f(z)=\sum_{i=1}^ny_i\prod_{j\neq i}\frac{z-x_j}{x_i-x_j}.
\]

现在的问题是分母上的 \(\prod_{j\neq i}(x_i-x_j)\) 不好求。若令

\[g(x)=\prod_{i=1}^n(x-x_i),
\]

\[f(z)=\sum_{i=1}^ny_i\left(\lim_{x\rightarrow x_i}\frac{g(x)}{x-x_i}\right)\prod_{i\neq j}(z-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}\),整理得到

\[f(z)=\sum_{i=1}^n\frac{y_i}{g'(x_i)}\prod_{i\neq j}(z-x_j).
\]

先分治求出 \(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}\)

  1. /*+Rainybunny+*/
  2. #include <bits/stdc++.h>
  3. #define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
  4. #define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)
  5. typedef std::vector<int> Poly;
  6. const int MAXN = 1 << 18, MOD = 998244353;
  7. int n, x[MAXN + 5], y[MAXN + 5];
  8. Poly X[MAXN << 2];
  9. inline int mul(const int u, const int v) { return 1ll * u * v % MOD; }
  10. inline int sub(int u, const int v) { return (u -= v) < 0 ? u + MOD : u; }
  11. inline int add(int u, const int v) { return (u += v) < MOD ? u : u - MOD; }
  12. inline int mpow(int u, int v) {
  13. int ret = 1;
  14. for (; v; u = mul(u, u), v >>= 1) ret = mul(ret, v & 1 ? u : 1);
  15. return ret;
  16. }
  17. namespace PolyOper {
  18. const int G = 3;
  19. int omega[19][MAXN + 5];
  20. inline void init() {
  21. rep (i, 1, 18) {
  22. int* wi = omega[i];
  23. wi[0] = 1, wi[1] = mpow(G, MOD - 1 >> i);
  24. rep (j, 2, (1 << i) - 1) wi[j] = mul(wi[j - 1], wi[1]);
  25. }
  26. }
  27. inline void ntt(Poly& u, const int tp) {
  28. static int rev[MAXN + 5]; int n = u.size();
  29. rep (i, 0, n - 1) rev[i] = rev[i >> 1] >> 1 | (i & 1) * n >> 1;
  30. rep (i, 0, n - 1) if (i < rev[i]) std::swap(u[i], u[rev[i]]);
  31. for (int i = 1, stp = 1; stp < n; ++i, stp <<= 1) {
  32. int* wi = omega[i];
  33. for (int j = 0; j < n; j += stp << 1) {
  34. rep (k, j, j + stp - 1) {
  35. int ev = u[k], ov = mul(wi[k - j], u[k + stp]);
  36. u[k] = add(ev, ov), u[k + stp] = sub(ev, ov);
  37. }
  38. }
  39. }
  40. if (!~tp) {
  41. int inv = mpow(n, MOD - 2);
  42. std::reverse(u.begin() + 1, u.end());
  43. for (int& a: u) a = mul(a, inv);
  44. }
  45. }
  46. inline Poly padd(Poly u, Poly v) {
  47. if (u.size() < v.size()) u.swap(v);
  48. rep (i, 0, int(v.size()) - 1) u[i] = add(u[i], v[i]);
  49. return u;
  50. }
  51. inline Poly pmul(Poly u, Poly v) {
  52. int res = u.size() + v.size() - 1, len = 1;
  53. while (len < res) len <<= 1;
  54. u.resize(len), v.resize(len);
  55. ntt(u, 1), ntt(v, 1);
  56. rep (i, 0, len - 1) u[i] = mul(u[i], v[i]);
  57. ntt(u, -1);
  58. return u.resize(res), u;
  59. }
  60. inline Poly pmulT(Poly u, Poly v) {
  61. int n = u.size(), m = v.size();
  62. std::reverse(v.begin(), v.end()), v = pmul(u, v);
  63. rep (i, 0, n - 1) u[i] = v[i + m - 1];
  64. return u;
  65. }
  66. inline void pinv(const int n, const Poly& u, Poly& r) {
  67. if (n == 1) return void(r = { { mpow(u[0], MOD - 2) } });
  68. static Poly tmp; pinv(n >> 1, u, r);
  69. tmp.resize(n << 1), r.resize(n << 1);
  70. rep (i, 0, n - 1) tmp[i] = i < u.size() ? u[i] : 0;
  71. rep (i, n, (n << 1) - 1) tmp[i] = 0;
  72. ntt(r, 1), ntt(tmp, 1);
  73. rep (i, 0, (n << 1) - 1) r[i] = mul(r[i], sub(2, mul(tmp[i], r[i])));
  74. ntt(r, -1), r.resize(n);
  75. }
  76. } // namespace PolyOper.
  77. inline void init(const int u, const int l, const int r) {
  78. if (l == r) return void(X[u] = { { 1, sub(0, x[l]) } });
  79. int mid = l + r >> 1;
  80. init(u << 1, l, mid), init(u << 1 | 1, mid + 1, r);
  81. X[u] = PolyOper::pmul(X[u << 1], X[u << 1 | 1]);
  82. }
  83. inline void calcG(const int u, const int l, const int r, Poly F) {
  84. F.resize(r - l + 1);
  85. if (l == r) return void(y[l] = mul(y[l], mpow(F[0], MOD - 2)));
  86. int mid = l + r >> 1;
  87. calcG(u << 1, l, mid, PolyOper::pmulT(F, X[u << 1 | 1]));
  88. calcG(u << 1 | 1, mid + 1, r, PolyOper::pmulT(F, X[u << 1]));
  89. }
  90. inline Poly calcF(const int u, const int l, const int r) {
  91. std::reverse(X[u].begin(), X[u].end());
  92. if (l == r) return { { y[l] } };
  93. int mid = l + r >> 1;
  94. Poly &&p(calcF(u << 1, l, mid)), &&q(calcF(u << 1 | 1, mid + 1, r));
  95. return PolyOper::padd(PolyOper::pmul(p, X[u << 1 | 1]),
  96. PolyOper::pmul(q, X[u << 1]));
  97. }
  98. int main() {
  99. scanf("%d", &n);
  100. rep (i, 0, n - 1) scanf("%d %d", &x[i], &y[i]);
  101. PolyOper::init(), init(1, 0, n - 1);
  102. int len = 1; while (len < n) len <<= 1;
  103. Poly T; PolyOper::pinv(len, X[1], T);
  104. Poly Q(X[1]); std::reverse(Q.begin(), Q.end());
  105. rep (i, 0, n - 1) Q[i] = mul(i + 1, Q[i + 1]);
  106. Q.resize(n);
  107. calcG(1, 0, n - 1, PolyOper::pmulT(Q, T));
  108. Poly&& ans = calcF(1, 0, n - 1);
  109. for (int u: ans) printf("%d ", u);
  110. putchar('\n');
  111. return 0;
  112. }

Note/Solution -「洛谷 P5158」「模板」多项式快速插值的更多相关文章

  1. 【洛谷P5158】 【模板】多项式快速插值

    卡常严重,可有采用如下优化方案: 1.预处理单位根 2.少取几次模 3.复制数组时用 memcpy 4.进行多项式乘法项数少的时候直接暴力乘 5.进行多项式多点求值时如果项数小于500的话直接秦九昭展 ...

  2. 洛谷P5158 【模板】多项式快速插值

    题面 传送门 前置芝士 拉格朗日插值,多项式多点求值 题解 首先根据拉格朗日插值公式我们可以暴力\(O(n^2)\)插出这个多项式,然而这显然是\(gg\)的 那么看看怎么优化,先来看一看拉格朗日插值 ...

  3. 【总结】对FFT的理解 / 【洛谷 P3803】 【模板】多项式乘法(FFT)

    题目链接 \(\Huge\text{无图,慎入}\) \(FFT\)即快速傅里叶变换,用于加速多项式乘法. 如果暴力做卷积的话就是一个多项式的每个单项式去乘另一个多项式然后加起来,时间复杂度为\(O( ...

  4. 模板【洛谷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, ...

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

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

  6. 「区间DP」「洛谷P1043」数字游戏

    「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...

  7. 「 洛谷 」P2768 珍珠项链

    珍珠项链 题目限制 内存限制:125.00MB 时间限制:1.00s 标准输入输出 题目知识点 动态规划 \(dp\) 矩阵 矩阵乘法 矩阵加速 矩阵快速幂 题目来源 「 洛谷 」P2768 珍珠项链 ...

  8. 「 洛谷 」P4539 [SCOI2006]zh_tree

    小兔的话 推荐 小兔的CSDN [SCOI2006]zh_tree 题目限制 内存限制:250.00MB 时间限制:1.00s 标准输入输出 题目知识点 思维 动态规划 \(dp\) 区间\(dp\) ...

  9. 「 洛谷 」P2151 [SDOI2009]HH去散步

    小兔的话 欢迎大家在评论区留言哦~ HH去散步 题目限制 内存限制:125.00MB 时间限制:1.00s 标准输入 标准输出 题目知识点 动态规划 \(dp\) 矩阵 矩阵乘法 矩阵加速 矩阵快速幂 ...

随机推荐

  1. js 模块化 -- 基本的导出与引入class模块

    1.目录结构 2.类语法与导出 class food { } //定义常量 let c = "苹果"; //正确的函数写法 food.prototype.getfood = fun ...

  2. vue iview element-ui兼容IE11浏览器

    首先安装babel-polyfill npm install babel-polyfill --save-dev 入口文件引用,在main.js中引用 import 'babel-polyfill' ...

  3. 【PTA】6-2 读文章(*) (31 分)

    请编写函数,从文件中读出文章,将其输出到屏幕上. 函数原型 void ReadArticle(FILE *f); 说明:参数 f 为文件指针.函数读出 f 所指示文件中的文章,将其输出到屏幕上. 裁判 ...

  4. 嫌Excel VBA执行速度慢,这些建议你一定要看

    Excel是办公利器,这无需多言.尤其在办公室,Excel用的熟练与否,会的Excel知识点多不多,很大程度上决定了你工作是否高效,能否按时打卡下班.可我们也时常听到这样的吐槽:Excel好是好,可就 ...

  5. POSIX之共享内存

    shm_write.c: #include<stdio.h> #include<stdlib.h> #include <stdlib.h> #include < ...

  6. golang操作mysql

    1. 安装mysql驱动库和sqlx基于官方sql库的扩展库 go get github.com/go-sql-driver/mysql go get github.com/jmoiron/sqlx ...

  7. java-异常-自定义异常异常类的抛出throws

    1 package p1.exception; 2 /* 3 * 对于角标是整数不存在,可以用角标越界表示, 4 * 对于负数为角标的情况,准备用负数角标异常来表示. 5 * 6 * 负数角标这种异常 ...

  8. 在Chrome中打开网页时出现以下问题 您的连接不是私密连接 攻击者可能会试图从 x.x.x.x 窃取您的信息(例如:密码、通讯内容或信用卡信息)

    现象:在Chrome中打开网页时出现以下问题 您的连接不是私密连接 攻击者可能会试图从 x.x.x.x 窃取您的信息(例如:密码.通讯内容或信用卡信息). 当点开"了解详情"后显示 ...

  9. /usr/local /opt

    Linux 的软件安装目录是也是有讲究的,理解这一点,在对系统管理是有益的 /usr:系统级的目录,可以理解为C:/Windows/, /usr/lib理解为C:/Windows/System32. ...

  10. 学习JAVAWEB第十八天

    今天解决了登录过程中的数据库连接池的一些问题,如本地服务器不能被访问,主要是连接池的配置文件的问题 明天做一个htm页面,不同用户的不同html页面