【HNOI2017/BZOJ4827】礼物 FFT

原题走这里

原题相当于求 (a[i]b[i]+C)2 ∑ ( a [ i ] − b [ i ] + C ) 2 ,其中b可以任意旋转,C为整数,由于原题中有a,b小于m的限制条件,所以C范围最大也大不过 (m,m) ( − m , m )
那么我们可以把式子拆开:

i=0n1(a[i]2+b[i]22a[i]b[i]+2C(a[i]b[i])+C2) ∑ i = 0 n − 1 ( a [ i ] 2 + b [ i ] 2 − 2 ∗ a [ i ] ∗ b [ i ] + 2 ∗ C ( a [ i ] − b [ i ] ) + C 2 )

由于除了 a[i]b[i] a [ i ] ∗ b [ i ] 这一项以外,其他所有项都可以预处理或枚举,所以问题就转化为了:如何最大化: n1i=0a[i]b[i] ∑ i = 0 n − 1 a [ i ] ∗ b [ i ]
把b反转,就变成了 n1i=0a[i]b[n1i] ∑ i = 0 n − 1 a [ i ] ∗ b [ n − 1 − i ] ,就可以FFT了。

然而旋转我们怎么处理呢?我们可以把上式拆开成:
n1i=0a[i]b[nj1i]+n1i=nja[i]+b[2nj1i] ∑ i = 0 n − 1 a [ i ] ∗ b [ n − j − 1 − i ] + ∑ i = n − j n − 1 a [ i ] + b [ 2 ∗ n − j − 1 − i ]
FFT求出 D[i]=A[i]B[i] D [ i ] = A [ i ] ∗ B [ i ] ,上式就等于 D[nj1]+D[2nj1] D [ n − j − 1 ] + D [ 2 ∗ n − j − 1 ]
枚举C求出最小值就可以了。

具体实现见代码如下:

#include <bits/stdc++.h>
using namespace std;
int n,m,N,sum,mx,mn=2e9,summ;
complex<double> a[200000],b[200000],aa[200000],w[200000];
void init() {
    const double pi=acos(-1);
    for(int i=0; i<N; i++) {
        w[i]=complex<double>(cos(2.0*pi*i/N),sin(2.0*pi*i/N));
    }
}
void FFT(complex<double> *a) {
    for(int i=0,j=0; i<N; i++) {
        if(i>j)swap(a[i],a[j]);
        for(int k=N>>1; (j^=k)<k; k>>=1);
    }
    for(int i=2; i<=N; i<<=1) {
        int m=i>>1,l=N/i;
        for(int j=0; j<N; j+=i) {
            for(int k=0; k!=m; k++) {
                complex<double> t=a[j+m+k]*w[l*k];
                a[j+m+k]=a[j+k]-t;
                a[j+k]+=t;
            }
        }
    }
}
int main() {
    cin>>n>>m;
    for(int i=0; i<n; i++) {
        cin>>a[i];
    }
    for(int i=0; i<n; i++) {
        cin>>b[n-i-1];
    }
    for(int i=0; i<n; i++) {
        sum+=round((a[i]*a[i]+b[n-i-1]*b[n-i-1]).real());
        summ+=round((b[i]-a[i]).real());
    }
    N=n<<1;
    for(int i=1; i<N; N=max(i<<=1,N));
    init();
    FFT(a);
    FFT(b);
    reverse(w+1,w+N);
    for(int i=0; i<N; i++) {
        a[i]*=b[i];
    }
    FFT(a);
    for(int i=0; i<N; i++) {
        a[i]/=N;
    }
    mx=a[n-1].real();
    for(int i=0; i<n-1; i++) {
        mx=max(mx,int(round((a[i]+a[i+n]).real())));
    }
    for(int i=-m; i<=m; i++) {
        mn=min(mn,n*i*i+2*i*summ);
    }
    cout<<sum-2*mx+mn<<endl;
    return 0;
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值