title
简化题意:
给定两个数列:\(\{x_i\}\) 和 \(\{y_i\}\) 可对各数列进行 循环移动 或 改变数列中某一项的值 ,使得下面的式子的值最小。
\[ \sum^n_{i=1}(x_i-y_i)^2 \]
analysis
我们令增加量为 \(C\) ,那么这个式子就可以化简为:
\[ \sum^n_{i=1}(x_i-y_i+C)^2\\ \sum^n_{i=1}(x_i-y_i)^2+2C\sum^n_{i=1}(x_i-y_i)+\sum^n_{i=1}C^2\\ \sum^n_{i=1}x_i^2-2\sum^n_{i=1}x_iy_i+\sum^n_{i=1}y_i^2+2C\sum^n_{i=1}(x_i-y_i)+nC^2 \]
可以发现,除了第 \(2\) 项之外,其他项都是定值,那么我们要求的就变成了 使 \(\sum^n_{i=1}x_iy_i\) 最大 。
我们把 \(\{y_i\}\) 倍长,这样就不用再考虑环的问题了。
考虑将 \(\{y_i\}\) 转动 \(k\) 位时的答案: \(\sum^n_{i=1}x_iy_{i+k}\) 。
然后再把 \(\{x_i\}\) 翻转,答案就变成了 \(\sum^n_{i=1}x_{n-i+1}y_{i+k}\) ,这非常符合卷积的形式,所以就可以进行 \(FFT\) 快速求值了。
卷积完了之后,因为答案的第 \(n+1\sim 2n\) 项的最大值就是 \(\sum^n_{i=1}x_iy_i\) 的最大值,所以扫一遍求出 \(\max\) 。
然后再 \(O(n)\) 求出其他定值项。
然后枚举一下 \(C\) 求出最终答案。
code
#include<bits/stdc++.h>
const int maxn=2e6+10;
const double Pi=acos(-1);
namespace IO
{
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
char Out[1<<24],*fe=Out;
inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
template<typename T>inline void write(T x,char str)
{
if (!x) *fe++=48;
if (x<0) *fe++='-', x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) *fe++=ch[num--];
*fe++=str;
}
}
using IO::read;
using IO::write;
template<typename T>inline bool chkMin(T &a,const T &b) { return a>b ? (a=b, true) : false; }
template<typename T>inline bool chkMax(T &a,const T &b) { return a<b ? (a=b, true) : false; }
struct Orz{double a,b;}A[maxn],B[maxn],W[maxn];
inline Orz operator + (Orz a,Orz b) { return (Orz){a.a+b.a,a.b+b.b}; }
inline Orz operator - (Orz a,Orz b) { return (Orz){a.a-b.a,a.b-b.b}; }
inline Orz operator * (Orz a,Orz b) { return (Orz){a.a*b.a-a.b*b.b,a.a*b.b+a.b*b.a}; }
int N,r[maxn],l;
inline void FFT(Orz *P,int opt)
{
for (int i=0; i<N; ++i) if (i<r[i]) std::swap(P[i],P[r[i]]);
for (int i=1; i<N; i<<=1)
for (int p=i<<1, j=0; j<N; j+=p)
for (int k=0; k<i; ++k)
{
Orz w=(Orz){W[N/i*k].a,W[N/i*k].b*opt};
Orz x=P[j+k], y=w*P[j+k+i];
P[j+k]=x+y, P[j+k+i]=x-y;
}
}
int n,m,M;
int a[maxn],b[maxn],S[maxn];
inline void solve()
{
N=n-1, M=n+m-1;
for (int i=0; i<=N; ++i) A[i].a=a[i+1], A[i].b=0;
for (int i=0; i<n; ++i) B[i].a=b[n-i], B[i].b=0;
for (int i=0; i<n; ++i) B[i+n].a=B[i].a, B[i+n].b=0;
M+=N;
for (N=1; N<=M; N<<=1) ++l;
for (int i=0; i<N; ++i) r[i]=( (r[i>>1]>>1) | ((i&1)<<(l-1)) );
for (int i=1; i<N; i<<=1)
for (int k=0; k<i; ++k) W[N/i*k]=(Orz){cos(k*Pi/i),sin(k*Pi/i)};
FFT(A,1), FFT(B,1);
for (int i=0; i<N; ++i) A[i]=A[i]*B[i];
FFT(A,-1);
for (int i=0; i<=M; ++i) S[i]=(int)(A[i].a/N+0.5);
}
int main()
{
read(n);read(m);
for (int i=1; i<=n; ++i) read(a[i]);
for (int i=1; i<=n; ++i) read(b[i]);
solve();
int p1=0, p2=0, t1=0, t2=0, G=-1e9, ans=1e9;
for (int i=1; i<=n; ++i) p1+=a[i]*a[i], p2+=b[i]*b[i], t1+=a[i], t2+=b[i];
for (int i=n-1; i<(n<<1); ++i) chkMax(G,S[i]);
for (int C=-m; C<=m; ++C)
{
int tot=p1+p2+n*C*C+(t1-t2)*C*2-G*2;
chkMin(ans,tot);
}
write(ans,'\n');
IO::flush();
return 0;
}