【[AH2017/HNOI2017]礼物】一道FFT题

最近学了FFT,真是恶心 开心极了

洛谷传送门

我们假设增加的亮度为r,那么

ANS == segma ( xi - yi + r )^2 == segma( xi ^2 + yi^2 + r^2 ) + 2 * r * segma( xi - yi ) - 2 * segma( xi*yi )

我们可以发现xi*yi可以用FFT来处理,而M的范围很小可以直接枚举r来搞定其他,r可以用二次函数最值处理(比较懒,直接暴力枚举)

发现除了xiyi并不会因为旋转而影响,用fft搞xiyi。xiyi处理方法---》将b翻转,之后每个匹配位置的和,一一匹配xiyi就为a,b相乘之后所得的c,c的c[i]+c[i+n],可以在纸上稍推一下就可以发现这是系数的匹配。

于是就这么搞出来啦!

code:

/*
ans=segma( xi - yi + r )^2
==segma(x^2+y^2+r^2) + 2*r*segma(xi-yi) - 2*segma(xi*yi)
*/
#include<complex>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn = 200000;
const double pi = 3.1415926535;
typedef complex<double> cd;
char s1[maxn],s2[maxn];
cd aa[maxn],bb[maxn];
int out[maxn],rev[maxn];
void getrev(int n)
{
	for(int i=0,j=0;i<n;i++)
	{
		rev[i]=j;
		for(int k=n>>1;(j^=k)<k;k>>=1);
	}
}
void fft(cd *a,int n,int dft)
{
	for(int i=0;i<n;i++)
		if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int st=1;st<n;st<<=1)
	{
		cd dwfg=exp(cd(0,dft*pi/st));
		for(int i=0;i<n;i+=(st<<1))
		{
			cd nfg=1;
			for(int j=i;j<i+st;j++)
			{
				cd x=a[j],y=a[j+st]*nfg;
				nfg*=dwfg;
				a[j]=x+y; a[j+st]=x-y;
			}
		}
	}
	if(dft==-1) for(int i=0;i<n;i++) a[i]/=n;
}
int n,m;
int ans=0x3f3f3f3f;
int ma,sum1,sum2;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%lf",&aa[i].real());
	for(int i=1;i<=n;i++) scanf("%lf",&bb[i].real() );
	for(int i=1;i<=n;i++)
	{
		sum1+=aa[i].real()*aa[i].real()+bb[i].real()*bb[i].real();
		sum2+=aa[i].real()-bb[i].real();
	}
	int s;
	reverse(bb+1,bb+1+n);
	for(s=2;s<=2*n;s<<=1);
	getrev(s);
	fft(aa,s,1); fft(bb,s,1);
	for(int i=0;i<s;i++) aa[i]*=bb[i];
	fft(aa,s,-1);
	for(int i=0;i<s;i++) out[i]=(int)(aa[i].real() +0.5);
	for(int i=0;i<=n;i++) ma=max(out[i]+out[i+n],ma);
	for(int r=-m;r<=m;r++)
	{
		ans=min(ans,-ma*2+sum1+n*r*r+2*r*sum2);
	}
	printf("%d",ans);
}

 

posted @ 2018-05-04 15:42  Newuser233  阅读(6)  评论(0)    收藏  举报