1. 程式人生 > >【HNOI2017】禮物

【HNOI2017】禮物

題面

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個數,分別代表第一條手環和第二條手環上從某個位置開始逆時針方向上各裝飾物的亮度。

Output

輸出一個數,表示兩個手環能產生的最小差異值。注意在將手環改造之後,裝飾物的亮度可以大於 m。

Sample Input

5 6

1 2 3 4 5

6 3 3 4 5

Sample Output

1

Hint

【樣例解釋】

需要將第一個手環的亮度增加1,第一個手環的亮度變為: 2 3 4 5 6

旋轉一下第二個手環。對於該樣例,是將第二個手環的亮度6 3 3 4 5向左迴圈移動一個位置,使得第二手環的最終的亮度為: 3 3 4 5 6。

此時兩個手環的亮度差異值為1。

【資料範圍】

30%的資料滿足n≤500, m≤10;

70%的資料滿足n≤5000;

100%的資料滿足1≤n≤50000, 1≤m≤100, 1≤ai≤m。

題目分析

由於可以選擇一個裝飾物加上非負數,等價於固定一個裝飾物加上任意實數\(x\)
\[ \begin{split} \sum_{i=1}^n(a_i-b_i+x)^2&=\sum_{i=1}^n(a_i^2+b_i^2+x^2+2\cdot x(a_i-b_i)-2a_ib_i)\\ &=\sum_{i=1}^n(a_i^2+b_i^2)+(n\cdot x^2+2\cdot x(\sum_{i=1}^n(a_i-b_i)))-2\sum_{i=1}^na_ib_i \end{split} \]


其中,第一部分為定值,第二部分為二次函式\(x=-\frac {\sum\limits_{i=1}^n(a_i-b_i)} n\)時為最值。

第三部分,如果我們把\(a\)陣列反轉,相當於\(\sum_{i=1}^na_{n-i}b_i\),此時,這就相當於一個卷積的形式。

對於旋轉手環,我們只需把\(a\)陣列複製兩份求卷積,在最後\(ans_{n+1}\sim ans_{2n}\)中取最大值即可。

程式碼實現

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<iomanip>
#include<cstdlib>
#include<complex> 
#define MAXN 0x7fffffff
typedef long long LL;
const int N=270005;
using namespace std;
inline int Getint(){register int x=0,f=1;register char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}return x*f;}
typedef complex<double> Z;
const double Pi=M_PI; 
void FFT(Z *a,int x,int K){
    static int rev[N],lst;
    int n=1<<x;
    if(n!=lst){
        for(int i=0;i<n;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<x-1);
        lst=n;
    } 
    for(int i=0;i<n;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
    for(int i=1;i<n;i<<=1){
        int tmp=i<<1;
        Z wn(cos(Pi/i),sin(K*Pi/i));
        for(int j=0;j<n;j+=tmp){
            Z w(1,0);
            for(int k=0;k<i;k++,w=w*wn){
                Z x=a[j+k],y=w*a[i+j+k];
                a[j+k]=x+y,a[i+j+k]=x-y;
            }
        }
    }
    if(K==-1)for(int i=0;i<n;i++)a[i]/=n;
} 
Z a[N],b[N];
LL ans,sum;
int main(){
    int n=Getint(),m=Getint();
    for(int i=n;i>=1;i--){
        int x=Getint();
        a[i+n].real()=a[i].real()=x,ans+=x*x,sum+=x; 
    }
    for(int i=1;i<=n;i++){
        int x=Getint();
        b[i].real()=x,ans+=x*x,sum-=x;
    }
    double t=-1.0*sum/n;
    ans=ans+min(ceil(t)*ceil(t)*n+2*ceil(t)*sum,floor(t)*floor(t)*n+2*floor(t)*sum);
    int x=ceil(log2(n*3+1));
    FFT(a,x,1),FFT(b,x,1);
    for(int i=0;i<(1<<x);i++)a[i]=a[i]*b[i];
    FFT(a,x,-1);
    int ret=0;
    for(int i=n+1;i<=(n<<1);i++)ret=max(ret,(int)(a[i].real()+0.5));
    cout<<ans-2*ret;
    return 0;
}