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\n",sum-2LL*ans);
    return 0;
}

 

猜你喜欢

转载自www.cnblogs.com/Never-mind/p/8983639.html
今日推荐