[JZOJ5083].【GDSOI2017第三轮模拟】Gift

题目描述

这里写图片描述
这里写图片描述

分析

先不管怎么添加,我们看看怎么样快速算出一个手环旋转n位后的差异值。
(xiyi)2=x2i+y2i2xiyi=Const1+Const22xiyi
我们只用算最后那个东西就行了。这种形式很容易想到把其中一个数组反过来,注意下标从0开始。然后就成了卷积了,即 z[k]=ki=0xn1iyi ,那么z的第n项就是没有旋转过的差异值,把y数组倍长,则第k项就是旋转了k-n位的差异值。FFT即可。
现在考虑对x加上一个数p。由于我们现在只关心差值,所以说p可以为负数,相当于给y加上-p。枚举p可能超时,那么化一下式子,看看如何。
(xiyi+p)2=(xiyi)2+np2+2p(xiyi) ,这样可以发现,那些 与p无关,所以实际上是一个二次函数,那么求出对称轴,直接加就可以了。
注意FFT的时候,逆DFT之后一定要除n····
一般来说DFT是不会错的,都是外面错了,不要乱调里面····

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int N=400005;
const int mo=1004535809;
const int mo2=998244353;
const int mo3=104857601;
const int root=3;
int len,Log,ref[N],a[N],b[N],c[N],d[N],t[N],sa,sb,T,ans,w[2][N],n,m,i,invn;
int ksm(int x,int y)
{
    int ret=1;
    while (y)
    {
        if (y&1) ret=1ll*ret*x%mo;
        y>>=1;
        x=1ll*x*x%mo;
    }
    return ret;
}
int max2(int n)
{
    int ret=1;
    for(;ret<=n;ret*=2);
    return ret*2;
}
int ci(int len){return (len==1)?0:ci(len/2)+1;}
void predo()
{
    len=max2(2*n-1);
    Log=ci(len);
    int i,c,tp;
    fo(i,0,len-1)
    {
        int tmp=0;
        for(tp=i,c=0;c<Log;tp>>=1,c++) tmp=(tmp<<1)+(tp&1);
        ref[i]=tmp;
    }
    int siz=1;
    fo(i,0,Log)
    {
        w[1][i]=ksm(root,(mo-1)/siz);
        w[0][i]=ksm(w[1][i],mo-2);
        siz*=2;
    }
}
void dft(int *a,int n,int sig)
{
    int i,j,k,W,siz,c,half,u,v;
    fo(i,0,n-1) t[ref[i]]=a[i];
    for(siz=2,c=1;siz<=n;siz*=2,c++)
    {
        half=siz/2;
        W=1;
        fo(i,0,half-1)
        {
            for(j=i;j<=n;j+=siz)
            {
                k=j+half;
                u=t[j],v=1ll*W*t[k]%mo;
                t[j]=(u+v)%mo;
                t[k]=(u-v+mo)%mo;
            }
            W=1ll*W*w[sig][c]%mo;
        }
    }
    fo(i,0,n-1) a[i]=t[i];
}
int solve(int add)
{
    sa=sb=0;
    int i;
    len=max2(2*n-1);
    fo(i,0,len-1) c[i]=d[i]=0;
    fo(i,0,n-1) c[i]=a[n-1-i]+add,sa+=c[i]*c[i];
    fo(i,0,n-1) d[i]=d[i+n]=b[i],sb+=d[i]*d[i];
    Log=ci(len);
    int invlen=ksm(len,mo-2);
    dft(c,len,1);
    dft(d,len,1);
    fo(i,0,len-1) c[i]=1ll*c[i]*d[i]%mo;
    dft(c,len,0);
    fo(i,0,len-1) c[i]=1ll*c[i]*invlen%mo;
    int ret=0;
    fo(i,n-1,n*2-1)
        ret=max(ret,c[i]);
    return sa+sb-2*ret;
}
int main()
{
    freopen("gift.in","r",stdin);
//  freopen("gift.out","w",stdout);
    scanf("%d %d",&n,&m);
    fo(i,0,n-1) scanf("%d",a+i);
    fo(i,0,n-1) scanf("%d",b+i),T+=a[i]-b[i];
    predo();
    ans=1e9;
    ans=min(solve(-T/n),ans);
    ans=min(solve(-T/n+1),ans);
    ans=min(solve(-T/n-1),ans);
    printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值