[AH2017/HNOI2017]礼物

题目

传送门

题解

我们称手链串为 \(a,b\) 两个.

假设最终我们对于其中某一个手镯增加 \(c(c\in R)\) 的光亮度,将后者旋转 \(k\) 位,首先,我们可以经典地破环为链,这里将 \(b\) 重复了一遍,那么最后的答案就是

\[Ans=\sum_{i=0}^{n-1}\left[a_i-(b_i+c)\right]^2 \]

我们可以将其打开,得到

\[\begin{aligned} Ans&=\sum_{i=0}^{n-1}\left[a_i-(b_{i+k}+c)\right]^2 \\ &=\sum_{i=0}^{n-1}a_i^2+\sum_{i=0}^{n-1}(b_i+c)^2-\sum_{i=0}^{n-1}2a_i^2(b_{i+k}+c) \\ &=\sum_{i=0}^{n-1}a_i^2+\sum_{i=0}^{n-1}b_i^2+nc^2+2c\sum_{i=0}^{n-1}(a_i-b_{i+k})-2\sum_{i=0}^{n-1}a_ib_{i+k} \end{aligned} \]

发现关于 \(c\) 的项都是定值,二函经典求最值,只有 \(-2\sum_{i=0}^{n-1}a_ib_{i+k}\) 这个东东是关于 \(k\) 的,但是这个东西难道不熟悉?我们将 \(b\) 反转,那么这个东西就变成 \(-2\sum_{i=0}^{n-1}a_ib_{2n-i-k}\),这不就是卷积吗?它最后被卷到 \(2n-k\) 这一位了,所以我们只需要将 \(b\) 反转,将 \(a,b\) 卷起来,然后枚举 \(k\)\((a*b_{\text{rev}})_{2n-k}\) 的最大值即可.

注意不要忘记了系数 \(2\).

代码

const int maxn=1e5;
const double Pi=acos(-1.0);

struct cplx{
    double x,y;
    inline cplx(const double X=0,const double Y=0):x(X),y(Y){}
    inline cplx operator +(const cplx rhs)const{return cplx(x+rhs.x,y+rhs.y);}
    inline cplx operator -(const cplx rhs)const{return cplx(x-rhs.x,y-rhs.y);}
    inline cplx operator *(const cplx rhs)const{return cplx(x*rhs.x-y*rhs.y,x*rhs.y+y*rhs.x);}
    inline cplx operator +=(const cplx rhs){return (*this)=(*this)+rhs;}
    inline cplx operator -=(const cplx rhs){return (*this)=(*this)-rhs;}
    inline cplx operator *=(const cplx rhs){return (*this)=(*this)*rhs;}
};

void fft(cplx* f,const int len,const short opt=1){
    if(!len)return;
    cplx f0[len+5],f1[len+5];
    for(int i=0;i<len;++i)
        f0[i]=f[i<<1],f1[i]=f[i<<1|1];
    fft(f0,len>>1,opt);fft(f1,len>>1,opt);
    cplx w=cplx(cos(Pi/len),opt*sin(Pi/len));
    cplx buf=cplx(1,0);
    for(int i=0;i<len;++i,buf*=w){
        f[i]=f0[i]+buf*f1[i];
        f[i+len]=f0[i]-buf*f1[i];
    }
}

int n,m;
int a[maxn*2+5],b[maxn*2+5];

int sum;

inline void input(){
    n=readin(1),m=readin(1);
    for(int i=0;i<n;++i)sum+=(a[i]=readin(1));
    for(int i=0;i<n;++i)sum-=(b[n-i-1]=readin(1));
}

cplx f[maxn*4+5],g[maxn*4+5];
int val[maxn*4+5];
inline void getval(){
    int N=n*3;
    for(int i=0;i<n;++i)
        f[i]=cplx(a[i],0),g[i]=cplx(b[i],0);
    for(int i=n;i<(n<<1);++i)g[i]=cplx(b[i-n],0);
    int sz;for(sz=1;sz<N;sz<<=1);
    fft(f,sz>>1);fft(g,sz>>1);
    for(int i=0;i<sz;++i)f[i]*=g[i];
    fft(f,sz>>1,-1);
    for(int i=0;i<sz;++i)val[i]=(int)(f[i].x/sz+0.5);
}

inline int calc(const int c){
    return n*c*c+2*sum*c;
}

inline void getans(){
    int x=-sum/n;
    // printf("x == %d\n",x);
    int ans=Min(Min(calc(x-1),calc(x)),calc(x+1));
    // printf("init :> ans == %d\n",ans);
    for(int i=0;i<n;++i)
        ans+=a[i]*a[i]+b[i]*b[i];
    // printf("after pow, ans == %d\n",ans);
    int del=0;
    // rep(i,0,(n<<1))printf("%d ",val[i]);Endl;
    // for(int i=n;i<(n<<1);++i)del=Max(del,val[i]);
    for(int k=1;k<=n;++k)del=Max(del,val[(n<<1)-k]);
    printf("%d\n",ans-2*del);
}

signed main(){
    input();
    getval();
    getans();
    return 0;
}

用到の小 \(\tt trick\)

第一,我们需要推柿子......

第二,识别出 \(-2\sum_{i=0}^{n-1}a_ib_{i+k}\) 其实是卷积的一种隐藏形式,将其中一个反转之后就是卷积的基本形式.

其实也可以说任何 \(\sum a_ib_i\) 其实都可以转换成卷积形式,只需要将其中一个数组反转即可.

posted @ 2021-02-02 14:59  Arextre  阅读(48)  评论(0编辑  收藏  举报