【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;
}