【LG3723】[AHOI2017/HNOI2017]礼物

【LG3723】[AHOI2017/HNOI2017]礼物

题面

洛谷

题解

首先我们将\(c\)看作一个可以为负的整数,那么我们就可以省去讨论在哪个手环加\(c\)的繁琐步骤了

设我们当前已经选好了手环的顺序

\[ Ans=\sum_{i=1}^n(x_i-y_i+c)^2\\ =\sum_{i=1}^nx_i^2+\sum_{i=1}^ny_i^2+n*c^2+2c\sum_{i=1}^n(x_i-y_i)-2\sum_{i=1}^nx_i*y_i \]

实际上,因为前面都是定值(\(C\)的取值可以枚举),所以要求

\[ \sum_{i=1}^nx_i*y_i \]

最大就行了。

我们将\(y_i\)反向,那么原式就是一个卷积。

这里有个很巧妙的\(trick\):将\(y\)再倍长一下,取\(x*y\)中第\(n+1\)\(2n\)项的最小值即可,这样

就很巧妙地模拟了翻转的过程

代码

#include <iostream> 
#include <cstdio> 
#include <cstdlib> 
#include <cstring> 
#include <cmath> 
#include <algorithm> 
using namespace std; 
inline int gi() {
    register int data = 0, w = 1;
    register char ch = 0;
    while (!isdigit(ch) && ch != '-') ch = getchar(); 
    if (ch == '-') w = -1, ch = getchar();
    while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
    return w * data; 
}
const double PI = acos(-1.0);
const int MAX_N = 2000005; 
struct Complex { double x, y; } a[MAX_N], b[MAX_N]; 
inline Complex operator + (const Complex &l, const Complex &r) { return (Complex){l.x + r.x, l.y + r.y}; }
inline Complex operator - (const Complex &l, const Complex &r) { return (Complex){l.x - r.x, l.y - r.y}; }
inline Complex operator * (const Complex &l, const Complex &r) { return (Complex){l.x * r.x - l.y * r.y, l.y * r.x + l.x * r.y}; } 
int n, m, N, M, s1[MAX_N], s2[MAX_N], r[MAX_N], res[MAX_N]; 
void FFT(Complex *p, int op) { 
    for (int i = 0; i < N; i++) if (i < r[i]) swap(p[i], p[r[i]]); 
    for (int i = 1; i < N; i <<= 1) {
        Complex rot = (Complex){cos(PI / i), op * sin(PI / i)};
        for (int tmp = i << 1, j = 0; j < N; j += tmp) { 
            Complex w = (Complex){1, 0}; 
            for (int k = 0; k < i; k++, w = w * rot) { 
                Complex x = p[j + k], y = w * p[i + j + k]; 
                p[j + k] = x + y, p[i + j + k] = x - y; 
            } 
        } 
    } 
} 
void Prepare () { 
    N = n - 1, M = n + n - 1; 
    for (int i = 0; i <= N; i++) a[i].x = s1[i + 1]; 
    for (int i = 0; i < n; i++) b[i].x = s2[n - i]; 
    for (int i = 0; i < n; i++) b[i + n] = b[i];
    int P = 0; 
    for (M += N, N = 1; N <= M; N <<= 1, ++P) ; 
    for (int i = 0; i < N; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (P - 1)); 
    FFT(a, 1), FFT(b, 1); 
    for (int i = 0; i < N; i++) a[i] = a[i] * b[i]; 
    FFT(a, -1); 
    for (int i = 0; i <= M; i++) res[i] = (int)(a[i].x / N + 0.5); 
} 
int main () {
    n = gi(), m = gi(); 
    for (int i = 1; i <= n; i++) s1[i] = gi(); 
    for (int i = 1; i <= n; i++) s2[i] = gi(); 
    Prepare(); 
    int p1 = 0, p2 = 0, ans = 1e9, tmp1 = 0, tmp2 = 0, tt = -1e9;
    for (int i = 1; i <= n; i++) p1 += s1[i] * s1[i], p2 += s2[i] * s2[i], tmp1 += s1[i], tmp2 += s2[i]; 
    for (int i = n - 1; i < n + n; i++) tt = max(tt, res[i]); 
    for (int C = -m; C <= m; C++) {
        int tot = p1 + p2 + n * C * C + 2 * C * (tmp1 - tmp2) - 2 * tt; 
        ans = min(tot, ans); 
    } 
    printf("%d\n", ans); 
    return 0; 
} 

转载于:https://www.cnblogs.com/heyujun/p/10334711.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值