zoukankan      html  css  js  c++  java
  • BZOJ4827: [Hnoi2017]礼物

    【传送门:BZOJ4827


    简要题意:

      给出两个环,逆时针从1到n输入一开始每个环相应位置的值,两个环的差异值为$sum_{i=1}^{n}(x[i]-y[i])^2$

      可以给其中一个环的所有值都加上一个正整数,或者逆时针旋转其中一个环

      求出能得到的最小差异值


    题解:

      FFT

      我们其实可以发现把一个环的所有值都加上一个正整数,其实相当于另一个环的所有值都减去一个正整数

      那么我们其实可以直接求出这个数

      设$f(i)=sum_{i=1}^{n}(x[i]-y[i])^2$,$f'(i)=sum_{i=1}^{n}(x[i]+c-y[i])^2$

      将二式相减得到$f'(i)-f(i)=c*sum_{i=1}^{n}(c+2*x[i]-2*y[i])$

      设$sx=sum_{i=1}^{n}x[i]$,$sy=sum_{i=1}^{n}y[i]$

      得到$f'(i)-f(i)=c*(n*c+2*sx-2*sy)=nc^2+2c(sx-sy)$

      因为我们要使得这个式子尽量小,所以要$nc^2+2c(sx-sy)$尽量小,不就是二次函数求对称轴吗$(-frac{b}{2a})$

      得出来$c=frac{sy-sx}{n}$

      直接把c求出来,然后加进x[i]里面就好了

      首先对于当前$sum_{i=1}^{n}(x[i]-y[i])^2$(这个时候的x[i]已经加上c了)

      我们化成$sum_{i=1}^{n}x[i]^2-2x[i]y[i]+y[i]^2$

      显然$x[i]^2$和$y[i]^2$可以前缀和求出来,设为sum

      对于$sum_{i=1}^{n}2x[i]y[i]$,可以得到$2*sum_{i=1}^{n}x[i]y[i]$,还不能卷积,例题的方法是将y倒过来

      我的FFT习惯是把n-1,然后i从0开始,就得到$2*sum_{i=0}^{n}x[i]y[n-i]$,卷积形式√

      但是我们还有旋转操作,而实际上对于两个串,只需要对一个串进行旋转就可以了

      那么就只要在任意一个串中往后延伸,直到构成了两个相等的串

      然后再做FFT,之后枚举断点,求出以哪个位置为结尾,求出最大值ans

      最终答案为sum-2*ans

      注意加long long,如果全程有double就不用加long long


    参考代码:

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long LL;
    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);}
    }a[210000],b[210000];
    int R[210000];
    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<<=1)
        {
            Complex wn(cos(PI/i),sin(on*PI/i));
            for(int j=0;j<len;j+=(i<<1))
            {
                Complex w(1,0);
                for(int k=0;k<i;k++,w=w*wn)
                {
                    Complex u=y[j+k];
                    Complex v=w*y[j+k+i];
                    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;
    }
    void calc(int n)
    {
        int L=0,m=2*n;
        for(n=1;n<=m;n<<=1) L++;
        memset(R,0,sizeof(R));
        for(int i=0;i<n;i++) R[i]=(R[i>>1]>>1)|(i&1)<<(L-1);
        fft(a,n,1);fft(b,n,1);
        for(int i=0;i<=n;i++) a[i]=a[i]*b[i];
        fft(a,n,-1);
    }
    int r1[51000],r2[51000];
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);n--;
        int c=0;
        for(int i=0;i<=n;i++)
        {
            scanf("%d",&r1[i]);
            c-=r1[i];
            a[i].r=r1[i];
        }
        for(int i=n;i>=0;i--)
        {
            scanf("%d",&r2[i]);
            c+=r2[i];
            b[i].r=r2[i];
        }
        c=round(double(c)/double(n+1));
        for(int i=0;i<=n;i++) a[i].r+=c;
        for(int i=n+1;i<2*n+2;i++) a[i].r=a[i-n-1].r;
        LL sum=0;
        for(int i=0;i<=n;i++) sum+=(r1[i]+c)*(r1[i]+c)+r2[i]*r2[i];
        calc(n+1);
        LL ans=0;
        for(int i=n;i<2*n+1;i++) ans=max(ans,LL(a[i].r+0.5));
        printf("%lld
    ",sum-2LL*ans);
        return 0;
    }

     

  • 相关阅读:
    Source Insight中文注释乱码、字体大小、等宽解决方法
    linux API函数大全
    Linux常用命令
    iOS开发中,ScrollView放大后,子视图位置计算的数据分析
    将当前屏幕保存为图片
    AutoLayout相关方法及实现过程
    iOS开源库
    UIWebView开发中,js与oc,js与swift交互,相互传递参数的方法
    更新Xcode导致插件不能使用的解决办法
    关于Xcode不能打印崩溃日志
  • 原文地址:https://www.cnblogs.com/Never-mind/p/8983639.html
Copyright © 2011-2022 走看看