bzoj4827 [Hnoi2017]礼物(FFT)

题目链接

分析:
ni=1(xiyi)2 ∑ i = 1 n ( x i − y i ) 2

假设我们旋转了 j j 位,每一位上加c,则有式子

ni=1(xi+j+cyi)2 ∑ i = 1 n ( x i + j + c − y i ) 2

=ni=1x2i+j+y2i+c2+2xi+jc2yic2xi+jyi = ∑ i = 1 n x i + j 2 + y i 2 + c 2 + 2 x i + j c − 2 y i c − 2 x i + j y i

=x2i+y2i+nc2+2c(xiyi)2xi+jyi = ∑ x i 2 + ∑ y i 2 + n c 2 + 2 c ∑ ( x i − y i ) − 2 ∑ x i + j y i

上式中只有 xi+jyi x i + j y i 需要计算

这道题的启发
我们可以把 y y 串翻转,变成一个卷积的形式
又因为有一个循环的形式,我们把x串加倍即可

最后枚举c分别计算上式即可

Q.

为什么卷积可以完成这个任务呢

A.

我们看一个例子:
x:a,b,c
y:d,e,f y : d , e , f
如分析,首先我们把x加倍,变成环的形式: x:a,b,c,a,b,c x : a , b , c , a , b , c
把y翻转,方便卷积: y:f,e,d y : f , e , d

x:              a   b   c   a   b   c
y:                          f   e   d
//------------------------------------
                da  db  dc  da  db  dc
            ea  eb  ec  ea  eb  ec
        fa  fb  fc  fa  fb  fc
                 *   *   *   *

看一下标出来的几位,就是题目所求啦


GXZlegend大大表示, c c 不用枚举!
f=i=1n(xiyi)2
f=ni=1(xi+cyi)2 f ′ = ∑ i = 1 n ( x i + c − y i ) 2
ff=ni=1(2xic2yic+c2)=c2xi2yi+c f ′ − f = ∑ i = 1 n ( 2 x i c − 2 y i c + c 2 ) = c ∗ ∑ 2 x i − 2 y i + c

c=yixin c = ∑ y i − ∑ x i n

也就是说无论如何旋转,c的最优值总是固定的 yixin ∑ y i − ∑ x i n (四舍五入到整数)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#define ll long long

using namespace std;

const int N=200010;
const int INF=1e9;
const double Pi=acos(-1.0);
struct node{
    double x,y;
    node (double xx=0,double yy=0) {
        x=xx;y=yy;
    }
};
node a[N],b[N],o[N],_o[N];

node operator +(const node &a,const node &b) {return node(a.x+b.x,a.y+b.y);}
node operator -(const node &a,const node &b) {return node(a.x-b.x,a.y-b.y);}
node operator *(const node &a,const node &b) {return node(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}

int Sx2=0,Sy2=0,S=0,x[N],y[N],c,f[N];
int n,m,fn;

void init(int n) {
    for (int i=0;i<=n;i++) {
        o[i]=node(cos(2.0*Pi*i/n),sin(2.0*Pi*i/n));
        _o[i]=node(cos(2.0*Pi*i/n),-sin(2.0*Pi*i/n));
    }
}

void FFT(int n,node *a,node *w) {
    int i,j=0,k;
    for (i=0;i<n;i++) {
        if (i>j) swap(a[i],a[j]);
        for (int l=n>>1;(j^=l)<l;l>>=1);
    }
    for (i=2;i<=n;i<<=1) {
        int m=i>>1;
        for (j=0;j<n;j+=i)
            for (k=0;k<m;k++) {
                node z=a[j+m+k]*w[n/i*k];
                a[j+k+m]=a[j+k]-z;
                a[j+k]=a[j+k]+z;
            }
    }
}

int main()
{
    c=0;
    scanf("%d%d",&n,&m);
    for (int i=0;i<n;i++) {
        scanf("%d",&x[i]);
        Sx2+=x[i]*x[i];          //sum x^2
        S+=2*x[i];               //sum 2(x-y)
    }
    for (int i=0;i<n;i++) {
        scanf("%d",&y[i]);
        Sy2+=y[i]*y[i];          //sum y^2
        S-=2*y[i];
    }

    fn=1;
    while (fn<=n+n) fn<<=1;
    init(fn);
    for (int i=0;i<n*2;i++) a[i]=node(x[i%n],0);     //加倍环变序列 
    for (int i=0;i<n;i++) b[i]=node(y[n-i-1],0);     //翻转 
    FFT(fn,a,o); FFT(fn,b,o);
    for (int i=0;i<=fn;i++) a[i]=a[i]*b[i];
    FFT(fn,a,_o);
    for (int i=0;i<=fn;i++) f[i]=(int)(a[i].x/(double)fn+0.5);

    int ans=INF;
    for (c=-m;c<=m;c++)
        for (int i=n;i<2*n;i++) 
            ans=min(ans,Sx2+Sy2+c*S+n*c*c-2*f[i]);
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值