BZOJ4827: [Hnoi2017]礼物
【传送门:BZOJ4827】
简要题意:
给出两个环,逆时针从1到n输入一开始每个环相应位置的值,两个环的差异值为$\sum_{i=1}^{n}(x[i]-y[i])^2$
可以给其中一个环的所有值都加上一个正整数,或者逆时针旋转其中一个环
求出能得到的最小差异值
题解:
FFT
我们其实可以发现把一个环的所有值都加上一个正整数,其实相当于另一个环的所有值都减去一个正整数
那么我们其实可以直接求出这个数
设$f(i)=\sum_{i=1}^{n}(x[i]-y[i])^2$,$f'(i)=\sum_{i=1}^{n}(x[i]+c-y[i])^2$
将二式相减得到$f'(i)-f(i)=c*\sum_{i=1}^{n}(c+2*x[i]-2*y[i])$
设$sx=sum_{i=1}^{n}x[i]$,$sy=\sum_{i=1}^{n}y[i]$
得到$f'(i)-f(i)=c*(n*c+2*sx-2*sy)=nc^2+2c(sx-sy)$
因为我们要使得这个式子尽量小,所以要$nc^2+2c(sx-sy)$尽量小,不就是二次函数求对称轴吗$(-\frac{b}{2a})$
得出来$c=\frac{sy-sx}{n}$
直接把c求出来,然后加进x[i]里面就好了
首先对于当前$\sum_{i=1}^{n}(x[i]-y[i])^2$(这个时候的x[i]已经加上c了)
我们化成$\sum_{i=1}^{n}x[i]^2-2x[i]y[i]+y[i]^2$
显然$x[i]^2$和$y[i]^2$可以前缀和求出来,设为sum
对于$\sum_{i=1}^{n}2x[i]y[i]$,可以得到$2*\sum_{i=1}^{n}x[i]y[i]$,还不能卷积,例题的方法是将y倒过来
我的FFT习惯是把n-1,然后i从0开始,就得到$2*\sum_{i=0}^{n}x[i]y[n-i]$,卷积形式√
但是我们还有旋转操作,而实际上对于两个串,只需要对一个串进行旋转就可以了
那么就只要在任意一个串中往后延伸,直到构成了两个相等的串
然后再做FFT,之后枚举断点,求出以哪个位置为结尾,求出最大值ans
最终答案为sum-2*ans
注意加long long,如果全程有double就不用加long long
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; const double PI=acos(-1.0); struct Complex { double r,i; Complex(){} Complex(double _r,double _i){r=_r;i=_i;} friend Complex operator + (const Complex &x,const Complex &y){return Complex(x.r+y.r,x.i+y.i);} friend Complex operator - (const Complex &x,const Complex &y){return Complex(x.r-y.r,x.i-y.i);} friend Complex operator * (const Complex &x,const Complex &y){return Complex(x.r*y.r-x.i*y.i,x.r*y.i+x.i*y.r);} }a[210000],b[210000]; int R[210000]; void fft(Complex *y,int len,int on) { for(int i=0;i<len;i++) if(i<R[i]) swap(y[i],y[R[i]]); for(int i=1;i<len;i<<=1) { Complex wn(cos(PI/i),sin(on*PI/i)); for(int j=0;j<len;j+=(i<<1)) { Complex w(1,0); for(int k=0;k<i;k++,w=w*wn) { Complex u=y[j+k]; Complex v=w*y[j+k+i]; y[j+k]=u+v; y[j+k+i]=u-v; } } } if(on==-1) for(int i=0;i<=len;i++) y[i].r/=len; } void calc(int n) { int L=0,m=2*n; for(n=1;n<=m;n<<=1) L++; memset(R,0,sizeof(R)); for(int i=0;i<n;i++) R[i]=(R[i>>1]>>1)|(i&1)<<(L-1); fft(a,n,1);fft(b,n,1); for(int i=0;i<=n;i++) a[i]=a[i]*b[i]; fft(a,n,-1); } int r1[51000],r2[51000]; int main() { int n,m; scanf("%d%d",&n,&m);n--; int c=0; for(int i=0;i<=n;i++) { scanf("%d",&r1[i]); c-=r1[i]; a[i].r=r1[i]; } for(int i=n;i>=0;i--) { scanf("%d",&r2[i]); c+=r2[i]; b[i].r=r2[i]; } c=round(double(c)/double(n+1)); for(int i=0;i<=n;i++) a[i].r+=c; for(int i=n+1;i<2*n+2;i++) a[i].r=a[i-n-1].r; LL sum=0; for(int i=0;i<=n;i++) sum+=(r1[i]+c)*(r1[i]+c)+r2[i]*r2[i]; calc(n+1); LL ans=0; for(int i=n;i<2*n+1;i++) ans=max(ans,LL(a[i].r+0.5)); printf("%lld\n",sum-2LL*ans); return 0; }