BZOJ 4827 [Hnoi2017]礼物

FFT

把式子展开完发现c和顺序无关,可以直接算,最小化这个式子就是最小化一个乘积的东西,也就是一个裸的FFT……

涨姿势,C++有一个四舍五入的函数叫round()

#include<cmath>
#include<cstdio>
#include<algorithm>
#define N 400005
using namespace std;
namespace runzhe2000
{
    typedef long long ll;
    typedef double db;
    int n, m, len; ll c;
    db sum_a, sum_b;
    ll sum_a2, sum_b2;
    struct comp
    {
        db r, i; 
        comp operator + (const comp &that) const {return (comp){r+that.r, i+that.i};}
        comp operator - (const comp &that) const {return (comp){r-that.r, i-that.i};}
        comp operator * (const comp &that) const {return (comp){r*that.r-i*that.i, r*that.i+i*that.r};}
    }w[N], a[N], b[N];
    void init()
    {
        db pi = acos(-1.0);
        for(int i = 0; i < len; i++)
            w[i] = (comp){cos(2*pi*i/len), sin(2*pi*i/len)};
    }
    void FFT(comp *a, int n, comp *w)
    {
        for(int i = 0, j = 0; i < n; i++)
        {
            if(i<j) swap(a[i], a[j]);
            for(int l = n>>1; (j^=l)<l; l>>=1);
        }
        for(int i = 2; i <= n; i <<= 1)
        {
            int m = i>>1;
            for(int j = 0; j < n; j += i)
            {
                for(int k = 0; k < m; k++)
                {
                    comp tmp = w[n/i*k] * a[j+k+m];
                    a[j+k+m] = a[j+k] - tmp;
                    a[j+k] = a[j+k] + tmp;
                }
            }
        }
    }
    void main()
    {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n; i++)
        {
            scanf("%lf",&a[i+n-1].r);
            sum_a += a[i+n-1].r;
            sum_a2 += a[i+n-1].r*a[i+n-1].r;
        }
        for(int i = 1; i <= n; i++)
        {
            scanf("%lf",&b[n-i].r);
            sum_b += b[n-i].r; 
            b[n-i+n].r = b[n-i].r;
            sum_b2 += b[n-i].r*b[n-i].r;
        }
        c = (ll)round((db)(sum_b-sum_a)/n);
        for(len = 1; len <= (n<<2); len <<= 1);

        for(int i = 0; i < len; i++) a[i].i = b[i].i = 0;
        init(); FFT(a,len,w); FFT(b,len,w);
        for(int i = 0; i < len; i++) a[i] = a[i] * b[i];
        reverse(a+1, a+len); FFT(a, len,w);
        for(int i = 0; i < len; i++) a[i].r /= len;

        ll mx = 0;
        for(int i = 2*n-1, ii = 3*n-2; i <= ii; i++) mx = max(mx, (ll)(0.5 + a[i].r));
        printf("%lld\n",n*c*c+(ll)(sum_a-sum_b)*2*c+sum_a2+sum_b2-2*mx);
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值