BZOJ 4827: [AH2017/HNOI2017]礼物 FFT

title

BZOJ 4827

LUOGU 3723

简化题意:

给定两个数列:\(\{x_i\}\)\(\{y_i\}\) 可对各数列进行 循环移动改变数列中某一项的值 ,使得下面的式子的值最小。
\[ \sum^n_{i=1}(x_i-y_i)^2 \]

analysis

我们令增加量为 \(C\) ,那么这个式子就可以化简为:
\[ \sum^n_{i=1}(x_i-y_i+C)^2\\ \sum^n_{i=1}(x_i-y_i)^2+2C\sum^n_{i=1}(x_i-y_i)+\sum^n_{i=1}C^2\\ \sum^n_{i=1}x_i^2-2\sum^n_{i=1}x_iy_i+\sum^n_{i=1}y_i^2+2C\sum^n_{i=1}(x_i-y_i)+nC^2 \]

可以发现,除了第 \(2\) 项之外,其他项都是定值,那么我们要求的就变成了 使 \(\sum^n_{i=1}x_iy_i\) 最大

我们把 \(\{y_i\}\) 倍长,这样就不用再考虑环的问题了。

考虑将 \(\{y_i\}\) 转动 \(k\) 位时的答案: \(\sum^n_{i=1}x_iy_{i+k}\)

然后再把 \(\{x_i\}\) 翻转,答案就变成了 \(\sum^n_{i=1}x_{n-i+1}y_{i+k}\) ,这非常符合卷积的形式,所以就可以进行 \(FFT\) 快速求值了。

卷积完了之后,因为答案的第 \(n+1\sim 2n\) 项的最大值就是 \(\sum^n_{i=1}x_iy_i\) 的最大值,所以扫一遍求出 \(\max\)

然后再 \(O(n)\) 求出其他定值项。

然后枚举一下 \(C\) 求出最终答案。

code

#include<bits/stdc++.h>

const int maxn=2e6+10;
const double Pi=acos(-1);

namespace IO
{
    char buf[1<<15],*fs,*ft;
    inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1, ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }

    char Out[1<<24],*fe=Out;
    inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
    template<typename T>inline void write(T x,char str)
    {
        if (!x) *fe++=48;
        if (x<0) *fe++='-', x=-x;
        T num=0, ch[20];
        while (x) ch[++num]=x%10+48, x/=10;
        while (num) *fe++=ch[num--];
        *fe++=str;
    }
}

using IO::read;
using IO::write;

template<typename T>inline bool chkMin(T &a,const T &b) { return a>b ? (a=b, true) : false; }
template<typename T>inline bool chkMax(T &a,const T &b) { return a<b ? (a=b, true) : false; }

struct Orz{double a,b;}A[maxn],B[maxn],W[maxn];
inline Orz operator + (Orz a,Orz b) { return (Orz){a.a+b.a,a.b+b.b}; }
inline Orz operator - (Orz a,Orz b) { return (Orz){a.a-b.a,a.b-b.b}; }
inline Orz operator * (Orz a,Orz b) { return (Orz){a.a*b.a-a.b*b.b,a.a*b.b+a.b*b.a}; }

int N,r[maxn],l;
inline void FFT(Orz *P,int opt)
{
    for (int i=0; i<N; ++i) if (i<r[i]) std::swap(P[i],P[r[i]]);
    for (int i=1; i<N; i<<=1)
        for (int p=i<<1, j=0; j<N; j+=p)
            for (int k=0; k<i; ++k)
            {
                Orz w=(Orz){W[N/i*k].a,W[N/i*k].b*opt};
                Orz x=P[j+k], y=w*P[j+k+i];
                P[j+k]=x+y, P[j+k+i]=x-y;
            }
}

int n,m,M;
int a[maxn],b[maxn],S[maxn];
inline void solve()
{
    N=n-1, M=n+m-1;
    for (int i=0; i<=N; ++i) A[i].a=a[i+1], A[i].b=0;
    for (int i=0; i<n; ++i) B[i].a=b[n-i], B[i].b=0;
    for (int i=0; i<n; ++i) B[i+n].a=B[i].a, B[i+n].b=0;
    M+=N;
    for (N=1; N<=M; N<<=1) ++l;
    for (int i=0; i<N; ++i) r[i]=( (r[i>>1]>>1) | ((i&1)<<(l-1)) );
    for (int i=1; i<N; i<<=1)
        for (int k=0; k<i; ++k) W[N/i*k]=(Orz){cos(k*Pi/i),sin(k*Pi/i)};
    FFT(A,1), FFT(B,1);
    for (int i=0; i<N; ++i) A[i]=A[i]*B[i];
    FFT(A,-1);
    for (int i=0; i<=M; ++i) S[i]=(int)(A[i].a/N+0.5);
}

int main()
{
    read(n);read(m);
    for (int i=1; i<=n; ++i) read(a[i]);
    for (int i=1; i<=n; ++i) read(b[i]);
    solve();
    int p1=0, p2=0, t1=0, t2=0, G=-1e9, ans=1e9;
    for (int i=1; i<=n; ++i) p1+=a[i]*a[i], p2+=b[i]*b[i], t1+=a[i], t2+=b[i];
    for (int i=n-1; i<(n<<1); ++i) chkMax(G,S[i]);
    for (int C=-m; C<=m; ++C)
    {
        int tot=p1+p2+n*C*C+(t1-t2)*C*2-G*2;
        chkMin(ans,tot);
    }
    write(ans,'\n');
    IO::flush();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/G-hsm/p/11446928.html