题面
??
解法
FFT:
??和式可化为:
∑i=1n(xi+c?yi)2=∑i=1n(x2i+y2i)+n?c2?2?c?∑i=1n(yi?xi)?2?∑i=1n(xi?yi)
??当c确定时,前三项都为定值,为了使和式最小,只需要使得在旋转之后 xi?yi 的和最大,即求旋转后的 ∑ni=1(xi?yi)max
??* : c∈[-m,m]
??** : 求 ∑ni=1(xi?yi)max 只需利用FFT。假设旋转第一个环之后第一个环的第1个是原来第一个环的第k个,那么此时的 ∑ni=1(xi?yi) 就为:
∑i=1n?k+1(xk+i?1?yi)+∑i=1k?1(xi?yn?k+i+1)······①
??如果把第一个环翻转,把环看作多项式,那么①式就是两个多项式乘积中一个系数的值,所以我们可以只翻转第一个环做一次FFT,然后只翻转第二个环做一次FFT,取两次结果中最大的系数就可以了
复杂度
O(nlogn+m)
代码
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#define Rint register int
#define Lint long long int
using namespace std;
const double pi=acos(-1);
const int INF=0x3f3f3f3f;
const int N=150010;
struct complex
{ double r,i;complex operator + (const complex o){ return (complex){ r+o.r,i+o.i };}complex operator - (const complex o){ return (complex){ r-o.r,i-o.i };}complex operator * (const complex o){ return (complex){ r*o.r-i*o.i,r*o.i+i*o.r };}
}f[N],g[N],s[N];
int a[N],b[N],w[N],v[N];
int n,m,ans,mx,N1;
void FFT(complex a[N],int n,int m,int opt)
{for(int i=1,j=0;i<n;i++){for(int k=n;j^=k>>=1,~j&k;) ;if( i<j ) swap( a[i],a[j] );}complex wn,w,tmp;for(int d=0;(1<<d)<n;d++){int m=1<<d;wn=(complex){ cos(pi/m),sin(opt*pi/m) };for(int i=0;i<n;i+=m*2){w=(complex){ 1,0 };for(int j=0;j<m;j++,w=w*wn){tmp=w*a[i+j+m];a[i+j+m]=a[i+j]-tmp,a[i+j]=a[i+j]+tmp;}}}
}
void solve()
{if( !N1 ) for(N1=1;N1<=2*n;N1=N1*2) ;FFT( f,N1,n,1 ),FFT( g,N1,n,1 );for(int i=0;i<=N1;i++) s[i]=f[i]*g[i];FFT( s,N1,n,-1 );for(int i=0;i<=N1;i++) f[i]=(complex){ 0,0 },g[i]=(complex){ 0,0 };
}
int main()
{freopen("gift.in","r",stdin);freopen("gift.out","w",stdout);int sum1,sum2,x;scanf("%d%d",&n,&m);sum1=sum2=0,ans=INF;for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum1+=a[i]*a[i];for(int i=1;i<=n;i++) scanf("%d",&b[i]),sum1+=b[i]*b[i];for(int i=0;i<=n-1;i++) f[i]=(complex){ a[n-i]*1.0,0 },g[i]=(complex){ b[i+1]*1.0,0 };solve();for(int i=1;i<=n;i++) w[i]=(int)(s[n-i].r/N1+0.5);for(int i=0;i<=n-1;i++) f[i]=(complex){ a[i+1]*1.0,0 },g[i]=(complex){ b[n-i]*1.0,0 };solve();for(int i=1;i<=n;i++) v[i]=(int)(s[i-1].r/N1+0.5);for(int i=1;i<=n;i++)if( v[i-1]+w[i]>mx ) mx=v[i-1]+w[i],x=i;if( x>1 )//需要进行旋转{int tmp[N];for(int i=x;i<=n;i++) tmp[i]=a[i];for(int i=n;i>=n-x+2;i--) a[i]=a[i-n+x-1];for(int i=x;i<=n;i++) a[i-x+1]=tmp[i];}for(int i=1;i<=n;i++) sum2+=a[i]-b[i];if( sum2<0 ) sum2*=-1;for(int c=0;c<=m;c++) ans=min( ans,sum1+n*c*c-2*c*sum2-2*mx );printf("%d\n",ans);return 0;
}