ACM-ICPC国际大学生程序设计竞赛北京赛区(2016)网络赛 1006 Periodic Signal
传送门
题目大意
给你两个序列,求一个差值平方和最小
具体求法就是
min{(a1-b1)²+...+(an-bn)²,(a1-b2)²+...+(an-b1)²,...,(a1-bn)²+...+(an-b1)²}
知道题意后化简的
∑ni=1a2i+∑ni=1b2i−2(ai∗bj)(iϵn,jϵn)
现在问题就变成了怎么快速求出
ai∗bj
的最大值
普通方法的时间复杂度O(
n2
),肯定要超时。
考虑下面的循环矩阵
⎡⎣⎢⎢⎢⎢⎢⎢a1anan−1.a2a2a1an.a3a3a2a1.a4...............anan−1an−2.a1⎤⎦⎥⎥⎥⎥⎥⎥∗⎡⎣⎢⎢⎢⎢⎢⎢b1b2b3.bn⎤⎦⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢c1c2c3.cn⎤⎦⎥⎥⎥⎥⎥⎥
只需要求 cn 的最大值即可。
可以参考下面一个链接
http://www.zybang.com/question/dd8b336ad690b3e2f9aa4dc0b596e1ea.html
将循环矩阵*向量转换为求卷积
所以用快速傅里叶变换将时间复杂度降为了O( nlog2n )
具体做法是
令 a→ =( a1,a2...,an )
令 b→ =( bn,bn−1,...b2,b1,bn,bn−1...b2 )
之后对 a→ 求 c→ =DFT( a→ )
对 b→ 求 d→ = DFT( b→ )
时间复杂度为O( nlog2n )
然后求 S→=c→∗d→
时间复杂度为O(n)
再对 S→ 求 ans−→− =IDFT( S→ )
时间复杂度为O( nlog2n )
最后求 ans−→− 中最大的元素即可
不过这道题的坑点在于精度问题
WA了若干次
发现精度怎么都够不了
于是发现在 ans−→− 中答案的位置就是移动的位置
于是先求出移动的位置
然后暴力跑一边就求出答案了
代码如下(缩进懒得改了套的模板,将就看)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 150005
#define pi acos(-1.0)
using namespace std;
int l,T,n,num,wz;
long long ans,tot,sum[N],A[N],B[N];
struct complex
{
double r,i;
complex(double real=0.0,double image=0.0){r=real;i=image;}
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);
}
}x1[N],x2[N];
void brc(complex *y,int l)
{
register int i,j,k;
for(i=1,j=l/2;i<l-1;i++)
{
if(i<j)swap(y[i],y[j]);
k=l/2;
while(j>=k)j-=k,k/=2;
if(j<k)j+=k;
}
}
void fft(complex *y,int l,double on)
{
register int h,i,j,k;
complex u,t;
brc(y,l);
for(h=2;h<=l;h<<=1)
{
complex wn(cos(on*2*pi/h),sin(on*2*pi/h));
for(j=0;j<l;j+=h)
{
complex w(1,0);
for(k=j;k<j+h/2;k++)
{
u=y[k];
t=w*y[k+h/2];
y[k]=u+t;
y[k+h/2]=u-t;
w=w*wn;
}
}
}
if(on==-1)for(i=0;i<l;i++)y[i].r/=l;
}
long long Getans(int k)
{
ans=0;
for(int i=0;i<n;i++)ans+=(A[i]-B[(i+k)%n])*(A[i]-B[(i+k)%n]);
return ans;
}
int main()
{
register int i,j;
scanf("%d",&T);
while(T--)
{
l=1;
tot=0;
scanf("%d",&n);
while(l<n*2)l<<=1;
for(i=n-1;i>=0;i--)
{
scanf("%d",&num);
x1[i].r=num;
x1[i].i=0.0;
tot+=num*num;
A[n-i-1]=num;
}
for(i=n;i<2*n-1;i++)x1[i]=x1[i-n];
for(i=2*n-1;i<l;i++)x1[i].r=x1[i].i=0.0;
for(i=0;i<n;i++)
{
scanf("%d",&num);
x2[i].r=num;
x2[i].i=0.0;
tot+=num*num;
B[i]=num;
}
for(i=n;i<l;i++)x2[i].r=x2[i].i=0.0;
fft(x1,l,1);//DFT
fft(x2,l,1);//DFT
for(i=0;i<l;i++)x1[i]=x1[i]*x2[i];
fft(x1,l,-1);//IDFT
double m=-1;
for(i=n-1;i<2*n;i++)if(m<x1[i].r)m=x1[i].r,wz=i;//求位置
printf("%lld\n",Getans((wz+1)%n));
}
return 0;
}