Description
Solution
考虑对于一段单调递增的数,那么就是全部贴着选
对于一段单调递减的数,选中位数一定是最优的
那么我们可以考虑把整个序列分成m段,每段的最优解都是其中位数,那么我们只要保证这m段的中位数单调递增即可。
那么假设现在处理到第i个数,我们把a[i]设为单独的一段,那么这一段的答案就是a[i],如果a[i] < w[m-1]则把第m段和第m-1段合并即可。用左偏树维护这一段的值,如果左偏树的节点数大于区间长度/2则删除树顶元素。实际上只要是可合并的堆就行了
但如果这样做的话会导致b单调不下降,但我们需要b单调递增,那么一个常用的操作就是把每个a[i]减去i得到新的数列来搞
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
using std:: swap;
typedef long long ll;
const int N=1000005;
int a[N],son[N][2],val[N],d[N];
int root[N],size[N],l[N],r[N],cnt=0;
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
int merge(int x,int y) {
if (!x||!y) return x+y;
if (val[x]<val[y]) swap(x,y);
son[x][1]=merge(son[x][1],y);
if (d[son[x][0]]<d[son[x][1]]) swap(son[x][0],son[x][1]);
d[x]=d[son[x][1]]+1;
return x;
}
int pop(int x) {return merge(son[x][0],son[x][1]);}
int main(void) {
int n=read();
rep(i,1,n) val[i]=a[i]=read()-i;
rep(i,1,n) {
cnt++; l[cnt]=r[cnt]=i; size[cnt]=1; root[cnt]=i;
while (cnt>1&&val[root[cnt]]<val[root[cnt-1]]) {
cnt--;
size[cnt]+=size[cnt+1];
r[cnt]=r[cnt+1];
root[cnt]=merge(root[cnt],root[cnt+1]);
while (size[cnt]*2>r[cnt]-l[cnt]+2) {
root[cnt]=pop(root[cnt]);
size[cnt]--;
}
}
}
ll ans=0;
rep(i,1,cnt) {
rep(j,l[i],r[i]) ans+=(ll)(abs(a[j]-val[root[i]]));
}
printf("%lld\n", ans);
return 0;
}