题目链接
题目不难看懂,首先把公式变形:
∑i=0n−1Ai+B(i+k)modn2=∑i=0n−1A2i+∑i=0n−1B2(i+k)modn−2∑i=0n−1Ai∗B(i+k)modn
问题转化成求 ∑n−1i=0Ai∗B(i+k)modn 的最大值
很明显这是一个循环卷积求和的问题,利用FFT可以确定最大值 k <script type="math/tex" id="MathJax-Element-312">k</script>,之后我们再重新带入原始进行计算即可。
WA点1.不能利用FFT直接求结果,有较大的精度误差,只能用其判断k的值
WA点2.一定要熟悉convert的过程,跑FFT把两个卷积的数组构造好,并且知道我们需要那一部分的卷积结果
AC代码:FFT用bin神的模版:
最后,网络赛时候我们是暴力+XJBG做出来的,丑陋
#include <bits/stdc++.h>
const int maxn=200000+10;
const double PI=acos(-1);
typedef long long ll;
using namespace std;
int n,str1[maxn],str2[maxn],len;
struct Complex{
double r,i;
Complex(double _r = 0.0,double _i = 0.0){
r = _r; i = _i;
}
Complex operator +(const Complex &b){
return Complex(r+b.r,i+b.i);
}
Complex operator -(const Complex &b){
return Complex(r-b.r,i-b.i);
}
Complex operator *(const Complex &b){
return Complex(r*b.r-i*b.i,r*b.i+i*b.r);
}
};
void rader(Complex y[],int len){
int i,j,k;
for(i=1,j=len/2;i<len-1;i++){
if(i<j)
swap(y[i],y[j]);
k=len/2;
while(j>=k){
j-=k;
k/=2;
}
if(j<k)
j+=k;
}
}
void fft(Complex y[],int len,int on)
{
rader(y,len);
for(int h=2; h<=len;h=h<<1){
Complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
for(int j=0;j<len;j+=h){
Complex w(1,0);
for(int k = j;k<j+h/2;k++){
Complex u = y[k];
Complex t = w*y[k+h/2];
y[k]=u+t;
y[k+h/2]=u-t;
w=w*wn;
}
}
}
if(on==-1)
for(int i=0;i<len;i++)
y[i].r/=len;
}
void convert(Complex a[],Complex b[],int len){
fft(a,len,1); //DFT
fft(b,len,1);
for(int i=0;i<len;i++) //时域卷积=频域相乘
a[i]=a[i]*b[i];
fft(a,len,-1);//IDFT
}
Complex x1[maxn],x2[maxn];
void init(){
len=1;
while(len<n*2)
len=len<<1;
for(int i=0;i<n;i++)
x1[i]=Complex(str1[n-i-1],0); //注意此处取反,理解convert的性质
for(int i=n;i<len;i++)
x1[i]=Complex(0,0);
for(int i=0;i<n;i++)
x2[i]=Complex(str2[i],0);
for(int i=n;i<2*n;i++)
x2[i]=Complex(str2[i-n],0);
for(int i=2*n;i<len;i++)
x2[i]=Complex(0,0);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&str1[i]);
for(int i=0;i<n;i++)
scanf("%d",&str2[i]);
init();
convert(x1,x2,len);
ll max=-1,k=0;
for(int i=0;i<len;i++){
if(max<x1[i].r){
max=x1[i].r;
k=i-n+1;
}
}
ll ans=0;
for(int i=0;i<n;i++)
ans+=1ll*(str1[i]-str2[(i+k)%n])*(str1[i]-str2[(i+k)%n]);
printf("%lld\n",ans);
}
return 0;
}