[BOI2004]Sequence

题面描述

给定整数数组$a_1,a_2,a_3...a_n$,求递增数组$b_1,b_2,b_3...b_n$

使得$|a_1 - b_1| + |a_2 - b_2| + ... + |a_n - b_n|$最大

吐槽:

这道题不是人能想出来的,太神了

很有收获?$or; not;?$

题解:

考虑$b$数组严格递增这个条件过于苛刻,期望$b$数组不严格递增

那么,由于$b_1 < b_2 < ... < b_n$,有$b_1 - 1 leq b_2 - 2 ... leq b_n - n$

我们不妨考虑新的$b'_i = b_i - i$

在$b'_i$对$a'_i = a_i - i$取到最优时,$b_i$同时对$a_i$取到最优

这时,$b‘_i$就可以相等了

两个性质

1. 对于$a'_1 leq a'_2 leq ... leq a'_n$,我们取$b'i = a'i$最优

2. 对于$a'_1 geq a'_2 geq ... geq a'_n$,我们取$b'i$为中位数最优

并且由于$b'_i$相等,$a'_1, a'_2...a'_n$可以任意的排列

因此,我们不妨假设已经处理好了前$i - 1$个数,正要加入第$i$个数

我们假设维护出来的$b'_i$相同的并成一个区间,$b'_i$取这个区间的中位数

对于$a'_i > b'_{i - 1}$,取$b'_i = a'_i$时,相等于递增序列

否则$a'_i < b'_{i - 1}$,我们把$i$并入$b'_{i - 1}$,得到新的中位数(因为顺序任意,所以怎么合都没问题),并继续检查

那么,我们要做的事其实就是

1. 快速合并两个区间中的数

2. 确定出区间中的中位数

左偏树就是不错的选择

复杂度$O(nlog n)$

#include <cstdio>
#include <cmath>
#include <iostream>
#define sid 1000500
#define ll long long
#define ri register int
#define ls(o) t[o].ls
#define rs(o) t[o].rs
using namespace std;

char RR[23456];
#define isdigit(u) (u >= '0' && u <= '9')
extern inline char gc() {
    static char *S = RR + 23333, *T = RR + 23333;
    if(S == T) S = RR, fread(RR, 1, 23333, stdin);
    return *S ++;
}
inline int read() {
    int o = 0, w = 1; char c = gc();
    while(!isdigit(c)) {if(c == '-') w = -1; c = gc();}
    while(isdigit(c)) o = o * 10 + c - '0', c = gc();
    return o * w;
}

int n, cnt;
int rt[sid], ld[sid], rd[sid], a[sid];
struct reHeap {
    int ls, rs, sz, npl, key;
} t[sid];

inline void update(int o) {
    t[o].sz = t[ls(o)].sz + t[rs(o)].sz + 1;
    t[o].npl = t[rs(o)].npl + 1;
}

int merge(int x, int y) {
    if(!x || !y) return x + y;
    if(t[x].key < t[y].key) swap(x, y);
    rs(x) = merge(rs(x), y);
    if(t[rs(x)].npl > t[ls(x)].npl) swap(ls(x), rs(x));
    update(x); return x;
}

int main() {
    n = read();
    for(ri i = 1; i <= n; i ++) {
        ld[++ cnt] = rd[cnt] = rt[cnt] = i;
        a[i] = t[i].key = read() - i; t[i].sz = 1;
        while(cnt > 1 && t[rt[cnt]].key < t[rt[cnt - 1]].key) {
            cnt --; rd[cnt] = rd[cnt + 1];
            rt[cnt] = merge(rt[cnt], rt[cnt + 1]);
            while(t[rt[cnt]].sz * 2 > rd[cnt] - ld[cnt] + 2)
            rt[cnt] = merge(ls(rt[cnt]), rs(rt[cnt]));
        }
    }
    ll ans = 0;
    for(ri i = 1; i <= cnt; i ++)
    for(ri j = ld[i]; j <= rd[i]; j ++)
    ans += abs(t[rt[i]].key - a[j]);
    printf("%lld
", ans);
    for(ri i = 1; i <= cnt; i ++)
    for(ri j = ld[i]; j <= rd[i]; j ++)
    printf("%d ", t[rt[i]].key + j);
    return 0;
}
原文地址:https://www.cnblogs.com/reverymoon/p/9358978.html