1. 程式人生 > >[AH2017/HNOI2017]禮物(FFT)

[AH2017/HNOI2017]禮物(FFT)

opera long rev += fin 展開 strong truct 對齊

題目描述

我的室友最近喜歡上了一個可愛的小女生。馬上就要到她的生日了,他決定買一對情侶手 環,一個留給自己,一 個送給她。每個手環上各有 n 個裝飾物,並且每個裝飾物都有一定的亮度。但是在她生日的前一天,我的室友突 然發現他好像拿錯了一個手環,而且已經沒時間去更換它了!他只能使用一種特殊的方法,將其中一個手環中所有 裝飾物的亮度增加一個相同的自然數 c(即非負整數)。並且由於這個手環是一個圓,可以以任意的角度旋轉它, 但是由於上面 裝飾物的方向是固定的,所以手環不能翻轉。需要在經過亮度改造和旋轉之後,使得兩個手環的差 異值最小。在將兩個手環旋轉且裝飾物對齊了之後,從對齊的某個位置開始逆時針方向對裝飾物編號 1,2,…,n, 其中 n 為每個手環的裝飾物個數,第 1 個手環的 i 號位置裝飾物亮度為 xi,第 2 個手 環的 i 號位置裝飾物 亮度為 yi,兩個手環之間的差異值為(參見輸入輸出樣例和樣例解釋): ∑(xi
-yi)2麻煩你幫他 計算一下,進行調整(亮度改造和旋轉),使得兩個手環之間的差異值最小, 這個最小值是多少呢? 題解 這道題作為FFT的例題還是比較簡單的。 一開始我還naive的以為m比較小,可以枚舉c的值。 其實如果把平方展開後可以發現我們的輪換和c沒有關系。 所以就把a數組reverse一下,做一遍FFT,統計答案就好了。 至於c的值,我感覺暴力枚舉就可以,但其實我們也可以根據二次函數的對稱軸來算。 代碼
#include<iostream>
#include<cstdio>
#include<cmath>
#define N 200002
using namespace
std; typedef long long ll; const double pai=acos(-1.0); ll ans,sum,sum2,l,L,c[N]; int rev[N],n,m; inline ll rd(){ ll x=0;char c=getchar();bool f=0; while(!isdigit(c)){if(c==-)f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return f?-x:x; } struct fs{
double x,y; fs(){x=y=0;} fs(double xx,double yy){x=xx;y=yy;} fs operator +(const fs &b)const{return fs{x+b.x,y+b.y};} fs operator -(const fs &b)const{return fs{x-b.x,y-b.y};} fs operator *(const fs &b)const{return fs{x*b.x-y*b.y,x*b.y+y*b.x};} }a[N],b[N]; inline void FFT(fs *a,int tag){ for(int i=0;i<l;++i)if(i>rev[i])swap(a[i],a[rev[i]]); for(int i=1;i<l;i<<=1){ fs wn(cos(pai/i),tag*sin(pai/i)); for(int j=0;j<l;j+=(i<<1)){ fs w(1,0); for(int k=0;k<i;++k,w=w*wn){ fs x=a[j+k],y=w*a[i+j+k]; a[j+k]=x+y;a[i+j+k]=x-y; } } } } int main(){ n=rd();m=rd(); for(int i=1;i<=n;++i)a[n-i+1].x=rd(); for(int i=1;i<=n;++i){ b[i].x=rd(); sum+=a[i].x*a[i].x+b[i].x*b[i].x; sum2+=a[i].x-b[i].x; } l=1;L=0; while(l<(n<<1))l<<=1,L++; for(int i=1;i<l;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1)); FFT(a,1);FFT(b,1); for(int i=0;i<l;++i)a[i]=a[i]*b[i]; FFT(a,-1); for(int i=0;i<l;++i)a[i].x=(ll)(a[i].x/l+0.1); for(int i=1;i<=n;++i)a[i+n].x+=a[i].x; for(int i=n+1;i<=n<<1;++i){ ans=max(ans,(ll)a[i].x); } ll x=1e18; for(int i=sum2/n-6;i<=sum2/n+6;++i)x=min(x,n*i*i+2*i*sum2); printf("%lld",sum-2*ans+x); return 0; }

[AH2017/HNOI2017]禮物(FFT)