題目描述:

大意:
通過將某一項+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;
}
