[bzoj4827][Hnoi2017]礼物

来自FallDream的博客,未经允许,请勿转载,谢谢。


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

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

在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号1,2,…,n,其中 n 为每个手环的装饰物个数, 第 1 个手环的 i 号位置装饰物亮度为 xi,第 2 个手环的 i 号位置装饰物亮度为 yi,两个手环之间的差异值为$\sum_{i=1}^{n}(xi-yi)^{2}$

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

n<=50000   m<=100,表示所有装饰物的初始亮度不超过m

 

 

假设加上的是k,那么答案是$$\sum_{i=1}^{n}(xi+k-yi)^2$$

把这个拆开,得到$$Ans=min(\sum_{i=1}^{n}(ai^{2}+bi^{2}+k^{2}+k(2a-2b)))$$

平方项不变,得到$$Ans=min(\sum{(ai^2+bi^2)}+k^2+k(2\sum{a}-2\sum{b})-\sum{2ab})$$

k显然可以通过求一元二次方程最小值得到,然后要求的就是在任意移动的时候$\sum{ab}$的最大值

发现它很像卷积,所以可以构造这两个数组

a1   a2    a3  ..  an a1 a2 ... an(复制一遍)

bn bn-1 bn-2 ..  b1 0  0   0   0

这样通过fft/ntt求一个卷积,得到的c数组就是所有移动情况下的和啦,只要从中求一个最大值即可,答案用前面的式子容易计算

然后注意k可以是负数,因为你可以让第二个加上那么多。没注意到这个wa了几次。

然后求稳的话可以加个偏移量,或者直接O(2m)暴力就行了

复杂度nlogn

#include<iostream>
#include<cstdio>
#define MN 131077
#define mod 998244353
using namespace std;
inline int read()
{
    int x = 0; char ch = getchar();
    while(ch < '0' || ch > '9')ch = getchar();
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x;
}

int a[MN+5],b[MN+5],c[MN+5],n,N,w[2][MN+5],ans=0,sum=0,inv;

inline int pow(int x,int k)
{
    int res=1;
    for(;k;k>>=1,x=1LL*x*x%mod)
        if(k&1)res=1LL*res*x%mod;
    return res;
}

void fft(int*x,int r)
{
    for(int i=0,j=0;i<N;++i)
    {
        if(i>j) swap(x[i],x[j]);
        for(int k=N>>1;(j^=k)<k;k>>=1);
    }
    for(int i=2;i<=N;i<<=1)for(int j=0;j<N;j+=i)for(int k=0;k<i>>1;++k)
    {
        int t=1LL*x[j+k+(i>>1)]*w[r][N/i*k]%mod;
        x[j+k+(i>>1)]=(1LL*x[j+k]-t+mod)%mod;
        x[j+k]=(1LL*x[j+k]+t)%mod;
    }
    if(r) for(int i=0;i<N;++i) x[i]=(1LL*x[i]*inv)%mod;
}

int main()
{
    n=read();int m=read();
    for(int i=0;i<n;++i) a[i]=a[i+n]=read(),ans+=a[i]*a[i],sum+=a[i];
    for(int i=n-1;~i;--i) b[i]=read(),ans+=b[i]*b[i],sum-=b[i];
    for(N=1;N<=n<<1;N<<=1);inv=pow(N,mod-2);
    sum<<=1;int g=pow(3,(mod-1)/N);
    for(int i=0,j=1;i<=N;++i,j=1LL*j*g%mod)
        w[0][i]=w[1][N-i]=j;
    fft(a,0);fft(b,0);
    for(int i=0;i<N;++i) c[i]=1LL*a[i]*b[i]%mod;
    fft(c,1);int mx=0,k=-sum/2/n;
    for(int i=n-1;i<2*n;++i) mx=max(mx,c[i]);
    long long add=1LL*k*k*n+1LL*sum*k;
    add=min(add,1LL*(k-1)*(k-1)*n+1LL*sum*(k-1));
    add=min(add,1LL*(k+1)*(k+1)*n+1LL*sum*(k+1));
    printf("%lld\n",1LL*ans-2*mx+add);
    return 0;
}
posted @ 2017-05-20 11:38  FallDream  阅读(221)  评论(0编辑  收藏  举报