BZOJ 4827: [Hnoi2017]礼物(FFT)

来源:互联网 发布:linux 内核 usleep 编辑:程序博客网 时间:2024/05/29 06:45

题目传送门

轻轻戳我


Solution

题目欲求ni=1(xi+cyi)2的最小值。

先处理+c的问题,将式子展开

i=1n(xi+cyi)2=i=1n(xiyi)2+2i=1n(xiyi)c+nc2

由于那些x,y什么的都是常量,于是我们得到了一个关于c的开口向上的二次函数,现在要求其极小值,学过初三数学的我直接O(1)算对称轴求出c

然后+c的限制被去掉了。
原问题理所当然地变成了求ni=1(xiyi)2的值。

i=1n(xiyi)2=i=1nx2i+i=1ny2i2i=1nxiyi

前面一堆常量,预处理搞出。最后那项把y数组翻转过来后,就是个裸的卷积,直接上FFT

但由于可以旋转,所以其实要求的是循环卷积

有一个普通的类似破环为链的作法:我们发现如果将x旋转,相当于将x倍长,xi变成xi+j

于是:

i=1n(xi+jyi)2=i=1nx2i+j+i=1ny2i2i=1nxi+jyi

只用求最后那项,直接将y反过来,用FFT求卷积即可。

由于m挺小,其实也可以先FFT,再直接枚举c,计算出答案。

对于旋转的处理,还有一种神奇的方法:

把两个序列画出来,发现如果旋转一定位置的话,那么答案就是两个序列前i部分卷起来加上把剩下部分卷起来的和。

那么只要正着反着分别做一次FFT即可。(还是自己画个图容易懂一点。。)


Code

#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <iostream>#include <cmath>#define MAXN 100010#define oo 0x7fffffffusing namespace std;const double PI = acos(-1.0);int n, m, nn, X[MAXN], Y[MAXN], P[MAXN], sumX, sumY, sum, C, ans;struct Complex{    double real, image;    Complex() {}    Complex(double _real, double _image){real = _real, image = _image;}    friend Complex operator + (Complex A, Complex B){return Complex(A.real + B.real, A.image + B.image);}    friend Complex operator - (Complex A, Complex B){return Complex(A.real - B.real, A.image - B.image);}    friend Complex operator * (Complex A, Complex B){return Complex(A.real * B.real - A.image * B.image, A.real * B.image + A.image * B.real);}}a[MAXN<<2], b[MAXN<<2], c[MAXN<<2];void Get_P(){    for(int i = 1, t = 1; i < MAXN; i++)        P[i] = (t < i) ? (t <<= 1) : t;}void Reverse(Complex *A){    for(int i = 0; i < nn-1; i++){      int j = 0;      for(int k = 1, tmp = i; k < nn; k <<= 1, tmp >>= 1)        j = ((j << 1) | (tmp & 1));      if(i < j)  swap(A[i], A[j]);    }}void FFT(Complex *A, int DFT){    Reverse(A);    for(int s = 1; (1<<s) <= nn; s++){      int m = 1 << s;      Complex wm = Complex(cos(DFT*2*PI/m), sin(DFT*2*PI/m));      for(int k = 0; k < nn; k += m){        Complex w = Complex(1, 0);        for(int j = 0; j < (m>>1); j++){          Complex u = A[k+j], t = w * A[k+j+(m>>1)];          A[k+j] = u + t;          A[k+j+(m>>1)] = u - t;          w = w * wm;        }      }    }    if(!(~DFT))  for(int i = 0; i < nn; i++)  A[i].real /= nn;}int main(){    freopen("bzoj4827.in", "r", stdin);    freopen("bzoj4827.out", "w", stdout);    Get_P();    scanf("%d%d", &n, &m);    nn = P[n<<1] << 1;    for(int i = 0; i < n; i++)  scanf("%d", &X[i]), sumX += X[i];    for(int i = 0; i < n; i++)  scanf("%d", &Y[i]), sumY += Y[i];    C = (sumY - sumX) / n;    int Val1 = n * C * C + 2 * (sumX - sumY) * C;    int Val2 = n * (C+1) * (C+1) + 2 * (sumX - sumY) * (C+1);    if(Val1 > Val2)  C ++;    if(C > 0)  for(int i = 0; i < n; i++)  X[i] += C;    else  for(int i = 0; i < n; i++)  Y[i] += C;    for(int i = 0; i < n; i++)  sum += X[i] * X[i], a[i] = a[i+n] = Complex(X[i], 0);    for(int i = 0; i < n; i++)  sum += Y[i] * Y[i], b[i] = Complex(Y[n-i-1], 0);    for(int i = n<<1; i < nn; i++)  a[i] = Complex(0, 0);    for(int i = n; i < nn; i++)  b[i] = Complex(0, 0);    FFT(a, 1);    FFT(b, 1);    for(int i = 0; i < nn; i++)  c[i] = a[i] * b[i];    FFT(c, -1);    ans = oo;    for(int i = n-1; i < (n<<1); i++)  ans = min(ans, sum - 2 * (int)(c[i].real+.5));    printf("%d\n", ans);    return 0;}

这里写图片描述

原创粉丝点击