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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM