HDU-1402 A * B Problem Plus

题意:给定两个整数,整数的长度最多能达到50000位,输出两个整数的乘积。

分析:题意非常的明了,一个惊世骇俗的想法是使用两个数组将整数保留起来,然后模拟我们平常手算时的乘法,不过这样一来时间复杂度将是O(N^2),由于N过大,因此此题因寻求更加快速的解法。

  对于任何一个N位的整数都可以看作是An*10^(n-1) + An-1*10^(n-2) + ... + A2*10^2 + A1*10 + A0。如果把10看作是一个自变量,那么任何一个整数就可以视作为一个多项式,两个整数相乘也便可以看作是两个多项式相乘。对于一个多项式,我们平时所接触到的多是其系数表示法,普通的相乘也就是建立在两个整数均采用系数表示法的基础上进行的。那么要使得计算多项式相乘的复杂度下降的另一种方式就是寻找一种新的表示多项式的方法......

  若一个多项式的最高阶位为N-1,那么取N个点对(xi, yi)就能够唯一确定这个多项式,可以想象成有N个系数需要N个方程去求解。那么在此就可以寻找点对来表示一个多项式,对于一个大的数,看作多项式之后,那么舍弃掉原来以10为自变量的取值,而选取其他值,再通过计算多项式An*xi^(n-1) + An-1*xi^(n-2) + ... + A2*xi^2 + A1*xi + A0来保存这个多项式的信息。需要选取N个xi形成N对(xi, yi)方可唯一确定原来各个项前的系数,通过选取1的N次单位复根即可,并且利用单位复根的性质,可以使得计算量下降。

  通过点值法表示多项式后,计算乘法也就是O(N)的时间了,由于两个数相乘使得项数变多,因此需要在之前尽可能多取点。FFT算法能够在O(NlogN)时间内将系数法转化为点值法,相乘后再有点值法转为系数法,该题就是使用的这个方法。

  顺便说下一FFT过程中,计算叶子DTF时采用的二进制平摊反转置换,其作用是为了避免算法的递归而实现自底向上的计算方式。回顾一下在计算原串DFT的时候,假设离散点数为0-7,那么有以下过程:

(0 1 2 3 4 5 7) = (0 2 4 6) + (1 3 5 7)                           1
(0 2 4 6) = (0 4) + (2 6)                                               2
(1 3 5 7) = (1 3) + (5 7)                                               2
(0 4) = (0) + (4)                                                          3
(2 6) = (2) + (6)                                                          3
(1 3) = (1) + (3)                                                          3
(5 7) = (5) + (7)                                                          3

分析这些分组的二进制位会发现,第1次分组是根据第0位是否为1来划分的,即奇偶性;第2次分组是根据第1位是否为1来划分的;第三次分组是根据第2位是否为1来划分的。这个特性与与一般的按照大小划分的数很类似(首先按照最高为是否为1划分,然后是次高位...),因此就可以通过一个算法来使得00...00 - 11...11这样递增的序列中的每一个数实现高位和低位的翻转,二进制平摊反转置换就是用于达到这个目的。

算法从1开始到N-2(FFT算法要求N必须是2的幂,保证每次折半之后不会出现奇数),因此0和N-1翻转后还是本身,接着维护好一个下标 j ,这个数就是与递增中的第 i 个数翻转之后对应的数,初始化 j 的下标示 N/2,这个数要是从后往前来定义二进制数的话,就会是1,例如若N=8,那么4的二进制位为100,假定只有3位2进制位组成2进制数,从右往左看,其值为1。接下来就是要找 j 的下一个数了,这个数从右往左看应该是2才能够满足要求,于是从右往左寻找,遇到1变为0,知道遇到0就跳出,并且将该位赋值为1,这个伟大的过程的作用仅仅只是给在右往左定义的二进制数 j 加了一个1。

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std; struct C {
double r, i;
C() {}
C(double _r, double _i) : r(_r), i(_i) {}
inline C operator + (const C & a) const {
return C(r + a.r, i + a.i);
}
inline C operator - (const C & a) const {
return C(r - a.r, i - a.i);
}
inline C operator * (const C & a) const {
return C(r*a.r - i*a.i, r*a.i + i*a.r);
}
}; typedef long long LL;
const double pi = acos(-1.0);
const int N = ;
C a[N<<], b[N<<];
char num1[N], num2[N];
LL ret[N<<]; void brc(C *y, int L) {
int i, j, k;
for (i=,j=L>>; i<L-; ++i) { // 二进制平摊反转置换 O(NlogN)
if (i < j) swap(y[i], y[j]);
k = L>>;
while (j >= k) {
j -= k;
k >>= ;
}
j += k;
}
} void FFT(C *y, int L, int dir) {
brc(y, L);
for (int h = ; h <= L; h <<= ) { // 枚举所需计算的点数
C wn(cos(dir**pi/h), sin(dir**pi/h)); // h次单位复根
for (int j = ; j < L; j += h) { // 原序列被分成了L/h段h长序列
C w(, ); // 旋转因子
for (int k = j; k < j+h/; ++k) { // 因为折半定理,只需要计算枚举一半的长度即可
C u = y[k];
C t = w*y[k+h/];
y[k] = u + t;
y[k+h/] = u - t;
w = w * wn; // 更新旋转因子
}
}
}
if (dir == ) {
for (int i = ; i < L; ++i) {
y[i] = y[i] * C(1.0/L, );
}
}
} int main() {
while (scanf("%s %s", num1, num2) != EOF) {
memset(ret, , sizeof (ret));
int len1 = strlen(num1), len2 = strlen(num2);
int ML = len1+len2-, L = ;
while (L < ML) L <<= ;
for (int i = len1-, j = ; i >= ; --i, ++j) {
a[j] = C(num1[i]-'', );
}
for (int i = len2-, j = ; i >= ; --i, ++j) {
b[j] = C(num2[i]-'', );
}
for (int i = len1; i < L; ++i) a[i] = C(, );
for (int i = len2; i < L; ++i) b[i] = C(, );
FFT(a, L, -), FFT(b, L, -);
for (int i = ; i < L; ++i) {
a[i] = a[i] * b[i];
}
FFT(a, L, );
for (int i = ; i < L; ++i) {
ret[i] = (LL)floor(a[i].r + 0.5);
}
for (int i = ; i < L; ++i) {
ret[i+] += ret[i] / ;
ret[i] %= ;
}
int p = L;
while (!ret[p] && p) --p;
while (p >= ) printf("%d", (int)ret[p--]);
puts("");
}
return ;
}

