[bzoj4827][Hnoi2017]礼物
来自FallDream的博客,未经允许,请勿转载,谢谢。
我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手环,一个留给自己,一个送给她。每个手环上各有 n 个装饰物,并且每个装饰物都有一定的亮度。
但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有装饰物的亮度增加一个相同的自然数 c(即非负整数)。并且由于这个手环是一个圆,可以以任意的角度旋转它,但是由于上面装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。
在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号1,2,…,n,其中 n 为每个手环的装饰物个数, 第 1 个手环的 i 号位置装饰物亮度为 xi,第 2 个手环的 i 号位置装饰物亮度为 yi,两个手环之间的差异值为$\sum_{i=1}^{n}(xi-yi)^{2}$
麻烦你帮他计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小,这个最小值是多少呢?
n<=50000 m<=100,表示所有装饰物的初始亮度不超过m
假设加上的是k,那么答案是$$\sum_{i=1}^{n}(xi+k-yi)^2$$
把这个拆开,得到$$Ans=min(\sum_{i=1}^{n}(ai^{2}+bi^{2}+k^{2}+k(2a-2b)))$$
平方项不变,得到$$Ans=min(\sum{(ai^2+bi^2)}+k^2+k(2\sum{a}-2\sum{b})-\sum{2ab})$$
k显然可以通过求一元二次方程最小值得到,然后要求的就是在任意移动的时候$\sum{ab}$的最大值
发现它很像卷积,所以可以构造这两个数组
a1 a2 a3 .. an a1 a2 ... an(复制一遍)
bn bn-1 bn-2 .. b1 0 0 0 0
这样通过fft/ntt求一个卷积,得到的c数组就是所有移动情况下的和啦,只要从中求一个最大值即可,答案用前面的式子容易计算
然后注意k可以是负数,因为你可以让第二个加上那么多。没注意到这个wa了几次。
然后求稳的话可以加个偏移量,或者直接O(2m)暴力就行了
复杂度nlogn
#include<iostream> #include<cstdio> #define MN 131077 #define mod 998244353 using namespace std; inline int read() { int x = 0; char ch = getchar(); while(ch < '0' || ch > '9')ch = getchar(); while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x; } int a[MN+5],b[MN+5],c[MN+5],n,N,w[2][MN+5],ans=0,sum=0,inv; inline int pow(int x,int k) { int res=1; for(;k;k>>=1,x=1LL*x*x%mod) if(k&1)res=1LL*res*x%mod; return res; } void fft(int*x,int r) { for(int i=0,j=0;i<N;++i) { if(i>j) swap(x[i],x[j]); for(int k=N>>1;(j^=k)<k;k>>=1); } for(int i=2;i<=N;i<<=1)for(int j=0;j<N;j+=i)for(int k=0;k<i>>1;++k) { int t=1LL*x[j+k+(i>>1)]*w[r][N/i*k]%mod; x[j+k+(i>>1)]=(1LL*x[j+k]-t+mod)%mod; x[j+k]=(1LL*x[j+k]+t)%mod; } if(r) for(int i=0;i<N;++i) x[i]=(1LL*x[i]*inv)%mod; } int main() { n=read();int m=read(); for(int i=0;i<n;++i) a[i]=a[i+n]=read(),ans+=a[i]*a[i],sum+=a[i]; for(int i=n-1;~i;--i) b[i]=read(),ans+=b[i]*b[i],sum-=b[i]; for(N=1;N<=n<<1;N<<=1);inv=pow(N,mod-2); sum<<=1;int g=pow(3,(mod-1)/N); for(int i=0,j=1;i<=N;++i,j=1LL*j*g%mod) w[0][i]=w[1][N-i]=j; fft(a,0);fft(b,0); for(int i=0;i<N;++i) c[i]=1LL*a[i]*b[i]%mod; fft(c,1);int mx=0,k=-sum/2/n; for(int i=n-1;i<2*n;++i) mx=max(mx,c[i]); long long add=1LL*k*k*n+1LL*sum*k; add=min(add,1LL*(k-1)*(k-1)*n+1LL*sum*(k-1)); add=min(add,1LL*(k+1)*(k+1)*n+1LL*sum*(k+1)); printf("%lld\n",1LL*ans-2*mx+add); return 0; }