题目:
题解:
这道题让我想起了曲神的胡策,把一堆数字变成一样的数字要的最小代价是什么呢?就是全都变成中位数啦
那这个题目是什么意思呢?把一堆数字变成递增的需要的最小代价
先考虑 z1<=z2<=z3<=...<=zn z 1 <= z 2 <= z 3 <= . . . <= z n 下的最优解
我们的整体思路就是划分成m个片段,每个片段将所有的数字变成中位数,这样的解是最小的
假设已经求出了前k个数的最优解,被划分成了m个区间,每段区间的最优解为 w[i](w[1]<=w[2]<=...<=w[m]) w [ i ] ( w [ 1 ] <= w [ 2 ] <= . . . <= w [ m ] ) ,现在考虑第k + 1个数,先将a[k + 1]单独看作一个区间,最优解为w[m+1],此时假如 w[m]>w[m+1] w [ m ] > w [ m + 1 ] ,则合并区间m,m + 1,然后找出新区间的解(中位数),重复上述过程直到 w[m]<=w[m+1] w [ m ] <= w [ m + 1 ]
如何维护一段区间的中位数?我们使用左偏树,当size>空间长度的一半时,我们弹掉最大值,这样堆顶就是这个区间的中位数
这是
z1<=z2<=z3<=...<=zn
z
1
<=
z
2
<=
z
3
<=
.
.
.
<=
z
n
情况下的
如何不让他们有可能相等呢?转化a[i]->a[i]-i就行了,这样转化后相等的话考虑到转化前也不会相等至少差1
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
const int N=1000005;
int ls[N],rs[N],dis[N],l[N],r[N],size[N],tot[N],root[N];LL a[N];
int merge(int x,int y)
{
if (!x || !y) return x+y;
if (a[x]<a[y]) swap(x,y);
rs[x]=merge(rs[x],y);
size[x]=size[ls[x]]+size[rs[x]]+1;//堆中还在的元素个数
if (dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);
if (!rs[x]) dis[x]=0;else dis[x]=dis[rs[x]]+1;
return x;
}
int main()
{
int n,m=0;scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]),a[i]-=i;
for (int i=1;i<=n;i++)
{
++m;l[m]=r[m]=i;
root[m]=i;size[i]=tot[m]=1;
while (m>1 && a[root[m]]<a[root[m-1]])
{
m--;
r[m]=r[m+1];
tot[m]+=tot[m+1];//范围内总元素个数
root[m]=merge(root[m],root[m+1]);
while (size[root[m]]*2>tot[m]+1) //保证堆顶为中位数
root[m]=merge(ls[root[m]],rs[root[m]]);
}
}
LL ans=0;
for (int i=1;i<=m;i++)
for (int j=l[i];j<=r[i];j++)
ans+=(LL)abs(a[j]-a[root[i]]);
printf("%lld",ans);
}