正题
看题,发现有两个操作:
1.把第二个序列旋转。
2.把第一个序列整体+c。
因为旋转只用一个旋转就够了。
发现什么。
所以使xy的和最大即可。
假设表示第二个序列向有旋转i位的价值。
那么就有
不是卷积的形式。
那么怎么办,让,
。
那么就有
又因为。
所以可以直接用卷积来做。
其实就相当于把A反过来。
然后求之间的最小值。
那么现在有个问题就是要整体+c。
很好做,我们枚举一个c,把它加给第一个序列.
会发现
那么这就很有趣了,我们现在用FFT来确定了ab最大,又可以通过枚举来知道bc的大小。
那么每次算出平方和再减去ab+bc的两倍,取一个最小值就可以了。
<FFT的打法略奇特>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
struct complex{
double x,y;
complex (double xx=0,double yy=0){x=xx,y=yy;}
complex operator-(const complex b)const {return (complex){x-b.x,y-b.y};}
complex operator+(const complex b)const {return (complex){x+b.x,y+b.y};}
complex operator*(const complex b)const {return (complex){x*b.x-y*b.y,x*b.y+b.x*y};}
}a[300010],b[300010];
int d1[100010],d2[100010];
int n,m;
const double Pi=acos(-1.0);
int where[300010];
int limit,l;
void dft(complex *now,int idft){
for(int i=0;i<limit;i++)
if(i<where[i]) swap(now[i],now[where[i]]);
for(int mid=2;mid<=limit;mid<<=1){
complex wn(cos(2.0*Pi/mid),idft*sin(2.0*Pi/mid));
for(int i=0;i<limit;i+=mid){
complex w(1,0);
for(int x=i,y=i+mid/2;x<i+mid/2;x++,w=w*wn,y++){
complex a=now[x],b=w*now[y];
now[x]=a+b;
now[y]=a-b;
}
}
}
}
int main(){
scanf("%d %d",&n,&m);
int sum=0;
for(int i=n-1;i>=0;i--) scanf("%d",&d1[i]),a[i].x=d1[i];
for(int i=1;i<=n;i++) scanf("%d",&d2[i]),b[i+n].x=b[i].x=d2[i],sum+=d2[i];
limit=1,l=0;
while(limit<3*n+1) limit*=2,l++;
for(int i=0;i<limit;i++) where[i]=(where[i>>1]>>1) | ((i&1)<<(l-1));
dft(a,1);
dft(b,1);
for(int i=0;i<=limit;i++) a[i]=a[i]*b[i];
dft(a,-1);
int ans=2147483647;
int mmax=0;
for(int i=n;i<=2*n-1;i++)
mmax=max(mmax,(int)(a[i].x/limit+0.5));
int now=0,dat=0;
for(int i=0;i<n;i++) now+=(d1[i]-m)*(d1[i]-m),dat+=2*(d1[i]-m);//计算每次变化加的东西,自己推公式
for(int i=1;i<=n;i++) now+=d2[i]*d2[i];
dat+=n;
for(int i=-m;i<=m;i++){
ans=min(ans,now-2*(mmax+i*sum));
now+=dat;
dat+=2*n;
}
printf("%d",ans);
}