1. 程式人生 > >bzoj4827:[Hnoi2017]禮物

bzoj4827:[Hnoi2017]禮物

space splay tdi pri str rev 下一個 include 十分

沒想到湖南省選也出板子題啊

先把題目要求的式子拆一下
\[ \sum_{i=1}^{n}(a_i+x-b_i)^2\=\sum_{i=1}^{n}(a_i^2+b_i^2+x^2+2a_ix-2b_ix-2a_ib_i)\=\sum_{i=1}^{n}(a_i^2+b_i^2)+nx^2+2x\sum_{i=1}^{n}(a_i-b_i)-2\sum_{i=1}^{n}a_ib_i \]

容易發現只有最後的\(2\sum_{i=1}^{n}a_ib_i\)不是定值,其余的全都可以預先處理出來

然後發現這個式子和卷積十分相像,考慮將\(a\)數組reverse一下

式子就成了這樣
\[ 2\sum_{i=1}^{n}a_{n-i+1}b_i \]


再倍長一下(應該不用解釋吧),然後就可以用fft求解了,最後就查詢\(n+1\)\(2n\)內的最大值就好了

講一下一個細節:對於\(nx^2+2x\sum_{i=1}^{n}(a_i-b_i)\)用二次函數求解時,註意當\(x\)取負數時,取整應該是減去\(0.5\)\(x\)取正是加上\(0.5\)(原因不用多說,四舍五入之後是最靠近真實值的)

代碼:

    #include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
void read(int &x) {
    char ch; bool ok;
    for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=4e5+10;const double pi=acos(-1);
struct complex{double x,y;}a[maxn*2],b[maxn];
int n,m,nn,ans,sum,w,len,r[maxn];double tot;
complex operator-(complex a,complex b){return (complex){a.x-b.x,a.y-b.y};}
complex operator+(complex a,complex b){return (complex){a.x+b.x,a.y+b.y};}
complex operator*(complex a,complex b){return (complex){a.x*b.x-a.y*b.y,a.y*b.x+b.y*a.x};}
void fft(complex *a,int f)
{
    for(rg int i=0;i<n;i++)if(r[i]>i)swap(a[r[i]],a[i]);
    for(rg int i=1;i<n;i<<=1)
    {
        complex wn=(complex){cos(pi/i),f*sin(pi/i)};
        for(rg int j=0;j<n;j+=(i<<1))
        {
            complex w=(complex){1,0};
            for(rg int k=0;k<i;k++)
            {
                complex x=a[j+k],y=w*a[j+k+i];
                a[j+k]=x+y,a[j+k+i]=x-y,w=w*wn;
            }
        }
    }
    if(f==-1)for(rg int i=0;i<=m;i++)a[i].x=a[i].x/n+0.5; 
}
int main()
{
    read(n),read(m),nn=n;
    for(rg int i=0,x;i<n;i++)read(x),a[i].x=x,sum+=x*x,tot+=x;
    for(rg int i=0,x;i<n;i++)read(x),b[i].x=x,sum+=x*x,tot-=x;
    for(rg int i=0;i<n>>1;i++)swap(a[i],a[n-i-1]);
    for(rg int i=0;i<n;i++)a[i+n]=a[i];
    w=(int)((-tot/n)<0?(-tot/n)-0.5:(-tot/n)+0.5);sum+=n*w*w+tot*2*w;
    n<<=1;m=n+nn;for(n=1;n<=m;n<<=1)len++;
    for(rg int i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
    fft(a,1),fft(b,1);
    for(rg int i=0;i<n;i++)a[i]=a[i]*b[i];
    fft(a,-1);ans=-1e9;
    for(rg int i=0;i<nn;i++)ans=max(ans,(int)(a[i+nn].x));
    printf("%d\n",sum-ans*2);
}

bzoj4827:[Hnoi2017]禮物