【BZOJ4827】[Hnoi2017]礼物 FFT

【BZOJ4827】[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个数,分别代表第一条手环和第二条手环上从某个位置开始逆时 针方向上各装饰物的亮度。
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向左循环移动一个位置,使得第二手环的最终的亮度为
:3 3 4 5 6。 此时两个手环的亮度差异值为1。

题解:题中式子:

遇见式子不要害怕嘛,我们将它拆开来看

发现前面那个东西跟c无关,中间那个就是sumx-sumy,所以这变成了一个二次函数求对称轴的问题,直接O(1)计算出c就好了,因此,c是一个定值!

然后我们将xi都加上c,于是就有回到了题中给的那个式子,发现如果将x旋转,相当于x[i]变成x[i+j](将x倍长一遍),于是我们再将原式拆开

 

又一次发现前面那两个东西是一个定值,我们只要求出最后面那个东西就好了,发现我们将y数组反转后它就变成了一个卷积,直接用FFT求解

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
#define pi acos(-1.0)
using namespace std;
struct cp
{
	double x,y;
	cp (double a,double b){x=a,y=b;}
	cp (){}
	cp operator + (cp a){return cp(x+a.x,y+a.y);}
	cp operator - (cp a){return cp(x-a.x,y-a.y);}
	cp operator * (cp a){return cp(x*a.x-y*a.y,x*a.y+y*a.x);}
}n1[1<<20],n2[1<<20];
int n,m,C,sumx,sumy,X[1<<20],Y[1<<20],ans[1<<20],minn;
int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
void FFT(cp *a,int len,int f)
{
	int i,j,k,h;
	cp t;
	for(i=k=0;i<len;i++)
	{
		if(i>k)	swap(a[i],a[k]);
		for(j=(len>>1);(k^=j)<j;j>>=1);
	}
	for(h=2;h<=len;h<<=1)
	{
		cp wn(cos(f*2*pi/h),sin(f*2*pi/h));
		for(j=0;j<len;j+=h)
		{
			cp w(1,0);
			for(k=j;k<j+h/2;k++)	t=w*a[k+h/2],a[k+h/2]=a[k]-t,a[k]=a[k]+t,w=w*wn;
		}
	}
}
void work(cp *a,cp *b,int len)
{
	FFT(a,len,1),FFT(b,len,1);
	for(int i=0;i<len;i++)	a[i]=a[i]*b[i];
	FFT(a,len,-1);
	for(int i=0;i<len;i++)	ans[i]=(int)(a[i].x/len+0.1);
}
int main()
{
	n=rd(),m=rd(),minn=1<<30;
	int i,j,len=1,tmp=0;
	while(len<3*n)	len<<=1;
	for(i=0;i<n;i++)	X[i]=rd(),sumx+=X[i];
	for(i=0;i<n;i++)	Y[i]=rd(),sumy+=Y[i];
	C=(sumy-sumx)/n;
	if(abs(sumy-sumx-n*C)>abs(sumy-sumx-n*C-n))	C++;
	if(abs(sumy-sumx-n*C)>abs(sumy-sumx-n*C+n))	C--;
	if(C>0)	for(i=0;i<n;i++)	X[i]+=C;
	else	for(i=0;i<n;i++)	Y[i]+=C;
	for(i=0;i<n;i++)	tmp+=X[i]*X[i],n1[i]=n1[i+n]=cp(1.0*X[i],0.0);
	for(i=0;i<n;i++)	tmp+=Y[i]*Y[i],n2[n-i-1]=cp(1.0*Y[i],0.0);
	work(n1,n2,len);
	for(i=0;i<n;i++)	minn=min(minn,tmp-2*ans[n+i-1]);
	printf("%d",minn);
	return 0;
}
posted @ 2017-05-19 15:44  CQzhangyu  阅读(363)  评论(0编辑  收藏  举报