ICPC2021 济南 D-Arithmetic Sequence

题目描述:


大意:
通过将某一项+1/-1的操作,将序列改成等差序列,问最小操作数。

题目思路:

假定公差\(~ x ~\)已知,考虑如何寻找一个最优的首项\(a_1\)使得将原序列变为等差序列的总代价最小,设 \(c_i=a_i-(i-1)*x\),即将根据每一项的值求出对应的首项。当首项已知,就能求出等差序列,那么总代价即为等差序列与原序列每一项差的绝对值之和。对于首项\(a_1 \geq c_i\)时,当\(a_1\)增加1,第\(i\)项产生的代价加1;对于首项\(a_1<c_i\)时,当\(a_1\)增加1,第\(i\)项产生的代价减1。由上述可知,首项与总代价之间应该是一个凹函数的关系,而最小值点就是 代价加1的项的数量=代价减1的项的数量 。那么我们选取\(c\)数组的中位数,可作为最优的首项。再求出总代价,即可。求解中位数用\(nth\_element\)\(O(n)\)的。

考虑公差\(x\)与总代价之间的关系,证明搬运官方题解:

对于单峰函数来说,三分求极值即可。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5+5;
const LL inf=2e18+5;
LL a[N],n,c[N];
__int128 __abs(__int128 x)
{
    return x<0?-x:x;
}
__int128 Min(__int128 x, __int128 y)
{
    return x<y?x:y;
}
__int128 check(LL x)//求总代价可能会爆longlong
{
    for(int i=1;i<=n;i++) c[i]=a[i]-x*(i-1);
    nth_element(c+1,c+n/2+1,c+n+1);
    __int128 t=c[n/2+1],res=0;
    for(int i=1;i<=n;i++)
    {
        res+=__abs(t-a[i]);
        t+=x;
    }
    return res;
}
void print(__int128 x)
{
    if(x>9) print(x/10);
    putchar('0'+x%10);
}
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++) scanf("%lld",a+i);
    LL l=-1e13,r=1e13;//等差既能递增,也能递减
    __int128 ans=1e25;
    LL x,y;
    while(l<=r)
    {
        LL m1=l+(r-l)/3;
        LL m2=r-(r-l)/3;
        __int128 t1=check(m1);
        __int128 t2=check(m2);
        ans=Min(ans,Min(t1,t2));
        if(t1>t2) l=m1+1;
        else r=m2-1;
    }
    print(ans);
    printf("\n");
    return 0;
}

posted @ 2021-11-15 20:11  DeepJay  阅读(273)  评论(0编辑  收藏  举报