【HNOI 2017】礼物

Problem

Description

我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手环,一个留给自己,一个送给她。每个手环上各有 \(n\) 个装饰物,并且每个装饰物都有一定的亮度。

但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有装饰物的亮度增加一个相同的整数 \(c\)(可能是负数)。并且由于这个手环是一个圆,可以以任意的角度旋转它,但是由于上面装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。

在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号 \(1, 2,\cdots, n\),其中 \(n\) 为每个手环的装饰物个数,第一个手环的 \(i\) 号位置装饰物亮度为 \(x_i\),第二个手环的 \(i\) 号位置装饰物亮度为 \(y_i\),两个手环之间的差异值为(参见输入输出样例和样例解释):
\[ \sum_{i = 1}^n (x_i - y_i)^2 \]
麻烦你帮他计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小,这个最小值是多少呢?

Input Format

输入数据的第一行有两个数 \(n, m\),代表每条手环的装饰物的数量为 \(n\),每个装饰物的初始亮度小于等于 \(m\)

接下来两行,每行各有 \(n\) 个数,分别代表第一条手环和第二条手环上从某个位置开始逆时针方向上各装饰物的亮度。

Output Format

输出一个数,表示两个手环能产生的最小差异值。注意在将手环改造之后,装饰物的亮度可以大于 \(m\)

Sample

Input

5 6
1 2 3 4 5
6 3 3 4 5

Output

1

Explanation

Explanation for Sample

需要将第一个手环的亮度增加 \(1\),第一个手环的亮度变为:\(2, 3, 4, 5, 6\)。旋转一下第二个手环,对于该样例,是将第二个手环的亮度 \(6, 3, 3, 4, 5\) 向左循环移动一个位置,使得第二手环的最终的亮度为:\(3, 3, 4, 5, 6\)

此时两个手环的亮度差异值为 \(1\)

Range

对于 \(30\%\) 的数据,\(n\le 500, m\le 10\)

对于 \(70\%\) 的数据,\(n\le 5000\)

对于 \(100\%\) 的数据,\(1\le n\le 50000, 1\le m\le 100, 1\le a_i\le m\)

Algorithm

\(FFT\)

Mentality

由于加上的整数可以为负数,那么无论加在 \(x\) 还是 \(y\) 上没有本质差别。直接加在 \(x\) 上就好了。

那么对于式子:
\[ \sum (x_i+c-y_i)^2 \]
我们可以拆开平方:
\[ =\sum x_i^2+y_i^2+c^2+2x_ic-2y_ic-2x_iy_i\\ =\sum x_i^2+\sum y_i^2+nc^2+2(\sum x_i-\sum y_i)c-2\sum x_iy_i \]
好的,前面两项都是定值。对于带 \(c\) 的项,由于其他系数皆为定值,就是一个普通的二次函数,直接找顶点即可最小。

那么我们只需要令 \(\sum x_iy_i\) 最大即可。考虑将 \(x\) 数组的下标翻转,则得到 \(\sum x_{n-i+1}*y_i\)

那么将 \(x\) 数组复制加倍,直接 \(FFT\)\(x,y\) 乘起来,第 \(n+1\sim 2n\) 项就是 \(n\) 种旋转方法的结果,直接取 \(max\) 即可。

Code

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
long long read() {
  long long x = 0, w = 1;
  char ch = getchar();
  while (!isdigit(ch)) w = ch == '-' ? -1 : 1, ch = getchar();
  while (isdigit(ch)) {
    x = (x << 3) + (x << 1) + ch - '0';
    ch = getchar();
  }
  return x * w;
}
#define cp complex
const int Max_n = 5e5 + 5;
const double pi = acos(-1);
int n, m, ans;
int sx, sy, Max, mid, x[Max_n], y[Max_n];
int lim = 1, bit, rev[Max_n];
cp f[Max_n], g[Max_n];
void dft(cp *f, int t) {
  for (int i = 0; i < lim; i++)
    if (rev[i] > i) swap(f[i], f[rev[i]]);
  for (int len = 1; len < lim; len <<= 1) {
    cp Wn(cos(t * pi / len), sin(t * pi / len));
    for (int i = 0; i < lim; i += len << 1) {
      cp Wnk(1, 0);
      for (int k = i; k < i + len; k++) {
        cp a = f[k], b = Wnk * f[k + len];
        f[k] = a + b, f[k + len] = a - b;
        Wnk *= Wn;
      }
    }
  }
}
void FFT() {
  dft(f, 1), dft(g, 1);
  for (int i = 0; i < lim; i++) f[i] *= g[i];
  dft(f, -1);
  for (int i = 0; i < lim; i++) f[i] /= lim;
}
int main() {
#ifndef ONLINE_JUDGE
  freopen("3723.in", "r", stdin);
  freopen("3723.out", "w", stdout);
#endif
  n = read(), m = read();
  for (int i = 0; i < n; i++) {
    f[i] = x[i] = read();
    sx += x[i], ans += x[i] * x[i];
  }
  for (int i = 0; i < n; i++) {
    g[i] = y[i] = read();
    sy += y[i], ans += y[i] * y[i];
  }
  reverse(f, f + n);
  for (int i = 0; i < n; i++) f[i + n] = f[i];
  while (lim <= n * 3) bit++, lim <<= 1;
  for (int i = 0; i < lim; i++)
    rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
  FFT();
  for (int i = n; i < 2 * n; i++) Max = max(Max, (int)(f[i].real() + 0.5));
  double tp = double(sy - sx) / n;
  mid = (int)(tp += tp < 0 ? -0.5 : 0.5);
  cout << (ans += n * mid * mid + 2 * (sx - sy) * mid - 2 * Max);
}

你可能感兴趣的:(【HNOI 2017】礼物)