zoukankan      html  css  js  c++  java
  • bzoj 4827: [Hnoi2017]礼物【FFT】

    记得FFT要开大数组!!开到快MLE的那种!!我这个就是例子TAT,5e5都RE了
    在这题上花的时间太多了,还是FFT不太熟练。
    首先看70分的n方做法:从0下标开始存,先n--,把a数组倍增,然后枚举a数组的起点st(相当于环上a的st和b的0相匹配),设x为增量

    [sum_{i=0}^{n}(a[i+s]+x-b[i])^2 ]

    [=sum_{i=0}^{n}(a[i+s]-b[i])^2+x^2-2*x*(a[i+s]-b[i]) ]

    [=sum_{i=0}^{n}(a[i+s]-b[i])^2+n*x^2-x*2*sum_{i=0}^{n}(a[i+s]-b[i]) ]

    [a=n,b=2*sum_{i=0}^{n}(a[i+s]-b[i]),c=sum_{i=0}^{n}(a[i+s]-b[i])^2 ]

    显然这是一个开口向上的二次函数,所以可以用二次函数求顶点公式求。(注意!不能直接求y,因为x是整数,但是求顶点公式求出来的不一定是,我简单粗暴的把floor和ceil取了个min)
    这个暴力在考场上性价比非常高,大概20min就能得70分;
    然后正解,观察上面式子,发现设:

    [sa=sum_{i=0}^{n}a[i],saf=sum_{i=0}^{n}a[i]*a[i] ]

    [sb=sum_{i=0}^{n}b[i],sbf=sum_{i=0}^{n}b[i]*b[i] ]

    那么:

    [a=n,b=2*(sa-sb),c=saf+sab-2*sum_{i=0}^{n}(a[i+s]*b[i]) ]

    现在只有设( c[i]=sum_{i=0}^{n}(a[i+s]*b[i]) ),不是常数,考虑怎么把它化成卷积形式然后FFT预处理。
    设:

    [z[i]=sum_{j=0}^{n-i}x[j]y[i+j],f[i]=sum_{j=0}^{n-i}y[j]x[i+j] ]

    [c[i]=z[i]+f[n-i+1] ]

    设nx为x数组反过来,设ny为y数组反过来,然后随便推一推就会发现z和f变成这样:

    [z[n-i]=sum_{j=0}^{n-i}x[i-j]ny[j],f[n-i]=sum_{j=0}^{n-i}y[i-j]nx[j] ]

    然后FFT预处理即可;

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    const int N=1000005;
    int re[N],lm,bt;
    long long n,m,a[N],b[N],sa,saf,sb,sbf,c[N],x1,x2,ans=1e18;
    struct cd
    {
    	double r,i;
    	cd(double R=0,double I=0)
    	{
    		r=R,i=I;
    	}
    	cd operator + (const cd &a) const
    	{
    		return cd(r+a.r,i+a.i);
    	}
    	cd operator - (const cd &a) const
    	{
    		return cd(r-a.r,i-a.i);
    	}
    	cd operator * (const cd &a) const
    	{
    		return cd(r*a.r-i*a.i,a.r*i+a.i*r);
    	}
    }x[N],y[N],nx[N],ny[N];
    int read()
    {
    	int r=0,f=1;
    	char p=getchar();
    	while(p>'9'||p<'0')
    	{
    		if(p=='-')
    			f=-1;
    		p=getchar();
    	}
    	while(p>='0'&&p<='9')
    	{
    		r=r*10+p-48;
    		p=getchar();
    	}
    	return r*f;
    }
    void dft(cd a[],int f)
    {
    	for(int i=0;i<lm;i++)
    		if(i<re[i])
    			swap(a[i],a[re[i]]);
    	for(int i=1;i<lm;i<<=1)
    	{
    		cd wi=cd(cos(M_PI/i),f*sin(M_PI/i));
    		for(int k=0;k<lm;k+=(i<<1))
    		{
    			cd w=cd(1,0),x,y;
    			for(int j=0;j<i;j++)
    			{
    				x=a[k+j];
    				y=a[i+j+k]*w;
    				a[k+j]=x+y;
    				a[i+j+k]=x-y;
    				w=w*wi;
    			}
    		}
    	}
    	if(f==-1)
    	{
    		for(int i=0;i<lm;i++)
    			a[i].r/=lm;
    	}
    }
    void fft(cd a[],cd b[])
    {
    	dft(a,1);
    	dft(b,1);
    	for(int i=0;i<lm;i++)
    		a[i]=a[i]*b[i];
    	dft(a,-1);
    }
    int main()
    {
    	n=read()-1,m=read();
    	for(int i=0;i<=n;i++)
    		a[i]=read(),sa+=a[i],saf+=a[i]*a[i];
    	for(int i=0;i<=n;i++)
    		b[i]=read(),sb+=b[i],sbf+=b[i]*b[i];
    	for(int i=0;i<=n;i++)
    	{
    		x[i].r=b[i],y[i].r=a[i];
    		nx[i].r=b[n-i],ny[i].r=a[n-i];
    	}
    	for(lm=1,bt=0;lm<=2*n;bt++,lm<<=1);
    	for(int i=0;i<lm;i++)
    		re[i]=(re[i>>1]>>1)|((i&1)<<(bt-1));
    	fft(x,ny);
    	fft(y,nx);
    	for(int i=0;i<=n;i++)
    		c[i]=(int)(1.0*(x[n-i].r+y[i-1].r)+0.5);//,cout<<c[i]<<endl;
        x1=0-floor(1.0*(sa-sb)/(n+1)),x2=0-ceil(1.0*(sa-sb)/(n+1));//cout<<x1<<" "<<x2<<endl;
    	for(int i=0;i<=(n+1);i++)
    	{
    		ans=min(ans,min((n+1)*x1*x1+2*(sa-sb)*x1+saf+sbf-2*c[i],(n+1)*x2*x2+2*(sa-sb)*x2+saf+sbf-2*c[i]));
    		// cout<<sa<<" "<<sb<<" "<<saf<<" "<<sbf<<" "<<x1<<" "<<x2<<" "<<c[i]<<" "<<ans<<endl;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    WebQQ2.0 PHP
    HTML文档类型 PHP
    字符●圆角 PHP
    IIS日志分析器 PHP
    JS 像素数字 PHP
    3DTagCloud3D标签云 PHP
    QQ截屏工具提取 PHP
    .NET嵌入DLL ILMerge工具应用 PHP
    JS CSS 压缩工具(GUI界面) PHP
    Javascript 函数初探
  • 原文地址:https://www.cnblogs.com/lokiii/p/8516000.html
Copyright © 2011-2022 走看看