[BZOJ4827][HNOI2017]礼物(FFT)

来源:互联网 发布:淘宝下载手机版 编辑:程序博客网 时间:2024/06/05 14:31

可以想到,先旋转到最优方案,再枚举亮度。
而关键在于怎样求旋转到的最优方案。
把差异值的式子展开得到:
ni=1(xiyi)2=ni=1(x2i+y2i)2ni=1xiyi
所以目标就是让ni=1xiyi最大。
此时可以发现,如果旋转第一个环之后第一个环的第1个是原来第一个环的第k个,那么容易得出此时的差异值为:
nk+1i=1xk+i1yi+k1i=1xiynk+i+1
如果分别把第一个环和第二个环翻转,并把环看作多项式,那么可以发现nk+1i=1xk+i1yik1i=1xiynk+i+1都是两个多项式的乘积中一个系数的值,这个可以做两遍FFT求出。
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <bits/stdc++.h> using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}typedef complex<double> cyx;const int N = 2e5 + 5, INF = 0x3f3f3f3f; const double pi = acos(-1.0);int n, m, a[N], b[N], rev[N], lef[N], rig[N], ff, tmp[N];cyx A[N], B[N];void FFT(int n, cyx *y, int op) {    int i, j, k;    for (i = 0; i < n; i++) if (i < rev[i]) swap(y[i], y[rev[i]]);    for (k = 1; k < n; k <<= 1) {        cyx x(cos(pi / k), op * sin(pi / k));        for (i = 0; i < n; i += (k << 1)) {            cyx w(1, 0);            for (j = 0; j < k; j++) {                cyx u = y[i + j], v = w * y[i + j + k];                y[i + j] = u + v; y[i + j + k] = u - v;                w = w * x;            }        }    }}void solve() {    int i, tot = 0; ff = 1;    while (ff <= (n << 1) - 2) ff <<= 1, tot++;    memset(rev, 0, sizeof(rev));    for (i = 0; i <= ff; i++)        rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << tot - 1);    FFT(ff, A, 1); FFT(ff, B, 1); for (i = 0; i <= ff; i++)        A[i] = A[i] * B[i]; FFT(ff, A, -1);}int main() {    int i, sum = 0, pos = 0, now = INF, ans = INF, sm = 0;    n = read(); m = read(); for (i = 1; i <= n; i++) a[i] = read(),    sum += a[i] * a[i]; for (i = 1; i <= n; i++) b[i] = read(),    sum += b[i] * b[i]; for (i = 0; i < n; i++) A[i] = a[n - i],    B[i] = b[i + 1]; solve(); for (i = 1; i <= n; i++)        rig[i] = ((int) (A[n - i].real() / ff + 0.5));    for (i = 0; i <= ff; i++) A[i] = B[i] = 0;    for (i = 0; i < n; i++) A[i] = a[i + 1], B[i] = b[n - i];    solve(); for (i = 1; i <= n; i++)        lef[i] = ((int) (A[i - 1].real() / ff + 0.5));    for (i = 1; i <= n; i++) {        int x = sum - (lef[i - 1] + rig[i] << 1);        if (x < now) now = x, pos = i;    }    if (pos > 1) {        for (i = pos; i <= n; i++) tmp[i] = a[i];        for (i = n; i >= n - pos + 2; i--) a[i] = a[i - n + pos - 1];        for (i = pos; i <= n; i++) a[i - pos + 1] = tmp[i];    }    for (i = 1; i <= n; i++) sm += a[i] - b[i];    for (i = -100; i <= 100; i++)        ans = min(ans, now + n * i * i + 2 * sm * i);    printf("%d\n", ans);    return 0;}
原创粉丝点击