背景:
最近一直在补坑。
题意:
有两个序列
(围成两个圈),将
序列所有的值加上一个非负整数
(自己给的),将
序列逆时针旋转一定的角度,求
的最小值。
思路:
不妨假设旋转完的
序列为
。,则题目就是求
的最小值。
考虑化简式子。
拆括号,得:
发现
为定值,将其提出。
再提取一个
得:
提取出一些数,得到:
的问题比较容易搞定,因为它与最后一个因式无关。那么发现只有
为不定值,使
的值最小即可。
考虑构造 使得其最小,不妨使 为原 序列及其反向倍长序列。那么再使 为 的反向序列,此时形成了一个卷积的形式,那么答案一定就为可取的数中的最大值(前面有一个 号)。
最后再套入上式求解即可。
代码:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const double pai=acos(-1.0);
struct comp
{
double x,y;
comp(double xx=0,double yy=0):x(xx),y(yy) {}
friend comp operator+(const comp &x,const comp &y) {return comp(x.x+y.x,x.y+y.y);}
friend comp operator-(const comp &x,const comp &y) {return comp(x.x-y.x,x.y-y.y);}
friend comp operator*(const comp &a,const comp &b) {return comp(a.x*b.x-a.y*b.y,a.x*b.y+b.x*a.y);}
}a[500000],b[500000];
int n,m,limit=1,l=0;
int r[500000];
LL as1=0,as2=0,bs1=0,bs2=0,ans=2147483647;
void FFT(comp *now,int ty)
{
for(int i=0;i<limit;i++)
if(i<r[i]) swap(now[i],now[r[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
comp wn(cos(pai/mid),ty*sin(pai/mid));
for(int j=0,R=(mid<<1);j<limit;j+=R)
{
comp w(1,0);
for(int k=0;k<mid;k++,w=w*wn)
{
comp x=now[j+k],y=w*now[j+k+mid];
now[j+k]=x+y;
now[j+k+mid]=x-y;
}
}
}
}
int d1[50010],d2[50010];
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&d1[i]);
as1+=(LL)d1[i];
as2+=(LL)d1[i]*d1[i];
}
for(int i=1;i<=n;i++)
{
scanf("%d",&d2[i]);
bs1+=(LL)d2[i];
bs2+=(LL)d2[i]*d2[i];
}
while(limit<=(n*3))
limit<<=1,l++;
for(int i=1;i<=limit;i++)
r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));
for(int i=1;i<=n;i++)
{
a[i].x=a[i+n].x=(double)d1[i];
b[i].x=(double)d2[n-i+1];
}
FFT(a,1);
FFT(b,1);
for(int i=0;i<=limit;i++)
a[i]=a[i]*b[i];
FFT(a,-1);
for(int i=1;i<=n;i++)
for(int j=-m;j<=m;j++)
ans=min(ans,as2+bs2+j*j*n+(LL)2*j*(as1-bs1)-(LL)2*(LL)(a[i+n].x/limit+0.5));
printf("%lld",ans);
}