点击打开链接
首先做一个重要的转化:把 b i b_i bi 单调上升变为 b i b_i bi 单调不降
如何转化?将 a i − i a_i-i ai−i 变成新的 a i a_i ai,将 b i − i b_i-i bi−i 变新的 b i b_i bi,这样答案是不变的,且 b i b_i bi 变成了单调不降
考虑对于两段相邻序列 a 1 , . . . , a n a_1,...,a_n a1,...,an 和 a n + 1 , . . . , a n + m a_{n+1},...,a_{n+m} an+1,...,an+m,最优的一组 b b b 分别为 b 1 , . . . , b n ( b 1 = b 2 = . . . = b n ) b_1,...,b_n(b_1=b_2=...=b_n) b1,...,bn(b1=b2=...=bn) 和 b n + 1 , . . . , b n + m ( b b + 1 = b n + 2 = . . . = b n + m ) b_{n+1},...,b_{n+m}(b_{b+1}=b_{n+2}=...=b_{n+m}) bn+1,...,bn+m(bb+1=bn+2=...=bn+m)
考虑合并之后的 b 1 , . . . , b n + m b_1,...,b_{n+m} b1,...,bn+m
令 b 1 = u , b n + 1 = v b_1=u,\;b_{n+1}=v b1=u,bn+1=v
所以可以一个一个添加数,一段一段往前合并即可
考虑如何合并?用左偏树维护前一半的最大值
考虑合并如何合并两个左偏树?直接合并即可
直接合并左偏树维护中位数在没有限制的两个序列中是不对的
但这道题中是有特殊限制的
考虑新的中位数一定出现在橘色框内,且上面序列的橘色框是维护到了,但下面序列的橘色框内是没有维护到的,但因为只添加了一个数,且之前的中位数是大于上方序列的中位数的,所以下面序列的橘色框内没有数,所以直接合并恰好是可行的
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
#include
using namespace std;
typedef long long LL;
const int N(1000100);
struct Node{
int ed,rt,siz;
}stk[N];
int n,a[N],ans[N];
int lc[N],rc[N],dist[N],v[N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
int merge(int x,int y){
if(!x||!y) return x|y;
if(v[x]<v[y]) swap(x,y);
rc[x]=merge(rc[x],y);
if(dist[rc[x]]>dist[lc[x]]) swap(lc[x],rc[x]);
dist[x]=dist[rc[x]]+1;
return x;
}
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read(),a[i]-=i;
int top=0;
for(int i=1;i<=n;i++){
v[i]=a[i],dist[i]=1;
Node t={i,i,1};
while(top&&v[t.rt]<v[stk[top].rt]){
t.rt=merge(t.rt,stk[top].rt);
if((t.siz&1)&&(stk[top].siz&1)) t.rt=merge(lc[t.rt],rc[t.rt]);
t.siz+=stk[top].siz;
top--;
}
stk[++top]=t;
}
for(int i=1,j=1;i<=n;i++){
if(i>stk[j].ed) j++;
ans[i]=v[stk[j].rt];
}
LL tot=0;
for(int i=1;i<=n;i++) tot+=abs(ans[i]-a[i]);
printf("%lld\n",tot);
for(int i=1;i<=n;i++) printf("%d ",ans[i]+i);
return 0;
}