bzoj2194「快速傅立叶之二」
题目描述
请计算
\[c[k]=\sum_{i=0}^{n-1} a[i]\times b[i-k]\;(k\leq i < n)
\]
输入格式
第一行一个整数 \(n\) ,接下来 \(n\) 行,第 \(i+2...i+n-1\) 行,每行两个数,依次表示 \(a[i],b[i] (0 \leq i < n)\) 。
输出格式
输出 \(n\) 行,每行一个整数,第 \(i\) 行输出 \(c[i-1]\) 。
样例
样例输入
5
3 1
2 4
1 1
2 4
1 4
样例输出
24
12
10
6
1
数据范围
\(n\leq 1e5,0 \leq a[i],b[i]\leq 100,a[i],b[i]\in \mathbb{Z}\)
题解
一个比较常见的套路:将某个数组 \(reverse\) 一下,形成卷积的形式。
比如这个题,我们将 \(a\) 数组 \(reverse\),原始变成了
\[c[k]=\sum_{i=0}^{n-1} a[n-i]\times b[i-k]
\]
很容易发现这是一个卷积的形式
\[c[n-k]=\sum_{i+j=n-k} a[i]\times b[j]
\]
最后用 \(FFT\) 和 \(NTT\) 解决都可。
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 3e5 + 50, INF = 0x3f3f3f3f;
const double pi = acos (- 1.0);
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;
}
inline void write (register int x) {
if (x / 10) write (x / 10);
putchar (x % 10 + '0');
}
int n, bit, len = 1, rev[maxn];
struct Complex {
double x, y;
Complex () { x = y = 0; }
Complex (register double a, register double b) { x = a, y = b; }
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); }
inline Complex operator * (const Complex &a) const { return Complex (x * a.y + y * a.x, y * a.y - x * a.x); }
} a[maxn], b[maxn];
inline void FFT (register int len, register Complex *a, register int opt) {
for (register int i = 0; i < len; i ++) if (i < rev[i]) swap (a[i], a[rev[i]]);
for (register int d = 1; d < len; d <<= 1) {
register Complex w1 = Complex (opt * sin (pi / d), cos (pi / d));
for (register int i = 0; i < len; i += d << 1) {
register Complex w = Complex (0, 1);
for (register int j = 0; j < d; j ++, w = w * w1) {
register Complex x = a[i + j], y = w * a[i + j + d];
a[i + j] = x + y, a[i + j + d] = x - y;
}
}
}
}
int main () {
n = read();
for (register int i = 0; i < n; i ++) a[i].y = read(), b[i].y = read();
reverse (a, a + n + 1);
while (len <= n << 1) len <<= 1, bit ++;
for (register int i = 1; i < len; i ++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << bit - 1);
FFT (len, a, 1), FFT (len, b, 1);
for (register int i = 0; i < len; i ++) a[i] = a[i] * b[i];
FFT (len, a, -1);
for (register int i = n; i > 0; i --) printf ("%d\n", (int) (0.5 + a[i].y / len));
return 0;
}