[BZOJ 4827][Hnoi2017]礼物(FFT)

Description

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

Solution

列式子然后化成了Σ(xi2+yi2+2xic-2yic)-2*Σ(xi*yi+k)+n*c2

循环卷积来求中间那项的值,经过一些预处理额并枚举c和k最小化答案

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define PI acos(-1)
#define MAXN 50005
#define INF 1LL<<60
typedef long long LL;
using namespace std;
int n,m;
struct cp
{
    double r,i;
    cp(double r=0,double i=0):r(r),i(i){}
    cp operator + (const cp& x)
    {return cp(r+x.r,i+x.i);}
    cp operator - (const cp& x)
    {return cp(r-x.r,i-x.i);}
    cp operator * (const cp& x)
    {return cp(r*x.r-i*x.i,r*x.i+i*x.r);}
}a[MAXN*8],b[MAXN*8];
void brc(cp* x,int l)
{
    int j=l/2;
    for(int i=1;i<l-1;i++)
    {
        if(i<j)swap(x[i],x[j]);
        int k=l/2;
        while(k<=j)
        {
            j-=k;
            k>>=1;
        }
        if(j<k)j+=k;
    }
}
void fft(cp* x,int l,int on)
{
    brc(x,l);
    for(int h=2;h<=l;h<<=1)
    {
        cp wn(cos(2*on*PI/h),sin(2*on*PI/h));
        for(int i=0;i<l;i+=h)
        {
            cp w(1,0);
            for(int j=i;j<i+h/2;j++)
            {
                cp u=x[j];
                cp t=w*x[j+h/2];
                x[j]=u+t;
                x[j+h/2]=u-t;
                w=w*wn;
            }
        }
    }
    if(on==-1)for(int i=0;i<l;i++)x[i].r/=l;
}
int main()
{
    scanf("%d%d",&n,&m);
    int l=1;
    while(l<2*n+1)l<<=1;
    LL sum=0,t=0;
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        a[i]=a[i+n]=cp(x,0);
        sum+=x*x;
        t+=x;
    }
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        b[n-i+1]=cp(x,0);
        sum+=x*x;
        t-=x;
    }
    fft(a,l,1),fft(b,l,1);
    for(int i=0;i<l;i++)a[i]=a[i]*b[i];
    fft(a,l,-1);
    t=t>0?t:-t;
    LL ans=INF;
    for(int i=0;i<=m;i++)
    ans=min(ans,i*i*n-2*i*t);
    ans+=sum;
    LL p=0;
    for(int i=0;i<l;i++)
    p=max(p,(LL)(a[i].r+0.1));
    ans-=2*p;
    printf("%lld",ans);
    return 0;
} 

 

posted @ 2017-05-08 23:47  Zars19  阅读(358)  评论(0编辑  收藏  举报