【GDSOI2017第三轮模拟】Gift

Description

这里写图片描述
n<=50000

Solution

刚刚考完体育中考,整个人都颓废了_ (:з」∠) _
于是决定写一个FFT冷静一下~
我们可以强制把加的数都放到y上面,这样c的非负整数限制也没有了。
那么我们把式子化一下:

i=1n(xi(yi+c))2=i=1nx2i+i=1n(yi+c)22i=1nxi(yi+c)

=i=1nx2i+i=1ny2i+nc2+2ci=1n(yixi)2i=1nxiyi

发现前两项为定值,扔掉。
把和c有关的项提出来,发现是一个形如ac^2+bc的二次函数,要求最小值。
这样就可以把c求出来了,也就是我们只需要求旋转过后的 ni=1xiyi 的最大值。
考虑FFT,把y翻转,那么就是形如 ni=1xiyni+1 ,也就是卷积的形式。
将x串倍长,那么对于每一个x串的结尾,往前长度为n的串和y串的卷积就是这一段串的答案。
于是直接上FFT,最后对每个答案求个最大值就好了。
听说循环卷积跑得快?我不会啊QwQ

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define pi acos(-1)
using namespace std;
typedef double db;
const int N=4*1e5+5;
struct complex{
    db r,i;
    complex(db _r=0,db _i=0) {r=_r;i=_i;}
    friend complex operator + (complex x,complex y) {return complex(x.r+y.r,x.i+y.i);}
    friend complex operator - (complex x,complex y) {return complex(x.r-y.r,x.i-y.i);}
    friend complex operator * (complex x,complex y) {return complex(x.r*y.r-x.i*y.i,x.r*y.i+x.i*y.r);}
}A[N],B[N],C[N],t[N];
int n,m,ans,sumx,sumy,len,lg;
db x[N],y[N],z[N];
void DFT(complex *a,int flag) {
    for(int i=0;i<len;i++) {
        int p=0;
        for(int j=i,k=0;k<lg;k++,j/=2) p=p*2+(j&1);
        t[p]=a[i];
    }
    for(int m=2;m<=len;m*=2) {
        int half=m/2;
        for(int i=0;i<half;i++) {
            complex w=complex(cos(i*flag*pi/half),sin(i*flag*pi/half));
            for(int j=i;j<len;j+=m) {
                complex u=t[j],v=t[j+half]*w;
                t[j]=u+v;t[j+half]=u-v;
            }
        }
    }
    for(int i=0;i<len;i++) a[i]=t[i];
    if (flag==-1) for(int i=0;i<len;i++) a[i].r/=len;
}
void FFT(db *a,db *b,db *c) {
    for(int i=0;i<len;i++) {
        A[i]=complex(a[i],0);
        B[i]=complex(b[i],0);
    }
    DFT(A,1);DFT(B,1);
    for(int i=0;i<len;i++) C[i]=A[i]*B[i];
    DFT(C,-1);
    for(int i=0;i<len;i++) c[i]=C[i].r;
}
int main() {
    freopen("gift.in","r",stdin);
    freopen("gift.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,0,n-1) scanf("%lf",&x[i]),sumx+=x[i],ans+=x[i]*x[i];
    fo(i,0,n-1) scanf("%lf",&y[i]),sumy+=y[i],ans+=y[i]*y[i];

    db a=n,b=2*(sumy-sumx);
    db c=-b/2.0/a;
    int c1=floor(c),c2=ceil(c);
    if (c-c1<c2-c) c=c1;else c=c2;
    ans+=a*c*c+b*c;

    fo(i,n,2*n-1) x[i]=x[i-n];
    fo(i,1,n/2) swap(y[i-1],y[n-i]);

    int nn=n,sum=0;n*=2;
    for(len=1;len<2*n;len*=2) lg++;
    FFT(x,y,z);
    fo(i,nn,n-1) sum=max(sum,(int)z[i]);
    printf("%d\n",ans-2*sum);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值