bzoj #4827 礼物(FFT)(HNOI2017)

标签:FFT


Description

我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手 环,一个留给自己,一个送给她。每个手环上各有 n 个装饰物,并且每个装饰物都有一定的亮度。但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有装饰物的亮度增加一个相同的自然数 c(即非负整数)。并且由于这个手环是一个圆,可以以任意的角度旋转它,但是由于上面 装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号 1,2,…,n,其中 n 为每个手环的装饰物个数,第 1 个手环的 i 号位置装饰物亮度为 xi,第 2 个手 环的 i 号位置装饰物亮度为 yi,两个手环之间的差异值为(参见输入输出样例和样例解释): ni=1(xiyi)2 ∑ i = 1 n ( x i − y i ) 2 麻烦你帮他计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小, 这个最小值是多少呢?

Input

输入数据的第一行有两个数n, m,代表每条手环的装饰物的数量为n,每个装饰物的初始 亮度小于等于m。

接下来两行,每行各有n个数,分别代表第一条手环和第二条手环上从某个位置开始逆时 针方向上各装饰物的亮度。
1≤n≤50000, 1≤m≤100, 1≤ai≤m

Output

输出一个数,表示两个手环能产生的最小差异值。

注意在将手环改造之后,装饰物的亮度 可以大于 m。


不妨设第一个手环为S,第二个手环为T,则题意变为求 (SiTi+k+C)2 ∑ ( S i − T i + k + C ) 2 的最小值
我们将上式展开,可以得到

(S2i+T2i+k+C2+2C(SiTi+k)2SiTi+k) ∑ ( S i 2 + T i + k 2 + C 2 + 2 ∗ C ( S i − T i + k ) − 2 ∗ S i T i + k )

进一步得到
S2i+T2i+nC2+2c(SiTi)2SiTi+k ∑ S i 2 + ∑ T i 2 + n ∗ C 2 + 2 ∗ c ∗ ∑ ( S i − T i ) − 2 ∗ ∑ S i T i + k

先抛开 C C 不看,我们发现只有 SiTi+k ∑ S i T i + k 不是常数
如何求 SiTi+k ∑ S i T i + k 最大值呢?标准套路:将T数组反转,求出S与T的卷积,不难发现, SiTi+k ∑ S i T i + k 对应每一个k的取值,都是卷积中两个相差n次的项的系数之和,这里可以用FFT,将复杂度降到O(nlogn)。
求完 SiTi+k ∑ S i T i + k 最大值后,我们发现只有关于C的二次项与一次项,直接用二次函数求最值的方法即可,注意C只能为整数。

/**************************************************************
    Problem: 4827
    User: P1atform
    Language: C++
    Result: Accepted
    Time:592 ms
    Memory:9108 kb
****************************************************************/

#include
#include
#include
#include
#include
#define N 200000
#define INF 1000000000
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
ll n,m,M,p=0ll,q=0ll,z=0ll,ans=INF,r[N+50],x,l;
struct com
{
    double x,y;
    inline com operator +(com b){com ret;ret.x=x+b.x,ret.y=y+b.y;return ret;}
    inline com operator -(com b){com ret;ret.x=x-b.x,ret.y=y-b.y;return ret;}
    inline com operator *(com b){com ret;ret.x=x*b.x-y*b.y,ret.y=x*b.y+y*b.x;return ret;}
}s[N+50],t[N+50]; 
template<class _T> inline void read(_T &x)
{
    x=0;
    char ch=getchar();
    int f=0;
    while (!isdigit(ch)) {if (ch=='-') f=1;ch=getchar();}
    while (isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    if (f) x=-x; 
} 
inline void fft(com a[],int k)
{
    for (int i=1;iif (ifor (int i=1;i1)
    {
        com w,wn,X,Y;
        wn.x=cos(pi/i),wn.y=k*sin(pi/i);
        for (int j=0;j1))
        {
            w.x=1,w.y=0;
            for (int _=0;_if (k==-1) for (int i=0;iint main()
{
    read(n),n--,read(M),memset(s,0,sizeof(s)),memset(t,0,sizeof(t));
    for (int i=0;i<=n;i++) read(x),p+=x*x,q+=x,s[i].x=x;
    for (int i=0;i<=n;i++) read(x),p+=x*x,q-=x,t[n-i].x=x;
    for (m=2*n,n=1;n<=m;n<<=1) l++;
    for (int i=1;i>1]>>1)|((i&1)<<(l-1));
    fft(s,1),fft(t,1);
    for (int i=0;i<=n;i++) s[i]=s[i]*t[i];
    fft(s,-1),n=m/2,z=(ll)(s[n].x+0.5);
    for (int i=1;i<=n;i++) z=max(z,(ll)(s[i-1].x+0.5)+(ll)(s[i+n].x+0.5));
    for (int i=-M;i<=M;i++) ans=min(ans,p-2*z+i*((n+1)*i+2*q));
    printf("%lld\n",ans);
} 

你可能感兴趣的:(FFT,数学)