FFT
咕了大半个月。。。
设旋转后两个数列分别为 {an} { a n } 和 {bn} { b n } ,则答案为 ∑ni=1(ai−bi+c)2 ∑ i = 1 n ( a i − b i + c ) 2
推式子:
∑ni=1(a2i+b2i) ∑ i = 1 n ( a i 2 + b i 2 ) 和 ∑ni=1(ai−bi) ∑ i = 1 n ( a i − b i ) 是个定值,而后面两个关于 c c 的可以通过二次函数对称轴求得其最小值。那么我们只需要求即可。
把 ai a i 翻转,即求 ∑ni=1an−i+1bi ∑ i = 1 n a n − i + 1 b i 。而因为它可以旋转,那么把 b b <script type="math/tex" id="MathJax-Element-101">b</script>倍长(断环为链)后做一遍FFT即可。
代码:
#include<cmath>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1<<18
#define F inline
#define sqr(x) ((x)*(x))
using namespace std;
typedef double DB;
typedef long long LL;
const DB pi=acos(-1);
struct P{ DB x,y; }a[N],b[N];
int n,m,l,r[N];
LL ans;
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
return l==r?EOF:*l++;
}
F int _read(){
int x=0; char ch=readc();
while (!isdigit(ch)) ch=readc();
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x;
}
F P operator + (P a,P b){ return (P){a.x+b.x,a.y+b.y}; }
F P operator - (P a,P b){ return (P){a.x-b.x,a.y-b.y}; }
F P operator * (P a,P b){ return (P){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x}; }
F void FFT(P *a,int f){
for (int i=0;i<n;i++) if (i<r[i]) swap(a[i],a[r[i]]);
for (int k=1;k<n;k<<=1){
P w={1,0},wn={cos(pi/k),sin(f*pi/k)},x,y;
for (int i=0;i<n;i+=k<<1,w=(P){1,0})
for (int j=0;j<k;j++,w=w*wn)
x=a[i+j],y=w*a[i+j+k],a[i+j]=x+y,a[i+j+k]=x-y;
}
}
int main(){
n=_read(),m=_read(); DB d;
for (int i=0;i<n;i++) a[n-i-1].x=_read();
for (int i=0;i<n;i++) b[i].x=b[i+n].x=_read();
for (int i=0;i<n;i++)
ans+=1ll*sqr(a[i].x)+1ll*sqr(b[i].x),d+=a[i].x-b[i].x;
LL L=floor(d/n),R=ceil(d/n);
ans+=min(L*L*n-(LL)2*d*L,R*R*n-(LL)2*d*R);
for (m=n*3,n=1;n<m;n<<=1) l++;
for (int i=0;i<n;i++)
r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
FFT(a,1),FFT(b,1); LL mx=0;
for (int i=0;i<n;i++) a[i]=a[i]*b[i];
FFT (a,-1);
for (int i=0;i<n;i++) mx=max(mx,(LL)(a[i].x/n+0.5));
return printf("%lld\n",ans-2*mx),0;
}