1 条题解
-
0
🔍 问题转化
题目给出了 的表达式,并要求计算 。将 约去后, 的表达式为:
$$E_j = \sum_{ij} \frac{q_i}{(i-j)^2} $$ 为了简化问题,我们定义辅助函数:
- 令 (多项式的系数)
- 令 (注意 )
这样, 可以拆解为两部分:
- 第一部分:,这已经是一个标准的卷积形式。
- 第二部分:,需要通过数组翻转将其转化为卷积形式。
🧠 卷积处理与数组翻转
处理第二部分 的技巧:
- 构造一个翻转数组 。
- 通过变量代换(令 ),可以将 转化为: 这同样是一个卷积形式。
因此,整个计算过程可以归纳为:
- 计算卷积 。
- 计算卷积 。
- 对于每个 ,。
⚠️ 计算精度提醒
在计算 时,务必注意计算方式。推荐使用
1.0 / i / i,而不是1.0 / (i * i),因为当i较大时,i * i可能导致整数溢出或精度损失。🧮 FFT 代码实现
以下是基于 FFT 的 C++ 实现。代码中包含了复数类、FFT 变换以及如何组织多项式系数来计算卷积。
#include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> using namespace std; const int MAXN = 1 << 18; // 选择2的幂次,大于2*n const double PI = acos(-1.0); struct Complex { double r, i; Complex(double r = 0.0, double i = 0.0) : r(r), i(i) {} Complex operator + (const Complex &rhs) const { return Complex(r + rhs.r, i + rhs.i); } Complex operator - (const Complex &rhs) const { return Complex(r - rhs.r, i - rhs.i); } Complex operator * (const Complex &rhs) const { return Complex(r * rhs.r - i * rhs.i, r * rhs.i + i * rhs.r); } }; void FFT(Complex a[], int len, int inv) { for (int i = 0, j = 0; i < len; i++) { if (i > j) swap(a[i], a[j]); for (int k = len >> 1; (j ^= k) < k; k >>= 1); } for (int h = 2; h <= len; h <<= 1) { Complex wn(cos(inv * 2 * PI / h), sin(inv * 2 * PI / h)); for (int j = 0; j < len; j += h) { Complex w(1, 0); for (int k = j; k < j + h / 2; k++) { Complex u = a[k]; Complex t = w * a[k + h / 2]; a[k] = u + t; a[k + h / 2] = u - t; w = w * wn; } } } if (inv == -1) { for (int i = 0; i < len; i++) a[i].r /= len; } } int n; double q[MAXN]; Complex f[MAXN], g[MAXN], f_rev[MAXN], A[MAXN], B[MAXN]; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%lf", &q[i]); } int len = 1; while (len < 2 * n + 1) len <<= 1; // 确定扩展长度 // 构建多项式 f 和 g for (int i = 1; i <= n; i++) { f[i].r = q[i]; if (i > 0) { g[i].r = 1.0 / i / i; // 注意计算方式 } } // 计算第一部分卷积 A = f * g FFT(f, len, 1); FFT(g, len, 1); for (int i = 0; i < len; i++) A[i] = f[i] * g[i]; FFT(A, len, -1); // 构建翻转多项式 f_rev for (int i = 0; i < len; i++) f_rev[i] = Complex(0, 0); for (int i = 1; i <= n; i++) { f_rev[n - i + 1].r = q[i]; } // 计算第二部分卷积 B = f_rev * g FFT(f_rev, len, 1); for (int i = 0; i < len; i++) B[i] = f_rev[i] * g[i]; FFT(B, len, -1); // 输出结果 E_j = A[j] - B[n - j + 1] for (int i = 1; i <= n; i++) { double ans = A[i].r - B[n - i + 1].r; printf("%.3f\n", ans); } return 0; }💡 核心思路与技巧总结
- 卷积识别:识别出题目中的求和式可以转化为多项式卷积是解题的关键一步。
- 数组翻转:对于涉及负索引或反向索引的求和,数组翻转是一个常用的转化技巧。
- FFT应用:FFT能高效计算多项式卷积,将时间复杂度从 降为 。
- 精度处理:在数值计算中,注意运算顺序和数据类型,防止溢出和精度损失。
- 1
信息
- ID
- 5674
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 8
- 已通过
- 0
- 上传者