bzoj4827 [Hnoi2017]礼物 FFT

Description


给定长度为n的两个在环上的序列x[]和y[],求一种配对方式使得 i = 1 n ( x i + C + y i ) 2 最小,输出最小值
1≤n≤50000, 1≤m≤100, 1≤ai≤m

Solution


循环的问题复制一份就好了,差一下柿子可以发现其实就是 ( x i + C ) 2 + y i 2 2 x i y i 2 C y i
其中两个平方项可以预处理,中间那一坨可以把y倒过来FFT,带C的柿子可以枚举C求最小值
就酱

Code


#include <stdio.h>
#include <string.h>
#include <complex>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef std:: complex <double> com;
const double pi=3.14159265358;
const int INF=0x3f3f3f3f;
const int N=320005;

com c[N],d[N];

int rev[N],a[N],b[N];

int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

void FFT(com *a,int len,int f) {
    for (int i=0;i<len;i++) if (i<rev[i]) std:: swap(a[i],a[rev[i]]);
    for (int i=1;i<len;i*=2) {
        com wn(cos(pi/i),sin(pi/i)*f);
        for (int j=0;j<len;j+=i*2) {
            com w(1,0);
            for (int k=0;k<i;k++) {
                com u=a[j+k],v=a[j+k+i]*w;
                a[j+k]=u+v; a[j+k+i]=u-v;
                w*=wn;
            }
        }
    }
    if (f==-1) for (int i=0;i<len;i++) a[i]/=len;
}

int main(void) {
    int n=read(),m=read(),sumb=0;
    for (int i=1;i<=n;i++) a[i+n]=a[i]=read();
    for (int i=1;i<=n;i++) sumb+=(b[n-i+1]=read());
    int len,lg; for (len=1,lg=0;len<=n*2;len*=2,lg++);
    for (int i=0;i<len;i++) rev[i]=(rev[i/2]/2)|((i&1)<<(lg-1));
    for (int i=0;i<len;i++) c[i]=a[i+1],d[i]=b[i+1];
    FFT(c,len,1); FFT(d,len,1);
    for (int i=0;i<len;i++) c[i]*=d[i];
    FFT(c,len,-1);
    int ans=INF;
    for (int C=-m,sum=0;C<=m;C++,sum=0) {
        for (int i=1;i<=n;i++) sum+=(a[i]+C)*(a[i]+C)+b[i]*b[i];
        for (int i=n;i<2*n;i++) {
            ans=std:: min(ans,sum-2*(int)(c[i].real()+0.1)-2*C*sumb);
        }
    }
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/80055333
今日推荐