题目
思路
首先考虑原题弱化版:相邻的b可以相等
于是我们可以得到两个性感感性的结论:
- 如果a递增,则b=a最优
- 如果a递减,b=a的中位数最优
根据这两个结论,我们可以得到一个比较感性的做法:
将原序列分成单调不升的m段,则每一段我们都取中位数。
然而这个做法不完全正确,比如我们取完中位数后是这样的序列:
3 3 3 2 2
我们发现这仍然不单调不降,所以我们需要合并这两个区间并重新取个中位数。
也就是说我们需要合并区间以求中位数。这个用左偏树可以做到log
那么原题怎么办?
我们可以对每一对ai,bi减一个i,这样答案不受影响
代码
#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
int read()
{
int x=0,f=1; char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar(); }
while(c>='0'&&c<='9') x=x*10+c-48,c=getchar();
return x*f;
}
#define _ 1000006
int n,dis[_],ch[_][2],cnt;
ll a[_],b[_],ans;
struct node{int root,ls,rs,size,val; }e[_];
int merge(int x,int y)
{
if(!x||!y) return x+y;
if(a[x]<a[y]||(a[x]==a[y]&&x>y)) swap(x,y);
ch[x][1]=merge(ch[x][1],y);
if(dis[ch[x][0]]<dis[ch[x][1]]) swap(ch[x][0],ch[x][1]);
dis[x]=dis[ch[x][1]]+1;
return x;
}
int main()
{
n=read(); dis[0]=-1;
for(int i=1; i<=n; ++i) a[i]=read()-i;
for(int i=1; i<=n; ++i)
{
e[++cnt]=(node){i,i,i,1,a[i]};
while(cnt>1&&e[cnt].val<e[cnt-1].val)
{
--cnt;
e[cnt].root=merge(e[cnt].root,e[cnt+1].root);
e[cnt].size+=e[cnt+1].size;
e[cnt].rs=e[cnt+1].rs;
while(e[cnt].size*2>e[cnt].rs-e[cnt].ls+2)
{
--e[cnt].size;
e[cnt].root=merge(ch[e[cnt].root][0],ch[e[cnt].root][1]);
}
e[cnt].val=a[e[cnt].root];
}
}
for(int i=1; i<=cnt; ++i)
{
for(int j=e[i].ls; j<=e[i].rs; ++j)
{
b[j]=e[i].val;
ans+=abs(a[j]-b[j]);
}
}
printf("%lld\n",ans);
for(int i=1; i<=n; ++i) printf("%lld ",b[i]+i);
}