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

猜你喜欢

转载自www.cnblogs.com/Emiya-wjk/p/10028602.html
今日推荐