[4827][Hnoi2017]礼物——FFT 大佬们的博客 Some Links

版权声明:欢迎大家转载,转载请标明出处。 https://blog.csdn.net/ylsoi/article/details/81951358

题目大意:

我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手 环,一个留给自己,一个送给她。每个手环上各有 n 个装饰物,并且每个装饰物都有一定的亮度。但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有装饰物的亮度增加一个相同的自然数 c(即非负整数)。并且由于这个手环是一个圆,可以以任意的角度旋转它,但是由于上面 装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号 1,2,…,n,其中 n 为每个手环的装饰物个数,第 1 个手环的 i 号位置装饰物亮度为 xi,第 2 个手 环的 i 号位置装饰物亮度为 yi,两个手环之间的差异值为(参见输入输出样例和样例解释): \sum_{i=1}^{n}(x_i-y_i)^2麻烦你帮他计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小, 这个最小值是多少呢?

思路:

可以看一下可以不可以把式子拆开。

i = 1 n ( x i y i + c ) 2 = i = 1 n x i 2 + y i 2 + c 2 + 2 c ( x i y i ) 2 x y = i = 1 n ( x i 2 + y i 2 ) + 2 c i = 1 n ( x i y i ) + n c 2 i = 1 n 2 x i y i

只需要独立地对每一个部分求解最优解即可。
第一部分是定值,第二部分可以用二次函数的最值来做。
第三部分不难发现可以 n 2 枚举其中一个环的排列情况后计算答案。考虑把一段环拆开并且复制成两份,我们发现环在不断移动的过程就是每一个下标不断加1的过程。两个数组中的下标的差为定值,于是一个经典的做法就是把其中一个数组反转,反转之后就会变成和为定值,这也就刚好是满足多项式乘法的系数表示,直接FFT计算即可。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)

using namespace std;

void File(){
    freopen("bzoj4827.in","r",stdin);
    freopen("bzoj4827.out","w",stdout);
}

const double pi=acos(-1.0);
const int maxn=50000+10;
const int inf=0x3f3f3f3f;
int n,m,lim=1,dn[maxn<<3],cnt,ans=0,sum,sum0;

struct Complex{
    double x,y;
    Complex(double xx=0,double yy=0){x=xx,y=yy;}
};
Complex operator + (Complex a,Complex b){return (Complex){a.x+b.x,a.y+b.y};}
Complex operator - (Complex a,Complex b){return (Complex){a.x-b.x,a.y-b.y};}
Complex operator * (Complex a,Complex b){return (Complex){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};}
Complex a[maxn<<3],b[maxn<<3];

void fft(Complex *A,int ty){
    REP(i,0,lim-1)if(i<dn[i])swap(A[i],A[dn[i]]);
    for(int len=1;len<lim;len<<=1){
        Complex w(cos(2.0*pi/(len<<1)),ty*sin(2.0*pi/(len<<1)));
        for(int L=0;L<lim;L+=len<<1){
            Complex wk(1,0);
            REP(i,L,L+len-1){
                Complex u=A[i],v=wk*A[i+len];
                A[i]=u+v;
                A[i+len]=u-v;
                wk=wk*w;
            }
        }
    }
}

int main(){
    File();
    scanf("%d%d",&n,&m);
    int tmp;
    REP(i,1,n)scanf("%d",&tmp),a[n-i].x=tmp,sum+=tmp*tmp,sum0+=tmp;
    REP(i,1,n)scanf("%d",&tmp),b[i-1].x=b[i-1+n].x=tmp,sum+=tmp*tmp,sum0-=tmp;
    while(lim<=n*3-3)lim<<=1,++cnt;
    if(!cnt)cnt=1;
    REP(i,0,lim-1)dn[i]=(dn[i>>1]>>1)|((i&1)<<(cnt-1));
    fft(a,1);
    fft(b,1);
    REP(i,0,lim-1)a[i]=a[i]*b[i];
    fft(a,-1);
    //REP(i,0,lim-1)printf("%d\n",(int)(a[i].x/lim+0.5));
    REP(i,n-1,2*n-2)ans=max(ans,(int)(a[i].x/lim+0.5));
    int x=-sum0/n,Min=inf;
    Min=min(Min,n*x*x+2*sum0*x);
    Min=min(Min,n*(x+1)*(x+1)+2*sum0*(x+1));
    Min=min(Min,n*(x-1)*(x-1)+2*sum0*(x-1));
    ans=sum+Min-ans*2;
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/81951358