[AH2017/HNOI2017]礼物

嘟嘟嘟


感觉fft的题重点在于推式子……


因为\(n \leqslant 5e4, m \leqslant 100\),所以可以枚举旋转的位置和增加的亮度,然后想办法在\(O(1)\)时间内得到答案。
令枚举到第\(i\)个位置时\(A, B\)两个手环的序列为\(A_i, B_i\),此时\(B_i\)整体怎加了\(x\)亮度,则:

\[ans = \sum_{i = 0} ^ {n - 1} (a_i - b_i - x) ^ 2 \]

然后拆开\((a_i - b_i - x) ^ 2\)
\({a_i} ^ 2 + {b_i} ^ 2 + x ^ 2 - 2a_i b_i - 2a_i x + 2b_i x\).
于是

\[ans = \sum_{i = 0} ^ {n - 1} {a_i} ^ 2 + \sum_{i = 0} ^ {n - 1} {b_i} ^ 2 + nx ^ 2 - 2x(\sum_{i = 0} ^ {n - 1} b_i - \sum_{i = 0} ^ {n - 1} a_i) - 2 \sum_{i = 0} ^ {n - 1} a_i b_i \]

然后就发现只有最后一项是不知道的,而且只要把\(A\)翻转一下,就可以卷积了!
但是如果在每一次枚举都求一遍卷积,\(O(mn ^ 2 \log{n})\)时间肯定撑不住,而且求的时候也有很多重复的部分。
因此,我们可以断环为链,得到一个翻转后的长度为\(2n\)\(A\)的序列。然后和\(B\)卷积。
枚举到第\(i\)个位置的时候,根据卷积下标相加,\(c(n + i)\)就是答案。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define rg register
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 8e6 + 5;
const db PI = acos(-1);
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last = ' ';
  while(!isdigit(ch)) last = ch, ch = getchar();
  while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
  if(last == '-') ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar('-');
  if(x >= 10) write(x / 10);
  putchar(x % 10 + '0');
}

int n, m, len = 1;
ll a2 = 0, b2 = 0, suma = 0, sumb = 0;
ll Max = 0, ans = INF;

struct Comp
{
  db x, y;
  inline Comp operator + (const Comp& oth)const
  {
    return (Comp){x + oth.x, y + oth.y};
  }
  inline Comp operator - (const Comp& oth)const
  {
    return (Comp){x - oth.x, y - oth.y};
  }
  inline Comp operator * (const Comp& oth)const
  {
    return (Comp){x * oth.x - y * oth.y, x * oth.y + oth.x * y};
  }
  inline friend void swap(Comp& a, Comp& b)
  {
    swap(a.x, b.x); swap(a.y, b.y);
  }
}a[maxn], b[maxn], omg[maxn], inv[maxn];
void init()
{
  omg[0] = inv[0] = (Comp){1, 0};
  omg[1] = inv[len - 1] = (Comp){cos(2 * PI / len), sin(2 * PI / len)};
  for(int i = 2; i < len; ++i) omg[i] = inv[len - i] = omg[i - 1] * omg[1];
}
void fft(Comp* a, Comp* omg)
{
  int lim = 0;
  while((1 << lim) < len) lim++;
  for(int i = 0; i < len; ++i)
    {
      int t = 0;
      for(int j = 0; j < lim; ++j) if((i >> j) & 1) t |= (1 << (lim - j - 1));
      if(i < t) swap(a[i], a[t]);
    }
  for(int l = 2; l <= len; l <<= 1)
    {
      int q = l >> 1;
      for(Comp* p = a; p != a + len; p += l)
	for(int i = 0; i < q; ++i)
	  {
	    Comp t = omg[len / l * i] * p[i + q];
	    p[i + q] = p[i] - t, p[i] = p[i] + t;
	  }
    }
}

int main()
{
  n = read(); m = read();
  for(int i = 0; i < n; ++i)
    {
      db x = read();
      a[i] = a[n + i] = (Comp){x, 0};
      a2 += x * x; suma += x;
    }
  for(int i = 0; i < n; ++i)
    {
      db x = read();
      b[n - i - 1] = (Comp){x, 0};
      b2 += x * x; sumb += x;
    }
  while(len < (n << 1) + n) len <<= 1;
  init();
  fft(a, omg); fft(b, omg);
  for(int i = 0; i < len; ++i) a[i] = a[i] * b[i];
  fft(a, inv);
  for(int i = 0; i < len; ++i) a[i].x = (ll)(a[i].x / len + 0.5);
  for(int i = 0; i < n; ++i)
    for(int j = -m; j <= m; ++j)
      ans = min(ans, a2 + b2 + (ll)n * j * j - (((sumb - suma) * j) << 1) - ((ll)a[i + n].x << 1));
  write(ans), enter;
  return 0;
}
posted @ 2018-12-10 14:10  mrclr  阅读(87)  评论(0编辑  收藏  举报