zoukankan      html  css  js  c++  java
  • [AH2017/HNOI2017] 礼物 解题报告 (FFT)

    题目链接:

    https://www.luogu.org/problemnew/show/P3723

    题目:

    我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手环,一个留给自己,一个送给她。每个手环上各有 n 个装饰物,并且每个装饰物都有一定的亮度。

    但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有装饰物的亮度增加一个相同的自然数 c(即非负整数)。并且由于这个手环是一个圆,可以以任意的角度旋转它,但是由于上面装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。

    在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号1,2,…,n,其中 n 为每个手环的装饰物个数, 第 1 个手环的 i 号位置装饰物亮度为 xi,第 2 个手环的 i 号位置装饰物亮度为 yi,两个手环之间的差异值为(参见输入输出样例和样例解释):

    $sum_{i=1}^{n} (x_i-y_i)^2$

    麻烦你帮他计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小,这个最小值是多少呢?

    题解:

    差异值=$sum_{i=1}^{n}(a_i+x-b_i)^2$

    $sum_{i=1}^na_i^2+sum_{i=1}^{n}b_i^2+nx^2+2x(sum_{i=1}{n}a_i-sum_{i=1}{n}b_i)-2sum_{i=1}^{n}a_ib_i$

    我们枚举$x(-m<=x<=m)$,发现除了最后一项都是定值

    那么我们另最后一项最大即可

    由于$a$,$b$其实都是可以旋转的,那么我们把$a$倍长

    末项$=sum_{i=x}^{n+x-1}a_ib_{i-x+1}$

    再按照套路把$b$反向,$b_i=b_{n-i+1}$

    末项$=sum_{i=x}^{n-x+1}a_ib_{n-i+x}$

    $=sum_{i=1}^{n}a_{i-1+x}b_{n-i+1}$

    这显然是一个卷积的形式,即把$a$与$b$卷起来的第$n+x$项

     代码:

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<iostream>
    #include<cmath>
    using namespace std;
    typedef double db;
    typedef long long ll;
    
    const int N=1e6+15;
    const ll inf=1e18;
    const db pi=acos(-1.0);
    int r[N];
    struct complex
    {
        db x,y;
        complex (db xx=0,db yy=0) {x=xx;y=yy;}
    }A[N],B[N];
    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);}
    inline int read()
    {
        char ch=getchar();int s=0,f=1;
        while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
        while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
        return s*f;
    }
    void fft(int limit,complex *a,int type)
    {
        for (int i=0;i<limit;i++) if (i<r[i]) swap(a[i],a[r[i]]);
        for (int len=1;len<limit;len<<=1)
        {
            complex wn=complex(cos(pi/len),type*sin(pi/len));
            for (int k=0;k<limit;k+=(len<<1))
            {
                complex w=complex(1,0);
                for (int l=0;l<len;l++,w=w*wn)
                {
                    complex Nx=a[k+l],Ny=w*a[k+len+l];
                    a[k+l]=Nx+Ny;
                    a[k+len+l]=Nx-Ny;
                }
            }
        }
    }
    int n,m;
    int a[N],b[N];
    int main()
    {
        ll a1=0,b1=0,a2=0,b2=0;
        n=read();m=read();
        for (int i=1;i<=n;i++) a[i]=read(),a1+=a[i],a2+=a[i]*a[i];
        for (int i=1;i<=n;i++) b[i]=read(),b1+=b[i],b2+=b[i]*b[i];
        
        for (int i=1;i<=n;i++) 
        {
            A[i].x=A[i+n].x=a[i];
            B[i].x=b[n-i+1];
        }
        
        int limit=1,l=0;
        while (limit<n+n+n) limit<<=1,++l;
        for (int i=0;i<limit;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
        
        fft(limit,A,1);fft(limit,B,1);
        for (int i=0;i<=limit;i++) A[i]=A[i]*B[i];
        fft(limit,A,-1);
        for (int i=0;i<=limit;i++) A[i].x=(ll)(A[i].x/limit+0.5);
        
        
        ll ans=inf;
        for (int x=1;x<=n;x++)    
            for (int z=-m;z<=m;z++)
                ans=min(ans,a2+b2+n*z*z+2ll*z*(a1-b1)-2ll*(ll)A[x+n].x);
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    进程池的使用
    同步提交,异步提交
    协程
    单线程下实现并发的套接字
    批量上传
    TP中的session和cookie
    ajaxReturn
    ajax的两种方式
    Yii里表单的操作方法(展示渲染待续......)
    Yii里文件上传的操作方法(图片修改,在详情上展示,批量上传待续...)
  • 原文地址:https://www.cnblogs.com/xxzh/p/10609744.html
Copyright © 2011-2022 走看看