【bzoj4827】[Hnoi2017]礼物【FFT/NTT】
题目链接
题解:我们设两个手环增加的数相差c,且已经确定好了排列的顺序。
则差异值为
=>
=>
我们发现是确定的。不就是要求一个二次函数的最小值吗?直接带公式计算。最后我们要求的就是一个排列,使得的值最大。
我们不妨设第二个手环转了k次。
则我们要求的就是的值。
把x翻转一下=>
我们发现这是一个卷积的形式。于是我们设两个多项式,把两个多项式乘起来,次数为n+k+1的项代的系数就是转了k次的答案。取个max就好了。最后计算一下上面那个式子的答案就解决了!
脑抽的我根本没看到x和y是分两行读入的,居然还过了样例,贡献了好几次WA!
代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=270005;
const double pi=3.141592653589793;
int n,m,k,x[N],y[N],rev[N];
ll ans,tot,maxn;
struct complex{
double x,y;
complex(){
x=y=0;
}
complex(double x,double y):x(x),y(y){}
friend complex operator + (const complex &a,const complex &b){
return complex(a.x+b.x,a.y+b.y);
}
friend complex operator - (const complex &a,const complex &b){
return complex(a.x-b.x,a.y-b.y);
}
friend complex operator * (const complex &a,const complex &b){
return complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}
}a[N],b[N];
void fft(complex *a,int dft){
for(int i=0;i<k;i++){
if(i<rev[i]){
swap(a[i],a[rev[i]]);
}
}
for(int i=1;i<k;i<<=1){
complex wn=complex(cos(pi/i),dft*sin(pi/i));
for(int j=0;j<k;j+=i<<1){
complex w=complex(1,0),x,y;
for(int l=j;l<j+i;l++,w=w*wn){
x=a[l];
y=w*a[l+i];
a[l]=x+y;
a[l+i]=x-y;
}
}
}
if(dft==-1){
for(int i=0;i<k;i++){
a[i].x/=k;
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&x[i]);
ans+=x[i]*x[i];
tot+=2*x[i];
}
for(int i=1;i<=n;i++){
scanf("%d",&y[i]);
ans+=y[i]*y[i];
tot-=2*y[i];
}
double tmp=(-1.0*tot)/(2*n);
ll l=floor(tmp),r=ceil(tmp);
if(n*l*l+tot*l<n*r*r+tot*r){
ans+=n*l*l+tot*l;
}else{
ans+=n*r*r+tot*r;
}
for(int i=1;i<=n;i++){
a[i].x=x[n-i+1];
b[i].x=b[i+n].x=y[i];
}
for(k=1;k<=3*n;k<<=1);
for(int i=0;i<k;i++){
rev[i]=(rev[i>>1]>>1)|((i&1)*(k>>1));
}
fft(a,1);
fft(b,1);
for(int i=0;i<k;i++){
a[i]=a[i]*b[i];
}
fft(a,-1);
for(int i=n+1;i<=2*n;i++){
maxn=max(maxn,(ll)(a[i].x+0.5));
}
ans-=2*maxn;
printf("%lld\n",ans);
return 0;
}