多项式学习笔记

发布时间 2023-08-03 15:22:51作者: A_Big_Jiong

前言

不要问为啥跟全家桶是分开写的,问就是全家桶实在是太多了/jk

[ZJOI2014] 力

题目链接:[ZJOI2014] 力

题意

给出 \(n\) 个数 \(q_1,q_2, \dots q_n\),定义

\[F_j~=~\sum_{i = 1}^{j - 1} \frac{q_i \times q_j}{(i - j)^2}~-~\sum_{i = j + 1}^{n} \frac{q_i \times q_j}{(i - j)^2} \]

\[E_i~=~\frac{F_i}{q_i} \]

\(1 \leq i \leq n\),求 \(E_i\) 的值。

Solution

对于多项式相关题目,不应当仅仅局限于处理多项式问题上,应当将其看作一个卷积的问题形式进行处理,也就是:

\[C_i = \sum_{j = 1}^{i - 1}A_j · B_{i - j} \]

对于本题,处理思路就是将现有的问题,转化成一个卷积问题,然后利用 FFT 加速求解,下面开始推式子。

\[E_i~=~\frac{F_i}{q_i} = \sum_{j = 1}^{i - 1} \frac{q_j}{(i - j)^2}~-~\sum_{j = i + 1}^{n} \frac{q_j}{(i - j)^2} \]

对于 \(\sum_{j = 1}^{i - 1} \frac{q_j}{(i - j)^2}\),显然是一个卷积的形式,可以通过 FFT 直接求解,提出右侧部分求值。

\[\sum_{j = i + 1}^{n} \frac{q_j}{(i - j)^2} = \sum_{j = 1}^{n - i} \frac{q_{i+j}}{j^2} \]

下标问题解决了,但是卷积形式还没完全成立,有一个常用的小技巧,翻转数组,记 \(q'\)\(q\) 的翻转reverse,则有

\[\sum_{j = 1}^{n - i} \frac{q'_{n-j-i}}{j^2} \]

这样的话,右边也变成了卷积的形式,就可以 FFT 对左右部分分别求解,得出答案。

Code

点击查看代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

typedef long long lld;

const int N = 3e5 + 50;
const double PI = acos (-1.0);

struct Complex {
	double x, y;
	Complex (register double xx = 0, register double yy = 0) {
		x = xx, y = yy;
	}
	inline Complex operator + (const Complex &a) const {
		return Complex (a.x + x, a.y + y);
	}
	inline Complex operator - (const Complex &a) const {
		return Complex (x - a.x, y - a.y);
	}
	inline Complex operator * (const Complex &a) const {
		return Complex (x * a.x - y * a.y, x * a.y + y * a.x);
	}
} a[N], b[N], c[N], ans1[N], ans2[N];

inline int read () {
	register int x = 0, w = 1;
	register char ch = getchar ();
	for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
	return x * w;
}

int n;

int limit = 1;
int l, r[N];
inline void FFT (register Complex * A, register int type) {
	for (register int i = 0; i < limit; i ++)
		if (i < r[i])  swap (A[i], A[r[i]]);
	for (register int mid = 1; mid < limit; mid <<= 1) {
		register Complex Wn (cos (PI / mid), type * sin (PI / mid));
		for (register int R = mid << 1, j = 0; j < limit; j += R) {
			register Complex w (1, 0);
			for (register int k = 0; k < mid; k ++, w = w * Wn) {
				register Complex x = A[j + k], y = w * A[j + mid + k];
				A[j + k] = x + y;
				A[mid + j + k] = x - y;
			}
		}
	}
}

int main () {
	n = read() - 1;
	for (register int i = 0; i <= n; i ++)  scanf ("%lf", &a[i].x);
	for (register int i = 0; i <= n; i ++)  c[n - i].x = a[i].x;
	for (register int i = 1; i <= n; i ++)  b[i].x = (double)1.0 / (double)i / (double)i;
	while (limit <= (n << 1))  limit <<= 1, l ++;
	for (register int i = 0; i < limit; i ++)  r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
	FFT (a, 1), FFT (b, 1);
	for (register int i = 0; i < limit; i ++)  ans1[i] = a[i] * b[i];
	FFT (ans1, -1);
	FFT (c, 1);
	for (register int i = 0; i < limit; i ++)  ans2[i] = c[i] * b[i];
	FFT (ans2, -1);
	for (register int i = 0; i < limit; i ++)  ans1[i].x /= limit, ans2[i].x /= limit;
	for (register int i = 0; i <= n; i ++)  printf ("%.3lf\n", ans1[i].x - ans2[n - i].x);
	putchar ('\n');
	return 0;	
}