HDU-4609 3-idiots

题意:有N条线段,问从这N条线段中选出三条能过组成三角形的概率为多大?

分析:三条边能够组成三角形则满足等式x+y < z。首先将所有的边排序,然后将每条边的长度构成多项式的指数,同一长度的边的数量为系数。然后将这个多项式自己和自己作一个乘法,这里需要使用FFT来实现,去掉自己与自己的组合已经相互的组合情况,就能够得到两两之间组合形成边长和值为某一个值得方案数。使用这个方案数除以总方案数即可。

#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; const int N = ; typedef long long LL;
struct C {
double r, i;
C() {}
C(double _r, double _i) : r(_r), i(_i) {}
inline C operator + (const C &a) const {
return C(r + a.r, i + a.i);
}
inline C operator - (const C &a) const {
return C(r - a.r, i - a.i);
}
inline C operator * (const C &a) const {
return C(r*a.r-i*a.i, r*a.i+i*a.r);
}
}a[N], b[N]; const double pi = acos(-1.0);
int n, num[N], cnt[N];
LL res[N], sum[N]; void brc(C *y, int l) {
int i, j, k;
for (i=,j=l>>; i<l-; ++i) {
if (i < j) swap(y[i], y[j]);
k = l>>;
while (j >= k) {
j -= k;
k >>= ;
}
j += k;
}
} void FFT(C *y, int l, int on) {
int h, i, j, k;
C u, t;
brc(y, l); // 得到一个自底向上的序列
for (h = ; h <= l; h <<= ) { // 控制一个O(logn)的外层复杂度
C wn(cos(on**pi/h), sin(on**pi/h));
for (j = ; j < l; j+=h) { // 两个for循环共组成O(n)的复杂度
C w(, );
for (k = j; k <j+h/; ++k) {
u = y[k];
t = w*y[k+h/];
y[k] = u+t;
y[k+h/] = u-t;
w = w*wn;
}
}
}
if (on == ) {
for (i = ; i < l; ++i) {
y[i]= y[i] * C(1.0/l, 0.0);
}
}
} int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
int Max = ;
memset(cnt, , sizeof (cnt));
for (int i = ; i < n; ++i) {
scanf("%d", &num[i]);
Max = max(Max, num[i]);
++cnt[num[i]];
}
int L = ;
++Max;
while (L < (Max<<)) L <<= ;
for (int i = ; i < Max; ++i) {
a[i] = C(cnt[i], );
}
for (int i = Max; i < L; ++i) {
a[i] = C(, );
}
FFT(a, L, -);
for (int i = ; i < L; ++i) {
a[i] = a[i] * a[i];
}
FFT(a, L, );
for (int i = ; i < L; ++i) {
res[i] = (LL)floor(a[i].r + 0.5);
}
for (int i = ; i < Max; ++i) {
res[i<<] -= cnt[i];
}
for (int i = ; i < L; ++i) {
res[i] >>= ;
}
for (int i = ; i < L; ++i) {
sum[i] = sum[i-] + res[i];
}
double ret = , den = 1.0*n*(n-)*(n-)/6.0;
for (int i = ; i < n; ++i) {
ret += sum[num[i]] / den;
}
printf("%.7f\n", -ret);
}
return ;
}

