分类
标签
2-SAT AC自动机 BFS CDQ dfs DP fail树 FFT FFT&NTT FWT hash KD-Tree KMP LCA SPFA STL Tarjan Treap Trie 主席树 乱搞 二分 二分图匹配 二分答案 二维SPFA 交互 位运算 其他 最小生成树 分块 区间DP 半平面交 博弈论 可持久化 可持久化Trie树 后缀数组 图库 平衡树 并查集 插头DP 数学 数论 无旋Treap 日记 暴力 权值树状数组 栈 树DP 树套树 树状数组 树贪心 概率DP 模拟 欧拉定理 点分治 状压DP 生成函数 矩阵乘 线性规划 线段树 组合 网络流 群论 莫比乌斯反演 计算几何 贪心 费用流 高斯消元
1312 字
7 分钟
从N方到NlogN的转变——FFT
1.Why
为什么我们信息学竞赛要用到FFT
因为我们要优化卷积啊
将n边为log是一个非常大的优化啊
What
什么是FFT呢 在wiki上是这么说的
快速傅里叶变换(英语:Fast Fourier Transform, FFT),是计算序列的离散傅里叶变换(DFT)或其逆变换的一种算法。傅里叶分析将信号从原始域(通常是时间或空间)转换到频域的表示或者逆过来转换。FFT会通过把DFT矩阵分解为稀疏(大多为零)因子之积来快速计算此类变换。[1] 因此,它能够将计算DFT的复杂度从只用DFT定义计算需要的 ,降低到 ,其中 n 为数据大小。
啥你说啥我听不懂(黑人问号?)
如果简单的说就是求两个多项式的乘积
How
其实关于FFT的具体理论很复杂,我一篇博客肯定不能接受清楚 所以请有兴趣的人移步Google 或 baidu
如果简单来说FFT用的分制的方法
所以才能有log啊
将一个多项式按奇偶分开
但如果我们直接找值去计算时间复杂度还是的
所以出现了一个特殊的东西他叫做n次单位根
然后利用他的性质就可以搞了
先贴两个板子
UOJ 34 多项式乘法
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1 << 18 | 5;
namespace FFT
{
const double pi = acos(-1.0);
struct Complex
{
double x, y;
Complex operator+(const Complex &a)
{
return (Complex){x + a.x, y + a.y};
}
Complex operator-(const Complex &a)
{
return (Complex){x - a.x, y - a.y};
}
Complex operator*(const Complex &a)
{
return (Complex){x * a.x - y * a.y, x * a.y + y * a.x};
}
Complex operator*(const double &a)
{
return (Complex){x * a, y * a};
}
Complex Get()
{
return (Complex){x, -y};
}
} c[maxn], c1[maxn], V = (Complex){0.5, 0} * (Complex){0, -0.5};
int rev[maxn], N, FU;
double Inv;
void FFt(Complex *a, int op)
{
Complex w, wn, t;
for (int i = 0; i < N; i++)
if (i < rev[i])
swap(a[i], a[rev[i]]);
for (int k = 2; k <= N; k <<= 1)
{
wn = (Complex){cos(op * 2 * pi / k), sin(op * 2 * pi / k)};
for (int j = 0; j < N; j += k)
{
w = (Complex){1, 0};
for (int i = 0; i < (k >> 1); i++, w = w * wn)
{
t = a[i + j + (k >> 1)] * w;
a[i + j + (k >> 1)] = a[i + j] - t;
a[i + j] = a[i + j] + t;
}
}
}
if (op == -1)
for (int i = 0; i < N; i++)
a[i] = a[i] * Inv;
}
void FFt(int *a, int *b, int *res, int n)
{
int j;
N = 1;
while (N < n)
N <<= 1;
FU = N - 1;
Inv = 1. / N;
for (int i = 0; i < N; i++)
if (i & 1)
rev[i] = (rev[i >> 1] >> 1) | (N >> 1);
else
rev[i] = (rev[i >> 1] >> 1);
for (int i = 0; i < N; i++)
c[i].x = a[i], c[i].y = b[i];
FFt(c, 1);
for (int i = 0; i < N; i++)
j = (N - i) & FU, c1[i] = (c[i] + c[j].Get()) * (c[i] - c[j].Get()) * V;
FFt(c1, -1);
for (int i = 0; i < N; i++)
res[i] = round(c1[i].x);
}
} // FFT
int main(int argc, char const *argv[])
{
static int a[maxn],b[maxn];
int n1, n2, m;
scanf("%d%d", &n1, &n2);
m = n1 + n2 + 1;
for (int i = 0; i <= n1; i++)
scanf("%d", &a[i]);
for (int i = 0; i <= n2; i++)
scanf("%d", &b[i]);
FFT::FFt(a, b, a, m);
for (int i = 0; i < m; i++)
printf("%d ", a[i]);
return 0;
}
COGS 1473. 很强的乘法问题
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
const double pi = acos(-1.);
struct Complex
{
double x, y;
Complex() { ; }
Complex(double a, double b) : x(a), y(b) {}
Complex operator+(const Complex &a) { return Complex(x + a.x, y + a.y); }
Complex operator-(const Complex &a) { return Complex(x - a.x, y - a.y); }
Complex operator*(const Complex &a) { return Complex(x * a.x - y * a.y, x * a.y + y * a.x); }
Complex operator*(const double a) { return Complex(x * a, y * a); }
Complex Get() { return Complex(x, -y); }
} A[150005 << 3], B[150005 << 3];
int rev[150005 << 3], N, FU;
double INV;
void FFt(Complex *a, int op)
{
Complex w, wn, t;
for (int i = 0; i < N; i++)
if (i < rev[i])
std::swap(a[i], a[rev[i]]);
for (int k = 2; k <= N; k <<= 1)
{
wn = Complex(cos(op * 2 * pi / k), sin(op * 2 * pi / k));
for (int j = 0; j < N; j += k)
{
w = Complex(1, 0);
for (int i = 0; i < (k >> 1); i++, w = w * wn)
{
t = a[i + j + (k >> 1)] * w;
a[i + j + (k >> 1)] = a[i + j] - t;
a[i + j] = a[i + j] + t;
}
}
}
if (op == -1)
for (int i = 0; i < N; i++)
a[i] = a[i] * INV;
}
void FFt(const int *a, const int *b, int *res, int n)
{
N = 1;
while (N < n)
N <<= 1;
INV = 1. / N;
for (int i = 0; i < N; i++)
if (i & 1)
rev[i] = (rev[i >> 1] >> 1) | (N >> 1);
else
rev[i] = (rev[i >> 1] >> 1);
for (int i = 0; i < N; i++)
A[i].x = a[i], B[i].x = b[i];
FFt(A, 1), FFt(B, 1);
for (int i = 0; i < N; i++)
A[i] = A[i] * B[i];
FFt(A, -1);
for (int i = 0; i < N; i++)
res[i] = round(A[i].x);
}
char s[150005 << 2];
struct BigNum
{
int len;
int a[1000005];
void read()
{
scanf("%s", s);
len = strlen(s);
for (int i = len - 1, j = 0; i >= 0; i--, j++)
a[j] = s[i] - '0';
}
BigNum operator*(const BigNum &b)
{
BigNum ans;
ans.len = len + b.len - 1;
FFt(a, b.a, ans.a, ans.len + 1);
for (int i = 0; i <= ans.len + 2; i++)
{
if (ans.a[i] > 9)
{
ans.a[i + 1] += ans.a[i] / 10;
ans.a[i] %= 10;
}
}
while (ans.a[ans.len])
ans.len++;
return ans;
}
void print()
{
for (int i = len - 1; i >= 0; i--)
printf("%d", a[i]);
printf("\n");
}
} a, b;
int main()
{
freopen("bettermul.in","r",stdin);
freopen("bettermul.out","w",stdout);
a.read();
b.read();
(a * b).print();
//while (1)
;
}