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

ACM_数论 同时被 3 个专栏收录
97 篇文章 0 订阅
4 篇文章 0 订阅

传送门
时间限制: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;
}
/**
55
5
1 2 3 4 5
2 3 4 5 1
**/
  • 1
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页

打赏

ITAK

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值