【BZOJ4827】 [Hnoi2017]礼物
BZOJ4827 [Hnoi2017]礼物
Solution
如果一串数的增加,不就等于另一串数减吗?
那么我们可以把答案写成另一个形式:
\(ans=\sum_{i=1}^n(x_i-y_i+C)^2\)
\(y\)可以是重新排列
那么疯狂拆一下式子,化简之后就是:
\(ans=\sum_{i=1}^nx_i^2+\sum_{i=1}^ny_i^2+\sum_{i=1}^nC^2+2*C*\sum_{i=1}^n(x_i-y_i)-2*\sum_{i=1}^nx_i*y_i\)
如果我们枚举\(C\),那么现在的任务就是算出\(\sum_{i=1}^nx_i*y_i\)的最大值,这样才能让\(ans\)最小.
怎么做呢?
如果把\(y\)数列翻转一下,那么就是:
\(\sum_{i=1}^nx_i*y_i=\sum_{i=1}^nx_i*y_{n-i}\)
这个不就是卷积?
考虑可以逆时针旋转怎么做?断环成链就好了啊.
那么就是把\(y\)串翻转然后复制一遍,求一个卷积然后走人了.
是的结束了.
代码实现
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define ll long long
#define re register
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
inline int gi()
{
int f=1,sum=0;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return f*sum;
}
const int N=1000010,Inf=1e9+10;
const double Pi=acos(-1.0);
int x[N],y[N],r[N],limit,n,m,Ans[N];
struct node
{
double x,y;
node operator+(const node b)const{return (node){x+b.x,y+b.y};}
node operator-(const node b)const{return (node){x-b.x,y-b.y};}
node operator*(const node b)const{return (node){x*b.x-y*b.y,x*b.y+y*b.x};}
}A[N],B[N];
void FFT(node *A,int type)
{
for(int i=0;i<limit;i++)
if(i<r[i])swap(A[i],A[r[i]]);
for(int mid=1;mid<limit;mid<<=1)
{
node Root=(node){cos(Pi/mid),type*sin(Pi/mid)};
for(int R=mid<<1,j=0;j<limit;j+=R)
{
node Mi=(node){1,0};
for(int k=0;k<mid;k++,Mi=Mi*Root)
{
node X=A[j+k],Y=Mi*A[j+mid+k];
A[j+k]=X+Y;
A[j+mid+k]=X-Y;
}
}
}
}
void init()
{
limit=1;int l=0;
for(int i=0;i<n;i++)A[i].x=x[i+1];
for(int i=0;i<n;i++)B[i].x=y[n-i];
for(int i=0;i<n;i++)B[i+n]=B[i];
int M=n+n-1,N=n-1;
while(limit<=(N+M))limit<<=1,l++;
for(int i=0;i<limit;i++)
r[i]=(r[i>>1]>>1)|((i&1)<<(l-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=0;i<limit;i++)
Ans[i]=(int)(A[i].x/limit+0.5);
}
int main()
{
n=gi();m=gi();
for(int i=1;i<=n;i++)x[i]=gi();
for(int i=1;i<=n;i++)y[i]=gi();
int pf1=0,pf2=0,sum1=0,sum2=0,ans=Inf;
for(int i=1;i<=n;i++)pf1=pf1+x[i]*x[i];
for(int i=1;i<=n;i++)pf2=pf2+y[i]*y[i];
for(int i=1;i<=n;i++)sum1+=x[i];
for(int i=1;i<=n;i++)sum2+=y[i];
init();int Max=-Inf;
for(int i=n-1;i<n+n;i++)Max=max(Max,Ans[i]);
for(int C=-m;C<=m;C++)
{
int tot=pf1+pf2+C*C*n-2*Max+2*(sum1-sum2)*C;
ans=min(ans,tot);
}
printf("%d\n",ans);
return 0;
}