P3723 [AHOI2017/HNOI2017]礼物 题解
又是一道推式子+FFT。看到有些式子看起来很卷,不要犹豫,自信一点,说不定就推出来了呢。
设加的数为 \(x\),转完之后两个数组每一位对应为 \(a[1...n]\) 和 \(b[1...n]\),可得:
要求 \(\min\{\sum_{i=1}^{n}(a_i+x-b_i)^2\}\):
\[\begin{aligned}
\sum_{i=1}^{n}(a_i+x-b_i)^2 &= \sum_{i=1}^{n}(a_i+x)^2+b_i^2-2b_i(a_i+x)\\
&=\sum_{i=1}^{n}a_i^2+\sum_{i=1}^{n}b_i^2+nx^2-2x(\sum_{i=1}^{n}a_i-\sum_{i=1}^{n}b_i)-2\sum_{i=1}^{n}a_ib_i
\end{aligned}
\]
惊奇的发现,除了最后一项 \(\sum_{i=1}^{n}a_ib_i\) 意外,其他所有项都是确定的!只需要让这一项最大即可得到整个式子最小。如何让这一项最小呢?
设 \(f_i\) 表示上面那个数组以第 \(i\) 个数为开始的时候,得到的答案,然后把 \(b\) 翻转一下(老套路),\(a\) 环变链,直接跑 \(NTT\) 即可。考虑到值域非常小,最后只需要枚举 \(x\) 即可得到答案。
点击查看代码
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=3e5+13,P=998244353,G=3,Gi=332748118;
const ll LLINF=0x3f3f3f3f3f3f3f3fll;
int n,m,A[N],B[N],a[N],b[N],r[N];
inline int qpow(int a,int k){int s=1;for(;k;k>>=1,a=1ll*a*a%P)if(k&1)s=1ll*a*s%P;return s;}
inline void fft(int *f,int limit,int type){
for(int i=0;i<limit;++i)
if(i<r[i]) swap(f[i],f[r[i]]);
for(int mid=1;mid<limit;mid<<=1){
int Wn=qpow(type==1?G:Gi,(P-1)/(mid<<1));
for(int j=0;j<limit;j+=(mid<<1)){
int w=1;
for(int k=0;k<mid;++k,w=1ll*w*Wn%P){
int x=f[j+k],y=1ll*w*f[j+k+mid]%P;
f[j+k]=(x+y)%P;f[j+k+mid]=(x-y+P)%P;
}
}
}
}
int main(){
scanf("%d%d",&n,&m);int suma=0,sumb=0,sum2a=0,sum2b=0;
for(int i=1;i<=n;++i){
scanf("%d",&A[i]);A[i+n]=A[i];
suma+=A[i],sum2a+=A[i]*A[i];
}
for(int i=1;i<=n;++i){
scanf("%d",&B[i]);B[i+n]=B[i];
sumb+=B[i],sum2b+=B[i]*B[i];
}
for(int i=1;i<=(n<<1);++i){
a[i]=A[i],a[i+n]=A[i];
b[i]=B[n-i+1];
}
int limit=1,l=0;
while(limit<=(n*3)) limit<<=1,++l;
for(int i=0;i<limit;++i) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
fft(a,limit,1),fft(b,limit,1);
for(int i=0;i<limit;++i) a[i]=1ll*a[i]*b[i]%P;
fft(a,limit,-1);
int inv=qpow(limit,P-2);
for(int i=1;i<=(n<<1);++i) a[i]=1ll*a[i]*inv%P;
int res=0;
for(int i=1;i<=n;++i) res=max(res,a[i+n]);
ll ans=LLINF;
for(int c=-m;c<=m;++c){
ll ret=sum2a+sum2b+1ll*n*c*c-2ll*c*(suma-sumb)-2ll*res;
ans=min(ans,ret);
}
printf("%lld\n",ans);
return 0;
}