题解:【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(nlgn)了。

AC代码:

#pragma optimize(3)
#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;
}

猜你喜欢

转载自blog.csdn.net/ezoihqm/article/details/81166624