【FFT-快速傅立叶变换】的更多相关文章

  1. FFT快速傅立叶变换的工作原理

    实数DFT,复数DFT,FFTFFT是计算DFT的快速算法,但是它是基于复数的,所以计算实数DFT的时候需要将其转换为复数的格式,下图展示了实数DFT和虚数DFT的情况,实数DFT将时域中N点信号转换 ...

  2. spoj VFMUL FFT快速傅立叶变换模板题

    题意:求两个数相乘. 第一次写非递归的fft,因为一个数组开小了调了两天TAT. #include<iostream> #include<cstring> #include&l ...

  3. FFT(快速傅立叶变换):HDU 1402 A * B Problem Plus

    Calculate A * B. Input Each line will contain two integers A and B. Process to end of file. Note: th ...

  4. FFT快速傅立叶变换

    //最近突然发现博客园支持\(\rm\LaTeX\),非常高兴啊! 话说离省选只有不到五天了还在学新东西确实有点逗…… 切到正题,FFT还是非常神奇的一个东西,能够反直觉地把两个多项式相乘的时间复杂度 ...

  5. FFT快速傅立叶变换:解析wav波频图、Time Domain、Frequency Domain

    您好,此教程将教大家使用scipy.fft分析wav文件的波频图.Time Domain.Frequency Domain. 实际案例:声音降噪,去除高频. 结果: 波频图: Time Domain:

  6. 离散傅立叶变换与快速傅立叶变换(DFT与FFT)

    自从去年下半年接触三维重构以来,听得最多的词就是傅立叶变换,后来了解到这个变换在图像处理里面也是重点中的重点. 本身自己基于高数知识的理解是傅立叶变换是将一个函数变为一堆正余弦函数的和的变换.而图像处 ...

  7. 快速傅立叶变换(FFT)算法

    已知多项式f(x)=a0+a1x+a2x2+...+am-1xm-1, g(x)=b0+b1x+b2x2+...+bn-1xn-1.利用卷积的蛮力算法,得到h(x)=f(x)g(x),这一过程的时间复 ...

  8. $\mathcal{FFT}$·$\mathcal{Fast \ \ Fourier \ \ Transformation}$快速傅立叶变换

    \(2019.2.18upd:\) \(LINK\) 之前写的比较适合未接触FFT的人阅读--但是有几个地方出了错,大家可以找一下233 啊-本来觉得这是个比较良心的算法没想到这么抽搐这个算法真是将一 ...

  9. BZOJ 2194 快速傅立叶变换之二 | FFT

    BZOJ 2194 快速傅立叶变换之二 题意 给出两个长为\(n\)的数组\(a\)和\(b\),\(c_k = \sum_{i = k}^{n - 1} a[i] * b[i - k]\). 题解 ...

  10. 快速傅立叶变换(FFT)

    多项式 系数表示法 设\(f(x)\)为一个\(n-1\)次多项式,则 \(f(x)=\sum\limits_{i=0}^{n-1}a_i*x_i\) 其中\(a_i\)为\(f(x)\)的系数,用这 ...

随机推荐

  1. Overview of Flashback Technology

    Oracle Flashback Query : SELECT AS OFOracle Flashback Version Query :DBMS_FLASHBACK PackageOracle Fl ...

  2. mha的搭建步骤(一主一从架构)

    所需脚本文件到这里下载:http://note.youdao.com/share/web/file.html?id=ae8b11a61f7a8aa7b52aac3fcf0c4b83&type= ...

  3. hashcode与equals

    归纳一下就是hashCode是用于查找使用的,而equals是用于比较两个对象的是否相等的.以下这段话是从别人帖子回复拷贝过来的: .hashcode是用来查找的,如果你学过数据结构就应该知道,在查找 ...

  4. React.js入门小案例

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title&g ...

  5. IOS中两个view的切换

    在ios中,rootview为PassWordViewController,secondview为SecondViewController,实现在rootview中听过一个跳转按钮实现跳转到secon ...

  6. [转]在iOS项目中使用CorePlot框架

    转载地址:http://blog.csdn.net/llfjfz/article/details/7849190#comments Core Plot是OS X和IOS下的一个开源图形库,它提供数据的 ...

  7. WPF中viewmodel层怎样得到view层的TabControl控件对象?

    View层: <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns: ...

  8. .Net neatupload上传控件实现文件上传的进度条

    1. 引入bin文件 (可以到neatupload官网下载,也可以到教育厅申报系统中找) 2. 将控件加入到工具栏,在工具栏中点鼠标右键,如图: 3. 加入neatuplaod这个文件夹(可以到nea ...

  9. ThreadLocal深入理解二

    转载:http://doc00.com/doc/101101jf6 今天在看之前转载的博客:ThreadLocal的内部实现原理.突然有个疑问, 按照threadLocal的原理, 当把一个对象存入到 ...

  10. Auty自动化测试框架第一篇——生成执行列表

    [本文出自天外归云的博客园] 在Auty的scripts文件夹中编写一个create_selection.py文件,用于在同级目录下针对同级目录scripts下的所有脚本生成一个selection.t ...