项链

升级版AHOI/HNOI 2017礼物

题目大意

有两个排数$A,B$,每一排都有小于$m$的$n$个数,你可以任意的对某一排整体$+1$,然后找到一个排列$P$,记$d(x,y)=min(|x-y|^2,(m-|x-y|)^2)$使得$\sum\limits_{i=1}^{n}d(A_i,B_{P_i})$最小。

 

题解

把两排数放在两个相同数轴上,其中实心的格子表示这个位置有数。整体加一就可以变成将这个轴向右移,再将最右侧的格子补到左边。考虑一件事情,由于对于确定的$A_i,B_{P_i}$,在轴之间连一条线,若它们的值的计算方式不是以$|x-y|^2$计算,则轴需要跨越左右边界。

如果这样画图,那么在最优解中,线一定不会交叉。若存在两条交叉的线,交换他们的匹配,结果一定会更优。这意味着当我们确定$A_i$匹配$B_i+k$时,对于任意的$A_j$,一定匹配$B_{j+k}$(当$j+k>n$时匹配$B_{j+k-n}$)。也就是说,对于$B_i$若连向它的线是跨过边界的,那么对于所有这样$B_i$一定恰好是$B$的前缀。到这里,我们应该想到将下方的那排数倍长($B_{i+n}=B_i+m$)。

这样做还有一个好处,我们可以简化整体$+1$的操作。由于对于两个数组都进行一次$+1$的操作是没有意义的,不妨只对$A$进行操作。由于倍长了$B$,我们甚至可以不用取模。因为最多有意义的$+1$次数不超过$m$,而且每一种匹配的方式都可以在倍长$B$之后体现为$A_i\rightarrow B_{i+k}$,即上图中出现了跨越边界可以视为在倍长的$B$数组中匹配。

然后问题就变成了求$\min\sum\limits_{i=1}^{n}(A_i+d-B_{i+k})^2$。其中$d表示对$A$进行$+1$的次数,$k$表示匹配的编号错位的量。

你很容易发现$0\leq k<n$,然后我们就考虑枚举$k$,就有

$$Ans=\sum\limits_{i=1}^{n}(A_i+d-B_{i+k})^2$$

$$\sum\limits_{i=1}^{n}A_i^2+d^2-B_{i+k}^2+2(A_i-B_{i+k})d-2A_i\cdot B_{i+k}$$

$$Ans=t1+t2+t3$$

$$t1=\sum\limits_{i=1}^{n}A_i^2-B_{i+k}^2$$

$$t2=nd^2+(2\sum\limits_{i=1}^{n}A_i-B_{i+k})d$$

$$t3=\sum\limits_{i=1}^{n}-2A_i\cdot B_{i+k}$$ 

不难发现$t1$是我们可以与处理出来的,$t3$是一个减法意义下的卷积,可以通过将一个数组反转再用$FFT$做加法卷积预处理,而$t2$只有一个未知数$d$,而这恰好又是关于$d$的二次函数$n>0$决定了函数有最小值,所以我们可以通过计算二次函数对称轴的方式来求得$d$(注意$d$必须要临近取整,因为$+1$操作只能进行整数次,但是可以为负数,因为这表示相对的对$B$进行操作),求得$d$后就直接计算就可以了。对于每一个枚举的$k$计算答案,最后取$\min$即可。

复杂度为$O(T(n+n\log n))$。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 262170
using namespace std;
int read(){
    int nm=0,fh=1; char cw=getchar();
    for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    return nm*fh;
}
const double PI=acos(-1);
struct comp{
    double r,k; comp(){}
    comp(double _r,double _k){r=_r,k=_k;}
    comp operator *(const comp&ot)const{return comp(r*ot.r-k*ot.k,r*ot.k+k*ot.r);}
    comp operator +(const comp&ot)const{return comp(r+ot.r,k+ot.k);}
    comp operator -(const comp&ot)const{return comp(r-ot.r,k-ot.k);}
}A[M],B[M],C[M];
LL n,m,sumx[M],sumy[M],dt,X[M],Y[M],sx[M],sy[M],od[M],len,ans,nw;
void FFT(comp *x,double kd){
    for(int i=1;i<len;i++) if(i<od[i]) swap(x[i],x[od[i]]);
    for(int tt=1;tt<len;tt<<=1){
        comp unit(cos(PI*kd/(tt*1.0)),sin(PI*kd/(tt*1.0)));
        for(int st=0;st<len;st+=(tt<<1)){
            comp now(1.0,0.0);
            for(int pos=st;pos<st+tt;pos++,now=now*unit){
                comp t1=x[pos],t2=x[pos+tt]*now;
                x[pos]=t1+t2,x[pos+tt]=t1-t2;
            }
        }
    }
    if(kd<0.0){for(int i=0;i<len;i++) x[i].r/=(len*1.0);}
}
int main(){
    for(int Cs=read();Cs;Cs--){
        m=read(),n=read(),memset(&ans,0x3f,sizeof(LL));
        for(len=1,nw=-1;len<n*3;len<<=1,nw++);
        for(int i=0;i<len;i++) A[i]=B[i]=comp(0.0,0.0),od[i]=((od[i>>1]>>1)|((i&1)<<nw));
        for(int i=1;i<=n;i++) X[i]=read(),sumx[i]=sumx[i-1]+X[i];
        for(int i=1;i<=n;i++) Y[i]=read(),Y[i+n]=Y[i]+m,sumy[i]=sumy[i-1]+Y[i];
        for(int i=1;i<=n;i++){
            sumy[i+n]=sumy[i+n-1]+Y[i+n];
            sx[i]=sx[i-1]+X[i]*X[i],sy[i]=sy[i-1]+Y[i]*Y[i];
        }
        for(int i=n+1;i<=(n<<1);i++) sy[i]=sy[i-1]+Y[i]*Y[i];
        for(int i=0;i<n;i++) A[i]=comp(X[i+1]*1.0,0.0);
        for(int i=0;i<(n<<1);i++) B[i]=comp(Y[(n<<1)-i]*1.0,0.0);
        FFT(A,1.0),FFT(B,1.0);
        for(int i=0;i<len;i++) C[i]=A[i]*B[i]; FFT(C,-1.0);
        for(int k=0;k<n;k++){
            LL now=((LL)(C[(n<<1)-1-k].r+0.5)); now<<=1;
            LL dt=(LL)round((((sumy[n+k]-sumy[k])-sumx[n])*1.0)/(n*1.0));
            LL tk=n*dt*dt+2*dt*(sumx[n]-(sumy[n+k]-sumy[k]));
            ans=min(ans,sx[n]+sy[n+k]-sy[k]+tk-now);
        } printf("%lld\n",ans);
    }
    return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/OYJason/p/9712313.html