zoukankan      html  css  js  c++  java
  • 【洛谷3723】[AH2017/HNOI2017] 礼物(FFT)

    点此看题面

    大致题意: 有两个长度为(n)的序列,可将其中一个序列全加上一个相同的非负整数(c),也可将其中一个序列旋转任意位。求修改后(sum_{i=1}^n(x_i-y_i)^2)的最小值。

    前言

    出于个人习惯,此处将下标改为从(0)开始,即将题目改成求(sum_{i=0}^{n-1}(x_i-y_i)^2)的最小值。

    这是我自己推出来的第一道(FFT)题目,感觉还是挺有成就感的......

    推式子

    我们可以假设(x_i)变成了(x_{(i+p)\%n}+c)

    注意这里(c)的取值范围,尽管题目中强调(c)是非负整数,但由于是可以给(x)(y)中任意一个序列加上(c),所以如果给(x_i)全加上一个负整数(c),就相当于给(y_i)全加上一个非负整数(-c),因此,(c)可以为任意整数

    则此时的答案为:

    [sum_{i=0}^{n-1}(x_{(i+p)\%n}-y_i+c)^2 ]

    根据公式((a+b+c)^2=a^2+b^2+c^2+2ab+2ac+2bc),我们可以把平方项拆开得到:

    [sum_{i=0}^{n-1}(x_{(i+p)\%n}^2+y_i^2+c^2-2x_{(i+p)\%n}y_i+2x_{(i+p)\%n}c-2y_ic) ]

    把这个式子的各项分开再整理,得到:

    [sum_{i=0}^{n-1}(x_{(i+p)\%n}^2+y_i^2)+(nc^2+csum_{i=0}^{n-1}(2x_{(i+p)\%n}-2y_i))-2sum_{i=0}^{n-1}x_{(i+p)\%n}y_i ]

    然后我们可以发现,在这个式子的第一项中的(sum_{i=0}^{n-1}x_{(i+p)\%n}^2=sum_{i=0}^{n-1}x_i^2),第二项中的(sum_{i=0}^{n-1}x_{(i+p)\%n}=sum_{i=0}^{n-1}x_i),所以就得到:

    [sum_{i=0}^{n-1}(x_i^2+y_i^2)+(nc^2+csum_{i=0}^{n-1}(2x_i-2y_i))-2sum_{i=0}^{n-1}x_{(i+p)\%n}y_i ]

    在这个式子中,我们可以发现:

    1. 第一项是定值。

    2. 第二项是一个关于(c)的二次函数,其中各项系数也是确定的,可以直接套公式求出。

      由于(a=n>0),所以函数开口向上,在顶点处取最小值。

      而顶点横坐标(即(c)的值)为(-frac b{2a}),可以直接计算。这里注意(c)是整数,所以要将其四舍五入。

      然后把求出的(c)的值代入函数就可以求得这一项的最小值。

    3. 要使第三项最小,其实就相当于使(sum_{i=0}^{n-1}x_{(i+p)\%n}y_i)最大。

      如果我们将(x_i)复制一遍,即令(x_{n+i}=x_i),并设(y_i=y'_{n-i-1}),则可以推得新式子:(sum_{i=0}^{n-1}x_{i+p}y'_{n-i-1})

      这显然就是一个卷积形式,可以转化为(sum_{i+j=n+p-1}x_iy'_j)

      因此直接上(FFT)就可以了。

    具体实现可详见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 50000
    #define DB double
    using namespace std;
    int n,m,a[3*N+5],b[N+5],b_[N+5];
    I int Calc(CI x)//计算二次函数nc^2+xc的最小值
    {
    	DB t=-x/(2.0*n);RI l=floor(t),r=l+1,c=t-l<r-t?l:r;//计算顶点横坐标,然后四舍五入得到c的值
    	return n*c*c+x*c;//代入函数求值
    }
    class Poly
    {
    	private:
    		int P,L,R[6*N+5];DB Pi;
    		struct node
    		{
    			DB x,y;I node(Con DB& a=0,Con DB& b=0):x(a),y(b){}
    			I node operator + (Con node& o) Con {return node(x+o.x,y+o.y);}
    			I node operator - (Con node& o) Con {return node(x-o.x,y-o.y);}
    			I node operator * (Con node& o) Con {return node(x*o.x-y*o.y,x*o.y+y*o.x);}
    		}A[6*N+5],B[6*N+5];
    		I void T(node *s,CI op)
    		{
    			RI i,j,k;node U,S,x,y;for(i=0;i^P;++i) i<R[i]&&(x=s[i],s[i]=s[R[i]],s[R[i]]=x,0);
    			for(i=1;i^P;i<<=1) for(U=node(cos(Pi/i),op*sin(Pi/i)),j=0;j^P;j+=i<<1)
    				for(S=node(1,0),k=0;k^i;++k,S=S*U) s[j+k]=(x=s[j+k])+(y=S*s[i+j+k]),s[i+j+k]=x-y;
    		}
    	public:
    		I Poly() {Pi=acos(-1);}
    		I void FFT(CI n,int *a,CI m,int *b)//卷积
    		{
    			RI i;P=1,L=0;W(P<=n+m-2) P<<=1,++L;for(i=0;i^P;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);
    			for(i=0;i^n;++i) A[i]=node(a[i]);for(i=0;i^m;++i) B[i]=node(b[i]);
    			for(T(A,1),T(B,1),i=0;i^P;++i) A[i]=A[i]*B[i];for(T(A,-1),i=0;i<=n+m-2;++i) a[i]=A[i].x/P+0.5;
    		}
    }P;
    int main()
    {
    	RI i;for(scanf("%d%d",&n,&m),i=0;i^n;++i) scanf("%d",a+i);for(i=0;i^n;++i) scanf("%d",b+i);
    	RI t=0,k=0;for(i=0;i^n;++i) t+=a[i]*a[i]+b[i]*b[i],k+=2*a[i]-2*b[i];t+=Calc(k);
    	for(i=0;i^n;++i) a[n+i]=a[i],b_[i]=b[n-i-1];P.FFT(2*n,a,n,b_);//将a复制一遍,并求b的倒序数组
    	for(k=i=0;i^n;++i) k<a[n+i-1]&&(k=a[n+i-1]);return printf("%d",t-2*k),0;//求最大值,然后计算答案
    }
    
  • 相关阅读:
    spring容器启动
    springmvc流程
    bean作用域
    bean的生命周期
    web.xml详解
    设计模式 工厂和抽象工厂
    requests 模块
    爬虫基础
    提高级
    循环语句
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3723.html
Copyright © 2011-2022 走看看