bzoj1367

可并堆

对于一段递减的序列我们可以取中位数作为b

那么我们可以把a分成几段递减的序列,每段都取中位数,并且中位数递增

那么维护一个单调栈,每次新加入元素,如果当前段的中位数比之前小就吃掉之前的

中位数用可并堆维护,维护前n/2大的数就行了

#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;
}
View Code

 

posted @ 2018-03-05 09:09  19992147  阅读(121)  评论(0编辑  收藏  举报