ACM-ICPC国际大学生程序设计竞赛北京赛区(2016)网络赛 F. Periodic Signal(FFT 优化乘法)

来源:互联网 发布:cs1.5优化教程视频 编辑:程序博客网 时间:2024/04/30 11:50

传送门
时间限制:5000ms
单点时限:5000ms
内存限制:256MB
描述
Profess X is an expert in signal processing. He has a device which can send a particular 1 second signal repeatedly. The signal is A0 … An-1 under n Hz sampling.

One day, the device fell on the ground accidentally. Profess X wanted to check whether the device can still work properly. So he ran another n Hz sampling to the fallen device and got B0 … Bn-1.

To compare two periodic signals, Profess X define the DIFFERENCE of signal A and B as follow:
这里写图片描述
You may assume that two signals are the same if their DIFFERENCE is small enough.
Profess X is too busy to calculate this value. So the calculation is on you.

输入
The first line contains a single integer T, indicating the number of test cases.

In each test case, the first line contains an integer n. The second line contains n integers, A0 … An-1. The third line contains n integers, B0 … Bn-1.

T≤40 including several small test cases and no more than 4 large test cases.

For small test cases,0<n6103.

For large test cases,0<n6104.

For all test cases,0Ai,Bi<220.

输出
For each test case, print the answer in a single line.

样例输入
2
9
3 0 1 4 1 5 9 2 6
5 3 5 8 9 7 9 3 2
5
1 2 3 4 5
2 3 4 5 1
样例输出
80
0

题目大意:

给定一个 A 数组和 B 数组,求给定的这个函数值的最小值。

解题思路:

这是一个 比较裸的 FFT 模板题目,什么叫 FFT,其实它是快速傅里叶变换,是 DFT 的加速方法,它可以用来优化多项式乘法,然后这个题目就是套一个模板就ok了。

My Code

#include <iostream>#include <cstdio>#include <cstring>#include <cstdio>#include <cstdlib>#include <queue>#include <cmath>using namespace std;typedef long long LL;const double PI = acos(-1.0);const LL MAXN = 1e6+5;struct complex {    double a, b;    complex(double aa = 0.0, double bb = 0.0) { a = aa; b = bb; }    complex operator +(const complex &e) { return complex(a + e.a, b + e.b); }    complex operator -(const complex &e) { return complex(a - e.a, b - e.b); }    complex operator *(const complex &e) { return complex(a * e.a - b * e.b, a * e.b + b * e.a); }}x1[MAXN], x2[MAXN], x[MAXN];void change(complex * y, LL len) {    LL i, j, k;    for (i = 1, j = len / 2; i < len - 1; i++) {        if (i < j) swap(y[i], y[j]);        k = len / 2;        while (j >= k) {            j -= k;            k /= 2;        }        if (j < k) j += k;    }}void fft(complex *y, LL len, LL on) {    change(y, len);    for (LL h = 2; h <= len; h <<= 1) {        complex wn(cos(-on * 2 * PI / h), sin(-on * 2 * PI / h));        for (LL j = 0; j < len; j += h) {            complex w(1, 0);            for (LL k = j; k < j + h / 2; k++) {                complex u = y[k];                complex t = w * y[k + h / 2];                y[k] = u + t;                y[k + h / 2] = u - t;                w = w * wn;            }        }    }    if (on == -1)        for (LL i = 0; i < len; i++)            y[i].a /= len;}LL f[MAXN], g[MAXN], ans[MAXN];int main(){    LL T;    scanf("%lld",&T);    while(T--){        LL n;        scanf("%lld",&n);        for(LL i=0; i<n; i++)            scanf("%lld",&f[i]);        for(LL i=n-1; i>=0; i--)            scanf("%lld",&g[i]);        memset(ans, 0, sizeof(ans));        LL len1 , len2, len;        len1 = len2 = n;        len =  1;        while(len<2*len1 || len<2*len2)            len <<= 1LL;        for(LL i=0; i<=len1-1; i++){            x1[i].a = f[i];            x1[i].b = 0;        }        for(LL i=len1; i<=len-1; i++){            x1[i].a = 0;            x1[i].b = 0;        }        for(LL i=0; i<=len2-1; i++){            x2[i].a = g[i];            x2[i].b = 0;        }        for(LL i=len2; i<=len-1; i++){            x2[i].a = 0;            x2[i].b = 0;        }        fft(x1, len, 1), fft(x2, len, 1);        for(LL i=0; i<=len-1; i++)            x[i] = x1[i] * x2[i];        fft(x, len, -1);        for(LL i=0; i<len; i++)            ans[i] = (LL)(x[i].a+0.5);        LL sum = 0;        LL tmp = 0;///找 f[i]*g[j] 最大的 k        for(LL i=0; i<n; i++)            if(sum < ans[i]+ans[i+n])///ans[i]+ans[i+n]这个得从卷积原理上思考一下            {                sum = ans[i]+ans[i+n];///这样算出来 sum值 的会有精度问题                tmp = i;///但是这个下标 tmp 是正确的            }        sum = 0;        tmp = n - tmp - 1;        for(LL i=0; i<n/2; i++)///将 g[x] 还原成原样            swap(g[i],g[n-1-i]);        for(LL i=0; i<n; i++)            sum += (f[i]-g[(i+tmp)%n])*(f[i]-g[(i+tmp)%n]);        printf("%lld\n",sum);    }    return 0;}/**5551 2 3 4 52 3 4 5 1**/
1 0
原创粉丝点击