P3723 [AH2017/HNOI2017]礼物

Description

我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手环,一个留给自己,一个送给她。每个手环上各有 $n$ 个装饰物,并且每个装饰物都有一定的亮度。

但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有装饰物的亮度增加一个相同的非负整数 $c$。并且由于这个手环是一个圆,可以以任意的角度旋转它,但是由于上面装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。

在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号 $1 sim n$,其中 $n$ 为每个手环的装饰物个数, 第 $1$ 个手环的 $i$ 号位置装饰物亮度为 $x_i$,第 $2$ 个手环的 $i$ 号位置装饰物亮度为 $y_i$,两个手环之间的差异值为(参见输入输出样例和样例解释):

$$sum_{i=1}^{n} (x_i-y_i)^2$$

麻烦你帮他计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小,这个最小值是多少呢?

Solution

假设此时已经进行完旋转操作,对某一多项式每一项加$c$

$$sum _{i=0}^{n-1} (x_i-y_i+c)^2 = nc^2+2csum _{i=0}^{n-1} (x_i-y_i)+sum _{i=0}^{n-1}x_i+sum _{i=0}^{n-1} y_i-2sum _{i=0}^{n-1}x_iy_i$$

观察到前两项为二次函数,取极值即可,第三第四项很好算,现在只需解决旋转的问题

如果将$a$逆序,转化为多项式相乘的问题

FFT求解

#include<algorithm>
#include<iostream>
#include<complex>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m,rev[400005];
long long ans,temp,tot=1,s=2;
const double pie=acos(-1);
complex<double>a[400005],b[400005];
inline int read()
{
    int f=1,w=0;
    char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') w=(w<<1)+(w<<3)+ch-'0',ch=getchar();
    return f*w;
}
void fft(complex<double>*a,int N,int inv)
{
    for(int i=0;i<N;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int i=1;i<N;i*=2)
    {
        complex<double>wn=exp(complex<double>(0,inv*pie/i));
        for(int j=0;j<N;j+=i*2)
        {
            complex<double>w(1,0);
            for(int k=j;k<j+i;k++)
            {
                complex<double>x=a[k],y=w*a[k+i];
                a[k]=x+y,a[k+i]=x-y,w*=wn;
            }
        }
    }
    if(inv==-1) for(int i=0;i<N;i++) a[i]/=N;
}
int main()
{
    n=read(),m=read();
    for(int i=0;i<n;i++) a[i]=read();
    for(int i=0;i<n;i++) b[i]=read();
    for(int i=0;i<n;i++) ans+=a[i].real()*a[i].real()+b[i].real()*b[i].real(),temp+=b[i].real()-a[i].real();
    int p1=floor((double)temp/(double)n),p2=ceil((double)temp/(double)n);
    ans+=min(n*p1*p1-2*p1*temp,n*p2*p2-2*p2*temp);
    for(int i=0;i<n;i++) b[i+n]=b[i];
    reverse(a,a+n);
    while((1<<tot)<(3*n)) s<<=1,tot++;
    for(int i=0;i<s;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(tot-1));
    fft(a,s,1),fft(b,s,1);
    for(int i=0;i<s;i++) a[i]*=b[i];
    fft(a,s,-1),temp=0;
    for(int i=n-1;i<2*n-1;i++) temp=max(temp,(long long)(a[i].real()+0.5));
    printf("%lld
",ans-(temp<<1));
    return 0;
}
[AH2017/HNOI2017]礼物
原文地址:https://www.cnblogs.com/JDFZ-ZZ/p/14176584.html