• [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(2sum{a}-2sum{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
    ",1LL*ans-2*mx+add);
        return 0;
    }
  • 相关阅读:
    从尾到头打印链表
    在链表结尾插入一个结点 以及在 在链表中找到第一个含有某值的结点并删除该结点
    替换空格
    二维数组中的查找
    简单选择排序
    冒泡排序
    Hash表的实现
    二叉排序树
    ajax返回后台编译时都对,返回error
    sql删除重复的记录保留一条
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj4827.html
Copyright © 2020-2023  润新知