bzoj1367
可并堆
对于一段递减的序列我们可以取中位数作为b
那么我们可以把a分成几段递减的序列,每段都取中位数,并且中位数递增
那么维护一个单调栈,每次新加入元素,如果当前段的中位数比之前小就吃掉之前的
中位数用可并堆维护,维护前n/2大的数就行了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <cstring> #include <algorithm> #define abs(x) x < 0 ? -x : x using namespace std; const int M = 1e6 + 6; int n, top; struct node { int lc, rc, d, k; } t[M]; int a[M], half[M], root[M], st[M], sz[M], cnt[M], l[M], r[M]; int _abs(int x) { return x < 0 ? -x : x; } int Merge(int u, int v) { if(!u || !v) { return u + v; } if(t[u].k < t[v].k) { swap(u, v); } t[u].rc = Merge(t[u].rc, v); if(t[t[u].rc].d > t[t[u].lc].d) { swap(t[u].lc, t[u].rc); } if(!t[u].rc) { t[u].d = 0; } else { t[u].d = t[t[u].rc].d + 1; } return u; } int Top(int x) { return t[x].k; } int Pop(int x) { return Merge(t[x].lc, t[x].rc); } int main() { scanf("%d", &n); for(int i = 1; i <= n; ++i) { scanf("%d", &a[i]); a[i] -= i; } for(int i = 1; i <= n; ++i) { root[i] = i; sz[i] = 1; half[i] = 1; l[i] = r[i] = i; t[i].k = a[i]; while(top && Top(root[st[top]]) > Top(root[i])) { root[i] = Merge(root[st[top]], root[i]); sz[i] = sz[i] + sz[st[top]]; half[i] = half[i] + half[st[top]]; while(half[i] * 2 > sz[i] + 1) { root[i] = Pop(root[i]); --half[i]; } l[i] = l[st[top]]; --top; } st[++top] = i; } long long ans = 0; for(int i = 1; i <= top; ++i) { for(int j = l[st[i]]; j <= r[st[i]]; ++j) { ans += _abs(a[j] - Top(root[st[i]])); } } printf("%lld\n", ans); return 0; }