bzoj #4827 礼物(FFT)(HNOI2017)

本文介绍了一种通过亮度调整和旋转来最小化两个装饰手环差异值的方法。利用FFT快速傅里叶变换技术,实现复杂度从O(n^2)降至O(nlogn),并给出了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

标签:FFT


Description

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

Input

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

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

Output

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

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


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

(S2i+T2i+k+C2+2C(SiTi+k)2SiTi+k)∑(Si2+Ti+k2+C2+2∗C(Si−Ti+k)−2∗SiTi+k)

进一步得到
S2i+T2i+nC2+2c(SiTi)2SiTi+k∑Si2+∑Ti2+n∗C2+2∗c∗∑(Si−Ti)−2∗∑SiTi+k

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

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#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;i<n;i++) if (i<r[i]) swap(a[i],a[r[i]]);
    for (int i=1;i<n;i<<=1)
    {
        com w,wn,X,Y;
        wn.x=cos(pi/i),wn.y=k*sin(pi/i);
        for (int j=0;j<n;j+=(i<<1))
        {
            w.x=1,w.y=0;
            for (int _=0;_<i;_++,w=w*wn)
            {
                X=a[j+_],Y=w*a[j+_+i];
                a[j+_]=X+Y,a[j+_+i]=X-Y;
            }
        } 
    }
    if (k==-1) for (int i=0;i<n;i++) a[i].x/=n;
}
int 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<n;i++) r[i]=(r[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);
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值