【bzoj4827】[AH2017/HNOI2017]禮物(FFT)

 

Description

我的室友最近喜歡上了一個可愛的小女生。馬上就要到她的生日了,他決定買一對情侶手 環,一個留給自己,一

個送給她。每個手環上各有 n 個裝飾物,並且每個裝飾物都有一定的亮度。但是在她生日的前一天,我的室友突

然發現他好像拿錯了一個手環,而且已經沒時間去更換它了!他只能使用一種特殊的方法,將其中一個手環中所有

裝飾物的亮度增加一個相同的自然數 c(即非負整數)。並且由於這個手環是一個圓,可以以任意的角度旋轉它,

但是由於上面 裝飾物的方向是固定的,所以手環不能翻轉。需要在經過亮度改造和旋轉之後,使得兩個手環的差

異值最小。在將兩個手環旋轉且裝飾物對齊了之後,從對齊的某個位置開始逆時針方向對裝飾物編號 1,2,…,n,

其中 n 爲每個手環的裝飾物個數,第 1 個手環的 i 號位置裝飾物亮度爲 xi,第 2 個手 環的 i 號位置裝飾物

亮度爲 yi,兩個手環之間的差異值爲(參見輸入輸出樣例和樣例解釋): \sum_{i=1}^{n}(x_i-y_i)^2麻煩你幫他

計算一下,進行調整(亮度改造和旋轉),使得兩個手環之間的差異值最小, 這個最小值是多少呢?

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是一樣的。

所以,我們可以把總的差異值寫成:\sum_{i=0}^{n-1}(x_i+c-y_i)^2

分別令Sum_xSum_y表示兩個手環的初始裝飾度亮度之和。

我們就可以把這個式子變形拆成三部分得到:(\sum_{i=0}^{n-1}x_i^2+y_i^2)+(nc^2+2(Sum_x-Sum_y)c)-(2\sum_{i=0}^{n-1}x_iy_i)

第一部分是一個常數,所以我們可以直接計算。

對於第二部分,我們利用二次函數的知識可以發現:當c=-\frac{Sum_x-Sum_y}{n}時,這部分可以取到最小值。c爲整數,四捨五入即可。

對於第三部分,其實我們可以暴力求。沒錯,當沒有旋轉操作的時候,確實可以。

但是旋轉有n種情況啊?這樣做的話時間複雜度就是O(n^2)了啊!怎麼辦呢?

我們可以先將序列y反轉,這個式子就可以化爲2\sum_{i=0}^{n-1}x_iy_{n-1-i}

看到這個式子,我們發現它是一個卷積的形式,可以使用FFT。

我們可以把這個反轉後的序列倍長,然後我們發現對於任意的i<n,這個序列的第i項到第i+n-1項就是旋轉後的一種情況。這個時候,使用FFT進行優化,就可以做到O(nlog_2n)了。

如果有誤在評論區吼一聲哦!

代碼:

#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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章