【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
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。
【样例解释】
需要将第一个手环的亮度增加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; }
| 欢迎来原网站坐坐! >原文链接<