【BZOJ4827】 [Hnoi2017]礼物

BZOJ4827 [Hnoi2017]礼物


Solution

如果一串数的增加,不就等于另一串数减吗?

那么我们可以把答案写成另一个形式:

\(ans=\sum_{i=1}^n(x_i-y_i+C)^2\)

\(y\)可以是重新排列

那么疯狂拆一下式子,化简之后就是:

\(ans=\sum_{i=1}^nx_i^2+\sum_{i=1}^ny_i^2+\sum_{i=1}^nC^2+2*C*\sum_{i=1}^n(x_i-y_i)-2*\sum_{i=1}^nx_i*y_i​\)

如果我们枚举\(C\),那么现在的任务就是算出\(\sum_{i=1}^nx_i*y_i\)的最大值,这样才能让\(ans\)最小.

怎么做呢?

如果把\(y\)数列翻转一下,那么就是:
\(\sum_{i=1}^nx_i*y_i=\sum_{i=1}^nx_i*y_{n-i}\)

这个不就是卷积?

考虑可以逆时针旋转怎么做?断环成链就好了啊.

那么就是把\(y\)串翻转然后复制一遍,求一个卷积然后走人了.

是的结束了.

代码实现

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define ll long long
#define re register
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
inline int gi()
{
    int f=1,sum=0;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return f*sum;
}
const int N=1000010,Inf=1e9+10;
const double Pi=acos(-1.0);
int x[N],y[N],r[N],limit,n,m,Ans[N];
struct node
{
    double x,y;
    node operator+(const node b)const{return (node){x+b.x,y+b.y};}
    node operator-(const node b)const{return (node){x-b.x,y-b.y};}
    node operator*(const node b)const{return (node){x*b.x-y*b.y,x*b.y+y*b.x};}
}A[N],B[N];
void FFT(node *A,int type)
{
    for(int i=0;i<limit;i++)
        if(i<r[i])swap(A[i],A[r[i]]);
    for(int mid=1;mid<limit;mid<<=1)
    {
        node Root=(node){cos(Pi/mid),type*sin(Pi/mid)};
        for(int R=mid<<1,j=0;j<limit;j+=R)
        {
            node Mi=(node){1,0};
            for(int k=0;k<mid;k++,Mi=Mi*Root)
            {
                node X=A[j+k],Y=Mi*A[j+mid+k];
                A[j+k]=X+Y;
                A[j+mid+k]=X-Y;
            }
        }
    }
}
void init()
{
    limit=1;int l=0;
    for(int i=0;i<n;i++)A[i].x=x[i+1];
    for(int i=0;i<n;i++)B[i].x=y[n-i];
    for(int i=0;i<n;i++)B[i+n]=B[i];
    int M=n+n-1,N=n-1;
    while(limit<=(N+M))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++)
        Ans[i]=(int)(A[i].x/limit+0.5);
}
int main()
{
    n=gi();m=gi();
    for(int i=1;i<=n;i++)x[i]=gi();
    for(int i=1;i<=n;i++)y[i]=gi();
    int pf1=0,pf2=0,sum1=0,sum2=0,ans=Inf;
    for(int i=1;i<=n;i++)pf1=pf1+x[i]*x[i];
    for(int i=1;i<=n;i++)pf2=pf2+y[i]*y[i];
    for(int i=1;i<=n;i++)sum1+=x[i];
    for(int i=1;i<=n;i++)sum2+=y[i];
    init();int Max=-Inf;
    for(int i=n-1;i<n+n;i++)Max=max(Max,Ans[i]);
    for(int C=-m;C<=m;C++)
    {
        int tot=pf1+pf2+C*C*n-2*Max+2*(sum1-sum2)*C;
        ans=min(ans,tot);
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/MLEAutomaton/p/10333135.html
今日推荐