bzoj 4827: [Hnoi2017]礼物 (FFT)

来源:互联网 发布:淘宝网站的推广方法 编辑:程序博客网 时间:2024/05/21 08:39

题目描述

传送门

题目大意:给出两个串,可以旋转和整串权值增加c,求操作后最小的ni=1(xiyi)2

题解

先考虑不增加c。
ni=1(xiyi)2
=ni=1x2i+y2ini=12xiyi
式子的前一部分不受旋转的影响可以直接计算。
后一部分的话,可以将其中一个串扩大一倍,另一个串反置,然后做FFT.类似于字符串匹配。
然后考虑c。
对于c来说每次只会影响(xi+c)2,(xi+c)yi=xiyi+cyi两部分的影响都可以直接计算。
所以只需要最开始做FFT即可。后面枚举c的取值,都可以O(n)的直接计算。

代码

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define N 300013#define LL long long #define pi acos(-1)using namespace std;struct data{    double x,y;     data(double X=0,double Y=0) {        x=X,y=Y;    }}f[N],g[N];int n,m,n1,L,R[N],c[N],a[N],b[N];data operator +(data a,data b){    return data(a.x+b.x,a.y+b.y);}data operator -(data a,data b) {    return data(a.x-b.x,a.y-b.y);}data operator *(data a,data b){    return data(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}void FFT(data a[],int n,int opt){    for (int i=0;i<n;i++)     if (i>R[i]) swap(a[i],a[R[i]]);    for (int i=1;i<n;i<<=1) {        data wn=data(cos(pi/i),opt*sin(pi/i));        for (int p=i<<1,j=0;j<n;j+=p) {            data w=data(1,0);            for (int k=0;k<i;k++,w=w*wn) {                data x=a[j+k],y=w*a[j+k+i];                a[j+k]=x+y; a[j+k+i]=x-y;            }        }    }}int pow(int x){    return x*x; }int main(){     freopen("a.in","r",stdin);     freopen("my.out","w",stdout);     scanf("%d%d",&n,&m);     int m1=n*2; int sumb=0;     for (n1=1;n1<=m1;n1<<=1) L++;     for (int i=0;i<=n1;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));     for (int i=1;i<=n;i++) scanf("%d",&a[i]);     for (int i=n+1;i<=2*n;i++) a[i]=a[i-n];     for (int i=1;i<=n;i++) scanf("%d",&b[i]),sumb+=b[i];     for (int i=0;i<n;i++) g[n-i-1].x=b[i+1];     for (int i=0;i<2*n;i++) f[i].x=a[i+1];     FFT(f,n1,1); FFT(g,n1,1);     for (int i=0;i<n1;i++) f[i]=f[i]*g[i];     FFT(f,n1,-1);     for (int i=0;i<2*n;i++) c[i]=(int)(f[i].x/n1+0.5);     int ans=2000000000;     for (int t=-m;t<=m;t++) {        int sum=0;        for (int i=1;i<=n;i++) sum+=pow(a[i]+t)+pow(b[i]);        for (int i=n;i<2*n;i++){          int k=sum;          k=k-2*c[i]-2*sumb*t;          ans=min(ans,k);        }     }    printf("%d\n",ans);}
原创粉丝点击