题目
题目链接:https://www.luogu.com.cn/problem/P3723
我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手环,一个留给自己,一个送给她。每个手环上各有 \(n\) 个装饰物,并且每个装饰物都有一定的亮度。
但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有装饰物的亮度增加一个相同的非负整数 \(c\)。并且由于这个手环是一个圆,可以以任意的角度旋转它,但是由于上面装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。
在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号 \(1 \sim n\),其中 \(n\) 为每个手环的装饰物个数, 第 \(1\) 个手环的 \(i\) 号位置装饰物亮度为 \(x_i\),第 \(2\) 个手环的 \(i\) 号位置装饰物亮度为 \(y_i\),两个手环之间的差异值为(参见输入输出样例和样例解释):
\[\sum_{i=1}^{n} (x_i-y_i)^2
\]
麻烦你帮他计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小,这个最小值是多少呢?
思路
假设 \(a\) 旋转之后的序列为 \(c\),那么我们要求的就是
\[\min(\sum^{n}_{i=1}(c_i-b_i+x))
\]
拆开
\[\sum^{n}_{i=1}(c_i^2+b_i^2)+nx^2+2x(c_i-b_i)-2\sum^{n}_{i=1}c_ib_i
\]
由于 \(m\leq 100\),所以我们可以 \(O(nm)\) 枚举出除最后一项外的最小值。所以我们只需要让 \(-2\sum^{n}_{i=1}c_ib_i\) 最小即可。
设 \(c'(i)=c(n-i+1)\),那么原式就变成了一个卷积的形式。拆环为链后直接上 FFT 即可。
时间复杂度 \(O(n\log n+nm)\)。
代码
#include <bits/stdc++.h>
#define cp complex<double>
using namespace std;
typedef long long ll;
const int N=500010;
const double pi=acos(-1);
int n,m,lim,rev[N];
ll fsum,gsum,fpow,gpow,ans;
cp f[N],g[N];
void fft(cp *f,int inv)
{
for (int i=0;i<lim;i++)
if (rev[i]<i) swap(f[i],f[rev[i]]);
for (int mid=1;mid<lim;mid<<=1)
{
cp temp(cos(pi/mid),inv*sin(pi/mid));
for (int i=0;i<lim;i+=(mid<<1))
{
cp w=cp(1,0);
for (int j=0;j<mid;j++,w*=temp)
{
cp x=f[i+j],y=w*f[i+j+mid];
f[i+j]=x+y; f[i+j+mid]=x-y;
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=0,x;i<n;i++)
{
scanf("%d",&x);
fpow+=x*x; fsum+=x;
f[n-i-1]=cp(1.0*x,0.0);
}
for (int i=0,x;i<n;i++)
{
scanf("%d",&x);
gpow+=x*x; gsum+=x;
g[i]=g[i+n]=cp(1.0*x,0.0);
}
lim=1; ans=1145140000000000000LL;
while (lim<=n*3) lim<<=1;
for (int i=0;i<lim;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)?(lim>>1):0);
fft(f,1); fft(g,1);
for (int i=0;i<lim;i++)
f[i]*=g[i];
fft(f,-1);
for (ll j=-m;j<=m;j++)
for (int i=n;i<n*2;i++)
ans=min(ans,fpow+gpow+j*j*n+2LL*j*(fsum-gsum)-2LL*(ll)(f[i].real()/lim+0.4999));
printf("%lld",ans);
return 0;
}