[bzoj 4827--HNOI2017]礼物

124 篇文章 2 订阅
1 篇文章 0 订阅

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

这道题我们可以先化一下式子,sigma(((xi+c)-yi)^2)(c可正可负,因为如果为负,其实就相当于给yi+c),化成了sigma(xi^2-2* xi* yi+yi*yi+c*c+2*c*xi-2*c*yi)。其中的2* xi* yi是以不同开头来开始的,其实我们可以很快发现这东西可以用fft来求,而然后剩下的c*c+2*c*xi-2*c*yi,我们可以运用初中的数学知识O(1)快速求出最小值,但由于c要取整,所以-b/2a时要++时再算一次,–时再算一次。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int maxn=50010;
const double pi=acos(-1.0);
struct complex
{
    double r,i;
    complex(){}
    complex(double _r,double _i){r=_r,i=_i;}
    friend complex operator +(const complex &x,const complex &y){return complex(x.r+y.r,x.i+y.i);}
    friend complex operator -(const complex &x,const complex &y){return complex(x.r-y.r,x.i-y.i);}
    friend complex operator *(const complex &x,const complex &y){return complex(x.r*y.r-x.i*y.i,x.r*y.i+x.i*y.r);}
}a1[maxn*4],a2[maxn*4],b1[maxn*4],b2[maxn*4];
int n,m;
int R[maxn*4];
void fft(complex *y,int len,int on)
{
    for(int i=0;i<len;i++)if(i<R[i])swap(y[i],y[R[i]]);
    for(int i=1;i<len;i*=2)
    {
        complex wn(cos(pi/i),sin(pi*on/i));
        for(int j=0;j<len;j+=i*2)
        {
            complex w(1,0);
            for(int k=0;k<i;k++,w=w*wn)
            {
                complex u=y[j+k];
                complex v=y[j+k+i]*w;
                y[j+k]=u+v;
                y[j+k+i]=u-v;
            }
        }
    }
    if(on==-1)for(int i=0;i<len;i++)y[i].r/=len;
}
int main()
{
    double wy,ans=0.0,s=0.0;
    scanf("%d%lf",&n,&wy);n--;
    for(int i=0;i<=n;i++)
    {
        scanf("%lf",&a1[i].r),a2[n-i].r=a1[i].r;
        ans+=a1[i].r*a1[i].r;s+=a1[i].r;
    }
    for(int i=0;i<=n;i++)
    {
        scanf("%lf",&b1[i].r),b2[n-i].r=b1[i].r;
        ans+=b1[i].r*b1[i].r,s-=b1[i].r;
    }
    m=n+n;int L=0;
    for(n=1;n<=m;n*=2)L++;
    for(int i=0;i<=n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    fft(a1,n,1);fft(a2,n,1);fft(b1,n,1);fft(b2,n,1);
    for(int i=0;i<=n;i++)a1[i]=a1[i]*b2[i];
    for(int i=0;i<=n;i++)a2[i]=a2[i]*b1[i];
    fft(a1,n,-1);fft(a2,n,-1);
    double ss=0.0;
    for(int i=0;i<=m/2-1;i++)ss=max(ss,a1[i].r+a2[m/2-i-1].r);
    ans-=2.0*ss;
    int he;double hehe=999999999.0;
    he=-s/(m/2+1)-1;
    hehe=min(hehe,(m/2+1)*he*he+2.0*he*s);
    he++;
    hehe=min(hehe,(m/2+1)*he*he+2.0*he*s);
    he++;
    hehe=min(hehe,(m/2+1)*he*he+2.0*he*s);
    printf("%.0lf\n",hehe+ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值