cunzai_zsy0531

关注我

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;
}
posted @ 2022-05-11 18:32  cunzai_zsy0531  阅读(16)  评论(0编辑  收藏  举报