• [AH2017/HNOI2017]礼物(FFT)


    题目描述

    我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手 环,一个留给自己,一
    个送给她。每个手环上各有 n 个装饰物,并且每个装饰物都有一定的亮度。但是在她生日的前一天,我的室友突
    然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有
    装饰物的亮度增加一个相同的自然数 c(即非负整数)。并且由于这个手环是一个圆,可以以任意的角度旋转它,
    但是由于上面 装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差
    异值最小。在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号 1,2,…,n,
    其中 n 为每个手环的装饰物个数,第 1 个手环的 i 号位置装饰物亮度为 xi,第 2 个手 环的 i 号位置装饰物
    亮度为 yi,两个手环之间的差异值为(参见输入输出样例和样例解释): ∑(xi-yi)2麻烦你帮他
    计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小, 这个最小值是多少呢?
    题解
    这道题作为FFT的例题还是比较简单的。
    一开始我还naive的以为m比较小,可以枚举c的值。
    其实如果把平方展开后可以发现我们的轮换和c没有关系。
    所以就把a数组reverse一下,做一遍FFT,统计答案就好了。
    至于c的值,我感觉暴力枚举就可以,但其实我们也可以根据二次函数的对称轴来算。
    代码
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #define N 200002
    using namespace std;
    typedef long long ll;
    const double pai=acos(-1.0);
    ll ans,sum,sum2,l,L,c[N];
    int rev[N],n,m;
    inline ll rd(){
        ll x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    struct fs{
        double x,y;
        fs(){x=y=0;}
        fs(double xx,double yy){x=xx;y=yy;}
        fs operator +(const fs &b)const{return fs{x+b.x,y+b.y};}
        fs operator -(const fs &b)const{return fs{x-b.x,y-b.y};}
        fs operator *(const fs &b)const{return fs{x*b.x-y*b.y,x*b.y+y*b.x};}
    }a[N],b[N];
    inline void FFT(fs *a,int tag){
        for(int i=0;i<l;++i)if(i>rev[i])swap(a[i],a[rev[i]]);
        for(int i=1;i<l;i<<=1){
          fs wn(cos(pai/i),tag*sin(pai/i));
          for(int j=0;j<l;j+=(i<<1)){
              fs w(1,0); 
              for(int k=0;k<i;++k,w=w*wn){
                  fs x=a[j+k],y=w*a[i+j+k];
                  a[j+k]=x+y;a[i+j+k]=x-y;
            }
          } 
        }
    }
    int main(){
        n=rd();m=rd();
        for(int i=1;i<=n;++i)a[n-i+1].x=rd();
        for(int i=1;i<=n;++i){
          b[i].x=rd();
          sum+=a[i].x*a[i].x+b[i].x*b[i].x;
          sum2+=a[i].x-b[i].x;
        }
        l=1;L=0;
        while(l<(n<<1))l<<=1,L++;
        for(int i=1;i<l;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
        FFT(a,1);FFT(b,1);
        for(int i=0;i<l;++i)a[i]=a[i]*b[i];
        FFT(a,-1);
        for(int i=0;i<l;++i)a[i].x=(ll)(a[i].x/l+0.1);
        for(int i=1;i<=n;++i)a[i+n].x+=a[i].x;
        for(int i=n+1;i<=n<<1;++i){
            ans=max(ans,(ll)a[i].x);
        }
        ll x=1e18;
        for(int i=sum2/n-6;i<=sum2/n+6;++i)x=min(x,n*i*i+2*i*sum2);
        printf("%lld",sum-2*ans+x);
        return 0;
    }
     
  • 相关阅读:
    (转)oracle 11g安装后用户名忘记怎么办
    svn
    (转)ublox公司AGPS解决方案简介
    转(Google 全国 地图 纠偏数据 偏移数据 火星坐标修正 方案 )
    (转)真实经纬度的最简单获得方法
    (转)64bit上安装32位oracle 10 g出现错误:无法定位承诺工序输入点 getprocessimagifilenamew 于动态链接库PSAPI.DLL
    转】PPT带备注演示(只有讲解者看到备注)[转载]
    iphone应用程序结构
    ObjC 初识
    并行编程(PLINQ)学习笔记
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10257433.html
Copyright © 2020-2023  润新知