Description
我的室友最近喜歡上了一個可愛的小女生。馬上就要到她的生日了,他決定買一對情侶手 環,一個留給自己,一
個送給她。每個手環上各有 n 個裝飾物,並且每個裝飾物都有一定的亮度。但是在她生日的前一天,我的室友突
然發現他好像拿錯了一個手環,而且已經沒時間去更換它了!他只能使用一種特殊的方法,將其中一個手環中所有
裝飾物的亮度增加一個相同的自然數 c(即非負整數)。並且由於這個手環是一個圓,可以以任意的角度旋轉它,
但是由於上面 裝飾物的方向是固定的,所以手環不能翻轉。需要在經過亮度改造和旋轉之後,使得兩個手環的差
異值最小。在將兩個手環旋轉且裝飾物對齊了之後,從對齊的某個位置開始逆時針方向對裝飾物編號 1,2,…,n,
其中 n 爲每個手環的裝飾物個數,第 1 個手環的 i 號位置裝飾物亮度爲 xi,第 2 個手 環的 i 號位置裝飾物
亮度爲 yi,兩個手環之間的差異值爲(參見輸入輸出樣例和樣例解釋): 麻煩你幫他
計算一下,進行調整(亮度改造和旋轉),使得兩個手環之間的差異值最小, 這個最小值是多少呢?
Input
輸入數據的第一行有兩個數n, m,代表每條手環的裝飾物的數量爲n,每個裝飾物的初始 亮度小於等於m。
接下來兩行,每行各有n個數,分別代表第一條手環和第二條手環上從某個位置開始逆時 針方向上各裝飾物的亮度。
1≤n≤50000, 1≤m≤100, 1≤ai≤m
Output
輸出一個數,表示兩個手環能產生的最小差異值。
注意在將手環改造之後,裝飾物的亮度 可以大於 m。
Sample Input
5 6
1 2 3 4 5
6 3 3 4 5
Sample Output
1
【樣例解釋】
需要將第一個手環的亮度增加1,第一個手環的亮度變爲: 2 3 4 5 6 旋轉一下第二個手環。對於該樣例,是將第
二個手環的亮度6 3 3 4 5向左循環移動 2017-04-15 第 6 頁,共 6 頁 一個位置,使得第二手環的最終的亮度爲
:3 3 4 5 6。 此時兩個手環的亮度差異值爲1。
這道題是一道非常經典的FFT的題目。
爲了FFT 方便一點,以下的下標是以0開始的。
事實上,在第二個手環上加上c,它們的差異值的變化和第一個手環減去c是一樣的。
所以,我們可以把總的差異值寫成:。
分別令和表示兩個手環的初始裝飾度亮度之和。
我們就可以把這個式子變形拆成三部分得到:。
第一部分是一個常數,所以我們可以直接計算。
對於第二部分,我們利用二次函數的知識可以發現:當時,這部分可以取到最小值。c爲整數,四捨五入即可。
對於第三部分,其實我們可以暴力求。沒錯,當沒有旋轉操作的時候,確實可以。
但是旋轉有n種情況啊?這樣做的話時間複雜度就是了啊!怎麼辦呢?
我們可以先將序列反轉,這個式子就可以化爲。
看到這個式子,我們發現它是一個卷積的形式,可以使用FFT。
我們可以把這個反轉後的序列倍長,然後我們發現對於任意的,這個序列的第項到第項就是旋轉後的一種情況。這個時候,使用FFT進行優化,就可以做到了。
如果有誤在評論區吼一聲哦!
代碼:
#include<cmath>
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
const double pi=acos(-1.0);
int n,m,k,l,limit=1,A,B,ans=0x7f7f7f7f,r[400010];
struct cmplx{
double r,f;
cmplx(double a=0,double b=0):r(a),f(b){}
cmplx operator=(const int b){
*this=cmplx(b,0);
return *this;
}
cmplx operator+(const cmplx b){
return cmplx(r+b.r,f+b.f);
}
cmplx operator-(const cmplx b){
return cmplx(r-b.r,f-b.f);
}
cmplx operator*(const cmplx b){
return cmplx(r*b.r-f*b.f,r*b.f+f*b.r);
}
cmplx operator*=(const cmplx b){
*this=*this*b;
return *this;
}
}a[400010],b[400010];
int rd(){
int x=0;
char c;
do c=getchar();
while(!isdigit(c));
do{
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}while(isdigit(c));
return x;
}
void init(){
while(limit<=n+m){
limit<<=1;
l++;
}
for(int i=0;i<limit;i++)
r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
return;
}
void FFT(cmplx *a,int limit,int type){
for(int i=0;i<limit;i++)
if(i<r[i])
swap(a[i],a[r[i]]);
for(register int mid=1;mid<limit;mid<<=1){
const double x=pi/mid;
cmplx wn=cmplx(cos(x),type*sin(x));
for(register int R=mid<<1,j=0;j<limit;j+=R){
cmplx w=cmplx(1,0);
for(register int k=0;k<mid;k++,w*=wn){
cmplx y=a[j+k],z=w*a[j+mid+k];
a[j+k]=y+z;
a[j+mid+k]=y-z;
}
}
}
if(type==-1)
for(int i=0;i<=limit;i++)
a[i].r/=limit;
return;
}
int main(){
n=rd()-1;
k=rd();
m=n<<1;
init();
for(int i=0;i<=n;i++){
a[i]=rd();
A+=a[i].r*a[i].r;
B+=a[i].r;
}
for(int i=n;~i;i--){
b[i]=b[i+n+1]=rd();
A+=b[i].r*b[i].r;
B-=b[i].r;
}
int c=(int)(-B/(double)(n+1)+0.5);
B=(n+1)*c*c+2*B*c;
FFT(a,limit,1);
FFT(b,limit,1);
for(int i=0;i<=limit;i++)
a[i]*=b[i];
FFT(a,limit,-1);
for(int i=n;i<=m;i++)
ans=min(ans,A+B-(int)(a[i].r+0.5)*2);
printf("%d",ans);
return 0;
}