首页 > 技术文章 > [2016北京集训试题15]项链-[FFT]

coco-night 2018-09-27 20:13 原文

Description

Solution

设y[i+k]=y[i]+n。

由于我们要最优解,则假如将x[i]和y[σ[i]]连线的话,线是一定不会交叉的。

所以,$ans=\sum (x_{i}-y_{i+s}+c)^{2}$

拆开得$ans=\sum (x_{i}^{2}+y_{i+s}^{2}+c^{2}-2x_{i}y_{i+s}+2x_{i}c-2y_{i+s}c)$

其中,$x_{i}y_{i+s}$是卷积形式。

我们把经过处理的y数组reverse一下,和x数组进行卷积(这里用ntt似乎会爆常数,fft大法好)。然后针对不同的s,得到以c为未知数的所有常数或系数,ans就是一个二次函数了。c用公式解就可以。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const double pi=acos(-1);
struct C
{
    double r,i;
    friend C operator+(C a,C b){return C{a.r+b.r,a.i+b.i};}
    friend C operator*(C a,C b){return C{a.r*b.r-a.i*b.i,a.i*b.r+a.r*b.i};}
    friend C operator-(C a,C b){return C{a.r-b.r,a.i-b.i};}
}fx[100010],fy[100010];
int rev[100010];
void fft(C *num,int n,int dft)
{
    for (int i=1;i<n;i++)
        if (i<rev[i]) swap(num[i],num[rev[i]]);
    for (int step=1;step<n;step<<=1)
    {
        C wn{cos(pi/step),sin(pi/step)*dft};
        for (int j=0;j<n;j+=step*2)//从0开始! 
        {
            C w{1,0};
            for (int k=0;k<step;k++,w=w*wn)
            {
                C x=num[j+k],y=w*num[j+k+step];
                num[j+k]=x+y;
                num[j+k+step]=x-y;
            }
        }
    }
    if (dft==-1) for (int i=0;i<n;i++) num[i].r/=n;
}
int T,n,k,len,L;
int x[4010],y[8010];
ll sumx[8010],sumy[8010],sumx2[8010],sumy2[8010];
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&k);
        memset(sumx,0,sizeof(sumx));
        memset(sumy,0,sizeof(sumy));
        memset(sumx2,0,sizeof(sumx2));
        memset(sumy2,0,sizeof(sumy2));
        memset(fx,0,sizeof(fx));memset(fy,0,sizeof(fy));        
        for (int i=1;i<=k;i++) 
        {
            scanf("%d",&x[i]);
            sumx[i]=sumx[i-1]+x[i],sumx2[i]=sumx2[i-1]+1ll*x[i]*x[i];   
        }
        for (int i=1;i<=k;i++) 
        {scanf("%d",&y[i]);y[k+i]=y[i]+n;}
        for (int i=1;i<=2*k;i++)
        sumy[i]=sumy[i-1]+y[i],sumy2[i]=sumy2[i-1]+1ll*y[i]*y[i];
         
        for (int i=1;i<=k;i++) fx[i-1].r=x[i];
        for (int i=0,j=2*k;j;i++,j--) fy[i].r=y[j];
        len=1,L=0;
        for (;len<2*k;len<<=1,L++);
        L++;len<<=1;
        for (int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|(i&1)<<(L-1);
        fft(fx,len,1);fft(fy,len,1);
        for (int i=0;i<len;i++) fx[i]=fx[i]*fy[i];
        fft(fx,len,-1);
        ll re,c,b,ans=1e13;
        for (int i=2*k-1,j=0;i>=k;i--,j++)
        {
            re=fx[i].r+0.2;
            re=-2*re+sumx2[k]+sumy2[j+k]-sumy2[j];
            b=2*(sumx[k]-sumy[j+k]+sumy[j]);
            c=-b/(2*k);
            ans=min(ans,k*c*c+c*b+re);
            c++;
            ans=min(ans,k*c*c+c*b+re);
        }
        cout<<ans<<endl;
    }
}

 

推荐阅读