[AH2017/HNOI2017]礼物

礼物

题解

多简单的一道FFT板题呀!

我们发现,增加的亮度 c c c的值为正还是为负都是没有关系的,只是对不同手环操作的问题,所以下面不会考虑 c c c的正负。

∑ i = 1 n ( x i − y i + c ) 2 = ∑ i = 1 n ( x i 2 + y i 2 ) + 2 c ∑ i = 1 n ( x i − y i ) + c 2 n − 2 ∑ i = 1 n x i y i \sum_{i=1}^{n}(x_{i}-y_{i}+c)^2=\sum_{i=1}^{n}(x_{i}^2+y_{i}^2)+2c\sum_{i=1}^{n}(x_{i}-y_{i})+c^2n-2\sum_{i=1}^{n}x_{i}y_{i} i=1n(xiyi+c)2=i=1n(xi2+yi2)+2ci=1n(xiyi)+c2n2i=1nxiyi

很容易发现除了最后一个 − 2 ∑ i = 1 n x i y i -2\sum_{i=1}^{n}x_{i}y_{i} 2i=1nxiyi以外,其它的值都与 x x x y y y的顺序无关,可以直接求出来。
前面关于 c c c的式子也可以直接找对称轴求出,注意精度误差,可以枚举找到的对称轴前后两个点的值,取最小的。
可最后一个 ∑ i = 1 n x i y i \sum_{i=1}^{n}x_{i}y_{i} i=1nxiyi的最大值求着就有些麻烦了。
我们可以先将 x x x给倒过来,就成了 ∑ i = 1 n a n − i + 1 y i \sum_{i=1}^{n}a_{n-i+1}y_{i} i=1nani+1yi,此时两者的和是一定的,可以转化成多项式,将 y y y延长一倍,两个多项式相乘,第 n + 1 n+1 n+1 2 n 2n 2n项中系数最大的就是上式的最大值。
注意转化成多项式后应该是从 x 0 x^0 x0开始的,不是 x 1 x^1 x1

总时间复杂度 O ( n l o g   n ) O\left(nlog\, n\right) O(nlogn)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 300005
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x>0?x:-x;}
template<typename _T>
inline void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,m,rev[MAXN],lim;LL ans,suma,sumb;
struct cp{
	double x,y;cp(double X=0,double Y=0){x=X;y=Y;}
	inline friend cp operator + (const cp &a,const cp &b){return cp(a.x+b.x,a.y+b.y);}
	inline friend cp operator - (const cp &a,const cp &b){return cp(a.x-b.x,a.y-b.y);}
	inline friend cp operator * (const cp &a,const cp &b){return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
}a[MAXN],b[MAXN];
inline void fft(cp *A,int typ){
	for(reg int i=0;i<lim;++i)if(i<rev[i])swap(A[i],A[rev[i]]);
	for(reg int mid=1;mid<lim;mid<<=1){
		const cp Wn(cos(PI/mid),typ*sin(PI/mid));
		for(reg int R=mid<<1,j=0;j<lim;j+=R){
			cp w(1,0);
			for(int k=0;k<mid;++k,w=w*Wn){
				cp x=A[j+k],y=w*A[j+mid+k];
				A[j+k]=x+y;A[j+mid+k]=x-y;
			}
		}
	}
}
signed main(){
	read(n);read(m);
	for(reg int i=0,x;i<n;++i)read(x),a[i].x=x,suma+=x,ans+=1ll*x*x;
	for(reg int i=0,x;i<n;++i)read(x),b[i].x=x,sumb+=x,ans+=1ll*x*x;
	LL x=(LL)((1.0*sumb-1.0*suma)/(2.0*n)+0.5),maxx=0,tmp=1ll*x*x*n+2ll*x*(suma-sumb);
	tmp=min(1ll*(x-1LL)*(x-1LL)*n+2ll*(x-1LL)*(suma-sumb),tmp);
	tmp=min(1ll*(x+1LL)*(x+1LL)*n+2ll*(x+1LL)*(suma-sumb),tmp);
	reverse(a,a+n);for(reg int i=0;i<n;++i)b[i+n]=b[i];
	int l=0;lim=1;while(lim<3*n)lim<<=1,++l;
	for(reg int i=1;i<=lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<l-1);
	fft(a,1);fft(b,1);for(reg int i=0;i<=lim;++i)b[i]=a[i]*b[i];fft(b,-1);	
	for(reg int i=0;i<=lim;++i)maxx=max(maxx,(LL)(b[i].x/lim+0.5));
	printf("%lld\n",ans+tmp-2ll*maxx);
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值