[AH2017/HNOI2017]礼物(FFT)

[Luogu3723] [DarkBZOJ4827]

题解

首先,有一个结论:两个手环增加非负整数亮度,等于其中一个增加一个整数亮度(可以为负)

设增加亮度为x.求\(\sum_{i=1}^{n}(a_{i}+x-b_{i})^2\)

把式子拆开,问题转化为求 \(\sum_{i=1}^{n}a_{i}b_{i}\)的最大值 ,就是一个卷积

[一个套路] : 所以把反过来的数列 \({a}\) 倍长,和数列 \({b}\) 卷积,得到的项里面的第\(n+1\)\(n*2\)项的最大值,就是原式的最大值

(手模发现有些位置是0,不影响,所以很巧妙的构造出了卷积的答案)

枚举最大值是枚举翻转,同时对于增加的x,由于最大亮度只有100,所以也只需要枚举\([-100,100]\)即可

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
    register LL x=0,f=1;register char c=getchar();
    while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
    while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f*x;
}

const int MAXN=300005;
const double Pi=acos(-1);

namespace F_F_T{

    struct cmpx{
        double x,y;
        cmpx(double xx=0,double yy=0){x=xx,y=yy;}
        inline friend cmpx operator + (cmpx a,cmpx b){return cmpx(a.x+b.x,a.y+b.y);}
        inline friend cmpx operator - (cmpx a,cmpx b){return cmpx(a.x-b.x,a.y-b.y);}
        inline friend cmpx operator * (cmpx a,cmpx b){return cmpx(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
    }A[MAXN],B[MAXN];

    int r[MAXN],limit=1,l;

    inline void FFT(cmpx *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){
            cmpx Wn=(cmpx){cos(Pi/len),type*sin(Pi/len)};
            for(int j=0;j<limit;j+=(len<<1)){
                cmpx w=(cmpx){1,0};
                for(int k=0;k<len;k++,w=w*Wn){
                    cmpx x=A[j+k],y=w*A[j+len+k];
                    A[j+k]=x+y;
                    A[j+len+k]=x-y;
                }
            }
        }
    }

}using namespace F_F_T;

LL n,m,a1,a2,b1,b2,ans=INF;

int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++){
        A[i+n].x=A[i].x=read();
        a1+=A[i].x;
        a2+=A[i].x*A[i].x;
    }
    for(int i=n;i>=1;i--){
        B[i].x=read();
        b1+=B[i].x;
        b2+=B[i].x*B[i].x;
    }
    while(limit<=n*3) limit<<=1,l++;
    for(int i=0;i<limit;i++)
        r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    FFT(A,1);FFT(B,1);
    for(int i=0;i<limit;i++) A[i]=A[i]*B[i];
    FFT(A,-1);
    for(int i=0;i<limit;i++)
        A[i].x=(LL)(A[i].x/limit+0.5);
    for(int i=n+1;i<=(n<<1);i++)
        for(int j=-m;j<=m;j++)
            ans=min(ans,a2+b2+n*j*j+2ll*j*(a1-b1)-2ll*(LL)A[i].x);
    printf("%lld\n",ans);
}

猜你喜欢

转载自www.cnblogs.com/lizehon/p/10499420.html